此类选择器根据vue+elementUI实现,使用vue3的可以根据此案例稍作改动即可实现,主要功能有弹出选择、搜索过滤、搜索结果高亮等,此选择器只支持单选,如需多选可在此基础进行改造。
使用时,props-value必须要传,并且要保证其唯一性!!!
({{ data[props.children].length }})
export default {
props: {
value: {
type: undefined,
default: null,
},
data: {
type: Array,
default: () => new Array(),
},
props: {
type: Object,
default: () => {
return {
label: 'label',
value: 'value',
children: 'children',
};
},
},
},
data() {
return {
defaultExpandedKeys: [],
visible: false,
filterText: '',
};
},
created() {
this.propsInit();
},
mounted() {
setTimeout(this.initData, 10);
},
beforeUpdate() {
this.propsInit();
this.initData();
},
methods: {
open() {
this.visible = true;
},
initData() {
let newItem = this.recurrenceQuery(this.data, this.props.value, this.value);
if (newItem?.length) {
if (this.props.value && newItem[0][this.props.value]) {
this.defaultExpandedKeys = [newItem[0][this.props.value]];
}
this.$nextTick(() => {
if (this.$refs.myTree?.setCurrentNode) this.$refs.myTree.setCurrentNode(newItem[0]);
});
} else {
if (this.data.length == 1 && this.$attrs['expand-first-level'] !== false) {
this.defaultExpandedKeys = [this.data[0][this.props.value]];
}
}
this.$forceUpdate();
},
// 单选事件
handleNodeClick(data, e) {
if (this.props.disabled && e.disabled) {
return false;
} else {
if (data[this.props.children] && data[this.props.children]?.length) {
return false;
}
}
this.$emit('input', data[this.props.value]);
this.visible = false;
this.$emit('change', data, e);
},
// 递归查找通用方法
recurrenceQuery(list, key, value) {
if (!list || !key || !value) return [];
let queryData = [];
list.map(item => {
if (item[this.props.children] && item[this.props.children].length) {
queryData.push(...this.recurrenceQuery(item[this.props.children], key, value));
}
if (item[key] == value) {
queryData.push(item);
}
return item;
});
return queryData;
},
selectClear(flag) {
if (!flag) {
this.$emit('input', '');
this.$emit('change', null, null);
}
this.$refs.myTree.setCurrentKey(null);
this.remoteMethod('');
},
getCheckedNodes() {
if (this.value !== null && this.value !== undefined && this.value !== '') {
return this.$refs.myTree.getCheckedNodes();
}
return [];
},
getCurrentNode() {
if (this.value !== null && this.value !== undefined && this.value !== '') {
return this.$refs.myTree.getCurrentNode();
}
return null;
},
valueFilter(val) {
let res = '';
[res] = this.recurrenceQuery(this.data, this.props.value, val);
return res?.[this.props.label] || '';
},
propsInit() {
this.props.label = this.props.label || 'label';
this.props.value = this.props.value || 'value';
this.props.children = this.props.children || 'children';
},
remoteMethod(query) {
this.$refs.myTree.filter(query);
},
filterNode(value, data) {
if (!value) return true;
let result = true;
if (this.$listeners.onFilter) {
this.$emit('onFilter', value, data, res => {
result = res;
});
} else {
result = data[this.props.label].indexOf(value) !== -1;
}
return result;
},
searchHighlightFilter(node, data) {
let { label } = this.props;
if (this.$attrs['search-highlight'] === false) return data[label];
if (!this.filterText) return data[label];
const regex = new RegExp(this.filterText, 'gi');
let text = data[label].replace(regex, match => {
return `${match}`;
});
return text;
},
},
watch: {
value: {
deep: true,
handler(val) {
if (!val) {
this.selectClear(true);
}
},
},
filterText(val) {
this.$refs.myTree.filter(val);
},
},
};
CSS
.selecTree {
max-height: 50vh;
overflow: auto;
padding: 5px;
::v-deep .el-tree-node__content {
font-size: 14px;
}
}
::v-deep.slotSpan {
font-size: 14px;
> i {
color: #67c23a;
margin-right: 5px;
}
b {
font-weight: normal;
font-size: 12px;
color: #999;
}
.highlight {
color: #67c23a;
}
}
.treeDialogBody {
max-height: 60vh;
display: flex;
flex-direction: column;
::v-deep .el-input__validateIcon {
display: none;
}
::v-deep .treeDialogBodyTree {
flex: 1;
overflow: auto;
padding: 12px 8px;
margin: 12px 0;
background-color: #f5f7fa;
border-radius: 5px;
.el-tree {
background: transparent;
.el-tree-node__content:hover {
background-color: #eaeef4;
}
}
}
}
.footer {
text-align: right;
}
以下只列举主要属性与方法,更多具体的属性配置请移步element官网进行查看。
属性名 | 类型 | 默认值 | 是否必传 | 说明 |
---|---|---|---|---|
value / v-model | string / number | - | 是 | 绑定值 |
props |
object |
与element保持一致 |
否 | 配置选项,具体配置可以参照element ui库中el-tree的配置 |
expand-first-level |
boolean | true | 否 | 当根节点只有一个时,是否展开 |
search-highlight |
boolean | true | 否 | 是否开启搜索高亮,仅在slot-tree未传入时生效 |
show-count | boolean | false | 否 | 若节点中存在children,则在父节点展示所属children的数量,注意但设置插槽时 show-count将失效 |
事件名称 | 说明 | 回调参数 |
---|---|---|
change |
当选择项发生改变时触发 | 共两个参数,依次为:当前节点的数据,当前节点的 Node 对象 |
onFilter |
当过滤框输入的值改变时触发 |
共三个参数,依次为:搜索框的值,当前节点的数据,回调函数callback, 接受一个参数类型boolean,表示是否展示该节点 |
name | 说明 |
---|---|
- |
页面展示的输入框slot,如果传入默认插槽,则会不显示默认el-input,参数为 { value } |
prepend |
弹窗中过滤文本框的顶部插槽 |
tree |
自定义树节点的内容,参数为 { node, data } |