fix bug
This commit is contained in:
@@ -190,7 +190,9 @@ export default {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
const validateIcaoCode = (rule, value, callback) => {
|
const validateIcaoCode = (rule, value, callback) => {
|
||||||
if (!/^[A-Z]{4}$/.test(String(value || '').toUpperCase())) {
|
if (!value) {
|
||||||
|
callback();
|
||||||
|
} else if (!/^[A-Z]{4}$/.test(String(value).toUpperCase())) {
|
||||||
callback(new Error('ICAO代码为4位大写字母'));
|
callback(new Error('ICAO代码为4位大写字母'));
|
||||||
} else {
|
} else {
|
||||||
callback();
|
callback();
|
||||||
@@ -281,10 +283,7 @@ export default {
|
|||||||
prop: 'icaoCode',
|
prop: 'icaoCode',
|
||||||
minWidth: 100,
|
minWidth: 100,
|
||||||
maxlength: 4,
|
maxlength: 4,
|
||||||
rules: [
|
rules: [{ validator: validateIcaoCode, trigger: 'blur' }],
|
||||||
{ required: true, message: '请输入ICAO代码', trigger: 'blur' },
|
|
||||||
{ validator: validateIcaoCode, trigger: 'blur' },
|
|
||||||
],
|
|
||||||
change: ({ value }) => this.handleIcaoChange(value),
|
change: ({ value }) => this.handleIcaoChange(value),
|
||||||
blur: ({ value }) => this.handleIcaoChange(value),
|
blur: ({ value }) => this.handleIcaoChange(value),
|
||||||
},
|
},
|
||||||
@@ -376,7 +375,10 @@ export default {
|
|||||||
overHidden: false,
|
overHidden: false,
|
||||||
maxlength: 255,
|
maxlength: 255,
|
||||||
showWordLimit: true,
|
showWordLimit: true,
|
||||||
rules: [{ max: 255, message: '详细地址不能超过255字', trigger: 'blur' }],
|
rules: [
|
||||||
|
{ required: true, message: '请输入详细地址', trigger: 'blur' },
|
||||||
|
{ max: 255, message: '详细地址不能超过255字', trigger: 'blur' },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '经度',
|
label: '经度',
|
||||||
@@ -674,6 +676,7 @@ export default {
|
|||||||
row.districtCode = String(row.districtCode || '').trim();
|
row.districtCode = String(row.districtCode || '').trim();
|
||||||
row.districtName = String(row.districtName || '').trim();
|
row.districtName = String(row.districtName || '').trim();
|
||||||
row.detailAddress = String(row.detailAddress || '').trim();
|
row.detailAddress = String(row.detailAddress || '').trim();
|
||||||
|
row.icaoCode = String(row.icaoCode || '').trim() || undefined;
|
||||||
row.longitude = normalizeCoordinateInput(row.longitude);
|
row.longitude = normalizeCoordinateInput(row.longitude);
|
||||||
row.latitude = normalizeCoordinateInput(row.latitude);
|
row.latitude = normalizeCoordinateInput(row.latitude);
|
||||||
if (!row.dataSource) row.dataSource = '手动录入';
|
if (!row.dataSource) row.dataSource = '手动录入';
|
||||||
@@ -696,6 +699,9 @@ export default {
|
|||||||
validateUnique(row) {
|
validateUnique(row) {
|
||||||
const rowId = String(row.id || '');
|
const rowId = String(row.id || '');
|
||||||
const hasDuplicate = (params, prop) => {
|
const hasDuplicate = (params, prop) => {
|
||||||
|
if (!row[prop]) {
|
||||||
|
return Promise.resolve(false);
|
||||||
|
}
|
||||||
return getList(1, 10, params).then(res => {
|
return getList(1, 10, params).then(res => {
|
||||||
const records = res.data.data.records || [];
|
const records = res.data.data.records || [];
|
||||||
return records.some(item => item[prop] === row[prop] && String(item.id || '') !== rowId);
|
return records.some(item => item[prop] === row[prop] && String(item.id || '') !== rowId);
|
||||||
|
|||||||
@@ -213,9 +213,9 @@ export const decorateCargoTypeFailDetail = async workbook => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
reasonCell.value = decorated.join(';');
|
reasonCell.value = decorated.join(';');
|
||||||
// 仅操作 G 列:黑色字体、无背景填充,其余列保持后端原样
|
// 仅失败原因列使用红色字体,其余列保持普通字体
|
||||||
reasonCell.fill = { type: 'pattern', pattern: 'none' };
|
reasonCell.fill = { type: 'pattern', pattern: 'none' };
|
||||||
reasonCell.font = { color: { argb: 'FF000000' } };
|
reasonCell.font = { color: { argb: 'FFFF0000' } };
|
||||||
reasonCell.alignment = { wrapText: true, vertical: 'top' };
|
reasonCell.alignment = { wrapText: true, vertical: 'top' };
|
||||||
});
|
});
|
||||||
// 加宽各列,避免内容拥挤(仅改列宽,不改动单元格样式)
|
// 加宽各列,避免内容拥挤(仅改列宽,不改动单元格样式)
|
||||||
|
|||||||
+14
-60
@@ -21,12 +21,6 @@
|
|||||||
@on-load="onLoad"
|
@on-load="onLoad"
|
||||||
>
|
>
|
||||||
<template #menu-left>
|
<template #menu-left>
|
||||||
<el-button type="primary" plain v-if="permission.fee_item_import" @click="handleImport">
|
|
||||||
<el-icon class="el-icon--left"><Upload /></el-icon>批量导入
|
|
||||||
</el-button>
|
|
||||||
<el-button type="primary" plain v-if="permission.fee_item_template" @click="handleTemplate">
|
|
||||||
<el-icon class="el-icon--left"><Download /></el-icon>下载模板
|
|
||||||
</el-button>
|
|
||||||
<el-button type="primary" plain v-if="permission.fee_item_export" @click="handleExport">
|
<el-button type="primary" plain v-if="permission.fee_item_export" @click="handleExport">
|
||||||
<el-icon class="el-icon--left"><Download /></el-icon>批量导出
|
<el-icon class="el-icon--left"><Download /></el-icon>批量导出
|
||||||
</el-button>
|
</el-button>
|
||||||
@@ -80,16 +74,6 @@
|
|||||||
</el-link>
|
</el-link>
|
||||||
</template>
|
</template>
|
||||||
</avue-crud>
|
</avue-crud>
|
||||||
|
|
||||||
<el-dialog title="费用项导入" append-to-body v-model="excelBox" width="555px">
|
|
||||||
<avue-form :option="excelOption" v-model="excelForm">
|
|
||||||
<template #excelTemplate>
|
|
||||||
<el-button type="primary" @click="handleTemplate">
|
|
||||||
点击下载<i class="el-icon-download el-icon--right"></i>
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</avue-form>
|
|
||||||
</el-dialog>
|
|
||||||
</basic-container>
|
</basic-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -99,24 +83,20 @@ import { exportBlob } from '@/api/common';
|
|||||||
import { getDictionary } from '@/api/system/dictbiz';
|
import { getDictionary } from '@/api/system/dictbiz';
|
||||||
import { getDeptTree } from '@/api/system/dept';
|
import { getDeptTree } from '@/api/system/dept';
|
||||||
import { downloadXls } from '@/utils/util';
|
import { downloadXls } from '@/utils/util';
|
||||||
import { openImportDialog } from '@/utils/import-excel';
|
|
||||||
import { getToken } from '@/utils/auth';
|
import { getToken } from '@/utils/auth';
|
||||||
import { getUploadHeaders } from '@/utils/upload';
|
import { Download } from '@element-plus/icons-vue';
|
||||||
import { Upload, Download } from '@element-plus/icons-vue';
|
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import NProgress from 'nprogress';
|
import NProgress from 'nprogress';
|
||||||
import 'nprogress/nprogress.css';
|
import 'nprogress/nprogress.css';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { Upload, Download },
|
components: { Download },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
form: {},
|
form: {},
|
||||||
query: {},
|
query: {},
|
||||||
loading: true,
|
loading: true,
|
||||||
data: [],
|
data: [],
|
||||||
excelBox: false,
|
|
||||||
excelForm: {},
|
|
||||||
dialogType: 'add',
|
dialogType: 'add',
|
||||||
feeCategoryOptions: [],
|
feeCategoryOptions: [],
|
||||||
page: {
|
page: {
|
||||||
@@ -184,6 +164,17 @@ export default {
|
|||||||
maxlength: 50,
|
maxlength: 50,
|
||||||
rules: [{ required: true, message: '请输入费用项', trigger: 'blur' }],
|
rules: [{ required: true, message: '请输入费用项', trigger: 'blur' }],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: '备注',
|
||||||
|
prop: 'remark',
|
||||||
|
type: 'textarea',
|
||||||
|
minRows: 2,
|
||||||
|
span: 24,
|
||||||
|
hide: true,
|
||||||
|
maxlength: 200,
|
||||||
|
showWordLimit: true,
|
||||||
|
rules: [{ max: 200, message: '备注不能超过200个字', trigger: 'blur' }],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: '组织',
|
label: '组织',
|
||||||
prop: 'createDeptName',
|
prop: 'createDeptName',
|
||||||
@@ -259,32 +250,6 @@ export default {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
excelOption: {
|
|
||||||
submitBtn: false,
|
|
||||||
emptyBtn: false,
|
|
||||||
column: [
|
|
||||||
{
|
|
||||||
label: '模板上传',
|
|
||||||
prop: 'excelFile',
|
|
||||||
type: 'upload',
|
|
||||||
drag: true,
|
|
||||||
loadText: '模板上传中,请稍等',
|
|
||||||
span: 24,
|
|
||||||
propsHttp: {
|
|
||||||
res: 'data',
|
|
||||||
},
|
|
||||||
headers: getUploadHeaders(),
|
|
||||||
tip: '请上传 .xls,.xlsx 标准格式文件',
|
|
||||||
action: '/blade-system/fee-item/import-fee-item',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '模板下载',
|
|
||||||
prop: 'excelTemplate',
|
|
||||||
formslot: true,
|
|
||||||
span: 24,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
@@ -364,6 +329,7 @@ export default {
|
|||||||
values.feeCategory = String(values.feeCategory || '').trim();
|
values.feeCategory = String(values.feeCategory || '').trim();
|
||||||
values.name = String(values.name || '').trim();
|
values.name = String(values.name || '').trim();
|
||||||
values.englishName = prefix ? `${prefix}${code}` : code;
|
values.englishName = prefix ? `${prefix}${code}` : code;
|
||||||
|
values.remark = String(values.remark || '').trim() || undefined;
|
||||||
if (!values.status) values.status = 1;
|
if (!values.status) values.status = 1;
|
||||||
return values;
|
return values;
|
||||||
},
|
},
|
||||||
@@ -464,9 +430,6 @@ export default {
|
|||||||
refreshChange() {
|
refreshChange() {
|
||||||
this.onLoad(this.page, this.query);
|
this.onLoad(this.page, this.query);
|
||||||
},
|
},
|
||||||
handleImport() {
|
|
||||||
openImportDialog(this, '费用项');
|
|
||||||
},
|
|
||||||
buildExportParams() {
|
buildExportParams() {
|
||||||
return {
|
return {
|
||||||
...this.query,
|
...this.query,
|
||||||
@@ -492,15 +455,6 @@ export default {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleTemplate() {
|
|
||||||
exportBlob(
|
|
||||||
`/blade-system/fee-item/export-template?${this.website.tokenHeader}=${getToken()}`,
|
|
||||||
undefined,
|
|
||||||
{ feedback: true }
|
|
||||||
).then(res => {
|
|
||||||
downloadXls(res.data, '费用项模板.xlsx');
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onLoad(page, params = {}) {
|
onLoad(page, params = {}) {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
getList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {
|
getList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {
|
||||||
|
|||||||
@@ -393,7 +393,7 @@ export default {
|
|||||||
dicData: [],
|
dicData: [],
|
||||||
hide: true,
|
hide: true,
|
||||||
span: 12,
|
span: 12,
|
||||||
order: 100,
|
order: 101,
|
||||||
placeholder: '请选择',
|
placeholder: '请选择',
|
||||||
rules: [{ required: false, message: '请选择上级港口', trigger: 'change' }],
|
rules: [{ required: false, message: '请选择上级港口', trigger: 'change' }],
|
||||||
change: ({ value }) => this.handleParentChange(value),
|
change: ({ value }) => this.handleParentChange(value),
|
||||||
@@ -412,10 +412,11 @@ export default {
|
|||||||
minWidth: 120,
|
minWidth: 120,
|
||||||
slot: true,
|
slot: true,
|
||||||
span: 12,
|
span: 12,
|
||||||
|
order: 101,
|
||||||
|
disabled: true,
|
||||||
addDisplay: false,
|
addDisplay: false,
|
||||||
editDisplay: false,
|
editDisplay: false,
|
||||||
placeholder: '自动带出',
|
placeholder: '自动带出',
|
||||||
rules: [{ required: false, message: '请选择上级港口', trigger: 'change' }],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '国家',
|
label: '国家',
|
||||||
@@ -537,7 +538,10 @@ export default {
|
|||||||
placeholder: '请输入',
|
placeholder: '请输入',
|
||||||
maxlength: 255,
|
maxlength: 255,
|
||||||
showWordLimit: true,
|
showWordLimit: true,
|
||||||
rules: [{ max: 255, message: '详细地址不能超过255字', trigger: 'blur' }],
|
rules: [
|
||||||
|
{ required: true, message: '请输入详细地址', trigger: 'blur' },
|
||||||
|
{ max: 255, message: '详细地址不能超过255字', trigger: 'blur' },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '数据来源',
|
label: '数据来源',
|
||||||
@@ -551,9 +555,9 @@ export default {
|
|||||||
editDisplay: false,
|
editDisplay: false,
|
||||||
dicData: [
|
dicData: [
|
||||||
{ label: '全部', value: '' },
|
{ label: '全部', value: '' },
|
||||||
{ label: '初始化导入', value: '初始化导入' },
|
{ label: '初始化录入', value: '初始化录入' },
|
||||||
{ label: '批量导入', value: '批量导入' },
|
{ label: '批量导入', value: '批量导入' },
|
||||||
{ label: '手工导入', value: '手工导入' },
|
{ label: '手动录入', value: '手动录入' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -761,6 +765,7 @@ export default {
|
|||||||
parentColumn.rules[0].required = required;
|
parentColumn.rules[0].required = required;
|
||||||
}
|
}
|
||||||
this.setColumnDisplay('parentId', required);
|
this.setColumnDisplay('parentId', required);
|
||||||
|
this.setColumnDisplay('parentCode', required);
|
||||||
if (value === '港口') {
|
if (value === '港口') {
|
||||||
this.form.parentId = undefined;
|
this.form.parentId = undefined;
|
||||||
this.form.parentCode = '';
|
this.form.parentCode = '';
|
||||||
@@ -992,7 +997,7 @@ export default {
|
|||||||
submitRow.parentCode = undefined;
|
submitRow.parentCode = undefined;
|
||||||
submitRow.parentName = undefined;
|
submitRow.parentName = undefined;
|
||||||
}
|
}
|
||||||
if (!submitRow.dataSource) submitRow.dataSource = '手工导入';
|
if (!submitRow.dataSource) submitRow.dataSource = '手动录入';
|
||||||
if (!submitRow.status) submitRow.status = 1;
|
if (!submitRow.status) submitRow.status = 1;
|
||||||
delete submitRow.terminalCode;
|
delete submitRow.terminalCode;
|
||||||
delete submitRow.cityCode;
|
delete submitRow.cityCode;
|
||||||
@@ -1021,6 +1026,10 @@ export default {
|
|||||||
this.$message.warning('请选择区县');
|
this.$message.warning('请选择区县');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (isBlank(row.detailAddress)) {
|
||||||
|
this.$message.warning('请输入详细地址');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (isBlank(row.longitude)) {
|
if (isBlank(row.longitude)) {
|
||||||
this.$message.warning('请输入经度');
|
this.$message.warning('请输入经度');
|
||||||
return false;
|
return false;
|
||||||
@@ -1133,7 +1142,7 @@ export default {
|
|||||||
beforeOpen(done, type) {
|
beforeOpen(done, type) {
|
||||||
if (type === 'add') {
|
if (type === 'add') {
|
||||||
this.form.category = this.form.category || '港口';
|
this.form.category = this.form.category || '港口';
|
||||||
this.form.dataSource = this.form.dataSource || '手工导入';
|
this.form.dataSource = this.form.dataSource || '手动录入';
|
||||||
this.form.status = this.form.status || 1;
|
this.form.status = this.form.status || 1;
|
||||||
}
|
}
|
||||||
this.handleCategoryChange(this.form.category || '港口');
|
this.handleCategoryChange(this.form.category || '港口');
|
||||||
|
|||||||
@@ -347,7 +347,10 @@ export default {
|
|||||||
overHidden: false,
|
overHidden: false,
|
||||||
maxlength: 255,
|
maxlength: 255,
|
||||||
showWordLimit: true,
|
showWordLimit: true,
|
||||||
rules: [{ max: 255, message: '详细地址不能超过255字', trigger: 'blur' }],
|
rules: [
|
||||||
|
{ required: true, message: '请输入详细地址', trigger: 'blur' },
|
||||||
|
{ max: 255, message: '详细地址不能超过255字', trigger: 'blur' },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '经度',
|
label: '经度',
|
||||||
|
|||||||
@@ -166,7 +166,7 @@
|
|||||||
class="common-route-page__address-dialog"
|
class="common-route-page__address-dialog"
|
||||||
@opened="loadAddressList"
|
@opened="loadAddressList"
|
||||||
>
|
>
|
||||||
<section-card title="查询条件">
|
<section-card>
|
||||||
<div class="common-route-page__address-search">
|
<div class="common-route-page__address-search">
|
||||||
<el-form :model="addressQuery" label-position="right" label-width="86px">
|
<el-form :model="addressQuery" label-position="right" label-width="86px">
|
||||||
<el-row :gutter="16">
|
<el-row :gutter="16">
|
||||||
@@ -222,9 +222,6 @@
|
|||||||
</el-row>
|
</el-row>
|
||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
</section-card>
|
|
||||||
|
|
||||||
<section-card title="常用地址">
|
|
||||||
<el-table
|
<el-table
|
||||||
border
|
border
|
||||||
:data="addressData"
|
:data="addressData"
|
||||||
|
|||||||
@@ -10,6 +10,9 @@
|
|||||||
<el-descriptions-item label="方案名称">{{
|
<el-descriptions-item label="方案名称">{{
|
||||||
displayValue(draft.planName)
|
displayValue(draft.planName)
|
||||||
}}</el-descriptions-item>
|
}}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="运输方式">{{
|
||||||
|
transportModeLabel(draft.transportMode)
|
||||||
|
}}</el-descriptions-item>
|
||||||
<el-descriptions-item label="默认方案">{{
|
<el-descriptions-item label="默认方案">{{
|
||||||
isDefaultPlan(draft) ? '是' : '否'
|
isDefaultPlan(draft) ? '是' : '否'
|
||||||
}}</el-descriptions-item>
|
}}</el-descriptions-item>
|
||||||
@@ -31,10 +34,27 @@
|
|||||||
maxlength="100"
|
maxlength="100"
|
||||||
:disabled="readonly" /></el-form-item
|
:disabled="readonly" /></el-form-item
|
||||||
></el-col>
|
></el-col>
|
||||||
|
<el-col :span="8"
|
||||||
|
><el-form-item label="运输方式" prop="transportMode"
|
||||||
|
><el-select
|
||||||
|
v-model="draft.transportMode"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
:loading="transportModeLoading"
|
||||||
|
placeholder="请选择运输方式"
|
||||||
|
@visible-change="visible => visible && ensureTransportModes()"
|
||||||
|
><el-option
|
||||||
|
v-for="item in transportModeOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value" /></el-select></el-form-item
|
||||||
|
></el-col>
|
||||||
<el-col :span="8"
|
<el-col :span="8"
|
||||||
><el-form-item label="默认方案"
|
><el-form-item label="默认方案"
|
||||||
><el-checkbox v-model="draft.defaultPlan" :disabled="readonly"
|
><el-checkbox v-model="draft.defaultPlan" :disabled="readonly">默认方案</el-checkbox
|
||||||
>默认方案</el-checkbox
|
><el-icon class="billing-plan-editor__default-tip" @click="showDefaultPlanTip"
|
||||||
|
><InfoFilled
|
||||||
|
/></el-icon>
|
||||||
></el-form-item
|
></el-form-item
|
||||||
></el-col
|
></el-col
|
||||||
>
|
>
|
||||||
@@ -340,6 +360,7 @@
|
|||||||
import { getList as getCargoTypeList } from '@/api/base/cargo-type';
|
import { getList as getCargoTypeList } from '@/api/base/cargo-type';
|
||||||
import { getList as getFeeItemList } from '@/api/base/fee-item';
|
import { getList as getFeeItemList } from '@/api/base/fee-item';
|
||||||
import { getLazyTree as getRegionLazyTree } from '@/api/base/region';
|
import { getLazyTree as getRegionLazyTree } from '@/api/base/region';
|
||||||
|
import { InfoFilled } from '@element-plus/icons-vue';
|
||||||
import { getDictionary } from '@/api/system/dictbiz';
|
import { getDictionary } from '@/api/system/dictbiz';
|
||||||
|
|
||||||
const clone = value => JSON.parse(JSON.stringify(value));
|
const clone = value => JSON.parse(JSON.stringify(value));
|
||||||
@@ -370,6 +391,7 @@ const defaultRule = () => ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
components: { InfoFilled },
|
||||||
props: {
|
props: {
|
||||||
modelValue: Boolean,
|
modelValue: Boolean,
|
||||||
value: { type: Object, default: () => ({}) },
|
value: { type: Object, default: () => ({}) },
|
||||||
@@ -417,7 +439,10 @@ export default {
|
|||||||
cargoFlatOptions: [],
|
cargoFlatOptions: [],
|
||||||
cargoLoading: false,
|
cargoLoading: false,
|
||||||
cargoRequest: null,
|
cargoRequest: null,
|
||||||
formRules: { planName: [{ required: true, message: '请输入方案名称', trigger: 'blur' }] },
|
formRules: {
|
||||||
|
planName: [{ required: true, message: '请输入方案名称', trigger: 'blur' }],
|
||||||
|
transportMode: [{ required: true, message: '请选择运输方式', trigger: 'change' }],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -446,6 +471,7 @@ export default {
|
|||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.loadDictionaries();
|
this.loadDictionaries();
|
||||||
|
this.ensureTransportModes();
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
value: {
|
value: {
|
||||||
@@ -474,7 +500,7 @@ export default {
|
|||||||
const option = this.transportModeOptions.find(
|
const option = this.transportModeOptions.find(
|
||||||
item => String(item.value) === String(value) || String(item.label) === String(value)
|
item => String(item.value) === String(value) || String(item.label) === String(value)
|
||||||
);
|
);
|
||||||
return this.displayValue(option?.label || value);
|
return this.displayValue(option?.label || this.draft.transportModeLabel || value);
|
||||||
},
|
},
|
||||||
isDefaultPlan(plan) {
|
isDefaultPlan(plan) {
|
||||||
return (
|
return (
|
||||||
@@ -484,7 +510,14 @@ export default {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
normalizePlan(value = {}) {
|
normalizePlan(value = {}) {
|
||||||
const plan = { planName: '', defaultPlan: false, remark: '', ...clone(value || {}) };
|
const plan = {
|
||||||
|
planName: '',
|
||||||
|
transportMode: '',
|
||||||
|
transportModeLabel: '',
|
||||||
|
defaultPlan: false,
|
||||||
|
remark: '',
|
||||||
|
...clone(value || {}),
|
||||||
|
};
|
||||||
plan.rules = (value?.rules?.length ? value.rules : [defaultRule()]).map(rule =>
|
plan.rules = (value?.rules?.length ? value.rules : [defaultRule()]).map(rule =>
|
||||||
this.normalizeRule(rule)
|
this.normalizeRule(rule)
|
||||||
);
|
);
|
||||||
@@ -818,6 +851,10 @@ export default {
|
|||||||
this.$refs.formRef.validate(valid => {
|
this.$refs.formRef.validate(valid => {
|
||||||
if (!valid || !this.validateRules()) return;
|
if (!valid || !this.validateRules()) return;
|
||||||
const plan = clone(this.draft);
|
const plan = clone(this.draft);
|
||||||
|
const transportMode = this.transportModeOptions.find(
|
||||||
|
item => String(item.value) === String(plan.transportMode)
|
||||||
|
);
|
||||||
|
plan.transportModeLabel = transportMode?.label || plan.transportMode;
|
||||||
plan.rules = plan.rules.map(rule => {
|
plan.rules = plan.rules.map(rule => {
|
||||||
const ranges = this.canEditLimit(rule) ? this.getRanges(rule) : [];
|
const ranges = this.canEditLimit(rule) ? this.getRanges(rule) : [];
|
||||||
return {
|
return {
|
||||||
@@ -837,6 +874,9 @@ export default {
|
|||||||
this.visible = false;
|
this.visible = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
showDefaultPlanTip() {
|
||||||
|
this.$message.info('同一运输方式仅支持配置一个默认计费方案');
|
||||||
|
},
|
||||||
openMatch(row, index) {
|
openMatch(row, index) {
|
||||||
this.matchIndex = index;
|
this.matchIndex = index;
|
||||||
this.matchForm = { ...defaultRule().matchCondition, ...(row.matchCondition || {}) };
|
this.matchForm = { ...defaultRule().matchCondition, ...(row.matchCondition || {}) };
|
||||||
@@ -1097,6 +1137,12 @@ export default {
|
|||||||
margin: 12px 0;
|
margin: 12px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.billing-plan-editor__default-tip {
|
||||||
|
margin-left: 6px;
|
||||||
|
color: #909399;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
.limit-list {
|
.limit-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
@@ -80,7 +80,7 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="4"><el-form-item label="联系人"><el-input v-model="route.departureContact" placeholder="请输入" /></el-form-item></el-col>
|
<el-col :span="4"><el-form-item label="联系人"><el-input v-model="route.departureContact" placeholder="请输入" /></el-form-item></el-col>
|
||||||
<el-col :span="4"><el-form-item label="联系方式"><el-input v-model="route.departurePhone" placeholder="请输入" /></el-form-item></el-col>
|
<el-col :span="4"><el-form-item label="联系方式"><el-input v-model="route.departurePhone" placeholder="请输入" /></el-form-item></el-col>
|
||||||
<el-col :span="4"><el-form-item :label="dateStartLabel(route)" required><el-date-picker v-model="route.estimatedStartTime" type="date" value-format="YYYY-MM-DD" placeholder="请选择" /></el-form-item></el-col>
|
<el-col :span="4"><el-form-item :label="dateStartLabel(route)"><el-date-picker v-model="route.estimatedStartTime" type="date" value-format="YYYY-MM-DD" placeholder="请选择" /></el-form-item></el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row :gutter="16">
|
<el-row :gutter="16">
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
@@ -88,7 +88,7 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="4"><el-form-item label="联系人"><el-input v-model="route.arrivalContact" placeholder="请输入" /></el-form-item></el-col>
|
<el-col :span="4"><el-form-item label="联系人"><el-input v-model="route.arrivalContact" placeholder="请输入" /></el-form-item></el-col>
|
||||||
<el-col :span="4"><el-form-item label="联系方式"><el-input v-model="route.arrivalPhone" placeholder="请输入" /></el-form-item></el-col>
|
<el-col :span="4"><el-form-item label="联系方式"><el-input v-model="route.arrivalPhone" placeholder="请输入" /></el-form-item></el-col>
|
||||||
<el-col :span="4"><el-form-item :label="dateEndLabel(route)" required><el-date-picker v-model="route.estimatedEndTime" type="date" value-format="YYYY-MM-DD" placeholder="请选择" /></el-form-item></el-col>
|
<el-col :span="4"><el-form-item :label="dateEndLabel(route)"><el-date-picker v-model="route.estimatedEndTime" type="date" value-format="YYYY-MM-DD" placeholder="请选择" /></el-form-item></el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
@@ -601,8 +601,6 @@ export default {
|
|||||||
if (goods.some(item => Number(item.dispatchQuantity) > this.availableDispatchQuantity(route, item))) return this.$message.warning('本次数量不能超过剩余数量');
|
if (goods.some(item => Number(item.dispatchQuantity) > this.availableDispatchQuantity(route, item))) return this.$message.warning('本次数量不能超过剩余数量');
|
||||||
this.syncFreightItems(route);
|
this.syncFreightItems(route);
|
||||||
if (!this.validateFreightItems(route)) return;
|
if (!this.validateFreightItems(route)) return;
|
||||||
if (!route.estimatedStartTime) return this.$message.warning(`请选择${this.dateStartLabel(route)}`);
|
|
||||||
if (!route.estimatedEndTime) return this.$message.warning(`请选择${this.dateEndLabel(route)}`);
|
|
||||||
if (!this.validateRoutePhones(route)) return;
|
if (!this.validateRoutePhones(route)) return;
|
||||||
if (route.documentType === '运单') {
|
if (route.documentType === '运单') {
|
||||||
if (this.isRoad(route)) {
|
if (this.isRoad(route)) {
|
||||||
|
|||||||
@@ -4,35 +4,176 @@
|
|||||||
<section class="change-section">
|
<section class="change-section">
|
||||||
<div class="dialog-section-title">基本信息</div>
|
<div class="dialog-section-title">基本信息</div>
|
||||||
<el-row :gutter="28">
|
<el-row :gutter="28">
|
||||||
<el-col :span="24"><el-form-item label="变更类型"><el-radio-group v-model="form.changeType"><el-radio label="合同信息变更" /><el-radio label="终止合同" /></el-radio-group></el-form-item></el-col>
|
<el-col :span="24"
|
||||||
<el-col :span="6"><el-form-item label="合同编号"><el-input v-model="form.contractNo" disabled /></el-form-item></el-col>
|
><el-form-item label="变更类型"
|
||||||
<el-col :span="6"><el-form-item label="合同名称" prop="contractName"><el-input v-model="form.contractName" /></el-form-item></el-col>
|
><el-radio-group v-model="form.changeType"
|
||||||
<el-col :span="6"><el-form-item label="合同类型"><el-select v-model="form.contractCategory" disabled><el-option label="客户合同" value="客户合同" /><el-option label="承运商合同" value="承运商合同" /></el-select></el-form-item></el-col>
|
><el-radio label="合同信息变更" /><el-radio
|
||||||
<el-col :span="6"><el-form-item label="甲方"><el-input v-model="form.partyA" disabled /></el-form-item></el-col>
|
label="终止合同" /></el-radio-group></el-form-item
|
||||||
<el-col :span="6"><el-form-item label="乙方"><el-input v-model="form.partyB" disabled /></el-form-item></el-col>
|
></el-col>
|
||||||
<el-col :span="6"><el-form-item label="所属项目"><el-input v-model="form.projectName" disabled /></el-form-item></el-col>
|
<el-col :span="6"
|
||||||
<el-col :span="6"><el-form-item label="合同期限"><el-date-picker v-model="period" type="daterange" value-format="YYYY-MM-DD" range-separator="至" /></el-form-item></el-col>
|
><el-form-item label="合同编号"
|
||||||
<el-col :span="6"><el-form-item label="所属组织"><el-input v-model="form.organizationName" disabled /></el-form-item></el-col>
|
><el-input v-model="form.contractNo" disabled /></el-form-item
|
||||||
<el-col :span="6"><el-form-item label="签订日期"><el-date-picker v-model="form.signDate" type="date" value-format="YYYY-MM-DD" disabled /></el-form-item></el-col>
|
></el-col>
|
||||||
<el-col :span="6"><el-form-item label="合同格式"><el-select v-model="form.contractFormat"><el-option label="电子合同" value="电子合同" /><el-option label="纸质合同" value="纸质合同" /></el-select></el-form-item></el-col>
|
<el-col :span="6"
|
||||||
<el-col :span="6"><el-form-item label="结算方式"><el-input v-model="form.settlementMode" /></el-form-item></el-col>
|
><el-form-item label="合同名称" prop="contractName"
|
||||||
<el-col :span="6"><el-form-item label="是否需要加盖法人章"><el-select v-model="form.legalSealFlag"><el-option label="是" :value="1" /><el-option label="否" :value="0" /></el-select></el-form-item></el-col>
|
><el-input v-model="form.contractName" /></el-form-item
|
||||||
<el-col :span="6"><el-form-item label="一式(份)"><div class="inline-field"><el-input-number v-model="form.copyCount" :min="1" controls-position="right" /></div></el-form-item></el-col>
|
></el-col>
|
||||||
<el-col :span="6"><el-form-item label="回款账期(天)"><el-input-number v-model="form.paymentDays" :min="0" controls-position="right" /></el-form-item></el-col>
|
<el-col :span="6"
|
||||||
<el-col :span="24"><el-form-item label="备注"><el-input v-model="form.remark" type="textarea" maxlength="2000" show-word-limit /></el-form-item></el-col>
|
><el-form-item label="合同类型"
|
||||||
|
><el-select v-model="form.contractCategory" disabled
|
||||||
|
><el-option label="客户合同" value="客户合同" /><el-option
|
||||||
|
label="承运商合同"
|
||||||
|
value="承运商合同" /></el-select></el-form-item
|
||||||
|
></el-col>
|
||||||
|
<el-col :span="6"
|
||||||
|
><el-form-item label="甲方"><el-input v-model="form.partyA" disabled /></el-form-item
|
||||||
|
></el-col>
|
||||||
|
<el-col :span="6"
|
||||||
|
><el-form-item label="乙方"><el-input v-model="form.partyB" disabled /></el-form-item
|
||||||
|
></el-col>
|
||||||
|
<el-col :span="6"
|
||||||
|
><el-form-item label="所属项目"
|
||||||
|
><el-input v-model="form.projectName" disabled /></el-form-item
|
||||||
|
></el-col>
|
||||||
|
<el-col :span="6"
|
||||||
|
><el-form-item label="合同期限"
|
||||||
|
><el-date-picker
|
||||||
|
v-model="period"
|
||||||
|
type="daterange"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
range-separator="至" /></el-form-item
|
||||||
|
></el-col>
|
||||||
|
<el-col :span="6"
|
||||||
|
><el-form-item label="所属组织"
|
||||||
|
><el-input v-model="form.organizationName" disabled /></el-form-item
|
||||||
|
></el-col>
|
||||||
|
<el-col :span="6"
|
||||||
|
><el-form-item label="签订日期"
|
||||||
|
><el-date-picker
|
||||||
|
v-model="form.signDate"
|
||||||
|
type="date"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
disabled /></el-form-item
|
||||||
|
></el-col>
|
||||||
|
<el-col :span="6"
|
||||||
|
><el-form-item label="合同格式"
|
||||||
|
><el-select v-model="form.contractFormat"
|
||||||
|
><el-option label="电子合同" value="电子合同" /><el-option
|
||||||
|
label="纸质合同"
|
||||||
|
value="纸质合同" /></el-select></el-form-item
|
||||||
|
></el-col>
|
||||||
|
<el-col :span="6"
|
||||||
|
><el-form-item label="结算方式"><el-input v-model="form.settlementMode" /></el-form-item
|
||||||
|
></el-col>
|
||||||
|
<el-col :span="6"
|
||||||
|
><el-form-item label="是否需要加盖法人章"
|
||||||
|
><el-select v-model="form.legalSealFlag"
|
||||||
|
><el-option label="是" :value="1" /><el-option
|
||||||
|
label="否"
|
||||||
|
:value="0" /></el-select></el-form-item
|
||||||
|
></el-col>
|
||||||
|
<el-col :span="6"
|
||||||
|
><el-form-item label="一式(份)"
|
||||||
|
><div class="inline-field">
|
||||||
|
<el-input-number
|
||||||
|
v-model="form.copyCount"
|
||||||
|
:min="1"
|
||||||
|
controls-position="right"
|
||||||
|
/></div></el-form-item
|
||||||
|
></el-col>
|
||||||
|
<el-col :span="6"
|
||||||
|
><el-form-item label="回款账期(天)"
|
||||||
|
><el-input-number
|
||||||
|
v-model="form.paymentDays"
|
||||||
|
:min="0"
|
||||||
|
controls-position="right" /></el-form-item
|
||||||
|
></el-col>
|
||||||
|
<el-col :span="24"
|
||||||
|
><el-form-item label="备注"
|
||||||
|
><el-input
|
||||||
|
v-model="form.remark"
|
||||||
|
type="textarea"
|
||||||
|
maxlength="2000"
|
||||||
|
show-word-limit /></el-form-item
|
||||||
|
></el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="change-section">
|
<section class="change-section">
|
||||||
<div class="dialog-section-title">合同文件</div>
|
<div class="dialog-section-title">合同文件</div>
|
||||||
<div class="attachment-head"><vehicle-attachment-upload v-model="contractFileRows" :readonly="false" :file-types="attachmentFileTypes" :max-size="500" :show-tip="false" :show-file-list="false" button-text="上传附件" @change="handleContractFileChange" /><el-button type="primary" :disabled="!contractFileRows.length" @click="handleContractFileBatchDownload">批量下载</el-button></div>
|
<div class="attachment-head">
|
||||||
<el-table :data="contractFileRows" border class="change-table" @selection-change="selectedContractFiles = $event"><el-table-column type="selection" width="55" /><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, contractFileRows)">{{ row.originalName || row.name }}</el-link></template></el-table-column><el-table-column label="文件大小" width="120"><template #default="{ row }">{{ formatFileSize(row.size) }}</template></el-table-column><el-table-column prop="uploadUserName" label="上传人" width="140" /><el-table-column prop="uploadTime" label="上传时间" width="170" /><el-table-column label="操作" width="100"><template #default="{ $index }"><el-link type="danger" @click="removeContractFile($index)">删除</el-link></template></el-table-column></el-table>
|
<vehicle-attachment-upload
|
||||||
|
v-model="contractFileRows"
|
||||||
|
:readonly="false"
|
||||||
|
:file-types="attachmentFileTypes"
|
||||||
|
:max-size="500"
|
||||||
|
:show-tip="false"
|
||||||
|
:show-file-list="false"
|
||||||
|
button-text="上传附件"
|
||||||
|
@change="handleContractFileChange"
|
||||||
|
/><el-button
|
||||||
|
type="primary"
|
||||||
|
:disabled="!contractFileRows.length"
|
||||||
|
@click="handleContractFileBatchDownload"
|
||||||
|
>批量下载</el-button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<el-table
|
||||||
|
:data="contractFileRows"
|
||||||
|
border
|
||||||
|
class="change-table"
|
||||||
|
@selection-change="selectedContractFiles = $event"
|
||||||
|
><el-table-column type="selection" width="55" /><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, contractFileRows)">{{
|
||||||
|
row.originalName || row.name
|
||||||
|
}}</el-link></template
|
||||||
|
></el-table-column
|
||||||
|
><el-table-column label="文件大小" width="120"
|
||||||
|
><template #default="{ row }">{{ formatFileSize(row.size) }}</template></el-table-column
|
||||||
|
><el-table-column prop="uploadUserName" label="上传人" width="140" /><el-table-column
|
||||||
|
prop="uploadTime"
|
||||||
|
label="上传时间"
|
||||||
|
width="170"
|
||||||
|
/><el-table-column label="操作" width="100"
|
||||||
|
><template #default="{ $index }"
|
||||||
|
><el-link type="danger" @click="removeContractFile($index)">删除</el-link></template
|
||||||
|
></el-table-column
|
||||||
|
></el-table
|
||||||
|
>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="change-section">
|
<section class="change-section">
|
||||||
<div class="section-head"><div class="dialog-section-title">计费信息</div><el-button type="primary" plain @click="addPlan">添加</el-button></div>
|
<div class="section-head">
|
||||||
<el-radio-group v-model="feeGenerationMode"><el-radio label="system">系统生成</el-radio><el-radio label="manual">手动生成</el-radio></el-radio-group>
|
<div class="dialog-section-title">计费信息</div>
|
||||||
<el-table :data="plans" border class="change-table"><el-table-column type="index" label="序号" width="70" /><el-table-column prop="planName" label="方案名称" min-width="220" /><el-table-column label="默认方案" min-width="120"><template #default="{ row }">{{ row.defaultPlan ? '是' : '否' }}</template></el-table-column><el-table-column prop="remark" label="备注" min-width="260" /><el-table-column label="操作" width="180"><template #default="{ row, $index }"><el-link type="primary" @click="editPlan(row, $index)">编辑</el-link><el-link type="danger" @click="plans.splice($index, 1)">删除</el-link></template></el-table-column></el-table>
|
<el-button type="primary" plain @click="addPlan">添加</el-button>
|
||||||
|
</div>
|
||||||
|
<el-radio-group v-model="feeGenerationMode"
|
||||||
|
><el-radio label="system">系统生成</el-radio
|
||||||
|
><el-radio label="manual">手动生成</el-radio></el-radio-group
|
||||||
|
>
|
||||||
|
<el-table :data="plans" border class="change-table"
|
||||||
|
><el-table-column type="index" label="序号" width="70" /><el-table-column
|
||||||
|
prop="planName"
|
||||||
|
label="方案名称"
|
||||||
|
min-width="220"
|
||||||
|
/><el-table-column label="默认方案" min-width="120"
|
||||||
|
><template #default="{ row }">{{
|
||||||
|
row.defaultPlan ? '是' : '否'
|
||||||
|
}}</template></el-table-column
|
||||||
|
><el-table-column prop="remark" label="备注" min-width="260" /><el-table-column
|
||||||
|
label="操作"
|
||||||
|
width="180"
|
||||||
|
><template #default="{ row, $index }"
|
||||||
|
><el-link type="primary" @click="editPlan(row, $index)">编辑</el-link
|
||||||
|
><el-link type="danger" @click="plans.splice($index, 1)">删除</el-link></template
|
||||||
|
></el-table-column
|
||||||
|
></el-table
|
||||||
|
>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="change-section">
|
<section class="change-section">
|
||||||
@@ -41,13 +182,66 @@
|
|||||||
<el-tab-pane label="预结算配置" name="pre" />
|
<el-tab-pane label="预结算配置" name="pre" />
|
||||||
<el-tab-pane label="正式结算配置" name="formal" />
|
<el-tab-pane label="正式结算配置" name="formal" />
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
<div class="settlement-switch"><span>自动生成结算单</span><el-radio-group v-model="settlementRule.autoGenerate"><el-radio :label="1">开启</el-radio><el-radio :label="0">关闭</el-radio></el-radio-group></div>
|
<div class="settlement-switch">
|
||||||
<el-form v-if="settlementRule.autoGenerate === 1" :model="settlementRule" label-position="right" label-width="auto" class="settlement-form">
|
<span>自动生成结算单</span
|
||||||
<el-form-item label="账单起始日期" required><el-date-picker v-model="settlementRule.billStartDate" type="date" placeholder="请选择账单起始日期" format="YYYY-MM-DD" value-format="YYYY-MM-DD" /></el-form-item>
|
><el-radio-group v-model="settlementRule.autoGenerate"
|
||||||
<el-form-item label="结算类型" required><el-select v-model="settlementRule.settlementType" placeholder="请选择结算类型" @change="handleSettlementTypeChange"><el-option v-for="item in settlementTypeOptions" :key="item" :label="item" :value="item" /></el-select></el-form-item>
|
><el-radio :label="1">开启</el-radio
|
||||||
<el-form-item v-if="showSettlementBillCycleType" label="账单周期类型" required><el-select v-model="settlementRule.billCycleType" placeholder="请选择账单周期类型" @change="handleCycleTypeChange"><el-option v-for="item in billCycleTypeOptions" :key="item" :label="item" :value="item" /></el-select></el-form-item>
|
><el-radio :label="0">关闭</el-radio></el-radio-group
|
||||||
<el-form-item v-if="showSettlementBillCutoffDay" label="账单截单日" required><el-select v-model="settlementRule.billCutoffDay" placeholder="请选择账单截单日"><el-option v-for="item in billCutoffDayOptions" :key="item.value" :label="item.label" :value="item.value" /></el-select></el-form-item>
|
>
|
||||||
<el-form-item v-if="showSettlementCycleDays" label="周期天数" required><el-select v-model="settlementRule.cycleDays" placeholder="请选择周期天数"><el-option v-for="item in cycleDayOptions" :key="item.value" :label="item.label" :value="item.value" /></el-select></el-form-item>
|
</div>
|
||||||
|
<el-form
|
||||||
|
v-if="settlementRule.autoGenerate === 1"
|
||||||
|
:model="settlementRule"
|
||||||
|
label-position="right"
|
||||||
|
label-width="auto"
|
||||||
|
class="settlement-form"
|
||||||
|
>
|
||||||
|
<el-form-item label="账单起始日期" required
|
||||||
|
><el-date-picker
|
||||||
|
v-model="settlementRule.billStartDate"
|
||||||
|
type="date"
|
||||||
|
placeholder="请选择账单起始日期"
|
||||||
|
format="YYYY-MM-DD"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
/></el-form-item>
|
||||||
|
<el-form-item label="结算类型" required
|
||||||
|
><el-select
|
||||||
|
v-model="settlementRule.settlementType"
|
||||||
|
placeholder="请选择结算类型"
|
||||||
|
@change="handleSettlementTypeChange"
|
||||||
|
><el-option
|
||||||
|
v-for="item in settlementTypeOptions"
|
||||||
|
:key="item"
|
||||||
|
:label="item"
|
||||||
|
:value="item" /></el-select
|
||||||
|
></el-form-item>
|
||||||
|
<el-form-item v-if="showSettlementBillCycleType" label="账单周期类型" required
|
||||||
|
><el-select
|
||||||
|
v-model="settlementRule.billCycleType"
|
||||||
|
placeholder="请选择账单周期类型"
|
||||||
|
@change="handleCycleTypeChange"
|
||||||
|
><el-option
|
||||||
|
v-for="item in billCycleTypeOptions"
|
||||||
|
:key="item"
|
||||||
|
:label="item"
|
||||||
|
:value="item" /></el-select
|
||||||
|
></el-form-item>
|
||||||
|
<el-form-item v-if="showSettlementBillCutoffDay" label="账单截单日" required
|
||||||
|
><el-select v-model="settlementRule.billCutoffDay" placeholder="请选择账单截单日"
|
||||||
|
><el-option
|
||||||
|
v-for="item in billCutoffDayOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value" /></el-select
|
||||||
|
></el-form-item>
|
||||||
|
<el-form-item v-if="showSettlementCycleDays" label="周期天数" required
|
||||||
|
><el-select v-model="settlementRule.cycleDays" placeholder="请选择周期天数"
|
||||||
|
><el-option
|
||||||
|
v-for="item in cycleDayOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value" /></el-select
|
||||||
|
></el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@@ -56,29 +250,135 @@
|
|||||||
<el-table :data="paymentRatioRows" border class="change-table">
|
<el-table :data="paymentRatioRows" border class="change-table">
|
||||||
<el-table-column type="index" label="序号" width="70" />
|
<el-table-column type="index" label="序号" width="70" />
|
||||||
<el-table-column prop="paymentTerm" label="付款笔数" min-width="180" />
|
<el-table-column prop="paymentTerm" label="付款笔数" min-width="180" />
|
||||||
<el-table-column label="付款比例上限(%)" min-width="220"><template #default="{ row }"><el-input-number v-model="row.ratioLimit" :min="0" :max="100" :precision="2" controls-position="right" /></template></el-table-column>
|
<el-table-column label="付款比例上限" min-width="220"
|
||||||
<el-table-column label="备注" min-width="220"><template #default="{ row }"><el-input v-model="row.remark" maxlength="200" /></template></el-table-column>
|
><template #default="{ row }"
|
||||||
<el-table-column label="操作" width="100"><template #default="{ $index }"><el-link type="danger" @click="paymentRatioRows.splice($index, 1)">删除</el-link></template></el-table-column>
|
><el-input
|
||||||
|
v-model="row.ratioLimit"
|
||||||
|
placeholder="请输入"
|
||||||
|
@input="value => ratioInput(row, value)"
|
||||||
|
><template #append>%</template></el-input
|
||||||
|
></template
|
||||||
|
></el-table-column
|
||||||
|
>
|
||||||
|
<el-table-column label="备注" min-width="220"
|
||||||
|
><template #default="{ row }"
|
||||||
|
><el-input v-model="row.remark" maxlength="200" /></template
|
||||||
|
></el-table-column>
|
||||||
|
<el-table-column label="操作" width="100"
|
||||||
|
><template #default="{ $index }"
|
||||||
|
><el-link type="danger" @click="paymentRatioRows.splice($index, 1)"
|
||||||
|
>删除</el-link
|
||||||
|
></template
|
||||||
|
></el-table-column
|
||||||
|
>
|
||||||
</el-table>
|
</el-table>
|
||||||
<el-button type="primary" plain @click="addPaymentRatioRow">添加</el-button>
|
<el-button type="primary" plain @click="addPaymentRatioRow">添加</el-button>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<billing-plan-editor v-model="planDialogVisible" :value="planEditor" :index="planEditorIndex" @save="savePlan" />
|
<billing-plan-editor
|
||||||
|
v-model="planDialogVisible"
|
||||||
|
:value="planEditor"
|
||||||
|
:index="planEditorIndex"
|
||||||
|
@save="savePlan"
|
||||||
|
/>
|
||||||
|
|
||||||
<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">
|
||||||
<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>
|
<div class="dialog-section-title">其它附件</div>
|
||||||
<el-upload action="#" :auto-upload="false" multiple :show-file-list="false" @change="handleAttachment"><el-button plain>上传附件</el-button></el-upload>
|
<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-upload
|
||||||
|
action="#"
|
||||||
|
:auto-upload="false"
|
||||||
|
multiple
|
||||||
|
:show-file-list="false"
|
||||||
|
@change="handleAttachment"
|
||||||
|
><el-button plain>上传附件</el-button></el-upload
|
||||||
|
>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="change-section change-reason-section"><div class="dialog-section-title">变更原因</div><el-form-item prop="changeReason"><el-input v-model="form.changeReason" type="textarea" :rows="4" maxlength="2000" show-word-limit placeholder="请输入变更原因" /></el-form-item><div class="dialog-section-title">变更材料</div><el-upload action="#" :auto-upload="false" multiple :show-file-list="false" @change="handleChangeMaterial"><el-button plain>上传变更材料</el-button></el-upload></section>
|
<section class="change-section change-reason-section">
|
||||||
<div class="page-footer"><el-button type="primary" @click="submit">提交</el-button><el-button @click="$router.back()">关闭</el-button></div>
|
<div class="dialog-section-title">变更原因</div>
|
||||||
|
<el-form-item prop="changeReason"
|
||||||
|
><el-input
|
||||||
|
v-model="form.changeReason"
|
||||||
|
type="textarea"
|
||||||
|
:rows="4"
|
||||||
|
maxlength="2000"
|
||||||
|
show-word-limit
|
||||||
|
placeholder="请输入变更原因"
|
||||||
|
/></el-form-item>
|
||||||
|
<div class="dialog-section-title">变更材料</div>
|
||||||
|
<el-upload
|
||||||
|
action="#"
|
||||||
|
:auto-upload="false"
|
||||||
|
multiple
|
||||||
|
:show-file-list="false"
|
||||||
|
@change="handleChangeMaterial"
|
||||||
|
><el-button plain>上传变更材料</el-button></el-upload
|
||||||
|
>
|
||||||
|
</section>
|
||||||
|
<div class="page-footer">
|
||||||
|
<el-button type="primary" @click="submit">提交</el-button
|
||||||
|
><el-button @click="$router.back()">关闭</el-button>
|
||||||
|
</div>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
<el-dialog v-model="documentPreviewVisible" :title="previewFile.name || '附件预览'" append-to-body destroy-on-close width="90%" top="4vh">
|
<el-dialog
|
||||||
<open-file-viewer v-if="documentPreviewVisible && previewFile.url" :file="previewFile.url" :file-name="previewFile.name" :mime-type="previewFile.mimeType" width="100%" height="72vh" fit="contain" theme="auto" locale="zh-CN" :toolbar="viewerToolbar" :plugins="viewerPlugins" @unsupported="handlePreviewUnsupported" @error="handlePreviewError" />
|
v-model="documentPreviewVisible"
|
||||||
|
:title="previewFile.name || '附件预览'"
|
||||||
|
append-to-body
|
||||||
|
destroy-on-close
|
||||||
|
width="90%"
|
||||||
|
top="4vh"
|
||||||
|
>
|
||||||
|
<open-file-viewer
|
||||||
|
v-if="documentPreviewVisible && previewFile.url"
|
||||||
|
:file="previewFile.url"
|
||||||
|
:file-name="previewFile.name"
|
||||||
|
:mime-type="previewFile.mimeType"
|
||||||
|
width="100%"
|
||||||
|
height="72vh"
|
||||||
|
fit="contain"
|
||||||
|
theme="auto"
|
||||||
|
locale="zh-CN"
|
||||||
|
:toolbar="viewerToolbar"
|
||||||
|
:plugins="viewerPlugins"
|
||||||
|
@unsupported="handlePreviewUnsupported"
|
||||||
|
@error="handlePreviewError"
|
||||||
|
/>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
<el-image-viewer v-if="imagePreviewVisible" :url-list="imagePreviewUrls" :initial-index="imagePreviewIndex" @close="imagePreviewVisible = false" />
|
<el-image-viewer
|
||||||
|
v-if="imagePreviewVisible"
|
||||||
|
:url-list="imagePreviewUrls"
|
||||||
|
:initial-index="imagePreviewIndex"
|
||||||
|
@close="imagePreviewVisible = false"
|
||||||
|
/>
|
||||||
</basic-container>
|
</basic-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -87,7 +387,13 @@ import * as api from '@/api/business/contract-manage';
|
|||||||
import BillingPlanEditor from './components/billing-plan-editor.vue';
|
import BillingPlanEditor from './components/billing-plan-editor.vue';
|
||||||
import { ElImageViewer } from 'element-plus';
|
import { ElImageViewer } from 'element-plus';
|
||||||
import { OpenFileViewer } from '@open-file-viewer/vue';
|
import { OpenFileViewer } from '@open-file-viewer/vue';
|
||||||
import { fallbackPlugin, imagePlugin, officePlugin, pdfPlugin, textPlugin } from '@open-file-viewer/core';
|
import {
|
||||||
|
fallbackPlugin,
|
||||||
|
imagePlugin,
|
||||||
|
officePlugin,
|
||||||
|
pdfPlugin,
|
||||||
|
textPlugin,
|
||||||
|
} from '@open-file-viewer/core';
|
||||||
import '@open-file-viewer/core/style.css';
|
import '@open-file-viewer/core/style.css';
|
||||||
import pdfWorkerSrc from 'pdfjs-dist/build/pdf.worker.mjs?url';
|
import pdfWorkerSrc from 'pdfjs-dist/build/pdf.worker.mjs?url';
|
||||||
|
|
||||||
@@ -101,52 +407,396 @@ const viewerPlugins = [
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { BillingPlanEditor, ElImageViewer, OpenFileViewer },
|
components: { BillingPlanEditor, ElImageViewer, OpenFileViewer },
|
||||||
data() { return { form: {}, period: [], plans: [], attachments: [], contractFiles: [], contractFileRows: [], selectedContractFiles: [], paymentRatioRows: [], settlementConfigTab: 'pre', changeMaterials: [], imagePreviewVisible: false, imagePreviewUrls: [], imagePreviewIndex: 0, documentPreviewVisible: false, previewFile: {}, viewerPlugins, viewerToolbar: { zoom: true, rotate: true, download: true, fullscreen: true }, feeGenerationMode: 'system', settlementRule: { autoGenerate: 1, settlementType: '月结', billCycleType: '固定截单日', billCutoffDay: 25, cycleDays: '' }, preSettlementConfig: {}, formalSettlementConfig: {}, settlementTypeOptions: ['月结','日结','周结','半月结','固定天数周期结算'], billCycleTypeOptions: ['固定截单日','自然月'], billCutoffDayOptions: Array.from({ length: 31 }, (_, index) => ({ label: `${index + 1}日`, value: index + 1 })), cycleDayOptions: [7,15,30,60].map(value => ({ label: `${value}天`, value })), planDialogVisible: false, planEditor: { planName: '', defaultPlan: false, remark: '', rules: [] }, planEditorIndex: -1, billingElements: ['按重量','按体积','按车辆','按里程','按吨·公里','固定金额(整单一口价)','按数量'], attachmentFileTypes: ['pdf','bmp','jpeg','png','jpg','doc','docx','ppt','pptx','xlsx','xls','eml','msg','zip'], rules: { contractName: [{ required: true, message: '请输入合同名称', trigger: 'blur' }], changeReason: [{ required: true, message: '请输入变更原因', trigger: 'blur' }] } }; },
|
data() {
|
||||||
computed: { showSettlementBillCycleType() { return this.settlementRule.settlementType === '月结'; }, showSettlementBillCutoffDay() { return this.showSettlementBillCycleType && this.settlementRule.billCycleType === '固定截单日'; }, showSettlementCycleDays() { return this.settlementRule.settlementType === '固定天数周期结算'; } },
|
return {
|
||||||
mounted() { this.load(); },
|
form: {},
|
||||||
watch: { settlementConfigTab(tab, oldTab) { if (tab === oldTab) return; if (oldTab === 'pre') this.preSettlementConfig = { ...this.settlementRule }; else this.formalSettlementConfig = { ...this.settlementRule }; this.settlementRule = { ...(tab === 'pre' ? this.preSettlementConfig : this.formalSettlementConfig) }; } },
|
period: [],
|
||||||
|
plans: [],
|
||||||
|
attachments: [],
|
||||||
|
contractFiles: [],
|
||||||
|
contractFileRows: [],
|
||||||
|
selectedContractFiles: [],
|
||||||
|
paymentRatioRows: [],
|
||||||
|
settlementConfigTab: 'pre',
|
||||||
|
changeMaterials: [],
|
||||||
|
imagePreviewVisible: false,
|
||||||
|
imagePreviewUrls: [],
|
||||||
|
imagePreviewIndex: 0,
|
||||||
|
documentPreviewVisible: false,
|
||||||
|
previewFile: {},
|
||||||
|
viewerPlugins,
|
||||||
|
viewerToolbar: { zoom: true, rotate: true, download: true, fullscreen: true },
|
||||||
|
feeGenerationMode: 'system',
|
||||||
|
settlementRule: {
|
||||||
|
autoGenerate: 1,
|
||||||
|
settlementType: '月结',
|
||||||
|
billCycleType: '固定截单日',
|
||||||
|
billCutoffDay: 25,
|
||||||
|
cycleDays: '',
|
||||||
|
},
|
||||||
|
preSettlementConfig: {},
|
||||||
|
formalSettlementConfig: {},
|
||||||
|
settlementTypeOptions: ['月结', '日结', '周结', '半月结', '固定天数周期结算'],
|
||||||
|
billCycleTypeOptions: ['固定截单日', '自然月'],
|
||||||
|
billCutoffDayOptions: Array.from({ length: 31 }, (_, index) => ({
|
||||||
|
label: `${index + 1}日`,
|
||||||
|
value: index + 1,
|
||||||
|
})),
|
||||||
|
cycleDayOptions: [7, 15, 30, 60].map(value => ({ label: `${value}天`, value })),
|
||||||
|
planDialogVisible: false,
|
||||||
|
planEditor: { planName: '', defaultPlan: false, remark: '', rules: [] },
|
||||||
|
planEditorIndex: -1,
|
||||||
|
billingElements: [
|
||||||
|
'按重量',
|
||||||
|
'按体积',
|
||||||
|
'按车辆',
|
||||||
|
'按里程',
|
||||||
|
'按吨·公里',
|
||||||
|
'固定金额(整单一口价)',
|
||||||
|
'按数量',
|
||||||
|
],
|
||||||
|
attachmentFileTypes: [
|
||||||
|
'pdf',
|
||||||
|
'bmp',
|
||||||
|
'jpeg',
|
||||||
|
'png',
|
||||||
|
'jpg',
|
||||||
|
'doc',
|
||||||
|
'docx',
|
||||||
|
'ppt',
|
||||||
|
'pptx',
|
||||||
|
'xlsx',
|
||||||
|
'xls',
|
||||||
|
'eml',
|
||||||
|
'msg',
|
||||||
|
'zip',
|
||||||
|
],
|
||||||
|
rules: {
|
||||||
|
contractName: [{ required: true, message: '请输入合同名称', trigger: 'blur' }],
|
||||||
|
changeReason: [{ required: true, message: '请输入变更原因', trigger: 'blur' }],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
showSettlementBillCycleType() {
|
||||||
|
return this.settlementRule.settlementType === '月结';
|
||||||
|
},
|
||||||
|
showSettlementBillCutoffDay() {
|
||||||
|
return this.showSettlementBillCycleType && this.settlementRule.billCycleType === '固定截单日';
|
||||||
|
},
|
||||||
|
showSettlementCycleDays() {
|
||||||
|
return this.settlementRule.settlementType === '固定天数周期结算';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.load();
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
settlementConfigTab(tab, oldTab) {
|
||||||
|
if (tab === oldTab) return;
|
||||||
|
if (oldTab === 'pre') this.preSettlementConfig = { ...this.settlementRule };
|
||||||
|
else this.formalSettlementConfig = { ...this.settlementRule };
|
||||||
|
this.settlementRule = {
|
||||||
|
...(tab === 'pre' ? this.preSettlementConfig : this.formalSettlementConfig),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async load() { const id = this.$route.query.id; if (!id) return; const res = await api.getDetail(id); const data = res.data?.data || res.data || {}; this.form = { ...data, changeType: '合同信息变更' }; this.period = data.startDate && data.endDate ? [data.startDate, data.endDate] : []; this.plans = this.parse(data.billingPlanJson); this.attachments = this.parse(data.attachmentsJson); this.contractFileRows = this.parse(data.contractFileJson); const rules = this.parseObject(data.settlementRuleJson); const pre = this.parseObject(data.preSettlementConfigJson); const formal = this.parseObject(data.formalSettlementConfigJson); const legacy = Object.keys(rules).some(key => !['preSettlementConfig', 'formalSettlementConfig'].includes(key)) ? rules : {}; this.preSettlementConfig = rules.preSettlementConfig || (Object.keys(pre).length ? pre : legacy); this.formalSettlementConfig = rules.formalSettlementConfig || (Object.keys(formal).length ? formal : legacy); this.settlementRule = { ...this.preSettlementConfig }; this.feeGenerationMode = data.feeGenerationMode || (Number(data.billingEnabled) === 0 ? 'manual' : 'system'); this.paymentRatioRows = this.parse(data.paymentRatioJson); },
|
async load() {
|
||||||
parse(value) { try { const result = JSON.parse(value || '[]'); return Array.isArray(result) ? result : []; } catch { return []; } },
|
const id = this.$route.query.id;
|
||||||
parseObject(value) { try { return { autoGenerate: 1, settlementType: '月结', billCycleType: '固定截单日', billCutoffDay: 25, cycleDays: 15, ...(JSON.parse(value || '{}') || {}) }; } catch { return { autoGenerate: 1, settlementType: '月结', billCycleType: '固定截单日', billCutoffDay: 25, cycleDays: 15 }; } },
|
if (!id) return;
|
||||||
addPlan() { this.planEditorIndex = -1; this.planEditor = { planName: `计费方案${this.plans.length + 1}`, defaultPlan: !this.plans.length, remark: '', rules: [{}] }; this.planDialogVisible = true; },
|
const res = await api.getDetail(id);
|
||||||
editPlan(row, index) { this.planEditorIndex = index; this.planEditor = JSON.parse(JSON.stringify(row)); this.planDialogVisible = true; },
|
const data = res.data?.data || res.data || {};
|
||||||
toggleDefaultPlan(value) { if (value) this.plans.forEach(item => { item.defaultPlan = false; }); },
|
this.form = { ...data, changeType: '合同信息变更' };
|
||||||
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; }); },
|
this.period = data.startDate && data.endDate ? [data.startDate, data.endDate] : [];
|
||||||
handleSettlementTypeChange(value) { if (value !== '月结') { this.settlementRule.billCycleType = ''; this.settlementRule.billCutoffDay = ''; } else if (!this.settlementRule.billCycleType) this.settlementRule.billCycleType = '固定截单日'; if (value !== '固定天数周期结算') this.settlementRule.cycleDays = ''; },
|
this.plans = this.parse(data.billingPlanJson);
|
||||||
handleCycleTypeChange(value) { if (value === '固定截单日' && !this.settlementRule.billCutoffDay) this.settlementRule.billCutoffDay = 25; if (value !== '固定截单日') this.settlementRule.billCutoffDay = ''; },
|
this.attachments = this.parse(data.attachmentsJson);
|
||||||
handleAttachment(event) { const raw = event.raw; if (raw) this.attachments.push({ name: raw.name, size: `${Math.ceil(raw.size / 1024)}KB`, uploadTime: this.$dayjs().format('YYYY-MM-DD HH:mm:ss') }); },
|
this.contractFileRows = this.parse(data.contractFileJson);
|
||||||
handleContractFileChange(list) { this.contractFileRows = (list || []).map(item => ({ ...item, uploadTime: item.uploadTime || this.$dayjs().format('YYYY-MM-DD HH:mm:ss') })); },
|
const rules = this.parseObject(data.settlementRuleJson);
|
||||||
attachmentUrl(row = {}) { return row.url || row.link || row.fileUrl || row.downloadUrl || row.domain || ''; },
|
const pre = this.parseObject(data.preSettlementConfigJson);
|
||||||
attachmentName(row = {}) { return row.originalName || row.name || row.fileName || '附件'; },
|
const formal = this.parseObject(data.formalSettlementConfigJson);
|
||||||
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() : ''; },
|
const legacy = Object.keys(rules).some(
|
||||||
isAttachmentImage(row) { return ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'].includes(this.attachmentExtension(row)); },
|
key => !['preSettlementConfig', 'formalSettlementConfig'].includes(key)
|
||||||
previewAttachment(row, rows = this.attachments) { const url = this.attachmentUrl(row); if (!url) { this.$message.warning('附件地址为空,无法预览'); return; } if (this.isAttachmentImage(row)) { this.imagePreviewUrls = (rows || []).filter(item => this.isAttachmentImage(item) && this.attachmentUrl(item)).map(item => this.attachmentUrl(item)); this.imagePreviewIndex = Math.max(this.imagePreviewUrls.indexOf(url), 0); this.imagePreviewVisible = true; return; } this.previewFile = { name: this.attachmentName(row), url, mimeType: row.mimeType || row.contentType || '' }; this.documentPreviewVisible = true; },
|
)
|
||||||
handlePreviewUnsupported() { this.$message.warning('当前文件暂不支持在线预览'); },
|
? rules
|
||||||
handlePreviewError() { this.$message.error('附件预览失败'); },
|
: {};
|
||||||
removeContractFile(index) { this.contractFileRows.splice(index, 1); },
|
this.preSettlementConfig =
|
||||||
handleContractFileBatchDownload() { (this.selectedContractFiles.length ? this.selectedContractFiles : this.contractFileRows).forEach(row => { if (row.url) window.open(row.url, '_blank'); }); },
|
rules.preSettlementConfig || (Object.keys(pre).length ? pre : legacy);
|
||||||
formatFileSize(value) { const size = Number(value || 0); return size > 1024 * 1024 ? `${(size / 1024 / 1024).toFixed(2)}MB` : `${Math.max(1, Math.ceil(size / 1024))}KB`; },
|
this.formalSettlementConfig =
|
||||||
handleChangeMaterial(event) { if (event.raw) this.changeMaterials.push(event.raw); },
|
rules.formalSettlementConfig || (Object.keys(formal).length ? formal : legacy);
|
||||||
addPaymentRatioRow() { this.paymentRatioRows.push({ paymentTerm: `第${this.paymentRatioRows.length + 1}笔`, ratioLimit: '', remark: '' }); },
|
this.settlementRule = { ...this.preSettlementConfig };
|
||||||
async submit() { await this.$refs.formRef.validate(); const total = this.paymentRatioRows.reduce((sum, row) => sum + Number(row.ratioLimit || 0), 0); if (this.paymentRatioRows.length && Math.abs(total - 100) > 0.0001) { this.$message.warning('付款比例上限合计必须等于100%'); return; } const settlementRule = { preSettlementConfig: this.preSettlementConfig, formalSettlementConfig: this.formalSettlementConfig }; await api.submitChange({ ...this.form, startDate: this.period[0], endDate: this.period[1], feeGenerationMode: this.feeGenerationMode, billingEnabled: this.feeGenerationMode === 'system' ? 1 : 0, billingPlanJson: JSON.stringify(this.plans), settlementRuleJson: JSON.stringify(settlementRule), preSettlementConfigJson: JSON.stringify(this.preSettlementConfig), formalSettlementConfigJson: JSON.stringify(this.formalSettlementConfig), paymentRatioJson: JSON.stringify(this.paymentRatioRows), contractFileJson: JSON.stringify(this.contractFileRows), attachmentsJson: JSON.stringify(this.attachments), changeContent: this.form.changeReason, changeReason: this.form.changeReason }); this.$message.success('变更已提交'); this.$router.back(); },
|
this.feeGenerationMode =
|
||||||
|
data.feeGenerationMode || (Number(data.billingEnabled) === 0 ? 'manual' : 'system');
|
||||||
|
this.paymentRatioRows = this.parse(data.paymentRatioJson);
|
||||||
|
},
|
||||||
|
parse(value) {
|
||||||
|
try {
|
||||||
|
const result = JSON.parse(value || '[]');
|
||||||
|
return Array.isArray(result) ? result : [];
|
||||||
|
} catch {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
parseObject(value) {
|
||||||
|
try {
|
||||||
|
return {
|
||||||
|
autoGenerate: 1,
|
||||||
|
settlementType: '月结',
|
||||||
|
billCycleType: '固定截单日',
|
||||||
|
billCutoffDay: 25,
|
||||||
|
cycleDays: 15,
|
||||||
|
...(JSON.parse(value || '{}') || {}),
|
||||||
|
};
|
||||||
|
} catch {
|
||||||
|
return {
|
||||||
|
autoGenerate: 1,
|
||||||
|
settlementType: '月结',
|
||||||
|
billCycleType: '固定截单日',
|
||||||
|
billCutoffDay: 25,
|
||||||
|
cycleDays: 15,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
addPlan() {
|
||||||
|
this.planEditorIndex = -1;
|
||||||
|
this.planEditor = {
|
||||||
|
planName: `计费方案${this.plans.length + 1}`,
|
||||||
|
defaultPlan: !this.plans.length,
|
||||||
|
remark: '',
|
||||||
|
rules: [{}],
|
||||||
|
};
|
||||||
|
this.planDialogVisible = true;
|
||||||
|
},
|
||||||
|
editPlan(row, index) {
|
||||||
|
this.planEditorIndex = index;
|
||||||
|
this.planEditor = JSON.parse(JSON.stringify(row));
|
||||||
|
this.planDialogVisible = true;
|
||||||
|
},
|
||||||
|
toggleDefaultPlan(value) {
|
||||||
|
if (value)
|
||||||
|
this.plans.forEach(item => {
|
||||||
|
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 = '';
|
||||||
|
},
|
||||||
|
handleCycleTypeChange(value) {
|
||||||
|
if (value === '固定截单日' && !this.settlementRule.billCutoffDay)
|
||||||
|
this.settlementRule.billCutoffDay = 25;
|
||||||
|
if (value !== '固定截单日') this.settlementRule.billCutoffDay = '';
|
||||||
|
},
|
||||||
|
handleAttachment(event) {
|
||||||
|
const raw = event.raw;
|
||||||
|
if (raw)
|
||||||
|
this.attachments.push({
|
||||||
|
name: raw.name,
|
||||||
|
size: `${Math.ceil(raw.size / 1024)}KB`,
|
||||||
|
uploadTime: this.$dayjs().format('YYYY-MM-DD HH:mm:ss'),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleContractFileChange(list) {
|
||||||
|
this.contractFileRows = (list || []).map(item => ({
|
||||||
|
...item,
|
||||||
|
uploadTime: item.uploadTime || this.$dayjs().format('YYYY-MM-DD HH:mm:ss'),
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
attachmentUrl(row = {}) {
|
||||||
|
return row.url || row.link || row.fileUrl || row.downloadUrl || row.domain || '';
|
||||||
|
},
|
||||||
|
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() : '';
|
||||||
|
},
|
||||||
|
isAttachmentImage(row) {
|
||||||
|
return ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'].includes(this.attachmentExtension(row));
|
||||||
|
},
|
||||||
|
previewAttachment(row, rows = this.attachments) {
|
||||||
|
const url = this.attachmentUrl(row);
|
||||||
|
if (!url) {
|
||||||
|
this.$message.warning('附件地址为空,无法预览');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.isAttachmentImage(row)) {
|
||||||
|
this.imagePreviewUrls = (rows || [])
|
||||||
|
.filter(item => this.isAttachmentImage(item) && this.attachmentUrl(item))
|
||||||
|
.map(item => this.attachmentUrl(item));
|
||||||
|
this.imagePreviewIndex = Math.max(this.imagePreviewUrls.indexOf(url), 0);
|
||||||
|
this.imagePreviewVisible = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.previewFile = {
|
||||||
|
name: this.attachmentName(row),
|
||||||
|
url,
|
||||||
|
mimeType: row.mimeType || row.contentType || '',
|
||||||
|
};
|
||||||
|
this.documentPreviewVisible = true;
|
||||||
|
},
|
||||||
|
handlePreviewUnsupported() {
|
||||||
|
this.$message.warning('当前文件暂不支持在线预览');
|
||||||
|
},
|
||||||
|
handlePreviewError() {
|
||||||
|
this.$message.error('附件预览失败');
|
||||||
|
},
|
||||||
|
removeContractFile(index) {
|
||||||
|
this.contractFileRows.splice(index, 1);
|
||||||
|
},
|
||||||
|
handleContractFileBatchDownload() {
|
||||||
|
(this.selectedContractFiles.length
|
||||||
|
? this.selectedContractFiles
|
||||||
|
: this.contractFileRows
|
||||||
|
).forEach(row => {
|
||||||
|
if (row.url) window.open(row.url, '_blank');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
formatFileSize(value) {
|
||||||
|
const size = Number(value || 0);
|
||||||
|
return size > 1024 * 1024
|
||||||
|
? `${(size / 1024 / 1024).toFixed(2)}MB`
|
||||||
|
: `${Math.max(1, Math.ceil(size / 1024))}KB`;
|
||||||
|
},
|
||||||
|
handleChangeMaterial(event) {
|
||||||
|
if (event.raw) this.changeMaterials.push(event.raw);
|
||||||
|
},
|
||||||
|
addPaymentRatioRow() {
|
||||||
|
this.paymentRatioRows.push({
|
||||||
|
paymentTerm: `第${this.paymentRatioRows.length + 1}笔`,
|
||||||
|
ratioLimit: '',
|
||||||
|
remark: '',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
ratioInput(row, value) {
|
||||||
|
let nextValue = String(value ?? '').replace(/[^\d.]/g, '');
|
||||||
|
const parts = nextValue.split('.');
|
||||||
|
if (parts.length > 2) nextValue = `${parts.shift()}.${parts.join('')}`;
|
||||||
|
const [integerPart, decimalPart] = nextValue.split('.');
|
||||||
|
nextValue =
|
||||||
|
decimalPart === undefined ? integerPart : `${integerPart}.${decimalPart.slice(0, 2)}`;
|
||||||
|
if (Number(nextValue) > 100) nextValue = '100';
|
||||||
|
row.ratioLimit = nextValue;
|
||||||
|
},
|
||||||
|
async submit() {
|
||||||
|
await this.$refs.formRef.validate();
|
||||||
|
const total = this.paymentRatioRows.reduce(
|
||||||
|
(sum, row) => sum + Number(row.ratioLimit || 0),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
if (this.paymentRatioRows.length && Math.abs(total - 100) > 0.0001) {
|
||||||
|
this.$message.warning('付款比例上限合计必须等于100%');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const settlementRule = {
|
||||||
|
preSettlementConfig: this.preSettlementConfig,
|
||||||
|
formalSettlementConfig: this.formalSettlementConfig,
|
||||||
|
};
|
||||||
|
await api.submitChange({
|
||||||
|
...this.form,
|
||||||
|
startDate: this.period[0],
|
||||||
|
endDate: this.period[1],
|
||||||
|
feeGenerationMode: this.feeGenerationMode,
|
||||||
|
billingEnabled: this.feeGenerationMode === 'system' ? 1 : 0,
|
||||||
|
billingPlanJson: JSON.stringify(this.plans),
|
||||||
|
settlementRuleJson: JSON.stringify(settlementRule),
|
||||||
|
preSettlementConfigJson: JSON.stringify(this.preSettlementConfig),
|
||||||
|
formalSettlementConfigJson: JSON.stringify(this.formalSettlementConfig),
|
||||||
|
paymentRatioJson: JSON.stringify(this.paymentRatioRows),
|
||||||
|
contractFileJson: JSON.stringify(this.contractFileRows),
|
||||||
|
attachmentsJson: JSON.stringify(this.attachments),
|
||||||
|
changeContent: this.form.changeReason,
|
||||||
|
changeReason: this.form.changeReason,
|
||||||
|
});
|
||||||
|
this.$message.success('变更已提交');
|
||||||
|
this.$router.back();
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.contract-change-page { min-height: 100%; background: #fff; }
|
.contract-change-page {
|
||||||
.change-section { padding: 20px 24px; border-bottom: 1px solid #eff1f7; }
|
min-height: 100%;
|
||||||
.dialog-section-title { margin-bottom: 18px; font-size: 16px; font-weight: 600; }
|
background: #fff;
|
||||||
.dialog-section-title::before { display: inline-block; width: 4px; height: 16px; margin-right: 8px; vertical-align: -2px; background: #409eff; content: ''; }
|
}
|
||||||
.section-head { display: flex; align-items: center; justify-content: space-between; }
|
.change-section {
|
||||||
.change-table { margin: 16px 0; }
|
padding: 20px 24px;
|
||||||
.ratio-tip { margin-bottom: 8px; color: #f56c6c; }
|
border-bottom: 1px solid #eff1f7;
|
||||||
.unit { margin-left: 8px; }
|
}
|
||||||
.inline-field { display: flex; align-items: center; gap: 8px; }
|
.dialog-section-title {
|
||||||
.attachment-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }
|
margin-bottom: 18px;
|
||||||
.settlement-switch { display: flex; align-items: center; gap: 12px; margin-bottom: 16px; }
|
font-size: 16px;
|
||||||
.settlement-form { display: grid; grid-template-columns: repeat(3, minmax(240px, 1fr)); gap: 8px 28px; }
|
font-weight: 600;
|
||||||
.change-reason-section { border: 1px dashed #ff8f9a; margin: 20px 16px; }
|
}
|
||||||
.page-footer { display: flex; gap: 16px; padding: 20px 40px; border-top: 1px solid #eff1f7; }
|
.dialog-section-title::before {
|
||||||
|
display: inline-block;
|
||||||
|
width: 4px;
|
||||||
|
height: 16px;
|
||||||
|
margin-right: 8px;
|
||||||
|
vertical-align: -2px;
|
||||||
|
background: #409eff;
|
||||||
|
content: '';
|
||||||
|
}
|
||||||
|
.section-head {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
.change-table {
|
||||||
|
margin: 16px 0;
|
||||||
|
}
|
||||||
|
.ratio-tip {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
color: #f56c6c;
|
||||||
|
}
|
||||||
|
.unit {
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
.inline-field {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.attachment-head {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
.settlement-switch {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
.settlement-form {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, minmax(240px, 1fr));
|
||||||
|
gap: 8px 28px;
|
||||||
|
}
|
||||||
|
.change-reason-section {
|
||||||
|
border: 1px dashed #ff8f9a;
|
||||||
|
margin: 20px 16px;
|
||||||
|
}
|
||||||
|
.page-footer {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
padding: 20px 40px;
|
||||||
|
border-top: 1px solid #eff1f7;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -177,7 +177,7 @@
|
|||||||
filterable
|
filterable
|
||||||
clearable
|
clearable
|
||||||
:loading="contractPartyLoading"
|
:loading="contractPartyLoading"
|
||||||
:disabled="!form.projectId"
|
@visible-change="visible => visible && loadContractPartyOptions()"
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in contractPartyAOptions"
|
v-for="item in contractPartyAOptions"
|
||||||
@@ -194,7 +194,7 @@
|
|||||||
filterable
|
filterable
|
||||||
clearable
|
clearable
|
||||||
:loading="contractPartyLoading"
|
:loading="contractPartyLoading"
|
||||||
:disabled="!form.projectId"
|
@visible-change="visible => visible && loadContractPartyOptions()"
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in contractPartyBOptions"
|
v-for="item in contractPartyBOptions"
|
||||||
@@ -329,16 +329,11 @@
|
|||||||
<el-table :data="billingPlanRows" border>
|
<el-table :data="billingPlanRows" border>
|
||||||
<el-table-column type="index" label="序号" width="80" align="center" />
|
<el-table-column type="index" label="序号" width="80" align="center" />
|
||||||
<el-table-column prop="planName" label="方案名称" min-width="220" align="center" />
|
<el-table-column prop="planName" label="方案名称" min-width="220" align="center" />
|
||||||
<el-table-column label="默认方案" width="140" align="center">
|
<el-table-column label="运输方式" min-width="180" align="center">
|
||||||
<template #default="{ row }">{{ isDefaultBillingPlan(row) ? '是' : '' }}</template>
|
<template #default="{ row }">{{
|
||||||
|
row.transportModeLabel || row.transportMode || '-'
|
||||||
|
}}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
|
||||||
prop="remark"
|
|
||||||
label="备注"
|
|
||||||
min-width="260"
|
|
||||||
align="center"
|
|
||||||
show-overflow-tooltip
|
|
||||||
/>
|
|
||||||
<el-table-column label="操作" width="180" align="center" fixed="right">
|
<el-table-column label="操作" width="180" align="center" fixed="right">
|
||||||
<template #default="{ row, $index }">
|
<template #default="{ row, $index }">
|
||||||
<el-link type="primary" @click="openBillingPlan(row, $index)">编辑</el-link>
|
<el-link type="primary" @click="openBillingPlan(row, $index)">编辑</el-link>
|
||||||
@@ -436,14 +431,13 @@
|
|||||||
<el-table-column prop="paymentTerm" label="付款笔数" min-width="180" 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 }">
|
<template #default="{ row }">
|
||||||
<el-input-number
|
<el-input
|
||||||
v-model="row.ratioLimit"
|
v-model="row.ratioLimit"
|
||||||
:min="0"
|
placeholder="请输入"
|
||||||
:max="100"
|
@input="value => ratioInput(row, value)"
|
||||||
:precision="2"
|
>
|
||||||
controls-position="right"
|
<template #append>%</template>
|
||||||
/>
|
</el-input>
|
||||||
<span class="contract-manage-form__ratio-unit">%</span>
|
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="备注" min-width="220">
|
<el-table-column label="备注" min-width="220">
|
||||||
@@ -537,6 +531,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<billing-plan-editor
|
<billing-plan-editor
|
||||||
|
ref="billingPlanEditor"
|
||||||
v-model="billingPlanBox"
|
v-model="billingPlanBox"
|
||||||
:value="billingPlanForm"
|
:value="billingPlanForm"
|
||||||
:index="billingPlanIndex"
|
:index="billingPlanIndex"
|
||||||
@@ -653,16 +648,11 @@
|
|||||||
<el-table :data="detailBillingPlanRows" border>
|
<el-table :data="detailBillingPlanRows" border>
|
||||||
<el-table-column type="index" label="序号" width="80" align="center" />
|
<el-table-column type="index" label="序号" width="80" align="center" />
|
||||||
<el-table-column prop="planName" label="方案名称" min-width="220" align="center" />
|
<el-table-column prop="planName" label="方案名称" min-width="220" align="center" />
|
||||||
<el-table-column label="默认方案" width="140" align="center">
|
<el-table-column label="运输方式" min-width="180" align="center">
|
||||||
<template #default="{ row }">{{ isDefaultBillingPlan(row) ? '是' : '' }}</template>
|
<template #default="{ row }">{{
|
||||||
|
row.transportModeLabel || row.transportMode || '-'
|
||||||
|
}}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
|
||||||
prop="remark"
|
|
||||||
label="备注"
|
|
||||||
min-width="260"
|
|
||||||
align="center"
|
|
||||||
show-overflow-tooltip
|
|
||||||
/>
|
|
||||||
<el-table-column label="操作" width="120" align="center" fixed="right">
|
<el-table-column label="操作" width="120" align="center" fixed="right">
|
||||||
<template #default="{ row, $index }">
|
<template #default="{ row, $index }">
|
||||||
<el-link type="primary" @click="openDetailBillingPlan(row, $index)"
|
<el-link type="primary" @click="openDetailBillingPlan(row, $index)"
|
||||||
@@ -799,10 +789,8 @@ import NProgress from 'nprogress';
|
|||||||
import 'nprogress/nprogress.css';
|
import 'nprogress/nprogress.css';
|
||||||
import { exportBlob } from '@/api/common';
|
import { exportBlob } from '@/api/common';
|
||||||
import * as api from '@/api/business/contract-manage';
|
import * as api from '@/api/business/contract-manage';
|
||||||
import {
|
import { getList as getProjectList } from '@/api/business/project-apply';
|
||||||
getDetail as getProjectDetail,
|
import { getList as getCustomerArchiveList } from '@/api/vehicle/customer-archive';
|
||||||
getList as getProjectList,
|
|
||||||
} from '@/api/business/project-apply';
|
|
||||||
import { getDeptTree } from '@/api/system/dept';
|
import { getDeptTree } from '@/api/system/dept';
|
||||||
import { config, option } from '@/option/business/contract-manage';
|
import { config, option } from '@/option/business/contract-manage';
|
||||||
import { getToken } from '@/utils/auth';
|
import { getToken } from '@/utils/auth';
|
||||||
@@ -1463,7 +1451,10 @@ export default {
|
|||||||
},
|
},
|
||||||
initFormPage() {
|
initFormPage() {
|
||||||
this.resetFormState();
|
this.resetFormState();
|
||||||
if (this.formMode === 'add') return;
|
if (this.formMode === 'add') {
|
||||||
|
this.loadContractPartyOptions();
|
||||||
|
return;
|
||||||
|
}
|
||||||
const id = this.$route.query.id;
|
const id = this.$route.query.id;
|
||||||
if (!id) return;
|
if (!id) return;
|
||||||
this.api.getDetail(id).then(res => this.applyDetail(res.data?.data || {}));
|
this.api.getDetail(id).then(res => this.applyDetail(res.data?.data || {}));
|
||||||
@@ -1521,7 +1512,7 @@ export default {
|
|||||||
this.settlementRuleForm = { ...this.preSettlementRuleForm };
|
this.settlementRuleForm = { ...this.preSettlementRuleForm };
|
||||||
this.syncCurrentProjectOption();
|
this.syncCurrentProjectOption();
|
||||||
this.syncCurrentOrganizationOption();
|
this.syncCurrentOrganizationOption();
|
||||||
this.loadContractPartyOptions(detail.projectId, detail, true);
|
this.loadContractPartyOptions();
|
||||||
},
|
},
|
||||||
closeForm() {
|
closeForm() {
|
||||||
this.$router.$avueRouter?.closeTag?.();
|
this.$router.$avueRouter?.closeTag?.();
|
||||||
@@ -1650,76 +1641,29 @@ export default {
|
|||||||
handleProjectChange(projectId) {
|
handleProjectChange(projectId) {
|
||||||
const project = this.projectOptions.find(item => String(item.id) === String(projectId));
|
const project = this.projectOptions.find(item => String(item.id) === String(projectId));
|
||||||
if (!project) {
|
if (!project) {
|
||||||
Object.assign(this.form, { projectId: '', projectName: '', partyA: '', partyB: '' });
|
Object.assign(this.form, { projectId: '', projectName: '' });
|
||||||
this.contractPartyAOptions = [];
|
|
||||||
this.contractPartyBOptions = [];
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Object.assign(this.form, {
|
Object.assign(this.form, {
|
||||||
projectId: project.id,
|
projectId: project.id,
|
||||||
projectName: project.projectName || '',
|
projectName: project.projectName || '',
|
||||||
partyA: '',
|
|
||||||
partyB: '',
|
|
||||||
});
|
});
|
||||||
this.loadContractPartyOptions(project.id, project, false);
|
|
||||||
},
|
},
|
||||||
partyNames(value, type) {
|
loadContractPartyOptions() {
|
||||||
const names = [];
|
if (this.contractPartyLoading) return;
|
||||||
const append = item => {
|
|
||||||
if (item === undefined || item === null) return;
|
|
||||||
if (Array.isArray(item)) return item.forEach(append);
|
|
||||||
if (typeof item === 'object') {
|
|
||||||
append(
|
|
||||||
item[type === 'carrier' ? 'carrier' : 'customer'] ||
|
|
||||||
item.fullName ||
|
|
||||||
item.shortName ||
|
|
||||||
item.customerName ||
|
|
||||||
item.carrierName ||
|
|
||||||
item.name
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const text = String(item).trim();
|
|
||||||
if (!text) return;
|
|
||||||
try {
|
|
||||||
const parsed = JSON.parse(text);
|
|
||||||
if (parsed !== item) return append(parsed);
|
|
||||||
} catch (error) {
|
|
||||||
// 普通名称按项目维护时使用的分隔符拆分。
|
|
||||||
}
|
|
||||||
text
|
|
||||||
.split(/[、,,;;\n]/)
|
|
||||||
.map(name => name.trim())
|
|
||||||
.filter(Boolean)
|
|
||||||
.forEach(name => {
|
|
||||||
if (!names.includes(name)) names.push(name);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
append(value);
|
|
||||||
return names;
|
|
||||||
},
|
|
||||||
syncContractPartyOptions(project, preserve) {
|
|
||||||
const customers = this.partyNames(
|
|
||||||
[project.customerJson, project.customerNames, project.customerName, project.customers],
|
|
||||||
'customer'
|
|
||||||
);
|
|
||||||
const carriers = this.partyNames(
|
|
||||||
[project.carrierJson, project.carrierNames, project.carrierName, project.carriers],
|
|
||||||
'carrier'
|
|
||||||
);
|
|
||||||
this.contractPartyAOptions = customers.map(name => ({ label: name, value: name }));
|
|
||||||
this.contractPartyBOptions = carriers.map(name => ({ label: name, value: name }));
|
|
||||||
if (!preserve) Object.assign(this.form, { partyA: '', partyB: '' });
|
|
||||||
},
|
|
||||||
loadContractPartyOptions(projectId, project = {}, preserve = true) {
|
|
||||||
if (!projectId) return;
|
|
||||||
const requestId = ++this.contractPartyRequestId;
|
const requestId = ++this.contractPartyRequestId;
|
||||||
this.syncContractPartyOptions(project, preserve);
|
|
||||||
this.contractPartyLoading = true;
|
this.contractPartyLoading = true;
|
||||||
getProjectDetail(projectId)
|
getCustomerArchiveList(1, 9999, { status: 1, approvalStatus: 'approved' })
|
||||||
.then(res => {
|
.then(res => {
|
||||||
if (requestId !== this.contractPartyRequestId) return;
|
if (requestId !== this.contractPartyRequestId) return;
|
||||||
this.syncContractPartyOptions({ ...project, ...(res.data?.data || {}) }, preserve);
|
const names = extractRecords(res)
|
||||||
|
.map(item => item.fullName || item.shortName || item.customerName || item.customerCode)
|
||||||
|
.map(name => String(name || '').trim())
|
||||||
|
.filter(Boolean);
|
||||||
|
[this.form.partyA, this.form.partyB].filter(Boolean).forEach(name => names.unshift(name));
|
||||||
|
const options = [...new Set(names)].map(name => ({ label: name, value: name }));
|
||||||
|
this.contractPartyAOptions = options;
|
||||||
|
this.contractPartyBOptions = options;
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
if (requestId === this.contractPartyRequestId) this.contractPartyLoading = false;
|
if (requestId === this.contractPartyRequestId) this.contractPartyLoading = false;
|
||||||
@@ -1822,14 +1766,19 @@ export default {
|
|||||||
openBillingPlan(row = {}, index = -1) {
|
openBillingPlan(row = {}, index = -1) {
|
||||||
this.billingPlanIndex = index;
|
this.billingPlanIndex = index;
|
||||||
this.billingPlanForm =
|
this.billingPlanForm =
|
||||||
index < 0 ? { planName: '', defaultPlan: false, remark: '', rules: [] } : clone(row);
|
index < 0
|
||||||
|
? { planName: '', transportMode: '', defaultPlan: false, remark: '', rules: [] }
|
||||||
|
: clone(row);
|
||||||
this.billingPlanBox = true;
|
this.billingPlanBox = true;
|
||||||
|
this.$nextTick(() => this.$refs.billingPlanEditor?.ensureTransportModes?.());
|
||||||
},
|
},
|
||||||
saveBillingPlan(plan, index) {
|
saveBillingPlan(plan, index) {
|
||||||
const nextPlan = clone(plan);
|
const nextPlan = clone(plan);
|
||||||
if (this.isDefaultBillingPlan(nextPlan)) {
|
if (this.isDefaultBillingPlan(nextPlan)) {
|
||||||
this.billingPlanRows.forEach((item, itemIndex) => {
|
this.billingPlanRows.forEach((item, itemIndex) => {
|
||||||
if (itemIndex !== index) item.defaultPlan = false;
|
if (itemIndex !== index && item.transportMode === nextPlan.transportMode) {
|
||||||
|
item.defaultPlan = false;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (index < 0) this.billingPlanRows.push(nextPlan);
|
if (index < 0) this.billingPlanRows.push(nextPlan);
|
||||||
@@ -1850,9 +1799,18 @@ export default {
|
|||||||
},
|
},
|
||||||
validateBillingPlanDefault() {
|
validateBillingPlanDefault() {
|
||||||
if (!this.billingPlanRows.length) return true;
|
if (!this.billingPlanRows.length) return true;
|
||||||
const count = this.billingPlanRows.filter(this.isDefaultBillingPlan).length;
|
const defaultCountByTransportMode = this.billingPlanRows.reduce((result, plan) => {
|
||||||
if (count !== 1) {
|
const transportMode = plan.transportMode || '';
|
||||||
this.$message.warning(count ? '请仅保留一个默认计费方案' : '请勾选一个默认计费方案');
|
result[transportMode] =
|
||||||
|
(result[transportMode] || 0) + Number(this.isDefaultBillingPlan(plan));
|
||||||
|
return result;
|
||||||
|
}, {});
|
||||||
|
const invalidTransportMode = Object.entries(defaultCountByTransportMode).find(
|
||||||
|
([, count]) => count > 1
|
||||||
|
);
|
||||||
|
if (invalidTransportMode) {
|
||||||
|
const [transportMode, count] = invalidTransportMode;
|
||||||
|
this.$message.warning(`运输方式“${transportMode}”仅支持配置一个默认计费方案`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -1936,6 +1894,16 @@ export default {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
ratioInput(row, value) {
|
||||||
|
let nextValue = String(value ?? '').replace(/[^\d.]/g, '');
|
||||||
|
const parts = nextValue.split('.');
|
||||||
|
if (parts.length > 2) nextValue = `${parts.shift()}.${parts.join('')}`;
|
||||||
|
const [integerPart, decimalPart] = nextValue.split('.');
|
||||||
|
nextValue =
|
||||||
|
decimalPart === undefined ? integerPart : `${integerPart}.${decimalPart.slice(0, 2)}`;
|
||||||
|
if (Number(nextValue) > 100) nextValue = '100';
|
||||||
|
row.ratioLimit = nextValue;
|
||||||
|
},
|
||||||
openDetail(row) {
|
openDetail(row) {
|
||||||
this.detailBox = true;
|
this.detailBox = true;
|
||||||
this.detailLoading = true;
|
this.detailLoading = true;
|
||||||
|
|||||||
@@ -1676,10 +1676,25 @@ export default {
|
|||||||
loadDeptOptions() {
|
loadDeptOptions() {
|
||||||
getDeptTree().then(res => {
|
getDeptTree().then(res => {
|
||||||
const deptTree = res.data.data || [];
|
const deptTree = res.data.data || [];
|
||||||
|
const undertakeDeptTree =
|
||||||
|
this.dialogType === 'add' ? this.excludeExternalOrganization(deptTree) : deptTree;
|
||||||
this.deptOptions = this.flattenDept(deptTree);
|
this.deptOptions = this.flattenDept(deptTree);
|
||||||
this.deptTreeOptions = this.toDeptCascaderOptions(deptTree);
|
this.deptTreeOptions = this.toDeptCascaderOptions(undertakeDeptTree);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
excludeExternalOrganization(list = []) {
|
||||||
|
return list.reduce((result, item) => {
|
||||||
|
const deptName = item.title || item.deptName || item.name;
|
||||||
|
if (String(deptName || '').trim() === '外部组织') return result;
|
||||||
|
result.push({
|
||||||
|
...item,
|
||||||
|
children: item.children?.length
|
||||||
|
? this.excludeExternalOrganization(item.children)
|
||||||
|
: item.children,
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}, []);
|
||||||
|
},
|
||||||
toDeptCascaderOptions(list = []) {
|
toDeptCascaderOptions(list = []) {
|
||||||
return list.map(item => ({
|
return list.map(item => ({
|
||||||
label: item.title || item.deptName || item.name,
|
label: item.title || item.deptName || item.name,
|
||||||
|
|||||||
@@ -719,7 +719,6 @@ export default {
|
|||||||
this.contractLoading = true;
|
this.contractLoading = true;
|
||||||
try {
|
try {
|
||||||
const res = await getContractList(1, 999, {
|
const res = await getContractList(1, 999, {
|
||||||
approvalStatuses: 'approved,change_approved',
|
|
||||||
contractCategory,
|
contractCategory,
|
||||||
});
|
});
|
||||||
const data = this.unwrapPage(res);
|
const data = this.unwrapPage(res);
|
||||||
|
|||||||
@@ -731,7 +731,7 @@
|
|||||||
@close="qualificationImagePreviewVisible = false"
|
@close="qualificationImagePreviewVisible = false"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<section-card title="变更记录">
|
<section-card v-if="archiveForm.id" title="变更记录">
|
||||||
<el-table :data="changeRecordPage.records" border :show-overflow-tooltip="false">
|
<el-table :data="changeRecordPage.records" border :show-overflow-tooltip="false">
|
||||||
<el-table-column
|
<el-table-column
|
||||||
label="序号"
|
label="序号"
|
||||||
@@ -3510,6 +3510,8 @@ export default {
|
|||||||
customerType: this.splitValue(detail.customerType),
|
customerType: this.splitValue(detail.customerType),
|
||||||
invoiceTaxRate: this.normalizeOptionalAmount(detail.invoiceTaxRate),
|
invoiceTaxRate: this.normalizeOptionalAmount(detail.invoiceTaxRate),
|
||||||
registeredCapital: this.normalizeOptionalAmount(detail.registeredCapital),
|
registeredCapital: this.normalizeOptionalAmount(detail.registeredCapital),
|
||||||
|
maxCreditLimit: this.normalizeOptionalAmount(detail.maxCreditLimit),
|
||||||
|
applyCreditLimit: this.normalizeOptionalAmount(detail.applyCreditLimit),
|
||||||
businessScope,
|
businessScope,
|
||||||
businessTermType: detail.businessTermType || '固定期限',
|
businessTermType: detail.businessTermType || '固定期限',
|
||||||
registeredRegionPath: Array.isArray(detail.registeredRegionPath)
|
registeredRegionPath: Array.isArray(detail.registeredRegionPath)
|
||||||
@@ -3549,6 +3551,12 @@ export default {
|
|||||||
details: [],
|
details: [],
|
||||||
...score,
|
...score,
|
||||||
quantificationId: score.quantificationId ? String(score.quantificationId) : '',
|
quantificationId: score.quantificationId ? String(score.quantificationId) : '',
|
||||||
|
applyCreditLimit: this.normalizeOptionalAmount(
|
||||||
|
score.applyCreditLimit === undefined
|
||||||
|
? this.archiveForm.applyCreditLimit
|
||||||
|
: score.applyCreditLimit
|
||||||
|
),
|
||||||
|
maxCreditLimit: this.normalizeOptionalAmount(score.maxCreditLimit),
|
||||||
tempApplyCreditLimit: this.normalizeOptionalAmount(score.tempApplyCreditLimit),
|
tempApplyCreditLimit: this.normalizeOptionalAmount(score.tempApplyCreditLimit),
|
||||||
attachments: score.attachments || this.parseAttachments(score.proofAttachments),
|
attachments: score.attachments || this.parseAttachments(score.proofAttachments),
|
||||||
details: (score.details || []).map(detail => ({
|
details: (score.details || []).map(detail => ({
|
||||||
@@ -3733,8 +3741,22 @@ export default {
|
|||||||
archive.invoices = (archive.invoices || [])
|
archive.invoices = (archive.invoices || [])
|
||||||
.map(item => this.normalizeInvoice(item))
|
.map(item => this.normalizeInvoice(item))
|
||||||
.filter(item => item.invoiceTitle || item.taxNo || item.bankAccount);
|
.filter(item => item.invoiceTitle || item.taxNo || item.bankAccount);
|
||||||
archive.scores = (archive.scores || []).map(score => this.calculateScore(score));
|
archive.scores = (archive.scores || []).map(score => {
|
||||||
|
const calculatedScore = this.calculateScore(score);
|
||||||
|
const scoreMaxCreditLimit = this.normalizeOptionalAmount(calculatedScore.maxCreditLimit);
|
||||||
|
const scoreApplyCreditLimit = this.normalizeOptionalAmount(
|
||||||
|
calculatedScore.applyCreditLimit
|
||||||
|
);
|
||||||
|
calculatedScore.maxCreditLimit = scoreMaxCreditLimit === '' ? null : scoreMaxCreditLimit;
|
||||||
|
calculatedScore.applyCreditLimit =
|
||||||
|
scoreApplyCreditLimit === '' ? null : scoreApplyCreditLimit;
|
||||||
|
return calculatedScore;
|
||||||
|
});
|
||||||
this.applyArchiveCreditFromScores(archive);
|
this.applyArchiveCreditFromScores(archive);
|
||||||
|
const maxCreditLimit = this.normalizeOptionalAmount(archive.maxCreditLimit);
|
||||||
|
const applyCreditLimit = this.normalizeOptionalAmount(archive.applyCreditLimit);
|
||||||
|
archive.maxCreditLimit = maxCreditLimit === '' ? null : maxCreditLimit;
|
||||||
|
archive.applyCreditLimit = applyCreditLimit === '' ? null : applyCreditLimit;
|
||||||
return archive;
|
return archive;
|
||||||
},
|
},
|
||||||
saveArchive() {
|
saveArchive() {
|
||||||
|
|||||||
+13
-13
@@ -50,26 +50,26 @@ export default ({
|
|||||||
__VUE_I18N_LEGACY_API__: true,
|
__VUE_I18N_LEGACY_API__: true,
|
||||||
__INTLIFY_PROD_DEVTOOLS__: false,
|
__INTLIFY_PROD_DEVTOOLS__: false,
|
||||||
},
|
},
|
||||||
// server: {
|
|
||||||
// port: 2888,
|
|
||||||
// proxy: {
|
|
||||||
// '/api': {
|
|
||||||
// target: 'http://localhost',
|
|
||||||
// //target: 'https://saber3.bladex.cn/api',
|
|
||||||
// changeOrigin: true,
|
|
||||||
// rewrite: path => path.replace(/^\/api/, ''),
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
server: {
|
server: {
|
||||||
port: 2889,
|
port: 2888,
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api': {
|
'/api': {
|
||||||
target: 'http://172.16.203.228:8000',
|
target: 'http://localhost',
|
||||||
|
//target: 'https://saber3.bladex.cn/api',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
|
rewrite: path => path.replace(/^\/api/, ''),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// server: {
|
||||||
|
// port: 2889,
|
||||||
|
// proxy: {
|
||||||
|
// '/api': {
|
||||||
|
// target: 'http://172.16.203.228:8000',
|
||||||
|
// changeOrigin: true,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// },
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
'~': resolve(__dirname, './'),
|
'~': resolve(__dirname, './'),
|
||||||
|
|||||||
Reference in New Issue
Block a user