From c86e5468e666c64c087a430af8a9a8307b837f0f Mon Sep 17 00:00:00 2001 From: zp190265950 <41977379+zp190265950@users.noreply.github.com> Date: Thu, 9 May 2024 21:15:52 +0800 Subject: [PATCH] feat: table header appears repeatedly when paging #541 Co-authored-by: Hufe921 --- docs/en/guide/option.md | 14 +++++-- docs/en/guide/schema.md | 1 + docs/guide/option.md | 14 +++++-- docs/guide/schema.md | 1 + src/editor/core/command/CommandAdapt.ts | 7 ++-- src/editor/core/draw/Draw.ts | 40 +++++++++++++++---- .../core/draw/particle/table/TableTool.ts | 5 ++- src/editor/core/position/Position.ts | 8 +++- src/editor/dataset/constant/Table.ts | 7 ++++ src/editor/index.ts | 10 +++-- src/editor/interface/Editor.ts | 6 +-- src/editor/interface/Element.ts | 1 + src/editor/interface/table/Table.ts | 7 ++++ src/editor/interface/table/Tr.ts | 1 + src/editor/utils/element.ts | 8 ++-- 15 files changed, 98 insertions(+), 32 deletions(-) create mode 100644 src/editor/dataset/constant/Table.ts create mode 100644 src/editor/interface/table/Table.ts diff --git a/docs/en/guide/option.md b/docs/en/guide/option.md index 39fcf2b..f787972 100644 --- a/docs/en/guide/option.md +++ b/docs/en/guide/option.md @@ -43,10 +43,8 @@ interface IEditorOption { marginIndicatorColor?: string // The margin indicator color. default: #BABABA margins?: IMargin // Page margins. default: [100, 120, 100, 120] pageMode?: PageMode // Paper mode: Linkage, Pagination. default: Pagination - tdPadding?: IPadding // Cell padding. default: [0, 5, 5, 5] - defaultTrMinHeight?: number // Default table row minimum height. default: 42 - defaultColMinWidth?: number // Default minimum width for table columns (applied if the overall width is sufficient, otherwise scaled down). default: 40 defaultHyperlinkColor?: string // Default hyperlink color. default: #0000FF + table?: ITableOption // table configuration {tdPadding?:IPadding; defaultTrMinHeight?:number; defaultColMinWidth?:number} header?: IHeader // Header information.{top?:number; maxHeightRadio?:MaxHeightRatio;} footer?: IFooter // Footer information. {bottom?:number; maxHeightRadio?:MaxHeightRatio;} pageNumber?: IPageNumber // Page number information. {bottom:number; size:number; font:string; color:string; rowFlex:RowFlex; format:string; numberType:NumberType;} @@ -75,6 +73,16 @@ interface IEditorOption { } ``` +## Table Configuration + +```typescript +interface ITableOption { + tdPadding?: IPadding // Cell padding. default: [0, 5, 5, 5] + defaultTrMinHeight?: number // Default table row minimum height. default: 42 + defaultColMinWidth?: number // Default minimum width for table columns (applied if the overall width is sufficient, otherwise +} +``` + ## Header Configuration ```typescript diff --git a/docs/en/guide/schema.md b/docs/en/guide/schema.md index 93bf789..c98ced2 100644 --- a/docs/en/guide/schema.md +++ b/docs/en/guide/schema.md @@ -56,6 +56,7 @@ interface IElement { }[]; trList?: { height: number; + pagingRepeat?: boolean; tdList: { colspan: number; rowspan: number; diff --git a/docs/guide/option.md b/docs/guide/option.md index 75d1d65..f3bb0e6 100644 --- a/docs/guide/option.md +++ b/docs/guide/option.md @@ -43,10 +43,8 @@ interface IEditorOption { marginIndicatorColor?: string // 页边距指示器颜色。默认:#BABABA margins?: IMargin // 页面边距。默认:[100, 120, 100, 120] pageMode?: PageMode // 纸张模式:连页、分页。默认:分页 - tdPadding?: IPadding // 单元格内边距。默认:[0, 5, 5, 5] - defaultTrMinHeight?: number // 默认表格行最小高度。默认:42 - defaultColMinWidth?: number // 默认表格列最小宽度(整体宽度足够时应用,否则会按比例缩小)。默认:40 defaultHyperlinkColor?: string // 默认超链接颜色。默认:#0000FF + table?: ITableOption // 表格配置。{tdPadding?:IPadding; defaultTrMinHeight?:number; defaultColMinWidth?:number} header?: IHeader // 页眉信息。{top?:number; maxHeightRadio?:MaxHeightRatio;} footer?: IFooter // 页脚信息。{bottom?:number; maxHeightRadio?:MaxHeightRatio;} pageNumber?: IPageNumber // 页码信息。{bottom:number; size:number; font:string; color:string; rowFlex:RowFlex; format:string; numberType:NumberType;} @@ -75,6 +73,16 @@ interface IEditorOption { } ``` +## 表格配置 + +```typescript +interface ITableOption { + tdPadding?: IPadding // 单元格内边距。默认:[0, 5, 5, 5] + defaultTrMinHeight?: number // 默认表格行最小高度。默认:42 + defaultColMinWidth?: number // 默认表格列最小宽度(整体宽度足够时应用,否则会按比例缩小)。默认:40 +} +``` + ## 页眉配置 ```typescript diff --git a/docs/guide/schema.md b/docs/guide/schema.md index c4078c7..99fd1d3 100644 --- a/docs/guide/schema.md +++ b/docs/guide/schema.md @@ -56,6 +56,7 @@ interface IElement { }[]; trList?: { height: number; + pagingRepeat?: boolean; tdList: { colspan: number; rowspan: number; diff --git a/src/editor/core/command/CommandAdapt.ts b/src/editor/core/command/CommandAdapt.ts index 5ac1d20..8ec3523 100644 --- a/src/editor/core/command/CommandAdapt.ts +++ b/src/editor/core/command/CommandAdapt.ts @@ -771,6 +771,7 @@ export class CommandAdapt { if (activeControl) return const { startIndex, endIndex } = this.range.getRange() if (!~startIndex && !~endIndex) return + const { defaultTrMinHeight } = this.options.table const elementList = this.draw.getElementList() let offsetX = 0 if (elementList[startIndex]?.listId) { @@ -794,7 +795,7 @@ export class CommandAdapt { for (let r = 0; r < row; r++) { const tdList: ITd[] = [] const tr: ITr = { - height: this.options.defaultTrMinHeight, + height: defaultTrMinHeight, tdList } for (let c = 0; c < col; c++) { @@ -992,7 +993,7 @@ export class CommandAdapt { // 重新计算宽度 const colgroup = element.colgroup! colgroup.splice(curTdIndex, 0, { - width: this.options.defaultColMinWidth + width: this.options.table.defaultColMinWidth }) const colgroupWidth = colgroup.reduce((pre, cur) => pre + cur.width, 0) const width = this.draw.getOriginalInnerWidth() @@ -1051,7 +1052,7 @@ export class CommandAdapt { // 重新计算宽度 const colgroup = element.colgroup! colgroup.splice(curTdIndex, 0, { - width: this.options.defaultColMinWidth + width: this.options.table.defaultColMinWidth }) const colgroupWidth = colgroup.reduce((pre, cur) => pre + cur.width, 0) const width = this.draw.getOriginalInnerWidth() diff --git a/src/editor/core/draw/Draw.ts b/src/editor/core/draw/Draw.ts index eebf82a..b13bec5 100644 --- a/src/editor/core/draw/Draw.ts +++ b/src/editor/core/draw/Draw.ts @@ -398,7 +398,10 @@ export class Draw { } public getTdPadding(): IPadding { - const { tdPadding, scale } = this.options + const { + table: { tdPadding }, + scale + } = this.options return tdPadding.map(m => m * scale) } @@ -1124,8 +1127,13 @@ export class Draw { public computeRowList(payload: IComputeRowListPayload) { const { innerWidth, elementList, isPagingMode = false } = payload - const { defaultSize, defaultRowMargin, scale, tdPadding, defaultTabWidth } = - this.options + const { + defaultSize, + defaultRowMargin, + scale, + table: { tdPadding }, + defaultTabWidth + } = this.options const defaultBasicRowMarginHeight = this.getDefaultBasicRowMarginHeight() const canvas = document.createElement('canvas') const ctx = canvas.getContext('2d') as CanvasRenderingContext2D @@ -1214,7 +1222,10 @@ export class Draw { while (tableIndex < elementList.length) { const nextElement = elementList[tableIndex] if (nextElement.pagingId === element.pagingId) { - element.trList!.push(...nextElement.trList!) + const nexTrList = nextElement.trList!.filter( + tr => !tr.pagingRepeat + ) + element.trList!.push(...nexTrList) element.height! += nextElement.height! tableIndex++ combineCount++ @@ -1226,6 +1237,7 @@ export class Draw { elementList.splice(i + 1, combineCount) } } + element.pagingIndex = element.pagingIndex ?? 0 // 计算表格行列 this.tableParticle.computeRowColInfo(element) // 计算表格内元素信息 @@ -1323,11 +1335,12 @@ export class Draw { curPagePreHeight += row.height } } - // 当前剩余高度是否能容下当前表格第一行(可拆分)的高度 + // 当前剩余高度是否能容下当前表格第一行(可拆分)的高度,排除掉表头类型 const rowMarginHeight = rowMargin * 2 * scale if ( curPagePreHeight + element.trList![0].height! + rowMarginHeight > - height + height || + (element.pagingIndex !== 0 && element.trList![0].pagingRepeat) ) { // 无可拆分行则切换至新页 curPagePreHeight = marginHeight @@ -1378,6 +1391,14 @@ export class Draw { // 追加拆分表格 const cloneElement = deepClone(element) cloneElement.pagingId = pagingId + cloneElement.pagingIndex = element.pagingIndex! + 1 + // 处理分页重复表头 + const repeatTrList = trList.filter(tr => tr.pagingRepeat) + if (repeatTrList.length) { + const cloneRepeatTrList = deepClone(repeatTrList) + cloneRepeatTrList.forEach(tr => (tr.id = getUUID())) + cloneTrList.unshift(...cloneRepeatTrList) + } cloneElement.trList = cloneTrList cloneElement.id = getUUID() this.spliceElementList(elementList, i + 1, 0, cloneElement) @@ -1742,7 +1763,12 @@ export class Draw { // 优先绘制高亮元素 this._drawHighlight(ctx, payload) // 绘制元素、下划线、删除线、选区 - const { scale, tdPadding, group, lineBreak } = this.options + const { + scale, + table: { tdPadding }, + group, + lineBreak + } = this.options const { rowList, pageNo, diff --git a/src/editor/core/draw/particle/table/TableTool.ts b/src/editor/core/draw/particle/table/TableTool.ts index 7aecd85..5605af1 100644 --- a/src/editor/core/draw/particle/table/TableTool.ts +++ b/src/editor/core/draw/particle/table/TableTool.ts @@ -1,6 +1,7 @@ import { IElement } from '../../../..' import { EDITOR_PREFIX } from '../../../../dataset/constant/Editor' import { TableOrder } from '../../../../dataset/enum/table/TableTool' +import { DeepRequired } from '../../../../interface/Common' import { IEditorOption } from '../../../../interface/Editor' import { Position } from '../../../position/Position' import { Draw } from '../../Draw' @@ -22,7 +23,7 @@ export class TableTool { private draw: Draw private canvas: HTMLCanvasElement - private options: Required + private options: DeepRequired private position: Position private container: HTMLDivElement private toolRowContainer: HTMLDivElement | null @@ -255,7 +256,7 @@ export class TableTool { const trList = element.trList! const tr = trList[index] || trList[index - 1] // 最大移动高度-向上移动超出最小高度限定,则减少移动量 - const { defaultTrMinHeight } = this.options + const { defaultTrMinHeight } = this.options.table if (dy < 0 && tr.height + dy < defaultTrMinHeight) { dy = defaultTrMinHeight - tr.height } diff --git a/src/editor/core/position/Position.ts b/src/editor/core/position/Position.ts index f17d57c..d6a35e5 100644 --- a/src/editor/core/position/Position.ts +++ b/src/editor/core/position/Position.ts @@ -19,6 +19,7 @@ import { Draw } from '../draw/Draw' import { EditorMode, EditorZone } from '../../dataset/enum/Editor' import { deepClone } from '../../utils' import { ImageDisplay } from '../../dataset/enum/Common' +import { DeepRequired } from '../../interface/Common' export class Position { private cursorPosition: IElementPosition | null @@ -27,7 +28,7 @@ export class Position { private floatPositionList: IFloatPosition[] private draw: Draw - private options: Required + private options: DeepRequired constructor(draw: Draw) { this.positionList = [] @@ -114,7 +115,10 @@ export class Position { innerWidth, zone } = payload - const { scale, tdPadding } = this.options + const { + scale, + table: { tdPadding } + } = this.options let x = startX let y = startY let index = startIndex diff --git a/src/editor/dataset/constant/Table.ts b/src/editor/dataset/constant/Table.ts new file mode 100644 index 0000000..04aa9a4 --- /dev/null +++ b/src/editor/dataset/constant/Table.ts @@ -0,0 +1,7 @@ +import { ITableOption } from '../../interface/table/Table' + +export const defaultTableOption: Readonly> = { + tdPadding: [0, 5, 5, 5], + defaultTrMinHeight: 42, + defaultColMinWidth: 40 +} diff --git a/src/editor/index.ts b/src/editor/index.ts index 69b3f10..c67ba01 100644 --- a/src/editor/index.ts +++ b/src/editor/index.ts @@ -82,6 +82,8 @@ import { ILineBreakOption } from './interface/LineBreak' import { defaultLineBreak } from './dataset/constant/LineBreak' import { ISeparatorOption } from './interface/Separator' import { defaultSeparatorOption } from './dataset/constant/Separator' +import { ITableOption } from './interface/table/Table' +import { defaultTableOption } from './dataset/constant/Table' export default class Editor { public command: Command @@ -97,6 +99,10 @@ export default class Editor { data: IEditorData | IElement[], options: IEditorOption = {} ) { + const tableOptions: Required = { + ...defaultTableOption, + ...options.table + } const headerOptions: Required = { ...defaultHeaderOption, ...options.header @@ -192,9 +198,6 @@ export default class Editor { marginIndicatorColor: '#BABABA', margins: [100, 120, 100, 120], pageMode: PageMode.PAGING, - tdPadding: [0, 5, 5, 5], - defaultTrMinHeight: 42, - defaultColMinWidth: 40, defaultHyperlinkColor: '#0000FF', paperDirection: PaperDirection.VERTICAL, inactiveAlpha: 0.6, @@ -206,6 +209,7 @@ export default class Editor { contextMenuDisableKeys: [], scrollContainerSelector: '', ...options, + table: tableOptions, header: headerOptions, footer: footerOptions, pageNumber: pageNumberOptions, diff --git a/src/editor/interface/Editor.ts b/src/editor/interface/Editor.ts index 4019b6c..a2af604 100644 --- a/src/editor/interface/Editor.ts +++ b/src/editor/interface/Editor.ts @@ -8,7 +8,6 @@ import { import { IBackgroundOption } from './Background' import { ICheckboxOption } from './Checkbox' import { IRadioOption } from './Radio' -import { IPadding } from './Common' import { IControlOption } from './Control' import { ICursorOption } from './Cursor' import { IFooter } from './Footer' @@ -23,6 +22,7 @@ import { ITitleOption } from './Title' import { IWatermark } from './Watermark' import { IZoneOption } from './Zone' import { ISeparatorOption } from './Separator' +import { ITableOption } from './table/Table' export interface IEditorData { header?: IElement[] @@ -60,9 +60,6 @@ export interface IEditorOption { marginIndicatorColor?: string margins?: IMargin pageMode?: PageMode - tdPadding?: IPadding - defaultTrMinHeight?: number - defaultColMinWidth?: number defaultHyperlinkColor?: string paperDirection?: PaperDirection inactiveAlpha?: number @@ -73,6 +70,7 @@ export interface IEditorOption { contextMenuDisableKeys?: string[] scrollContainerSelector?: string wordBreak?: WordBreak + table?: ITableOption header?: IHeader footer?: IFooter pageNumber?: IPageNumber diff --git a/src/editor/interface/Element.ts b/src/editor/interface/Element.ts index 049fb5c..75534c6 100644 --- a/src/editor/interface/Element.ts +++ b/src/editor/interface/Element.ts @@ -69,6 +69,7 @@ export interface ITableElement { tableId?: string conceptId?: string pagingId?: string // 用于区分拆分的表格同属一个源表格 + pagingIndex?: number // 拆分的表格索引 } export type ITable = ITableAttr & ITableElement diff --git a/src/editor/interface/table/Table.ts b/src/editor/interface/table/Table.ts new file mode 100644 index 0000000..d10086c --- /dev/null +++ b/src/editor/interface/table/Table.ts @@ -0,0 +1,7 @@ +import { IPadding } from '../Common' + +export interface ITableOption { + tdPadding?: IPadding + defaultTrMinHeight?: number + defaultColMinWidth?: number +} diff --git a/src/editor/interface/table/Tr.ts b/src/editor/interface/table/Tr.ts index 0cebc7a..4b74afd 100644 --- a/src/editor/interface/table/Tr.ts +++ b/src/editor/interface/table/Tr.ts @@ -5,4 +5,5 @@ export interface ITr { height: number tdList: ITd[] minHeight?: number + pagingRepeat?: boolean // 在各页顶端以标题行的形式重复出现 } diff --git a/src/editor/utils/element.ts b/src/editor/utils/element.ts index 289d268..ccf8ef8 100644 --- a/src/editor/utils/element.ts +++ b/src/editor/utils/element.ts @@ -148,15 +148,13 @@ export function formatElementList( const tableId = getUUID() el.id = tableId if (el.trList) { + const { defaultTrMinHeight } = editorOptions.table for (let t = 0; t < el.trList.length; t++) { const tr = el.trList[t] const trId = getUUID() tr.id = trId - if ( - !tr.minHeight || - tr.minHeight < editorOptions.defaultTrMinHeight - ) { - tr.minHeight = editorOptions.defaultTrMinHeight + if (!tr.minHeight || tr.minHeight < defaultTrMinHeight) { + tr.minHeight = defaultTrMinHeight } if (tr.height < tr.minHeight) { tr.height = tr.minHeight