Files
stamp-ant/src/views/contract/launch/components/ContractCollectModal.vue
T
2026-07-14 12:58:25 +08:00

350 lines
10 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<a-modal
v-model:open="visible"
title="合同采集"
width="800px"
:mask-closable="false"
:confirm-loading="submitting"
ok-text="确认采集"
cancel-text="取消"
@ok="handleSubmit"
centered
>
<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>