Element-UI控件Tree實(shí)現(xiàn)數(shù)據(jù)樹形結(jié)構(gòu)的方法
在前端開發(fā)中,有時(shí)會(huì)遇到所有菜單數(shù)據(jù)在同一級(jí)的情況,后端未對(duì)數(shù)據(jù)進(jìn)行分級(jí)處理;但前端渲染需要是樹狀結(jié)構(gòu)的數(shù)據(jù),如何實(shí)現(xiàn)數(shù)據(jù)的樹狀化?將數(shù)組中通過父節(jié)點(diǎn)的ID與子節(jié)點(diǎn)的parentId關(guān)聯(lián),通過遞歸函數(shù)來實(shí)現(xiàn)。
前端框架這里使用element-ui的tree控件來實(shí)現(xiàn),對(duì)其不了解可以去官網(wǎng)查看文檔。
地址:Element - The world's most popular Vue UI framework
一、創(chuàng)建頁面
這里就不講vue項(xiàng)目的搭建了,基礎(chǔ)不好的,可以去官網(wǎng)查看文檔。
首先在src/pages目錄中,創(chuàng)建element-trees文件夾,再創(chuàng)建index.vue,代碼如下:
<template> <div class="tree-container"> </div> </template> <script> export default { data(){ return {} }, created() {}, methods: {} } </script> <style lang="scss"> </style>
在類選擇器tree-container容器中,添加相關(guān)按鈕和Tree控件,以及給容器添加些樣式進(jìn)行修飾一下,代碼如下:
<template> <div class="tree-container"> <div class="top-box"> <el-button type="primary" size="mini" @click="appendNode">添加菜單</el-button> <el-button type="info" size="mini" @click="removeSelected">刪除選中項(xiàng)</el-button> </div> <el-tree ref="tree" :data="treeData" :props="props" accordion show-checkbox node-key="id" default-expand-all :expand-on-click-node="false"> <span class="custom-tree-node" slot-scope="{ node, data }"> <span>{{ node.label }}</span> <span> <el-button type="text" size="mini" @click="append(data)">添加</el-button> <el-button type="text" size="mini" @click="editor(data)">修改</el-button> <el-button type="text" size="mini" @click="remove(node, data)">刪除</el-button> </span> </span> </el-tree> </div> </template> <script> export default { data(){ return { props: { label: 'name', children: 'children' }, treeData: [] } }, created() {}, methods: { //添加父節(jié)點(diǎn)數(shù)據(jù) appendNode(){}, //添加子項(xiàng)數(shù)據(jù) append(data){}, //編輯數(shù)據(jù) editor(data){}, //移出項(xiàng)目 remove(node, data){}, //刪除選中的節(jié)點(diǎn) removeSelected(){} } } </script> <style lang="scss"> .tree-container{ width: 360px; font-size: 12px; .top-box{ text-align: right; padding-bottom: 20px; } } .custom-tree-node { flex: 1; display: flex; align-items: center; justify-content: space-between; font-size: 14px; padding-right: 8px; } </style>
此時(shí)頁面效果如下:
二、模擬數(shù)據(jù)
在element-trees文件夾中添加data.js文件,用來存儲(chǔ)模擬數(shù)據(jù)。代碼如下:
<template> <div class="tree-container"> <div class="top-box"> <el-button type="primary" size="mini" @click="appendNode">添加菜單</el-button> <el-button type="info" size="mini" @click="removeSelected">刪除選中項(xiàng)</el-button> </div> <el-tree ref="tree" :data="treeData" :props="props" accordion show-checkbox node-key="id" default-expand-all :expand-on-click-node="false"> <span class="custom-tree-node" slot-scope="{ node, data }"> <span>{{ node.label }}</span> <span> <el-button type="text" size="mini" @click="append(data)">添加</el-button> <el-button type="text" size="mini" @click="editor(data)">修改</el-button> <el-button type="text" size="mini" @click="remove(node, data)">刪除</el-button> </span> </span> </el-tree> </div> </template> <script> export default { data(){ return { props: { label: 'name', children: 'children' }, treeData: [] } }, created() {}, methods: { //添加父節(jié)點(diǎn)數(shù)據(jù) appendNode(){}, //添加子項(xiàng)數(shù)據(jù) append(data){}, //編輯數(shù)據(jù) editor(data){}, //移出項(xiàng)目 remove(node, data){}, //刪除選中的節(jié)點(diǎn) removeSelected(){} } } </script> <style lang="scss"> .tree-container{ width: 360px; font-size: 12px; .top-box{ text-align: right; padding-bottom: 20px; } } .custom-tree-node { flex: 1; display: flex; align-items: center; justify-content: space-between; font-size: 14px; padding-right: 8px; } </style>
文件創(chuàng)建好后,我們將其引入到index.vue文件中,代碼如下:
<script> import DataList from './data' export default { data(){ return { props: { label: 'name', children: 'children' }, treeData: [] } }, created() { this.loadNode(); }, methods: { //加載節(jié)點(diǎn)數(shù)據(jù) loadNode(){ this.treeData = DataList.map(item => item); }, //略... } } </script>
如上代碼,通過loadNode()函數(shù)執(zhí)行后,模擬數(shù)據(jù)則已與Tree控件進(jìn)行了雙方綁定。但是頁面中顯示的數(shù)據(jù),全都顯示在同級(jí)位置。數(shù)據(jù)的歸類分級(jí),我們?cè)诤竺嬖僦v解。頁面效果如下:
三、遞歸函數(shù)
此時(shí)離成功緊一步之遙了,我們先來分析下data.js中的數(shù)據(jù)結(jié)構(gòu)。如下圖:
如何將數(shù)據(jù)歸類分級(jí)呢,可以通過循環(huán)判斷,每級(jí)的id與數(shù)據(jù)中的pid進(jìn)行關(guān)聯(lián);比如第一條數(shù)據(jù)id為1,將數(shù)組進(jìn)行遍歷查詢判斷pid有為1的數(shù)據(jù),則表示其有子項(xiàng)數(shù)據(jù),將其子項(xiàng)數(shù)據(jù)添加到children數(shù)組中。pid為0的為一級(jí)數(shù)據(jù),所以當(dāng)定義遞歸函數(shù)時(shí),傳入的pid默認(rèn)為0。
這里層次有可能是1層,2層,3層...... n層,要一層一層循環(huán)判斷,就太麻煩了;所以這里我們要使用遞歸方法,來實(shí)現(xiàn)n層級(jí)的數(shù)據(jù)處理。
在src下創(chuàng)建utils/utils.js文件,在utils.js中定義遞歸函數(shù),代碼如下:
/** * 遞歸函數(shù) * @param arr 數(shù)組 * @param pid 父ID,不傳默認(rèn)為0 */ export const DGFilterTrees = (arr, pid = 0) => { let newArr = []; //循環(huán)數(shù)組 arr.forEach((item, i) => { //判斷pid與遍歷元素中pid相同的數(shù)據(jù),相等的為同級(jí)數(shù)據(jù),追加到該級(jí)數(shù)組中 if(item.pid == pid){ newArr.push(item); /** * 判斷該item元素是否有子項(xiàng) * 當(dāng)數(shù)組中有元素的pid與item.id相等,some函數(shù)會(huì)返回true,表示該元素有子項(xiàng)數(shù)據(jù) */ if(arr.some(ele => ele.pid == item.id)){ //此時(shí),通過重新調(diào)用本函數(shù)進(jìn)行遞歸處理 item['children'] = DGFilterTrees(arr, item.id); } } }); return newArr; }
注:遞歸算法是一種直接或者間接調(diào)用自身函數(shù)或者方法的算法。
遞歸算法的特點(diǎn):
- 遞歸就是方法里調(diào)用自身。
- 在使用遞增歸策略時(shí),必須有一個(gè)明確的遞歸結(jié)束條件,稱為遞歸出口。
- 遞歸算法解題通常顯得很簡(jiǎn)潔,但遞歸算法解題的運(yùn)行效率較低。所以一般不提倡用遞歸算法設(shè)計(jì)程序。
- 在遞歸調(diào)用的過程當(dāng)中系統(tǒng)為每一層的返回點(diǎn)、局部量等開辟了棧來存儲(chǔ)。遞歸次數(shù)過多容易造成棧溢出等,所以一般不提倡用遞歸算法設(shè)計(jì)程序。
所以在使用遞歸算法時(shí),一定要注意判斷條件能正常退出,否則無法靠自身的控制終止的循環(huán),會(huì)導(dǎo)致棧溢出,程序卡死等問題。
將DGFilterTrees()遞歸函數(shù)引入到index.vue中,將模擬數(shù)據(jù)重組,代碼如下:
<script> import DataList from './data' import DGFilterTrees from '@/utils/utils.js' export default { data(){ return { props: { label: 'name', children: 'children' }, treeData: [] } }, created() { this.loadNode(); }, methods: { //加載節(jié)點(diǎn)數(shù)據(jù) loadNode(){ this.treeData = DGFilterTrees(DataList); }, //略... } } </script>
此時(shí),所有數(shù)據(jù)都進(jìn)行了歸類分級(jí)顯示,頁面的效果圖如下:
四、選中后顯示按鈕
如上圖,我們會(huì)發(fā)現(xiàn)每項(xiàng)后面的添加、修改、刪除全顯示出來,會(huì)顯示擁擠并不方便操作。這里我們將節(jié)點(diǎn)輸出后會(huì)發(fā)現(xiàn),可以使用isCurrent來判斷,只顯示選中當(dāng)前項(xiàng)的按鈕。
在span標(biāo)簽上添加v-if,判斷node.isCurrent是否為true,代碼如下:
<el-tree ref="tree" :data="treeData" :props="props" accordion show-checkbox node-key="id" default-expand-all :expand-on-click-node="false"> <span class="custom-tree-node" slot-scope="{ node, data }"> <span>{{ node.label }}</span> <span v-if="node.isCurrent"> <el-button type="text" size="mini" @click="append(data)">添加</el-button> <el-button type="text" size="mini" @click="editor(data)">修改</el-button> <el-button type="text" size="mini" @click="remove(node, data)">刪除</el-button> </span> </span> </el-tree>
如下圖,點(diǎn)擊選擇”后端基礎(chǔ)SQL“后,會(huì)顯示后面的操作按鈕。
五、刪除元素
這里我們是使用本地定義模擬數(shù)據(jù)進(jìn)行操作的,所以沒有真實(shí)項(xiàng)目中是通過接口進(jìn)行數(shù)據(jù)的增刪改查部分。代碼如下:
//移出項(xiàng)目 remove(node, data){ if(Array.isArray(data['children'])&&data.children.length>0){ this.$message.error('當(dāng)前數(shù)據(jù)有子項(xiàng),無法刪除~'); return; } //查詢數(shù)據(jù)位置索引 let index = DataList.findIndex(item => item.id==data.id); //刪除指定位置數(shù)據(jù) DataList.splice(index, 1); //重新加載數(shù)據(jù) this.loadNode(); }
效果如下圖:
六、刪除多選項(xiàng)
這里需要使用Tree控件的getCheckedNodes()函數(shù),來獲取被選中項(xiàng);然后通過循環(huán),刪除每條被選中元素,代碼如下:
//刪除選中的節(jié)點(diǎn) removeSelected(){ //獲取選中的數(shù)據(jù) let checks = this.$refs.tree.getCheckedNodes(); //循環(huán)刪除選中 checks.forEach(item => { DataList.splice(DataList.findIndex(sub => sub.id == item.id), 1); }); //重新加載數(shù)據(jù) this.loadNode(); }
效果如下圖:
本期就先介紹到這,添加、修改等功能也比較簡(jiǎn)單,可以通過element-ui的$prompt彈框控件來實(shí)現(xiàn)。
到此這篇關(guān)于Element-UI控件Tree實(shí)現(xiàn)數(shù)據(jù)樹形結(jié)構(gòu)的文章就介紹到這了,更多相關(guān)Element-UI數(shù)據(jù)樹形結(jié)構(gòu)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
在vue中v-for循環(huán)遍歷圖片不顯示錯(cuò)誤的解決方案
這篇文章主要介紹了在vue中v-for循環(huán)遍歷圖片不顯示錯(cuò)誤的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01總結(jié)4個(gè)方面優(yōu)化Vue項(xiàng)目
在本篇文章里我們給大家整理了一篇關(guān)于優(yōu)化VUE項(xiàng)目的四個(gè)總要點(diǎn),對(duì)此有需要的朋友們學(xué)習(xí)下天。2019-02-02vue數(shù)據(jù)監(jiān)聽解析Object.defineProperty與Proxy區(qū)別
這篇文章主要為大家介紹了vue數(shù)據(jù)監(jiān)聽解析Object.defineProperty Proxy源碼示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03vue中axios的get請(qǐng)求和post請(qǐng)求的傳參方式、攔截器示例代碼
Post是向服務(wù)器提交數(shù)據(jù)的一種請(qǐng)求,get是向服務(wù)器發(fā)索取數(shù)據(jù)的一種請(qǐng)求,post在真正接受數(shù)據(jù)之前會(huì)先將請(qǐng)求頭發(fā)送給服務(wù)器進(jìn)行確認(rèn),然后才真正發(fā)送數(shù)據(jù),本文給大家介紹vue中axios的get請(qǐng)求和post請(qǐng)求的傳參方式、攔截器示例代碼,感興趣的朋友一起看看吧2023-10-10關(guān)于Vue Router中路由守衛(wèi)的應(yīng)用及在全局導(dǎo)航守衛(wèi)中檢查元字段的方法
這篇文章主要介紹了關(guān)于Vue Router中路由守衛(wèi)的應(yīng)用及在全局導(dǎo)航守衛(wèi)中檢查元字段的方法,實(shí)現(xiàn)方法有兩種,本文通過實(shí)例代碼對(duì)每種方法介紹的很詳細(xì),需要的朋友參考下2018-12-12vue2實(shí)現(xiàn)封裝動(dòng)態(tài)表單組件
這篇文章主要介紹了vue2實(shí)現(xiàn)封裝動(dòng)態(tài)表單組件,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-08-08