上传已签署版本
This commit is contained in:
@@ -0,0 +1,248 @@
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:open="visible"
|
||||
title="上传已签署版本"
|
||||
width="560px"
|
||||
:mask-closable="false"
|
||||
:confirm-loading="submitting"
|
||||
ok-text="确认提交"
|
||||
cancel-text="取消"
|
||||
@ok="handleSubmit"
|
||||
>
|
||||
<div class="upload-signed-body">
|
||||
<a-descriptions :column="1" 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">
|
||||
<a-form-item label="上传已签署合同文件" required>
|
||||
<div
|
||||
class="upload-dragger"
|
||||
:class="{ 'is-dragover': dragOver }"
|
||||
@click="triggerFileInput"
|
||||
@dragover.prevent="dragOver = true"
|
||||
@dragleave.prevent="dragOver = false"
|
||||
@drop.prevent="handleFileDrop"
|
||||
>
|
||||
<input
|
||||
ref="fileInputRef"
|
||||
type="file"
|
||||
accept=".pdf,.doc,.docx"
|
||||
style="display: none"
|
||||
@change="handleFileSelect"
|
||||
/>
|
||||
<template v-if="!uploadFile">
|
||||
<cloud-upload-outlined :style="{ 'fontSize': '36px', 'color': '#b0b8c5' }" />
|
||||
<p class="upload-text">点击或将已签署文件拖拽到这里</p>
|
||||
<p class="upload-hint">支持 PDF、DOC、DOCX 文件,且小于 20M</p>
|
||||
</template>
|
||||
<template v-else>
|
||||
<file-text-outlined :style="{ 'fontSize': '36px', 'color': '#10b981' }" />
|
||||
<p class="file-name">{{ uploadFile.name }}</p>
|
||||
<p class="file-size">{{ formatFileSize(uploadFile.size) }}</p>
|
||||
<a-button size="small" danger @click.stop="removeFile">移除</a-button>
|
||||
</template>
|
||||
</div>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="不在线签署的原因" required>
|
||||
<a-textarea
|
||||
v-model:value="offlineReason"
|
||||
:rows="4"
|
||||
placeholder="请填写不在线签署的原因,例如:客户无法配合在线签署、对方无数字证书等"
|
||||
:maxlength="500"
|
||||
show-count
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import { message } from "ant-design-vue";
|
||||
import { CloudUploadOutlined, FileTextOutlined } from "@ant-design/icons-vue";
|
||||
import { finishLaunchSeal, uploadLaunchFile } from "@/api/launch.js";
|
||||
|
||||
const SUCCESS_CODE = "0000";
|
||||
|
||||
const visible = ref(false);
|
||||
const row = ref(null);
|
||||
const uploadFile = ref(null);
|
||||
const offlineReason = ref("");
|
||||
const dragOver = ref(false);
|
||||
const submitting = ref(false);
|
||||
const fileInputRef = ref(null);
|
||||
|
||||
const emit = defineEmits(["done"]);
|
||||
|
||||
function showModal(record) {
|
||||
row.value = record;
|
||||
uploadFile.value = null;
|
||||
offlineReason.value = "";
|
||||
dragOver.value = false;
|
||||
submitting.value = false;
|
||||
visible.value = true;
|
||||
}
|
||||
|
||||
function triggerFileInput() {
|
||||
fileInputRef.value?.click();
|
||||
}
|
||||
|
||||
function handleFileSelect(event) {
|
||||
const file = event.target?.files?.[0];
|
||||
if (file) {
|
||||
validateAndSetFile(file);
|
||||
}
|
||||
if (fileInputRef.value) {
|
||||
fileInputRef.value.value = "";
|
||||
}
|
||||
}
|
||||
|
||||
function handleFileDrop(event) {
|
||||
dragOver.value = false;
|
||||
const file = event.dataTransfer?.files?.[0];
|
||||
if (file) {
|
||||
validateAndSetFile(file);
|
||||
}
|
||||
}
|
||||
|
||||
function validateAndSetFile(file) {
|
||||
const allowedExts = [".pdf", ".doc", ".docx"];
|
||||
const ext = "." + (file.name.split(".").pop()?.toLowerCase() || "");
|
||||
if (!allowedExts.includes(ext)) {
|
||||
message.error("只支持 PDF、DOC、DOCX 格式文件");
|
||||
return;
|
||||
}
|
||||
const maxSize = 20 * 1024 * 1024;
|
||||
if (file.size > maxSize) {
|
||||
message.error("文件大小不能超过 20M");
|
||||
return;
|
||||
}
|
||||
uploadFile.value = file;
|
||||
}
|
||||
|
||||
function removeFile() {
|
||||
uploadFile.value = null;
|
||||
}
|
||||
|
||||
function formatFileSize(bytes) {
|
||||
if (!bytes) return "-";
|
||||
if (bytes < 1024) return bytes + " B";
|
||||
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + " KB";
|
||||
return (bytes / (1024 * 1024)).toFixed(1) + " MB";
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
if (!uploadFile.value) {
|
||||
message.warning("请先上传已签署文件");
|
||||
return;
|
||||
}
|
||||
const reason = (offlineReason.value || "").trim();
|
||||
if (!reason) {
|
||||
message.warning("请填写不在线签署的原因");
|
||||
return;
|
||||
}
|
||||
|
||||
submitting.value = true;
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append("file", uploadFile.value);
|
||||
const uploadRes = await uploadLaunchFile(formData);
|
||||
if (String(uploadRes?.code || "") !== SUCCESS_CODE) {
|
||||
message.error(uploadRes?.message || uploadRes?.msg || "文件上传失败");
|
||||
submitting.value = false;
|
||||
return;
|
||||
}
|
||||
const filePath = uploadRes?.result || "";
|
||||
const fileName = uploadFile.value.name;
|
||||
const fileType = fileName.split(".").pop()?.toLowerCase() || "pdf";
|
||||
const fileSize = uploadFile.value.size;
|
||||
|
||||
const finishRes = await finishLaunchSeal(row.value.id, {
|
||||
sealContractId: row.value.sealContractId || `OFFLINE-SEAL-${row.value.id}`,
|
||||
sealContractStatus: 2000,
|
||||
signedFileName: fileName,
|
||||
signedFilePath: filePath,
|
||||
signedFileType: fileType,
|
||||
signedFileSize: fileSize,
|
||||
signedOfflineReason: reason,
|
||||
});
|
||||
|
||||
if (String(finishRes?.code || "") !== SUCCESS_CODE) {
|
||||
message.error(finishRes?.message || finishRes?.msg || "签署完结回写失败");
|
||||
submitting.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
message.success("已签署版本上传成功,合同已流转到待采集");
|
||||
visible.value = false;
|
||||
emit("done");
|
||||
} catch (err) {
|
||||
message.error("操作异常:" + (err?.message || "未知错误"));
|
||||
} finally {
|
||||
submitting.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({ showModal });
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.upload-signed-body {
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.contract-brief {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.upload-dragger {
|
||||
border: 2px dashed #dcdfe6;
|
||||
border-radius: 10px;
|
||||
padding: 24px 16px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.25s;
|
||||
background: #fafbfc;
|
||||
min-height: 120px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.upload-dragger:hover,
|
||||
.is-dragover {
|
||||
border-color: #3b6fa0;
|
||||
background: #eaf2f9;
|
||||
}
|
||||
|
||||
.upload-text,
|
||||
.file-name {
|
||||
font-size: 14px;
|
||||
color: #2c3e50;
|
||||
margin: 4px 0 2px;
|
||||
}
|
||||
|
||||
.file-name {
|
||||
font-weight: 500;
|
||||
color: #10b981;
|
||||
}
|
||||
|
||||
.upload-hint,
|
||||
.file-size {
|
||||
font-size: 12px;
|
||||
color: #8b95a5;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -115,6 +115,7 @@
|
||||
<a v-if="canOnlineSign(row)" @click="handleOnlineSign(row)">
|
||||
{{ row.status === "completed" ? "查看签署" : "在线签订" }}
|
||||
</a>
|
||||
<a v-if="canUploadSigned(row)" @click="handleUploadSigned(row)">上传已签署版本</a>
|
||||
<a v-if="canMockFinishSign(row)" @click="handleMockFinishSign(row)">测试签署完成</a>
|
||||
<a v-if="canCollect(row)" @click="handleMockCollect(row)">测试采集</a>
|
||||
<a v-if="canArchive(row)" @click="handleArchive(row)">测试归档</a>
|
||||
@@ -142,6 +143,7 @@
|
||||
|
||||
<ContractPartyPlaceholderModal ref="contractModalRef" @save="handleModalSave" />
|
||||
<MixedSignModal ref="mixedSignModalRef" @business-updated="handleSignBusinessUpdated" />
|
||||
<UploadSignedModal ref="uploadSignedModalRef" @done="handleSignBusinessUpdated" />
|
||||
</section>
|
||||
</template>
|
||||
|
||||
@@ -156,6 +158,7 @@ import {
|
||||
import { Input, message, Modal } from "ant-design-vue";
|
||||
import ContractPartyPlaceholderModal from "./components/ContractPartyPlaceholderModal.vue";
|
||||
import MixedSignModal from "./components/mixedSignModal.vue";
|
||||
import UploadSignedModal from "./components/UploadSignedModal.vue";
|
||||
import { getContractTypeSimpleLabel } from "./contractLaunchOptions.js";
|
||||
import {
|
||||
archiveLaunch,
|
||||
@@ -194,6 +197,7 @@ const tableRef = ref(null);
|
||||
const toolbarRef = ref(null);
|
||||
const contractModalRef = ref(null);
|
||||
const mixedSignModalRef = ref(null);
|
||||
const uploadSignedModalRef = ref(null);
|
||||
|
||||
const total = ref(0);
|
||||
const loading = ref(false);
|
||||
@@ -307,6 +311,10 @@ function canMockFinishSign(row) {
|
||||
return ["pending_sign", "signing"].includes(row.status);
|
||||
}
|
||||
|
||||
function canUploadSigned(row) {
|
||||
return ["pending_sign", "signing"].includes(row.status);
|
||||
}
|
||||
|
||||
function canCollect(row) {
|
||||
return row.status === "pending_collect";
|
||||
}
|
||||
@@ -514,6 +522,10 @@ function handleMockFinishSign(row) {
|
||||
});
|
||||
}
|
||||
|
||||
function handleUploadSigned(row) {
|
||||
uploadSignedModalRef.value?.showModal(row);
|
||||
}
|
||||
|
||||
function handleOnlineSign(row) {
|
||||
getLaunchDetail(row.id).then((res) => {
|
||||
if (!isSuccessResponse(res) || !getDataResult(res)) {
|
||||
|
||||
@@ -45,11 +45,7 @@
|
||||
<strong>{{ item.value }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
<p class="upload-mode-hint" style="margin-top: 12px">
|
||||
当前仅保留
|
||||
<strong>预盖章模式</strong
|
||||
>,先上传合同,再拖拽印章到签署位置即可。
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user