一、效果图
二、代码(此代码是基于自己项目更改,根据各自项目进行调整)
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"两种
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
// 子组件使用
<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>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 232
- 233
- 234
- 235
- 236
- 237
- 238
- 239
- 240
- 241
- 242
- 243
- 244
- 245
- 246
- 247
- 248
- 249
- 250
- 251
- 252
- 253
- 254
- 255
- 256
- 257
- 258
- 259
- 260
- 261
- 262
- 263
- 264
- 265
- 266
- 267
- 268
- 269
- 270
- 271
- 272
- 273
- 274
- 275
- 276
- 277
- 278
- 279
- 280
- 281
- 282
- 283
- 284
- 285
- 286
- 287
- 288
- 289
- 290
- 291
- 292
- 293
- 294
- 295
- 296
- 297
- 298
- 299
- 300
- 301
- 302
- 303
- 304
- 305
- 306
- 307
- 308
- 309
- 310
- 311
- 312
- 313
- 314
- 315
- 316
- 317
- 318
- 319
- 320
- 321
- 322
- 323
- 324
- 325
- 326
- 327
- 328
- 329
- 330
- 331
- 332
- 333
- 334
- 335
- 336
- 337
- 338
- 339
- 340
- 341
- 342
- 343
- 344
- 345
- 346
- 347
- 348
- 349
- 350
- 351
- 352
- 353
- 354
- 355
- 356
- 357
- 358
- 359
- 360
- 361
- 362
- 363
- 364
父组件调用
<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>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
评论记录:
回复评论: