1、调整合同

2、调整项目
3、调整客商
This commit is contained in:
2026-08-26 15:56:35 +08:00
parent dc5a2235eb
commit 70ebe31103
5 changed files with 183 additions and 66 deletions
+22 -13
View File
@@ -424,7 +424,7 @@
<el-table :data="paymentRatioRows" border>
<el-table-column type="index" label="序号" width="80" align="center" />
<el-table-column prop="paymentTerm" label="付款笔数" min-width="180" align="center" />
<el-table-column label="付款比例上限" min-width="220" align="center">
<el-table-column label="付款比例上限%" min-width="220" align="center">
<template #default="{ row }">
<el-input
v-model="row.ratioLimit"
@@ -541,8 +541,17 @@
width="90%"
top="4vh"
>
<pdf-preview
v-if="
attachmentDocumentPreviewVisible &&
attachmentPreviewFile.url &&
attachmentPreviewFile.isPdf
"
:source="attachmentPreviewFile.url"
@error="handleAttachmentPreviewError"
/>
<open-file-viewer
v-if="attachmentDocumentPreviewVisible && attachmentPreviewFile.url"
v-else-if="attachmentDocumentPreviewVisible && attachmentPreviewFile.url"
:file="attachmentPreviewFile.url"
:file-name="attachmentPreviewFile.name"
:mime-type="attachmentPreviewFile.mimeType"
@@ -706,7 +715,7 @@
<el-table :data="detailPaymentRatioRows" border>
<el-table-column type="index" label="序号" width="80" align="center" />
<el-table-column prop="paymentTerm" label="付款笔数" min-width="180" align="center" />
<el-table-column label="付款比例上限" min-width="220" align="center">
<el-table-column label="付款比例上限%" min-width="220" align="center">
<template #default="{ row }">{{
detailObjectUnitValue(row, 'ratioLimit', '%')
}}</template>
@@ -791,15 +800,10 @@ import { config, option } from '@/option/business/contract-manage';
import { getToken } from '@/utils/auth';
import { downloadFileByUrl, downloadXls } from '@/utils/util';
import BillingPlanEditor from './components/billing-plan-editor.vue';
import PdfPreview from '@/components/pdf-preview/main.vue';
import { ElImageViewer } from 'element-plus';
import { OpenFileViewer } from '@open-file-viewer/vue';
import {
fallbackPlugin,
imagePlugin,
officePlugin,
pdfPlugin,
textPlugin,
} from '@open-file-viewer/core';
import { fallbackPlugin, imagePlugin, officePlugin, textPlugin } from '@open-file-viewer/core';
import '@open-file-viewer/core/style.css';
import pdfWorkerSrc from 'pdfjs-dist/build/pdf.worker.mjs?url';
@@ -879,6 +883,10 @@ const attachmentExtension = row => {
};
const isAttachmentImage = row =>
['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'].includes(attachmentExtension(row));
const isAttachmentPdf = row => {
const mimeType = String(row.mimeType || row.contentType || '').toLowerCase();
return mimeType.includes('application/pdf') || attachmentExtension(row) === 'pdf';
};
const attachmentFileTypes = [
'pdf',
'bmp',
@@ -897,7 +905,6 @@ const attachmentFileTypes = [
];
const attachmentViewerPlugins = [
imagePlugin(),
pdfPlugin({ workerSrc: pdfWorkerSrc, useFetchData: true }),
officePlugin({ pdf: { workerSrc: pdfWorkerSrc, useFetchData: true } }),
textPlugin(),
fallbackPlugin(),
@@ -1090,7 +1097,7 @@ const AttachmentSection = defineComponent({
export default {
name: 'ContractManage',
components: { AttachmentSection, BillingPlanEditor, ElImageViewer, OpenFileViewer },
components: { AttachmentSection, BillingPlanEditor, ElImageViewer, OpenFileViewer, PdfPreview },
data() {
return {
api,
@@ -1757,10 +1764,12 @@ export default {
this.attachmentImagePreviewVisible = true;
return;
}
const isPdf = isAttachmentPdf(row);
this.attachmentPreviewFile = {
name: attachmentName(row),
url,
mimeType: row.mimeType || row.contentType || '',
mimeType: isPdf ? 'application/pdf' : row.mimeType || row.contentType || '',
isPdf,
};
this.attachmentDocumentPreviewVisible = true;
},
+19 -4
View File
@@ -660,8 +660,17 @@
top="4vh"
class="project-apply-form__attachment-viewer-dialog"
>
<pdf-preview
v-if="
attachmentDocumentPreviewVisible &&
attachmentPreviewFile.url &&
attachmentPreviewFile.isPdf
"
:source="attachmentPreviewFile.url"
@error="handleAttachmentPreviewError"
/>
<open-file-viewer
v-if="attachmentDocumentPreviewVisible && attachmentPreviewFile.url"
v-else-if="attachmentDocumentPreviewVisible && attachmentPreviewFile.url"
:file="attachmentPreviewFile.url"
:file-name="attachmentPreviewFile.name"
:mime-type="attachmentPreviewFile.mimeType"
@@ -748,11 +757,11 @@ import { getToken } from '@/utils/auth';
import { downloadFileByUrl, downloadXls } from '@/utils/util';
import { ElImageViewer } from 'element-plus';
import { OpenFileViewer } from '@open-file-viewer/vue';
import PdfPreview from '@/components/pdf-preview/main.vue';
import {
fallbackPlugin,
imagePlugin,
officePlugin,
pdfPlugin,
textPlugin,
} from '@open-file-viewer/core';
import '@open-file-viewer/core/style.css';
@@ -821,7 +830,6 @@ const changeTypeOptions = [
const attachmentViewerPlugins = [
imagePlugin(),
pdfPlugin({ workerSrc: pdfWorkerSrc, useFetchData: true }),
officePlugin({ pdf: { workerSrc: pdfWorkerSrc, useFetchData: true } }),
textPlugin(),
fallbackPlugin(),
@@ -854,6 +862,7 @@ export default {
components: {
ElImageViewer,
OpenFileViewer,
PdfPreview,
},
data() {
const validateAmount = (rule, value, callback) => {
@@ -1982,6 +1991,10 @@ export default {
this.attachmentExtension(row)
);
},
isAttachmentPdf(row) {
const mimeType = String(row.mimeType || row.contentType || '').toLowerCase();
return mimeType.includes('application/pdf') || this.attachmentExtension(row) === 'pdf';
},
previewAttachment(row) {
const url = this.attachmentUrl(row);
if (!url) {
@@ -1999,10 +2012,12 @@ export default {
this.attachmentImagePreviewVisible = true;
return;
}
const isPdf = this.isAttachmentPdf(row);
this.attachmentPreviewFile = {
name: this.attachmentName(row),
url,
mimeType: row.mimeType || row.contentType || '',
mimeType: isPdf ? 'application/pdf' : row.mimeType || row.contentType || '',
isPdf,
};
this.attachmentDocumentPreviewVisible = true;
},
+91 -15
View File
@@ -703,8 +703,17 @@
width="90%"
top="4vh"
>
<pdf-preview
v-if="
qualificationDocumentPreviewVisible &&
qualificationPreviewFile.url &&
qualificationPreviewFile.isPdf
"
:source="qualificationPreviewFile.url"
@error="handleQualificationPreviewError"
/>
<open-file-viewer
v-if="qualificationDocumentPreviewVisible && qualificationPreviewFile.url"
v-else-if="qualificationDocumentPreviewVisible && qualificationPreviewFile.url"
:file="qualificationPreviewFile.url"
:file-name="qualificationPreviewFile.name"
:mime-type="qualificationPreviewFile.mimeType"
@@ -1385,7 +1394,7 @@ import { getDictionary } from '@/api/system/dictbiz';
import { getDeptTree } from '@/api/system/dept';
import { getLazyTree } from '@/api/base/region';
import { exportBlob } from '@/api/common';
import { downloadXls } from '@/utils/util';
import { downloadFileByUrl, downloadXls } from '@/utils/util';
import { getToken } from '@/utils/auth';
import { getUploadHeaders } from '@/utils/upload';
import { normalizeSearchRangeParams } from '@/utils/search-range';
@@ -1396,7 +1405,6 @@ import {
fallbackPlugin,
imagePlugin,
officePlugin,
pdfPlugin,
textPlugin,
} from '@open-file-viewer/core';
import '@open-file-viewer/core/style.css';
@@ -1405,6 +1413,7 @@ import { mapGetters } from 'vuex';
import NProgress from 'nprogress';
import 'nprogress/nprogress.css';
import SectionCard from '@/components/section-card/main.vue';
import PdfPreview from '@/components/pdf-preview/main.vue';
const createTimeRangeMap = {
createTimeRange: ['createTimeStart', 'createTimeEnd'],
@@ -1415,7 +1424,6 @@ const AMAP_SECURITY_CODE = '5aab65c632e48ebae0d0e28f5b28e21a';
let amapLoader;
const qualificationViewerPlugins = [
imagePlugin(),
pdfPlugin({ workerSrc: pdfWorkerSrc, useFetchData: true }),
officePlugin({ pdf: { workerSrc: pdfWorkerSrc, useFetchData: true } }),
textPlugin(),
fallbackPlugin(),
@@ -1428,6 +1436,7 @@ export default {
Location,
ElImageViewer,
OpenFileViewer,
PdfPreview,
},
data() {
const nonNegativeLabelMap = {
@@ -2427,8 +2436,10 @@ export default {
initDeptTree() {
getDeptTree(this.userInfo.tenantId)
.then(res => {
this.deptTree = this.normalizeDeptTree(res.data.data || []);
const deptTree = this.excludeExternalOrganization(res.data.data || []);
this.deptTree = this.normalizeDeptTree(deptTree);
this.deptOptions = this.flattenDept(this.deptTree);
this.setDefaultTopLevelDept();
const deptColumn = this.findColumn(this.option.column, 'deptName');
deptColumn.dicData = [
{ label: '全部', value: '' },
@@ -2611,6 +2622,17 @@ export default {
};
});
},
excludeExternalOrganization(tree = []) {
return tree.reduce((result, item) => {
const deptName = item.label || item.title || item.deptName || item.name || '';
if (String(deptName).trim() === '外部组织') return result;
result.push({
...item,
children: this.excludeExternalOrganization(item.children || []),
});
return result;
}, []);
},
flattenDept(tree, level = 0) {
const result = [];
tree.forEach(item => {
@@ -2625,6 +2647,18 @@ export default {
});
return result;
},
setDefaultTopLevelDept() {
const deptIds = Array.isArray(this.archiveForm.deptId)
? this.archiveForm.deptId
: this.archiveForm.deptId
? [this.archiveForm.deptId]
: [];
const firstTopLevelDept = this.deptTree[0];
if (!this.isArchivePage || this.$route.query.id || deptIds.length || !firstTopLevelDept) {
return;
}
this.onDeptChange([firstTopLevelDept.value]);
},
onDeptChange(value) {
const deptIds = Array.isArray(value) ? value : value ? [value] : [];
const deptNames = deptIds
@@ -3387,6 +3421,12 @@ export default {
this.getQualificationFileExtension(file)
);
},
isQualificationPdf(file) {
const mimeType = String(file.mimeType || file.contentType || '').toLowerCase();
return (
mimeType.includes('application/pdf') || this.getQualificationFileExtension(file) === 'pdf'
);
},
previewQualificationFile(file) {
const url = this.getQualificationFileUrl(file);
if (!url) {
@@ -3404,10 +3444,12 @@ export default {
this.qualificationImagePreviewVisible = true;
return;
}
const isPdf = this.isQualificationPdf(file);
this.qualificationPreviewFile = {
name: file.originalName || file.name || '附件',
url,
mimeType: file.mimeType || file.contentType || '',
mimeType: isPdf ? 'application/pdf' : file.mimeType || file.contentType || '',
isPdf,
};
this.qualificationDocumentPreviewVisible = true;
},
@@ -3457,20 +3499,46 @@ export default {
})
.map(item => item.file);
},
getMissingQualificationTypes(files = this.qualificationFiles) {
getRequiredQualificationTypes(customerType = this.archiveForm.customerType) {
const customerTypes = Array.isArray(customerType)
? customerType
: this.splitValue(customerType);
const requiredTypes = ['营业执照', '法人身份证'];
const includesCarrier = customerTypes.some(item =>
['承运商', 'carrier'].includes(String(item || '').trim().toLowerCase())
);
if (includesCarrier) requiredTypes.push('运输许可证');
return requiredTypes;
},
isUploadedQualificationType(uploadedType, requiredType) {
const uploaded = String(uploadedType || '')
.replace(/\s/g, '')
.trim();
if (uploaded === requiredType || uploaded.includes(requiredType)) return true;
return requiredType === '运输许可证' && /运输.*许可证/.test(uploaded);
},
getMissingQualificationTypes(
files = this.qualificationFiles,
customerType = this.archiveForm.customerType
) {
const uploadedTypes = new Set(
(files || [])
.filter(item => item.url)
.map(item => String(item.type || '').trim())
.filter(Boolean)
);
return this.qualificationTypeOptions
.filter(item => String(item.label || item.value || '').trim() !== '其他附件')
.filter(item => !uploadedTypes.has(String(item.value || item.label || '').trim()))
.map(item => item.label || item.value);
return this.getRequiredQualificationTypes(customerType).filter(
requiredType =>
![...uploadedTypes].some(uploadedType =>
this.isUploadedQualificationType(uploadedType, requiredType)
)
);
},
validateQualificationMaterials(files = this.qualificationFiles) {
const missing = this.getMissingQualificationTypes(files);
validateQualificationMaterials(
files = this.qualificationFiles,
customerType = this.archiveForm.customerType
) {
const missing = this.getMissingQualificationTypes(files, customerType);
if (!missing.length) return true;
this.$message.warning(`请先上传客商材料:${missing.join('、')}`);
return false;
@@ -3486,7 +3554,14 @@ export default {
);
return;
}
files.forEach(item => window.open(item.url, '_blank'));
files.forEach((item, index) => {
window.setTimeout(() => {
downloadFileByUrl(
item.url,
item.originalName || item.name || `客商材料${index + 1}`
);
}, index * 300);
});
},
handleQualificationSelectionChange(rows) {
this.selectedQualificationFiles = rows || [];
@@ -3690,7 +3765,8 @@ export default {
validateFormalForApproval(archive) {
if (
!this.validateQualificationMaterials(
this.parseAttachments(archive.qualificationAttachments)
this.parseAttachments(archive.qualificationAttachments),
archive.customerType
)
) {
return false;