From a1d7e81859f554f3a53680cc35f0f49bf1f77098 Mon Sep 17 00:00:00 2001
From: wwf <1971391498@qq.com>
Date: 星期四, 14 五月 2026 14:37:02 +0800
Subject: [PATCH] 导入项目

---
 src/components/Editor/src/Editor.vue |  294 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 294 insertions(+), 0 deletions(-)

diff --git a/src/components/Editor/src/Editor.vue b/src/components/Editor/src/Editor.vue
new file mode 100644
index 0000000..30146be
--- /dev/null
+++ b/src/components/Editor/src/Editor.vue
@@ -0,0 +1,294 @@
+<script lang="ts" setup>
+import { PropType } from 'vue'
+import { Editor, Toolbar } from '@wangeditor-next/editor-for-vue'
+import { i18nChangeLanguage, IDomEditor, IEditorConfig } from '@wangeditor-next/editor'
+import { propTypes } from '@/utils/propTypes'
+import { isNumber } from '@/utils/is'
+import { ElMessage } from 'element-plus'
+import { useLocaleStore } from '@/store/modules/locale'
+import { getRefreshToken, getTenantId } from '@/utils/auth'
+import { getUploadUrl } from '@/components/UploadFile/src/useUpload'
+import merge from 'lodash-es/merge'
+
+defineOptions({ name: 'Editor' })
+
+type InsertFnType = (url: string, alt: string, href: string) => void
+
+const localeStore = useLocaleStore()
+
+const currentLocale = computed(() => localeStore.getCurrentLocale)
+
+i18nChangeLanguage(unref(currentLocale).lang)
+
+const props = defineProps({
+  editorId: propTypes.string.def('wangEditor-1'),
+  height: propTypes.oneOfType([Number, String]).def('500px'),
+  editorConfig: {
+    type: Object as PropType<Partial<IEditorConfig>>,
+    default: () => undefined
+  },
+  readonly: propTypes.bool.def(false),
+  modelValue: propTypes.string.def(''),
+  directory: propTypes.string.def('editor-default')
+})
+
+const emit = defineEmits(['change', 'update:modelValue'])
+
+// 缂栬緫鍣ㄥ疄渚嬶紝蹇呴』鐢� shallowRef
+const editorRef = shallowRef<IDomEditor>()
+
+const valueHtml = ref('')
+
+watch(
+  () => props.modelValue,
+  (val: string) => {
+    if (!val) {
+      val = ''
+    }
+    if (val === unref(valueHtml)) return
+    valueHtml.value = val
+  },
+  {
+    immediate: true
+  }
+)
+
+// 鐩戝惉
+watch(
+  () => valueHtml.value,
+  (val: string) => {
+    emit('update:modelValue', val)
+  }
+)
+watch(
+  () => props.readonly,
+  async (val) => {
+    // 鐗规畩锛氱瓑寰� editorRef 娓叉煋瀹屾垚
+    if (!editorRef.value) {
+      await nextTick()
+    }
+    if (val) {
+      editorRef.value?.disable()
+    } else {
+      editorRef.value?.enable()
+    }
+  }
+)
+
+const handleCreated = (editor: IDomEditor) => {
+  editorRef.value = editor
+}
+
+// 缂栬緫鍣ㄩ厤缃�
+const editorConfig = computed((): IEditorConfig => {
+  return merge(
+    {
+      placeholder: '璇疯緭鍏ュ唴瀹�...',
+      readOnly: props.readonly,
+      customAlert: (s: string, t: string) => {
+        switch (t) {
+          case 'success':
+            ElMessage.success(s)
+            break
+          case 'info':
+            ElMessage.info(s)
+            break
+          case 'warning':
+            ElMessage.warning(s)
+            break
+          case 'error':
+            ElMessage.error(s)
+            break
+          default:
+            ElMessage.info(s)
+            break
+        }
+      },
+      autoFocus: false,
+      scroll: true,
+      EXTEND_CONF: {
+        mentionConfig: {
+          showModal: () => {},
+          hideModal: () => {}
+        }
+      },
+      MENU_CONF: {
+        ['uploadImage']: {
+          server: getUploadUrl(),
+          // 鍗曚釜鏂囦欢鐨勬渶澶т綋绉檺鍒讹紝榛樿涓� 2M
+          maxFileSize: 10 * 1024 * 1024,
+          // 鏈�澶氬彲涓婁紶鍑犱釜鏂囦欢锛岄粯璁や负 100
+          maxNumberOfFiles: 100,
+          // 閫夋嫨鏂囦欢鏃剁殑绫诲瀷闄愬埗锛岄粯璁や负 ['image/*'] 銆傚涓嶆兂闄愬埗锛屽垯璁剧疆涓� []
+          allowedFileTypes: ['image/*'],
+
+          // 鑷畾涔夊鍔� http  header
+          headers: {
+            Accept: '*',
+            Authorization: 'Bearer ' + getRefreshToken(), // 浣跨敤 getRefreshToken() 鏂规硶锛岃�屼笉浣跨敤 getAccessToken() 鏂规硶鐨勫師鍥狅細Editor 鏃犳硶鏂逛究鐨勫埛鏂拌闂护鐗�
+            'tenant-id': getTenantId()
+          },
+
+          // 瓒呮椂鏃堕棿锛岄粯璁や负 10 绉�
+          timeout: 15 * 1000, // 15 绉�
+
+          // form-data fieldName锛屽悗绔帴鍙e弬鏁板悕绉帮紝榛樿鍊紈angeditor-uploaded-image
+          fieldName: 'file',
+          // 闄勫姞鍙傛暟
+          meta: {
+            directory: `${props.directory}-image`
+          },
+          metaWithUrl: false,
+
+          // uppy 閰嶇疆椤�
+          uppyConfig: {
+            onBeforeFileAdded: (newFile: any) => {
+              newFile.id = `${newFile.id}-${Date.now()}`
+              return newFile
+            }
+          },
+
+          // 涓婁紶涔嬪墠瑙﹀彂
+          onBeforeUpload(file: File) {
+            // console.log(file)
+            return file
+          },
+          // 涓婁紶杩涘害鐨勫洖璋冨嚱鏁�
+          onProgress(progress: number) {
+            // progress 鏄� 0-100 鐨勬暟瀛�
+            console.log('progress', progress)
+          },
+          onSuccess(file: File, res: any) {
+            console.log('onSuccess', file, res)
+          },
+          onFailed(file: File, res: any) {
+            alert(res.message)
+            console.log('onFailed', file, res)
+          },
+          onError(file: File, err: any, res: any) {
+            alert(err.message)
+            console.error('onError', file, err, res)
+          },
+          // 鑷畾涔夋彃鍏ュ浘鐗�
+          customInsert(res: any, insertFn: InsertFnType) {
+            insertFn(res.data, 'image', res.data)
+          }
+        },
+        ['uploadVideo']: {
+          server: getUploadUrl(),
+          // 鍗曚釜鏂囦欢鐨勬渶澶т綋绉檺鍒讹紝榛樿涓� 10M
+          maxFileSize: 1024 * 1024 * 1024,
+          // 鏈�澶氬彲涓婁紶鍑犱釜鏂囦欢锛岄粯璁や负 100
+          maxNumberOfFiles: 10,
+          // 閫夋嫨鏂囦欢鏃剁殑绫诲瀷闄愬埗锛岄粯璁や负 ['video/*'] 銆傚涓嶆兂闄愬埗锛屽垯璁剧疆涓� []
+          allowedFileTypes: ['video/*'],
+
+          // 鑷畾涔夊鍔� http  header
+          headers: {
+            Accept: '*',
+            Authorization: 'Bearer ' + getRefreshToken(), // 浣跨敤 getRefreshToken() 鏂规硶锛岃�屼笉浣跨敤 getAccessToken() 鏂规硶鐨勫師鍥狅細Editor 鏃犳硶鏂逛究鐨勫埛鏂拌闂护鐗�
+            'tenant-id': getTenantId()
+          },
+
+          // 瓒呮椂鏃堕棿锛岄粯璁や负 30 绉�
+          timeout: 15 * 1000, // 15 绉�
+
+          // form-data fieldName锛屽悗绔帴鍙e弬鏁板悕绉帮紝榛樿鍊紈angeditor-uploaded-image
+          fieldName: 'file',
+          // 闄勫姞鍙傛暟
+          meta: {
+            directory: `${props.directory}-video`
+          },
+          metaWithUrl: false,
+
+          // uppy 閰嶇疆椤�
+          uppyConfig: {
+            onBeforeFileAdded: (newFile: any) => {
+              newFile.id = `${newFile.id}-${Date.now()}`
+              return newFile
+            }
+          },
+
+          // 涓婁紶涔嬪墠瑙﹀彂
+          onBeforeUpload(file: File) {
+            // console.log(file)
+            return file
+          },
+          // 涓婁紶杩涘害鐨勫洖璋冨嚱鏁�
+          onProgress(progress: number) {
+            // progress 鏄� 0-100 鐨勬暟瀛�
+            console.log('progress', progress)
+          },
+          onSuccess(file: File, res: any) {
+            console.log('onSuccess', file, res)
+          },
+          onFailed(file: File, res: any) {
+            alert(res.message)
+            console.log('onFailed', file, res)
+          },
+          onError(file: File, err: any, res: any) {
+            alert(err.message)
+            console.error('onError', file, err, res)
+          },
+          // 鑷畾涔夋彃鍏ュ浘鐗�
+          customInsert(res: any, insertFn: InsertFnType) {
+            insertFn(res.data, 'mp4', res.data)
+          }
+        }
+      },
+      uploadImgShowBase64: true
+    },
+    props.editorConfig || {}
+  )
+})
+
+const editorStyle = computed(() => {
+  return {
+    height: isNumber(props.height) ? `${props.height}px` : props.height
+  }
+})
+
+// 鍥炶皟鍑芥暟
+const handleChange = (editor: IDomEditor) => {
+  emit('change', editor)
+}
+
+// 缁勪欢閿�姣佹椂锛屽強鏃堕攢姣佺紪杈戝櫒
+onBeforeUnmount(() => {
+  const editor = unref(editorRef.value)
+
+  // 閿�姣侊紝骞剁Щ闄� editor
+  editor?.destroy()
+})
+
+const getEditorRef = async (): Promise<IDomEditor> => {
+  await nextTick()
+  return unref(editorRef.value) as IDomEditor
+}
+
+defineExpose({
+  getEditorRef
+})
+</script>
+
+<template>
+  <div class="border-1 border-solid border-[var(--tags-view-border-color)] z-10">
+    <!-- 宸ュ叿鏍� -->
+    <Toolbar
+      :editor="editorRef"
+      :editorId="editorId"
+      class="border-0 b-b-1 border-solid border-[var(--tags-view-border-color)]"
+    />
+    <!-- 缂栬緫鍣� -->
+    <Editor
+      v-model="valueHtml"
+      :defaultConfig="editorConfig"
+      :editorId="editorId"
+      :style="editorStyle"
+      @on-change="handleChange"
+      @on-created="handleCreated"
+    />
+  </div>
+</template>
+
+<style src="@wangeditor-next/editor/dist/css/style.css"></style>

--
Gitblit v1.8.0