diff --git a/src/assets/styles/prosemirror.scss b/src/assets/styles/prosemirror.scss index 15f1735c..7b7bd1f0 100644 --- a/src/assets/styles/prosemirror.scss +++ b/src/assets/styles/prosemirror.scss @@ -17,7 +17,6 @@ } p { - text-indent: var(--textIndent); margin-top: var(--paragraphSpace); } p:first-child { @@ -61,25 +60,28 @@ } [data-indent='1'] { - padding-left: 48px; + padding-left: 24px; } [data-indent='2'] { - padding-left: 96px; + padding-left: 48px; } [data-indent='3'] { - padding-left: 144px; + padding-left: 72px; } [data-indent='4'] { - padding-left: 192px; + padding-left: 96px; } [data-indent='5'] { - padding-left: 240px; + padding-left: 120px; } [data-indent='6'] { - padding-left: 288px; + padding-left: 144px; } [data-indent='7'] { - padding-left: 336px; + padding-left: 168px; + } + [data-indent='8'] { + padding-left: 192px; } } diff --git a/src/types/slides.ts b/src/types/slides.ts index 52922885..b594071b 100644 --- a/src/types/slides.ts +++ b/src/types/slides.ts @@ -140,8 +140,6 @@ interface PPTBaseElement { * * shadow?: 阴影 * - * textIndent?: 段落首行缩进 - * * paragraphSpace?: 段间距,默认 5px * * vertical?: 竖向文本 @@ -157,7 +155,6 @@ export interface PPTTextElement extends PPTBaseElement { wordSpace?: number opacity?: number shadow?: PPTElementShadow - textIndent?: number paragraphSpace?: number vertical?: boolean } diff --git a/src/utils/prosemirror/commands/setTextIndent.ts b/src/utils/prosemirror/commands/setTextIndent.ts index 4589b58b..78281880 100644 --- a/src/utils/prosemirror/commands/setTextIndent.ts +++ b/src/utils/prosemirror/commands/setTextIndent.ts @@ -3,30 +3,32 @@ import { type Transaction, TextSelection, AllSelection } from 'prosemirror-state import type { EditorView } from 'prosemirror-view' import { isList } from './toggleList' -function setNodeIndentMarkup(tr: Transaction, pos: number, delta: number): Transaction { +type IndentKey = 'indent' | 'textIndent' + +function setNodeIndentMarkup(tr: Transaction, pos: number, delta: number, indentKey: IndentKey): Transaction { if (!tr.doc) return tr const node = tr.doc.nodeAt(pos) if (!node) return tr const minIndent = 0 - const maxIndent = 7 + const maxIndent = 8 - let indent = (node.attrs.indent || 0) + delta + let indent = (node.attrs[indentKey] || 0) + delta if (indent < minIndent) indent = minIndent if (indent > maxIndent) indent = maxIndent - if (indent === node.attrs.indent) return tr + if (indent === node.attrs[indentKey]) return tr const nodeAttrs = { ...node.attrs, - indent, + [indentKey]: indent, } return tr.setNodeMarkup(pos, node.type, nodeAttrs, node.marks) } -const setTextIndent = (tr: Transaction, schema: Schema, delta: number): Transaction => { +const setIndent = (tr: Transaction, schema: Schema, delta: number, indentKey: IndentKey): Transaction => { const { selection, doc } = tr if (!selection || !doc) return tr @@ -38,7 +40,7 @@ const setTextIndent = (tr: Transaction, schema: Schema, delta: number): Transact const nodeType = node.type if (nodeType.name === 'paragraph' || nodeType.name === 'blockquote') { - tr = setNodeIndentMarkup(tr, pos, delta) + tr = setNodeIndentMarkup(tr, pos, delta, indentKey) return false } else if (isList(node, schema)) return false @@ -52,10 +54,29 @@ export const indentCommand = (view: EditorView, delta: number) => { const { state } = view const { schema, selection } = state - const tr = setTextIndent( + const tr = setIndent( state.tr.setSelection(selection), schema, delta, + 'indent', + ) + if (tr.docChanged) { + view.dispatch(tr) + return true + } + + return false +} + +export const textIndentCommand = (view: EditorView, delta: number) => { + const { state } = view + const { schema, selection } = state + + const tr = setIndent( + state.tr.setSelection(selection), + schema, + delta, + 'textIndent', ) if (tr.docChanged) { view.dispatch(tr) diff --git a/src/utils/prosemirror/schema/nodes.ts b/src/utils/prosemirror/schema/nodes.ts index 08010933..45f22553 100644 --- a/src/utils/prosemirror/schema/nodes.ts +++ b/src/utils/prosemirror/schema/nodes.ts @@ -84,6 +84,9 @@ const paragraph: NodeSpec = { indent: { default: 0, }, + textIndent: { + default: 0, + }, }, content: 'inline*', group: 'block', @@ -91,14 +94,20 @@ const paragraph: NodeSpec = { { tag: 'p', getAttrs: dom => { - const { textAlign } = (dom as HTMLElement).style + const { textAlign, textIndent } = (dom as HTMLElement).style let align = (dom as HTMLElement).getAttribute('align') || textAlign || '' align = /(left|right|center|justify)/.test(align) ? align : '' + let textIndentLevel = 0 + if (textIndent) { + textIndentLevel = Math.floor(parseInt(textIndent) / 24) + if (!textIndentLevel) textIndentLevel = 1 + } + const indent = +((dom as HTMLElement).getAttribute('data-indent') || 0) - return { align, indent } + return { align, indent, textIndent: textIndentLevel } } }, { @@ -111,9 +120,10 @@ const paragraph: NodeSpec = { }, ], toDOM: (node: Node) => { - const { align, indent } = node.attrs + const { align, indent, textIndent } = node.attrs let style = '' if (align && align !== 'left') style += `text-align: ${align};` + if (textIndent) style += `text-indent: ${textIndent * 24}px;` const attr: Attr = { style } if (indent) attr['data-indent'] = indent diff --git a/src/views/Editor/Toolbar/ElementStylePanel/TextStylePanel.vue b/src/views/Editor/Toolbar/ElementStylePanel/TextStylePanel.vue index ef8beedf..e59f57fe 100644 --- a/src/views/Editor/Toolbar/ElementStylePanel/TextStylePanel.vue +++ b/src/views/Editor/Toolbar/ElementStylePanel/TextStylePanel.vue @@ -256,14 +256,35 @@ - - - - - - - - +
+ + + + + + + + + +
+ + + + + + + + + +
@@ -288,13 +309,6 @@ {{item}}px -
-
首行缩进:
- -
文本框填充:
@@ -439,6 +453,8 @@ const updateElement = (props: Partial) => { const bulletListPanelVisible = ref(false) const orderedListPanelVisible = ref(false) +const indentLeftPanelVisible = ref(false) +const indentRightPanelVisible = ref(false) const bulletListStyleTypeOption = ref(['disc', 'circle', 'square']) const orderedListStyleTypeOption = ref(['decimal', 'lower-roman', 'upper-roman', 'lower-alpha', 'upper-alpha', 'lower-greek']) @@ -446,7 +462,6 @@ const orderedListStyleTypeOption = ref(['decimal', 'lower-roman', 'upper-roman', const fill = ref('#000') const lineHeight = ref() const wordSpace = ref() -const textIndent = ref() const paragraphSpace = ref() watch(handleElement, () => { @@ -455,7 +470,6 @@ watch(handleElement, () => { fill.value = handleElement.value.fill || '#fff' lineHeight.value = handleElement.value.lineHeight || 1.5 wordSpace.value = handleElement.value.wordSpace || 0 - textIndent.value = handleElement.value.textIndent || 0 paragraphSpace.value = handleElement.value.paragraphSpace === undefined ? 5 : handleElement.value.paragraphSpace }, { deep: true, immediate: true }) @@ -466,7 +480,6 @@ const fontSizeOptions = [ ] const lineHeightOptions = [0.9, 1.0, 1.15, 1.2, 1.4, 1.5, 1.8, 2.0, 2.5, 3.0] const wordSpaceOptions = [0, 1, 2, 3, 4, 5, 6, 8, 10] -const textIndentOptions = [0, 48, 96, 144, 192, 240, 288, 336] const paragraphSpaceOptions = [0, 5, 10, 15, 20, 25, 30, 40, 50, 80] // 设置行高 @@ -484,11 +497,6 @@ const updateWordSpace = (value: number) => { updateElement({ wordSpace: value }) } -// 设置首行缩进 -const updateTextIndent = (value: number) => { - updateElement({ textIndent: value }) -} - // 设置文本框填充 const updateFill = (value: string) => { updateElement({ fill: value }) @@ -626,6 +634,21 @@ const updateLink = (link?: string) => { background-color: #666; } } +.popover-list { + display: flex; + flex-direction: column; + padding: 5px; + margin: -12px; +} +.popover-item { + padding: 9px 12px; + border-radius: 2px; + cursor: pointer; + + &:hover { + background-color: #f5f5f5; + } +} .popover-btn { padding: 0 3px; } diff --git a/src/views/components/element/ProsemirrorEditor.vue b/src/views/components/element/ProsemirrorEditor.vue index b8154109..2ac75883 100644 --- a/src/views/components/element/ProsemirrorEditor.vue +++ b/src/views/components/element/ProsemirrorEditor.vue @@ -18,7 +18,7 @@ import { initProsemirrorEditor, createDocument } from '@/utils/prosemirror' import { findNodesWithSameMark, getTextAttrs, autoSelectAll, addMark, markActive, getFontsize } from '@/utils/prosemirror/utils' import emitter, { EmitterEvents, type RichTextAction, type RichTextCommand } from '@/utils/emitter' import { alignmentCommand } from '@/utils/prosemirror/commands/setTextAlign' -import { indentCommand } from '@/utils/prosemirror/commands/setTextIndent' +import { indentCommand, textIndentCommand } from '@/utils/prosemirror/commands/setTextIndent' import { toggleList } from '@/utils/prosemirror/commands/toggleList' import type { TextFormatPainterKeys } from '@/types/edit' @@ -175,6 +175,9 @@ const execCommand = ({ target, action }: RichTextCommand) => { else if (item.command === 'indent' && item.value) { indentCommand(editorView, +item.value) } + else if (item.command === 'textIndent' && item.value) { + textIndentCommand(editorView, +item.value) + } else if (item.command === 'bulletList') { const listStyleType = item.value || '' const { bullet_list: bulletList, list_item: listItem } = editorView.state.schema.nodes diff --git a/src/views/components/element/TextElement/BaseTextElement.vue b/src/views/components/element/TextElement/BaseTextElement.vue index a0a8847f..4bde3852 100644 --- a/src/views/components/element/TextElement/BaseTextElement.vue +++ b/src/views/components/element/TextElement/BaseTextElement.vue @@ -34,7 +34,9 @@ />
@@ -43,7 +45,7 @@