feat(user-management-relation): 在用户管理页面集成用户带人关系组件,并修复相关的诸多BUG和样式问题

This commit is contained in:
dk
2026-04-14 16:33:47 +08:00
parent 9b6f5955c3
commit a6fc7b48dc
7 changed files with 187 additions and 98 deletions

View File

@@ -15,21 +15,33 @@
* - 叶子节点:基层员工,没有下级
*/
import { nextTick, onMounted, reactive, ref } from 'vue';
import type { ElTree } from 'element-plus';
import { ElButton, ElPopconfirm, ElTag } from 'element-plus';
import { useBoolean } from '@sa/hooks';
import {nextTick, onMounted, reactive, ref} from 'vue';
import type {ElTree} from 'element-plus';
import {ElButton, ElPopconfirm, ElTag} from 'element-plus';
import {useBoolean} from '@sa/hooks';
import {
fetchBatchDeleteUserManagementRelation,
fetchDeleteUserManagementRelation,
fetchDeleteUserManagementRelation, fetchGetUserListByDeptId,
fetchGetUserManagementRelationQuery,
fetchGetUserManagementRelationTree,
fetchGetUserSimpleList
} from '@/service/api';
import RelationOperateDialog from './modules/relation-operate-dialog.vue';
import RelationSearch from './modules/relation-search.vue';
defineOptions({ name: 'UserManagementRelation' });
defineOptions({name: 'UserManagementRelation'});
/**
* 组件 userQuery 定义
*
* @param fromUserIndex 是否不是从带人关系 index 页面访问(从 user 页面访问时为 true
*/
interface userQuery {
fromUserIndex?: boolean;
deptId?: number | null;
}
//从user的index组件访问带人关系fromUserIndex为true否则false; dept=100是灿能电力的id
const {fromUserIndex = false, deptId = 100} = defineProps<userQuery>()
/**
* 初始化搜索参数
@@ -74,7 +86,7 @@ const treeProps: any = {
* 获取用户简单列表,供搜索组件和对话框组件共享使用
*/
async function loadUserList() {
const { data, error } = await fetchGetUserSimpleList();
const {data, error} = await fetchGetUserListByDeptId(deptId);
if (!error) {
userList.value = data || [];
}
@@ -89,7 +101,12 @@ async function loadTreeData() {
loading.value = true;
try {
const { data, error } = await fetchGetUserManagementRelationTree();
// 默认不是来自user的index组件访问且deptId=100查询灿能电力及其以下所有部门的用户的带人关系
const query: Api.SystemManage.UserManagementRelationQueryReqVO = {
fromUserIndex: fromUserIndex,
deptId: deptId
};
const {data, error} = await fetchGetUserManagementRelationTree(query);
if (!error) {
treeData.value = data || [];
@@ -110,7 +127,7 @@ async function loadTreeDataByQuery(query: Api.SystemManage.UserManagementRelatio
loading.value = true;
try {
const { data, error } = await fetchGetUserManagementRelationQuery(query);
const {data, error} = await fetchGetUserManagementRelationQuery(query);
if (!error) {
treeData.value = data || [];
@@ -143,13 +160,13 @@ async function handleSearch() {
// 判断是否有搜索条件
const hasSearchCondition = searchParams.subordinateUserId !== undefined && searchParams.subordinateUserId !== null;
if (hasSearchCondition) {
// 有搜索条件,调用查询接口
const query: Api.SystemManage.UserManagementRelationQueryReqVO = {
subordinateUserId: searchParams.subordinateUserId
subordinateUserId: searchParams.subordinateUserId,
fromUserIndex: fromUserIndex,
deptId: deptId
};
console.log('查询参数 query:', query);
await loadTreeDataByQuery(query);
} else {
// 无搜索条件,加载完整树
@@ -171,7 +188,7 @@ function resetSearchParams() {
}
// 对话框相关状态
const { bool: operateVisible, setTrue: openOperateModal, setFalse: closeOperateModal } = useBoolean();
const {bool: operateVisible, setTrue: openOperateModal, setFalse: closeOperateModal} = useBoolean();
const operateType = ref<UI.TableOperateType>('add');
const editingData = ref<Api.SystemManage.UserManagementRelation | null>(null);
@@ -186,14 +203,14 @@ function openAdd(item?: Api.SystemManage.UserManagementRelationTreeRespVO) {
// 否则默认管理者为当前登录用户(在对话框组件中处理)
editingData.value = item
? {
id: null,
managerUserId: item.userId,
subordinateUserId: null,
effectiveFrom: null,
effectiveUntil: null,
remark: null,
createTime: Date.now()
}
id: null,
managerUserId: item.userId,
subordinateUserId: null,
effectiveFrom: null,
effectiveUntil: null,
remark: null,
createTime: Date.now()
}
: null;
openOperateModal();
}
@@ -208,14 +225,14 @@ function openEdit(item: Api.SystemManage.UserManagementRelationTreeRespVO) {
// 构建树节点数据为编辑所需格式
editingData.value = item.id
? {
id: item.id,
managerUserId: item.managerUserId,
subordinateUserId: item.userId,
effectiveFrom: null,
effectiveUntil: null,
remark: null,
createTime: Date.now()
}
id: item.id,
managerUserId: item.managerUserId,
subordinateUserId: item.userId,
effectiveFrom: null,
effectiveUntil: null,
remark: null,
createTime: Date.now()
}
: null;
openOperateModal();
}
@@ -226,7 +243,7 @@ function openEdit(item: Api.SystemManage.UserManagementRelationTreeRespVO) {
* @param item 要删除的关系记录
*/
async function handleDelete(item: Api.SystemManage.UserManagementRelationTreeRespVO) {
const { error } = await fetchDeleteUserManagementRelation(item.id);
const {error} = await fetchDeleteUserManagementRelation(item.id);
if (error) {
return;
@@ -246,7 +263,7 @@ async function handleBatchDelete() {
return;
}
const { error } = await fetchBatchDeleteUserManagementRelation(checkedNodeKeys.value);
const {error} = await fetchBatchDeleteUserManagementRelation(checkedNodeKeys.value);
if (error) {
return;
@@ -287,6 +304,25 @@ function hasChildren(node: Api.SystemManage.UserManagementRelationTreeRespVO): b
return Boolean(node.children && node.children.length > 0);
}
/**
* 计算树形数据中所有节点的数量
*
* 递归遍历树形结构,统计所有节点总数
*
* @param nodes 树形数据数组
* @returns 节点总数
*/
function countTreeNodes(nodes: Api.SystemManage.UserManagementRelationTreeRespVO[]): number {
let count = 0;
for (const node of nodes) {
count += 1;
if (node.children && node.children.length > 0) {
count += countTreeNodes(node.children);
}
}
return count;
}
onMounted(async () => {
await loadUserList();
await reloadTreeData();
@@ -309,28 +345,28 @@ onMounted(async () => {
<div class="flex items-center justify-between gap-12px">
<div class="flex items-center gap-10px">
<p>用户带人关系树</p>
<ElTag effect="plain">{{ treeData.length }}</ElTag>
<ElTag effect="plain">{{ countTreeNodes(treeData) }}</ElTag>
</div>
<div class="flex items-center gap-10px">
<ElButton plain type="primary" @click="openAdd()">
<template #icon>
<icon-ic-round-plus class="text-icon" />
<icon-ic-round-plus class="text-icon"/>
</template>
新增
</ElButton>
<ElPopconfirm title="确认删除选中的关系吗?" @confirm="handleBatchDelete">
<template #reference>
<ElButton type="danger" plain :disabled="checkedNodeKeys.length === 0">
<template #icon>
<icon-ic-round-delete class="text-icon" />
</template>
批量删除
</ElButton>
</template>
</ElPopconfirm>
<!-- <ElPopconfirm title="确认删除选中的关系吗?" @confirm="handleBatchDelete">-->
<!-- <template #reference>-->
<!-- <ElButton type="danger" plain :disabled="checkedNodeKeys.length === 0">-->
<!-- <template #icon>-->
<!-- <icon-ic-round-delete class="text-icon"/>-->
<!-- </template>-->
<!-- 批量删除-->
<!-- </ElButton>-->
<!-- </template>-->
<!-- </ElPopconfirm>-->
<ElButton @click="reloadTreeData">
<template #icon>
<icon-ic-round-refresh class="text-icon" />
<icon-ic-round-refresh class="text-icon"/>
</template>
刷新
</ElButton>
@@ -346,7 +382,6 @@ onMounted(async () => {
:data="treeData"
:props="treeProps"
node-key="userId"
show-checkbox
default-expand-all
:expand-on-click-node="false"
@check="handleNodeCheck"
@@ -355,18 +390,18 @@ onMounted(async () => {
<div class="flex flex-1 items-center justify-between">
<span class="flex items-center gap-8px">
<span>{{ node.label }}</span>
<ElTag v-if="data.managerNickname" size="small" type="info">上级{{ data.managerNickname }}</ElTag>
<!-- <ElTag v-if="data.managerNickname" size="small" type="info">上级{{ data.managerNickname }}</ElTag>-->
</span>
<div class="flex items-center">
<ElButton link type="primary" size="default" @click.stop="openAdd(data)">
<template #icon>
<icon-ic-round-plus class="text-icon" />
<icon-ic-round-plus class="text-icon"/>
</template>
新增
</ElButton>
<ElButton link type="primary" size="small" @click.stop="openEdit(data)">
<template #icon>
<icon-ic-round-edit class="text-icon" />
<icon-ic-round-edit class="text-icon"/>
</template>
编辑
</ElButton>
@@ -378,14 +413,13 @@ onMounted(async () => {
<template #reference>
<ElButton link type="danger" size="small">
<template #icon>
<icon-ic-round-delete class="text-icon" />
<icon-ic-round-delete class="text-icon"/>
</template>
删除
</ElButton>
</template>
</ElPopconfirm>
<ElTag v-else-if="hasChildren(data)" size="small" type="info" style="margin-left: 6px">有下级</ElTag>
<ElTag v-else size="small" type="warning">不可删除</ElTag>
<span v-else-if="hasChildren(data)" style="margin-left: 56px"></span>
</div>
</div>
</template>