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": "五班"}]......]

 

 

 

 

posted @ 2021-09-28 15:58  有梦想的小猪  阅读(1372)  评论(0)    收藏  举报