antd 级联选择器Cascader(或TreeSelect树选择器 )如何仅根据最后一级value回显完整中文路径

504

本文源自道招网的# antd 级联选择器Cascader(或TreeSelect树选择器 )如何仅根据最后一级value回显完整中文路径

Cascader(或TreeSelect树选择器 )往往会在项目中用在分类管理这一需求,可以很好的展示分类的层级结构,有时可能因为分类会存在改变层级(如由四级分类变成了二级分类)的情况,后端接口会要求在存储分类数据的时候,仅传递最后一级的信息。

例子

举个antd官网的例子:

import { Cascader } from 'antd';

const options = [
  {
    value: 'zhejiang',
    label: 'Zhejiang',
    children: [
      {
        value: 'hangzhou',
        label: 'Hangzhou',
        children: [
          {
            value: 'xihu',
            label: 'West Lake',
          },
        ],
      },
    ],
  },
  {
    value: 'jiangsu',
    label: 'Jiangsu',
    disabled: true,
    children: [
      {
        value: 'nanjing',
        label: 'Nanjing',
        children: [
          {
            value: 'zhonghuamen',
            label: 'Zhong Hua Men',
          },
        ],
      },
    ],
  },
];

function onChange(value) {
  console.log(value);
}

ReactDOM.render(<Cascader options={options} onChange={onChange} />, mountNode);

在选中Zhejiang / Hangzhou / Wes tLake时,对应的value是['zhejiang', 'hangzhou', 'xihu'],后端可能只需要你传递xihu即可,而非数组形式的网站路径信息。

这时前端在回显的时候会出现一个问题,无法根据xihu完整的回显之前的路径名Zhejiang / Hangzhou / Wes tLake了。

如何收集完整的节点路径信息

我们可以借助官网在TreeSelect给的提示onChange时如何获得父节点信息?

file

其思路就是将节点value作为对象valueMap的key,遍历节点的子节点children(如果有的话),通过parent该节点和其子节点链接起来。后续就可以用getPath方法根据valuevalueMap通过parent一层一层找到对应的父节点信息了。

const valueMap = {};
function loops(list, parent) {
  return (list || []).map(({ children, value, label }) => {
    const node = (valueMap[value] = {
      parent,
      data: {
        label,
        value
      }
    });
    node.children = loops(children, node);
    return node;
  });
}

loops(treeData);

function getPath(value, prop) {
  const path = [];
  let current = valueMap[value];
  while (current) {
    path.unshift(current.data[prop]);
    current = current.parent;
  }
  return path;
}

简单改造下,同时valueMap同时收集下lable信息,同时getPath支持返回指定prop的信息,让回显更加灵活。

如何使用getPath完成数据

方法一:我们可以直接通过ref暴露出去
useImperativeHandle(ref, () => ({
valueMap,
getPath,
}))

但是这样在部分使用起来不合适,比如列表展示时,并且这样也会存在因为列表数据渲染时CascadervalueMap还未收集好,而getPath获取结果为空的情况。

我们可以试试方法二

方法二:支持自定义render
function CategorySelect(props, ref) {
  const [value, setValue] = useState([]);
  const [valueMap, setValueMap] = useState({});
  // 根据value回显对应的label
  const fullLabelPath = useMemo(() => {
    if (!props.displayLabel) {
      return;
    }
    return getPath(props.value, props.displayLabel);
  }, [props.displayLabel, props.value, props.data, valueMap]);

  ...

  return {
    (props.fullLabelRender)
      ? props.fullLabelRender(fullLabelPath)
      : (
          
        )
  }
}

我们将全路径的渲染能力开放通过props.fullLabelRender

使用可以这样使用

fullLabelRender={(fullLabelPath) => ({ fullLabelPath.join(' / ') })}

因为fullLabelPathuseMemo的返回值,valueMapuseState的返回值,这样我们也不必担心列表数据渲染时Cascader的valueMap还未收集好的情况了,待valueMap收集完毕后,自会重新渲染的。

同样的思路也可以用在TreeSelect树选择器,我就不具体写了哦。