vue+循环多或n个element的Cascader 级联选择器数据关联的实例
- 遇到一个分班管理的系统,要求把所有报名学生进行手动分班,后台返回所有报名的学生,分几个班是动态的;
- 下图点击添加分班会创建一个新班级,前一个班的学生不能出现在其他班级中(如把一堆苹果分别装到多个篮子里分一个少一个,可以把篮子里的苹果拿出来)
图1

图2

源码:
<template>
<div class="course-right">
<el-form
:model="classForm"
ref="classForm"
label-width="130px"
:rules="rules"
>
<div v-for="(item, index) in classForm.classList" :key="'form' + index">
<div @click="isShowItem(index)" class="calss-box">
<div class="item-class">
<div class="line"></div>
<div class="name">{{ item.name ? item.name : "班级名称" }}</div>
<div ref="isUnfold" class="switch">收起</div>
<div
@click.stop="delClass(index)"
class="el-icon-delete del-icon"
></div>
</div>
</div>
<div class="show-content" ref="showContent">
<el-form-item
label="学生分配"
class="activity-name"
:prop="`classList.${index}.value`"
:rules="rules.value"
>
<el-cascader
clearable
v-model="item.value"
:options="item.pupilOption"
:show-all-levels="false"
:key="cascaderKey"
placeholder="请分配学生"
:props="props"
filterable
ref="cascaderShow"
@change="
(val) => {
changeList(val, index);
}
"
collapse-tags
></el-cascader>
</el-form-item>
<div class="info">
<!--展示勾选的数据-->
<span>{{ item.value | filterCascader }}</span>
</div>
</div>
</div>
<div class="add-class" @click="addClass('classForm')">添加分班</div>
</el-form>
</div>
</template>
// ---------------------------------js--------------------------------------------
export default {
data() {
return {
cascaderKey: 1,
rules: {
value: [
{ required: true, message: "请选择学生分配", trigger: "change" },
],
},
props: { multiple: true, value: "all" },
pupilOption: [], // 分配学生下拉数据
classForm: {
classList: [],
}, // 班级表单
initForm: {
value: "",
pupilOption: [],
}, // 初始化每一项表单数据
};
},
created() {
let obj = JSON.parse(JSON.stringify(this.initForm));
this.classForm.classList.push(obj);
this.getStudentInfo();
},
filters: {
filterCascader(list) {
// 展示勾选班级学生
if (!list) {
return "";
}
let data = JSON.parse(JSON.stringify(list));
return data
.map((checkedList) => checkedList.map((item) => item.label).join("/"))
.join("、");
},
},
methods: {
/*
* fn 添加子节点
* @param arr {Array} 要添加的班级
* @param value {String} 要添加的节点位置
* @param item {Array} 要添加的学生children
**********************************重点方法*************************************
*/
addChildren(arr, value, item) {
let items = JSON.parse(JSON.stringify(item)); // 深拷贝传入要添加的学生防止数据被关联
let flag = false; // 如果未找到父节点直接创建父节点
let index = 0; // 找到了要添加的节点的位置
for (let i = 0; i < arr.length; i++) {
if (arr[i].label === value) {
// 找到要添加的节点的位置
flag = true;
if (arr[i].children) {
// 如果有子节点
index = i;
} else {
arr[i]["children"] = items;
}
break;
}
}
// 找到了要添加的节点
if (index) {
let chn = arr[index].children; // 要添加数据的节点
for (let j = 0; j < items.length; j++) {
let f = true;
for (let l = 0; l < chn.length; l++) {
if (items[j].label == chn[l].label) {
f = false;
break;
}
}
if (f) {
// 没有的子节点添加操作
chn.push(items[j]);
}
}
return;
}
let len = JSON.parse(JSON.stringify(arr.length));
if (!flag) {
// 如果未找到父节点直接创建父节点
arr.push({
all: { label: value },
value: len - 1,
label: value,
children: items,
});
}
},
/*
* fn 根据label删除节点
* @params treeList {Array} 要删除的节点的数组
* @params label {String} 要删除的节点
****************************重点方法***********************************************
*/
removeTreeListItem(treeList, label) {
if (!treeList || !treeList.length) {
return;
}
for (let i = 0; i < treeList.length; i++) {
if (treeList[i].label == label) {
treeList.splice(i, 1);
break;
}
this.removeTreeListItem(treeList[i].children, label);
}
},
// 删除班级
delClass(index) {
if (this.classForm.classList.length == 1) {
this.$message("已是最后一个班级");
return;
}
this.$confirm(
"将移除该分班,并且该班内所有学生将改为未分班状态,是否继续?",
"提示",
{
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
}
)
.then(() => {
let data = JSON.parse(JSON.stringify(this.classForm.classList));
let list = JSON.parse(JSON.stringify(data[index].pupilOption));
data.splice(index, 1);
if (data.length == 1) {
this.classForm.classList.splice(index, 1);
++this.cascaderKey;
this.classForm.classList[0].pupilOption = JSON.parse(
JSON.stringify(this.pupilOption)
);
} else {
this.classForm.classList.splice(index, 1);
let classList = this.classForm.classList;
++this.cascaderKey;
for (let inx = 0; inx < classList.length; inx++) {
for (let j = 0; j < list.length; j++) {
if (list[j].children.length) {
this.addChildren(
classList[inx].pupilOption,
list[j].label,
list[j].children
);
}
}
}
}
})
.catch(() => {});
},
/*
* fn 监听学生分配选中菜单
* @param val {Array} 选中的学生数据
* @param ind {Number} 当前展开的班级位置(索引)
*/
changeList(val, ind) {
let data = this.classForm.classList; // 所有班级
if (data.length > 1) {
let list = JSON.parse(JSON.stringify(data[ind].pupilOption)); // 深拷贝当前展开的所有学生
for (let i = 0; i < val.length; i++) {
// 循环选中的数据
for (let n = 0; n < data.length; n++) {
// 循环所有班级
if (n != ind) {
// 选中删除其他班级的学生节点
// 根据label删除节点数据
this.removeTreeListItem(data[n].pupilOption, val[i][1].label);
}
}
// 根据label删除节点数据(删除选中的,没选中的其他班级就应该有未选中的学生)
this.removeTreeListItem(list, val[i][1].label);
}
for (let inx = 0; inx < data.length; inx++) {
// 循环所有班级
if (inx != ind) {
// 不等于当前展开班级时
for (let j = 0; j < list.length; j++) {
// 循环未选中的所有学生
if (list[j].children.length) {
// 如果有学生
// 学生都添加到对应班级内
this.addChildren(
data[inx].pupilOption,
list[j].label,
list[j].children
);
}
}
}
}
++this.cascaderKey; // 上面把学生添加进班级会使element级联组件进行数据更新操作保证key是新值
data.forEach((item, iny) => {
// 再次循环所有班级
if (iny != ind) {
// 不等于当前班级进行过滤班级内学生已经被当前班级选完了
item.pupilOption = item.pupilOption.filter((el) => {
return el.children.length !== 0;
});
}
});
}
// 解决多个级联相互关联更新数据是多选自动收回的问题
setTimeout(() => {
this.$refs.cascaderShow[ind].dropDownVisible = true;
});
},
// 获取数据
getStudentInfo() {
this.get(url, {
id
}).then(res => {
if (res.data.code == 200) {
let data = res.data.data;
this.pupilOption = this.formatToTree(data);
this.classForm.classList[0].pupilOption = JSON.parse(JSON.stringify(this.pupilOption));
}
});
},
/*
* fn 把有父子关系的list数据过滤成树形数据,如:[{id:0,label:一级},{parentId:0,label:1-1}]
* @param ary {Array} 有父子关联是list
* @param pid {String} 父节点
*/
/* formatToTree(ary, pid) {
return ary
.filter((item) =>
// 如果没有父id(第一次递归的时候)将所有父级查询出来
// 这里认为 item.parentId === 1 就是最顶层 需要根据业务调整
pid === undefined ? item.parentId === 1 : item.parentId === pid
)
.map((item) => {
// 通过父节点ID查询所有子节点
item.children = formatToTree(ary, item.id);
return item;
});
}, */
// 后台数据{
/* "高一一班": [{"id": 6,"realname": "刘大红","grade_id": 1,"class_id": 1,"grade": "高一","class": "一班"}],
"高二五班": [{"id": 20,"realname": "学生-金","grade_id": 2,"class_id": 5,"grade": "高二","class": "五班"}], */
formatToTree(ary) {
let data = [];
let i = 0;
for (let key in ary) {
i++;
data.push({
value: i,
label: key,
all: {label: key},
children: []
});
ary[key].forEach(item => {
data[data.length - 1].children.push({
value: item.id,
label: item.realname,
all: {label: item.realname, value: item.id}
});
});
}
return data;
},
// 点击班级(展开或收起)
isShowItem(index) {
let maxH = this.$refs.showContent[index].style.maxHeight;
if (!maxH || maxH == "600px") {
this.$refs.showContent[index].style.maxHeight = "0px";
this.$refs.isUnfold[index].innerHTML = "展开";
} else {
this.$refs.showContent[index].style.maxHeight = "600px";
this.$refs.isUnfold[index].innerHTML = "收起";
}
},
// 点击添加分班
addClass(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
let obj = JSON.parse(JSON.stringify(this.initForm));
let data = JSON.parse(JSON.stringify(this.classForm.classList));
data.push(obj);
let len = data.length;
// 拿到前一个所有学生数据
let list = data[len - 2].pupilOption;
// 拿到前一个选中的学生数据
let selList = data[len - 2].value;
for (let i = 0; i < selList.length; i++) {
this.removeTreeListItem(list, selList[i][1].label);
}
let arr = list.filter((val) => {
return val.children.length !== 0;
});
if (arr.length) {
data[len - 1].pupilOption = arr;
++this.cascaderKey;
this.classForm.classList.push(data[len - 1]);
for (let i = 0; i < len - 1; i++) {
this.$refs.showContent[i].style.maxHeight = "0px";
this.$refs.isUnfold[i].innerHTML = "展开";
}
} else {
this.$message("学生已分配完");
}
} else {
return false;
}
});
},
},
};
// --------------------------------style--------------------------------------------------------------------
<style lang="less" scoped> /deep/.el-cascader__search-input{ display: none; } .course-right{ width: 900px; margin: 0 auto; .calss-box { width: 100%; height: 40px; background: -webkit-linear-gradient( left #5294f159,#7faff3); background: -moz-linear-gradient( right,#5294f159,#7faff3); background: -o-linear-gradient( right,#5294f159,#7faff3); background: linear-gradient(to right,#5294f159,#7faff3); border-radius: 10px; display: flex; align-items: center; color: rgb(255, 255, 255); font-weight: 500; margin-bottom: 15px; transition: transform .5s; cursor: pointer; .item-class { width: 100%; height: 100%; display: flex; align-items: center; position: relative; .line { width: 4px; height: 30px; border: 0; border-radius: 2px; background: #5294f1; margin: 0 15px 0 0; } .switch { position: absolute; right: 35px; padding: 0 20px; box-sizing: border-box; } .del-icon { font-size: 20px; padding: 15px; position: absolute; right: 10px; } } } .show-content { max-height: 600px; overflow: hidden; transition: max-Height .5s; .cmm-inp { width: 350px; } .info{ margin-left: 130px; width: 600px; height: 100px; border: 1px solid #e5eaf3; span{ padding: 30px 20px; font-size: 14px; color: #606266; } } } .add-class{ width: 400px; height: 40px; opacity: 1; background: #4e6ff4; border-radius: 20px; font-size: 18px; font-weight: 400; color: #ffffff; line-height: 40px; cursor: pointer; text-align: center; margin: 45px auto; } } </style>
- 以上复制把getStudentInfo获取数据的方法中的data换成以下
["高一一班": [{"id": 6,"realname": "刘大红","grade_id": 1,"class_id": 1,"grade": "高一","class": "一班"}],"高二五班": [{"id": 20,"realname": "学生-金","grade_id": 2,"class_id": 5,"grade": "高二","class": "五班"}]......]
浙公网安备 33010602011771号