Antd - Table 父子表格Checkbox联动

Antd - Table 父子表格Checkbox联动

前言

由于Antd中的父子组件之间,如果有多选功能,那么不会有联动的关系,需要自己实现。

一. 勾选父子组件联动

代码如下:

import React, { useState } from 'react';
import { Table } from 'antd';
const Column = Table.Column;
interface Parent {
    parentId: number;
    name: string;
    orderID: number;
    childList: Children[]
}
interface Children {
    name: string;
    childId: number;
}
const dataSource: Parent[] = [
    { parentId: 1, name: '张三', orderID: 123456, childList: [{ name: '吹风机', childId: 1001, }, { name: '牛奶', childId: 1002, }] },
    { parentId: 2, name: '李四', orderID: 987654, childList: [{ name: '鼠标', childId: 1003, }, { name: '键盘', childId: 1004, }] }
]
const Page = () => {
    const [parentSelectedRowKeys, setParentSelectedRowKeys] = useState<number[]>([])
    const [childSelectedRowKeys, setChildSelectedRowKeys] = useState<number[]>([])


    const onParentSelectChange = (record: Parent, selected: boolean) => {
        // 目前为止选择的父节点和子节点的RowKey
        const preParentRowKeys: number[] = [...(parentSelectedRowKeys || [])];
        let preChildRowKeys: number[] = [...(childSelectedRowKeys || [])];
        // 当前勾选的父节点,其对应的所有子节点的RowKey
        let currentChildRowKeys = dataSource.find(parent => parent.parentId === record.parentId)?.childList.map(item => item.childId) || [];
        // 判断是否选中,选中就加入,否则从老的RowKey中删除它(去重)
        if (selected) {
            preParentRowKeys.push(record.parentId)
            preChildRowKeys = Array.from(new Set([...currentChildRowKeys, ...preChildRowKeys]))
        } else {
            // 否则,父节点取消选中,子节点全部取消
            preParentRowKeys.splice(preParentRowKeys.findIndex(parentRowKey => parentRowKey === record.parentId), 1)
            preChildRowKeys = preChildRowKeys.filter(childRowKey => !currentChildRowKeys.some(rowkey => rowkey === childRowKey))
        }
        // 最后重新设置父,子的SelectedRowKeys
        setParentSelectedRowKeys(preParentRowKeys)
        setChildSelectedRowKeys(preChildRowKeys)
    }
    // 父节点选中全部
    const onParentSelectAll = (selected: true, selectedRows: Parent[], changeRows: Parent[]) => {
        let preParentRowKeys = [...(parentSelectedRowKeys || [])];
        let currentChildRowKeys: number[] = [];
        changeRows.forEach(e => {
            currentChildRowKeys = [...currentChildRowKeys, ...e.childList.map(child => child.childId)]
        });
        // 如果选中,那么所有子节点全部选中
        if (selected) {
            preParentRowKeys = Array.from(new Set([...preParentRowKeys, ...changeRows.map(item => item.parentId)]))
            setChildSelectedRowKeys(currentChildRowKeys)
        } else {
            // 否则所有子节点取消选中
            preParentRowKeys = preParentRowKeys.filter(item => !changeRows.some(e => e.parentId === item))
            setChildSelectedRowKeys([])
        }
        // 设置父节点RowKey
        setParentSelectedRowKeys(preParentRowKeys)
    }

    const parentRowSelection = {
        selectedRowKeys: parentSelectedRowKeys,
        onSelect: onParentSelectChange,
        onSelectAll: onParentSelectAll,
    }

    const onChildSelectChange = (record: Children, selected: true, selectedRows: Children[]) => {
        const preChildRowKeys: number[] = [...(childSelectedRowKeys || [])];
        // 判断当前子节点是 取消勾选/勾选 状态,对应维护
        if (selected) {
            preChildRowKeys.push(record.childId)
        } else {
            preChildRowKeys.splice(preChildRowKeys.findIndex(item => item === record.childId), 1)
        }
        selectedRows = selectedRows.filter(a => a)
        // 判断子节点选中的个数,和对应当前的父节点下的子节点个数是否相等,如果是,那么对应父节点也要勾选上
        for (const item of dataSource) {
            if (item.childList.find(d => d.childId === record.childId)) {
                const preParentRowKeys: number[] = [...(parentSelectedRowKeys || [])];
                if (item.childList.length === selectedRows.length) {
                    preParentRowKeys.push(item.parentId)
                } else {
                    if (preParentRowKeys.find(rowkey => rowkey === item.parentId)) {
                        preParentRowKeys.splice(preParentRowKeys.findIndex(rowkey => rowkey === item.parentId), 1)
                    }
                }
                setParentSelectedRowKeys(preParentRowKeys)
                break;
            }
        }
        setChildSelectedRowKeys(preChildRowKeys)
    }
    const onChildSelectAll = (selected: true, selectedRows: Children[], changeRows: Children[]) => {
        let preChildRowKeys: number[] = [...(childSelectedRowKeys || [])];
        if (selected) {
            preChildRowKeys = Array.from(new Set([...preChildRowKeys, ...changeRows.map(item => item.childId)]))
        } else {
            preChildRowKeys = preChildRowKeys.filter(item => !changeRows.some(child => child.childId === item))
        }
        // 子节点全部选中或者取消全部选中,那么对应父节点的状态也要勾选或者取消勾选
        for (const item of dataSource) {
            if (item.childList.find(d => d.childId === changeRows[0].childId)) {
                const preParentRowKeys: number[] = [...(parentSelectedRowKeys || [])];
                if (selected) {
                    //全选
                    preParentRowKeys.push(item.parentId)
                } else {
                    //取消全选
                    preParentRowKeys.splice(preParentRowKeys.findIndex(rowkey => rowkey === item.parentId), 1)
                }
                setParentSelectedRowKeys(preParentRowKeys)
                break;
            }
        }
        setChildSelectedRowKeys(preChildRowKeys)
    }

    const childRowSelection = {
        selectedRowKeys: childSelectedRowKeys,
        onSelect: onChildSelectChange,
        onSelectAll: onChildSelectAll
    }

    const expandedRowRender = (record: Parent) => {
        const { childList } = record;
        return <Table dataSource={childList} rowKey={'childId'} rowSelection={childRowSelection} pagination={false}>
            <Column key={'childId'} dataIndex={'childId'} title={'childId'} />
            <Column key={'name'} dataIndex={'name'} title={'购买产品'} />
        </Table>
    }

    return <>
        <Table dataSource={dataSource} rowKey={'parentId'} expandable={{ expandedRowRender }} rowSelection={parentRowSelection} pagination={false}>
            <Column key={'parentId'} dataIndex={'parentId'} title={'ID'} />
            <Column key={'name'} dataIndex={'name'} title={'姓名'} />
            <Column key={'orderID'} dataIndex={'orderID'} title={'订单号'} />
        </Table>
    </>
}

