diff --git a/src/views/onboarding/fill/index.vue b/src/views/onboarding/fill/index.vue index b633b66..bca865e 100644 --- a/src/views/onboarding/fill/index.vue +++ b/src/views/onboarding/fill/index.vue @@ -628,11 +628,18 @@ v-if="canEdit" v-model="c.isAgreed" class="agree-checkbox" - :disabled="!isContractPrepared(c.id)" + :disabled="!isContractPrepared(c.id) || !c.hasViewed" :label="`我已完整阅读并同意《${c.contractName}》全部条款`" /> ✓ 候选人已确认同意 + +
+ 请先点击“下载并查看合同”,查看后才能勾选确认。 +
@@ -897,6 +904,7 @@ async function load(silent = false) { task.value = data.task; contracts.value = (data.contracts || []).map((c: any) => ({ ...c, + hasViewed: Boolean(c.readAt), isAgreed: c.contractStatus === "confirmed" })); contracts.value.forEach((contract: any) => { @@ -1103,6 +1111,7 @@ async function prepareContract(contract: any) { contractPrepareErrors[key] = ""; try { + const previousFileId = contract.generatedDocxFileId; const response = await onboardingPublicApi.prepareContract({ token: token.value, taskContractId: contract.id @@ -1110,13 +1119,20 @@ async function prepareContract(contract: any) { // 使用后端返回的新文件 Id 作为下载版本,避免手机浏览器复用旧合同缓存。 contract.generatedDocxFileId = response.data.fileId; contract.generatedAt = response.data.generatedAt; + if (previousFileId && previousFileId !== response.data.fileId) { + contract.hasViewed = false; + contract.isAgreed = false; + } contractPrepareStatus[key] = "ready"; return true; } catch (err: any) { - const message = err?.message || "合同准备失败,请稍后重试"; + const message = err?.msg || err?.message || "合同准备失败,请稍后重试"; contractPrepareStatus[key] = "error"; contractPrepareErrors[key] = message; - ElMessage.error(`${contract.contractName}:${message}`); + // 业务异常已由统一请求拦截器提示;仅为网络等非标准异常补充提示,避免重复弹窗。 + if (!err?.msg) { + ElMessage.error(`${contract.contractName}:${message}`); + } return false; } } @@ -1132,7 +1148,10 @@ async function prepareContracts() { async function handleContractAction(contract: any) { if (isContractPrepareFailed(contract.id)) { - await prepareContract(contract); + const prepared = await prepareContract(contract); + if (prepared) { + ElMessage.success("合同已重新准备完成,请再次点击下载查看"); + } return; } @@ -1148,7 +1167,9 @@ function previewAndDownloadContract(c: any) { fileVersion: c.generatedDocxFileId }); - // 下载必须直接发生在用户点击事件中,避免手机浏览器拦截异步打开或二次跳转。 + c.hasViewed = true; + c.isAgreed = false; + // 下载地址必须在用户点击事件中直接打开,微信及部分手机浏览器会拦截异步跳转空白页。 const downloadWindow = window.open(url, "_blank"); if (!downloadWindow) { window.location.assign(url); @@ -1161,6 +1182,11 @@ async function submitAll() { return ElMessage.warning(`请先等待合同准备完成:${unprepared.map(c => c.contractName).join("、")}`); } + const unviewed = contracts.value.filter(c => !c.hasViewed); + if (unviewed.length) { + return ElMessage.warning(`请先下载并查看合同:${unviewed.map(c => c.contractName).join("、")}`); + } + const unconfirmed = contracts.value.filter(c => !c.isAgreed); if (unconfirmed.length) { return ElMessage.warning(`请先勾选同意全部合同(未勾选:${unconfirmed.map(c => c.contractName).join("、")})`); @@ -1179,7 +1205,7 @@ async function submitAll() { submitting.value = true; try { - await saveCurrentEmployeeInfo(); + // 员工资料在进入材料步骤前已经保存;此处重复保存会使刚生成的合同失效。 for (const c of contracts.value) { await onboardingPublicApi.confirmContract({ token: token.value, taskContractId: c.id }); } @@ -1571,6 +1597,13 @@ onBeforeUnmount(() => { font-weight: 600; } +.contract-view-hint { + margin-top: 10px; + color: #d97706; + font-size: 13px; + line-height: 1.5; +} + .confirmed-text { color: #16a34a; font-weight: 600;