diff --git a/.gitignore b/.gitignore index eeb9e98de..184de956f 100644 --- a/.gitignore +++ b/.gitignore @@ -107,4 +107,5 @@ packages/vtable/nodejs/*.png .rollup.cache/ tsconfig.tsbuildinfo -.history \ No newline at end of file +.history +.trae/ \ No newline at end of file diff --git a/common/changes/@visactor/vtable/feat-radio-aggregation-row-4027_2026-03-31-08-56.json b/common/changes/@visactor/vtable/feat-radio-aggregation-row-4027_2026-03-31-08-56.json new file mode 100644 index 000000000..ccce48a73 --- /dev/null +++ b/common/changes/@visactor/vtable/feat-radio-aggregation-row-4027_2026-03-31-08-56.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "fix: radio cell type with rowseriesnumber error\n\n", + "type": "none", + "packageName": "@visactor/vtable" + } + ], + "packageName": "@visactor/vtable", + "email": "892739385@qq.com" +} \ No newline at end of file diff --git a/common/changes/@visactor/vtable/fix-radio-aggregation-row-4027-v2_2026-03-30-02-28.json b/common/changes/@visactor/vtable/fix-radio-aggregation-row-4027-v2_2026-03-30-02-28.json new file mode 100644 index 000000000..cee3e82dd --- /dev/null +++ b/common/changes/@visactor/vtable/fix-radio-aggregation-row-4027-v2_2026-03-30-02-28.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "fix: degrade radio/checkbox cells to text in aggregation rows", + "type": "patch", + "packageName": "@visactor/vtable" + } + ], + "packageName": "@visactor/vtable", + "email": "2779428708@qq.com" +} \ No newline at end of file diff --git a/packages/vtable/examples/menu.ts b/packages/vtable/examples/menu.ts index 658a0acdf..7868a6d2a 100644 --- a/packages/vtable/examples/menu.ts +++ b/packages/vtable/examples/menu.ts @@ -580,6 +580,10 @@ export const menus = [ path: 'type', name: 'radio' }, + { + path: 'type', + name: 'radio-aggregation' + }, { path: 'type', name: 'switch' diff --git a/packages/vtable/examples/type/radio-aggregation.ts b/packages/vtable/examples/type/radio-aggregation.ts new file mode 100644 index 000000000..b1e75f937 --- /dev/null +++ b/packages/vtable/examples/type/radio-aggregation.ts @@ -0,0 +1,110 @@ +import * as VTable from '../../src'; +import { AggregationType } from '../../src/ts-types'; +import { bindDebugTool } from '../../src/scenegraph/debug-tool'; +const ListTable = VTable.ListTable; +const CONTAINER_ID = 'vTable'; + +/** + * 验证场景:合计行中 radio 单元格应降级为文本单元格,与 checkbox 行为保持一致。 + * 对应 issue: https://github.com/VisActor/VTable/issues/4027 + * + * 复现步骤(修复前): + * 1. 表格配置了 bottomFrozenRowCount 和 aggregation(合计行) + * 2. 某列配置 cellType: 'radio' + * 3. 修复前:合计行中 radio 渲染异常,可能导致表格整体空白 + * 4. 修复后:合计行中 radio 列正确降级为纯文本显示,表格正常渲染 + */ +export function createTable() { + const data = [ + { percent: '100%', value: 20, check: { text: 'unchecked', checked: false, disable: false } }, + { percent: '80%', value: 18, check: { text: 'checked', checked: true, disable: false } }, + { percent: '20%', value: 12, check: { text: 'unchecked', checked: false, disable: false } }, + { percent: '0%', value: 10, check: { text: 'checked', checked: false, disable: false } }, + { percent: '60%', value: 16, check: { text: 'disable', checked: true, disable: true } }, + { percent: '40%', value: 14, check: { text: 'disable', checked: false, disable: true } }, + { percent: '0%', value: -10, check: true }, + { percent: '0%', value: -10, check: ['选中', '选中'] } + ]; + let records: any[] = []; + for (let i = 0; i < 10; i++) { + records = records.concat(data); + } + + const columns: VTable.ColumnsDefine = [ + { + field: 'percent', + title: 'percent', + width: 120, + sort: true + }, + { + field: 'value', + title: 'value', + width: 100, + aggregation: [ + { + aggregationType: AggregationType.SUM, + formatFun(value) { + return '合计: ' + Math.round(value); + } + } + ] + }, + { + field: 'percent', + title: 'column radio', + width: 120, + cellType: 'radio' + }, + { + field: 'check', + title: 'cell radio', + width: 200, + cellType: 'radio', + radioCheckType: 'cell', + radioDirectionInCell: 'vertical', + style: { + spaceBetweenRadio: 10 + } + }, + { + field: 'percent', + title: 'checkbox', + width: 120, + cellType: 'checkbox', + disable: true, + checked: true + } + ]; + + const option: VTable.ListTableConstructorOptions = { + container: document.getElementById(CONTAINER_ID), + columns, + records, + widthMode: 'standard', + heightMode: 'autoHeight', + bottomFrozenRowCount: 1, + rowSeriesNumber: { + width: 50, + cellType: 'radio' + }, + theme: VTable.themes.DEFAULT.extends({ + bottomFrozenStyle: { + fontWeight: 500 + } + }) + }; + + const instance = new ListTable(option); + + bindDebugTool(instance.scenegraph.stage as any, { + customGrapicKeys: ['role', '_updateTag'] + }); + + const { RADIO_STATE_CHANGE } = VTable.ListTable.EVENT_TYPE; + instance.on(RADIO_STATE_CHANGE, e => { + console.log('Radio state changed:', e.col, e.row, e.radioIndexInCell); + }); + + (window as any).tableInstance = instance; +} diff --git a/packages/vtable/src/core/BaseTable.ts b/packages/vtable/src/core/BaseTable.ts index 42f3169dd..e0cd2fd54 100644 --- a/packages/vtable/src/core/BaseTable.ts +++ b/packages/vtable/src/core/BaseTable.ts @@ -3765,7 +3765,11 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { getCellType(col: number, row: number): ColumnTypeOption { let cellType; if (this.isSeriesNumberInHeader(col, row)) { - return (this.internalProps.layoutMap as SimpleHeaderLayoutMap).getSeriesNumberHeader(col, row).cellType; + const seriesHeaderCellType = (this.internalProps.layoutMap as SimpleHeaderLayoutMap).getSeriesNumberHeader( + col, + row + ).cellType; + return seriesHeaderCellType === 'radio' ? 'text' : seriesHeaderCellType; } else if (this.isHeader(col, row)) { cellType = (this.internalProps.layoutMap.getHeader(col, row) as HeaderData).headerType; } else { diff --git a/packages/vtable/src/header-helper/header-helper.ts b/packages/vtable/src/header-helper/header-helper.ts index f3d66f549..a448fe003 100644 --- a/packages/vtable/src/header-helper/header-helper.ts +++ b/packages/vtable/src/header-helper/header-helper.ts @@ -464,6 +464,8 @@ export class HeaderHelper { return TextHeaderStyle; case 'checkbox': return CheckboxStyle; + default: + return TextHeaderStyle; } } diff --git a/packages/vtable/src/scenegraph/group-creater/cell-helper.ts b/packages/vtable/src/scenegraph/group-creater/cell-helper.ts index 88217769e..4268b776d 100644 --- a/packages/vtable/src/scenegraph/group-creater/cell-helper.ts +++ b/packages/vtable/src/scenegraph/group-creater/cell-helper.ts @@ -374,8 +374,7 @@ export function createCell( } else if (type === 'checkbox') { const isAggregation = 'isAggregation' in table.internalProps.layoutMap && table.internalProps.layoutMap.isAggregation(col, row); - const isSeriesNumber = table.internalProps.layoutMap.isSeriesNumber(col, row); - if (isAggregation && isSeriesNumber) { + if (isAggregation) { const createTextCellGroup = Factory.getFunction('createTextCellGroup') as CreateTextCellGroup; cellGroup = createTextCellGroup( table, @@ -423,25 +422,52 @@ export function createCell( ); } } else if (type === 'radio') { - const createRadioCellGroup = Factory.getFunction('createRadioCellGroup') as CreateRadioCellGroup; - cellGroup = createRadioCellGroup( - null, - columnGroup, - 0, - y, - col, - row, - colWidth, - cellWidth, - cellHeight, - padding, - textAlign, - textBaseline, - table, - cellTheme, - define as RadioColumnDefine, - range - ); + const isAggregation = + 'isAggregation' in table.internalProps.layoutMap && table.internalProps.layoutMap.isAggregation(col, row); + if (isAggregation) { + const createTextCellGroup = Factory.getFunction('createTextCellGroup') as CreateTextCellGroup; + cellGroup = createTextCellGroup( + table, + value, + columnGroup, + 0, + y, + col, + row, + colWidth, + cellWidth, + cellHeight, + padding, + textAlign, + textBaseline, + false, + undefined, + true, + cellTheme, + range, + isAsync + ); + } else { + const createRadioCellGroup = Factory.getFunction('createRadioCellGroup') as CreateRadioCellGroup; + cellGroup = createRadioCellGroup( + null, + columnGroup, + 0, + y, + col, + row, + colWidth, + cellWidth, + cellHeight, + padding, + textAlign, + textBaseline, + table, + cellTheme, + define as RadioColumnDefine, + range + ); + } } else if (type === 'switch') { const createSwitchCellGroup = Factory.getFunction('createSwitchCellGroup') as CreateSwitchCellGroup; cellGroup = createSwitchCellGroup(