diff --git a/skills/vtable-development-assistant/references/examples/README.md b/skills/vtable-development-assistant/references/examples/README.md new file mode 100644 index 000000000..2f9ffb53c --- /dev/null +++ b/skills/vtable-development-assistant/references/examples/README.md @@ -0,0 +1,15 @@ +# VTable 示例索引 + +本目录收录常用代码示例,可作为 skill 生成代码时的参考模板。 + +## 目录 + +| 文件 | 说明 | +|------|------| +| [list-table-basic.md](list-table-basic.md) | ListTable 基础、排序、过滤、分页 | +| [list-table-tree.md](list-table-tree.md) | ListTable 树形结构与分组 | +| [pivot-table-analysis.md](pivot-table-analysis.md) | PivotTable 多维分析 | +| [pivot-chart.md](pivot-chart.md) | PivotChart 透视组合图 | +| [custom-layout-jsx.md](custom-layout-jsx.md) | JSX 自定义布局完整示例 | +| [cell-types.md](cell-types.md) | 13 种单元格类型示例 | +| [interaction-patterns.md](interaction-patterns.md) | 交互模式(编辑、选中、菜单等) | diff --git a/skills/vtable-development-assistant/references/examples/cell-types.md b/skills/vtable-development-assistant/references/examples/cell-types.md new file mode 100644 index 000000000..e7532f856 --- /dev/null +++ b/skills/vtable-development-assistant/references/examples/cell-types.md @@ -0,0 +1,254 @@ +# 13 种单元格类型示例 + +## 1. text — 文本(默认) + +```javascript +{ + field: 'name', + title: '姓名', + width: 120, + cellType: 'text', // 可省略,默认即 text + style: { fontWeight: 'bold', color: '#333' } +} +``` + +## 2. link — 链接 + +```javascript +{ + field: 'url', + title: '链接', + width: 200, + cellType: 'link', + linkJump: true, // 点击跳转 + style: { linkColor: '#1890ff', cursor: 'pointer' } +} +// 模板链接:用 field 值替换模板 +{ + field: 'id', + title: '详情', + cellType: 'link', + templateLink: 'https://example.com/detail/{id}', + linkJump: true +} +``` + +## 3. image — 图片 + +```javascript +{ + field: 'avatar', + title: '头像', + width: 80, + cellType: 'image', + keepAspectRatio: true, + imageAutoSizing: false, + style: { padding: 4 } +} +``` + +## 4. video — 视频 + +```javascript +{ + field: 'videoUrl', + title: '视频', + width: 200, + cellType: 'video', + keepAspectRatio: true +} +``` + +## 5. progressbar — 进度条 + +```javascript +{ + field: 'completion', + title: '完成率', + width: 150, + cellType: 'progressbar', + min: 0, + max: 100, + barType: 'default', + style: { + barColor: (args) => { + if (args.dataValue >= 80) return '#52c41a'; + if (args.dataValue >= 50) return '#faad14'; + return '#ff4d4f'; + }, + barBgColor: '#f0f0f0', + barHeight: 12, + barBottom: 6 + } +} +``` + +## 6. sparkline — 迷你图 + +```javascript +{ + field: 'trend', + title: '趋势', + width: 180, + cellType: 'sparkline', + sparklineSpec: { + type: 'line', + xField: { field: 'x' }, + yField: { field: 'y' }, + pointShowRule: 'none', + line: { + style: { stroke: '#1890ff', strokeWidth: 2 } + } + } +} +// 数据格式: record.trend = [{ x: 1, y: 10 }, { x: 2, y: 15 }, ...] +``` + +## 7. chart — 嵌入图表 + +```javascript +import VChart from '@visactor/vchart'; +VTable.register.chartModule('vchart', VChart); + +{ + field: 'chartData', + title: '分布', + width: 250, + cellType: 'chart', + chartModule: 'vchart', + chartSpec: { + type: 'pie', + categoryField: 'type', + valueField: 'value', + label: { visible: true }, + legends: { visible: false } + } +} +``` + +## 8. checkbox — 复选框 + +```javascript +{ + field: 'selected', + title: '选择', + width: 60, + cellType: 'checkbox', + // 表头也显示 checkbox(全选) + headerType: 'checkbox' +} + +// 监听变更 +table.on('checkbox_state_change', (args) => { + console.log(args.col, args.row, args.checked); +}); + +// API +table.getCheckboxState(); // 获取所有 checkbox 状态 +table.getCheckboxState({ col: 0 }); // 获取指定列的 checkbox 状态 +table.setCellCheckboxState(0, 3, true); // 设置指定单元格选中 +``` + +## 9. radio — 单选框 + +```javascript +{ + field: 'priority', + title: '优先级', + width: 80, + cellType: 'radio' +} + +// 监听变更 +table.on('radio_state_change', (args) => { + console.log(args.col, args.row, args.radioIndexInCell); +}); +``` + +## 10. switch — 开关 + +```javascript +{ + field: 'enabled', + title: '启用', + width: 80, + cellType: 'switch', + disable: (args) => args.row === 1 // 禁用第1行 +} + +// 监听变更 +table.on('switch_state_change', (args) => { + console.log(args.col, args.row, args.checked); +}); +``` + +## 11. button — 按钮 + +```javascript +{ + field: 'action', + title: '操作', + width: 100, + cellType: 'button', + text: '点击', + style: { + color: '#fff', + bgColor: '#1890ff', + cornerRadius: 4, + padding: [4, 12] + } +} + +// 监听点击 +table.on('button_click', (args) => { + const record = table.getCellOriginRecord(args.col, args.row); + console.log('按钮点击:', record); +}); +``` + +## 12. composite — 组合 + +```javascript +// 组合类型通常与 customRender 或 customLayout 配合使用 +{ + field: 'info', + title: '综合信息', + width: 300, + cellType: 'composite', + customLayout: (args) => { + // ... 使用 JSX 组合多种元素 + } +} +``` + +## 13. multilinetext — 多行文本 + +```javascript +{ + field: 'description', + title: '描述', + width: 250, + cellType: 'multilinetext', + style: { + autoWrapText: true, + lineClamp: 3 // 最多3行 + } +} +``` + +## 单元格类型选择指南 + +| 需求 | 推荐类型 | +|------|----------| +| 普通文字 | text | +| 可点击跳转 | link | +| 显示头像/图片 | image | +| 百分比/进度 | progressbar | +| 数据趋势 | sparkline | +| 复杂图表 | chart | +| 勾选/多选 | checkbox | +| 单选 | radio | +| 开关 | switch | +| 操作按钮 | button | +| 长文本 | multilinetext | +| 复杂自定义 | composite + customLayout | diff --git a/skills/vtable-development-assistant/references/examples/custom-layout-jsx.md b/skills/vtable-development-assistant/references/examples/custom-layout-jsx.md new file mode 100644 index 000000000..2862746ff --- /dev/null +++ b/skills/vtable-development-assistant/references/examples/custom-layout-jsx.md @@ -0,0 +1,209 @@ +# JSX 自定义布局完整示例 + +## 1. 用户卡片(头像 + 信息 + 标签) + +```jsx +import * as VTable from '@visactor/vtable'; +const { Group, Text, Image, Tag } = VTable.CustomLayout; + +const table = new VTable.ListTable({ + container: document.getElementById('tableContainer'), + records: users, + columns: [ + { + field: 'user', + title: '用户', + width: 320, + customLayout: (args) => { + const { table, row, col } = args; + const record = table.getCellOriginRecord(col, row); + return { + rootContainer: ( + + + + + + + + + ), + renderDefault: false, + expectedHeight: 64 + }; + } + }, + { field: 'department', title: '部门', width: 120 }, + { field: 'joinDate', title: '入职日期', width: 120 } + ], + defaultRowHeight: 64 +}); +``` + +## 2. 多指标仪表板单元格 + +```jsx +const { Group, Text, Rect } = VTable.CustomLayout; + +function dashboardCell(args) { + const { table, row, col, rect } = args; + const record = table.getCellOriginRecord(col, row); + + const rate = record.completion / 100; + const barWidth = (rect.width - 24) * rate; + const barColor = rate >= 0.8 ? '#52c41a' : rate >= 0.5 ? '#faad14' : '#ff4d4f'; + + return { + rootContainer: ( + + + + + + + + + + + + + + ), + renderDefault: false, + expectedHeight: 72 + }; +} +``` + +## 3. 可点击操作列 + +```jsx +const { Group, Text, Image } = VTable.CustomLayout; + +const table = new VTable.ListTable({ + container: document.getElementById('tableContainer'), + records: orderData, + columns: [ + { field: 'orderId', title: '订单号', width: 120 }, + { field: 'product', title: '产品', width: 150 }, + { + field: 'actions', + title: '操作', + width: 200, + customLayout: (args) => { + const { table, row, col } = args; + const record = table.getCellOriginRecord(col, row); + + return { + rootContainer: ( + + handleView(record)} + > + + + handleEdit(record)} + > + + + handleDelete(record)} + > + + + + ), + renderDefault: false + }; + } + } + ] +}); +``` + +## 4. 带状态和 hover 效果的列表项 + +```jsx +const { Group, Text, Image, Circle } = VTable.CustomLayout; + +function statusCell(args) { + const { table, row, col } = args; + const record = table.getCellOriginRecord(col, row); + + const statusMap = { + running: { color: '#52c41a', text: '运行中' }, + stopped: { color: '#ff4d4f', text: '已停止' }, + pending: { color: '#faad14', text: '等待中' } + }; + const status = statusMap[record.status] || { color: '#999', text: '未知' }; + + return { + rootContainer: ( + + + + + ), + renderDefault: false + }; +} +``` + +## 5. 表头自定义布局 + +```jsx +const { Group, Text, Image } = VTable.CustomLayout; + +const table = new VTable.ListTable({ + container: document.getElementById('tableContainer'), + records: data, + columns: [ + { + field: 'sales', + title: '销售额', + width: 180, + headerCustomLayout: (args) => { + return { + rootContainer: ( + + + + + + + + ), + renderDefault: false + }; + }, + style: { textAlign: 'right' } + } + ] +}); +``` diff --git a/skills/vtable-development-assistant/references/examples/interaction-patterns.md b/skills/vtable-development-assistant/references/examples/interaction-patterns.md new file mode 100644 index 000000000..87c04aa98 --- /dev/null +++ b/skills/vtable-development-assistant/references/examples/interaction-patterns.md @@ -0,0 +1,281 @@ +# 交互模式示例 + +## 1. 可编辑表格 + +```javascript +import * as VTable from '@visactor/vtable'; +// 引入编辑器 +import { InputEditor, DateInputEditor, ListEditor } from '@visactor/vtable-editors'; + +// 注册编辑器 +const inputEditor = new InputEditor(); +const dateEditor = new DateInputEditor(); +const listEditor = new ListEditor({ values: ['低', '中', '高'] }); + +VTable.register.editor('input', inputEditor); +VTable.register.editor('date', dateEditor); +VTable.register.editor('list', listEditor); + +const table = new VTable.ListTable({ + container: document.getElementById('tableContainer'), + records: data, + columns: [ + { field: 'name', title: '名称', width: 150, editor: 'input' }, + { field: 'date', title: '日期', width: 120, editor: 'date' }, + { field: 'priority', title: '优先级', width: 100, editor: 'list' }, + { field: 'readonly', title: '只读列', width: 100 } // 无 editor = 不可编辑 + ], + editCellTrigger: 'doubleclick' // 双击进入编辑 +}); + +// 监听编辑 +table.on('change_cell_value', (args) => { + console.log('值变更:', args.col, args.row, args.rawValue, '→', args.changedValue); +}); +``` + +## 2. 选中与高亮 + +```javascript +const table = new VTable.ListTable({ + container: document.getElementById('tableContainer'), + records: data, + columns: [...], + // 选中配置 + select: { + disableSelect: false, + disableHeaderSelect: false, + highlightMode: 'cell' // 'cell' | 'cross' | 'row' | 'column' + }, + // hover 配置 + hover: { + highlightMode: 'row', // hover 整行高亮 + disableHover: false, + disableHeaderHover: false + }, + // 主题中自定义选中样式 + theme: VTable.themes.DEFAULT.extends({ + selectionStyle: { + cellBgColor: 'rgba(24, 144, 255, 0.1)', + cellBorderColor: '#1890ff', + cellBorderLineWidth: 2 + } + }) +}); + +// 监听选中 +table.on('selected_cell', (args) => { + console.log('选中:', args.ranges); +}); + +// API: 设置选中区域 +table.selectCells([{ start: { col: 0, row: 1 }, end: { col: 2, row: 3 } }]); + +// API: 获取选中区域数据 +const selectedData = table.getSelectedCellInfos(); +``` + +## 3. 右键菜单 + +```javascript +const table = new VTable.ListTable({ + container: document.getElementById('tableContainer'), + records: data, + columns: [...], + menu: { + contextMenuItems: [ + { text: '复制', menuKey: 'copy' }, + { text: '粘贴', menuKey: 'paste' }, + '-', // 分隔线 + { text: '删除行', menuKey: 'delete', icon: '🗑️' }, + { + text: '导出', + menuKey: 'export', + children: [ + { text: '导出CSV', menuKey: 'export-csv' }, + { text: '导出Excel', menuKey: 'export-excel' } + ] + } + ], + // 下拉菜单(表头按钮) + dropDownMenuHighlight: [ + { menuKey: 'filter-asc', text: '升序' }, + { menuKey: 'filter-desc', text: '降序' } + ] + } +}); + +// 监听菜单点击 +table.on('dropdown_menu_click', (args) => { + switch (args.menuKey) { + case 'copy': + table.copyCellsToClipboard(); + break; + case 'delete': + // 删除对应行 + const records = [...table.records]; + records.splice(args.row - table.columnHeaderLevelCount, 1); + table.setRecords(records); + break; + case 'export-csv': + exportToCSV(table); + break; + } +}); +``` + +## 4. 拖拽排序列/行 + +```javascript +const table = new VTable.ListTable({ + container: document.getElementById('tableContainer'), + records: data, + columns: [...], + dragHeaderMode: 'all', // 'all' | 'column' | 'row' | 'none' +}); + +// 监听拖拽完成 +table.on('change_header_position', (args) => { + console.log('列位置变更:', args.source, '→', args.target); +}); +``` + +## 5. 列宽调整 + +```javascript +const table = new VTable.ListTable({ + container: document.getElementById('tableContainer'), + records: data, + columns: [...], + columnResizeMode: 'header', // 'all' | 'header' | 'body' | 'none' + columnResizeType: 'column' // 'column' | 'indicator' | 'all' | 'indicatorGroup' +}); + +// 监听列宽变化 +table.on('resize_column_end', (args) => { + console.log('列宽:', args.col, args.colWidth); +}); +``` + +## 6. 键盘导航 + +```javascript +const table = new VTable.ListTable({ + container: document.getElementById('tableContainer'), + records: data, + columns: [...], + keyboardOptions: { + moveFocusCellOnTab: true, // Tab 移动焦点 + moveFocusCellOnEnter: true, // Enter 移动焦点 + copySelected: true, // Ctrl+C 复制 + pasteValueToCell: true, // Ctrl+V 粘贴 + selectAllOnCtrlA: true, // Ctrl+A 全选 + editCellOnEnter: true // Enter 编辑 + } +}); + +// 监听键盘事件 +table.on('keydown', (args) => { + if (args.event.key === 'Delete') { + // 清除选中区域数据 + const ranges = table.getSelectedCellRanges(); + ranges.forEach(range => { + for (let r = range.start.row; r <= range.end.row; r++) { + for (let c = range.start.col; c <= range.end.col; c++) { + table.changeCellValue(c, r, ''); + } + } + }); + } +}); +``` + +## 7. Tooltip + +```javascript +const table = new VTable.ListTable({ + container: document.getElementById('tableContainer'), + records: data, + columns: [ + { + field: 'description', + title: '描述', + width: 120, + style: { textOverflow: 'ellipsis' } + } + ], + tooltip: { + isShowOverflowTextTooltip: true // 文字省略时自动提示 + } +}); + +// 自定义 tooltip +table.showTooltip(col, row, { + content: '自定义提示内容', + referencePosition: { rect: { x: 100, y: 100 }, placement: 'bottom' }, + style: { bgColor: '#333', color: '#fff' } +}); +``` + +## 8. 冻结列 + +```javascript +const table = new VTable.ListTable({ + container: document.getElementById('tableContainer'), + records: data, + columns: [...], + frozenColCount: 2, // 左侧冻结2列 + rightFrozenColCount: 1, // 右侧冻结1列 + bottomFrozenRowCount: 1, // 底部冻结1行(如汇总行) + allowFrozenColCount: 5 // 允许冻结的最大列数 +}); + +// 动态更新冻结列 +table.updateOption({ frozenColCount: 3 }); +``` + +## 9. 搜索高亮 + +```javascript +import { SearchComponent } from '@visactor/vtable-search'; + +const table = new VTable.ListTable({ + container: document.getElementById('tableContainer'), + records: data, + columns: [...] +}); + +// 初始化搜索组件 +const search = new SearchComponent({ + table, + autoJump: true // 自动跳转到匹配项 +}); + +// 搜索 +const results = search.search('关键词'); + +// 跳转到下一个/上一个匹配 +search.next(); +search.prev(); + +// 清除搜索 +search.clear(); +``` + +## 10. 导出 + +```javascript +import { exportVTableToExcel } from '@visactor/vtable-export'; + +// 导出为 Excel +exportVTableToExcel(table, { + fileName: 'data-export', + formatExcelJSCell: (cellInfo, cellInExcelFile) => { + // 自定义 Excel 单元格格式 + return cellInExcelFile; + } +}); + +// 导出为 CSV(内置) +const csvString = table.exportCellsContent(); +``` diff --git a/skills/vtable-development-assistant/references/examples/list-table-basic.md b/skills/vtable-development-assistant/references/examples/list-table-basic.md new file mode 100644 index 000000000..8dec7be29 --- /dev/null +++ b/skills/vtable-development-assistant/references/examples/list-table-basic.md @@ -0,0 +1,160 @@ +# ListTable 基础示例 + +## 1. 最简 ListTable + +```javascript +import * as VTable from '@visactor/vtable'; + +const table = new VTable.ListTable({ + container: document.getElementById('tableContainer'), + records: [ + { name: '张三', age: 28, city: '北京' }, + { name: '李四', age: 35, city: '上海' }, + { name: '王五', age: 22, city: '深圳' } + ], + columns: [ + { field: 'name', title: '姓名', width: 120 }, + { field: 'age', title: '年龄', width: 80 }, + { field: 'city', title: '城市', width: 120 } + ] +}); +``` + +## 2. 带排序和格式化 + +```javascript +const table = new VTable.ListTable({ + container: document.getElementById('tableContainer'), + records: salesData, + columns: [ + { field: 'product', title: '产品', width: 150, sort: true }, + { + field: 'revenue', + title: '收入', + width: 120, + sort: true, + fieldFormat: (record) => `¥${record.revenue.toLocaleString()}`, + style: { + textAlign: 'right', + color: (args) => args.dataValue > 10000 ? '#52c41a' : '#333' + } + }, + { + field: 'date', + title: '日期', + width: 120, + sort: { + orderFn: (a, b) => new Date(a.date) - new Date(b.date) + } + } + ], + sortState: { field: 'revenue', order: 'desc' } +}); +``` + +## 3. 分页 + +```javascript +const table = new VTable.ListTable({ + container: document.getElementById('tableContainer'), + records: bigDataset, // 所有数据 + columns: [ + { field: 'id', title: 'ID', width: 60 }, + { field: 'name', title: '名称', width: 150 }, + { field: 'value', title: '值', width: 100 } + ], + pagination: { + perPageCount: 20, + currentPage: 1 + } +}); + +// 翻页 +function changePage(pageNum) { + table.updatePagination({ currentPage: pageNum }); +} +``` + +## 4. 冻结列与自适应宽度 + +```javascript +const table = new VTable.ListTable({ + container: document.getElementById('tableContainer'), + records: data, + columns: [ + { field: 'id', title: 'ID', width: 60 }, + { field: 'name', title: '名称', width: 200 }, + { field: 'category', title: '分类', width: 120 }, + { field: 'price', title: '价格', width: 100 }, + { field: 'stock', title: '库存', width: 80 }, + { field: 'description', title: '描述', width: 'auto' } + ], + frozenColCount: 2, // 冻结前2列 + rightFrozenColCount: 1, // 冻结最后1列 + widthMode: 'autoWidth', // 自动列宽 + heightMode: 'autoHeight', // 自动行高 + autoWrapText: true // 自动换行 +}); +``` + +## 5. 多列排序 + 条件样式 + +```javascript +const table = new VTable.ListTable({ + container: document.getElementById('tableContainer'), + records: data, + columns: [ + { field: 'name', title: '姓名', width: 120, sort: true }, + { + field: 'score', + title: '得分', + width: 100, + sort: true, + style: { + bgColor: (args) => { + if (args.dataValue >= 90) return '#f6ffed'; + if (args.dataValue >= 60) return '#fffbe6'; + return '#fff2f0'; + }, + color: (args) => { + if (args.dataValue >= 90) return '#52c41a'; + if (args.dataValue >= 60) return '#faad14'; + return '#ff4d4f'; + } + } + }, + { field: 'grade', title: '等级', width: 80, sort: true } + ], + multipleSort: true, + sortState: [ + { field: 'grade', order: 'asc' }, + { field: 'score', order: 'desc' } + ] +}); +``` + +## 6. 聚合统计行 + +```javascript +const table = new VTable.ListTable({ + container: document.getElementById('tableContainer'), + records: data, + columns: [ + { field: 'category', title: '分类', width: 120 }, + { + field: 'amount', + title: '金额', + width: 120, + aggregation: [ + { aggregationType: VTable.TYPES.AggregationType.SUM, showOnTop: false, formatFun: (val) => `合计: ¥${val.toFixed(2)}` }, + ] + }, + { + field: 'count', + title: '数量', + width: 100, + aggregation: { aggregationType: VTable.TYPES.AggregationType.AVG, formatFun: (val) => `均值: ${val.toFixed(1)}` } + } + ] +}); +``` diff --git a/skills/vtable-development-assistant/references/examples/list-table-tree.md b/skills/vtable-development-assistant/references/examples/list-table-tree.md new file mode 100644 index 000000000..52d50225b --- /dev/null +++ b/skills/vtable-development-assistant/references/examples/list-table-tree.md @@ -0,0 +1,162 @@ +# ListTable 树形与分组示例 + +## 1. 基础树形表格 + +```javascript +import * as VTable from '@visactor/vtable'; + +const records = [ + { + name: '技术部', + budget: 500000, + children: [ + { + name: '前端组', + budget: 200000, + children: [ + { name: '张三', budget: 80000 }, + { name: '李四', budget: 120000 } + ] + }, + { + name: '后端组', + budget: 300000, + children: [ + { name: '王五', budget: 150000 }, + { name: '赵六', budget: 150000 } + ] + } + ] + }, + { + name: '产品部', + budget: 300000, + children: [ + { name: '小明', budget: 150000 }, + { name: '小红', budget: 150000 } + ] + } +]; + +const table = new VTable.ListTable({ + container: document.getElementById('tableContainer'), + records, + columns: [ + { + field: 'name', + title: '名称', + width: 250, + tree: true // 开启树形展开 + }, + { + field: 'budget', + title: '预算', + width: 150, + fieldFormat: (record) => `¥${record.budget.toLocaleString()}` + } + ], + hierarchyIndent: 20, // 缩进像素 + hierarchyExpandLevel: 1 // 默认展开1层 +}); +``` + +## 2. 自定义展开层级与事件 + +```javascript +const table = new VTable.ListTable({ + container: document.getElementById('tableContainer'), + records: treeData, + columns: [ + { field: 'name', title: '名称', width: 250, tree: true }, + { field: 'type', title: '类型', width: 100 }, + { field: 'size', title: '大小', width: 100 } + ], + hierarchyExpandLevel: Infinity, // 全部展开 + hierarchyTextStartAlignment: true // 同级文字对齐 +}); + +// 监听展开/折叠 +table.on('tree_hierarchy_state_change', (args) => { + console.log('节点状态变更:', args.col, args.row, args.hierarchyState); +}); + +// API: 展开/折叠指定节点 +table.toggleHierarchyState(0, 3); // 切换第3行的展开状态 + +// API: 展开所有 / 折叠所有 +table.setAllHierarchyState('expand'); +table.setAllHierarchyState('collapse'); +``` + +## 3. 分组表格 + +```javascript +const records = [ + { category: '电子产品', product: '手机', price: 3999, stock: 150 }, + { category: '电子产品', product: '笔记本', price: 6999, stock: 80 }, + { category: '食品', product: '牛奶', price: 59, stock: 500 }, + { category: '食品', product: '面包', price: 15, stock: 300 }, + { category: '服装', product: 'T恤', price: 99, stock: 200 } +]; + +const table = new VTable.ListTable({ + container: document.getElementById('tableContainer'), + records, + columns: [ + { field: 'category', title: '分类', width: 120, tree: true }, + { field: 'product', title: '产品', width: 150 }, + { field: 'price', title: '价格', width: 100 }, + { field: 'stock', title: '库存', width: 80 } + ], + groupConfig: { + groupBy: 'category', // 按 category 字段分组 + titleCustomLayout: (args) => { + const { table, row, col, rect } = args; + const record = table.getCellOriginRecord(col, row); + const { Group, Text } = VTable.CustomLayout; + return { + rootContainer: ( + + + + + ), + renderDefault: false + }; + } + } +}); +``` + +## 4. 懒加载树形 + +```javascript +const table = new VTable.ListTable({ + container: document.getElementById('tableContainer'), + records: rootNodes, // 只包含顶层节点 + columns: [ + { + field: 'name', + title: '组织', + width: 250, + tree: true + }, + { field: 'count', title: '人数', width: 100 } + ], + hierarchyExpandLevel: 0 // 默认全部折叠 +}); + +// 监听展开事件,懒加载子节点 +table.on('tree_hierarchy_state_change', async (args) => { + if (args.hierarchyState === 'expand') { + const record = table.getCellOriginRecord(args.col, args.row); + if (!record.children || record.children.length === 0) { + // 异步加载子节点 + const children = await fetchChildren(record.id); + record.children = children; + // 刷新表格 + table.setRecords(table.records); + } + } +}); +``` diff --git a/skills/vtable-development-assistant/references/examples/pivot-chart.md b/skills/vtable-development-assistant/references/examples/pivot-chart.md new file mode 100644 index 000000000..7d08a2fb4 --- /dev/null +++ b/skills/vtable-development-assistant/references/examples/pivot-chart.md @@ -0,0 +1,195 @@ +# PivotChart 透视组合图示例 + +## 1. 基础 PivotChart + +```javascript +import * as VTable from '@visactor/vtable'; +import VChart from '@visactor/vchart'; + +// 注册 VChart 模块 +VTable.register.chartModule('vchart', VChart); + +const records = [ + { region: '华东', product: '手机', sales: 12000, profit: 3000 }, + { region: '华东', product: '电脑', sales: 8000, profit: 2000 }, + { region: '华北', product: '手机', sales: 9000, profit: 2500 }, + { region: '华北', product: '电脑', sales: 11000, profit: 3500 } +]; + +const table = new VTable.PivotChart({ + container: document.getElementById('tableContainer'), + records, + rows: [ + { dimensionKey: 'region', title: '区域', width: 100 } + ], + columns: [ + { dimensionKey: 'product', title: '产品' } + ], + indicators: [ + { + indicatorKey: 'sales', + title: '销售额', + width: 250, + cellType: 'chart', + chartModule: 'vchart', + chartSpec: { + type: 'bar', + xField: 'region', + yField: 'sales', + bar: { + style: { + fill: '#1890ff' + } + } + } + } + ], + axes: [ + { + orient: 'bottom', + visible: true, + label: { visible: true } + }, + { + orient: 'left', + visible: true, + title: { visible: true, text: '销售额' } + } + ] +}); +``` + +## 2. 多指标(柱形 + 折线) + +```javascript +const table = new VTable.PivotChart({ + container: document.getElementById('tableContainer'), + records: monthlyData, + rows: [ + { dimensionKey: 'category', title: '品类', width: 100 } + ], + columns: [ + { dimensionKey: 'month', title: '月份' } + ], + indicators: [ + { + indicatorKey: 'sales', + title: '销售额', + width: 200, + cellType: 'chart', + chartModule: 'vchart', + chartSpec: { + type: 'bar', + xField: 'month', + yField: 'sales', + bar: { style: { fill: '#5B8FF9' } } + } + }, + { + indicatorKey: 'growth', + title: '增长率', + width: 200, + cellType: 'chart', + chartModule: 'vchart', + chartSpec: { + type: 'line', + xField: 'month', + yField: 'growth', + line: { style: { stroke: '#5AD8A6' } }, + point: { style: { fill: '#5AD8A6' } } + } + } + ], + indicatorsAsCol: true, + axes: [ + { orient: 'bottom', visible: true }, + { orient: 'left', visible: true, title: { visible: true, text: '销售额' } }, + { orient: 'right', visible: true, title: { visible: true, text: '增长率' } } + ] +}); +``` + +## 3. 组合图 + 图例联动 + +```javascript +const table = new VTable.PivotChart({ + container: document.getElementById('tableContainer'), + records: data, + rows: [{ dimensionKey: 'region', title: '区域', width: 100 }], + columns: [{ dimensionKey: 'quarter', title: '季度' }], + indicators: [ + { + indicatorKey: 'revenue', + title: '营收分析', + width: 300, + cellType: 'chart', + chartModule: 'vchart', + chartSpec: { + type: 'common', + seriesField: 'type', + series: [ + { + type: 'bar', + data: { id: 'bar' }, + xField: 'quarter', + yField: 'revenue', + bar: { style: { fill: { field: 'type', scale: 'color' } } } + }, + { + type: 'line', + data: { id: 'line' }, + xField: 'quarter', + yField: 'target', + line: { style: { stroke: '#FF6B6B', lineDash: [5, 5] } } + } + ], + legends: { visible: true, orient: 'top' } + } + } + ], + chartDimensionLinkage: true // 跨单元格维度联动高亮 +}); + +// 监听图例事件 +table.on('legend_item_click', (args) => { + console.log('图例点击:', args); +}); +``` + +## 4. 迷你图 + 进度条混用 + +```javascript +// PivotTable 中直接使用 sparkline 和 progressbar(不需要 PivotChart) +const table = new VTable.PivotTable({ + container: document.getElementById('tableContainer'), + records: data, + rows: [{ dimensionKey: 'department', title: '部门', width: 120 }], + columns: [{ dimensionKey: 'metric', title: '指标' }], + indicators: [ + { + indicatorKey: 'trend', + title: '趋势', + width: 180, + cellType: 'sparkline', + sparklineSpec: { + type: 'line', + xField: 'day', + yField: 'value', + line: { style: { stroke: '#1890ff' } }, + point: { style: { fill: '#1890ff', size: 3 } } + } + }, + { + indicatorKey: 'completion', + title: '完成率', + width: 150, + cellType: 'progressbar', + min: 0, + max: 100, + style: { + barColor: (args) => args.dataValue >= 80 ? '#52c41a' : args.dataValue >= 50 ? '#faad14' : '#ff4d4f' + } + } + ] +}); +``` diff --git a/skills/vtable-development-assistant/references/examples/pivot-table-analysis.md b/skills/vtable-development-assistant/references/examples/pivot-table-analysis.md new file mode 100644 index 000000000..b50190e40 --- /dev/null +++ b/skills/vtable-development-assistant/references/examples/pivot-table-analysis.md @@ -0,0 +1,220 @@ +# PivotTable 多维分析示例 + +## 1. 基础透视表(自动聚合) + +```javascript +import * as VTable from '@visactor/vtable'; + +const records = [ + { region: '华东', product: '手机', quarter: 'Q1', sales: 12000 }, + { region: '华东', product: '手机', quarter: 'Q2', sales: 15000 }, + { region: '华东', product: '电脑', quarter: 'Q1', sales: 8000 }, + { region: '华北', product: '手机', quarter: 'Q1', sales: 9000 }, + { region: '华北', product: '电脑', quarter: 'Q2', sales: 11000 } +]; + +const table = new VTable.PivotTable({ + container: document.getElementById('tableContainer'), + records, + rows: [ + { dimensionKey: 'region', title: '区域', width: 120 } + ], + columns: [ + { dimensionKey: 'product', title: '产品' }, + { dimensionKey: 'quarter', title: '季度' } + ], + indicators: [ + { + indicatorKey: 'sales', + title: '销售额', + width: 120, + format: (value) => `¥${value?.toLocaleString() ?? '-'}` + } + ], + dataConfig: { + aggregationRules: [ + { indicatorKey: 'sales', field: 'sales', aggregationType: VTable.TYPES.AggregationType.SUM } + ] + } +}); +``` + +## 2. 多指标 + 合计 + +```javascript +const table = new VTable.PivotTable({ + container: document.getElementById('tableContainer'), + records: salesData, + rows: [ + { dimensionKey: 'region', title: '区域', width: 100 }, + { dimensionKey: 'city', title: '城市', width: 100 } + ], + columns: [ + { dimensionKey: 'category', title: '品类' } + ], + indicators: [ + { + indicatorKey: 'sales', + title: '销售额', + width: 110, + format: (v) => `¥${(v ?? 0).toLocaleString()}`, + style: { textAlign: 'right' } + }, + { + indicatorKey: 'profit', + title: '利润', + width: 110, + format: (v) => `¥${(v ?? 0).toLocaleString()}`, + style: { + textAlign: 'right', + color: (args) => args.dataValue < 0 ? '#ff4d4f' : '#52c41a' + } + } + ], + indicatorsAsCol: true, // 指标在列方向 + dataConfig: { + aggregationRules: [ + { indicatorKey: 'sales', field: 'sales', aggregationType: VTable.TYPES.AggregationType.SUM }, + { indicatorKey: 'profit', field: 'profit', aggregationType: VTable.TYPES.AggregationType.SUM } + ], + totals: { + row: { + showGrandTotals: true, + showSubTotals: true, + grandTotalLabel: '总计', + subTotalLabel: '小计' + }, + column: { + showGrandTotals: true, + grandTotalLabel: '汇总' + } + } + }, + corner: { + titleOnDimension: 'row' + } +}); +``` + +## 3. 树形行表头 + +```javascript +const table = new VTable.PivotTable({ + container: document.getElementById('tableContainer'), + records: data, + rows: [ + { + dimensionKey: 'department', + title: '部门', + width: 180, + hierarchyType: 'tree' // 树形显示 + }, + { dimensionKey: 'team', title: '团队', width: 120 } + ], + columns: [ + { dimensionKey: 'month', title: '月份' } + ], + indicators: [ + { indicatorKey: 'headcount', title: '人数', width: 80 }, + { indicatorKey: 'budget', title: '预算', width: 100 } + ], + rowExpandLevel: 1, // 默认展开1层 + rowHierarchyIndent: 20 // 缩进像素 +}); +``` + +## 4. 自定义表头树(rowTree / columnTree) + +```javascript +const table = new VTable.PivotTable({ + container: document.getElementById('tableContainer'), + records: data, + // 手动定义行表头树 + rowTree: [ + { + dimensionKey: 'region', + value: '华东', + children: [ + { dimensionKey: 'city', value: '上海' }, + { dimensionKey: 'city', value: '杭州' } + ] + }, + { + dimensionKey: 'region', + value: '华北', + children: [ + { dimensionKey: 'city', value: '北京' }, + { dimensionKey: 'city', value: '天津' } + ] + } + ], + // 手动定义列表头树 + columnTree: [ + { + dimensionKey: 'quarter', + value: 'Q1', + children: [ + { indicatorKey: 'sales', value: '销售额' }, + { indicatorKey: 'profit', value: '利润' } + ] + }, + { + dimensionKey: 'quarter', + value: 'Q2', + children: [ + { indicatorKey: 'sales', value: '销售额' }, + { indicatorKey: 'profit', value: '利润' } + ] + } + ], + rows: [ + { dimensionKey: 'region', title: '区域', width: 120 }, + { dimensionKey: 'city', title: '城市', width: 100 } + ], + columns: [ + { dimensionKey: 'quarter', title: '季度' } + ], + indicators: [ + { indicatorKey: 'sales', title: '销售额', width: 100 }, + { indicatorKey: 'profit', title: '利润', width: 100 } + ], + corner: { + titleOnDimension: 'row', + headerStyle: { fontWeight: 'bold' } + } +}); +``` + +## 5. 排序和过滤 + +```javascript +const table = new VTable.PivotTable({ + container: document.getElementById('tableContainer'), + records: data, + rows: [ + { dimensionKey: 'category', title: '品类', width: 120, showSort: true } + ], + columns: [ + { dimensionKey: 'year', title: '年份' } + ], + indicators: [ + { indicatorKey: 'amount', title: '金额', width: 100, showSort: true } + ], + dataConfig: { + aggregationRules: [ + { indicatorKey: 'amount', field: 'amount', aggregationType: VTable.TYPES.AggregationType.SUM } + ], + sortRules: [ + { sortField: 'category', sortType: 'ASC' } // 按品类升序 + ], + filterRules: [ + { filterFunc: (record) => record.amount > 0 } // 过滤掉负值 + ] + } +}); + +// 监听排序事件 +table.on('pivot_sort_click', (args) => { + console.log('排序:', args); +}); +``` diff --git a/skills/vtable-development-assistant/references/knowledge/00-overview.md b/skills/vtable-development-assistant/references/knowledge/00-overview.md new file mode 100644 index 000000000..fce89ebaa --- /dev/null +++ b/skills/vtable-development-assistant/references/knowledge/00-overview.md @@ -0,0 +1,139 @@ +# VTable 整体架构概览 + +## 项目简介 + +VTable 是一款基于 VRender Canvas 引擎构建的高性能多维数据分析表格组件。它支持百万级数据量的流畅渲染,提供丰富的单元格类型和强大的自定义能力。 + +## 核心包 + +| 包名 | npm 包名 | 说明 | +|---|---|---| +| vtable | `@visactor/vtable` | 核心表格库 | +| vtable-editors | `@visactor/vtable-editors` | 编辑器组件(输入框、日期选择等) | +| vtable-export | `@visactor/vtable-export` | 导出功能(Excel、CSV) | +| vtable-search | `@visactor/vtable-search` | 搜索高亮功能 | +| vtable-plugins | `@visactor/vtable-plugins` | 官方插件集 | +| vtable-gantt | `@visactor/vtable-gantt` | 甘特图组件 | +| vtable-calendar | `@visactor/vtable-calendar` | 日历组件 | +| react-vtable | `@visactor/react-vtable` | React 封装 | +| vue-vtable | `@visactor/vue-vtable` | Vue 封装 | +| openinula-vtable | `@visactor/openinula-vtable` | OpenInula 封装 | + +## 三种核心表格类型 + +### 1. ListTable — 基本表格 +用于展示扁平的列表数据,类似传统的 HTML table。 + +```typescript +import { ListTable } from '@visactor/vtable'; + +const table = new ListTable({ + container: document.getElementById('container'), + records: [{ name: 'Alice', age: 25 }], + columns: [ + { field: 'name', title: '姓名', cellType: 'text' }, + { field: 'age', title: '年龄', cellType: 'text' } + ] +}); +``` + +**适用场景**: 数据列表、可编辑表格、树形表格、分组表格 + +### 2. PivotTable — 透视表 +用于多维数据分析,支持行/列维度交叉展示。 + +```typescript +import { PivotTable } from '@visactor/vtable'; + +const table = new PivotTable({ + container: document.getElementById('container'), + records: data, + rows: ['region', 'city'], + columns: ['category'], + indicators: [{ indicatorKey: 'sales', title: '销售额' }] +}); +``` + +**适用场景**: 数据聚合分析、多维度交叉报表 + +### 3. PivotChart — 透视图 +在透视表的基础上嵌入 VChart 图表,实现图表矩阵。 + +```typescript +import { PivotChart } from '@visactor/vtable'; + +const table = new PivotChart({ + container: document.getElementById('container'), + records: data, + rows: ['region'], + columns: ['category'], + indicators: [{ + indicatorKey: 'sales', + title: '销售额', + cellType: 'chart', + chartModule: 'vchart', + chartSpec: { type: 'bar' } + }] +}); +``` + +**适用场景**: 图表矩阵、多维图表分析 + +## 类型继承体系 + +``` +BaseTableConstructorOptions ← 公共配置(container, theme, widthMode, hover, select, menu...) + ├── ListTableConstructorOptions ← records, columns, transpose, sortState, editor, pagination... + ├── PivotTableConstructorOptions ← rows, columns, indicators, rowTree, columnTree, dataConfig, corner... + └── PivotChartConstructorOptions ← 同 PivotTable + axes, chartDimensionLinkage +``` + +## 核心导出 + +```typescript +// 表格类 +import { ListTable, PivotTable, PivotChart } from '@visactor/vtable'; + +// 类型命名空间 +import type * as VTable from '@visactor/vtable'; + +// 内置主题 +import { themes } from '@visactor/vtable'; +// themes.DEFAULT, themes.ARCO, themes.BRIGHT, themes.DARK, themes.SIMPLIFY + +// 注册系统 +import { register } from '@visactor/vtable'; +// register.bindChartModule(VChart) — 注册 VChart 模块用于 PivotChart + +// JSX 布局原语 +import { Group, Text, Rect, Circle, Image, Tag, Line, Checkbox, Radio } from '@visactor/vtable'; +``` + +## 渲染架构 + +VTable 基于 Canvas 场景图(Scenegraph)模式渲染: +1. **数据层**: DataSource / records → 数据管理 +2. **布局层**: LayoutMap → 行列映射、合并计算 +3. **场景图层**: Scenegraph → Group/Cell 节点树 +4. **渲染层**: VRender → Canvas 绑定绘制 + +## 源码目录结构(packages/vtable/src/) + +| 目录 | 说明 | +|---|---| +| `ts-types/` | 用户可见的 TypeScript 类型定义 | +| `core/` | 核心基础类(BaseTable、动画、样式计算) | +| `scenegraph/` | 场景图渲染系统 | +| `event/` | 事件系统(鼠标、键盘、滚动) | +| `state/` | 状态管理(选择、hover、冻结、排序) | +| `data/` | 数据源抽象 | +| `dataset/` | 数据集与统计 | +| `layout/` | 布局计算 | +| `themes/` | 内置主题定义 | +| `render/` | 渲染布局与 JSX | +| `components/` | 内置组件(tooltip, menu, legend, title) | +| `plugins/` | 插件系统 | +| `edit/` | 编辑管理 | +| `body-helper/` | body 区域样式计算 | +| `header-helper/` | 表头区域样式计算 | +| `tools/` | 工具函数 | diff --git a/skills/vtable-development-assistant/references/knowledge/01-table-types.md b/skills/vtable-development-assistant/references/knowledge/01-table-types.md new file mode 100644 index 000000000..6b042a622 --- /dev/null +++ b/skills/vtable-development-assistant/references/knowledge/01-table-types.md @@ -0,0 +1,326 @@ +# 表格类型与配置详解 + +## 一、公共配置 BaseTableConstructorOptions + +所有表格类型共享的配置项,定义于 `packages/vtable/src/ts-types/table-engine.ts`。 + +### 容器与尺寸 + +| 配置项 | 类型 | 默认值 | 说明 | +|---|---|---|---| +| `container` | `HTMLElement \| string` | **必填** | DOM 容器元素 | +| `widthMode` | `'standard' \| 'adaptive' \| 'autoWidth'` | `'standard'` | 列宽模式:固定/自适应容器/按内容 | +| `heightMode` | `'standard' \| 'adaptive' \| 'autoHeight'` | `'standard'` | 行高模式:固定/自适应容器/按内容 | +| `autoFillWidth` | `boolean` | `false` | 列总宽不足时自动拉伸填满容器 | +| `autoFillHeight` | `boolean` | `false` | 行总高不足时自动拉伸填满容器 | +| `defaultRowHeight` | `number \| 'auto'` | `40` | 默认行高 | +| `defaultColWidth` | `number` | `80` | 默认列宽 | +| `defaultHeaderRowHeight` | `number \| (number \| 'auto')[]` | — | 表头行高(可按行设置) | +| `defaultHeaderColWidth` | `(number \| 'auto') \| (number \| 'auto')[]` | — | 表头列宽 | +| `limitMaxAutoWidth` | `boolean \| number` | `450` | 自动计算列宽时的最大列宽限制 | +| `limitMinWidth` | `number` | — | 最小列宽限制 | +| `pixelRatio` | `number` | `devicePixelRatio` | 画布像素比 | + +### 冻结 + +| 配置项 | 类型 | 默认值 | 说明 | +|---|---|---|---| +| `frozenColCount` | `number` | `0` | 左侧冻结列数(含表头) | +| `frozenRowCount` | `number` | — | 顶部冻结行数 | +| `rightFrozenColCount` | `number` | `0` | 右侧冻结列数 | +| `bottomFrozenRowCount` | `number` | `0` | 底部冻结行数 | + +### 文字 + +| 配置项 | 类型 | 默认值 | 说明 | +|---|---|---|---| +| `autoWrapText` | `boolean` | `false` | 全局文字自动换行 | +| `enableLineBreak` | `boolean` | `false` | 是否识别换行符 `\n` | + +### 交互 — hover + +```typescript +hover: { + highlightMode: 'cross' | 'column' | 'row' | 'cell'; // 高亮模式,默认 'cross' + disableHover?: boolean; // 禁用 hover 效果 + disableHeaderHover?: boolean; // 禁用表头 hover + enableSingleCell?: boolean; // 是否使能单单元格 hover +} +``` + +### 交互 — select + +```typescript +select: { + disableSelect?: boolean; // 禁用选择 + disableHeaderSelect?: boolean; // 禁用表头选择 + highlightMode?: 'cross' | 'column' | 'row' | 'cell'; + headerSelectMode?: 'inline' | 'cell'; // 点击表头是选中整行/列还是单个表头 + blankAreaClickDeselect?: boolean; // 点击空白区域取消选中 +} +``` + +### 交互 — 键盘 + +```typescript +keyboardOptions: { + moveFocusCellOnTab?: boolean; // Tab 键移动焦点,默认 true + editCellOnEnter?: boolean; // Enter 键进入编辑,默认 true + moveFocusCellOnEnter?: boolean; // Enter 键移动焦点(与 editCellOnEnter 互斥) + moveEditCellOnArrowKeys?: boolean; // 方向键在编辑态切换单元格 + selectAllOnCtrlA?: boolean; // Ctrl+A 全选 + copySelected?: boolean; // Ctrl+C 复制 + pasteValueToCell?: boolean; // Ctrl+V 粘贴 + cutSelected?: boolean; // Ctrl+X 剪切 + moveSelectedCellOnArrowKeys?: boolean; // 方向键切换选中,默认 true + ctrlMultiSelect?: boolean; // Ctrl 多选 + shiftMultiSelect?: boolean; // Shift 范围选 +} +``` + +### 交互 — 列宽/行高调整 + +```typescript +resize: { + columnResizeMode?: 'all' | 'none' | 'header' | 'body'; // 列宽调整范围 + rowResizeMode?: 'all' | 'none' | 'header' | 'body'; + columnResizeWidth?: number; // 触发调整的热区宽度 +} +``` + +### 交互 — 拖拽 + +```typescript +dragOrder: { + dragHeaderMode?: 'all' | 'none' | 'column' | 'row'; // 拖拽表头模式 + frozenColDragHeaderMode?: 'disabled' | 'adjustFrozenCount' | 'fixedFrozenCount'; +} +``` + +### 菜单 + +```typescript +menu: { + renderMode?: 'canvas' | 'html'; + defaultHeaderMenuItems?: MenuListItem[] | ((args) => MenuListItem[]); + contextMenuItems?: MenuListItem[] | ((field, row, col, table) => MenuListItem[]); + dropDownMenuHighlight?: DropDownMenuHighlightInfo[]; +} +``` + +### 提示框 + +```typescript +tooltip: { + renderMode?: 'html' | 'canvas'; + isShowOverflowTextTooltip?: boolean; // 省略文字悬浮提示 + confine?: boolean; // 限制在表格区域内 + overflowTextTooltipDisappearDelay?: number; +} +``` + +### 主题与标题 + +| 配置项 | 类型 | 说明 | +|---|---|---| +| `theme` | `ITableThemeDefine` | 主题配置(详见 `03-style-theme.md`) | +| `title` | `ITitle` | 表格标题(text, subtext, orient, padding) | +| `emptyTip` | `IEmptyTip` | 数据为空提示 | + +### 其他 + +| 配置项 | 类型 | 说明 | +|---|---|---| +| `customMergeCell` | `CustomMergeCell` | 自定义合并单元格规则 | +| `rowSeriesNumber` | `IRowSeriesNumber` | 行序号列配置 | +| `overscrollBehavior` | `'auto' \| 'none'` | 滚动到边界的行为 | +| `renderChartAsync` | `boolean` | 图表异步渲染 | +| `animationAppear` | `IAnimationAppear` | 出场动画 | +| `customCellStyle` | `CustomCellStyle[]` | 预注册自定义样式 | +| `customCellStyleArrangement` | `CustomCellStyleArrangement[]` | 预分配自定义样式 | + +--- + +## 二、ListTable — 基本表格 + +定义于 `ListTableConstructorOptions`,继承 `BaseTableConstructorOptions`。 + +### 专有配置 + +| 配置项 | 类型 | 说明 | +|---|---|---| +| `records` | `any[]` | 数据数组,每项为一个记录对象 | +| `columns` | `ColumnsDefine` | 列定义数组(详见 `02-column-cell-types.md`) | +| `transpose` | `boolean` | 是否转置(行列互换) | +| `showHeader` | `boolean` | 是否显示表头 | +| `pagination` | `IPagination` | 分页配置 | +| `sortState` | `SortState \| SortState[]` | 排序状态 | +| `multipleSort` | `boolean` | 是否开启多列排序 | +| `editor` | `string \| IEditor \| Function` | 全局编辑器 | +| `headerEditor` | `string \| IEditor \| Function` | 全局表头编辑器 | +| `editCellTrigger` | `'doubleclick' \| 'click' \| 'api' \| 'keydown'` | 编辑触发方式 | +| `hierarchyIndent` | `number` | 树形缩进像素 | +| `hierarchyExpandLevel` | `number` | 树形默认展开层级 | +| `aggregation` | `Aggregation \| Aggregation[]` | 聚合汇总配置 | +| `groupConfig` | `GroupConfig` | 分组配置 | + +### 分页 IPagination + +```typescript +interface IPagination { + totalCount?: number; // 数据总条数 + perPageCount: number; // 每页条数 + currentPage?: number; // 当前页码(从 1 开始) +} +``` + +### 排序 SortState + +```typescript +interface SortState { + field: FieldDef; // 排序字段 + order: 'asc' | 'desc' | 'normal'; // 排序方向 + orderFn?: (a: any, b: any, order: string) => -1|0|1; // 自定义排序函数 +} +``` + +### 最小示例 + +```typescript +import { ListTable } from '@visactor/vtable'; + +const table = new ListTable({ + container: document.getElementById('container'), + records: [ + { id: 1, name: 'Alice', age: 25, city: 'Beijing' }, + { id: 2, name: 'Bob', age: 30, city: 'Shanghai' } + ], + columns: [ + { field: 'id', title: 'ID', width: 80 }, + { field: 'name', title: '姓名', width: 120 }, + { field: 'age', title: '年龄', width: 80, sort: true }, + { field: 'city', title: '城市', width: 120 } + ], + widthMode: 'standard', + defaultRowHeight: 40 +}); +``` + +--- + +## 三、PivotTable — 透视表 + +定义于 `PivotTableConstructorOptions`,继承 `BaseTableConstructorOptions`。 + +### 专有配置 + +| 配置项 | 类型 | 说明 | +|---|---|---| +| `records` | `any[]` | 平坦数据集 | +| `rows` | `(IRowDimension \| string)[]` | 行维度定义 | +| `columns` | `(IColumnDimension \| string)[]` | 列维度定义 | +| `indicators` | `(IIndicator \| string)[]` | 指标定义 | +| `rowTree` | `IHeaderTreeDefine[]` | 自定义行表头树 | +| `columnTree` | `IHeaderTreeDefine[]` | 自定义列表头树 | +| `indicatorsAsCol` | `boolean` | 指标按列展示 | +| `hideIndicatorName` | `boolean` | 隐藏指标名 | +| `corner` | `ICornerDefine` | 角头配置 | +| `showColumnHeader` | `boolean` | 显示列表头 | +| `showRowHeader` | `boolean` | 显示行表头 | +| `columnHeaderTitle` | `ITitleDefine` | 列表头维度名行 | +| `rowHeaderTitle` | `ITitleDefine` | 行表头维度名列 | +| `dataConfig` | `IPivotTableDataConfig` | 数据分析配置(聚合、排序、筛选规则) | +| `rowHierarchyType` | `'grid' \| 'tree' \| 'grid-tree'` | 行表头展示形式 | +| `rowExpandLevel` | `number` | 行展开层数 | +| `columnExpandLevel` | `number` | 列展开层数 | +| `rowHierarchyIndent` | `number` | 行缩进距离 | +| `pivotSortState` | `{ dimensions, order }[]` | 排序状态 | +| `pagination` | `IPagination` | 分页 | +| `editor` | `string \| IEditor \| Function` | 编辑器 | + +### 数据分析配置 dataConfig + +```typescript +interface IPivotTableDataConfig { + aggregationRules?: AggregationRule[]; // 聚合规则 + sortRules?: SortRule[]; // 排序规则 + filterRules?: FilterRule[]; // 筛选规则 + derivedFieldRules?: DerivedFieldRule[]; // 派生字段规则 + totals?: Totals; // 小计/总计配置 + mappingRules?: MappingRule[]; // 数据映射规则 +} +``` + +### 最小示例 + +```typescript +import { PivotTable } from '@visactor/vtable'; + +const table = new PivotTable({ + container: document.getElementById('container'), + records: [ + { region: '华东', category: '办公用品', sales: 1200 }, + { region: '华东', category: '家具', sales: 3400 }, + { region: '华北', category: '办公用品', sales: 800 }, + { region: '华北', category: '家具', sales: 2100 } + ], + rows: ['region'], + columns: ['category'], + indicators: [{ + indicatorKey: 'sales', + title: '销售额', + width: 'auto', + format: (value) => `¥${value?.toLocaleString() ?? ''}` + }], + corner: { + titleOnDimension: 'row' + } +}); +``` + +--- + +## 四、PivotChart — 透视图 + +定义于 `PivotChartConstructorOptions`,继承 `BaseTableConstructorOptions`。 + +### 与 PivotTable 的差异 + +| 差异点 | PivotTable | PivotChart | +|---|---|---| +| `indicators` | `IIndicator` | `IChartIndicator`(含 chartSpec) | +| `axes` | 无 | `ITableAxisOption[]` 坐标轴配置 | +| `chartDimensionLinkage` | 无 | 多图表联动交互 | +| `dataConfig` | 可选 | 内部自动开启 | + +### 最小示例 + +```typescript +import { PivotChart, register } from '@visactor/vtable'; +import VChart from '@visactor/vchart'; + +// 必须先注册 VChart 模块 +register.bindChartModule(VChart); + +const table = new PivotChart({ + container: document.getElementById('container'), + records: data, + rows: ['region'], + columns: ['category'], + indicators: [{ + indicatorKey: 'sales', + title: '销售额', + cellType: 'chart', + chartModule: 'vchart', + chartSpec: { + type: 'bar', + xField: ['category'], + yField: 'sales' + } + }], + axes: [ + { orient: 'bottom', type: 'band' }, + { orient: 'left', type: 'linear' } + ] +}); +``` diff --git a/skills/vtable-development-assistant/references/knowledge/02-column-cell-types.md b/skills/vtable-development-assistant/references/knowledge/02-column-cell-types.md new file mode 100644 index 000000000..cff6fac37 --- /dev/null +++ b/skills/vtable-development-assistant/references/knowledge/02-column-cell-types.md @@ -0,0 +1,325 @@ +# 列定义与单元格类型 + +## 一、列定义结构 + +ListTable 的 `columns` 配置使用 `ColumnsDefine` 类型,定义于 `packages/vtable/src/ts-types/list-table/define/`。 + +```typescript +type ColumnsDefine = (ColumnDefine | GroupColumnDefine)[]; + +// 13 种列定义的联合类型 +type ColumnDefine = TextColumnDefine | LinkColumnDefine | ImageColumnDefine | SparklineColumnDefine + | ProgressbarColumnDefine | ChartColumnDefine | CheckboxColumnDefine | RadioColumnDefine + | SwitchColumnDefine | ButtonColumnDefine | CompositeColumnDefine | VideoColumnDefine + | MultilineTextColumnDefine; + +// 分组列(嵌套表头) +interface GroupColumnDefine { + title: string; + headerStyle?: ColumnStyleOption; + headerIcon?: string | ColumnIconOption | (string | ColumnIconOption)[]; + columns: ColumnsDefine; // 递归嵌套 +} +``` + +## 二、公共列属性 (所有列类型共享) + +来自 `HeaderDefine` + `ColumnBodyDefine`: + +### 表头属性 (HeaderDefine) + +| 属性 | 类型 | 说明 | +|---|---|---| +| `title` | `string` | 列标题 | +| `headerType` | `ColumnTypeOption` | 表头单元格类型 | +| `headerStyle` | `ColumnStyleOption \| Function` | 表头样式 | +| `headerIcon` | `string \| ColumnIconOption \| Array` | 表头图标 | +| `headerCustomRender` | `ICustomRender` | 表头自定义渲染 | +| `headerCustomLayout` | `ICustomLayout` | 表头自定义布局 | +| `headerEditor` | `string \| IEditor \| Function` | 表头编辑器 | +| `sort` | `boolean \| Function` | 排序配置 | +| `showSort` | `boolean` | 仅显示排序图标不排序 | +| `dropDownMenu` | `MenuListItem[]` | 下拉菜单项 | +| `description` | `string` | 表头描述(hover 提示) | +| `disableHeaderHover` | `boolean` | 禁用表头 hover | +| `disableHeaderSelect` | `boolean` | 禁用表头选中 | + +### Body 属性 (ColumnBodyDefine) + +| 属性 | 类型 | 说明 | +|---|---|---| +| `field` | `string \| number \| string[]` | 数据字段名(支持嵌套 `'a.b.c'`) | +| `fieldFormat` | `Function` | 数据格式化函数 | +| `cellType` | `ColumnTypeOption` | 单元格类型 | +| `width` | `number \| string` | 列宽(支持百分比 `'20%'`) | +| `minWidth` | `number \| string` | 最小列宽 | +| `maxWidth` | `number \| string` | 最大列宽 | +| `style` | `ColumnStyleOption \| Function` | 单元格样式 | +| `icon` | `string \| ColumnIconOption \| Array \| Function` | 单元格图标 | +| `customRender` | `ICustomRender` | 自定义渲染 | +| `customLayout` | `ICustomLayout` | 自定义布局 | +| `editor` | `string \| IEditor \| Function` | 编辑器 | +| `tree` | `boolean` | 是否为树形展示列 | +| `mergeCell` | `boolean \| Function` | 合并单元格 | +| `aggregation` | `Aggregation \| Aggregation[]` | 聚合计算 | +| `hide` | `boolean` | 隐藏列 | +| `disableHover` | `boolean` | 禁用 hover | +| `disableSelect` | `boolean` | 禁用选中 | +| `disableColumnResize` | `boolean` | 禁止调整列宽 | +| `dragHeader` | `boolean` | 是否允许拖拽表头 | + +## 三、13 种单元格类型详解 + +### 1. text — 文本(默认类型) + +```typescript +{ + field: 'name', + title: '姓名', + cellType: 'text', // 可省略,默认就是 text + width: 120, + style: { + color: '#333', + fontSize: 14, + textAlign: 'left' + } +} +``` + +### 2. link — 链接 + +```typescript +{ + field: 'url', + title: '链接', + cellType: 'link', + linkJump: true, // 点击跳转 + linkDetect: true, // 自动检测 URL + templateLink: 'https://example.com/{id}', // URL 模板 + style: { + color: '#1890ff', + underline: true + } +} +``` + +### 3. image — 图片 + +```typescript +{ + field: 'avatar', + title: '头像', + cellType: 'image', + width: 80, + keepAspectRatio: true, // 保持宽高比 + imageAutoSizing: false, // 图片自适应大小 + style: { + margin: 4 // 图片边距 + } +} +``` + +### 4. video — 视频 + +```typescript +{ + field: 'videoUrl', + title: '视频', + cellType: 'video', + width: 200 +} +``` + +### 5. progressbar — 进度条 + +```typescript +{ + field: 'progress', + title: '进度', + cellType: 'progressbar', + min: 0, + max: 100, + barType: 'default', // 'default' | 'negative' | 'negative_no_axis' + style: { + barHeight: 20, + barColor: (value) => value > 80 ? '#52c41a' : '#1890ff', + barBgColor: '#f5f5f5', + barBorderRadius: 10 + } +} +``` + +### 6. sparkline — 迷你图 + +```typescript +{ + field: 'trend', + title: '趋势', + cellType: 'sparkline', + width: 200, + sparklineSpec: { + type: 'line', // 'line' | 'area' | 'bar' + xField: { field: 'x' }, + yField: { field: 'y' }, + line: { style: { stroke: '#1890ff', lineWidth: 2 } }, + point: { style: { fill: '#1890ff' } } + } +} +``` + +### 7. chart — 嵌入图表 + +需要先注册 VChart 模块:`register.bindChartModule(VChart)` + +```typescript +{ + field: 'chartData', + title: '图表', + cellType: 'chart', + chartModule: 'vchart', + width: 300, + chartSpec: { + type: 'bar', + data: { id: 'data' }, + xField: 'x', + yField: 'y' + } +} +``` + +### 8. checkbox — 复选框 + +```typescript +{ + field: 'checked', + title: '选中', + cellType: 'checkbox', + headerType: 'checkbox', // 表头也是复选框 + checked: true, // 默认值 + disable: false +} +``` + +### 9. radio — 单选框 + +```typescript +{ + field: 'selected', + title: '选择', + cellType: 'radio', + radioCheckType: 'column', // 'cell' | 'column' — 整列单选互斥 + radioDirectionInCell: 'horizontal' // 同一单元格内多选项排列方向 +} +``` + +### 10. switch — 开关 + +```typescript +{ + field: 'enabled', + title: '启用', + cellType: 'switch' +} +``` + +### 11. button — 按钮 + +```typescript +{ + field: 'action', + title: '操作', + cellType: 'button', + text: '编辑', // 按钮文字(也可以从 field 取值) + style: { + color: '#fff', + bgColor: '#1890ff', + borderRadius: 4, + padding: [4, 12] + } +} +``` + +### 12. composite — 组合单元格 + +在一个单元格中组合多种元素: + +```typescript +{ + title: '综合信息', + cellType: 'composite', + columns: [ + { cellType: 'image', field: 'avatar', width: 40 }, + { + cellType: 'composite', + columns: [ + { cellType: 'text', field: 'name', style: { fontWeight: 'bold' } }, + { cellType: 'text', field: 'desc', style: { fontSize: 12, color: '#999' } } + ], + layout: 'vertical' + } + ] +} +``` + +### 13. multilinetext — 多行文本 + +```typescript +{ + field: 'content', + title: '内容', + cellType: 'multilinetext', + autoWrapText: true, + style: { + autoWrapText: true, + lineHeight: 20 + } +} +``` + +## 四、列类型选择指南 + +| 数据类型 | 推荐 cellType | 备注 | +|---|---|---| +| 普通文本/数字 | `text` | 默认类型,最常用 | +| URL/链接 | `link` | 支持自动检测和跳转 | +| 图片 URL | `image` | 支持保持宽高比 | +| 布尔值/开关 | `checkbox` / `switch` | checkbox 支持表头全选 | +| 数值进度 | `progressbar` | 可配色、可显示负值 | +| 时序数据 | `sparkline` | 迷你折线/面积/柱状图 | +| 复杂图表 | `chart` | 集成 VChart,功能最强 | +| 单选 | `radio` | 支持整列互斥 | +| 多字段组合 | `composite` | 一个单元格展示多字段 | +| 操作按钮 | `button` | 支持点击事件 | + +## 五、列样式 ColumnStyleOption + +所有列类型的 `style` 和 `headerStyle` 属性使用 `ColumnStyleOption` 类型,支持静态值或回调函数: + +```typescript +// 静态样式 +style: { + bgColor: '#f5f5f5', + color: '#333', + fontSize: 14 +} + +// 动态样式(根据数据变化) +style: (args: StylePropertyFunctionArg) => { + return { + bgColor: args.dataValue > 100 ? '#e6f7ff' : '#fff', + color: args.dataValue > 100 ? '#1890ff' : '#333' + }; +} +``` + +`StylePropertyFunctionArg` 参数: +```typescript +{ + row: number; // 行号 + col: number; // 列号 + table: BaseTableAPI; // 表格实例 + value?: FieldData; // 格式化后的值 + dataValue?: FieldData; // 原始值 + cellHeaderPaths?: ICellHeaderPaths; // 表头路径 +} +``` diff --git a/skills/vtable-development-assistant/references/knowledge/03-style-theme.md b/skills/vtable-development-assistant/references/knowledge/03-style-theme.md new file mode 100644 index 000000000..b609ecc6e --- /dev/null +++ b/skills/vtable-development-assistant/references/knowledge/03-style-theme.md @@ -0,0 +1,273 @@ +# 样式系统与主题 + +## 一、样式属性速查 + +所有样式属性定义于 `packages/vtable/src/ts-types/style-define.ts`,可用于列定义的 `style`/`headerStyle` 和主题配置中。 + +### 文本样式 + +| 属性 | 类型 | 默认值 | 说明 | +|---|---|---|---| +| `color` | `string \| Function` | `'#000'` | 文字颜色 | +| `fontSize` | `number \| Function` | `14` | 字号 | +| `fontFamily` | `string \| Function` | `'sans-serif'` | 字体 | +| `fontWeight` | `string \| number \| Function` | `'normal'` | 字重 | +| `fontStyle` | `string \| Function` | `'normal'` | 字体样式(italic) | +| `textAlign` | `'left' \| 'center' \| 'right'` | `'left'` | 水平对齐 | +| `textBaseline` | `'top' \| 'middle' \| 'bottom'` | `'middle'` | 垂直对齐 | +| `lineHeight` | `number` | — | 行高 | +| `textOverflow` | `string` | `'...'` | 溢出省略字符 | +| `lineClamp` | `number \| 'auto'` | `1` | 最大行数 | +| `autoWrapText` | `boolean` | `false` | 自动换行 | +| `underline` | `boolean \| Function` | `false` | 下划线 | +| `lineThrough` | `boolean \| Function` | `false` | 删除线 | + +### 单元格样式 + +| 属性 | 类型 | 说明 | +|---|---|---| +| `bgColor` | `string \| Function` | 背景色(支持渐变、CanvasPattern) | +| `borderColor` | `string \| string[]` | 边框颜色(可分别设置四边) | +| `borderLineWidth` | `number \| number[]` | 边框宽度(可分别设置四边) | +| `borderLineDash` | `number[] \| number[][]` | 边框虚线样式 | +| `padding` | `number \| number[]` | 内边距(支持 [上,右,下,左]) | +| `cursor` | `string \| Function` | 鼠标指针样式 | + +### 特殊样式 + +| 属性 | 类型 | 说明 | +|---|---|---| +| `textStick` | `boolean` | 文字吸附(滚动时保持可见) | +| `textStickBaseOnAlign` | `boolean` | 吸附基于对齐方式 | +| `marked` | `boolean \| MarkCellStyle \| Function` | 单元格标记(角标) | +| `tag` | `string \| Function` | 标签文字 | + +### 标记样式 MarkCellStyle + +```typescript +{ + bgColor?: string; // 标记颜色 + shape?: 'rect' | 'triangle' | 'sector'; // 标记形状 + position?: 'left-top' | 'left-bottom' | 'right-top' | 'right-bottom'; + size?: number; // 标记大小 + offset?: number; // 偏移量 +} +``` + +## 二、动态样式(回调函数) + +所有样式属性都支持回调函数形式,参数为 `StylePropertyFunctionArg`: + +```typescript +const columns = [{ + field: 'score', + title: '分数', + style: (args) => ({ + // 根据分数值动态设置背景色 + bgColor: args.dataValue >= 90 ? '#f6ffed' : + args.dataValue >= 60 ? '#e6f7ff' : + '#fff1f0', + // 根据分数值动态设置字体颜色 + color: args.dataValue >= 90 ? '#52c41a' : + args.dataValue >= 60 ? '#1890ff' : + '#f5222d', + fontWeight: args.dataValue >= 90 ? 'bold' : 'normal' + }) +}]; +``` + +### StylePropertyFunctionArg 完整定义 + +```typescript +interface StylePropertyFunctionArg { + row: number; // 行号 + col: number; // 列号 + table: BaseTableAPI; // 表格实例 + value?: FieldData; // 格式化后的展示值 + dataValue?: FieldData; // 原始数据值 + percentile?: number; // 百分位(进度条用) + cellHeaderPaths?: ICellHeaderPaths; // 表头路径(透视表用) +} +``` + +## 三、主题系统 + +### 内置主题 + +VTable 提供 5 种内置主题: + +```typescript +import { themes } from '@visactor/vtable'; + +// 使用内置主题 +const table = new ListTable({ + theme: themes.ARCO, // 或 themes.DEFAULT, themes.BRIGHT, themes.DARK, themes.SIMPLIFY + // ... +}); +``` + +| 主题 | 风格 | +|---|---| +| `themes.DEFAULT` | 默认主题,浅色系 | +| `themes.ARCO` | ArcoDesign 风格 | +| `themes.BRIGHT` | 明亮风格 | +| `themes.DARK` | 暗色主题 | +| `themes.SIMPLIFY` | 简约风格 | + +### 自定义主题 ITableThemeDefine + +```typescript +const myTheme: VTable.ITableThemeDefine = { + // 默认样式 + defaultStyle: { + color: '#333', + bgColor: '#fff', + fontSize: 14, + fontFamily: 'PingFang SC', + borderColor: '#e1e4e8', + borderLineWidth: 1, + padding: [8, 12] + }, + + // 表头样式 + headerStyle: { + color: '#1f2329', + bgColor: '#f5f7fa', + fontSize: 14, + fontWeight: 'bold', + borderColor: '#e1e4e8', + borderLineWidth: 1, + padding: [8, 12] + }, + + // 行表头样式(透视表用) + rowHeaderStyle: { + color: '#1f2329', + bgColor: '#f5f7fa' + }, + + // 角头样式(透视表用) + cornerHeaderStyle: { + color: '#1f2329', + bgColor: '#e8eaed' + }, + + // body 样式 + bodyStyle: { + color: '#333', + bgColor: '#fff', + hover: { + cellBgColor: '#e6f7ff', + inlineColumnBgColor: '#f0f5ff', + inlineRowBgColor: '#f0f5ff' + } + }, + + // 选择框样式 + selectionStyle: { + cellBorderColor: '#1890ff', + cellBorderLineWidth: 2, + cellBgColor: 'rgba(24,144,255,0.1)' + }, + + // 边框样式 + frameStyle: { + borderColor: '#e1e4e8', + borderLineWidth: 1, + borderLineDash: [], + cornerRadius: 4, // 表格圆角 + shadowBlur: 0 + }, + + // 滚动条样式 + scrollStyle: { + visible: 'scrolling', // 'always' | 'scrolling' | 'none' | 'focus' + scrollSliderColor: 'rgba(0,0,0,0.3)', + scrollRailColor: 'rgba(0,0,0,0.05)', + width: 7, + hoverOn: true // hover 时变粗 + }, + + // 冻结列分割线样式 + frozenColumnLine: { + shadow: { + width: 4, + startColor: 'rgba(0,0,0,0.05)', + endColor: 'transparent' + } + }, + + // 提示框样式 + tooltipStyle: { + bgColor: '#333', + color: '#fff', + fontSize: 12, + padding: [6, 8] + } +}; +``` + +### 基于内置主题扩展 + +```typescript +import { themes } from '@visactor/vtable'; + +const customTheme = { + ...themes.ARCO, + bodyStyle: { + ...themes.ARCO.bodyStyle, + color: '#1f2329', + hover: { + cellBgColor: '#e8f3ff', + inlineColumnBgColor: '#f2f3f5', + inlineRowBgColor: '#f2f3f5' + } + }, + selectionStyle: { + cellBorderColor: '#165DFF', + cellBorderLineWidth: 2, + cellBgColor: 'rgba(22,93,255,0.1)' + } +}; +``` + +## 四、registerCustomCellStyle — 运行时自定义样式 + +可以在运行时注册和分配自定义样式: + +```typescript +// 1. 注册样式 +table.registerCustomCellStyle('highlight', { + bgColor: '#ffffcc', + color: '#f5222d', + fontWeight: 'bold' +}); + +// 2. 分配样式到单元格 +table.arrangeCustomCellStyle({ + cellPosition: { col: 2, row: 3 }, // 单个单元格 + customStyleId: 'highlight' +}); + +// 或分配到区域 +table.arrangeCustomCellStyle({ + cellPosition: { + range: { start: { col: 1, row: 1 }, end: { col: 3, row: 5 } } + }, + customStyleId: 'highlight' +}); +``` + +也可以通过配置项预注册: + +```typescript +const table = new ListTable({ + customCellStyle: [ + { id: 'warning', style: { bgColor: '#fff7e6', color: '#fa8c16' } }, + { id: 'error', style: { bgColor: '#fff1f0', color: '#f5222d' } } + ], + customCellStyleArrangement: [ + { cellPosition: { col: 1, row: 2 }, customStyleId: 'warning' } + ] +}); +``` diff --git a/skills/vtable-development-assistant/references/knowledge/04-custom-render-layout.md b/skills/vtable-development-assistant/references/knowledge/04-custom-render-layout.md new file mode 100644 index 000000000..d45986930 --- /dev/null +++ b/skills/vtable-development-assistant/references/knowledge/04-custom-render-layout.md @@ -0,0 +1,354 @@ +# 自定义渲染与布局 + +VTable 提供两套自定义渲染方案:**Custom Render**(低级元素 API)和 **Custom Layout**(高级 JSX 布局 API)。 + +## 一、Custom Render — 元素 API + +### 使用方式 + +在列定义中配置 `customRender` 或 `headerCustomRender`: + +```typescript +{ + field: 'info', + title: '信息', + customRender: (args: CustomRenderFunctionArg) => { + return { + renderDefault: false, // 是否也渲染默认内容 + elements: [ + // 元素数组 + ] + }; + } +} +``` + +### CustomRenderFunctionArg 参数 + +```typescript +interface CustomRenderFunctionArg { + col: number; // 列号 + row: number; // 行号 + value: FieldData; // 格式化后的值 + dataValue: FieldData; // 原始值 + rect: RectProps; // 单元格矩形 { left, right, top, bottom, width, height } + table: BaseTableAPI; // 表格实例 + cellHeaderPaths?: ICellHeaderPaths; // 表头路径 +} +``` + +### 7 种元素类型 + +#### text — 文本 + +```typescript +{ + type: 'text', + x: 10, y: 10, + text: '标题文字', + fill: '#333', + fontSize: 14, + fontFamily: 'PingFang SC', + fontWeight: 'bold', + textAlign: 'left', + textBaseline: 'top', + ellipsis: true, // 溢出省略 + lineClamp: 2, // 最大行数 + maxLineWidth: 100 // 最大行宽 +} +``` + +#### rect — 矩形 + +```typescript +{ + type: 'rect', + x: 0, y: 0, + width: 100, height: 30, + fill: '#e6f7ff', + stroke: '#1890ff', + lineWidth: 1, + radius: 4 // 圆角 +} +``` + +#### circle — 圆形 + +```typescript +{ + type: 'circle', + x: 20, y: 20, + radius: 10, + fill: '#52c41a', + stroke: '#389e0d', + lineWidth: 1 +} +``` + +#### arc — 弧形 + +```typescript +{ + type: 'arc', + x: 30, y: 30, + radius: 20, + startAngle: 0, + endAngle: Math.PI * 1.5, + fill: '#1890ff', + stroke: '#096dd9', + lineWidth: 2 +} +``` + +#### line — 线段 + +```typescript +{ + type: 'line', + points: [ + { x: 0, y: 20 }, + { x: 50, y: 10 }, + { x: 100, y: 30 } + ], + stroke: '#1890ff', + lineWidth: 2 +} +``` + +#### icon — SVG 图标 + +```typescript +{ + type: 'icon', + x: 5, y: 5, + width: 20, height: 20, + svg: '...', + hover: { + width: 22, height: 22 // hover 时放大 + } +} +``` + +#### image — 图片 + +```typescript +{ + type: 'image', + x: 5, y: 5, + width: 40, height: 40, + src: 'https://example.com/avatar.png', + shape: 'circle', // 'circle' | 'square' + hover: { + width: 44, height: 44 + } +} +``` + +### 完整示例 + +```typescript +{ + field: 'user', + title: '用户', + width: 200, + customRender: (args) => { + const { rect, dataValue, table } = args; + const record = table.getCellOriginRecord(args.col, args.row); + return { + renderDefault: false, + elements: [ + // 头像 + { + type: 'image', + x: 10, y: 8, + width: 32, height: 32, + src: record.avatar, + shape: 'circle' + }, + // 用户名 + { + type: 'text', + x: 50, y: 10, + text: record.name, + fill: '#1f2329', + fontSize: 14, + fontWeight: 'bold' + }, + // 描述 + { + type: 'text', + x: 50, y: 28, + text: record.desc, + fill: '#86909c', + fontSize: 12 + } + ] + }; + } +} +``` + +--- + +## 二、Custom Layout — JSX 布局 API(推荐) + +Custom Layout 使用 VRender 的场景图原语,支持 **Flex 布局**,开发体验更好。 + +### 使用方式 + +```typescript +import { Group, Text, Image, Rect, Tag } from '@visactor/vtable'; + +{ + field: 'user', + title: '用户', + width: 250, + customLayout: (args) => { + const { table, row, col, rect, value } = args; + const record = table.getCellOriginRecord(col, row); + + const container = ( + + + + + + + + ); + + return { + rootContainer: container, + renderDefault: false + }; + } +} +``` + +### 可用 JSX 原语 + +| 原语 | 说明 | 关键属性 | +|---|---|---| +| `Group` | 容器/布局 | `display`, `flexDirection`, `alignItems`, `justifyContent`, `flexWrap`, `gap` | +| `Text` | 文本 | `text`, `fontSize`, `fill`, `fontWeight`, `maxLineWidth`, `ellipsis` | +| `Rect` | 矩形 | `width`, `height`, `fill`, `stroke`, `cornerRadius` | +| `Circle` | 圆形 | `radius`, `fill`, `stroke` | +| `Image` | 图片 | `src`, `width`, `height`, `cornerRadius` | +| `Arc` | 弧形 | `radius`, `startAngle`, `endAngle`, `fill` | +| `Tag` | 标签 | `text`, `fill`, `textStyle`, `padding`, `cornerRadius` | +| `Line` | 线段 | `points`, `stroke`, `lineWidth` | +| `Checkbox` | 复选框 | `checked`, `text` | +| `Radio` | 单选框 | `checked`, `text` | + +### Flex 布局属性 + +Group 支持完整的 Flex 布局: + +```typescript + + {/* 子元素 */} + +``` + +### 事件绑定 + +JSX 元素支持事件绑定: + +```typescript + { /* hover效果 */ }} + onMouseLeave={(event) => { /* 取消hover */ }} + onClick={(event) => { + console.log('点击了', args.row, args.col); + }} +> + + +``` + +### 响应式自定义样式 + +```typescript +customLayout: (args) => { + const { rect, table, col, row } = args; + const record = table.getCellOriginRecord(col, row); + + // 根据数据动态生成标签 + const tags = record.tags?.map((tag, i) => ( + + )) ?? []; + + return { + rootContainer: ( + + {tags} + + ), + renderDefault: false + }; +} +``` + +--- + +## 三、全局自定义渲染 + +可在表格级别设置全局 customRender/customLayout: + +```typescript +const table = new ListTable({ + customRender: (args) => { + // 对所有单元格生效 + // ... + }, + headerCustomRender: (args) => { + // 对所有表头单元格生效 + // ... + }, + customLayout: (args) => { + // 全局 JSX 布局 + // ... + } +}); +``` + +列定义上的 customRender/customLayout 优先级高于全局配置。 + +--- + +## 四、两套方案对比 + +| 特性 | Custom Render | Custom Layout | +|---|---|---| +| 定位方式 | 绝对坐标 (x, y) | Flex 布局(自动排列) | +| 学习成本 | 低 | 中(需了解 Flex) | +| 布局能力 | 手动计算位置 | 自动 Flex 排列 | +| 事件支持 | 有限 | 完整事件系统 | +| 适用场景 | 简单图元组合 | 复杂卡片式布局 | +| 推荐程度 | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | + +**建议**: 新项目优先使用 Custom Layout(JSX 方案),只在极简场景下使用 Custom Render。 diff --git a/skills/vtable-development-assistant/references/knowledge/05-api-methods.md b/skills/vtable-development-assistant/references/knowledge/05-api-methods.md new file mode 100644 index 000000000..4c21d1db3 --- /dev/null +++ b/skills/vtable-development-assistant/references/knowledge/05-api-methods.md @@ -0,0 +1,201 @@ +# API 方法速查 + +表格实例方法,通过 `tableInstance.methodName()` 调用。完整文档: `docs/assets/api/zh/methods.md`。 + +## 生命周期 + +| 方法 | 签名 | 说明 | +|---|---|---| +| `updateOption` | `(option: TableConstructorOptions) => void` | 全量更新配置并重绘 | +| `renderWithRecreateCells` | `() => void` | 重建单元格并重新渲染(批量配置更新后调用) | +| `release` | `() => void` | **销毁表格实例**(必须调用,释放资源) | + +## 数据操作 + +| 方法 | 签名 | 说明 | 适用表格 | +|---|---|---|---| +| `setRecords` | `(records: any[], option?: { sortState }) => void` | 设置/更新全量数据 | 全部 | +| `addRecord` | `(record: any, recordIndex?: number) => void` | 添加单条数据 | ListTable | +| `addRecords` | `(records: any[], recordIndex?: number) => void` | 添加多条数据 | ListTable | +| `deleteRecords` | `(recordIndexs: number[]) => void` | 删除多条数据 | ListTable | +| `updateRecords` | `(records: any[], recordIndexs: (number\|number[])[]) => void` | 更新多条数据 | ListTable | +| `changeCellValue` | `(col, row, value, workOnEditableCell?) => void` | 更改单个单元格值 | 全部 | +| `changeCellValues` | `(col, row, values[][]) => void` | 批量更改单元格值 | 全部 | +| `setRecordChildren` | `(children, recordIndex) => void` | 树形结构插入子节点 | ListTable | +| `setTreeNodeChildren` | `(children, ...) => void` | 透视表树形插入子节点 | PivotTable | +| `updateFilterRules` | `(filterRules) => void` | 更新数据过滤规则 | ListTable | +| `getFilteredRecords` | `() => any[]` | 获取过滤后的数据 | ListTable | + +## 查询 + +| 方法 | 签名 | 说明 | +|---|---|---| +| `getCellValue` | `(col, row) => string` | 获取单元格**格式化后**的展示值 | +| `getCellOriginValue` | `(col, row) => any` | 获取单元格 format 前的值 | +| `getCellRawValue` | `(col, row) => any` | 获取数据源最原始值(从 records 直取) | +| `getCellStyle` | `(col, row) => CellStyle` | 获取单元格样式对象 | +| `getCellRange` | `(col, row) => CellRange` | 获取单元格合并范围 | +| `getCellRect` | `(col, row) => RectProps` | 获取单元格在表格中的位置 | +| `getCellRelativeRect` | `(col, row) => RectProps` | 获取相对于左上角的位置(含滚动) | +| `getCellHeaderPaths` | `(col, row) => ICellHeaderPaths` | 获取行列表头路径 | +| `getCellHeaderTreeNodes` | `(col, row) => object` | 获取表头树节点(含自定义属性) | +| `getRecordByCell` | `(col, row) => any` | 获取完整数据记录 | +| `getCellOriginRecord` | `(col, row) => any` | 获取源数据项 | +| `getCellOverflowText` | `(col, row) => string` | 获取省略文字全文 | +| `getCellAddress` | `(findRecord, field) => CellAddress` | 根据数据查找地址 (ListTable) | +| `getCellAddressByHeaderPaths` | `(paths) => CellAddress` | 根据表头路径查找地址 (PivotTable) | +| `getBodyColumnDefine` | `(col, row) => ColumnDefine` | 获取列配置定义 | +| `getHeaderField` | `(col, row) => string` | 获取 field / indicatorKey | +| `getAllCells` | `(range?) => CellInfo[]` | 获取所有单元格信息 | +| `getAllBodyCells` | `(range?) => CellInfo[]` | 获取 body 区域单元格 | +| `getAllColumnHeaderCells` | `() => CellInfo[]` | 获取列表头单元格 | +| `getAllRowHeaderCells` | `() => CellInfo[]` | 获取行表头单元格 | + +## 索引转换 + +| 方法 | 说明 | 适用表格 | +|---|---|---| +| `getBodyIndexByTableIndex(col, row)` | 表格行列号 → body 索引 | 全部 | +| `getTableIndexByBodyIndex(col, row)` | body 索引 → 表格行列号 | 全部 | +| `getTableIndexByRecordIndex(recordIndex)` | 数据索引 → 表格行列号 | ListTable | +| `getRecordIndexByCell(col, row)` | 单元格 → 数据索引 | ListTable | +| `getBodyRowIndexByRecordIndex(index)` | 数据索引 → body 行号 | ListTable | +| `getTableIndexByField(field)` | 字段名 → 列号 | ListTable | +| `getRecordShowIndexByCell(col, row)` | 单元格 → body 展示索引 | ListTable | +| `getCellAddrByFieldRecord(field, index)` | 字段+数据索引 → 行列号 | ListTable | + +## 选择 + +| 方法 | 签名 | 说明 | +|---|---|---| +| `selectCell` | `(col, row) => void` | 选中单元格(传空清除) | +| `selectCells` | `(ranges: CellRange[]) => void` | 选中多个区域 | +| `selectRow` | `(row, isShift?, isCtrl?) => void` | 选中整行 | +| `selectCol` | `(col, isShift?, isCtrl?) => void` | 选中整列 | +| `getSelectedCellInfos` | `() => CellInfo[][]` | 获取选中单元格信息 | +| `clearSelected` | `() => void` | 清除选中 | +| `getCopyValue` | `() => string` | 获取选中区域复制内容 | + +## 滚动 + +| 方法 | 签名 | 说明 | +|---|---|---| +| `getScrollTop` | `() => number` | 获取竖向滚动位置 | +| `getScrollLeft` | `() => number` | 获取横向滚动位置 | +| `setScrollTop` | `(top: number) => void` | 设置竖向滚动位置 | +| `setScrollLeft` | `(left: number) => void` | 设置横向滚动位置 | +| `scrollToCell` | `(cellAddr: { col?, row? }) => void` | 滚动到指定单元格 | +| `disableScroll` | `() => void` | 禁用滚动 | +| `enableScroll` | `() => void` | 启用滚动 | + +## 编辑 + +| 方法 | 签名 | 说明 | +|---|---|---| +| `startEditCell` | `(col?, row?, value?) => void` | 开启单元格编辑 | +| `completeEditCell` | `() => void` | 结束编辑(保存) | +| `cancelEditCell` | `() => void` | 取消编辑(不保存) | +| `getEditor` | `(col, row) => IEditor` | 获取单元格编辑器 | + +## 树形操作 + +| 方法 | 说明 | 适用表格 | +|---|---|---| +| `toggleHierarchyState(col, row)` | 切换展开/收起 | 全部 | +| `getHierarchyState(col, row)` | 获取展开状态 | 全部 | +| `expandAllTreeNode()` | 展开所有节点 | ListTable | +| `collapseAllTreeNode()` | 折叠所有节点 | ListTable | +| `expandAllForRowTree()` | 展开行表头所有节点 | PivotTable | +| `collapseAllForRowTree()` | 折叠行表头所有节点 | PivotTable | +| `expandAllForColumnTree()` | 展开列表头所有节点 | PivotTable | +| `collapseAllForColumnTree()` | 折叠列表头所有节点 | PivotTable | + +## 布局更新 + +| 方法 | 说明 | 适用表格 | +|---|---|---| +| `updateColumns(columns)` | 更新列定义 | ListTable | +| `updatePagination(pagination)` | 更新分页 | 全部 | +| `updateTheme(theme)` | 更新主题 | 全部 | +| `updateSortState(sortState)` | 更新排序状态 | ListTable | +| `updateSortRules(sortRules)` | 全量更新排序规则 | PivotTable | +| `updatePivotSortState(state)` | 更新透视排序(仅图标) | PivotTable | +| `setDropDownMenuHighlight(info)` | 设置菜单选中高亮 | 全部 | + +## 尺寸 + +| 方法 | 签名 | 说明 | +|---|---|---| +| `getColWidth` | `(col) => number` | 获取列宽 | +| `getRowHeight` | `(row) => number` | 获取行高 | +| `setColWidth` | `(col, width) => void` | 设置列宽 | +| `setRowHeight` | `(row, height) => void` | 设置行高 | +| `getAllRowsHeight` | `() => number` | 获取所有行总高 | +| `getAllColsWidth` | `() => number` | 获取所有列总宽 | +| `setCanvasSize` | `(w, h) => void` | 设置画布尺寸 | +| `setPixelRatio` | `(ratio) => void` | 设置像素比 | + +## 导出 + +| 方法 | 签名 | 说明 | +|---|---|---| +| `exportImg` | `() => string` | 导出可视区域为 base64 图片 | +| `exportCellImg` | `(col, row, options?) => string` | 导出单个单元格图片 | +| `exportCellRangeImg` | `(range) => string` | 导出单元格区域图片 | + +## 事件 + +| 方法 | 签名 | 说明 | +|---|---|---| +| `on` | `(type, listener) => EventListenerId` | 监听事件 | +| `off` | `(idOrType, listener?) => void` | 移除监听 | +| `onVChartEvent` | `(type, query, listener) => void` | 监听 VChart 事件 | +| `offVChartEvent` | `(type, listener) => void` | 移除 VChart 事件 | + +## Checkbox / Radio / Switch 状态 + +| 方法 | 说明 | +|---|---| +| `getCheckboxState(field?)` | 获取 checkbox 选中状态 | +| `getCellCheckboxState(col, row)` | 获取单元格 checkbox 状态 | +| `setCellCheckboxState(col, row, checked)` | 设置 checkbox 状态 | +| `getRadioState(field?)` | 获取 radio 选中状态 | +| `getCellRadioState(col, row)` | 获取单元格 radio 状态 | +| `setCellRadioState(col, row, index)` | 设置 radio 选中 | +| `getSwitchState(field?)` | 获取 switch 状态 | +| `getCellSwitchState(col, row)` | 获取单元格 switch 状态 | +| `setCellSwitchState(col, row, open)` | 设置 switch 状态 | + +## 可视区域查询 + +| 方法 | 说明 | +|---|---| +| `getBodyVisibleCellRange()` | 获取 body 可见单元格范围 | +| `getBodyVisibleColRange()` | 获取 body 可见列范围 | +| `getBodyVisibleRowRange()` | 获取 body 可见行范围 | +| `cellIsInVisualView(col, row)` | 判断单元格是否完全可见 | +| `getCellAtRelativePosition(x, y)` | 获取坐标处的单元格 | + +## 其他 + +| 方法 | 说明 | +|---|---| +| `getDrawRange()` | 获取实际绘制区域 | +| `showTooltip(col, row, tooltipOptions)` | 显示提示框 | +| `showDropdownMenu(col, row, items)` | 显示下拉菜单 | +| `setLegendSelected(selectedData)` | 设置图例选中 | +| `getChartDatumPosition(...)` | 获取图表图元位置 | +| `registerCustomCellStyle(id, style)` | 注册自定义样式 | +| `arrangeCustomCellStyle(config)` | 分配自定义样式 | +| `setSortedIndexMap(field, map)` | 设置预排序索引 | +| `showMoverLine(col, row)` | 显示移动标记线 | +| `hideMoverLine()` | 隐藏移动标记线 | +| `updateCellContent(col, row)` | 更新单元格内容 | +| `updateCellContentRange(range)` | 更新区域内容 | +| `setTranslate(x, y)` | 设置偏移量 | +| `setLoadingHierarchyState(col, row)` | 设置加载中状态 | +| `getAggregateValuesByField(field)` | 获取聚合值 | +| `isAggregation(col, row)` | 判断是否聚合单元格 | +| `refreshAfterSourceChange()` | 源数据变更后刷新 | +| `getLayoutRowTree()` | 获取行表头树 (PivotTable) | +| `getLayoutColumnTree()` | 获取列表头树 (PivotTable) | diff --git a/skills/vtable-development-assistant/references/knowledge/06-api-properties.md b/skills/vtable-development-assistant/references/knowledge/06-api-properties.md new file mode 100644 index 000000000..af0da2e6f --- /dev/null +++ b/skills/vtable-development-assistant/references/knowledge/06-api-properties.md @@ -0,0 +1,55 @@ +# API 属性速查 + +表格实例属性,通过 `tableInstance.propertyName` 访问。完整文档: `docs/assets/api/zh/properties.md`。 + +## 只读属性 + +| 属性 | 类型 | 说明 | +|---|---|---| +| `records` | `any[]` | 当前表格数据数组(用 `setRecords()` 更新) | +| `options` | `TableConstructorOptions` | 当前表格配置(用 `updateOption()` 更新) | +| `columns` | `ColumnsDefine` | 当前列定义(ListTable,用 `updateColumns()` 更新) | +| `rowCount` | `number` | 表格总行数(含表头) | +| `colCount` | `number` | 表格总列数(含行表头) | +| `rowHeaderLevelCount` | `number` | 行表头层级数 | +| `columnHeaderLevelCount` | `number` | 列表头层级数 | +| `frozenColCount` | `number` | 左侧冻结列数 | +| `frozenRowCount` | `number` | 顶部冻结行数 | +| `rightFrozenColCount` | `number` | 右侧冻结列数 | +| `bottomFrozenRowCount` | `number` | 底部冻结行数 | +| `theme` | `ITableThemeDefine` | 当前主题(用 `updateTheme()` 更新) | +| `pixelRatio` | `number` | 画布像素比(用 `setPixelRatio()` 更新) | +| `rowHierarchyType` | `'grid' \| 'tree'` | 行层次展示方式(PivotTable 只读) | + +## 可读写属性 + +| 属性 | 类型 | 说明 | 注意 | +|---|---|---|---| +| `scrollLeft` | `number` | 横向滚动位置 | 赋值后自动重绘 | +| `scrollTop` | `number` | 纵向滚动位置 | 赋值后自动重绘 | +| `widthMode` | `'standard' \| 'adaptive' \| 'autoWidth'` | 列宽模式 | 赋值后需手动 `renderWithRecreateCells()` | +| `heightMode` | `'standard' \| 'adaptive' \| 'autoHeight'` | 行高模式 | 赋值后需手动 `renderWithRecreateCells()` | +| `autoWrapText` | `boolean` | 自动换行 | 赋值后需刷新 | +| `transpose` | `boolean` | 转置显示 | 赋值后需重建 | + +## 属性使用示例 + +```typescript +const table = new ListTable(options); + +// 读取数据 +console.log(table.records.length); // 数据条数 +console.log(table.rowCount); // 总行数 +console.log(table.colCount); // 总列数 + +// 滚动控制 +table.scrollTop = 100; // 滚动到 100px 位置 +table.scrollLeft = 50; + +// 修改模式(需手动刷新) +table.widthMode = 'adaptive'; +table.renderWithRecreateCells(); + +// 获取冻结信息 +console.log(`冻结 ${table.frozenColCount} 列, ${table.frozenRowCount} 行`); +``` diff --git a/skills/vtable-development-assistant/references/knowledge/07-events.md b/skills/vtable-development-assistant/references/knowledge/07-events.md new file mode 100644 index 000000000..fa47b7a96 --- /dev/null +++ b/skills/vtable-development-assistant/references/knowledge/07-events.md @@ -0,0 +1,249 @@ +# 事件系统 + +## 使用方式 + +```typescript +// 监听事件 +const listenerId = tableInstance.on('click_cell', (args) => { + console.log('点击了', args.col, args.row); +}); + +// 移除监听(方式一:通过 ID) +tableInstance.off(listenerId); + +// 移除监听(方式二:通过类型+函数) +tableInstance.off('click_cell', handler); +``` + +## 事件分类速查 + +### 鼠标事件 + +| 事件名 | 参数类型 | 说明 | +|---|---|---| +| `click_cell` | `MousePointerCellEvent` | 单击单元格 | +| `dblclick_cell` | `MousePointerCellEvent` | 双击单元格 | +| `mouseenter_cell` | `MousePointerCellEvent` | 鼠标进入单元格 | +| `mouseleave_cell` | `MousePointerCellEvent` | 鼠标离开单元格 | +| `mousemove_cell` | `MousePointerCellEvent` | 鼠标在单元格上移动 | +| `mousedown_cell` | `MousePointerCellEvent` | 单元格鼠标按下 | +| `mouseup_cell` | `MousePointerCellEvent` | 单元格鼠标松开 | +| `contextmenu_cell` | `MousePointerCellEvent` | 单元格右键 | +| `mouseenter_table` | — | 鼠标进入表格 | +| `mouseleave_table` | — | 鼠标离开表格 | +| `mousedown_table` | — | 鼠标在表格按下 | +| `contextmenu_canvas` | — | 画布区域右键 | + +### MousePointerCellEvent 参数 + +```typescript +{ + col: number; // 列号 + row: number; // 行号 + value: FieldData; // 格式化后的值 + dataValue: FieldData; // 原始值 + cellRange: CellRange; // 单元格范围 + cellType: string; // 单元格类型 + title: string; // 列标题 + field: FieldDef; // 字段名 + cellLocation: CellLocation; // 'body' | 'rowHeader' | 'columnHeader' | 'cornerHeader' + originData: any; // 完整记录 + event: MouseEvent; // 原生事件 + target: any; // 场景图节点 + targetCell: any; // 单元格节点 +} +``` + +### 选择事件 + +| 事件名 | 说明 | +|---|---| +| `selected_cell` | 单元格选中状态改变 | +| `selected_clear` | 清除所有选中 | +| `selected_changed` | 选中范围改变(拖拽选择过程中持续触发) | +| `drag_select_end` | 拖拽框选鼠标松开 | +| `copy_data` | 复制内容 | + +### 滚动事件 + +| 事件名 | 说明 | +|---|---| +| `scroll` | 滚动事件(scrollLeft, scrollTop, scrollDirection) | +| `scroll_vertical_end` | 竖向滚动到底部 | +| `scroll_horizontal_end` | 横向滚动到最右 | + +### 调整大小事件 + +| 事件名 | 说明 | +|---|---| +| `resize_column` | 列宽调整中(持续触发) | +| `resize_column_end` | 列宽调整完成 | +| `resize_row` | 行高调整中 | +| `resize_row_end` | 行高调整完成 | + +### 拖拽排序事件 + +| 事件名 | 说明 | +|---|---| +| `change_header_position` | 拖拽移动表头完成 | +| `change_header_position_start` | 开始拖拽 | +| `changing_header_position` | 拖拽中 | +| `change_header_position_fail` | 拖拽失败 | + +### 排序事件 + +| 事件名 | 说明 | 适用表格 | +|---|---|---| +| `sort_click` | 点击排序图标 | ListTable | +| `after_sort` | 排序完成 | ListTable | +| `pivot_sort_click` | 透视表排序点击 | PivotTable | + +### 菜单事件 + +| 事件名 | 说明 | +|---|---| +| `dropdown_menu_click` | 下拉菜单项点击 | +| `dropdown_icon_click` | 点击下拉图标 | +| `dropdown_menu_clear` | 清空下拉菜单 | +| `show_menu` | 显示菜单 | +| `hide_menu` | 隐藏菜单 | + +### 图标事件 + +| 事件名 | 说明 | +|---|---| +| `icon_click` | 点击图标 | +| `drillmenu_click` | 下钻按钮点击(PivotTable) | + +### 键盘事件 + +| 事件名 | 说明 | +|---|---| +| `keydown` | 键盘按下 | + +### 树形展开事件 + +| 事件名 | 说明 | +|---|---| +| `tree_hierarchy_state_change` | 树形节点展开/收起 | + +### 表单控件事件 + +| 事件名 | 说明 | +|---|---| +| `checkbox_state_change` | checkbox 状态变更 | +| `radio_state_change` | radio 状态变更 | +| `switch_state_change` | switch 状态变更 | +| `button_click` | 按钮点击 | + +### 数据变更事件 + +| 事件名 | 说明 | +|---|---| +| `change_cell_value` | 更改单元格值 | +| `change_cell_values` | 批量更改单元格值(聚合事件) | +| `paste_data` | 粘贴数据 | +| `add_record` | 添加记录 | +| `delete_record` | 删除记录 | +| `update_record` | 更新记录 | + +### 图例事件 + +| 事件名 | 说明 | +|---|---| +| `legend_item_click` | 图例项点击 | +| `legend_item_hover` | 图例项 hover | +| `legend_item_unhover` | 图例项取消 hover | +| `legend_change` | 颜色/尺寸图例变更 | + +### 坐标轴事件 + +| 事件名 | 说明 | +|---|---| +| `mouseenter_axis` | 鼠标进入轴 | +| `mouseleave_axis` | 鼠标离开轴 | + +### 迷你图事件 + +| 事件名 | 说明 | +|---|---| +| `mouseover_chart_symbol` | 鼠标经过迷你图标记 | + +### 生命周期事件 + +| 事件名 | 说明 | +|---|---| +| `before_init` | 初始化前 | +| `initialized` | 初始化完成 | +| `after_render` | 每次渲染完成 | +| `before_update_option` | 更新配置前 | +| `before_set_size` | 设置大小前 | +| `updated` | 更新完成 | + +### 空数据提示事件 + +| 事件名 | 说明 | +|---|---| +| `empty_tip_click` | 空提示点击 | +| `empty_tip_dblclick` | 空提示双击 | + +## 常用事件示例 + +### 单元格点击 + +```typescript +table.on('click_cell', (args) => { + const { col, row, cellLocation, originData } = args; + if (cellLocation === 'body') { + console.log('点击数据行:', originData); + } +}); +``` + +### 排序 + +```typescript +table.on('sort_click', (args) => { + const { field, order } = args; + console.log(`排序: ${field} ${order}`); + // 自定义排序逻辑 +}); +``` + +### 编辑完成 + +```typescript +table.on('change_cell_value', (args) => { + const { col, row, value, oldValue } = args; + console.log(`单元格(${col},${row}) 从 ${oldValue} 改为 ${value}`); +}); +``` + +### 滚动加载 + +```typescript +table.on('scroll_vertical_end', () => { + // 滚动到底部,加载更多数据 + loadMoreData().then(newRecords => { + table.addRecords(newRecords); + }); +}); +``` + +### checkbox 状态 + +```typescript +table.on('checkbox_state_change', (args) => { + const { col, row, checked } = args; + console.log(`checkbox(${col},${row}): ${checked}`); +}); +``` + +### VChart 图表事件 + +```typescript +// 监听嵌入 VChart 的事件(PivotChart 用) +table.onVChartEvent('click', { markName: 'bar' }, (args) => { + console.log('点击了柱形:', args.datum); +}); +``` diff --git a/skills/vtable-development-assistant/references/knowledge/08-pivot-dimensions.md b/skills/vtable-development-assistant/references/knowledge/08-pivot-dimensions.md new file mode 100644 index 000000000..7379700f2 --- /dev/null +++ b/skills/vtable-development-assistant/references/knowledge/08-pivot-dimensions.md @@ -0,0 +1,329 @@ +# 透视表维度与指标 + +## 一、概念说明 + +透视表(PivotTable/PivotChart)通过 **维度**(Dimension)和 **指标**(Indicator)组织多维数据: + +- **行维度** (`rows`): 决定行表头结构,如"地区"、"城市" +- **列维度** (`columns`): 决定列表头结构,如"年份"、"季度" +- **指标** (`indicators`): 需要展示的度量值,如"销售额"、"利润" + +``` + ┌── 列维度(category) ──┐ + │ 办公用品 │ 家具 │ +┌─ 行维度 ──┐ ├──────────┼───────────┤ +│ 华东 │ │ ¥1,200 │ ¥3,400 │ ← 指标值(sales) +│ 华北 │ │ ¥800 │ ¥2,100 │ +└───────────┘ └──────────┴───────────┘ +``` + +## 二、维度定义 IDimension + +定义于 `packages/vtable/src/ts-types/pivot-table/dimension/`。 + +### 基础维度属性 (IBasicDimension) + +| 属性 | 类型 | 说明 | +|---|---|---| +| `dimensionKey` | `string \| number` | **必填**。维度字段名,对应 records 中的 key | +| `title` | `string` | 维度标题(显示在角头或表头标题行) | +| `headerFormat` | `(value: any) => string` | 表头值格式化 | +| `width` | `number \| string` | 列宽(仅行维度有效) | +| `minWidth` | `number \| string` | 最小宽度 | +| `maxWidth` | `number \| string` | 最大宽度 | +| `headerStyle` | `ColumnStyleOption \| Function` | 表头样式 | +| `headerIcon` | `string \| ColumnIconOption \| Array` | 表头图标 | +| `headerCustomRender` | `ICustomRender` | 表头自定义渲染 | +| `headerCustomLayout` | `ICustomLayout` | 表头自定义布局 | +| `headerEditor` | `string \| IEditor` | 表头编辑器 | +| `description` | `string` | 维度描述(hover 提示) | +| `drillDown` | `boolean` | 显示下钻按钮 | +| `drillUp` | `boolean` | 显示上钻按钮 | +| `dropDownMenu` | `MenuListItem[]` | 下拉菜单 | +| `showSort` | `boolean` | 显示排序图标 | +| `sort` | `boolean` | 开启排序功能 | +| `disableHeaderHover` | `boolean` | 禁用 hover | +| `disableHeaderSelect` | `boolean` | 禁用选中 | +| `dragHeader` | `boolean` | 是否允许拖拽 | + +### 维度类型变体 + +```typescript +// 行维度:支持 width/minWidth/maxWidth +type IRowDimension = ILinkDimension | IImageDimension | ITextDimension; + +// 列维度:不支持 width(由指标决定) +type IColumnDimension = Omit; + +// 联合类型 +type IDimension = IRowDimension | IColumnDimension; +``` + +不同表头类型对应不同维度变体: +- `ITextDimension`: 默认文本维度 +- `ILinkDimension`: 链接维度(表头显示为链接) +- `IImageDimension`: 图片维度(表头显示为图片) + +### 维度配置示例 + +```typescript +rows: [ + { + dimensionKey: 'region', + title: '地区', + width: 120, + headerStyle: { fontWeight: 'bold' }, + drillDown: true, // 下钻 + sort: true + }, + { + dimensionKey: 'city', + title: '城市', + width: 100, + headerFormat: (value) => `📍 ${value}` + } +], +columns: [ + { + dimensionKey: 'category', + title: '品类' + }, + { + dimensionKey: 'year', + title: '年份' + } +] +``` + +## 三、指标定义 IIndicator + +定义于 `packages/vtable/src/ts-types/pivot-table/indicator/`。 + +### 基础指标属性 + +**表头部分 (HeaderIndicator)**: + +| 属性 | 类型 | 说明 | +|---|---|---| +| `indicatorKey` | `string \| number` | **必填**。指标字段名,对应 records 中的 key | +| `title` | `string` | 指标标题 | +| `headerIcon` | `string \| ColumnIconOption \| Array` | 表头图标 | +| `headerStyle` | `ColumnStyleOption \| Function` | 表头样式 | +| `headerCustomRender` | `ICustomRender` | 表头自定义渲染 | +| `headerCustomLayout` | `ICustomLayout` | 表头自定义布局 | +| `sort` | `boolean` | 排序配置 | +| `showSort` | `boolean` | 显示排序图标 | +| `dropDownMenu` | `MenuListItem[]` | 下拉菜单 | +| `hide` | `boolean` | 隐藏该指标列 | +| `description` | `string` | 描述 | +| `disableHeaderHover` | `boolean` | 禁用 hover | +| `disableHeaderSelect` | `boolean` | 禁用选中 | + +**Body 部分 (ColumnIndicator)**: + +| 属性 | 类型 | 说明 | +|---|---|---| +| `width` | `number \| string` | 列宽 | +| `minWidth` | `number \| string` | 最小宽度 | +| `maxWidth` | `number \| string` | 最大宽度 | +| `format` | `(value, col, row, table) => string` | 值格式化 | +| `cellType` | `ColumnTypeOption` | 单元格类型 | +| `style` | `ColumnStyleOption \| Function` | 单元格样式 | +| `icon` | `string \| ColumnIconOption \| Array \| Function` | 图标 | +| `customRender` | `ICustomRender` | 自定义渲染 | +| `customLayout` | `ICustomLayout` | 自定义布局 | +| `editor` | `string \| IEditor` | 编辑器 | +| `disableHover` | `boolean` | 禁用 hover | +| `disableSelect` | `boolean` | 禁用选中 | + +### IChartIndicator(透视图专用) + +在 IIndicator 基础上增加图表配置: + +| 属性 | 类型 | 说明 | +|---|---|---| +| `cellType` | `'chart'` | 固定为 chart | +| `chartModule` | `string` | 图表模块名('vchart') | +| `chartSpec` | `any` | VChart 图表规范配置 | + +### 指标配置示例 + +```typescript +indicators: [ + { + indicatorKey: 'sales', + title: '销售额', + width: 120, + format: (value) => value ? `¥${Number(value).toLocaleString()}` : '-', + style: (args) => ({ + bgColor: args.dataValue > 3000 ? '#f6ffed' : '#fff', + color: args.dataValue > 3000 ? '#52c41a' : '#333' + }), + sort: true + }, + { + indicatorKey: 'profit', + title: '利润', + width: 100, + format: (value) => value ? `¥${Number(value).toLocaleString()}` : '-' + } +] +``` + +## 四、角头配置 ICornerDefine + +```typescript +corner: { + titleOnDimension: 'row' | 'column' | 'all' | 'none'; // 标题显示位置 + headerStyle: ColumnStyleOption; // 角头样式 + customLayout: ICustomLayout; // 自定义布局 + customRender: ICustomRender; // 自定义渲染 +} +``` + +## 五、自定义表头树 IHeaderTreeDefine + +除了通过 `rows/columns + records` 自动生成表头,也可以手动定义表头树: + +```typescript +type IHeaderTreeDefine = IDimensionHeaderNode | IIndicatorHeaderNode; + +// 维度节点 +interface IDimensionHeaderNode { + dimensionKey: string | number; // 维度字段 + value: string; // 维度值 + children?: IHeaderTreeDefine[]; // 子节点 + hierarchyState?: 'expand' | 'collapse'; + virtual?: boolean; // 虚拟节点(忽略数据匹配) + levelSpan?: number; // 跨层级合并 +} + +// 指标节点 +interface IIndicatorHeaderNode { + indicatorKey: string | number; // 指标 key + value?: string; // 显示值 + children?: IHeaderTreeDefine[]; + hide?: boolean; // 隐藏 +} +``` + +### 自定义表头树示例 + +```typescript +const table = new PivotTable({ + columnTree: [ + { + dimensionKey: 'year', + value: '2023', + children: [ + { indicatorKey: 'sales', value: '销售额' }, + { indicatorKey: 'profit', value: '利润' } + ] + }, + { + dimensionKey: 'year', + value: '2024', + children: [ + { indicatorKey: 'sales', value: '销售额' }, + { indicatorKey: 'profit', value: '利润' } + ] + } + ], + rowTree: [ + { + dimensionKey: 'region', + value: '华东', + children: [ + { dimensionKey: 'city', value: '上海' }, + { dimensionKey: 'city', value: '杭州' } + ] + }, + { dimensionKey: 'region', value: '华北' } + ] +}); +``` + +## 六、数据分析配置 dataConfig + +```typescript +dataConfig: { + // 聚合规则 + aggregationRules: [ + { + indicatorKey: 'sales', + field: 'sales', + aggregationType: VTable.AggregationType.SUM // SUM | COUNT | AVG | MAX | MIN | NONE + } + ], + + // 排序规则 + sortRules: [ + { + sortField: 'region', + sortType: VTable.SortType.ASC, // ASC | DESC | NONE + sortByIndicator: 'sales' // 按指标值排序 + } + ], + + // 筛选规则 + filterRules: [ + { + filterFunc: (record) => record.sales > 100 + } + ], + + // 小计/总计 + totals: { + row: { + showGrandTotals: true, // 显示行总计 + showSubTotals: true, // 显示行小计 + grandTotalLabel: '总计', + subTotalLabel: '小计', + subTotalsDimensions: ['region'] // 对哪些维度显示小计 + }, + column: { + showGrandTotals: true, + grandTotalLabel: '总计' + } + }, + + // 派生字段 + derivedFieldRules: [ + { + fieldName: 'profitRate', + derivedFunc: (record) => { + return record.profit / record.sales; + } + } + ] +} +``` + +## 七、透视表行层级展示 + +```typescript +// grid 模式(默认):每个维度独立一列 +rowHierarchyType: 'grid' + +// tree 模式:所有行维度在一列中树形展示 +rowHierarchyType: 'tree' + +// grid-tree 模式:grid 布局但支持展开收起 +rowHierarchyType: 'grid-tree' +``` + +### 表头标题行/列 + +```typescript +// 在列表头顶部增加一行显示维度名称 +columnHeaderTitle: { + title: true, // 或自定义字符串 + headerStyle: { fontWeight: 'bold' } +} + +// 在行表头左侧增加一列显示维度名称 +rowHeaderTitle: { + title: true, + headerStyle: { fontWeight: 'bold' } +} +``` diff --git a/skills/vtable-development-assistant/references/knowledge/09-data-bindingd.md b/skills/vtable-development-assistant/references/knowledge/09-data-bindingd.md new file mode 100644 index 000000000..dc5d463eb --- /dev/null +++ b/skills/vtable-development-assistant/references/knowledge/09-data-bindingd.md @@ -0,0 +1,301 @@ +# 数据绑定 + +## 一、ListTable 数据绑定 + +### records — 直接传入数据 + +最常用的方式,将数组数据直接传入: + +```typescript +const table = new ListTable({ + records: [ + { id: 1, name: 'Alice', age: 25, city: 'Beijing' }, + { id: 2, name: 'Bob', age: 30, city: 'Shanghai' } + ], + columns: [ + { field: 'id', title: 'ID' }, + { field: 'name', title: '姓名' } + ] +}); + +// 更新全量数据 +table.setRecords(newRecords); + +// 增删改 +table.addRecord({ id: 3, name: 'Charlie', age: 28 }); +table.addRecords([record1, record2], 0); // 在索引0处插入 +table.deleteRecords([0, 2]); // 删除第0、2条 +table.updateRecords([updatedRecord], [1]); // 更新第1条 +``` + +### dataSource — 数据源对象(懒加载/大数据) + +用于懒加载或自定义数据获取逻辑: + +```typescript +import { CachedDataSource, DataSource } from '@visactor/vtable'; + +// 创建数据源 +const dataSource = new CachedDataSource({ + get(index) { + // 根据索引获取数据(支持返回 Promise) + return records[index]; + }, + length: records.length, + getField(index, field) { + return records[index]?.[field]; + } +}); + +const table = new ListTable({ + dataSource, + columns: [...] +}); +``` + +### field — 字段映射 + +`field` 支持多种形式: + +```typescript +// 1. 简单字段名 +{ field: 'name' } + +// 2. 嵌套字段(点号分隔) +{ field: 'address.city' } + +// 3. 数组路径 +{ field: ['address', 'city'] } + +// 4. 数字索引(数据为数组时) +{ field: 0 } +``` + +### fieldFormat — 格式化显示 + +```typescript +{ + field: 'price', + title: '价格', + fieldFormat: (record, col, row, table) => { + return `¥${record.price?.toFixed(2)}`; + } +} +``` + +注意: `fieldFormat` 影响的是**展示值**,`getCellValue()` 返回格式化后的值,`getCellRawValue()` 返回原始值。 + +### 排序 + +```typescript +// 方式1:列定义上开启 +{ + field: 'age', + title: '年龄', + sort: true // 使用默认排序 +} + +// 方式2:自定义排序函数 +{ + field: 'name', + title: '姓名', + sort: (a, b, order) => { + return order === 'asc' + ? a.localeCompare(b, 'zh') + : b.localeCompare(a, 'zh'); + } +} + +// 方式3:预排序(大数据性能优化) +table.setSortedIndexMap('age', sortedMap); + +// 方式4:配置初始排序状态 +const table = new ListTable({ + sortState: { field: 'age', order: 'asc' } +}); +``` + +### 过滤 + +```typescript +// 更新过滤规则 +table.updateFilterRules([ + { + filterKey: 'city', + filteredValues: ['Beijing', 'Shanghai'] + } +]); + +// 自定义过滤函数 +table.updateFilterRules([ + { + filterFunc: (record) => record.age > 20 + } +]); + +// 获取过滤后的数据 +const filtered = table.getFilteredRecords(); +``` + +### 聚合 + +```typescript +{ + field: 'sales', + title: '销售额', + aggregation: [ + { + aggregationType: VTable.AggregationType.SUM, + showOnTop: false, // 显示在底部 + formatFun: (value) => `合计: ¥${value.toLocaleString()}` + } + ] +} +``` + +支持的聚合类型:`SUM`, `COUNT`, `AVG`, `MAX`, `MIN`, `NONE`, `CUSTOM` + +### 分页 + +```typescript +const table = new ListTable({ + pagination: { + perPageCount: 20, // 每页 20 条 + currentPage: 1 // 当前第 1 页 + } +}); + +// 翻页 +table.updatePagination({ currentPage: 2 }); +``` + +## 二、PivotTable 数据绑定 + +### 方式1:records + rows/columns/indicators(自动聚合) + +最常用,由 VTable 内部完成数据分析: + +```typescript +const table = new PivotTable({ + records: flatData, // 平坦的原始数据 + rows: ['region', 'city'], + columns: ['category', 'subCategory'], + indicators: [{ + indicatorKey: 'sales', + title: '销售额' + }], + dataConfig: { + aggregationRules: [{ + indicatorKey: 'sales', + field: 'sales', + aggregationType: VTable.AggregationType.SUM + }] + } +}); +``` + +### 方式2:rowTree/columnTree + records(自定义表头) + +手动定义表头结构: + +```typescript +const table = new PivotTable({ + records: flatData, + rowTree: [ + { dimensionKey: 'region', value: '华东', children: [...] }, + { dimensionKey: 'region', value: '华北' } + ], + columnTree: [ + { dimensionKey: 'year', value: '2023', children: [ + { indicatorKey: 'sales' }, + { indicatorKey: 'profit' } + ]} + ], + rows: [{ dimensionKey: 'region', title: '地区' }], + indicators: [ + { indicatorKey: 'sales', title: '销售额' } + ] +}); +``` + +### 方式3:rowTree/columnTree 无 records(纯展示) + +手动指定所有数据,不做聚合: + +```typescript +// 数据可以不传 records,在 tree 节点中带数据 +// 此时必须设置 parseCustomTreeToMatchRecords +``` + +## 三、PivotChart 数据绑定 + +```typescript +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' + } + }] +}); +``` + +注意:PivotChart 内部自动开启数据分析,不需要手动配置 `dataConfig`。 + +## 四、数据更新最佳实践 + +```typescript +// ✅ 推荐:使用 API 更新 +table.setRecords(newRecords); // 全量更新 +table.addRecords(newRecords); // 增量添加 +table.changeCellValue(col, row, value); // 单格修改 + +// ❌ 不推荐:直接修改 records 数组 +table.records.push(newRecord); // 不会触发重新渲染! + +// 如果必须直接修改数据源,需手动刷新 +table.records[0].name = 'NewName'; +table.refreshAfterSourceChange(); // ListTable 专有 +``` + +## 五、树形数据 + +```typescript +const table = new ListTable({ + records: [ + { + name: '总部', + children: [ + { name: '研发部', children: [ + { name: '前端组' }, + { name: '后端组' } + ]}, + { name: '市场部' } + ] + } + ], + columns: [ + { field: 'name', title: '部门', tree: true }, // tree: true 标记树形列 + { field: 'headcount', title: '人数' } + ], + hierarchyIndent: 20, // 缩进像素 + hierarchyExpandLevel: 2 // 默认展开2层 +}); + +// 动态加载子节点 +table.on('tree_hierarchy_state_change', (args) => { + if (args.hierarchyState === 'expand') { + loadChildren(args.originData).then(children => { + table.setRecordChildren(children, args.row); + }); + } +}); +``` diff --git a/skills/vtable-development-assistant/references/knowledge/10-interaction.md b/skills/vtable-development-assistant/references/knowledge/10-interaction.md new file mode 100644 index 000000000..2a1382632 --- /dev/null +++ b/skills/vtable-development-assistant/references/knowledge/10-interaction.md @@ -0,0 +1,347 @@ +# 交互配置 + +## 一、选择 + +### 基本选择配置 + +```typescript +const table = new ListTable({ + select: { + disableSelect: false, // 是否禁用选择 + disableHeaderSelect: false, // 是否禁用表头选择 + + // 选择高亮模式 + highlightMode: 'cell', // 'cross' | 'column' | 'row' | 'cell' + + // 点击表头的选择行为 + headerSelectMode: 'inline', // 'inline' 选中整行/列 | 'cell' 只选表头 + + // 点击空白区域取消选中 + blankAreaClickDeselect: true + } +}); +``` + +### 选择 API + +```typescript +// 选中单元格 +table.selectCell(col, row); + +// 选中区域 +table.selectCells([ + { start: { col: 1, row: 1 }, end: { col: 3, row: 5 } } +]); + +// 选中整行/列 +table.selectRow(3); +table.selectCol(2); + +// 清除选中 +table.clearSelected(); + +// 获取选中信息 +const selected = table.getSelectedCellInfos(); +``` + +### 选择事件 + +```typescript +table.on('selected_cell', (args) => { + console.log('选中:', args.ranges); +}); + +table.on('selected_changed', (args) => { + // 拖拽选择过程中持续触发 +}); + +table.on('selected_clear', () => { + console.log('取消选中'); +}); +``` + +## 二、Hover 高亮 + +```typescript +const table = new ListTable({ + hover: { + // 高亮模式 + highlightMode: 'cross', // 'cross' 十字花 | 'column' | 'row' | 'cell' + + disableHover: false, // 禁用 hover + disableHeaderHover: false, // 禁用表头 hover + enableSingleCell: false // 只高亮单个单元格 + } +}); +``` + +主题中配置 hover 样式: + +```typescript +theme: { + bodyStyle: { + hover: { + cellBgColor: '#e6f7ff', // hover 单元格背景色 + inlineColumnBgColor: '#f0f5ff', // 十字花列背景色 + inlineRowBgColor: '#f0f5ff' // 十字花行背景色 + } + } +} +``` + +## 三、编辑 + +### 配置编辑器 + +```typescript +import { ListTable } from '@visactor/vtable'; +import { InputEditor, DateInputEditor, ListEditor } from '@visactor/vtable-editors'; + +// 1. 注册编辑器 +const inputEditor = new InputEditor(); +const dateEditor = new DateInputEditor(); +const listEditor = new ListEditor({ values: ['选项A', '选项B', '选项C'] }); + +VTable.register.editor('input', inputEditor); +VTable.register.editor('date', dateEditor); +VTable.register.editor('list', listEditor); + +// 2. 在列定义中使用 +const table = new ListTable({ + columns: [ + { field: 'name', title: '姓名', editor: 'input' }, + { field: 'date', title: '日期', editor: 'date' }, + { field: 'status', title: '状态', editor: 'list' } + ], + editCellTrigger: 'doubleclick' // 编辑触发方式 +}); +``` + +### 编辑触发方式 + +```typescript +editCellTrigger: 'doubleclick' // 双击(默认) +editCellTrigger: 'click' // 单击 +editCellTrigger: 'api' // 仅通过 API +editCellTrigger: 'keydown' // 键盘按下 +editCellTrigger: ['doubleclick', 'api'] // 多种方式 +``` + +### 编辑 API + +```typescript +// 开启编辑 +table.startEditCell(col, row); +table.startEditCell(col, row, 'customValue'); // 自定义编辑初始值 + +// 结束编辑 +table.completeEditCell(); // 保存 +table.cancelEditCell(); // 取消 + +// 获取编辑器 +const editor = table.getEditor(col, row); +``` + +### 编辑事件 + +```typescript +table.on('change_cell_value', (args) => { + const { col, row, value, oldValue } = args; + console.log('值改变:', oldValue, '->', value); +}); +``` + +### 动态编辑器 + +```typescript +{ + field: 'value', + title: '值', + editor: (args) => { + // 根据行数据动态返回编辑器 + const record = args.table.getCellOriginRecord(args.col, args.row); + if (record.type === 'date') return 'date'; + if (record.type === 'select') return 'list'; + return 'input'; + } +} +``` + +## 四、排序 + +### 列定义排序 + +```typescript +{ + field: 'name', + title: '姓名', + sort: true, // 开启默认排序 + showSort: true // 显示排序图标(默认 sort:true 时自动显示) +} + +// 自定义排序 +{ + field: 'name', + sort: (a, b, order) => { + return order === 'asc' + ? a.localeCompare(b, 'zh') + : b.localeCompare(a, 'zh'); + } +} +``` + +### 排序状态 + +```typescript +// 初始排序 +const table = new ListTable({ + sortState: { field: 'age', order: 'asc' }, + multipleSort: true // 支持多列排序 +}); + +// 更新排序 +table.updateSortState({ + field: 'name', + order: 'desc' +}); +``` + +### 排序事件 + +```typescript +table.on('sort_click', (args) => { + const { field, order } = args; + // 可以阻止默认排序,实现服务端排序 + // args.preventDefault?.(); + // fetchSortedData(field, order).then(data => table.setRecords(data)); +}); +``` + +## 五、拖拽 + +### 拖拽调整列/行顺序 + +```typescript +const table = new ListTable({ + dragOrder: { + dragHeaderMode: 'column', // 'all' | 'none' | 'column' | 'row' + + // 冻结列拖拽规则 + frozenColDragHeaderMode: 'fixedFrozenCount' + // 'disabled': 禁止拖入/出冻结列 + // 'adjustFrozenCount': 拖入冻结区增加冻结数 + // 'fixedFrozenCount': 自由拖拽,冻结数不变 + } +}); +``` + +### 拖拽事件 + +```typescript +table.on('change_header_position', (args) => { + console.log(`列从 ${args.source} 移到 ${args.target}`); +}); +``` + +### 行拖拽排序 + +通过 `rowSeriesNumber.dragOrder` 启用行拖拽: + +```typescript +const table = new ListTable({ + rowSeriesNumber: { + title: '#', + width: 50, + dragOrder: true // 启用行拖拽排序 + } +}); +``` + +## 六、列宽/行高调整 + +```typescript +const table = new ListTable({ + resize: { + columnResizeMode: 'header', // 'all' | 'none' | 'header' | 'body' + rowResizeMode: 'none', + columnResizeWidth: 6 // 调整热区宽度 + } +}); +``` + +### 列宽调整事件 + +```typescript +table.on('resize_column_end', (args) => { + console.log(`列 ${args.col} 宽度调整为 ${args.colWidth}`); +}); +``` + +## 七、冻结列/行 + +```typescript +const table = new ListTable({ + frozenColCount: 2, // 左侧冻结2列 + rightFrozenColCount: 1, // 右侧冻结1列 + frozenRowCount: 1, // 顶部冻结(通常是表头) + bottomFrozenRowCount: 1 // 底部冻结(如汇总行) +}); +``` + +## 八、键盘交互 + +```typescript +const table = new ListTable({ + keyboardOptions: { + moveFocusCellOnTab: true, // Tab 键移动 + editCellOnEnter: true, // Enter 进入编辑 + selectAllOnCtrlA: true, // Ctrl+A 全选 + copySelected: true, // Ctrl+C 复制 + pasteValueToCell: true, // Ctrl+V 粘贴 + moveSelectedCellOnArrowKeys: true, // 方向键移动 + ctrlMultiSelect: true, // Ctrl 多选 + shiftMultiSelect: true // Shift 范围选 + } +}); +``` + +## 九、右键菜单 + +```typescript +const table = new ListTable({ + menu: { + contextMenuItems: [ + { text: '复制', menuKey: 'copy' }, + { text: '粘贴', menuKey: 'paste' }, + { text: '---' }, // 分割线 + { text: '删除行', menuKey: 'delete' } + ] + } +}); + +table.on('dropdown_menu_click', (args) => { + switch (args.menuKey) { + case 'copy': table.getCopyValue(); break; + case 'delete': table.deleteRecords([args.row]); break; + } +}); +``` + +## 十、Tooltip 提示 + +```typescript +const table = new ListTable({ + tooltip: { + renderMode: 'html', // 'html' | 'canvas' + isShowOverflowTextTooltip: true, // 省略文字自动提示 + confine: true, // 限制在表格内 + overflowTextTooltipDisappearDelay: 1000 // 延迟消失(ms) + } +}); + +// 手动显示 tooltip +table.showTooltip(col, row, { + content: '自定义提示内容', + style: { bgColor: '#333', color: '#fff' } +}); +``` diff --git a/skills/vtable-development-assistant/references/knowledge/11-common-patterns.md b/skills/vtable-development-assistant/references/knowledge/11-common-patterns.md new file mode 100644 index 000000000..96c32a485 --- /dev/null +++ b/skills/vtable-development-assistant/references/knowledge/11-common-patterns.md @@ -0,0 +1,355 @@ +# 常用代码模式与最佳实践 + +## 一、表格初始化模式 + +### 基本模板 + +```typescript +import { ListTable } from '@visactor/vtable'; + +// 创建容器 +const container = document.getElementById('tableContainer'); + +// 创建表格 +const table = new ListTable({ + container, + records: data, + columns: [...], + widthMode: 'standard', + defaultRowHeight: 40 +}); + +// 窗口大小变化时自适应 +window.addEventListener('resize', () => { + table.renderWithRecreateCells(); +}); + +// 页面销毁时释放 +// table.release(); +``` + +### 自适应容器 + +```typescript +const table = new ListTable({ + container, + records: data, + columns: [...], + widthMode: 'adaptive', // 列宽自适应容器 + heightMode: 'adaptive', // 行高自适应容器 + autoFillWidth: true, // 列不足时自动拉伸 + autoFillHeight: true // 行不足时自动拉伸 +}); +``` + +### 大数据量优化 + +```typescript +const table = new ListTable({ + container, + records: largeData, // 百万级数据 + columns: [...], + renderChartAsync: true, // 图表异步渲染 + // VTable 内部使用虚拟滚动,无需额外配置 +}); + +// 使用预排序优化排序性能 +const sortedMap = new Map(); +largeData.forEach((r, i) => sortedMap.set(i, r.sortKey)); +table.setSortedIndexMap('field', sortedMap); +``` + +## 二、样式条件格式 + +### 根据值设置背景色 + +```typescript +{ + field: 'score', + title: '分数', + style: (args) => ({ + bgColor: args.dataValue >= 90 ? '#f6ffed' : + args.dataValue >= 60 ? '#e6f7ff' : + '#fff1f0', + color: args.dataValue >= 90 ? '#52c41a' : + args.dataValue >= 60 ? '#1890ff' : + '#f5222d' + }) +} +``` + +### 交替行颜色 + +```typescript +{ + field: 'name', + title: '姓名', + style: (args) => ({ + bgColor: args.row % 2 === 0 ? '#fafafa' : '#fff' + }) +} +``` + +### 运行时动态样式 + +```typescript +// 注册样式 +table.registerCustomCellStyle('highlight', { + bgColor: '#ffffcc', + fontWeight: 'bold' +}); + +// 根据条件分配 +data.forEach((record, index) => { + if (record.isImportant) { + table.arrangeCustomCellStyle({ + cellPosition: { row: index + table.columnHeaderLevelCount }, + customStyleId: 'highlight' + }); + } +}); +``` + +## 三、编辑表格模式 + +### 完整编辑表格 + +```typescript +import { ListTable, register } from '@visactor/vtable'; +import { InputEditor, ListEditor } from '@visactor/vtable-editors'; + +// 注册编辑器 +register.editor('input', new InputEditor()); +register.editor('list', new ListEditor({ values: ['进行中', '已完成', '已取消'] })); + +const table = new ListTable({ + container, + records: data, + columns: [ + { field: 'name', title: '姓名', editor: 'input' }, + { field: 'status', title: '状态', editor: 'list' }, + { field: 'score', title: '分数' } // 不可编辑 + ], + editCellTrigger: 'doubleclick', + keyboardOptions: { + moveFocusCellOnTab: true, + editCellOnEnter: true, + copySelected: true, + pasteValueToCell: true + } +}); + +// 监听值变更 +table.on('change_cell_value', (args) => { + // 保存到后端 + saveToServer(args.col, args.row, args.value); +}); +``` + +## 四、透视分析表模式 + +### 销售数据透视 + +```typescript +import { PivotTable } from '@visactor/vtable'; +import * as VTable from '@visactor/vtable'; + +const table = new PivotTable({ + container, + records: salesData, + rows: [ + { dimensionKey: 'region', title: '地区', width: 120 }, + { dimensionKey: 'city', title: '城市', width: 100 } + ], + columns: [ + { dimensionKey: 'category', title: '品类' } + ], + indicators: [ + { + indicatorKey: 'sales', + title: '销售额', + width: 120, + format: (value) => value ? `¥${Number(value).toLocaleString()}` : '-', + style: (args) => ({ + color: args.dataValue > 5000 ? '#52c41a' : '#333' + }) + }, + { + indicatorKey: 'profit', + title: '利润', + width: 100, + format: (value) => value ? `¥${Number(value).toLocaleString()}` : '-' + } + ], + dataConfig: { + aggregationRules: [ + { indicatorKey: 'sales', field: 'sales', aggregationType: VTable.AggregationType.SUM }, + { indicatorKey: 'profit', field: 'profit', aggregationType: VTable.AggregationType.SUM } + ], + totals: { + row: { showGrandTotals: true, grandTotalLabel: '总计' }, + column: { showGrandTotals: true, grandTotalLabel: '总计' } + } + }, + corner: { titleOnDimension: 'row' }, + rowHierarchyType: 'tree', + rowExpandLevel: 1 +}); +``` + +## 五、自定义卡片布局 + +### 用户信息卡片 + +```typescript +import { ListTable, Group, Text, Image, Tag } from '@visactor/vtable'; + +const table = new ListTable({ + container, + records: users, + defaultRowHeight: 64, + columns: [{ + field: 'user', + title: '用户', + width: 280, + customLayout: (args) => { + const { rect, table, col, row } = args; + const record = table.getCellOriginRecord(col, row); + + return { + rootContainer: ( + + + + + + + + + + + ), + renderDefault: false + }; + } + }, + { field: 'email', title: '邮箱', width: 200 }, + { field: 'joinDate', title: '入职日期', width: 120 }] +}); +``` + +## 六、搜索与高亮 + +```typescript +import { SearchComponent } from '@visactor/vtable-search'; + +const searchComponent = new SearchComponent({ + table, + autoJump: true // 自动跳转到第一个匹配 +}); + +// 搜索 +searchComponent.search('keyword'); + +// 下一个/上一个 +searchComponent.next(); +searchComponent.prev(); + +// 清除搜索 +searchComponent.clear(); +``` + +## 七、导出 + +```typescript +import { exportVTableToExcel } from '@visactor/vtable-export'; + +// 导出 Excel +exportVTableToExcel(table, { + fileName: 'table-data', + ignoreIcon: true +}); + +// 导出图片 +const imgBase64 = table.exportImg(); + +// 导出单个单元格图片 +const cellImg = table.exportCellImg(col, row); +``` + +## 八、生命周期最佳实践 + +```typescript +// 创建 +const table = new ListTable(options); + +// 等待初始化完成 +table.on('initialized', () => { + console.log('表格初始化完成'); +}); + +// 每次渲染完成 +table.on('after_render', () => { + console.log('渲染完成'); +}); + +// 更新配置 +table.updateOption(newOptions); // 全量更新 +table.updateColumns(newColumns); // 只更新列 +table.updateTheme(newTheme); // 只更新主题 +table.setRecords(newRecords); // 只更新数据 + +// ⚠️ 销毁(必须调用,避免内存泄漏) +table.release(); +``` + +## 九、常见问题模式 + +### 表格不显示 + +```typescript +// ✅ 确保容器有明确的宽高 +const container = document.getElementById('table'); +container.style.width = '800px'; +container.style.height = '600px'; + +const table = new ListTable({ container, ... }); +``` + +### 数据更新后不刷新 + +```typescript +// ❌ 直接修改不会触发更新 +table.records[0].name = 'new'; + +// ✅ 使用 API +table.changeCellValue(col, row, 'new'); +// 或 +table.setRecords(newRecords); +``` + +### 动态调整冻结列 + +```typescript +// 设置后需要重建 +table.frozenColCount = 3; +table.renderWithRecreateCells(); +``` + +### 自适应容器大小变化 + +```typescript +const resizeObserver = new ResizeObserver(() => { + table.renderWithRecreateCells(); +}); +resizeObserver.observe(container); +``` diff --git a/skills/vtable-development-assistant/references/type/base-table-options.md b/skills/vtable-development-assistant/references/type/base-table-options.md new file mode 100644 index 000000000..961df524f --- /dev/null +++ b/skills/vtable-development-assistant/references/type/base-table-options.md @@ -0,0 +1,262 @@ +# VTable 基础表格配置 + +## BaseTableConstructorOptions + +```typescript +/** + * VTable 基础表格配置 — 用户可见类型精简版 + * 所有表格类型共享的公共配置 + * 源码: packages/vtable/src/ts-types/table-engine.ts + */ + +/** 基础表格配置(ListTable / PivotTable / PivotChart 共享) */ +export interface BaseTableConstructorOptions { + /** DOM 容器元素 */ + container: HTMLElement | string; + + // ===== 尺寸 ===== + /** 列宽模式 */ + widthMode?: 'standard' | 'adaptive' | 'autoWidth'; + /** 行高模式 */ + heightMode?: 'standard' | 'adaptive' | 'autoHeight'; + /** 列总宽不足时自动拉伸填满 */ + autoFillWidth?: boolean; + /** 行总高不足时自动拉伸填满 */ + autoFillHeight?: boolean; + /** 默认行高 */ + defaultRowHeight?: number | 'auto'; + /** 默认列宽 */ + defaultColWidth?: number; + /** 表头行高(可按行设置) */ + defaultHeaderRowHeight?: (number | 'auto') | (number | 'auto')[]; + /** 表头列宽 */ + defaultHeaderColWidth?: (number | 'auto') | (number | 'auto')[]; + /** 自动列宽最大限制 */ + limitMaxAutoWidth?: boolean | number; + /** 最小列宽限制 */ + limitMinWidth?: number; + /** 画布像素比 */ + pixelRatio?: number; + + // ===== 冻结 ===== + /** 左侧冻结列数 */ + frozenColCount?: number; + /** 顶部冻结行数 */ + frozenRowCount?: number; + /** 右侧冻结列数 */ + rightFrozenColCount?: number; + /** 底部冻结行数 */ + bottomFrozenRowCount?: number; + + // ===== 文字 ===== + /** 全局自动换行 */ + autoWrapText?: boolean; + /** 识别换行符 \n */ + enableLineBreak?: boolean; + + // ===== 交互 ===== + /** hover 高亮配置 */ + hover?: { + highlightMode?: 'cross' | 'column' | 'row' | 'cell'; + disableHover?: boolean; + disableHeaderHover?: boolean; + enableSingleCell?: boolean; + }; + + /** 选择配置 */ + select?: { + disableSelect?: boolean; + disableHeaderSelect?: boolean; + highlightMode?: 'cross' | 'column' | 'row' | 'cell'; + headerSelectMode?: 'inline' | 'cell'; + blankAreaClickDeselect?: boolean; + }; + + /** 键盘交互 */ + keyboardOptions?: TableKeyboardOptions; + + /** 列宽/行高调整 */ + resize?: { + columnResizeMode?: 'all' | 'none' | 'header' | 'body'; + rowResizeMode?: 'all' | 'none' | 'header' | 'body'; + columnResizeWidth?: number; + }; + + /** 拖拽表头排序 */ + dragOrder?: { + dragHeaderMode?: 'all' | 'none' | 'column' | 'row'; + frozenColDragHeaderMode?: 'disabled' | 'adjustFrozenCount' | 'fixedFrozenCount'; + }; + + // ===== 菜单 ===== + menu?: { + renderMode?: 'canvas' | 'html'; + defaultHeaderMenuItems?: MenuListItem[] | ((args: { row: number; col: number; table: any }) => MenuListItem[]); + contextMenuItems?: MenuListItem[] | ((field: any, row: number, col: number, table?: any) => MenuListItem[]); + dropDownMenuHighlight?: DropDownMenuHighlightInfo[]; + contextMenuWorkOnlyCell?: boolean; + }; + + // ===== 提示框 ===== + tooltip?: { + renderMode?: 'html' | 'canvas'; + isShowOverflowTextTooltip?: boolean; + confine?: boolean; + overflowTextTooltipDisappearDelay?: number; + }; + + // ===== 主题 ===== + /** 主题配置 */ + theme?: ITableThemeDefine; + + // ===== 标题 ===== + /** 表格标题 */ + title?: ITitle; + /** 数据为空提示 */ + emptyTip?: IEmptyTip; + + // ===== 其他 ===== + /** 行序号列 */ + rowSeriesNumber?: IRowSeriesNumber; + /** 自定义合并单元格 */ + customMergeCell?: CustomMergeCell; + /** 全局自定义渲染 */ + customRender?: ICustomRender; + /** 全局自定义布局 */ + customLayout?: ICustomLayout; + /** 全局表头自定义渲染 */ + headerCustomRender?: ICustomRender; + /** 全局表头自定义布局 */ + headerCustomLayout?: ICustomLayout; + /** 出场动画 */ + animationAppear?: IAnimationAppear; + /** 滚动到边界行为 */ + overscrollBehavior?: 'auto' | 'none'; + /** 图表异步渲染 */ + renderChartAsync?: boolean; + /** 预注册自定义样式 */ + customCellStyle?: CustomCellStyle[]; + /** 预分配自定义样式 */ + customCellStyleArrangement?: CustomCellStyleArrangement[]; +} +``` + +```typescript +/** 键盘交互配置 */ +export interface TableKeyboardOptions { + /** Tab 键移动焦点 */ + moveFocusCellOnTab?: boolean; + /** Enter 键编辑 */ + editCellOnEnter?: boolean; + /** Enter 键移动(与 editCellOnEnter 互斥) */ + moveFocusCellOnEnter?: boolean; + /** 方向键切换编辑单元格 */ + moveEditCellOnArrowKeys?: boolean; + /** Ctrl+A 全选 */ + selectAllOnCtrlA?: boolean | SelectAllOnCtrlAOption; + /** Ctrl+C 复制 */ + copySelected?: boolean; + /** Ctrl+V 粘贴 */ + pasteValueToCell?: boolean; + /** Ctrl+X 剪切 */ + cutSelected?: boolean; + /** 方向键移动选中 */ + moveSelectedCellOnArrowKeys?: boolean; + /** Ctrl 多选 */ + ctrlMultiSelect?: boolean; + /** Shift 范围选 */ + shiftMultiSelect?: boolean; +} +``` + +## 依赖类型说明 + +```typescript +/** 菜单项 */ +export type MenuListItem = string | { text: string; menuKey: string; icon?: string; children?: MenuListItem[] } | '-'; // 分隔线 + +/** 下拉菜单高亮信息 */ +export interface DropDownMenuHighlightInfo { + col?: number; + row?: number; + field?: string; + menuKey?: string; +} +/** 表格标题 */ +export interface ITitle { + text?: string; + subtext?: string; + align?: 'left' | 'center' | 'right'; + textStyle?: { fontSize?: number; fontFamily?: string; fontWeight?: string | number; color?: string }; + subtextStyle?: { fontSize?: number; fontFamily?: string; color?: string }; + padding?: number | number[]; +} + +/** 空数据提示 */ +export interface IEmptyTip { + text?: string; + icon?: { width?: number; height?: number; svg?: string; src?: string }; + textStyle?: { fontSize?: number; color?: string; fontFamily?: string }; + spaceBetweenTextAndIcon?: number; +} + +/** 行序号列 */ +export interface IRowSeriesNumber { + width?: number | 'auto'; + title?: string; + format?: (col: number, row: number, table: any) => string | number; + style?: IStyleOption; + headerStyle?: IStyleOption; + dragOrder?: boolean; + disableColumnResize?: boolean; +} + +/** 自定义合并单元格 */ +export type CustomMergeCell = ( + col: number, + row: number, + table: any +) => + | { + range: { start: { col: number; row: number }; end: { col: number; row: number } }; + text?: string; + style?: IStyleOption; + customLayout?: ICustomLayout; + customRender?: ICustomRender; + } + | undefined; + +/** 自定义渲染(详见 custom-render.md) */ +export type ICustomRender = (args: CustomRenderFunctionArg) => ICustomRenderObj | null; + +/** 自定义布局(详见 custom-layout.md) */ +export type ICustomLayout = (args: CustomRenderFunctionArg) => ICustomLayoutObj; + +/** 出场动画 */ +export interface IAnimationAppear { + type?: 'all' | 'one-by-one'; + direction?: 'row' | 'column'; + duration?: number; + delay?: number; +} + +/** 自定义样式分配 */ +export interface CustomCellStyleArrangement { + cellPosition: { + col?: number; + row?: number; + range?: { start: { col: number; row: number }; end: { col: number; row: number } }; + }; + customStyleId: string; +} + +/** Ctrl+A 全选选项 */ +export type SelectAllOnCtrlAOption = { + includeHeader?: boolean; +}; +``` + +## 其他依赖 + +- [ITableThemeDefine](./style-defines.md#主题定义) +- [CustomCellStyle](./style-defines.md) diff --git a/skills/vtable-development-assistant/references/type/column-defines.md b/skills/vtable-development-assistant/references/type/column-defines.md new file mode 100644 index 000000000..6c2968428 --- /dev/null +++ b/skills/vtable-development-assistant/references/type/column-defines.md @@ -0,0 +1,382 @@ +# VTable 列定义类型 + +```typescript +/** 列定义数组 */ +export type ColumnsDefine = ( + | ITextColumnBodyDefine + | ILinkColumnBodyDefine + | IImageColumnBodyDefine + | IVideoColumnBodyDefine + | IProgressbarColumnBodyDefine + | ISparklineColumnBodyDefine + | IChartColumnBodyDefine + | ICheckboxColumnBodyDefine + | IRadioColumnBodyDefine + | ISwitchColumnBodyDefine + | IButtonColumnBodyDefine + | ICompositeColumnBodyDefine + | IMultilineTextColumnBodyDefine + | GroupColumnDefine +)[]; +``` + +## 通用表头属性 + +```typescript +/** 所有列共有的表头属性 */ +export interface IHeaderDefine { + /** 表头显示文字 */ + title?: string; + /** 表头自定义渲染 */ + headerCustomRender?: ICustomRender; + /** 表头自定义布局(JSX) */ + headerCustomLayout?: ICustomLayout; + /** 表头样式 */ + headerStyle?: ColumnStyleOption; + /** 表头类型 */ + headerType?: 'text' | 'link' | 'image' | 'video' | 'checkbox' | 'multilinetext'; + /** 排序 */ + sort?: boolean | SortOption; + /** 表头图标 */ + headerIcon?: string | ColumnIconOption | (string | ColumnIconOption)[]; + /** 是否显示排序图标 */ + showSort?: boolean; + /** 表头描述(悬停提示) */ + description?: string; + /** 是否允许拖拽列 */ + dragHeader?: boolean; + /** 列隐藏 */ + hide?: boolean; + /** 列宽 */ + width?: number | string | 'auto'; + /** 最大列宽 */ + maxWidth?: number | string; + /** 最小列宽 */ + minWidth?: number | string; +} +``` + +## 通用 body 属性 + +```typescript +/** 所有列共有的 body 属性 */ +export interface IColumnBodyDefine extends IHeaderDefine { + /** 数据字段名 */ + field?: string | number; + /** 数据格式化 */ + fieldFormat?: (record: any, col?: number, row?: number, table?: any) => any; + /** 单元格类型 */ + cellType?: CellType; + /** body 样式 */ + style?: ColumnStyleOption; + /** 自定义渲染 */ + customRender?: ICustomRender; + /** 自定义布局 */ + customLayout?: ICustomLayout; + /** 图标 */ + icon?: + | string + | ColumnIconOption + | (string | ColumnIconOption)[] + | ((args: CellInfo) => string | ColumnIconOption | (string | ColumnIconOption)[]); + /** 编辑器 */ + editor?: string | IEditor | ((args: BaseCellInfo & { table: any }) => string | IEditor); + /** 聚合 */ + aggregation?: Aggregation | Aggregation[]; + /** 是否禁用列宽调整 */ + disableColumnResize?: boolean; + /** 是否允许点击后选中单元格 */ + disableSelect?: boolean; + /** 是否禁用表头菜单 */ + disableHeaderSelect?: boolean; + /** 合并单元格规则 */ + mergeCell?: boolean | CustomMergeCell; +} +``` + +```typescript +/** 单元格类型 */ +export type CellType = + | 'text' + | 'link' + | 'image' + | 'video' + | 'progressbar' + | 'sparkline' + | 'chart' + | 'checkbox' + | 'radio' + | 'switch' + | 'button' + | 'composite' + | 'multilinetext'; +``` + +## 各类型列定义 + +```typescript +/** 文本列 — 最基础 */ +export interface ITextColumnBodyDefine extends IColumnBodyDefine { + cellType?: 'text'; +} +``` + +```typescript +/** 链接列 */ +export interface ILinkColumnBodyDefine extends IColumnBodyDefine { + cellType: 'link'; + /** 跳转模板 */ + linkJump?: boolean; + /** 检测是否为链接 */ + linkDetect?: boolean; + /** 自定义链接模板 */ + templateLink?: string; +} +``` + +```typescript +/** 图片列 */ +export interface IImageColumnBodyDefine extends IColumnBodyDefine { + cellType: 'image'; + /** 图片保持宽高比 */ + keepAspectRatio?: boolean; + /** 图片拉伸方式 */ + imageAutoSizing?: boolean; +} +``` + +```typescript +/** 视频列 */ +export interface IVideoColumnBodyDefine extends IColumnBodyDefine { + cellType: 'video'; + /** 视频保持宽高比 */ + keepAspectRatio?: boolean; +} +``` + +```typescript +/** 进度条列 */ +export interface IProgressbarColumnBodyDefine extends IColumnBodyDefine { + cellType: 'progressbar'; + /** 最小值 */ + min?: number; + /** 最大值 */ + max?: number; + /** 进度条颜色 */ + barType?: 'default' | 'negative' | 'both'; + /** 是否在进度条上方显示文字 */ + showBarMark?: boolean; + /** 依赖另一个字段 */ + dependField?: string; +} +``` + +```typescript +/** 迷你图列 */ +export interface ISparklineColumnBodyDefine extends IColumnBodyDefine { + cellType: 'sparkline'; + /** 迷你图规格(VChart spec 子集) */ + sparklineSpec?: SparklineSpec; +} +``` + +```typescript +/** 图表列 */ +export interface IChartColumnBodyDefine extends IColumnBodyDefine { + cellType: 'chart'; + /** VChart 图表规格 */ + chartModule?: string; + /** 图表 spec */ + chartSpec?: any; +} +``` + +```typescript +/** 复选框列 */ +export interface ICheckboxColumnBodyDefine extends IColumnBodyDefine { + cellType: 'checkbox'; + /** 选中时文字 */ + checkedValue?: string; + /** 未选中时文字 */ + uncheckedValue?: string; + /** 是否禁用 */ + disable?: boolean | ((args: CellInfo) => boolean); +} +``` + +```typescript +/** 单选框列 */ +export interface IRadioColumnBodyDefine extends IColumnBodyDefine { + cellType: 'radio'; + /** 是否禁用 */ + disable?: boolean | ((args: CellInfo) => boolean); +} +``` + +```typescript +/** 开关列 */ +export interface ISwitchColumnBodyDefine extends IColumnBodyDefine { + cellType: 'switch'; + /** 是否禁用 */ + disable?: boolean | ((args: CellInfo) => boolean); +} +``` + +```typescript +/** 按钮列 */ +export interface IButtonColumnBodyDefine extends IColumnBodyDefine { + cellType: 'button'; + /** 按钮文字 */ + text?: string; +} +``` + +```typescript +/** 组合类型列 */ +export interface ICompositeColumnBodyDefine extends IColumnBodyDefine { + cellType: 'composite'; +} +``` + +```typescript +/** 多行文本列 */ +export interface IMultilineTextColumnBodyDefine extends IColumnBodyDefine { + cellType: 'multilinetext'; +} +``` + +```typescript +// ======================== +// 分组列 +// ======================== + +/** 分组列 — columns 嵌套实现多级表头 */ +export interface GroupColumnDefine extends IHeaderDefine { + /** 子列 */ + columns: ColumnsDefine; +} +``` + +## 列样式相关 + +```typescript +/** 列样式可为静态对象或函数(详见 style-defines.md) */ +export type ColumnStyleOption = IStyleOption | ((args: StylePropertyFunctionArg) => IStyleOption); + +/** 样式回调参数 */ +export interface StylePropertyFunctionArg { + col: number; + row: number; + /** 当前单元格的原始数据 */ + dataValue: any; + /** 完整数据记录 */ + value: any; + /** 表头路径 */ + cellHeaderPaths: ICellHeaderPaths; + table: any; +} + +/** 表头路径信息 */ +export interface ICellHeaderPaths { + colHeaderPaths?: { dimensionKey?: string; indicatorKey?: string; value?: string }[]; + rowHeaderPaths?: { dimensionKey?: string; indicatorKey?: string; value?: string }[]; +} +``` + +## 依赖类型说明 + +```typescript +/** 排序选项 */ +export interface SortOption { + /** 自定义排序函数 */ + orderFn?: (a: any, b: any, order: string) => -1 | 0 | 1; +} + +/** 图标配置 */ +export interface ColumnIconOption { + type: 'svg' | 'image' | 'path'; + svg?: string; + src?: string; + width?: number; + height?: number; + name?: string; + positionType?: 'left' | 'right' | 'contentLeft' | 'contentRight' | 'absoluteRight'; + funcType?: 'sort' | 'frozen' | 'drillDown' | 'collapse' | 'expand'; + marginLeft?: number; + marginRight?: number; + hover?: { svg?: string; src?: string; width?: number; height?: number }; + tooltip?: { title?: string; style?: any }; + cursor?: string; +} + +/** 单元格信息(icon 回调参数) */ +export interface CellInfo { + col: number; + row: number; + dataValue: any; + value: any; + table: any; +} + +/** 单元格基础信息(editor 回调参数) */ +export interface BaseCellInfo { + col: number; + row: number; + dataValue: any; + value: any; +} + +/** 编辑器接口(详见 list-table-options.md) */ +export interface IEditor { + onStart: (context: any) => void; + onEnd: () => void; + getValue: () => any; + isEditorElement?: (target: HTMLElement) => boolean; +} + +/** 聚合配置 */ +export interface Aggregation { + aggregationType: 'SUM' | 'COUNT' | 'MAX' | 'MIN' | 'AVG' | 'NONE' | 'CUSTOM' | 'RECALCULATE'; + showOnTop?: boolean; + aggregationFun?: (values: any[], records: any[]) => any; + formatFun?: (value: any) => string; +} + +/** 自定义合并单元格 */ +export type CustomMergeCell = ( + col: number, + row: number, + table: any +) => + | { + range: { start: { col: number; row: number }; end: { col: number; row: number } }; + text?: string; + style?: any; + } + | undefined; + +/** 自定义渲染(详见 ./custom-render.md) */ +export type ICustomRender = (args: any) => any; + +/** 自定义布局(详见 ./custom-layout.md) */ +export type ICustomLayout = (args: any) => any; + +/** 迷你图规格 */ +export interface SparklineSpec { + type: 'line' | 'area' | 'bar'; + xField?: string | { field: string }; + yField?: string | { field: string }; + pointShowRule?: 'all' | 'none' | 'isolatedPoint'; + line?: { style?: { stroke?: string; strokeWidth?: number } }; + point?: { style?: { fill?: string; size?: number } }; + area?: { style?: { fill?: string } }; + bar?: { style?: { fill?: string } }; +} + +/** 单元格样式(详见 style-defines.md) */ +// IStyleOption → 见 style-defines.md +``` + +``` + +``` diff --git a/skills/vtable-development-assistant/references/type/custom-layout.md b/skills/vtable-development-assistant/references/type/custom-layout.md new file mode 100644 index 000000000..e22bb3068 --- /dev/null +++ b/skills/vtable-development-assistant/references/type/custom-layout.md @@ -0,0 +1,260 @@ +# VTable Custom Layout(JSX 方式)类型定义 + +> Custom Layout 使用 JSX 语法描述单元格内容,支持 Flex 布局,是推荐的自定义渲染方案。 +> 元素 API 方式见 custom-render.md。 + +## 入口类型 + +```typescript +/** JSX 布局函数签名 — 在列定义中通过 customLayout 使用 */ +export type ICustomLayout = (args: CustomRenderFunctionArg) => ICustomLayoutObj; + +/** 自定义布局返回对象 */ +export interface ICustomLayoutObj { + /** JSX 根节点 */ + rootContainer: VRenderJSXElement; + /** 是否仍然渲染默认内容 */ + renderDefault?: boolean; + /** 期望单元格高度 */ + expectedHeight?: number; + /** 期望单元格宽度 */ + expectedWidth?: number; + /** 是否可 hover 响应子元素 */ + enableCellPadding?: boolean; +} + +/** 自定义渲染函数参数(与 Custom Render 共用) */ +export interface CustomRenderFunctionArg { + col: number; + row: number; + dataValue: any; + value: string | number; + rect: { left: number; top: number; width: number; height: number; right: number; bottom: number }; + table: any; + treeLevel?: number; +} + +/** VRender JSX 元素 — JSX 表达式返回值 */ +type VRenderJSXElement = any; +``` + +## 组件引入方式 + +```typescript +import * as VTable from '@visactor/vtable'; +const { Group, Text, Image, Rect, Circle, Arc, Tag, Line, Checkbox, Radio } = VTable.CustomLayout; +``` + +## Group — 容器 / Flex 布局 + +```typescript +export interface GroupProps { + /** 开启 Flex 布局 */ + display?: 'flex'; + /** 主轴方向 */ + flexDirection?: 'row' | 'column' | 'row-reverse' | 'column-reverse'; + /** 换行 */ + flexWrap?: 'wrap' | 'nowrap' | 'wrap-reverse'; + /** 主轴对齐 */ + justifyContent?: 'flex-start' | 'flex-end' | 'center' | 'space-between' | 'space-around' | 'space-evenly'; + /** 交叉轴对齐 */ + alignItems?: 'flex-start' | 'flex-end' | 'center' | 'stretch' | 'baseline'; + /** 自身交叉轴对齐 */ + alignSelf?: 'flex-start' | 'flex-end' | 'center' | 'stretch' | 'baseline'; + /** 自身 flex 占比 */ + flex?: number; + /** 宽度 */ + width?: number; + /** 高度 */ + height?: number; + /** 内边距 */ + padding?: number | number[]; + /** 外边距 */ + margin?: number | number[]; + /** 背景色 */ + fill?: string; + /** 圆角 */ + cornerRadius?: number; + /** 边框色 */ + stroke?: string; + /** 边框宽度 */ + lineWidth?: number; + /** 光标 */ + cursor?: string; + /** 点击事件 */ + onClick?: (event: any) => void; + /** hover 事件 */ + onMouseEnter?: (event: any) => void; + onMouseLeave?: (event: any) => void; + /** 裁剪溢出内容 */ + clip?: boolean; +} +``` + +## Text — 文本 + +```typescript +export interface TextProps { + text: string; + fontSize?: number; + fontFamily?: string; + fontWeight?: string | number; + fill?: string; + textAlign?: 'left' | 'center' | 'right'; + textBaseline?: 'top' | 'middle' | 'bottom'; + /** 最大宽度 */ + maxLineWidth?: number; + /** 是否省略 */ + ellipsis?: boolean | string; + /** 行高 */ + lineHeight?: number; + underline?: boolean; + lineThrough?: boolean; + cursor?: string; + onClick?: (event: any) => void; + margin?: number | number[]; + flex?: number; +} +``` + +## Image — 图片 + +```typescript +export interface ImageProps { + src: string; + width: number; + height: number; + cornerRadius?: number; + margin?: number | number[]; + cursor?: string; + onClick?: (event: any) => void; +} +``` + +## Rect — 矩形 + +```typescript +export interface RectProps { + width: number; + height: number; + fill?: string; + stroke?: string; + lineWidth?: number; + cornerRadius?: number; + margin?: number | number[]; +} +``` + +## Circle — 圆形 + +```typescript +export interface CircleProps { + radius: number; + fill?: string; + stroke?: string; + lineWidth?: number; + margin?: number | number[]; +} +``` + +## Arc — 弧形 + +```typescript +export interface ArcProps { + outerRadius: number; + innerRadius?: number; + startAngle: number; + endAngle: number; + fill?: string; + stroke?: string; + margin?: number | number[]; +} +``` + +## Tag — 标签(文本 + 背景) + +```typescript +export interface TagProps { + text: string; + fill?: string; + textStyle?: { + fontSize?: number; + fontFamily?: string; + fill?: string; + fontWeight?: string | number; + }; + padding?: number | number[]; + cornerRadius?: number; + margin?: number | number[]; + cursor?: string; + onClick?: (event: any) => void; +} +``` + +## Line — 线条 + +```typescript +export interface LineProps { + points: { x: number; y: number }[]; + stroke?: string; + lineWidth?: number; + lineDash?: number[]; + margin?: number | number[]; +} +``` + +## Checkbox — 复选框 + +```typescript +export interface CheckboxProps { + checked: boolean; + disabled?: boolean; + text?: string; + cursor?: string; + onChange?: (checked: boolean) => void; + margin?: number | number[]; +} +``` + +## Radio — 单选框 + +```typescript +export interface RadioProps { + checked: boolean; + disabled?: boolean; + text?: string; + cursor?: string; + onChange?: (checked: boolean) => void; + margin?: number | number[]; +} +``` + +## 使用示例 + +```tsx +customLayout: args => { + const { table, row, col, rect, dataValue } = args; + const record = table.getCellOriginRecord(col, row); + + const container = ( + + + + + + + + + ); + return { + rootContainer: container, + renderDefault: false + }; +}; +``` diff --git a/skills/vtable-development-assistant/references/type/custom-render.md b/skills/vtable-development-assistant/references/type/custom-render.md new file mode 100644 index 000000000..f3d5d418c --- /dev/null +++ b/skills/vtable-development-assistant/references/type/custom-render.md @@ -0,0 +1,198 @@ +# VTable Custom Render 类型定义 + +> Custom Render 是元素 API 方式的自定义渲染,通过返回元素数组描述单元格内容。 +> 如需更灵活的 JSX/Flex 布局,推荐使用 Custom Layout(见 custom-layout.md)。 + +## 入口类型 + +```typescript +/** 自定义渲染函数签名 — 在列定义中通过 customRender 使用 */ +export type ICustomRender = (args: CustomRenderFunctionArg) => ICustomRenderObj | null; + +/** 自定义渲染函数参数 */ +export interface CustomRenderFunctionArg { + /** 列号 */ + col: number; + /** 行号 */ + row: number; + /** 原始数据值 */ + dataValue: any; + /** 显示值 */ + value: string | number; + /** 单元格绘制区域 */ + rect: CellRectProps; + /** 表格实例 */ + table: any; + /** 当前单元格所属层级(树形表格) */ + treeLevel?: number; +} + +/** 单元格绘制区域 */ +export interface CellRectProps { + left: number; + top: number; + width: number; + height: number; + right: number; + bottom: number; +} + +/** 自定义渲染返回对象 */ +export interface ICustomRenderObj { + /** 元素数组 */ + elements: ICustomRenderElement[]; + /** 期望单元格高度 */ + expectedHeight?: number; + /** 期望单元格宽度 */ + expectedWidth?: number; + /** 是否仍然渲染默认内容 */ + renderDefault?: boolean; +} +``` + +## 7 种元素类型 + +```typescript +export type ICustomRenderElement = + | TextElement + | RectElement + | CircleElement + | ArcElement + | LineElement + | IconElement + | ImageElement; +``` + +### 通用基础属性 + +```typescript +/** 所有元素共有的基础属性 */ +interface BaseElement { + /** 相对 x 坐标 */ + x: number; + /** 相对 y 坐标 */ + y: number; + /** 点击事件 */ + onClick?: (event: any) => void; + /** hover 光标 */ + cursor?: string; +} +``` + +### 文本元素 + +```typescript +export interface TextElement extends BaseElement { + type: 'text'; + text: string; + fontSize?: number; + fontFamily?: string; + fontWeight?: string | number; + fontColor?: string; + color?: string; + textAlign?: 'left' | 'center' | 'right'; + textBaseline?: 'top' | 'middle' | 'bottom'; + lineHeight?: number; + underline?: boolean; + lineThrough?: boolean; + /** 最大宽度(超出省略) */ + maxLineWidth?: number; + /** 富文本配置 */ + textConfig?: RichTextConfig[]; +} +``` + +### 矩形元素 + +```typescript +export interface RectElement extends BaseElement { + type: 'rect'; + width: number; + height: number; + fill?: string; + stroke?: string; + lineWidth?: number; + cornerRadius?: number; +} +``` + +### 圆形元素 + +```typescript +export interface CircleElement extends BaseElement { + type: 'circle'; + radius: number; + fill?: string; + stroke?: string; + lineWidth?: number; +} +``` + +### 弧形元素 + +```typescript +export interface ArcElement extends BaseElement { + type: 'arc'; + radius: number; + startAngle: number; + endAngle: number; + fill?: string; + stroke?: string; + lineWidth?: number; +} +``` + +### 线条元素 + +```typescript +export interface LineElement extends BaseElement { + type: 'line'; + points: { x: number; y: number }[]; + stroke?: string; + lineWidth?: number; + lineDash?: number[]; +} +``` + +### 图标元素 + +```typescript +export interface IconElement extends BaseElement { + type: 'icon'; + /** 注册的图标 SVG 字符串 */ + svg: string; + width: number; + height: number; + /** 图标功能类型 */ + funcType?: string; + /** hover 状态图标 */ + hoverSvg?: string; +} +``` + +### 图片元素 + +```typescript +export interface ImageElement extends BaseElement { + type: 'image'; + src: string; + width: number; + height: number; +} +``` + +### 富文本片段 + +```typescript +/** 富文本片段配置 — 用于 TextElement.textConfig */ +export interface RichTextConfig { + text: string; + fontSize?: number; + fontFamily?: string; + fontWeight?: string | number; + fontColor?: string; + fill?: string; + textDecoration?: 'underline' | 'line-through' | 'none'; + lineHeight?: number; +} +``` diff --git a/skills/vtable-development-assistant/references/type/event-types.md b/skills/vtable-development-assistant/references/type/event-types.md new file mode 100644 index 000000000..efc8a9968 --- /dev/null +++ b/skills/vtable-development-assistant/references/type/event-types.md @@ -0,0 +1,283 @@ +# VTable 事件类型定义 + +## 事件名常量 + +> 通过 `VTable.ListTable.EVENT_TYPE.XXX` 或直接字符串使用。 + +```typescript +export const TABLE_EVENT_TYPE = { + // —— 鼠标事件 —— + CLICK_CELL: 'click_cell', + DBLCLICK_CELL: 'dblclick_cell', + MOUSEDOWN_CELL: 'mousedown_cell', + MOUSEUP_CELL: 'mouseup_cell', + MOUSEMOVE_CELL: 'mousemove_cell', + MOUSEENTER_CELL: 'mouseenter_cell', + MOUSELEAVE_CELL: 'mouseleave_cell', + CONTEXTMENU_CELL: 'contextmenu_cell', + MOUSEENTER_TABLE: 'mouseenter_table', + MOUSELEAVE_TABLE: 'mouseleave_table', + MOUSEOVER_CHART_SYMBOL: 'mouseover_chart_symbol', + MOUSEDOWN_TABLE: 'mousedown_table', + POINTER_MOVE: 'pointermove', + + // —— 选中事件 —— + SELECTED_CELL: 'selected_cell', + SELECTED_CLEAR: 'selected_clear', + ALL_SELECTED: 'all_selected', + + // —— 滚动事件 —— + SCROLL: 'scroll', + SCROLL_HORIZONTAL_END: 'scroll_horizontal_end', + SCROLL_VERTICAL_END: 'scroll_vertical_end', + + // —— 列宽/行高调整 —— + RESIZE_COLUMN: 'resize_column', + RESIZE_COLUMN_END: 'resize_column_end', + RESIZE_ROW: 'resize_row', + RESIZE_ROW_END: 'resize_row_end', + + // —— 拖拽 —— + DRAG_SELECT_END: 'drag_select_end', + CHANGE_HEADER_POSITION: 'change_header_position', + CHANGING_HEADER_POSITION: 'changing_header_position', + CHANGE_HEADER_POSITION_FAIL: 'change_header_position_fail', + + // —— 排序 —— + SORT_CLICK: 'sort_click', + FREEZE_CLICK: 'freeze_click', + PIVOT_SORT_CLICK: 'pivot_sort_click', + + // —— 菜单 —— + DROPDOWN_MENU_CLICK: 'dropdown_menu_click', + SHOW_MENU: 'show_menu', + HIDE_MENU: 'hide_menu', + DROPDOWN_ICON_CLICK: 'dropdown_icon_click', + DROPDOWN_MENU_CLEAR: 'dropdown_menu_clear', + + // —— 图标 —— + ICON_CLICK: 'icon_click', + MULTI_LEVEL_HEADER_ICON_CLICK: 'multi_level_header_icon_click', + + // —— 键盘 —— + KEYDOWN: 'keydown', + + // —— 树形 —— + TREE_HIERARCHY_STATE_CHANGE: 'tree_hierarchy_state_change', + + // —— 表单控件 —— + CHECKBOX_STATE_CHANGE: 'checkbox_state_change', + RADIO_STATE_CHANGE: 'radio_state_change', + SWITCH_STATE_CHANGE: 'switch_state_change', + BUTTON_CLICK: 'button_click', + + // —— 数据变更 —— + CHANGE_CELL_VALUE: 'change_cell_value', + AFTER_CELL_VALUE_CHANGE: 'after_cell_value_change', + EDITOR_CHANGE: 'editor_change', + COPY_DATA: 'copy_data', + PASTE_VALUE: 'paste_value', + DELETE_VALUE: 'delete_value', + EDITOR_FOCUS: 'editor_focus', + + // —— 图例 —— + LEGEND_ITEM_CLICK: 'legend_item_click', + LEGEND_ITEM_HOVER: 'legend_item_hover', + LEGEND_ITEM_UNHOVER: 'legend_item_unhover', + LEGEND_CHANGE: 'legend_change', + + // —— 坐标轴 —— + MOUSEENTER_AXIS: 'mouseenter_axis', + MOUSELEAVE_AXIS: 'mouseleave_axis', + + // —— 迷你图 —— + MOUSEENTER_SPARKLINE: 'mouseenter_sparkline', + + // —— 生命周期 —— + INITIALIZED: 'initialized', + AFTER_RENDER: 'after_render', + RESIZE: 'resize', + READY: 'ready', + ANIMATION_PLAY_END: 'animation_play_end', + + // —— 空提示 —— + EMPTY_TIP_SHOW: 'empty_tip_show', + EMPTY_TIP_HIDE: 'empty_tip_hide' +} as const; +``` + +## 事件参数类型 + +### MousePointerCellEvent — 最常用 + +```typescript +/** 鼠标/指针单元格事件参数 */ +export interface MousePointerCellEvent { + /** 列号 */ + col: number; + /** 行号 */ + row: number; + /** 当前单元格数据值 */ + value: any; + /** 原始数据值 */ + dataValue: any; + /** 单元格区域坐标 */ + cellRange: { left: number; top: number; right: number; bottom: number }; + /** 单元格区域类型 */ + cellType: + | 'header' + | 'body' + | 'cornerHeader' + | 'rowHeader' + | 'columnHeader' + | 'indicator' + | 'bottomFrozen' + | 'rightFrozen'; + /** 原始浏览器事件 */ + event: Event; + /** 表格实例 */ + table: any; + /** 表头路径 */ + cellHeaderPaths?: ICellHeaderPaths; + /** 关联的原始数据记录(body 单元格) */ + originData?: any; + /** 鼠标命中的图标信息 */ + targetIcon?: { name: string; position: any; funcType: string }; +} +``` + +### SelectedCellEvent + +```typescript +/** 选中事件参数 */ +export interface SelectedCellEvent { + col: number; + row: number; + ranges: { start: { col: number; row: number }; end: { col: number; row: number } }[]; +} +``` + +### ResizeColumnEvent + +```typescript +/** 列宽调整事件参数 */ +export interface ResizeColumnEvent { + col: number; + colWidth: number; +} +``` + +### ChangeHeaderPositionEvent + +```typescript +/** 拖拽换位事件参数 */ +export interface ChangeHeaderPositionEvent { + source: { col: number; row: number }; + target: { col: number; row: number }; +} +``` + +### SortClickEvent + +```typescript +/** 排序点击事件参数 */ +export interface SortClickEvent { + col: number; + row: number; + field: string; + order: 'asc' | 'desc' | 'normal'; +} +``` + +### DropdownMenuClickEvent + +```typescript +/** 菜单点击事件参数 */ +export interface DropdownMenuClickEvent { + col: number; + row: number; + menuKey: string; + field: string; + cellHeaderPaths: ICellHeaderPaths; +} +``` + +### CheckboxStateChangeEvent + +```typescript +/** checkbox 状态变更事件参数 */ +export interface CheckboxStateChangeEvent { + col: number; + row: number; + checked: boolean; + dataValue: any; + field: string; +} +``` + +### TreeHierarchyStateChangeEvent + +```typescript +/** 树形展开折叠事件参数 */ +export interface TreeHierarchyStateChangeEvent { + col: number; + row: number; + hierarchyState: 'expand' | 'collapse'; +} +``` + +### ChangeCellValueEvent + +```typescript +/** 编辑器值变更事件参数 */ +export interface ChangeCellValueEvent { + col: number; + row: number; + rawValue: any; + currentValue: any; + changedValue: any; + field: string; +} +``` + +### ScrollEvent + +```typescript +/** 滚动事件参数 */ +export interface ScrollEvent { + scrollLeft: number; + scrollTop: number; + scrollDirection: 'horizontal' | 'vertical'; + scrollRatioX: number; + scrollRatioY: number; +} +``` + +## 依赖类型 + +```typescript +/** 表头路径信息 */ +export interface ICellHeaderPaths { + colHeaderPaths?: { dimensionKey?: string; indicatorKey?: string; value?: string }[]; + rowHeaderPaths?: { dimensionKey?: string; indicatorKey?: string; value?: string }[]; +} +``` + +## 使用示例 + +```typescript +// 方式1: 字符串 +table.on('click_cell', (args: MousePointerCellEvent) => { + console.log(args.col, args.row, args.value); +}); + +// 方式2: EVENT_TYPE 常量 +table.on(VTable.ListTable.EVENT_TYPE.CLICK_CELL, args => { + console.log(args.col, args.row); +}); + +// 方式3: 取消监听 +const handler = args => console.log(args); +table.on('click_cell', handler); +table.off('click_cell', handler); +``` diff --git a/skills/vtable-development-assistant/references/type/list-table-options.md b/skills/vtable-development-assistant/references/type/list-table-options.md new file mode 100644 index 000000000..44a7011fc --- /dev/null +++ b/skills/vtable-development-assistant/references/type/list-table-options.md @@ -0,0 +1,126 @@ +# VTable ListTable 配置 + +> ListTable 继承 BaseTableConstructorOptions(见 base-table-options.md),以下仅列出 ListTable 专有配置。 + +## ListTableConstructorOptions + +```typescript +/** ListTable 专有配置 */ +export interface ListTableConstructorOptions extends BaseTableConstructorOptions { + /** 数据数组 */ + records?: any[]; + /** 列定义(详见 column-defines.md) */ + columns?: ColumnsDefine; + /** 是否转置(行列互换) */ + transpose?: boolean; + /** 是否显示表头 */ + showHeader?: boolean; + /** 分页 */ + pagination?: IPagination; + /** 排序状态 */ + sortState?: SortState | SortState[]; + /** 多列排序 */ + multipleSort?: boolean; + /** 全局编辑器 */ + editor?: string | IEditor | ((args: BaseCellInfo & { table: any }) => string | IEditor); + /** 全局表头编辑器 */ + headerEditor?: string | IEditor | ((args: BaseCellInfo & { table: any }) => string | IEditor); + /** 编辑触发方式 */ + editCellTrigger?: 'doubleclick' | 'click' | 'api' | 'keydown' | ('doubleclick' | 'click' | 'api' | 'keydown')[]; + /** 树形缩进像素 */ + hierarchyIndent?: number; + /** 树形默认展开层级 */ + hierarchyExpandLevel?: number; + /** 同层级节点文字对齐 */ + hierarchyTextStartAlignment?: boolean; + /** 聚合计算 */ + aggregation?: + | Aggregation + | Aggregation[] + | ((args: { col: number; field: string }) => Aggregation | Aggregation[] | null); + /** 分组配置 */ + groupConfig?: { + groupBy: string | string[]; + titleCustomLayout?: ICustomLayout; + titleFieldFormat?: (record: any, col?: number, row?: number, table?: any) => string; + enableTreeStickCell?: boolean; + titleCheckbox?: boolean; + }; + /** 列宽预配置 */ + columnWidthConfig?: { key: string | number; width: number }[]; +} +``` + +## 分页配置 + +```typescript +/** 分页配置 */ +export interface IPagination { + /** 数据总条数 */ + totalCount?: number; + /** 每页显示条数 */ + perPageCount: number; + /** 当前页码(从1开始) */ + currentPage?: number; +} +``` + +## 排序状态 + +```typescript +/** 排序状态 */ +export interface SortState { + /** 排序字段 */ + field: string | number; + /** 排序方向 */ + order: 'asc' | 'desc' | 'normal'; + /** 自定义排序函数 */ + orderFn?: (a: any, b: any, order: string) => -1 | 0 | 1; +} +``` + +## 依赖类型说明 + +```typescript +/** 编辑器接口 — 来自 @visactor/vtable-editors */ +export interface IEditor { + /** 进入编辑 */ + onStart: (context: EditContext) => void; + /** 退出编辑 */ + onEnd: () => void; + /** 获取编辑值 */ + getValue: () => any; + /** 是否有效 */ + isEditorElement?: (target: HTMLElement) => boolean; +} + +/** 编辑上下文 */ +export interface EditContext { + container: HTMLElement; + referencePosition: { rect: RectProps; placement?: string }; + value: any; + col: number; + row: number; + table: any; +} + +/** 单元格基础信息 */ +export interface BaseCellInfo { + col: number; + row: number; + dataValue: any; + value: any; +} + +/** 聚合配置 */ +export interface Aggregation { + /** 聚合类型 */ + aggregationType: 'SUM' | 'COUNT' | 'MAX' | 'MIN' | 'AVG' | 'NONE' | 'CUSTOM' | 'RECALCULATE'; + /** 是否在顶部显示 */ + showOnTop?: boolean; + /** 自定义聚合函数 */ + aggregationFun?: (values: any[], records: any[]) => any; + /** 格式化 */ + formatFun?: (value: any) => string; +} +``` diff --git a/skills/vtable-development-assistant/references/type/pivot-chart-options.md b/skills/vtable-development-assistant/references/type/pivot-chart-options.md new file mode 100644 index 000000000..d9c4453b9 --- /dev/null +++ b/skills/vtable-development-assistant/references/type/pivot-chart-options.md @@ -0,0 +1,82 @@ +# VTable PivotChart 配置 + +> PivotChart 继承 PivotTableConstructorOptions(见 pivot-table-options.md),以下仅列出 PivotChart 专有配置。 + +## PivotChartConstructorOptions + +```typescript +/** 透视组合图配置 — 在透视表基础上扩展图表能力 */ +export interface PivotChartConstructorOptions extends PivotTableConstructorOptions { + /** 轴配置 */ + axes?: ITableAxisOption[]; + /** 图表维度联动高亮 */ + chartDimensionLinkage?: boolean; +} +``` + +## 轴配置 + +```typescript +/** 轴配置 */ +export interface ITableAxisOption { + /** 轴方向 */ + orient: 'left' | 'right' | 'top' | 'bottom'; + /** 是否可见 */ + visible?: boolean; + /** 轴标题 */ + title?: { visible?: boolean; text?: string }; + /** 轴标签 */ + label?: { visible?: boolean; formatter?: (text: string) => string }; + /** 轴线 */ + domainLine?: { visible?: boolean }; + /** 网格线 */ + grid?: { visible?: boolean }; + /** 轴范围 */ + range?: { min?: number; max?: number }; + /** 是否同步不同指标的零值 */ + sync?: { axisId: string; zeroAlign?: boolean }; + /** 轴类型 */ + type?: 'linear' | 'band' | 'time' | 'log' | 'symlog'; + /** 是否 nice 处理 */ + nice?: boolean; + /** 是否反向 */ + inverse?: boolean; +} +``` + +## 使用说明 + +PivotChart 需要先注册 VChart 模块: + +```typescript +import * as VTable from '@visactor/vtable'; +import VChart from '@visactor/vchart'; + +// 注册图表模块 +VTable.register.chartModule('vchart', VChart); + +// 在 indicators 中使用 cellType: 'chart' +const table = new VTable.PivotChart({ + container: document.getElementById('tableContainer'), + records: data, + rows: [{ dimensionKey: 'region', title: '区域', width: 100 }], + columns: [{ dimensionKey: 'product', title: '产品' }], + indicators: [ + { + indicatorKey: 'sales', + title: '销售额', + cellType: 'chart', + chartModule: 'vchart', + chartSpec: { + type: 'bar', + xField: 'region', + yField: 'sales' + } + } + ], + axes: [ + { orient: 'bottom', visible: true }, + { orient: 'left', visible: true } + ] +}); +``` diff --git a/skills/vtable-development-assistant/references/type/pivot-table-options.md b/skills/vtable-development-assistant/references/type/pivot-table-options.md new file mode 100644 index 000000000..01943261c --- /dev/null +++ b/skills/vtable-development-assistant/references/type/pivot-table-options.md @@ -0,0 +1,199 @@ +# VTable PivotTable 配置 + +> PivotTable 继承 BaseTableConstructorOptions(见 base-table-options.md),以下仅列出 PivotTable 专有配置。 +> 维度/指标/表头树详细定义见 pivot-types.md。 + +## PivotTableConstructorOptions + +```typescript +/** 透视表配置 */ +export interface PivotTableConstructorOptions extends BaseTableConstructorOptions { + /** 行维度定义(详见 pivot-types.md) */ + rows?: IRowDimension[]; + /** 列维度定义(详见 pivot-types.md) */ + columns?: IColumnDimension[]; + /** 指标定义(详见 pivot-types.md) */ + indicators?: (IIndicator | string)[]; + /** 行维度树(自定义结构,详见 pivot-types.md) */ + rowTree?: IHeaderTreeDefine[]; + /** 列维度树(自定义结构,详见 pivot-types.md) */ + columnTree?: IHeaderTreeDefine[]; + /** 角表头定义(详见 pivot-types.md) */ + corner?: ICornerDefine; + /** 指标在行还是列方向显示 */ + indicatorsAsCol?: boolean; + /** 指标标题 */ + indicatorTitle?: string; + /** 是否隐藏指标名 */ + hideIndicatorName?: boolean; + /** 行层级缩进 */ + rowHierarchyIndent?: number; + /** 行层级类型 */ + rowHierarchyType?: 'grid' | 'tree'; + /** 行默认展开层级 */ + rowExpandLevel?: number; + /** 行表头中维度文字与树结构对齐 */ + rowHierarchyTextStartAlignment?: boolean; + /** 数据分析配置 */ + dataConfig?: IDataConfig; + /** 是否开启数据懒加载 */ + enableDataAnalysis?: boolean; + /** 补全指标节点 */ + supplementIndicatorNodes?: boolean; + /** 全局编辑器 */ + editor?: string | IEditor | ((args: BaseCellInfo & { table: any }) => string | IEditor); + /** 全局表头编辑器 */ + headerEditor?: string | IEditor | ((args: BaseCellInfo & { table: any }) => string | IEditor); + /** 编辑触发方式 */ + editCellTrigger?: 'doubleclick' | 'click' | 'api' | 'keydown' | ('doubleclick' | 'click' | 'api' | 'keydown')[]; + /** 分页 */ + pagination?: IPagination; +} +``` + +## 数据分析配置 + +```typescript +/** 数据分析配置 */ +export interface IDataConfig { + /** 聚合规则 */ + aggregationRules?: AggregationRule[]; + /** 排序规则 */ + sortRules?: SortRule[]; + /** 过滤规则 */ + filterRules?: FilterRule[]; + /** 小计/合计配置 */ + totals?: Totals; + /** 派生字段 */ + derivedFieldRules?: DerivedFieldRule[]; + /** 收集值 */ + collectValuesBy?: Record; + /** 是否为透视图模式 */ + isPivotChart?: boolean; +} +``` + +## 聚合规则 + +```typescript +/** 聚合规则 */ +export interface AggregationRule { + /** 指标字段 */ + indicatorKey: string; + /** 聚合方式 */ + aggregationType: T; + /** 聚合源字段 */ + field?: string | string[]; + /** 自定义聚合函数(当 aggregationType 为 CUSTOM 时) */ + aggregationFun?: (values: any[], records: any[]) => any; +} + +/** 聚合类型 */ +export type AggregationType = 'SUM' | 'COUNT' | 'MAX' | 'MIN' | 'AVG' | 'NONE' | 'CUSTOM' | 'RECALCULATE'; +``` + +## 排序规则 + +```typescript +/** 排序规则 */ +export interface SortRule { + /** 排序维度 */ + sortField: string; + /** 自然排序方向 */ + sortType?: 'ASC' | 'DESC'; + /** 按指定指标排序 */ + sortByIndicator?: string; + /** 自定义排序值列表 */ + sortBy?: string[]; + /** 自定义排序函数 */ + sortFunc?: (a: any, b: any) => number; +} +``` + +## 过滤规则 + +```typescript +/** 过滤规则 */ +export interface FilterRule { + /** 过滤字段 */ + filterField?: string; + /** 过滤值列表 */ + filteredValues?: any[]; + /** 自定义过滤函数 */ + filterFunc?: (record: any) => boolean; +} +``` + +## 合计 / 小计配置 + +```typescript +/** 合计 / 小计配置 */ +export interface Totals { + row?: TotalConfig; + column?: TotalConfig; +} + +export interface TotalConfig { + /** 是否显示合计 */ + showGrandTotals?: boolean; + /** 是否显示小计 */ + showSubTotals?: boolean; + /** 合计文字 */ + grandTotalLabel?: string; + /** 小计文字 */ + subTotalLabel?: string; + /** 合计在前 */ + grandTotalsFirst?: boolean; + /** 小计在前 */ + subTotalsFirst?: boolean; +} +``` + +## 依赖类型说明 + +```typescript +/** 派生字段规则 */ +export interface DerivedFieldRule { + /** 派生字段名 */ + fieldName: string; + /** 依赖字段 */ + dependFields?: string[]; + /** 派生函数 */ + derivedFunc?: (record: any) => any; +} + +/** 收集值配置 */ +export interface CollectValueBy { + /** 按哪个字段收集 */ + by: string; + /** 收集类型 */ + type?: 'xField' | 'yField' | 'seriesField'; + /** 排序方向 */ + sortType?: 'ASC' | 'DESC'; + /** 排序回调 */ + sortFunc?: (a: any, b: any) => number; +} + +/** 分页配置 */ +export interface IPagination { + totalCount?: number; + perPageCount: number; + currentPage?: number; +} + +/** 编辑器接口(见 list-table-options.md) */ +export interface IEditor { + onStart: (context: EditContext) => void; + onEnd: () => void; + getValue: () => any; + isEditorElement?: (target: HTMLElement) => boolean; +} + +/** 单元格基础信息 */ +export interface BaseCellInfo { + col: number; + row: number; + dataValue: any; + value: any; +} +``` diff --git a/skills/vtable-development-assistant/references/type/pivot-types.md b/skills/vtable-development-assistant/references/type/pivot-types.md new file mode 100644 index 000000000..a1803746b --- /dev/null +++ b/skills/vtable-development-assistant/references/type/pivot-types.md @@ -0,0 +1,240 @@ +# VTable 透视表维度/指标类型定义 + +> 本文件定义透视表(PivotTable / PivotChart)的行维度、列维度、指标、角表头、表头树节点等类型。 + +## 维度定义 + +```typescript +/** 基础维度 — 行维度和列维度的公共属性 */ +export interface IDimension { + /** 维度唯一标识 */ + dimensionKey: string; + /** 维度显示名称 */ + title: string; + /** 表头类型 */ + headerType?: 'text' | 'link' | 'image' | 'video' | 'checkbox' | 'multilinetext'; + /** 表头样式 */ + headerStyle?: ColumnStyleOption; + /** 表头自定义渲染(详见 custom-render.md) */ + headerCustomRender?: ICustomRender; + /** 表头自定义布局(详见 custom-layout.md) */ + headerCustomLayout?: ICustomLayout; + /** 宽度 */ + width?: number | string | 'auto'; + /** 最大宽度 */ + maxWidth?: number | string; + /** 最小宽度 */ + minWidth?: number | string; + /** 是否显示排序图标 */ + showSort?: boolean; + /** 排序 */ + sort?: boolean; + /** 表头图标 */ + headerIcon?: string | ColumnIconOption | (string | ColumnIconOption)[]; + /** 隐藏 */ + hide?: boolean; + /** 描述(悬停提示) */ + description?: string; + /** 角表头文字 */ + cornerDescription?: string; + /** 拖拽 */ + dragHeader?: boolean; + /** 编辑器 */ + headerEditor?: string | IEditor; + /** 下钻按钮 */ + drillDown?: boolean; + /** 上卷按钮 */ + drillUp?: boolean; +} + +/** 行维度 — 增加行特有属性 */ +export interface IRowDimension extends IDimension { + /** 行维度类型: grid(平铺)或 tree(树形) */ + hierarchyType?: 'grid' | 'tree'; + /** 默认展开层级 */ + expandLevel?: number; + /** body 样式 */ + style?: ColumnStyleOption; + /** body 格式化 */ + format?: (value: any) => string; +} + +/** 列维度 */ +export interface IColumnDimension extends IDimension { + /** body 样式 */ + style?: ColumnStyleOption; + /** body 格式化 */ + format?: (value: any) => string; +} +``` + +## 指标定义 + +```typescript +/** 指标 — 定义数据展示方式 */ +export interface IIndicator { + /** 指标唯一标识 */ + indicatorKey: string; + /** 指标显示名称 */ + title: string; + /** 宽度 */ + width?: number | string | 'auto'; + /** 最大宽度 */ + maxWidth?: number | string; + /** 最小宽度 */ + minWidth?: number | string; + /** 表头样式 */ + headerStyle?: ColumnStyleOption; + /** body 样式 */ + style?: ColumnStyleOption; + /** 表头类型 */ + headerType?: 'text' | 'link' | 'image' | 'video' | 'checkbox' | 'multilinetext'; + /** body 单元格类型 */ + cellType?: CellType; + /** 格式化 */ + format?: (value: any, col?: number, row?: number, table?: any) => any; + /** 自定义渲染 */ + customRender?: ICustomRender; + /** 自定义布局 */ + customLayout?: ICustomLayout; + /** 表头自定义渲染 */ + headerCustomRender?: ICustomRender; + /** 表头自定义布局 */ + headerCustomLayout?: ICustomLayout; + /** 图标 */ + icon?: string | ColumnIconOption | (string | ColumnIconOption)[]; + /** 表头图标 */ + headerIcon?: string | ColumnIconOption | (string | ColumnIconOption)[]; + /** 排序 */ + sort?: boolean; + /** 是否显示排序图标 */ + showSort?: boolean; + /** 隐藏 */ + hide?: boolean; + /** 编辑器 */ + editor?: string | IEditor; + /** 表头编辑器 */ + headerEditor?: string | IEditor; + /** 描述 */ + description?: string; + /** 禁用列宽调整 */ + disableColumnResize?: boolean; +} + +/** PivotChart 专用指标 — 增加图表 spec */ +export interface IChartIndicator extends IIndicator { + /** 图表模块名(需先注册 VTable.register.chartModule) */ + chartModule?: string; + /** VChart 图表 spec */ + chartSpec?: any; +} +``` + +## 角表头 + +```typescript +/** 角表头定义 — 行列交汇处的单元格 */ +export interface ICornerDefine { + /** 角表头标题显示方式 */ + titleOnDimension?: 'row' | 'column' | 'none' | 'all'; + /** 角表头样式 */ + headerStyle?: ColumnStyleOption; + /** 角表头自定义渲染 */ + headerCustomRender?: ICustomRender; + /** 角表头自定义布局 */ + headerCustomLayout?: ICustomLayout; +} +``` + +## 表头树 + +```typescript +/** 表头树节点 — 用于 rowTree / columnTree 自定义表头结构 */ +export type IHeaderTreeDefine = IDimensionHeaderNode | IIndicatorHeaderNode; + +/** 维度表头节点 */ +export interface IDimensionHeaderNode { + /** 维度标识 */ + dimensionKey: string; + /** 维度值 */ + value: string; + /** 子节点 */ + children?: IHeaderTreeDefine[]; + /** 是否折叠 */ + hierarchyState?: 'expand' | 'collapse'; + /** 虚拟节点(跨多层级) */ + virtual?: boolean; +} + +/** 指标表头节点 */ +export interface IIndicatorHeaderNode { + /** 指标标识 */ + indicatorKey: string; + /** 指标值(显示文字) */ + value?: string; +} +``` + +## 依赖类型说明 + +```typescript +/** 列样式(详见 style-defines.md) */ +export type ColumnStyleOption = IStyleOption | ((args: StylePropertyFunctionArg) => IStyleOption); + +/** 单元格类型 */ +export type CellType = + | 'text' + | 'link' + | 'image' + | 'video' + | 'progressbar' + | 'sparkline' + | 'chart' + | 'checkbox' + | 'radio' + | 'switch' + | 'button' + | 'composite' + | 'multilinetext'; + +/** 自定义渲染函数(详见 custom-render.md) */ +export type ICustomRender = (args: CustomRenderFunctionArg) => ICustomRenderObj | null; + +/** 自定义布局函数(详见 custom-layout.md) */ +export type ICustomLayout = (args: CustomRenderFunctionArg) => ICustomLayoutObj; + +/** 图标配置 */ +export interface ColumnIconOption { + /** 图标类型 */ + type: 'svg' | 'image' | 'path'; + /** SVG 字符串或图片 URL */ + svg?: string; + src?: string; + /** 宽高 */ + width?: number; + height?: number; + /** 名称标识 */ + name?: string; + /** 位置 */ + positionType?: 'left' | 'right' | 'contentLeft' | 'contentRight' | 'absoluteRight'; + /** 功能类型 */ + funcType?: 'sort' | 'frozen' | 'drillDown' | 'collapse' | 'expand'; + /** 外边距 */ + marginLeft?: number; + marginRight?: number; + /** hover 图标 */ + hover?: { svg?: string; src?: string; width?: number; height?: number }; + /** tooltip */ + tooltip?: { title?: string; style?: any }; + /** 光标 */ + cursor?: string; +} + +/** 编辑器接口(见 list-table-options.md) */ +export interface IEditor { + onStart: (context: any) => void; + onEnd: () => void; + getValue: () => any; + isEditorElement?: (target: HTMLElement) => boolean; +} +``` diff --git a/skills/vtable-development-assistant/references/type/style-defines.md b/skills/vtable-development-assistant/references/type/style-defines.md new file mode 100644 index 000000000..694633b8d --- /dev/null +++ b/skills/vtable-development-assistant/references/type/style-defines.md @@ -0,0 +1,254 @@ +# VTable 样式类型定义 + +## 文字样式 + +```typescript +export interface ITextStyleOption { + /** 字体 */ + fontFamily?: string; + /** 字号 */ + fontSize?: number | string; + /** 粗细 */ + fontWeight?: 'normal' | 'bold' | number | string; + /** 斜体 */ + fontStyle?: 'normal' | 'italic'; + /** 文字颜色 */ + color?: ColorPropertyDefine; + /** 文字对齐 */ + textAlign?: 'left' | 'center' | 'right'; + /** 垂直对齐 */ + textBaseline?: 'top' | 'middle' | 'bottom'; + /** 文字省略号位置 */ + textOverflow?: 'ellipsis' | 'clip' | string; + /** 最大行数('auto' 为自动换行) */ + lineClamp?: number | 'auto'; + /** 文字行高 */ + lineHeight?: number; + /** 下划线 */ + underline?: boolean; + /** 中划线 */ + lineThrough?: boolean; + /** 文字装饰线颜色 */ + underlineColor?: ColorPropertyDefine; + /** 文字吸附(表头文字始终可见) */ + textStick?: boolean; + /** 文字吸附基于对齐方向 */ + textStickBaseOnAlign?: boolean; + /** 标记(右上角红点) */ + marked?: boolean | ((args: StylePropertyFunctionArg) => boolean); +} +``` + +## 单元格样式 + +```typescript +/** 单元格完整样式 — 继承文字样式 */ +export interface IStyleOption extends ITextStyleOption { + /** 背景色 */ + bgColor?: ColorPropertyDefine; + /** 内边距(单值或 [上, 右, 下, 左]) */ + padding?: number | number[]; + /** 边框颜色 */ + borderColor?: ColorPropertyDefine; + /** 边框线宽 */ + borderLineWidth?: number | number[]; + /** 边框虚线 */ + borderLineDash?: number[]; + /** 链接文字颜色 */ + linkColor?: ColorPropertyDefine; + /** 鼠标光标 */ + cursor?: string; +} +``` + +## 颜色属性与样式回调 + +```typescript +/** 颜色值:可以是字符串、字符串数组(不同行交替)、或回调函数 */ +export type ColorPropertyDefine = string | string[] | ((args: StylePropertyFunctionArg) => string); + +/** 列样式可为静态对象或回调函数 */ +export type ColumnStyleOption = IStyleOption | ((args: StylePropertyFunctionArg) => IStyleOption); + +/** 样式回调参数 — 所有样式回调函数都接收此参数 */ +export interface StylePropertyFunctionArg { + /** 列号 */ + col: number; + /** 行号 */ + row: number; + /** 当前单元格的原始数据值 */ + dataValue: any; + /** 当前单元格的显示值 */ + value: any; + /** 表头路径 */ + cellHeaderPaths: ICellHeaderPaths; + /** 表格实例 */ + table: any; +} + +/** 表头路径信息 */ +export interface ICellHeaderPaths { + /** 列方向的表头路径 */ + colHeaderPaths?: { dimensionKey?: string; indicatorKey?: string; value?: string }[]; + /** 行方向的表头路径 */ + rowHeaderPaths?: { dimensionKey?: string; indicatorKey?: string; value?: string }[]; +} +``` + +## 主题定义 + +```typescript +/** 完整主题结构 */ +export interface ITableThemeDefine { + /** 默认 body 样式 */ + defaultStyle?: IStyleOption; + /** 列表头样式 */ + headerStyle?: IStyleOption; + /** 行表头样式 */ + rowHeaderStyle?: IStyleOption; + /** 角表头样式 */ + cornerHeaderStyle?: IStyleOption; + /** 底部冻结行样式 */ + bottomFrozenStyle?: IStyleOption; + /** 右侧冻结列样式 */ + rightFrozenStyle?: IStyleOption; + /** body 样式 */ + bodyStyle?: IStyleOption; + /** 帧外边框 */ + frameStyle?: { + borderColor?: string; + borderLineWidth?: number | number[]; + borderLineDash?: number[]; + cornerRadius?: number; + shadowBlur?: number; + shadowColor?: string; + shadowOffsetX?: number; + shadowOffsetY?: number; + }; + /** 选中区域样式 */ + selectionStyle?: { + cellBgColor?: string; + cellBorderColor?: string; + cellBorderLineWidth?: number; + headerHighlightBgColor?: string; + inlineRowBgColor?: string; + inlineColumnBgColor?: string; + }; + /** hover 样式 */ + hoverStyle?: { + cellBgColor?: string; + inlineRowBgColor?: string; + inlineColumnBgColor?: string; + }; + /** 滚动条样式 */ + scrollStyle?: { + visible?: 'always' | 'scrolling' | 'none' | 'focus'; + hoverOn?: boolean; + barToSide?: boolean; + scrollSliderColor?: string; + scrollRailColor?: string; + scrollSliderCornerRadius?: number; + width?: number; + }; + /** tooltip 样式 */ + tooltipStyle?: { + bgColor?: string; + color?: string; + fontSize?: number; + fontFamily?: string; + padding?: number[]; + }; + /** 列宽调整线样式 */ + columnResize?: { + lineColor?: string; + lineWidth?: number; + bgColor?: string; + width?: number; + }; + /** 冻结线样式 */ + frozenColumnLine?: { + shadow?: { + width?: number; + startColor?: string; + endColor?: string; + }; + }; + /** 拖拽表头样式 */ + dragHeaderSplitLine?: { + lineColor?: string; + lineWidth?: number; + shadowBlockColor?: string; + }; + /** checkbox 样式 */ + checkboxStyle?: { + checkedFill?: string; + uncheckedStroke?: string; + indeterminateFill?: string; + size?: number; + }; + /** radio 样式 */ + radioStyle?: { + checkedFill?: string; + uncheckedStroke?: string; + size?: number; + }; +} +``` + +## 内置主题 + +```typescript +/** 5 个内置主题 — 通过 VTable.themes.XXX 使用 */ +export type BuiltinThemeName = + | 'DEFAULT' // 默认浅色 + | 'ARCO' // ArcoDesign 风格 + | 'BRIGHT' // 明亮色调 + | 'DARK' // 深色主题 + | 'SIMPLIFY'; // 极简风格 + +// 使用方式: +// const table = new VTable.ListTable({ theme: VTable.themes.ARCO }); +// 扩展主题: +// const theme = VTable.themes.DEFAULT.extends({ bodyStyle: { bgColor: '#f5f5f5' } }); +``` + +## 条件样式 + +```typescript +/** 条件格式 */ +export interface ConditionalStyle { + /** 字段名 */ + field?: string; + /** 条件函数 */ + filter?: (args: { col: number; row: number; dataValue: any; value: any; table: any }) => boolean; + /** 满足条件时的样式 */ + style: IStyleOption; +} + +/** MarkCellStyle — 标记指定单元格样式 */ +export interface MarkCellStyle { + col?: number; + row?: number; + field?: string; + fieldValue?: any; + style: IStyleOption; +} + +/** 运行时自定义样式注册 */ +// table.registerCustomCellStyle(customStyleId, customStyle) +// 列定义中使用: customCellStyle: 'myStyleId' +export interface CustomCellStyle { + id: string; + style: IStyleOption; +} + +/** 自定义样式分配 */ +export interface CustomCellStyleArrangement { + cellPosition: { + col?: number; + row?: number; + range?: { start: { col: number; row: number }; end: { col: number; row: number } }; + }; + customStyleId: string; +} +``` diff --git a/skills/vtable-development-assistant/skill.md b/skills/vtable-development-assistant/skill.md new file mode 100644 index 000000000..f40f836cb --- /dev/null +++ b/skills/vtable-development-assistant/skill.md @@ -0,0 +1,53 @@ +--- +name: VTable Development Assistant +description: 帮助用户使用 @visactor/vtable 进行高性能表格组件开发,涵盖 ListTable、PivotTable、PivotChart 三种表格类型、13 种单元格类型、样式主题、自定义渲染、事件系统和完整 API。 +--- + +# VTable Development Assistant Skill + +## 角色定义 + +你是 VTable 开发助手,专门帮助用户使用 `@visactor/vtable` 进行高性能表格组件的开发。你熟悉 VTable 的三种核心表格类型(ListTable、PivotTable、PivotChart)、全部 13 种单元格类型、样式/主题系统、自定义渲染、事件系统和完整 API。 + +## 知识库结构 + +``` +references/ + knowledge/ # 结构化知识文档 + type/ # 用户可见的类型定义(markdown 格式) + examples/ # 精选示例代码模式 +``` + +## 查询路由规则 + +根据用户问题,查询对应知识模块: + +| 用户意图关键词 | 查询文件 | +| ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | +| 创建表格、初始化、入门 | `references/knowledge/00-overview.md` → `references/knowledge/01-table-types.md` | +| ListTable、基本表格、columns | `references/knowledge/01-table-types.md` → `references/type/list-table-options.md` | +| PivotTable、透视表、维度、指标 | `references/knowledge/01-table-types.md` → `references/knowledge/08-pivot-dimensions.md` → `references/type/pivot-table-options.md` | +| PivotChart、透视图、图表 | `references/knowledge/01-table-types.md` → `references/type/pivot-chart-options.md` | +| 列配置、cellType、单元格类型 | `references/knowledge/02-column-cell-types.md` → `references/type/column-defines.md` | +| 样式、style、颜色、字体 | `references/knowledge/03-style-theme.md` → `references/type/style-defines.md` | +| 主题、theme、DARK、ARCO | `references/knowledge/03-style-theme.md` | +| 自定义渲染、customRender | `references/knowledge/04-custom-render-layout.md` → `references/type/custom-render.md` | +| 自定义布局、customLayout、JSX | `references/knowledge/04-custom-render-layout.md` → `references/type/custom-layout.md` | +| API、方法、函数 | `references/knowledge/05-api-methods.md` | +| 属性、property | `references/knowledge/06-api-properties.md` | +| 事件、on、监听、click、scroll | `references/knowledge/07-events.md` → `references/type/event-types.md` | +| 维度、dimension、指标、indicator | `references/knowledge/08-pivot-dimensions.md` → `references/type/pivot-types.md` | +| 数据、records、dataSource | `references/knowledge/09-data-binding.md` | +| 交互、选择、hover、编辑、拖拽、排序 | `references/knowledge/10-interaction.md` | +| 最佳实践、模式、怎么做 | `references/knowledge/11-common-patterns.md` → `references/examples/` | + +## 代码生成规范 + +1. **始终使用 TypeScript**,import 来源为 `'@visactor/vtable'` +2. **表格实例化必须指定 `container`**(HTMLElement) +3. **列定义使用 `ColumnsDefine` 类型**(ListTable)或 `rows/columns/indicators`(PivotTable) +4. **样式属性支持两种形式**:静态值 和 `(arg: StylePropertyFunctionArg) => value` 回调函数 +5. **事件监听使用 `tableInstance.on('event_name', handler)`** +6. **销毁表格必须调用 `tableInstance.release()`** +7. 透视表数据分析需要配置 `dataConfig` 中的 `aggregationRules` +8. 自定义布局优先推荐 JSX 方案(`customLayout`),低级需求用 `customRender`