diff --git a/skills/vtable-development-assistant/references/examples/pivot-chart.md b/skills/vtable-development-assistant/references/examples/pivot-chart.md index 7d08a2fb4..4164cd1f7 100644 --- a/skills/vtable-development-assistant/references/examples/pivot-chart.md +++ b/skills/vtable-development-assistant/references/examples/pivot-chart.md @@ -19,12 +19,8 @@ const records = [ const table = new VTable.PivotChart({ container: document.getElementById('tableContainer'), records, - rows: [ - { dimensionKey: 'region', title: '区域', width: 100 } - ], - columns: [ - { dimensionKey: 'product', title: '产品' } - ], + rows: [{ dimensionKey: 'region', title: '区域', width: 100 }], + columns: [{ dimensionKey: 'product', title: '产品' }], indicators: [ { indicatorKey: 'sales', @@ -65,12 +61,8 @@ const table = new VTable.PivotChart({ const table = new VTable.PivotChart({ container: document.getElementById('tableContainer'), records: monthlyData, - rows: [ - { dimensionKey: 'category', title: '品类', width: 100 } - ], - columns: [ - { dimensionKey: 'month', title: '月份' } - ], + rows: [{ dimensionKey: 'category', title: '品类', width: 100 }], + columns: [{ dimensionKey: 'month', title: '月份' }], indicators: [ { indicatorKey: 'sales', @@ -147,16 +139,52 @@ const table = new VTable.PivotChart({ } } ], - chartDimensionLinkage: true // 跨单元格维度联动高亮 + chartDimensionLinkage: true // 跨单元格维度联动高亮 }); // 监听图例事件 -table.on('legend_item_click', (args) => { +table.on('legend_item_click', args => { console.log('图例点击:', args); }); ``` -## 4. 迷你图 + 进度条混用 +## 4. PivotChart records 对象格式(分指标分组时的注意事项) + +```javascript +// 当 records 是对象格式时,key 必须与每个 indicator 的 indicatorKey 严格一致 +// 否则图表只渲染轴,不渲染柱/折线等图形元素 + +const table = new VTable.PivotChart({ + container: document.getElementById('tableContainer'), + indicatorsAsCol: false, + columnTree: [], + rowTree: [], + rows: [], + columns: [], + indicators: [ + { indicatorKey: 'METRIC_A', cellType: 'chart', chartModule: 'vchart', + chartSpec: { type: 'bar', xField: 'date', yField: 'value' } }, + { indicatorKey: 'METRIC_B', cellType: 'chart', chartModule: 'vchart', + chartSpec: { type: 'bar', xField: 'date', yField: 'count' } } + ], + // ✅ 正确:records 的 key 与 indicatorKey 完全一致 + records: { + 'METRIC_A': [ { date: '2024-01', value: 100 }, ... ], + 'METRIC_B': [ { date: '2024-01', count: 50 }, ... ] + } + // ❌ 错误示例(会导致图表无数据): + // records: { 'METRICA': [...], 'METRICB': [...] } // 缺少下划线 +}); +``` + +**副作用对照表:** + +| 配置 | 现象 | +| -------------------------- | ----------------------------- | +| records key = indicatorKey | ✅ 柱/线等图形正常渲染 | +| records key ≠ indicatorKey | ❌ 只渲染坐标轴,图形区域空白 | + +## 5. 迷你图 + 进度条混用 ```javascript // PivotTable 中直接使用 sparkline 和 progressbar(不需要 PivotChart) @@ -187,7 +215,7 @@ const table = new VTable.PivotTable({ min: 0, max: 100, style: { - barColor: (args) => args.dataValue >= 80 ? '#52c41a' : args.dataValue >= 50 ? '#faad14' : '#ff4d4f' + barColor: args => (args.dataValue >= 80 ? '#52c41a' : args.dataValue >= 50 ? '#faad14' : '#ff4d4f') } } ] diff --git a/skills/vtable-development-assistant/references/knowledge/09-data-binding.md b/skills/vtable-development-assistant/references/knowledge/09-data-binding.md index dc5d463eb..279281cf3 100644 --- a/skills/vtable-development-assistant/references/knowledge/09-data-binding.md +++ b/skills/vtable-development-assistant/references/knowledge/09-data-binding.md @@ -23,8 +23,8 @@ table.setRecords(newRecords); // 增删改 table.addRecord({ id: 3, name: 'Charlie', age: 28 }); -table.addRecords([record1, record2], 0); // 在索引0处插入 -table.deleteRecords([0, 2]); // 删除第0、2条 +table.addRecords([record1, record2], 0); // 在索引0处插入 +table.deleteRecords([0, 2]); // 删除第0、2条 table.updateRecords([updatedRecord], [1]); // 更新第1条 ``` @@ -59,16 +59,24 @@ const table = new ListTable({ ```typescript // 1. 简单字段名 -{ field: 'name' } +{ + field: 'name'; +} // 2. 嵌套字段(点号分隔) -{ field: 'address.city' } +{ + field: 'address.city'; +} // 3. 数组路径 -{ field: ['address', 'city'] } +{ + field: ['address', 'city']; +} // 4. 数字索引(数据为数组时) -{ field: 0 } +{ + field: 0; +} ``` ### fieldFormat — 格式化显示 @@ -100,8 +108,8 @@ const table = new ListTable({ field: 'name', title: '姓名', sort: (a, b, order) => { - return order === 'asc' - ? a.localeCompare(b, 'zh') + return order === 'asc' + ? a.localeCompare(b, 'zh') : b.localeCompare(a, 'zh'); } } @@ -129,7 +137,7 @@ table.updateFilterRules([ // 自定义过滤函数 table.updateFilterRules([ { - filterFunc: (record) => record.age > 20 + filterFunc: record => record.age > 20 } ]); @@ -160,8 +168,8 @@ const filtered = table.getFilteredRecords(); ```typescript const table = new ListTable({ pagination: { - perPageCount: 20, // 每页 20 条 - currentPage: 1 // 当前第 1 页 + perPageCount: 20, // 每页 20 条 + currentPage: 1 // 当前第 1 页 } }); @@ -177,19 +185,23 @@ table.updatePagination({ currentPage: 2 }); ```typescript const table = new PivotTable({ - records: flatData, // 平坦的原始数据 + records: flatData, // 平坦的原始数据 rows: ['region', 'city'], columns: ['category', 'subCategory'], - indicators: [{ - indicatorKey: 'sales', - title: '销售额' - }], - dataConfig: { - aggregationRules: [{ + indicators: [ + { indicatorKey: 'sales', - field: 'sales', - aggregationType: VTable.AggregationType.SUM - }] + title: '销售额' + } + ], + dataConfig: { + aggregationRules: [ + { + indicatorKey: 'sales', + field: 'sales', + aggregationType: VTable.AggregationType.SUM + } + ] } }); ``` @@ -234,36 +246,74 @@ const table = new PivotChart({ records: flatData, rows: ['region'], columns: ['category'], - indicators: [{ - indicatorKey: 'sales', - title: '销售额', - cellType: 'chart', - chartModule: 'vchart', - chartSpec: { - type: 'bar', - xField: ['category'], - yField: 'sales' + indicators: [ + { + indicatorKey: 'sales', + title: '销售额', + cellType: 'chart', + chartModule: 'vchart', + chartSpec: { + type: 'bar', + xField: ['category'], + yField: 'sales' + } } - }] + ] }); ``` 注意:PivotChart 内部自动开启数据分析,不需要手动配置 `dataConfig`。 +### PivotChart records 对象格式(按指标分组) + +当数据已经按指标分好组时,`records` 可以是对象格式(key 为指标分组名): + +```typescript +const table = new PivotChart({ + indicators: [ + { indicatorKey: 'sales', cellType: 'chart', ... }, + { indicatorKey: 'profit', cellType: 'chart', ... } + ], + // records 的 key 必须与 indicators[i].indicatorKey 严格一致 + records: { + 'sales': [ { region: '华东', sales: 1200, ... }, ... ], + 'profit': [ { region: '华东', profit: 300, ... }, ... ] + } +}); +``` + +> ⚠️ **关键规则:records 对象格式时,key 必须与 `indicatorKey` 完全一致(大小写、下划线均须匹配)。** +> +> 内部处理逻辑(参见 `dataset.ts` `processRecords`):当 `records` 是对象时,每个 key 会作为 `assignedIndicatorKey` 传入,与 `indicators[i].indicatorKey` 进行严格字符串比较。key 不匹配时,该组数据被忽略,对应图表单元格显示为空白(只渲染轴,不渲染柱/线等图形元素)。 + +**常见错误示例:** + +```typescript +// ❌ 错误:key 与 indicatorKey 不一致 +indicators: [{ indicatorKey: 'INDICATOR_KEY_0', ... }] +records: { 'INDICATORKEY0': [...] } // 下划线缺失 → 数据丢失 + +// ✅ 正确:key 与 indicatorKey 完全相同 +indicators: [{ indicatorKey: 'INDICATOR_KEY_0', ... }] +records: { 'INDICATOR_KEY_0': [...] } +``` + +> 注意:`columnTree: []` / `rowTree: []` 为空数组是合法的,PivotChart 会在内部自动用指标节点补全,无需手动添加虚拟节点。 + ## 四、数据更新最佳实践 ```typescript // ✅ 推荐:使用 API 更新 -table.setRecords(newRecords); // 全量更新 -table.addRecords(newRecords); // 增量添加 -table.changeCellValue(col, row, value); // 单格修改 +table.setRecords(newRecords); // 全量更新 +table.addRecords(newRecords); // 增量添加 +table.changeCellValue(col, row, value); // 单格修改 // ❌ 不推荐:直接修改 records 数组 -table.records.push(newRecord); // 不会触发重新渲染! +table.records.push(newRecord); // 不会触发重新渲染! // 如果必须直接修改数据源,需手动刷新 table.records[0].name = 'NewName'; -table.refreshAfterSourceChange(); // ListTable 专有 +table.refreshAfterSourceChange(); // ListTable 专有 ``` ## 五、树形数据 @@ -273,25 +323,19 @@ const table = new ListTable({ records: [ { name: '总部', - children: [ - { name: '研发部', children: [ - { name: '前端组' }, - { name: '后端组' } - ]}, - { name: '市场部' } - ] + children: [{ name: '研发部', children: [{ name: '前端组' }, { name: '后端组' }] }, { name: '市场部' }] } ], columns: [ - { field: 'name', title: '部门', tree: true }, // tree: true 标记树形列 + { field: 'name', title: '部门', tree: true }, // tree: true 标记树形列 { field: 'headcount', title: '人数' } ], - hierarchyIndent: 20, // 缩进像素 - hierarchyExpandLevel: 2 // 默认展开2层 + hierarchyIndent: 20, // 缩进像素 + hierarchyExpandLevel: 2 // 默认展开2层 }); // 动态加载子节点 -table.on('tree_hierarchy_state_change', (args) => { +table.on('tree_hierarchy_state_change', args => { if (args.hierarchyState === 'expand') { loadChildren(args.originData).then(children => { table.setRecordChildren(children, args.row);