Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"comment": "refactor: when has merge cells to delete column #4848\n\n",
"type": "none",
"packageName": "@visactor/vtable"
}
],
"packageName": "@visactor/vtable",
"email": "[email protected]"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"comment": "refactor: update cell merge delete records logic #4848\n\n",
"type": "none",
"packageName": "@visactor/vtable"
}
],
"packageName": "@visactor/vtable",
"email": "[email protected]"
}
32 changes: 32 additions & 0 deletions docs/assets/api/en/methods.md
Original file line number Diff line number Diff line change
Expand Up @@ -2324,3 +2324,35 @@ Usage:
// Collapse all column header tree nodes
tableInstance.collapseAllForColumnTree();
```

## updateCellContent(Function)

Update the content of a single cell. This interface only refreshes the content of the scenegraph node, not rendering. The render() interface will not actively update the content of the scenegraph node.

```ts
/**
* Update the content of a single cell
*/
updateCellContent: (col: number, row: number) => void;
```
## updateCellContentRange(Function)

Update the content of a range of cells. This interface only refreshes the content of the scenegraph node, not rendering. The render() interface will not actively update the content of the scenegraph node.

```ts
/**
* Update the content of a range of cells
*/
updateCellContentRange: (startCol: number, startRow: number, endCol: number, endRow: number) => void;
```

## updateCellContentRanges(Function)

Update the content of a range of cells. This interface only refreshes the content of the scenegraph node, not rendering. The render() interface will not actively update the content of the scenegraph node.

```ts
/**
* Update the content of a range of cells
*/
updateCellContentRanges: (ranges: CellRange[]) => void;
```
32 changes: 32 additions & 0 deletions docs/assets/api/zh/methods.md
Original file line number Diff line number Diff line change
Expand Up @@ -2326,3 +2326,35 @@ tableInstance.expandAllForColumnTree();
// 折叠列表头树的所有节点
tableInstance.collapseAllForColumnTree();
```
## updateCellContent(Function)

更新某个单元格内容. 这个接口仅是刷新场景树节点内容而非渲染。重新渲染接口render()不会主动更新场景树节点内容。

```ts
/**
* 更新某个单元格内容
*/
updateCellContent: (col: number, row: number) => void;
```

## updateCellContentRange(Function)

更新某个区域单元格内容. 这个接口仅是刷新场景树节点内容而非渲染。重新渲染接口render()不会主动更新场景树节点内容。

```ts
/**
* 更新某个区域单元格内容
*/
updateCellContentRange: (startCol: number, startRow: number, endCol: number, endRow: number) => void;
```

## updateCellContentRanges(Function)

更新某个区域单元格内容. 这个接口仅是刷新场景树节点内容而非渲染。重新渲染接口render()不会主动更新场景树节点内容。

