项目需求,需要选择树,针对人员选择、部门选择、公司选择、多选或者单选,并且很多地方都用得到,刚舒舒服服摸鱼没几天,又来,那封呗,一劳永逸。使用简单,同样只需要传递对应的接口数据及参数,其他就不用你操心了。
先上效果
回显部分
选择部分
代码部分
index.vue
<template>
<div class="SelectionTree" :style="{ width: width }">
<!-- 选择树部分 -->
<template>
<div
v-show="lable"
style="padding: 6px 0px;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;"
>
{{ lable }}
</div>
<el-select
class="autoWidth"
style="width: 100%;"
v-model="echoValue"
:multiple="multiple"
placeholder="请选择"
:collapse-tags="false"
popper-class="popper-class"
:popper-append-to-body="false"
@change="SelectionTreeChange"
@focus="selectClick"
@remove-tag="removeTag"
>
<el-option
v-for="item in options"
:key="item[optionsProps.value]"
:label="item[optionsProps.label]"
:value="item[optionsProps.value]"
>
</el-option>
</el-select>
</template>
<!-- 弹窗部分 -->
<template>
<Dialogbox
width="50%"
:title="title || ''"
:dialogVisible="RenewalShow"
v-if="RenewalShow"
@confirm="confirm"
@cancel="cancel"
@close="close"
:customFooter="true"
>
<template slot="content">
<div style="display: flex;justify-content: space-between;">
<div class="hb">
<el-input
class="treeSearch"
placeholder="输入关键字进行搜索"
v-model="filterText"
@change="filterChange"
/>
<div class="hb-c" style="height:460px;">
<el-tree
:data="data"
show-checkbox
ref="tree"
:check-strictly="true"
class="filter-tree"
@check-change="checkChange"
:highlight-current="true"
:props="defaultProps"
:default-expanded-keys="defaultExpandedArr"
:default-checked-keys="defaultCheckedArr"
:filter-node-method="filterNode"
:node-key="nodekey"
@check="handleCheckChange"
>
</el-tree>
</div>
</div>
<div class="hb">
<div style="height:504px;overflow-y: scroll;">
<ul style="padding: 4px;overflow: hidden;">
<li
class="hb-li"
v-for="(item, index) in checkedData"
:key="index"
style="padding: 4px 8px 4px 4px;overflow: hidden;"
>
<div
style="display: flex; justify-content: space-between;line-height: 16px !important;"
>
<span style="font-weight: bold;">{{
item[defaultProps.label]
}}</span>
<i
class="el-icon-delete"
style="cursor: pointer;"
title="删除"
@click="treeDel(item)"
></i>
</div>
</li>
</ul>
<div v-show="checkedData.length == 0">
<el-empty description="暂无数据,请先勾选"></el-empty>
</div>
</div>
</div>
</div>
</template>
<template slot="footer">
<el-button
v-for="(item, index) in butArr"
:key="index"
:size="item.size"
:type="item.type"
:icon="item.icon"
:class="item.class"
@click="item.clickFun"
>{{ item.text }}</el-button
>
</template>
</Dialogbox>
</template>
</div>
</template>
<script>
import Dialogbox from "@/components/Dialogbox/index";
import { hrmDeptsFullPersonnelTree } from "@/api/hrm/employee";
export default {
components: {
Dialogbox
},
data() {
return {
// 自定义接口数据
customFun: null,
oooooo: false,
// 计数
changeFlag: 1,
// 宽度
width: "200px",
// 标题
lable: "",
// 选中值
selectValue: [],
// 回显值
echoValue: [],
// 字段名
field: "",
// 提示语
placeholder: "",
// 是否多选下拉框
multiple: true,
// 是否多选 -- 树
multipleChoice: false,
// 弹窗标题
title: "",
// 树结构唯一字段 默认id
nodekey: "id",
// 树结构绑定字段
defaultProps: { children: "childes", label: "name" },
// 选择框绑定字段
optionsProps: { label: "name", value: "value" },
// 弹窗显隐
RenewalShow: false,
// 选中内容
checkedData: [],
// 默认展开 需要加node-key
defaultExpandedArr: [],
// 默认选中 需要加node-key
defaultCheckedArr: [],
filterText: "",
butArr: [
{
text: "确定",
icon: "",
class: "ok-btn",
size: "small",
type: "primary",
clickFun: this.confirm
},
{
text: "取消",
icon: "",
class: "cancel-btn",
size: "small",
type: "default",
clickFun: this.cancel
}
],
// 1 为公司 2为部门 e 为人员,不同则禁用
type: [],
// 下拉框数据
options: [],
data: []
};
},
props: {
value: {
type: true,
default: ""
},
config: {
type: Object,
default: () => {
return {
lable: "标题", // 标题
customFun: null,
width: "200px", // 下拉框宽度
selectValue: [], // 选中值
field: "selectValue", // 字段名
placeholder: "请选择", // 提示语
title: "选择",
nodekey: "id", // 唯一值
// 树指定字段
defaultProps: { children: "childes", label: "name" },
// 下拉框指定字段
optionsProps: {
label: "name",
value: "value"
},
// 1 为公司 2为部门 e 为人员,不同则禁用 A 为不禁用
// [1,2,e]
type: "A",
multipleChoice: false //是否多选
};
}
}
},
computed: {},
watch: {
config: {
handler(val) {
this.init();
console.log("---初始化");
},
immediate: true,
deep: true
},
value: {
handler(val) {
if (val) {
this.selectValue = val.split(",");
}
},
immediate: true,
deep: true
}
},
created() {
this.gethrmDeptsFullPersonnelTree();
}, // 生命周期 - 创建完成
mounted() {}, // 生命周期 - 挂载完成
beforeCreate() {}, // 生命周期 - 创建之前
beforeMount() {}, // 生命周期 - 挂载之前
beforeUpdate() {}, // 生命周期 - 更新之前
updated() {}, // 生命周期 - 更新之后
beforeDestroy() {}, // 生命周期 - 销毁之前
destroyed() {}, // 生命周期 - 销毁完成
activated() {}, // 如果页面有keep-alive缓存功能,这个函数会触发
methods: {
// 删除标签时触发
removeTag(val) {
let index = this.checkedData.findIndex(
item => item[this.nodekey] === val
);
if (index !== -1) {
this.checkedData.splice(index, 1);
this.selectValue.splice(index, 1);
this.defaultCheckedArr.splice(index, 1);
this.defaultExpandedArr.splice(index, 1);
this.options.splice(index, 1);
}
// 空数据默认展开第一级
this.defaultExpandFirst();
this.$emit("input", this.echoValue.join(",")); // v-model
this.$emit("confirm", this.echoValue, this.checkedData, this.field);
},
// 过滤
filterChange(val) {
this.$refs.tree.filter(val);
},
// 控制单选多选
handleCheckChange(node, list) {
if (!this.multipleChoice) {
if (list.checkedKeys.length == 2) {
//单选实现
this.$refs.tree.setCheckedKeys([node[this.nodekey]]);
}
}
},
// 获取树
gethrmDeptsFullPersonnelTree() {
// 是否存在自定义接口
if (typeof this.customFun === "function" && this.customFun != null) {
this.customFun().then(res => {
if (res.code != 0) {
this.dataErr();
return;
}
if (res.data.length) {
let list = res.data;
// 禁用部分
this.getDisabled(list);
this.data = list;
this.firstEcho();
} else {
this.dataErr();
return;
}
});
} else {
hrmDeptsFullPersonnelTree().then(res => {
if (res.code != 0) {
this.dataErr();
return;
}
if (res.data.length) {
let list = res.data;
// 禁用部分
this.getDisabled(list);
this.data = list;
this.firstEcho();
} else {
this.dataErr();
return;
}
});
}
},
// 数据异常
dataErr() {
let err = {};
err[this.defaultProps.label] = "网络异常,获取数据失败!";
err[this.nodekey] = "ERROR";
err.disabled = true;
err[this.defaultProps.children] = [];
this.data = [err];
},
// 根据type类型 默认禁用部分
getDisabled(data) {
// 1 为公司 2为部门 e 为人员
if (this.type) {
data.forEach(item => {
// 当传入A的时候 全部可选
if (this.type != "A") {
if (
!this.type.includes(
this.customFun != null ? item.deptType : item.type
)
) {
item.disabled = true;
}
}
// 判断是否存在子集
if (
item[this.defaultProps.children] &&
item[this.defaultProps.children].length > 0
) {
this.getDisabled(item[this.defaultProps.children]);
}
});
}
},
// 初始化数据
init() {
const {
lable,
type,
width,
multipleChoice,
selectValue,
field,
placeholder,
title,
nodekey,
defaultProps,
optionsProps,
defaultExpandedArr,
defaultCheckedArr,
customFun
} = this.config;
// 自定义方法
this.customFun = customFun != null ? customFun : null;
// 标题
this.lable = lable != undefined ? lable : "";
// 选择类型 默认人员
this.type = type != undefined ? type : [1, 2, "e"];
// 宽度
this.width = width != undefined ? width : "200px";
// 是否多选-树
this.multipleChoice =
multipleChoice != undefined ? multipleChoice : false;
// 选中值
this.selectValue = selectValue != undefined ? selectValue : [];
// 字段名
this.field = field != undefined ? field : "value";
// 提示语
this.placeholder = placeholder != undefined ? placeholder : "请选择";
// 弹窗标题
this.title = title != undefined ? title : "选择";
// 树结构唯一字段 默认id
this.nodekey = nodekey != undefined ? nodekey : "id";
// 树结构绑定字段
this.defaultProps =
defaultProps != undefined
? defaultProps
: { children: "childes", label: "name" };
// 选择框绑定字段
this.optionsProps =
optionsProps != undefined
? optionsProps
: { label: "name", value: "value" };
// 默认展开内容
this.defaultExpandedArr =
defaultExpandedArr != undefined ? defaultExpandedArr : [];
// 默认选中内容
this.defaultCheckedArr =
defaultCheckedArr != undefined ? defaultCheckedArr : [];
// 发生变化时调用 前提存在数据
if (this.data && this.data.length) {
this.firstEcho();
}
},
// 首次回显
firstEcho() {
if (this.selectValue && this.selectValue.length) {
this.$nextTick(() => {
// 初始化
this.options = [];
this.filterText = "";
this.checkedData = [];
let arr = this.data;
// 默认展开
this.defaultExpandedArr = this.selectValue;
// 默认选中
this.defaultCheckedArr = this.selectValue;
// 获取选中的节点
let list = this.selectValue.map(item => this.findNode(item, arr));
// 过滤掉空节点
list = list.filter(item => item !== undefined);
this.checkedData = list;
this.checkedData.forEach((item, index) => {
let obj = {};
obj[this.optionsProps.label] = item[this.defaultProps.label];
obj[this.optionsProps.value] = item[this.nodekey];
this.options.push(obj);
});
this.echoValue = this.selectValue;
});
} else {
// 没有任何选中
this.checkedData = [];
// 空数据默认展开第一级
this.defaultExpandFirst();
}
},
// 递归查找 找出对应的数据
findNode(id, arr) {
for (let i = 0; i < arr.length; i++) {
if (arr[i][this.nodekey] == id) {
return arr[i];
}
if (arr[i][this.defaultProps.children]) {
let node = this.findNode(id, arr[i][this.defaultProps.children]);
if (node) {
return node;
}
}
}
},
// 切换事件
SelectionTreeChange() {},
// 点击了
selectClick(val) {
this.RenewalShow = true;
},
// 确定
confirm() {
this.RenewalShow = false;
// changeFlag 用来判断是否是默认回显,还是手动操作
this.changeFlag += 1;
this.reset(); // 重置
this.checkedData.forEach((item, index) => {
let obj = {};
obj[this.optionsProps.label] = item[this.defaultProps.label];
obj[this.optionsProps.value] = item[this.nodekey];
this.options.push(obj);
this.echoValue.push(item[this.nodekey]);
this.selectValue.push(item[this.nodekey]);
// 默认展开
this.defaultExpandedArr.push(item[this.nodekey]);
// 默认选中
this.defaultCheckedArr.push(item[this.nodekey]);
});
// 空数据默认展开第一级
this.defaultExpandFirst();
this.closeMask(); // 蒙层层级
this.$emit("input", this.echoValue.join(",")); // v-model
this.$emit("confirm", this.echoValue, this.checkedData, this.field);
},
// 默认展开第一级
defaultExpandFirst() {
if (
this.defaultExpandedArr.length == 0 &&
this.data &&
this.data.length
) {
this.defaultExpandedArr.push(this.data[0][this.nodekey]);
}
},
// 重置
reset() {
this.filterText = "";
this.options = [];
this.selectValue = [];
this.echoValue = [];
this.defaultExpandedArr = [];
this.defaultCheckedArr = [];
},
// 取消
cancel() {
this.RenewalShow = false;
if (this.data && this.data.length) {
this.firstEcho();
}
this.closeMask();
},
// 关闭
close() {
this.RenewalShow = false;
if (this.data && this.data.length) {
this.firstEcho();
}
this.closeMask();
},
// 过滤
filterNode(value, data) {
if (!value) return true;
return data[this.defaultProps.label].indexOf(value) !== -1;
},
// 节点选中
checkChange(data, checked, indeterminate) {
if (checked) {
// 选中就新增
this.checkedData.push(data);
} else {
// 取消选中就判断数组是否存在,存在及删除
let index = this.checkedData.findIndex(
item => item[this.nodekey] === data[this.nodekey]
);
if (index !== -1) {
this.checkedData.splice(index, 1);
}
}
},
// 删除
treeDel(val) {
let index = this.checkedData.findIndex(
item => item[this.nodekey] === val[this.nodekey]
);
if (index !== -1) {
this.checkedData.splice(index, 1);
this.removeCheckedKeys(this.checkedData);
}
},
// 同步取消树选中-通过node
removeCheckedKeys(arr) {
this.$refs.tree.setCheckedNodes(arr);
},
// 多层级 关闭 蒙层还在的问题
closeMask() {
this.$nextTick(() => {
let dom = document.getElementsByClassName("SelectionTree");
for (let index = 0; index < dom.length; index++) {
const element = dom[index];
let model = element.getElementsByClassName("v-modal");
if (model[0]) {
model[0].style.zIndex = -1;
}
}
});
}
}
};
</script>
<style scoped lang="scss">
/deep/ .popper-class {
display: none;
}
.SelectionTree {
.hb {
width: 49%;
height: 520px;
border: 1px solid #ccc;
padding: 8px;
overflow: hidden;
// border-radius: 4px;
}
.hb-c {
margin: 8px 0px;
overflow-y: scroll;
}
.hb-li:hover {
background-color: #f5f7fa;
}
}
/deep/ .el-dialog__header {
border-bottom: 1px solid #ccc;
}
/deep/ .el-dialog__body {
padding: 20px !important;
}
/deep/.el-select__tags {
flex-wrap: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/deep/ .el-tag {
background-color: #f3f7ff;
max-width: 93%;
color: #333333;
}
/deep/ .el-input--suffix {
cursor: pointer;
}
/deep/ .el-select__tags-text {
font-size: 13px !important;
}
.treeSearch {
/deep/ .el-input__inner {
border-color: #ccc !important;
}
}
</style>
使用方法
HTML
<SelectionTree
:config="config"
v-model="value"
@confirm="confirm"
ref="SelectionTree"
/>
JS
import SelectionTree from "@/components/SelectionTree/index";
export default {
components: {
SelectionTree
},
data(){
return{
value:"",
config:{
// 自定义接口数据
customFun: null,
lable: "标题", // 标题
width: "200px", // 宽度
selectValue: [], // 选中值 用来回显
field: "selectValue", // 字段名
placeholder: "请选择", // 提示语
title: "选择", // 弹窗标题
nodekey: "id", // 树唯一值
// 树指定字段
defaultProps: {
children: "childes",
label: "name"
},
// 下拉框指定字段
optionsProps: {
label: "name",
value: "value"
},
// 1 为公司 2为部门 e 为人员,不同则禁用 A 为不禁用
// [1,2,'e']
type: "A",
multipleChoice: false //是否多选
};
}
},
methods:{
// 确定事件
// echoValue 返回值
// checkedData 被勾选的数组
// field 字段名
confirm(echoValue,checkedData,field){
console.log(echoValue,checkedData,field,'confirm====>');
}
}
}