完善第三方签署
This commit is contained in:
@@ -119,6 +119,13 @@ export interface OnboardingElectronicSignDocument {
|
|||||||
documentSort: number;
|
documentSort: number;
|
||||||
originalDocxFileId?: number;
|
originalDocxFileId?: number;
|
||||||
signedPdfFileId?: number;
|
signedPdfFileId?: number;
|
||||||
|
employeeSignedPreviewed: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 员工已签、企业未盖章文件的契约锁只读预览。 */
|
||||||
|
export interface OnboardingElectronicSignPreview {
|
||||||
|
signDocumentId: number;
|
||||||
|
previewUrl: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 管理端查看的入职电子签流程详情。 */
|
/** 管理端查看的入职电子签流程详情。 */
|
||||||
@@ -188,6 +195,8 @@ export interface OnboardingCompany extends OnboardingCompanyOption {
|
|||||||
legalRepresentative?: string;
|
legalRepresentative?: string;
|
||||||
registeredAddress?: string;
|
registeredAddress?: string;
|
||||||
contactPhone?: string;
|
contactPhone?: string;
|
||||||
|
qiyuesuoCategoryId?: string;
|
||||||
|
qiyuesuoSealId?: string;
|
||||||
remark?: string;
|
remark?: string;
|
||||||
status: string;
|
status: string;
|
||||||
}
|
}
|
||||||
@@ -476,6 +485,21 @@ export const onboardingTaskApi = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/** 获取一份员工已签合同的契约锁只读浏览页面。 */
|
||||||
|
previewEmployeeSignedDocument(taskId: number, signDocumentId: number) {
|
||||||
|
return electronicSignHttp.post<OnboardingElectronicSignPreview>(
|
||||||
|
"preview-employee-signed",
|
||||||
|
{ taskId, signDocumentId }
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 全部合同核对完成后,由后台管理员确认企业盖章。 */
|
||||||
|
companySealElectronicSign(taskId: number) {
|
||||||
|
return electronicSignHttp.post<OnboardingElectronicSignDetail>("company-seal", {
|
||||||
|
taskId
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
/** 催办当前待签员工。 */
|
/** 催办当前待签员工。 */
|
||||||
noticeElectronicSign(taskId: number) {
|
noticeElectronicSign(taskId: number) {
|
||||||
return electronicSignHttp.post("notice", { taskId });
|
return electronicSignHttp.post("notice", { taskId });
|
||||||
|
|||||||
@@ -75,20 +75,22 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
||||||
<el-table-column prop="remark" label="说明备注" min-width="200" show-overflow-tooltip />
|
<el-table-column prop="remark" label="说明备注" min-width="140" show-overflow-tooltip />
|
||||||
|
|
||||||
<el-table-column label="操作" width="140" align="center" fixed="right">
|
<el-table-column label="操作" width="160" align="center" fixed="right">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-button link type="primary" icon="Edit" @click="openEdit(row)">编辑</el-button>
|
<div style="display: flex; align-items: center; justify-content: center; gap: 8px; white-space: nowrap;">
|
||||||
<el-button
|
<el-button link type="primary" icon="Edit" @click="openEdit(row)">编辑</el-button>
|
||||||
v-if="!row.isBuiltIn"
|
<el-button
|
||||||
link
|
v-if="!row.isBuiltIn"
|
||||||
type="danger"
|
link
|
||||||
icon="Delete"
|
type="danger"
|
||||||
@click="remove(row)"
|
icon="Delete"
|
||||||
>
|
@click="remove(row)"
|
||||||
删除
|
>
|
||||||
</el-button>
|
删除
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="main-box setup-container">
|
<div class="main-box setup-container">
|
||||||
<el-tabs v-model="activeTab" class="setup-tabs">
|
<el-tabs v-model="activeTab" class="setup-tabs">
|
||||||
<!-- 1. 入职公司管理 -->
|
<!-- 1. 入职公司管理 -->
|
||||||
@@ -23,6 +23,15 @@
|
|||||||
</el-tag>
|
</el-tag>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<template #qiyuesuoConfig="scope">
|
||||||
|
<el-tag
|
||||||
|
size="small"
|
||||||
|
:type="scope.row.qiyuesuoCategoryId && scope.row.qiyuesuoSealId ? 'success' : 'warning'"
|
||||||
|
>
|
||||||
|
{{ scope.row.qiyuesuoCategoryId && scope.row.qiyuesuoSealId ? "已配置" : "未配置" }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
|
||||||
<template #operation="scope">
|
<template #operation="scope">
|
||||||
<el-button link type="primary" icon="Edit" @click="openCompanyEdit(scope.row)">
|
<el-button link type="primary" icon="Edit" @click="openCompanyEdit(scope.row)">
|
||||||
编辑
|
编辑
|
||||||
@@ -130,50 +139,62 @@
|
|||||||
<el-dialog
|
<el-dialog
|
||||||
v-model="dialogVisible"
|
v-model="dialogVisible"
|
||||||
:title="dialogTitle"
|
:title="dialogTitle"
|
||||||
width="680px"
|
width="820px"
|
||||||
|
top="6vh"
|
||||||
destroy-on-close
|
destroy-on-close
|
||||||
append-to-body
|
append-to-body
|
||||||
>
|
>
|
||||||
<!-- 1. 公司表单 -->
|
<!-- 1. 公司表单 -->
|
||||||
<el-form v-if="dialogKind === 'company'" :model="companyForm" label-width="140px">
|
<el-form
|
||||||
|
v-if="dialogKind === 'company'"
|
||||||
|
ref="companyFormRef"
|
||||||
|
:model="companyForm"
|
||||||
|
:rules="companyRules"
|
||||||
|
label-width="140px"
|
||||||
|
>
|
||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="公司编码" required>
|
<el-form-item label="公司编码" prop="companyCode">
|
||||||
<el-input v-model="companyForm.companyCode" placeholder="如:TECH_CORP" />
|
<el-input v-model.trim="companyForm.companyCode" placeholder="如:TECH_CORP" clearable />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="公司简称/名称" required>
|
<el-form-item label="公司简称/名称" prop="companyName">
|
||||||
<el-input v-model="companyForm.companyName" placeholder="如:总公司 / 科技分部" />
|
<el-input v-model.trim="companyForm.companyName" placeholder="如:总公司 / 科技分部" clearable />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<el-form-item label="签署材料甲方全称" required>
|
<el-form-item label="签署材料甲方全称" prop="legalName">
|
||||||
<el-input v-model="companyForm.legalName" placeholder="用于签署材料填写的法定主体全称" />
|
<el-input v-model.trim="companyForm.legalName" placeholder="必须与契约锁中的企业名称完全一致" clearable />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="统一社会信用代码">
|
<el-form-item label="统一社会信用代码" prop="unifiedSocialCreditCode">
|
||||||
<el-input v-model="companyForm.unifiedSocialCreditCode" placeholder="18位统一信用代码" />
|
<el-input
|
||||||
|
v-model.trim="companyForm.unifiedSocialCreditCode"
|
||||||
|
placeholder="18位统一社会信用代码"
|
||||||
|
maxlength="18"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="法定代表人">
|
<el-form-item label="法定代表人" prop="legalRepresentative">
|
||||||
<el-input v-model="companyForm.legalRepresentative" placeholder="法定代表人姓名" />
|
<el-input v-model.trim="companyForm.legalRepresentative" placeholder="法定代表人姓名" clearable />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="联系电话">
|
<el-form-item label="联系电话" prop="contactPhone">
|
||||||
<el-input v-model="companyForm.contactPhone" placeholder="企业联系电话" />
|
<el-input v-model.trim="companyForm.contactPhone" placeholder="企业联系电话(手机或固话)" clearable />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="状态" required>
|
<el-form-item label="状态" prop="status">
|
||||||
<el-select v-model="companyForm.status" class="w-full">
|
<el-select v-model="companyForm.status" class="w-full">
|
||||||
<el-option label="启用" value="enable" />
|
<el-option label="启用" value="enable" />
|
||||||
<el-option label="停用" value="disable" />
|
<el-option label="停用" value="disable" />
|
||||||
@@ -182,13 +203,34 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<el-form-item label="注册地址">
|
<el-form-item label="注册地址" prop="registeredAddress">
|
||||||
<el-input v-model="companyForm.registeredAddress" placeholder="企业法定注册地址" />
|
<el-input v-model.trim="companyForm.registeredAddress" placeholder="企业法定注册地址" clearable />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item label="备注">
|
<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="2" placeholder="备注信息(选填)" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-divider content-position="left">契约锁电子签配置(由技术人员添加)</el-divider>
|
||||||
|
<el-alert
|
||||||
|
title="说明:默认业务分类 Id 与企业印章 Id 由技术人员添加配置,非技术人员请勿随意修改。"
|
||||||
|
type="info"
|
||||||
|
:closable="false"
|
||||||
|
show-icon
|
||||||
|
style="margin-bottom: 18px"
|
||||||
|
/>
|
||||||
|
<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-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-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
<!-- 2. 部门表单 -->
|
<!-- 2. 部门表单 -->
|
||||||
@@ -322,8 +364,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="onboardingSetup">
|
<script setup lang="ts" name="onboardingSetup">
|
||||||
import { ref, reactive, computed, onMounted } from "vue";
|
import { ref, reactive, computed, onMounted, nextTick } from "vue";
|
||||||
import { ElMessage, ElMessageBox } from "element-plus";
|
import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from "element-plus";
|
||||||
import { Plus, Edit, Delete } from "@element-plus/icons-vue";
|
import { Plus, Edit, Delete } from "@element-plus/icons-vue";
|
||||||
import {
|
import {
|
||||||
onboardingTaskApi,
|
onboardingTaskApi,
|
||||||
@@ -370,6 +412,8 @@ const emptyCompany = (): Partial<OnboardingCompany> => ({
|
|||||||
legalRepresentative: "",
|
legalRepresentative: "",
|
||||||
registeredAddress: "",
|
registeredAddress: "",
|
||||||
contactPhone: "",
|
contactPhone: "",
|
||||||
|
qiyuesuoCategoryId: "",
|
||||||
|
qiyuesuoSealId: "",
|
||||||
status: "enable",
|
status: "enable",
|
||||||
remark: ""
|
remark: ""
|
||||||
});
|
});
|
||||||
@@ -394,7 +438,72 @@ const emptyPosition = (): Partial<OnboardingPosition> => ({
|
|||||||
remark: ""
|
remark: ""
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const companyFormRef = ref<FormInstance>();
|
||||||
const companyForm = reactive<Partial<OnboardingCompany>>(emptyCompany());
|
const companyForm = reactive<Partial<OnboardingCompany>>(emptyCompany());
|
||||||
|
|
||||||
|
const companyRules = reactive<FormRules>({
|
||||||
|
companyCode: [
|
||||||
|
{ required: true, message: "请输入公司编码", trigger: "blur" },
|
||||||
|
{
|
||||||
|
pattern: /^[a-zA-Z0-9_-]{2,50}$/,
|
||||||
|
message: "公司编码格式不正确,支持2-50位字母、数字、下划线或短横线",
|
||||||
|
trigger: "blur"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
companyName: [
|
||||||
|
{ required: true, message: "请输入公司简称/名称", trigger: "blur" },
|
||||||
|
{ min: 2, max: 100, message: "公司名称长度需在 2 到 100 个字符之间", trigger: "blur" }
|
||||||
|
],
|
||||||
|
legalName: [
|
||||||
|
{ required: true, message: "请输入签署材料甲方全称", trigger: "blur" },
|
||||||
|
{ min: 2, max: 100, message: "甲方全称长度需在 2 到 100 个字符之间", trigger: "blur" }
|
||||||
|
],
|
||||||
|
unifiedSocialCreditCode: [
|
||||||
|
{ required: true, message: "请输入统一社会信用代码", trigger: "blur" },
|
||||||
|
{
|
||||||
|
pattern: /^[0-9A-HJ-NPQRTUWXY]{2}\d{6}[0-9A-HJ-NPQRTUWXY]{10}$/i,
|
||||||
|
message: "统一社会信用代码格式不正确,请输入18位标准社会信用代码",
|
||||||
|
trigger: "blur"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
legalRepresentative: [
|
||||||
|
{ required: true, message: "请输入法定代表人姓名", trigger: "blur" },
|
||||||
|
{
|
||||||
|
pattern: /^[\u4e00-\u9fa5a-zA-Z·.\s]{2,20}$/,
|
||||||
|
message: "法定代表人姓名格式不正确,请输入2-20位中英文姓名",
|
||||||
|
trigger: "blur"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
contactPhone: [
|
||||||
|
{ required: true, message: "请输入企业联系电话", trigger: "blur" },
|
||||||
|
{
|
||||||
|
pattern: /^(1[3-9]\d{9}|(0\d{2,3}-?)?\d{7,8}|400(-?\d{3,4}){2})$/,
|
||||||
|
message: "联系电话格式不正确,请输入有效的手机号或固话号码",
|
||||||
|
trigger: "blur"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
status: [
|
||||||
|
{ required: true, message: "请选择公司启用状态", trigger: "change" }
|
||||||
|
],
|
||||||
|
registeredAddress: [
|
||||||
|
{ required: true, message: "请输入企业法定注册地址", trigger: "blur" },
|
||||||
|
{ min: 4, max: 200, message: "注册地址请至少输入4个字符", trigger: "blur" }
|
||||||
|
],
|
||||||
|
qiyuesuoCategoryId: [
|
||||||
|
{
|
||||||
|
pattern: /^\d*$/,
|
||||||
|
message: "契约锁业务分类 Id 必须为纯数字",
|
||||||
|
trigger: "blur"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
qiyuesuoSealId: [
|
||||||
|
{
|
||||||
|
pattern: /^\d*$/,
|
||||||
|
message: "契约锁企业印章 Id 必须为纯数字",
|
||||||
|
trigger: "blur"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
const departmentForm = reactive<Partial<OnboardingDepartment>>(emptyDepartment());
|
const departmentForm = reactive<Partial<OnboardingDepartment>>(emptyDepartment());
|
||||||
const positionForm = reactive<Partial<OnboardingPosition>>(emptyPosition());
|
const positionForm = reactive<Partial<OnboardingPosition>>(emptyPosition());
|
||||||
|
|
||||||
@@ -438,6 +547,12 @@ const companyColumns: ColumnProps<OnboardingCompany>[] = [
|
|||||||
label: "联系电话",
|
label: "联系电话",
|
||||||
width: 140
|
width: 140
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
prop: "qiyuesuoConfig",
|
||||||
|
label: "契约锁配置",
|
||||||
|
width: 120,
|
||||||
|
align: "center"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
prop: "status",
|
prop: "status",
|
||||||
label: "状态",
|
label: "状态",
|
||||||
@@ -602,6 +717,9 @@ function openCompanyCreate() {
|
|||||||
editing.value = false;
|
editing.value = false;
|
||||||
resetCompany();
|
resetCompany();
|
||||||
dialogVisible.value = true;
|
dialogVisible.value = true;
|
||||||
|
nextTick(() => {
|
||||||
|
companyFormRef.value?.clearValidate();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function openDepartmentCreate() {
|
function openDepartmentCreate() {
|
||||||
@@ -625,6 +743,9 @@ async function openCompanyEdit(row: OnboardingCompany) {
|
|||||||
editing.value = true;
|
editing.value = true;
|
||||||
Object.assign(companyForm, data);
|
Object.assign(companyForm, data);
|
||||||
dialogVisible.value = true;
|
dialogVisible.value = true;
|
||||||
|
nextTick(() => {
|
||||||
|
companyFormRef.value?.clearValidate();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function openDepartmentEdit(row: OnboardingDepartment) {
|
async function openDepartmentEdit(row: OnboardingDepartment) {
|
||||||
@@ -648,8 +769,21 @@ async function openPositionEdit(row: OnboardingPosition) {
|
|||||||
|
|
||||||
async function save() {
|
async function save() {
|
||||||
if (dialogKind.value === "company") {
|
if (dialogKind.value === "company") {
|
||||||
if (!companyForm.companyCode || !companyForm.companyName || !companyForm.legalName) {
|
const valid = await companyFormRef.value?.validate().catch(() => false);
|
||||||
return ElMessage.warning("请填写公司编码、公司名称和签署材料甲方全称");
|
if (!valid) return;
|
||||||
|
|
||||||
|
const categoryId = companyForm.qiyuesuoCategoryId?.trim() || "";
|
||||||
|
const sealId = companyForm.qiyuesuoSealId?.trim() || "";
|
||||||
|
if (Boolean(categoryId) !== Boolean(sealId)) {
|
||||||
|
return ElMessage.warning("契约锁默认业务分类 Id 和企业印章 Id 必须同时填写(或均不填)");
|
||||||
|
}
|
||||||
|
if (categoryId && (!/^\d+$/.test(categoryId) || !/^\d+$/.test(sealId))) {
|
||||||
|
return ElMessage.warning("契约锁默认业务分类 Id 和企业印章 Id 只能填写数字");
|
||||||
|
}
|
||||||
|
companyForm.qiyuesuoCategoryId = categoryId;
|
||||||
|
companyForm.qiyuesuoSealId = sealId;
|
||||||
|
if (companyForm.unifiedSocialCreditCode) {
|
||||||
|
companyForm.unifiedSocialCreditCode = companyForm.unifiedSocialCreditCode.trim().toUpperCase();
|
||||||
}
|
}
|
||||||
await onboardingTaskApi.saveCompany(companyForm, editing.value);
|
await onboardingTaskApi.saveCompany(companyForm, editing.value);
|
||||||
companyTable.value?.refresh();
|
companyTable.value?.refresh();
|
||||||
|
|||||||
@@ -250,49 +250,67 @@
|
|||||||
style="margin-bottom: 16px;"
|
style="margin-bottom: 16px;"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<el-row :gutter="20">
|
<!-- 按材料分组展示人事参数 -->
|
||||||
<el-col
|
<div class="contract-vars-group-list">
|
||||||
v-for="item in form.contractVariables"
|
<div
|
||||||
:key="`${item.contractTemplateVersionId}-${item.variableCode}`"
|
v-for="group in groupedContractVariables"
|
||||||
:xs="24"
|
:key="group.contractTemplateVersionId"
|
||||||
:sm="12"
|
class="contract-var-group-card"
|
||||||
>
|
>
|
||||||
<el-form-item>
|
<div class="var-group-header">
|
||||||
<template #label>
|
<div class="var-group-title">
|
||||||
<div class="var-label-row">
|
<el-icon class="var-group-icon"><Document /></el-icon>
|
||||||
<span v-if="item.required" class="required-star">*</span>
|
<span class="var-group-name">{{ group.contractName }}</span>
|
||||||
<span class="var-name">{{ item.variableName }}</span>
|
</div>
|
||||||
<el-tag size="small" type="info" effect="plain" class="contract-source-tag">
|
<el-tag size="small" type="primary" effect="plain" class="var-group-count">
|
||||||
{{ item.contractName }}
|
{{ group.items.length }} 项人事参数
|
||||||
</el-tag>
|
</el-tag>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
|
||||||
|
|
||||||
<el-date-picker
|
<div class="var-group-content">
|
||||||
v-if="item.dataType === 'date'"
|
<el-row :gutter="20">
|
||||||
v-model="item.variableValue"
|
<el-col
|
||||||
type="date"
|
v-for="item in group.items"
|
||||||
value-format="YYYY-MM-DD"
|
:key="`${item.contractTemplateVersionId}-${item.variableCode}`"
|
||||||
placeholder="选择日期"
|
:xs="24"
|
||||||
class="var-input-field"
|
:sm="12"
|
||||||
/>
|
>
|
||||||
<el-input-number
|
<el-form-item>
|
||||||
v-else-if="item.dataType === 'number' || item.dataType === 'decimal'"
|
<template #label>
|
||||||
v-model="item.variableValue"
|
<div class="var-label-row">
|
||||||
controls-position="right"
|
<span v-if="item.required" class="required-star">*</span>
|
||||||
class="var-input-field"
|
<span class="var-name">{{ item.variableName }}</span>
|
||||||
placeholder="输入金额或数值"
|
</div>
|
||||||
/>
|
</template>
|
||||||
<el-input
|
|
||||||
v-else
|
<el-date-picker
|
||||||
v-model="item.variableValue"
|
v-if="item.dataType === 'date'"
|
||||||
:placeholder="`请输入${item.variableName}`"
|
v-model="item.variableValue"
|
||||||
class="var-input-field"
|
type="date"
|
||||||
clearable
|
value-format="YYYY-MM-DD"
|
||||||
/>
|
placeholder="选择日期"
|
||||||
</el-form-item>
|
class="var-input-field"
|
||||||
</el-col>
|
/>
|
||||||
</el-row>
|
<el-input-number
|
||||||
|
v-else-if="item.dataType === 'number' || item.dataType === 'decimal'"
|
||||||
|
v-model="item.variableValue"
|
||||||
|
controls-position="right"
|
||||||
|
class="var-input-field"
|
||||||
|
placeholder="输入金额或数值"
|
||||||
|
/>
|
||||||
|
<el-input
|
||||||
|
v-else
|
||||||
|
v-model="item.variableValue"
|
||||||
|
:placeholder="`请输入${item.variableName}`"
|
||||||
|
class="var-input-field"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 3. 合同逐份预览与确认 -->
|
<!-- 3. 合同逐份预览与确认 -->
|
||||||
@@ -1093,7 +1111,7 @@
|
|||||||
|
|
||||||
<el-alert
|
<el-alert
|
||||||
v-if="!electronicSignDetail.hasProcess"
|
v-if="!electronicSignDetail.hasProcess"
|
||||||
title="审核已通过,可将下列全部合同作为同一个签约流程发起。员工先签,完成后企业自动盖章。"
|
title="审核已通过,可将下列全部合同作为同一个签约流程发起。员工签完后,人事逐份核对,再由后台确认企业盖章。"
|
||||||
type="info"
|
type="info"
|
||||||
show-icon
|
show-icon
|
||||||
:closable="false"
|
:closable="false"
|
||||||
@@ -1159,6 +1177,15 @@
|
|||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
|
|
||||||
|
<el-alert
|
||||||
|
v-if="electronicSignDetail.signStatus === 'pending_company_seal'"
|
||||||
|
title="员工已完成签署,请逐份查看下方员工已签文件。全部核对后,才能确认企业盖章。"
|
||||||
|
type="warning"
|
||||||
|
show-icon
|
||||||
|
:closable="false"
|
||||||
|
class="electronic-sign-seal-alert"
|
||||||
|
/>
|
||||||
|
|
||||||
<div class="electronic-sign-section-title">
|
<div class="electronic-sign-section-title">
|
||||||
本次签约文件({{ electronicSignDetail.documents.length }} 份)
|
本次签约文件({{ electronicSignDetail.documents.length }} 份)
|
||||||
</div>
|
</div>
|
||||||
@@ -1180,6 +1207,29 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="electronic-sign-document-actions">
|
<div class="electronic-sign-document-actions">
|
||||||
|
<el-tag
|
||||||
|
v-if="
|
||||||
|
electronicSignDetail.signStatus === 'pending_company_seal'
|
||||||
|
&& documentItem.employeeSignedPreviewed
|
||||||
|
"
|
||||||
|
type="success"
|
||||||
|
size="small"
|
||||||
|
effect="plain"
|
||||||
|
>
|
||||||
|
已查看
|
||||||
|
</el-tag>
|
||||||
|
<el-button
|
||||||
|
v-if="
|
||||||
|
electronicSignDetail.signStatus === 'pending_company_seal'
|
||||||
|
&& documentItem.providerDocumentId
|
||||||
|
"
|
||||||
|
link
|
||||||
|
type="warning"
|
||||||
|
:loading="electronicSignPreviewingDocumentId === documentItem.id"
|
||||||
|
@click="previewEmployeeSignedDocument(documentItem)"
|
||||||
|
>
|
||||||
|
{{ documentItem.employeeSignedPreviewed ? '再次查看员工已签文件' : '查看员工已签文件' }}
|
||||||
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
v-if="documentItem.originalDocxFileId"
|
v-if="documentItem.originalDocxFileId"
|
||||||
link
|
link
|
||||||
@@ -1291,6 +1341,18 @@
|
|||||||
>
|
>
|
||||||
同步状态
|
同步状态
|
||||||
</el-button>
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
v-if="
|
||||||
|
electronicSignDetail?.hasProcess
|
||||||
|
&& electronicSignDetail.signStatus === 'pending_company_seal'
|
||||||
|
"
|
||||||
|
type="primary"
|
||||||
|
:loading="electronicSignLoading"
|
||||||
|
:disabled="!allEmployeeSignedDocumentsPreviewed"
|
||||||
|
@click="confirmCompanySeal"
|
||||||
|
>
|
||||||
|
确认企业盖章
|
||||||
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
v-if="electronicSignDetail?.archiveFileId"
|
v-if="electronicSignDetail?.archiveFileId"
|
||||||
type="success"
|
type="success"
|
||||||
@@ -1354,7 +1416,8 @@ import {
|
|||||||
type OnboardingContractTemplate,
|
type OnboardingContractTemplate,
|
||||||
type OnboardingTask,
|
type OnboardingTask,
|
||||||
type OnboardingTaskContractVariableGroup,
|
type OnboardingTaskContractVariableGroup,
|
||||||
type OnboardingElectronicSignDetail
|
type OnboardingElectronicSignDetail,
|
||||||
|
type OnboardingElectronicSignDocument
|
||||||
} from "@/api/modules/biz/onboarding";
|
} from "@/api/modules/biz/onboarding";
|
||||||
import type { ColumnProps, ProTableInstance } from "@/components/ProTable/interface";
|
import type { ColumnProps, ProTableInstance } from "@/components/ProTable/interface";
|
||||||
import { getMessageBoxPromptResult, isMessageBoxConfirmed } from "@/utils/messageBox";
|
import { getMessageBoxPromptResult, isMessageBoxConfirmed } from "@/utils/messageBox";
|
||||||
@@ -1549,6 +1612,7 @@ const savingAllVars = ref(false);
|
|||||||
|
|
||||||
const electronicSignVisible = ref(false);
|
const electronicSignVisible = ref(false);
|
||||||
const electronicSignLoading = ref(false);
|
const electronicSignLoading = ref(false);
|
||||||
|
const electronicSignPreviewingDocumentId = ref<number>();
|
||||||
const electronicSignTask = ref<OnboardingTask>();
|
const electronicSignTask = ref<OnboardingTask>();
|
||||||
const electronicSignDetail = ref<OnboardingElectronicSignDetail>();
|
const electronicSignDetail = ref<OnboardingElectronicSignDetail>();
|
||||||
const electronicSignStep = computed(() => {
|
const electronicSignStep = computed(() => {
|
||||||
@@ -1565,6 +1629,13 @@ const canInvalidElectronicSign = computed(() => {
|
|||||||
current.signStatus || ""
|
current.signStatus || ""
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
const allEmployeeSignedDocumentsPreviewed = computed(() => {
|
||||||
|
const current = electronicSignDetail.value;
|
||||||
|
return Boolean(
|
||||||
|
current?.documents.length
|
||||||
|
&& current.documents.every(item => item.employeeSignedPreviewed)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
function countVarType(variables: any[], type: string) {
|
function countVarType(variables: any[], type: string) {
|
||||||
if (!variables) return 0;
|
if (!variables) return 0;
|
||||||
@@ -1644,6 +1715,29 @@ const form = reactive({
|
|||||||
remark: ""
|
remark: ""
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const groupedContractVariables = computed(() => {
|
||||||
|
const groups: Array<{
|
||||||
|
contractTemplateVersionId: number;
|
||||||
|
contractName: string;
|
||||||
|
items: typeof form.contractVariables;
|
||||||
|
}> = [];
|
||||||
|
|
||||||
|
form.contractVariables.forEach(item => {
|
||||||
|
let group = groups.find(g => g.contractTemplateVersionId === item.contractTemplateVersionId);
|
||||||
|
if (!group) {
|
||||||
|
group = {
|
||||||
|
contractTemplateVersionId: item.contractTemplateVersionId,
|
||||||
|
contractName: item.contractName || "签署材料",
|
||||||
|
items: []
|
||||||
|
};
|
||||||
|
groups.push(group);
|
||||||
|
}
|
||||||
|
group.items.push(item);
|
||||||
|
});
|
||||||
|
|
||||||
|
return groups;
|
||||||
|
});
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => [
|
() => [
|
||||||
form.companyId,
|
form.companyId,
|
||||||
@@ -2559,7 +2653,7 @@ async function initiateElectronicSign() {
|
|||||||
const documentCount = current.documents.length;
|
const documentCount = current.documents.length;
|
||||||
const confirmed = await isMessageBoxConfirmed(
|
const confirmed = await isMessageBoxConfirmed(
|
||||||
ElMessageBox.confirm(
|
ElMessageBox.confirm(
|
||||||
`将把 ${documentCount} 份合同放入同一个契约锁签约流程,员工先签、企业随后自动盖章。确认现在发起吗?`,
|
`将把 ${documentCount} 份合同放入同一个契约锁签约流程。员工签完后,需要人事逐份核对并在后台确认企业盖章。确认现在发起吗?`,
|
||||||
current.hasProcess ? "继续电子签流程" : "发起电子签确认",
|
current.hasProcess ? "继续电子签流程" : "发起电子签确认",
|
||||||
{
|
{
|
||||||
type: "warning",
|
type: "warning",
|
||||||
@@ -2600,6 +2694,82 @@ async function syncElectronicSign() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function previewEmployeeSignedDocument(
|
||||||
|
documentItem: OnboardingElectronicSignDocument
|
||||||
|
) {
|
||||||
|
const task = electronicSignTask.value;
|
||||||
|
if (!task || !documentItem.id) return;
|
||||||
|
|
||||||
|
// 必须在用户点击时先打开空白页,否则等待接口返回后再打开可能被浏览器拦截。
|
||||||
|
const previewWindow = window.open("", "_blank");
|
||||||
|
if (!previewWindow) {
|
||||||
|
ElMessage.warning("浏览器拦截了预览窗口,请允许本站打开新窗口后重试");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
previewWindow.opener = null;
|
||||||
|
try {
|
||||||
|
previewWindow.document.title = "员工已签合同加载中";
|
||||||
|
previewWindow.document.body.innerText = "正在打开契约锁员工已签文件,请稍候……";
|
||||||
|
} catch {
|
||||||
|
// 空白页提示失败不影响后续跳转。
|
||||||
|
}
|
||||||
|
|
||||||
|
electronicSignPreviewingDocumentId.value = documentItem.id;
|
||||||
|
try {
|
||||||
|
const { data } = await onboardingTaskApi.previewEmployeeSignedDocument(
|
||||||
|
task.id,
|
||||||
|
documentItem.id
|
||||||
|
);
|
||||||
|
if (!data.previewUrl) throw new Error("契约锁没有返回合同浏览地址");
|
||||||
|
documentItem.employeeSignedPreviewed = true;
|
||||||
|
previewWindow.location.replace(data.previewUrl);
|
||||||
|
} catch (error) {
|
||||||
|
previewWindow.close();
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
electronicSignPreviewingDocumentId.value = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function confirmCompanySeal() {
|
||||||
|
const task = electronicSignTask.value;
|
||||||
|
const current = electronicSignDetail.value;
|
||||||
|
if (!task || current?.signStatus !== "pending_company_seal") return;
|
||||||
|
if (!allEmployeeSignedDocumentsPreviewed.value) {
|
||||||
|
ElMessage.warning("请先逐份查看所有员工已签文件");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const confirmed = await isMessageBoxConfirmed(
|
||||||
|
ElMessageBox.confirm(
|
||||||
|
`已逐份核对 ${current.documents.length} 份员工已签文件。确认使用当前公司的印章执行企业盖章吗?盖章后合同内容不能修改。`,
|
||||||
|
"确认企业盖章",
|
||||||
|
{
|
||||||
|
type: "warning",
|
||||||
|
confirmButtonText: "确认盖章",
|
||||||
|
cancelButtonText: "取消"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
if (!confirmed) return;
|
||||||
|
|
||||||
|
electronicSignLoading.value = true;
|
||||||
|
try {
|
||||||
|
const { data } = await onboardingTaskApi.companySealElectronicSign(task.id);
|
||||||
|
electronicSignDetail.value = data;
|
||||||
|
if (["completed", "archived"].includes(data.signStatus || "")) {
|
||||||
|
ElMessage.success("企业盖章已完成");
|
||||||
|
} else {
|
||||||
|
ElMessage.success("企业盖章请求已提交,系统正在同步签署结果");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
await loadElectronicSignDetail().catch(() => undefined);
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
electronicSignLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
async function reinitiateElectronicSign() {
|
async function reinitiateElectronicSign() {
|
||||||
const task = electronicSignTask.value;
|
const task = electronicSignTask.value;
|
||||||
@@ -3210,6 +3380,65 @@ onMounted(loadCompanies);
|
|||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.contract-vars-group-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contract-var-group-card {
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
|
border-radius: 8px;
|
||||||
|
background-color: #f8fafc;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: border-color 0.2s, box-shadow 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contract-var-group-card:hover {
|
||||||
|
border-color: #cbd5e1;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.var-group-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 10px 16px;
|
||||||
|
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
|
||||||
|
border-bottom: 1px solid #e2e8f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.var-group-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.var-group-icon {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #3b82f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.var-group-name {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1e293b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.var-group-count {
|
||||||
|
font-weight: 500;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.var-group-content {
|
||||||
|
padding: 16px 16px 0 16px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.var-group-content :deep(.el-form-item) {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
.var-label-row {
|
.var-label-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -3582,6 +3811,10 @@ onMounted(loadCompanies);
|
|||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.electronic-sign-seal-alert {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
.electronic-sign-section-title {
|
.electronic-sign-section-title {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
color: var(--el-text-color-primary);
|
color: var(--el-text-color-primary);
|
||||||
@@ -3620,6 +3853,13 @@ onMounted(loadCompanies);
|
|||||||
|
|
||||||
.electronic-sign-document-actions {
|
.electronic-sign-document-actions {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
justify-content: flex-end;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.electronic-sign-document-actions :deep(.el-button) {
|
||||||
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.document-state-text {
|
.document-state-text {
|
||||||
@@ -3656,5 +3896,9 @@ onMounted(loadCompanies);
|
|||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.electronic-sign-document-actions {
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -7,8 +7,8 @@
|
|||||||
<el-icon><Promotion /></el-icon>
|
<el-icon><Promotion /></el-icon>
|
||||||
<span>OnboardX 智能入职服务</span>
|
<span>OnboardX 智能入职服务</span>
|
||||||
</div>
|
</div>
|
||||||
<h1 class="brand-title">欢迎加入新团队</h1>
|
<h1 class="brand-title">很高兴与你相遇,欢迎加入!</h1>
|
||||||
<p class="brand-subtitle">请按照 4 步指引完成入职登记表、证件上传与签署材料查阅确认</p>
|
<p class="brand-subtitle">请完成以下入职流程:填写登记信息、上传证件、阅读并签署相关入职材料,开启你的职场新旅程。</p>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
@@ -102,7 +102,7 @@
|
|||||||
<div v-show="currentStep === 0" class="step-content">
|
<div v-show="currentStep === 0" class="step-content">
|
||||||
<div class="registration-header">
|
<div class="registration-header">
|
||||||
<h2 class="reg-title">入 职 登 记 表</h2>
|
<h2 class="reg-title">入 职 登 记 表</h2>
|
||||||
<p class="reg-subtitle">请准确填写您的真实个人档案、学习经历与家庭信息(带 * 为必填项)</p>
|
<p class="reg-subtitle">请如实填写个人基础档案、教育经历及家庭相关信息,标注 * 为必填项,信息将用于建立人事档案,请务必保证资料真实准确。</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-form ref="regFormRef" :model="form" :rules="regRules" label-position="top" class="reg-form">
|
<el-form ref="regFormRef" :model="form" :rules="regRules" label-position="top" class="reg-form">
|
||||||
@@ -1696,6 +1696,7 @@ onBeforeUnmount(() => {
|
|||||||
.brand-subtitle {
|
.brand-subtitle {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
line-height: 1.6;
|
||||||
opacity: 0.92;
|
opacity: 0.92;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1767,6 +1768,7 @@ onBeforeUnmount(() => {
|
|||||||
|
|
||||||
.reg-subtitle {
|
.reg-subtitle {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
|
line-height: 1.6;
|
||||||
color: #64748b;
|
color: #64748b;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user