This commit is contained in:
2026-07-21 13:20:06 +08:00
parent 76df2b9cd2
commit e50d1b8fda
21 changed files with 714 additions and 129 deletions

View File

@@ -131,6 +131,12 @@
</el-button>
</template>
</avue-crud>
<empty-pagination
:page="page"
@size-change="sizeChange"
@current-change="currentChange"
@load="onLoad(page, query)"
/>
<el-dialog
:title="dialogTitle"
@@ -150,6 +156,13 @@
class="archive-form"
>
<div class="section-title">基本信息</div>
<el-alert
type="warning"
:closable="false"
show-icon
title="临时客商可暂存基础信息,但不能参与结算;正式客商需资质完整、评分完成并审批通过后生效。"
class="archive-rule-alert"
/>
<el-row :gutter="18">
<el-col :span="6">
<el-form-item label="客商编号" prop="customerCode">
@@ -196,10 +209,9 @@
</el-col>
<el-col :span="6">
<el-form-item label="客商性质" prop="customerNature">
<el-select v-model="archiveForm.customerNature" filterable allow-create default-first-option>
<el-option label="企业" value="企业" />
<el-option label="个" value="个" />
<el-option label="事业单位" value="事业单位" />
<el-select v-model="archiveForm.customerNature" filterable>
<el-option label="有限公司" value="有限公司" />
<el-option label="个体工商户" value="个体工商户" />
</el-select>
</el-form-item>
</el-col>
@@ -215,7 +227,7 @@
</el-col>
<el-col :span="6">
<el-form-item label="客商类型" prop="customerType">
<el-select v-model="archiveForm.customerType" filterable allow-create default-first-option>
<el-select v-model="archiveForm.customerType" multiple filterable>
<el-option
v-for="item in customerTypeOptions"
:key="item.value"
@@ -242,14 +254,15 @@
</el-col>
<el-col :span="6">
<el-form-item label="所属组织" prop="deptName">
<el-select v-model="archiveForm.deptId" filterable clearable @change="onDeptChange">
<el-option
v-for="item in deptOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<el-tree-select
v-model="archiveForm.deptId"
:data="deptTree"
:props="{ label: 'title', value: 'id', children: 'children' }"
check-strictly
filterable
clearable
@change="onDeptChange"
/>
</el-form-item>
</el-col>
<el-col :span="6">
@@ -330,20 +343,66 @@
<div class="section-head">
<div class="section-title">资质附件</div>
<el-button type="primary" icon="el-icon-plus" v-if="!readonly" @click="addAttachment">
添加附件
</el-button>
<div v-if="!readonly" class="section-actions">
<el-button type="primary" icon="el-icon-upload" @click="addAttachment">OCR上传识别</el-button>
<el-button type="primary" icon="el-icon-plus" @click="addAttachment">添加附件</el-button>
<el-button icon="el-icon-download" @click="downloadAttachments">批量下载</el-button>
</div>
</div>
<el-alert
type="info"
:closable="false"
title="支持 JPG、PNG、PDF单文件大小不超过 10MB正式客商需上传公司营业执照、道路运输证等核心材料。"
class="archive-rule-alert"
/>
<el-alert
v-if="missingQualificationText"
type="error"
:closable="false"
:title="missingQualificationText"
class="archive-rule-alert"
/>
<el-table :data="qualificationFiles" border>
<el-table-column label="序号" type="index" width="70" align="center" />
<el-table-column label="附件名称" min-width="180">
<el-table-column label="附件类型" min-width="160">
<template #default="{ row }">
<el-select v-model="row.type" :disabled="readonly" filterable>
<el-option label="法人身份证" value="法人身份证" />
<el-option label="公司营业执照" value="公司营业执照" />
<el-option label="道路运输证" value="道路运输证" />
<el-option label="开户许可证" value="开户许可证" />
<el-option label="其他" value="其他" />
</el-select>
</template>
</el-table-column>
<el-table-column label="文件名称" min-width="180">
<template #default="{ row }">
<el-input v-model="row.name" :disabled="readonly" maxlength="100" />
</template>
</el-table-column>
<el-table-column label="附件地址" min-width="320">
<el-table-column label="附件描述" min-width="220">
<template #default="{ row }">
<el-input v-model="row.url" :disabled="readonly" maxlength="500" />
<el-input v-model="row.description" :disabled="readonly" maxlength="200" />
</template>
</el-table-column>
<el-table-column label="文件大小" min-width="120">
<template #default="{ row }">
<el-input v-model="row.size" :disabled="readonly" maxlength="30" />
</template>
</el-table-column>
<el-table-column label="上传人" min-width="120">
<template #default="{ row }">
<el-input v-model="row.uploadUserName" :disabled="readonly" maxlength="30" />
</template>
</el-table-column>
<el-table-column label="上传时间" min-width="170">
<template #default="{ row }">
<el-date-picker
v-model="row.uploadTime"
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
:disabled="readonly"
/>
</template>
</el-table-column>
<el-table-column label="操作" width="90" align="center" v-if="!readonly">
@@ -358,7 +417,7 @@
<div class="section-head">
<span></span>
<el-button type="primary" icon="el-icon-plus" v-if="!readonly" @click="addScore">
读取评分量化表
添加评分记录
</el-button>
</div>
<el-table :data="archiveForm.scores" border>
@@ -376,6 +435,11 @@
<el-table-column label="自评得分" prop="selfScore" width="110" align="center" />
<el-table-column label="复评得分" prop="reviewScore" width="110" align="center" />
<el-table-column label="最终得分" prop="finalScore" width="110" align="center" />
<el-table-column label="额度" min-width="150">
<template #default="{ row }">
<el-input-number v-model="row.applyCreditLimit" :min="0" :precision="2" :disabled="readonly" />
</template>
</el-table-column>
<el-table-column label="信用等级" min-width="130">
<template #default="{ row }">
<el-input v-model="row.creditLevel" :disabled="readonly" maxlength="30" />
@@ -431,6 +495,8 @@
<template #footer>
<el-button @click="archiveBox = false">{{ readonly ? '关闭' : '取消' }}</el-button>
<el-button type="primary" v-if="!readonly" @click="saveArchive">保存</el-button>
<el-button type="success" v-if="!readonly" @click="saveAndSubmitArchive">提交</el-button>
<el-button v-if="!readonly" @click="archiveBox = false">返回</el-button>
</template>
</el-dialog>
@@ -566,6 +632,13 @@ export default {
callback();
}
};
const validateBusinessEndDate = (rule, value, callback) => {
if (this.archiveForm.businessTermType === '固定期限' && !value) {
callback(new Error('请选择营业期限截止日'));
} else {
callback();
}
};
return {
form: {},
query: {},
@@ -585,11 +658,11 @@ export default {
archiveForm: this.emptyArchive(),
currentScore: { details: [] },
qualificationFiles: [],
deptTree: [],
deptOptions: [],
customerTypeOptions: [
{ label: '客户', value: '客户' },
{ label: '供应商', value: '供应商' },
{ label: '客户/供应商', value: '客户/供应商' },
{ label: '承运商', value: '承运商' },
],
accessTypeMap: {
temporary: '临时客商',
@@ -648,7 +721,9 @@ export default {
trigger: 'blur',
},
],
customerType: [{ required: true, message: '请选择客商类型', trigger: 'change' }],
customerType: [
{ type: 'array', required: true, message: '请选择客商类型', trigger: 'change' },
],
legalPerson: [{ required: true, message: '请输入法人/负责人', trigger: 'blur' }],
contactPhone: [
{ required: true, message: '请输入联系电话', trigger: 'blur' },
@@ -659,6 +734,7 @@ export default {
registeredCapital: [{ validator: validateNonNegative, trigger: 'blur' }],
maxCreditLimit: [{ validator: validateNonNegative, trigger: 'blur' }],
applyCreditLimit: [{ validator: validateNonNegative, trigger: 'blur' }],
businessEndDate: [{ validator: validateBusinessEndDate, trigger: 'change' }],
registeredAddress: [{ max: 200, message: '地址最多200个字符', trigger: 'blur' }],
businessScope: [{ max: 500, message: '经营范围最多500个字符', trigger: 'blur' }],
remark: [{ max: 500, message: '备注最多500个字符', trigger: 'blur' }],
@@ -667,6 +743,8 @@ export default {
height: 'auto',
calcHeight: 32,
tip: false,
searchBtnText: '查询',
emptyBtnText: '重置',
searchShow: true,
searchMenuSpan: 6,
border: true,
@@ -691,9 +769,8 @@ export default {
search: true,
minWidth: 120,
dicData: [
{ label: '企业', value: '企业' },
{ label: '个', value: '个' },
{ label: '事业单位', value: '事业单位' },
{ label: '有限公司', value: '有限公司' },
{ label: '个体工商户', value: '个体工商户' },
],
},
{ label: '统一信用代码', prop: 'unifiedCreditCode', search: true, minWidth: 190 },
@@ -746,6 +823,7 @@ export default {
{ label: '启用', value: 1 },
{ label: '停用', value: 2 },
],
clearable: true,
},
{
label: '创建开始时间',
@@ -797,6 +875,14 @@ export default {
if (this.readonly) return '查看客商档案';
return this.archiveForm.id ? '编辑客商档案' : '新增客商档案';
},
missingQualificationText() {
if (this.archiveForm.accessType !== 'formal') return '';
const types = this.qualificationFiles.map(item => item.type || item.name).filter(Boolean);
const missing = ['公司营业执照', '道路运输证'].filter(
item => !types.some(type => String(type).includes(item))
);
return missing.length ? `缺失核心资质材料:${missing.join('、')}` : '';
},
},
methods: {
hasPermission(code) {
@@ -807,6 +893,7 @@ export default {
accessType: 'temporary',
approvalStatus: 'draft',
status: 1,
customerType: [],
contacts: [],
receiptAccounts: [],
invoices: [],
@@ -845,7 +932,8 @@ export default {
},
initDeptTree() {
getDeptTree(this.userInfo.tenantId).then(res => {
this.deptOptions = this.flattenDept(res.data.data || []);
this.deptTree = res.data.data || [];
this.deptOptions = this.flattenDept(this.deptTree);
});
},
initCustomerTypes() {
@@ -856,6 +944,11 @@ export default {
label: item.name,
value: item.name,
}));
['客户', '承运商'].forEach(type => {
if (!this.customerTypeOptions.some(item => item.value === type)) {
this.customerTypeOptions.push({ label: type, value: type });
}
});
}
});
},
@@ -894,19 +987,47 @@ export default {
stringifyAttachments(list) {
const files = (list || [])
.map(item => ({
type: String(item.type || '').trim(),
name: String(item.name || '').trim(),
description: String(item.description || '').trim(),
size: String(item.size || '').trim(),
uploadUserName: String(item.uploadUserName || '').trim(),
uploadTime: String(item.uploadTime || '').trim(),
url: String(item.url || '').trim(),
}))
.filter(item => item.name || item.url);
.filter(item => item.type || item.name || item.url);
return files.length ? JSON.stringify(files) : '';
},
addAttachment() {
this.qualificationFiles.push({ name: '', url: '' });
this.qualificationFiles.push({
type: '',
name: '',
description: '',
size: '',
uploadUserName: this.userInfo.realName || this.userInfo.userName || '',
uploadTime: this.$dayjs().format('YYYY-MM-DD HH:mm:ss'),
url: '',
});
},
downloadAttachments() {
const files = this.qualificationFiles.filter(item => item.url);
if (!files.length) {
this.$message.warning('暂无可下载附件');
return;
}
files.forEach(item => window.open(item.url, '_blank'));
},
normalizeDetail(detail) {
const customerType = detail.customerType
? String(detail.customerType)
.split(',')
.map(item => item.trim())
.filter(Boolean)
: [];
return {
...this.emptyArchive(),
...detail,
customerType,
contacts: detail.contacts || [],
receiptAccounts: detail.receiptAccounts || [],
invoices: detail.invoices || [],
@@ -964,6 +1085,9 @@ export default {
},
normalizeArchive() {
const archive = JSON.parse(JSON.stringify(this.archiveForm));
archive.customerType = Array.isArray(archive.customerType)
? archive.customerType.join(',')
: archive.customerType;
archive.qualificationAttachments = this.stringifyAttachments(this.qualificationFiles);
archive.contacts = (archive.contacts || []).filter(item => item.contactName || item.contactPhone);
archive.receiptAccounts = (archive.receiptAccounts || []).filter(
@@ -986,6 +1110,31 @@ export default {
});
});
},
saveAndSubmitArchive() {
this.$refs.archiveForm.validate(valid => {
if (!valid) {
this.$message.warning('请先完整填写必填项');
return;
}
const archive = this.normalizeArchive();
if (!this.validateFormalForApproval(archive)) return;
submit(archive).then(res => {
const data = res.data.data;
const id = (data && typeof data === 'object' ? data.id : data) || this.archiveForm.id;
if (!id) {
this.archiveBox = false;
this.onLoad(this.page);
this.$message({ type: 'success', message: '保存成功,请在列表提交审批' });
return;
}
submitApproval(id).then(() => {
this.archiveBox = false;
this.onLoad(this.page);
this.$message({ type: 'success', message: '提交成功!' });
});
});
});
},
addScore() {
getScoreTemplate().then(res => {
const score = this.normalizeScore(res.data.data);
@@ -1228,6 +1377,17 @@ export default {
margin: 18px 0 12px;
}
.section-actions {
display: flex;
flex-wrap: wrap;
gap: 8px;
justify-content: flex-end;
}
.archive-rule-alert {
margin-bottom: 12px;
}
.archive-tabs {
margin-top: 18px;
}