美文网首页
antd tree + 可编辑的表格

antd tree + 可编辑的表格

作者: kll982982 | 来源:发表于2020-06-08 09:50 被阅读0次
    业务场景:

       树和表格联动,当选中树的某个节点时,动态添加表格展示.

    难点:

       1. 树的层级不固定,当我选中某个子节点的时候,表格中始终展示第二个子节点的数据.
       2.表格中带有删除按钮,可以动态取消树的勾选状态

    实际应用

    反参结构:

    [
        {
            "dspId": "0-0",
            "dspName": "第一级数据",
            "bsLists": [
                {
                    "bsId": "0-0-1",
                    "bsName": "要展示在表格中的第二级数据1",
                    "bsNumber": "30",
                    "bsUnit": "天",
                    "outputParamList": [
                        {
                            "paramName": "第三级数据1-1",
                            "paramId": "0-0-1-1",
                            "checkable": 1,
                            "childParams": [
                                {
                                    "paramName": "第四级数据1-1-1",
                                    "paramId": "0-0-1-1-1",
                                    "checkable": 1,
                                    "childParams": []
                                }
                            ]
                        }
                    ]
                },
                {
                    "bsId": "0-0-2",
                    "bsName": "要展示在表格中的第二级数据2",
                    "bsNumber": "30",
                    "bsUnit": "天",
                    "outputParamList": [
                        {
                            "paramName": "第三级数据2-1",
                            "paramId": "0-0-2-1",
                            "checkable": 1,
                            "childParams": [
                                {
                                    "paramName": "第四级数据2-1-1",
                                    "paramId": "0-0-2-1-1",
                                    "checkable": 1,
                                    "childParams": []
                                },
                                {
                                    "paramName": "第四级数据2-1-2",
                                    "paramId": "0-0-2-1-2",
                                    "checkable": 1,
                                    "childParams": []
                                }
                            ]
                        }
                    ]
                }
            ]
        }
    ]
    

    结合antd的数据结构,发现后台反参并不足以直接实现tree的结构,首先第一步就是处理数据结构,注意,重点来了,无论是勾选0-0-2-1-1还是0-0-2-1-2 亦或者是 0-0-2-1,表格中数据都是动态渲染0-0-2中的参数.此处为了减少循环,采用的方法是把需要的参数次第下传.

    数据处理成符合tree结构的数据

    // apiTreeData 指的是在接口中直接获取到的数据
    
    // 初始值
    apiTreeData.map((item, index) => {
      const treeChild = [];
      // 一级数据处理
      item.checkable = item.checkable * 1 === 1; // 复选框
      item.tier = 0; // 层级
      item.className = tierClass(0);
      // 一级的children
      item.bsLists.map((it, i) => {
        // 二级数据处理
        it.checkable = it.checkable * 1 === 1;
        it.tier = 1; // 层级
        it.className = tierClass(1);
        const params = {
          // 传递的参数
        }
        let child = it.outputParamList || [];
        console.log('i', i);
        // 二级的children
        if (it.outputParamList) {
          child = loopChild(it.outputParamList, params, 2);
        }
    
        treeChild.push({
          ...params,
          title: it.bsName,
          key: it.bsId,
          children: child,
          ...item,
        });
        expandedKey.push(item.dspId);
      })
      filterTreeData.push({
        title: item.dspName,
        key: item.dspId,
        children: treeChild,
        ...item
      });
    })
    
    // 三级及三级以下
    function loopChild(child, params, tier) {
      const filterData = [];
      child.map(children => {
        children.title = children.paramName;
        children.key = children.paramId;
        children.checkable = children.checkable * 1 === 1
        children.children = children.childParams;
        children.tier = tier;
        children.className = tierClass(tier);
        console.log('tier', tier)
        // 包含下级时
        if (children.childParams && JSON.stringify(children.childParams) !== '[]') {
          children.children = loopChild(children.children, params, tier + 1);
        }
        filterData.push({ ...children, ...params });
      })
      return filterData;
    }
    
    

    展示如下:


    数据处理之后

    现在我们如愿的实现了tree的展示,接下来就是表格,表格为了区分是否可编辑,在可编辑的tr上添加了icon[不要疑惑 删除按钮对应的函数为什么是props,因为这块的数据处理起来较为麻烦,特意分成了俩个组件来编写]

    {
            title: '有效期',
            dataIndex: 'bsNumber',
            key: 'bsNumber',
            editable: true,
            filterDropdown: true, // 自定义的列筛选功能,我们占位为信息提示Icon的位置
            filterIcon: <Tooltip placement="top" title="输入日期" >
              <EditOutlined />
            </Tooltip>,
            // type: 'input',
          },
          {
            title: '有效期',
            dataIndex: 'bsUnit',
            key: 'bsUnit',
            editable: true,
            filterDropdown: true, // 自定义的列筛选功能,我们占位为信息提示Icon的位置
            filterIcon: <Tooltip placement="top" title="选择单位" >
              <EditOutlined />
            </Tooltip>,
            type: {
              kind: 'select',
              option: ['年', '月', '周', '天']
            },
          },
          {
            title: '操作',
            dataIndex: 'action',
            key: 'action',
            render: (text, record) =>
              this.state.dataSource.length >= 1 ? (
                <spanonClick={() => { this.props.tableDelete && this.props.tableDelete(record) }}>删除</span>
              ) : null,
          }
    
    效果
    表格.png

    接下来的任务就是将俩个组件联动

    • 勾选联动
    // --------------------------------tree组件中------------------------------------------------
    // 树-选中
      const [checkedKeys, setCheckedKeys] = useState([]);
    // 监听props传值
      useEffect(() => {
        setCheckedKeys(props.checkedKeys);
      }, [props.checkedKeys])
    
      const onCheck = (checkedKeys, info) => {
        setCheckedKeys(checkedKeys);
        let parInfo = [];
        if (info.checked) {
          info.checkedNodes.map(item => {
            parInfo.push({
              bsId: item.bsId, // 基础服务id
              bsNumber: item.bsNumber,
              bsUnit: item.bsUnit,
            })
          })
        }
        parInfo = deWeight(parInfo, 'bsId'); // 去重
        props.onCheck && props.onCheck(checkedKeys, info, parInfo);
      };
    
      // 根据数组对象的某一个key去重
      function deWeight(arr, key) {
        let map = new Map();
        for (let item of arr) {
            if (!map.has(item[key])) {
                map.set(item[key], item);
            }
        }
        return [...map.values()];
      }
    
    // -----------------------------------------公众组件------------------------------------------
      const [checkedKeys, setCheckedKeys] = useState([]);
      const [dataSource, setDataSource] = useState([]);
    <TreeMenu
              checkedKeys={checkedKeys}
              onCheck={(checkedKeys, info, parInfo) => {
                setCheckedKeys(checkedKeys);
                setDataSource(parInfo);
              }}
            />
    
    //-----------------------------------表格组件------------------------------------------------
     UNSAFE_componentWillReceiveProps(nextProps) {
        if (this.props.dataSource !== nextProps.dataSource) {
          this.setState({
            dataSource: nextProps.dataSource,
            count: nextProps.dataSource.length,
          })
        }
      }
    
    • 删除联动
    // ----------------------------表格组件--------------------------------------------
    
    <span className={'global-btn-table rc-linkColor '} onClick={() => { this.props.tableDelete && this.props.tableDelete(record) }}>删除</span>
    
    // -----------------------------------------公众组件------------------------------------------
      const [checkedKeys, setCheckedKeys] = useState([]);
      const [dataSource, setDataSource] = useState([]);
    
    const tableDelete = (record) => {
        const filterDataSource = [...dataSource], checked = [...checkedKeys];
        const filterChecked = deleteTreeLoop(filterTreeData, checked, record.bsId);
        const filterSource = filterDataSource.filter(item => item.bsId !== record.bsId)
        setCheckedKeys(filterChecked);
        setDataSource(filterSource);
      }
    
      // 1.循环树
      // 2.循环keys
      // 3.树的key=keys[n]&&bsId相同时,delete keys[n];
      // 4.得到keys
      const deleteTreeLoop = (treeData, checkedKeys, bsId, ck = []) => {
        treeData.forEach(item => {
          // 筛掉基础服务id相同的数据,checkkey中删除;
          checkedKeys.forEach((it, i) => {
            if (it === item.key && item.bsId !== bsId) {
              item.checked = false;
              ck.push(item.key);
            }
          })
          if (item.children && JSON.stringify(item.children) !== '[]') {
            deleteTreeLoop(item.children, checkedKeys, bsId, ck);
          }
        })
        return ck; // 筛选之后的数组
      }
    
    <EditableTable
              dataSource={dataSource || []}
              tableDelete={(data) => tableDelete(data)}
            />
    
    最终效果图

    总结
       后期主要遇到的问题在 deleteTreeLoop函数中,第三个参数的问题

    PS: 时间有点赶了,写的有点不知所谓,目前还在联调中,继续研究保存,待我联调通之后在整理下

    相关文章

      网友评论

          本文标题:antd tree + 可编辑的表格

          本文链接:https://www.haomeiwen.com/subject/axaazhtx.html