上传已签署版本
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)">
|
<a v-if="canOnlineSign(row)" @click="handleOnlineSign(row)">
|
||||||
{{ row.status === "completed" ? "查看签署" : "在线签订" }}
|
{{ row.status === "completed" ? "查看签署" : "在线签订" }}
|
||||||
</a>
|
</a>
|
||||||
|
<a v-if="canUploadSigned(row)" @click="handleUploadSigned(row)">上传已签署版本</a>
|
||||||
<a v-if="canMockFinishSign(row)" @click="handleMockFinishSign(row)">测试签署完成</a>
|
<a v-if="canMockFinishSign(row)" @click="handleMockFinishSign(row)">测试签署完成</a>
|
||||||
<a v-if="canCollect(row)" @click="handleMockCollect(row)">测试采集</a>
|
<a v-if="canCollect(row)" @click="handleMockCollect(row)">测试采集</a>
|
||||||
<a v-if="canArchive(row)" @click="handleArchive(row)">测试归档</a>
|
<a v-if="canArchive(row)" @click="handleArchive(row)">测试归档</a>
|
||||||
@@ -142,6 +143,7 @@
|
|||||||
|
|
||||||
<ContractPartyPlaceholderModal ref="contractModalRef" @save="handleModalSave" />
|
<ContractPartyPlaceholderModal ref="contractModalRef" @save="handleModalSave" />
|
||||||
<MixedSignModal ref="mixedSignModalRef" @business-updated="handleSignBusinessUpdated" />
|
<MixedSignModal ref="mixedSignModalRef" @business-updated="handleSignBusinessUpdated" />
|
||||||
|
<UploadSignedModal ref="uploadSignedModalRef" @done="handleSignBusinessUpdated" />
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -156,6 +158,7 @@ import {
|
|||||||
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 MixedSignModal from "./components/mixedSignModal.vue";
|
import MixedSignModal from "./components/mixedSignModal.vue";
|
||||||
|
import UploadSignedModal from "./components/UploadSignedModal.vue";
|
||||||
import { getContractTypeSimpleLabel } from "./contractLaunchOptions.js";
|
import { getContractTypeSimpleLabel } from "./contractLaunchOptions.js";
|
||||||
import {
|
import {
|
||||||
archiveLaunch,
|
archiveLaunch,
|
||||||
@@ -194,6 +197,7 @@ const tableRef = ref(null);
|
|||||||
const toolbarRef = ref(null);
|
const toolbarRef = ref(null);
|
||||||
const contractModalRef = ref(null);
|
const contractModalRef = ref(null);
|
||||||
const mixedSignModalRef = ref(null);
|
const mixedSignModalRef = ref(null);
|
||||||
|
const uploadSignedModalRef = ref(null);
|
||||||
|
|
||||||
const total = ref(0);
|
const total = ref(0);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
@@ -307,6 +311,10 @@ function canMockFinishSign(row) {
|
|||||||
return ["pending_sign", "signing"].includes(row.status);
|
return ["pending_sign", "signing"].includes(row.status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function canUploadSigned(row) {
|
||||||
|
return ["pending_sign", "signing"].includes(row.status);
|
||||||
|
}
|
||||||
|
|
||||||
function canCollect(row) {
|
function canCollect(row) {
|
||||||
return row.status === "pending_collect";
|
return row.status === "pending_collect";
|
||||||
}
|
}
|
||||||
@@ -514,6 +522,10 @@ function handleMockFinishSign(row) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleUploadSigned(row) {
|
||||||
|
uploadSignedModalRef.value?.showModal(row);
|
||||||
|
}
|
||||||
|
|
||||||
function handleOnlineSign(row) {
|
function handleOnlineSign(row) {
|
||||||
getLaunchDetail(row.id).then((res) => {
|
getLaunchDetail(row.id).then((res) => {
|
||||||
if (!isSuccessResponse(res) || !getDataResult(res)) {
|
if (!isSuccessResponse(res) || !getDataResult(res)) {
|
||||||
|
|||||||
@@ -45,11 +45,7 @@
|
|||||||
<strong>{{ item.value }}</strong>
|
<strong>{{ item.value }}</strong>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p class="upload-mode-hint" style="margin-top: 12px">
|
|
||||||
当前仅保留
|
|
||||||
<strong>预盖章模式</strong
|
|
||||||
>,先上传合同,再拖拽印章到签署位置即可。
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user