antd tree + 可编辑的表格

业务场景:

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

难点:

   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: 
          
        ,
        // type: 'input',
      },
      {
        title: '有效期',
        dataIndex: 'bsUnit',
        key: 'bsUnit',
        editable: true,
        filterDropdown: true, // 自定义的列筛选功能,我们占位为信息提示Icon的位置
        filterIcon: 
          
        ,
        type: {
          kind: 'select',
          option: ['年', '月', '周', '天']
        },
      },
      {
        title: '操作',
        dataIndex: 'action',
        key: 'action',
        render: (text, record) =>
          this.state.dataSource.length >= 1 ? (
             { this.props.tableDelete && this.props.tableDelete(record) }}>删除
          ) : 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([]);
 {
            setCheckedKeys(checkedKeys);
            setDataSource(parInfo);
          }}
        />

//-----------------------------------表格组件------------------------------------------------
 UNSAFE_componentWillReceiveProps(nextProps) {
    if (this.props.dataSource !== nextProps.dataSource) {
      this.setState({
        dataSource: nextProps.dataSource,
        count: nextProps.dataSource.length,
      })
    }
  }
  • 删除联动
// ----------------------------表格组件--------------------------------------------

 { this.props.tableDelete && this.props.tableDelete(record) }}>删除

// -----------------------------------------公众组件------------------------------------------
  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; // 筛选之后的数组
  }

 tableDelete(data)}
        />
最终效果图

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

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

你可能感兴趣的:(antd tree + 可编辑的表格)