实现 select 中嵌套 tree 外加搜索
参考地址实现地址
代码
<el-form-item label="考核人员" prop="userIdArr" v-if="title == '发起考核'">
<el-popover v-model="popoverVisible" placement="bottom" trigger="click" ref="popover">
// click:点击select时弹出框显示
// popover+tree用于选择,树形控件放在弹出框中
<el-input class="input" placeholder="此处键入'关键词'搜索查询" prefix-icon="el-icon-search" v-model="treeFilter"
size="mini" v-focus clearable />
<!-- 通过 checkbox 进行选择,通过 checkChange 来进行保存值 -->
<el-tree :data="deptOptions" :props="defaultProps" show-checkbox @check="checkChange"
:default-checked-keys="this.form.userIdArr" ref="tree" node-key="iid" :filter-node-method="filterNode"
:default-expand-all="false" :style="`max-height: ${treeMaxHeight}px; overflow-y: auto;`" />
// select展示选择结果,储存选择值typeValue
<el-select slot="reference" multiple collapse-tags v-model="form.userIdArr" placeholder="请选择"
popper-class="hiddenSel" clearable @clear="clearTag" @remove-tag="removeTag">
<el-option v-for="item in typeOption" :key="item.iid" :label="item.label" :value="item.iid" />
</el-select>
</el-popover>
</el-form-item>
data 数据
// 需把数据整理成以下结构
// tree数据(children的id第一位为父级id,用于在select中清除某一点,可找到其父级去掉全选)
deptOptions: [], // tree 的数据
typeOption: [], // select 选择框一维数据
defaultProps: {
// tree 的显示类名
children: "children",
label: "label",
},
treeFilter: "", // 搜索框绑定值,用作过滤
// 选中数组
ids: [],
methods 方法:
watch: {
// 搜索过滤,监听input搜索框绑定的treeFilter
treeFilter(val) {
this.$refs.tree.filter(val);
// 当搜索框键入值改变时,将该值作为入参执行树形控件的过滤事件filterNode
},
}
methods: {
// 发起考核
// select 框部分
/** 查询部门下拉树结构 */
getDeptTree() {
userTreeList().then((response) => {
// 将数据 user 添加进 children,并且将字段名更改下
this.deptOptions = this.copyAndRenameFieldsInArray(response.data);
// 对数据进行添加 iid
this.deptOptions = this.addIid(this.deptOptions);
// 处理好的数据 进行扁平化
this.typeOption = this.flattenData(this.deptOptions);
});
},
// 将部门tree进行扁平化
flattenData(data) {
const result = [];
function flatten(item) {
result.push({ id: item.id, label: item.label, iid: item.iid });
if (item.children) {
item.children.forEach((child) => {
flatten(child);
});
}
}
data.forEach((item) => {
flatten(item);
});
return result;
},
// 将 user 添加进 children
copyAndRenameFieldsInArray(data) {
data.forEach((node) => {
if (node.users && node.users.length > 0) {
node.children = node.children || [];
// 将复制并重命名字段的数据拼接到 children 数组的最前面
node.children.unshift(
...node.users.map((user) => ({
userId: user.userId,
id: user.userId,
userName: user.userName,
label: user.nickName,
deptId: user.deptId,
nickName: user.nickName,
}))
);
node.users = [];
}
if (node.children && node.children.length > 0) {
this.copyAndRenameFieldsInArray(node.children);
}
});
return data;
},
// 加字段 iid
addIid(data, iidPrefix = "") {
return data.map((item) => {
const iid = iidPrefix + item.id;
const newItem = { ...item, iid };
if (item.children) {
newItem.children = this.addIid(item.children, iid + "-");
}
return newItem;
});
},
// tree选择值修改时
checkChange() {
this.form.userIdArr = [];
// 将tree选择的id赋值给select
this.$refs["tree"]?.getCheckedNodes(true).forEach((value) => {
// 父级在select中不展示
if (value.iid.indexOf("-") > 0) {
this.form.userIdArr.push(value.iid);
}
});
},
// 模糊查询(搜索过滤),实质为筛选出树形控件中符合输入条件的选项,过滤掉其他选项
filterNode(value, data) {
if (!value) return true;
let filterRes =
data.label.toLowerCase().indexOf(value.toLowerCase()) !== -1;
return filterRes;
},
// 清空select
clearTag() {
// 清空tree选择
this.$refs["tree"].setCheckedKeys([]);
},
// 从select中单个移除时,保持tree选择值同步移除
removeTag(data) {
// 获取tree目前选择的值
var chooseData = this.$refs["tree"].getCheckedKeys(true);
var deleteIndex = "";
// 找到chooseData中与清除的data相同的值
chooseData.forEach((value, index) => {
if (value === data) {
deleteIndex = index;
}
});
// 从tree目前选择值中去掉
chooseData.splice(deleteIndex, 1);
// 若有全选情况,tree的选择值中有父级id,而select中无父级id,需用children的id找到父级id并去掉
// 查找其父级id是否在chooseData中(即原来此父级是否全选),若在则去掉
var findFatherData = chooseData.find(
(element) => element === data.split("-")[0]
);
if (findFatherData) {
chooseData.splice(chooseData.indexOf(findFatherData), 1);
}
// 将修改后的值再赋给tree
this.$refs["tree"].setCheckedKeys(chooseData);
},
// 时间戳转化为时间
formatDate(dateString) {
const date = new Date(dateString);
const year = date.getFullYear();
const month = ("0" + (date.getMonth() + 1)).slice(-2);
const day = ("0" + date.getDate()).slice(-2);
const hours = ("0" + date.getHours()).slice(-2);
const minutes = ("0" + date.getMinutes()).slice(-2);
const seconds = ("0" + date.getSeconds()).slice(-2);
const formattedDate = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
return formattedDate;
},
}