回顾
回顾一下当时的问题,当时博主需要使用 el-tree 标签做一个带部门-用户层级的复选框
使用该标签开发完,出现了一个问题,当首次进入界面时,选中部门不能全选部门下的所有用户
当时反复鞭策了 Trae 很多轮,就是改不好,要么是这里改好了,右侧移除用户左侧的勾选框又改坏了
最终无奈,博主只要设置一进入界面,就默认展开所有部门-用户列表,也算是一种解决方案
相关博客
柳暗花明
今天试用了 Claude Code,配合 kimi-k2.5 模型,竟然三言两语就解决了
效果如下
看看人家代码是怎么写的
<template>
<div class="container">
<!-- 加一个按钮 显示/隐藏 树状框 -->
<el-button type="primary" @click="toggleTreeVisibility" style="margin-bottom: 10px; margin-right: 10px;">
{{ treeVisible ? '隐藏树状框' : '显示树状框' }}
</el-button>
<!-- 树状框 -->
<div class="left-section">
<el-tree
ref="tree"
v-if="treeVisible"
:props="props"
:data="treeData"
show-checkbox
node-key="userId"
@check="handleCheck"
:expanded-keys="expandedKeys"
style="width: 300px;">
</el-tree>
</div>
<!-- 已选择的用户框 -->
<div class="right-section">
<h3>已选择的用户</h3>
<div class="selected-users">
<el-tag
v-for="user in selectedUsers"
:key="user.userId"
closable
@close="removeUser(user.userId)"
style="margin: 5px;">
{{ user.userName }}
</el-tag>
<div v-if="selectedUsers.length === 0" class="empty-tip">
暂无选择的用户
</div>
</div>
<div class="button-group">
<el-button @click="clearAllUsers">取消</el-button>
<el-button type="success" @click="openDialog">确认选择</el-button>
</div>
</div>
<!-- 确认对话框 -->
<el-dialog
title="确认选择"
v-model="dialogVisible"
width="500px">
<span>确定要提交选中的用户吗?</span>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleCancel">取消</el-button>
<el-button type="primary" @click="handleConfirm">确认</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script>
import axios from 'axios'
export default {
data() {
return {
props: {
label: function (node) {
// 如果有 userVOList 属性,说明是部门节点,使用 departmentName
// 如果没有 userVOList 属性,说明是用户节点,使用 userName
return node.userVOList ? node.departmentName : node.userName;
},
children: 'userVOList'
},
treeData: [],
dialogVisible: false,
selectedUserIds: [],
treeVisible: false,
selectedUsers: [],
defaultExpandAll: true,
expandedKeys: []
};
},
methods: {
/**
* 切换树状框的显示/隐藏状态
*/
toggleTreeVisibility() {
this.treeVisible = !this.treeVisible;
if (this.treeVisible) {
this.loadTreeData();
}
},
/**
* 处理节点勾选状态变化(用户点击复选框时触发)
* 注意:Element Plus Tree 在 check-strictly=false 时会自动处理父子关联,
* 所以我们只需要更新 selectedUsers 列表,不需要手动调用 setChecked
*/
handleCheck(data, checkedInfo) {
// 使用 syncSelectedUsersWithTree 统一同步选中状态
// 因为 checkedInfo 是一个对象,不是布尔值,无法直接判断当前节点是否被选中
this.syncSelectedUsersWithTree();
// 如果是部门节点,展开该部门
if (data.userVOList) {
this.expandedKeys = [...this.expandedKeys, data.userId];
}
},
/**
* 同步 selectedUsers 与 Tree 当前勾选状态
*/
syncSelectedUsersWithTree() {
const tree = this.$refs.tree;
if (tree) {
// 获取所有选中的用户节点(叶子节点,即没有 userVOList 的节点)
const checkedNodes = tree.getCheckedNodes(false, true);
this.selectedUsers = checkedNodes
.filter(node => !node.userVOList)
.map(node => ({
userId: node.userId,
userName: node.userName
}));
}
},
/**
* 从选中列表移除用户
*/
removeUser(userId) {
console.log('移除用户:', userId);
// 从选中列表移除用户
this.selectedUsers = this.selectedUsers.filter(user => user.userId !== userId);
// 同时取消树节点的勾选状态
const tree = this.$refs.tree;
if (tree) {
console.log('树组件引用:', tree);
// 直接使用 setChecked 方法,根据 userId 取消勾选
// 第三个参数设置为 false,不递归处理子节点
tree.setChecked(userId, false, false);
console.log('取消勾选操作执行完成');
}
},
/**
* 清空所有选中的用户
*/
clearAllUsers() {
// 清空选中用户列表
this.selectedUsers = [];
// 取消所有树节点的勾选状态
const tree = this.$refs.tree;
if (tree) {
// 获取所有选中的节点的 key
const checkedKeys = tree.getCheckedKeys(true);
// 取消每个节点的勾选状态
checkedKeys.forEach(key => {
tree.setChecked(key, false, false);
});
}
},
/**
* 加载部门和用户数据到树结构
*/
loadTreeData() {
axios.post('http://localhost:8080/departments')
.then(response => {
if (response.data.status === 'success') {
this.treeData = response.data.data;
}
})
.catch(error => {
console.error('加载数据失败:', error);
});
},
/**
* 打开确认对话框
*/
openDialog() {
this.dialogVisible = true;
},
/**
* 处理确认提交选中的用户ID
*/
handleConfirm() {
// 从selectedUsers中提取用户ID
this.selectedUserIds = this.selectedUsers.map(user => user.userId);
// 调用后端接口
this.uploadUsers();
// 关闭对话框
this.dialogVisible = false;
},
/**
* 处理取消提交选中的用户ID
*/
handleCancel() {
this.dialogVisible = false;
},
/**
* 提交选中的用户ID到后端
*/
uploadUsers() {
// 调用后端接口
axios.post('http://localhost:8080/uploadUsers', {
userIds: this.selectedUserIds
})
.then(response => {
console.log('接口调用成功:', response.data);
this.$message.success('提交成功');
})
.catch(error => {
console.error('接口调用失败:', error);
this.$message.error('提交失败');
});
}
}
};
</script>
<style scoped>
.container {
display: flex;
gap: 20px;
padding: 20px;
}
.left-section {
flex: 0 0 300px;
}
.right-section {
flex: 1;
border: 1px solid #e4e7ed;
border-radius: 4px;
padding: 15px;
padding-bottom: 80px; /* 为固定按钮留出空间 */
min-width: 300px;
min-height: 400px;
position: relative;
}
.right-section h3 {
margin-top: 0;
margin-bottom: 15px;
font-size: 16px;
font-weight: 500;
}
.selected-users {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.empty-tip {
color: #909399;
font-size: 14px;
margin-top: 10px;
}
.button-group {
display: flex;
justify-content: flex-end;
gap: 10px;
padding: 15px;
border-top: 1px solid #e4e7ed;
position: absolute;
bottom: 0;
right: 0;
left: 0;
background-color: #ffffff;
}
/* 树节点样式,增加文本与复选框之间的间距 */
:deep(.el-tree-node__content) {
padding: 4px 0;
}
/* 复选框与文本之间的间距 */
:deep(.el-checkbox__inner) {
margin-right: 8px;
}
</style>