需求:
- 多級分類
- 支援多選
- 選擇子節點需要把父節點的值也選中,用于篩選時選中父節點也能篩出子節點
- 分類可以無限級添加子節點
開發
這個需求使用ElementUI, Cascader 級聯選擇器
文檔:
https://element.eleme.cn/#/zh-CN/component/cascader參數設定
// 篩選項
options:[ {
value: "1",
label: "one",
children: [
{
value: "1-1",
label: "one-one"
}
]
}]
// 屬性配置
props: {
checkStrictly: true, // 父子關聯
expandTrigger: 'hover',
multiple: true, // 多選
emitPath: true // true 傳回二維數組,false 傳回一維數組
}
前後端互動問題
1、選擇了子節點誰補全父節點?
(1)前端補全,那麼Cascader需要傳回二維數組,包含完整路徑,後端傳回也需要這個完整路徑
不過,此時後端接收的值是一個一維數組,傳回資料結構溝通後和options一樣的層級結構
(2)後端補全,如果Cascader傳回一個一維數組,隻包含葉子節點,
(2.1 如果後端在存儲時自己添加父節點,資料回顯的時候會出現父節點的值,資料變得不可控
(2.2 如果後端在篩選查詢資料時自己通過子節點擷取父節點,查詢邏輯會變得複雜。
按照經驗來說,資料的查詢次數遠遠大于寫次數
為了查詢邏輯簡單,隻能在編輯時處理。
綜上,需要采用前端補全父節點的方式,需要Cascader傳回一個完整路徑的二維數組
2、資料回顯問題
最簡單的方式是後端增加一個備援參數,按照Cascader傳回的資料格式存儲
後端傳回一個options一樣的層級結構
為了減少和後端的溝通,采用了後端傳回一個options一樣的層級結構
這樣就需要遞歸的處理這個樹形層級結構,代碼如下
// 篩選項
let options = [
{
value: "1",
label: "one",
children: [
{
value: "1-1",
label: "one-one",
children: [
{
value: "1-1-1",
label: "one-one-one"
},
{
value: "1-1-2",
label: "one-one-two"
},
]
},
{
value: "1-1-2",
label: "one-one-two",
children: [
{
value: "1-1-2-1",
label: "one-one-two-one"
},
{
value: "1-1-2-2",
label: "one-one-two-two"
}
]
}
]
}
];
// 實作深拷貝
function deepCopy(obj) {
return JSON.parse(JSON.stringify(obj));
}
// 節點遞歸添加到數組
function treeToArray(gloablList, option, parentList) {
// 先将該節點的值和父節點的拷貝合并
let copyList = deepCopy(parentList);
copyList.push(option.value);
// 如果該節點有子節點,遞歸處理;
// 如果沒有子節點,說明該節點是葉子節點,加入到全局list
if (option.children) {
for (let child of option.children) {
treeToArray(gloablList, child, copyList);
}
} else {
gloablList.push(copyList);
}
}
// 處理清單
function treeTo2dArray(options) {
// 定義一個全局清單,用于存放最後的值
let gloablList = [];
// 處理每一個節點
for (let option of options) {
treeToArray(gloablList, option, []);
}
return gloablList;
}
let ret = treeTo2dArray(options);
console.log(ret);
/**
[
[ '1', '1-1', '1-1-1' ],
[ '1', '1-1', '1-1-2' ],
[ '1', '1-1-2', '1-1-2-1' ],
[ '1', '1-1-2', '1-1-2-2' ]
]
*/