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;