From 5dc5b8f925fd78557c9a058f48dd4e01a8772ee8 Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Wed, 29 Jul 2026 20:39:40 +0800 Subject: [PATCH 01/12] fix(vtable): avoid frozen row height duplication --- .../debug/issue-4904-frozen-row-gap.ts | 90 +++++++++++++++++++ packages/vtable/examples/menu.ts | 4 + packages/vtable/src/scenegraph/scenegraph.ts | 6 +- 3 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 packages/vtable/examples/debug/issue-4904-frozen-row-gap.ts diff --git a/packages/vtable/examples/debug/issue-4904-frozen-row-gap.ts b/packages/vtable/examples/debug/issue-4904-frozen-row-gap.ts new file mode 100644 index 0000000000..34bd29c2b0 --- /dev/null +++ b/packages/vtable/examples/debug/issue-4904-frozen-row-gap.ts @@ -0,0 +1,90 @@ +import * as VTable from '../../src'; + +const CONTAINER_ID = 'vTable'; + +const generatePersons = (count: number) => { + return Array.from(new Array(count)).map((_, i) => ({ + id: i + 1, + email1: `${i + 1}@xxx.com`, + name: `小明${i + 1}`, + lastName: '王', + date1: '2022年9月1日', + tel: '000-0000-0000', + sex: i % 2 === 0 ? 'boy' : 'girl', + work: i % 2 === 0 ? 'back-end engineer' : 'front-end engineer', + city: 'beijing' + })); +}; + +export function createTable() { + const container = document.getElementById(CONTAINER_ID)!; + container.style.width = '800px'; + container.style.height = '800px'; + + const records = generatePersons(10); + const columns: VTable.ColumnsDefine = [ + { + field: 'id', + title: 'ID', + width: 120, + sort: true + }, + { + field: 'email1', + title: 'email', + width: 200, + sort: true + }, + { + title: 'full name', + columns: [ + { + field: 'name', + title: 'First Name', + width: 200 + }, + { + field: 'name', + title: 'Last Name', + width: 200 + } + ] + }, + { + field: 'date1', + title: 'birthday', + width: 200 + }, + { + field: 'sex', + title: 'sex', + width: 100 + }, + { + field: 'tel', + title: 'telephone', + width: 150 + }, + { + field: 'work', + title: 'job', + width: 200 + }, + { + field: 'city', + title: 'city', + width: 150 + } + ]; + const option: VTable.ListTableConstructorOptions = { + container, + records, + columns, + frozenRowCount: 5, + bottomFrozenRowCount: 2, + allowFrozenColCount: 2 + }; + + const tableInstance = new VTable.ListTable(option); + window.tableInstance = tableInstance; +} diff --git a/packages/vtable/examples/menu.ts b/packages/vtable/examples/menu.ts index 49090d023b..7921b2ff1a 100644 --- a/packages/vtable/examples/menu.ts +++ b/packages/vtable/examples/menu.ts @@ -70,6 +70,10 @@ export const menus = [ path: 'debug', name: 'issue-4816-functional-icons-theme' }, + { + path: 'debug', + name: 'issue-4904-frozen-row-gap' + }, { path: 'debug', name: 'header-frame-border-null-color' diff --git a/packages/vtable/src/scenegraph/scenegraph.ts b/packages/vtable/src/scenegraph/scenegraph.ts index 0330409fa0..0838225326 100644 --- a/packages/vtable/src/scenegraph/scenegraph.ts +++ b/packages/vtable/src/scenegraph/scenegraph.ts @@ -1316,7 +1316,7 @@ export class Scenegraph { 0 ); - const contentHeight = + let contentHeight = Math.max( this.colHeaderGroup.attribute.height, this.cornerHeaderGroup.attribute.height, @@ -1335,6 +1335,10 @@ export class Scenegraph { this.rightBottomCornerGroup.attribute.height, 0 ); + const allRowsHeight = this.table.getAllRowsHeight(); + if (allRowsHeight > 0) { + contentHeight = allRowsHeight; + } // 处理containerFit模式 let tableWidth = contentWidth; From c94ab2f4cda9fe690deb80b0f8544d6cc796500d Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Thu, 30 Jul 2026 11:10:35 +0800 Subject: [PATCH 02/12] fix(vtable): restore scenegraph visible height calculation --- packages/vtable/src/scenegraph/scenegraph.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/vtable/src/scenegraph/scenegraph.ts b/packages/vtable/src/scenegraph/scenegraph.ts index 0838225326..0330409fa0 100644 --- a/packages/vtable/src/scenegraph/scenegraph.ts +++ b/packages/vtable/src/scenegraph/scenegraph.ts @@ -1316,7 +1316,7 @@ export class Scenegraph { 0 ); - let contentHeight = + const contentHeight = Math.max( this.colHeaderGroup.attribute.height, this.cornerHeaderGroup.attribute.height, @@ -1335,10 +1335,6 @@ export class Scenegraph { this.rightBottomCornerGroup.attribute.height, 0 ); - const allRowsHeight = this.table.getAllRowsHeight(); - if (allRowsHeight > 0) { - contentHeight = allRowsHeight; - } // 处理containerFit模式 let tableWidth = contentWidth; From 550b23d67dd4dede4a1e453fd76048de43a74c44 Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Thu, 30 Jul 2026 14:18:06 +0800 Subject: [PATCH 03/12] fix(vtable): align issue 4904 demo last name field --- packages/vtable/examples/debug/issue-4904-frozen-row-gap.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vtable/examples/debug/issue-4904-frozen-row-gap.ts b/packages/vtable/examples/debug/issue-4904-frozen-row-gap.ts index 34bd29c2b0..22884137d5 100644 --- a/packages/vtable/examples/debug/issue-4904-frozen-row-gap.ts +++ b/packages/vtable/examples/debug/issue-4904-frozen-row-gap.ts @@ -44,7 +44,7 @@ export function createTable() { width: 200 }, { - field: 'name', + field: 'lastName', title: 'Last Name', width: 200 } From 10307a352bf0536fffa1ac23f162442f757b0213 Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Thu, 30 Jul 2026 14:48:00 +0800 Subject: [PATCH 04/12] fix(vtable): keep bottom frozen rows connected for short content --- .../options/listTable-api-with-frozen.test.ts | 23 +++++++++++++++++++ packages/vtable/src/scenegraph/scenegraph.ts | 16 +++++++------ 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/packages/vtable/__tests__/options/listTable-api-with-frozen.test.ts b/packages/vtable/__tests__/options/listTable-api-with-frozen.test.ts index 4a243da287..ddf5d86c9d 100644 --- a/packages/vtable/__tests__/options/listTable-api-with-frozen.test.ts +++ b/packages/vtable/__tests__/options/listTable-api-with-frozen.test.ts @@ -143,6 +143,29 @@ describe('listTable init test', () => { }); }); + test('listTable bottom frozen rows should stay connected after short body content', () => { + const optionWithBottomFrozenRows = { + ...option, + frozenRowCount: 5, + rightFrozenColCount: 0, + bottomFrozenRowCount: 2, + container: createDiv(), + records: records.slice(0, 10) + }; + optionWithBottomFrozenRows.container.style.position = 'relative'; + optionWithBottomFrozenRows.container.style.width = '1000px'; + optionWithBottomFrozenRows.container.style.height = '800px'; + + const frozenTable = new ListTable(optionWithBottomFrozenRows); + const { scenegraph } = frozenTable; + + expect(scenegraph.bottomFrozenGroup.attribute.y).toBe( + scenegraph.bodyGroup.attribute.y + scenegraph.bodyGroup.attribute.height + ); + + frozenTable.release(); + }); + test('listTable should support decreasing rightFrozenColCount by setter with row series number', () => { const optionWithRightFrozen = { ...option, diff --git a/packages/vtable/src/scenegraph/scenegraph.ts b/packages/vtable/src/scenegraph/scenegraph.ts index 0330409fa0..6c28b10fbc 100644 --- a/packages/vtable/src/scenegraph/scenegraph.ts +++ b/packages/vtable/src/scenegraph/scenegraph.ts @@ -1436,20 +1436,22 @@ export class Scenegraph { } if (this.table.bottomFrozenRowCount > 0) { - this.bottomFrozenGroup.setAttribute( - 'y', - this.tableGroup.attribute.height - this.table.getBottomFrozenRowsHeight() + const bottomFrozenRowsHeight = this.table.getBottomFrozenRowsHeight(); + const bottomFrozenY = Math.min( + this.tableGroup.attribute.height - bottomFrozenRowsHeight, + this.bodyGroup.attribute.y + this.bodyGroup.attribute.height ); + this.bottomFrozenGroup.setAttribute('y', bottomFrozenY); this.leftBottomCornerGroup.setAttributes({ visible: true, - y: this.tableGroup.attribute.height - this.table.getBottomFrozenRowsHeight(), - height: this.table.getBottomFrozenRowsHeight(), + y: bottomFrozenY, + height: bottomFrozenRowsHeight, width: this.table.getFrozenColsWidth() }); this.rightBottomCornerGroup.setAttributes({ visible: true, - y: this.tableGroup.attribute.height - this.table.getBottomFrozenRowsHeight(), - height: this.table.getBottomFrozenRowsHeight() + y: bottomFrozenY, + height: bottomFrozenRowsHeight }); } From 6ded357b2d800e7ba54302c1c743cf2282312d24 Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Fri, 31 Jul 2026 10:20:25 +0800 Subject: [PATCH 05/12] fix(vtable): align right frozen area for short content --- .../options/listTable-api-with-frozen.test.ts | 35 +++++++++++++++++++ packages/vtable/src/scenegraph/scenegraph.ts | 15 +++++--- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/packages/vtable/__tests__/options/listTable-api-with-frozen.test.ts b/packages/vtable/__tests__/options/listTable-api-with-frozen.test.ts index ddf5d86c9d..9fc2643e5c 100644 --- a/packages/vtable/__tests__/options/listTable-api-with-frozen.test.ts +++ b/packages/vtable/__tests__/options/listTable-api-with-frozen.test.ts @@ -166,6 +166,41 @@ describe('listTable init test', () => { frozenTable.release(); }); + test('listTable bottom right corner should stay connected after short content', () => { + const shortColumns = columns.slice(0, 5).map(column => ({ + ...column, + width: 100 + })); + const optionWithBottomRightFrozen = { + ...option, + columns: shortColumns, + defaultColWidth: 100, + frozenColCount: 2, + frozenRowCount: 5, + rightFrozenColCount: 2, + bottomFrozenRowCount: 2, + containerFit: true, + container: createDiv(), + records: records.slice(0, 10) + }; + optionWithBottomRightFrozen.container.style.position = 'relative'; + optionWithBottomRightFrozen.container.style.width = '1000px'; + optionWithBottomRightFrozen.container.style.height = '800px'; + + const frozenTable = new ListTable(optionWithBottomRightFrozen); + const { scenegraph } = frozenTable; + + expect(scenegraph.rightFrozenGroup.attribute.x).toBe( + scenegraph.bodyGroup.attribute.x + scenegraph.bodyGroup.attribute.width + ); + expect(scenegraph.rightBottomCornerGroup.attribute.x).toBe(scenegraph.rightFrozenGroup.attribute.x); + expect(scenegraph.rightBottomCornerGroup.attribute.y).toBe( + scenegraph.bodyGroup.attribute.y + scenegraph.bodyGroup.attribute.height + ); + + frozenTable.release(); + }); + test('listTable should support decreasing rightFrozenColCount by setter with row series number', () => { const optionWithRightFrozen = { ...option, diff --git a/packages/vtable/src/scenegraph/scenegraph.ts b/packages/vtable/src/scenegraph/scenegraph.ts index 6c28b10fbc..7158672daa 100644 --- a/packages/vtable/src/scenegraph/scenegraph.ts +++ b/packages/vtable/src/scenegraph/scenegraph.ts @@ -1456,17 +1456,22 @@ export class Scenegraph { } if (this.table.rightFrozenColCount > 0) { - this.rightFrozenGroup.setAttribute('x', this.tableGroup.attribute.width - this.table.getRightFrozenColsWidth()); + const rightFrozenColsWidth = this.table.getRightFrozenColsWidth(); + const rightFrozenX = Math.min( + this.tableGroup.attribute.width - rightFrozenColsWidth, + this.bodyGroup.attribute.x + this.bodyGroup.attribute.width + ); + this.rightFrozenGroup.setAttribute('x', rightFrozenX); this.rightTopCornerGroup.setAttributes({ visible: true, - x: this.tableGroup.attribute.width - this.table.getRightFrozenColsWidth(), - width: this.table.getRightFrozenColsWidth(), + x: rightFrozenX, + width: rightFrozenColsWidth, height: this.table.getFrozenRowsHeight() }); this.rightBottomCornerGroup.setAttributes({ visible: true, - x: this.tableGroup.attribute.width - this.table.getRightFrozenColsWidth(), - width: this.table.getRightFrozenColsWidth() + x: rightFrozenX, + width: rightFrozenColsWidth }); } From a62d79e70e8d904deb78e2180dc37c5df90105d2 Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Fri, 31 Jul 2026 11:16:07 +0800 Subject: [PATCH 06/12] fix(vtable): guard bottom right corner visibility --- .../__tests__/options/listTable-api-with-frozen.test.ts | 1 + packages/vtable/src/scenegraph/scenegraph.ts | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/vtable/__tests__/options/listTable-api-with-frozen.test.ts b/packages/vtable/__tests__/options/listTable-api-with-frozen.test.ts index 9fc2643e5c..c2a052e9f1 100644 --- a/packages/vtable/__tests__/options/listTable-api-with-frozen.test.ts +++ b/packages/vtable/__tests__/options/listTable-api-with-frozen.test.ts @@ -162,6 +162,7 @@ describe('listTable init test', () => { expect(scenegraph.bottomFrozenGroup.attribute.y).toBe( scenegraph.bodyGroup.attribute.y + scenegraph.bodyGroup.attribute.height ); + expect(scenegraph.rightBottomCornerGroup.attribute.visible).toBe(false); frozenTable.release(); }); diff --git a/packages/vtable/src/scenegraph/scenegraph.ts b/packages/vtable/src/scenegraph/scenegraph.ts index 7158672daa..6e8f29373b 100644 --- a/packages/vtable/src/scenegraph/scenegraph.ts +++ b/packages/vtable/src/scenegraph/scenegraph.ts @@ -1449,8 +1449,10 @@ export class Scenegraph { width: this.table.getFrozenColsWidth() }); this.rightBottomCornerGroup.setAttributes({ - visible: true, + visible: this.table.rightFrozenColCount > 0, + x: 0, y: bottomFrozenY, + width: 0, height: bottomFrozenRowsHeight }); } From fcad11f70e141399a56d1fa4d5f20b6dd320dce0 Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Fri, 31 Jul 2026 12:00:03 +0800 Subject: [PATCH 07/12] fix(vtable): hide bottom right corner without bottom frozen rows --- .../vtable/__tests__/options/listTable-api-with-frozen.test.ts | 1 + packages/vtable/src/scenegraph/scenegraph.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/vtable/__tests__/options/listTable-api-with-frozen.test.ts b/packages/vtable/__tests__/options/listTable-api-with-frozen.test.ts index c2a052e9f1..a242a72292 100644 --- a/packages/vtable/__tests__/options/listTable-api-with-frozen.test.ts +++ b/packages/vtable/__tests__/options/listTable-api-with-frozen.test.ts @@ -224,5 +224,6 @@ describe('listTable init test', () => { rightFrozenTable.rightFrozenColCount = 1; }).not.toThrow(); expect(rightFrozenTable.rightFrozenColCount).toBe(1); + expect(rightFrozenTable.scenegraph.rightBottomCornerGroup.attribute.visible).toBe(false); }); }); diff --git a/packages/vtable/src/scenegraph/scenegraph.ts b/packages/vtable/src/scenegraph/scenegraph.ts index 6e8f29373b..58b4a387d2 100644 --- a/packages/vtable/src/scenegraph/scenegraph.ts +++ b/packages/vtable/src/scenegraph/scenegraph.ts @@ -1471,7 +1471,7 @@ export class Scenegraph { height: this.table.getFrozenRowsHeight() }); this.rightBottomCornerGroup.setAttributes({ - visible: true, + visible: this.table.bottomFrozenRowCount > 0, x: rightFrozenX, width: rightFrozenColsWidth }); From a2dd44e7eef83ca08f0c004208a00edb553bd807 Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Fri, 31 Jul 2026 13:08:13 +0800 Subject: [PATCH 08/12] fix(vtable): hide bottom left corner without frozen columns --- .../options/listTable-api-with-frozen.test.ts | 22 +++++++++++++++++++ packages/vtable/src/scenegraph/scenegraph.ts | 4 ++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/packages/vtable/__tests__/options/listTable-api-with-frozen.test.ts b/packages/vtable/__tests__/options/listTable-api-with-frozen.test.ts index a242a72292..2eef2ac6c8 100644 --- a/packages/vtable/__tests__/options/listTable-api-with-frozen.test.ts +++ b/packages/vtable/__tests__/options/listTable-api-with-frozen.test.ts @@ -167,6 +167,28 @@ describe('listTable init test', () => { frozenTable.release(); }); + test('listTable bottom left corner should be hidden without frozen columns', () => { + const optionWithOnlyBottomFrozenRows = { + ...option, + frozenColCount: 0, + rightFrozenColCount: 0, + bottomFrozenRowCount: 2, + container: createDiv(), + records: records.slice(0, 10) + }; + optionWithOnlyBottomFrozenRows.container.style.position = 'relative'; + optionWithOnlyBottomFrozenRows.container.style.width = '1000px'; + optionWithOnlyBottomFrozenRows.container.style.height = '800px'; + + const frozenTable = new ListTable(optionWithOnlyBottomFrozenRows); + + expect(frozenTable.scenegraph.leftBottomCornerGroup.attribute.visible).toBe(false); + expect(frozenTable.scenegraph.leftBottomCornerGroup.attribute.width).toBe(0); + expect(frozenTable.scenegraph.rightBottomCornerGroup.attribute.visible).toBe(false); + + frozenTable.release(); + }); + test('listTable bottom right corner should stay connected after short content', () => { const shortColumns = columns.slice(0, 5).map(column => ({ ...column, diff --git a/packages/vtable/src/scenegraph/scenegraph.ts b/packages/vtable/src/scenegraph/scenegraph.ts index 58b4a387d2..5ae6de3269 100644 --- a/packages/vtable/src/scenegraph/scenegraph.ts +++ b/packages/vtable/src/scenegraph/scenegraph.ts @@ -1443,10 +1443,10 @@ export class Scenegraph { ); this.bottomFrozenGroup.setAttribute('y', bottomFrozenY); this.leftBottomCornerGroup.setAttributes({ - visible: true, + visible: this.table.frozenColCount > 0, y: bottomFrozenY, height: bottomFrozenRowsHeight, - width: this.table.getFrozenColsWidth() + width: this.table.frozenColCount > 0 ? this.table.getFrozenColsWidth() : 0 }); this.rightBottomCornerGroup.setAttributes({ visible: this.table.rightFrozenColCount > 0, From 2d448729974665c73462756b521d19da3c4b2e26 Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Fri, 31 Jul 2026 15:10:36 +0800 Subject: [PATCH 09/12] fix(vtable): reset frozen corner visibility --- .../options/listTable-api-with-frozen.test.ts | 56 +++++++++++++++++++ packages/vtable/src/scenegraph/scenegraph.ts | 43 ++++++++++---- 2 files changed, 89 insertions(+), 10 deletions(-) diff --git a/packages/vtable/__tests__/options/listTable-api-with-frozen.test.ts b/packages/vtable/__tests__/options/listTable-api-with-frozen.test.ts index 2eef2ac6c8..08f9ae1c9b 100644 --- a/packages/vtable/__tests__/options/listTable-api-with-frozen.test.ts +++ b/packages/vtable/__tests__/options/listTable-api-with-frozen.test.ts @@ -247,5 +247,61 @@ describe('listTable init test', () => { }).not.toThrow(); expect(rightFrozenTable.rightFrozenColCount).toBe(1); expect(rightFrozenTable.scenegraph.rightBottomCornerGroup.attribute.visible).toBe(false); + expect(rightFrozenTable.scenegraph.rightBottomCornerGroup.attribute.width).toBe(0); + }); + + test('listTable bottom corner groups should be reset after clearing bottom frozen rows', () => { + const optionWithBottomFrozenRows = { + ...option, + frozenColCount: 2, + rightFrozenColCount: 2, + bottomFrozenRowCount: 2, + container: createDiv(), + records: records.slice(0, 10) + }; + optionWithBottomFrozenRows.container.style.position = 'relative'; + optionWithBottomFrozenRows.container.style.width = '1000px'; + optionWithBottomFrozenRows.container.style.height = '800px'; + + const frozenTable = new ListTable(optionWithBottomFrozenRows); + + expect(frozenTable.scenegraph.leftBottomCornerGroup.attribute.visible).toBe(true); + expect(frozenTable.scenegraph.rightBottomCornerGroup.attribute.visible).toBe(true); + + frozenTable.bottomFrozenRowCount = 0; + + expect(frozenTable.scenegraph.leftBottomCornerGroup.attribute.visible).toBe(false); + expect(frozenTable.scenegraph.leftBottomCornerGroup.attribute.height).toBe(0); + expect(frozenTable.scenegraph.rightBottomCornerGroup.attribute.visible).toBe(false); + expect(frozenTable.scenegraph.rightBottomCornerGroup.attribute.width).toBe(0); + expect(frozenTable.scenegraph.rightBottomCornerGroup.attribute.height).toBe(0); + + frozenTable.release(); + }); + + test('listTable right top corner group should be reset after clearing frozen rows', () => { + const optionWithRightFrozenRows = { + ...option, + frozenRowCount: 5, + bottomFrozenRowCount: 0, + rightFrozenColCount: 2, + container: createDiv(), + records + }; + optionWithRightFrozenRows.container.style.position = 'relative'; + optionWithRightFrozenRows.container.style.width = '1000px'; + optionWithRightFrozenRows.container.style.height = '800px'; + + const frozenTable = new ListTable(optionWithRightFrozenRows); + + expect(frozenTable.scenegraph.rightTopCornerGroup.attribute.visible).toBe(true); + + frozenTable.frozenRowCount = 0; + + expect(frozenTable.scenegraph.rightTopCornerGroup.attribute.visible).toBe(false); + expect(frozenTable.scenegraph.rightTopCornerGroup.attribute.width).toBe(0); + expect(frozenTable.scenegraph.rightTopCornerGroup.attribute.height).toBe(0); + + frozenTable.release(); }); }); diff --git a/packages/vtable/src/scenegraph/scenegraph.ts b/packages/vtable/src/scenegraph/scenegraph.ts index 5ae6de3269..837fde2511 100644 --- a/packages/vtable/src/scenegraph/scenegraph.ts +++ b/packages/vtable/src/scenegraph/scenegraph.ts @@ -1435,7 +1435,12 @@ export class Scenegraph { } } - if (this.table.bottomFrozenRowCount > 0) { + const hasFrozenCols = this.table.frozenColCount > 0; + const hasRightFrozenCols = this.table.rightFrozenColCount > 0; + const hasFrozenRows = this.table.frozenRowCount > 0; + const hasBottomFrozenRows = this.table.bottomFrozenRowCount > 0; + + if (hasBottomFrozenRows) { const bottomFrozenRowsHeight = this.table.getBottomFrozenRowsHeight(); const bottomFrozenY = Math.min( this.tableGroup.attribute.height - bottomFrozenRowsHeight, @@ -1443,21 +1448,32 @@ export class Scenegraph { ); this.bottomFrozenGroup.setAttribute('y', bottomFrozenY); this.leftBottomCornerGroup.setAttributes({ - visible: this.table.frozenColCount > 0, + visible: hasFrozenCols, y: bottomFrozenY, height: bottomFrozenRowsHeight, - width: this.table.frozenColCount > 0 ? this.table.getFrozenColsWidth() : 0 + width: hasFrozenCols ? this.table.getFrozenColsWidth() : 0 }); this.rightBottomCornerGroup.setAttributes({ - visible: this.table.rightFrozenColCount > 0, + visible: hasRightFrozenCols, x: 0, y: bottomFrozenY, width: 0, height: bottomFrozenRowsHeight }); + } else { + this.leftBottomCornerGroup.setAttributes({ + visible: false, + width: 0, + height: 0 + }); + this.rightBottomCornerGroup.setAttributes({ + visible: false, + width: 0, + height: 0 + }); } - if (this.table.rightFrozenColCount > 0) { + if (hasRightFrozenCols) { const rightFrozenColsWidth = this.table.getRightFrozenColsWidth(); const rightFrozenX = Math.min( this.tableGroup.attribute.width - rightFrozenColsWidth, @@ -1465,15 +1481,22 @@ export class Scenegraph { ); this.rightFrozenGroup.setAttribute('x', rightFrozenX); this.rightTopCornerGroup.setAttributes({ - visible: true, + visible: hasFrozenRows, x: rightFrozenX, - width: rightFrozenColsWidth, - height: this.table.getFrozenRowsHeight() + width: hasFrozenRows ? rightFrozenColsWidth : 0, + height: hasFrozenRows ? this.table.getFrozenRowsHeight() : 0 }); this.rightBottomCornerGroup.setAttributes({ - visible: this.table.bottomFrozenRowCount > 0, + visible: hasBottomFrozenRows, x: rightFrozenX, - width: rightFrozenColsWidth + width: hasBottomFrozenRows ? rightFrozenColsWidth : 0, + height: hasBottomFrozenRows ? this.table.getBottomFrozenRowsHeight() : 0 + }); + } else { + this.rightTopCornerGroup.setAttributes({ + visible: false, + width: 0, + height: 0 }); } From 4469bce03fc406216c0b112c3e60606c58747d4f Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Fri, 31 Jul 2026 15:55:09 +0800 Subject: [PATCH 10/12] fix(vtable): keep right frozen header at right edge --- .../options/listTable-api-with-frozen.test.ts | 24 +++++++++++++++++++ packages/vtable/src/scenegraph/scenegraph.ts | 8 ++++--- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/packages/vtable/__tests__/options/listTable-api-with-frozen.test.ts b/packages/vtable/__tests__/options/listTable-api-with-frozen.test.ts index 08f9ae1c9b..7fe91e2b50 100644 --- a/packages/vtable/__tests__/options/listTable-api-with-frozen.test.ts +++ b/packages/vtable/__tests__/options/listTable-api-with-frozen.test.ts @@ -224,6 +224,30 @@ describe('listTable init test', () => { frozenTable.release(); }); + test('listTable right frozen header should stay on the right side without records', () => { + const optionWithRightFrozenHeader = { + ...option, + frozenColCount: 2, + rightFrozenColCount: 2, + bottomFrozenRowCount: 0, + container: createDiv(), + records: [] + }; + optionWithRightFrozenHeader.container.style.position = 'relative'; + optionWithRightFrozenHeader.container.style.width = '1000px'; + optionWithRightFrozenHeader.container.style.height = '800px'; + + const frozenTable = new ListTable(optionWithRightFrozenHeader); + const { scenegraph } = frozenTable; + const rightFrozenColsWidth = frozenTable.getRightFrozenColsWidth(); + + expect(scenegraph.rightTopCornerGroup.attribute.visible).toBe(true); + expect(scenegraph.rightFrozenGroup.attribute.x).toBe(scenegraph.tableGroup.attribute.width - rightFrozenColsWidth); + expect(scenegraph.rightTopCornerGroup.attribute.x).toBe(scenegraph.rightFrozenGroup.attribute.x); + + frozenTable.release(); + }); + test('listTable should support decreasing rightFrozenColCount by setter with row series number', () => { const optionWithRightFrozen = { ...option, diff --git a/packages/vtable/src/scenegraph/scenegraph.ts b/packages/vtable/src/scenegraph/scenegraph.ts index 837fde2511..83e2609cb2 100644 --- a/packages/vtable/src/scenegraph/scenegraph.ts +++ b/packages/vtable/src/scenegraph/scenegraph.ts @@ -1475,10 +1475,12 @@ export class Scenegraph { if (hasRightFrozenCols) { const rightFrozenColsWidth = this.table.getRightFrozenColsWidth(); - const rightFrozenX = Math.min( - this.tableGroup.attribute.width - rightFrozenColsWidth, - this.bodyGroup.attribute.x + this.bodyGroup.attribute.width + const middleContentRight = Math.max( + this.colHeaderGroup.attribute.x + this.colHeaderGroup.attribute.width, + this.bodyGroup.attribute.x + this.bodyGroup.attribute.width, + this.bottomFrozenGroup.attribute.x + this.bottomFrozenGroup.attribute.width ); + const rightFrozenX = Math.min(this.tableGroup.attribute.width - rightFrozenColsWidth, middleContentRight); this.rightFrozenGroup.setAttribute('x', rightFrozenX); this.rightTopCornerGroup.setAttributes({ visible: hasFrozenRows, From a4c11f42e569c08a264de104d81a3ccc279f2211 Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Fri, 31 Jul 2026 18:01:58 +0800 Subject: [PATCH 11/12] fix(vtable): place bottom frozen rows below header --- .../options/listTable-api-with-frozen.test.ts | 32 +++++++++++++++++++ packages/vtable/src/scenegraph/scenegraph.ts | 12 +++++-- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/packages/vtable/__tests__/options/listTable-api-with-frozen.test.ts b/packages/vtable/__tests__/options/listTable-api-with-frozen.test.ts index 7fe91e2b50..41a9048c06 100644 --- a/packages/vtable/__tests__/options/listTable-api-with-frozen.test.ts +++ b/packages/vtable/__tests__/options/listTable-api-with-frozen.test.ts @@ -248,6 +248,38 @@ describe('listTable init test', () => { frozenTable.release(); }); + test('listTable bottom corner rows should stay below header when body is empty', () => { + const twoColumns = columns.slice(0, 2).map(column => ({ + ...column, + width: 150 + })); + const optionWithOnlyCornerRows = { + ...option, + columns: twoColumns, + frozenColCount: 1, + rightFrozenColCount: 1, + bottomFrozenRowCount: 1, + container: createDiv(), + records: records.slice(0, 1) + }; + optionWithOnlyCornerRows.container.style.position = 'relative'; + optionWithOnlyCornerRows.container.style.width = '1000px'; + optionWithOnlyCornerRows.container.style.height = '800px'; + + const frozenTable = new ListTable(optionWithOnlyCornerRows); + const { scenegraph } = frozenTable; + const headerBottom = Math.max( + scenegraph.cornerHeaderGroup.attribute.y + scenegraph.cornerHeaderGroup.attribute.height, + scenegraph.rightTopCornerGroup.attribute.y + scenegraph.rightTopCornerGroup.attribute.height + ); + + expect(scenegraph.bodyGroup.attribute.height).toBe(0); + expect(scenegraph.leftBottomCornerGroup.attribute.y).toBe(headerBottom); + expect(scenegraph.rightBottomCornerGroup.attribute.y).toBe(headerBottom); + + frozenTable.release(); + }); + test('listTable should support decreasing rightFrozenColCount by setter with row series number', () => { const optionWithRightFrozen = { ...option, diff --git a/packages/vtable/src/scenegraph/scenegraph.ts b/packages/vtable/src/scenegraph/scenegraph.ts index 83e2609cb2..06335fd25e 100644 --- a/packages/vtable/src/scenegraph/scenegraph.ts +++ b/packages/vtable/src/scenegraph/scenegraph.ts @@ -1442,10 +1442,16 @@ export class Scenegraph { if (hasBottomFrozenRows) { const bottomFrozenRowsHeight = this.table.getBottomFrozenRowsHeight(); - const bottomFrozenY = Math.min( - this.tableGroup.attribute.height - bottomFrozenRowsHeight, - this.bodyGroup.attribute.y + this.bodyGroup.attribute.height + const topFrozenBottom = Math.max( + this.colHeaderGroup.attribute.y + this.colHeaderGroup.attribute.height, + this.cornerHeaderGroup.attribute.y + this.cornerHeaderGroup.attribute.height, + this.rightTopCornerGroup.attribute.y + this.rightTopCornerGroup.attribute.height ); + const middleContentBottom = Math.max( + this.bodyGroup.attribute.y + this.bodyGroup.attribute.height, + topFrozenBottom + ); + const bottomFrozenY = Math.min(this.tableGroup.attribute.height - bottomFrozenRowsHeight, middleContentBottom); this.bottomFrozenGroup.setAttribute('y', bottomFrozenY); this.leftBottomCornerGroup.setAttributes({ visible: hasFrozenCols, From fedb4d834e525b16e0a05aa99f7a207c0f99b63a Mon Sep 17 00:00:00 2001 From: fangsmile <892739385@qq.com> Date: Fri, 31 Jul 2026 18:53:02 +0800 Subject: [PATCH 12/12] fix(vtable): place bottom frozen rows below right frozen body --- .../options/listTable-api-with-frozen.test.ts | 25 +++++++++++++++++++ packages/vtable/src/scenegraph/scenegraph.ts | 2 ++ 2 files changed, 27 insertions(+) diff --git a/packages/vtable/__tests__/options/listTable-api-with-frozen.test.ts b/packages/vtable/__tests__/options/listTable-api-with-frozen.test.ts index 41a9048c06..c6081a0e07 100644 --- a/packages/vtable/__tests__/options/listTable-api-with-frozen.test.ts +++ b/packages/vtable/__tests__/options/listTable-api-with-frozen.test.ts @@ -280,6 +280,31 @@ describe('listTable init test', () => { frozenTable.release(); }); + test('listTable bottom corner rows should stay below right frozen body when only right frozen column exists', () => { + const optionWithOnlyRightFrozenColumn = { + ...option, + columns: columns.slice(0, 1), + frozenColCount: 0, + rightFrozenColCount: 1, + bottomFrozenRowCount: 3, + container: createDiv(), + records: records.slice(0, 4) + }; + optionWithOnlyRightFrozenColumn.container.style.position = 'relative'; + optionWithOnlyRightFrozenColumn.container.style.width = '1000px'; + optionWithOnlyRightFrozenColumn.container.style.height = '800px'; + + const frozenTable = new ListTable(optionWithOnlyRightFrozenColumn); + const { scenegraph } = frozenTable; + const rightFrozenBottom = scenegraph.rightFrozenGroup.attribute.y + scenegraph.rightFrozenGroup.attribute.height; + + expect(scenegraph.bodyGroup.attribute.height).toBe(0); + expect(scenegraph.rightFrozenGroup.attribute.height).toBeGreaterThan(0); + expect(scenegraph.rightBottomCornerGroup.attribute.y).toBe(rightFrozenBottom); + + frozenTable.release(); + }); + test('listTable should support decreasing rightFrozenColCount by setter with row series number', () => { const optionWithRightFrozen = { ...option, diff --git a/packages/vtable/src/scenegraph/scenegraph.ts b/packages/vtable/src/scenegraph/scenegraph.ts index 06335fd25e..f3dc9a32a8 100644 --- a/packages/vtable/src/scenegraph/scenegraph.ts +++ b/packages/vtable/src/scenegraph/scenegraph.ts @@ -1448,7 +1448,9 @@ export class Scenegraph { this.rightTopCornerGroup.attribute.y + this.rightTopCornerGroup.attribute.height ); const middleContentBottom = Math.max( + this.rowHeaderGroup.attribute.y + this.rowHeaderGroup.attribute.height, this.bodyGroup.attribute.y + this.bodyGroup.attribute.height, + this.rightFrozenGroup.attribute.y + this.rightFrozenGroup.attribute.height, topFrozenBottom ); const bottomFrozenY = Math.min(this.tableGroup.attribute.height - bottomFrozenRowsHeight, middleContentBottom);