一、效果图
二、代码(此代码是基于自己项目更改,根据各自项目进行调整)
1、子组件封装
注意使用:正常使用
// 在el-tree触发@check-change="checkChange" 事件,方法如下:
checkChange() {
// 节点选中状态更改
// 获取选中的node节点
let selectedArray = this.getCheckedNodes();
// 设置select展示的label
this.selectShowLabel = selectedArray.map(node => node[this.defaultProps.label]);
// 更新model绑定值
let selectValueArray = selectedArray.map(node => node[this.defaultProps.value]);
this.$emit("changeChecked", selectValueArray)
},
正常使用的情况下可以不需要这些,个人需求需要
useData: {
type: Array,
default: () => []
},
item: {
type: Object,
default: () => {}
},
initData: {
type: Object,
default: () => {}
},
// 因项目需求不一致,故使用了@check="checkChange(useData, item,initData)" @check-change="onSetCheckedNodes"两种
// 子组件使用文章来源:https://www.toymoban.com/news/detail-681321.html
<template>
<div class="root">
<el-select v-model="selectShowLabel" :clearable="clearable" :placeholder="placeholder" multiple :collapse-tags="collapseTags" :popper-append-to-body="false" @blur="handleBlur" @visible-change="visibleChange" @change="changeHandle" @clear="clear" @remove-tag="removeTag">
<el-option :value="selectShowLabel" disabled :style="'margin:5px'">
<el-input v-model="filterText" size="small" prefix-icon="el-icon-search" clearable placeholder="输入关键字进行查找"> </el-input>
</el-option>
<el-tree ref="tree" :data="treeData" :expand-on-click-node="false" check-on-click-node check-strictly show-checkbox :node-key="defaultProps.value" :props="defaultProps" :default-expand-all="expandAll" :filter-node-method="filterNode" @check="checkChange(useData, item,initData)" @check-change="onSetCheckedNodes">
</el-tree>
</el-select>
</div>
</template>
<script>
import CircularJSON from 'circular-json';
export default {
model: {
prop: 'checkedArray', // 把父组件传过来的值重命名为checkedArray
event: 'changeChecked' // 把父组件传过来的方法重命名为changeChecked 其实就是 input
},
props: {
// 选中节点的值
checkedArray: {
type: Array,
default: () => {
return [];
}
},
// 树数据
treeData: {
type: Array,
required: true
},
useData: {
type: Array,
default: () => []
},
item: {
type: Object,
default: () => {}
},
initData: {
type: Object,
default: () => {}
},
// 设置指定的label,value,children
nodeConfig: {
type: Object,
default: () => {
return {
value: 'code',
label: 'name',
children: 'childrenList'
};
}
}
},
data() {
return {
// 用于下拉列表展示
selectShowLabel: '',
// 筛选输入框绑定值
filterText: '',
placeholder: '请选择',
clearable: true, // 开启下拉框一键清空
collapseTags: true, // 下拉框tag是否折叠
expandAll: false, // 是否展开所有节点
modelData: [],
modelList: [],
modelListData: [],
filtraArr: [],
filterMessage: ''
};
},
computed: {
defaultProps() {
return Object.assign(
{
value: 'code',
label: 'name',
children: 'childrenList'
},
this.nodeConfig
);
}
},
watch: {
// 设置回显
checkedArray: {
handler(val) {
if (val && val.length > 0) {
this.setCheckedNodes(val);
}
},
// 监听第一次数据更改
immediate: true
},
// 筛选符合条件选项
filterText(val) {
this.$refs.tree.filter(val);
this.getFilterData();
}
},
methods: {
onSetCheckedNodes(data) {
this.filtraArr = [data.code];
},
visibleChange($event) {
if (!$event) {
this.filterText = '';
}
},
// el-select失去焦点后校验
handleBlur(val) {
this.$emit('blur', val.target.value);
this.dispatch('ElFormItem', 'el.form.blur', [this.checkedArray]);
},
// el-select清空树选择的内容
clear() {
this.$emit('changeChecked', []);
this.$refs.tree.setCheckedKeys([]);
},
// el-select选择值发生改变后校验
changeHandle() {
this.dispatch('ElFormItem', 'el.form.change', [this.checkedArray]);
},
// select移除选中标签
removeTag(label) {
// 选中项的value
let selectedValueArray = this.$refs.tree
.getCheckedNodes()
.filter((o) => o[this.defaultProps.label] !== label)
.map((o) => o[this.defaultProps.value]);
// 移除的节点
let removeNode = this.$refs.tree
.getCheckedNodes()
.filter((o) => o[this.defaultProps.label] === label);
// 更新树选中节点
removeNode.forEach((o) => {
this.$refs.tree.setChecked(o, false, true);
});
// 更新父组件绑定值
this.$emit('changeChecked', selectedValueArray);
},
// 树节点过滤方法
filterNode(value, data) {
if (!value) return true;
return data[this.defaultProps.label].indexOf(value) !== -1;
},
getFilterData() {
if (this.filterText) {
const rootData = this.$refs.tree.root;
if (rootData.visible) {
const childNodesStr = CircularJSON.stringify(rootData.childNodes);
const childNodes = CircularJSON.parse(childNodesStr);
this.filterData = this.recursionNodes(childNodes);
this.filterMessage = '';
} else {
this.filterMessage = '暂无数据';
}
}
},
// 这里解释一下为什么要用CircularJSON这个插件,因为element tree 这node数据存在一个对象里的子项存在循环引用,存在循环引用的对象
recursionNodes(childNodes) {
const nodes = childNodes;
const result = [];
for (const item of nodes) {
if (item.visible) {
result.push(item.data);
}
if (item.childNodes && item.childNodes.length) {
const tempResult = this.recursionNodes(item.childNodes);
item.data.children = tempResult;
}
}
return result;
},
// 获取选中节点
getCheckedNodes() {
let onlyLeaf = true;
return this.$refs.tree.getCheckedNodes(onlyLeaf).map((node) => ({
[this.defaultProps.label]: node[this.defaultProps.label],
[this.defaultProps.value]: node[this.defaultProps.value]
}));
},
// 设置选中节点
async setCheckedNodes(selectedArray) {
if (!selectedArray || selectedArray.length === 0) {
this.clear();
return;
}
// 外层"this.$nextTick"处理第一次回显dom可能未加载导致setCheckedKeys报错
this.$nextTick(() => {
this.$refs.tree.setCheckedKeys(selectedArray);
});
},
// 过滤出当前点击得code
filtrationArr(arr, arr1) {
const tempArr = [];
arr.forEach((item) => {
const itemStr = item.toString();
let flag = false;
arr1.forEach((item1) => {
const itemStr1 = item1.toString();
if (itemStr === itemStr1) {
flag = true;
}
});
if (!flag) {
tempArr.push(item);
}
});
return tempArr;
},
// 数据将属性结构扁平化
treeArr(data, parentCode, res = []) {
data.forEach((item) => {
const { code, name, level, childrenList } = item;
res.push({
code: code,
parentCode: parentCode,
name: name,
level: level
});
if (childrenList && childrenList.length) {
this.treeArr(childrenList, code, res);
}
});
return res;
},
// 节点选中状态更改
checkChange(model, item, initData) {
let filtraArr = null;
// 1、树结构扁平化处理数组放到vuex
this.$store.commit('cascaderData', this.treeArr(item.options));
// 2、记录点击选中与上一次得数据
if (this.modelData.length === 0) {
this.modelData = [...this.$refs.tree.getCheckedNodes()];
} else {
this.modelListData = [...this.modelList];
this.modelList = [...this.modelData];
this.modelData = [...this.$refs.tree.getCheckedNodes()];
}
// 3、过滤出当前选中得数据(判断选中数据大于等2是且记录数据不为0,并且选中数据大于记录数据)
if (this.modelData.length >= 1 && this.modelList.length !== 0) {
let modelData = [];
let modelList = [];
let modelListData = [];
this.modelData.forEach((item) => {
modelData.push(item.code);
});
this.modelList.forEach((item) => {
modelList.push(item.code);
});
this.modelListData.forEach((item) => {
modelListData.push(item.code);
});
filtraArr = this.filtraArr;
}
// 4、根据选中得值跟 扁平化数据对比
if (filtraArr !== undefined && filtraArr !== null) {
let flatData = this.$store.state.user.cascaderData; // 扁平数据
let currentValue = filtraArr[0];
let currentNode = flatData.filter(
(item) => item.code === currentValue
)[0];
let parent = flatData.filter(
(item) => item.code === currentNode.parentCode
)[0];
let count = 1;
const deleteCodes = [];
if (parent !== undefined) {
deleteCodes.push(parent.code);
// 往上寻找
while (parent.level !== '1' && count < 10) {
parent = flatData.filter(
(item) => item.code === parent.parentCode
)[0];
deleteCodes.push(parent.code);
count++;
}
}
let childrenNodes = flatData.filter(
(item) => item.parentCode === currentValue
);
let childrenCodes = [];
childrenNodes.forEach((item) => {
childrenCodes.push(item.code);
});
while (childrenCodes.length > 0) {
const code = childrenCodes.shift();
deleteCodes.push(code);
const children = flatData.filter((item) => item.parentCode === code);
if (children.length > 0) {
children.forEach((item) => {
childrenCodes.push(item.code);
});
}
}
const modelArr = [];
const modelAller = [];
this.modelData.forEach((item) => {
modelAller.push(item.code);
});
modelAller.forEach((item) => {
let deleteState = deleteCodes.indexOf(item);
if (deleteState === -1) {
if (filtraArr[0] === item) {
modelArr.unshift(item);
} else {
modelArr.push(item);
}
}
});
let modelArrData = [];
modelArr.forEach((items) => {
let tree = flatData.filter((item) => item.code === items);
modelArrData.push({
code: tree[0].code,
name: tree[0].name
});
});
// // 设置select展示的label
this.selectShowLabel = modelArrData.map(
(node) => node[this.defaultProps.label]
);
// // // 更新model绑定值
let selectValueArray = modelArrData.map(
(node) => node[this.defaultProps.value]
);
this.$emit('changeChecked', selectValueArray);
} else if (this.modelData.length === 1) {
this.selectShowLabel = this.modelData.map(
(node) => node[this.defaultProps.label]
);
// // // 更新model绑定值
let selectValueArray = this.modelData.map(
(node) => node[this.defaultProps.value]
);
this.$emit('changeChecked', selectValueArray);
}
},
// 提供表单校验
dispatch(componentName, eventName, params) {
var parent = this.$parent || this.$root;
var name = parent.$options.componentName;
while (parent && (!name || name !== componentName)) {
parent = parent.$parent;
if (parent) {
name = parent.$options.componentName;
}
}
if (parent) {
parent.$emit.apply(parent, [eventName].concat(params));
}
}
}
};
</script>
<style lang="scss" scoped>
::v-deep .el-input .el-input__inner {
height: 34px;
line-height: 34px;
}
</style>
父组件调用文章来源地址https://www.toymoban.com/news/detail-681321.html
<template>
<div id="app">
<selectTree v-model="address" placeholder="请选择地址" :tree-data="mockData" />
</div>
</template>
<script>
import selectTree from './components/selectTree.vue'
export default {
name: 'App',
components: {
selectTree
},
data(){
return{
address:[],
mockData:[],// 树形结构数据
}
}
}
</script>
到了这里,关于Element-UI实现的下拉搜索树组件(el-select、el-input、el-tree组合使用)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!