diff --git a/docs/assets/demo/en/table-type/list-table-tree-lazy-load.md b/docs/assets/demo/en/table-type/list-table-tree-lazy-load.md index 098d1cff6d..2af576e049 100644 --- a/docs/assets/demo/en/table-type/list-table-tree-lazy-load.md +++ b/docs/assets/demo/en/table-type/list-table-tree-lazy-load.md @@ -17,6 +17,8 @@ Basic table tree display, turn on the tree mode of a certain column, if the chil - children: true Set to enable tree display on a certain row of data and lazily load child node data - setRecordChildren(children: any[], col: number, row: number) Call this method after lazily loading child node data in a row of data and return it to the table component - TREE_HIERARCHY_STATE_CHANGE tree display state change events +- cellType: 'checkbox' Turns on the checkbox. Use it with tree:true to display the checkbox in a tree format. +- enableCheckboxCascade: true It is used globally to turn on the checkbox cascade. It must be used with cellType: 'checkbox' and tree:true to synchronize the parent and child element selections. ## Code Demo @@ -184,6 +186,9 @@ const option = { columns: [ { field: 'category', + // Supports checkbox tree display. + // To synchronize parent and child element checkboxes, be sure to configure enableCheckboxCascade: true in the option global configuration. + // cellType: 'checkbox', tree: true, title: 'category', width: 'auto', @@ -203,6 +208,7 @@ const option = { sort: true } ], + // enableCheckboxCascade:true, showPin: true, //显示VTable内置冻结列图标 widthMode: 'standard', allowFrozenColCount: 2, diff --git a/docs/assets/demo/zh/table-type/list-table-tree-lazy-load.md b/docs/assets/demo/zh/table-type/list-table-tree-lazy-load.md index a51edf9ddd..6517f0fa0a 100644 --- a/docs/assets/demo/zh/table-type/list-table-tree-lazy-load.md +++ b/docs/assets/demo/zh/table-type/list-table-tree-lazy-load.md @@ -17,6 +17,8 @@ option: ListTable-columns-text#tree - children: true 在某一行数据上设置开启树形展示 并懒加载子节点数据 - setRecordChildren(children: any[], col: number, row: number) 在某一行数据懒加载子节点数据后调用此方法回传到表格组件中 - TREE_HIERARCHY_STATE_CHANGE 树形展示状态改变事件 +- cellType: 'checkbox' 开启复选框,与 tree:true 配合使用可进行复选框树形展示 +- enableCheckboxCascade: true 在全局使用,用于开启复选框级联,必须与 cellType: 'checkbox'和 tree:true 配合使用,可实现父子元素勾选同步 ## 代码演示 @@ -184,6 +186,9 @@ const option = { columns: [ { field: '类别', + // 支持checkbox复选框树形展示。 + // 欲使父子级元素勾选同步时务必在option全局配置 enableCheckboxCascade: true。 + // cellType: 'checkbox', tree: true, title: '类别', width: 'auto', @@ -203,6 +208,7 @@ const option = { sort: true } ], + // enableCheckboxCascade:true, showPin: true, //显示VTable内置冻结列图标 widthMode: 'standard', allowFrozenColCount: 2, diff --git a/packages/vtable/examples/list/list-checkbox-tree.ts b/packages/vtable/examples/list/list-checkbox-tree.ts index 1fe04b66af..255baf511e 100644 --- a/packages/vtable/examples/list/list-checkbox-tree.ts +++ b/packages/vtable/examples/list/list-checkbox-tree.ts @@ -148,7 +148,7 @@ export function createTable() { field: '类别', tree: true, cellType: 'checkbox', - enableTreeCheckbox: true, + // headerType: 'checkbox', title: '类别', width: 'auto', sort: true @@ -185,7 +185,8 @@ export function createTable() { defaultRowHeight: 32, select: { disableDragSelect: true - } + }, + enableCheckboxCascade: true }; const instance = new ListTable(option); diff --git a/packages/vtable/examples/list/list-tree-checkbox.ts b/packages/vtable/examples/list/list-tree-checkbox.ts index 67fba2e6fb..3878c4516c 100644 --- a/packages/vtable/examples/list/list-tree-checkbox.ts +++ b/packages/vtable/examples/list/list-tree-checkbox.ts @@ -122,6 +122,7 @@ export function createTable() { const data = [ { + // _vtable_rowSeries_number:{text: 'checked', checked: true, disable: true }, 类别: '办公用品', 销售额: '129.696', 数量: '2', diff --git a/packages/vtable/src/core/BaseTable.ts b/packages/vtable/src/core/BaseTable.ts index f672a7aea6..07c02225cb 100644 --- a/packages/vtable/src/core/BaseTable.ts +++ b/packages/vtable/src/core/BaseTable.ts @@ -281,6 +281,7 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { keyboardOptions, eventOptions, rowSeriesNumber, + enableCheckboxCascade, // columnSeriesNumber, // disableRowHeaderColumnResize, columnResizeMode, @@ -393,6 +394,7 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { internalProps.keyboardOptions = keyboardOptions; internalProps.eventOptions = eventOptions; internalProps.rowSeriesNumber = rowSeriesNumber; + internalProps.enableCheckboxCascade = enableCheckboxCascade; // internalProps.columnSeriesNumber = columnSeriesNumber; internalProps.columnResizeMode = resize?.columnResizeMode ?? columnResizeMode; @@ -2390,6 +2392,7 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { keyboardOptions, eventOptions, rowSeriesNumber, + enableCheckboxCascade, // columnSeriesNumber, // disableRowHeaderColumnResize, columnResizeMode, @@ -2471,6 +2474,7 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { internalProps.keyboardOptions = keyboardOptions; internalProps.eventOptions = eventOptions; internalProps.rowSeriesNumber = rowSeriesNumber; + internalProps.enableCheckboxCascade = enableCheckboxCascade; // internalProps.columnSeriesNumber = columnSeriesNumber; internalProps.columnResizeMode = resize?.columnResizeMode ?? columnResizeMode; diff --git a/packages/vtable/src/event/self-event-listener/list-table/checkbox.ts b/packages/vtable/src/event/self-event-listener/list-table/checkbox.ts index 54b26d1bad..bf2fa5a3f0 100644 --- a/packages/vtable/src/event/self-event-listener/list-table/checkbox.ts +++ b/packages/vtable/src/event/self-event-listener/list-table/checkbox.ts @@ -1,5 +1,5 @@ import { isArray, isNumber } from '@visactor/vutils'; -import type { BaseTableAPI, ListTableProtected } from '../../../ts-types/base-table'; +import type { BaseTableAPI } from '../../../ts-types/base-table'; import { setCellCheckboxStateByAttribute } from '../../../state/checkbox/checkbox'; import type { ListTableAPI } from '../../../ts-types'; import { HierarchyState } from '../../../ts-types'; @@ -9,7 +9,10 @@ export function bindGroupTitleCheckboxChange(table: BaseTableAPI) { table.on('checkbox_state_change', args => { const { col, row, checked, field } = args; - if (field !== '_vtable_rowSeries_number' || table.internalProps.rowSeriesNumber?.enableTreeCheckbox !== true) { + if ( + !table.internalProps.layoutMap.isSeriesNumber(col, row) || + table.internalProps.rowSeriesNumber?.enableTreeCheckbox !== true + ) { return; } @@ -30,7 +33,7 @@ export function bindGroupTitleCheckboxChange(table: BaseTableAPI) { // 1.1 group title check // 1.1.1 check all children if (getHierarchyState(table, col, row) === HierarchyState.collapse) { - updateChildrenCheckboxState(true, titleIndex, table, field); + updateChildrenCheckboxState(true, titleIndex, table, field as string); } else { setAllChildrenCheckboxState(true, titleShowIndex, titleIndex, indexedData, table, col); } @@ -40,7 +43,7 @@ export function bindGroupTitleCheckboxChange(table: BaseTableAPI) { // 1.2 group title uncheck // 1.2.1 uncheck all children if (getHierarchyState(table, col, row) === HierarchyState.collapse) { - updateChildrenCheckboxState(false, titleIndex, table, field); + updateChildrenCheckboxState(false, titleIndex, table, field as string); } else { setAllChildrenCheckboxState(false, titleShowIndex, titleIndex, indexedData, table, col); } @@ -66,9 +69,8 @@ export function bindGroupCheckboxTreeChange(table: ListTableAPI) { table.on('checkbox_state_change', args => { const { col, row, checked, field } = args; - const isCheckboxAndTree = - table.internalProps.columns.some(column => column.tree && (column as any).enableTreeCheckbox) && - field !== '_vtable_rowSeries_number'; + const isCheckboxAndTree = table.internalProps.columns.some(column => column.tree); + table.internalProps.enableCheckboxCascade && field !== '_vtable_rowSeries_number'; if (!isCheckboxAndTree) { return; @@ -200,12 +202,16 @@ function updateParentCheckboxState( }); const stateArr = keys.map(key => checkedState.get(key)); + // currentIndex的子元素 + const childOfCurrentIndex = (keys as string[]).filter(item => item.startsWith(key + ',') && item !== key); + stateArr.forEach((state, i) => { const index = keys[i] as string; const value = state; + const isChildOfCurrentIndex = childOfCurrentIndex.includes(index); + if (start) { - const indexData = index.split(','); - if (indexData.length === currentIndexLength) { + if (!isChildOfCurrentIndex) { start = false; } else { result.push(value[fieldName]); diff --git a/packages/vtable/src/layout/simple-header-layout.ts b/packages/vtable/src/layout/simple-header-layout.ts index 4ee01e26b6..32dd6cc282 100644 --- a/packages/vtable/src/layout/simple-header-layout.ts +++ b/packages/vtable/src/layout/simple-header-layout.ts @@ -142,7 +142,7 @@ export class SimpleHeaderLayoutMap implements LayoutMapAPI { style: rowSeriesNumber.style, width: rowSeriesNumber.width, format: rowSeriesNumber.format, - field: '_vtable_rowSeries_number', //rowSeriesNumber.field, + field: rowSeriesNumber.field, icon: rowSeriesNumber.icon, headerIcon: rowSeriesNumber.headerIcon, isChildNode: false diff --git a/packages/vtable/src/scenegraph/group-creater/cell-type/checkbox-cell.ts b/packages/vtable/src/scenegraph/group-creater/cell-type/checkbox-cell.ts index 9fd7983bd0..36b2c77b19 100644 --- a/packages/vtable/src/scenegraph/group-creater/cell-type/checkbox-cell.ts +++ b/packages/vtable/src/scenegraph/group-creater/cell-type/checkbox-cell.ts @@ -1,12 +1,13 @@ import type { IThemeSpec } from '@src/vrender'; import { Group } from '../../graphic/group'; -import type { - CellInfo, - CellRange, - CheckboxColumnDefine, - CheckboxStyleOption, - ColumnIconOption, - SparklineSpec +import { + InternalIconName, + type CellInfo, + type CellRange, + type CheckboxColumnDefine, + type CheckboxStyleOption, + type ColumnIconOption, + type SparklineSpec } from '../../../ts-types'; import type { BaseTableAPI } from '../../../ts-types/base-table'; import { isObject } from '@visactor/vutils'; @@ -20,6 +21,7 @@ import { getCellBorderStrokeWidth } from '../../utils/cell-border-stroke-width'; import { dealWithIcon, dealWithIconLayout } from '../../utils/text-icon-layout'; import { CheckboxContent } from '../../component/checkbox-content'; import { CUSTOM_CONTAINER_NAME } from '../../component/custom'; +import type { ListTable } from '../../..'; export function createCheckboxCellGroup( cellGroup: Group | null, @@ -164,7 +166,12 @@ export function createCheckboxCellGroup( isCheckboxTree ); - if (cellContentLeftIcons.length !== 0 || cellContentRightIcons.length !== 0) { + // 目前只支持展示折叠或者展开icons + if ( + cellContentLeftIcons.length === 1 && + (cellContentLeftIcons[0].name === InternalIconName.expandIconName || + cellContentLeftIcons[0].name === InternalIconName.collapseIconName) + ) { const checkContent = new CheckboxContent({ x: 0, y: 0, @@ -325,6 +332,15 @@ function createCheckbox( isChecked = value; text = ''; } + // 处理 rowSeriesNumbe 在record设置checkbox是否勾选与是否禁用的场景 + if (table.internalProps.layoutMap.isSeriesNumber(col, row)) { + const checkboxSeriesNumberStyle = (table as ListTable).getFieldData(define.field, col, row); + if (checkboxSeriesNumberStyle) { + isChecked = checkboxSeriesNumberStyle.checked; + isDisabled = checkboxSeriesNumberStyle.disable; + text = checkboxSeriesNumberStyle.text ?? ''; + } + } isChecked = table.stateManager.syncCheckedState(col, row, define.field as string | number, isChecked); const hierarchyOffset = getHierarchyOffset(col, row, table); const cellStyle = table._getCellStyle(col, row) as CheckboxStyleOption; // to be fixed diff --git a/packages/vtable/src/scenegraph/scenegraph.ts b/packages/vtable/src/scenegraph/scenegraph.ts index 79e09b91f3..c8cb96a85a 100644 --- a/packages/vtable/src/scenegraph/scenegraph.ts +++ b/packages/vtable/src/scenegraph/scenegraph.ts @@ -76,6 +76,7 @@ import { updateReactContainer } from './layout/frozen-react'; import * as registerIcons from '../icons'; import { temporarilyUpdateSelectRectStyle } from './select/update-select-style'; +import type { CheckboxContent } from './component/checkbox-content'; // import { contextModule } from './context/module'; registerForVrender(); @@ -803,6 +804,28 @@ export class Scenegraph { (node as CheckBox).setAttribute('checked', checked); } } + // 适配cellType: 'checkbox'与tree: true,并且开启enableTreeCheckbox: true的情况 + if (node.name === 'checkbox-content') { + if (checked === 'indeterminate') { + ((node as CheckboxContent)._checkboxGroup.getChildByName('checkbox') as CheckBox).setAttribute( + 'indeterminate', + true + ); + ((node as CheckboxContent)._checkboxGroup.getChildByName('checkbox') as CheckBox).setAttribute( + 'checked', + undefined + ); + } else { + ((node as CheckboxContent)._checkboxGroup.getChildByName('checkbox') as CheckBox).setAttribute( + 'indeterminate', + undefined + ); + ((node as CheckboxContent)._checkboxGroup.getChildByName('checkbox') as CheckBox).setAttribute( + 'checked', + checked + ); + } + } }); }); } else { @@ -818,6 +841,28 @@ export class Scenegraph { (node as CheckBox).setAttribute('checked', checked); } } + // 适配cellType: 'checkbox'与tree: true,并且开启enableTreeCheckbox: true的情况 + if (node.name === 'checkbox-content') { + if (checked === 'indeterminate') { + ((node as CheckboxContent)._checkboxGroup.getChildByName('checkbox') as CheckBox).setAttribute( + 'indeterminate', + true + ); + ((node as CheckboxContent)._checkboxGroup.getChildByName('checkbox') as CheckBox).setAttribute( + 'checked', + undefined + ); + } else { + ((node as CheckboxContent)._checkboxGroup.getChildByName('checkbox') as CheckBox).setAttribute( + 'indeterminate', + undefined + ); + ((node as CheckboxContent)._checkboxGroup.getChildByName('checkbox') as CheckBox).setAttribute( + 'checked', + checked + ); + } + } }); }); } @@ -838,6 +883,28 @@ export class Scenegraph { (node as CheckBox).setAttribute('checked', checked); } } + // 适配cellType: 'checkbox'与tree: true,并且开启enableTreeCheckbox: true的情况 + if (node.name === 'checkbox-content') { + if (checked === 'indeterminate') { + ((node as CheckboxContent)._checkboxGroup.getChildByName('checkbox') as CheckBox).setAttribute( + 'indeterminate', + true + ); + ((node as CheckboxContent)._checkboxGroup.getChildByName('checkbox') as CheckBox).setAttribute( + 'checked', + undefined + ); + } else { + ((node as CheckboxContent)._checkboxGroup.getChildByName('checkbox') as CheckBox).setAttribute( + 'indeterminate', + undefined + ); + ((node as CheckboxContent)._checkboxGroup.getChildByName('checkbox') as CheckBox).setAttribute( + 'checked', + checked + ); + } + } }); }); } else { @@ -853,6 +920,28 @@ export class Scenegraph { (node as CheckBox).setAttribute('checked', checked); } } + // 适配cellType: 'checkbox'与tree: true,并且开启enableTreeCheckbox: true的情况 + if (node.name === 'checkbox-content') { + if (checked === 'indeterminate') { + ((node as CheckboxContent)._checkboxGroup.getChildByName('checkbox') as CheckBox).setAttribute( + 'indeterminate', + true + ); + ((node as CheckboxContent)._checkboxGroup.getChildByName('checkbox') as CheckBox).setAttribute( + 'checked', + undefined + ); + } else { + ((node as CheckboxContent)._checkboxGroup.getChildByName('checkbox') as CheckBox).setAttribute( + 'indeterminate', + undefined + ); + ((node as CheckboxContent)._checkboxGroup.getChildByName('checkbox') as CheckBox).setAttribute( + 'checked', + checked + ); + } + } }); }); } diff --git a/packages/vtable/src/ts-types/base-table.ts b/packages/vtable/src/ts-types/base-table.ts index cd340c9c48..56a8dacf83 100644 --- a/packages/vtable/src/ts-types/base-table.ts +++ b/packages/vtable/src/ts-types/base-table.ts @@ -132,6 +132,8 @@ export interface IBaseTableProtected { keyboardOptions?: TableKeyboardOptions; eventOptions?: TableEventOptions; rowSeriesNumber?: IRowSeriesNumber; + /** 启动复选框级联 */ + enableCheckboxCascade?: boolean; columnSeriesNumber?: ColumnSeriesNumber[]; // disableRowHeaderColumnResize?: boolean; @@ -537,6 +539,8 @@ export interface BaseTableConstructorOptions { beforeRender?: (stage: any) => void; afterRender?: (stage: any) => void; rowSeriesNumber?: IRowSeriesNumber; + /** 启用复选框级联 */ + enableCheckboxCascade?: boolean; // columnSeriesNumber?: ColumnSeriesNumber[]; customCellStyle?: CustomCellStyle[]; customCellStyleArrangement?: CustomCellStyleArrangement[]; diff --git a/packages/vtable/src/ts-types/table-engine.ts b/packages/vtable/src/ts-types/table-engine.ts index 51d1e13560..ac785add4b 100644 --- a/packages/vtable/src/ts-types/table-engine.ts +++ b/packages/vtable/src/ts-types/table-engine.ts @@ -121,7 +121,7 @@ export interface IRowSeriesNumber { // align?: 'left' | 'right'; // span?: number | 'dependOnNear'; title?: string; - // field?: FieldDef; + field?: FieldDef; format?: (col?: number, row?: number, table?: BaseTableAPI) => any; cellType?: 'text' | 'link' | 'image' | 'video' | 'checkbox' | 'radio'; style?: ITextStyleOption | ((styleArg: StylePropertyFunctionArg) => ITextStyleOption); @@ -139,7 +139,8 @@ export interface IRowSeriesNumber { /** 是否禁止列宽调整 */ disableColumnResize?: boolean; - /** 是否开启树形结构复选框 */ + /** @deprecated 请使用全局 enableCheckboxCascade配置 + * 是否开启树形结构复选框 */ enableTreeCheckbox?: boolean; customLayout?: ICustomLayout; headerCustomLayout?: ICustomLayout;