From 29d16c83cd7005f4dc796f43dc24aaf00f99248e Mon Sep 17 00:00:00 2001 From: puhui999 Date: Mon, 12 May 2025 17:57:43 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AF=8C=E6=96=87=E6=9C=AC=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E8=BF=81=E7=A7=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web-ele/package.json | 4 + apps/web-ele/src/adapter/component/index.ts | 3 + .../web-ele/src/components/tinymce/editor.vue | 357 ++++++++++++++++++ apps/web-ele/src/components/tinymce/helper.ts | 85 +++++ .../src/components/tinymce/img-upload.vue | 89 +++++ apps/web-ele/src/components/tinymce/index.ts | 1 + .../web-ele/src/components/tinymce/tinymce.ts | 17 + 7 files changed, 556 insertions(+) create mode 100644 apps/web-ele/src/components/tinymce/editor.vue create mode 100644 apps/web-ele/src/components/tinymce/helper.ts create mode 100644 apps/web-ele/src/components/tinymce/img-upload.vue create mode 100644 apps/web-ele/src/components/tinymce/index.ts create mode 100644 apps/web-ele/src/components/tinymce/tinymce.ts diff --git a/apps/web-ele/package.json b/apps/web-ele/package.json index a51f5a9b..ccd69cf0 100644 --- a/apps/web-ele/package.json +++ b/apps/web-ele/package.json @@ -26,6 +26,7 @@ "#/*": "./src/*" }, "dependencies": { + "@tinymce/tinymce-vue": "catalog:", "@vben/access": "workspace:*", "@vben/common-ui": "workspace:*", "@vben/constants": "workspace:*", @@ -41,8 +42,11 @@ "@vben/types": "workspace:*", "@vben/utils": "workspace:*", "@vueuse/core": "catalog:", + "cropperjs": "catalog:", + "crypto-js": "catalog:", "dayjs": "catalog:", "element-plus": "catalog:", + "highlight.js": "catalog:", "pinia": "catalog:", "vue": "catalog:", "vue-router": "catalog:" diff --git a/apps/web-ele/src/adapter/component/index.ts b/apps/web-ele/src/adapter/component/index.ts index 470caaa7..71152983 100644 --- a/apps/web-ele/src/adapter/component/index.ts +++ b/apps/web-ele/src/adapter/component/index.ts @@ -21,6 +21,7 @@ import { $t } from '@vben/locales'; import { ElNotification } from 'element-plus'; +import { Tinymce as RichTextarea } from '#/components/tinymce'; import { FileUpload, ImageUpload } from '#/components/upload'; const ElButton = defineAsyncComponent(() => @@ -175,6 +176,7 @@ export type ComponentType = | 'Input' | 'InputNumber' | 'RadioGroup' + | 'RichTextarea' | 'Select' | 'Space' | 'Switch' @@ -321,6 +323,7 @@ async function initComponentAdapter() { Upload: ElUpload, FileUpload, ImageUpload, + RichTextarea, }; // 将组件注册到全局共享状态中 diff --git a/apps/web-ele/src/components/tinymce/editor.vue b/apps/web-ele/src/components/tinymce/editor.vue new file mode 100644 index 00000000..cb874f6c --- /dev/null +++ b/apps/web-ele/src/components/tinymce/editor.vue @@ -0,0 +1,357 @@ + + + + + + diff --git a/apps/web-ele/src/components/tinymce/helper.ts b/apps/web-ele/src/components/tinymce/helper.ts new file mode 100644 index 00000000..1f98dda4 --- /dev/null +++ b/apps/web-ele/src/components/tinymce/helper.ts @@ -0,0 +1,85 @@ +const validEvents = new Set([ + 'onActivate', + 'onAddUndo', + 'onBeforeAddUndo', + 'onBeforeExecCommand', + 'onBeforeGetContent', + 'onBeforePaste', + 'onBeforeRenderUI', + 'onBeforeSetContent', + 'onBlur', + 'onChange', + 'onClearUndos', + 'onClick', + 'onContextMenu', + 'onCopy', + 'onCut', + 'onDblclick', + 'onDeactivate', + 'onDirty', + 'onDrag', + 'onDragDrop', + 'onDragEnd', + 'onDragGesture', + 'onDragOver', + 'onDrop', + 'onExecCommand', + 'onFocus', + 'onFocusIn', + 'onFocusOut', + 'onGetContent', + 'onHide', + 'onInit', + 'onKeyDown', + 'onKeyPress', + 'onKeyUp', + 'onLoadContent', + 'onMouseDown', + 'onMouseEnter', + 'onMouseLeave', + 'onMouseMove', + 'onMouseOut', + 'onMouseOver', + 'onMouseUp', + 'onNodeChange', + 'onObjectResized', + 'onObjectResizeStart', + 'onObjectSelected', + 'onPaste', + 'onPostProcess', + 'onPostRender', + 'onPreProcess', + 'onProgressState', + 'onRedo', + 'onRemove', + 'onReset', + 'onSaveContent', + 'onSelectionChange', + 'onSetAttrib', + 'onSetContent', + 'onShow', + 'onSubmit', + 'onUndo', + 'onVisualAid', +]); + +const isValidKey = (key: string) => validEvents.has(key); + +export const bindHandlers = ( + initEvent: Event, + listeners: any, + editor: any, +): void => { + Object.keys(listeners) + .filter((element) => isValidKey(element)) + .forEach((key: string) => { + const handler = listeners[key]; + if (typeof handler === 'function') { + if (key === 'onInit') { + handler(initEvent, editor); + } else { + editor.on(key.slice(2), (e: any) => handler(e, editor)); + } + } + }); +}; diff --git a/apps/web-ele/src/components/tinymce/img-upload.vue b/apps/web-ele/src/components/tinymce/img-upload.vue new file mode 100644 index 00000000..8aefc08a --- /dev/null +++ b/apps/web-ele/src/components/tinymce/img-upload.vue @@ -0,0 +1,89 @@ + + + + diff --git a/apps/web-ele/src/components/tinymce/index.ts b/apps/web-ele/src/components/tinymce/index.ts new file mode 100644 index 00000000..c277d781 --- /dev/null +++ b/apps/web-ele/src/components/tinymce/index.ts @@ -0,0 +1 @@ +export { default as Tinymce } from './editor.vue'; diff --git a/apps/web-ele/src/components/tinymce/tinymce.ts b/apps/web-ele/src/components/tinymce/tinymce.ts new file mode 100644 index 00000000..45a867b6 --- /dev/null +++ b/apps/web-ele/src/components/tinymce/tinymce.ts @@ -0,0 +1,17 @@ +// Any plugins you want to setting has to be imported +// Detail plugins list see https://www.tiny.cloud/docs/plugins/ +// Custom builds see https://www.tiny.cloud/download/custom-builds/ +// colorpicker/contextmenu/textcolor plugin is now built in to the core editor, please remove it from your editor configuration + +export const plugins = + 'preview importcss searchreplace autolink autosave save directionality code visualblocks visualchars fullscreen image link media codesample table charmap pagebreak nonbreaking anchor insertdatetime advlist lists wordcount help emoticons accordion'; + +// 和 vben2.0 不同,从 https://www.tiny.cloud/ 拷贝 Vue 部分,然后去掉 importword exportword exportpdf | math 部分,并额外增加最后一行(来自 vben2.0 差异的部分) +export const toolbar = + 'undo redo | accordion accordionremove | \\\n' + + ' blocks fontfamily fontsize | bold italic underline strikethrough | \\\n' + + ' align numlist bullist | link image | table media | \\\n' + + ' lineheight outdent indent | forecolor backcolor removeformat | \\\n' + + ' charmap emoticons | code fullscreen preview | save print | \\\n' + + ' pagebreak anchor codesample | ltr rtl | \\\n' + + ' hr searchreplace alignleft aligncenter alignright blockquote subscript superscript';