首页 > 技术文章 > [转]iView Cascader、Tree 数据处理

dirgo 2020-02-24 16:12 原文

原文地址:https://www.jianshu.com/p/1daf7d762502

    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 [可选]

发个牢骚

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

话说就不能通过传递 value、label、children 的键值映射就配置 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 参数,用于指定 value、label、children 的字段映射:
{
    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

总结

    递归是个好东西,能省很多代码(让我想起一个面试题...淡淡的忧伤)
    代码写得不顺手,肯定哪里有问题



推荐阅读