377 lines
12 KiB
Vue
377 lines
12 KiB
Vue
<template>
|
||
<div class="table-box">
|
||
<ProTable
|
||
ref="proTable"
|
||
title="正式员工档案"
|
||
:columns="columns"
|
||
:request-api="onboardingTaskApi.employeePage"
|
||
>
|
||
<template #operation="scope">
|
||
<el-button link type="primary" icon="UserFilled" @click="showDetail(scope.row)">
|
||
查看完整档案
|
||
</el-button>
|
||
</template>
|
||
</ProTable>
|
||
|
||
<!-- 员工完整档案抽屉 -->
|
||
<el-drawer
|
||
v-model="detailVisible"
|
||
:title="`员工档案详情 · ${detail?.employee?.employeeName || ''}`"
|
||
size="820px"
|
||
destroy-on-close
|
||
>
|
||
<div v-if="detail?.employee" class="employee-drawer-content">
|
||
<!-- 头部个人简要信息 -->
|
||
<div class="employee-header-card">
|
||
<el-avatar :size="64" class="employee-avatar">
|
||
{{ detail.employee.employeeName ? detail.employee.employeeName.substring(0, 1) : '员' }}
|
||
</el-avatar>
|
||
<div class="employee-header-info">
|
||
<div class="employee-name-row">
|
||
<span class="employee-name">{{ detail.employee.employeeName }}</span>
|
||
<el-tag size="small" :type="detail.employee.gender === '女' ? 'danger' : 'primary'" effect="plain">
|
||
{{ detail.employee.gender || '未知' }}
|
||
</el-tag>
|
||
<el-tag size="small" type="success">正式员工</el-tag>
|
||
</div>
|
||
<div class="employee-meta-row">
|
||
<span>员工编号:{{ detail.employee.employeeNo || '-' }}</span>
|
||
<span>联系电话:{{ detail.employee.phone }}</span>
|
||
<span>入职日期:{{ detail.employee.entryDate || '-' }}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<el-tabs v-model="activeTab" class="employee-tabs">
|
||
<!-- 1. 基本人事档案 -->
|
||
<el-tab-pane label="基本人事档案" name="basic">
|
||
<el-card shadow="never" class="section-card">
|
||
<template #header>
|
||
<div class="card-header-title">身份与联系信息</div>
|
||
</template>
|
||
<el-descriptions :column="2" border>
|
||
<el-descriptions-item label="姓名">{{ detail.employee.employeeName }}</el-descriptions-item>
|
||
<el-descriptions-item label="性别">{{ detail.employee.gender || '-' }}</el-descriptions-item>
|
||
<el-descriptions-item label="联系电话">{{ detail.employee.phone }}</el-descriptions-item>
|
||
<el-descriptions-item label="身份证号码">{{ detail.employee.idCardNumber }}</el-descriptions-item>
|
||
<el-descriptions-item label="出生日期">{{ detail.employee.birthDate || '-' }}</el-descriptions-item>
|
||
<el-descriptions-item label="民族">{{ detail.employee.nation || '-' }}</el-descriptions-item>
|
||
<el-descriptions-item label="户口所在地" :span="2">
|
||
{{ detail.employee.registeredResidence || '-' }}
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="现居通讯地址" :span="2">
|
||
{{ detail.employee.contactAddress || '-' }}
|
||
</el-descriptions-item>
|
||
</el-descriptions>
|
||
</el-card>
|
||
|
||
<el-card shadow="never" class="section-card mt-4">
|
||
<template #header>
|
||
<div class="card-header-title">学历与就职信息</div>
|
||
</template>
|
||
<el-descriptions :column="2" border>
|
||
<el-descriptions-item label="毕业院校">{{ detail.employee.college || '-' }}</el-descriptions-item>
|
||
<el-descriptions-item label="最高学历">{{ detail.employee.education || '-' }}</el-descriptions-item>
|
||
<el-descriptions-item label="所学专业">{{ detail.employee.major || '-' }}</el-descriptions-item>
|
||
<el-descriptions-item label="部门名称">{{ detail.employee.departmentName || '-' }}</el-descriptions-item>
|
||
<el-descriptions-item label="合同社保单位">{{ detail.employee.socialInsuranceCompany || '-' }}</el-descriptions-item>
|
||
<el-descriptions-item label="合同性质">{{ detail.employee.contractNature || '全职劳动合同' }}</el-descriptions-item>
|
||
<el-descriptions-item label="合同开始日期">{{ detail.employee.contractStartDate || '-' }}</el-descriptions-item>
|
||
<el-descriptions-item label="合同终止日期">{{ detail.employee.contractEndDate || '-' }}</el-descriptions-item>
|
||
</el-descriptions>
|
||
</el-card>
|
||
|
||
<el-card shadow="never" class="section-card mt-4">
|
||
<template #header>
|
||
<div class="card-header-title">工资卡账户信息</div>
|
||
</template>
|
||
<el-descriptions :column="2" border>
|
||
<el-descriptions-item label="银行卡号">{{ detail.employee.bankCardNumber || '-' }}</el-descriptions-item>
|
||
<el-descriptions-item label="开户人姓名">{{ detail.employee.bankAccountName || detail.employee.employeeName }}</el-descriptions-item>
|
||
<el-descriptions-item label="工资卡开户行" :span="2">{{ detail.employee.bankName || '-' }}</el-descriptions-item>
|
||
</el-descriptions>
|
||
</el-card>
|
||
</el-tab-pane>
|
||
|
||
<!-- 2. 归档合同与协议 -->
|
||
<el-tab-pane label="归档合同协议" name="contracts">
|
||
<el-empty v-if="!detail.contracts?.length" description="暂无已归档的劳动合同" />
|
||
<div v-else class="contracts-list">
|
||
<div v-for="contract in detail.contracts" :key="contract.id" class="contract-box">
|
||
<div class="contract-info-left">
|
||
<div class="contract-title font-bold">{{ contract.contractName }}</div>
|
||
<div class="contract-meta-text text-secondary">
|
||
<span>状态:{{ contract.contractStatus }}</span>
|
||
<span v-if="contract.offlineSignedAt">· 线下签署于:{{ formatTime(contract.offlineSignedAt) }}</span>
|
||
</div>
|
||
</div>
|
||
<div class="contract-action-right">
|
||
<el-button
|
||
v-if="contract.generatedDocxFileId"
|
||
type="primary"
|
||
plain
|
||
size="small"
|
||
icon="Download"
|
||
@click="download(contract.generatedDocxFileId)"
|
||
>
|
||
下载合同 DOCX
|
||
</el-button>
|
||
<el-tag v-else type="info">尚未生成电子文件</el-tag>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</el-tab-pane>
|
||
|
||
<!-- 3. 入职流转与审核日志 -->
|
||
<el-tab-pane label="入职流转时间轴" name="audits">
|
||
<el-empty v-if="!detail.audits?.length" description="暂无流转记录" />
|
||
<div v-else class="timeline-box">
|
||
<el-timeline>
|
||
<el-timeline-item
|
||
v-for="item in detail.audits"
|
||
:key="item.id"
|
||
type="primary"
|
||
:timestamp="formatTime(item.auditedAt)"
|
||
placement="top"
|
||
>
|
||
<div class="audit-card">
|
||
<div class="audit-action-name font-bold">
|
||
{{ auditActionText(item.auditAction) }}
|
||
<span v-if="item.createUser" class="audit-operator">(操作人:{{ item.createUser }})</span>
|
||
</div>
|
||
<div v-if="item.auditOpinion" class="audit-opinion">
|
||
审核意见:{{ item.auditOpinion }}
|
||
</div>
|
||
</div>
|
||
</el-timeline-item>
|
||
</el-timeline>
|
||
</div>
|
||
</el-tab-pane>
|
||
</el-tabs>
|
||
</div>
|
||
<template #footer>
|
||
<el-button @click="detailVisible = false">关闭</el-button>
|
||
</template>
|
||
</el-drawer>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts" name="onboardingEmployee">
|
||
import { ref } from "vue";
|
||
import { UserFilled, Download } from "@element-plus/icons-vue";
|
||
import {
|
||
onboardingTaskApi,
|
||
type OnboardingEmployee
|
||
} from "@/api/modules/biz/onboarding";
|
||
import type { ColumnProps } from "@/components/ProTable/interface";
|
||
|
||
const detailVisible = ref(false);
|
||
const activeTab = ref("basic");
|
||
const detail = ref<any>();
|
||
|
||
const columns: ColumnProps<OnboardingEmployee>[] = [
|
||
{
|
||
prop: "employeeNo",
|
||
label: "员工编号",
|
||
minWidth: 160
|
||
},
|
||
{
|
||
prop: "employeeName",
|
||
label: "姓名",
|
||
minWidth: 140,
|
||
search: { el: "input" }
|
||
},
|
||
{
|
||
prop: "gender",
|
||
label: "性别",
|
||
minWidth: 80,
|
||
align: "center"
|
||
},
|
||
{
|
||
prop: "phone",
|
||
label: "手机号码",
|
||
minWidth: 160,
|
||
search: { el: "input" }
|
||
},
|
||
{
|
||
prop: "departmentName",
|
||
label: "所属部门",
|
||
minWidth: 140
|
||
},
|
||
{
|
||
prop: "entryDate",
|
||
label: "入职日期",
|
||
minWidth: 140
|
||
},
|
||
{
|
||
prop: "createTime",
|
||
label: "建档时间",
|
||
minWidth: 180,
|
||
render: scope => formatTime(scope.row.createTime)
|
||
},
|
||
{
|
||
prop: "operation",
|
||
label: "操作",
|
||
fixed: "right",
|
||
width: 160
|
||
}
|
||
];
|
||
|
||
const auditActionText = (action: string) => {
|
||
const map: Record<string, string> = {
|
||
submit: "候选人提交入职资料",
|
||
approve: "人事复核通过并转正建档",
|
||
reject: "人事退回补充资料",
|
||
archive: "任务归档",
|
||
revoke: "任务撤销"
|
||
};
|
||
return map[action] || action;
|
||
};
|
||
|
||
const formatTime = (timeStr?: string) => {
|
||
if (!timeStr) return "-";
|
||
return timeStr.replace("T", " ").substring(0, 19);
|
||
};
|
||
|
||
async function showDetail(row: OnboardingEmployee) {
|
||
const { data } = await onboardingTaskApi.employeeDetail({
|
||
id: row.id
|
||
});
|
||
|
||
detail.value = data;
|
||
activeTab.value = "basic";
|
||
detailVisible.value = true;
|
||
}
|
||
|
||
function download(fileId: number) {
|
||
window.open(`/api/sys/dev/file/download?id=${fileId}`, "_blank");
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
:deep(.table-search) {
|
||
margin-bottom: 12px !important;
|
||
border-radius: 8px;
|
||
}
|
||
|
||
:deep(.table-main) {
|
||
border-radius: 8px;
|
||
}
|
||
|
||
.employee-drawer-content {
|
||
padding: 0 8px;
|
||
}
|
||
|
||
.employee-header-card {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 20px;
|
||
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
|
||
border: 1px solid #e2e8f0;
|
||
border-radius: 12px;
|
||
padding: 20px;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.employee-avatar {
|
||
background: #2563eb;
|
||
color: #fff;
|
||
font-size: 24px;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.employee-header-info {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
}
|
||
|
||
.employee-name-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
}
|
||
|
||
.employee-name {
|
||
font-size: 20px;
|
||
font-weight: 700;
|
||
color: #1e293b;
|
||
}
|
||
|
||
.employee-meta-row {
|
||
display: flex;
|
||
gap: 16px;
|
||
font-size: 13px;
|
||
color: #64748b;
|
||
}
|
||
|
||
.section-card {
|
||
border-radius: 8px;
|
||
border: 1px solid #e2e8f0;
|
||
}
|
||
|
||
.card-header-title {
|
||
font-weight: 600;
|
||
font-size: 14px;
|
||
color: #1e293b;
|
||
}
|
||
|
||
.mt-4 {
|
||
margin-top: 16px;
|
||
}
|
||
|
||
.font-bold {
|
||
font-weight: 600;
|
||
}
|
||
|
||
.text-secondary {
|
||
color: #64748b;
|
||
}
|
||
|
||
/* 合同列表 */
|
||
.contracts-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 12px;
|
||
}
|
||
|
||
.contract-box {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 16px;
|
||
border-radius: 8px;
|
||
border: 1px solid #e2e8f0;
|
||
background: #f8fafc;
|
||
}
|
||
|
||
.contract-meta-text {
|
||
font-size: 13px;
|
||
margin-top: 4px;
|
||
}
|
||
|
||
/* 时间轴 */
|
||
.timeline-box {
|
||
padding: 16px 8px;
|
||
}
|
||
|
||
.audit-card {
|
||
background: #f8fafc;
|
||
padding: 10px 14px;
|
||
border-radius: 8px;
|
||
border: 1px solid #e2e8f0;
|
||
}
|
||
|
||
.audit-operator {
|
||
font-size: 13px;
|
||
color: #64748b;
|
||
font-weight: normal;
|
||
}
|
||
|
||
.audit-opinion {
|
||
font-size: 13px;
|
||
color: #dc2626;
|
||
margin-top: 4px;
|
||
}
|
||
</style>
|