diff --git a/common/changes/@visactor/vtable/3645-bug-tree-node-move-position-error_2025-04-08-02-45.json b/common/changes/@visactor/vtable/3645-bug-tree-node-move-position-error_2025-04-08-02-45.json new file mode 100644 index 0000000000..be432b69f1 --- /dev/null +++ b/common/changes/@visactor/vtable/3645-bug-tree-node-move-position-error_2025-04-08-02-45.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "fix: when move tree node position code occor error #3645 #3706\n\n", + "type": "none", + "packageName": "@visactor/vtable" + } + ], + "packageName": "@visactor/vtable", + "email": "892739385@qq.com" +} \ No newline at end of file diff --git a/common/changes/@visactor/vtable/3684-bug-frame-border-with-array_2025-04-07-03-54.json b/common/changes/@visactor/vtable/3684-bug-frame-border-with-array_2025-04-07-03-54.json new file mode 100644 index 0000000000..5ef5a8be02 --- /dev/null +++ b/common/changes/@visactor/vtable/3684-bug-frame-border-with-array_2025-04-07-03-54.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "fix: frame border set array render bottom line position error #3684\n\n", + "type": "none", + "packageName": "@visactor/vtable" + } + ], + "packageName": "@visactor/vtable", + "email": "892739385@qq.com" +} \ No newline at end of file diff --git a/common/changes/@visactor/vtable/3693-bug-mobile-touch-event-error_2025-04-03-06-30.json b/common/changes/@visactor/vtable/3693-bug-mobile-touch-event-error_2025-04-03-06-30.json new file mode 100644 index 0000000000..98e04114ab --- /dev/null +++ b/common/changes/@visactor/vtable/3693-bug-mobile-touch-event-error_2025-04-03-06-30.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "fix: mobile touch event resize column width #3693\n\n", + "type": "none", + "packageName": "@visactor/vtable" + } + ], + "packageName": "@visactor/vtable", + "email": "892739385@qq.com" +} \ No newline at end of file diff --git a/common/changes/@visactor/vtable/3702-bug-disabledragselect-frozencolcount_2025-04-07-07-57.json b/common/changes/@visactor/vtable/3702-bug-disabledragselect-frozencolcount_2025-04-07-07-57.json new file mode 100644 index 0000000000..f2133f76cb --- /dev/null +++ b/common/changes/@visactor/vtable/3702-bug-disabledragselect-frozencolcount_2025-04-07-07-57.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "fix: when set frozen disableDragSelect not work #3702\n\n", + "type": "none", + "packageName": "@visactor/vtable" + } + ], + "packageName": "@visactor/vtable", + "email": "892739385@qq.com" +} \ No newline at end of file diff --git a/common/changes/@visactor/vtable/feat-changeCellValue_2025-04-08-13-35.json b/common/changes/@visactor/vtable/feat-changeCellValue_2025-04-08-13-35.json new file mode 100644 index 0000000000..3b99a3abf0 --- /dev/null +++ b/common/changes/@visactor/vtable/feat-changeCellValue_2025-04-08-13-35.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "feat: listTable added tiggerEvent parameter to changeCellValue\n\n", + "type": "none", + "packageName": "@visactor/vtable" + } + ], + "packageName": "@visactor/vtable", + "email": "blinyo@163.com" +} \ No newline at end of file diff --git a/common/changes/@visactor/vtable/feat-listtable-columntree_2025-04-03-10-14.json b/common/changes/@visactor/vtable/feat-listtable-columntree_2025-04-03-10-14.json new file mode 100644 index 0000000000..e83ceffab6 --- /dev/null +++ b/common/changes/@visactor/vtable/feat-listtable-columntree_2025-04-03-10-14.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "feat: list table header support hierarchy \n\n", + "type": "none", + "packageName": "@visactor/vtable" + } + ], + "packageName": "@visactor/vtable", + "email": "892739385@qq.com" +} \ No newline at end of file diff --git a/common/changes/@visactor/vtable/fix-custom-flex-layout_2025-04-09-08-08.json b/common/changes/@visactor/vtable/fix-custom-flex-layout_2025-04-09-08-08.json new file mode 100644 index 0000000000..61daf547f9 --- /dev/null +++ b/common/changes/@visactor/vtable/fix-custom-flex-layout_2025-04-09-08-08.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@visactor/vtable", + "comment": "fix: fix flex layout update in react-custom-layout component #3696", + "type": "none" + } + ], + "packageName": "@visactor/vtable" +} \ No newline at end of file diff --git a/common/changes/@visactor/vtable/fix-gantt-updateRecord_2025-04-07-11-35.json b/common/changes/@visactor/vtable/fix-gantt-updateRecord_2025-04-07-11-35.json new file mode 100644 index 0000000000..65b05c424b --- /dev/null +++ b/common/changes/@visactor/vtable/fix-gantt-updateRecord_2025-04-07-11-35.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "fix: updateTaskRecord api #3639\n\n", + "type": "none", + "packageName": "@visactor/vtable" + } + ], + "packageName": "@visactor/vtable", + "email": "892739385@qq.com" +} \ No newline at end of file diff --git a/common/changes/@visactor/vtable/refactor-computeColsWidth_api_call_time_2025-04-09-06-31.json b/common/changes/@visactor/vtable/refactor-computeColsWidth_api_call_time_2025-04-09-06-31.json new file mode 100644 index 0000000000..3a47be852c --- /dev/null +++ b/common/changes/@visactor/vtable/refactor-computeColsWidth_api_call_time_2025-04-09-06-31.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "fix: repeat call computeColsWidth adaptive mode result error\n\n", + "type": "none", + "packageName": "@visactor/vtable" + } + ], + "packageName": "@visactor/vtable", + "email": "892739385@qq.com" +} \ No newline at end of file diff --git a/docs/assets/api/en/methods.md b/docs/assets/api/en/methods.md index 3974e4fda7..c2ac4a6125 100644 --- a/docs/assets/api/en/methods.md +++ b/docs/assets/api/en/methods.md @@ -1060,7 +1060,7 @@ Change the value of a cell: ``` /\*_ Set the value of the cell. Note that it corresponds to the original value of the source data, and the vtable instance records will be modified accordingly _/ -changeCellValue: (col: number, row: number, value: string | number | null, workOnEditableCell = false) => void; +changeCellValue: (col: number, row: number, value: string | number | null, workOnEditableCell = false, triggerEvent = true) => void; ``` @@ -1077,7 +1077,7 @@ Change the value of cells in batches: - @param row The starting row number of pasted data - @param values Data array of multiple cells \*/ - changeCellValues(startCol: number, startRow: number, values: string[][]) + changeCellValues(startCol: number, startRow: number, values: string[][], workOnEditableCell = false, triggerEvent=true) => void; ``` diff --git a/docs/assets/api/zh/methods.md b/docs/assets/api/zh/methods.md index 94e58cc911..7bf508a0a1 100644 --- a/docs/assets/api/zh/methods.md +++ b/docs/assets/api/zh/methods.md @@ -956,8 +956,15 @@ use case: 对于透视图的场景上,点击图例项后 更新过滤规则 更改单元格的 value 值: ``` - /** 设置单元格的value值,注意对应的是源数据的原始值,vtable实例records会做对应修改 */ - changeCellValue: (col: number, row: number, value: string | number | null, workOnEditableCell = false) => void; + /** + * 设置单元格的value值,注意对应的是源数据的原始值,vtable实例records会做对应修改 + * @param col 单元格的起始列号 + * @param row 单元格的起始行号 + * @param value 更改后的值 + * @param workOnEditableCell 是否仅更改可编辑单元格 + * @param triggerEvent 是否在值发生改变的时候触发change_cell_value事件 + */ + changeCellValue: (col: number, row: number, value: string | number | null, workOnEditableCell = false, triggerEvent = true) => void; ``` ## changeCellValues(Function) @@ -971,8 +978,9 @@ use case: 对于透视图的场景上,点击图例项后 更新过滤规则 * @param row 粘贴数据的起始行号 * @param values 多个单元格的数据数组 * @param workOnEditableCell 是否仅更改可编辑单元格 + * @param triggerEvent 是否在值发生改变的时候触发change_cell_value事件 */ - changeCellValues(startCol: number, startRow: number, values: string[][], workOnEditableCell = false) + changeCellValues(startCol: number, startRow: number, values: string[][], workOnEditableCell = false, triggerEvent=true) => void; ``` ## getEditor(Function) diff --git a/docs/assets/demo/en/basic-functionality/list-table-header-group-collapse.md b/docs/assets/demo/en/basic-functionality/list-table-header-group-collapse.md new file mode 100644 index 0000000000..782e9b8312 --- /dev/null +++ b/docs/assets/demo/en/basic-functionality/list-table-header-group-collapse.md @@ -0,0 +1,115 @@ +--- +category: examples +group: Basic Features +title: List Table - Header Group Collapse +cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/preview/list-table-header-group.png +link: table-type/list-table +option: ListTable-columns-text#columns +--- + +# List Table - Header Group Collapse + +Configure columns as a nested multi-layer structure to achieve multi-layer header grouping effects. Enable tree-style expansion and collapse functionality through `headerHierarchyType: 'grid-tree'`, and set the default expansion level with `headerExpandLevel`. + +## Key Configurations + +- columns +- `headerHierarchyType` Set hierarchy display to `grid-tree` to enable tree-style expand/collapse +- `headerExpandLevel` Configure default expansion level (defaults to 1) + +## Code demo + +```javascript livedemo template=vtable +let tableInstance; +const records = [ + { + id: 1, + name: 'name.1', + name_1: 'name_1.1', + name_2: 'name_2.1', + name_2_1: 'name_2_1.1', + name_2_2: 'name_2_2.1' + }, + { + id: 2, + name: 'name.2', + name_1: 'name_1.2', + name_2: 'name_2.2', + name_2_1: 'name_2_1.2', + name_2_2: 'name_2_2.2' + }, + { + id: 3, + name: 'name.3', + name_1: 'name_1.3', + name_2: 'name_2.3', + name_2_1: 'name_2_1.3', + name_2_2: 'name_2_2.3' + }, + { + id: 4, + name: 'name.4', + name_1: 'name_1.4', + name_2: 'name_2.4', + name_2_1: 'name_2_1.4', + name_2_2: 'name_2_2.4' + }, + { + id: 5, + name: 'name.5', + name_1: 'name_1.5', + name_2: 'name_2.5', + name_2_1: 'name_2_1.5', + name_2_2: 'name_2_2.5' + } +]; + +const columns = [ + { + field: 'id', + title: 'ID', + width: 100 + }, + { + field: 'name', + title: 'Name', + columns: [ + { + field: 'name_1', + title: 'Name_1', + width: 120 + }, + { + field: 'name_2', + title: 'Name_2', + width: 150, + columns: [ + { + field: 'name_2_1', + title: 'Name_2_1', + width: 150 + }, + { + field: 'name_2_2', + title: 'Name_2_2', + width: 150 + } + ] + } + ] + } +]; + +const option = { + records, + columns, + headerHierarchyType: 'grid-tree', + headerExpandLevel: 3, + widthMode: 'standard', + autoWrapText: true, + autoRowHeight: true, + defaultColWidth: 150 +}; +tableInstance = new VTable.ListTable(document.getElementById(CONTAINER_ID), option); +window['tableInstance'] = tableInstance; +``` diff --git a/docs/assets/demo/en/component/dropdown.md b/docs/assets/demo/en/component/dropdown.md index 8e237859f2..7aa2715a4b 100644 --- a/docs/assets/demo/en/component/dropdown.md +++ b/docs/assets/demo/en/component/dropdown.md @@ -11,7 +11,7 @@ option: ListTable-columns-text#dropDownMenu # drop down menu In this example, the dropDownMenu is configured in the first column of columns, and when hovered to the header cell, a drop-down menu is displayed for further operation. -At the same time through monitoring`click_cell`Event, when the mouse clicks the order icon in the first column, the interface showDropDownMenu is called to display the drop-down menu. To continue the operation according to the item clicked on the drop-down menu, you can listen to the event dropdownmenu_click. +At the same time through monitoring`click_cell`Event, when the mouse clicks the order icon in the first column, the interface showDropDownMenu is called to display the drop-down menu. To continue the operation according to the item clicked on the drop-down menu, you can listen to the event dropdown_menu_click. ## Key Configurations diff --git a/docs/assets/demo/en/interaction/context-menu.md b/docs/assets/demo/en/interaction/context-menu.md index c1f987733a..ceb4e41a0f 100644 --- a/docs/assets/demo/en/interaction/context-menu.md +++ b/docs/assets/demo/en/interaction/context-menu.md @@ -10,7 +10,7 @@ option: ListTable#menu.contextMenuItems # Right click menu -Right-click pop-up menu, if you need to click on the drop-down menu to continue the operation, you can listen to the event dropdownmenu_click. +Right-click pop-up menu, if you need to click on the drop-down menu to continue the operation, you can listen to the event dropdown_menu_click. In this example, after clicking the right mouse button, a copy, paste, delete and other functions will appear in the drop-down menu. After clicking the copy, the selected cell content will be copied to the clipboard, after clicking paste, the content in the clipboard will be pasted to the selected cell, and after clicking delete, the content of the selected cell will be set to empty. diff --git a/docs/assets/demo/menu.json b/docs/assets/demo/menu.json index 51b1716f84..465824021a 100644 --- a/docs/assets/demo/menu.json +++ b/docs/assets/demo/menu.json @@ -520,6 +520,13 @@ "en": "List Table - Header Group" } }, + { + "path": "list-table-header-group-collapse", + "title": { + "zh": "基本表格表头分组与折叠", + "en": "List Table - Header Group Collapse" + } + }, { "path": "auto-wrap-text", "title": { diff --git a/docs/assets/demo/zh/basic-functionality/list-table-header-group-collapse.md b/docs/assets/demo/zh/basic-functionality/list-table-header-group-collapse.md new file mode 100644 index 0000000000..bf8500cf6a --- /dev/null +++ b/docs/assets/demo/zh/basic-functionality/list-table-header-group-collapse.md @@ -0,0 +1,115 @@ +--- +category: examples +group: Basic Features +title: 基本表格表头分组与折叠 +cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/preview/list-table-header-group.png +link: table-type/list-table +option: ListTable-columns-text#columns +--- + +# 基本表格表头分组与折叠 + +将 columns 配置为嵌套多层结构来实现多层表头分组效果,可通过配置 `headerHierarchyType: 'grid-tree'` 开启树形的展开和折叠,并通过 `headerExpandLevel` 来设置默认展开层级。 + +## 关键配置 + +- columns +- `headerHierarchyType` 将层级展示设置为 `grid-tree`,开启树形的展开和折叠功能 +- `headerExpandLevel` 设置默认展开层级,默认为`1` + +## 代码演示 + +```javascript livedemo template=vtable +let tableInstance; +const records = [ + { + id: 1, + name: 'name.1', + name_1: 'name_1.1', + name_2: 'name_2.1', + name_2_1: 'name_2_1.1', + name_2_2: 'name_2_2.1' + }, + { + id: 2, + name: 'name.2', + name_1: 'name_1.2', + name_2: 'name_2.2', + name_2_1: 'name_2_1.2', + name_2_2: 'name_2_2.2' + }, + { + id: 3, + name: 'name.3', + name_1: 'name_1.3', + name_2: 'name_2.3', + name_2_1: 'name_2_1.3', + name_2_2: 'name_2_2.3' + }, + { + id: 4, + name: 'name.4', + name_1: 'name_1.4', + name_2: 'name_2.4', + name_2_1: 'name_2_1.4', + name_2_2: 'name_2_2.4' + }, + { + id: 5, + name: 'name.5', + name_1: 'name_1.5', + name_2: 'name_2.5', + name_2_1: 'name_2_1.5', + name_2_2: 'name_2_2.5' + } +]; + +const columns = [ + { + field: 'id', + title: 'ID', + width: 100 + }, + { + field: 'name', + title: 'Name', + columns: [ + { + field: 'name_1', + title: 'Name_1', + width: 120 + }, + { + field: 'name_2', + title: 'Name_2', + width: 150, + columns: [ + { + field: 'name_2_1', + title: 'Name_2_1', + width: 150 + }, + { + field: 'name_2_2', + title: 'Name_2_2', + width: 150 + } + ] + } + ] + } +]; + +const option = { + records, + columns, + headerHierarchyType: 'grid-tree', + headerExpandLevel: 3, + widthMode: 'standard', + autoWrapText: true, + autoRowHeight: true, + defaultColWidth: 150 +}; +tableInstance = new VTable.ListTable(document.getElementById(CONTAINER_ID), option); +window['tableInstance'] = tableInstance; +``` diff --git a/docs/assets/demo/zh/component/dropdown.md b/docs/assets/demo/zh/component/dropdown.md index 88812b3a10..637e7182e6 100644 --- a/docs/assets/demo/zh/component/dropdown.md +++ b/docs/assets/demo/zh/component/dropdown.md @@ -11,7 +11,7 @@ option: ListTable-columns-text#dropDownMenu # 下拉菜单 在该示例中,在 columns 第一列中配置了 dropDownMenu, 当 hover 到表头单元格时会显示下拉菜单以进行进一步操作。 -同时通过监听`click_cell`事件,鼠标点击第一列中订单 icon 时,调用接口 showDropDownMenu 来显示下拉菜单。如需根据点击下拉菜单的项目来继续操作,可以监听事件 dropdownmenu_click。 +同时通过监听`click_cell`事件,鼠标点击第一列中订单 icon 时,调用接口 showDropDownMenu 来显示下拉菜单。如需根据点击下拉菜单的项目来继续操作,可以监听事件 dropdown_menu_click。 ## 关键配置 diff --git a/docs/assets/demo/zh/interaction/context-menu.md b/docs/assets/demo/zh/interaction/context-menu.md index e8f3695573..2a80edf1b1 100644 --- a/docs/assets/demo/zh/interaction/context-menu.md +++ b/docs/assets/demo/zh/interaction/context-menu.md @@ -10,7 +10,7 @@ option: ListTable#menu.contextMenuItems # 右键菜单 -右键弹出菜单, 如需根据点击下拉菜单的项目来继续操作,可以监听事件 dropdownmenu_click。 +右键弹出菜单, 如需根据点击下拉菜单的项目来继续操作,可以监听事件 dropdown_menu_click。 本例中,点击右键后会出现复制、粘贴、删除等功能的下拉菜单,点击复制后,会将选中的单元格内容复制到剪贴板,点击粘贴后,会将剪贴板中的内容粘贴到选中的单元格中,点击删除后,会将选中的单元格内容设置为空。 diff --git a/docs/assets/faq/en/16-How to customize the drop-down menu in table component.md b/docs/assets/faq/en/16-How to customize the drop-down menu in table component.md index c2f8e4b1be..9ffaecf78d 100644 --- a/docs/assets/faq/en/16-How to customize the drop-down menu in table component.md +++ b/docs/assets/faq/en/16-How to customize the drop-down menu in table component.md @@ -58,11 +58,11 @@ menu: { 2. Configure in the header dropDownMenu can be configured in columns. The items are the same as defaultHeaderMenuItems. The menu only takes effect in the corresponding column. 3. Menu selection status update - After the drop-down menu item is selected, the "dropdownmenu_click" event will be triggered. The listening event updates the drop-down menu status through the setDropDownMenuHighlight interface. The selected item text and icon will change the style. + After the drop-down menu item is selected, the "dropdown_menu_click" event will be triggered. The listening event updates the drop-down menu status through the setDropDownMenuHighlight interface. The selected item text and icon will change the style. ```javascript -table.on('dropdownmenu_click', (args: any) => { - console.log('dropdownmenu_click', args); +table.on('dropdown_menu_click', (args: any) => { + console.log('dropdown_menu_click', args); table.setDropDownMenuHighlight([args]); }); ``` @@ -140,8 +140,8 @@ const option: TYPES.ListTableConstructorOptions = { } }; const table = new ListTable(document.getElementById('container'), option); -table.on('dropdownmenu_click', (args: any) => { - console.log('dropdownmenu_click', args); +table.on('dropdown_menu_click', (args: any) => { + console.log('dropdown_menu_click', args); table.setDropDownMenuHighlight([args]); }); ``` diff --git a/docs/assets/faq/en/18-How to customize the context menu in the table component.md b/docs/assets/faq/en/18-How to customize the context menu in the table component.md index c3d5ec639b..753869eb55 100644 --- a/docs/assets/faq/en/18-How to customize the context menu in the table component.md +++ b/docs/assets/faq/en/18-How to customize the context menu in the table component.md @@ -38,10 +38,10 @@ Menu item configuration: - text: the text of the menu item - menuKey: unique identifier of the menu item -After the drop-down menu item is selected, the "dropdownmenu_click" event will be triggered, and you can listen to the event and perform related operations. +After the drop-down menu item is selected, the "dropdown_menu_click" event will be triggered, and you can listen to the event and perform related operations. ```javascript -table.on('dropdownmenu_click', (args: any) => { +table.on('dropdown_menu_click', (args: any) => { console.log('menu_click', args); }); ``` @@ -65,7 +65,7 @@ const option: TYPES.ListTableConstructorOptions = { } }; const table = new ListTable(document.getElementById('container'), option); -table.on('dropdownmenu_click', (args: any) => { +table.on('dropdown_menu_click', (args: any) => { console.log('menu_click', args); }); ``` diff --git a/docs/assets/faq/zh/16-How to customize the drop-down menu in table component.md b/docs/assets/faq/zh/16-How to customize the drop-down menu in table component.md index 9683ef1b7f..d887811c69 100644 --- a/docs/assets/faq/zh/16-How to customize the drop-down menu in table component.md +++ b/docs/assets/faq/zh/16-How to customize the drop-down menu in table component.md @@ -58,11 +58,11 @@ menu: { 2. 在表头中配置 在 columns 中可以配置 dropDownMenu,项目与 defaultHeaderMenuItems 相同,该菜单只在对应的列中生效。 3. 菜单选中状态更新 - 下拉菜单项目选中后,会触发"dropdownmenu_click"事件,监听事件事件通过 setDropDownMenuHighlight 接口更新下拉菜单状态,选中的项目文字和 icon 会更改样式。 + 下拉菜单项目选中后,会触发"dropdown_menu_click"事件,监听事件事件通过 setDropDownMenuHighlight 接口更新下拉菜单状态,选中的项目文字和 icon 会更改样式。 ```javascript -table.on('dropdownmenu_click', (args: any) => { - console.log('dropdownmenu_click', args); +table.on('dropdown_menu_click', (args: any) => { + console.log('dropdown_menu_click', args); table.setDropDownMenuHighlight([args]); }); ``` @@ -140,8 +140,8 @@ const option: TYPES.ListTableConstructorOptions = { } }; const table = new ListTable(document.getElementById('container'), option); -table.on('dropdownmenu_click', (args: any) => { - console.log('dropdownmenu_click', args); +table.on('dropdown_menu_click', (args: any) => { + console.log('dropdown_menu_click', args); table.setDropDownMenuHighlight([args]); }); ``` diff --git a/docs/assets/faq/zh/18-How to customize the context menu in the table component.md b/docs/assets/faq/zh/18-How to customize the context menu in the table component.md index 95eaf64ac5..517d9c4cc4 100644 --- a/docs/assets/faq/zh/18-How to customize the context menu in the table component.md +++ b/docs/assets/faq/zh/18-How to customize the context menu in the table component.md @@ -38,10 +38,10 @@ menu: { - text: 菜单项目的文字 - menuKey: 菜单项目的唯一标识符 -下拉菜单项目选中后,会触发"dropdownmenu_click"事件,可以监听事件事件执行相关操作。 +下拉菜单项目选中后,会触发"dropdown_menu_click"事件,可以监听事件事件执行相关操作。 ```javascript -table.on('dropdownmenu_click', (args: any) => { +table.on('dropdown_menu_click', (args: any) => { console.log('menu_click', args); }); ``` @@ -65,7 +65,7 @@ const option: TYPES.ListTableConstructorOptions = { } }; const table = new ListTable(document.getElementById('container'), option); -table.on('dropdownmenu_click', (args: any) => { +table.on('dropdown_menu_click', (args: any) => { console.log('menu_click', args); }); ``` diff --git a/docs/assets/guide/en/Event/event_list.md b/docs/assets/guide/en/Event/event_list.md index ced2ecbcdf..4391c9eb27 100644 --- a/docs/assets/guide/en/Event/event_list.md +++ b/docs/assets/guide/en/Event/event_list.md @@ -33,7 +33,7 @@ For a more comprehensive list of events, please refer to: https://visactor.io/vt | After sort | AFTER_SORT | Execute after sorting event | | Click Fixed Column | FREEZE_CLICK | Click Fixed Column Icon Event | | Scroll | SCROLL | Scroll Table Events | -| Click the drop-down icon | DROPDOWNMENU_CLICK | Click the drop-down menu icon event | +| Click the drop-down icon | DROPDOWN_MENU_CLICK | Click the drop-down menu icon event | | Click on the drop-down menu | MENU_CLICK | Click on the drop-down menu Events | | Mouse over miniature | MOUSEOVER_CHART_SYMBOL | Mouse over miniature mark events | | Drag and drop box to select mouse release | DRAG_SELECT_END | Drag and drop box to select cell mouse release event | diff --git a/docs/assets/guide/en/gantt/introduction.md b/docs/assets/guide/en/gantt/introduction.md index e7eb24a496..725f1caab2 100644 --- a/docs/assets/guide/en/gantt/introduction.md +++ b/docs/assets/guide/en/gantt/introduction.md @@ -156,11 +156,6 @@ If there is no field data for the task date in the original data, you can create The button style can be configured via `taskBar.scheduleCreation.buttonStyle`. If the current configuration does not meet your needs, you can also customize the display effect of the creation schedule through the `taskBar.scheduleCreation.customLayout` configuration item. -**Note: Different Gantt chart instances have different capabilities to create schedules.** - -When `tasksShowMode` is `TasksShowMode.Tasks_Separate` or `TasksShowMode.Sub_Tasks_Separate`, each piece of data has a corresponding row position display, but when there is no `startDate` and `endDate` field set in the data, a create button will appear when the mouse hovers over the row, and clicking the button will create a schedule and display the task bar. - -When `tasksShowMode` is `TasksShowMode.Sub_Tasks_Inline`, `TasksShowMode.Sub_Tasks_Arrange`, or `TasksShowMode.Sub_Tasks_Compact`, a create button will be displayed when the mouse hovers over the blank area, and clicking the button will trigger the event `GANTT_EVENT_TYPE.CREATE_TASK_SCHEDULE`, but it will not actually create a task schedule. The user needs to listen for this event and create a schedule update data according to business needs. **Note: Different Gantt chart instances have different capabilities to create schedules.** diff --git a/docs/assets/guide/en/table_type/List_table/columns_tree.md b/docs/assets/guide/en/table_type/List_table/columns_tree.md new file mode 100644 index 0000000000..ecbb3f6b3e --- /dev/null +++ b/docs/assets/guide/en/table_type/List_table/columns_tree.md @@ -0,0 +1,114 @@ +## Basic Table Header Grouping and Collapsing + +In this tutorial, we will learn how to implement multi-level header grouping and collapsing in VTable using a tree structure to visualize complex header hierarchies. + +--- + +## Use Cases + +Header grouping and collapsing are suitable for the following scenarios: + +- **Multidimensional Data Analysis**: Combine related fields into logical groups (e.g., "Sales Data" includes sub-columns like "Revenue" and "Profit"). +- **Complex Data Structures**: Tables with explicit hierarchical relationships (e.g., "Region-Province-City" three-level structure). +- **Space Optimization**: Improve readability by collapsing non-critical columns. +- **Dynamic Interaction**: Allow users to expand/collapse specific groups on demand for flexible data exploration. + +--- + +## Implementation Steps + +### 1. Configure Multi-Level Header Structure + +Define header hierarchies using nested structures in the `columns` configuration. Each group adds sub-columns via the `columns` field to form a tree relationship. + +### 2. Enable Tree-Style Collapsing + +Set `headerHierarchyType: 'grid-tree'` to enable interactive tree-style collapsing for headers. + +### 3. Set Default Expansion Level + +Specify the initial expansion level using `headerExpandLevel` (default: `1`, showing only the first-level groups). + +--- + +## Example + +```javascript livedemo template=vtable +const records = [ + { region: 'North', province: 'Province A', city: 'City 1', revenue: 1000, cost: 600 }, + { region: 'North', province: 'Province A', city: 'City 2', revenue: 1500, cost: 800 }, + { region: 'South', province: 'Province B', city: 'City 3', revenue: 2000, cost: 1100 } +]; + +const columns = [ + { + title: 'Region', + field: 'region', + width: 150, + columns: [ + { + title: 'Province', + field: 'province', + width: 150, + columns: [ + { + title: 'City', + field: 'city', + width: 150 + } + ] + } + ] + }, + { + title: 'Financial Metrics', + field: 'metrics', + width: 180, + columns: [ + { title: 'Revenue', field: 'revenue', width: 150 }, + { title: 'Cost', field: 'cost', width: 150 } + ] + } +]; + +const option = { + records, + columns, + headerHierarchyType: 'grid-tree', // Enable tree-style collapsing + headerExpandLevel: 2, // Expand to the second level by default + widthMode: 'standard', + defaultRowHeight: 40 +}; + +const tableInstance = new VTable.ListTable(document.getElementById(CONTAINER_ID), option); +window.tableInstance = tableInstance; +``` + +--- + +## Advanced Configuration + +### Listen to Collapse Events + +Capture user interactions and execute custom logic: + +```javascript +tableInstance.on(VTable.ListTable.EVENT_TYPE.TREE_HIERARCHY_STATE_CHANGE, args => { + if (args.cellLocation === 'columnHeader') { + console.log('Header state changed:', args); + } +}); +``` + +### Dynamically Update Header States + +Programmatically control header expansion/collapse: + +```javascript +// Toggle the expansion state of a specific column +tableInstance.toggleHierarchyState(col, row); +``` + +--- + +With these configurations, you can quickly implement structured multi-level headers with dynamic interactions, ideal for complex data analysis scenarios. diff --git a/docs/assets/guide/menu.json b/docs/assets/guide/menu.json index c4e7535d66..0577c37e5d 100644 --- a/docs/assets/guide/menu.json +++ b/docs/assets/guide/menu.json @@ -87,6 +87,13 @@ "en": "tree list" } }, + { + "path": "columns_tree", + "title": { + "zh": "表头分组及折叠", + "en": "Columns tree" + } + }, { "path": "group_list", "title": { diff --git a/docs/assets/guide/zh/Event/event_list.md b/docs/assets/guide/zh/Event/event_list.md index a31f2ed4f1..9927d0a0e9 100644 --- a/docs/assets/guide/zh/Event/event_list.md +++ b/docs/assets/guide/zh/Event/event_list.md @@ -35,7 +35,7 @@ | 滚动 | SCROLL | 滚动表格事件 | | 滚动 | SCROLL_HORIZONTAL_END | 横向滚动右侧事件 | | 滚动 | SCROLL_VERTICAL_END | 竖向滚动底部事件 | -| 点击下拉图标 | DROPDOWNMENU_CLICK | 点击下拉菜单图标事件 | +| 点击下拉图标 | DROPDOWN_MENU_CLICK | 点击下拉菜单图标事件 | | 点击下拉菜单 | MENU_CLICK | 点击下拉菜单事件 | | 鼠标经过迷你图 | MOUSEOVER_CHART_SYMBOL | 鼠标经过迷你图标记事件 | | 拖拽框选鼠标松开 | DRAG_SELECT_END | 拖拽框选单元格鼠标松开事件 | diff --git a/docs/assets/guide/zh/gantt/introduction.md b/docs/assets/guide/zh/gantt/introduction.md index 521f6ea387..6f1555c939 100644 --- a/docs/assets/guide/zh/gantt/introduction.md +++ b/docs/assets/guide/zh/gantt/introduction.md @@ -156,11 +156,6 @@ links:[ 按钮的样式可以通过`taskBar.scheduleCreation.buttonStyle`配置。 如果当前配置不能满足需求,也可以通过`taskBar.scheduleCreation.customLayout`配置项自定义创建排期的展示效果。 -**注意:不同的甘特图实例,创建排期能力不同。:** - -当`tasksShowMode`为`TasksShowMode.Tasks_Separate`或`TasksShowMode.Sub_Tasks_Separate`,也就是每条数据有对应的一行位置展示,但是数据中没有设置 startDate 和 endDate 的字段时,鼠标 hover 到该行会出现创建按钮,点击按钮会创建排期并展示任务条。 - -当`tasksShowMode`为`TasksShowMode.Sub_Tasks_Inline`或`TasksShowMode.Sub_Tasks_Arrange`或`TasksShowMode.Sub_Tasks_Compact`,需要明确指明 scheduleCreatable 为`true`,才可出现创建按钮。当鼠标 hover 到空白区域即会显示创建按钮,点击按钮会触发事件`GANTT_EVENT_TYPE.CREATE_TASK_SCHEDULE`但不会真正的创建任务排期,使用者需要监听该事件根据业务需求来自行创建排期更新数据。 **注意:不同的甘特图实例,创建排期能力不同。:** diff --git a/docs/assets/guide/zh/table_type/List_table/columns_tree.md b/docs/assets/guide/zh/table_type/List_table/columns_tree.md new file mode 100644 index 0000000000..5f32092b7e --- /dev/null +++ b/docs/assets/guide/zh/table_type/List_table/columns_tree.md @@ -0,0 +1,108 @@ +## 基本表格表头分组及折叠 + +在本教程中,我们将学习如何使用 VTable 实现多层表头分组及折叠功能,通过树形结构展示复杂表头层级。 + +## 使用场景 + +表头分组及折叠功能适用于以下场景: + +- **多维数据分析**:需要将多个关联字段合并为逻辑分组(如“销售数据”包含“销售额”“利润”等子列)。 +- **复杂数据结构**:数据表字段具有明确的层级关系(如“地区-省份-城市”三级结构)。 +- **空间优化**:通过折叠功能隐藏非关键列,提升表格可读性。 +- **动态交互**:允许用户按需展开/收起特定分组,灵活查看数据。 + +## 使用方式 + +### 1. 配置多层表头结构 + +在 `columns` 配置中使用嵌套结构定义表头层级。每个分组通过 `columns` 字段添加子列,形成树形关系。 + +### 2. 启用树形折叠功能 + +设置 `headerHierarchyType: 'grid-tree'` 开启表头树形折叠交互。 + +### 3. 设置默认展开层级 + +通过 `headerExpandLevel` 指定初始展开层级(默认值为 `1`,即仅展示第一级分组)。 + +## 示例 + +```javascript livedemo template=vtable +const records = [ + { region: 'North', province: 'A', city: 'City1', revenue: 1000, cost: 600 }, + { region: 'North', province: 'A', city: 'City2', revenue: 1500, cost: 800 }, + { region: 'South', province: 'B', city: 'City3', revenue: 2000, cost: 1100 } +]; + +const columns = [ + { + title: 'Region', + field: 'region', + width: 150, + columns: [ + { + title: 'Province', + field: 'province', + width: 150, + columns: [ + { + title: 'City', + field: 'city', + width: 150 + } + ] + } + ] + }, + { + title: 'Financial Metrics', + field: 'metrics', + width: 180, + columns: [ + { title: 'Revenue', field: 'revenue', width: 150 }, + { title: 'Cost', field: 'cost', width: 150 } + ] + } +]; + +const option = { + records, + columns, + headerHierarchyType: 'grid-tree', // 启用树形折叠 + headerExpandLevel: 2, // 默认展开至第二级 + widthMode: 'standard', + defaultRowHeight: 40 +}; + +const tableInstance = new VTable.ListTable(document.getElementById(CONTAINER_ID), option); +window.tableInstance = tableInstance; +``` + +--- + +## 高级配置 + +### 监听折叠事件 + +获取用户交互行为并执行自定义逻辑: + +```javascript +tableInstance.on(VTable.ListTable.EVENT_TYPE.TREE_HIERARCHY_STATE_CHANGE, args => { + if (args.cellLocation === 'columnHeader') { + console.log('表头状态变化:', args); + } +}); +``` + +### 动态更新表头状态 + +手动控制表头展开/收起: + +```javascript +// 切换指定列的展开状态 +tableInstance.toggleHierarchyState(col, row); +``` + +--- + +通过上述配置,可快速实现多层表头的结构化展示与动态交互,适用于复杂数据场景下的灵活分析需求。 diff --git a/docs/assets/option/en/common/option-secondary.md b/docs/assets/option/en/common/option-secondary.md index 894a21bff9..0b91964b75 100644 --- a/docs/assets/option/en/common/option-secondary.md +++ b/docs/assets/option/en/common/option-secondary.md @@ -194,7 +194,6 @@ Align excel advanced capabilities Fill handle, when set to true, when a cell is selected, the fill handle will be displayed on the lower right side of the cell. You can drag the fill handle to edit the value of the cell. Or double-click the fill handle to change the value of the cell you want to edit. - #${prefix} hover(Object) Hover interaction configuration, specific configuration items as follows: @@ -231,6 +230,20 @@ Possible values: 'body': Do not select the table header. Clicking a row header selects all body cells in the row. Clicking a column header selects all body cells in the column. +##${prefix} cornerHeaderSelectMode ('inline' | 'cell' | 'body' | 'all') = 'all' + +When clicking on the corner header cell, the selection mode to be applied. + +Possible values: + +'inline': Clicking the corner header selects the entire column; + +'cell': Select only the currently clicked corner header cell; + +'body': Clicking the corner header selects all body cells; + +'all': Clicking the corner header selects the entire table. + ##${prefix} disableSelect (boolean | ((col: number, row: number, table: BaseTableAPI) => boolean)) = false Do not respond to mouse select interaction. @@ -653,4 +666,4 @@ Validate when the drag to move position ends. ``` validateDragOrderOnEnd?: (source: CellAddress, target: CellAddress) => boolean; -``` \ No newline at end of file +``` diff --git a/docs/assets/option/en/table/listTable.md b/docs/assets/option/en/table/listTable.md index 50c73f0741..3ff595bb3d 100644 --- a/docs/assets/option/en/table/listTable.md +++ b/docs/assets/option/en/table/listTable.md @@ -93,6 +93,13 @@ When displayed as a tree structure, the number of levels is expanded by default. Whether nodes at the same level are aligned by text, such as nodes without collapsed expansion icons and nodes with icons. Default is false +## headerHierarchyType('grid-tree') + +Defines the hierarchy display mode for headers. When set to 'grid-tree', it enables tree-style expand/collapse functionality in the header structure. + +## headerExpandLevel(number) + +Sets the initial expansion level of headers. Defaults to 1. ## aggregation(Aggregation|CustomAggregation|Array|Function) diff --git a/docs/assets/option/zh/common/option-secondary.md b/docs/assets/option/zh/common/option-secondary.md index b0cef896ce..9290e53fb5 100644 --- a/docs/assets/option/zh/common/option-secondary.md +++ b/docs/assets/option/zh/common/option-secondary.md @@ -192,8 +192,6 @@ export interface SelectAllOnCtrlAOption { 填充柄,设置为 true 后,当选中单元格后,填充柄会显示在单元格右下方,可以拖动填充柄来编辑单元格的值。或者双击填充柄来改变需要编辑单元格的值。 - - #${prefix} hover(Object) hover 交互配置,具体配置项如下: @@ -227,6 +225,20 @@ hover 交互响应模式:十字交叉、整列、整行或者单个单元格 'body': 不选择表头,点击行表头则选择该行所有 body 单元格,点击列表头则选择该列所有 body 单元格。 +##${prefix} cornerHeaderSelectMode ('inline' | 'cell' | 'body' | 'all') = 'all' + +点击角头 corner 单元格时的选中方式。 + +可选值: + +'inline': 点击 corner 角头整列选中; + +'cell': 仅仅选择当前点击的 corner 角头单元格; + +'body': 点击 corner 角头,选择所有 body 单元格; + +'all': 点击 corner 角头,选中整个图表。 + ##${prefix} disableSelect (boolean | ((col: number, row: number, table: BaseTableAPI) => boolean)) = false 不响应鼠标 select 交互。 @@ -638,7 +650,7 @@ animationAppear?: boolean | { ##${prefix} frozenColDragHeaderMode(string) = 'fixedFrozenCount' -拖拽表头移动位置 针对冻结部分的规则 默认为 fixedFrozenCount。基本表格ListTable类型设置才有效! +拖拽表头移动位置 针对冻结部分的规则 默认为 fixedFrozenCount。基本表格 ListTable 类型设置才有效! - "disabled"(禁止调整冻结列位置):不允许其他列的表头移入冻结列,也不允许冻结列移出,冻结列保持不变。 - "adjustFrozenCount"(根据交互结果调整冻结数量):允许其他列的表头移入冻结列,及冻结列移出,并根据拖拽的动作调整冻结列的数量。当其他列的表头被拖拽进入冻结列位置时,冻结列数量增加;当其他列的表头被拖拽移出冻结列位置时,冻结列数量减少。 @@ -651,6 +663,3 @@ animationAppear?: boolean | { ``` validateDragOrderOnEnd?: (source: CellAddress, target: CellAddress) => boolean; ``` - - - diff --git a/docs/assets/option/zh/table/listTable.md b/docs/assets/option/zh/table/listTable.md index 46912dd2f7..42b7aa478b 100644 --- a/docs/assets/option/zh/table/listTable.md +++ b/docs/assets/option/zh/table/listTable.md @@ -90,6 +90,13 @@ SortState { 同层级的结点是否按文字对齐 如没有收起展开图标的节点和有图标的节点文字对齐 默认 false +## headerHierarchyType('grid-tree') + +表头中层级维度结构显示形式,设置为 'grid-tree' 时开启树形结构的展开折叠功能。 + +## headerExpandLevel(number) + +表头初始化展开层数,默认是 1。 ## aggregation(Aggregation|CustomAggregation|Array|Function) diff --git a/packages/react-vtable/demo/src/App.tsx b/packages/react-vtable/demo/src/App.tsx index 245516aea8..0e34141142 100644 --- a/packages/react-vtable/demo/src/App.tsx +++ b/packages/react-vtable/demo/src/App.tsx @@ -23,6 +23,8 @@ import customLayoutDomSite from './component/custom-layout-dom-site'; import customLayoutDomSite1 from './component/custom-layout-dom-site-1'; import customLayoutPivot from './component/custom-layout-pivot'; +import userCustomLayoutUpdate from './component/user/custom-layout-update'; + // export default listTable; // export default listEditor; // export default listOptionRecord; @@ -45,5 +47,7 @@ import customLayoutPivot from './component/custom-layout-pivot'; // export default customLayout; // export default customLayoutDom; // export default customLayoutDomSite; -export default customLayoutDomSite1; +// export default customLayoutDomSite1; // export default customLayoutPivot; + +export default userCustomLayoutUpdate; diff --git a/packages/react-vtable/demo/src/component/user/custom-layout-update.tsx b/packages/react-vtable/demo/src/component/user/custom-layout-update.tsx new file mode 100644 index 0000000000..e4b6eed6df --- /dev/null +++ b/packages/react-vtable/demo/src/component/user/custom-layout-update.tsx @@ -0,0 +1,188 @@ +/* eslint-disable max-len */ +import { useEffect, useRef, useState } from 'react'; +import ReactDOM from 'react-dom/client'; +import type { CustomLayoutFunctionArg } from '../../../../src'; +import { + ListTable, + ListColumn, + CustomLayout, + Group, + Text, + Tag, + Checkbox, + Radio, + Button, + Link, + Avatar, + Image, + Popover +} from '../../../../src'; + +const UserProfileComponent = (props: any) => { + const { table, row, col, rect, dataValue, align } = props; + if (!table || row === undefined || col === undefined) { + return null; + } + const { height, width } = rect || table.getCellRect(col, row); + const record = table.getRecordByCell(col, row); + + const [hover, setHover] = useState(false); + + return ( + + + + + + ); +}; + +function App() { + const [col, setCol] = useState([ + { title: 'ID', field: 'bloggerId' }, + { title: 'Name', field: 'bloggerName' } + ]); + const records = [ + { + bloggerId: 1, + bloggerName: 'Virtual Anchor Xiaohua', + bloggerAvatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg', + introduction: + 'Hi everyone, I am Xiaohua, the virtual host. I am a little fairy who likes games, animation and food. I hope to share happy moments with you through live broadcast.', + fansCount: 400, + worksCount: 10, + viewCount: 5, + city: 'Dream City', + tags: ['game', 'anime', 'food'] + }, + { + bloggerId: 2, + bloggerName: 'Virtual anchor little wolf', + bloggerAvatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg', + introduction: + 'Hello everyone, I am the virtual anchor Little Wolf. I like music, travel and photography, and I hope to explore the beauty of the world with you through live broadcast.', + fansCount: 800, + worksCount: 20, + viewCount: 15, + city: 'City of Music', + tags: ['music', 'travel', 'photography'] + }, + { + bloggerId: 3, + bloggerName: 'Virtual anchor bunny', + bloggerAvatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg', + introduction: + 'Hello everyone, I am the virtual anchor Xiaotu. I like painting, handicrafts and beauty makeup. I hope to share creativity and fashion with you through live broadcast.', + fansCount: 600, + worksCount: 15, + viewCount: 10, + city: 'City of Art', + tags: ['painting', 'handmade', 'beauty makeup'] + }, + { + bloggerId: 4, + bloggerName: 'Virtual anchor kitten', + bloggerAvatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg', + introduction: + 'Hello everyone, I am the virtual host Kitty. I am a lazy cat who likes dancing, fitness and cooking. I hope to live a healthy and happy life with everyone through the live broadcast.', + fansCount: 1000, + worksCount: 30, + viewCount: 20, + city: 'Health City', + tags: ['dance', 'fitness', 'cooking'] + }, + { + bloggerId: 5, + bloggerName: 'Virtual anchor Bear', + bloggerAvatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg', + introduction: + 'Hello everyone, I am the virtual host Xiaoxiong. A little wise man who likes movies, reading and philosophy, I hope to explore the meaning of life with you through live broadcast.', + fansCount: 1200, + worksCount: 25, + viewCount: 18, + city: 'City of Wisdom', + tags: ['Movie', 'Literature'] + }, + { + bloggerId: 6, + bloggerName: 'Virtual anchor bird', + bloggerAvatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bird.jpeg', + introduction: + 'Hello everyone, I am the virtual anchor Xiaoniao. I like singing, acting and variety shows. I hope to be happy with everyone through the live broadcast.', + fansCount: 900, + worksCount: 12, + viewCount: 8, + city: 'Happy City', + tags: ['music', 'performance', 'variety'] + } + ]; + + const columns = []; + const handleChange = () => { + setCol([ + { title: 'ID', field: 'bloggerId' }, + { title: 'Name', field: 'bloggerName' } + ]); + }; + return ( +
+ + { + // eslint-disable-next-line no-undef + (window as any).tableInstance = table; + }} + // ReactDOM={ReactDom} + > + {col.map((item, i) => { + return ( + + + + ); + })} + +
+ ); +} + +export default App; diff --git a/packages/react-vtable/src/table-components/custom/reconciler.ts b/packages/react-vtable/src/table-components/custom/reconciler.ts index fe669f89b4..6ab82d2f28 100644 --- a/packages/react-vtable/src/table-components/custom/reconciler.ts +++ b/packages/react-vtable/src/table-components/custom/reconciler.ts @@ -1,6 +1,6 @@ import { application, REACT_TO_CANOPUS_EVENTS, Tag } from '@visactor/vtable/es/vrender'; -import type { Graphic, IGraphic, IGraphicCreator } from '@visactor/vtable/es/vrender'; -import { isFunction, merge } from '@visactor/vutils'; +import type { FlexLayoutPlugin, Graphic, IGraphic, IGraphicCreator } from '@visactor/vtable/es/vrender'; +import { isFunction, isNumber, merge } from '@visactor/vutils'; import React from 'react'; import ReactReconciler from 'react-reconciler'; import { DefaultEventPriority } from 'react-reconciler/constants.js'; @@ -175,10 +175,24 @@ function updateGraphicProps(graphic: IGraphic, newProps: any, oldProps: any) { ); } } - // update all attribute const attribute = newProps.attribute ?? merge({}, newProps); + + // update all attribute graphic.initAttributes(attribute); if (graphic.type === 'image') { graphic.loadImage(attribute.image); } + + if ( + attribute.display === 'flex' && + attribute.width === undefined && + attribute.height === undefined && + isNumber(oldProps.attribute.width) && + isNumber(oldProps.attribute.height) + ) { + const plugin = graphic.stage.pluginService.findPluginsByName('FlexLayoutPlugin')[0] as FlexLayoutPlugin; + if (plugin) { + plugin.tryLayoutChildren(graphic); + } + } } diff --git a/packages/vtable-gantt/src/Gantt.ts b/packages/vtable-gantt/src/Gantt.ts index 4abe2c78f2..955141ec80 100644 --- a/packages/vtable-gantt/src/Gantt.ts +++ b/packages/vtable-gantt/src/Gantt.ts @@ -929,7 +929,10 @@ export class Gantt extends EventTarget { this.data.adjustOrder(source_index, source_sub_task_index, target_index, target_sub_task_index); } // 定义多个函数签名 - /** 更新数据信息 */ + /** 更新数据信息 + * 如果TasksShowModes是 tasks_separate 模式 则需要传入task_index即可 + * 如果TasksShowModes是 sub_tasks_*** 模式 则需要传入task_index和sub_task_index + */ updateTaskRecord(record: any, task_index: number | number[]): void; updateTaskRecord(record: any, task_index: number, sub_task_index: number): void; updateTaskRecord(record: any, task_index: number | number[], sub_task_index?: number) { @@ -947,17 +950,16 @@ export class Gantt extends EventTarget { this._refreshTaskBar(index, sub_index); return; } - const index = task_index as number; - - // if (this.taskListTableInstance.rowHierarchyType === 'tree' && typeof index === 'number') { - // //如果是树形结构 需要获取数据源对应的索引 - // index = this.taskListTableInstance.getRecordIndexByCell( - // 0, - // index + this.taskListTableInstance.columnHeaderLevelCount - // ); - // } - this._updateRecordToListTable(record, index); - this._refreshTaskBar(index, undefined); + let recordIndexs: number | number[] = task_index; + if (this.taskListTableInstance.rowHierarchyType === 'tree') { + //如果是树形结构 需要获取数据源对应的索引 + recordIndexs = this.taskListTableInstance.getRecordIndexByCell( + 0, + task_index + this.taskListTableInstance.columnHeaderLevelCount + ); + } + this._updateRecordToListTable(record, recordIndexs); + this._refreshTaskBar(task_index, undefined); } /** diff --git a/packages/vtable-gantt/src/scenegraph/task-bar.ts b/packages/vtable-gantt/src/scenegraph/task-bar.ts index 6630ba8f3a..ac7107a831 100644 --- a/packages/vtable-gantt/src/scenegraph/task-bar.ts +++ b/packages/vtable-gantt/src/scenegraph/task-bar.ts @@ -161,7 +161,9 @@ export class TaskBar { // clip: true }); barGroupBox.name = 'task-bar'; + //如果TaskShowMode是tasks_separate模式 这里的task_index其实是table中的bodyIndex;如果TaskShowMode是sub_tasks_***模式 task_index也是对应父节点任务条在table中的bodyIndex(但不会渲染父节点,只是渲染子节点) barGroupBox.task_index = index; + //如果TaskShowMode是tasks_separate模式,不会赋值sub_task_index;如果TaskShowMode是sub_tasks_***模式 这里的sub_task_index是父节点下子元素的index barGroupBox.sub_task_index = childIndex; barGroupBox.record = taskRecord; diff --git a/packages/vtable/examples/menu.ts b/packages/vtable/examples/menu.ts index c91c6946fa..547ff6ede7 100644 --- a/packages/vtable/examples/menu.ts +++ b/packages/vtable/examples/menu.ts @@ -217,6 +217,10 @@ export const menus = [ path: 'pivot', name: 'pivot-grid-tree-totals' }, + { + path: 'pivot', + name: 'pivot-grid-tree-select-cornerHeaderSelectMode' + }, { path: 'pivot', name: 'pivot-tree-lazy-load' diff --git a/packages/vtable/examples/pivot/pivot-grid-tree-select-cornerHeaderSelectMode.ts b/packages/vtable/examples/pivot/pivot-grid-tree-select-cornerHeaderSelectMode.ts new file mode 100644 index 0000000000..e7521b6681 --- /dev/null +++ b/packages/vtable/examples/pivot/pivot-grid-tree-select-cornerHeaderSelectMode.ts @@ -0,0 +1,383 @@ +import * as VTable from '../../src'; +import { bindDebugTool } from '../../src/scenegraph/debug-tool'; +const PivotTable = VTable.PivotTable; +const CONTAINER_ID = 'vTable'; + +export function createTable() { + fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/test-demo-data/supermarket-flat.json') + .then(res => res.json()) + .then(data => { + const option: VTable.PivotTableConstructorOptions = { + container: document.getElementById(CONTAINER_ID), + records: data, + menu: { + contextMenuItems: ['复制单元格内容', '查询详情'] + }, + // columnTree: [ + // { + // dimensionKey: '220524114340021', + // value: '办公用品', + // // hierarchyState: 'collapse', + // children: [ + // { + // dimensionKey: '220524114340022', + // value: '公司', + // // hierarchyState: 'expand',//设置默认展开 + // children: [ + // { + // dimensionKey: '220524114340023', + // value: '一级', + // children: [ + // { + // dimensionKey: '2205241143400232', + // value: '一级' + // }, + // { + // dimensionKey: '2205241143400232', + // value: '二级' + // }, + // { + // dimensionKey: '2205241143400232', + // value: '三级' + // } + // ] + // }, + // { + // dimensionKey: '220524114340023', + // value: '二级', + // children: [ + // { + // dimensionKey: '2205241143400232', + // value: '一级' + // }, + // { + // dimensionKey: '2205241143400232', + // value: '二级' + // }, + // { + // dimensionKey: '2205241143400232', + // value: '三级' + // } + // ] + // }, + // { + // dimensionKey: '220524114340023', + // value: '三级' + // } + // ] + // }, + // { + // dimensionKey: '220524114340022', + // value: '消费者', + // children: [ + // { + // dimensionKey: '220524114340023', + // value: '一级1' + // // hierarchyState: 'expand', + // }, + // { + // dimensionKey: '220524114340023', + // value: '二级1' + // }, + // { + // dimensionKey: '220524114340023', + // value: '三级1' + // } + // ] + // }, + // { + // dimensionKey: '220524114340022', + // value: '小型企业' + // } + // ] + // }, + // { + // dimensionKey: '220524114340021', + // //title: '220524114340021', + // value: '家具', + // children: [ + // { + // dimensionKey: '220524114340022', + // value: '公司1' + // // hierarchyState: 'expand', + // }, + // { + // dimensionKey: '220524114340022', + // value: '消费者1' + // }, + // { + // dimensionKey: '220524114340022', + // value: '小型企业1' + // } + // ] + // }, + // { + // dimensionKey: '220524114340021', + // //title: '220524114340021', + // value: '餐饮', + // children: [ + // { + // dimensionKey: '220524114340022', + // value: '公司2' + // // hierarchyState: 'expand', + // }, + // { + // dimensionKey: '220524114340022', + // value: '消费者2' + // }, + // { + // dimensionKey: '220524114340022', + // value: '小型企业2' + // } + // ] + // }, + // { + // dimensionKey: '220524114340021', + // //title: '220524114340021', + // value: '技术', + // children: [ + // { + // dimensionKey: '220524114340022', + // value: '公司3' + // // hierarchyState: 'expand', + // }, + // { + // dimensionKey: '220524114340022', + // value: '消费者3' + // }, + // { + // dimensionKey: '220524114340022', + // value: '小型企业3' + // } + // ] + // } + // ], + // rowTree: [ + // { + // dimensionKey: '220524114340020', + // value: '东北', + // children: [ + // { + // dimensionKey: '220524114340031', + // value: '黑龙江', + // children: [ + // { + // indicatorKey: '220524114340013', + // value: '销售额' + // }, + // { + // indicatorKey: '220524114340014', + // value: '利润' + // } + // ] + // }, + // { + // dimensionKey: '220524114340031', + // value: '吉林', + // children: [ + // { + // indicatorKey: '220524114340013', + // value: '销售额' + // }, + // { + // indicatorKey: '220524114340014', + // value: '利润' + // } + // ] + // }, + // { + // dimensionKey: '220524114340031', + // value: '辽宁', + // children: [ + // { + // indicatorKey: '220524114340013', + // value: '销售额' + // }, + // { + // indicatorKey: '220524114340014', + // value: '利润' + // } + // ] + // } + // ] + // }, + // { + // dimensionKey: '220524114340020', + // value: '华北' + // }, + // { + // dimensionKey: '220524114340020', + // value: '中南', + // children: [ + // { + // dimensionKey: '220524114340031', + // value: '广东', + // children: [ + // { + // indicatorKey: '220524114340013', + // value: '销售额' + // }, + // { + // indicatorKey: '220524114340014', + // value: '利润' + // } + // ] + // }, + // { + // dimensionKey: '220524114340031', + // value: '广西', + // children: [ + // { + // indicatorKey: '220524114340013', + // value: '销售额' + // }, + // { + // indicatorKey: '220524114340014', + // value: '利润' + // } + // ] + // }, + // { + // dimensionKey: '220524114340031', + // value: '湖南', + // children: [ + // { + // indicatorKey: '220524114340013', + // value: '销售额' + // }, + // { + // indicatorKey: '220524114340014', + // value: '利润' + // } + // ] + // } + // ] + // } + // ], + columns: [ + { + dimensionKey: '220524114340021', + title: '类别', + headerFormat(value) { + return `${value}`; + } + // width: 200 + } + // { + // dimensionKey: '220524114340022', + // title: '子类别' + + // // headerType: 'MULTILINETEXT', + // }, + // { + // dimensionKey: '220524114340023', + // title: '邮寄方式' + // } + ], + rows: [ + { + dimensionKey: '220524114340020', + title: '地区', + headerFormat(value) { + return `${value}地区`; + }, + headerStyle: { + textBaseline: 'top' + }, + // 指标菜单 + dropDownMenu: ['升序排序I', '降序排序I', '冻结列I'], + // corner菜单 + cornerDropDownMenu: ['升序排序C', '降序排序C', '冻结列C'], + drillDown: true + }, + { + dimensionKey: '220524114340031', + title: '省份' + } + ], + indicators: [ + { + indicatorKey: '220524114340013', + title: '销售额', + width: 'auto', + format(value, col, row, table) { + // if (rec.rowDimensions[0].value === '东北') return `${rec.dataValue}%`; + if (!value) { + return '--'; + } + return Math.floor(parseFloat(value)); + }, + headerStyle: { + color: 'red' + } + // headerType: 'MULTILINETEXT', + }, + { + indicatorKey: '220524114340014', + title: '利润', + format(value) { + // if (rec.rowDimensions[0].value === '东北') return `${rec.dataValue}%`; + if (!value) { + return '--'; + } + return Math.floor(parseFloat(value)); + }, + width: 'auto', + headerStyle: {} + } + ], + indicatorTitle: '指标', + corner: { + titleOnDimension: 'column', + headerStyle: { + textAlign: 'center', + borderColor: 'red', + color: 'red', + underline: true, + fontSize: 16, + fontWeight: 'bold', + fontFamily: 'sans-serif' + } + }, + select: { + headerSelectMode: 'body', // 为 body 时,优先选中body + cornerHeaderSelectMode: 'all' // 默认选中整个图表 + }, + dataConfig: { + totals: { + row: { + showGrandTotals: true, + showSubTotals: true, + subTotalsDimensions: ['220524114340020'], + grandTotalLabel: '行总计', + subTotalLabel: '小计' + } + // column: { + // showGrandTotals: true, + // showSubTotals: true, + // subTotalsDimensions: ['220524114340022'], + // grandTotalLabel: '列总计', + // subTotalLabel: '小计' + // } + } + }, + heightMode: 'autoHeight', + defaultHeaderColWidth: 100, + autoWrapText: true, + widthMode: 'autoWidth', + rowHierarchyType: 'grid-tree', + rowExpandLevel: 1, + rowHierarchyIndent: 20, + theme: VTable.themes.ARCO, + indicatorsAsCol: false, + // hideIndicatorName:true, + dragHeaderMode: 'all', + bottomFrozenRowCount: 2 + }; + const tableInstance = new PivotTable(option); + // 只为了方便控制太调试用,不要拷贝 + window.tableInstance = tableInstance; + }) + // eslint-disable-next-line no-console + .catch(e => console.log(e)); +} diff --git a/packages/vtable/src/ListTable.ts b/packages/vtable/src/ListTable.ts index fc45ffa453..67bc135294 100644 --- a/packages/vtable/src/ListTable.ts +++ b/packages/vtable/src/ListTable.ts @@ -36,7 +36,7 @@ import { computeColWidth } from './scenegraph/layout/compute-col-width'; import { computeRowHeight } from './scenegraph/layout/compute-row-height'; import { defaultOrderFn } from './tools/util'; import type { IEditor } from '@visactor/vtable-editors'; -import type { ColumnData, ColumnDefine } from './ts-types/list-table/layout-map/api'; +import type { ColumnData, ColumnDefine, HeaderData } from './ts-types/list-table/layout-map/api'; import { getCellRadioState, setCellRadioState } from './state/radio/radio'; import { cloneDeepSpec } from '@visactor/vutils-extension'; import { getGroupCheckboxState, setCellCheckboxState } from './state/checkbox/checkbox'; @@ -839,6 +839,9 @@ export class ListTable extends BaseTable implements ListTableAPI { * @returns */ getHierarchyState(col: number, row: number) { + if (this.isHeader(col, row)) { + return (this._getHeaderLayoutMap(col, row) as HeaderData)?.hierarchyState; + } if (!this.options.groupBy || (isArray(this.options.groupBy) && this.options.groupBy.length === 0)) { const define = this.getBodyColumnDefine(col, row) as ColumnDefine; if (!define.tree) { @@ -857,6 +860,34 @@ export class ListTable extends BaseTable implements ListTableAPI { toggleHierarchyState(col: number, row: number, recalculateColWidths: boolean = true) { this.stateManager.updateHoverIcon(col, row, undefined, undefined); const hierarchyState = this.getHierarchyState(col, row); + if (this.isHeader(col, row)) { + // 表头的展开和收起 + const headerTreeNode = this.internalProps.layoutMap.getHeader(col, row) as any; + const { hierarchyState: rawHierarchyState, define: columnDefine } = headerTreeNode; + if (![HierarchyState.collapse, HierarchyState.expand].includes(rawHierarchyState) || !columnDefine) { + return; + } + const children = columnDefine.columns; + // 有子节点才需要自动展开和折叠 + if (!!Array.isArray(children) && children.length > 0) { + const hierarchyState = + rawHierarchyState === HierarchyState.expand ? HierarchyState.collapse : HierarchyState.expand; + headerTreeNode.hierarchyState = hierarchyState; + headerTreeNode.define.hierarchyState = hierarchyState; + // 全量更新 + this.updateColumns(this.internalProps.columns); + } + + this.fireListeners(TABLE_EVENT_TYPE.TREE_HIERARCHY_STATE_CHANGE, { + col, + row, + hierarchyState, + originData: headerTreeNode, + cellLocation: this.getCellLocation(col, row) + }); + return; + } + if (hierarchyState === HierarchyState.expand) { this._refreshHierarchyState(col, row, recalculateColWidths); this.fireListeners(TABLE_EVENT_TYPE.TREE_HIERARCHY_STATE_CHANGE, { @@ -1326,9 +1357,16 @@ export class ListTable extends BaseTable implements ListTableAPI { * @param row * @param value 更改后的值 * @param workOnEditableCell 限制只能更改配置了编辑器的单元格值。快捷键paste这里配置的true,限制只能修改可编辑单元格值 + * @param triggerEvent 是否在值发生改变的时候触发change_cell_value事件 */ - changeCellValue(col: number, row: number, value: string | number | null, workOnEditableCell = false) { - return listTableChangeCellValue(col, row, value, workOnEditableCell, this); + changeCellValue( + col: number, + row: number, + value: string | number | null, + workOnEditableCell = false, + triggerEvent = true + ) { + return listTableChangeCellValue(col, row, value, workOnEditableCell, triggerEvent, this); } /** * 批量更新多个单元格的数据 @@ -1336,9 +1374,16 @@ export class ListTable extends BaseTable implements ListTableAPI { * @param row 粘贴数据的起始行号 * @param values 多个单元格的数据数组 * @param workOnEditableCell 是否仅更改可编辑单元格 + * @param triggerEvent 是否在值发生改变的时候触发change_cell_value事件 */ - changeCellValues(startCol: number, startRow: number, values: (string | number)[][], workOnEditableCell = false) { - return listTableChangeCellValues(startCol, startRow, values, workOnEditableCell, this); + changeCellValues( + startCol: number, + startRow: number, + values: (string | number)[][], + workOnEditableCell = false, + triggerEvent = true + ) { + return listTableChangeCellValues(startCol, startRow, values, workOnEditableCell, triggerEvent, this); } /** * 添加数据 单条数据 diff --git a/packages/vtable/src/core/record-helper.ts b/packages/vtable/src/core/record-helper.ts index eed1083f77..062e2cac93 100644 --- a/packages/vtable/src/core/record-helper.ts +++ b/packages/vtable/src/core/record-helper.ts @@ -14,12 +14,14 @@ import { TABLE_EVENT_TYPE } from './TABLE_EVENT_TYPE'; * @param row * @param value 更改后的值 * @param workOnEditableCell 限制只能更改配置了编辑器的单元格值。快捷键paste这里配置的true,限制只能修改可编辑单元格值 + * @param triggerEvent 是否在值发生改变的时候触发change_cell_value事件 */ export function listTableChangeCellValue( col: number, row: number, value: string | number | null, workOnEditableCell: boolean, + triggerEvent: boolean, table: ListTable ) { if ((workOnEditableCell && table.isHasEditorDefine(col, row)) || workOnEditableCell === false) { @@ -91,7 +93,7 @@ export function listTableChangeCellValue( table.scenegraph.updateRowHeight(row, newHeight - oldHeight); } const changedValue = table.getCellOriginValue(col, row); - if (oldValue !== changedValue) { + if (oldValue !== changedValue && triggerEvent) { table.fireListeners(TABLE_EVENT_TYPE.CHANGE_CELL_VALUE, { col, row, @@ -109,12 +111,14 @@ export function listTableChangeCellValue( * @param row 粘贴数据的起始行号 * @param values 多个单元格的数据数组 * @param workOnEditableCell 是否仅更改可编辑单元格 + * @param triggerEvent 是否在值发生改变的时候触发change_cell_value事件 */ export function listTableChangeCellValues( startCol: number, startRow: number, values: (string | number)[][], workOnEditableCell: boolean, + triggerEvent: boolean, table: ListTable ) { let pasteColEnd = startCol; @@ -193,7 +197,7 @@ export function listTableChangeCellValues( table.dataSource.changeFieldValue(value, recordIndex, field, startCol + j, startRow + i, table); } const changedValue = table.getCellOriginValue(startCol + j, startRow + i); - if (oldValue !== changedValue) { + if (oldValue !== changedValue && triggerEvent) { table.fireListeners(TABLE_EVENT_TYPE.CHANGE_CELL_VALUE, { col: startCol + j, row: startRow + i, diff --git a/packages/vtable/src/event/listener/container-dom.ts b/packages/vtable/src/event/listener/container-dom.ts index e027054367..a026bed238 100644 --- a/packages/vtable/src/event/listener/container-dom.ts +++ b/packages/vtable/src/event/listener/container-dom.ts @@ -785,7 +785,7 @@ export function bindContainerDomListener(eventManager: EventManager) { table.stateManager.updateInteractionState(InteractionState.grabing); const targetCol = table.getTargetColAtConsiderRightFrozen(selectX, considerFrozenX); const targetRow = table.getTargetRowAtConsiderBottomFrozen(selectY, considerFrozenY); - if (isValid(targetCol) && isValid(targetRow)) { + if (!table.options.select?.disableDragSelect && isValid(targetCol) && isValid(targetRow)) { table.stateManager.updateSelectPos(targetCol.col, targetRow.row, false, false, false, false); } }); diff --git a/packages/vtable/src/event/listener/table-group.ts b/packages/vtable/src/event/listener/table-group.ts index 779e2e7170..98bb271688 100644 --- a/packages/vtable/src/event/listener/table-group.ts +++ b/packages/vtable/src/event/listener/table-group.ts @@ -823,6 +823,7 @@ export function bindTableGroupListener(eventManager: EventManager) { eventManager.downIcon = hitIcon; // 处理列宽调整 这里和tableGroup.addEventListener('pointerdown' 逻辑一样 if ( + e.pointerType !== 'touch' && // 移动端不应该在这里处理列宽调整 下面有eventManager.touchMove的逻辑 !hitIcon && !eventManager.checkCellFillhandle(eventArgsSet) && !stateManager.columnResize.resizing && diff --git a/packages/vtable/src/layout/simple-header-layout.ts b/packages/vtable/src/layout/simple-header-layout.ts index 99c8f05217..40c529e298 100644 --- a/packages/vtable/src/layout/simple-header-layout.ts +++ b/packages/vtable/src/layout/simple-header-layout.ts @@ -2,15 +2,15 @@ import { isValid, merge } from '@visactor/vutils'; import type { ListTable } from '../ListTable'; import { DefaultSparklineSpec } from '../tools/global'; -import type { - CellAddress, - CellRange, - CellLocation, - IListTableCellHeaderPaths, - LayoutObjectId, - AggregationType, - Aggregation, - IRowSeriesNumber +import { + type CellAddress, + type CellRange, + type CellLocation, + type IListTableCellHeaderPaths, + type LayoutObjectId, + type Aggregation, + type IRowSeriesNumber, + HierarchyState } from '../ts-types'; import type { ChartColumnDefine, ColumnsDefine } from '../ts-types/list-table/define'; import type { @@ -66,6 +66,10 @@ export class SimpleHeaderLayoutMap implements LayoutMapAPI { _hasAggregationOnBottomCount: number = 0; /**层级维度结构显示形式 */ rowHierarchyType?: 'grid' | 'tree'; + /** 列表头树形展示模式 */ + columnHierarchyType?: 'grid-tree'; + /** 列表头默认展开层级 */ + columnExpandLevel?: number; // 缓存行号列号对应的cellRange 需要注意当表头位置拖拽后 这个缓存的行列号已不准确 进行重置 _cellRangeMap: Map; //存储单元格的行列号范围 针对解决是否为合并单元格情况 constructor(table: ListTable, columns: ColumnsDefine, showHeader: boolean, hierarchyIndent: number) { @@ -77,7 +81,14 @@ export class SimpleHeaderLayoutMap implements LayoutMapAPI { this._headerCellIds = []; this.hierarchyIndent = hierarchyIndent ?? 20; this.hierarchyTextStartAlignment = table.options.hierarchyTextStartAlignment; - this.columnTree = new DimensionTree(columns as any, { seqId: 0 }, null); //seqId这里没有利用上 所有顺便传了0 + this.columnHierarchyType = table.options.headerHierarchyType; + this.columnExpandLevel = table.options.headerExpandLevel ?? 1; + this.columnTree = new DimensionTree( + columns as any, + { seqId: 0 }, + this.columnHierarchyType ?? null, + this.columnHierarchyType === 'grid-tree' ? this.columnExpandLevel : undefined + ); //seqId这里没有利用上 所有顺便传了0 this._headerObjectsIncludeHided = this._addHeaders(0, columns, []); // this._headerObjectMapIncludeHided = this._headerObjectsIncludeHided.reduce((o, e) => { // o[e.id as number] = e; @@ -989,6 +1000,8 @@ export class SimpleHeaderLayoutMap implements LayoutMapAPI { headerType: hd.headerType ?? 'text', dropDownMenu: hd.dropDownMenu, define: hd, + // 展开/折叠状态 + hierarchyState: (hd as HeaderData).hierarchyState, columnWidthComputeMode: hd.columnWidthComputeMode // iconPositionList:[] }; @@ -1002,7 +1015,9 @@ export class SimpleHeaderLayoutMap implements LayoutMapAPI { } else if (this._headerCellIds[row - 1]) { rowCells[col] = this._headerCellIds[row - 1][col]; } - if (hd.columns) { + // 当前节点是展开状态才需要添加子节点 + const expand = !(hd as HeaderData).hierarchyState || (hd as HeaderData).hierarchyState === HierarchyState.expand; + if (!!hd.columns && !!expand) { const isAllHided = hd.columns.every((c: any) => c.hide); !isAllHided && this._addHeaders( diff --git a/packages/vtable/src/scenegraph/component/menu.ts b/packages/vtable/src/scenegraph/component/menu.ts index 1a5b324095..3381d0c917 100644 --- a/packages/vtable/src/scenegraph/component/menu.ts +++ b/packages/vtable/src/scenegraph/component/menu.ts @@ -326,7 +326,7 @@ export class MenuHandler { result.event = e.nativeEvent; this._table.fireListeners(TABLE_EVENT_TYPE.DROPDOWN_MENU_CLICK, result); - // 由DROPDOWNMENU_CLICK事件清空菜单 + // 由DROPDOWN_MENU_CLICK事件清空菜单 // this.detach(); } }); diff --git a/packages/vtable/src/scenegraph/graphic/contributions/rect-contribution-render.ts b/packages/vtable/src/scenegraph/graphic/contributions/rect-contribution-render.ts index adbff93c3d..581a2ff542 100644 --- a/packages/vtable/src/scenegraph/graphic/contributions/rect-contribution-render.ts +++ b/packages/vtable/src/scenegraph/graphic/contributions/rect-contribution-render.ts @@ -226,8 +226,8 @@ export class SplitRectAfterRenderContribution implements IRectRenderContribution stroke, strokeArrayWidth || lineWidth, strokeArrayColor || strokeColor, - Math.ceil(width + deltaWidth), - Math.ceil(height + deltaHeight) + rect.name !== 'table-border-rect' ? Math.ceil(width + deltaWidth) : width + deltaWidth, + rect.name !== 'table-border-rect' ? Math.ceil(height + deltaHeight) : height + deltaHeight ); } } diff --git a/packages/vtable/src/scenegraph/group-creater/progress/create-group-for-first-screen.ts b/packages/vtable/src/scenegraph/group-creater/progress/create-group-for-first-screen.ts index b305114724..a601651e39 100644 --- a/packages/vtable/src/scenegraph/group-creater/progress/create-group-for-first-screen.ts +++ b/packages/vtable/src/scenegraph/group-creater/progress/create-group-for-first-screen.ts @@ -66,11 +66,15 @@ export function createGroupForFirstScreen( if (distCol < table.colCount - table.rightFrozenColCount) { // compute right frozen row height - computeColsWidth(table, table.colCount - table.rightFrozenColCount, table.colCount - 1); + if (table.colCount - table.rightFrozenColCount <= table.colCount - 1) { + computeColsWidth(table, table.colCount - table.rightFrozenColCount, table.colCount - 1); + } } if (distRow < table.rowCount - table.bottomFrozenRowCount) { // compute bottom frozen row height - computeRowsHeight(table, table.rowCount - table.bottomFrozenRowCount, table.rowCount - 1); + if (table.rowCount - table.bottomFrozenRowCount <= table.rowCount - 1) { + computeRowsHeight(table, table.rowCount - table.bottomFrozenRowCount, table.rowCount - 1); + } } // update colHeaderGroup rowHeaderGroup bodyGroup position diff --git a/packages/vtable/src/scenegraph/group-creater/progress/proxy.ts b/packages/vtable/src/scenegraph/group-creater/progress/proxy.ts index c39dc285e7..e6949a3a17 100644 --- a/packages/vtable/src/scenegraph/group-creater/progress/proxy.ts +++ b/packages/vtable/src/scenegraph/group-creater/progress/proxy.ts @@ -392,7 +392,9 @@ export class SceneProxy { createColGroup(onceCount: number) { // compute rows height const endCol = Math.min(this.totalCol, this.currentCol + onceCount); - computeColsWidth(this.table, this.currentCol + 1, endCol); + if (this.table.widthMode !== 'adaptive') { + computeColsWidth(this.table, this.currentCol + 1, endCol); + } this.colEnd = endCol; @@ -693,7 +695,9 @@ export class SceneProxy { // colGroup.needUpdate = false; // } // } - computeColsWidth(this.table, this.colUpdatePos, distCol); + if (this.table.widthMode === 'autoWidth') { + computeColsWidth(this.table, this.colUpdatePos, distCol); + } updateColContent(this.colUpdatePos, distCol, this); this.colUpdatePos = distCol + 1; } diff --git a/packages/vtable/src/state/checkbox/checkbox.ts b/packages/vtable/src/state/checkbox/checkbox.ts index 5e5878da2a..2b70d5a694 100644 --- a/packages/vtable/src/state/checkbox/checkbox.ts +++ b/packages/vtable/src/state/checkbox/checkbox.ts @@ -36,7 +36,7 @@ function setSingleCheckedState( const recordIndex = state.table.getRecordShowIndexByCell(col, row); if (recordIndex >= 0) { const dataIndex = state.table.dataSource.getIndexKey(recordIndex).toString(); - if (state.checkedState.has(dataIndex)) { + if (state.checkedState.get(dataIndex)) { state.checkedState.get(dataIndex)[field] = checked; } else { state.checkedState.set(dataIndex, { @@ -89,7 +89,7 @@ export function syncCheckedState( if (isValid(state.checkedState.get(dataIndex)?.[field])) { return state.checkedState.get(dataIndex)[field]; } - if (state.checkedState.has(dataIndex)) { + if (state.checkedState.get(dataIndex)) { state.checkedState.get(dataIndex)[field] = checked; } else if (dataIndex.includes(',')) { // child record, sync parent record state diff --git a/packages/vtable/src/state/select/update-position.ts b/packages/vtable/src/state/select/update-position.ts index 27fcf04656..03429842ae 100644 --- a/packages/vtable/src/state/select/update-position.ts +++ b/packages/vtable/src/state/select/update-position.ts @@ -248,8 +248,8 @@ export function updateSelectPosition( } else if ((table.internalProps.layoutMap as SimpleHeaderLayoutMap).isCornerHeader(col, row)) { // 选中表头行号单元格 extendSelectRange = false; - - if (state.select.headerSelectMode === 'body') { + const { cornerHeaderSelectMode } = state.select; + if (state.select.headerSelectMode === 'body' || cornerHeaderSelectMode === 'body') { state.select.ranges.push({ start: { col: table.rowHeaderLevelCount + table.leftRowSeriesNumberCount, @@ -259,11 +259,39 @@ export function updateSelectPosition( skipBodyMerge: true }); } else { - state.select.ranges.push({ - start: { col: table.leftRowSeriesNumberCount, row: 0 }, - end: { col: table.colCount - 1, row: table.rowCount - 1 }, - skipBodyMerge: true - }); + if (cornerHeaderSelectMode === 'cell') { + // 选中普通单元格 + const cellRange = skipBodyMerge ? { start: { col, row }, end: { col, row } } : table.getCellRange(col, row); + state.select.ranges.push({ + start: { col: cellRange.start.col, row: cellRange.start.row }, + end: { col: cellRange.end.col, row: cellRange.end.row }, + skipBodyMerge: skipBodyMerge || undefined + }); + } else if (cornerHeaderSelectMode === 'inline') { + // inline + const cellRange = skipBodyMerge ? { start: { col, row }, end: { col, row } } : table.getCellRange(col, row); + state.select.ranges.push({ + start: { col: cellRange.start.col, row: cellRange.start.row }, + end: { col: cellRange.end.col, row: table.rowCount - 1 }, + skipBodyMerge: true + }); + } else { + // all 或者用户传的其他的什么值 :'' | 'test',虽然类型会提示用户不能为其他的值, + state.select.ranges.push({ + start: { + col: table.leftRowSeriesNumberCount, + row: 0 + }, + end: { col: table.colCount - 1, row: table.rowCount - 1 }, + skipBodyMerge: true + }); + } + // 选中全部 + // state.select.ranges.push({ + // start: { col: table.leftRowSeriesNumberCount, row: 0 }, + // end: { col: table.colCount - 1, row: table.rowCount - 1 }, + // skipBodyMerge: true + // }); } } else if (col >= 0 && row >= 0) { // 选中普通单元格 diff --git a/packages/vtable/src/state/state.ts b/packages/vtable/src/state/state.ts index a52d5c754c..c71c307610 100644 --- a/packages/vtable/src/state/state.ts +++ b/packages/vtable/src/state/state.ts @@ -89,6 +89,13 @@ export class StateManager { * 'body': 不选择表头,点击行表头则选择该行所有 body 单元格,点击列表头则选择该列所有 body 单元格。 */ headerSelectMode?: 'inline' | 'cell' | 'body'; + /** 点击表头corner单元格效果 + * 'inline': 点击corner选择列表头则整列选中; + * 'cell': 仅仅选择当前点击的corner表头单元格; + * 'body': 点击corner列表头则选择该列所有 body 单元格。 + * 'all': 点击corner选择整个图表 + */ + cornerHeaderSelectMode?: 'inline' | 'cell' | 'body' | 'all'; highlightInRange?: boolean; selecting: boolean; customSelectRanges?: { @@ -434,6 +441,7 @@ export class StateManager { // enableColumnHighlight, /** 点击表头单元格时连带body整行或整列选中 或仅选中当前单元格,默认或整行或整列选中*/ headerSelectMode, + cornerHeaderSelectMode, disableSelect, disableHeaderSelect, highlightMode, @@ -443,6 +451,7 @@ export class StateManager { { /** 点击表头单元格时连带body整行或整列选中 或仅选中当前单元格,默认或整行或整列选中*/ headerSelectMode: 'inline', + cornerHeaderSelectMode: 'all', // 点击 corner 默认选中整个图表 disableSelect: false, disableHeaderSelect: false, highlightMode: 'cell', @@ -475,6 +484,8 @@ export class StateManager { this.select.singleStyle = !disableSelect; this.select.disableHeader = disableHeaderSelect; this.select.headerSelectMode = headerSelectMode; + + this.select.cornerHeaderSelectMode = cornerHeaderSelectMode; this.select.highlightInRange = highlightInRange; this.select.disableCtrlMultiSelect = this.table.options.keyboardOptions?.ctrlMultiSelect === false; } diff --git a/packages/vtable/src/ts-types/base-table.ts b/packages/vtable/src/ts-types/base-table.ts index 129ba10239..99349eae5b 100644 --- a/packages/vtable/src/ts-types/base-table.ts +++ b/packages/vtable/src/ts-types/base-table.ts @@ -389,6 +389,13 @@ export interface BaseTableConstructorOptions { * 'body': 不选择表头,点击行表头则选择该行所有 body 单元格,点击列表头则选择该列所有 body 单元格。 */ headerSelectMode?: 'inline' | 'cell' | 'body'; + /** 点击表头corner单元格效果 + * 'inline': 点击corner选择列表头则整列选中; + * 'cell': 仅仅选择当前点击的corner表头单元格; + * 'body': 点击corner列表头则选择该列所有 body 单元格; + * 'all': 点击corner选择整个图表。 + */ + cornerHeaderSelectMode?: 'inline' | 'cell' | 'body' | 'all'; /** 不响应鼠标select交互 */ disableSelect?: boolean | ((col: number, row: number, table: BaseTableAPI) => boolean); /** 单独设置表头不响应鼠标select交互 */ diff --git a/packages/vtable/src/ts-types/events.ts b/packages/vtable/src/ts-types/events.ts index 49509d8caf..acab71eb7f 100644 --- a/packages/vtable/src/ts-types/events.ts +++ b/packages/vtable/src/ts-types/events.ts @@ -203,6 +203,7 @@ export interface TableEventHandlersEventArgumentMap { dimensionInfo?: IDimensionInfo[]; /**整条数据-原始数据 */ originData?: any; + cellLocation?: CellLocation; }; vchart_event_type: { eventName: string; diff --git a/packages/vtable/src/ts-types/table-engine.ts b/packages/vtable/src/ts-types/table-engine.ts index b2da3755c0..ce2a7fdddd 100644 --- a/packages/vtable/src/ts-types/table-engine.ts +++ b/packages/vtable/src/ts-types/table-engine.ts @@ -239,6 +239,10 @@ export interface ListTableConstructorOptions extends BaseTableConstructorOptions hierarchyExpandLevel?: number; /** 同层级的结点是否按文字对齐 如没有收起展开图标的节点和有图标的节点文字对齐 默认false */ hierarchyTextStartAlignment?: boolean; + /** 表头树形展示模式(设置成 'grid-tree' 则支持展开和折叠) */ + headerHierarchyType?: 'grid-tree'; + /** 表头默认展开层级(headerHierarchyType 为 'grid-tree' 时有效) */ + headerExpandLevel?: number; /** 分页配置 */ pagination?: IPagination;