iView Cascader、Tree 数据处理

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.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,755评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,369评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,799评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,910评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,096评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,159评论 3 411
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,917评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,360评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,673评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,814评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,509评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,156评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,123评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,641评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,728评论 2 351

推荐阅读更多精彩内容

  • 背景 一年多以前我在知乎上答了有关LeetCode的问题, 分享了一些自己做题目的经验。 张土汪:刷leetcod...
    土汪阅读 12,740评论 0 33
  • 题量有点多,建议Ctrl + F题号或题目哦~ 二叉树的遍历(前序遍历,中序遍历,后序遍历)[144] Binar...
    野狗子嗷嗷嗷阅读 9,085评论 2 37
  • 废话不多说,亮代码。python执行 耗时:0.011912sPHP执行 耗时:0.002054s结论:PHP效率...
    xhat阅读 5,474评论 3 1
  • 开学以后的每一个月都好长,回头看的时候会发现很多新鲜的情绪,比如思念,比如心疼,比如感动… 四月一日的时候和阿熙特...
    人间甜妞儿阅读 344评论 0 1
  • 好多天没有写日记了,以至于我连日期都记的不是很好。 这段时间也算不上有多努力,而日子就这么一天天过去了。我现在正在...
    muziyue阅读 183评论 0 1