<!--
????/**
?????*?樹形下拉選擇組件,下拉框展示樹形結(jié)構(gòu),提供選擇某節(jié)點(diǎn)功能,方便其他模塊調(diào)用
?????*?@author?wz
?????*?@date?2020-06-09
?????*?調(diào)用示例:
?????*?<tree-select?:height="400"?//?下拉框中樹形高度
?????*??????????????:width="200"?//?下拉框中樹形寬度?不填寫自適應(yīng)el-select?input框大小
?????*??????????????size="small"??//?輸入框的尺寸:?medium/small/mini
?????*??????????????:data="data"?//?樹結(jié)構(gòu)的數(shù)據(jù)或者普通包含主鍵,父主鍵的普通集合
????????????????????:obj="{}"????//可自定義字段,字段映射如下
?????*??????????????multiple???//?多選
????????????????????//默認(rèn)值:單選可傳入數(shù)字,字符串,對象;多選傳入【數(shù)字|字符|對象】數(shù)組,其他非法
????????????????????:default-key="..."
?????*??????????????clearable???//?可清空選擇
?????*??????????????collapseTags???//?多選時將選中值按文字的形式展示
????????????????????expand-click-node???//點(diǎn)擊節(jié)點(diǎn)自動展開。多選有效
????????????????????check-click-node?//?是否點(diǎn)擊節(jié)點(diǎn)是選中?多選生效
?????*??????????????checkStrictly?//?多選時,嚴(yán)格遵循父子不互相關(guān)聯(lián)?效果參考elementui?對應(yīng)屬性效果
?????*??????????????@getValue="父組件獲取值方法">?//?事件有兩個參數(shù):第一個是所有選中的節(jié)點(diǎn)ID,第二個是所有選中的節(jié)點(diǎn)數(shù)據(jù)
?????*??????????????</tree-select>
??????<select-tree
?????obj?字段映射如下,值填寫你實(shí)際字段,可拓展字段。最終返回主鍵以及選擇對象【全部字段】
????????????????????????id:'id',//可改成自己對應(yīng)主鍵【改值】
????????????????????????label:?'name',//?顯示名稱
????????????????????????children:?'children',?//子級字段名
????????????????????????path:'path',//路徑
????????????????????????content:'content',//描述
????????????????????????pid:'pid',//父id
?????*/
-->
<template>
??<div?id="treeSelect">
????<div?class="mask"
?????????v-show="isShowSelect"></div>
????<el-popover?placement="top-start"
????????????????title=""
????????????????trigger="manual"
????????????????v-model="isShowSelect"
????????????????@hide="popoverHide">
??????<div?class="mrb10">
????????<el-input?placeholder="輸入關(guān)鍵字進(jìn)行過濾"
??????????????????v-model="filterText"
??????????????????size="small">
????????</el-input>
??????</div>
??????<el-tree?class="common-tree"
???????????????width="width"
???????????????style="height:300px"
???????????????ref="tree"
???????????????:data="data"
???????????????:props="obj"
???????????????:show-checkbox="multiple"
???????????????:node-key="obj.id"
???????????????:check-strictly="checkStrictly"
???????????????:default-expanded-keys="defaultKey"
???????????????:default-checked-keys="checkKeys"
???????????????:expand-on-click-node="multiple&&expandClickNode"
???????????????:check-on-click-node="checkClickNode"
???????????????:highlight-current="true"
???????????????@check-change="nodeClick"
???????????????:filter-node-method="filterNode"
???????????????@node-click="nodeClick"></el-tree>
??????<el-select?slot="reference"
?????????????????ref="select"
?????????????????:size="size"
?????????????????popper-class="operateDropOption"
?????????????????:popper-append-to-body="true"
?????????????????style="width:?300px"
?????????????????v-model="returnDataKeys"
?????????????????:multiple="multiple"
?????????????????:clearable="clearable"
?????????????????:collapse-tags="collapseTags"
?????????????????@click.native="selectClick"
?????????????????@remove-tag="removeTag"
?????????????????@clear="clean"
?????????????????class="tree-select">
????????<el-option?v-for="item?in?options"
???????????????????:key="item.value"
???????????????????:label="item.label"
???????????????????:value="item.value"></el-option>
??????</el-select>
??????<el-row>
????????<!--?<el-button?v-if="multiple"
???????????????????class="ok"
???????????????????@click="isShowSelect=false"
???????????????????size="mini"
???????????????????type="text">確定</el-button>?-->
??????</el-row>
????</el-popover>
??</div>
</template>
<script>
export?default?{
??name:?"tree-select",
??props:?{
????//?樹結(jié)構(gòu)數(shù)據(jù)
????data:?{
??????type:?Array,
??????default()?{
????????return?[];
??????}
????},
????obj:?{
??????type:?Object,
??????required:?false,
??????default:?()?=>?{
????????return?{
??????????id:?'value',//?ID
??????????label:?'label',//?顯示名稱
??????????children:?'children',?//子級字段名
??????????path:?'path',//路徑
??????????content:?'content',//描述
??????????pid:?'pid',//父id
????????}
??????}
????},
????//?配置是否可多選
????multiple:?{
??????type:?Boolean,
??????default()?{
????????return?true;
??????}
????},
????//?配置是否可清空選擇
????clearable:?{
??????type:?Boolean,
??????default()?{
????????return?false;
??????}
????},
????//?配置多選時是否將選中值按文字的形式展示
????collapseTags:?{
??????type:?Boolean,
??????default()?{
????????return?false;
??????}
????},
????//?顯示復(fù)選框情況下,是否嚴(yán)格遵循父子不互相關(guān)聯(lián)
????checkStrictly:?{
??????type:?Boolean,
??????default()?{
????????return?false;
??????}
????},
????//多選是設(shè)置點(diǎn)擊節(jié)點(diǎn)是否可以選中
????checkClickNode:?{
??????type:?Boolean,
??????default()?{
????????return?false;
??????}
????},
????//多選時:點(diǎn)擊節(jié)點(diǎn)展開還是點(diǎn)三角標(biāo)
????expandClickNode:?{
??????type:?Boolean,
??????default()?{
????????return?false;
??????}
????},
????//?默認(rèn)選中的節(jié)點(diǎn)key
????defaultKey:?{
??????type:?[Number,?String,?Array,?Object],
??????default()?{
????????return?[''];
??????}
????},
????checkKeys:?{
??????type:?[Number,?String,?Array,?Object],
??????default()?{
????????return?[''];
??????}
????},
????size:?{
??????type:?String,
??????default()?{
????????return?'small';
??????}
????},
????width:?{
??????type:?String,
??????default()?{
????????return?'100%';
??????}
????},
????height:?{
??????type:?String,
??????default()?{
????????return?'100%';
??????}
????},
??},
??data()?{
????return?{
??????filterText:?'',
??????popoverWidth:?"300px",//下拉框大小
??????isShowSelect:?false,?//?是否顯示樹狀選擇器
??????options:?[],//select?option選項(xiàng)
??????returnDatas:?[],//返回給父組件數(shù)組對象
??????returnDataKeys:?[],//返回父組件數(shù)組主鍵值
????};
??},
??computed:?{
????//?treeData()?{?//?若非樹狀結(jié)構(gòu),則轉(zhuǎn)化為樹狀結(jié)構(gòu)數(shù)據(jù)
????//???return?JSON.stringify(this.data).indexOf(this.obj.children)?!==?-1???this.data?:?this.switchTree();
????//?},
??},
??mounted()?{
????this.nodeClick()
????addEventListener('click',?(val)?=>?{
??????this.isShowSelect?=?false
????})
??},
??deactivated()?{
????this.clean()
??},
??methods:?{
????init()?{
??????//?eslint-disable-next-line?no-undef,no-debugger
??????//?debugger
??????if?(this.defaultKey?!=?undefined?&&?this.defaultKey.length?>?0)?{
????????if?(this.multiple)?{
??????????//?多選
??????????if?(Object.prototype.toString.call(this.defaultKey).indexOf("Array")?!=?-1)?{
????????????if?(Object.prototype.toString.call(this.defaultKey[0]).indexOf("Object")?!=?-1)?{//對象
??????????????this.setDatas(this.defaultKey);
????????????}?else?if?(Object.prototype.toString.call(this.defaultKey[0]).indexOf("Number")?!=?-1
??????????????||?Object.prototype.toString.call(this.defaultKey[0]).indexOf("String")?!=?-1)?{
??????????????this.setKeys(this.defaultKey);
????????????}?else?{
??????????????//?console.log("多選:傳入?yún)?shù)類型不匹配");
??????????????return;
????????????}
??????????}?else?{
????????????//?console.log("多選:傳入?yún)?shù)類型不匹配");
????????????return;
??????????}
????????}?else?{
??????????//?單選
??????????if?(Object.prototype.toString.call(this.defaultKey).indexOf("Number")?!=?-1
????????????||?Object.prototype.toString.call(this.defaultKey).indexOf("String")?!=?-1
????????????||?Object.prototype.toString.call(this.defaultKey).indexOf("Object")?!=?-1)?{
????????????this.setKey(this.defaultKey);
??????????}?else?{
????????????//?console.log("單選:傳入?yún)?shù)類型不匹配");
????????????return;
??????????}
????????}
??????}
????},
????//下拉框select點(diǎn)擊[入口]
????selectClick()?{
??????this.$nextTick(function?()?{//設(shè)置下拉框自適應(yīng)寬度
????????this.popoverWidth?=?this.$refs.select.$el.clientWidth?-?26;
??????})
??????//顯示下拉框
??????return?this.isShowSelect?=?!this.isShowSelect
????},
????//單選:?樹點(diǎn)擊方法
????nodeClick(data,?node)?{
??????if?(!this.multiple)?{//單選
????????this.isShowSelect?=?false;
????????this.setKey(node.key);
??????}?else?{//多選
????????var?checkedNodes?=?this.$refs.tree.getCheckedNodes(true,?false);?//?所有被選中的節(jié)點(diǎn)
????????var?t?=?[];
????????//?數(shù)組去重
????????const?map?=?new?Map()
????????checkedNodes.forEach((item)?=>?{
??????????if?(!map.has(item['id']))?{
????????????map.set(item['id'],?item)
??????????}
????????})
????????const?newCheckedNodes?=?[...map.values()]
????????var?checkedKeys?=?newCheckedNodes.map(item?=>?item.id)?//?所有被選中的節(jié)點(diǎn)的?key?所組成的數(shù)組數(shù)據(jù)
????????this.options?=?checkedKeys.map((item)?=>?{//設(shè)置option選項(xiàng)
??????????var?node?=?this.$refs.tree.getNode(item);?//?所有被選中的節(jié)點(diǎn)對應(yīng)的node
??????????t.push(node.data);
??????????return?{?label:?node.label,?value:?node.key?};
????????});
????????this.returnDataKeys?=?this.options.map((item)?=>?{
??????????return?item.value;
????????});
????????this.$emit('change',?this.returnDataKeys)
????????this.returnDatas?=?t;
??????}
????},
????filterNode(value,?data)?{
??????if?(!value)?return?true;
??????return?data.label.indexOf(value)?!==?-1;
????},
????//單選:清空選中
????clean()?{
??????this.$refs.tree.setCurrentKey(null);//清除樹選中key
??????this.returnDatas?=?null;?this.returnDataKeys?=?'';
??????this.popoverHide();
??????this.isShowSelect?=?false;
??????this.$refs.tree.setCheckedKeys([]);
??????this.$refs.tree.setChecked([]);
??????//折疊全部節(jié)點(diǎn)
??????for?(let?i?=?0;?i?<?this.$refs.tree.store._getAllNodes().length;?i++)?{
????????this.$refs.tree.store._getAllNodes()[i].expanded?=?false;
??????}
????},
????//單選:設(shè)置、初始化值?key
????setKey(thisKey)?{
??????this.$refs.tree.setCurrentKey(thisKey);
??????var?node?=?this.$refs.tree.getNode(thisKey);
??????this.setData(node.data);
????},
????//單選:設(shè)置、初始化對象
????setData(data)?{
??????this.options?=?[];
??????this.options.push({?label:?data[this.obj.label],?value:?data[this.obj.id]?});
??????this.returnDatas?=?data;
??????this.returnDataKeys?=?data[this.obj.id]
????},
????//多選:設(shè)置、初始化值?keys
????setKeys(thisKeys)?{
??????this.$refs.tree.setCheckedKeys(thisKeys);
??????this.returnDataKeys?=?thisKeys;
??????var?t?=?[];
??????thisKeys.map((item)?=>?{//設(shè)置option選項(xiàng)
????????var?node?=?this.$refs.tree.getNode(item);?//?所有被選中的節(jié)點(diǎn)對應(yīng)的node
????????t.push(node.data);
????????return?{?label:?node.label,?value:?node.key?};
??????});
??????this.returnDatas?=?t;
??????this.popoverHide()
????},
????//多選:設(shè)置、初始化對象
????setDatas(data)?{
??????this.$refs.tree.setCheckedNodes(data);
??????this.returnDatas?=?data;
??????var?t?=?[];
??????data.map((item)?=>?{//設(shè)置option選項(xiàng)
????????t.push(item[this.obj.id]);
??????});
??????this.returnDataKeys?=?t;
??????this.popoverHide()
????},
????//?多選,刪除任一select選項(xiàng)的回調(diào)
????removeTag(val)?{
??????this.$refs.tree.setChecked(val,?false);//設(shè)置為未選中
??????var?node?=?this.$refs.tree.getNode(val);//獲取節(jié)點(diǎn)
??????if?(!this.checkStrictly?&&?node.childNodes.length?>?0)?{
????????this.treeToList(node).map(item?=>?{
??????????if?(item.childNodes.length?<=?0)?{
????????????this.$refs.tree.setChecked(item,?false);
??????????}
????????});
??????}
??????this.nodeClick();
??????this.popoverHide();
????},
????//下拉框關(guān)閉執(zhí)行
????popoverHide()?{
??????this.$emit('getValue',?this.returnDataKeys,?this.returnDatas);
????},
????//?多選,清空所有勾選
????clearSelectedNodes()?{
??????var?checkedKeys?=?this.$refs.tree.getCheckedKeys();?//?所有被選中的節(jié)點(diǎn)的?key?所組成的數(shù)組數(shù)據(jù)
??????for?(let?i?=?0;?i?<?checkedKeys.length;?i++)?{
????????this.$refs.tree.setChecked(checkedKeys[i],?false);
??????}
????},
????//樹形轉(zhuǎn)為集合
????treeToList(tree)?{
??????var?queen?=?[];
??????var?out?=?[];
??????queen?=?queen.concat(tree);
??????while?(queen.length)?{
????????var?first?=?queen.shift();
????????if?(first.childNodes)?{
??????????queen?=?queen.concat(first.childNodes);
????????}
????????out.push(first);
??????}
??????return?out;
????},
????//?switchTree()?{
????//???return?this.buildTree(this.data,?this.defaultValue);
????//?},
????//?將一維的扁平數(shù)組轉(zhuǎn)換為多層級對象
????//?buildTree(data,?id)?{
????//???const?fa?=?(id)?=>?{
????//?????const?temp?=?[];
????//?????for?(let?i?=?0;?i?<?data.length;?i++)?{
????//???????const?n?=?data[i];
????//???????if?(n[this.obj.pid]?===?id)?{
????//?????????n[this.obj.children]?=?fa(n[this.obj.id]);
????//?????????temp.push(n);
????//???????}
????//?????}
????//?????return?temp;
????//???};
????//???return?fa(id);
????//?},
??},
??watch:?{
????//?eslint-disable-next-line?no-unused-vars
????isShowSelect(val)?{
??????//?隱藏select自帶的下拉框
??????if?(!val)?{
????????this.$refs.select.blur();
??????}
????},
????checkKeys:?{
??????handler(newValue,?oldValue)?{
????????this.returnDataKeys?=?newValue;
??????},
??????deep:?true,
??????immediate:?true
????},
????//?treeData()?{//監(jiān)聽tree數(shù)據(jù)
????//???this.$nextTick(()?=>?{
????//?????this.init();
????//???})
????//?},
????filterText(val)?{
??????this.$nextTick(()?=>?{
????????this.$refs.tree.filter(val);
??????})
????}
??}
};
</script>
<style?scoped>
.mask?{
??height:?300px;
??position:?fixed;
??top:?300px;
??left:?0;
??opacity:?0;
??z-index:?11;
}
.common-tree?{
??overflow:?auto;
}
.tree-select?{
??z-index:?111;
}
.ok?{
??float:?right;
}
.el-row?{
??padding-top:?0px?!important;
}
.checkall-box?{
??width:?100%;
??height:?40px;
??line-height:?40px;
??border-bottom:?1px?solid?#fff;
}
.el-popover?{
??min-width:?300px?!important;
}
</style>
<style?lang="less">
.el-popover?{
??min-width:?274px;
}
/*?隱藏默認(rèn)下拉?*/
.operateDropOption.el-select-dropdown__empty?{
??display:?none;
}
.operateDropOption.el-select-dropdown__list?{
??display:?none;
}
.operateDropOption.el-select-dropdown?{
??display:?none;
}
//?滾動條
::-webkit-scrollbar?{
??max-width:?12px;
??height:?8px;
??background-color:?#fff?!important;
}
::-webkit-scrollbar-thumb?{
??border-radius:?8px;
??background-color:?#d8dce0?!important;
??border-style:?solid;
??border-color:?transparent;
??border-width:?2px;
??background-clip:?padding-box;
}
::-webkit-scrollbar-track?{
??border-radius:?12px;
??border-radius:?0?!important;
??background-color:?#fff?!important;
}
</style>
調(diào)用:
<template>
??<tree-select?:height="height"
???????????????width="width"
???????????????size="small"
???????????????:data="dataList"
???????????????:obj="obj"
???????????????multiple
???????????????:default-key="defaultKey"
???????????????:checkKeys="checkKeys"
???????????????clearable
???????????????collapseTags
???????????????expand-click-node
???????????????check-click-node
???????????????@change="change">
??</tree-select>
</template>
<script>
import?TreeSelect?from?'../tree-select/tree-select.vue';
import?{?DeviceService?}?from?"@/service";
export?default?{
??components:?{
????TreeSelect
??},
??data()?{
????return?{
??????defaultKey:?[],
??????dataList:?[],
????};
??},
??props:?{
????value:?[String,?Array],?????????//?選擇的值
????width:?{
??????type:?String,
??????default()?{
????????return?'300px';
??????}
????},
????height:?{
??????type:?String,
??????default()?{
????????return?'200px';
??????}
????},
????obj:?{
??????type:?Object,
??????required:?false,
??????default:?()?=>?{
????????return?{
??????????id:?'id',//?ID
??????????label:?'label',//?顯示名稱
??????????children:?'children',?//子級字段名
??????????path:?'path',//路徑
??????????content:?'content',//描述
??????????pid:?'pid',//父id
????????}
??????}
????},
??},
??watch:?{
????value:?{
??????handler(newValue,?oldValue)?{
????????this.checkKeys?=?newValue;
????????this.defaultKey?=?newValue;
????????this.$emit("input",?newValue);
??????},
??????deep:?true,
??????immediate:?true
????},
??},
??created()?{
????this.getDeviceGroupTree();
??},
??methods:?{
????change(val)?{
??????this.$emit("input",?val);
??????this.$emit("onChange",?val);
????},
????//?獲取機(jī)型組樹形數(shù)據(jù)
????getDeviceGroupTree()?{
??????DeviceService.getDeviceGroupTree().then(res?=>?{
????????if?(res?&&?res.data)?{
??????????this.dataList?=?res.data.data
????????}
??????})
????}
??}
};
</script>
<style??lang="less"?>
</style>