做后端管理系統,永遠是最蛋疼、最復雜也最欠揍的事情,也永遠是前端開發人員最苦逼、最無奈也最尿性的時刻。蛋疼的是需求變幻無窮,如同二師兄的三十六般變化;復雜的是開發難度寸步難行,如同蜀道難,難於上青天;欠揍的是產品隨心所欲、為所欲為,如同村霸橫行鄉里、只手遮天;苦逼的是前端苦不堪言,如同啞巴吃黃連,有苦說不出;無奈的是前端無可奈何花落去,如同至尊寶戴上金箍無法愛你,摘下金箍無法救你;尿性的是前端苦盡甘來,如同唐僧師徒歷經九九八十一難,終成正果的高光時刻!
又特么的南轅北轍了,矯情個鳥啊!有需求,上啊,很復雜,想啊,開發周期短,擼起袖子加油干啊(真特么的下流,一天到晚都是上啊,干啊,你特么泰迪啊)!好,閑話少敘,言歸正傳,咱們上一次講到白眉大俠徐良...(你給我滾犢子)
需求是這樣的,有一個樹形控件,默認所有節點都不選中。
PS:如果貴司的需求沒有那么復雜,就可以跳過以下內容,直接使用element-ui框架提供的tree樹形控件及API即可。
要求:
1、選中父節點,子節點及孫子節點(甭管有多少層級,茲要是子輩節點)可以不選中,取消選中父節點,子節點及孫子節點也還是沒有任何變化;
2、選中子節點或孫子節點,必須選中父節點及祖父節點(甭管有多少層級,茲要是祖輩節點),取消選中子節點或孫子節點,父節點及祖父節點可以不取消選中;
3、基於需求2,此時子節點及孫子節點和父節點、祖父節點都已選中,那么若取消選中父節點,則子節點及孫子節點必須取消選中,但父節點的父節點可以不取消選中,若取消選中祖父節點,則其下邊的所有節點都要取消選中。
看了需求,沒有實操,是不是有點懵啊?第一次接到這樣的需求且網上沒有這樣的解決方案,是不是無從下手啊?
來看看實際的效果吧:https://runjs.cn/code/lu9nsctl
看了效果,是不是恍然大悟?緊接着是不是就覺得既然在某種程度上父子節點沒有關聯關系,那還要這種樹形控件干嘛?恭喜你,抓住重點了!
首先,為什么我本人要用這種控件,因為我們的后台管理系統就是用的vue+element-ui框架,而且element-ui提供了這種樹形控件的很多方法,比如選中子節點后要同時獲取到該子節點的id以及其父節點和祖父節點的id,再比如通過接口返回的數據回顯已經選中的所有節點,使用起來很方便的,只是需要熟悉它的API並靈活運用罷了。如果你沒有用element-ui框架或者你想自己實現這樣的效果,也未嘗不可,只是在開發周期比較短的情況下,你要自己去寫html、js、css,暫且不說js實現起來有多費勁,單說html和css就夠你喝一壺的。當然,我也不是說不提倡自己去寫,在時間允許的情況下自己寫也可以,自己實現出來的,很能提高自己的js能力和業務邏輯的!
接下來就是具體的實現代碼了,這篇文章的解決方案是基於vue+element-ui的。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<style>
.el-tree-node__label{
position: relative;
}
.el-tree-node__label:before{
content:'';
width:20px;
height: 20px;
display: block;
position:absolute;
top:0px;
left:-24px;
z-index:999;
}
.el-checkbox__inner{
top:0;
}
</style>
<title>element-ui樹形控件</title>
</head>
<body>
<div id="app">
<el-tree
:data="treeData"
show-checkbox
node-key="id"
check-on-click-node
:default-expand-all="true"
:check-strictly="true"
:expand-on-click-node="false"
@node-click="nodeClick"
>
</el-tree>
</div>
<script>
let vm = new Vue({
el: '#app',
data: {
treeData: [{
id: 1,
label: '一級 1',
children: [{
id: 4,
label: '二級 1-1',
children: [{
id: 9,
label: '三級 1-1-1',
children: [{
id: 11,
label: '四級 1-1-1',
}]
}, {
id: 10,
label: '三級 1-1-2'
}]
}]
}, {
id: 2,
label: '一級 2',
children: [{
id: 5,
label: '二級 2-1'
}, {
id: 6,
label: '二級 2-2'
}]
}, {
id: 3,
label: '一級 3',
children: [{
id: 7,
label: '二級 3-1'
}, {
id: 8,
label: '二級 3-2'
}]
}]
},
methods: {
nodeClick(data, node){
vm.childNodesChange(node);
vm.parentNodesChange(node);
},
childNodesChange(node){
let len = node.childNodes.length;
for(let i = 0; i < len; i++){
node.childNodes[i].checked = false;
vm.childNodesChange(node.childNodes[i]);
}
},
parentNodesChange(node){
if(node.parent){
for(let key in node){
if(key == "parent"){
node[key].checked = true;
vm.parentNodesChange(node[key]);
}
}
}
}
}
})
</script>
</body>
</html>
注意:
1、關於代碼中element-ui樹形控件的API可自行查閱官方文檔,這里不做贅述;
2、控件中使用了check-on-click-node
這個屬性,官方文檔的解釋是:是否在點擊節點的時候選中節點,默認值為 false,即只有在點擊復選框時才會選中節點。為什么要使用這個屬性呢?
因為在點擊復選框時,雖然官方文檔給了一個check
的方法,解釋是:當復選框被點擊的時候觸發,其有兩個參數,依次為:傳遞給 data 屬性的數組中該節點所對應的對象、樹目前的選中狀態對象,包含 checkedNodes、checkedKeys、halfCheckedNodes、halfCheckedKeys 四個屬性,但依舊無法實現我想要的效果。
這三張圖就是check
方法打印出來的效果,打印出來的參數的屬性中根本就沒有可供設置為true或false的checked,更不包含父節點,這怎么玩?還怎么設置父節點?
好在控件有node-click
這個方法,官方解釋是:節點被點擊時的回調,其有三個參數,依次為:傳遞給 data 屬性的數組中該節點所對應的對象、節點對應的 Node、節點組件本身。正好這個方法的第二個參數Node就是我想要的,因為既然某種程度上父子節點無關聯,那么要想獲得當前節點的父節點和子節點,就只能通過第二個參數Node去獲取了。這里具體的參數的打印效果我就不展示了。
其實這里還有一個問題,就是如果沒有下邊的樣式:
<style>
.el-tree-node__label{
position: relative;
}
.el-tree-node__label:before{
content:'';
width:20px;
height: 20px;
display: block;
position:absolute;
top:0px;
left:-24px;
z-index:999;
}
.el-checkbox__inner{
top:0;
}
</style>
那么只點擊復選框時,由於上邊已說明的問題,還是無法實現需求,所以既然節點被點擊時可以實現選中對應的復選框並實現需求,那么就可以用節點的:before
樣式來覆蓋在checkbox控件上,這樣就等於模擬了點擊復選框,就可以完美的實現需求想要的效果了!
需求有點復雜,也寫了這么多,並在文中伴有實操demo,相信各位童鞋都能看懂。
本文系原創,轉載請注明版權,謝謝!