diff --git a/common/config/rush/version-policies.json b/common/config/rush/version-policies.json index 02429c54cc..cc7f033cc9 100644 --- a/common/config/rush/version-policies.json +++ b/common/config/rush/version-policies.json @@ -1 +1 @@ -[{"definitionName":"lockStepVersion","policyName":"vtableMain","version":"1.17.4","mainProject":"@visactor/vtable","nextBump":"patch"}] +[{"definitionName":"lockStepVersion","policyName":"vtableMain","version":"1.17.5","mainProject":"@visactor/vtable","nextBump":"patch"}] diff --git a/docs/assets/changelog/en/release.md b/docs/assets/changelog/en/release.md index e1881bcffc..6fa207b2ce 100644 --- a/docs/assets/changelog/en/release.md +++ b/docs/assets/changelog/en/release.md @@ -1,3 +1,22 @@ +# v1.17.4 + +2025-03-31 + + +**🆕 New feature** + +- **@visactor/vtable**: add barMarkInBar style config in progressbar [#3616](https://github.com/VisActor/VTable/issues/3616) + +**🐛 Bug fix** + +- **@visactor/vtable**: fix button style problem [#3614](https://github.com/VisActor/VTable/issues/3614) +- **@visactor/vtable**: fix checkbox state order update [#3606](https://github.com/VisActor/VTable/issues/3606) +- **@visactor/vtable**: add isCustom tag for merge cell range [#3504](https://github.com/VisActor/VTable/issues/3504) +- **@visactor/vtable**: fix tree checkbox state update problem +- **@visactor/vtable**: disable group title editor + +[more detail about v1.17.4](https://github.com/VisActor/VTable/releases/tag/v1.17.4) + # v1.17.3 2025-03-24 diff --git a/docs/assets/changelog/zh/release.md b/docs/assets/changelog/zh/release.md index 927d84ac1b..725fa30621 100644 --- a/docs/assets/changelog/zh/release.md +++ b/docs/assets/changelog/zh/release.md @@ -1,3 +1,21 @@ +# v1.17.4 + +2025-03-31 + +**🆕 新增功能** + +- **@visactor/vtable**: 在进度条中添加条形标记样式配置 [#3616](https://github.com/VisActor/VTable/issues/3616) + +**🐛 功能修复** + +- **@visactor/vtable**: 修复按钮样式问题 [#3614](https://github.com/VisActor/VTable/issues/3614) +- **@visactor/vtable**: 修复复选框状态顺序更新问题 [#3606](https://github.com/VisActor/VTable/issues/3606) +- **@visactor/vtable**: 为合并单元格范围添加自定义标记 [#3504](https://github.com/VisActor/VTable/issues/3504) +- **@visactor/vtable**: 修复树形复选框状态更新问题 +- **@visactor/vtable**: 禁用分组标题编辑器 + +[更多详情请查看 v1.17.4](https://github.com/VisActor/VTable/releases/tag/v1.17.4) + # v1.17.3 2025-03-24 diff --git a/docs/assets/option/en/common/style-item-define.md b/docs/assets/option/en/common/style-item-define.md index 8406f84bc3..7760bc1267 100644 --- a/docs/assets/option/en/common/style-item-define.md +++ b/docs/assets/option/en/common/style-item-define.md @@ -134,7 +134,20 @@ type CursorPropertyDefine = string | ((args: StylePropertyFunctionArg) => string {{ target: common-marked }} ``` -type MarkedPropertyDefine = boolean | ((args: StylePropertyFunctionArg) => boolean); +type MarkedPropertyDefine = boolean | MarkCellStyle | ((args: StylePropertyFunctionArg) => boolean | MarkCellStyle); + +type MarkCellStyle = { + /** 标记背景色 默认蓝色*/ + bgColor?: CanvasRenderingContext2D['fillStyle']; + /** 标记形状 默认'sector' */ + shape?: 'rect' | 'triangle' | 'sector'; + /** 标记位置 默认'right-top' */ + position?: 'left-top' | 'left-bottom' | 'right-top' | 'right-bottom'; + /** 标记大小 默认10 */ + size?: number; + /** 标记偏移量 默认0 */ + offset?: number; +}; ``` diff --git a/docs/assets/option/en/common/style.md b/docs/assets/option/en/common/style.md index 6f53497fab..45b10e0287 100644 --- a/docs/assets/option/en/common/style.md +++ b/docs/assets/option/en/common/style.md @@ -123,10 +123,12 @@ Set whether the text in the cell has a sticking effect 【Text can dynamically a When the cell text has an adsorption effect [the text can dynamically adjust its position when scrolling], the basis for adsorption is the horizontal alignment of the cell. For example, when `textStickBaseOnAlign` is `true` and `textAlign` is `'center'`, the text will be adsorbed to the horizontal center of the cell; otherwise, it will be adsorbed to the left or right edge of the cell (depending on the scroll position) #${prefix} marked(MarkedPropertyDefine) + Set whether the cell has a marked style {{ use: common-marked( prefix = ${prefix} ) }} + #${prefix} autoWrapText(boolean) Set whether the cell's text should automatically wrap diff --git a/docs/assets/option/zh/common/style-item-define.md b/docs/assets/option/zh/common/style-item-define.md index 8406f84bc3..c8b3687a21 100644 --- a/docs/assets/option/zh/common/style-item-define.md +++ b/docs/assets/option/zh/common/style-item-define.md @@ -134,8 +134,20 @@ type CursorPropertyDefine = string | ((args: StylePropertyFunctionArg) => string {{ target: common-marked }} ``` -type MarkedPropertyDefine = boolean | ((args: StylePropertyFunctionArg) => boolean); - +type MarkedPropertyDefine = boolean | MarkCellStyle | ((args: StylePropertyFunctionArg) => boolean | MarkCellStyle); + +type MarkCellStyle = { + /** 标记背景色 默认蓝色*/ + bgColor?: CanvasRenderingContext2D['fillStyle']; + /** 标记形状 默认'sector' */ + shape?: 'rect' | 'triangle' | 'sector'; + /** 标记位置 默认'right-top' */ + position?: 'left-top' | 'left-bottom' | 'right-top' | 'right-bottom'; + /** 标记大小 默认10 */ + size?: number; + /** 标记偏移量 默认0 */ + offset?: number; +}; ``` {{ target: common-colorsDef }} diff --git a/docs/assets/option/zh/common/style.md b/docs/assets/option/zh/common/style.md index 9667ea0a1d..938569d836 100644 --- a/docs/assets/option/zh/common/style.md +++ b/docs/assets/option/zh/common/style.md @@ -124,10 +124,12 @@ 当单元格的文本有吸附效果【当滚动时文本可动态调整位置】时,吸附的基准是单元格的水平对齐方式。例如当`textStickBaseOnAlign`为`true`时,`textAlign`为`'center'`时,文本会吸附在单元格的水平中心位置;否则就会吸附在单元格左边缘或右边缘(依据滚动位置决定)。 #${prefix} marked(MarkedPropertyDefine) + 设置单元格是否有标记样式 {{ use: common-marked( prefix = ${prefix} ) }} + #${prefix} autoWrapText(boolean) 设置单元格是否自动换行 diff --git a/packages/openinula-vtable/package.json b/packages/openinula-vtable/package.json index 75e4f8d83c..55925200cc 100644 --- a/packages/openinula-vtable/package.json +++ b/packages/openinula-vtable/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/openinula-vtable", - "version": "1.17.4", + "version": "1.17.5", "description": "The openinula version of VTable", "keywords": [ "openinula", diff --git a/packages/react-vtable/package.json b/packages/react-vtable/package.json index f066362874..c14d49c71f 100644 --- a/packages/react-vtable/package.json +++ b/packages/react-vtable/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/react-vtable", - "version": "1.17.4", + "version": "1.17.5", "description": "The react version of VTable", "keywords": [ "react", diff --git a/packages/vtable-calendar/package.json b/packages/vtable-calendar/package.json index 692dbcb706..cb36bf2a42 100644 --- a/packages/vtable-calendar/package.json +++ b/packages/vtable-calendar/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vtable-calendar", - "version": "1.17.4", + "version": "1.17.5", "description": "The calendar component of VTable", "author": { "name": "VisActor", diff --git a/packages/vtable-editors/package.json b/packages/vtable-editors/package.json index a74a691300..d42b98b5e9 100644 --- a/packages/vtable-editors/package.json +++ b/packages/vtable-editors/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vtable-editors", - "version": "1.17.4", + "version": "1.17.5", "description": "", "sideEffects": false, "main": "cjs/index.js", diff --git a/packages/vtable-export/package.json b/packages/vtable-export/package.json index 2fbf6f1c89..4e7ba0909e 100644 --- a/packages/vtable-export/package.json +++ b/packages/vtable-export/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vtable-export", - "version": "1.17.4", + "version": "1.17.5", "description": "The export util of VTable", "author": { "name": "VisActor", diff --git a/packages/vtable-gantt/package.json b/packages/vtable-gantt/package.json index a745bec73e..bde6885b0b 100644 --- a/packages/vtable-gantt/package.json +++ b/packages/vtable-gantt/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vtable-gantt", - "version": "1.17.4", + "version": "1.17.5", "description": "canvas table width high performance", "keywords": [ "vtable-gantt", diff --git a/packages/vtable-gantt/src/Gantt.ts b/packages/vtable-gantt/src/Gantt.ts index 060e6baa52..4abe2c78f2 100644 --- a/packages/vtable-gantt/src/Gantt.ts +++ b/packages/vtable-gantt/src/Gantt.ts @@ -782,6 +782,13 @@ export class Gantt extends EventTarget { // index + this.taskListTableInstance.columnHeaderLevelCount // ); // } + // if (this.taskListTableInstance.rowHierarchyType === 'tree' && typeof index === 'number') { + // //如果是树形结构 需要获取数据源对应的索引 + // index = this.taskListTableInstance.getRecordIndexByCell( + // 0, + // index + this.taskListTableInstance.columnHeaderLevelCount + // ); + // } this.taskListTableInstance.updateRecords([record], [index]); } /** @@ -935,11 +942,20 @@ export class Gantt extends EventTarget { if (Array.isArray(task_index)) { const index = (task_index as number[])[0]; const sub_index = (task_index as number[])[1]; - this._updateRecordToListTable(record, isValid(sub_index) ? [index, sub_index] : index); + // this._updateRecordToListTable(record, isValid(sub_index) ? [index, sub_index] : index); + this._updateRecordToListTable(record, task_index); this._refreshTaskBar(index, sub_index); return; } const index = task_index as number; + + // if (this.taskListTableInstance.rowHierarchyType === 'tree' && typeof index === 'number') { + // //如果是树形结构 需要获取数据源对应的索引 + // index = this.taskListTableInstance.getRecordIndexByCell( + // 0, + // index + this.taskListTableInstance.columnHeaderLevelCount + // ); + // } this._updateRecordToListTable(record, index); this._refreshTaskBar(index, undefined); } diff --git a/packages/vtable-gantt/src/event/scroll.ts b/packages/vtable-gantt/src/event/scroll.ts index ce6d419581..91cd0c239a 100644 --- a/packages/vtable-gantt/src/event/scroll.ts +++ b/packages/vtable-gantt/src/event/scroll.ts @@ -41,7 +41,7 @@ export function handleWhell( } isWheelEvent && state.resetInteractionState(); if ( - event.cancelable && + event.nativeEvent?.cancelable && (state._gantt.parsedOptions.overscrollBehavior === 'none' || (Math.abs(deltaY) >= Math.abs(deltaX) && deltaY !== 0 && isVerticalScrollable(deltaY, state)) || (Math.abs(deltaY) <= Math.abs(deltaX) && deltaX !== 0 && isHorizontalScrollable(deltaX, state))) diff --git a/packages/vtable-gantt/src/tools/util.ts b/packages/vtable-gantt/src/tools/util.ts index 67955bb1e0..21716b6a12 100644 --- a/packages/vtable-gantt/src/tools/util.ts +++ b/packages/vtable-gantt/src/tools/util.ts @@ -704,11 +704,9 @@ export function computeCountToTimeScale( case 'quarter': difference = (adjusted_date.getFullYear() - startDate.getFullYear()) * 4 + - Math.floor(adjusted_date.getMonth() / 3) - - Math.floor(startDate.getMonth() / 3); + (adjusted_date.getMonth() - startDate.getMonth()) / 3; difference += - (adjusted_date.getTime() - startDate.getTime()) / - DayTimes / + (adjusted_date.getDate() - startDate.getDate()) / (3 * new Date(adjusted_date.getFullYear(), adjusted_date.getMonth() + 1, 0).getDate()); break; case 'year': diff --git a/packages/vtable-plugins/package.json b/packages/vtable-plugins/package.json index 84529e7846..4e5b5418e7 100644 --- a/packages/vtable-plugins/package.json +++ b/packages/vtable-plugins/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vtable-plugins", - "version": "1.17.4", + "version": "1.17.5", "description": "The search util of VTable", "author": { "name": "VisActor", diff --git a/packages/vtable-search/package.json b/packages/vtable-search/package.json index db8ad4b5c0..585c9a3457 100644 --- a/packages/vtable-search/package.json +++ b/packages/vtable-search/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vtable-search", - "version": "1.17.4", + "version": "1.17.5", "description": "The search util of VTable", "author": { "name": "VisActor", diff --git a/packages/vtable/CHANGELOG.json b/packages/vtable/CHANGELOG.json index 8df5428172..3d0bb1fbf6 100644 --- a/packages/vtable/CHANGELOG.json +++ b/packages/vtable/CHANGELOG.json @@ -1,6 +1,42 @@ { "name": "@visactor/vtable", "entries": [ + { + "version": "1.17.5", + "tag": "@visactor/vtable_v1.17.5", + "date": "Wed, 02 Apr 2025 09:56:31 GMT", + "comments": { + "none": [ + { + "comment": "refactor: fillHandle function #3582\n\n" + }, + { + "comment": "feat: cell support marked function #3583\n\n" + }, + { + "comment": "fix: gantt scale set quarter parser problem #3612\n\n" + }, + { + "comment": "fix: gantt overscrollBehavior none work #3638\n\n" + }, + { + "comment": "fix: gantt chart updateRecords error when table is tree mode #3639\n\n" + }, + { + "comment": "fix: rowHeight error when set adaptive heightMode #3640\n\n" + }, + { + "comment": "feat: refactor pivotTable corner with no columns or rows case #3653\n\n" + }, + { + "comment": "fix: when set renderChartAsync setRecords api render error #3661\n\n" + }, + { + "comment": "fix: fix merge cell checkbox state update #3668" + } + ] + } + }, { "version": "1.17.4", "tag": "@visactor/vtable_v1.17.4", diff --git a/packages/vtable/CHANGELOG.md b/packages/vtable/CHANGELOG.md index 06e42b2bbe..58f0512af0 100644 --- a/packages/vtable/CHANGELOG.md +++ b/packages/vtable/CHANGELOG.md @@ -1,6 +1,37 @@ # Change Log - @visactor/vtable -This log was last generated on Mon, 31 Mar 2025 12:27:48 GMT and should not be manually modified. +This log was last generated on Wed, 02 Apr 2025 09:56:31 GMT and should not be manually modified. + +## 1.17.5 +Wed, 02 Apr 2025 09:56:31 GMT + +### Updates + +- refactor: fillHandle function #3582 + + +- feat: cell support marked function #3583 + + +- fix: gantt scale set quarter parser problem #3612 + + +- fix: gantt overscrollBehavior none work #3638 + + +- fix: gantt chart updateRecords error when table is tree mode #3639 + + +- fix: rowHeight error when set adaptive heightMode #3640 + + +- feat: refactor pivotTable corner with no columns or rows case #3653 + + +- fix: when set renderChartAsync setRecords api render error #3661 + + +- fix: fix merge cell checkbox state update #3668 ## 1.17.4 Mon, 31 Mar 2025 12:27:48 GMT diff --git a/packages/vtable/examples/interactive/fill-handle.ts b/packages/vtable/examples/interactive/fill-handle.ts index 634f2293b1..9e5831864e 100644 --- a/packages/vtable/examples/interactive/fill-handle.ts +++ b/packages/vtable/examples/interactive/fill-handle.ts @@ -82,7 +82,27 @@ export function createTable() { columns, widthMode: 'standard', excelOptions: { - fillHandle: true + fillHandle: args => { + const { selectRanges, table } = args; + if (selectRanges.length === 1) { + const { start, end } = selectRanges[0]; + console.log('fillHandle', start, end); + const minCol = Math.min(start.col, end.col); + const maxCol = Math.max(start.col, end.col); + const minRow = Math.min(start.row, end.row); + const maxRow = Math.max(start.row, end.row); + //判断start到end 所有单元格有没有不能编辑的 + for (let col = minCol; col <= maxCol; col++) { + for (let row = minRow; row <= maxRow; row++) { + if (row === 2 && col === 2) { + return false; + } + } + } + return true; + } + return false; + } } }; const tableInstance = new VTable.ListTable(document.getElementById(CONTAINER_ID), option); diff --git a/packages/vtable/examples/list/list.ts b/packages/vtable/examples/list/list.ts index 6d63994d65..bf81d019a3 100644 --- a/packages/vtable/examples/list/list.ts +++ b/packages/vtable/examples/list/list.ts @@ -62,7 +62,35 @@ export function createTable() { title: 'email', width: 200, sort: true, + headerStyle: { + marked: { + bgColor: 'red', + shape: 'rect', + position: 'right-top', + size: 10, + offset: 6 + } + }, style: { + marked: args => { + const value = args.value; + if (value === '4@xxx.com') { + return { + bgColor: 'red', + shape: 'rect', + position: 'right-top', + size: 10, + offset: 6 + }; + } + return { + bgColor: 'red', + shape: 'triangle', + position: 'right-top', + size: 10, + offset: 6 + }; + }, underline: true, underlineDash: [2, 0], underlineOffset: 3 @@ -191,6 +219,10 @@ export function createTable() { container: document.getElementById(CONTAINER_ID), emptyTip: true, records, + rowSeriesNumber: { + dragOrder: true + }, + columns: [ ...columns // ...columns, diff --git a/packages/vtable/package.json b/packages/vtable/package.json index b0fc9f4c33..4d21327d7d 100644 --- a/packages/vtable/package.json +++ b/packages/vtable/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vtable", - "version": "1.17.4", + "version": "1.17.5", "description": "canvas table width high performance", "keywords": [ "grid", diff --git a/packages/vtable/src/ListTable.ts b/packages/vtable/src/ListTable.ts index b8e56836c4..fc45ffa453 100644 --- a/packages/vtable/src/ListTable.ts +++ b/packages/vtable/src/ListTable.ts @@ -55,6 +55,7 @@ import { } from './core/record-helper'; import type { IListTreeStickCellPlugin, ListTreeStickCellPlugin } from './plugins/list-tree-stick-cell'; import { fixUpdateRowRange } from './tools/update-row'; +import { clearChartRenderQueue } from './scenegraph/graphic/contributions/chart-render-helper'; // import { // registerAxis, // registerEmptyTip, @@ -1158,6 +1159,7 @@ export class ListTable extends BaseTable implements ListTableAPI { * @param option 附近参数,其中的sortState为排序状态,如果设置null 将清除目前的排序状态 */ setRecords(records: Array, option?: { sortState?: SortState | SortState[] | null }): void { + clearChartRenderQueue(); // 释放事件 及 对象 this.internalProps.dataSource?.release(); // 过滤掉dataSource的引用 diff --git a/packages/vtable/src/PivotChart.ts b/packages/vtable/src/PivotChart.ts index 8f06adfca3..24ca9d3c1a 100644 --- a/packages/vtable/src/PivotChart.ts +++ b/packages/vtable/src/PivotChart.ts @@ -73,7 +73,7 @@ import { registerVideoCell } from './scenegraph/group-creater/cell-type'; import { hasLinearAxis } from './layout/chart-helper/get-axis-config'; -import { cacheStageCanvas } from './scenegraph/graphic/contributions/chart-render-helper'; +import { cacheStageCanvas, clearChartRenderQueue } from './scenegraph/graphic/contributions/chart-render-helper'; registerAxis(); registerEmptyTip(); @@ -1529,6 +1529,8 @@ export class PivotChart extends BaseTable implements PivotChartAPI { * @param sort */ setRecords(records: Array): void { + this.internalProps.layoutMap.release(); + clearChartRenderQueue(); const oldHoverState = { col: this.stateManager.hover.cellPos.col, row: this.stateManager.hover.cellPos.row }; this.options.records = this.internalProps.records = records; const options = this.options; diff --git a/packages/vtable/src/PivotTable.ts b/packages/vtable/src/PivotTable.ts index 2d9324adb5..970ad2f0d3 100644 --- a/packages/vtable/src/PivotTable.ts +++ b/packages/vtable/src/PivotTable.ts @@ -53,6 +53,7 @@ import { import type { IEmptyTipComponent } from './components/empty-tip/empty-tip'; import { Factory } from './core/factory'; import { callUpdateColOnScenegraph, callUpdateRowOnScenegraph } from './tools/diff-cell'; +import { clearChartRenderQueue } from './scenegraph/graphic/contributions/chart-render-helper'; export class PivotTable extends BaseTable implements PivotTableAPI { layoutNodeId: { seqId: number } = { seqId: 0 }; @@ -1697,6 +1698,7 @@ export class PivotTable extends BaseTable implements PivotTableAPI { * @param sort */ setRecords(records: Array): void { + clearChartRenderQueue(); const oldHoverState = { col: this.stateManager.hover.cellPos.col, row: this.stateManager.hover.cellPos.row }; this.options.records = this.internalProps.records = records; this.internalProps.recordsIsTwoDimensionalArray = false; diff --git a/packages/vtable/src/core/BaseTable.ts b/packages/vtable/src/core/BaseTable.ts index e0c4ea6c3c..35ed9a2ad5 100644 --- a/packages/vtable/src/core/BaseTable.ts +++ b/packages/vtable/src/core/BaseTable.ts @@ -121,7 +121,11 @@ import type { CreateLegend } from '../components/legend/create-legend'; import type { DataSet } from '@visactor/vdataset'; import { Title } from '../components/title/title'; import type { Chart } from '../scenegraph/graphic/chart'; -import { setBatchRenderChartCount } from '../scenegraph/graphic/contributions/chart-render-helper'; +import { + chartRenderQueueList, + clearChartRenderQueue, + setBatchRenderChartCount +} from '../scenegraph/graphic/contributions/chart-render-helper'; import { isLeftOrRightAxis, isTopOrBottomAxis } from '../layout/chart-helper/get-axis-config'; import { NumberRangeMap } from '../layout/row-height-map'; import { ListTable } from '../ListTable'; @@ -1184,7 +1188,7 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { } get rowHierarchyType(): 'grid' | 'tree' | 'grid-tree' { - return 'grid'; + return this.dataSource.rowHierarchyType; } // /** @@ -2316,6 +2320,7 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { this.internalProps = null; this.reactCustomLayout?.clearCache(); + clearChartRenderQueue(); } fireListeners( @@ -2486,6 +2491,7 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { internalProps.emptyTip?.release(); internalProps.emptyTip = null; internalProps.layoutMap.release(); + clearChartRenderQueue(); this.scenegraph.clearCells(); this.scenegraph.updateComponent(); this.stateManager.updateOptionSetState(); diff --git a/packages/vtable/src/event/event.ts b/packages/vtable/src/event/event.ts index bb1c473f43..3a6f4126b8 100644 --- a/packages/vtable/src/event/event.ts +++ b/packages/vtable/src/event/event.ts @@ -530,9 +530,17 @@ export class EventManager { } return false; } - checkCellFillhandle(eventArgsSet: SceneEvent, update?: boolean): boolean { - if (this.table.options.excelOptions?.fillHandle) { + let isFillHandle = false; + if (typeof this.table.options.excelOptions?.fillHandle === 'function') { + isFillHandle = this.table.options.excelOptions.fillHandle({ + selectRanges: this.table.stateManager.select.ranges, + table: this.table + }); + } else { + isFillHandle = this.table.options.excelOptions?.fillHandle; + } + if (isFillHandle) { const { eventArgs } = eventArgsSet; if (eventArgs) { if (this.table.stateManager.select?.ranges?.length) { @@ -544,11 +552,31 @@ export class EventManager { this.table.stateManager.select.ranges[this.table.stateManager.select.ranges.length - 1].start.row, this.table.stateManager.select.ranges[this.table.stateManager.select.ranges.length - 1].end.row ); - - const lastCellBound = this.table.scenegraph.highPerformanceGetCell(lastCol, lastRow).globalAABBBounds; - // 计算鼠标与fillhandle矩形中心之间的距离 + const startCol = Math.min( + this.table.stateManager.select.ranges[this.table.stateManager.select.ranges.length - 1].start.col, + this.table.stateManager.select.ranges[this.table.stateManager.select.ranges.length - 1].end.col + ); + const startRow = Math.min( + this.table.stateManager.select.ranges[this.table.stateManager.select.ranges.length - 1].start.row, + this.table.stateManager.select.ranges[this.table.stateManager.select.ranges.length - 1].end.row + ); + // 计算鼠标与fillhandle矩形中心之间的距离 distanceX 和 distanceY + // 考虑最后一行和最后一列的特殊情况 + let lastCellBound; + if (lastCol < this.table.colCount - 1) { + lastCellBound = this.table.scenegraph.highPerformanceGetCell(lastCol, lastRow).globalAABBBounds; + } else { + lastCellBound = this.table.scenegraph.highPerformanceGetCell(startCol - 1, lastRow).globalAABBBounds; + } const distanceX = Math.abs(eventArgsSet.abstractPos.x - lastCellBound.x2); + + if (lastRow < this.table.rowCount - 1) { + lastCellBound = this.table.scenegraph.highPerformanceGetCell(lastCol, lastRow).globalAABBBounds; + } else { + lastCellBound = this.table.scenegraph.highPerformanceGetCell(lastCol, startRow - 1).globalAABBBounds; + } const distanceY = Math.abs(eventArgsSet.abstractPos.y - lastCellBound.y2); + const squareSize = 6 * 3; // 判断鼠标是否落在fillhandle矩形内 if ( diff --git a/packages/vtable/src/layout/pivot-header-layout.ts b/packages/vtable/src/layout/pivot-header-layout.ts index 4998086d7e..007bb98f84 100644 --- a/packages/vtable/src/layout/pivot-header-layout.ts +++ b/packages/vtable/src/layout/pivot-header-layout.ts @@ -798,6 +798,9 @@ export class PivotHeaderLayoutMap implements LayoutMapAPI { this.rowHierarchyType === 'grid-tree' ? this._getRowHeaderTreeExpandedMaxLevelCount() || this.rowHeaderLevelCount : this.rowHeaderLevelCount; + if (colLevelCount === 0 || rowLevelCount === 0) { + return results; + } if (this.cornerSetting.titleOnDimension === 'all') { if (this.indicatorsAsCol) { if (colDimensionKeys) { @@ -1581,7 +1584,10 @@ export class PivotHeaderLayoutMap implements LayoutMapAPI { !this.dataset.customColTree?.length //根据情况来加的判断条件 之前是只兼容没有设置两个自定义树的情况 现在对有自定义树的情况也处理出现角头 // && !this.dataset.customRowTree?.length ) { - if (this.cornerSetting.titleOnDimension === 'row' && this.cornerSetting.forceShowHeader) { + if ( + (this.cornerSetting.titleOnDimension === 'row' || this.cornerSetting.titleOnDimension === 'all') && + this.cornerSetting.forceShowHeader + ) { count = 1; } else if ( !this._table.isPivotChart() && @@ -1658,7 +1664,10 @@ export class PivotHeaderLayoutMap implements LayoutMapAPI { // && !this.dataset.customColTree !this.dataset.customRowTree?.length //根据情况来加的判断条件 之前是只兼容没有设置两个自定义树的情况 现在对有自定义树的情况也处理出现角头 ) { - if (this.cornerSetting.titleOnDimension === 'column' && this.cornerSetting.forceShowHeader) { + if ( + (this.cornerSetting.titleOnDimension === 'column' || this.cornerSetting.titleOnDimension === 'all') && + this.cornerSetting.forceShowHeader + ) { count = 1; } else if ( !this._table.isPivotChart() && @@ -1714,17 +1723,20 @@ export class PivotHeaderLayoutMap implements LayoutMapAPI { this._rowHeaderLevelCount = count; } get colCount(): number { - return ( - (this._getColumnHeaderTreeExpandedMaxLevelCount() > 0 || + let bodyColCount; + if ( + this._getColumnHeaderTreeExpandedMaxLevelCount() > 0 || this._table.isPivotChart() || (this.dataset.records as Array)?.length > 0 || (this.dataset.records && !Array.isArray(this.dataset.records)) - ? this._columnHeaderCellIds[0]?.length ?? this.columnDimensionTree.tree.size - : 0) + - this.rowHeaderLevelCount + - this.rightHeaderColCount + - this.leftRowSeriesNumberColumnCount - ); // 小心rightFrozenColCount和colCount的循环引用 造成调用栈溢出 + ) { + bodyColCount = + (this._columnHeaderCellIds[0]?.length ?? this.columnDimensionTree.tree.size) || + (this._indicators?.length > 0 ? 1 : 0); + } else { + bodyColCount = 0; + } + return bodyColCount + this.rowHeaderLevelCount + this.rightHeaderColCount + this.leftRowSeriesNumberColumnCount; // 小心rightFrozenColCount和colCount的循环引用 造成调用栈溢出 } get rowCount(): number { return ( @@ -1740,7 +1752,6 @@ export class PivotHeaderLayoutMap implements LayoutMapAPI { this.columnHeaderLevelCount + this.bottomHeaderRowCount // 小心bottomFrozenRowCount和rowCount的循环引用 造成调用栈溢出 ); - // return (this._rowHeaderCellIds?.length ?? 0) + this.columnHeaderLevelCount + this.bottomFrozenRowCount; } get bodyRowSpanCount() { return this.rowDimensionTree.tree.size; diff --git a/packages/vtable/src/layout/tree-helper.ts b/packages/vtable/src/layout/tree-helper.ts index 054afadaa6..8802e553e8 100644 --- a/packages/vtable/src/layout/tree-helper.ts +++ b/packages/vtable/src/layout/tree-helper.ts @@ -169,7 +169,7 @@ export class DimensionTree { size += this.setTreeNode(n, size, node); }); } else { - size = 1; + node.level === -1 ? (size = 0) : (size = 1); // re.totalLevel = Math.max(re.totalLevel, (node.level ?? -1) + 1); } } else if (node.hierarchyState === HierarchyState.expand && children?.length >= 1) { @@ -224,7 +224,7 @@ export class DimensionTree { } else { //树形展示 无children子节点。但不能确定是最后一层的叶子节点 totalLevel还不能确定是计算完整棵树的整体深度 node.hierarchyState = HierarchyState.none; - size = 1; + node.level === -1 ? (size = 0) : (size = 1); } node.size = size; diff --git a/packages/vtable/src/scenegraph/graphic/contributions/chart-render-helper.ts b/packages/vtable/src/scenegraph/graphic/contributions/chart-render-helper.ts index 633bd1e384..44fae47464 100644 --- a/packages/vtable/src/scenegraph/graphic/contributions/chart-render-helper.ts +++ b/packages/vtable/src/scenegraph/graphic/contributions/chart-render-helper.ts @@ -4,19 +4,26 @@ import type { IAABBBounds } from '@visactor/vutils'; import { Bounds, isValid } from '@visactor/vutils'; import type { BaseTableAPI } from '../../../ts-types/base-table'; export const cancelRenderChartQueue = false; -export const chartRenderKeys: string[] = []; -export const chartRenderQueueList: Chart[] = []; +export let chartRenderKeys: string[] = []; +export let chartRenderQueueList: Chart[] = []; interface chartRenderQueueItem { chart: Chart; } //每次消费的图表数量 let batchRenderChartCount = 5; let isHandlingChartQueue = false; +let requestAnimationFrameId: number; export function setBatchRenderChartCount(count: number) { if (isValid(count)) { batchRenderChartCount = count; } } +export function clearChartRenderQueue() { + chartRenderKeys = []; + chartRenderQueueList = []; + isHandlingChartQueue = false; + cancelAnimationFrame(requestAnimationFrameId); +} export function IsHandlingChartQueue() { return isHandlingChartQueue; } @@ -175,7 +182,7 @@ export function startRenderChartQueue(table: any) { if (chartRenderQueueList.length > 0) { // 使用 requestAnimationFrame 或 setTimeout 来调度下一批图表的渲染 // requestAnimationFrame(() => renderChartQueue(table)); - requestAnimationFrame(() => { + requestAnimationFrameId = requestAnimationFrame(() => { // 从集合中获取要渲染的图表上下文 const chartsToRender = chartRenderQueueList.splice(0, batchRenderChartCount); chartRenderKeys.splice(0, batchRenderChartCount); diff --git a/packages/vtable/src/scenegraph/graphic/mark.ts b/packages/vtable/src/scenegraph/graphic/mark.ts new file mode 100644 index 0000000000..c4a4599250 --- /dev/null +++ b/packages/vtable/src/scenegraph/graphic/mark.ts @@ -0,0 +1,142 @@ +import { createRect, createArc, createPolygon } from '@src/vrender'; + +import type { Group, IFillType } from '@src/vrender'; + +import type { MarkCellStyle, MarkedPropertyDefine } from '../../ts-types'; +import type { BaseTableAPI } from '../../ts-types/base-table'; + +export function createMark(marked: MarkedPropertyDefine, cellGroup: Group, table: BaseTableAPI) { + if (typeof marked === 'boolean') { + // 默认是右上角显示个扇形 + const mark = createArc({ + x: cellGroup.attribute.width, + y: 0, + startAngle: Math.PI / 2, + endAngle: Math.PI, + outerRadius: 6, + fill: '#3073F2', + pickable: false + }); + mark.name = 'mark'; + + cellGroup.appendChild(mark); + } else { + const { + bgColor = '#3073F2', + shape = 'sector', + position = 'right-top', + size = 10, + offset = 0 + } = marked as MarkCellStyle; + let x; + let y; + let startAngle; + let endAngle; + let fill; + let mark; + + if (shape === 'sector') { + if (position === 'right-top') { + x = cellGroup.attribute.width - offset; + y = offset; + startAngle = Math.PI / 2; + endAngle = Math.PI; + } else if (position === 'left-top') { + x = offset; + y = offset; + startAngle = 0; + endAngle = Math.PI / 2; + } else if (position === 'right-bottom') { + x = cellGroup.attribute.width - offset; + y = cellGroup.attribute.height - offset; + startAngle = Math.PI; + endAngle = (Math.PI / 2) * 3; + } else if (position === 'left-bottom') { + x = offset; + y = cellGroup.attribute.height - offset; + startAngle = (Math.PI / 2) * 3; + endAngle = Math.PI * 2; + } + + fill = bgColor; + mark = createArc({ + x, + y, + startAngle, + endAngle, + outerRadius: size, + fill: fill as IFillType, + pickable: false + }); + } else if (shape === 'triangle') { + let x2; + let y2; + let x3; + let y3; + if (position === 'right-top') { + x = cellGroup.attribute.width - offset; + y = offset; + x2 = x - size; + y2 = y; + x3 = x; + y3 = y + size; + } else if (position === 'left-top') { + x = offset; + y = offset; + x2 = x + size; + y2 = y; + x3 = x; + y3 = y + size; + } else if (position === 'right-bottom') { + x = cellGroup.attribute.width - offset; + y = cellGroup.attribute.height - offset; + x2 = x - size; + y2 = y; + x3 = x; + y3 = y - size; + } else if (position === 'left-bottom') { + x = offset; + y = cellGroup.attribute.height - offset; + x2 = x + size; + y2 = y; + x3 = x; + y3 = y - size; + } + fill = bgColor; + mark = createPolygon({ + points: [ + { x: x, y: y }, + { x: x2, y: y2 }, + { x: x3, y: y3 } + ], + fill: fill as IFillType, + pickable: false + }); + } else if (shape === 'rect') { + if (position === 'right-top') { + x = cellGroup.attribute.width - size - offset; + y = offset; + } else if (position === 'left-top') { + x = offset; + y = offset; + } else if (position === 'right-bottom') { + x = cellGroup.attribute.width - size - offset; + y = cellGroup.attribute.height - size - offset; + } else if (position === 'left-bottom') { + x = offset; + y = cellGroup.attribute.height - size - offset; + } + fill = bgColor; + mark = createRect({ + x, + y, + width: size, + height: size, + fill: fill as IFillType, + pickable: false + }); + } + mark.name = 'mark'; + cellGroup.appendChild(mark); + } +} diff --git a/packages/vtable/src/scenegraph/group-creater/cell-helper.ts b/packages/vtable/src/scenegraph/group-creater/cell-helper.ts index 739c412b53..82013fe443 100644 --- a/packages/vtable/src/scenegraph/group-creater/cell-helper.ts +++ b/packages/vtable/src/scenegraph/group-creater/cell-helper.ts @@ -460,7 +460,8 @@ function _generateCustomElementsGroup( // custom merge custom render customElementsGroup = customResult.elementsGroup; renderDefault = customResult.renderDefault; - } else if (range?.isCustom) { + } else if (range?.isCustom && !table.isCornerHeader(col, row)) { + // 判断不是角头单元格,来兼容corner中设置的customLayout // custom merge && no custom render // do not use column custom render } else { diff --git a/packages/vtable/src/scenegraph/group-creater/cell-type/text-cell.ts b/packages/vtable/src/scenegraph/group-creater/cell-type/text-cell.ts index bd801eb43f..95d79da0a2 100644 --- a/packages/vtable/src/scenegraph/group-creater/cell-type/text-cell.ts +++ b/packages/vtable/src/scenegraph/group-creater/cell-type/text-cell.ts @@ -1,15 +1,14 @@ /* eslint-disable no-undef */ import type { IThemeSpec, Group as VGroup } from '@src/vrender'; -import { createArc } from '@src/vrender'; import { isValid } from '@visactor/vutils'; import { Group } from '../../graphic/group'; // import { parseFont } from '../../utils/font'; import { getFunctionalProp } from '../../utils/get-prop'; import { createCellContent } from '../../utils/text-icon-layout'; import type { BaseTableAPI } from '../../../ts-types/base-table'; -import { getStyleTheme } from '../../../core/tableHelper'; import type { CellRange } from '../../../ts-types'; import { getCellBorderStrokeWidth } from '../../utils/cell-border-stroke-width'; +import { createMark } from '../../graphic/mark'; /** * @description: 创建单元格场景节点 @@ -155,18 +154,7 @@ export function createCellGroup( ); if ((cellTheme as any)?._vtable?.marked) { - const mark = createArc({ - x: cellGroup.attribute.width, - y: 0, - startAngle: Math.PI / 2, - endAngle: Math.PI, - outerRadius: 6, - fill: '#3073F2', - pickable: false - }); - mark.name = 'mark'; - - cellGroup.appendChild(mark); + createMark((cellTheme as any)?._vtable?.marked, cellGroup, table); } } if (customElementsGroup) { diff --git a/packages/vtable/src/scenegraph/group-creater/progress/proxy.ts b/packages/vtable/src/scenegraph/group-creater/progress/proxy.ts index c1751950b3..c39dc285e7 100644 --- a/packages/vtable/src/scenegraph/group-creater/progress/proxy.ts +++ b/packages/vtable/src/scenegraph/group-creater/progress/proxy.ts @@ -95,6 +95,12 @@ export class SceneProxy { if (this.table.options.maintainedColumnCount) { this.colLimit = this.table.options.maintainedColumnCount; } + if (this.table.heightMode === 'adaptive') { + this.rowLimit = this.table.rowCount; + } + if (this.table.widthMode === 'adaptive') { + this.colLimit = this.table.colCount; + } } get bodyLeftCol(): number { @@ -302,7 +308,9 @@ export class SceneProxy { createRowCellGroup(onceCount: number) { const endRow = Math.min(this.totalRow, this.currentRow + onceCount); // compute rows height - computeRowsHeight(this.table, this.currentRow + 1, endRow, false); + if (this.table.heightMode !== 'adaptive') { + computeRowsHeight(this.table, this.currentRow + 1, endRow, false); + } this.rowEnd = endRow; diff --git a/packages/vtable/src/scenegraph/scenegraph.ts b/packages/vtable/src/scenegraph/scenegraph.ts index c598986167..65f288fa19 100644 --- a/packages/vtable/src/scenegraph/scenegraph.ts +++ b/packages/vtable/src/scenegraph/scenegraph.ts @@ -1233,8 +1233,10 @@ export class Scenegraph { */ setBodyAndRowHeaderY(y: number) { // correct y, avoid scroll out of range - const firstBodyCell = this.bodyGroup.firstChild?.firstChild as Group; - const lastBodyCell = this.bodyGroup.firstChild?.lastChild as Group; + const firstBodyCell = + (this.bodyGroup.firstChild?.firstChild as Group) ?? (this.rowHeaderGroup.firstChild?.firstChild as Group); + const lastBodyCell = + (this.bodyGroup.firstChild?.lastChild as Group) ?? (this.rowHeaderGroup.firstChild?.lastChild as Group); if ( y === 0 && firstBodyCell && diff --git a/packages/vtable/src/scenegraph/select/update-select-border.ts b/packages/vtable/src/scenegraph/select/update-select-border.ts index 913aeaa3cc..10e6e96e57 100644 --- a/packages/vtable/src/scenegraph/select/update-select-border.ts +++ b/packages/vtable/src/scenegraph/select/update-select-border.ts @@ -94,10 +94,6 @@ function updateComponent( computeRectCellRangeStartCol, computeRectCellRangeStartRow ).globalAABBBounds; - const lastCellBound = scene.highPerformanceGetCell( - computeRectCellRangeEndCol, - computeRectCellRangeEndRow - ).globalAABBBounds; selectComp.rect.setAttributes({ x: firstCellBound.x1 - scene.tableGroup.attribute.x, //坐标xy在下面的逻辑中会做适当调整 @@ -107,12 +103,50 @@ function updateComponent( visible: true }); if (selectComp.fillhandle) { + const fillHandle = scene.table.options.excelOptions?.fillHandle; + let visible = true; + if (typeof fillHandle === 'function') { + visible = fillHandle({ selectRanges: scene.table.stateManager.select.ranges, table: scene.table }); + } + //#region 计算填充柄小方块的位置 + + let lastCellBound; + //当选择区域没有到到最后一列时 + if (computeRectCellRangeEndCol < table.colCount - 1) { + lastCellBound = scene.highPerformanceGetCell( + computeRectCellRangeEndCol, + computeRectCellRangeEndRow + ).globalAABBBounds; + } else { + // 最后一列 + lastCellBound = scene.highPerformanceGetCell( + computeRectCellRangeStartCol - 1, + computeRectCellRangeEndRow + ).globalAABBBounds; + } + const handlerX = lastCellBound.x2 - scene.tableGroup.attribute.x - 3; + //当选择区域没有到到最后一行时 + if (computeRectCellRangeEndRow < table.rowCount - 1) { + lastCellBound = scene.highPerformanceGetCell( + computeRectCellRangeEndCol, + computeRectCellRangeEndRow + ).globalAABBBounds; + } else { + // 最后一行 + lastCellBound = scene.highPerformanceGetCell( + computeRectCellRangeEndCol, + computeRectCellRangeStartRow - 1 + ).globalAABBBounds; + } + const handlerY = lastCellBound.y2 - scene.tableGroup.attribute.y - 3; + //#endregion + selectComp.fillhandle?.setAttributes({ - x: lastCellBound.x2 - scene.tableGroup.attribute.x - 3, // 调整小方块位置 - y: lastCellBound.y2 - scene.tableGroup.attribute.y - 3, // 调整小方块位置 + x: handlerX, // 调整小方块位置 + y: handlerY, // 调整小方块位置 width: 6, height: 6, - visible: true + visible }); } diff --git a/packages/vtable/src/state/checkbox/checkbox.ts b/packages/vtable/src/state/checkbox/checkbox.ts index a2b9d3bd55..5e5878da2a 100644 --- a/packages/vtable/src/state/checkbox/checkbox.ts +++ b/packages/vtable/src/state/checkbox/checkbox.ts @@ -12,6 +12,26 @@ export function setCheckedState( field: string | number, checked: boolean | 'indeterminate', state: StateManager +) { + const table = state.table; + const cellRange = table.getCellRange(col, row); + if (cellRange.start.col !== cellRange.end.col || cellRange.start.row !== cellRange.end.row) { + for (let i = cellRange.start.col; i <= cellRange.end.col; i++) { + for (let j = cellRange.start.row; j <= cellRange.end.row; j++) { + setSingleCheckedState(i, j, field, checked, state); + } + } + } else { + setSingleCheckedState(col, row, field, checked, state); + } +} + +function setSingleCheckedState( + col: number, + row: number, + field: string | number, + checked: boolean | 'indeterminate', + state: StateManager ) { const recordIndex = state.table.getRecordShowIndexByCell(col, row); if (recordIndex >= 0) { diff --git a/packages/vtable/src/ts-types/base-table.ts b/packages/vtable/src/ts-types/base-table.ts index 4c78edb138..129ba10239 100644 --- a/packages/vtable/src/ts-types/base-table.ts +++ b/packages/vtable/src/ts-types/base-table.ts @@ -327,7 +327,7 @@ export interface BaseTableConstructorOptions { /** 快捷键功能设置 */ keyboardOptions?: TableKeyboardOptions; excelOptions?: { - fillHandle?: boolean; + fillHandle?: boolean | ((args: { selectRanges: CellRange[]; table: BaseTableAPI }) => boolean); }; /** 事件触发相关设置 */ eventOptions?: TableEventOptions; diff --git a/packages/vtable/src/ts-types/style-define.ts b/packages/vtable/src/ts-types/style-define.ts index 3849bff8d5..3067dedd44 100644 --- a/packages/vtable/src/ts-types/style-define.ts +++ b/packages/vtable/src/ts-types/style-define.ts @@ -74,7 +74,22 @@ export type PaddingsPropertyDefine = | (number | null)[] | ((args: StylePropertyFunctionArg) => (number | null)[]); -export type MarkedPropertyDefine = boolean | ((args: StylePropertyFunctionArg) => boolean); +export type MarkCellStyle = { + /** 标记背景色 默认蓝色*/ + bgColor?: CanvasRenderingContext2D['fillStyle']; + /** 标记形状 默认'sector' */ + shape?: 'rect' | 'triangle' | 'sector'; + /** 标记位置 默认'right-top' */ + position?: 'left-top' | 'left-bottom' | 'right-top' | 'right-bottom'; + /** 标记大小 默认10 */ + size?: number; + /** 标记偏移量 默认0 */ + offset?: number; +}; +export type MarkedPropertyDefine = + | boolean + | MarkCellStyle + | ((args: StylePropertyFunctionArg) => boolean | MarkCellStyle); export type CellStyle = { textAlign: CanvasTextAlign; diff --git a/packages/vue-vtable/package.json b/packages/vue-vtable/package.json index a71e06c60c..a92e1fbc63 100644 --- a/packages/vue-vtable/package.json +++ b/packages/vue-vtable/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vue-vtable", - "version": "1.17.4", + "version": "1.17.5", "description": "The vue version of VTable", "keywords": [ "vue",