归档功能完善
This commit is contained in:
@@ -0,0 +1,447 @@
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:open="visible"
|
||||
title="合同归档"
|
||||
width="920px"
|
||||
:mask-closable="false"
|
||||
:confirm-loading="submitting"
|
||||
ok-text="确认归档"
|
||||
cancel-text="取消"
|
||||
@ok="handleSubmit"
|
||||
>
|
||||
<div class="archive-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-item label="法务合同编号">
|
||||
{{ row?.lawContractCode || "-" }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="当前状态">
|
||||
{{ row?.status || "-" }}
|
||||
</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
|
||||
<a-divider style="margin: 16px 0" />
|
||||
|
||||
<a-form layout="vertical">
|
||||
<div class="archive-grid">
|
||||
<a-form-item label="归档接收部门编码" required>
|
||||
<a-input
|
||||
v-model:value="formState.archiveReceiveDept"
|
||||
placeholder="默认带出当前组织编码,可按实际调整"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="移交人账号" required>
|
||||
<a-input
|
||||
v-model:value="formState.archiveTransferor"
|
||||
placeholder="请输入移交人账号"
|
||||
/>
|
||||
</a-form-item>
|
||||
</div>
|
||||
|
||||
<div class="section-header">
|
||||
<span>纸质档案材料</span>
|
||||
<a-button type="link" @click="addPaper">新增纸质档案</a-button>
|
||||
</div>
|
||||
<div
|
||||
v-for="(paper, index) in formState.archivePapers"
|
||||
:key="paper._key"
|
||||
class="archive-card"
|
||||
>
|
||||
<div class="archive-card-header">
|
||||
<span>纸质档案 {{ index + 1 }}</span>
|
||||
<a-button
|
||||
v-if="formState.archivePapers.length > 1"
|
||||
type="link"
|
||||
danger
|
||||
@click="removePaper(paper._key)"
|
||||
>
|
||||
删除
|
||||
</a-button>
|
||||
</div>
|
||||
<div class="archive-grid archive-grid-paper">
|
||||
<a-form-item label="档案名称" required>
|
||||
<a-input v-model:value="paper.archivesName" placeholder="如:合同文本" />
|
||||
</a-form-item>
|
||||
<a-form-item label="份数" required>
|
||||
<a-input v-model:value="paper.archivesNum" placeholder="如:1" />
|
||||
</a-form-item>
|
||||
<a-form-item label="归档时间" required class="field-span-2">
|
||||
<a-date-picker
|
||||
v-model:value="paper.archivesTime"
|
||||
show-time
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
style="width: 100%"
|
||||
placeholder="请选择归档时间"
|
||||
/>
|
||||
</a-form-item>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section-header">
|
||||
<span>电子档案材料</span>
|
||||
<a-button type="link" @click="addEle">新增电子档案</a-button>
|
||||
</div>
|
||||
<div
|
||||
v-for="(ele, index) in formState.archiveEles"
|
||||
:key="ele._key"
|
||||
class="archive-card"
|
||||
>
|
||||
<div class="archive-card-header">
|
||||
<span>电子档案 {{ index + 1 }}</span>
|
||||
<a-button
|
||||
v-if="formState.archiveEles.length > 1"
|
||||
type="link"
|
||||
danger
|
||||
@click="removeEle(ele._key)"
|
||||
>
|
||||
删除
|
||||
</a-button>
|
||||
</div>
|
||||
|
||||
<a-form-item label="档案名称" required>
|
||||
<a-input v-model:value="ele.archivesName" placeholder="如:合同扫描件" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="归档附件" required>
|
||||
<a-upload-dragger
|
||||
:file-list="ele.approvalAttachs"
|
||||
:before-upload="(file) => handleEleAttachUpload(file, ele)"
|
||||
:show-upload-list="{ showRemoveIcon: true }"
|
||||
@remove="(file) => handleRemoveEleAttach(ele, file)"
|
||||
multiple
|
||||
>
|
||||
<div class="upload-text">点击或拖拽上传电子档案附件</div>
|
||||
<div class="upload-hint">支持多个附件,当前仅本地上传到业务系统文件服务</div>
|
||||
</a-upload-dragger>
|
||||
</a-form-item>
|
||||
</div>
|
||||
</a-form>
|
||||
</div>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref } from "vue";
|
||||
import dayjs from "dayjs";
|
||||
import { message } from "ant-design-vue";
|
||||
import { archiveLaunch, getLaunchSelfProfile, uploadLaunchFile } from "@/api/launch.js";
|
||||
|
||||
const SUCCESS_CODE = "0000";
|
||||
|
||||
const visible = ref(false);
|
||||
const submitting = ref(false);
|
||||
const row = ref(null);
|
||||
const selfProfile = ref(null);
|
||||
|
||||
const formState = reactive({
|
||||
archiveReceiveDept: "",
|
||||
archiveTransferor: "",
|
||||
archivePapers: [],
|
||||
archiveEles: [],
|
||||
});
|
||||
|
||||
const emit = defineEmits(["done"]);
|
||||
|
||||
function buildKey(prefix) {
|
||||
return `${prefix}_${Date.now()}_${Math.random().toString(16).slice(2, 8)}`;
|
||||
}
|
||||
|
||||
function normalizeArchiveFile(item = {}) {
|
||||
const fileName = item.name || item.fileName || "";
|
||||
const fileType = item.fileType || resolveFileType(fileName);
|
||||
return {
|
||||
uid: item.uid || buildKey("archive_file"),
|
||||
name: fileName,
|
||||
status: item.status || "done",
|
||||
size: item.size ?? item.fileSize ?? 0,
|
||||
fileName,
|
||||
filePath: item.filePath || "",
|
||||
fileType,
|
||||
fileSize: item.size ?? item.fileSize ?? 0,
|
||||
};
|
||||
}
|
||||
|
||||
function createPaperItem(source = {}) {
|
||||
return {
|
||||
id: source.id || null,
|
||||
_key: source._key || buildKey("paper"),
|
||||
archivesName: source.archivesName || "",
|
||||
archivesNum: source.archivesNum || "",
|
||||
archivesTime: source.archivesTime || dayjs().format("YYYY-MM-DD HH:mm:ss"),
|
||||
};
|
||||
}
|
||||
|
||||
function createEleItem(source = {}) {
|
||||
return {
|
||||
id: source.id || null,
|
||||
_key: source._key || buildKey("ele"),
|
||||
archivesName: source.archivesName || "",
|
||||
approvalAttachs: (source.approvalAttachs || []).map((item) =>
|
||||
normalizeArchiveFile(item),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
function resolveFileType(fileName = "") {
|
||||
const suffix = fileName.split(".").pop()?.toLowerCase() || "";
|
||||
return suffix;
|
||||
}
|
||||
|
||||
function buildSignedArchiveFile(record = {}) {
|
||||
const signedFromList = (record.fileList || []).find(
|
||||
(item) => item.fileCategory === "signed" && item.filePath,
|
||||
);
|
||||
const fileName =
|
||||
record.signedFileName ||
|
||||
signedFromList?.fileName ||
|
||||
"";
|
||||
const filePath =
|
||||
record.signedFilePath ||
|
||||
signedFromList?.filePath ||
|
||||
"";
|
||||
if (!fileName || !filePath) {
|
||||
return null;
|
||||
}
|
||||
return normalizeArchiveFile({
|
||||
uid: buildKey("archive_file"),
|
||||
name: fileName,
|
||||
status: "done",
|
||||
size: record.signedFileSize ?? signedFromList?.fileSize ?? 0,
|
||||
fileName,
|
||||
filePath,
|
||||
fileType: record.signedFileType || signedFromList?.fileType || resolveFileType(fileName),
|
||||
fileSize: record.signedFileSize ?? signedFromList?.fileSize ?? 0,
|
||||
});
|
||||
}
|
||||
|
||||
function buildDefaultEles(record = {}) {
|
||||
if (record.archiveEles?.length) {
|
||||
return record.archiveEles.map((item) => createEleItem(item));
|
||||
}
|
||||
const signedFile = buildSignedArchiveFile(record);
|
||||
if (!signedFile) {
|
||||
return [createEleItem()];
|
||||
}
|
||||
return [
|
||||
createEleItem({
|
||||
archivesName: "已签署合同文件",
|
||||
approvalAttachs: [signedFile],
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
function resetForm(record = {}, profile = {}) {
|
||||
formState.archiveReceiveDept =
|
||||
record.archiveReceiveDept || record.orgId || profile.id || "";
|
||||
formState.archiveTransferor =
|
||||
record.archiveTransferor || record.initiator || record.createBy || "";
|
||||
formState.archivePapers =
|
||||
record.archivePapers?.length
|
||||
? record.archivePapers.map((item) => createPaperItem(item))
|
||||
: [createPaperItem()];
|
||||
formState.archiveEles = buildDefaultEles(record);
|
||||
}
|
||||
|
||||
async function showModal(record) {
|
||||
row.value = record || {};
|
||||
selfProfile.value = await getLaunchSelfProfile();
|
||||
resetForm(record || {}, selfProfile.value || {});
|
||||
visible.value = true;
|
||||
}
|
||||
|
||||
function addPaper() {
|
||||
formState.archivePapers.push(createPaperItem());
|
||||
}
|
||||
|
||||
function removePaper(key) {
|
||||
formState.archivePapers = formState.archivePapers.filter((item) => item._key !== key);
|
||||
}
|
||||
|
||||
function addEle() {
|
||||
formState.archiveEles.push(createEleItem());
|
||||
}
|
||||
|
||||
function removeEle(key) {
|
||||
formState.archiveEles = formState.archiveEles.filter((item) => item._key !== key);
|
||||
}
|
||||
|
||||
async function handleEleAttachUpload(file, ele) {
|
||||
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;
|
||||
}
|
||||
ele.approvalAttachs.push(
|
||||
normalizeArchiveFile({
|
||||
uid: buildKey("archive_file"),
|
||||
name: file.name,
|
||||
status: "done",
|
||||
size: file.size,
|
||||
fileName: file.name,
|
||||
filePath: res?.result || "",
|
||||
fileType: resolveFileType(file.name),
|
||||
fileSize: file.size,
|
||||
}),
|
||||
);
|
||||
message.success(`已上传附件:${file.name}`);
|
||||
} catch (error) {
|
||||
message.error("归档附件上传失败");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function handleRemoveEleAttach(ele, file) {
|
||||
ele.approvalAttachs = ele.approvalAttachs.filter(
|
||||
(item) => item.uid !== file.uid && item.filePath !== file.filePath,
|
||||
);
|
||||
}
|
||||
|
||||
function validateForm() {
|
||||
if (!String(formState.archiveReceiveDept || "").trim()) {
|
||||
return "请填写归档接收部门编码";
|
||||
}
|
||||
if (!String(formState.archiveTransferor || "").trim()) {
|
||||
return "请填写移交人账号";
|
||||
}
|
||||
if (!formState.archivePapers.length) {
|
||||
return "请至少填写一条纸质档案材料";
|
||||
}
|
||||
for (const paper of formState.archivePapers) {
|
||||
if (!String(paper.archivesName || "").trim()) {
|
||||
return "请填写纸质档案名称";
|
||||
}
|
||||
if (!String(paper.archivesNum || "").trim()) {
|
||||
return "请填写纸质档案份数";
|
||||
}
|
||||
if (!paper.archivesTime) {
|
||||
return "请填写纸质档案归档时间";
|
||||
}
|
||||
}
|
||||
if (!formState.archiveEles.length) {
|
||||
return "请至少填写一条电子档案材料";
|
||||
}
|
||||
for (const ele of formState.archiveEles) {
|
||||
if (!String(ele.archivesName || "").trim()) {
|
||||
return "请填写电子档案名称";
|
||||
}
|
||||
if (!(ele.approvalAttachs || []).length) {
|
||||
return "请上传电子档案附件";
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function buildPayload() {
|
||||
return {
|
||||
archiveReceiveDept: String(formState.archiveReceiveDept || "").trim(),
|
||||
archiveTransferor: String(formState.archiveTransferor || "").trim(),
|
||||
archivePapers: formState.archivePapers.map((paper) => ({
|
||||
id: paper.id || null,
|
||||
archivesName: String(paper.archivesName || "").trim(),
|
||||
archivesNum: String(paper.archivesNum || "").trim(),
|
||||
archivesTime: paper.archivesTime,
|
||||
})),
|
||||
archiveEles: formState.archiveEles.map((ele) => ({
|
||||
id: ele.id || null,
|
||||
archivesName: String(ele.archivesName || "").trim(),
|
||||
approvalAttachs: ele.approvalAttachs.map((file) => ({
|
||||
fileName: file.fileName || file.name,
|
||||
filePath: file.filePath,
|
||||
fileType: file.fileType,
|
||||
fileSize: file.fileSize ?? file.size ?? 0,
|
||||
})),
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
const validateMessage = validateForm();
|
||||
if (validateMessage) {
|
||||
message.warning(validateMessage);
|
||||
return;
|
||||
}
|
||||
submitting.value = true;
|
||||
try {
|
||||
const res = await archiveLaunch(row.value.id, buildPayload());
|
||||
if (String(res?.code || "") !== SUCCESS_CODE) {
|
||||
message.error(res?.message || res?.msg || "归档失败");
|
||||
return;
|
||||
}
|
||||
message.success("归档完成,当前为本地模拟流转,法务接口暂未实际调用");
|
||||
visible.value = false;
|
||||
emit("done");
|
||||
} catch (error) {
|
||||
message.error("归档失败");
|
||||
} finally {
|
||||
submitting.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({ showModal });
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.archive-body {
|
||||
max-height: 72vh;
|
||||
overflow-y: auto;
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.contract-brief {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.archive-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.archive-grid-paper .field-span-2 {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin: 14px 0 8px;
|
||||
font-weight: 600;
|
||||
color: #1f2937;
|
||||
}
|
||||
|
||||
.archive-card {
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 10px;
|
||||
padding: 12px 14px 6px;
|
||||
background: #fafcff;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.archive-card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.upload-text {
|
||||
color: #374151;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.upload-hint {
|
||||
color: #9ca3af;
|
||||
font-size: 12px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
</style>
|
||||
@@ -116,7 +116,7 @@
|
||||
<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>
|
||||
<a v-if="canArchive(row)" @click="handleArchive(row)">归档</a>
|
||||
<a v-if="canTerminate(row)" @click="handleTerminate(row)">终止</a>
|
||||
<a v-if="canDelete(row)" @click="handleDelete(row)">删除</a>
|
||||
</div>
|
||||
@@ -142,6 +142,7 @@
|
||||
<ContractPartyPlaceholderModal ref="contractModalRef" @save="handleModalSave" />
|
||||
<MixedSignModal ref="mixedSignModalRef" @business-updated="handleSignBusinessUpdated" />
|
||||
<UploadSignedModal ref="uploadSignedModalRef" @done="handleSignBusinessUpdated" />
|
||||
<ContractArchiveModal ref="contractArchiveModalRef" @done="handleArchiveDone" />
|
||||
</section>
|
||||
</template>
|
||||
|
||||
@@ -157,9 +158,9 @@ 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 ContractArchiveModal from "./components/ContractArchiveModal.vue";
|
||||
import { getContractTypeSimpleLabel } from "./contractLaunchOptions.js";
|
||||
import {
|
||||
archiveLaunch,
|
||||
copyLaunchDraft,
|
||||
createLaunchChangeDraft,
|
||||
createLaunchDraft,
|
||||
@@ -197,6 +198,7 @@ const toolbarRef = ref(null);
|
||||
const contractModalRef = ref(null);
|
||||
const mixedSignModalRef = ref(null);
|
||||
const uploadSignedModalRef = ref(null);
|
||||
const contractArchiveModalRef = ref(null);
|
||||
|
||||
const total = ref(0);
|
||||
const loading = ref(false);
|
||||
@@ -685,27 +687,19 @@ function handleMockCollect(row) {
|
||||
}
|
||||
|
||||
function handleArchive(row) {
|
||||
Modal.confirm({
|
||||
title: "确认归档合同",
|
||||
content: h("div", [
|
||||
h("p", `确认将合同【${row.contractName}】归档?`),
|
||||
h("p", { style: { color: "#999", fontSize: "12px" } }, "归档后合同进入「已完成」状态,全生命周期结束。"),
|
||||
]),
|
||||
okText: "确认归档",
|
||||
cancelText: "取消",
|
||||
onOk: () =>
|
||||
archiveLaunch(row.id, {}).then((res) => {
|
||||
if (!isSuccessResponse(res)) {
|
||||
message.error(getResponseMessage(res, "测试归档失败"));
|
||||
getLaunchDetail(row.id).then((res) => {
|
||||
if (!isSuccessResponse(res) || !getDataResult(res)) {
|
||||
message.error(getResponseMessage(res, "获取归档详情失败"));
|
||||
return;
|
||||
}
|
||||
message.success("测试归档完成,合同已流转到已完成");
|
||||
getList();
|
||||
}),
|
||||
icon: null,
|
||||
contractArchiveModalRef.value?.showModal(getDataResult(res));
|
||||
});
|
||||
}
|
||||
|
||||
function handleArchiveDone() {
|
||||
getList();
|
||||
}
|
||||
|
||||
function handleTerminate(row) {
|
||||
let terminateReason = "";
|
||||
Modal.confirm({
|
||||
|
||||
Reference in New Issue
Block a user