[轉]iView Cascader、Tree 數據處理


原文地址: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

總結

    遞歸是個好東西,能省很多代碼(讓我想起一個面試題...淡淡的憂傷)
    代碼寫得不順手,肯定哪里有問題




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM