Skip to content

Commit 93317e3

Browse files
authored
Merge pull request #5012 from Violet2314/feat/masterDetail-children-othername
feat(master-detail): add support for custom childrenKey in master-det…
2 parents 70fcf08 + 8e4fc99 commit 93317e3

7 files changed

Lines changed: 76 additions & 17 deletions

File tree

docs/assets/guide/en/plugin/master-detail.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ MasterDetailPlugin follows TypeScript interface specifications to ensure type sa
1414
interface MasterDetailPluginOptions {
1515
/** Whether to enable checkbox cascade functionality - controls checkbox linkage between master and detail tables, default is true */
1616
enableCheckboxCascade?: boolean;
17+
/** Field name of sub-table data - This is used to specify the attribute name where the sub-table data is located in the record. The default value is 'children'. */
18+
childrenKey?: string;
1719
/** Detail table configuration - can be static configuration object or dynamic configuration function */
1820
detailTableOptions?: DetailTableOptions | ((params: { data: unknown; bodyRowIndex: number }) => DetailTableOptions);
1921
}

docs/assets/guide/zh/plugin/master-detail.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ MasterDetailPlugin 采用 TypeScript 接口规范,确保类型安全和开发
1414
interface MasterDetailPluginOptions {
1515
/** 是否启用checkbox级联功能 - 控制主从表之间的复选框联动,默认为 true */
1616
enableCheckboxCascade?: boolean;
17+
/** 子表数据的字段名 - 用于指定记录中子表数据所在的属性名,默认为 'children' */
18+
childrenKey?: string;
1719
/** 子表配置选项 - 支持静态配置对象或动态配置函数 */
1820
detailTableOptions?: DetailTableOptions | ((params: { data: unknown; bodyRowIndex: number }) => DetailTableOptions);
1921
}

