index.vue 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. <template>
  2. <!-- 组件外部的 form-item -->
  3. <div style="z-index: 100; border: 1px solid #ccc; width: 100%">
  4. <Toolbar style="border-bottom: 1px solid #ccc" :editor="editorRef" :defaultConfig="toolbarConfig" :mode="mode" />
  5. <Editor
  6. :style="{ height: props.height + 'px', overflowY: 'hidden' }"
  7. v-model="content"
  8. :defaultConfig="editorConfig"
  9. :mode="props.mode"
  10. @onCreated="handleCreated" />
  11. <a-modal style="z-index: 1000" v-model:visible="resourceVisible" :render-to-body="false" :width="1080" :footer="false" draggable>
  12. <template #title>资源选择器</template>
  13. <sa-resource v-model="list" multiple ref="resource" returnType="url" />
  14. </a-modal>
  15. </div>
  16. </template>
  17. <script setup>
  18. import '@wangeditor/editor/dist/css/style.css'
  19. import { onBeforeUnmount, ref, shallowRef, watch, computed } from 'vue'
  20. import { Boot } from '@wangeditor/editor'
  21. import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
  22. import { useAppStore } from '@/store'
  23. import commonApi from '@/api/common'
  24. import file2md5 from 'file2md5'
  25. import tool from '@/utils/tool'
  26. const resourceVisible = ref(false)
  27. const appStore = useAppStore()
  28. const props = defineProps({
  29. modelValue: { type: String },
  30. component: Object,
  31. height: { type: Number, default: 300 },
  32. mode: { type: String, default: 'default' },
  33. customField: { type: String, default: undefined },
  34. })
  35. const emit = defineEmits(['update:modelValue', 'change'])
  36. let registerWangEditorButtonFlag = appStore.appCurrentSetting.registerWangEditorButtonFlag
  37. const list = ref([])
  38. const resource = ref()
  39. let content = computed({
  40. get() {
  41. return props.modelValue
  42. },
  43. set(value) {
  44. emit('update:modelValue', value)
  45. },
  46. })
  47. watch(
  48. () => content.value,
  49. (vl) => emit('change', vl)
  50. )
  51. watch(
  52. () => list.value,
  53. (imgs) => {
  54. let tmp = ''
  55. imgs.map((img) => {
  56. if (
  57. img.indexOf('.jpg') > -1 ||
  58. img.indexOf('.png') > -1 ||
  59. img.indexOf('.bmp') > -1 ||
  60. img.indexOf('.jpeg') > -1 ||
  61. img.indexOf('.svg') > -1 ||
  62. img.indexOf('.gif') > -1
  63. ) {
  64. const node = { type: 'image', src: img, href: '', alt: '', style: {}, children: [{ text: '' }] }
  65. editorRef.value.insertNode(node)
  66. }
  67. })
  68. resource.value.clearSelecteds()
  69. resourceVisible.value = false
  70. }
  71. )
  72. const editorRef = shallowRef()
  73. const toolbarConfig = {}
  74. toolbarConfig.excludeKeys = ['group-video', 'insertImage']
  75. class MyButtonMenu {
  76. constructor() {
  77. this.title = '资源选择器'
  78. this.tag = 'button'
  79. }
  80. // 获取菜单执行时的 value ,用不到则返回空 字符串或 false
  81. getValue(editor) {
  82. return ''
  83. }
  84. // // 菜单是否需要激活(如选中加粗文本,“加粗”菜单会激活),用不到则返回 false
  85. isActive(editor) {
  86. return false
  87. }
  88. // 菜单是否需要禁用(如选中 H1 ,“引用”菜单被禁用),用不到则返回 false
  89. isDisabled(editor) {
  90. return false
  91. }
  92. // 点击菜单时触发的函数
  93. exec(editor, value) {
  94. editor.emit('click_menu')
  95. }
  96. }
  97. const menu1Conf = {
  98. key: 'menu1', // 定义 menu key :要保证唯一、不重复(重要)
  99. factory() {
  100. return new MyButtonMenu()
  101. },
  102. }
  103. if (registerWangEditorButtonFlag === undefined || registerWangEditorButtonFlag === false) {
  104. Boot.registerMenu(menu1Conf)
  105. appStore.setRegisterWangEditorButtonFlag(true)
  106. }
  107. toolbarConfig.insertKeys = {
  108. index: 1, // 插入的位置,基于当前的 toolbarKeys
  109. keys: ['menu1'],
  110. }
  111. const editorConfig = {
  112. placeholder: '请输入内容...',
  113. MENU_CONF: {},
  114. hoverbarKeys: {
  115. // 在编辑器中,选中链接文本时,要弹出的菜单
  116. link: {
  117. menuKeys: [
  118. // 默认的配置可以通过 `editor.getConfig().hoverbarKeys.image` 获取
  119. 'imageWidth30',
  120. 'imageWidth50',
  121. 'imageWidth100',
  122. '|', // 分割符
  123. 'imageFloatNone', // 增加 '图片浮动' 菜单
  124. 'imageFloatLeft',
  125. 'imageFloatRight',
  126. '|', // 分割符
  127. 'editImage',
  128. 'viewImageLink',
  129. 'deleteImage',
  130. ],
  131. },
  132. },
  133. }
  134. editorConfig.MENU_CONF['uploadImage'] = {
  135. async customUpload(file, insertFn) {
  136. uploadRequest(file, 'image', 'uploadImage').then((res) => {
  137. insertFn(tool.attachUrl(res.url))
  138. })
  139. },
  140. }
  141. const uploadRequest = async (file, type, method, requestData = {}) => {
  142. const hash = await file2md5(file)
  143. const dataForm = new FormData()
  144. dataForm.append(type, file)
  145. dataForm.append('isChunk', false)
  146. dataForm.append('hash', hash)
  147. for (let name in requestData) {
  148. dataForm.append(name, requestData[name])
  149. }
  150. const response = await commonApi[method](dataForm)
  151. return response.data
  152. }
  153. const handleCreated = (editor) => {
  154. editorRef.value = editor
  155. editorRef.value.on('click_menu', () => {
  156. resourceVisible.value = true
  157. })
  158. }
  159. onBeforeUnmount(() => {
  160. const editor = editorRef.value
  161. if (editor == null) return
  162. editor.destroy()
  163. })
  164. </script>
  165. <style scoped></style>