feat: add design mode #795
This commit is contained in:
@@ -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
|
||||
|
||||
+1
-1
@@ -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]
|
||||
// 设计模式不验证删除权限
|
||||
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')!
|
||||
|
||||
Reference in New Issue
Block a user