原文地址: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
总结
递归是个好东西,能省很多代码(让我想起一个面试题...淡淡的忧伤)
代码写得不顺手,肯定哪里有问题
问题简述
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
总结
递归是个好东西,能省很多代码(让我想起一个面试题...淡淡的忧伤)
代码写得不顺手,肯定哪里有问题