11
This commit is contained in:
@@ -121,6 +121,14 @@ export function mockCollectLaunch(id) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function saveContractCollectInfo(id, data) {
|
||||||
|
return request({
|
||||||
|
url: `/contract/launch/${id}/collect/save`,
|
||||||
|
method: "post",
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function archiveLaunch(id, data = {}) {
|
export function archiveLaunch(id, data = {}) {
|
||||||
return request({
|
return request({
|
||||||
url: `/contract/launch/${id}/archive`,
|
url: `/contract/launch/${id}/archive`,
|
||||||
|
|||||||
@@ -0,0 +1,348 @@
|
|||||||
|
<template>
|
||||||
|
<a-modal
|
||||||
|
v-model:open="visible"
|
||||||
|
title="合同采集"
|
||||||
|
width="800px"
|
||||||
|
:mask-closable="false"
|
||||||
|
:confirm-loading="submitting"
|
||||||
|
ok-text="确认采集"
|
||||||
|
cancel-text="取消"
|
||||||
|
@ok="handleSubmit"
|
||||||
|
>
|
||||||
|
<div class="collect-body">
|
||||||
|
<a-descriptions :column="2" size="small" bordered class="contract-brief">
|
||||||
|
<a-descriptions-item label="合同名称">
|
||||||
|
{{ row?.contractName || "-" }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="合同编码">
|
||||||
|
{{ row?.contractCode || "-" }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
</a-descriptions>
|
||||||
|
|
||||||
|
<a-divider style="margin: 16px 0" />
|
||||||
|
|
||||||
|
<a-form layout="vertical">
|
||||||
|
<div class="section-header">
|
||||||
|
<span class="section-title-icon">📜</span>
|
||||||
|
用印信息(c_seal)
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field-row">
|
||||||
|
<a-form-item label="是否用印" required class="field-half">
|
||||||
|
<a-radio-group v-model:value="formState.sealIsSeal">
|
||||||
|
<a-radio value="1">是</a-radio>
|
||||||
|
<a-radio value="0">否</a-radio>
|
||||||
|
</a-radio-group>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
label="用印类型"
|
||||||
|
class="field-half"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
:value="sealTypeDisplay"
|
||||||
|
readonly
|
||||||
|
placeholder="沿用合同签订方式"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field-row">
|
||||||
|
<a-form-item label="审批是否一致" required class="field-half">
|
||||||
|
<a-radio-group v-model:value="formState.sealApproveSame">
|
||||||
|
<a-radio value="1">是</a-radio>
|
||||||
|
<a-radio value="0">否</a-radio>
|
||||||
|
</a-radio-group>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="用印数量" class="field-half">
|
||||||
|
<a-input-number
|
||||||
|
v-model:value="formState.sealQuantity"
|
||||||
|
:min="1"
|
||||||
|
:precision="0"
|
||||||
|
:disabled="formState.sealIsSeal !== '1'"
|
||||||
|
style="width: 100%"
|
||||||
|
placeholder="默认1份"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field-row">
|
||||||
|
<a-form-item label="用印时间" required class="field-half">
|
||||||
|
<a-date-picker
|
||||||
|
v-model:value="formState.sealDate"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
:disabled="formState.sealIsSeal !== '1'"
|
||||||
|
style="width: 100%"
|
||||||
|
placeholder="用印日期"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section-header" style="margin-top: 20px">
|
||||||
|
<span class="section-title-icon">✍️</span>
|
||||||
|
签订信息(c_sign)
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field-row">
|
||||||
|
<a-form-item label="签订日期" required class="field-half">
|
||||||
|
<a-date-picker
|
||||||
|
v-model:value="formState.signDate"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
style="width: 100%"
|
||||||
|
placeholder="请选择签订日期"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="合同生效日期" required class="field-half">
|
||||||
|
<a-date-picker
|
||||||
|
v-model:value="formState.activeDate"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
style="width: 100%"
|
||||||
|
placeholder="必填,且需在合同期限内"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field-row">
|
||||||
|
<a-form-item label="合同履行执行人" required class="field-half">
|
||||||
|
<a-input
|
||||||
|
v-model:value="formState.executorAccount"
|
||||||
|
placeholder="履行执行人账号"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section-header" style="margin-top: 20px">
|
||||||
|
<span class="section-title-icon">📎</span>
|
||||||
|
合同签署文本
|
||||||
|
</div>
|
||||||
|
<a-form-item label="签署文件" required>
|
||||||
|
<a-upload-dragger
|
||||||
|
:file-list="signedFileList"
|
||||||
|
:before-upload="handleSignedFileUpload"
|
||||||
|
:show-upload-list="{ showRemoveIcon: true }"
|
||||||
|
@remove="handleRemoveSignedFile"
|
||||||
|
>
|
||||||
|
<div class="upload-text">
|
||||||
|
点击或拖拽上传签署完成的合同文件
|
||||||
|
</div>
|
||||||
|
<div class="upload-hint">
|
||||||
|
支持 .pdf 格式,当前仅本地上传到业务系统文件服务
|
||||||
|
</div>
|
||||||
|
</a-upload-dragger>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</div>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { computed, reactive, ref } from "vue";
|
||||||
|
import { message } from "ant-design-vue";
|
||||||
|
import {
|
||||||
|
getLaunchDetail,
|
||||||
|
saveContractCollectInfo,
|
||||||
|
mockCollectLaunch,
|
||||||
|
uploadLaunchFile,
|
||||||
|
} from "@/api/launch.js";
|
||||||
|
|
||||||
|
const SUCCESS_CODE = "0000";
|
||||||
|
|
||||||
|
const visible = ref(false);
|
||||||
|
const submitting = ref(false);
|
||||||
|
const row = ref(null);
|
||||||
|
const signedFileList = ref([]);
|
||||||
|
|
||||||
|
const formState = reactive({
|
||||||
|
sealIsSeal: "1",
|
||||||
|
sealApproveSame: "1",
|
||||||
|
sealQuantity: 1,
|
||||||
|
sealDate: "",
|
||||||
|
signDate: "",
|
||||||
|
activeDate: "",
|
||||||
|
executorAccount: "",
|
||||||
|
signedFileName: "",
|
||||||
|
signedFilePath: "",
|
||||||
|
signedFileType: "",
|
||||||
|
signedFileSize: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(["done"]);
|
||||||
|
|
||||||
|
const sealTypeDisplay = computed(() => {
|
||||||
|
return row.value?.sealTypeLabel || row.value?.sealType || "公章";
|
||||||
|
});
|
||||||
|
|
||||||
|
function resetForm(record) {
|
||||||
|
formState.sealIsSeal = record.sealIsSeal || "1";
|
||||||
|
formState.sealApproveSame = record.sealApproveSame || "1";
|
||||||
|
formState.sealQuantity = record.sealQuantity || 1;
|
||||||
|
formState.sealDate = record.sealDate || "";
|
||||||
|
formState.signDate = record.signDate || "";
|
||||||
|
formState.activeDate = record.activeDate || "";
|
||||||
|
formState.executorAccount = record.executorAccount || record.initiator || "";
|
||||||
|
formState.signedFileName = record.signedFileName || "";
|
||||||
|
formState.signedFilePath = record.signedFilePath || "";
|
||||||
|
formState.signedFileType = record.signedFileType || "";
|
||||||
|
formState.signedFileSize = record.signedFileSize || null;
|
||||||
|
if (record.signedFileName) {
|
||||||
|
signedFileList.value = [
|
||||||
|
{
|
||||||
|
uid: "signed_existing",
|
||||||
|
name: record.signedFileName,
|
||||||
|
status: "done",
|
||||||
|
size: record.signedFileSize || 0,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
signedFileList.value = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function showModal(record) {
|
||||||
|
row.value = record || {};
|
||||||
|
resetForm(record || {});
|
||||||
|
visible.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildKey(prefix) {
|
||||||
|
return `${prefix}_${Date.now()}_${Math.random().toString(16).slice(2, 8)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleSignedFileUpload(file) {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append("file", file);
|
||||||
|
try {
|
||||||
|
const res = await uploadLaunchFile(formData);
|
||||||
|
if (String(res?.code || "") !== SUCCESS_CODE) {
|
||||||
|
message.error(res?.message || res?.msg || "签署文件上传失败");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const fileItem = {
|
||||||
|
uid: buildKey("signed"),
|
||||||
|
name: file.name,
|
||||||
|
status: "done",
|
||||||
|
size: file.size,
|
||||||
|
fileName: file.name,
|
||||||
|
filePath: res?.result || "",
|
||||||
|
fileType: file.name.split(".").pop()?.toLowerCase() || "",
|
||||||
|
fileSize: file.size,
|
||||||
|
};
|
||||||
|
signedFileList.value = [fileItem];
|
||||||
|
formState.signedFileName = fileItem.fileName;
|
||||||
|
formState.signedFilePath = fileItem.filePath;
|
||||||
|
formState.signedFileType = fileItem.fileType;
|
||||||
|
formState.signedFileSize = fileItem.fileSize;
|
||||||
|
message.success(`已上传签署文件:${file.name}`);
|
||||||
|
} catch (error) {
|
||||||
|
message.error("签署文件上传失败");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleRemoveSignedFile() {
|
||||||
|
signedFileList.value = [];
|
||||||
|
formState.signedFileName = "";
|
||||||
|
formState.signedFilePath = "";
|
||||||
|
formState.signedFileType = "";
|
||||||
|
formState.signedFileSize = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateForm() {
|
||||||
|
if (!formState.sealIsSeal) return "请选择是否用印";
|
||||||
|
if (!formState.sealApproveSame) return "请选择审批是否一致";
|
||||||
|
if (formState.sealIsSeal === "1") {
|
||||||
|
if (!formState.sealDate) return "请选择用印时间";
|
||||||
|
if (!formState.sealQuantity || formState.sealQuantity < 1)
|
||||||
|
return "请填写用印数量";
|
||||||
|
}
|
||||||
|
if (!formState.signDate) return "请选择签订日期";
|
||||||
|
if (!formState.activeDate) return "请选择合同生效日期";
|
||||||
|
if (!formState.executorAccount?.trim()) return "请填写合同履行执行人";
|
||||||
|
if (!signedFileList.value.length) return "请上传签署完成的合同文件";
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleSubmit() {
|
||||||
|
const validateMessage = validateForm();
|
||||||
|
if (validateMessage) {
|
||||||
|
message.warning(validateMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
submitting.value = true;
|
||||||
|
try {
|
||||||
|
// 1. 保存采集信息到合同记录
|
||||||
|
const saveRes = await saveContractCollectInfo(row.value.id, {
|
||||||
|
sealIsSeal: formState.sealIsSeal,
|
||||||
|
sealApproveSame: formState.sealApproveSame,
|
||||||
|
sealQuantity: formState.sealIsSeal === "1" ? formState.sealQuantity : null,
|
||||||
|
sealDate: formState.sealIsSeal === "1" ? formState.sealDate : null,
|
||||||
|
signDate: formState.signDate,
|
||||||
|
activeDate: formState.activeDate,
|
||||||
|
executorAccount: formState.executorAccount?.trim(),
|
||||||
|
signedFileName: formState.signedFileName,
|
||||||
|
signedFilePath: formState.signedFilePath,
|
||||||
|
signedFileType: formState.signedFileType,
|
||||||
|
signedFileSize: formState.signedFileSize,
|
||||||
|
});
|
||||||
|
if (String(saveRes?.code || "") !== SUCCESS_CODE) {
|
||||||
|
message.error(saveRes?.message || "保存采集信息失败");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 触发采集流转(push to 法务)
|
||||||
|
const collectRes = await mockCollectLaunch(row.value.id);
|
||||||
|
if (String(collectRes?.code || "") !== SUCCESS_CODE) {
|
||||||
|
message.error(collectRes?.message || "合同采集失败");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
message.success("合同采集完成,已流转到待归档");
|
||||||
|
visible.value = false;
|
||||||
|
emit("done");
|
||||||
|
} catch (error) {
|
||||||
|
message.error("合同采集失败");
|
||||||
|
} finally {
|
||||||
|
submitting.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ showModal });
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.collect-body {
|
||||||
|
max-height: 72vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding-right: 4px;
|
||||||
|
}
|
||||||
|
.contract-brief {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
.field-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
.field-half {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.section-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
margin: 14px 0 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 15px;
|
||||||
|
color: #1f2937;
|
||||||
|
}
|
||||||
|
.section-title-icon {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
.upload-text {
|
||||||
|
color: #374151;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.upload-hint {
|
||||||
|
color: #9ca3af;
|
||||||
|
font-size: 12px;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -691,39 +691,7 @@
|
|||||||
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-date-picker
|
|
||||||
v-model:value="detailState.activeDate"
|
|
||||||
value-format="YYYY-MM-DD"
|
|
||||||
style="width: 100%"
|
|
||||||
placeholder="必填,且必须在合同期限范围内"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item class="tight-item" label="用印数量">
|
|
||||||
<a-input-number
|
|
||||||
v-model:value="detailState.sealQuantity"
|
|
||||||
:min="1"
|
|
||||||
:precision="0"
|
|
||||||
style="width: 100%"
|
|
||||||
placeholder="默认1份"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="boolean-grid">
|
<div class="boolean-grid">
|
||||||
@@ -794,20 +762,7 @@
|
|||||||
<a-radio value="0">否</a-radio>
|
<a-radio value="0">否</a-radio>
|
||||||
</a-radio-group>
|
</a-radio-group>
|
||||||
</div>
|
</div>
|
||||||
<div class="boolean-row">
|
|
||||||
<span>是否用印</span>
|
|
||||||
<a-radio-group v-model:value="detailState.sealIsSeal">
|
|
||||||
<a-radio value="1">是</a-radio>
|
|
||||||
<a-radio value="0">否</a-radio>
|
|
||||||
</a-radio-group>
|
|
||||||
</div>
|
|
||||||
<div class="boolean-row">
|
|
||||||
<span>审批是否一致</span>
|
|
||||||
<a-radio-group v-model:value="detailState.sealApproveSame">
|
|
||||||
<a-radio value="1">是</a-radio>
|
|
||||||
<a-radio value="0">否</a-radio>
|
|
||||||
</a-radio-group>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="extra-amount-section">
|
<div class="extra-amount-section">
|
||||||
@@ -866,7 +821,7 @@
|
|||||||
:data-source="detailState.performancePlans"
|
:data-source="detailState.performancePlans"
|
||||||
:pagination="false"
|
:pagination="false"
|
||||||
size="small"
|
size="small"
|
||||||
:scroll="{ x: 820 }"
|
:scroll="{ x: 780 }"
|
||||||
row-key="id"
|
row-key="id"
|
||||||
class="plan-table"
|
class="plan-table"
|
||||||
>
|
>
|
||||||
@@ -926,27 +881,20 @@
|
|||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</a-table-column>
|
</a-table-column>
|
||||||
<a-table-column
|
|
||||||
title="所属主体编码"
|
|
||||||
key="planSubjectCode"
|
|
||||||
width="160"
|
|
||||||
>
|
|
||||||
<template #default="{ record }">
|
|
||||||
<a-input
|
|
||||||
v-model:value="record.planSubjectCode"
|
|
||||||
placeholder="所属主体机构编码"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</a-table-column>
|
|
||||||
<a-table-column
|
<a-table-column
|
||||||
title="所属主体"
|
title="所属主体"
|
||||||
key="planSubjectName"
|
key="planSubjectName"
|
||||||
width="180"
|
width="200"
|
||||||
>
|
>
|
||||||
<template #default="{ record }">
|
<template #default="{ record }">
|
||||||
<a-input
|
<a-select
|
||||||
v-model:value="record.planSubjectName"
|
v-model:value="record.planSubjectName"
|
||||||
placeholder="所属主体名称"
|
:options="ourParties.map(p => ({ label: `${p.companyName} (${p.subjectCode})`, value: p.companyName, subjectCode: p.subjectCode }))"
|
||||||
|
allow-clear
|
||||||
|
placeholder="从已选我方主体中选择"
|
||||||
|
:disabled="!ourParties.length"
|
||||||
|
style="width: 100%"
|
||||||
|
@change="(value, option) => { record.planSubjectCode = option?.subjectCode || ''; }"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</a-table-column>
|
</a-table-column>
|
||||||
@@ -1471,12 +1419,6 @@ function createContractFormData() {
|
|||||||
yfkAmount: 0,
|
yfkAmount: 0,
|
||||||
bzjAmount: 0,
|
bzjAmount: 0,
|
||||||
performancePlans: [],
|
performancePlans: [],
|
||||||
signDate: "",
|
|
||||||
sealDate: "",
|
|
||||||
activeDate: "",
|
|
||||||
sealQuantity: 1,
|
|
||||||
sealIsSeal: "1",
|
|
||||||
sealApproveSame: "1",
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1534,11 +1476,8 @@ function copyContractFormData(data = {}) {
|
|||||||
: null
|
: null
|
||||||
: null;
|
: null;
|
||||||
} else if (
|
} else if (
|
||||||
key === "signDate" ||
|
|
||||||
key === "sealDate" ||
|
|
||||||
key === "invoiceDate" ||
|
key === "invoiceDate" ||
|
||||||
key === "receiveDate" ||
|
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") {
|
||||||
@@ -2026,7 +1965,7 @@ function handleBodyConfigSave(payload = {}) {
|
|||||||
syncUploadLists();
|
syncUploadLists();
|
||||||
|
|
||||||
if (modalMode.value === "edit" && detailState.value.id) {
|
if (modalMode.value === "edit" && detailState.value.id) {
|
||||||
const errorMessage = validateState();
|
const errorMessage = validateDraft();
|
||||||
if (errorMessage) {
|
if (errorMessage) {
|
||||||
message.success(
|
message.success(
|
||||||
"合同正文配置已保存到当前页面,请继续完善主信息后再保存合同草稿。",
|
"合同正文配置已保存到当前页面,请继续完善主信息后再保存合同草稿。",
|
||||||
@@ -2064,7 +2003,85 @@ function removePerformancePlan(id) {
|
|||||||
detailState.value.performancePlans.filter((item) => item.id !== id);
|
detailState.value.performancePlans.filter((item) => item.id !== id);
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateState() {
|
/** 草稿保存校验(宽松):仅校验必填核心字段 */
|
||||||
|
function validateDraft() {
|
||||||
|
if (
|
||||||
|
!ourParties.value.length ||
|
||||||
|
ourParties.value.some((party) => !party.companyName?.trim())
|
||||||
|
)
|
||||||
|
return "请确认我方信息";
|
||||||
|
if (
|
||||||
|
currentSelfSubjectCode.value &&
|
||||||
|
!ourParties.value.some(
|
||||||
|
(party) =>
|
||||||
|
String(party.subjectCode || "").trim() === currentSelfSubjectCode.value,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return "当前登录主体必须包含在我方主体中";
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
isSingleAgreementChecked.value &&
|
||||||
|
currentSelfSubjectCode.value &&
|
||||||
|
ourParties.value.some(
|
||||||
|
(party) =>
|
||||||
|
String(party.subjectCode || "").trim() !== currentSelfSubjectCode.value,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return "单方协议仅允许使用当前登录主体";
|
||||||
|
}
|
||||||
|
if (!isSingleAgreementChecked.value && !counterpartyParties.value.length)
|
||||||
|
return "请至少添加一个相对方";
|
||||||
|
if (
|
||||||
|
!isSingleAgreementChecked.value &&
|
||||||
|
counterpartyParties.value.some((party) => !party.companyName?.trim())
|
||||||
|
)
|
||||||
|
return "请选择相对方";
|
||||||
|
if (!detailState.value.contractName?.trim()) return "请填写合同名称";
|
||||||
|
if (!detailState.value.contractTypeCode) return "请选择合同类型";
|
||||||
|
if (
|
||||||
|
detailState.value.contractPeriodType === "0" &&
|
||||||
|
(!detailState.value.effectiveStart || !detailState.value.effectiveEnd)
|
||||||
|
)
|
||||||
|
return "请选择固定期限起止日期";
|
||||||
|
if (
|
||||||
|
detailState.value.contractPeriodType === "1" &&
|
||||||
|
!detailState.value.periodExplain?.trim()
|
||||||
|
)
|
||||||
|
return "请填写期限说明";
|
||||||
|
if (!detailState.value.contractBodyFileName?.trim())
|
||||||
|
return "请先配置合同正文";
|
||||||
|
if (
|
||||||
|
!isSingleAgreementChecked.value &&
|
||||||
|
requiresAuthAttachment.value &&
|
||||||
|
!(detailState.value.authAttachmentList || []).length
|
||||||
|
)
|
||||||
|
return "请上传授权委托书附件";
|
||||||
|
// 履行计划:草稿保存时即要求至少一条且字段完整
|
||||||
|
if (!(detailState.value.performancePlans || []).length)
|
||||||
|
return "请至少添加一条履行计划";
|
||||||
|
for (const plan of detailState.value.performancePlans || []) {
|
||||||
|
if (!plan.planMatter?.trim()) return "请填写履行计划的履行事项";
|
||||||
|
if (!plan.planWhere?.trim()) return "请填写履行计划的履约条件";
|
||||||
|
if (!plan.planTime) return "请选择履行计划的节点日期";
|
||||||
|
if (
|
||||||
|
plan.planDay === null ||
|
||||||
|
plan.planDay === undefined ||
|
||||||
|
plan.planDay === ""
|
||||||
|
)
|
||||||
|
return "请填写履行计划的提醒提前天数";
|
||||||
|
if (
|
||||||
|
!["0", "3"].includes(detailState.value.valuationMode) &&
|
||||||
|
(plan.planMoney === null ||
|
||||||
|
plan.planMoney === undefined ||
|
||||||
|
plan.planMoney === "")
|
||||||
|
)
|
||||||
|
return "请填写履行计划的款项金额";
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 提交法务校验(严格):补全 pushContract 所有必填项 + 履行计划 */
|
||||||
|
function validateSubmit() {
|
||||||
if (
|
if (
|
||||||
!ourParties.value.length ||
|
!ourParties.value.length ||
|
||||||
ourParties.value.some((party) => !party.companyName?.trim())
|
ourParties.value.some((party) => !party.companyName?.trim())
|
||||||
@@ -2143,6 +2160,13 @@ function validateState() {
|
|||||||
detailState.value.needAmendments === ""
|
detailState.value.needAmendments === ""
|
||||||
)
|
)
|
||||||
return "请选择是否需修订条款";
|
return "请选择是否需修订条款";
|
||||||
|
// 以下为 pushContract 必填项补充校验(接口文档要求)
|
||||||
|
if (detailState.value.isMajorContract === undefined || detailState.value.isMajorContract === null || detailState.value.isMajorContract === "")
|
||||||
|
return "请选择是否重大合同";
|
||||||
|
if (detailState.value.isPublicTender === undefined || detailState.value.isPublicTender === null)
|
||||||
|
return "请选择是否公开招标";
|
||||||
|
if (detailState.value.isInvolveHongKongMacaoTaiwan === undefined || detailState.value.isInvolveHongKongMacaoTaiwan === null)
|
||||||
|
return "请选择是否涉及港澳台";
|
||||||
if (!detailState.value.paymentDirection) return "请选择收支方向";
|
if (!detailState.value.paymentDirection) return "请选择收支方向";
|
||||||
if (!detailState.value.contractTypeCode) return "请选择合同类型编码";
|
if (!detailState.value.contractTypeCode) return "请选择合同类型编码";
|
||||||
if (!detailState.value.executorAccount?.trim()) return "请填写合同执行人";
|
if (!detailState.value.executorAccount?.trim()) return "请填写合同执行人";
|
||||||
@@ -2180,7 +2204,7 @@ function validateState() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleSave() {
|
function handleSave() {
|
||||||
const errorMessage = validateState();
|
const errorMessage = validateDraft();
|
||||||
if (errorMessage) {
|
if (errorMessage) {
|
||||||
message.warning(errorMessage);
|
message.warning(errorMessage);
|
||||||
return;
|
return;
|
||||||
@@ -2359,14 +2383,8 @@ function buildLaunchRecord() {
|
|||||||
contractTextFilePath: detailState.value.contractBodyFilePath || "",
|
contractTextFilePath: detailState.value.contractBodyFilePath || "",
|
||||||
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),
|
|
||||||
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),
|
|
||||||
sealQuantity: detailState.value.sealQuantity,
|
|
||||||
sealIsSeal: detailState.value.sealIsSeal || "1",
|
|
||||||
sealApproveSame: detailState.value.sealApproveSame || "1",
|
|
||||||
templateFieldValuesJson: JSON.stringify(
|
templateFieldValuesJson: JSON.stringify(
|
||||||
detailState.value.templateFieldValues || {},
|
detailState.value.templateFieldValues || {},
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -247,7 +247,7 @@
|
|||||||
}}</a
|
}}</a
|
||||||
>
|
>
|
||||||
<a v-if="canCollect(row)" @click="handleMockCollect(row)">{{
|
<a v-if="canCollect(row)" @click="handleMockCollect(row)">{{
|
||||||
isSingleAgreementRecord(row) ? "测试生效" : "测试采集"
|
isSingleAgreementRecord(row) ? "测试生效" : "合同采集"
|
||||||
}}</a>
|
}}</a>
|
||||||
<a v-if="canArchive(row)" @click="handleArchive(row)">{{
|
<a v-if="canArchive(row)" @click="handleArchive(row)">{{
|
||||||
isSingleAgreementRecord(row) ? "办结" : "归档"
|
isSingleAgreementRecord(row) ? "办结" : "归档"
|
||||||
@@ -278,6 +278,10 @@
|
|||||||
ref="contractModalRef"
|
ref="contractModalRef"
|
||||||
@save="handleModalSave"
|
@save="handleModalSave"
|
||||||
/>
|
/>
|
||||||
|
<ContractCollectModal
|
||||||
|
ref="contractCollectModalRef"
|
||||||
|
@done="handleCollectDone"
|
||||||
|
/>
|
||||||
<MixedSignModal
|
<MixedSignModal
|
||||||
ref="mixedSignModalRef"
|
ref="mixedSignModalRef"
|
||||||
@business-updated="handleSignBusinessUpdated"
|
@business-updated="handleSignBusinessUpdated"
|
||||||
@@ -303,6 +307,7 @@ import {
|
|||||||
} from "@ant-design/icons-vue";
|
} from "@ant-design/icons-vue";
|
||||||
import { Input, message, Modal } from "ant-design-vue";
|
import { Input, message, Modal } from "ant-design-vue";
|
||||||
import ContractPartyPlaceholderModal from "./components/ContractPartyPlaceholderModal.vue";
|
import ContractPartyPlaceholderModal from "./components/ContractPartyPlaceholderModal.vue";
|
||||||
|
import ContractCollectModal from "./components/ContractCollectModal.vue";
|
||||||
import MixedSignModal from "./components/mixedSignModal.vue";
|
import MixedSignModal from "./components/mixedSignModal.vue";
|
||||||
import UploadSignedModal from "./components/UploadSignedModal.vue";
|
import UploadSignedModal from "./components/UploadSignedModal.vue";
|
||||||
import ContractArchiveModal from "./components/ContractArchiveModal.vue";
|
import ContractArchiveModal from "./components/ContractArchiveModal.vue";
|
||||||
@@ -318,7 +323,6 @@ import {
|
|||||||
getLaunchDetail,
|
getLaunchDetail,
|
||||||
getLawApproveCallbackSample,
|
getLawApproveCallbackSample,
|
||||||
mockApproveLaunch,
|
mockApproveLaunch,
|
||||||
mockCollectLaunch,
|
|
||||||
mockRejectLaunch,
|
mockRejectLaunch,
|
||||||
finishLaunchSeal,
|
finishLaunchSeal,
|
||||||
queryApproveViewUrl,
|
queryApproveViewUrl,
|
||||||
@@ -347,6 +351,7 @@ const pageWrapperRef = ref(null);
|
|||||||
const tableRef = ref(null);
|
const tableRef = ref(null);
|
||||||
const toolbarRef = ref(null);
|
const toolbarRef = ref(null);
|
||||||
const contractModalRef = ref(null);
|
const contractModalRef = ref(null);
|
||||||
|
const contractCollectModalRef = ref(null);
|
||||||
const mixedSignModalRef = ref(null);
|
const mixedSignModalRef = ref(null);
|
||||||
const uploadSignedModalRef = ref(null);
|
const uploadSignedModalRef = ref(null);
|
||||||
const contractArchiveModalRef = ref(null);
|
const contractArchiveModalRef = ref(null);
|
||||||
@@ -699,6 +704,41 @@ function handleChange(row) {
|
|||||||
|
|
||||||
function handleSubmit(row) {
|
function handleSubmit(row) {
|
||||||
const singleAgreement = isSingleAgreementRecord(row);
|
const singleAgreement = isSingleAgreementRecord(row);
|
||||||
|
// 非单方协议:提交法务前校验必填项完整性
|
||||||
|
if (!singleAgreement) {
|
||||||
|
return getLaunchDetail(row.id).then((res) => {
|
||||||
|
if (!isSuccessResponse(res) || !getDataResult(res)) {
|
||||||
|
message.error(getResponseMessage(res, "获取合同详情失败"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const detail = getDataResult(res);
|
||||||
|
const missingFields = [];
|
||||||
|
if (!detail.contractTypeCode) missingFields.push("合同类型");
|
||||||
|
if (!detail.valuationMode) missingFields.push("计价方式");
|
||||||
|
if (!detail.currencyName) missingFields.push("币种");
|
||||||
|
if (!detail.sealType) missingFields.push("签订方式");
|
||||||
|
if (!detail.isElectron) missingFields.push("签署方式");
|
||||||
|
if (!detail.paymentDirection) missingFields.push("收支方向");
|
||||||
|
if (!detail.textSource) missingFields.push("文本来源");
|
||||||
|
if (!detail.contractSource) missingFields.push("合同来源");
|
||||||
|
if (!detail.executorAccount) missingFields.push("合同执行人");
|
||||||
|
if (detail.isMajorContract === null || detail.isMajorContract === undefined) missingFields.push("是否重大合同");
|
||||||
|
if (detail.isOpenBidding === null || detail.isOpenBidding === undefined) missingFields.push("是否公开招标");
|
||||||
|
if (detail.isSgat === null || detail.isSgat === undefined) missingFields.push("是否涉及港澳台");
|
||||||
|
if (!detail.performancePlans || !detail.performancePlans.length) missingFields.push("履行计划");
|
||||||
|
if (!detail.contractTextFileName) missingFields.push("合同正文");
|
||||||
|
if (missingFields.length) {
|
||||||
|
message.warning(`以下必填项不完整,请先在编辑草稿中补全后再提交:\n${missingFields.join("、")}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
showSubmitConfirm(row, detail, false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 单方协议:直接提交内部审批
|
||||||
|
showSubmitConfirm(row, null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function showSubmitConfirm(row, detail, singleAgreement) {
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
title: singleAgreement ? "确认提交内部审批" : "确认提交法务审批",
|
title: singleAgreement ? "确认提交内部审批" : "确认提交法务审批",
|
||||||
content: h("div", [
|
content: h("div", [
|
||||||
@@ -1032,24 +1072,19 @@ function handleModalSave(payload) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleMockCollect(row) {
|
function handleMockCollect(row) {
|
||||||
Modal.confirm({
|
getLaunchDetail(row.id).then((res) => {
|
||||||
title: "测试操作:模拟合同采集",
|
if (!isSuccessResponse(res) || !getDataResult(res)) {
|
||||||
content: `模拟将合同【${row.contractName}】信息采集同步至法务系统,状态将变为「待归档」。`,
|
message.error(getResponseMessage(res, "获取合同详情失败"));
|
||||||
okText: "确认模拟",
|
|
||||||
cancelText: "取消",
|
|
||||||
onOk: () =>
|
|
||||||
mockCollectLaunch(row.id).then((res) => {
|
|
||||||
if (!isSuccessResponse(res)) {
|
|
||||||
message.error(getResponseMessage(res, "模拟合同采集失败"));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
message.success("测试合同采集完成,合同已流转到待归档");
|
contractCollectModalRef.value?.showModal(getDataResult(res));
|
||||||
getList();
|
|
||||||
}),
|
|
||||||
icon: null,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleCollectDone() {
|
||||||
|
getList();
|
||||||
|
}
|
||||||
|
|
||||||
function handleArchive(row) {
|
function handleArchive(row) {
|
||||||
getLaunchDetail(row.id).then((res) => {
|
getLaunchDetail(row.id).then((res) => {
|
||||||
if (!isSuccessResponse(res) || !getDataResult(res)) {
|
if (!isSuccessResponse(res) || !getDataResult(res)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user