原文地址: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
總結
遞歸是個好東西,能省很多代碼(讓我想起一個面試題...淡淡的憂傷)
代碼寫得不順手,肯定哪里有問題