Skip to content

antd 树形 table 实现父子联动选择

antd table 文档 暂不支持父子数据递归关联选择。

antd 的 tree table 未实现父子关联选择,需要自行实现

想要的效果如下

tsx
// 获取所有后代
const getOffspringKeys = (list: any[]) => {
  const ret: any[] = []
  list.forEach((item) => {
    ret.push(item.key)
    if (item.children && item.children.length) {
      ret.push(...getOffspringKeys(item.children))
    }
  })
  return ret
}

// 处理 tree 数据: 裁剪字段 + 父子关联数据,_parent _offspringKeys
const formatData = (list: any[], parent: any[] = []): any[] =>
  list.map((item) => {
    const base = _.pick(item, ['key', 'name'])
    if (item.children && item.children.length) {
      const keys = item.children.map((x: any) => x.key)
      const children = formatData(item.children, [...parent, { key: item.key, children: keys }])
      const _offspringKeys = getOffspringKeys(item.children)
      return { ...base, children, _offspringKeys, _parent: parent }
    }
    return { ...base, _parent: parent }
  })
tsx
const [selectedKeys, setSelectedKeys] = useState<any[]>([])

// 单选 / 多选
const rowSelection: TableRowSelection<any> = {
  type: 'checkbox',
  selectedRowKeys: selectedKeys,
  getCheckboxProps: (record: any) => {
    let indeterminate = false // 半选
    if (record._offspringKeys) {
      const inter = _.intersection(selectedKeys, record._offspringKeys)
      indeterminate = inter.length > 0 && inter.length < record._offspringKeys.length
    }
    return { indeterminate }
  },
  onSelect: (record: any, selected: any) => {
    let retKeys: any[] = []
    const children = record._offspringKeys || []
    if (selected) {
      retKeys = _.union(selectedKeys, [record.key], children) // 选中自己 + 后代
      // 选中上游节点
      _.forEachRight(record._parent, (el) => {
        const hasNot = el.children.some((x: any) => retKeys.indexOf(x) === -1)
        if (!hasNot) {
          retKeys.push(el.key)
        }
      })
    } else {
      retKeys = _.without(selectedKeys, record.key, ...children) // 取消自己 + 后代
      // 取消上游节点
      _.forEachRight(record._parent, (el) => {
        const hasNot = el.children.some((x: any) => retKeys.indexOf(x) === -1)
        if (hasNot) {
          retKeys = retKeys.filter((x) => x !== el.key)
        }
      })
    }
    setSelectedKeys(retKeys)
  },
}
tsx
const tableDataDemo = [
  {
    key: 1,
    name: 'John Brown sr.',
    age: 60,
    address: 'New York No. 1 Lake Park',
    children: [
      {
        key: 13,
        name: 'Jim Green sr.',
        age: 72,
        address: 'London No. 1 Lake Park',
        children: [
          {
            key: 131,
            name: 'Jim Green',
            age: 42,
            address: 'London No. 2 Lake Park',
            children: [
              {
                key: 1311,
                name: 'Jim Green jr.',
                age: 25,
                address: 'London No. 3 Lake Park',
              },
              {
                key: 1312,
                name: 'Jimmy Green sr.',
                age: 18,
                address: 'London No. 4 Lake Park',
              },
            ],
          },
        ],
      },
      {
        key: 11,
        name: 'John Brown',
        age: 42,
        address: 'New York No. 2 Lake Park',
      },
    ],
  },
]

const tableData = formatData(tableDataDemo)

<Table
  rowKey="key"
  columns={columns}
  showHeader={false}
  pagination={false}
  rowSelection={rowSelection}
  dataSource={tableData}
/>