最近在学vue,公司项目采用的是webpack+vue3+element plus,之前用的时候element plus还没出treeSelect相关的插件,就自己用select和tree封了个下拉树选择的组件,当然也可以用官方新出的treeSelect。
ps:公司只有我一个小前端,就没采用ts,边学边做喽
最终效果:可以搜索的下拉树选择
element plus版本:v2.1.11
element plus版本和文档好像都在飞快的更新中,不同版本可能会有意想不到的bug哈哈哈哈
<!--下拉树选择-->
<template>
<el-select
collapse-tags
v-model="statusData"
placeholder="请选择"
@remove-tag="removeTag"
class="treeDataWrap"
>
<div class="filterWrap">
<el-input v-model="filterText" placeholder="请检索关键字" />
</div>
<el-option
style="height: auto; padding: 0; margin-right: 10px; margin-top: 50px"
>
<el-tree
width="100%"
:filter-node-method="filterNode"
:data="treeData"
show-checkbox
node-key="id" // 根据实际项目指定
ref="treeRef"
highlight-current
:props="defaultProps" // 根据实际项目指定
@check-change="handleCheckChange"
@check="onCheck"
></el-tree>
</el-option>
</el-select>
</template>
<script>
import {
ref,
toRefs,
reactive,
watch,
nextTick,
onMounted,
computed,
} from "vue";
import { useStore } from "vuex";
import isEmpty from "lodash/isEmpty";
export default {
name: "treeDataSelect",
props: {
modelValue: {
type: Object,
},
// 显示复选框情况下,是否严格遵循父子不互相关联
checkStrictly: {
type: Boolean,
default() {
return false;
},
},
},
setup(props, { emit }) {
const store = useStore();
const treeRef = ref(null);
const filterText = ref("");
const statusData = ref(null);
const defaultProps = {
children: "children",
label: "text",
};
const treeData = [
{
id: "1",
text: "标签1",
children: [
{
id: "1-1",
text: "标签1-1",
},
{
id: "1-2",
text: "标签1-2",
},
{
id: "1-3",
text: "标签1-3",
},
],
},
{
id: "2",
text: "标签2",
children: [
{
id: "2-1",
text: "标签2-1",
},
{
id: "2-2",
text: "标签2-2",
},
],
},
{
id: "3-1",
text: "标签3-1",
},
];
const treeSelectData = computed(() => store.state.global.treeSelectData);
watch(filterText, (val) => {
treeRef.value.filter(val);
});
// 下拉选择框是作为子组件嵌入到表单里面的,会有一些和父组件表单的交互,清空、表单值更新等
// 多选清空
watch(statusData, (val) => {
nextTick(() => {
if (isEmpty(val)) {
treeRef.value.setCheckedNodes([]);
emit("update:modelValue", []);
}
});
});
watch(treeSelectData, (val) => {
nextTick(() => {
if (isEmpty(val)) {
treeRef.value.setCheckedNodes([]);
emit("update:modelValue", []);
}
});
});
const filterNode = (value, data) => {
if (!value) return true;
return data.text.indexOf(value) !== -1;
};
const handleCheckChange = () => {
// 树选择时的操作,需根据实际业务
let response = treeRef.value.getCheckedNodes(false, true);
let labelData = [];
let selectedIds = [];
response.forEach((item) => {
labelData.push(item.text);
selectedIds.push(item.id);
});
emit("update:modelValue", selectedIds);
store.commit("global/UPDATE_DATA", {
treeSelectData: selectedIds,
});
statusData.value = labelData;
};
const removeTag = (val) => {
console.log(val, "清空");
};
const onCheck = (checkedObj, checkedData) => {
console.log("oncheck", checkedObj, checkedData);
const { checkedKeys, halfCheckedKeys } = checkedData;
// 由于实际业务全选和全不选的效果一致,这里增加了是否全选的判断,可以根据实际业务取舍
if (checkedKeys.length > 0 && halfCheckedKeys.length == 0) {
// 选中根结点
console.log("选中根结点");
store.commit("global/UPDATE_DATA", {
allSelected: true,
});
} else {
store.commit("global/UPDATE_DATA", {
allSelected: false,
});
}
};
return {
...toRefs(state),
handleCheckChange,
defaultProps,
data,
treeRef,
filterText,
filterNode,
removeTag,
statusData,
onCheck,
treeData,
};
},
};
</script>
<style lang="scss" scoped>
.filterWrap {
padding: 0 10px;
margin-bottom: 10px;
}
.treeDataWrap {
position: relative;
}
.treeDataWrap :deep(.el-select-tags-wrapper) {
display: flex;
}
.filterWrap {
padding-top: 10px;
padding-bottom: 8px;
position: absolute;
top: 0;
left: 0;
width: 100%;
z-index: 999;
background: #fff;
box-sizing: border-box;
}
</style>
表单使用:
<el-form ref="searchForm" :inline="false" style="width: 100%">
<el-row>
<el-col :md="6" :sm="12">
<el-form-item label="树选择:">
<TreeDataSelect
multiple
:checkStrictly="true"
v-model="valueData"
clearable
></TreeDataSelect>
</el-form-item>
</el-col>
</el-row>
</el-form>