Compare commits

..

7 Commits

@ -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), Design (Do not handle configurations such as non deletable and read-only). 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). 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>

5263
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -2,7 +2,7 @@
"name": "@liushuai05/canvas-editor", "name": "@liushuai05/canvas-editor",
"author": "Hufe", "author": "Hufe",
"license": "MIT", "license": "MIT",
"version": "0.9.89", "version": "0.9.95",
"description": "rich text editor by canvas/svg", "description": "rich text editor by canvas/svg",
"publishConfig": { "publishConfig": {
"registry": "https://registry.yarnpkg.com/", "registry": "https://registry.yarnpkg.com/",
@ -18,10 +18,10 @@
"typings": "./dist/src/editor/index.d.ts", "typings": "./dist/src/editor/index.d.ts",
"main": "./dist/canvas-editor.umd.js", "main": "./dist/canvas-editor.umd.js",
"module": "./dist/canvas-editor.es.js", "module": "./dist/canvas-editor.es.js",
"homepage": "https://github.com/Hufe921/canvas-editor", "homepage": "https://git.liushuai.cq.cn/lsadmin/canvas-editor_fex675",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/Hufe921/canvas-editor.git" "url": "git+https://git.liushuai.cq.cn/lsadmin/canvas-editor_fex675.git"
}, },
"keywords": [ "keywords": [
"canvas-editor", "canvas-editor",
@ -70,5 +70,11 @@
"simple-git-hooks": { "simple-git-hooks": {
"pre-commit": "npm run lint && npm run type:check", "pre-commit": "npm run lint && npm run type:check",
"commit-msg": "node scripts/verifyCommit.js" "commit-msg": "node scripts/verifyCommit.js"
},
"directories": {
"doc": "docs"
},
"bugs": {
"url": "https://github.com/Hufe921/canvas-editor/issues"
} }
} }

File diff suppressed because it is too large Load Diff

@ -827,9 +827,7 @@ 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,
@ -1590,9 +1588,7 @@ 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,
@ -1737,9 +1733,7 @@ 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
@ -2244,8 +2238,7 @@ 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)
} }

