diff --git a/docs/en/guide/option.md b/docs/en/guide/option.md index 7c7c9eb..5e0d853 100644 --- a/docs/en/guide/option.md +++ b/docs/en/guide/option.md @@ -14,7 +14,7 @@ new Editor(container, IEditorData | IElement[], { ```typescript interface IEditorOption { - mode?: EditorMode // Editor mode: Edit, Clean (Visual aids are not displayed, For example: page break), ReadOnly, Form (Only editable within the control), Print (Visual aids are not displayed, Unwritten content control). default: Edit + mode?: EditorMode // Editor mode: Edit, Clean (Visual aids are not displayed, For example: page break), ReadOnly, Form (Only editable within the control), Print (Visual aids are not displayed, Unwritten content control), Design (Do not handle configurations such as non deletable and read-only). default: Edit defaultType?: string // Default element type. default: TEXT defaultColor?: string // Default color. default: #000000 defaultFont?: string // Default font. default: Microsoft YaHei diff --git a/docs/guide/option.md b/docs/guide/option.md index d30fc41..b9161b7 100644 --- a/docs/guide/option.md +++ b/docs/guide/option.md @@ -14,7 +14,7 @@ new Editor(container, IEditorData | IElement[], { ```typescript interface IEditorOption { - mode?: EditorMode // 编辑器模式:编辑、清洁(不显示视觉辅助元素。如:分页符)、只读、表单(仅控件内可编辑)、打印(不显示辅助元素、未书写控件及前后括号)。默认:编辑 + mode?: EditorMode // 编辑器模式:编辑、清洁(不显示视觉辅助元素。如:分页符)、只读、表单(仅控件内可编辑)、打印(不显示辅助元素、未书写控件及前后括号)、设计模式(不可删除、只读等配置不控制)。默认:编辑 defaultType?: string // 默认元素类型。默认:TEXT defaultColor?: string // 默认字体颜色。默认:#000000 defaultFont?: string // 默认字体。默认:Microsoft YaHei diff --git a/index.html b/index.html index e3d64ce..a1177b3 100644 --- a/index.html +++ b/index.html @@ -365,7 +365,7 @@ 页面:1/1 字数:0 -
编辑模式
+
编辑模式
diff --git a/src/editor/core/command/CommandAdapt.ts b/src/editor/core/command/CommandAdapt.ts index 68b7b31..40c1479 100644 --- a/src/editor/core/command/CommandAdapt.ts +++ b/src/editor/core/command/CommandAdapt.ts @@ -827,7 +827,9 @@ export class CommandAdapt { formatElementList([element], { editorOptions: this.options }) - formatElementContext(elementList, [element], startIndex) + formatElementContext(elementList, [element], startIndex, { + editorOptions: this.options + }) const curIndex = startIndex + 1 this.draw.spliceElementList( elementList, @@ -1588,7 +1590,9 @@ export class CommandAdapt { })) if (!newElementList) return const start = startIndex + 1 - formatElementContext(elementList, newElementList, startIndex) + formatElementContext(elementList, newElementList, startIndex, { + editorOptions: this.options + }) this.draw.spliceElementList( elementList, start, @@ -1733,7 +1737,9 @@ export class CommandAdapt { dashArray: payload } // 从行头增加分割线 - formatElementContext(elementList, [newElement], startIndex) + formatElementContext(elementList, [newElement], startIndex, { + editorOptions: this.options + }) if (startIndex !== 0 && elementList[startIndex].value === ZERO) { this.draw.spliceElementList(elementList, startIndex, 1, newElement) curIndex = startIndex - 1 @@ -2238,7 +2244,8 @@ export class CommandAdapt { const { startIndex } = this.range.getRange() const elementList = this.draw.getElementList() formatElementContext(elementList, cloneElementList, startIndex, { - isBreakWhenWrap: true + isBreakWhenWrap: true, + editorOptions: this.options }) this.draw.insertElementList(cloneElementList) } diff --git a/src/editor/core/draw/Draw.ts b/src/editor/core/draw/Draw.ts index 1625281..b7bae07 100644 --- a/src/editor/core/draw/Draw.ts +++ b/src/editor/core/draw/Draw.ts @@ -314,6 +314,8 @@ export class Draw { public isReadonly() { switch (this.mode) { + case EditorMode.DESIGN: + return false case EditorMode.READONLY: case EditorMode.PRINT: return true @@ -325,6 +327,7 @@ export class Draw { } public isDisabled() { + if (this.mode === EditorMode.DESIGN) return false const { startIndex, endIndex } = this.range.getRange() const elementList = this.getElementList() if (startIndex === endIndex) { @@ -341,6 +344,10 @@ export class Draw { ) } + public isDesignMode() { + return this.mode === EditorMode.DESIGN + } + public getOriginalWidth(): number { const { paperDirection, width, height } = this.options return paperDirection === PaperDirection.VERTICAL ? width : height @@ -698,6 +705,7 @@ export class Draw { deleteCount: number, ...items: IElement[] ) { + const isDesignMode = this.isDesignMode() if (deleteCount > 0) { // 当最后元素与开始元素列表信息不一致时:清除当前列表信息 const endIndex = start + deleteCount @@ -728,8 +736,9 @@ export class Draw { while (deleteIndex >= start) { const deleteElement = elementList[deleteIndex] if ( - deleteElement?.control?.deletable !== false && - deleteElement?.title?.deletable !== false + isDesignMode || + (deleteElement?.control?.deletable !== false && + deleteElement?.title?.deletable !== false) ) { elementList.splice(deleteIndex, 1) } diff --git a/src/editor/core/draw/control/Control.ts b/src/editor/core/draw/control/Control.ts index 4773787..014a0e6 100644 --- a/src/editor/core/draw/control/Control.ts +++ b/src/editor/core/draw/control/Control.ts @@ -208,7 +208,7 @@ export class Control { } public getIsDisabledControl(context: IControlContext = {}): boolean { - if (!this.activeControl) return false + if (this.draw.isDesignMode() || !this.activeControl) return false const { startIndex, endIndex } = context.range || this.range.getRange() if (startIndex === endIndex && ~startIndex && ~endIndex) { const elementList = context.elementList || this.getElementList() @@ -452,8 +452,11 @@ export class Control { ): number | null { const elementList = context.elementList || this.getElementList() const startElement = elementList[startIndex] - const { deletable = true } = startElement.control! - if (!deletable) return null + // 设计模式不验证删除权限 + if (!this.draw.isDesignMode()) { + const { deletable = true } = startElement.control! + if (!deletable) return null + } let leftIndex = -1 let rightIndex = -1 // 向左查找 @@ -538,7 +541,9 @@ export class Control { controlComponent: ControlComponent.PLACEHOLDER, color: this.controlOptions.placeholderColor } - formatElementContext(elementList, [newElement], startIndex) + formatElementContext(elementList, [newElement], startIndex, { + editorOptions: this.options + }) this.draw.spliceElementList( elementList, startIndex + p + 1, diff --git a/src/editor/core/draw/control/date/DateControl.ts b/src/editor/core/draw/control/date/DateControl.ts index 6e13a99..1a84114 100644 --- a/src/editor/core/draw/control/date/DateControl.ts +++ b/src/editor/core/draw/control/date/DateControl.ts @@ -6,11 +6,13 @@ import { import { ControlComponent } from '../../../../dataset/enum/Control' import { ElementType } from '../../../../dataset/enum/Element' import { KeyMap } from '../../../../dataset/enum/KeyMap' +import { DeepRequired } from '../../../../interface/Common' import { IControlContext, IControlInstance, IControlRuleOption } from '../../../../interface/Control' +import { IEditorOption } from '../../../../interface/Editor' import { IElement } from '../../../../interface/Element' import { omitObject, pickObject } from '../../../../utils' import { formatElementContext } from '../../../../utils/element' @@ -24,10 +26,12 @@ export class DateControl implements IControlInstance { private control: Control private isPopup: boolean private datePicker: DatePicker | null + private options: DeepRequired constructor(element: IElement, control: Control) { const draw = control.getDraw() this.draw = draw + this.options = draw.getOptions() this.element = element this.control = control this.isPopup = false @@ -138,7 +142,9 @@ export class DateControl implements IControlInstance { ...data[i], controlComponent: ControlComponent.VALUE } - formatElementContext(elementList, [newElement], startIndex) + formatElementContext(elementList, [newElement], startIndex, { + editorOptions: this.options + }) draw.spliceElementList(elementList, start + i, 0, newElement) } return start + data.length - 1 @@ -207,7 +213,9 @@ export class DateControl implements IControlInstance { value: date[i], controlComponent: ControlComponent.VALUE } - formatElementContext(elementList, [newElement], prefixIndex) + formatElementContext(elementList, [newElement], prefixIndex, { + editorOptions: this.options + }) draw.spliceElementList(elementList, start + i, 0, newElement) } // 重新渲染控件 diff --git a/src/editor/core/draw/control/select/SelectControl.ts b/src/editor/core/draw/control/select/SelectControl.ts index a45de98..37365a7 100644 --- a/src/editor/core/draw/control/select/SelectControl.ts +++ b/src/editor/core/draw/control/select/SelectControl.ts @@ -10,11 +10,13 @@ import { ControlComponent } from '../../../../dataset/enum/Control' import { EditorComponent } from '../../../../dataset/enum/Editor' import { ElementType } from '../../../../dataset/enum/Element' import { KeyMap } from '../../../../dataset/enum/KeyMap' +import { DeepRequired } from '../../../../interface/Common' import { IControlContext, IControlInstance, IControlRuleOption } from '../../../../interface/Control' +import { IEditorOption } from '../../../../interface/Editor' import { IElement } from '../../../../interface/Element' import { omitObject, pickObject, splitText } from '../../../../utils' import { formatElementContext } from '../../../../utils/element' @@ -25,8 +27,10 @@ export class SelectControl implements IControlInstance { private control: Control private isPopup: boolean private selectDom: HTMLDivElement | null + private options: DeepRequired constructor(element: IElement, control: Control) { + this.options = control.getDraw().getOptions() this.element = element this.control = control this.isPopup = false @@ -270,7 +274,9 @@ export class SelectControl implements IControlInstance { value: data[i], controlComponent: ControlComponent.VALUE } - formatElementContext(elementList, [newElement], prefixIndex) + formatElementContext(elementList, [newElement], prefixIndex, { + editorOptions: this.options + }) draw.spliceElementList(elementList, start + i, 0, newElement) } // 设置状态 diff --git a/src/editor/core/draw/control/text/TextControl.ts b/src/editor/core/draw/control/text/TextControl.ts index 82c1fa6..d42c8e5 100644 --- a/src/editor/core/draw/control/text/TextControl.ts +++ b/src/editor/core/draw/control/text/TextControl.ts @@ -4,11 +4,13 @@ import { } from '../../../../dataset/constant/Element' import { ControlComponent } from '../../../../dataset/enum/Control' import { KeyMap } from '../../../../dataset/enum/KeyMap' +import { DeepRequired } from '../../../../interface/Common' import { IControlContext, IControlInstance, IControlRuleOption } from '../../../../interface/Control' +import { IEditorOption } from '../../../../interface/Editor' import { IElement } from '../../../../interface/Element' import { omitObject, pickObject } from '../../../../utils' import { formatElementContext } from '../../../../utils/element' @@ -17,8 +19,10 @@ import { Control } from '../Control' export class TextControl implements IControlInstance { private element: IElement private control: Control + private options: DeepRequired constructor(element: IElement, control: Control) { + this.options = control.getDraw().getOptions() this.element = element this.control = control } @@ -114,7 +118,9 @@ export class TextControl implements IControlInstance { ...data[i], controlComponent: ControlComponent.VALUE } - formatElementContext(elementList, [newElement], startIndex) + formatElementContext(elementList, [newElement], startIndex, { + editorOptions: this.options + }) draw.spliceElementList(elementList, start + i, 0, newElement) } return start + data.length - 1 diff --git a/src/editor/core/draw/particle/date/DateParticle.ts b/src/editor/core/draw/particle/date/DateParticle.ts index b6a9d3b..da31fd7 100644 --- a/src/editor/core/draw/particle/date/DateParticle.ts +++ b/src/editor/core/draw/particle/date/DateParticle.ts @@ -1,4 +1,6 @@ import { ElementType } from '../../../../dataset/enum/Element' +import { DeepRequired } from '../../../../interface/Common' +import { IEditorOption } from '../../../../interface/Editor' import { IElement, IElementPosition } from '../../../../interface/Element' import { formatElementContext } from '../../../../utils/element' import { RangeManager } from '../../../range/RangeManager' @@ -9,9 +11,11 @@ export class DateParticle { private draw: Draw private range: RangeManager private datePicker: DatePicker + private options: DeepRequired constructor(draw: Draw) { this.draw = draw + this.options = draw.getOptions() this.range = draw.getRange() this.datePicker = new DatePicker(draw, { onSubmit: this._setValue.bind(this) @@ -43,7 +47,9 @@ export class DateParticle { } ] } - formatElementContext(elementList, [dateElement], leftIndex) + formatElementContext(elementList, [dateElement], leftIndex, { + editorOptions: this.options + }) this.draw.insertElementList([dateElement]) } diff --git a/src/editor/core/event/handlers/input.ts b/src/editor/core/event/handlers/input.ts index 215d208..61967f6 100644 --- a/src/editor/core/event/handlers/input.ts +++ b/src/editor/core/event/handlers/input.ts @@ -30,11 +30,15 @@ export function input(data: string, host: CanvasEvent) { const elementList = draw.getElementList() const copyElement = getAnchorElement(elementList, endIndex) if (!copyElement) return + const isDesignMode = draw.isDesignMode() const inputData: IElement[] = splitText(text).map(value => { const newElement: IElement = { value } - if (!copyElement.title?.disabled && !copyElement.control?.disabled) { + if ( + isDesignMode || + (!copyElement.title?.disabled && !copyElement.control?.disabled) + ) { const nextElement = elementList[endIndex + 1] if ( !copyElement.type || @@ -69,7 +73,9 @@ export function input(data: string, host: CanvasEvent) { if (startIndex !== endIndex) { draw.spliceElementList(elementList, start, endIndex - startIndex) } - formatElementContext(elementList, inputData, startIndex) + formatElementContext(elementList, inputData, startIndex, { + editorOptions: draw.getOptions() + }) draw.spliceElementList(elementList, start, 0, ...inputData) curIndex = startIndex + inputData.length } diff --git a/src/editor/core/event/handlers/keydown/enter.ts b/src/editor/core/event/handlers/keydown/enter.ts index 5ef46d9..c7c279d 100644 --- a/src/editor/core/event/handlers/keydown/enter.ts +++ b/src/editor/core/event/handlers/keydown/enter.ts @@ -40,7 +40,8 @@ export function enter(evt: KeyboardEvent, host: CanvasEvent) { } // 格式化上下文 formatElementContext(elementList, [enterText], startIndex, { - isBreakWhenWrap: true + isBreakWhenWrap: true, + editorOptions: draw.getOptions() }) // 标题结尾处回车无需格式化及样式复制 if ( diff --git a/src/editor/core/event/handlers/keydown/tab.ts b/src/editor/core/event/handlers/keydown/tab.ts index e863910..5c71ca4 100644 --- a/src/editor/core/event/handlers/keydown/tab.ts +++ b/src/editor/core/event/handlers/keydown/tab.ts @@ -25,7 +25,9 @@ export function tab(evt: KeyboardEvent, host: CanvasEvent) { const rangeManager = draw.getRange() const { startIndex } = rangeManager.getRange() const elementList = draw.getElementList() - formatElementContext(elementList, [tabElement], startIndex) + formatElementContext(elementList, [tabElement], startIndex, { + editorOptions: draw.getOptions() + }) draw.insertElementList([tabElement]) } } diff --git a/src/editor/core/event/handlers/mouseup.ts b/src/editor/core/event/handlers/mouseup.ts index 23aac0d..81e572a 100644 --- a/src/editor/core/event/handlers/mouseup.ts +++ b/src/editor/core/event/handlers/mouseup.ts @@ -176,7 +176,9 @@ export function mouseup(evt: MouseEvent, host: CanvasEvent) { return newElement } }) - formatElementContext(elementList, replaceElementList, range.startIndex) + formatElementContext(elementList, replaceElementList, range.startIndex, { + editorOptions: draw.getOptions() + }) // 缓存拖拽选区开始元素、位置、开始结束id const cacheStartElement = cacheElementList[cacheStartIndex] const cacheStartPosition = cachePositionList[cacheStartIndex] diff --git a/src/editor/core/event/handlers/paste.ts b/src/editor/core/event/handlers/paste.ts index 6a26e67..401c558 100644 --- a/src/editor/core/event/handlers/paste.ts +++ b/src/editor/core/event/handlers/paste.ts @@ -50,7 +50,8 @@ export function pasteElement(host: CanvasEvent, elementList: IElement[]) { } } formatElementContext(originalElementList, elementList, startIndex, { - isBreakWhenWrap: true + isBreakWhenWrap: true, + editorOptions: draw.getOptions() }) } draw.insertElementList(elementList) @@ -87,7 +88,9 @@ export function pasteImage(host: CanvasEvent, file: File | Blob) { height: image.height } if (~startIndex) { - formatElementContext(elementList, [imageElement], startIndex) + formatElementContext(elementList, [imageElement], startIndex, { + editorOptions: draw.getOptions() + }) } draw.insertElementList([imageElement]) } diff --git a/src/editor/dataset/enum/Editor.ts b/src/editor/dataset/enum/Editor.ts index c19757a..454f0a9 100644 --- a/src/editor/dataset/enum/Editor.ts +++ b/src/editor/dataset/enum/Editor.ts @@ -19,7 +19,8 @@ export enum EditorMode { CLEAN = 'clean', // 清洁模式(隐藏辅助元素) READONLY = 'readonly', // 只读模式(文档不可编辑) FORM = 'form', // 表单模式(仅控件内可编辑) - PRINT = 'print' // 打印模式(文档不可编辑、隐藏辅助元素、选区、未书写控件及边框) + PRINT = 'print', // 打印模式(文档不可编辑、隐藏辅助元素、选区、未书写控件及边框) + DESIGN = 'design' // 设计模式(不可删除、只读等配置不控制) } export enum EditorZone { diff --git a/src/editor/utils/element.ts b/src/editor/utils/element.ts index 434f7f8..6bd4bd3 100644 --- a/src/editor/utils/element.ts +++ b/src/editor/utils/element.ts @@ -9,6 +9,7 @@ import { splitText } from '.' import { + EditorMode, ElementType, IEditorOption, IElement, @@ -821,7 +822,8 @@ export function getAnchorElement( } export interface IFormatElementContextOption { - isBreakWhenWrap: boolean + isBreakWhenWrap?: boolean + editorOptions?: DeepRequired } export function formatElementContext( @@ -832,11 +834,12 @@ export function formatElementContext( ) { let copyElement = getAnchorElement(sourceElementList, anchorIndex) if (!copyElement) return - // 标题元素禁用时不复制标题属性 - if (copyElement.title?.disabled) { + const { isBreakWhenWrap = false, editorOptions } = options || {} + const { mode } = editorOptions || {} + // 非设计模式时:标题元素禁用时不复制标题属性 + if (mode !== EditorMode.DESIGN && copyElement.title?.disabled) { copyElement = omitObject(copyElement, TITLE_CONTEXT_ATTR) } - const { isBreakWhenWrap = false } = options || {} // 是否已经换行 let isBreakWarped = false for (let e = 0; e < formatElementList.length; e++) { @@ -865,7 +868,8 @@ export function formatElementContext( formatElementContext( sourceElementList, targetElement.valueList, - anchorIndex + anchorIndex, + options ) } // 非块类元素,需处理行属性 diff --git a/src/main.ts b/src/main.ts index 858ffeb..ad95f3f 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1412,6 +1412,10 @@ window.onload = function () { { mode: EditorMode.PRINT, name: '打印模式' + }, + { + mode: EditorMode.DESIGN, + name: '设计模式' } ] const modeElement = document.querySelector('.editor-mode')!