Editor.vue 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. <script lang="ts" name="Editor" setup>
  2. import { PropType } from 'vue'
  3. import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
  4. import { i18nChangeLanguage, IDomEditor, IEditorConfig } from '@wangeditor/editor'
  5. import { propTypes } from '@/utils/propTypes'
  6. import { isNumber } from '@/utils/is'
  7. import { ElMessage } from 'element-plus'
  8. import { useLocaleStore } from '@/store/modules/locale'
  9. import { getAccessToken, getTenantId } from '@/utils/auth'
  10. type InsertFnType = (url: string, alt: string, href: string) => void
  11. const localeStore = useLocaleStore()
  12. const currentLocale = computed(() => localeStore.getCurrentLocale)
  13. i18nChangeLanguage(unref(currentLocale).lang)
  14. const props = defineProps({
  15. editorId: propTypes.string.def('wangeEditor-1'),
  16. height: propTypes.oneOfType([Number, String]).def('500px'),
  17. editorConfig: {
  18. type: Object as PropType<Partial<IEditorConfig>>,
  19. default: () => undefined
  20. },
  21. readonly: propTypes.bool.def(false),
  22. modelValue: propTypes.string.def('')
  23. })
  24. const emit = defineEmits(['change', 'update:modelValue'])
  25. // 编辑器实例,必须用 shallowRef
  26. const editorRef = shallowRef<IDomEditor>()
  27. const valueHtml = ref('')
  28. watch(
  29. () => props.modelValue,
  30. (val: string) => {
  31. if (val === unref(valueHtml)) return
  32. valueHtml.value = val
  33. },
  34. {
  35. immediate: true
  36. }
  37. )
  38. // 监听
  39. watch(
  40. () => valueHtml.value,
  41. (val: string) => {
  42. emit('update:modelValue', val)
  43. }
  44. )
  45. const handleCreated = (editor: IDomEditor) => {
  46. editorRef.value = editor
  47. }
  48. // 编辑器配置
  49. const editorConfig = computed((): IEditorConfig => {
  50. return Object.assign(
  51. {
  52. placeholder: '请输入内容...',
  53. readOnly: props.readonly,
  54. customAlert: (s: string, t: string) => {
  55. switch (t) {
  56. case 'success':
  57. ElMessage.success(s)
  58. break
  59. case 'info':
  60. ElMessage.info(s)
  61. break
  62. case 'warning':
  63. ElMessage.warning(s)
  64. break
  65. case 'error':
  66. ElMessage.error(s)
  67. break
  68. default:
  69. ElMessage.info(s)
  70. break
  71. }
  72. },
  73. autoFocus: false,
  74. scroll: true,
  75. MENU_CONF: {
  76. ['uploadImage']: {
  77. server: import.meta.env.VITE_UPLOAD_URL,
  78. // 单个文件的最大体积限制,默认为 2M
  79. maxFileSize: 5 * 1024 * 1024,
  80. // 最多可上传几个文件,默认为 100
  81. maxNumberOfFiles: 10,
  82. // 选择文件时的类型限制,默认为 ['image/*'] 。如不想限制,则设置为 []
  83. allowedFileTypes: ['image/*'],
  84. // 自定义上传参数,例如传递验证的 token 等。参数会被添加到 formData 中,一起上传到服务端。
  85. meta: { updateSupport: 0 },
  86. // 将 meta 拼接到 url 参数中,默认 false
  87. metaWithUrl: true,
  88. // 自定义增加 http header
  89. headers: {
  90. Accept: '*',
  91. Authorization: 'Bearer ' + getAccessToken(),
  92. 'tenant-id': getTenantId()
  93. },
  94. // 跨域是否传递 cookie ,默认为 false
  95. withCredentials: true,
  96. // 超时时间,默认为 10 秒
  97. timeout: 5 * 1000, // 5 秒
  98. // form-data fieldName,后端接口参数名称,默认值wangeditor-uploaded-image
  99. fieldName: 'file',
  100. // 上传之前触发
  101. onBeforeUpload(file: File) {
  102. console.log(file)
  103. return file
  104. },
  105. // 上传进度的回调函数
  106. onProgress(progress: number) {
  107. // progress 是 0-100 的数字
  108. console.log('progress', progress)
  109. },
  110. onSuccess(file: File, res: any) {
  111. console.log('onSuccess', file, res)
  112. },
  113. onFailed(file: File, res: any) {
  114. alert(res.message)
  115. console.log('onFailed', file, res)
  116. },
  117. onError(file: File, err: any, res: any) {
  118. alert(err.message)
  119. console.error('onError', file, err, res)
  120. },
  121. // 自定义插入图片
  122. customInsert(res: any, insertFn: InsertFnType) {
  123. insertFn(res.data, 'image', res.data)
  124. }
  125. }
  126. },
  127. uploadImgShowBase64: true
  128. },
  129. props.editorConfig || {}
  130. )
  131. })
  132. const editorStyle = computed(() => {
  133. return {
  134. height: isNumber(props.height) ? `${props.height}px` : props.height
  135. }
  136. })
  137. // 回调函数
  138. const handleChange = (editor: IDomEditor) => {
  139. emit('change', editor)
  140. }
  141. // 组件销毁时,及时销毁编辑器
  142. onBeforeUnmount(() => {
  143. const editor = unref(editorRef.value)
  144. if (editor === null) return
  145. // 销毁,并移除 editor
  146. editor?.destroy()
  147. })
  148. const getEditorRef = async (): Promise<IDomEditor> => {
  149. await nextTick()
  150. return unref(editorRef.value) as IDomEditor
  151. }
  152. defineExpose({
  153. getEditorRef
  154. })
  155. </script>
  156. <template>
  157. <div class="border-1 border-solid border-[var(--tags-view-border-color)] z-99">
  158. <!-- 工具栏 -->
  159. <Toolbar
  160. :editor="editorRef"
  161. :editorId="editorId"
  162. class="border-bottom-1 border-solid border-[var(--tags-view-border-color)]"
  163. />
  164. <!-- 编辑器 -->
  165. <Editor
  166. v-model="valueHtml"
  167. :defaultConfig="editorConfig"
  168. :editorId="editorId"
  169. :style="editorStyle"
  170. @on-change="handleChange"
  171. @on-created="handleCreated"
  172. />
  173. </div>
  174. </template>
  175. <style src="@wangeditor/editor/dist/css/style.css"></style>