要實現的功能截圖:
要求:
1、點擊收件人輸入框可以根據拼音自動篩選數據,並且標記已經選擇的數據,沒有結果的時候提示,相應的更新左邊樹節點狀態
2、勾選樹右側樹的節點左側輸入框出現一一對應的節點名稱
用到的插件
vue+chosen+ztree
vue:組件化的MVVM庫
chosen:基於jquery的單選列表和多選列表增強插件
ztree:基於jquery的樹插件
分析
chosen插件已經可以實現要求1中的大部分效果,我們只需要預先提供chosen需要的數據,通過輸入框值的變化實現左右兩側數據一 一對應,最後點擊發送獲取最終的數據集合ID
具體實現:
一、左側選擇數據,右側樹節點更新
chosen需要的html結構
//只需要提供包含數據的select標籤即可,該select默認隱藏,chosen依據該數據構建新的html結構
<select name="dept" style="width:150px;" id="dept" class="dept_select" multiple="multiple">
//loop start
<option value="部門1">部門1</option>
<option value="部門2">部門2</option>
<option value="部門3">部門3</option>
//loop end
</select>
//chosen初始化
$(function(){
$('.dept_select').chosen({
no_results_text:'沒有結果',
allow_single_deselect:true
});
});
這樣要求1中的大部分效果就實現了,option裏面的數據我們需要通過接口從後臺獲取,這裏採用vue解析數據,相應的html結構和js爲:
//基於vue解析的html結構
<select data-placeholder="選擇發件人" class="chosen-select form-control" tabindex="-1" multiple="multiple">
<template v-for='key in zmailTree'>
<option v-for='item in key.userList' value='{{item.id}}'>{{item.name}}</option>
</template>
</select>
//vue實例
var zmailForm=new Vue({
el:'#zmail-form',
ready:function(){
var that=this;
var getToken=$.cookie('dcValidate');
$.ajax({
type:'get',
async:false,
url:'後臺數據接口地址',
dataType: "json",
success: function(msg){
that.$set('zmailTree', msg);
}
});
},
data:{
zmailTree:[]
}
});
//總結:通過vue獲取後臺數據,將json數據賦值給zmailTree這個數組,它是含有層級結構的,我們不需要輸出層級結構,只需要輸出裏面的人員就行了,但是實踐中發現一個人問題,數據解析了,鼠標點擊輸入框出現的下拉列表中並沒有出現我們剛纔解析出來的數據,我們需要VUE的Vue.nextTick方法,延遲迴調chosen初始化代碼:
//延遲初始化chosen
Vue.nextTick(function () {
$('#zmail-select').chosen({
no_results_text: '沒有找到該結果',// 當檢索時沒有找到匹配項時顯示的提示文本
search_contains: true //從任意位置開始檢索
});
});
然後出現了下面的效果:
接下來我們要做的就是勾選與左側選擇的數據相對應的節點。chosen提供了一個change方法,該方法當選擇的值發生改變時觸發,有這個方法我們就很容易根據select值的變化來勾選右側樹的節點
$('select.chosen-select').on('change', function(){
// 用戶改變了選擇,快快處理
});
我們同樣要寫到Vue.nextTick中,其中涉及到樹的操作請參照zTree API
//延遲初始化chosen
Vue.nextTick(function () {
$('#zmail-select').chosen({
no_results_text: '沒有找到該結果',// 當檢索時沒有找到匹配項時顯示的提示文本
search_contains: true //從任意位置開始檢索
});
$('#zmail-select').on('change', function(){
//用戶改變了值之後作如下處理
var treeObj = $.fn.zTree.getZTreeObj("zmail-tree");//獲取目標容器
treeObj.expandAll(true);//展開所有樹節點
treeObj.checkAllNodes(false);//清空所有樹節點
$("#zmail-select option:selected").each(function(i,obj){//循環選擇出來的數據
//根據節點數據的ID屬性搜索,獲取條件完全匹配的節點數據,注:option中解析的是ID
var node = treeObj.getNodeByParam("id", obj.value, null);
//將選出的節點全部勾選
treeObj.checkNode(node, true, true);
});
});
});
//總結:輸入框的值只要發生了改變option的狀態就會自動更新,我們根據已選中的option的value(即ID),加上ztree已有的getNodeByParam方法來獲取與左側數據ID一一對應的樹中的節點,然後通過checkNode方法勾選這些節點
到這裏要求1中的效果我們就全部實現了。
二、勾選樹節點,左側輸入框數據更新
//樹的html結構,不要忘記寫ztree這個class,否則不顯示數據
<ul class="ztree" id="zmail-tree">
</ul>
//--------樹初始化代碼js-----------
//人員樹基本設置
var zmailTreeSet={
view:{
dblClickExpand:false
},
async:{
enable:true,
type:'get',
url:'服務器數據地址',
},
data:{
simpleData:{
enable:true,
idKey:'id',
pIdKey:'parentId'
},
key:{
children:'userList'
}
},
check:{
enable:true,
chkboxType:{'Y':'s','N':'s'}
},
callback:{
onCheck:zmailCheck
}
};
//勾選節點之後要處理的回調函數
function zmailCheck(){
}
//初始化人員樹
$.fn.zTree.init($('#zmail-tree'),zmailTreeSet);
zmailCheck方法中實現的思路
(1)首先獲取點擊複選框的節點
var zmaObj = $.fn.zTree.getZTreeObj(treeId);//getZTreeObj插件方法,獲取目標ID
var zmaNodes = zmaObj.getCheckedNodes(true);//getCheckedNodes獲取輸入框被勾選的節點集合
(2)其次清空select中的選中狀態,將其恢復到初始狀態,
$("#zmail-select option").each(function(j,obj){
obj.selected='';
});
(3)根據勾選的節點集合使select中的相應option選中,此處判斷的關鍵是數據id
for(var i = 0;i < zmaNodes.length; i++){
if(typeof(zmaNodes[i].userList) == 'undefined'){//如果該節點的userList屬性爲空說明不是父節點,存取它的值,如果不爲空則跳過
//不寫判斷,直接使用勾選的樹的數據的ID來進行選擇,使其屬性select改爲selected
//$('').attr('selected',true)或者.attr('selected','selected')不起作用不知道爲啥
$('#zmail-select option[value="+zmaNodes[i].id+"]')[0].selected='selected';
}
}
(4)更新select option列表
//循環外更新select列表
$("#zmail-select").trigger('chosen:updated');
完整的zmailCheck()代碼爲:
function zmailCheck(){
var zmaObj = $.fn.zTree.getZTreeObj(treeId);
var zmaNodes = zmaObj.getCheckedNodes(true);
$("#zmail-select option").each(function(j,obj){
obj.selected='';
});
for(var i = 0;i < zmaNodes.length; i++){
if(typeof(zmaNodes[i].userList) == 'undefined'){
$("#zmail-select option[value="+zmaNodes[i].id+"]")[0].selected='selected';
}
}
$("#zmail-select").trigger('chosen:updated');
}
至此要求2的效果我們也實現了。
總結
這個功能的完成離不開對zTree和chosen這兩個插件的熟練使用,chosen用到的API爲change方法、chosen:updated方法,ztree用到的方法爲:getNodeByParam(),checkNode(),getCheckedNodes(),checkAllNodes(),expandAll()。
實現左側=》右側功能關鍵點:選出option的selected狀態id,通過getNodeByParam()獲取含有該ID的所有節點,然後使用checkNode()使這些節點的狀態變爲選中;
右側=》左側實現的關鍵點:通過getCheckedNodes()獲取所有選中的節點,根據選中的節點ID使左側的select下的option selected狀態變爲true,循環結束後(’chosen:updated’)更新option列表。
這個功能還有不完美的地方,那就是獲取後臺數據那裏,我說過後臺數據是有層級的,我現在只循環了兩次,只取出了二級列表,如果層級有三級四級五級那獲取的還是二級列表,vue我也剛開始學,不知道循環後臺數據那裏如何一勞永逸的獲取不帶層級的所有後續動態添加的子級列表。
爲了便於討論給大家看下我從後臺獲取的json數據:
[
{
"id": "20160912113609940493862810692577",
"name": "xx公司",
"parentId": "0",
"userList": [
{
"id": "20160912113613185306623590148664",
"name": "演示帳號"
},
{
"id": "20160912113613154939672576045528",
"name": "成員1"
},
{
"id": "20160919121104625924937109002003",
"name": "成員2"
}
]
},
{
"id": "20160912113611625082971312564873",
"name": "組1",
"parentId": "20160912113609940493862810692577",
"userList": [
{
"id": "20160912113613138161662196502779",
"name": "成員1"
},
{
"id": "20160912113613044493434182821910",
"name": "成員2"
},
{
"id": "20160912113613169436263538841431",
"name": "成員3"
},
{
"id": "20160912113613169255388288458627",
"name": "成員4"
},
{
"id": "20160912113613185135119969199907",
"name": "成員5"
}
]
},
{
"id": "20160912113611625563131574216031",
"name": "組2",
"parentId": "20160912113609940493862810692577",
"userList": [
{
"id": "20160912113613044536244270423866",
"name": "成員1"
},
{
"id": "20160912113613044570977235101231",
"name": "成員2"
},
{
"id": "20160912113613044945364721175243",
"name": "成員3"
},
{
"id": "20160912113613138666099077077505",
"mobileNum": "000256364",
"name": "成員4"
},
{
"id": "20160912113613154114377983132349",
"name": "成員5"
},
{
"id": "20160912113613169433129887352698",
"mobileNum": "0004210919",
"name": "成員6"
},
{
"id": "20160912113613169883649070173455",
"name": "成員7"
},
{
"id": "20160912113613185006644025528363",
"name": "成員8"
},
{
"id": "20160912113613185400355507045112",
"name": "成員9"
}
]
},
{
"id": "20160919115007372634170390528661",
"name": "組3",
"parentId": "20160912113609940493862810692577",
"userList": [
{
"id": "20160914151121249285889869855783",
"name": "成員1"
},
{
"id": "20160912113613060689705413198400",
"name": "成員2"
},
{
"id": "20160912113613060223674583460143",
"name": "成員3"
},
{
"id": "20160912113613154692470389028360",
"mobileNum": "00032549532",
"name": "成員4"
}
]
}
]