签订日期

This commit is contained in:
2026-06-08 14:32:00 +08:00
parent 9df278e85f
commit 92fd726e2b
4 changed files with 222 additions and 5 deletions
@@ -602,12 +602,28 @@
placeholder="选填,用于应收账款提醒" placeholder="选填,用于应收账款提醒"
/> />
</a-form-item> </a-form-item>
<a-form-item class="tight-item" label="签订日期">
<a-date-picker
v-model:value="detailState.signDate"
value-format="YYYY-MM-DD"
style="width: 100%"
placeholder="签署完成前需补齐"
/>
</a-form-item>
<a-form-item class="tight-item" label="用印时间">
<a-date-picker
v-model:value="detailState.sealDate"
value-format="YYYY-MM-DD"
style="width: 100%"
placeholder="签署完成前需补齐"
/>
</a-form-item>
<a-form-item class="tight-item" label="合同生效日期"> <a-form-item class="tight-item" label="合同生效日期">
<a-date-picker <a-date-picker
v-model:value="detailState.activeDate" v-model:value="detailState.activeDate"
value-format="YYYY-MM-DD" value-format="YYYY-MM-DD"
style="width: 100%" style="width: 100%"
placeholder="合同生效日期必须在合同期限范围内" placeholder="必填,且必须在合同期限范围内"
/> />
</a-form-item> </a-form-item>
<a-form-item class="tight-item" label="用印数量"> <a-form-item class="tight-item" label="用印数量">
@@ -1450,6 +1466,7 @@ function createContractFormData() {
bzjAmount: 0, bzjAmount: 0,
performancePlans: [], performancePlans: [],
signDate: "", signDate: "",
sealDate: "",
activeDate: "", activeDate: "",
sealQuantity: 1, sealQuantity: 1,
}; };
@@ -1503,7 +1520,7 @@ function copyContractFormData(data = {}) {
? source.contractBodyArrayBuffer.slice(0) ? source.contractBodyArrayBuffer.slice(0)
: null : null
: null; : null;
} else if (key === "signDate" || key === "invoiceDate" || key === "receiveDate" || key === "activeDate") { } else if (key === "signDate" || key === "sealDate" || key === "invoiceDate" || key === "receiveDate" || key === "activeDate") {
form[key] = source[key] ? String(source[key]).slice(0, 10) : form[key]; form[key] = source[key] ? String(source[key]).slice(0, 10) : form[key];
} else if (key === "assistDept") { } else if (key === "assistDept") {
form.assistDept = source.assistDept ?? source.assistDepartmentName ?? form.assistDept; form.assistDept = source.assistDept ?? source.assistDepartmentName ?? form.assistDept;
@@ -2032,6 +2049,15 @@ function validateState() {
!detailState.value.periodExplain?.trim() !detailState.value.periodExplain?.trim()
) )
return "请填写期限说明"; return "请填写期限说明";
if (!detailState.value.activeDate) return "请选择合同生效日期";
if (
detailState.value.contractPeriodType === "0" &&
detailState.value.effectiveStart &&
detailState.value.effectiveEnd &&
(detailState.value.activeDate < detailState.value.effectiveStart ||
detailState.value.activeDate > detailState.value.effectiveEnd)
)
return "合同生效日期必须在合同期限范围内";
if (!detailState.value.valuationMode) return "请选择计价方式"; if (!detailState.value.valuationMode) return "请选择计价方式";
// 固定总价时标的额必填 // 固定总价时标的额必填
if ( if (
@@ -2073,12 +2099,19 @@ function validateState() {
!(detailState.value.authAttachmentList || []).length !(detailState.value.authAttachmentList || []).length
) )
return "请上传授权委托书附件"; return "请上传授权委托书附件";
if (!(detailState.value.performancePlans || []).length)
return "请至少添加一条履行计划";
for (const plan of detailState.value.performancePlans || []) { for (const plan of detailState.value.performancePlans || []) {
if (!plan.planMatter?.trim()) return "请填写履行计划的履行事项"; if (!plan.planMatter?.trim()) return "请填写履行计划的履行事项";
if (!plan.planWhere?.trim()) return "请填写履行计划的履约条件"; if (!plan.planWhere?.trim()) return "请填写履行计划的履约条件";
if (!plan.planTime) return "请选择履行计划的节点日期"; if (!plan.planTime) return "请选择履行计划的节点日期";
if (plan.planDay === null || plan.planDay === undefined || plan.planDay === "") if (plan.planDay === null || plan.planDay === undefined || plan.planDay === "")
return "请填写履行计划的提醒提前天数"; return "请填写履行计划的提醒提前天数";
if (
!["0", "3"].includes(detailState.value.valuationMode) &&
(plan.planMoney === null || plan.planMoney === undefined || plan.planMoney === "")
)
return "请填写履行计划的款项金额";
} }
return ""; return "";
} }
@@ -2258,6 +2291,7 @@ function buildLaunchRecord() {
contractTextFileType: detailState.value.contractBodyFileType || "docx", contractTextFileType: detailState.value.contractBodyFileType || "docx",
contractTextFileSize: detailState.value.contractBodyFileSize || null, contractTextFileSize: detailState.value.contractBodyFileSize || null,
signDate: formatDateTimeValue(detailState.value.signDate), signDate: formatDateTimeValue(detailState.value.signDate),
sealDate: formatDateTimeValue(detailState.value.sealDate),
invoiceDate: formatDateTimeValue(detailState.value.invoiceDate), invoiceDate: formatDateTimeValue(detailState.value.invoiceDate),
receiveDate: formatDateTimeValue(detailState.value.receiveDate), receiveDate: formatDateTimeValue(detailState.value.receiveDate),
activeDate: formatDateTimeValue(detailState.value.activeDate), activeDate: formatDateTimeValue(detailState.value.activeDate),
@@ -52,6 +52,33 @@
</div> </div>
</a-form-item> </a-form-item>
<a-form-item label="签订日期" required>
<a-date-picker
v-model:value="signDate"
value-format="YYYY-MM-DD"
style="width: 100%"
placeholder="请选择签订日期"
/>
</a-form-item>
<a-form-item label="用印时间" required>
<a-date-picker
v-model:value="sealDate"
value-format="YYYY-MM-DD"
style="width: 100%"
placeholder="请选择用印时间"
/>
</a-form-item>
<a-form-item label="合同生效日期" required>
<a-date-picker
v-model:value="activeDate"
value-format="YYYY-MM-DD"
style="width: 100%"
placeholder="请选择合同生效日期"
/>
</a-form-item>
<a-form-item label="不在线签署的原因" required> <a-form-item label="不在线签署的原因" required>
<a-textarea <a-textarea
v-model:value="offlineReason" v-model:value="offlineReason"
@@ -78,6 +105,9 @@ const visible = ref(false);
const row = ref(null); const row = ref(null);
const uploadFile = ref(null); const uploadFile = ref(null);
const offlineReason = ref(""); const offlineReason = ref("");
const signDate = ref("");
const sealDate = ref("");
const activeDate = ref("");
const dragOver = ref(false); const dragOver = ref(false);
const submitting = ref(false); const submitting = ref(false);
const fileInputRef = ref(null); const fileInputRef = ref(null);
@@ -88,11 +118,39 @@ function showModal(record) {
row.value = record; row.value = record;
uploadFile.value = null; uploadFile.value = null;
offlineReason.value = ""; offlineReason.value = "";
signDate.value = sliceDateValue(record?.signDate) || todayString();
sealDate.value = sliceDateValue(record?.sealDate) || signDate.value;
activeDate.value = sliceDateValue(record?.activeDate) || sliceDateValue(record?.effectiveStart);
dragOver.value = false; dragOver.value = false;
submitting.value = false; submitting.value = false;
visible.value = true; visible.value = true;
} }
function todayString() {
const now = new Date();
const month = String(now.getMonth() + 1).padStart(2, "0");
const day = String(now.getDate()).padStart(2, "0");
return `${now.getFullYear()}-${month}-${day}`;
}
function sliceDateValue(value) {
return value ? String(value).slice(0, 10) : "";
}
function formatDateTimeValue(value) {
return value ? `${value} 00:00:00` : null;
}
function isActiveDateWithinTerm() {
if (!activeDate.value) return false;
if (row.value?.contractPeriodType !== "0") return true;
const start = sliceDateValue(row.value?.effectiveStart);
const end = sliceDateValue(row.value?.effectiveEnd);
if (start && activeDate.value < start) return false;
if (end && activeDate.value > end) return false;
return true;
}
function triggerFileInput() { function triggerFileInput() {
fileInputRef.value?.click(); fileInputRef.value?.click();
} }
@@ -151,6 +209,22 @@ async function handleSubmit() {
message.warning("请填写不在线签署的原因"); message.warning("请填写不在线签署的原因");
return; return;
} }
if (!signDate.value) {
message.warning("请选择签订日期");
return;
}
if (!sealDate.value) {
message.warning("请选择用印时间");
return;
}
if (!activeDate.value) {
message.warning("请选择合同生效日期");
return;
}
if (!isActiveDateWithinTerm()) {
message.warning("合同生效日期必须在合同期限范围内");
return;
}
submitting.value = true; submitting.value = true;
try { try {
@@ -175,6 +249,9 @@ async function handleSubmit() {
signedFileType: fileType, signedFileType: fileType,
signedFileSize: fileSize, signedFileSize: fileSize,
signedOfflineReason: reason, signedOfflineReason: reason,
signDate: formatDateTimeValue(signDate.value),
sealDate: formatDateTimeValue(sealDate.value),
activeDate: formatDateTimeValue(activeDate.value),
}); });
if (String(finishRes?.code || "") !== SUCCESS_CODE) { if (String(finishRes?.code || "") !== SUCCESS_CODE) {
+25 -3
View File
@@ -266,6 +266,21 @@ function getResponseMessage(res, fallback = "操作失败") {
return res?.message || res?.msg || fallback; return res?.message || res?.msg || fallback;
} }
function sliceDateValue(value) {
return value ? String(value).slice(0, 10) : "";
}
function todayString() {
const now = new Date();
const month = String(now.getMonth() + 1).padStart(2, "0");
const day = String(now.getDate()).padStart(2, "0");
return `${now.getFullYear()}-${month}-${day}`;
}
function formatDateTimeValue(value) {
return value ? `${value} 00:00:00` : null;
}
function connectToolbar() { function connectToolbar() {
const table = tableRef.value; const table = tableRef.value;
const toolbar = toolbarRef.value; const toolbar = toolbarRef.value;
@@ -563,8 +578,11 @@ function handleMockFinishSign(row) {
content: `模拟合同【${row.contractName}】签署完成,状态将变为「待采集」。`, content: `模拟合同【${row.contractName}】签署完成,状态将变为「待采集」。`,
okText: "确认模拟", okText: "确认模拟",
cancelText: "取消", cancelText: "取消",
onOk: () => onOk: () => {
finishLaunchSeal(row.id, { const signDate = sliceDateValue(row.signDate) || todayString();
const sealDate = sliceDateValue(row.sealDate) || signDate;
const activeDate = sliceDateValue(row.activeDate) || sliceDateValue(row.effectiveStart) || signDate;
return finishLaunchSeal(row.id, {
sealContractId: row.sealContractId || `TEST-SEAL-${row.id}`, sealContractId: row.sealContractId || `TEST-SEAL-${row.id}`,
sealContractStatus: 2000, sealContractStatus: 2000,
signedFileName: `${row.contractName || "合同"}-测试已签署.pdf`, signedFileName: `${row.contractName || "合同"}-测试已签署.pdf`,
@@ -572,6 +590,9 @@ function handleMockFinishSign(row) {
signedFileType: "pdf", signedFileType: "pdf",
signedFileSize: row.contractTextFileSize || null, signedFileSize: row.contractTextFileSize || null,
signedOfflineReason: "测试按钮模拟签署完成真实签章接入后可删除此按钮", signedOfflineReason: "测试按钮模拟签署完成真实签章接入后可删除此按钮",
signDate: formatDateTimeValue(signDate),
sealDate: formatDateTimeValue(sealDate),
activeDate: formatDateTimeValue(activeDate),
}).then((res) => { }).then((res) => {
if (!isSuccessResponse(res)) { if (!isSuccessResponse(res)) {
message.error(getResponseMessage(res, "测试签署完成失败")); message.error(getResponseMessage(res, "测试签署完成失败"));
@@ -579,7 +600,8 @@ function handleMockFinishSign(row) {
} }
message.success("测试签署完成合同已流转到待采集"); message.success("测试签署完成合同已流转到待采集");
getList(); getList();
}), });
},
icon: null, icon: null,
}); });
} }
+84
View File
@@ -486,6 +486,27 @@
<span class="step-title">结束合约</span> <span class="step-title">结束合约</span>
</div> </div>
<div class="step-card-body"> <div class="step-card-body">
<div class="complete-form-grid">
<a-date-picker
v-model:value="completeSignDate"
value-format="YYYY-MM-DD"
style="width: 100%"
placeholder="签订日期"
/>
<a-date-picker
v-model:value="completeSealDate"
value-format="YYYY-MM-DD"
style="width: 100%"
placeholder="用印时间"
/>
<a-date-picker
v-model:value="completeActiveDate"
value-format="YYYY-MM-DD"
style="width: 100%"
placeholder="合同生效日期"
/>
</div>
<div <div
class="btn btn-success" class="btn btn-success"
:class="{ :class="{
@@ -656,6 +677,9 @@ function getResponseMessage(res, fallback = "操作失败") {
const visible = ref(false); const visible = ref(false);
const businessRecord = ref(null); const businessRecord = ref(null);
const emit = defineEmits(["business-updated"]); const emit = defineEmits(["business-updated"]);
const completeSignDate = ref("");
const completeSealDate = ref("");
const completeActiveDate = ref("");
const businessStatusLabelMap = { const businessStatusLabelMap = {
draft: "草稿", draft: "草稿",
@@ -723,6 +747,50 @@ function formatBusinessAmount(value) {
}).format(Number(value))} 元`; }).format(Number(value))} 元`;
} }
function todayString() {
const now = new Date();
const month = String(now.getMonth() + 1).padStart(2, "0");
const day = String(now.getDate()).padStart(2, "0");
return `${now.getFullYear()}-${month}-${day}`;
}
function sliceDateValue(value) {
return value ? String(value).slice(0, 10) : "";
}
function formatDateTimeValue(value) {
return value ? `${value} 00:00:00` : null;
}
function syncCompletionDates(record = {}) {
completeSignDate.value = sliceDateValue(record?.signDate) || todayString();
completeSealDate.value = sliceDateValue(record?.sealDate) || completeSignDate.value;
completeActiveDate.value = sliceDateValue(record?.activeDate) || sliceDateValue(record?.effectiveStart);
}
function validateCompletionDates() {
if (!completeSignDate.value) {
return "请选择签订日期";
}
if (!completeSealDate.value) {
return "请选择用印时间";
}
if (!completeActiveDate.value) {
return "请选择合同生效日期";
}
if (businessRecord.value?.contractPeriodType === "0") {
const start = sliceDateValue(businessRecord.value?.effectiveStart);
const end = sliceDateValue(businessRecord.value?.effectiveEnd);
if (start && completeActiveDate.value < start) {
return "合同生效日期不能早于合同开始日期";
}
if (end && completeActiveDate.value > end) {
return "合同生效日期不能晚于合同结束日期";
}
}
return "";
}
function mapBusinessStatusToContractStatus(status) { function mapBusinessStatusToContractStatus(status) {
if (status === "completed") return 2000; if (status === "completed") return 2000;
if (status === "signing") return 20; if (status === "signing") return 20;
@@ -863,6 +931,9 @@ async function writeBackLaunchSealFinished(status = 2000) {
const res = await finishLaunchSeal(businessRecord.value.id, { const res = await finishLaunchSeal(businessRecord.value.id, {
sealContractId: contractId.value, sealContractId: contractId.value,
sealContractStatus: status, sealContractStatus: status,
signDate: formatDateTimeValue(completeSignDate.value),
sealDate: formatDateTimeValue(completeSealDate.value),
activeDate: formatDateTimeValue(completeActiveDate.value),
...signedFileInfo, ...signedFileInfo,
}); });
@@ -882,6 +953,7 @@ function resetSealPlacementState() {
function resetModalState(record = null) { function resetModalState(record = null) {
businessRecord.value = record ? { ...record } : null; businessRecord.value = record ? { ...record } : null;
syncCompletionDates(record || {});
contractId.value = ""; contractId.value = "";
contractStatus.value = mapBusinessStatusToContractStatus(record?.status); contractStatus.value = mapBusinessStatusToContractStatus(record?.status);
pdfImages.value = []; pdfImages.value = [];
@@ -1623,6 +1695,11 @@ async function handleCompleteContract() {
message.warning("合约已结束"); message.warning("合约已结束");
return; return;
} }
const validateMessage = validateCompletionDates();
if (validateMessage) {
message.warning(validateMessage);
return;
}
Modal.confirm({ Modal.confirm({
title: "确认结束", title: "确认结束",
@@ -2454,6 +2531,13 @@ watch(visible, (value) => {
font-size: 13px; font-size: 13px;
} }
.complete-form-grid {
display: grid;
grid-template-columns: 1fr;
gap: 10px;
margin-bottom: 12px;
}
.tag-success { .tag-success {
color: #4a9c5d; color: #4a9c5d;
font-weight: 500; font-weight: 500;