完善第三方签署
This commit is contained in:
@@ -250,49 +250,67 @@
|
||||
style="margin-bottom: 16px;"
|
||||
/>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col
|
||||
v-for="item in form.contractVariables"
|
||||
:key="`${item.contractTemplateVersionId}-${item.variableCode}`"
|
||||
:xs="24"
|
||||
:sm="12"
|
||||
<!-- 按材料分组展示人事参数 -->
|
||||
<div class="contract-vars-group-list">
|
||||
<div
|
||||
v-for="group in groupedContractVariables"
|
||||
:key="group.contractTemplateVersionId"
|
||||
class="contract-var-group-card"
|
||||
>
|
||||
<el-form-item>
|
||||
<template #label>
|
||||
<div class="var-label-row">
|
||||
<span v-if="item.required" class="required-star">*</span>
|
||||
<span class="var-name">{{ item.variableName }}</span>
|
||||
<el-tag size="small" type="info" effect="plain" class="contract-source-tag">
|
||||
{{ item.contractName }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
<div class="var-group-header">
|
||||
<div class="var-group-title">
|
||||
<el-icon class="var-group-icon"><Document /></el-icon>
|
||||
<span class="var-group-name">{{ group.contractName }}</span>
|
||||
</div>
|
||||
<el-tag size="small" type="primary" effect="plain" class="var-group-count">
|
||||
{{ group.items.length }} 项人事参数
|
||||
</el-tag>
|
||||
</div>
|
||||
|
||||
<el-date-picker
|
||||
v-if="item.dataType === 'date'"
|
||||
v-model="item.variableValue"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="选择日期"
|
||||
class="var-input-field"
|
||||
/>
|
||||
<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 class="var-group-content">
|
||||
<el-row :gutter="20">
|
||||
<el-col
|
||||
v-for="item in group.items"
|
||||
:key="`${item.contractTemplateVersionId}-${item.variableCode}`"
|
||||
:xs="24"
|
||||
:sm="12"
|
||||
>
|
||||
<el-form-item>
|
||||
<template #label>
|
||||
<div class="var-label-row">
|
||||
<span v-if="item.required" class="required-star">*</span>
|
||||
<span class="var-name">{{ item.variableName }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-date-picker
|
||||
v-if="item.dataType === 'date'"
|
||||
v-model="item.variableValue"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="选择日期"
|
||||
class="var-input-field"
|
||||
/>
|
||||
<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>
|
||||
|
||||
<!-- 3. 合同逐份预览与确认 -->
|
||||
@@ -1093,7 +1111,7 @@
|
||||
|
||||
<el-alert
|
||||
v-if="!electronicSignDetail.hasProcess"
|
||||
title="审核已通过,可将下列全部合同作为同一个签约流程发起。员工先签,完成后企业自动盖章。"
|
||||
title="审核已通过,可将下列全部合同作为同一个签约流程发起。员工签完后,人事逐份核对,再由后台确认企业盖章。"
|
||||
type="info"
|
||||
show-icon
|
||||
:closable="false"
|
||||
@@ -1159,6 +1177,15 @@
|
||||
</el-descriptions-item>
|
||||
</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">
|
||||
本次签约文件({{ electronicSignDetail.documents.length }} 份)
|
||||
</div>
|
||||
@@ -1180,6 +1207,29 @@
|
||||
</div>
|
||||
</div>
|
||||
<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
|
||||
v-if="documentItem.originalDocxFileId"
|
||||
link
|
||||
@@ -1291,6 +1341,18 @@
|
||||
>
|
||||
同步状态
|
||||
</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
|
||||
v-if="electronicSignDetail?.archiveFileId"
|
||||
type="success"
|
||||
@@ -1354,7 +1416,8 @@ import {
|
||||
type OnboardingContractTemplate,
|
||||
type OnboardingTask,
|
||||
type OnboardingTaskContractVariableGroup,
|
||||
type OnboardingElectronicSignDetail
|
||||
type OnboardingElectronicSignDetail,
|
||||
type OnboardingElectronicSignDocument
|
||||
} from "@/api/modules/biz/onboarding";
|
||||
import type { ColumnProps, ProTableInstance } from "@/components/ProTable/interface";
|
||||
import { getMessageBoxPromptResult, isMessageBoxConfirmed } from "@/utils/messageBox";
|
||||
@@ -1549,6 +1612,7 @@ const savingAllVars = ref(false);
|
||||
|
||||
const electronicSignVisible = ref(false);
|
||||
const electronicSignLoading = ref(false);
|
||||
const electronicSignPreviewingDocumentId = ref<number>();
|
||||
const electronicSignTask = ref<OnboardingTask>();
|
||||
const electronicSignDetail = ref<OnboardingElectronicSignDetail>();
|
||||
const electronicSignStep = computed(() => {
|
||||
@@ -1565,6 +1629,13 @@ const canInvalidElectronicSign = computed(() => {
|
||||
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) {
|
||||
if (!variables) return 0;
|
||||
@@ -1644,6 +1715,29 @@ const form = reactive({
|
||||
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(
|
||||
() => [
|
||||
form.companyId,
|
||||
@@ -2559,7 +2653,7 @@ async function initiateElectronicSign() {
|
||||
const documentCount = current.documents.length;
|
||||
const confirmed = await isMessageBoxConfirmed(
|
||||
ElMessageBox.confirm(
|
||||
`将把 ${documentCount} 份合同放入同一个契约锁签约流程,员工先签、企业随后自动盖章。确认现在发起吗?`,
|
||||
`将把 ${documentCount} 份合同放入同一个契约锁签约流程。员工签完后,需要人事逐份核对并在后台确认企业盖章。确认现在发起吗?`,
|
||||
current.hasProcess ? "继续电子签流程" : "发起电子签确认",
|
||||
{
|
||||
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() {
|
||||
const task = electronicSignTask.value;
|
||||
@@ -3210,6 +3380,65 @@ onMounted(loadCompanies);
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -3582,6 +3811,10 @@ onMounted(loadCompanies);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.electronic-sign-seal-alert {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.electronic-sign-section-title {
|
||||
margin-bottom: 10px;
|
||||
color: var(--el-text-color-primary);
|
||||
@@ -3620,6 +3853,13 @@ onMounted(loadCompanies);
|
||||
|
||||
.electronic-sign-document-actions {
|
||||
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 {
|
||||
@@ -3656,5 +3896,9 @@ onMounted(loadCompanies);
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.electronic-sign-document-actions {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user