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
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

@ -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

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

@ -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)
}

@ -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)
}

@ -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,

@ -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<IEditorOption>
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)
}
// 重新渲染控件

@ -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<IEditorOption>
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)
}
// 设置状态

@ -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<IEditorOption>
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

@ -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<IEditorOption>
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])
}

@ -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
}

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

@ -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])
}
}

@ -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]

@ -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])
}

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

@ -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<IEditorOption>
}
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
)
}
// 非块类元素,需处理行属性

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

Loading…
Cancel
Save