多级嵌套手风琴组件设计与实现
一个可复用的多级嵌套手风琴组件,实现层级独立控制,点击子级不影响父级,点击兄弟层级关闭其他兄弟的效果。
Collapse 折叠面板
无限层级嵌套实现方法,需求是在构建一个部门树,部门是后端一次性返回的,部门人员通过另外一个接口返回
一级二级图片
二级嵌套
引入组件
<member-node
:nodes="DeptTree"
:level="0"
:checkList="checkList"
:roleType="roleType"
@check-change="handleCheckChange"
/>
handleCheckChange(user, checked) {
console.log("用户:", user);
console.log("选中状态:", checked);
if (user.checkeds) {
// 添加到选中列表
console.log("用户:", !this.checkList.some((u) => u.userId === user.userId))
if (!this.checkList.some((u) => u.userId === user.userId)) {
this.checkList.push({ ...user });
}
} else {
// 从选中列表中移除
const index = this.checkList.findIndex((u) => u.userId === user.userId);
if (index !== -1) {
this.checkList.splice(index, 1);
}
}
console.log("选中用户:", this.checkList);
},
组件代码
<template>
<div>
<el-collapse
accordion
v-model="activeName"
v-for="item in nodes"
:key="item.skillGroupId"
@change="handleChange(item)"
>
<el-collapse-item :name="item.skillGroupId">
<template slot="title">
<div class="member_item">
<div class="icon"></div>
<div class="name">{{ item.skillGroupName }}</div>
</div>
</template>
<div style="margin-left: 20px; min-height: 50px">
<!-- 递归渲染子节点 -->
<member-node
v-if="item.children && item.children.length"
:nodes="item.children"
:level="level + 1"
:checkList="checkList"
:roleType="roleType"
@item-change="handleChildChange"
@check-change="$emit('check-change', $event)"
></member-node>
<div
style="margin-top: 10px; margin-left: 0px"
class="user_list"
v-for="(user, userIndex) in users || []"
:key="'user_' + userIndex"
>
<el-checkbox
style="display: flex; align-items: center"
v-model="user.checkeds"
@change="onChecked(user)"
:disabled="user.isSysFlag === '0' || user.disable"
>
<div class="membersBox_content_right">
<div class="user">
<span v-if="user.imgFlag == 1">{{
formatString(user.userName)
}}</span>
<img
style="display: inline-block"
v-else
:src="formatPhoto(user.userId)"
alt="后端图片"
/>
</div>
<div class="userinfo">
<div class="userinfo_box">
<div class="username">
{{ user.userName }}
</div>
</div>
<div class="user_id">
<span style="margin-right: 10px" v-if="user.userPosition">{{
user.userPosition
}}</span>
<span
style="margin-right: 10px"
v-if="user.skillGroupName"
>{{ user.skillGroupName }}</span
>{{ user.userId }}
</div>
</div>
</div>
</el-checkbox>
</div>
<!-- </div> -->
</div>
</el-collapse-item>
</el-collapse>
</div>
</template>
<script>
export default {
name: "MemberNode",
data() {
return {
activeName: "",
// 当前节点的用户列表
users: [],
};
},
props: {
nodes: {
type: Array,
default: () => [],
},
level: {
type: Number,
default: 0,
},
checkList: {
type: Array,
default: () => [],
},
roleType: String,
},
watch: {
nodes() {
this.activeName = "";
this.users = [];
},
checkList: {
handler(newVal, oldVal) {
// console.log(newVal, oldVal);
if (this.checkList.length > 0) {
this.users.forEach((item) => {
item.checkeds = false;
this.checkList.forEach((checkItem) => {
if (item.userId === checkItem.userId) {
item.checkeds = true;
}
});
});
}
},
deep: true, // 开启深度监听
},
},
methods: {
handleChildChange(item) {
console.log(item);
this.$emit("item-change", item);
},
handleChange(data) {
const isActive = this.activeName === data.skillGroupId;
if (!isActive) {
console.log("该项已展开,不会重复请求数据");
return; // 如果已经展开,直接返回,避免重复请求
} else {
// this.localActiveName = data.skillGroupId;
this.$emit("item-change", data.skillGroupId);
}
console.log("当前展开项:", this.localActiveName);
console.log("请求数据checkList:", this.checkList);
},
onChecked(user) {
console.log("用户选中状态变更:", user);
console.log("当前选中用户:", user.checkeds);
// this.logEvent(`用户选中状态变更: ${user.userName} - ${user.checkeds}`);
this.$emit("check-change", user, user.checkeds);
},
formatString(name) {
// 格式化用户名字逻辑
return name.slice(-2);
},
formatPhoto(userId) {
// 获取用户头像 URL 逻辑
},
},
};
</script>