feat(gerp): 部门/公司详情员工卡片标题显示角色数与用户数

部门详情卡片标题改为「部门员工(角色数:x/用户数:y)」,
公司详情改为「员工(角色数:x/用户数:y)」:
角色数取列表 total(用户-角色关系条数),用户数取后端新增的 user_count(去重用户数)。

计数与子表同源:复用 List 接口带 WithUserCount=1 只取 1 行拿计数,保证与表格
完全相同的 DeptUUID/CompanyUUID 过滤;批量调整职务成功后与子表一起刷新。
PagedData 类型补充可选的 user_count 字段。
This commit is contained in:
TermiNexus committed 2026-10-01 23:16:14 +08:00
1 parent dfed6ef8bc
commit 66304f9c5e
3 files changed
+79 -6

No files matched your search

+6
View File
@@ -21,6 +21,12 @@ export interface PagedData<T> {
page: number
/** 每页大小 */
pageSize: number
/**
* 去重用户数(仅请求带 WithUserCount=1 时后端才返回)
*
* 员工卡片标题「角色数:x/用户数:y」用:total 是角色(关系)条数,本字段是去重用户数。
*/
user_count?: number
}
/** statusMap 单个状态的渲染配置 */
+36 -3
View File
@@ -70,6 +70,10 @@ const resolvedNames = ref<Record<string, Record<string, string>>>({})
/** 从路由 query 读取公司 UUID(computed 响应式:同一 tab 复用且 query 变化时自动更新) */
const companyUUID = computed(() => (route.query.CompanyUUID as string) || '')
// 员工计数(卡片标题「角色数:x/用户数:y」)
// 角色数 = 用户-角色关系条数;用户数 = 去重用户数(同一人挂多个角色只算一个)
const empCounts = ref<{ roles: number; users: number }>({ roles: 0, users: 0 })
/** 公司主键字段名 */
const PK_FIELD = 'CompanyUUID'
@@ -150,6 +154,8 @@ async function loadCompany(): Promise<void> {
}
companyData.value = data
setPageTitle(asStr(data.CompanyName) || '公司详情')
// 员工计数与子表同源(同样的 CompanyUUID 过滤),随主数据一起刷新
void loadEmpCounts()
// 解析 ParentCompanyUUID(Company 表)与 TenantUUID(Tenant 表)
const uuidMap: Record<string, { table: string; uuids: string[] }> = {}
@@ -169,6 +175,33 @@ async function loadCompany(): Promise<void> {
}
}
/**
* 加载「员工(角色数:x/用户数:y)」
*
* 计数必须与子表同源(同样的 CompanyUUID 过滤条件),所以复用 List 接口:
* 只要 1 行数据,靠 total 拿角色数、user_count 拿去重用户数(后端按 WithUserCount 现算)。
*/
async function loadEmpCounts(): Promise<void> {
if (!companyUUID.value) return
try {
const result = await formApi.list<RowData>('UserRole', {
CompanyUUID: companyUUID.value,
WithUserCount: 1,
pageSize: 1,
})
const paged = result as unknown as { total?: number; user_count?: number }
empCounts.value = { roles: paged?.total ?? 0, users: paged?.user_count ?? 0 }
} catch {
// 计数失败不影响页面主体(错误提示已由响应拦截器统一处理)
}
}
/** 刷新员工子表 + 计数(计数与子表同源,必须一起更新) */
function refreshEmployees(): void {
empTableRef.value?.refresh?.()
void loadEmpCounts()
}
// ===== 子表行跳转(onView)=====
/** 跳转部门详情 */
function goDeptDetail(row: RowData): void {
@@ -475,7 +508,7 @@ watch(companyUUID, () => {
<template #header>
<div class="detail-card-header">
<el-icon><User /></el-icon>
<span>员工</span>
<span>员工(角色数:{{ empCounts.roles }}/用户数:{{ empCounts.users }})</span>
</div>
</template>
<CrudTable
@@ -489,7 +522,7 @@ watch(companyUUID, () => {
pk-field="UserRoleUUID"
:on-view="goEmployeeDetail"
>
<template #toolbar="{ refresh }">
<template #toolbar>
<el-button type="warning" @click="openBatchDialog">
<el-icon><Edit /></el-icon>批量调整职务
</el-button>
@@ -499,7 +532,7 @@ watch(companyUUID, () => {
:context="{ CompanyUUID: companyUUID }"
:preselected="batchPreselected"
title="员工批量调整职务"
@success="refresh"
@success="refreshEmployees"
/>
</template>
</CrudTable>
+37 -3
View File
@@ -51,6 +51,10 @@ const editSubmitting = ref(false)
const columns = ref<ColumnSchema[]>([])
const editForm = reactive<Record<string, any>>({})
// 部门员工计数(卡片标题「角色数:x/用户数:y」)
// 角色数 = 用户-角色关系条数;用户数 = 去重用户数(同一人挂多个角色只算一个)
const empCounts = ref<{ roles: number; users: number }>({ roles: 0, users: 0 })
// 主键字段
const PK_FIELD = 'DeptUUID'
@@ -89,11 +93,41 @@ async function loadData(): Promise<void> {
setPageTitle(row.DeptName || '部门详情')
// 解析上级部门 UUID → 名称
await resolveParentDept(row.ParentDeptUUID)
// 计数依赖 row.CompanyUUID,必须等主数据就位后再取
await loadEmpCounts()
} finally {
loading.value = false
}
}
/**
* 加载「部门员工(角色数:x/用户数:y)」
*
* 计数必须与子表同源(同样的 DeptUUID + CompanyUUID 过滤条件),所以复用 List 接口:
* 只要 1 行数据,靠 total 拿角色数、user_count 拿去重用户数(后端按 WithUserCount 现算)。
*/
async function loadEmpCounts(): Promise<void> {
if (!deptUUID.value) return
try {
const result = await formApi.list<Record<string, unknown>>('UserRoleDepartment', {
DeptUUID: deptUUID.value,
CompanyUUID: data.value?.CompanyUUID || '',
WithUserCount: 1,
pageSize: 1,
})
const paged = result as unknown as { total?: number; user_count?: number }
empCounts.value = { roles: paged?.total ?? 0, users: paged?.user_count ?? 0 }
} catch {
// 计数失败不影响页面主体(错误提示已由响应拦截器统一处理)
}
}
/** 刷新子表 + 计数(计数与子表同源,必须一起更新) */
function refreshEmployees(): void {
deptTableRef.value?.refresh?.()
void loadEmpCounts()
}
/** 解析 ParentDeptUUID → DeptName */
async function resolveParentDept(parentUUID?: string): Promise<void> {
if (!parentUUID) {
@@ -224,7 +258,7 @@ watch(deptUUID, () => {
<template #header>
<div class="detail-card-header">
<el-icon><User /></el-icon>
<span>部门员工</span>
<span>部门员工(角色数:{{ empCounts.roles }}/用户数:{{ empCounts.users }})</span>
</div>
</template>
<!-- 部门成员须走入职流程(EmployeeProfile → UserRole/UserRoleDepartment),
@@ -242,7 +276,7 @@ watch(deptUUID, () => {
:max-height="400"
:on-view="goUserDetail"
>
<template #toolbar="{ refresh }">
<template #toolbar>
<el-button type="warning" @click="openBatchDialog">
<el-icon><Edit /></el-icon>批量调整职务
</el-button>
@@ -252,7 +286,7 @@ watch(deptUUID, () => {
:context="{ DeptUUID: deptUUID, CompanyUUID: data?.CompanyUUID || '' }"
:preselected="batchPreselected"
title="部门员工批量调整职务"
@success="refresh"
@success="refreshEmployees"
/>
</template>
</CrudTable>