diff --git a/packages/antd/src/select-table/index.tsx b/packages/antd/src/select-table/index.tsx index b52828f7687..477b9213ff4 100644 --- a/packages/antd/src/select-table/index.tsx +++ b/packages/antd/src/select-table/index.tsx @@ -9,7 +9,7 @@ import { useFilterOptions } from './useFilterOptions' import { useFlatOptions } from './useFlatOptions' import { useSize } from './useSize' import { useTitleAddon } from './useTitleAddon' -import { useCheckSlackly } from './useCheckSlackly' +import { useCheckSlackly, getIndeterminate } from './useCheckSlackly' import { getUISelected, getOutputData } from './utils' import { usePrefixCls } from '../__builtins__' @@ -121,7 +121,8 @@ export const SelectTable: ComposedSelectTable = observer((props) => { const filteredDataSource = useFilterOptions( dataSource, searchValue, - filterOption + filterOption, + rowSelection?.checkStrictly ) // Order dataSource By filterSort @@ -133,6 +134,7 @@ export const SelectTable: ComposedSelectTable = observer((props) => { }, [filteredDataSource, filterSort]) const flatDataSource = useFlatOptions(dataSource) + const flatFilteredDataSource = useFlatOptions(filteredDataSource) // selected keys for Table UI const selected = getUISelected( @@ -159,13 +161,17 @@ export const SelectTable: ComposedSelectTable = observer((props) => { onSearch?.(formatted) } - const onInnerChange = (selectedRowKeys: any[], records: any[]) => { + const onInnerChange = (selectedRowKeys: any[]) => { if (readOnly) { return } + // 筛选后onChange默认的records数据不完整,此处需使用完整数据过滤 + const wholeRecords = flatDataSource.filter((item) => + selectedRowKeys.includes(item?.[primaryKey]) + ) const { outputValue, outputOptions } = getOutputData( selectedRowKeys, - records, + wholeRecords, dataSource, primaryKey, valueType, @@ -173,6 +179,7 @@ export const SelectTable: ComposedSelectTable = observer((props) => { mode, rowSelection?.checkStrictly ) + onChange?.(outputValue, outputOptions) } @@ -183,22 +190,17 @@ export const SelectTable: ComposedSelectTable = observer((props) => { const selectedRowKey = record?.[primaryKey] const isSelected = selected?.includes(selectedRowKey) let selectedRowKeys = [] - let records = [] if (mode === 'single') { selectedRowKeys = [selectedRowKey] - records = [record] } else { if (isSelected) { selectedRowKeys = selected.filter((item) => item !== selectedRowKey) } else { selectedRowKeys = [...selected, selectedRowKey] } - records = flatDataSource.filter((item) => - selectedRowKeys.includes(item?.[primaryKey]) - ) } if (rowSelection?.checkStrictly !== false) { - onInnerChange(selectedRowKeys, records) + onInnerChange(selectedRowKeys) } else { onSlacklyChange(selectedRowKeys) } @@ -206,23 +208,27 @@ export const SelectTable: ComposedSelectTable = observer((props) => { // TreeData SlacklyChange const onSlacklyChange = (currentSelected: any[]) => { - let { selectedRowKeys, records } = useCheckSlackly( + let { selectedRowKeys } = useCheckSlackly( currentSelected, selected, + flatDataSource, + flatFilteredDataSource, primaryKey, - flatDataSource + rowSelection?.checkStrictly ) - onInnerChange(selectedRowKeys, records) + onInnerChange(selectedRowKeys) } // Table All Checkbox const titleAddon = useTitleAddon( selected, - useFlatOptions(filteredDataSource), + flatDataSource, + flatFilteredDataSource, primaryKey, mode, disabled, readOnly, + rowSelection?.checkStrictly, onInnerChange ) @@ -260,9 +266,32 @@ export const SelectTable: ComposedSelectTable = observer((props) => { ...(rowSelection?.getCheckboxProps?.(record) as any), disabled: disabled || record?.disabled, }), // antd + ...(rowSelection?.checkStrictly !== false + ? {} + : { + renderCell: (checked, record, index, originNode) => { + return React.cloneElement( + originNode as React.ReactElement, + { + indeterminate: getIndeterminate( + record, + flatDataSource, + selected, + primaryKey + ), + } + ) + }, + }), selectedRowKeys: selected, - onChange: onInnerChange, + // onChange: onInnerChange, + onChange: + rowSelection?.checkStrictly !== false + ? onInnerChange + : onSlacklyChange, type: modeAsType, + preserveSelectedRowKeys: true, + checkStrictly: true, } } columns={props.columns || columns} diff --git a/packages/antd/src/select-table/useCheckSlackly.tsx b/packages/antd/src/select-table/useCheckSlackly.tsx index 195387826a5..92598fcce1f 100644 --- a/packages/antd/src/select-table/useCheckSlackly.tsx +++ b/packages/antd/src/select-table/useCheckSlackly.tsx @@ -1,68 +1,91 @@ -import { getTreeKeys, hasSelectedKey, completedKeys } from './utils' +import { + getTreeKeys, + hasSelectedKey, + completedKeys, + getCompatibleAllSelected, +} from './utils' /** * 判断该字段的 indeterminate 属性 * @param record 当前字段 + * @param flatDataSource 完整平铺数据 * @param selected 已选中的字段值集合 * @param primaryKey 键名称 * @returns indeterminate 属性值 */ -const getIndeterminate = (record: any, selected: any[], primaryKey: string) => { +const getIndeterminate = ( + record: any, + flatDataSource: any, + selected: any[], + primaryKey: string +) => { if (selected?.includes(record[primaryKey])) { return undefined } - return hasSelectedKey(record.children, selected, primaryKey) || undefined + const wholeRecord = flatDataSource.find( + (item) => item[primaryKey] === record[primaryKey] + ) + return hasSelectedKey(wholeRecord.children, selected, primaryKey) || undefined } interface ICheckSlackly { ( currentSelected: any[], - allSelected: any[], + selected: any[], + flatDataSource: any[], + flatFilteredDataSource: any[], primaryKey: string, - flatDataSource: any[] - ): { - selectedRowKeys: any[] - records: any[] - } + checkStrictly: boolean + ): { selectedRowKeys: any[] } } +// 父子节点(节点状态按全完整数据计算,节点操作按筛选数据计算) const useCheckSlackly: ICheckSlackly = ( currentSelected, // onChange 返回的 keys - allSelected, // Table UI 展示的 keys + selected, // Table UI 展示的 keys + flatDataSource, + flatFilteredDataSource, primaryKey, - flatDataSource + checkStrictly ) => { - const isSelected = currentSelected.length > allSelected.length // 判断是选中还是取消 - const currentKey = [...currentSelected, ...allSelected].find( - (key) => !(currentSelected.includes(key) && allSelected.includes(key)) // 当前变化key不同时存在于两个selected + let isSelected = currentSelected.length > selected.length // 判断是选中还是取消 + + const currentKey = [...currentSelected, ...selected].find( + (key) => !(currentSelected.includes(key) && selected.includes(key)) // 当前变化key不同时存在于两个selected ) - const currentRecords = flatDataSource.find( + // 从过滤后的数据中获取当前record + const currentRecord = flatFilteredDataSource.find( (item) => item[primaryKey] === currentKey ) - const currentTreeKeys = getTreeKeys([currentRecords], primaryKey) - let newSelectedRowKeys = [] + const currentTreeKeys = getTreeKeys(currentRecord.children, primaryKey) + + // 在筛选状态下(按钮的indeterminate状态处于异常)需要通过数据对比判断是否处于全选中状态 + if ( + getCompatibleAllSelected( + selected, + currentRecord.children, + currentTreeKeys, + checkStrictly, + primaryKey + ) + ) { + isSelected = false + } + + let newSelected = [] if (isSelected) { // 选中当前key及其子keys - newSelectedRowKeys = [...new Set([...allSelected, ...currentTreeKeys])] + newSelected = [...new Set([...selected, currentKey, ...currentTreeKeys])] } else { // 移除当前key及其子keys - newSelectedRowKeys = allSelected.filter( - (key) => !currentTreeKeys.includes(key) + newSelected = selected.filter( + (key) => ![currentKey, ...currentTreeKeys].includes(key) ) } - newSelectedRowKeys = completedKeys( - flatDataSource, - newSelectedRowKeys, - primaryKey - ) + newSelected = completedKeys(flatDataSource, newSelected, primaryKey) - return { - selectedRowKeys: newSelectedRowKeys, - records: flatDataSource.filter((item) => - newSelectedRowKeys.includes(item[primaryKey]) - ), - } + return { selectedRowKeys: newSelected } } export { useCheckSlackly, getIndeterminate } diff --git a/packages/antd/src/select-table/useFilterOptions.tsx b/packages/antd/src/select-table/useFilterOptions.tsx index 3bfe9a51d57..186c1eddcb5 100644 --- a/packages/antd/src/select-table/useFilterOptions.tsx +++ b/packages/antd/src/select-table/useFilterOptions.tsx @@ -40,7 +40,8 @@ function toArray(value: T | T[]): T[] { const useFilterOptions = ( options: any[], searchValue?: string | string[], - filterOption?: IFilterOption + filterOption?: IFilterOption, + checkStrictly?: boolean ) => React.useMemo(() => { if (!searchValue || filterOption === false) { @@ -57,7 +58,8 @@ const useFilterOptions = ( const filterChildren = doFilter(item.children) if (filterChildren.length) { filterArr.push({ ...item, children: filterChildren }) - } else if (filterFunc(searchValue, item)) { + } else if (filterFunc(searchValue, item) && checkStrictly !== false) { + // 父子关系启用时,没有可用子元素,不添加父元素 filterArr.push({ ...item, children: [] }) } } else if (filterFunc(searchValue, item)) { diff --git a/packages/antd/src/select-table/useTitleAddon.tsx b/packages/antd/src/select-table/useTitleAddon.tsx index 929b890dae2..9d602d778fd 100644 --- a/packages/antd/src/select-table/useTitleAddon.tsx +++ b/packages/antd/src/select-table/useTitleAddon.tsx @@ -1,56 +1,82 @@ import React from 'react' import { Checkbox } from 'antd' +import { completedKeys, getCompatibleAllSelected } from './utils' -// 重写表格表头Checkbox +// 重写表格表头Checkbox(节点状态按全完整数据计算,节点操作按筛选数据计算) const newCheckbox = ( selected, - filteredFlatDataSource, + flatDataSource, + flatFilteredDataSource, primaryKey, disabled, readOnly, + checkStrictly, onChange ) => () => { - const currentDataSource = filteredFlatDataSource.filter( - (item) => !item.disabled - ) - const currentDataSourceKeys = currentDataSource.map( - (item) => item?.[primaryKey] - ) - const currentSelected = selected.filter((item) => - currentDataSourceKeys.includes(item) - ) - const indeterminate = !!( - currentSelected?.length && - currentSelected.length !== currentDataSource.length + // 全选框是否选中 + const checked = Boolean( + selected?.length && + selected?.length === + flatDataSource.filter((item) => !item.disabled).length ) + // 全选框是否未完全选中 + const indeterminate = Boolean(selected?.length && !checked) + + const onInnerChange = (e) => { + if (!readOnly) { + let isSelected = e.target.checked + // 当前可执行全选的keys + const usableKeys = flatFilteredDataSource + .filter((item) => !item.disabled) + .map((item) => item?.[primaryKey]) + // 在筛选状态下(按钮的indeterminate状态处于异常)需要通过数据对比判断是否处于全选中状态 + if ( + getCompatibleAllSelected( + selected, + flatFilteredDataSource, + usableKeys, + checkStrictly, + primaryKey + ) + ) { + isSelected = false + } + + let newSelected = [] + if (isSelected) { + // 执行全选 + newSelected = [...new Set([...selected, ...usableKeys])] + } else { + // 执行取消全选 + newSelected = selected.filter((key) => !usableKeys.includes(key)) + } + newSelected = completedKeys(flatDataSource, newSelected, primaryKey) + onChange?.(newSelected) + } + } + return ( { - if (!readOnly) { - if (e.target.checked || indeterminate) { - onChange?.(currentDataSourceKeys, currentDataSource) - } else { - onChange?.([], []) - } - } - }} + onChange={onInnerChange} /> ) } const useTitleAddon = ( selected: any[], - filteredFlatDataSource: any[], + flatDataSource: any[], + flatFilteredDataSource: any[], primaryKey: string, mode: string, disabled: boolean, readOnly: boolean, + checkStrictly: boolean, onChange: (selectedRowKeys: any[], record: any[]) => any ) => { if (mode === 'single') { @@ -59,10 +85,12 @@ const useTitleAddon = ( return { columnTitle: newCheckbox( selected, - filteredFlatDataSource, + flatDataSource, + flatFilteredDataSource, primaryKey, disabled, readOnly, + checkStrictly, onChange ), } diff --git a/packages/antd/src/select-table/utils.ts b/packages/antd/src/select-table/utils.ts index 6f8bb9cf2b5..77aaefe6d1c 100644 --- a/packages/antd/src/select-table/utils.ts +++ b/packages/antd/src/select-table/utils.ts @@ -139,7 +139,7 @@ const deleteTreeItem = (tree: any[], key: string) => * @returns 最终输出的 keys 和 options */ const getOutputData = ( - keys, + keys, // selected options, dataSource, primaryKey, @@ -257,6 +257,36 @@ const getUISelected = ( return newKeys } +/** + * 获取兼容筛选模式下是否全部选中子元素 + * @param selected 已选中项 + * @param dataSource 当前数据结构 + * @param usableKeys 当前数据结构的可执行项 + * @param checkStrictly + * @param primaryKey + * @returns 是否全部选中 + */ +const getCompatibleAllSelected = ( + selected, + dataSource, + usableKeys, + checkStrictly, + primaryKey +) => { + if (!usableKeys.length) { + return false + } + // 当前模式下已选中的项 + const currentSelected = selected.filter((item) => usableKeys.includes(item)) + // 获取有效选中(父子模式或非父子模式) + const validSelected = + checkStrictly !== false + ? currentSelected // 非父子模式选中项 + : completedKeys(dataSource, currentSelected, primaryKey) // 父子模式选中项 + // 有效选中项数量等于可执行项数量则全部选中子元素 + return validSelected.length === usableKeys.length +} + export { hasSelectedKey, getTreeKeys, @@ -265,4 +295,5 @@ export { getUISelected, getOutputData, completedKeys, + getCompatibleAllSelected, } diff --git a/packages/next/src/select-table/index.tsx b/packages/next/src/select-table/index.tsx index 379720dcacd..00e73c7c547 100644 --- a/packages/next/src/select-table/index.tsx +++ b/packages/next/src/select-table/index.tsx @@ -264,12 +264,16 @@ export const SelectTable: ComposedSelectTable = observer((props) => { ...titleAddon, getProps: (record, index) => ({ ...(rowSelection?.getProps?.(record, index) as any), - indeterminate: getIndeterminate( - record, - flatDataSource, - selected, - primaryKey - ), // 父子关联模式indeterminate值 + ...(rowSelection?.checkStrictly !== false + ? {} + : { + indeterminate: getIndeterminate( + record, + flatDataSource, + selected, + primaryKey + ), + }), // 父子关联模式indeterminate值 disabled: disabled || record?.disabled, }), // fusion selectedRowKeys: selected,