前些天实现了一个树结构的下拉组件,可最近又让我反对下拉搜寻性能,查了一圈没有找到适合的,只好明天本人写了一下,对付能用吧,反对搜寻性能了最起码,可有些参数还是要配置的
<template>
<div>
<el-popover
v-model:visible="popVisible"
placement="bottom"
:width="popoverWidth"
trigger="click"
>
<template #reference>
<el-input
v-model="filterText"
v-bind="$attrs"
:placeholder="placeholder"
@blur="handleBlur"
@focus="handleFocus"
>
<template #suffix>
<div class="suffix" @click="handleIcon">
<i :class="`el-icon-arrow-${popVisible ?'up':'down'}`" />
</div>
</template>
</el-input>
</template>
<el-tree
ref="tree"
class="filter-tree"
:data="options"
:props="defaultProps"
default-expand-all
:filter-node-method="filterNode"
@node-click="handleNodeClick"
/>
</el-popover>
</div>
</template>
<script>
import {defineComponent, watch, onMounted, ref, computed} from "vue";
export default defineComponent({
props: {
popoverWidth: {
type: Number,
default: 400,
},
modelValue: {
type: String,
default: "",
},
options: {
type: Array,
default: () => [],
},
},
emits: ["update:modelValue", "selected"],
setup(props, context) {
const defaultProps = ref({
children: "children",
label: "label",
});
const popVisible = ref(false);
function handleIcon() {popVisible.value = !popVisible.value;}
const preText = ref("");
const placeholder = computed(() =>
preText.value ? preText.value : "select"
);
function filterNode(value, data) {if (!value) return true;
return data.label.indexOf(value) !== -1;
}
function findLabel(arr, target) {
let res = target;
function find(arr) {for (let i = 0; i < arr.length; i += 1) {if (arr[i].value === target) {res = arr[i].label;
return;
}
if (arr[i].children && arr[i].children.length) {find(arr[i].children, target);
}
}
}
find(arr);
return res;
}
const filterText = ref("");
function handleNodeClick(node) {
popVisible.value = false;
filterText.value = node.label;
preText.value = node.label;
context.emit("selected", node);
context.emit("update:modelValue", node.value);
}
function handleBlur() {setTimeout(() => {filterText.value = preText.value;}, 100);
}
function handleFocus() {
preText.value = filterText.value;
filterText.value = "";
}
watch(() => props.modelValue,
() => {filterText.value = findLabel(props.options, props.modelValue);
}
);
const tree = ref();
watch(() => filterText.value,
(val) => {tree.value.filter(val);
}
);
onMounted(() => {filterText.value = findLabel(props.options, props.modelValue);
});
return {
tree,
placeholder,
defaultProps,
filterText,
popVisible,
preText,
handleNodeClick,
handleBlur,
handleFocus,
filterNode,
handleIcon,
};
},
});
</script>
<style scoped>
.suffix {
cursor: pointer;
display: flex;
width: 100%;
height: 100%;
align-items: center;
}
</style>
应用起来比之前那一篇麻烦了一丢丢,绝对还是好用的
且看,
<treeSelect
style="width: 226px"
:popoverWidth="200"
v-model="selectData"
:options="options"
@selected="handleSelect"
></treeSelect>
在父组件里咱们组要把对应要批改的 value 传进去,options 对应树结构的 array 模样,不过肯定要有 value 的,或者读者能够本人再略微改改,emit 进去的参数。
对于 el-popover 的宽度和高度都没有做设置,不适宜过多的数据,需要的话就本人加个 div 限度一下,或者须要的人多的话,我再把它优化下。。
const selectData = ref("");
const options = ref([
{
label: "选项 1",
value: "1",
children: [{label: "选项 1 -1", value: "1-1"}],
},
{label: "选项 2", value: "2"},
]);
function handleSelect(node) {console.log(node);
}
有哪里须要优化的提一下,继续改良哈。