美文网首页Vue.js开发技巧
iView Cascader、Tree 数据处理

iView Cascader、Tree 数据处理

作者: 於風聽語 | 来源:发表于2018-05-29 16:46 被阅读156次

    iView 有个 Cascader、Tree 组件,数据要求比较严格(简直弱爆了好吗...)

    问题简述

    Cascader 数据要求一览(Tree 其实类似):

    {
        value: 'jiangsu',
        label: '江苏',
        children: [
            {
                value: 'nanjing',
                label: '南京',
                children: [
                    {
                        value: 'fuzimiao',
                        label: '夫子庙',
                    }
                ]
            }, {
                value: 'suzhou',
                label: '苏州',
                children: [
                    {
                        value: 'zhuozhengyuan',
                        label: '拙政园',
                    }, {
                        value: 'shizilin',
                        label: '狮子林',
                    }
                ]
            }
        ]
    }
    

    即:

    • value
    • label
    • children [可选]

    发个牢骚

    呃,谁的数据结构默认会是这样的?肯定很少,几乎没有....不服咬我


    话说就不能通过传递 valuelabelchildren 的键值映射就配置 OK 了吗,非得每个使用的地方转一遍数据,累...就没爱过

    数据递归处理

    好吧,做完一个项目了,稍微整理整理...


    总得来说,这种数据还是比较好处理的。既然是树结构,其实和 Cascader 组件所要求的数据格式基本类似,无非字段名称不一样,字段可能更多而已。

    比如项目中某个需要展示部分数据:

    [
        {
            "department_id": 1,
            "department_name": "Test",
            "super_department_id": "0",
            "child_departments": [
                {
                    "department_id": "34",
                    "department_name": "修图",
                    "super_department_id": "1",
                    "child_departments": []
                },
                {
                    "department_id": "35",
                    "department_name": "系统研发",
                    "super_department_id": "1",
                    "child_departments": [
                        {
                            "department_id": "48",
                            "department_name": "测试组",
                            "super_department_id": "35",
                            "child_departments": []
                        },
                        {
                            "department_id": "49",
                            "department_name": "产品组",
                            "super_department_id": "35",
                            "child_departments": []
                        },
                        {
                            "department_id": "50",
                            "department_name": "运营",
                            "super_department_id": "35",
                            "child_departments": []
                        },
                        {
                            "department_id": "51",
                            "department_name": "技术开发组",
                            "super_department_id": "35",
                            "child_departments": []
                        }
                    ]
                }
            ]
        }
    ]
    

    那么需要做的转换如下:

    • department_id -> value
    • department_name -> label
    • children -> child_departments

    这个做个简单的递归就解决了,代码、注释如下:

    /**
     * tree 数据转换
     * @param  {Array} tree 待转换的 tree
     * @return {Array}      转换后的 tree
     */
    function convertTree (tree) {
        const result = []
    
        // 遍历 tree
        tree.forEach((item) => {
            // 解构赋值
            let {
                department_id: value,
                department_name: label,
                child_departments: children
            } = item
    
            // 如果有子节点,递归
            if (children) {
                children = convertTree(children)
            }
    
            result.push({
                value,
                label,
                children
            })
        })
    
        return result
    }
    

    最终得到数据如下:

    [
        {
            "value": 1,
            "label": "Test",
            "children": [
                {
                    "value": "34",
                    "label": "修图",
                    "children": []
                },
                {
                    "value": "35",
                    "label": "系统研发",
                    "children": [
                        {
                            "value": "48",
                            "label": "测试组",
                            "children": []
                        },
                        {
                            "value": "49",
                            "label": "产品组",
                            "children": []
                        },
                        {
                            "value": "50",
                            "label": "运营",
                            "children": []
                        },
                        {
                            "value": "51",
                            "label": "技术开发组",
                            "children": []
                        }
                    ]
                }
            ]
        }
    ]
    

    在线演示地址:https://jsfiddle.net/Roam/5xxcjfk8/

    貌似结束了

    其实好像也就那么回事,十来行代码就敲定了。
    但是,回头一想,也不对,每种数据都要写个转换,也是神烦 = =
    好吧,继续优化优化吧...

    其实可以把递归函数再改改:

    /**
     * tree 数据转换
     * @param  {Array} tree 待转换的 tree
     * @param  {Object} map  键值对映射
     * @return {Array}      转换后的 tree
     */
    function convertTree (tree, map) {
        const result = []
    
        // 遍历 tree
        tree.forEach((item) => {
            // 读取 map 的键值映射
            const value = item[ map.value ]
            const label = item[ map.label ]
            let children = item[ map.children ]
    
            // 如果有子节点,递归
            if (children) {
                children = convertTree(children, map)
            }
    
            result.push({
                value,
                label,
                children
            })
        })
    
        return result
    }
    

    就是增加了一个 map 参数,用于指定 valuelabelchildren 的字段映射:

    {
        value: 'department_id',
        label: 'department_name',
        children: 'child_departments'
    }
    

    这样这个递归方法就可以抽出来了,需要转换的地方,调这个方法就行了
    感觉可以提个 feature

    再来个复杂点的数据处理

    在做部门展示权限的时候,遇到个问题,简化如下:

    • 如果一个节点有权限,那么显示该节点,且显示所属的父节点
    • 如果该节点有权限,且该节点有子节点,子节点全部显示

    用图描述一下好了:

    selected-tree.png
    • A 为 root 节点
    • 绿色表示有权限

    需要将上面的转换得到如下 tree 结构:

    filtered-tree.png

    用数据来说话就是:

     [
        {
            "name": "A",
            "children": [
                {
                    "name": "B",
                }, {
                    "name": "C",
                    "children": [
                        {
                            "name": "E",
                            "visible": true
                        }, {
                            "name": "F"
                        }
                    ]
                }, {
                    "name": "D",
                    "visible": true,
                    "children": [
                        {
                            "name": "G"
                        }, {
                            "name": "H"
                        }, {
                            "name": "I"
                        }
                    ]
                }
            ]
        }
    ]
    

    转成:

    [
        {
            "name": "A",
            "children": [
                {
                    "name": "C",
                    "children": [
                        {
                            "name": "E",
                            "visible": true
                        }
                    ]
                }, {
                    "name": "D",
                    "visible": true,
                    "children": [
                        {
                            "name": "G"
                        }, {
                            "name": "H"
                        }, {
                            "name": "I"
                        }
                    ]
                }
            ]
        }
    ]
    

    初看一脸懵逼
    再看还是一脸懵逼....


    细细捋一捋...

    • 遍历树
    • 如果当前节点有权限,塞进来
    • 如果当前节点无权限,并且无子节点,抛弃
    • 如果当前节点无权限,遍历子节点(重复如上)

    嗯~ o( ̄▽ ̄)o,就是这样的...

    这里有个技巧,就是使用 Array.prototype.filter()

     // 原始数据
     const raw = [
        {
            "name": "A",
            "children": [
                {
                    "name": "B",
                }, {
                    "name": "C",
                    "children": [
                        {
                            "name": "E",
                            "visible": true
                        }, {
                            "name": "F"
                        }
                    ]
                }, {
                    "name": "D",
                    "visible": true,
                    "children": [
                        {
                            "name": "G"
                        }, {
                            "name": "H"
                        }, {
                            "name": "I"
                        }
                    ]
                }
            ]
        }
    ]
    
    /**
     * Tree 过滤
     * @param  {Array} tree 待过滤的 tree
     * @return {Array}      已过滤的 tree
     */
    function filterTree (tree) {
        let result = []
    
        // filter 遍历
        result = tree.filter((item) => {
            // 如果有权限
            if (item.visible) {
                return true
    
            // 如果有子节点,递归子节点
            // 如果有权限,返回的值应该为非空数组
            } else if (item.children && item.children.length > 0) {
                item.children = filterTree(item.children)
    
                return item.children.length > 0
    
            // 抛弃
            } else {
                return false
            }
        })
    
        return result
    }
    
    console.log( JSON.stringify(filterTree(raw), null, 4) )
    
    // 打印结果
    // [
    //     {
    //         "name": "A",
    //         "children": [
    //             {
    //                 "name": "C",
    //                 "children": [
    //                     {
    //                         "name": "E",
    //                         "visible": true
    //                     }
    //                 ]
    //             },
    //             {
    //                 "name": "D",
    //                 "visible": true,
    //                 "children": [
    //                     {
    //                         "name": "G"
    //                     },
    //                     {
    //                         "name": "H"
    //                     },
    //                     {
    //                         "name": "I"
    //                     }
    //                 ]
    //             }
    //         ]
    //     }
    // ]
    

    其实也就十来行...

    在线演示链接:https://jsfiddle.net/Roam/5jb0r8y5/

    tree.gif

    总结

    • 递归是个好东西,能省很多代码(让我想起一个面试题...淡淡的忧伤)
    • 代码写得不顺手,肯定哪里有问题
    • 知乎上前后端就是俩冤家

    今年本来打算告别后台管理开发,找个移动开发的工作
    不想局限于 PC,不想成为一个拧螺丝的,不想看见 IE6
    无奈之前做的全是 PC 后台管理项目,没有移动经验,逛了一圈又回去了

    如果只局限于当前的工作内容,也就那样了,以后也就真的那样了

    啊,Hexo 皮肤还没写完啊,许久没动了....


    少年...

    —— 2018/05/29 By Live, Haze.

    相关文章

      网友评论

      本文标题:iView Cascader、Tree 数据处理

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