feat(ehrm): 用户详情员工档案改为单条档案面板(去表格)
去掉员工档案子表,改为一人一份的档案面板:无档案且有 eHRM 权限(菜单可见)时居中显示「点击填写」(抽屉动态表单),无权限显示「无权修改」;已有档案时用 el-descriptions 键值只读展示,卡片右上角提供编辑/删除。表单与列表共用 /API/FormAPI/Columns/EmployeeProfile 列定义,UserUUID 由本页上下文注入。撤回上一版 CrudTable 的 singleRow 属性(该方案已废弃)。
This commit is contained in:
1 parent
1cdc44c1d0
commit
cf621cb9ec
1 file changed
+244
-14
+244
-14
@@ -25,17 +25,18 @@
|
||||
*/
|
||||
import { ref, reactive, computed, onMounted, watch } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import DetailPage from '@/components/DetailPage.vue'
|
||||
import InfoGrid from '@/components/InfoGrid.vue'
|
||||
import CrudTable from '@/components/CrudTable.vue'
|
||||
import CrudFormPanel from '@/components/CrudFormPanel.vue'
|
||||
import EntityPicker from '@/components/EntityPicker.vue'
|
||||
import { formApi } from '@/api/form'
|
||||
import { get } from '@/api/request'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { getServerScopeParams, setPageTitle } from '@/utils/serverScope'
|
||||
import { formatUtcTime as formatTime } from '@/utils/dateTime'
|
||||
import type { ColumnSchema } from '@/types/api'
|
||||
import type { ColumnSchema, MenuItem } from '@/types/api'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
@@ -199,8 +200,171 @@ async function handleSubmit(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
// === 子表跳转 ===
|
||||
// ===== 员工档案(eHRM)=====
|
||||
// 一人一份:无档案时给「点击填写」入口,已有档案时键值只读展示 + 右上角编辑/删除。
|
||||
// 是否能写按 eHRM 权限判定(后端按应用安装 + 职务绑定过滤菜单,菜单可见即视为有权限)。
|
||||
// 后端 form_service 已对「同一员工重复建档 / 改绑到已有档案的员工」做拦截。
|
||||
|
||||
/** eHRM 档案管理菜单路由(清单 MenuLink /ehrm/profile 规范化后的 Path) */
|
||||
const EHRM_PROFILE_PATH = '/ehrm/profile'
|
||||
|
||||
/** 档案只读展示时排除的列:主键与纯 UUID 引用列(员工=本页用户,部门已有 DeptName 展示) */
|
||||
const PROFILE_HIDDEN_FIELDS = ['EmployeeProfileUUID', 'UserUUID', 'DeptUUID', 'TenantUUID', 'CompanyUUID']
|
||||
|
||||
const profileLoading = ref(false)
|
||||
/** 该员工的档案记录(一人一份,最多一条) */
|
||||
const profileRecord = ref<Record<string, any> | null>(null)
|
||||
/** 档案列定义(只读展示与填写表单共用) */
|
||||
const profileColumns = ref<ColumnSchema[]>([])
|
||||
const profileDialogVisible = ref(false)
|
||||
const profileSubmitting = ref(false)
|
||||
/** 表单是否为编辑态(false=首次填写) */
|
||||
const profileIsEdit = ref(false)
|
||||
const profileForm = reactive<Record<string, any>>({})
|
||||
|
||||
/** 菜单树中是否存在指定路由(判断 eHRM 档案管理菜单是否可见) */
|
||||
function menuTreeHasPath(items: MenuItem[], path: string): boolean {
|
||||
return items.some(
|
||||
(m) => m.Path === path || (m.Children?.length ? menuTreeHasPath(m.Children, path) : false),
|
||||
)
|
||||
}
|
||||
|
||||
/** 是否具备 eHRM 档案修改权限(菜单可见即视为有权限) */
|
||||
const canEditProfile = computed(() => menuTreeHasPath(userStore.menus, EHRM_PROFILE_PATH))
|
||||
|
||||
/** 只读展示列:在表格可见列基础上排除主键与纯 UUID 引用列 */
|
||||
const profileDisplayColumns = computed(() =>
|
||||
profileColumns.value.filter(
|
||||
(c) =>
|
||||
c.visible !== false &&
|
||||
c.type !== 'hidden' &&
|
||||
c.type !== 'divider' &&
|
||||
c.type !== 'html' &&
|
||||
!PROFILE_HIDDEN_FIELDS.includes(c.field),
|
||||
),
|
||||
)
|
||||
|
||||
/** 填写/编辑表单列:排除隐藏/主键/非表单类型,以及由本页上下文注入的 UserUUID */
|
||||
const profileFormColumns = computed(() =>
|
||||
profileColumns.value.filter(
|
||||
(c) =>
|
||||
c.editable !== false &&
|
||||
!c.hideInForm &&
|
||||
c.type !== 'hidden' &&
|
||||
c.type !== 'divider' &&
|
||||
c.type !== 'html' &&
|
||||
c.field !== 'EmployeeProfileUUID' &&
|
||||
c.field !== 'UserUUID',
|
||||
),
|
||||
)
|
||||
|
||||
/** 只读展示值:实体标签(*_label)优先 → 选项映射 → 日期格式化 → 原值 */
|
||||
function profileValue(col: ColumnSchema): string {
|
||||
const row = profileRecord.value
|
||||
if (!row) return '-'
|
||||
const label = row[`${col.field}_label`]
|
||||
if (label) return String(label)
|
||||
const raw = row[col.field]
|
||||
if (raw === null || raw === undefined || raw === '') return '-'
|
||||
if (col.options?.length) {
|
||||
const hit = col.options.find((o) => String(o.value) === String(raw))
|
||||
if (hit) return hit.label
|
||||
}
|
||||
if (col.type === 'date' || col.type === 'datetime') return formatTime(String(raw))
|
||||
return String(raw)
|
||||
}
|
||||
|
||||
/** 加载该员工的档案(列表按 UserUUID 过滤,取首条) */
|
||||
async function loadProfile(): Promise<void> {
|
||||
if (!userUUID.value) {
|
||||
profileRecord.value = null
|
||||
return
|
||||
}
|
||||
profileLoading.value = true
|
||||
try {
|
||||
if (!profileColumns.value.length) {
|
||||
profileColumns.value = await formApi.getColumns('EmployeeProfile')
|
||||
}
|
||||
} catch {
|
||||
// 列定义加载失败不阻塞只读展示
|
||||
}
|
||||
try {
|
||||
const res = await formApi.list<Record<string, any>>('EmployeeProfile', {
|
||||
UserUUID: userUUID.value,
|
||||
pageSize: 1,
|
||||
})
|
||||
const list = Array.isArray(res) ? res : ((res as { list?: Record<string, any>[] })?.list ?? [])
|
||||
profileRecord.value = list[0] ?? null
|
||||
} catch {
|
||||
// 错误提示已由响应拦截器统一弹出
|
||||
profileRecord.value = null
|
||||
} finally {
|
||||
profileLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 「点击填写」:新建档案(UserUUID 由本页上下文注入,表单内不出现该字段) */
|
||||
function openProfileCreate(): void {
|
||||
if (!canEditProfile.value) return
|
||||
Object.keys(profileForm).forEach((k) => delete profileForm[k])
|
||||
profileForm.UserUUID = userUUID.value
|
||||
profileIsEdit.value = false
|
||||
profileDialogVisible.value = true
|
||||
}
|
||||
|
||||
/** 「编辑」:回填现有档案 */
|
||||
function openProfileEdit(): void {
|
||||
if (!canEditProfile.value || !profileRecord.value) return
|
||||
Object.keys(profileForm).forEach((k) => delete profileForm[k])
|
||||
Object.assign(profileForm, profileRecord.value)
|
||||
profileForm.UserUUID = userUUID.value
|
||||
profileIsEdit.value = true
|
||||
profileDialogVisible.value = true
|
||||
}
|
||||
|
||||
/** 提交档案表单(新增 Add / 编辑 Modify) */
|
||||
async function submitProfile(): Promise<void> {
|
||||
profileSubmitting.value = true
|
||||
try {
|
||||
if (profileIsEdit.value && profileRecord.value) {
|
||||
await formApi.update('EmployeeProfile', profileRecord.value.EmployeeProfileUUID as string, {
|
||||
...profileForm,
|
||||
})
|
||||
} else {
|
||||
await formApi.create('EmployeeProfile', { ...profileForm })
|
||||
}
|
||||
ElMessage.success(profileIsEdit.value ? '保存成功' : '填写成功')
|
||||
profileDialogVisible.value = false
|
||||
await loadProfile()
|
||||
} catch {
|
||||
// 错误提示已由响应拦截器统一弹出(如"该员工已存在档案")
|
||||
} finally {
|
||||
profileSubmitting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 「删除」:删除后可重新填写 */
|
||||
async function deleteProfile(): Promise<void> {
|
||||
if (!canEditProfile.value || !profileRecord.value) return
|
||||
try {
|
||||
await ElMessageBox.confirm('确认删除该员工的档案?删除后可重新填写。', '删除档案', {
|
||||
type: 'warning',
|
||||
confirmButtonText: '删除',
|
||||
cancelButtonText: '取消',
|
||||
})
|
||||
} catch {
|
||||
return // 用户取消
|
||||
}
|
||||
try {
|
||||
await formApi.remove('EmployeeProfile', profileRecord.value.EmployeeProfileUUID as string)
|
||||
ElMessage.success('已删除')
|
||||
await loadProfile()
|
||||
} catch {
|
||||
// 错误提示已由响应拦截器统一弹出
|
||||
}
|
||||
}
|
||||
|
||||
// === 子表跳转 ===
|
||||
/** UserRole 子表查看 → 角色详情 */
|
||||
function goRoleDetail(row: Record<string, any>): void {
|
||||
router.push({ path: '/system/role-detail', query: { RoleUUID: row.RoleUUID || '' } })
|
||||
@@ -216,6 +380,7 @@ onMounted(async () => {
|
||||
// 空值场景静默显示空态(loadData 内处理),不再弹警告
|
||||
watch(userUUID, () => {
|
||||
void loadData()
|
||||
void loadProfile()
|
||||
}, { immediate: true })
|
||||
</script>
|
||||
|
||||
@@ -337,24 +502,47 @@ watch(userUUID, () => {
|
||||
/>
|
||||
</el-card>
|
||||
|
||||
<!-- eHRM 联动子表:员工档案 / 考核 / 绩效(query 中的 UserUUID 自动注入新增/编辑表单) -->
|
||||
<!-- 员工档案(eHRM,一人一份):无档案给填写入口,有档案键值只读展示 + 右上角编辑/删除 -->
|
||||
<el-card shadow="never" class="detail-card">
|
||||
<template #header>
|
||||
<div class="detail-card-header">
|
||||
<el-icon><Document /></el-icon>
|
||||
<span>员工档案(eHRM)</span>
|
||||
<template v-if="profileRecord && canEditProfile">
|
||||
<span class="profile-header-actions">
|
||||
<el-button link type="primary" size="small" @click="openProfileEdit">
|
||||
<el-icon><Edit /></el-icon>编辑
|
||||
</el-button>
|
||||
<el-button link type="danger" size="small" @click="deleteProfile">
|
||||
<el-icon><Delete /></el-icon>删除
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<CrudTable
|
||||
v-if="userUUID"
|
||||
table="EmployeeProfile"
|
||||
title="员工档案"
|
||||
:query="{ UserUUID: userUUID }"
|
||||
pk-field="EmployeeProfileUUID"
|
||||
selectable
|
||||
pageable
|
||||
:max-height="400"
|
||||
/>
|
||||
|
||||
<div v-loading="profileLoading" class="profile-panel">
|
||||
<!-- 已有档案:键值只读展示(不可更改) -->
|
||||
<el-descriptions v-if="profileRecord" :column="3" border class="profile-desc">
|
||||
<el-descriptions-item
|
||||
v-for="col in profileDisplayColumns"
|
||||
:key="col.field"
|
||||
:label="col.title"
|
||||
>
|
||||
{{ profileValue(col) }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<!-- 无档案 + 有 eHRM 修改权限:居中「点击填写」 -->
|
||||
<div v-else-if="canEditProfile" class="profile-empty">
|
||||
<el-button type="primary" size="large" @click="openProfileCreate">
|
||||
<el-icon><Plus /></el-icon>点击填写
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 无档案 + 无 eHRM 修改权限 -->
|
||||
<el-empty v-else description="无权修改" :image-size="70" />
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="never" class="detail-card">
|
||||
@@ -481,6 +669,17 @@ watch(userUUID, () => {
|
||||
<el-button type="primary" :loading="editSubmitting" @click="handleSubmit">保存</el-button>
|
||||
</template>
|
||||
</el-drawer>
|
||||
|
||||
<!-- 员工档案 填写/编辑表单(动态列渲染,UserUUID 由本页上下文注入) -->
|
||||
<CrudFormPanel
|
||||
v-model:visible="profileDialogVisible"
|
||||
:title="profileIsEdit ? '编辑员工档案' : '填写员工档案'"
|
||||
:submitting="profileSubmitting"
|
||||
:columns="profileFormColumns"
|
||||
:form-model="profileForm"
|
||||
mode="drawer"
|
||||
@submit="submitProfile"
|
||||
/>
|
||||
</DetailPage>
|
||||
</template>
|
||||
|
||||
@@ -504,4 +703,35 @@ watch(userUUID, () => {
|
||||
flex-shrink: 0;
|
||||
background: var(--el-fill-color-light);
|
||||
}
|
||||
|
||||
// ===== 员工档案面板(eHRM)=====
|
||||
// 卡片头右侧操作按钮(编辑/删除)推到最右
|
||||
.profile-header-actions {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
// 面板主体:加载态用 v-loading,故给最小高度避免抖动
|
||||
.profile-panel {
|
||||
min-height: 72px;
|
||||
}
|
||||
|
||||
// 只读键值表:标签区紧凑 + 内容换行
|
||||
.profile-desc {
|
||||
:deep(.el-descriptions__label) {
|
||||
width: 110px;
|
||||
font-weight: 500;
|
||||
color: var(--el-text-color-regular);
|
||||
}
|
||||
}
|
||||
|
||||
// 无档案空态:居中「点击填写」
|
||||
.profile-empty {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 24px 0;
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user