@ -102,7 +102,6 @@ import { PUNCTUATION_REG } from '../../dataset/constant/Regular'
import { LineBreakParticle } from './particle/LineBreakParticle' import { LineBreakParticle } from './particle/LineBreakParticle'
import { MouseObserver } from '../observer/MouseObserver' import { MouseObserver } from '../observer/MouseObserver'
import { LineNumber } from './frame/LineNumber' import { LineNumber } from './frame/LineNumber'
import { ITd } from '../../interface/table/Td'
import { PageBorder } from './frame/PageBorder' import { PageBorder } from './frame/PageBorder'
export class Draw { export class Draw {
@ -315,8 +314,6 @@ 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
@ -328,7 +325,6 @@ 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) {
@ -345,10 +341,6 @@ 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
@ -706,7 +698,6 @@ 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
@ -737,9 +728,8 @@ export class Draw {
while (deleteIndex >= start) { while (deleteIndex >= start) {
const deleteElement = elementList[deleteIndex] const deleteElement = elementList[deleteIndex]
if ( if (
isDesignMode || deleteElement?.control?.deletable !== false &&
(deleteElement?.control?.deletable !== false && deleteElement?.title?.deletable !== false
deleteElement?.title?.deletable !== false)
) { ) {
elementList.splice(deleteIndex, 1) elementList.splice(deleteIndex, 1)
} }
@ -1215,7 +1205,6 @@ export class Draw {
table: { tdPadding }, table: { tdPadding },
defaultTabWidth defaultTabWidth
} = this.options } = this.options
let curIndex = payload.curIndex
const defaultBasicRowMarginHeight = this.getDefaultBasicRowMarginHeight() const defaultBasicRowMarginHeight = this.getDefaultBasicRowMarginHeight()
const canvas = document.createElement('canvas') const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d') as CanvasRenderingContext2D const ctx = canvas.getContext('2d') as CanvasRenderingContext2D
@ -1289,30 +1278,9 @@ export class Draw {
} else if (element.type === ElementType.TABLE) { } else if (element.type === ElementType.TABLE) {
const tdPaddingWidth = tdPadding[1] + tdPadding[3] const tdPaddingWidth = tdPadding[1] + tdPadding[3]
const tdPaddingHeight = tdPadding[0] + tdPadding[2] const tdPaddingHeight = tdPadding[0] + tdPadding[2]
const height = this.getHeight()
const marginHeight = this.getMainOuterHeight()
const emptyMainHeight = (height - marginHeight - rowMargin * 2) / scale
// 表格分页处理进度https://github.com/Hufe921/canvas-editor/issues/41 // 表格分页处理进度https://github.com/Hufe921/canvas-editor/issues/41
// 查看后续表格是否属于同一个源表格-存在即合并 // 查看后续表格是否属于同一个源表格-存在即合并
if (element.pagingId) { if (element.pagingId) {
// 为当前表格构建一个虚拟表格
const virtualTable = Array.from(
{ length: element.trList!.length },
() => new Array(element.colgroup!.length)
) as Array<Array<ITd | undefined | null>>
element.trList!.forEach((tr, trIndex) => {
let tdIndex = 0
tr.tdList.forEach(td => {
while (virtualTable[trIndex][tdIndex] === null) {
tdIndex++
}
virtualTable[trIndex][tdIndex] = td
for (let i = 1; i < td.rowspan; i++) {
virtualTable[trIndex + i][tdIndex] = null
}
tdIndex += td.colspan
})
})
let tableIndex = i + 1 let tableIndex = i + 1
let combineCount = 0 let combineCount = 0
while (tableIndex < elementList.length) { while (tableIndex < elementList.length) {
@ -1321,65 +1289,8 @@ export class Draw {
const nexTrList = nextElement.trList!.filter( const nexTrList = nextElement.trList!.filter(
tr => !tr.pagingRepeat tr => !tr.pagingRepeat
) )
// 判断后续表格第一行是拆分出来的还是从原表格挪到下一页的 element.trList!.push(...nexTrList)
const isNextTrSplit = element.height! += nextElement.height!
element.trList![element.trList!.length - 1].id ===
nexTrList[0].pagingOriginId
let tdIndex = 0
const mergedTds: ITd[] = []
nexTrList[0].tdList.forEach(td => {
let targetTd
// 如果虚拟表格最后一行对应位置有单元格,则其就为目标单元格,否则向上查找
if (virtualTable[virtualTable.length - 1][tdIndex]) {
targetTd = virtualTable[virtualTable.length - 1][tdIndex]
} else {
for (let i = virtualTable.length - 2; i >= 0; i--) {
if (virtualTable[i][tdIndex]) {
targetTd = virtualTable[i][tdIndex]
break
}
}
}
if (targetTd) {
if (targetTd.id === td.pagingOriginId) {
targetTd.value.push(...td.value)
if (isNextTrSplit) {
targetTd.rowspan = targetTd.rowspan + td.rowspan - 1
} else {
targetTd.rowspan = targetTd.rowspan + td.rowspan
mergedTds.push(td)
}
}
tdIndex += targetTd.colspan
}
})
nexTrList[0].tdList = nexTrList[0].tdList.filter(td => {
const isNotMerged = mergedTds.every(
mergedTd => mergedTd.id !== td.id
)
delete td.pagingOriginId
return isNotMerged
})
while (nexTrList.length > 0) {
const lastTr = element.trList![element.trList!.length - 1]
const nextTr = nexTrList.shift()!
if (lastTr.id === nextTr.pagingOriginId) {
lastTr.height += nextTr.pagingOriginHeight || 0
// 更新id关联
lastTr.tdList.forEach(td => {
td.value.forEach(v => {
v.tdId = td.id
v.trId = lastTr.id
v.tableId = element.id
})
})
} else {
nextTr.height = nextTr.pagingOriginHeight || nextTr.height
element.trList!.push(nextTr)
}
delete nextTr.pagingOriginHeight
delete nextTr.pagingOriginId
}
tableIndex++ tableIndex++
combineCount++ combineCount++
} else { } else {
@ -1397,17 +1308,9 @@ export class Draw {
const trList = element.trList! const trList = element.trList!
for (let t = 0; t < trList.length; t++) { for (let t = 0; t < trList.length; t++) {
const tr = trList[t] const tr = trList[t]
// 行的minHeight最大只能为页面主内容区域高度如果表格前第一行是空行由于此时表格不能换页因此需减掉空行的高度
if (tr.minHeight && tr.minHeight > emptyMainHeight) {
if (i === 1 && elementList[0].value === ZERO) {
tr.minHeight = emptyMainHeight - rowList[0].height
} else {
tr.minHeight = emptyMainHeight
}
}
for (let d = 0; d < tr.tdList.length; d++) { for (let d = 0; d < tr.tdList.length; d++) {
const td = tr.tdList[d] const td = tr.tdList[d]
const { rowList } = this.computeRowList({ const rowList = this.computeRowList({
innerWidth: (td.width! - tdPaddingWidth) * scale, innerWidth: (td.width! - tdPaddingWidth) * scale,
elementList: td.value, elementList: td.value,
isPagingMode isPagingMode
@ -1482,6 +1385,8 @@ export class Draw {
metrics.boundingBoxAscent = -rowMargin metrics.boundingBoxAscent = -rowMargin
// 表格分页处理(拆分表格) // 表格分页处理(拆分表格)
if (isPagingMode) { if (isPagingMode) {
const height = this.getHeight()
const marginHeight = this.getMainOuterHeight()
let curPagePreHeight = marginHeight let curPagePreHeight = marginHeight
for (let r = 0; r < rowList.length; r++) { for (let r = 0; r < rowList.length; r++) {
const row = rowList[r] const row = rowList[r]
@ -1495,11 +1400,9 @@ export class Draw {
} }
} }
// 当前剩余高度是否能容下当前表格第一行(可拆分)的高度,排除掉表头类型 // 当前剩余高度是否能容下当前表格第一行(可拆分)的高度,排除掉表头类型
const rowMarginHeight = rowMargin * 2 const rowMarginHeight = rowMargin * 2 * scale
if ( if (
curPagePreHeight + curPagePreHeight + element.trList![0].height! + rowMarginHeight >
element.trList![0].height! * scale +
rowMarginHeight >
height || height ||
(element.pagingIndex !== 0 && element.trList![0].pagingRepeat) (element.pagingIndex !== 0 && element.trList![0].pagingRepeat)
) { ) {
@ -1509,262 +1412,159 @@ export class Draw {
// 表格高度超过页面高度开始截断行 // 表格高度超过页面高度开始截断行
if (curPagePreHeight + rowMarginHeight + elementHeight > height) { if (curPagePreHeight + rowMarginHeight + elementHeight > height) {
const trList = element.trList! const trList = element.trList!
/** 在哪一行截断表格 */ // 计算需要移除的行数
let splitTrIndex = -1 let deleteStart = 0 //拆分开始行
/** 被截断的行在截断线之前的高度 */ let deleteCount = 0 //拆分行数
let splitTrPreHeight = 0 let preTrHeight = 0 //前面tr的高度
// 根据表格行列数构造出一个虚拟表格 const colInfoCache = new Map() //缓存需要加临时td的信息
const virtualTable = Array.from({ length: trList.length }, () => // 大于一行时再拆分避免循环
new Array(element.colgroup?.length).fill(undefined) if (trList.length > 1) {
) as Array<Array<ITd | undefined>> // 拆分后剩余高度
trList.forEach(tr => { // 思路改变tr的结构 得到第几行开始删除删除的第一行添加rowspan: rowspan - 前面的tr的row,
tr.tdList.forEach(td => { // 添加临时td开始分页的head的上一级判断最大的colspan记录最大colspan在td的index
virtualTable[td.rowIndex!][td.colIndex!] = td // 在分页的第一行的index位置添加临时td
}) for (let r = 0; r < trList.length; r++) {
}) const tr = trList[r]
// 加上表格上方的行间距 const trHeight = tr.height * scale
curPagePreHeight += rowMargin if (
for (const [trIndex, tr] of trList.entries()) { curPagePreHeight + rowMarginHeight + preTrHeight + trHeight >
// 找到需要截断的行,以及该行在截断线之前的高度 height
if ( ) {
!tr.pagingRepeat && // 标题行不可截断 // 开始计算添加临时td
curPagePreHeight + rowMargin + tr.height * scale > height colInfoCache.forEach((item, key) => {
) { const mdRowIndex = parseInt(key.split('-')[1], 10)
splitTrIndex = trIndex const mdColIndex = parseInt(key.split('-')[0], 10)
splitTrPreHeight = height - curPagePreHeight - rowMargin // 额外减去被拆分的表格到下方的行间距
break // 确保 mdRowIndex 和 mdColIndex 是有效的索引
} else { if (
curPagePreHeight += tr.height * scale !trList[mdRowIndex] ||
} !trList[mdRowIndex].tdList[mdColIndex]
} ) {
if (splitTrIndex > -1) { console.error(
// 构建出一个行作为被截断出来的新行 `Invalid index: mdRowIndex=${mdRowIndex}, mdColIndex=${mdColIndex}`
const cloneTr = new Array(element.colgroup!.length) as Array< )
ITd | undefined return
>
// 判断目标行是否可截断
const allowSplitTr =
splitTrPreHeight >= trList[splitTrIndex].minHeight! * scale && // 最小行高区间内不可截断
trList[splitTrIndex].tdList.every(
// 如果截断线穿过该行所有单元格中第一个排版行,此时该行也不可截断
td => td.rowList![0].height < splitTrPreHeight
)
if (!allowSplitTr) {
splitTrPreHeight = 0
}
// 遍历虚拟表格中的截断行如果某个列的位置有td表明截断的是这个td如果该位置没有td且该位置不是被列合并单元格所占此时向上查找实际被截断的td
let preMergedEndIndex = -1
let splitTrReduceHeight =
trList[splitTrIndex].height - splitTrPreHeight / scale // 被拆分的行减少的高度
virtualTable[splitTrIndex].forEach((td, tdIndex) => {
if (tdIndex <= preMergedEndIndex) {
return
}
// 虚拟表格中截断行的当前索引位置是否有td
let hasTdAtCurIndex = false
// 找到被截断的td
let splitTd
if (td) {
splitTd = td
hasTdAtCurIndex = true
preMergedEndIndex = td.colIndex! + td.colspan - 1
} else {
for (let i = splitTrIndex; i >= 0; i--) {
if (virtualTable[i][tdIndex]) {
splitTd = virtualTable[i][tdIndex]
preMergedEndIndex =
splitTd!.colIndex! + splitTd!.colspan - 1
break
}
}
}
if (splitTd) {
cloneTr[tdIndex] = deepClone(splitTd)
// 如果tr可拆分根据截断位置将td中的内容拆分到新行中
// 如果tr不可拆分但当前位置td是跨行单元格同样需要拆分
if (allowSplitTr || splitTd.rowspan > 1) {
cloneTr[tdIndex]!.pagingOriginId =
splitTd.pagingOriginId || splitTd.id
cloneTr[tdIndex]!.id = getUUID()
// 计算当前td在截断线之前的高度默认为被截断行在截断线之前的高度若td跨行则加上其他行高度
let splitTdPreHeight = splitTrPreHeight
for (let i = splitTd.rowIndex!; i < splitTrIndex; i++) {
splitTdPreHeight += trList[i].height * scale
} }
// 根据td中已排版的内容的高度计算出哪些内容需要拆分到新行中 // 计算要被分割的rowspan
let splitTdPreRowHeight = 0 const cell = trList[mdRowIndex].tdList[mdColIndex]
let splitTdRowIndex = -1 const newRowspan = cell.rowspan - (item.row - item.count)
for (const [rowIndex, row] of splitTd.rowList!.entries()) { cell.rowspan = newRowspan
if ( let initHeight = 0
row.height + let initRealHeight = 0
splitTdPreRowHeight + let initRealMinHeight = 0
tdPaddingHeight * scale > for (let i = mdRowIndex; i < newRowspan; i++) {
splitTdPreHeight if (trList[i]?.tdList[mdColIndex]?.height) {
) { const td = trList[i]?.tdList[mdColIndex]
splitTdRowIndex = rowIndex if (td && td.height) {
break initHeight += td.height
} else { } else if (td && td.realHeight) {
splitTdPreRowHeight += row.height initRealHeight += td.realHeight
} else if (td && td.realMinHeight) {
initRealMinHeight += td.realMinHeight
} else {
console.error('td.height is undefined')
}
} }
} }
if (splitTdRowIndex > -1) { tr.tdList.splice(item.col, 0, tr.tdList[tr.tdList.length - 1])
// 拆分td中的内容 const tempRow = item.row - item.count
cloneTr[tdIndex]!.rowList = tr.tdList[mdColIndex] = {
splitTd.rowList!.splice(splitTdRowIndex) ...cell,
cloneTr[tdIndex]!.value = cloneTr[tdIndex]!.rowList!.map( id: getUUID(),
row => row.elementList rowspan: tempRow,
).flat() height: height - initHeight,
splitTd.value = splitTd realHeight: height - initRealHeight,
.rowList!.map(row => row.elementList) realMinHeight: height - initRealMinHeight,
.flat() temporaryFlag: true,
// 计算被拆分的单元格高度减少了多少 parentTr: `${mdColIndex}-${mdRowIndex}`,
const splitTdReduceMainHeight = cloneTr[ value: cell.value.map(item => ({ ...item, value: '' }))
tdIndex
]!.rowList!.reduce((ret, row) => {
return ret + row.height / scale
}, 0)
splitTd.mainHeight! -= splitTdReduceMainHeight
// 如果目标行可拆分,计算出行减小的高度
if (
allowSplitTr &&
splitTd.rowspan === 1 &&
splitTdRowIndex > 0
) {
splitTrReduceHeight = Math.min(
splitTd.height! - splitTdPreRowHeight / scale,
splitTrReduceHeight
)
}
} else {
// 执行到这里表示当前td虽然被截断但是内容高度未达到截断线位置
// FIXME: 此时拆分出来的单元格为空value中不应有元素否则会影响单元格可编辑性以及单元格尺寸
cloneTr[tdIndex]!.value = [{ value: ZERO }]
if (allowSplitTr && splitTd.rowspan === 1) {
splitTrReduceHeight = Math.min(
splitTd.height! - splitTdPreHeight / scale,
splitTrReduceHeight
)
}
} }
// 更新td的rowspan cell.height = initHeight
if (hasTdAtCurIndex) { cell.realHeight = initRealHeight
cloneTr[tdIndex]!.rowspan = splitTd.rowspan cell.realMinHeight = initRealMinHeight
splitTd.rowspan = 1 this.tableParticle.computeRowColInfo(element)
} else { })
cloneTr[tdIndex]!.rowspan = break
splitTd.rowspan - splitTrIndex + splitTd.rowIndex! } else {
splitTd.rowspan = splitTrIndex - splitTd.rowIndex! + 1 deleteStart = r + 1
// 如果tr不可截断且当前td是跨行单元格由于tr会被挪到后续表格因此需要将跨行单元格的rowspan减1 deleteCount = trList.length - deleteStart
if (!allowSplitTr && splitTd.rowspan > 1) { preTrHeight += trHeight
splitTd.rowspan-- // 重新记录临时的td
for (let i = 0; i < trList.length; i++) {
for (let j = trList[i]?.tdList.length - 1; j >= 0; j--) {
if (trList[i]?.tdList[j]?.temporaryFlag) {
const tempTd = trList[i].tdList[j]
if (tempTd.parentTr) {
const mdRowIndex = parseInt(
tempTd?.parentTr.split('-')[1],
10
)
const mdColIndex = parseInt(
tempTd?.parentTr.split('-')[0],
10
)
const cell = trList[mdRowIndex].tdList[mdColIndex]
cell.rowspan += trList[i]?.tdList[j].rowspan
let initHeight = 0
let initRealHeight = 0
let initRealMinHeight = 0
for (let i = mdRowIndex; i < tempTd.rowspan; i++) {
if (trList[i]?.tdList[mdColIndex]?.height) {
const td = trList[i]?.tdList[mdColIndex]
if (td && td.height) {
initHeight += td.height
} else if (td && td.realHeight) {
initRealHeight += td.realHeight
} else if (td && td.realMinHeight) {
initRealMinHeight += td.realMinHeight
} else {
console.error('td.height is undefined')
}
}
}
cell.height = initHeight
cell.realHeight = initRealHeight
cell.realMinHeight = initRealMinHeight
trList[i]?.tdList.splice(j, 1)
}
} }
} }
} }
} // 找到要分页的位置存入Map
}) tr.tdList.forEach((td, index) => {
// 构造新行 if (td.rowspan > 1) {
const newTr = deepClone(trList[splitTrIndex]) colInfoCache.set(`${index}-${r}`, {
newTr.tdList = cloneTr.filter(td => td !== undefined) as ITd[] col: index,
if (allowSplitTr) { row: td.rowspan,
newTr.pagingOriginId = newTr.pagingOriginId || newTr.id count: 0
newTr.id = getUUID() })
trList[splitTrIndex].height -= splitTrReduceHeight }
trList[splitTrIndex].tdList.forEach(td => { })
td.realHeight! -= splitTrReduceHeight // 计算分页是否需要添加临时td
td.height! -= splitTrReduceHeight colInfoCache.forEach((item, index) => {
}) ++item.count
// 记录拆分出来的新行的原始高度 if (item.count >= item.row) {
newTr.pagingOriginHeight = splitTrReduceHeight colInfoCache.delete(index)
newTr.height = Math.max( }
splitTrReduceHeight + tdPaddingHeight,
newTr.minHeight!
)
} else {
// 记录被挪到下一页的行的原始高度
newTr.pagingOriginHeight = newTr.height
}
// 构造出后续表格的行
const cloneTrList = trList.splice(splitTrIndex + 1)
cloneTrList.unshift(newTr)
// 如果当前表格行不可从中间拆分,此行将被挪至后续表格,因此将其从当前表格中移除
if (!allowSplitTr) {
trList.splice(splitTrIndex, 1)
}
let totalHeight = 0
// 更新后续表格首行行高
const crossRowTds: ITd[] = []
newTr.tdList.forEach(td => {
if (td.rowspan === 1) {
const mainHeight =
td.rowList!.reduce(
(ret, row) => ret + row.height / scale,
0
) + tdPaddingHeight
newTr.height = Math.max(mainHeight, newTr.height!)
} else {
crossRowTds.push(td)
}
})
totalHeight += newTr.height
const groupTds = crossRowTds.reduce((ret, td) => {
const key = td.rowspan
if (ret[key]) {
ret[key].push(td)
} else {
ret[key] = [td]
}
return ret
}, {} as { [key: number]: ITd[] })
const maxRowspan = Math.max(
...Object.keys(groupTds).map(parseInt)
)
// 计算出跨行单元格最大内容高度如果高于其rowspan区间的行高之和增加rowspan区间中最后一行的行高
for (let i = 2; i <= maxRowspan; i++) {
const tds = groupTds[i]
if (tds) {
let maxMainHeight = 0
tds.forEach(td => {
const mainHeight =
td.rowList!.reduce(
(ret, row) => ret + row.height / scale,
0
) + tdPaddingHeight
maxMainHeight = Math.max(mainHeight, maxMainHeight)
}) })
if (maxMainHeight > totalHeight) {
// 记录此行原始高度,便于合并时还原行高
cloneTrList[i - 1].pagingOriginHeight =
cloneTrList[i - 1].height
cloneTrList[i - 1].height += maxMainHeight - totalHeight
}
totalHeight += maxMainHeight
} }
} }
const totalOriginHeight = cloneTrList.reduce( }
(ret, tr) => ret + (tr.pagingOriginHeight || tr.height), if (deleteCount) {
const cloneTrList = trList.splice(deleteStart, deleteCount)
const cloneTrHeight = cloneTrList.reduce(
(pre, cur) => pre + cur.height,
0 0
) )
// 追加拆分表格
const pagingId = element.pagingId || getUUID() const pagingId = element.pagingId || getUUID()
element.pagingId = pagingId element.pagingId = pagingId
element.height -= totalOriginHeight element.height -= cloneTrHeight
metrics.height -= totalOriginHeight * scale metrics.height -= cloneTrHeight
metrics.boundingBoxDescent -= totalOriginHeight * scale metrics.boundingBoxDescent -= cloneTrHeight
// 追加拆分表格
const cloneElement = deepClone(element) const cloneElement = deepClone(element)
cloneElement.id = getUUID()
cloneElement.pagingId = pagingId cloneElement.pagingId = pagingId
cloneElement.pagingIndex = element.pagingIndex! + 1 cloneElement.pagingIndex = element.pagingIndex! + 1
this.tableParticle.computeRowColInfo(element)
// 更新拆分出来的新表格中的id关联信息
cloneTrList.forEach(tr => {
tr.tdList.forEach(td => {
td.value.forEach(v => {
v.tdId = td.id
v.trId = tr.id
v.tableId = cloneElement.id
})
})
})
// 处理分页重复表头 // 处理分页重复表头
const repeatTrList = trList.filter(tr => tr.pagingRepeat) const repeatTrList = trList.filter(tr => tr.pagingRepeat)
if (repeatTrList.length) { if (repeatTrList.length) {
@ -1773,46 +1573,35 @@ export class Draw {
cloneTrList.unshift(...cloneRepeatTrList) cloneTrList.unshift(...cloneRepeatTrList)
} }
cloneElement.trList = cloneTrList cloneElement.trList = cloneTrList
cloneElement.id = getUUID()
this.spliceElementList(elementList, i + 1, 0, cloneElement) this.spliceElementList(elementList, i + 1, 0, cloneElement)
} }
} }
// 表格经过分页处理-需要处理上下文和选区 // 表格经过分页处理-需要处理上下文
if (element.pagingId) { if (element.pagingId) {
const positionContext = this.position.getPositionContext() const positionContext = this.position.getPositionContext()
if ( if (positionContext.isTable) {
positionContext.isTable && // 查找光标所在表格索引根据trId搜索
positionContext.tableId === element.id let newPositionContextIndex = -1
) { let newPositionContextTrIndex = -1
const trIndex = element.trList!.findIndex( let tableIndex = i
r => while (tableIndex < elementList.length) {
r.pagingOriginId === positionContext.trId || const curElement = elementList[tableIndex]
r.id === positionContext.trId if (curElement.pagingId !== element.pagingId) break
) const trIndex = curElement.trList!.findIndex(
if (~trIndex) { r => r.id === positionContext.trId
const tr = element.trList![trIndex]
const tdIndex = tr.tdList!.findIndex(
d =>
d.pagingOriginId === positionContext.tdId ||
d.id === positionContext.tdId
) )
if (~tdIndex) { if (~trIndex) {
const td = tr.tdList![tdIndex] newPositionContextIndex = tableIndex
if (curIndex !== undefined && curIndex > -1) { newPositionContextTrIndex = trIndex
if (td.value[curIndex]) { break
positionContext.index = i
positionContext.trIndex = trIndex
positionContext.tdIndex = tdIndex
positionContext.trId = tr.id
positionContext.tdId = td.id
this.range.setRange(curIndex, curIndex)
} else {
positionContext.tableId = elementList[i + 1].id
curIndex -= td.value.length
}
}
} }
} else { tableIndex++
positionContext.tableId = elementList[i + 1].id }
if (~newPositionContextIndex) {
positionContext.index = newPositionContextIndex
positionContext.trIndex = newPositionContextTrIndex
this.position.setPositionContext(positionContext)
} }
} }
} }
@ -2059,7 +1848,7 @@ export class Draw {
} }
} }
} }
return { rowList, curIndex } return rowList
} }
private _computePageList(): IRow[][] { private _computePageList(): IRow[][] {
@ -2692,14 +2481,11 @@ export class Draw {
} }
} }
// 行信息 // 行信息
const { rowList, curIndex: newIndex } = this.computeRowList({ this.rowList = this.computeRowList({
isPagingMode, isPagingMode,
innerWidth, innerWidth,
elementList: this.elementList, elementList: this.elementList
curIndex
}) })
this.rowList = rowList
curIndex = newIndex
// 页面信息 // 页面信息
this.pageRowList = this._computePageList() this.pageRowList = this._computePageList()
// 位置信息 // 位置信息

@ -208,7 +208,7 @@ export class Control {
} }
public getIsDisabledControl(context: IControlContext = {}): boolean { public getIsDisabledControl(context: IControlContext = {}): boolean {
if (this.draw.isDesignMode() || !this.activeControl) return false if (!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,11 +452,8 @@ 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]
// 设计模式不验证删除权限 const { deletable = true } = startElement.control!
if (!this.draw.isDesignMode()) { if (!deletable) return null
const { deletable = true } = startElement.control!
if (!deletable) return null
}
let leftIndex = -1 let leftIndex = -1
let rightIndex = -1 let rightIndex = -1
// 向左查找 // 向左查找
@ -541,9 +538,7 @@ 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,13 +6,11 @@ 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'
@ -26,12 +24,10 @@ 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
@ -142,9 +138,7 @@ 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
@ -213,9 +207,7 @@ 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,13 +10,11 @@ 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'
@ -27,10 +25,8 @@ 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
@ -274,9 +270,7 @@ 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,13 +4,11 @@ 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'
@ -19,10 +17,8 @@ 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
} }
@ -118,9 +114,7 @@ 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

@ -58,7 +58,7 @@ export class Footer {
this.rowList = this.draw.computeRowList({ this.rowList = this.draw.computeRowList({
innerWidth, innerWidth,
elementList: this.elementList elementList: this.elementList
}).rowList })
} }
private _computePositionList() { private _computePositionList() {

@ -58,7 +58,7 @@ export class Header {
this.rowList = this.draw.computeRowList({ this.rowList = this.draw.computeRowList({
innerWidth, innerWidth,
elementList: this.elementList elementList: this.elementList
}).rowList })
} }
private _computePositionList() { private _computePositionList() {

@ -42,7 +42,7 @@ export class Placeholder {
this.rowList = this.draw.computeRowList({ this.rowList = this.draw.computeRowList({
innerWidth, innerWidth,
elementList: this.elementList elementList: this.elementList
}).rowList })
} }
private _computePositionList() { private _computePositionList() {

@ -1,6 +1,4 @@
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'
@ -11,11 +9,9 @@ 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)
@ -47,9 +43,7 @@ export class DateParticle {
} }
] ]
} }
formatElementContext(elementList, [dateElement], leftIndex, { formatElementContext(elementList, [dateElement], leftIndex)
editorOptions: this.options
})
this.draw.insertElementList([dateElement]) this.draw.insertElementList([dateElement])
} }

@ -30,15 +30,11 @@ 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 ( if (!copyElement.title?.disabled && !copyElement.control?.disabled) {
isDesignMode ||
(!copyElement.title?.disabled && !copyElement.control?.disabled)
) {
const nextElement = elementList[endIndex + 1] const nextElement = elementList[endIndex + 1]
if ( if (
!copyElement.type || !copyElement.type ||
@ -73,9 +69,7 @@ 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,8 +40,7 @@ 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,9 +25,7 @@ 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,9 +176,7 @@ 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,8 +50,7 @@ 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)
@ -88,9 +87,7 @@ 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,8 +19,7 @@ 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 {

@ -70,5 +70,4 @@ export interface IComputeRowListPayload {
innerWidth: number innerWidth: number
elementList: IElement[] elementList: IElement[]
isPagingMode?: boolean isPagingMode?: boolean
curIndex?: number
} }

@ -29,5 +29,6 @@ export interface ITd {
mainHeight?: number // 内容 + 内边距高度 mainHeight?: number // 内容 + 内边距高度
realHeight?: number // 真实高度(包含跨列) realHeight?: number // 真实高度(包含跨列)
realMinHeight?: number // 真实最小高度(包含跨列) realMinHeight?: number // 真实最小高度(包含跨列)
pagingOriginId?: string // 被拆分到下一页的单元格的原始id temporaryFlag?: boolean, // 临时单元格标识
parentTr?: string // 记录临时单元格的父级tr用于合并单元格
} }

@ -6,6 +6,4 @@ export interface ITr {
tdList: ITd[] tdList: ITd[]
minHeight?: number minHeight?: number
pagingRepeat?: boolean // 在各页顶端以标题行的形式重复出现 pagingRepeat?: boolean // 在各页顶端以标题行的形式重复出现
pagingOriginHeight?: number // 被拆分到下一页的行的原始高度
pagingOriginId?: string // 被拆分到下一页的行的原始id
} }

@ -9,7 +9,6 @@ import {
splitText splitText
} from '.' } from '.'
import { import {
EditorMode,
ElementType, ElementType,
IEditorOption, IEditorOption,
IElement, IElement,
@ -607,85 +606,13 @@ export function zipElementList(
} else if (element.type === ElementType.TABLE) { } else if (element.type === ElementType.TABLE) {
// 分页表格先进行合并 // 分页表格先进行合并
if (element.pagingId) { if (element.pagingId) {
// 为当前表格构建一个虚拟表格
const virtualTable = Array.from(
{ length: element.trList!.length },
() => new Array(element.colgroup!.length)
) as Array<Array<ITd | undefined | null>>
element.trList!.forEach((tr, trIndex) => {
let tdIndex = 0
tr.tdList.forEach(td => {
while (virtualTable[trIndex][tdIndex] === null) {
tdIndex++
}
virtualTable[trIndex][tdIndex] = td
for (let i = 1; i < td.rowspan; i++) {
virtualTable[trIndex + i][tdIndex] = null
}
tdIndex += td.colspan
})
})
let tableIndex = e + 1 let tableIndex = e + 1
let combineCount = 0 let combineCount = 0
while (tableIndex < elementList.length) { while (tableIndex < elementList.length) {
const nextElement = elementList[tableIndex] const nextElement = elementList[tableIndex]
if (nextElement.pagingId === element.pagingId) { if (nextElement.pagingId === element.pagingId) {
const nexTrList = nextElement.trList!.filter( element.height! += nextElement.height!
tr => !tr.pagingRepeat element.trList!.push(...nextElement.trList!)
)
// 判断后续表格第一行是拆分出来的还是从原表格挪到下一页的
const isNextTrSplit =
element.trList![element.trList!.length - 1].id ===
nexTrList[0].pagingOriginId
// 遍历后续表格首行中的单元格,在虚拟表格中找到其对应单元格
let tdIndex = 0
const mergedTds: ITd[] = []
nexTrList[0].tdList.forEach(td => {
let targetTd
// 如果虚拟表格最后一行对应位置有单元格,则其就为目标单元格,否则向上查找
if (virtualTable[virtualTable.length - 1][tdIndex]) {
targetTd = virtualTable[virtualTable.length - 1][tdIndex]
} else {
for (let i = virtualTable.length - 2; i >= 0; i--) {
if (virtualTable[i][tdIndex]) {
targetTd = virtualTable[i][tdIndex]
break
}
}
}
if (targetTd) {
if (targetTd.id === td.pagingOriginId) {
targetTd.value.push(...td.value)
if (isNextTrSplit) {
targetTd.rowspan = targetTd.rowspan + td.rowspan - 1
} else {
targetTd.rowspan = targetTd.rowspan + td.rowspan
mergedTds.push(td)
}
}
tdIndex += targetTd.colspan
}
})
nexTrList[0].tdList = nexTrList[0].tdList.filter(td => {
const isNotMerged = mergedTds.every(
mergedTd => mergedTd.id !== td.id
)
delete td.pagingOriginId
return isNotMerged
})
// 更新行高,逐行合并
while (nexTrList.length > 0) {
const lastTr = element.trList![element.trList!.length - 1]
const nextTr = nexTrList.shift()!
if (lastTr.id === nextTr.pagingOriginId) {
lastTr.height += nextTr.pagingOriginHeight || 0
} else {
nextTr.height = nextTr.pagingOriginHeight || nextTr.height
element.trList!.push(nextTr)
}
delete nextTr.pagingOriginHeight
delete nextTr.pagingOriginId
}
tableIndex++ tableIndex++
combineCount++ combineCount++
} else { } else {
@ -894,8 +821,7 @@ export function getAnchorElement(
} }
export interface IFormatElementContextOption { export interface IFormatElementContextOption {
isBreakWhenWrap?: boolean isBreakWhenWrap: boolean
editorOptions?: DeepRequired<IEditorOption>
} }
export function formatElementContext( export function formatElementContext(
@ -906,12 +832,11 @@ export function formatElementContext(
) { ) {
let copyElement = getAnchorElement(sourceElementList, anchorIndex) let copyElement = getAnchorElement(sourceElementList, anchorIndex)
if (!copyElement) return if (!copyElement) return
const { isBreakWhenWrap = false, editorOptions } = options || {} // 标题元素禁用时不复制标题属性
const { mode } = editorOptions || {} if (copyElement.title?.disabled) {
// 非设计模式时:标题元素禁用时不复制标题属性
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++) {
@ -940,8 +865,7 @@ export function formatElementContext(
formatElementContext( formatElementContext(
sourceElementList, sourceElementList,
targetElement.valueList, targetElement.valueList,
anchorIndex, anchorIndex
options
) )
} }
// 非块类元素,需处理行属性 // 非块类元素,需处理行属性

@ -1412,10 +1412,6 @@ 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')!

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save