Editor.vue 5.4 KB

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