```ts
/**
* 更新某个区域单元格内容
*/
updateCellContentRanges: (ranges: CellRange[]) => void;
```
141 changes: 141 additions & 0 deletions packages/vtable-sheet/src/core/WorkSheet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,76 @@ export class WorkSheet extends EventTarget implements IWorkSheetAPI {
// 为了简化,我们假设删除的是连续的行,从最小的索引开始
const minIndex = Math.min(...rowIndexs.flat());
this.vtableSheet.formulaManager.removeRows(sheetKey, minIndex, deletedCount);
// 删除行后,需要更新合并单元格状态
// 完全在删除范围内:删除合并单元格
// 与删除范围有重叠(startRow <= deleteEndIndex && endRow >= minIndex):
// 起始行在删除范围内:移到 minIndex
// 起始行在删除范围之前:保持不变
// 结束行在删除范围内:移到 minIndex - 1
// 结束行在删除范围之后:减去 deletedCount
// 完全在删除范围之后(startRow > deleteEndIndex):起始行和结束行都减去 deletedCount
if (Array.isArray(this.tableInstance.options.customMergeCell)) {
const mergeCellsToRemove: number[] = [];
const deleteEndIndex = minIndex + deletedCount - 1;
const customMergeCellArray = this.tableInstance.options.customMergeCell;
// 需要clone一份mergeCellArray,因为后续会修改mergeCellArray
const cloneMergeCellArray = customMergeCellArray.map(mergeCell => ({
...mergeCell,
range: {
start: { ...mergeCell.range.start },
end: { ...mergeCell.range.end }
}
}));
customMergeCellArray.forEach((mergeCell, index) => {
const startRow = mergeCell.range.start.row;
const endRow = mergeCell.range.end.row;

// 如果合并单元格完全在删除范围内,标记为删除
if (startRow >= minIndex && endRow <= deleteEndIndex) {
mergeCellsToRemove.push(index);
return;
}

// 如果合并单元格与删除范围有重叠
if (startRow <= deleteEndIndex && endRow >= minIndex) {
// 如果起始行在删除范围内,将起始行移到删除范围的起始位置(删除后这个位置不存在,所以移到 minIndex)
if (startRow >= minIndex) {
mergeCell.range.start.row = minIndex;
}
// 如果起始行在删除范围之前,不需要调整(保持不变)

// 如果结束行在删除范围内,将结束行移到删除范围之前
if (endRow <= deleteEndIndex) {
mergeCell.range.end.row = minIndex - 1;
} else {
// 结束行在删除范围之后,需要减去删除的行数
mergeCell.range.end.row -= deletedCount;
}

// 如果调整后起始行大于结束行,标记为删除
if (mergeCell.range.start.row > mergeCell.range.end.row) {
mergeCellsToRemove.push(index);
}
}
// 如果合并单元格完全在删除范围之后,只需要向前移动行索引
else if (startRow > deleteEndIndex) {
mergeCell.range.start.row -= deletedCount;
mergeCell.range.end.row -= deletedCount;
}
});

// 从后往前删除,避免索引变化影响
mergeCellsToRemove
.sort((a, b) => b - a)
.forEach(index => {
customMergeCellArray.splice(index, 1);
});
const updateRanges = cloneMergeCellArray.map(mergeCell => ({
start: { ...mergeCell.range.start },
end: { ...mergeCell.range.end }
}));
(this.tableInstance as any).updateCellContentRange(updateRanges);
}
}
}
// update 事件不需要调整引用,因为只是数据内容变更
Expand Down Expand Up @@ -489,6 +559,77 @@ export class WorkSheet extends EventTarget implements IWorkSheetAPI {
const minIndex = Math.min(...deleteColIndexs.flat());
const deletedCount = deleteColIndexs.length;
this.vtableSheet.formulaManager.removeColumns(sheetKey, minIndex, deletedCount);
// 删除列后,需要更新合并单元格状态
// 完全在删除范围内:删除合并单元格
// 与删除范围有重叠(startCol <= deleteEndIndex && endCol >= minIndex):
// 起始列在删除范围内:移到 minIndex
// 起始列在删除范围之前:保持不变
// 结束列在删除范围内:移到 minIndex - 1
// 结束列在删除范围之后:减去 deletedCount
// 完全在删除范围之后(startCol > deleteEndIndex):起始列和结束列都减去 deletedCount
if (Array.isArray(this.tableInstance.options.customMergeCell)) {
const mergeCellsToRemove: number[] = [];
const deleteEndIndex = minIndex + deletedCount - 1;
const customMergeCellArray = this.tableInstance.options.customMergeCell;
// 需要clone一份mergeCellArray,因为后续会修改mergeCellArray
const cloneMergeCellArray = customMergeCellArray.map(mergeCell => ({
...mergeCell,
range: {
start: { ...mergeCell.range.start },
end: { ...mergeCell.range.end }
}
}));
customMergeCellArray.forEach((mergeCell, index) => {
const startCol = mergeCell.range.start.col;
const endCol = mergeCell.range.end.col;

// 如果合并单元格完全在删除范围内,标记为删除
if (startCol >= minIndex && endCol <= deleteEndIndex) {
mergeCellsToRemove.push(index);
return;
}

// 如果合并单元格与删除范围有重叠
if (startCol <= deleteEndIndex && endCol >= minIndex) {
// 如果起始列在删除范围内,将起始列移到删除范围的起始位置(删除后这个位置不存在,所以移到 minIndex)
if (startCol >= minIndex) {
mergeCell.range.start.col = minIndex;
}
// 如果起始列在删除范围之前,不需要调整(保持不变)

// 如果结束列在删除范围内,将结束列移到删除范围之前
if (endCol <= deleteEndIndex) {
mergeCell.range.end.col = minIndex - 1;
} else {
// 结束列在删除范围之后,需要减去删除的列数
mergeCell.range.end.col -= deletedCount;
}

// 如果调整后起始列大于结束列,标记为删除
if (mergeCell.range.start.col > mergeCell.range.end.col) {
mergeCellsToRemove.push(index);
}
}
// 如果合并单元格完全在删除范围之后,只需要向前移动列索引
else if (startCol > deleteEndIndex) {
mergeCell.range.start.col -= deletedCount;
mergeCell.range.end.col -= deletedCount;
}
});

// 从后往前删除,避免索引变化影响
mergeCellsToRemove
.sort((a, b) => b - a)
.forEach(index => {
customMergeCellArray.splice(index, 1);
});

const updateRanges = cloneMergeCellArray.map(mergeCell => ({
start: { ...mergeCell.range.start },
end: { ...mergeCell.range.end }
}));
(this.tableInstance as any).updateCellContentRange(updateRanges);
}
}
}
// update 事件不需要调整引用,因为只是数据内容变更
Expand Down
2 changes: 1 addition & 1 deletion packages/vtable/examples/list/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ export function createTable() {
};
const tableInstance = new VTable.ListTable(document.getElementById(CONTAINER_ID)!, option);
window.tableInstance = tableInstance;