export default Page;

注意:

  1. 可以选择Redux去存储父子组件对应的RowKey,否则就要在同一个组件中维护状态。
  2. 建议把类型定义描述好,否则父子组件联动很容易出现问题,不要总是写any,否则很难维护的。

二. 效果

在这里插入图片描述

### Antd Table CheckStrictly 设置为 False 时不可勾选解决方案 当 `checkStrictly` 设定为 `false` 时,Antd 的 Tree 或者 Table 组件默认情况下会保持父子节点之间的关联关系。这意味着节点的状态会影响其子节点的状态,反之亦然。然而,在某些场景下可能会遇到无法正常勾选的问题。 为了确保在 `checkStrictly=false` 下能够正确操作复选框并维持父子间的联动效果,建议采取如下措施: #### 方法一:手动管理 checkedKeys 和 halfCheckedKeys 属性 可以通过编程方式维护两个列表——一个是完全被选中的项(`checked`);另一个则是部分选中状态下的项目(`halfChecked`)。每当发生改变时更新这两个集合,并调用 `setCheckedKeys()` 来同步 UI 显示[^3]。 ```javascript const onCheck = (checkedKeys, info) => { const { eventKey } = info.node; let newCheckedKeys = [...checkedKeys.checked]; if (!info.checked) { // 如果取消了某个节点,则移除它以及它的后代们 removeDescendantNodes(eventKey); } else { addAncestorAndSelfToSelection(eventKey); } setCheckedKeys({ checked: newCheckedKeys, halfChecked: calculateHalfSelectedItems(newCheckedKeys), }); }; ``` 此函数负责处理每次点击复选框后的逻辑变化,包括但不限于添加祖先节点到已选项里、删除子孙节点等动作。 #### 方法二:利用自定义渲染器增强原有功能 对于更复杂的业务需求来说,可能还需要进一步定制化组件行为。此时可以考虑重载默认的 Checkbox 渲染过程,加入额外判断条件以适应特定的应用环境[^1]。 ```jsx <Table.Column title="Name" dataIndex="name" key="name" render={(text, record) => <span> {/* 自定义checkbox */} <Checkbox onChange={() => handleCustomCheckBoxChange(record)} checked={record.selected} /> {text} </span>} /> ``` 上述代码片段展示了如何通过覆盖列配置里的 `render` 函数来自定义每一行前缀处显示的内容。这里引入了一个新的 `handleCustomCheckBoxChange` 处理程序用于单独控制每一条记录的选择情况。 #### 方法三:调整数据源结构支持嵌套层次遍历 有时问题根源在于原始 JSON 数据缺乏足够的层信息描述,这使得框架难以识别哪些元素属于同一组内。因此优化输入的数据集也是一个有效的途径之一[^4]。 ```json [ { "title": "Parent Node", "key": "0-0", "children": [ {"title":"Child One","key":"0-0-0"}, {"title":"Child Two","key":"0-0-1"} ] } ] ``` 这种形式化的表示法有助于提高算法效率的同时也简化了前端开发人员的工作量。 综上所述,针对 a-table 中 checkStrictly 设置为 false 导致不可勾选的问题,可以从多个角度出发寻找合适的对策。具体采用哪种策略取决于实际应用场景和个人偏好等因素的影响。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Zong_0915

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值