index.vue 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. <template>
  2. <div class="wangeditor-wrapper">
  3. <div ref="toolbarContainer" class="toolbar-container"></div>
  4. <div ref="editorContainer" class="editor-container"></div>
  5. </div>
  6. </template>
  7. <script>
  8. import '@wangeditor/editor/dist/css/style.css'
  9. import { createEditor, createToolbar } from '@wangeditor/editor'
  10. import Cookies from 'js-cookie'
  11. export default {
  12. name: 'EditorVue',
  13. props: {
  14. content: {
  15. type: String,
  16. default: ''
  17. }
  18. },
  19. data() {
  20. return {
  21. editor: null,
  22. toolbar: null
  23. }
  24. },
  25. watch: {
  26. content(newVal) {
  27. // 外部 prop 变化(异步加载详情)才回填,避免输入过程中光标跳动与循环触发
  28. if (this.editor && newVal !== this.editor.getHtml()) {
  29. this.editor.setHtml(newVal || '')
  30. }
  31. }
  32. },
  33. mounted() {
  34. this._createEditor()
  35. },
  36. beforeDestroy() {
  37. this._destroyEditor()
  38. },
  39. methods: {
  40. // 父组件 ref 调用的初始化入口(对齐老 Quill 实现的 init() 行为)
  41. init() {
  42. this._destroyEditor()
  43. this._createEditor()
  44. },
  45. _destroyEditor() {
  46. if (this.editor) {
  47. this.editor.destroy()
  48. this.editor = null
  49. }
  50. if (this.toolbar) {
  51. this.toolbar.destroy()
  52. this.toolbar = null
  53. }
  54. },
  55. _createEditor() {
  56. const editorConfig = {
  57. html: this.content || '',
  58. placeholder: '请在这里输入',
  59. readOnly: false,
  60. autoFocus: false,
  61. MENU_CONF: {
  62. uploadImage: {
  63. // 沿用项目原有的 OSS 上传端点,token 走 URL 参数
  64. server: `${window.SITE_CONFIG['apiURL']}/sys/oss/upload?token=${Cookies.get('token') || ''}`,
  65. fieldName: 'file',
  66. // 后端响应是项目老格式 { code: 0, data: 'url', msg: '...' },
  67. // wangEditor 默认期望 { errno, data: [{ url, alt, href }] },这里桥接一下
  68. customInsert(res, insertFn) {
  69. if (res && res.code === 0 && res.data) {
  70. insertFn(res.data, '', res.data)
  71. }
  72. }
  73. },
  74. fontFamily: {
  75. fontFamilyList: [
  76. { name: '宋体', value: 'SimSun' },
  77. { name: '黑体', value: 'SimHei' },
  78. { name: '微软雅黑', value: 'Microsoft YaHei' },
  79. { name: '楷体', value: 'KaiTi' },
  80. { name: '仿宋', value: 'FangSong' },
  81. { name: 'Arial', value: 'Arial' }
  82. ]
  83. }
  84. },
  85. onChange: (editor) => {
  86. this.$emit('EditorChange', editor.getHtml())
  87. }
  88. }
  89. this.editor = createEditor({
  90. selector: this.$refs.editorContainer,
  91. config: editorConfig,
  92. mode: 'default'
  93. })
  94. const toolbarConfig = {
  95. toolbarKeys: [
  96. 'undo', 'redo',
  97. '|',
  98. 'headerSelect',
  99. 'bold', 'italic', 'underline', 'strikeThrough',
  100. '|',
  101. 'color', 'bgColor',
  102. '|',
  103. 'fontSize', 'fontFamily',
  104. 'lineHeight',
  105. '|',
  106. 'justifyLeft', 'justifyCenter', 'justifyRight',
  107. '|',
  108. 'bulletedList', 'numberedList',
  109. 'indent',
  110. '|',
  111. 'insertLink', 'uploadImage',
  112. 'blockquote', 'codeBlock',
  113. '|',
  114. 'divider', 'splitLine'
  115. ]
  116. }
  117. this.toolbar = createToolbar({
  118. editor: this.editor,
  119. selector: this.$refs.toolbarContainer,
  120. config: toolbarConfig,
  121. mode: 'default'
  122. })
  123. }
  124. }
  125. }
  126. </script>
  127. <style>
  128. .wangeditor-wrapper {
  129. border: 1px solid #dcdfe6;
  130. border-radius: 4px;
  131. background: #fff;
  132. }
  133. .wangeditor-wrapper .toolbar-container {
  134. border-bottom: 1px solid #dcdfe6;
  135. }
  136. .wangeditor-wrapper .editor-container {
  137. min-height: 600px;
  138. height: auto !important;
  139. }
  140. </style>