tableInstance.mergeCells(3, 4, 5, 4);
bindDebugTool(tableInstance.scenegraph.stage, {
customGrapicKeys: ['col', 'row']
});
Expand Down
33 changes: 32 additions & 1 deletion packages/vtable/src/core/BaseTable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3256,6 +3256,8 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI {
this.options.select?.makeSelectCellVisible ?? true,
true
);
//防止触发到pointertap事件执行endSelectCells方法 会导致select.ranges被合并扩大范围
this.stateManager.select.selecting = false;
}
/**
* 拖拽选择列. 当结合插件table-series-number使用时,需要使用这个方法来拖拽选择整列
Expand All @@ -3277,13 +3279,16 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI {
this.options.select?.makeSelectCellVisible ?? true,
true
);
//防止触发到pointertap事件执行endSelectCells方法 会导致select.ranges被合并扩大范围
this.stateManager.select.selecting = false;
}
/**
* 结束拖拽选择列. 当结合插件table-series-number使用时,需要使用这个方法来结束拖拽选择整列或者整行
*/
endDragSelect() {
this.stateManager.updateInteractionState(InteractionState.default);
this.stateManager.endSelectCells(false, false);
//上面方法dragSelectCol和startDragSelectCol方法中已经设置了select.selecting = false,所以这里不需要再调用endSelectCells方法
// this.stateManager.endSelectCells(false, false);
}

/**
Expand Down Expand Up @@ -3317,6 +3322,8 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI {
this.options.select?.makeSelectCellVisible ?? true,
true
);
//防止触发到pointertap事件执行endSelectCells方法 会导致select.ranges被合并扩大范围
this.stateManager.select.selecting = false;
}
/**
* 拖拽选择行. 当结合插件table-series-number使用时,需要使用这个方法来拖拽选择整行
Expand All @@ -3338,6 +3345,8 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI {
this.options.select?.makeSelectCellVisible ?? true,
true
);
//防止触发到pointertap事件执行endSelectCells方法 会导致select.ranges被合并扩大范围
this.stateManager.select.selecting = false;
}

abstract isListTable(): boolean;
Expand Down Expand Up @@ -5028,4 +5037,26 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI {
}
return false;
}
/** 更新某个单元格内容 ,重新渲染不会更新。 这个接口也仅是更新而非渲染*/
updateCellContent(col: number, row: number) {
this.scenegraph.updateCellContent(col, row);
}

/** 更新某个区域单元格内容 ,重新渲染不会更新。 这个接口也仅是更新而非渲染*/
updateCellContentRange(startCol: number, startRow: number, endCol: number, endRow: number) {
for (let i = startCol; i <= endCol; i++) {
for (let j = startRow; j <= endRow; j++) {
this.scenegraph.updateCellContent(i, j);
}
}
}

/** 更新某个区域单元格内容 ,重新渲染不会更新。 这个接口也仅是更新而非渲染*/
updateCellContentRanges(ranges: CellRange[]) {
//ranges中每个range都调用updateCellContent
for (let i = 0; i < ranges.length; i++) {
const range = ranges[i];
this.updateCellContentRange(range.start.col, range.start.row, range.end.col, range.end.row);
}
}
}
5 changes: 3 additions & 2 deletions packages/vtable/src/state/select/update-position.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { isValid } from '@visactor/vutils';
import type { ListTable } from '../..';
import type { SimpleHeaderLayoutMap } from '../../layout';
import type { Scenegraph } from '../../scenegraph/scenegraph';
Expand Down Expand Up @@ -160,7 +161,7 @@ export function updateSelectPosition(
// // 更新select border
// scenegraph.updateCellSelectBorder(cellPos);
} else {
let extendSelectRange = true;
let extendSelectRange = isValid(skipBodyMerge) ? !skipBodyMerge : true;
// 单选或多选开始
if (cellPos.col !== -1 && cellPos.row !== -1 && !enableCtrlSelectMode) {
state.select.ranges = [];
Expand Down Expand Up @@ -349,7 +350,7 @@ export function updateSelectPosition(
(interactionState === InteractionState.grabing || table.eventManager.isDraging) &&
!table.stateManager.isResizeCol()
) {
let extendSelectRange = true;
let extendSelectRange = isValid(skipBodyMerge) ? !skipBodyMerge : true;
// 可能有cellPosStart从-1开始grabing的情况
if (cellPos.col === -1) {
cellPos.col = col;
Expand Down
3 changes: 3 additions & 0 deletions packages/vtable/src/ts-types/base-table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1072,6 +1072,9 @@ export interface BaseTableAPI {
_getComputedFrozenColCount: (frozenColCount: number) => number;
isColumnSelected: (col: number) => boolean;
isRowSelected: (row: number) => boolean;
updateCellContentRanges: (ranges: CellRange[]) => void;
updateCellContent: (col: number, row: number) => void;
updateCellContentRange: (startCol: number, startRow: number, endCol: number, endRow: number) => void;
}
export interface ListTableProtected extends IBaseTableProtected {
/** 表格数据 */
Expand Down
Loading