feat: add design mode #795

npr765^2
Hufe921 2 years ago
parent 3c176318e0
commit 55a58cdfb2

@ -14,7 +14,7 @@ new Editor(container, IEditorData | IElement[], {
```typescript ```typescript
interface IEditorOption { 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 defaultType?: string // Default element type. default: TEXT
defaultColor?: string // Default color. default: #000000 defaultColor?: string // Default color. default: #000000
defaultFont?: string // Default font. default: Microsoft YaHei defaultFont?: string // Default font. default: Microsoft YaHei

@ -14,7 +14,7 @@ new Editor(container, IEditorData | IElement[], {
```typescript ```typescript
interface IEditorOption { interface IEditorOption {
mode?: EditorMode // 编辑器模式:编辑、清洁(不显示视觉辅助元素。如:分页符)、只读、表单(仅控件内可编辑)、打印(不显示辅助元素、未书写控件及前后括号)。默认:编辑 mode?: EditorMode // 编辑器模式:编辑、清洁(不显示视觉辅助元素。如:分页符)、只读、表单(仅控件内可编辑)、打印(不显示辅助元素、未书写控件及前后括号)、设计模式(不可删除、只读等配置不控制)。默认:编辑
defaultType?: string // 默认元素类型。默认TEXT defaultType?: string // 默认元素类型。默认TEXT
defaultColor?: string // 默认字体颜色。默认:#000000 defaultColor?: string // 默认字体颜色。默认:#000000
defaultFont?: string // 默认字体。默认Microsoft YaHei defaultFont?: string // 默认字体。默认Microsoft YaHei

@ -365,7 +365,7 @@
<span>页面:<span class="page-no">1</span>/<span class="page-size">1</span></span> <span>页面:<span class="page-no">1</span>/<span class="page-size">1</span></span>
<span>字数:<span class="word-count">0</span></span> <span>字数:<span class="word-count">0</span></span>
</div> </div>
<div class="editor-mode" title="编辑模式(编辑、清洁、只读、表单)">编辑模式</div> <div class="editor-mode" title="编辑模式(编辑、清洁、只读、表单、设计)">编辑模式</div>
<div> <div>
<div class="page-scale-minus" title="缩小(Ctrl+-)"> <div class="page-scale-minus" title="缩小(Ctrl+-)">
<i></i> <i></i>

@ -827,7 +827,9 @@ export class CommandAdapt {
formatElementList([element], { formatElementList([element], {
editorOptions: this.options editorOptions: this.options
}) })
formatElementContext(elementList, [element], startIndex) formatElementContext(elementList, [element], startIndex, {
editorOptions: this.options
})
const curIndex = startIndex + 1 const curIndex = startIndex + 1
this.draw.spliceElementList( this.draw.spliceElementList(
elementList, elementList,
@ -1588,7 +1590,9 @@ export class CommandAdapt {
})) }))
if (!newElementList) return if (!newElementList) return
const start = startIndex + 1 const start = startIndex + 1
formatElementContext(elementList, newElementList, startIndex) formatElementContext(elementList, newElementList, startIndex, {
editorOptions: this.options
})
this.draw.spliceElementList( this.draw.spliceElementList(
elementList, elementList,
start, start,
@ -1733,7 +1737,9 @@ export class CommandAdapt {
dashArray: payload dashArray: payload
} }
// 从行头增加分割线 // 从行头增加分割线
formatElementContext(elementList, [newElement], startIndex) formatElementContext(elementList, [newElement], startIndex, {
editorOptions: this.options
})
if (startIndex !== 0 && elementList[startIndex].value === ZERO) { if (startIndex !== 0 && elementList[startIndex].value === ZERO) {
this.draw.spliceElementList(elementList, startIndex, 1, newElement) this.draw.spliceElementList(elementList, startIndex, 1, newElement)
curIndex = startIndex - 1 curIndex = startIndex - 1
@ -2238,7 +2244,8 @@ export class CommandAdapt {
const { startIndex } = this.range.getRange() const { startIndex } = this.range.getRange()
const elementList = this.draw.getElementList() const elementList = this.draw.getElementList()
formatElementContext(elementList, cloneElementList, startIndex, { formatElementContext(elementList, cloneElementList, startIndex, {
isBreakWhenWrap: true isBreakWhenWrap: true,
editorOptions: this.options
}) })
this.draw.insertElementList(cloneElementList) this.draw.insertElementList(cloneElementList)
} }

@ -314,6 +314,8 @@ export class Draw {
public isReadonly() { public isReadonly() {
switch (this.mode) { switch (this.mode) {
case EditorMode.DESIGN:
return false
case EditorMode.READONLY: case EditorMode.READONLY:
case EditorMode.PRINT: case EditorMode.PRINT:
return true return true
@ -325,6 +327,7 @@ export class Draw {
} }
public isDisabled() { public isDisabled() {
if (this.mode === EditorMode.DESIGN) return false
const { startIndex, endIndex } = this.range.getRange() const { startIndex, endIndex } = this.range.getRange()
const elementList = this.getElementList() const elementList = this.getElementList()
if (startIndex === endIndex) { if (startIndex === endIndex) {
@ -341,6 +344,10 @@ export class Draw {
) )
} }
public isDesignMode() {
return this.mode === EditorMode.DESIGN
}
public getOriginalWidth(): number { public getOriginalWidth(): number {
const { paperDirection, width, height } = this.options const { paperDirection, width, height } = this.options
return paperDirection === PaperDirection.VERTICAL ? width : height return paperDirection === PaperDirection.VERTICAL ? width : height
@ -698,6 +705,7 @@ export class Draw {
deleteCount: number, deleteCount: number,
...items: IElement[] ...items: IElement[]
) { ) {
const isDesignMode = this.isDesignMode()
if (deleteCount > 0) { if (deleteCount > 0) {
// 当最后元素与开始元素列表信息不一致时:清除当前列表信息 // 当最后元素与开始元素列表信息不一致时:清除当前列表信息
const endIndex = start + deleteCount const endIndex = start + deleteCount
@ -728,8 +736,9 @@ export class Draw {
while (deleteIndex >= start) { while (deleteIndex >= start) {
const deleteElement = elementList[deleteIndex] const deleteElement = elementList[deleteIndex]
if ( if (
deleteElement?.control?.deletable !== false && isDesignMode ||
deleteElement?.title?.deletable !== false (deleteElement?.control?.deletable !== false &&
deleteElement?.title?.deletable !== false)
) { ) {
elementList.splice(deleteIndex, 1) elementList.splice(deleteIndex, 1)
} }

@ -208,7 +208,7 @@ export class Control {
} }
public getIsDisabledControl(context: IControlContext = {}): boolean { 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() const { startIndex, endIndex } = context.range || this.range.getRange()
if (startIndex === endIndex && ~startIndex && ~endIndex) { if (startIndex === endIndex && ~startIndex && ~endIndex) {
const elementList = context.elementList || this.getElementList() const elementList = context.elementList || this.getElementList()
@ -452,8 +452,11 @@ export class Control {
): number | null { ): number | null {
const elementList = context.elementList || this.getElementList() const elementList = context.elementList || this.getElementList()
const startElement = elementList[startIndex] const startElement = elementList[startIndex]
// 设计模式不验证删除权限
if (!this.draw.isDesignMode()) {
const { deletable = true } = startElement.control! const { deletable = true } = startElement.control!
if (!deletable) return null if (!deletable) return null
}
let leftIndex = -1 let leftIndex = -1
let rightIndex = -1 let rightIndex = -1
// 向左查找 // 向左查找
@ -538,7 +541,9 @@ export class Control {
controlComponent: ControlComponent.PLACEHOLDER, controlComponent: ControlComponent.PLACEHOLDER,
color: this.controlOptions.placeholderColor color: this.controlOptions.placeholderColor
} }
formatElementContext(elementList, [newElement], startIndex) formatElementContext(elementList, [newElement], startIndex, {
editorOptions: this.options
})
this.draw.spliceElementList( this.draw.spliceElementList(
elementList, elementList,
startIndex + p + 1, startIndex + p + 1,

@ -6,11 +6,13 @@ import {
import { ControlComponent } from '../../../../dataset/enum/Control' import { ControlComponent } from '../../../../dataset/enum/Control'
import { ElementType } from '../../../../dataset/enum/Element' import { ElementType } from '../../../../dataset/enum/Element'
import { KeyMap } from '../../../../dataset/enum/KeyMap' import { KeyMap } from '../../../../dataset/enum/KeyMap'
import { DeepRequired } from '../../../../interface/Common'
import { import {
IControlContext, IControlContext,
IControlInstance, IControlInstance,
IControlRuleOption IControlRuleOption
} from '../../../../interface/Control' } from '../../../../interface/Control'
import { IEditorOption } from '../../../../interface/Editor'
import { IElement } from '../../../../interface/Element' import { IElement } from '../../../../interface/Element'
import { omitObject, pickObject } from '../../../../utils' import { omitObject, pickObject } from '../../../../utils'
import { formatElementContext } from '../../../../utils/element' import { formatElementContext } from '../../../../utils/element'
@ -24,10 +26,12 @@ export class DateControl implements IControlInstance {
private control: Control private control: Control
private isPopup: boolean private isPopup: boolean
private datePicker: DatePicker | null private datePicker: DatePicker | null
private options: DeepRequired<IEditorOption>
constructor(element: IElement, control: Control) { constructor(element: IElement, control: Control) {
const draw = control.getDraw() const draw = control.getDraw()
this.draw = draw this.draw = draw
this.options = draw.getOptions()
this.element = element this.element = element
this.control = control this.control = control
this.isPopup = false this.isPopup = false
@ -138,7 +142,9 @@ export class DateControl implements IControlInstance {
...data[i], ...data[i],
controlComponent: ControlComponent.VALUE controlComponent: ControlComponent.VALUE
} }
formatElementContext(elementList, [newElement], startIndex) formatElementContext(elementList, [newElement], startIndex, {
editorOptions: this.options
})
draw.spliceElementList(elementList, start + i, 0, newElement) draw.spliceElementList(elementList, start + i, 0, newElement)
} }
return start + data.length - 1 return start + data.length - 1
@ -207,7 +213,9 @@ export class DateControl implements IControlInstance {
value: date[i], value: date[i],
controlComponent: ControlComponent.VALUE controlComponent: ControlComponent.VALUE
} }
formatElementContext(elementList, [newElement], prefixIndex) formatElementContext(elementList, [newElement], prefixIndex, {
editorOptions: this.options
})
draw.spliceElementList(elementList, start + i, 0, newElement) draw.spliceElementList(elementList, start + i, 0, newElement)
} }
// 重新渲染控件 // 重新渲染控件

@ -10,11 +10,13 @@ import { ControlComponent } from '../../../../dataset/enum/Control'
import { EditorComponent } from '../../../../dataset/enum/Editor' import { EditorComponent } from '../../../../dataset/enum/Editor'
import { ElementType } from '../../../../dataset/enum/Element' import { ElementType } from '../../../../dataset/enum/Element'
import { KeyMap } from '../../../../dataset/enum/KeyMap' import { KeyMap } from '../../../../dataset/enum/KeyMap'
import { DeepRequired } from '../../../../interface/Common'
import { import {
IControlContext, IControlContext,
IControlInstance, IControlInstance,
IControlRuleOption IControlRuleOption
} from '../../../../interface/Control' } from '../../../../interface/Control'
import { IEditorOption } from '../../../../interface/Editor'
import { IElement } from '../../../../interface/Element' import { IElement } from '../../../../interface/Element'
import { omitObject, pickObject, splitText } from '../../../../utils' import { omitObject, pickObject, splitText } from '../../../../utils'
import { formatElementContext } from '../../../../utils/element' import { formatElementContext } from '../../../../utils/element'
@ -25,8 +27,10 @@ export class SelectControl implements IControlInstance {
private control: Control private control: Control
private isPopup: boolean private isPopup: boolean
private selectDom: HTMLDivElement | null private selectDom: HTMLDivElement | null
private options: DeepRequired<IEditorOption>
constructor(element: IElement, control: Control) { constructor(element: IElement, control: Control) {
this.options = control.getDraw().getOptions()
this.element = element this.element = element
this.control = control this.control = control
this.isPopup = false this.isPopup = false
@ -270,7 +274,9 @@ export class SelectControl implements IControlInstance {
value: data[i], value: data[i],
controlComponent: ControlComponent.VALUE controlComponent: ControlComponent.VALUE
} }
formatElementContext(elementList, [newElement], prefixIndex) formatElementContext(elementList, [newElement], prefixIndex, {
editorOptions: this.options
})
draw.spliceElementList(elementList, start + i, 0, newElement) draw.spliceElementList(elementList, start + i, 0, newElement)
} }
// 设置状态 // 设置状态

@ -4,11 +4,13 @@ import {
} from '../../../../dataset/constant/Element' } from '../../../../dataset/constant/Element'
import { ControlComponent } from '../../../../dataset/enum/Control' import { ControlComponent } from '../../../../dataset/enum/Control'
import { KeyMap } from '../../../../dataset/enum/KeyMap' import { KeyMap } from '../../../../dataset/enum/KeyMap'
import { DeepRequired } from '../../../../interface/Common'
import { import {
IControlContext, IControlContext,
IControlInstance, IControlInstance,
IControlRuleOption IControlRuleOption
} from '../../../../interface/Control' } from '../../../../interface/Control'
import { IEditorOption } from '../../../../interface/Editor'
import { IElement } from '../../../../interface/Element' import { IElement } from '../../../../interface/Element'
import { omitObject, pickObject } from '../../../../utils' import { omitObject, pickObject } from '../../../../utils'
import { formatElementContext } from '../../../../utils/element' import { formatElementContext } from '../../../../utils/element'
@ -17,8 +19,10 @@ import { Control } from '../Control'
export class TextControl implements IControlInstance { export class TextControl implements IControlInstance {
private element: IElement private element: IElement
private control: Control private control: Control
private options: DeepRequired<IEditorOption>
constructor(element: IElement, control: Control) { constructor(element: IElement, control: Control) {
this.options = control.getDraw().getOptions()
this.element = element this.element = element
this.control = control this.control = control
} }
@ -114,7 +118,9 @@ export class TextControl implements IControlInstance {
...data[i], ...data[i],
controlComponent: ControlComponent.VALUE controlComponent: ControlComponent.VALUE
} }
formatElementContext(elementList, [newElement], startIndex) formatElementContext(elementList, [newElement], startIndex, {
editorOptions: this.options
})
draw.spliceElementList(elementList, start + i, 0, newElement) draw.spliceElementList(elementList, start + i, 0, newElement)
} }
return start + data.length - 1 return start + data.length - 1

@ -1,4 +1,6 @@
import { ElementType } from '../../../../dataset/enum/Element' import { ElementType } from '../../../../dataset/enum/Element'
import { DeepRequired } from '../../../../interface/Common'
import { IEditorOption } from '../../../../interface/Editor'
import { IElement, IElementPosition } from '../../../../interface/Element' import { IElement, IElementPosition } from '../../../../interface/Element'
import { formatElementContext } from '../../../../utils/element' import { formatElementContext } from '../../../../utils/element'
import { RangeManager } from '../../../range/RangeManager' import { RangeManager } from '../../../range/RangeManager'
@ -9,9 +11,11 @@ export class DateParticle {
private draw: Draw private draw: Draw
private range: RangeManager private range: RangeManager
private datePicker: DatePicker private datePicker: DatePicker
private options: DeepRequired<IEditorOption>
constructor(draw: Draw) { constructor(draw: Draw) {
this.draw = draw this.draw = draw
this.options = draw.getOptions()
this.range = draw.getRange() this.range = draw.getRange()
this.datePicker = new DatePicker(draw, { this.datePicker = new DatePicker(draw, {
onSubmit: this._setValue.bind(this) 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]) this.draw.insertElementList([dateElement])
} }

@ -30,11 +30,15 @@ export function input(data: string, host: CanvasEvent) {
const elementList = draw.getElementList() const elementList = draw.getElementList()
const copyElement = getAnchorElement(elementList, endIndex) const copyElement = getAnchorElement(elementList, endIndex)
if (!copyElement) return if (!copyElement) return
const isDesignMode = draw.isDesignMode()
const inputData: IElement[] = splitText(text).map(value => { const inputData: IElement[] = splitText(text).map(value => {
const newElement: IElement = { const newElement: IElement = {
value value
} }
if (!copyElement.title?.disabled && !copyElement.control?.disabled) { if (
isDesignMode ||
(!copyElement.title?.disabled && !copyElement.control?.disabled)
) {
const nextElement = elementList[endIndex + 1] const nextElement = elementList[endIndex + 1]
if ( if (
!copyElement.type || !copyElement.type ||
@ -69,7 +73,9 @@ export function input(data: string, host: CanvasEvent) {
if (startIndex !== endIndex) { if (startIndex !== endIndex) {
draw.spliceElementList(elementList, start, endIndex - startIndex) draw.spliceElementList(elementList, start, endIndex - startIndex)
} }
formatElementContext(elementList, inputData, startIndex) formatElementContext(elementList, inputData, startIndex, {
editorOptions: draw.getOptions()
})
draw.spliceElementList(elementList, start, 0, ...inputData) draw.spliceElementList(elementList, start, 0, ...inputData)
curIndex = startIndex + inputData.length curIndex = startIndex + inputData.length
} }

@ -40,7 +40,8 @@ export function enter(evt: KeyboardEvent, host: CanvasEvent) {
} }
// 格式化上下文 // 格式化上下文
formatElementContext(elementList, [enterText], startIndex, { formatElementContext(elementList, [enterText], startIndex, {
isBreakWhenWrap: true isBreakWhenWrap: true,
editorOptions: draw.getOptions()
}) })
// 标题结尾处回车无需格式化及样式复制 // 标题结尾处回车无需格式化及样式复制
if ( if (

@ -25,7 +25,9 @@ export function tab(evt: KeyboardEvent, host: CanvasEvent) {
const rangeManager = draw.getRange() const rangeManager = draw.getRange()
const { startIndex } = rangeManager.getRange() const { startIndex } = rangeManager.getRange()
const elementList = draw.getElementList() const elementList = draw.getElementList()
formatElementContext(elementList, [tabElement], startIndex) formatElementContext(elementList, [tabElement], startIndex, {
editorOptions: draw.getOptions()
})
draw.insertElementList([tabElement]) draw.insertElementList([tabElement])
} }
} }

@ -176,7 +176,9 @@ export function mouseup(evt: MouseEvent, host: CanvasEvent) {
return newElement return newElement
} }
}) })
formatElementContext(elementList, replaceElementList, range.startIndex) formatElementContext(elementList, replaceElementList, range.startIndex, {
editorOptions: draw.getOptions()
})
// 缓存拖拽选区开始元素、位置、开始结束id // 缓存拖拽选区开始元素、位置、开始结束id
const cacheStartElement = cacheElementList[cacheStartIndex] const cacheStartElement = cacheElementList[cacheStartIndex]
const cacheStartPosition = cachePositionList[cacheStartIndex] const cacheStartPosition = cachePositionList[cacheStartIndex]

@ -50,7 +50,8 @@ export function pasteElement(host: CanvasEvent, elementList: IElement[]) {
} }
} }
formatElementContext(originalElementList, elementList, startIndex, { formatElementContext(originalElementList, elementList, startIndex, {
isBreakWhenWrap: true isBreakWhenWrap: true,
editorOptions: draw.getOptions()
}) })
} }
draw.insertElementList(elementList) draw.insertElementList(elementList)
@ -87,7 +88,9 @@ export function pasteImage(host: CanvasEvent, file: File | Blob) {
height: image.height height: image.height
} }
if (~startIndex) { if (~startIndex) {
formatElementContext(elementList, [imageElement], startIndex) formatElementContext(elementList, [imageElement], startIndex, {
editorOptions: draw.getOptions()
})
} }
draw.insertElementList([imageElement]) draw.insertElementList([imageElement])
} }

@ -19,7 +19,8 @@ export enum EditorMode {
CLEAN = 'clean', // 清洁模式(隐藏辅助元素) CLEAN = 'clean', // 清洁模式(隐藏辅助元素)
READONLY = 'readonly', // 只读模式(文档不可编辑) READONLY = 'readonly', // 只读模式(文档不可编辑)
FORM = 'form', // 表单模式(仅控件内可编辑) FORM = 'form', // 表单模式(仅控件内可编辑)
PRINT = 'print' // 打印模式(文档不可编辑、隐藏辅助元素、选区、未书写控件及边框) PRINT = 'print', // 打印模式(文档不可编辑、隐藏辅助元素、选区、未书写控件及边框)
DESIGN = 'design' // 设计模式(不可删除、只读等配置不控制)
} }
export enum EditorZone { export enum EditorZone {

@ -9,6 +9,7 @@ import {
splitText splitText
} from '.' } from '.'
import { import {
EditorMode,
ElementType, ElementType,
IEditorOption, IEditorOption,
IElement, IElement,
@ -821,7 +822,8 @@ export function getAnchorElement(
} }
export interface IFormatElementContextOption { export interface IFormatElementContextOption {
isBreakWhenWrap: boolean isBreakWhenWrap?: boolean
editorOptions?: DeepRequired<IEditorOption>
} }
export function formatElementContext( export function formatElementContext(
@ -832,11 +834,12 @@ export function formatElementContext(
) { ) {
let copyElement = getAnchorElement(sourceElementList, anchorIndex) let copyElement = getAnchorElement(sourceElementList, anchorIndex)
if (!copyElement) return if (!copyElement) return
// 标题元素禁用时不复制标题属性 const { isBreakWhenWrap = false, editorOptions } = options || {}
if (copyElement.title?.disabled) { const { mode } = editorOptions || {}
// 非设计模式时:标题元素禁用时不复制标题属性
if (mode !== EditorMode.DESIGN && copyElement.title?.disabled) {
copyElement = omitObject(copyElement, TITLE_CONTEXT_ATTR) copyElement = omitObject(copyElement, TITLE_CONTEXT_ATTR)
} }
const { isBreakWhenWrap = false } = options || {}
// 是否已经换行 // 是否已经换行
let isBreakWarped = false let isBreakWarped = false
for (let e = 0; e < formatElementList.length; e++) { for (let e = 0; e < formatElementList.length; e++) {
@ -865,7 +868,8 @@ export function formatElementContext(
formatElementContext( formatElementContext(
sourceElementList, sourceElementList,
targetElement.valueList, targetElement.valueList,
anchorIndex anchorIndex,
options
) )
} }
// 非块类元素,需处理行属性 // 非块类元素,需处理行属性

@ -1412,6 +1412,10 @@ window.onload = function () {
{ {
mode: EditorMode.PRINT, mode: EditorMode.PRINT,
name: '打印模式' name: '打印模式'
},
{
mode: EditorMode.DESIGN,
name: '设计模式'
} }
] ]
const modeElement = document.querySelector<HTMLDivElement>('.editor-mode')! const modeElement = document.querySelector<HTMLDivElement>('.editor-mode')!

Loading…
Cancel
Save