diff --git a/index.html b/index.html index 278de2c..1f9ec73 100644 --- a/index.html +++ b/index.html @@ -32,7 +32,7 @@
%VITE_APP_TITLE%
diff --git a/package.json b/package.json index cf22a3a..522068b 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,8 @@ }, "dependencies": { "@element-plus/icons-vue": "^2.3.2", + "@wangeditor/editor": "^5.1.23", + "@wangeditor/editor-for-vue": "^5.1.12", "axios": "1.12.0", "cron-parser": "^4.9", "cropperjs": "^1.6.2", diff --git a/src/components/piEditor/ext/FontSize.ts b/src/components/piEditor/ext/FontSize.ts deleted file mode 100644 index 38444ef..0000000 --- a/src/components/piEditor/ext/FontSize.ts +++ /dev/null @@ -1,18 +0,0 @@ -import {TextStyle} from '@tiptap/extension-text-style' - -export default TextStyle.extend({ - addAttributes() { - return { - fontSize: { - default: null, - parseHTML: element => element.style.fontSize || null, - renderHTML: attributes => { - if (!attributes.fontSize) return {} - return { - style: `font-size: ${attributes.fontSize}`, - } - }, - }, - } - }, -}) diff --git a/src/components/piEditor/ext/Image/Action.vue b/src/components/piEditor/ext/Image/Action.vue deleted file mode 100644 index a9de392..0000000 --- a/src/components/piEditor/ext/Image/Action.vue +++ /dev/null @@ -1,111 +0,0 @@ - - - - - diff --git a/src/components/piEditor/ext/Image/index.ts b/src/components/piEditor/ext/Image/index.ts deleted file mode 100644 index 3804894..0000000 --- a/src/components/piEditor/ext/Image/index.ts +++ /dev/null @@ -1,29 +0,0 @@ -import Action from "./Action.vue" -import {VueNodeViewRenderer} from "@tiptap/vue-3" -import { Image } from '@tiptap/extension-image' - -export default Image.extend({ - addAttributes() { - return { - ...this.parent?.(), - class: { - default: null, - }, - style: { - default: null, - }, - width: { - default: null, - }, - height: { - default: null - }, - alt: { - default: null - } - } - }, - addNodeView() { - return VueNodeViewRenderer(Action) - }, -}) diff --git a/src/components/piEditor/ext/Indent.ts b/src/components/piEditor/ext/Indent.ts deleted file mode 100644 index 31d7a86..0000000 --- a/src/components/piEditor/ext/Indent.ts +++ /dev/null @@ -1,109 +0,0 @@ -import {Extension, Command} from '@tiptap/core' - -declare module '@tiptap/core' { - interface Commands { - indent: { - increaseIndent: () => ReturnType - decreaseIndent: () => ReturnType - } - } -} - -export default Extension.create({ - name: 'indent', - addOptions() { - return { - maxIndent: 50, - types: ['paragraph', 'heading', 'code_block', 'bullet_list', 'ordered_list'], - } - }, - addGlobalAttributes() { - return [ - { - types: this.options.types, - attributes: { - 'indent': { - default: 0, - parseHTML: element => parseInt(element.getAttribute('indent') || '0', 10), - renderHTML: attributes => { - const level = attributes['indent'] - return level > 0 && { - 'indent': level, - style: `text-indent: ${level * 2}em;` - } - }, - }, - }, - }, - ] - }, - addCommands() { - return { - increaseIndent: - (): Command => - ({tr, state, dispatch}) => { - const {from, to} = state.selection - let modified = false - state.doc.nodesBetween(from, to, (node, pos) => { - if (this.options.types.includes(node.type.name)) { - const indent = node.attrs['indent'] || 0 - if (indent + 1 > this.options.maxIndent) return - tr.setNodeMarkup(pos, undefined, { - ...node.attrs, - 'indent': indent + 1, - }) - modified = true - } - }) - if (modified) { - dispatch && dispatch(tr) - return false - } - return true - }, - - decreaseIndent: - (): Command => - ({tr, state, dispatch}) => { - const {from, to} = state.selection - let modified = false - state.doc.nodesBetween(from, to, (node, pos) => { - if (this.options.types.includes(node.type.name)) { - const indent = node.attrs['indent'] || 0 - if (indent - 1 < 0) return - const newIndent = Math.max(indent - 1, 0) - tr.setNodeMarkup(pos, undefined, { - ...node.attrs, - 'indent': newIndent, - }) - modified = true - } - }) - if (modified) { - dispatch && dispatch(tr) - return false - } - return true - }, - } - }, - addKeyboardShortcuts() { - return { - Tab: () => this.editor.commands.increaseIndent(), - 'Shift-Tab': () => this.editor.commands.decreaseIndent(), - Backspace: () => { - const {state} = this.editor - const {selection} = state - const {$from} = selection - if ($from.parentOffset !== 0) return false - const node = $from.node() - if (!this.options.types.includes(node.type.name)) return false - const indent = node.attrs['indent'] || 0 - if (indent > 0) { - return this.editor.commands.decreaseIndent() - } - return false - }, - } - }, -}) diff --git a/src/components/piEditor/ext/Selection.ts b/src/components/piEditor/ext/Selection.ts deleted file mode 100644 index 22a89a4..0000000 --- a/src/components/piEditor/ext/Selection.ts +++ /dev/null @@ -1,79 +0,0 @@ -import {Extension} from '@tiptap/core' -import {Plugin, PluginKey} from '@tiptap/pm/state' -import {Decoration, DecorationSet} from '@tiptap/pm/view' - -declare module '@tiptap/core' { - interface Commands { - selection: { - setSelection: () => ReturnType - clearSelection: () => ReturnType - } - } -} - -export default Extension.create({ - name: 'selection', - addOptions() { - return { - class: 'selection', - } - }, - addStorage() { - return { - fakeRange: null as { from: number, to: number } | null, - } - }, - addProseMirrorPlugins() { - return [ - new Plugin({ - key: new PluginKey('selection'), - props: { - decorations: (state) => { - const {fakeRange} = this.storage - if (!fakeRange) return null - const deco = Decoration.inline( - fakeRange.from, - fakeRange.to, - {class: this.options.class}, - ) - return DecorationSet.create(state.doc, [deco]) - }, - handleClick: (view, pos, event) => { - const { fakeRange } = this.storage - if (!fakeRange) return false - - const clickedInsideFake = - pos >= fakeRange.from && pos <= fakeRange.to - - if (!clickedInsideFake) { - // 点击了 fake selection 外部,清除它 - this.storage.fakeRange = null - view.dispatch(view.state.tr) - } - - return false - }, - }, - }), - ] - }, - addCommands() { - return { - setSelection: () => ({state, view}) => { - const {from, to, empty} = state.selection - if (!empty && from !== to) { - this.storage.fakeRange = {from, to} - view.dispatch(state.tr) - } - return true - }, - clearSelection: () => ({state, view}) => { - if (this.storage.fakeRange) { - this.storage.fakeRange = null - view.dispatch(state.tr) - } - return true - }, - } - }, -}) diff --git a/src/components/piEditor/ext/Table/Action.vue b/src/components/piEditor/ext/Table/Action.vue deleted file mode 100644 index 37de2a8..0000000 --- a/src/components/piEditor/ext/Table/Action.vue +++ /dev/null @@ -1,86 +0,0 @@ - - - - - diff --git a/src/components/piEditor/ext/Table/index.ts b/src/components/piEditor/ext/Table/index.ts deleted file mode 100644 index c92f2a0..0000000 --- a/src/components/piEditor/ext/Table/index.ts +++ /dev/null @@ -1,137 +0,0 @@ -import {VueNodeViewRenderer} from "@tiptap/vue-3"; -import {NodeSelection} from 'prosemirror-state' -import {Table} from '@tiptap/extension-table' -import Action from "./Action.vue" - -export default Table.extend({ - selectable: true, - atom: true, - // 添加属性配置 - addAttributes() { - return { - ...this.parent?.(), - width: { - default: '100%', - renderHTML: attributes => { - return { - width: attributes.width, - } - } - }, - class: { - default: null, - renderHTML: attributes => { - return { - class: attributes.class, - } - } - }, - style: { - default: 'table-layout: fixed;border-collapse: collapse;', - renderHTML: attributes => { - if (!attributes.style) return {} - return { - style: attributes.style, - } - } - } - } - }, - renderHTML({node, HTMLAttributes}) { - return ['table', HTMLAttributes, ['tbody', 0]] - }, - addKeyboardShortcuts() { - return { - Backspace: ({editor}) => { - const {state, view} = editor - const {selection} = state - - // 没有深度直接删除 - const {$from} = selection - if ($from.depth == 0) { - editor.commands.deleteSelection() - return true - } - - // 选中整个表格删除 - if (selection instanceof NodeSelection && selection.node.type.name === 'table') { - const tr = state.tr.delete(selection.from, selection.to) - view.dispatch(tr) - return true - } - const pos = $from.before($from.depth) // 当前段落的开始位置 - const index = $from.index($from.depth - 1) - - if (index == 0) return false - const parent = $from.node($from.depth - 1) - - // 查前一个节点是不是 table - const beforeNode = parent.child(index - 1) - - if (beforeNode?.type.name === 'table') { - const deletePos = pos - beforeNode.nodeSize - view.dispatch( - state.tr - .setSelection(NodeSelection.create(state.doc, deletePos)) - .deleteSelection() - ) - return true - } - - return false - }, - Delete: ({editor}) => { - const {state, view} = editor - const {selection} = state - - // 没有深度直接删除 - const {$from} = selection - if ($from.depth == 0) { - editor.commands.deleteSelection() - return true - } - - //选中整个表格删除 - if (selection instanceof NodeSelection && selection.node.type.name === 'table') { - const tr = state.tr.delete(selection.from, selection.to) - view.dispatch(tr) - return true - } - - // 在表格前删除 - const pos = $from.before($from.depth) // 当前段落的开始位置 - const index = $from.index($from.depth - 1) - const parent = $from.node($from.depth - 1) - - if (index < parent.childCount - 1) { - const afterNode = parent.child(index + 1) - if (afterNode?.type.name === 'table') { - const deletePos = pos + $from.node().nodeSize - view.dispatch( - state.tr - .setSelection(NodeSelection.create(state.doc, deletePos)) - .deleteSelection() - ) - return true - } - } else { - if (index == 0) return false - const beforeNode = parent.child(index - 1) - if (beforeNode?.type.name === 'table') { - const deletePos = pos - beforeNode.nodeSize - view.dispatch( - state.tr - .setSelection(NodeSelection.create(state.doc, deletePos)) - .deleteSelection() - ) - return true - } - } - return false - }, - } - }, - addNodeView() { - return VueNodeViewRenderer(Action) - } -}) diff --git a/src/components/piEditor/ext/Video/Action.vue b/src/components/piEditor/ext/Video/Action.vue deleted file mode 100644 index e9f78e8..0000000 --- a/src/components/piEditor/ext/Video/Action.vue +++ /dev/null @@ -1,105 +0,0 @@ - - - - - diff --git a/src/components/piEditor/ext/Video/index.ts b/src/components/piEditor/ext/Video/index.ts deleted file mode 100644 index 4b9b149..0000000 --- a/src/components/piEditor/ext/Video/index.ts +++ /dev/null @@ -1,92 +0,0 @@ -// extensions/Video.ts -import {Node, mergeAttributes, Command, CommandProps} from '@tiptap/core' -import {VueNodeViewRenderer} from "@tiptap/vue-3"; -import Action from "./Action.vue"; - -export interface VideoOptions { - HTMLAttributes: Record -} - -declare module '@tiptap/core' { - interface Commands { - video: { - setVideo: (options: { src: string }) => ReturnType - } - } -} - -export default Node.create({ - name: 'video', - group: 'block', - atom: true, - selectable: true, - addOptions() { - return { - HTMLAttributes: {}, - } - }, - addAttributes() { - return { - src: { - default: null, - }, - width: { - default: '100%', - renderHTML: attributes => { - return { - width: attributes.width, - } - } - }, - height: { - default: null - }, - class: { - default: null, - renderHTML: attributes => { - return { - class: attributes.class, - } - } - }, - style: { - default: '', - renderHTML: attributes => { - if (!attributes.style) return {} - return { - style: attributes.style, - } - } - } - } - }, - parseHTML() { - return [ - { - tag: 'video', - }, - ] - }, - renderHTML({HTMLAttributes}) { - return [ - 'video', - mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, { - controls: true, - }), - ['source', {src: HTMLAttributes.src, type: 'video/mp4'}], - ] - }, - addCommands() { - return { - setVideo: (attrs: { src: string }): Command => ({commands}: CommandProps) => { - return commands.insertContent({ - type: this.name, - attrs, - }) - }, - } - }, - addNodeView() { - return VueNodeViewRenderer(Action) - }, -}) diff --git a/src/components/piEditor/index.vue b/src/components/piEditor/index.vue index 3807080..1a02b71 100644 --- a/src/components/piEditor/index.vue +++ b/src/components/piEditor/index.vue @@ -1,245 +1,7 @@