1、调整导入运单
2、调整对账
This commit is contained in:
@@ -36,6 +36,7 @@ export const roadLoading = ids =>
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const getImportBatches = params => request({ url: `${baseUrl}/import-batch/list`, method: 'get', params });
|
export const getImportBatches = params => request({ url: `${baseUrl}/import-batch/list`, method: 'get', params });
|
||||||
|
export const getImportBatchNextCode = () => request({ url: `${baseUrl}/import-batch/next-code`, method: 'get' });
|
||||||
export const getImportDetails = params => request({ url: `${baseUrl}/import-batch/details`, method: 'get', params });
|
export const getImportDetails = params => request({ url: `${baseUrl}/import-batch/details`, method: 'get', params });
|
||||||
export const removeImportBatches = ids => request({ url: `${baseUrl}/import-batch/remove`, method: 'post', params: { ids } });
|
export const removeImportBatches = ids => request({ url: `${baseUrl}/import-batch/remove`, method: 'post', params: { ids } });
|
||||||
export const getImportOptions = () => request({ url: `${baseUrl}/import-batch/options`, method: 'get' });
|
export const getImportOptions = () => request({ url: `${baseUrl}/import-batch/options`, method: 'get' });
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ export const complete = id =>
|
|||||||
request({ url: `${baseUrl}/complete`, method: 'post', params: { id } });
|
request({ url: `${baseUrl}/complete`, method: 'post', params: { id } });
|
||||||
export const completeWithData = data =>
|
export const completeWithData = data =>
|
||||||
request({ url: `${baseUrl}/complete-with-data`, method: 'post', data });
|
request({ url: `${baseUrl}/complete-with-data`, method: 'post', data });
|
||||||
export const template = mode =>
|
export const template = (mode, params) =>
|
||||||
request({ url: `${baseUrl}/template`, method: 'get', params: { mode }, responseType: 'blob' });
|
request({ url: `${baseUrl}/template`, method: 'get', params: { mode, ...params }, responseType: 'blob' });
|
||||||
|
|
||||||
const importFile = (url, id, file) => {
|
const importFile = (url, id, file) => {
|
||||||
const data = new FormData();
|
const data = new FormData();
|
||||||
|
|||||||
@@ -97,6 +97,11 @@ export const businessTypeOptions = [
|
|||||||
{ label: '普通业务', value: '普通业务' },
|
{ label: '普通业务', value: '普通业务' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const businessModeOptions = [
|
||||||
|
{ label: '国内运输', value: '国内运输' },
|
||||||
|
{ label: '跨境运输', value: '跨境运输' },
|
||||||
|
];
|
||||||
|
|
||||||
export const settlementModeOptions = [
|
export const settlementModeOptions = [
|
||||||
{ label: '先票后款', value: '先票后款' },
|
{ label: '先票后款', value: '先票后款' },
|
||||||
{ label: '先款后票', value: '先款后票' },
|
{ label: '先款后票', value: '先款后票' },
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
auditColumns,
|
auditColumns,
|
||||||
|
businessModeOptions,
|
||||||
businessTypeOptions,
|
businessTypeOptions,
|
||||||
createCrudOption,
|
createCrudOption,
|
||||||
nonNegativeRule,
|
nonNegativeRule,
|
||||||
@@ -129,7 +130,7 @@ export const option = createCrudOption([
|
|||||||
hide: true,
|
hide: true,
|
||||||
minWidth: 140,
|
minWidth: 140,
|
||||||
maxlength: 20,
|
maxlength: 20,
|
||||||
rules: textRule('项目简称', 20, true),
|
rules: textRule('项目简称', 20),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '项目类型',
|
label: '项目类型',
|
||||||
@@ -305,6 +306,15 @@ export const option = createCrudOption([
|
|||||||
hide: true,
|
hide: true,
|
||||||
minWidth: 130,
|
minWidth: 130,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: '业务模式',
|
||||||
|
prop: 'businessMode',
|
||||||
|
type: 'select',
|
||||||
|
dicData: businessModeOptions,
|
||||||
|
hide: true,
|
||||||
|
minWidth: 120,
|
||||||
|
rules: [selectRule('业务模式')],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: '项目规模(万元)',
|
label: '项目规模(万元)',
|
||||||
prop: 'projectScale',
|
prop: 'projectScale',
|
||||||
@@ -323,6 +333,15 @@ export const option = createCrudOption([
|
|||||||
minWidth: 150,
|
minWidth: 150,
|
||||||
rules: [nonNegativeRule('预计利润')],
|
rules: [nonNegativeRule('预计利润')],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: '利润率(%)',
|
||||||
|
prop: 'profitRate',
|
||||||
|
type: 'number',
|
||||||
|
precision: 2,
|
||||||
|
hide: true,
|
||||||
|
minWidth: 120,
|
||||||
|
rules: [nonNegativeRule('利润率')],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: '资金需求(万元)',
|
label: '资金需求(万元)',
|
||||||
prop: 'fundDemand',
|
prop: 'fundDemand',
|
||||||
|
|||||||
@@ -67,9 +67,7 @@ export const feeDetailBaseColumns = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export const feeDetailTailColumns = [
|
export const feeDetailTailColumns = [
|
||||||
{ label: '原总金额', prop: 'originalAmountText', minWidth: 120 },
|
{ label: '结算金额', prop: 'originalAmountText', minWidth: 120 },
|
||||||
{ label: '调整金额', prop: 'adjustAmountText', minWidth: 120 },
|
|
||||||
{ label: '调整后总金额', prop: 'afterAmountText', minWidth: 140 },
|
|
||||||
{ label: '备注', prop: 'remark', minWidth: 160 },
|
{ label: '备注', prop: 'remark', minWidth: 160 },
|
||||||
{ label: '最后录入人', prop: 'updateUserName', minWidth: 120 },
|
{ label: '最后录入人', prop: 'updateUserName', minWidth: 120 },
|
||||||
{ label: '最后录入时间', prop: 'updateTime', minWidth: 170 },
|
{ label: '最后录入时间', prop: 'updateTime', minWidth: 170 },
|
||||||
|
|||||||
@@ -76,6 +76,11 @@ export const option = {
|
|||||||
prop: 'posts',
|
prop: 'posts',
|
||||||
minWidth: 150,
|
minWidth: 150,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: '驾驶车辆',
|
||||||
|
prop: 'drivingVehicle',
|
||||||
|
minWidth: 130,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: '所属组织',
|
label: '所属组织',
|
||||||
prop: 'organizationName',
|
prop: 'organizationName',
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export const option = {
|
|||||||
label: '车牌号',
|
label: '车牌号',
|
||||||
prop: 'plateNo',
|
prop: 'plateNo',
|
||||||
search: true,
|
search: true,
|
||||||
searchOrder: 6,
|
searchOrder: 7,
|
||||||
slot: true,
|
slot: true,
|
||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
},
|
},
|
||||||
@@ -29,7 +29,7 @@ export const option = {
|
|||||||
label: '业务关系',
|
label: '业务关系',
|
||||||
prop: 'businessRelation',
|
prop: 'businessRelation',
|
||||||
search: true,
|
search: true,
|
||||||
searchOrder: 5,
|
searchOrder: 6,
|
||||||
searchValue: '',
|
searchValue: '',
|
||||||
type: 'select',
|
type: 'select',
|
||||||
dicData: [
|
dicData: [
|
||||||
@@ -50,11 +50,18 @@ export const option = {
|
|||||||
slot: true,
|
slot: true,
|
||||||
minWidth: 150,
|
minWidth: 150,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: '使用部门',
|
||||||
|
prop: 'useDepartment',
|
||||||
|
search: true,
|
||||||
|
searchOrder: 3,
|
||||||
|
minWidth: 140,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: '车辆类型',
|
label: '车辆类型',
|
||||||
prop: 'vehicleType',
|
prop: 'vehicleType',
|
||||||
search: true,
|
search: true,
|
||||||
searchOrder: 4,
|
searchOrder: 5,
|
||||||
type: 'select',
|
type: 'select',
|
||||||
dicData: [
|
dicData: [
|
||||||
{ label: '全部', value: '' },
|
{ label: '全部', value: '' },
|
||||||
@@ -108,7 +115,7 @@ export const option = {
|
|||||||
label: '车辆状态',
|
label: '车辆状态',
|
||||||
prop: 'status',
|
prop: 'status',
|
||||||
search: true,
|
search: true,
|
||||||
searchOrder: 3,
|
searchOrder: 4,
|
||||||
type: 'select',
|
type: 'select',
|
||||||
slot: true,
|
slot: true,
|
||||||
dicData: [
|
dicData: [
|
||||||
|
|||||||
@@ -655,23 +655,10 @@ const extractRecords = res => {
|
|||||||
if (Array.isArray(data?.data?.records)) return data.data.records;
|
if (Array.isArray(data?.data?.records)) return data.data.records;
|
||||||
return [];
|
return [];
|
||||||
};
|
};
|
||||||
// 批次号规则:PC + 导入日期 + 当日批次流水号(4 位),如 PC202606010001。
|
// 批次号由后端按 PC + 导入日期 + 当日流水生成,已删除数据也计入流水,避免唯一索引冲突。
|
||||||
const batchNoPrefix = 'PC';
|
|
||||||
const batchNoSequenceLength = 4;
|
|
||||||
const generateBatchNo = async () => {
|
const generateBatchNo = async () => {
|
||||||
const prefix = `${batchNoPrefix}${dayjs().format('YYYYMMDD')}`;
|
const res = await api.getImportBatchNextCode();
|
||||||
let maxSequence = 0;
|
return res?.data?.data || '';
|
||||||
try {
|
|
||||||
const res = await api.getImportBatches({ current: 1, size: 9999 });
|
|
||||||
extractRecords(res).forEach(item => {
|
|
||||||
const no = String(item.batchNo || '').trim();
|
|
||||||
if (!no.startsWith(prefix)) return;
|
|
||||||
maxSequence = Math.max(maxSequence, Number(no.slice(prefix.length)) || 0);
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
maxSequence = 0;
|
|
||||||
}
|
|
||||||
return `${prefix}${String(maxSequence + 1).padStart(batchNoSequenceLength, '0')}`;
|
|
||||||
};
|
};
|
||||||
// 承运商合同的承运商固定取合同乙方,与运单管理(waybill-manage-page)保持一致。
|
// 承运商合同的承运商固定取合同乙方,与运单管理(waybill-manage-page)保持一致。
|
||||||
const getCarrierContractPartyName = (contract = {}) =>
|
const getCarrierContractPartyName = (contract = {}) =>
|
||||||
|
|||||||
@@ -92,7 +92,7 @@
|
|||||||
|
|
||||||
<section class="change-section attachment-section">
|
<section class="change-section attachment-section">
|
||||||
<div class="section-head"><div class="dialog-section-title">其它附件</div><el-button type="primary" plain>批量下载</el-button></div>
|
<div class="section-head"><div class="dialog-section-title">其它附件</div><el-button type="primary" plain>批量下载</el-button></div>
|
||||||
<el-table :data="attachments" border class="change-table"><el-table-column type="index" label="序号" width="70" /><el-table-column label="文件名" min-width="240"><template #default="{ row }"><el-link type="primary" @click="previewAttachment(row, attachments)">{{ row.originalName || row.name }}</el-link></template></el-table-column><el-table-column prop="description" label="附件描述" min-width="240" /><el-table-column prop="size" label="文件大小" width="120" /><el-table-column prop="userName" label="上传人" width="140" /><el-table-column prop="uploadTime" label="上传时间" width="180" /><el-table-column label="操作" width="100"><template #default="{ $index }"><el-link type="danger" @click="attachments.splice($index, 1)">删除</el-link></template></el-table-column></el-table>
|
<el-table :data="attachments" border class="change-table"><el-table-column type="index" label="序号" width="70" /><el-table-column label="文件名" min-width="240"><template #default="{ row }"><el-link type="primary" @click="previewAttachment(row, attachments)">{{ row.originalName || row.name }}</el-link></template></el-table-column><el-table-column prop="description" label="附件描述" min-width="240" /><el-table-column prop="size" label="文件大小" width="120" /><el-table-column prop="uploadUserName" label="上传人" width="140" /><el-table-column prop="uploadTime" label="上传时间" width="180" /><el-table-column label="操作" width="100"><template #default="{ $index }"><el-link type="danger" @click="attachments.splice($index, 1)">删除</el-link></template></el-table-column></el-table>
|
||||||
<el-upload action="#" :auto-upload="false" multiple :show-file-list="false" @change="handleAttachment"><el-button type="primary" plain icon="el-icon-upload">上传附件</el-button></el-upload>
|
<el-upload action="#" :auto-upload="false" multiple :show-file-list="false" @change="handleAttachment"><el-button type="primary" plain icon="el-icon-upload">上传附件</el-button></el-upload>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@@ -171,8 +171,33 @@ export default {
|
|||||||
savePlan(value, index) { if (index < 0) this.plans.push(value); else this.plans.splice(index, 1, value); if (value.defaultPlan) this.plans.forEach((item, current) => { if (current !== (index < 0 ? this.plans.length - 1 : index)) item.defaultPlan = false; }); },
|
savePlan(value, index) { if (index < 0) this.plans.push(value); else this.plans.splice(index, 1, value); if (value.defaultPlan) this.plans.forEach((item, current) => { if (current !== (index < 0 ? this.plans.length - 1 : index)) item.defaultPlan = false; }); },
|
||||||
handleSettlementTypeChange(value) { if (value !== '月结') { this.settlementRule.billCycleType = ''; this.settlementRule.billCutoffDay = ''; } else if (!this.settlementRule.billCycleType) this.settlementRule.billCycleType = '固定截单日'; if (value !== '固定天数周期结算') this.settlementRule.cycleDays = ''; },
|
handleSettlementTypeChange(value) { if (value !== '月结') { this.settlementRule.billCycleType = ''; this.settlementRule.billCutoffDay = ''; } else if (!this.settlementRule.billCycleType) this.settlementRule.billCycleType = '固定截单日'; if (value !== '固定天数周期结算') this.settlementRule.cycleDays = ''; },
|
||||||
handleCycleTypeChange(value) { if (value === '固定截单日' && !this.settlementRule.billCutoffDay) this.settlementRule.billCutoffDay = 25; if (value !== '固定截单日') this.settlementRule.billCutoffDay = ''; },
|
handleCycleTypeChange(value) { if (value === '固定截单日' && !this.settlementRule.billCutoffDay) this.settlementRule.billCutoffDay = 25; if (value !== '固定截单日') this.settlementRule.billCutoffDay = ''; },
|
||||||
handleAttachment(event) { const raw = event.raw; if (!raw) return; if (raw.size > 50 * 1024 * 1024) { this.$message.warning('单个文件大小不能超过50M'); return; } this.attachments.push({ name: raw.name, size: `${Math.ceil(raw.size / 1024)}KB`, uploadTime: this.$dayjs().format('YYYY-MM-DD HH:mm:ss') }); },
|
currentUploadUserName() {
|
||||||
handleContractFileChange(list) { this.contractFileRows = (list || []).map(item => ({ ...item, uploadTime: item.uploadTime || this.$dayjs().format('YYYY-MM-DD HH:mm:ss') })); },
|
const userInfo = this.$store.getters.userInfo || {};
|
||||||
|
return userInfo.realName || userInfo.userName || '';
|
||||||
|
},
|
||||||
|
handleAttachment(event) {
|
||||||
|
const raw = event.raw;
|
||||||
|
if (!raw) return;
|
||||||
|
if (raw.size > 50 * 1024 * 1024) {
|
||||||
|
this.$message.warning('单个文件大小不能超过50M');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.attachments.push({
|
||||||
|
name: raw.name,
|
||||||
|
size: `${Math.ceil(raw.size / 1024)}KB`,
|
||||||
|
uploadUserName: this.currentUploadUserName(),
|
||||||
|
uploadTime: this.$dayjs().format('YYYY-MM-DD HH:mm:ss'),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleContractFileChange(list) {
|
||||||
|
const uploadUserName = this.currentUploadUserName();
|
||||||
|
const uploadTime = this.$dayjs().format('YYYY-MM-DD HH:mm:ss');
|
||||||
|
this.contractFileRows = (list || []).map(item => ({
|
||||||
|
...item,
|
||||||
|
uploadUserName: item.uploadUserName || uploadUserName,
|
||||||
|
uploadTime: item.uploadTime || uploadTime,
|
||||||
|
}));
|
||||||
|
},
|
||||||
attachmentUrl(row = {}) { return row.url || row.link || row.fileUrl || row.downloadUrl || row.domain || ''; },
|
attachmentUrl(row = {}) { return row.url || row.link || row.fileUrl || row.downloadUrl || row.domain || ''; },
|
||||||
attachmentName(row = {}) { return row.originalName || row.name || row.fileName || '附件'; },
|
attachmentName(row = {}) { return row.originalName || row.name || row.fileName || '附件'; },
|
||||||
attachmentExtension(row = {}) { const source = String(this.attachmentName(row) || this.attachmentUrl(row)).split('?')[0]; const index = source.lastIndexOf('.'); return index > -1 ? source.slice(index + 1).toLowerCase() : ''; },
|
attachmentExtension(row = {}) { const source = String(this.attachmentName(row) || this.attachmentUrl(row)).split('?')[0]; const index = source.lastIndexOf('.'); return index > -1 ? source.slice(index + 1).toLowerCase() : ''; },
|
||||||
|
|||||||
@@ -384,6 +384,16 @@
|
|||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="业务模式" prop="businessMode">
|
||||||
|
<el-select v-model="form.businessMode" placeholder="请选择业务模式">
|
||||||
|
<el-option
|
||||||
|
v-for="item in businessModeOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item label="项目规模" prop="projectScale">
|
<el-form-item label="项目规模" prop="projectScale">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="form.projectScale"
|
v-model="form.projectScale"
|
||||||
@@ -410,6 +420,14 @@
|
|||||||
<template #suffix>万元</template>
|
<template #suffix>万元</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="利润率" prop="profitRate">
|
||||||
|
<el-input
|
||||||
|
v-model="form.profitRate"
|
||||||
|
@input="value => handleNumberInput('profitRate', value)"
|
||||||
|
>
|
||||||
|
<template #suffix>%</template>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item label="履约保证金" prop="fundDemand">
|
<el-form-item label="履约保证金" prop="fundDemand">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="form.fundDemand"
|
v-model="form.fundDemand"
|
||||||
@@ -670,17 +688,6 @@
|
|||||||
</el-table>
|
</el-table>
|
||||||
|
|
||||||
<template v-if="isChangeDialog">
|
<template v-if="isChangeDialog">
|
||||||
<div class="dialog-section-title">变更内容</div>
|
|
||||||
<el-form-item prop="changeContent" class="project-apply-form__change-textarea">
|
|
||||||
<el-input
|
|
||||||
v-model="form.changeContent"
|
|
||||||
type="textarea"
|
|
||||||
:rows="2"
|
|
||||||
placeholder="2000字以内"
|
|
||||||
maxlength="2000"
|
|
||||||
show-word-limit
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<div class="dialog-section-title">变更原因</div>
|
<div class="dialog-section-title">变更原因</div>
|
||||||
<el-form-item prop="changeReason" class="project-apply-form__change-textarea">
|
<el-form-item prop="changeReason" class="project-apply-form__change-textarea">
|
||||||
<el-input
|
<el-input
|
||||||
@@ -852,6 +859,7 @@ import { mapGetters } from 'vuex';
|
|||||||
import NProgress from 'nprogress';
|
import NProgress from 'nprogress';
|
||||||
import 'nprogress/nprogress.css';
|
import 'nprogress/nprogress.css';
|
||||||
import {
|
import {
|
||||||
|
businessModeOptions,
|
||||||
businessTypeOptions,
|
businessTypeOptions,
|
||||||
projectSourceOptions,
|
projectSourceOptions,
|
||||||
projectTypeOptions,
|
projectTypeOptions,
|
||||||
@@ -886,9 +894,11 @@ const emptyForm = () => ({
|
|||||||
transportRoute: '',
|
transportRoute: '',
|
||||||
transportType: '',
|
transportType: '',
|
||||||
businessType: '',
|
businessType: '',
|
||||||
|
businessMode: '国内运输',
|
||||||
projectScale: '',
|
projectScale: '',
|
||||||
settlementMode: '',
|
settlementMode: '',
|
||||||
estimatedProfit: '',
|
estimatedProfit: '',
|
||||||
|
profitRate: '',
|
||||||
fundDemand: '',
|
fundDemand: '',
|
||||||
handlerUserId: '',
|
handlerUserId: '',
|
||||||
handlerUserName: '',
|
handlerUserName: '',
|
||||||
@@ -909,6 +919,7 @@ const emptyForm = () => ({
|
|||||||
const optionalSentinelFields = [
|
const optionalSentinelFields = [
|
||||||
'projectScale',
|
'projectScale',
|
||||||
'estimatedProfit',
|
'estimatedProfit',
|
||||||
|
'profitRate',
|
||||||
'fundDemand',
|
'fundDemand',
|
||||||
'receivableDays',
|
'receivableDays',
|
||||||
'paymentDays',
|
'paymentDays',
|
||||||
@@ -1001,6 +1012,7 @@ export default {
|
|||||||
projectSourceOptions,
|
projectSourceOptions,
|
||||||
transportTypeOptions: [],
|
transportTypeOptions: [],
|
||||||
businessTypeOptions,
|
businessTypeOptions,
|
||||||
|
businessModeOptions,
|
||||||
settlementModeOptions: [],
|
settlementModeOptions: [],
|
||||||
changeTypeOptions,
|
changeTypeOptions,
|
||||||
tableForm: {},
|
tableForm: {},
|
||||||
@@ -1035,11 +1047,11 @@ export default {
|
|||||||
{ max: 50, message: '项目名称不能超过50个字符', trigger: 'blur' },
|
{ max: 50, message: '项目名称不能超过50个字符', trigger: 'blur' },
|
||||||
],
|
],
|
||||||
projectShortName: [
|
projectShortName: [
|
||||||
{ required: true, message: '请输入项目简称', trigger: 'blur' },
|
|
||||||
{ max: 20, message: '项目简称不能超过20个字符', trigger: 'blur' },
|
{ max: 20, message: '项目简称不能超过20个字符', trigger: 'blur' },
|
||||||
],
|
],
|
||||||
businessDeptId: [{ required: true, message: '请选择业务部门', trigger: 'change' }],
|
businessDeptId: [{ required: true, message: '请选择业务部门', trigger: 'change' }],
|
||||||
undertakeDeptId: [{ required: true, message: '请选择承办部门', trigger: 'change' }],
|
undertakeDeptId: [{ required: true, message: '请选择承办部门', trigger: 'change' }],
|
||||||
|
businessMode: [{ required: true, message: '请选择业务模式', trigger: 'change' }],
|
||||||
customerNames: [{ required: true, message: '请选择客户名称', trigger: 'change' }],
|
customerNames: [{ required: true, message: '请选择客户名称', trigger: 'change' }],
|
||||||
carrierNames: [{ required: true, message: '请选择下游承运商', trigger: 'change' }],
|
carrierNames: [{ required: true, message: '请选择下游承运商', trigger: 'change' }],
|
||||||
fundLimit: [
|
fundLimit: [
|
||||||
@@ -1066,6 +1078,7 @@ export default {
|
|||||||
],
|
],
|
||||||
projectScale: [{ label: '项目规模', validator: validateAmount, trigger: 'blur' }],
|
projectScale: [{ label: '项目规模', validator: validateAmount, trigger: 'blur' }],
|
||||||
estimatedProfit: [{ label: '预估利润', validator: validateAmount, trigger: 'blur' }],
|
estimatedProfit: [{ label: '预估利润', validator: validateAmount, trigger: 'blur' }],
|
||||||
|
profitRate: [{ label: '利润率', validator: validateAmount, trigger: 'blur' }],
|
||||||
fundDemand: [{ label: '履约保证金', validator: validateAmount, trigger: 'blur' }],
|
fundDemand: [{ label: '履约保证金', validator: validateAmount, trigger: 'blur' }],
|
||||||
handlerUserId: [
|
handlerUserId: [
|
||||||
{
|
{
|
||||||
@@ -1087,9 +1100,6 @@ export default {
|
|||||||
trigger: 'change',
|
trigger: 'change',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
changeContent: [
|
|
||||||
{ max: 2000, message: '变更内容不能超过2000个字符', trigger: 'blur' },
|
|
||||||
],
|
|
||||||
changeReason: [
|
changeReason: [
|
||||||
{ max: 2000, message: '变更原因不能超过2000个字符', trigger: 'blur' },
|
{ max: 2000, message: '变更原因不能超过2000个字符', trigger: 'blur' },
|
||||||
],
|
],
|
||||||
@@ -1570,6 +1580,7 @@ export default {
|
|||||||
receivableLimit: this.formatAmount(row.receivableLimit),
|
receivableLimit: this.formatAmount(row.receivableLimit),
|
||||||
projectScale: this.normalizeOptionalSentinel(row.projectScale),
|
projectScale: this.normalizeOptionalSentinel(row.projectScale),
|
||||||
estimatedProfit: this.normalizeOptionalSentinel(row.estimatedProfit),
|
estimatedProfit: this.normalizeOptionalSentinel(row.estimatedProfit),
|
||||||
|
profitRate: this.normalizeOptionalSentinel(row.profitRate),
|
||||||
fundDemand: this.normalizeOptionalSentinel(row.fundDemand),
|
fundDemand: this.normalizeOptionalSentinel(row.fundDemand),
|
||||||
receivableDays: this.normalizeOptionalSentinel(row.receivableDays),
|
receivableDays: this.normalizeOptionalSentinel(row.receivableDays),
|
||||||
paymentDays: this.normalizeOptionalSentinel(row.paymentDays),
|
paymentDays: this.normalizeOptionalSentinel(row.paymentDays),
|
||||||
@@ -1760,8 +1771,9 @@ export default {
|
|||||||
if (!options.includeChangeType) {
|
if (!options.includeChangeType) {
|
||||||
delete submitRow.changeType;
|
delete submitRow.changeType;
|
||||||
}
|
}
|
||||||
|
// 变更内容字段已下线,提交时始终清空,避免沿用历史值
|
||||||
|
submitRow.changeContent = '';
|
||||||
if (this.isNewProjectDialog && !options.includeChangeType) {
|
if (this.isNewProjectDialog && !options.includeChangeType) {
|
||||||
delete submitRow.changeContent;
|
|
||||||
delete submitRow.changeReason;
|
delete submitRow.changeReason;
|
||||||
}
|
}
|
||||||
['projectIntro', 'profitRemark', 'riskPoint', 'emergencyPlan'].forEach(
|
['projectIntro', 'profitRemark', 'riskPoint', 'emergencyPlan'].forEach(
|
||||||
|
|||||||
@@ -279,7 +279,7 @@
|
|||||||
class="voucher-manage-page__upload-progress"
|
class="voucher-manage-page__upload-progress"
|
||||||
/></el-form-item>
|
/></el-form-item>
|
||||||
<el-form-item label="关联运输批次" prop="waybillImportBatchIds"
|
<el-form-item label="关联运输批次" prop="waybillImportBatchIds"
|
||||||
><el-button type="primary" @click="openBatchDialog">选择</el-button></el-form-item
|
><el-button type="primary" @click="openBatchDialog()">选择</el-button></el-form-item
|
||||||
>
|
>
|
||||||
</el-form>
|
</el-form>
|
||||||
</section-card>
|
</section-card>
|
||||||
@@ -886,13 +886,15 @@ watch(uploadVisible, visible => {
|
|||||||
if (!visible && !closingUploadDialog.value) void stopCurrentUpload(editing.fileTaskId);
|
if (!visible && !closingUploadDialog.value) void stopCurrentUpload(editing.fileTaskId);
|
||||||
});
|
});
|
||||||
const openBatchDialog = async row => {
|
const openBatchDialog = async row => {
|
||||||
batchDialogMode.value = row ? 'change' : 'upload';
|
// 按钮 @click 会传入 MouseEvent,不能把事件对象当成凭证行。
|
||||||
batchTarget.value = row;
|
const voucher = row instanceof Event ? undefined : row;
|
||||||
|
batchDialogMode.value = voucher?.id ? 'change' : 'upload';
|
||||||
|
batchTarget.value = voucher?.id ? voucher : undefined;
|
||||||
batchSelection.value = [];
|
batchSelection.value = [];
|
||||||
batchVisible.value = true;
|
batchVisible.value = true;
|
||||||
await loadBatches();
|
await loadBatches();
|
||||||
if (row?.waybillBatchNo) {
|
if (voucher?.waybillBatchNo) {
|
||||||
const currentBatch = batchRows.value.find(item => item.batchNo === row.waybillBatchNo);
|
const currentBatch = batchRows.value.find(item => item.batchNo === voucher.waybillBatchNo);
|
||||||
if (currentBatch) batchSelection.value = [currentBatch];
|
if (currentBatch) batchSelection.value = [currentBatch];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -565,7 +565,7 @@
|
|||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="合同类别">
|
<el-form-item label="合同类别">
|
||||||
<el-select v-model="contractDialog.query.contractCategory" clearable placeholder="请选择">
|
<el-select :model-value="contractDialogCategory" disabled placeholder="请选择">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in contractCategoryOptions"
|
v-for="item in contractCategoryOptions"
|
||||||
:key="item.value"
|
:key="item.value"
|
||||||
@@ -1027,7 +1027,7 @@ export default {
|
|||||||
contractName: '',
|
contractName: '',
|
||||||
projectName: '',
|
projectName: '',
|
||||||
organizationName: '',
|
organizationName: '',
|
||||||
contractCategory: '',
|
contractCategory: '承运商合同',
|
||||||
signType: '',
|
signType: '',
|
||||||
effectiveType: '',
|
effectiveType: '',
|
||||||
contractStage: '',
|
contractStage: '',
|
||||||
@@ -1161,6 +1161,9 @@ export default {
|
|||||||
settlementTypeName() {
|
settlementTypeName() {
|
||||||
return this.form.settlementType === 'receivable' ? '应收' : '应付';
|
return this.form.settlementType === 'receivable' ? '应收' : '应付';
|
||||||
},
|
},
|
||||||
|
contractDialogCategory() {
|
||||||
|
return '承运商合同';
|
||||||
|
},
|
||||||
summaryTotal() {
|
summaryTotal() {
|
||||||
// 与结算合计表格的合计行保持一致:包含手工添加的费用与明细调整后的金额。
|
// 与结算合计表格的合计行保持一致:包含手工添加的费用与明细调整后的金额。
|
||||||
if (this.summaryFees.length) {
|
if (this.summaryFees.length) {
|
||||||
@@ -1257,8 +1260,8 @@ export default {
|
|||||||
deptId: first.deptId,
|
deptId: first.deptId,
|
||||||
deptName: first.deptName,
|
deptName: first.deptName,
|
||||||
settlementType,
|
settlementType,
|
||||||
partyA: settlementType === 'receivable' ? first.payeeName : first.payerName,
|
partyA: first.payerName,
|
||||||
partyB: settlementType === 'receivable' ? first.payerName : first.payeeName,
|
partyB: first.payeeName,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.handleContractChange(contractId);
|
this.handleContractChange(contractId);
|
||||||
@@ -1415,7 +1418,10 @@ export default {
|
|||||||
const { data } = await getContractList(
|
const { data } = await getContractList(
|
||||||
this.contractDialog.page.current,
|
this.contractDialog.page.current,
|
||||||
this.contractDialog.page.size,
|
this.contractDialog.page.size,
|
||||||
{ ...this.contractDialog.query }
|
{
|
||||||
|
...this.contractDialog.query,
|
||||||
|
contractCategory: this.contractDialogCategory,
|
||||||
|
}
|
||||||
);
|
);
|
||||||
const page = data?.data || {};
|
const page = data?.data || {};
|
||||||
this.contractDialog.rows = page.records || [];
|
this.contractDialog.rows = page.records || [];
|
||||||
@@ -1434,7 +1440,7 @@ export default {
|
|||||||
contractName: '',
|
contractName: '',
|
||||||
projectName: '',
|
projectName: '',
|
||||||
organizationName: '',
|
organizationName: '',
|
||||||
contractCategory: '',
|
contractCategory: this.contractDialogCategory,
|
||||||
signType: '',
|
signType: '',
|
||||||
effectiveType: '',
|
effectiveType: '',
|
||||||
contractStage: '',
|
contractStage: '',
|
||||||
@@ -1519,13 +1525,8 @@ export default {
|
|||||||
this.form.deptId = contract.deptId;
|
this.form.deptId = contract.deptId;
|
||||||
this.form.deptName = contract.deptName;
|
this.form.deptName = contract.deptName;
|
||||||
this.form.settlementType = contract.settlementType || 'payable';
|
this.form.settlementType = contract.settlementType || 'payable';
|
||||||
if (this.form.settlementType === 'receivable') {
|
this.form.payerName = contract.partyA;
|
||||||
this.form.payerName = contract.partyB;
|
this.form.payeeName = contract.partyB;
|
||||||
this.form.payeeName = contract.partyA;
|
|
||||||
} else {
|
|
||||||
this.form.payerName = contract.partyA;
|
|
||||||
this.form.payeeName = contract.partyB;
|
|
||||||
}
|
|
||||||
this.summaryFees = [];
|
this.summaryFees = [];
|
||||||
},
|
},
|
||||||
async saveDraft(shouldSubmit) {
|
async saveDraft(shouldSubmit) {
|
||||||
|
|||||||
@@ -205,10 +205,18 @@
|
|||||||
<section-card title="导入外部账单">
|
<section-card title="导入外部账单">
|
||||||
<template #extra>
|
<template #extra>
|
||||||
<div class="reconciliation-editor__actions" v-if="editable">
|
<div class="reconciliation-editor__actions" v-if="editable">
|
||||||
<el-button type="primary" plain @click="downloadTemplate('vehicle')"
|
<el-button
|
||||||
|
v-if="form.reconciliationMode === 'vehicle'"
|
||||||
|
type="primary"
|
||||||
|
plain
|
||||||
|
@click="downloadTemplate('vehicle')"
|
||||||
>下载整车对账模板</el-button
|
>下载整车对账模板</el-button
|
||||||
>
|
>
|
||||||
<el-button type="primary" plain @click="downloadTemplate('cargo')"
|
<el-button
|
||||||
|
v-if="form.reconciliationMode === 'cargo'"
|
||||||
|
type="primary"
|
||||||
|
plain
|
||||||
|
@click="downloadTemplate('cargo')"
|
||||||
>下载货物明细对账模板</el-button
|
>下载货物明细对账模板</el-button
|
||||||
>
|
>
|
||||||
<el-button type="primary" plain @click="chooseImport">导入</el-button>
|
<el-button type="primary" plain @click="chooseImport">导入</el-button>
|
||||||
@@ -374,6 +382,11 @@
|
|||||||
formatMoney(row.settlementAmount, row.currency)
|
formatMoney(row.settlementAmount, row.currency)
|
||||||
}}</template></el-table-column
|
}}</template></el-table-column
|
||||||
>
|
>
|
||||||
|
<el-table-column label="审批状态" width="110"
|
||||||
|
><template #default="{ row }">{{
|
||||||
|
formalApprovalStatusLabel(row.approvalStatus)
|
||||||
|
}}</template></el-table-column
|
||||||
|
>
|
||||||
<el-table-column label="操作" width="100" fixed="right" align="center">
|
<el-table-column label="操作" width="100" fixed="right" align="center">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-link type="primary" @click="selectFormal(row)">选择</el-link>
|
<el-link type="primary" @click="selectFormal(row)">选择</el-link>
|
||||||
@@ -1081,6 +1094,19 @@ export default {
|
|||||||
getFeeItemAmount(row, name) {
|
getFeeItemAmount(row, name) {
|
||||||
return (row.feeItems || this.parseFeeItems(row.feeItemsJson))[name] ?? 0;
|
return (row.feeItems || this.parseFeeItems(row.feeItemsJson))[name] ?? 0;
|
||||||
},
|
},
|
||||||
|
formalApprovalStatusLabel(value) {
|
||||||
|
return (
|
||||||
|
{
|
||||||
|
draft: '草稿',
|
||||||
|
reviewing: '审批中',
|
||||||
|
approved: '审批通过',
|
||||||
|
returned: '已驳回',
|
||||||
|
voided: '已作废',
|
||||||
|
}[value] ||
|
||||||
|
value ||
|
||||||
|
'-'
|
||||||
|
);
|
||||||
|
},
|
||||||
async loadFormalOptions() {
|
async loadFormalOptions() {
|
||||||
this.formalDialog.loading = true;
|
this.formalDialog.loading = true;
|
||||||
try {
|
try {
|
||||||
@@ -1096,20 +1122,8 @@ export default {
|
|||||||
params
|
params
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
let rows = data.records || [];
|
this.formalDialog.rows = data.records || [];
|
||||||
let total = data.total || 0;
|
this.formalDialog.page.total = data.total || 0;
|
||||||
if (!rows.length) {
|
|
||||||
const fallback = await formalSettlementApi.getList(
|
|
||||||
this.formalDialog.page.current,
|
|
||||||
this.formalDialog.page.size,
|
|
||||||
params
|
|
||||||
);
|
|
||||||
const fallbackData = this.unwrapData(fallback);
|
|
||||||
rows = fallbackData.records || [];
|
|
||||||
total = fallbackData.total || 0;
|
|
||||||
}
|
|
||||||
this.formalDialog.rows = rows;
|
|
||||||
this.formalDialog.page.total = total;
|
|
||||||
} finally {
|
} finally {
|
||||||
this.formalDialog.loading = false;
|
this.formalDialog.loading = false;
|
||||||
}
|
}
|
||||||
@@ -1120,6 +1134,9 @@ export default {
|
|||||||
await this.selectFormal(this.formalDialog.selected[0]);
|
await this.selectFormal(this.formalDialog.selected[0]);
|
||||||
},
|
},
|
||||||
async selectFormal(selected) {
|
async selectFormal(selected) {
|
||||||
|
if (selected?.approvalStatus && selected.approvalStatus !== 'draft') {
|
||||||
|
return this.$message.warning('只能选择草稿状态的正式结算单');
|
||||||
|
}
|
||||||
const formalSettlementChanged = this.form.formalSettlementId !== selected.id;
|
const formalSettlementChanged = this.form.formalSettlementId !== selected.id;
|
||||||
this.form = {
|
this.form = {
|
||||||
...this.form,
|
...this.form,
|
||||||
@@ -1489,7 +1506,14 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
async downloadTemplate(mode) {
|
async downloadTemplate(mode) {
|
||||||
const response = await api.template(mode);
|
if (!this.form.formalSettlementId) {
|
||||||
|
return this.$message.warning('请先选择正式结算单');
|
||||||
|
}
|
||||||
|
const response = await api.template(mode, {
|
||||||
|
id: this.currentId,
|
||||||
|
formalSettlementId: this.form.formalSettlementId,
|
||||||
|
feeItems: this.internalFeeItemNames.join(','),
|
||||||
|
});
|
||||||
downloadXls(
|
downloadXls(
|
||||||
response.data,
|
response.data,
|
||||||
`${mode === 'cargo' ? '货物明细对账模板' : '整车总额对账模板'}.xlsx`
|
`${mode === 'cargo' ? '货物明细对账模板' : '整车总额对账模板'}.xlsx`
|
||||||
|
|||||||
@@ -145,7 +145,9 @@
|
|||||||
:align="column.align || 'center'"
|
:align="column.align || 'center'"
|
||||||
show-overflow-tooltip
|
show-overflow-tooltip
|
||||||
>
|
>
|
||||||
<template #default="{ row }">{{ formatDetailCell(row, column.prop) }}</template>
|
<template #default="{ row }">
|
||||||
|
{{ formatFeeDetailCell(row, column.prop) }}
|
||||||
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
@@ -398,12 +400,14 @@
|
|||||||
maxlength="300"
|
maxlength="300"
|
||||||
placeholder="请输入调整原因"
|
placeholder="请输入调整原因"
|
||||||
/>
|
/>
|
||||||
<span v-else-if="column.prop === 'adjustAmountText'">
|
<span v-else-if="column.prop === 'originalAmountText'">
|
||||||
{{ fixedTwoDecimals(row.adjustAmount) }}
|
{{
|
||||||
</span>
|
Number(row.adjustAmount || 0) !== 0
|
||||||
<span v-else-if="column.prop === 'afterAmountText'">
|
? fixedTwoDecimals(row.afterAmount)
|
||||||
{{ fixedTwoDecimals(row.afterAmount) }}
|
: fixedTwoDecimals(row.originalAmount)
|
||||||
|
}}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span v-else>{{ formatDetailCell(row, column.prop) }}</span>
|
<span v-else>{{ formatDetailCell(row, column.prop) }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -1135,7 +1139,13 @@ export default {
|
|||||||
},
|
},
|
||||||
feeDetailColumns() {
|
feeDetailColumns() {
|
||||||
const baseColumns = feeDetailBaseColumns.filter(column => column.prop !== 'freightAmount');
|
const baseColumns = feeDetailBaseColumns.filter(column => column.prop !== 'freightAmount');
|
||||||
return [...baseColumns, ...this.dynamicFeeColumns, ...feeDetailTailColumns];
|
const hasAdjustment = this.feeRows.some(row => Number(row.adjustAmount || 0) !== 0);
|
||||||
|
const tailColumns = feeDetailTailColumns.map(col =>
|
||||||
|
col.prop === 'originalAmountText' && hasAdjustment
|
||||||
|
? { ...col, label: '调整后金额' }
|
||||||
|
: col
|
||||||
|
);
|
||||||
|
return [...baseColumns, ...this.dynamicFeeColumns, ...tailColumns];
|
||||||
},
|
},
|
||||||
displayTableColumns() {
|
displayTableColumns() {
|
||||||
const columns = this.tableColumns.filter(
|
const columns = this.tableColumns.filter(
|
||||||
@@ -1181,7 +1191,7 @@ export default {
|
|||||||
adjustFeeColumns() {
|
adjustFeeColumns() {
|
||||||
const baseColumns = feeDetailBaseColumns.filter(column => column.prop !== 'freightAmount');
|
const baseColumns = feeDetailBaseColumns.filter(column => column.prop !== 'freightAmount');
|
||||||
const tailColumns = feeDetailTailColumns.filter(column => column.prop !== 'remark');
|
const tailColumns = feeDetailTailColumns.filter(column => column.prop !== 'remark');
|
||||||
const afterAmountIndex = tailColumns.findIndex(column => column.prop === 'afterAmountText');
|
const afterAmountIndex = tailColumns.findIndex(column => column.prop === 'originalAmountText');
|
||||||
tailColumns.splice(afterAmountIndex + 1, 0, {
|
tailColumns.splice(afterAmountIndex + 1, 0, {
|
||||||
label: '调整原因',
|
label: '调整原因',
|
||||||
prop: 'changeReason',
|
prop: 'changeReason',
|
||||||
@@ -2596,6 +2606,22 @@ export default {
|
|||||||
}
|
}
|
||||||
return this.formatCell(row?.[prop]);
|
return this.formatCell(row?.[prop]);
|
||||||
},
|
},
|
||||||
|
formatFeeDetailCell(row, prop) {
|
||||||
|
if (prop !== 'originalAmountText') return this.formatDetailCell(row, prop);
|
||||||
|
|
||||||
|
const adjustAmount = Number(row?.adjustAmount || 0);
|
||||||
|
if (adjustAmount === 0) return this.formatDetailCell(row, prop);
|
||||||
|
|
||||||
|
const adjustedAmount = Number(
|
||||||
|
row?.afterAmount ?? row?.adjustedAmount ?? row?.settlementAmount
|
||||||
|
);
|
||||||
|
if (Number.isFinite(adjustedAmount)) return this.fixedTwoDecimals(adjustedAmount);
|
||||||
|
|
||||||
|
const originalAmount = Number(row?.originalAmount ?? row?.originalAmountText);
|
||||||
|
return Number.isFinite(originalAmount)
|
||||||
|
? this.fixedTwoDecimals(originalAmount + adjustAmount)
|
||||||
|
: this.formatCell(row?.afterAmountText);
|
||||||
|
},
|
||||||
formatCell(value) {
|
formatCell(value) {
|
||||||
return value === null || value === undefined || value === '' ? '-' : value;
|
return value === null || value === undefined || value === '' ? '-' : value;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -225,7 +225,11 @@
|
|||||||
v-model="driverForm.drivingVehicle"
|
v-model="driverForm.drivingVehicle"
|
||||||
maxlength="30"
|
maxlength="30"
|
||||||
clearable
|
clearable
|
||||||
placeholder="请输入或选择车辆"
|
readonly
|
||||||
|
placeholder="请选择车辆"
|
||||||
|
:disabled="readonly"
|
||||||
|
@clear="driverForm.drivingVehicle = ''"
|
||||||
|
@click="!readonly && openVehicleSelector()"
|
||||||
>
|
>
|
||||||
<template #append>
|
<template #append>
|
||||||
<el-tooltip content="选择车辆" placement="top">
|
<el-tooltip content="选择车辆" placement="top">
|
||||||
@@ -582,8 +586,8 @@
|
|||||||
<template #organizationName="{ row }">
|
<template #organizationName="{ row }">
|
||||||
{{ String(row.organizationName || '').replace(/^[\s\u3000]+/, '') }}
|
{{ String(row.organizationName || '').replace(/^[\s\u3000]+/, '') }}
|
||||||
</template>
|
</template>
|
||||||
<template #boundDriver>
|
<template #boundDriver="{ row }">
|
||||||
<span>-</span>
|
<span>{{ row.boundDriver || '-' }}</span>
|
||||||
</template>
|
</template>
|
||||||
<template #certificationStatus="{ row }">
|
<template #certificationStatus="{ row }">
|
||||||
<el-tag :type="getVehicleCertificationStatus(row).type" class="status-text">
|
<el-tag :type="getVehicleCertificationStatus(row).type" class="status-text">
|
||||||
|
|||||||
@@ -66,8 +66,8 @@
|
|||||||
{{ row.status === 1 ? '启用' : '停用' }}
|
{{ row.status === 1 ? '启用' : '停用' }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
</template>
|
</template>
|
||||||
<template #boundDriver>
|
<template #boundDriver="{ row }">
|
||||||
<span>-</span>
|
<span>{{ row.boundDriver || '-' }}</span>
|
||||||
</template>
|
</template>
|
||||||
<template #certificationStatus="{ row }">
|
<template #certificationStatus="{ row }">
|
||||||
<el-tag :type="getCertificationStatus(row).type" class="status-text">
|
<el-tag :type="getCertificationStatus(row).type" class="status-text">
|
||||||
@@ -541,19 +541,14 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<el-form-item label="使用部门" prop="useDepartment">
|
<el-form-item label="使用部门" prop="useDepartment">
|
||||||
<el-select
|
<el-cascader
|
||||||
v-model="vehicleForm.useDepartment"
|
v-model="useDepartmentCascaderValue"
|
||||||
|
:options="organizationTreeOptions"
|
||||||
|
:props="organizationCascaderProps"
|
||||||
filterable
|
filterable
|
||||||
clearable
|
clearable
|
||||||
placeholder="请选择"
|
placeholder="请选择"
|
||||||
>
|
/>
|
||||||
<el-option
|
|
||||||
v-for="item in organizationOptions"
|
|
||||||
:key="item.id"
|
|
||||||
:label="item.rawLabel"
|
|
||||||
:value="item.rawLabel"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
@@ -908,6 +903,15 @@ export default {
|
|||||||
this.vehicleForm.organizationName = this.resolveOrgName(path);
|
this.vehicleForm.organizationName = this.resolveOrgName(path);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// 使用部门级联:与所属组织同一套部门树
|
||||||
|
useDepartmentCascaderValue: {
|
||||||
|
get() {
|
||||||
|
return this.resolveOrgPath(this.vehicleForm.useDepartment);
|
||||||
|
},
|
||||||
|
set(path) {
|
||||||
|
this.vehicleForm.useDepartment = this.resolveOrgName(path);
|
||||||
|
},
|
||||||
|
},
|
||||||
isAdmin() {
|
isAdmin() {
|
||||||
const authority = this.userInfo.authority || '';
|
const authority = this.userInfo.authority || '';
|
||||||
return authority.includes('admin');
|
return authority.includes('admin');
|
||||||
|
|||||||
@@ -57,6 +57,36 @@
|
|||||||
{{ accessTypeMap[row.accessType] || '临时客商' }}
|
{{ accessTypeMap[row.accessType] || '临时客商' }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
</template>
|
</template>
|
||||||
|
<template #businessEndDate="{ row }">
|
||||||
|
<span :class="{ 'business-license-expiring': isBusinessLicenseExpiringSoon(row) }">
|
||||||
|
{{ formatBusinessLicenseValidPeriod(row) }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<template #fundUseRate="{ row }">
|
||||||
|
{{ formatFundUseRate(row) }}
|
||||||
|
</template>
|
||||||
|
<template #fundUseRisk="{ row }">
|
||||||
|
<el-tag v-if="row.fundUseRisk === 'high'" type="danger">
|
||||||
|
{{ row.fundUseRiskName || '高风险' }}
|
||||||
|
</el-tag>
|
||||||
|
<el-tag v-else-if="row.fundUseRisk === 'medium'" type="warning">
|
||||||
|
{{ row.fundUseRiskName || '中风险' }}
|
||||||
|
</el-tag>
|
||||||
|
<span v-else class="fund-use-risk-none">-</span>
|
||||||
|
</template>
|
||||||
|
<template #fundUseRisk-header>
|
||||||
|
<span class="fund-use-risk-header">
|
||||||
|
资金使用额度风险
|
||||||
|
<el-tooltip placement="top" effect="dark">
|
||||||
|
<template #content>
|
||||||
|
<div>中风险:80% ≤ 客户资金额度使用率 < 90%</div>
|
||||||
|
<div>高风险:客户资金额度使用率 ≥ 90%</div>
|
||||||
|
<div>使用率 = (累计付款金额 - 累计收票金额) / 最大资金使用额度 × 100%</div>
|
||||||
|
</template>
|
||||||
|
<i class="el-icon-question fund-use-risk-help" />
|
||||||
|
</el-tooltip>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
<template #approvalStatus="{ row }">
|
<template #approvalStatus="{ row }">
|
||||||
<el-tag
|
<el-tag
|
||||||
class="status-text"
|
class="status-text"
|
||||||
@@ -176,11 +206,29 @@
|
|||||||
<el-form-item label="客商编号" prop="customerCode">
|
<el-form-item label="客商编号" prop="customerCode">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="archiveForm.customerCode"
|
v-model="archiveForm.customerCode"
|
||||||
disabled
|
maxlength="20"
|
||||||
placeholder="保存后自动生成"
|
show-word-limit
|
||||||
|
placeholder="请输入,留空则自动生成"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-form-item label="客商类型" prop="customerKind">
|
||||||
|
<el-select
|
||||||
|
v-model="archiveForm.customerKind"
|
||||||
|
placeholder="请选择"
|
||||||
|
filterable
|
||||||
|
clearable
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in customerKindOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<el-form-item label="客商性质" prop="customerNature">
|
<el-form-item label="客商性质" prop="customerNature">
|
||||||
<el-select
|
<el-select
|
||||||
@@ -241,23 +289,6 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
|
||||||
<el-form-item label="客商类型" prop="customerKind">
|
|
||||||
<el-select
|
|
||||||
v-model="archiveForm.customerKind"
|
|
||||||
placeholder="请选择"
|
|
||||||
filterable
|
|
||||||
clearable
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="item in customerKindOptions"
|
|
||||||
:key="item.value"
|
|
||||||
:label="item.label"
|
|
||||||
:value="item.value"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<el-form-item label="企业法人" prop="legalPerson">
|
<el-form-item label="企业法人" prop="legalPerson">
|
||||||
<el-input v-model="archiveForm.legalPerson" maxlength="10" show-word-limit />
|
<el-input v-model="archiveForm.legalPerson" maxlength="10" show-word-limit />
|
||||||
@@ -1676,6 +1707,7 @@ export default {
|
|||||||
rejected: { label: '审核不通过', type: 'danger' },
|
rejected: { label: '审核不通过', type: 'danger' },
|
||||||
},
|
},
|
||||||
formRules: {
|
formRules: {
|
||||||
|
customerCode: [{ max: 20, message: '客商编号最多20个字符', trigger: 'blur' }],
|
||||||
fullName: [{ required: true, message: '请输入客商名称', trigger: 'blur' }],
|
fullName: [{ required: true, message: '请输入客商名称', trigger: 'blur' }],
|
||||||
customerNature: [{ required: true, message: '请选择客商性质', trigger: 'change' }],
|
customerNature: [{ required: true, message: '请选择客商性质', trigger: 'change' }],
|
||||||
unifiedCreditCode: [
|
unifiedCreditCode: [
|
||||||
@@ -1856,6 +1888,24 @@ export default {
|
|||||||
{ label: '正式', value: 'formal' },
|
{ label: '正式', value: 'formal' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: '营业执照有效期',
|
||||||
|
prop: 'businessEndDate',
|
||||||
|
slot: true,
|
||||||
|
minWidth: 140,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '资金使用情况',
|
||||||
|
prop: 'fundUseRate',
|
||||||
|
slot: true,
|
||||||
|
minWidth: 130,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '资金使用额度风险',
|
||||||
|
prop: 'fundUseRisk',
|
||||||
|
slot: true,
|
||||||
|
minWidth: 150,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: '审批状态',
|
label: '审批状态',
|
||||||
prop: 'approvalStatus',
|
prop: 'approvalStatus',
|
||||||
@@ -2098,6 +2148,7 @@ export default {
|
|||||||
accessType: 'temporary',
|
accessType: 'temporary',
|
||||||
approvalStatus: 'draft',
|
approvalStatus: 'draft',
|
||||||
status: 1,
|
status: 1,
|
||||||
|
customerKind: 'external',
|
||||||
deptId: [],
|
deptId: [],
|
||||||
deptIds: '',
|
deptIds: '',
|
||||||
deptName: '',
|
deptName: '',
|
||||||
@@ -2268,6 +2319,40 @@ export default {
|
|||||||
if (typeof normalized === 'object') return Object.keys(normalized).length === 0;
|
if (typeof normalized === 'object') return Object.keys(normalized).length === 0;
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
normalizeComparableChangeValue(value) {
|
||||||
|
const normalized = this.normalizeChangeFieldValue(value);
|
||||||
|
if (this.isEmptyChangeValue(normalized)) return null;
|
||||||
|
if (typeof normalized === 'number') return Number.isFinite(normalized) ? normalized : null;
|
||||||
|
if (typeof normalized === 'boolean') return normalized;
|
||||||
|
if (typeof normalized === 'string') {
|
||||||
|
const text = normalized.trim();
|
||||||
|
if (/^-?\d+(\.\d+)?$/.test(text)) {
|
||||||
|
const number = Number(text);
|
||||||
|
return Number.isFinite(number) ? number : text;
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
if (Array.isArray(normalized)) {
|
||||||
|
return normalized.map(item => this.normalizeComparableChangeValue(item));
|
||||||
|
}
|
||||||
|
if (normalized && typeof normalized === 'object') {
|
||||||
|
return Object.keys(normalized)
|
||||||
|
.sort()
|
||||||
|
.reduce((result, key) => {
|
||||||
|
result[key] = this.normalizeComparableChangeValue(normalized[key]);
|
||||||
|
return result;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
return normalized;
|
||||||
|
},
|
||||||
|
isSameChangeValue(beforeValue, afterValue) {
|
||||||
|
if (beforeValue === afterValue) return true;
|
||||||
|
if (this.isEmptyChangeValue(beforeValue) && this.isEmptyChangeValue(afterValue)) return true;
|
||||||
|
return (
|
||||||
|
JSON.stringify(this.normalizeComparableChangeValue(beforeValue)) ===
|
||||||
|
JSON.stringify(this.normalizeComparableChangeValue(afterValue))
|
||||||
|
);
|
||||||
|
},
|
||||||
formatScoreChangeValue(value) {
|
formatScoreChangeValue(value) {
|
||||||
const scores = Array.isArray(value) ? value : [value];
|
const scores = Array.isArray(value) ? value : [value];
|
||||||
const categoryNames = { basic: '基础得分项', plus: '加分项目', minus: '减分项目' };
|
const categoryNames = { basic: '基础得分项', plus: '加分项目', minus: '减分项目' };
|
||||||
@@ -2354,7 +2439,7 @@ export default {
|
|||||||
fields.forEach(([field, label]) => {
|
fields.forEach(([field, label]) => {
|
||||||
const leftValue = left[field];
|
const leftValue = left[field];
|
||||||
const rightValue = right[field];
|
const rightValue = right[field];
|
||||||
if (JSON.stringify(leftValue) === JSON.stringify(rightValue)) return;
|
if (this.isSameChangeValue(leftValue, rightValue)) return;
|
||||||
const prefix = size > 1 ? `第${index + 1}条-${label}` : label;
|
const prefix = size > 1 ? `第${index + 1}条-${label}` : label;
|
||||||
before.push(`${prefix}:${formatValue(field, leftValue)}`);
|
before.push(`${prefix}:${formatValue(field, leftValue)}`);
|
||||||
after.push(`${prefix}:${formatValue(field, rightValue)}`);
|
after.push(`${prefix}:${formatValue(field, rightValue)}`);
|
||||||
@@ -2437,11 +2522,7 @@ export default {
|
|||||||
const rows = fields
|
const rows = fields
|
||||||
.filter(
|
.filter(
|
||||||
field =>
|
field =>
|
||||||
field !== '变更内容' &&
|
field !== '变更内容' && !this.isSameChangeValue(beforeData[field], afterData[field])
|
||||||
!(
|
|
||||||
this.isEmptyChangeValue(beforeData[field]) &&
|
|
||||||
this.isEmptyChangeValue(afterData[field])
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
.map(field => ({
|
.map(field => ({
|
||||||
field: this.getChangeFieldLabel(field),
|
field: this.getChangeFieldLabel(field),
|
||||||
@@ -2451,7 +2532,8 @@ export default {
|
|||||||
before: this.formatChangeFieldValue(field, beforeData[field]),
|
before: this.formatChangeFieldValue(field, beforeData[field]),
|
||||||
after: this.formatChangeFieldValue(field, afterData[field]),
|
after: this.formatChangeFieldValue(field, afterData[field]),
|
||||||
}),
|
}),
|
||||||
}));
|
}))
|
||||||
|
.filter(item => item.before !== item.after);
|
||||||
const content = String(row.changeContent || '').trim();
|
const content = String(row.changeContent || '').trim();
|
||||||
if (content) {
|
if (content) {
|
||||||
rows.unshift({ field: '变更内容', before: '空', after: content });
|
rows.unshift({ field: '变更内容', before: '空', after: content });
|
||||||
@@ -2467,27 +2549,11 @@ export default {
|
|||||||
this.changeRecordDetailVisible = true;
|
this.changeRecordDetailVisible = true;
|
||||||
},
|
},
|
||||||
formatChangeContent(row) {
|
formatChangeContent(row) {
|
||||||
const beforeData = this.parseChangeData(row.beforeData);
|
const rows = this.buildChangeRecordDetailRows(row);
|
||||||
const afterData = this.parseChangeData(row.afterData);
|
|
||||||
const fields = [...new Set([...Object.keys(beforeData), ...Object.keys(afterData)])];
|
|
||||||
const details = fields
|
|
||||||
.filter(
|
|
||||||
field =>
|
|
||||||
field !== '变更内容' &&
|
|
||||||
!(
|
|
||||||
this.isEmptyChangeValue(beforeData[field]) &&
|
|
||||||
this.isEmptyChangeValue(afterData[field])
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.map(field => {
|
|
||||||
const scoreDiff = this.isScoreChangeField(field)
|
|
||||||
? this.formatScoreChangeDiff(beforeData[field], afterData[field])
|
|
||||||
: null;
|
|
||||||
const before = scoreDiff?.before || this.formatChangeFieldValue(field, beforeData[field]);
|
|
||||||
const after = scoreDiff?.after || this.formatChangeFieldValue(field, afterData[field]);
|
|
||||||
return `${this.getChangeFieldLabel(field)}:变更前:${before} → 变更后:${after}`;
|
|
||||||
});
|
|
||||||
const content = String(row.changeContent || '').trim();
|
const content = String(row.changeContent || '').trim();
|
||||||
|
const details = rows
|
||||||
|
.filter(item => item.field !== '变更内容')
|
||||||
|
.map(item => `${item.field}:变更前:${item.before} → 变更后:${item.after}`);
|
||||||
return [content, ...details].filter(Boolean).join(';');
|
return [content, ...details].filter(Boolean).join(';');
|
||||||
},
|
},
|
||||||
emptyContact() {
|
emptyContact() {
|
||||||
@@ -2833,6 +2899,25 @@ export default {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
formatBusinessLicenseValidPeriod(row = {}) {
|
||||||
|
if (row.businessTermType === '长期') return '无固定期限';
|
||||||
|
return row.businessEndDate || '';
|
||||||
|
},
|
||||||
|
formatFundUseRate(row = {}) {
|
||||||
|
if (row.fundUseRate === undefined || row.fundUseRate === null || row.fundUseRate === '') {
|
||||||
|
return '-';
|
||||||
|
}
|
||||||
|
const rate = Number(row.fundUseRate);
|
||||||
|
return Number.isFinite(rate) ? `${rate.toFixed(2)}%` : '-';
|
||||||
|
},
|
||||||
|
isBusinessLicenseExpiringSoon(row = {}) {
|
||||||
|
if (row.businessTermType === '长期' || !row.businessEndDate) return false;
|
||||||
|
const endDate = this.$dayjs(row.businessEndDate);
|
||||||
|
if (!endDate.isValid()) return false;
|
||||||
|
const today = this.$dayjs().startOf('day');
|
||||||
|
const expireSoonDate = today.add(30, 'day');
|
||||||
|
return !endDate.isAfter(expireSoonDate, 'day');
|
||||||
|
},
|
||||||
handleBusinessEndDateChange(value) {
|
handleBusinessEndDateChange(value) {
|
||||||
if (value) {
|
if (value) {
|
||||||
this.archiveForm.businessTermType = '固定期限';
|
this.archiveForm.businessTermType = '固定期限';
|
||||||
@@ -4737,6 +4822,26 @@ export default {
|
|||||||
--el-tag-text-color: #409eff;
|
--el-tag-text-color: #409eff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.business-license-expiring {
|
||||||
|
color: var(--el-color-danger);
|
||||||
|
}
|
||||||
|
|
||||||
|
.fund-use-risk-none {
|
||||||
|
color: #c0c4cc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fund-use-risk-header {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fund-use-risk-help {
|
||||||
|
margin-left: 4px;
|
||||||
|
color: #a8abb2;
|
||||||
|
cursor: help;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
.archive-form {
|
.archive-form {
|
||||||
:deep(.el-form-item__label) {
|
:deep(.el-form-item__label) {
|
||||||
flex: 0 0 calc(6em + 24px) !important;
|
flex: 0 0 calc(6em + 24px) !important;
|
||||||
|
|||||||
Reference in New Issue
Block a user