packages/vtable-plugins/demo/master-detail-plugin/master-detail-plugin9.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ export function createTable() {
5757
status: '已完成',
5858
date: '2024-01-15',
5959
// 静态子表数据
60-
children: [
60+
items: [
6161
{ productName: '笔记本电脑', quantity: 2, price: 5000, total: 10000 },
6262
{ productName: '鼠标', quantity: 5, price: 100, total: 500 }
6363
]
@@ -69,7 +69,7 @@ export function createTable() {
6969
amount: 25000,
7070
status: '处理中',
7171
date: '2024-01-16',
72-
children: true // 懒加载标识 - 需要异步加载数据
72+
items: true // 懒加载标识 - 需要异步加载数据
7373
},
7474
{
7575
id: 3,
@@ -78,7 +78,7 @@ export function createTable() {
7878
amount: 35000,
7979
status: '已完成',
8080
date: '2024-01-17',
81-
children: true // 懒加载标识 - 需要异步加载数据
81+
items: true // 懒加载标识 - 需要异步加载数据
8282
},
8383
{
8484
id: 4,
@@ -87,12 +87,12 @@ export function createTable() {
8787
amount: 18000,
8888
status: '待发货',
8989
date: '2024-01-18'
90-
// 没有children属性,表示没有子数据,不显示展开图标
9190
}
9291
];
9392

9493
// 创建主从表插件
9594
const plugin = new MasterDetailPlugin({
95+
childrenKey: 'items',
9696
enableCheckboxCascade: true,
9797
detailTableOptions: params => {
9898
const { data } = params;
@@ -116,7 +116,7 @@ export function createTable() {
116116
// 主表配置
117117
const tableOptions: VTable.ListTableConstructorOptions = {
118118
columns: [
119-
{ field: 'orderNo', title: '订单号', width: 120 },
119+
{ field: 'orderNo', title: '订单号', width: 120, sort: true },
120120
{ field: 'customer', title: '客户名称', width: 150 },
121121
{ field: 'amount', title: '订单金额', width: 120 },
122122
{ field: 'status', title: '状态', width: 100 },

packages/vtable-plugins/src/master-detail-plugin/config.ts

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,18 @@ import type { DetailTableOptions, MasterDetailPluginOptions } from './types';
66
*/
77
export class ConfigManager {
88
private expandRowCallback?: (rowIndex: number) => void;
9+
private childrenKey: string;
910

10-
constructor(private pluginOptions: MasterDetailPluginOptions, private table: VTable.ListTable) {}
11+
constructor(private pluginOptions: MasterDetailPluginOptions, private table: VTable.ListTable) {
12+
this.childrenKey = pluginOptions.childrenKey || 'children';
13+
}
14+
15+
/**
16+
* 获取子数据字段名
17+
*/
18+
getChildrenKey(): string {
19+
return this.childrenKey;
20+
}
1121

1222
/**
1323
* 设置展开行的回调函数
@@ -16,12 +26,41 @@ export class ConfigManager {
1626
this.expandRowCallback = callback;
1727
}
1828

29+
/**
30+
* 创建 children 属性的 getter/setter,让 VTable 内部硬编码的 record.children
31+
* 用来确保本来VTable中对于children的硬编码起作用
32+
*/
33+
ensureChildrenAlias(record: unknown): void {
34+
if (this.childrenKey === 'children') {
35+
return; // 不需要别名
36+
}
37+
if (record && typeof record === 'object') {
38+
const recordObj = record as Record<string, unknown>;
39+
const key = this.childrenKey;
40+
const descriptor = Object.getOwnPropertyDescriptor(recordObj, 'children');
41+
if (descriptor && descriptor.get) {
42+
return; // 已经设置过 getter,跳过
43+
}
44+
Object.defineProperty(recordObj, 'children', {
45+
get() {
46+
return this[key];
47+
},
48+
set(value: unknown) {
49+
this[key] = value;
50+
},
51+
enumerable: false,
52+
configurable: true
53+
});
54+
}
55+
}
56+
1957
/**
2058
* 检查记录是否有子数据
2159
*/
22-
private hasChildren(record: unknown): boolean {
23-
if (record && typeof record === 'object' && 'children' in record) {
24-
const children = record.children;
60+
hasChildren(record: unknown): boolean {
61+
const key = this.childrenKey;
62+
if (record && typeof record === 'object' && key in record) {
63+
const children = (record as Record<string, unknown>)[key];
2564
return Array.isArray(children) && children.length > 0;
2665
}
2766
return false;
@@ -130,8 +169,10 @@ export class ConfigManager {
130169
recordList.forEach(record => {
131170
if (record && typeof record === 'object') {
132171
const recordObj = record as Record<string, unknown>;
172+
// 让 VTable 内部的 getHierarchyState / toggleHierarchyState 能正确检测
173+
this.ensureChildrenAlias(record);
133174
// 处理有子数据的记录
134-
if (this.hasChildren(record) || recordObj.children === true) {
175+
if (this.hasChildren(record) || recordObj[this.childrenKey] === true) {
135176
// 优先级:records 中的 hierarchyState > hierarchyExpandLevel
136177
if (recordObj.hierarchyState === 'expand') {
137178
// 明确设置为展开
@@ -199,7 +240,7 @@ export class ConfigManager {
199240
const recordObj = record as Record<string, unknown>;
200241
// 检查是否需要展开
201242
if (
202-
(this.hasChildren(record) || recordObj.children === true) &&
243+
(this.hasChildren(record) || recordObj[this.childrenKey] === true) &&
203244
recordObj.hierarchyState === HierarchyState.expand
204245
) {
205246
// 将记录索引添加到 expandedRecordIndices 中

packages/vtable-plugins/src/master-detail-plugin/core.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ export class MasterDetailPlugin implements pluginsDefinition.IVTablePlugin {
6969
*/
7070
private initializeManagers(): void {
7171
this.configManager = new ConfigManager(this.pluginOptions, this.table);
72-
this.eventManager = new EventManager(this.table);
72+
this.eventManager = new EventManager(this.table, this.configManager.getChildrenKey());
7373
const enableCheckboxCascade = this.pluginOptions.enableCheckboxCascade ?? true;
7474
this.subTableManager = new SubTableManager(this.table, enableCheckboxCascade);
7575

@@ -255,7 +255,10 @@ export class MasterDetailPlugin implements pluginsDefinition.IVTablePlugin {
255255
const detailConfig = this.configManager.getDetailConfigForRecord(record, bodyRowIndex);
256256
const height = detailConfig?.style?.height || 300;
257257

258-
const childrenData = Array.isArray(record.children) ? record.children : [];
258+
const childrenKey = this.configManager.getChildrenKey();
259+
const childrenData = Array.isArray((record as Record<string, unknown>)[childrenKey])
260+
? ((record as Record<string, unknown>)[childrenKey] as unknown[])
261+
: [];
259262

260263
// 处理初始高度:如果是auto,先用默认值300展开
261264
const isAutoHeight = height === 'auto';
@@ -571,8 +574,9 @@ export class MasterDetailPlugin implements pluginsDefinition.IVTablePlugin {
571574
return;
572575
}
573576

574-
// 直接修改原始记录的 children 属性
575-
(record as Record<string, unknown>).children = children;
577+
// 直接修改原始记录的子数据属性
578+
const childrenKey = this.configManager.getChildrenKey();
579+
(record as Record<string, unknown>)[childrenKey] = children;
576580
this.expandRow(row, col);
577581
this.table.scenegraph.updateCellContent(col, row);
578582
}

packages/vtable-plugins/src/master-detail-plugin/events.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@ export class EventManager {
99
private eventHandlers: Array<{ type: string; handler: (...args: unknown[]) => unknown }> = [];
1010
private allExpandedRowsBeforeMove: Set<number> = new Set();
1111
private isCleanedUp = false;
12+
private childrenKey: string;
1213

13-
constructor(private table: VTable.ListTable) {}
14+
constructor(private table: VTable.ListTable, childrenKey: string = 'children') {
15+
this.childrenKey = childrenKey;
16+
}
1417

1518
/**
1619
* 绑定事件处理器
@@ -301,7 +304,12 @@ export class EventManager {
301304
row: number;
302305
};
303306
const record = this.getRecordByRowIndex(row - this.table.columnHeaderLevelCount);
304-
if (record && typeof record === 'object' && 'children' in record && record.children === true) {
307+
if (
308+
record &&
309+
typeof record === 'object' &&
310+
this.childrenKey in record &&
311+
(record as Record<string, unknown>)[this.childrenKey] === true
312+
) {
305313
return;
306314
}
307315
this.onToggleRowExpand?.(row, col);

packages/vtable-plugins/src/master-detail-plugin/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ export interface DetailTableOptions extends VTable.ListTableConstructorOptions {
1515
export interface MasterDetailPluginOptions {
1616
/** 是否启用checkbox级联功能 - 控制主从表之间的复选框联动,默认为 true */
1717
enableCheckboxCascade?: boolean;
18+
/** 子表数据的字段名 - 用于指定记录中子表数据所在的属性名,默认为 'children' */
19+
childrenKey?: string;
1820
/** 子表配置 - 可以是静态配置对象或动态配置函数 */
1921
detailTableOptions?: DetailTableOptions | ((params: { data: unknown; bodyRowIndex: number }) => DetailTableOptions);
2022
}

0 commit comments

Comments
 (0)