修改相关bug
This commit is contained in:
@@ -756,6 +756,7 @@ export const onboardingTaskApi = {
|
||||
|
||||
versionPage(params: {
|
||||
templateId: number;
|
||||
versionStatus?: string;
|
||||
pageNum: number;
|
||||
pageSize: number;
|
||||
}) {
|
||||
|
||||
@@ -230,8 +230,12 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="effectiveDate" label="生效日期" width="130" align="center" />
|
||||
<el-table-column prop="expiryDate" label="失效日期" width="130" align="center" />
|
||||
<el-table-column label="生效时间" width="175" align="center">
|
||||
<template #default="scope">{{ formatVersionTime(scope.row.effectiveDate) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="失效时间" width="175" align="center">
|
||||
<template #default="scope">{{ formatVersionTime(scope.row.expiryDate) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="remark" label="版本说明" min-width="150" />
|
||||
|
||||
<el-table-column label="操作" width="340" fixed="right" align="center">
|
||||
@@ -344,12 +348,26 @@
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="生效日期">
|
||||
<el-date-picker v-model="versionForm.effectiveDate" value-format="YYYY-MM-DD" class="w-full" />
|
||||
<el-form-item label="生效时间">
|
||||
<el-date-picker
|
||||
v-model="versionForm.effectiveDate"
|
||||
type="datetime"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
placeholder="请选择生效时间"
|
||||
class="w-full"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="失效日期">
|
||||
<el-date-picker v-model="versionForm.expiryDate" value-format="YYYY-MM-DD" class="w-full" />
|
||||
<el-form-item label="失效时间">
|
||||
<el-date-picker
|
||||
v-model="versionForm.expiryDate"
|
||||
type="datetime"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
placeholder="请选择失效时间"
|
||||
class="w-full"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="版本备注">
|
||||
@@ -476,7 +494,7 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="变量类型" required>
|
||||
<el-select v-model="variableForm.variableType" class="w-full">
|
||||
<el-select v-model="variableForm.variableType" disabled class="w-full">
|
||||
<el-option label="员工填写(从新员工基本信息/动态字段读取)" value="employee" />
|
||||
<el-option label="公司信息(从公司档案自动读取)" value="company" />
|
||||
<el-option label="人事预设填写(人事发起时录入)" value="contract" />
|
||||
@@ -485,7 +503,7 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="数据类型" required>
|
||||
<el-select v-model="variableForm.dataType" class="w-full">
|
||||
<el-select v-model="variableForm.dataType" disabled class="w-full">
|
||||
<el-option label="文本" value="string" />
|
||||
<el-option label="日期" value="date" />
|
||||
<el-option label="数字" value="number" />
|
||||
@@ -494,7 +512,7 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="['employee', 'company'].includes(variableForm.variableType || '')" label="取值字段">
|
||||
<el-input v-model="variableForm.employeeField" placeholder="如:EmployeeName、IdCardNumber、Phone" />
|
||||
<el-input v-model="variableForm.employeeField" disabled placeholder="如:EmployeeName、IdCardNumber、Phone" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="默认值">
|
||||
@@ -529,6 +547,7 @@
|
||||
<script setup lang="ts" name="onboardingContract">
|
||||
import { computed, onMounted, reactive, ref } from "vue";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import dayjs from "dayjs";
|
||||
import { Check } from "@element-plus/icons-vue";
|
||||
import {
|
||||
onboardingTaskApi,
|
||||
@@ -892,10 +911,21 @@ async function uploadDocx(options: any) {
|
||||
}
|
||||
}
|
||||
|
||||
function formatVersionTime(value?: string) {
|
||||
return value ? dayjs(value).format("YYYY-MM-DD HH:mm:ss") : "--";
|
||||
}
|
||||
|
||||
async function saveVersion() {
|
||||
if (!selectedTemplate.value || !versionForm.versionNo || !versionForm.sourceDocxFileId) {
|
||||
return ElMessage.warning("请填写版本号并上传 DOCX 文件");
|
||||
}
|
||||
if (
|
||||
versionForm.effectiveDate
|
||||
&& versionForm.expiryDate
|
||||
&& !dayjs(versionForm.expiryDate).isAfter(dayjs(versionForm.effectiveDate))
|
||||
) {
|
||||
return ElMessage.warning("失效时间必须晚于生效时间");
|
||||
}
|
||||
await onboardingTaskApi.addVersion({ ...versionForm, templateId: selectedTemplate.value.id });
|
||||
versionVisible.value = false;
|
||||
await loadVersions();
|
||||
|
||||
@@ -295,6 +295,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, ref } from "vue";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import dayjs from "dayjs";
|
||||
import {
|
||||
OnboardingCompany,
|
||||
OnboardingContractTemplate,
|
||||
@@ -508,10 +509,23 @@ async function onContractTemplateChange(row: any) {
|
||||
await loadContractVersions(row.contractTemplateId);
|
||||
}
|
||||
|
||||
function isVersionCurrentlyAvailable(version: OnboardingContractVersion) {
|
||||
const now = dayjs();
|
||||
const effectiveAt = version.effectiveDate ? dayjs(version.effectiveDate) : undefined;
|
||||
const expiresAt = version.expiryDate ? dayjs(version.expiryDate) : undefined;
|
||||
return version.versionStatus === "enabled"
|
||||
&& (!effectiveAt || (effectiveAt.isValid() && !effectiveAt.isAfter(now)))
|
||||
&& (!expiresAt || (expiresAt.isValid() && expiresAt.isAfter(now)));
|
||||
}
|
||||
|
||||
async function loadContractVersions(templateId: number) {
|
||||
if (versionOptions[templateId]) return;
|
||||
const res = await onboardingTaskApi.versionPage({ templateId, pageNum: 1, pageSize: 100 });
|
||||
versionOptions[templateId] = (res.data?.list || []).filter((it: any) => it.versionStatus === "enabled");
|
||||
const res = await onboardingTaskApi.versionPage({
|
||||
templateId,
|
||||
versionStatus: "enabled",
|
||||
pageNum: 1,
|
||||
pageSize: 100
|
||||
});
|
||||
versionOptions[templateId] = (res.data?.list || []).filter(isVersionCurrentlyAvailable);
|
||||
}
|
||||
|
||||
function addContract() {
|
||||
@@ -619,12 +633,25 @@ async function openEdit(row: OnboardingTemplate) {
|
||||
const positionRes = await onboardingTaskApi.listPositionsByDepartment(form.departmentId);
|
||||
positions.value = positionRes.data || [];
|
||||
}
|
||||
const configuredContractCount = form.contracts.length;
|
||||
await loadAvailableContractTemplates();
|
||||
const removedTemplateCount = configuredContractCount - form.contracts.length;
|
||||
let clearedVersionCount = 0;
|
||||
for (const contract of form.contracts) {
|
||||
if (contract.contractTemplateId) {
|
||||
await loadContractVersions(contract.contractTemplateId);
|
||||
const availableVersions = versionOptions[contract.contractTemplateId] || [];
|
||||
if (!availableVersions.some(item => item.id === contract.contractTemplateVersionId)) {
|
||||
contract.contractTemplateVersionId = undefined as any;
|
||||
clearedVersionCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (removedTemplateCount > 0 || clearedVersionCount > 0) {
|
||||
ElMessage.warning(
|
||||
"该入职模板原来关联了已停用、未生效或已失效的签署材料,已从当前选择中移除或清空,请重新选择后保存"
|
||||
);
|
||||
}
|
||||
visible.value = true;
|
||||
}
|
||||
|
||||
@@ -645,6 +672,14 @@ async function save() {
|
||||
if (!form.contracts.length || form.contracts.some(item => !item.contractTemplateId || !item.contractTemplateVersionId)) {
|
||||
return ElMessage.warning("请完整选择至少一份签署材料模板及版本");
|
||||
}
|
||||
const containsUnavailableVersion = form.contracts.some(contract => {
|
||||
const versions = versionOptions[contract.contractTemplateId] || [];
|
||||
const selected = versions.find(item => item.id === contract.contractTemplateVersionId);
|
||||
return !selected || !isVersionCurrentlyAvailable(selected);
|
||||
});
|
||||
if (containsUnavailableVersion) {
|
||||
return ElMessage.warning("所选签署材料版本已停用、尚未生效或已经失效,请重新选择");
|
||||
}
|
||||
|
||||
saving.value = true;
|
||||
try {
|
||||
|
||||
@@ -78,9 +78,17 @@
|
||||
</template>
|
||||
|
||||
<template #companyId="scope">
|
||||
<el-tag effect="plain" type="primary">
|
||||
{{ scope.row.companyName || getCompanyName(scope.row.companyId) }}
|
||||
</el-tag>
|
||||
<el-tooltip
|
||||
v-if="scope.row.companyName || getCompanyName(scope.row.companyId)"
|
||||
:content="scope.row.companyName || getCompanyName(scope.row.companyId)"
|
||||
placement="top"
|
||||
:show-after="300"
|
||||
>
|
||||
<el-tag effect="plain" type="primary" class="truncate-tag">
|
||||
{{ scope.row.companyName || getCompanyName(scope.row.companyId) }}
|
||||
</el-tag>
|
||||
</el-tooltip>
|
||||
<span v-else class="text-gray-400">-</span>
|
||||
</template>
|
||||
|
||||
<template #status="scope">
|
||||
@@ -135,15 +143,30 @@
|
||||
</template>
|
||||
|
||||
<template #companyId="scope">
|
||||
<el-tag effect="plain" type="primary">
|
||||
{{ scope.row.companyName || getCompanyName(scope.row.companyId) }}
|
||||
</el-tag>
|
||||
<el-tooltip
|
||||
v-if="scope.row.companyName || getCompanyName(scope.row.companyId)"
|
||||
:content="scope.row.companyName || getCompanyName(scope.row.companyId)"
|
||||
placement="top"
|
||||
:show-after="300"
|
||||
>
|
||||
<el-tag effect="plain" type="primary" class="truncate-tag">
|
||||
{{ scope.row.companyName || getCompanyName(scope.row.companyId) }}
|
||||
</el-tag>
|
||||
</el-tooltip>
|
||||
<span v-else class="text-gray-400">-</span>
|
||||
</template>
|
||||
|
||||
<template #departmentName="scope">
|
||||
<el-tag v-if="scope.row.departmentName" effect="light" type="warning">
|
||||
{{ scope.row.departmentName }}
|
||||
</el-tag>
|
||||
<el-tooltip
|
||||
v-if="scope.row.departmentName"
|
||||
:content="scope.row.departmentName"
|
||||
placement="top"
|
||||
:show-after="300"
|
||||
>
|
||||
<el-tag effect="light" type="warning" class="truncate-tag">
|
||||
{{ scope.row.departmentName }}
|
||||
</el-tag>
|
||||
</el-tooltip>
|
||||
<span v-else class="text-gray-400">-</span>
|
||||
</template>
|
||||
|
||||
@@ -204,18 +227,18 @@
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="公司编码" prop="companyCode">
|
||||
<el-input v-model.trim="companyForm.companyCode" placeholder="如:TECH_CORP" clearable />
|
||||
<el-input v-model.trim="companyForm.companyCode" placeholder="如:TECH_CORP" maxlength="50" show-word-limit clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="公司简称/名称" prop="companyName">
|
||||
<el-input v-model.trim="companyForm.companyName" placeholder="如:总公司 / 科技分部" clearable />
|
||||
<el-input v-model.trim="companyForm.companyName" placeholder="如:总公司 / 科技分部" maxlength="50" show-word-limit clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-form-item label="签署材料甲方全称" prop="legalName">
|
||||
<el-input v-model.trim="companyForm.legalName" placeholder="必须与契约锁中的企业名称完全一致" clearable />
|
||||
<el-input v-model.trim="companyForm.legalName" placeholder="必须与契约锁中的企业名称完全一致" maxlength="100" show-word-limit clearable />
|
||||
</el-form-item>
|
||||
|
||||
<el-row :gutter="20">
|
||||
@@ -225,14 +248,15 @@
|
||||
v-model.trim="companyForm.unifiedSocialCreditCode"
|
||||
placeholder="18位统一社会信用代码"
|
||||
maxlength="18"
|
||||
show-word-limit
|
||||
clearable
|
||||
@input="val => companyForm.unifiedSocialCreditCode = (val || '').toUpperCase()"
|
||||
@input="val => companyForm.unifiedSocialCreditCode = (val || '').replace(/[^0-9A-Za-z]/g, '').toUpperCase()"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="法定代表人" prop="legalRepresentative">
|
||||
<el-input v-model.trim="companyForm.legalRepresentative" placeholder="法定代表人姓名" clearable />
|
||||
<el-input v-model.trim="companyForm.legalRepresentative" placeholder="法定代表人姓名" maxlength="20" show-word-limit clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -240,7 +264,7 @@
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="联系电话" prop="contactPhone">
|
||||
<el-input v-model.trim="companyForm.contactPhone" placeholder="企业联系电话(手机或固话)" clearable />
|
||||
<el-input v-model.trim="companyForm.contactPhone" placeholder="企业联系电话(手机或固话)" maxlength="20" show-word-limit clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
@@ -254,11 +278,11 @@
|
||||
</el-row>
|
||||
|
||||
<el-form-item label="注册地址" prop="registeredAddress">
|
||||
<el-input v-model.trim="companyForm.registeredAddress" placeholder="企业法定注册地址" clearable />
|
||||
<el-input v-model.trim="companyForm.registeredAddress" placeholder="企业法定注册地址" maxlength="150" show-word-limit clearable />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="companyForm.remark" type="textarea" :rows="2" placeholder="备注信息(选填)" />
|
||||
<el-input v-model="companyForm.remark" type="textarea" :rows="3" placeholder="备注信息(选填)" maxlength="300" show-word-limit />
|
||||
</el-form-item>
|
||||
|
||||
<el-divider content-position="left">契约锁电子签配置(由技术人员添加)</el-divider>
|
||||
@@ -272,12 +296,26 @@
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="默认业务分类 Id" prop="qiyuesuoCategoryId">
|
||||
<el-input v-model.trim="companyForm.qiyuesuoCategoryId" placeholder="契约锁当前公司的分类 Id" clearable />
|
||||
<el-input
|
||||
v-model.trim="companyForm.qiyuesuoCategoryId"
|
||||
placeholder="契约锁当前公司的分类 Id"
|
||||
maxlength="19"
|
||||
show-word-limit
|
||||
clearable
|
||||
@input="val => companyForm.qiyuesuoCategoryId = (val || '').replace(/\D/g, '')"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="企业印章 Id" prop="qiyuesuoSealId">
|
||||
<el-input v-model.trim="companyForm.qiyuesuoSealId" placeholder="契约锁当前公司的印章 Id" clearable />
|
||||
<el-input
|
||||
v-model.trim="companyForm.qiyuesuoSealId"
|
||||
placeholder="契约锁当前公司的印章 Id"
|
||||
maxlength="19"
|
||||
show-word-limit
|
||||
clearable
|
||||
@input="val => companyForm.qiyuesuoSealId = (val || '').replace(/\D/g, '')"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -299,12 +337,12 @@
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="部门编码" required>
|
||||
<el-input v-model="departmentForm.departmentCode" placeholder="如:DEPT_TECH" />
|
||||
<el-input v-model.trim="departmentForm.departmentCode" placeholder="如:DEPT_TECH" maxlength="50" show-word-limit clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="部门名称" required>
|
||||
<el-input v-model="departmentForm.departmentName" placeholder="如:技术研发部" />
|
||||
<el-input v-model.trim="departmentForm.departmentName" placeholder="如:技术研发部" maxlength="50" show-word-limit clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -326,7 +364,7 @@
|
||||
</el-row>
|
||||
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="departmentForm.remark" type="textarea" :rows="2" placeholder="备注信息" />
|
||||
<el-input v-model="departmentForm.remark" type="textarea" :rows="2" placeholder="备注信息" maxlength="200" show-word-limit />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
@@ -370,12 +408,12 @@
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="岗位编码" required>
|
||||
<el-input v-model="positionForm.positionCode" placeholder="如:DEV_FRONTEND" />
|
||||
<el-input v-model.trim="positionForm.positionCode" placeholder="如:DEV_FRONTEND" maxlength="50" show-word-limit clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="岗位名称" required>
|
||||
<el-input v-model="positionForm.positionName" placeholder="如:高级前端开发工程师" />
|
||||
<el-input v-model.trim="positionForm.positionName" placeholder="如:高级前端开发工程师" maxlength="50" show-word-limit clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -401,7 +439,7 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="positionForm.remark" type="textarea" :rows="2" placeholder="备注信息" />
|
||||
<el-input v-model="positionForm.remark" type="textarea" :rows="2" placeholder="备注信息" maxlength="200" show-word-limit />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
@@ -505,7 +543,7 @@ const companyRules = reactive<FormRules>({
|
||||
],
|
||||
companyName: [
|
||||
{ required: true, message: "请输入公司简称/名称", trigger: "blur" },
|
||||
{ min: 2, max: 100, message: "公司名称长度需在 2 到 100 个字符之间", trigger: "blur" }
|
||||
{ min: 2, max: 50, message: "公司名称长度需在 2 到 50 个字符之间", trigger: "blur" }
|
||||
],
|
||||
legalName: [
|
||||
{ required: true, message: "请输入签署材料甲方全称", trigger: "blur" },
|
||||
@@ -540,21 +578,26 @@ const companyRules = reactive<FormRules>({
|
||||
],
|
||||
registeredAddress: [
|
||||
{ required: true, message: "请输入企业法定注册地址", trigger: "blur" },
|
||||
{ min: 4, max: 200, message: "注册地址请至少输入4个字符", trigger: "blur" }
|
||||
{ min: 4, max: 150, message: "注册地址需在 4 到 150 个字符之间", trigger: "blur" }
|
||||
],
|
||||
remark: [
|
||||
{ max: 300, message: "备注不能超过 300 个字符", trigger: "blur" }
|
||||
],
|
||||
qiyuesuoCategoryId: [
|
||||
{
|
||||
pattern: /^\d*$/,
|
||||
message: "业务分类 Id 必须为纯数字",
|
||||
trigger: "blur"
|
||||
}
|
||||
},
|
||||
{ max: 19, message: "业务分类 Id 不能超过 19 位数字", trigger: "blur" }
|
||||
],
|
||||
qiyuesuoSealId: [
|
||||
{
|
||||
pattern: /^\d*$/,
|
||||
message: "企业印章 Id 必须为纯数字",
|
||||
trigger: "blur"
|
||||
}
|
||||
},
|
||||
{ max: 19, message: "企业印章 Id 不能超过 19 位数字", trigger: "blur" }
|
||||
]
|
||||
});
|
||||
const departmentForm = reactive<Partial<OnboardingDepartment>>(emptyDepartment());
|
||||
@@ -646,7 +689,8 @@ const departmentColumns: ColumnProps<OnboardingDepartment>[] = [
|
||||
prop: "departmentName",
|
||||
label: "部门名称",
|
||||
minWidth: 180,
|
||||
search: { el: "input" }
|
||||
search: { el: "input" },
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
{
|
||||
prop: "sortCode",
|
||||
@@ -693,13 +737,15 @@ const positionColumns: ColumnProps<OnboardingPosition>[] = [
|
||||
prop: "positionCode",
|
||||
label: "岗位编码",
|
||||
width: 150,
|
||||
search: { el: "input" }
|
||||
search: { el: "input" },
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
{
|
||||
prop: "positionName",
|
||||
label: "岗位名称",
|
||||
minWidth: 180,
|
||||
search: { el: "input" }
|
||||
search: { el: "input" },
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
{
|
||||
prop: "defaultContractYears",
|
||||
@@ -831,7 +877,10 @@ async function save() {
|
||||
return ElMessage.warning("契约锁默认业务分类 Id 和企业印章 Id 必须同时填写(或均不填)");
|
||||
}
|
||||
if (categoryId && (!/^\d+$/.test(categoryId) || !/^\d+$/.test(sealId))) {
|
||||
return ElMessage.warning("契约锁默认业务分类 Id 和企业印章 Id 只能填写数字");
|
||||
return ElMessage.warning("契约锁默认业务分类 Id 和企业印章 Id 只能填写纯数字");
|
||||
}
|
||||
if (categoryId && (categoryId.length > 19 || sealId.length > 19)) {
|
||||
return ElMessage.warning("契约锁默认业务分类 Id 和企业印章 Id 不能超过 19 位数字");
|
||||
}
|
||||
companyForm.qiyuesuoCategoryId = categoryId;
|
||||
companyForm.qiyuesuoSealId = sealId;
|
||||
@@ -971,4 +1020,17 @@ onMounted(loadCompanies);
|
||||
padding-top: 3px;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
.truncate-tag {
|
||||
max-width: 140px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.truncate-tag :deep(.el-tag__content) {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -769,7 +769,7 @@
|
||||
<el-descriptions-item
|
||||
v-for="item in detailCustomVars"
|
||||
:key="item.key"
|
||||
:label="item.key"
|
||||
:label="item.label"
|
||||
>
|
||||
{{ item.value || '-' }}
|
||||
</el-descriptions-item>
|
||||
@@ -1685,10 +1685,30 @@ const detailFamilyList = computed(() => {
|
||||
}
|
||||
});
|
||||
|
||||
const registrationEmployeeFieldCodes = new Set([
|
||||
"employeename", "name", "username", "gender", "nation", "birthday", "registeredresidence",
|
||||
"height", "weight", "healthcondition", "maritalstatus", "education", "major", "graduatedschool",
|
||||
"college", "vocationalqualification", "qualificationlevel", "technicalspecialty", "phone",
|
||||
"mobile", "email", "idcardnumber", "idcard", "currentaddress", "contactaddress", "landlordcontact",
|
||||
"bankcardnumber", "bankcard", "bankname", "bankaccountname", "emergencycontact", "emergencyphone",
|
||||
"emergencyaddress", "onboardingdepartment", "onboardingdate", "onboardingposition", "otherremarks"
|
||||
]);
|
||||
const detailContractVariableNames = ref<Record<string, string>>({});
|
||||
|
||||
function isRegistrationEmployeeVariable(variableCode: string) {
|
||||
const fieldCode = (String(variableCode || "").split(".")[1] || variableCode || "").toLowerCase();
|
||||
return registrationEmployeeFieldCodes.has(fieldCode);
|
||||
}
|
||||
const detailCustomVars = computed(() => {
|
||||
try {
|
||||
const obj = JSON.parse(detail.value?.employeeInfo?.customVariablesJson || "{}");
|
||||
return Object.entries(obj).map(([k, v]) => ({ key: k, value: String(v) }));
|
||||
return Object.entries(obj)
|
||||
.filter(([key]) => !isRegistrationEmployeeVariable(key))
|
||||
.map(([key, value]) => ({
|
||||
key,
|
||||
label: detailContractVariableNames.value[key] || key,
|
||||
value: value == null ? "" : String(value)
|
||||
}));
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
@@ -2604,7 +2624,16 @@ async function copyOrShareTaskLink(row: OnboardingTask) {
|
||||
|
||||
async function showDetail(row: OnboardingTask, preserveTab: boolean = false) {
|
||||
const currentTab = activeDetailTab.value;
|
||||
const { data }: any = await onboardingTaskApi.detail({ id: row.id });
|
||||
const [{ data }, variableResponse]: any[] = await Promise.all([
|
||||
onboardingTaskApi.detail({ id: row.id }),
|
||||
onboardingTaskApi.taskContractVariables(row.id).catch(() => ({ data: [] }))
|
||||
]);
|
||||
detailContractVariableNames.value = Object.fromEntries(
|
||||
(variableResponse.data || [])
|
||||
.flatMap((group: OnboardingTaskContractVariableGroup) => group.variables || [])
|
||||
.filter((variable: any) => variable.variableType === "employee" && !isRegistrationEmployeeVariable(variable.variableCode))
|
||||
.map((variable: any) => [variable.variableCode, variable.variableName || variable.variableCode])
|
||||
);
|
||||
detail.value = data;
|
||||
if (!preserveTab) {
|
||||
activeDetailTab.value = "profile";
|
||||
|
||||
@@ -76,33 +76,32 @@
|
||||
<!-- 步骤导航条:PC端展示完整步骤,移动端展示紧凑自适应导航 -->
|
||||
<div class="steps-wrapper desktop-only">
|
||||
<el-steps :active="currentStep" finish-status="success" align-center>
|
||||
<el-step title="入职登记表" icon="User" />
|
||||
<el-step title="材料补充项" icon="Document" />
|
||||
<el-step title="证件材料" icon="FolderChecked" />
|
||||
<el-step title="材料确认" icon="Tickets" />
|
||||
<el-step v-for="step in visibleSteps" :key="step.key" :title="step.title" :icon="step.icon" />
|
||||
|
||||
|
||||
|
||||
</el-steps>
|
||||
</div>
|
||||
|
||||
<div class="mobile-steps-wrapper mobile-only">
|
||||
<div class="mobile-steps-track">
|
||||
<div v-for="(stepName, index) in ['1. 登记表', '2. 补充项', '3. 证件材料', '4. 材料确认']" :key="index"
|
||||
<div v-for="(step, index) in visibleSteps" :key="step.key"
|
||||
class="mobile-step-item" :class="{
|
||||
'is-active': currentStep === index,
|
||||
'is-finished': currentStep > index,
|
||||
'is-pending': currentStep < index
|
||||
}">
|
||||
<div class="step-indicator-bar" />
|
||||
<span class="step-label">{{ stepName }}</span>
|
||||
<span class="step-label">{{ (index + 1) + '. ' + step.shortTitle }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mobile-current-indicator">
|
||||
当前进行:<span class="highlight-step">第 {{ currentStep + 1 }}/4 步 · {{ ['入职登记表填写', '签署材料补充项', '证件材料上传',
|
||||
'签署材料查阅与确认'][currentStep] }}</span>
|
||||
当前进行:<span class="highlight-step">第 {{ currentStep + 1 }}/{{ visibleSteps.length }} 步 · {{ currentStepDefinition.description }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 步骤 1: 入职登记表 -->
|
||||
<div v-show="currentStep === 0" class="step-content">
|
||||
<div v-show="currentStepKey === 'registration'" class="step-content">
|
||||
<div class="registration-header">
|
||||
<h2 class="reg-title">入 职 登 记 表</h2>
|
||||
<p class="reg-subtitle">请如实填写个人基础档案、教育经历及家庭相关信息,标注 * 为必填项,信息将用于建立人事档案,请务必保证资料真实准确。</p>
|
||||
@@ -458,23 +457,13 @@
|
||||
</div>
|
||||
|
||||
<!-- 步骤 2: 合同专属增量补充项 (自适应差集) -->
|
||||
<div v-show="currentStep === 1" class="step-content">
|
||||
<div v-if="extraVariables.length" v-show="currentStepKey === 'extra'" class="step-content">
|
||||
<div class="section-header">
|
||||
<h3 class="section-title">签署材料专属补充信息</h3>
|
||||
<p class="section-desc">以下是本次签署材料中特殊约定的增量字段,登记表未涵盖的项需在此补充确认</p>
|
||||
</div>
|
||||
|
||||
<div v-if="!extraVariables.length" class="all-covered-card">
|
||||
<div class="covered-status-mark">
|
||||
<el-icon :size="28"><Check /></el-icon>
|
||||
</div>
|
||||
<h4 class="covered-status-title">个人信息已完整覆盖</h4>
|
||||
<p class="covered-status-desc">
|
||||
您在《入职登记表》中填写的姓名、身份证、电话、银行卡等信息已完整满足本次签署材料所需,无需额外补充!
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<el-form v-else label-position="top" class="extra-form">
|
||||
<el-form label-position="top" class="extra-form">
|
||||
<el-alert title="请补充填写以下签署材料专有信息:" type="info" show-icon :closable="false" class="mb-3" />
|
||||
<el-row :gutter="16">
|
||||
<el-col v-for="(v, index) in extraVariables" :id="`extra-variable-${index}`" :key="v.variableCode"
|
||||
@@ -503,7 +492,7 @@
|
||||
</div>
|
||||
|
||||
<!-- 步骤 3: 证件材料上传 -->
|
||||
<div v-show="currentStep === 2" class="step-content">
|
||||
<div v-show="currentStepKey === 'materials'" class="step-content">
|
||||
<div class="section-header">
|
||||
<h3 class="section-title">证件与材料上传</h3>
|
||||
<p class="section-desc">请上传清晰真实有效的证件扫描件或照片(支持 PNG、JPG、PDF)</p>
|
||||
@@ -588,7 +577,7 @@
|
||||
</div>
|
||||
|
||||
<!-- 步骤 4: 合同查阅与签署 -->
|
||||
<div v-show="currentStep === 3" class="step-content">
|
||||
<div v-show="currentStepKey === 'confirmation'" class="step-content">
|
||||
<div v-if="canEdit" id="identity-confirmation-card" class="identity-confirmation-card">
|
||||
<div class="identity-confirmation-heading">
|
||||
<div>
|
||||
@@ -678,13 +667,13 @@
|
||||
<!-- 底部向导操作栏 -->
|
||||
<div class="wizard-footer">
|
||||
<el-button v-if="currentStep > 0" size="large" :disabled="savingStep || isAnyMaterialUploading" @click="currentStep--">上一步</el-button>
|
||||
<el-button v-if="currentStep < 3" type="primary" size="large"
|
||||
:loading="savingStep || (currentStep === 2 && isAnyMaterialUploading)"
|
||||
<el-button v-if="currentStep < visibleSteps.length - 1" type="primary" size="large"
|
||||
:loading="savingStep || (currentStepKey === 'materials' && isAnyMaterialUploading)"
|
||||
:disabled="savingStep || isAnyMaterialUploading"
|
||||
@click="nextStep">
|
||||
{{ (currentStep === 2 && isAnyMaterialUploading) ? "材料上传中..." : (savingStep ? "保存中..." : "下一步") }}
|
||||
{{ (currentStepKey === 'materials' && isAnyMaterialUploading) ? "材料上传中..." : (savingStep ? "保存中..." : "下一步") }}
|
||||
</el-button>
|
||||
<el-button v-if="currentStep === 3 && canEdit" type="success" size="large" icon="Check"
|
||||
<el-button v-if="currentStepKey === 'confirmation' && canEdit" type="success" size="large" icon="Check"
|
||||
:loading="submitting" :disabled="savingStep || isAnyMaterialUploading" @click="submitAll">
|
||||
确认无误并提交入职资料
|
||||
</el-button>
|
||||
@@ -1159,6 +1148,23 @@ const extraVariables = computed(() => {
|
||||
return result;
|
||||
});
|
||||
|
||||
type OnboardingStepKey = "registration" | "extra" | "materials" | "confirmation";
|
||||
const visibleSteps = computed(() => {
|
||||
const steps = [
|
||||
{ key: "registration" as OnboardingStepKey, title: "入职登记表", shortTitle: "登记表", description: "入职登记表填写", icon: "User" }
|
||||
];
|
||||
if (extraVariables.value.length) {
|
||||
steps.push({ key: "extra", title: "材料补充项", shortTitle: "补充项", description: "签署材料补充项", icon: "Document" });
|
||||
}
|
||||
steps.push(
|
||||
{ key: "materials", title: "证件材料", shortTitle: "证件材料", description: "证件材料上传", icon: "FolderChecked" },
|
||||
{ key: "confirmation", title: "材料确认", shortTitle: "材料确认", description: "签署材料查阅与确认", icon: "Tickets" }
|
||||
);
|
||||
return steps;
|
||||
});
|
||||
const currentStepDefinition = computed(() => visibleSteps.value[currentStep.value] || visibleSteps.value[0]);
|
||||
const currentStepKey = computed<OnboardingStepKey>(() => currentStepDefinition.value.key);
|
||||
|
||||
function addEducation() {
|
||||
educationHistory.value.push({ timeRange: "", location: "", content: "", result: "" });
|
||||
}
|
||||
@@ -1459,7 +1465,8 @@ async function load(silent = false) {
|
||||
uploadedMaterials.value = data.materials || [];
|
||||
contractVariables.value = data.contractVariables || [];
|
||||
formFieldRequirements.value = data.formFields || [];
|
||||
contractVariables.value.forEach((variable: any) => {
|
||||
Object.keys(customVariables).forEach(key => delete customVariables[key]);
|
||||
extraVariables.value.forEach((variable: any) => {
|
||||
if (!(variable.variableCode in customVariables) && variable.defaultValue != null) {
|
||||
customVariables[variable.variableCode] = String(variable.defaultValue);
|
||||
}
|
||||
@@ -1478,7 +1485,14 @@ async function load(silent = false) {
|
||||
try { familyMembers.value = JSON.parse(data.employeeInfo.familyMembersJson); } catch { }
|
||||
}
|
||||
if (data.employeeInfo.customVariablesJson) {
|
||||
try { Object.assign(customVariables, JSON.parse(data.employeeInfo.customVariablesJson)); } catch { }
|
||||
try {
|
||||
const savedVariables = JSON.parse(data.employeeInfo.customVariablesJson);
|
||||
extraVariables.value.forEach((variable: any) => {
|
||||
if (Object.prototype.hasOwnProperty.call(savedVariables, variable.variableCode)) {
|
||||
customVariables[variable.variableCode] = savedVariables[variable.variableCode];
|
||||
}
|
||||
});
|
||||
} catch { }
|
||||
}
|
||||
}
|
||||
await refreshMaterialPreviews();
|
||||
@@ -1505,7 +1519,9 @@ async function saveCurrentEmployeeInfo() {
|
||||
educationHistoryJson: JSON.stringify(educationHistory.value),
|
||||
workHistoryJson: JSON.stringify(workHistory.value),
|
||||
familyMembersJson: JSON.stringify(familyMembers.value),
|
||||
customVariablesJson: JSON.stringify(customVariables)
|
||||
customVariablesJson: JSON.stringify(Object.fromEntries(
|
||||
extraVariables.value.map((variable: any) => [variable.variableCode, customVariables[variable.variableCode] ?? ""])
|
||||
))
|
||||
};
|
||||
await onboardingPublicApi.saveEmployeeInfo(payload);
|
||||
}
|
||||
@@ -1546,7 +1562,7 @@ async function nextStep() {
|
||||
}
|
||||
savingStep.value = true;
|
||||
try {
|
||||
if (currentStep.value === 0) {
|
||||
if (currentStepKey.value === "registration") {
|
||||
if (!await validateRegistrationForm()) {
|
||||
return;
|
||||
}
|
||||
@@ -1558,14 +1574,14 @@ async function nextStep() {
|
||||
ElMessage.closeAll();
|
||||
ElMessage.success("入职登记表已临时保存");
|
||||
}
|
||||
} else if (currentStep.value === 1) {
|
||||
} else if (currentStepKey.value === "extra") {
|
||||
if (!validateExtraVariables()) {
|
||||
return;
|
||||
}
|
||||
if (canEdit.value) {
|
||||
await saveCurrentEmployeeInfo();
|
||||
}
|
||||
} else if (currentStep.value === 2) {
|
||||
} else if (currentStepKey.value === "materials") {
|
||||
if (isAnyMaterialUploading.value) {
|
||||
ElMessage.closeAll();
|
||||
ElMessage.warning("材料正在上传中,请稍候再点击下一步");
|
||||
@@ -1581,7 +1597,7 @@ async function nextStep() {
|
||||
}
|
||||
currentStep.value++;
|
||||
window.scrollTo({ top: 0, behavior: "smooth" });
|
||||
if (currentStep.value === 3) {
|
||||
if (currentStepKey.value === "confirmation") {
|
||||
await prepareContracts();
|
||||
}
|
||||
} catch (error: any) {
|
||||
|
||||
Reference in New Issue
Block a user