告别树形杈杈树,实现无限级多选下拉

·  阅读 1050
告别树形杈杈树,实现无限级多选下拉

思考一个问题:如何获取河北和天津下一级的数据集合呢?

带着这个问题看下面↓↓↓树形结构

const tree = [
    {
        value: '1',
        name: '河北',
        children: [
            {
                value: '10',
                name: '沧州',
                children: [
                    { value: '101', name: '沧县' },
                    { value: '102', name: '泊头' },
                    { value: '103', name: '衡水' }
                ]
            }
        ]
    },
    {
        value: '2',
        name: '北京',
        children: [
            { value: '20', name: '朝阳区'},
            { value: '21', name: '西城区', },
            { value: '22', name: '东城区', }
        ]
    },
     {
        value: '3',
        name: '天津',
        children: [
            { value: '31', name: '武清区'},
            { value: '32', name: '津南区' },
            { value: '33', name: '津北区' }
        ]
    }
]
复制代码

让我猜猜你是怎么想的

通过filter过滤出name == 天津 || name == 河北 ?

如果你也是这么想的,如果让你获取沧州下一级的信息怎么处理呢?

在树形结构上,我们操作元素是很复杂的,需要很多循环和递归,每次寻找都要遍历,子级查找父级也很繁琐,怎么才能一劳永逸呢?是否可以转换一次之后,以后调用转换后的数据结果?

给你一个数据最好获取属性的是什么数据结构? 对象{}

如何把树形结构转换为对象?

我们要注意以下三点

  • 保持树形原有的数据结构
  • 属性唯一性
  • 父子级关联性

找到数据结构中唯一值,value值是唯一的,可以使用value作为属性的[key],在子集添加属性parentIds来存储父级所有的key做子父级的关联属性,转换后的数据结构如下:

{
    "1":{
        "value":"1",
        "name":"河北",
        "children":[
            {
                "value":"10",
                "name":"沧州",
                "children":[
                    { "value":"101", "name":"沧县" },
            	    { "value":"102", "name":"泊头" },
 	            { "value":"103", "name":"衡水" }
                ]
            }
        ],
        "parentIds":[ ]
    },
    "2":{
        "value":"2",
        "name":"北京",
        "children":[
            { "value":"20", "name":"朝阳区" },
            { "value":"21", "name":"西城区" },
            {  "value":"22", "name":"东城区" }
        ],
        "parentIds":[ ]
    },
    "3":{
        "value":"3",
        "name":"天津",
        "children":[
            { "value":"31", "name":"武清区" },
            { "value":"32", "name":"津南区" },
            { "value":"33", "name":"津北区" }
        ],
        "parentIds":[  ]
    },
    "10":{
        "value":"10",
        "name":"沧州",
        "children":[
            { "value":"101", "name":"沧县" },
            { "value":"102", "name":"泊头" },
 	    { "value":"103", "name":"衡水" }
        ],
        "parentIds":[ "1" ]
    },
    "20":{
        "value":"20",
        "name":"朝阳区",
        "parentIds":[ "2" ]
    },
    "21":{
        "value":"21",
        "name":"西城区",
        "parentIds":[ "2"  ]
    },
    "22":{
        "value":"21",
        "name":"东城区",
        "parentIds":[ "2"  ]
    },
    "31":{
        "value":"31",
        "name":"武清区",
        "parentIds":[  "3" ]
    },
    "32":{
        "value":"32",
        "name":"津南区",
        "parentIds":[ "3" ]
    },
    "33":{
        "value":"33",
        "name":"津北区",
        "parentIds":[ "3" ]
    },
    "101":{
        "value":"101",
        "name":"沧县",
        "parentIds":[  "1",  "10" ]
    },
    "102":{
        "value":"102",
        "name":"泊头",
        "parentIds"::[  "1",  "10" ]
    },
    "103":{
        "value":"103",
        "name":"衡水",
        "parentIds":[  "1",  "10" ]
    }
}
复制代码

转换tree为obj

obj的key为value,parentIds存储所有父类的value,无父类则返回[]

/**
 *  转换tree为obj,obj的key为value,parentIds存储所有父类的value,无父类则返回[]
 * @param {*} tree 树形节点
 * @param {*} parentIds 父级ID
 * @param {*} obj 上一次处理后obj的结果
 */
function getObjByTree(tree, parentIds = [], obj = {}) {
//遍历树形下的元素结构
    for (let i = 0; i < tree.length; i++) {
        const item = tree[i];
        obj[item.value] = {//设置转换后的属性和值
            ...item,//保存当前数据结构
            parentIds: parentIds //存储所有的父级ID
        }
        if (item.children) {//如果存在子集则递归执行以上逻辑
            getObjByTree(item.children, [...parentIds, item.value], obj)
        }
    }
    return obj
}
复制代码

实际案例

背景介绍

树形层级不确定,实现树形数据多选查找下级

效果展示

代码实现

状态存储初始化

  • 初始值设定
 this.state = {
            treeMapDistance: {},
            selectKeyObj: { 0: { list: [], values: [] } }
};
复制代码
  • 获取转换后的树形结构,并设置初始值
componentDidMount = () => {
        let treeMapDistance = getObjByTree(tree)
        this.setState({
            treeMapDistance: treeMapDistance,
            selectKeyObj: { 0: { list: tree, values: [] } }
        })
 }
复制代码

页面结构

使用 Object.keys(selectKeyObj) 实现多层渲染

render() {
        const { selectKeyObj } = this.state
        return (
            <div>
                {(Object.keys(selectKeyObj).map((selectKey, index) => <Select
                    mode="multiple"
                    value={selectKeyObj[selectKey].values}
                    onChange={(values) => this.setSelectKey(values, index)}
                    key={index}
                    className='multiple-select-item'>
                    {selectKeyObj[selectKey].list.map(one => <Option 
                    value={one.value} 
                    key={one.value}> {one.name} </Option>)}
                </Select>))}
            </div>
 );
复制代码

设置当前选中项和下级列表数据 setSelectKey

  • 存储当前选中值,并获取下级元素,有责设置list数据,无责设置[]
setSelectKey = (values, key) => {
        const { selectKeyObj } = this.state
        let selectKeyNew = { ...selectKeyObj }
        //补充当次点击数据
        selectKeyNew[key] = {
            list: selectKeyObj[key].list,
            values: values
        }
        //添加下一级list展示项
        const nextList = this.getList(values)
        if (nextList.length > 0) {
            selectKeyNew[key + 1] = {
                list: nextList,
                values: []
            }
        }
        this.setState({
            selectKeyObj: selectKeyNew,
        })
}
复制代码
  • 获取下级元素方法
 /**
     * 根据values获取下一级的数组
     * @param {*} values 父级元素选中的值
     */
    getList = (values) => {
        const { treeMapDistance } = this.state
        return values.map(item => treeMapDistance[item].children || []).flat()
    }
复制代码
  • 效果展示

跳级处理

  • 修改父级,清空子集选中项
setSelectKey = (values, key) => {
    const { selectKeyObj } = this.state
    let selectKeyNew = { ...selectKeyObj }
    //+++++添加跳级处理开始
    let list = Object.keys(selectKeyObj)//获取当前数组
    let children = list.splice(key, list.length)//获取下级数据
    for (let i = 0; i < children.length; i++) {//清空下级数据
        const key = children[i];
        delete selectKeyNew[key]
    }
    //+++++添加跳级处理结束
    //补充当次点击数据
    selectKeyNew[key] = {
        list: selectKeyObj[key].list,
        values: values
}
....
复制代码
  • 效果如下
分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改