1、调整收付款

2、调整基础配置
3、调整结算
This commit is contained in:
2026-09-01 17:35:53 +08:00
parent f858eb2bcd
commit 80a15fc626
7 changed files with 932 additions and 121 deletions
+12
View File
@@ -10,6 +10,18 @@ export const getDetail = id => request({ url: `${baseUrl}/detail`, method: 'get'
export const getContractOptions = keyword => export const getContractOptions = keyword =>
request({ url: `${baseUrl}/contract-options`, method: 'get', params: { keyword } }); request({ url: `${baseUrl}/contract-options`, method: 'get', params: { keyword } });
export const getContractList = (current, size, params) =>
request({
url: '/blade-transport/contract-manage/list',
method: 'get',
params: {
current,
size,
approvalStatuses: 'approved,change_approved',
...params,
},
});
export const getFeeOptions = () => request({ url: `${baseUrl}/fee-options`, method: 'get' }); export const getFeeOptions = () => request({ url: `${baseUrl}/fee-options`, method: 'get' });
export const getCandidateDetails = (current, size, params) => export const getCandidateDetails = (current, size, params) =>
@@ -138,4 +138,11 @@ export const generatePreviewColumns = [
{ label: '货物名称', prop: 'cargoName', minWidth: 140 }, { label: '货物名称', prop: 'cargoName', minWidth: 140 },
{ label: '货物类型', prop: 'cargoType', minWidth: 140 }, { label: '货物类型', prop: 'cargoType', minWidth: 140 },
{ label: '规格', prop: 'specification', minWidth: 140 }, { label: '规格', prop: 'specification', minWidth: 140 },
{ label: '型号', prop: 'model', minWidth: 120 },
{ label: '计费要素', prop: 'billingFactor', minWidth: 130 },
{ label: '计费类型', prop: 'billingType', minWidth: 130 },
{ label: '运输量', prop: 'transportQuantityText', minWidth: 120 },
{ label: '运费计算单位', prop: 'priceUnit', minWidth: 130 },
{ label: '运输单价', prop: 'unitPrice', minWidth: 120 },
{ label: '里程(KM', prop: 'mileage', minWidth: 120 },
]; ];
+49 -19
View File
@@ -35,7 +35,6 @@
v-model="form.feeCategory" v-model="form.feeCategory"
class="fee-item-form-control" class="fee-item-form-control"
clearable clearable
:disabled="dialogType !== 'add'"
filterable filterable
placeholder="请选择费用类型" placeholder="请选择费用类型"
@change="handleFeeCategoryChange" @change="handleFeeCategoryChange"
@@ -43,7 +42,7 @@
<el-option <el-option
v-for="item in feeCategoryOptions" v-for="item in feeCategoryOptions"
:key="item.id || item.dictKey" :key="item.id || item.dictKey"
:label="item.dictValue" :label="`${item.dictValue}/${item.dictKey}`"
:value="item.dictKey" :value="item.dictKey"
/> />
</el-select> </el-select>
@@ -54,7 +53,6 @@
:maxlength="feeItemCodeMaxlength" :maxlength="feeItemCodeMaxlength"
class="fee-item-form-control" class="fee-item-form-control"
clearable clearable
:disabled="dialogType !== 'add'"
placeholder="请输入费用项代码" placeholder="请输入费用项代码"
@change="handleFeeItemCodeChange" @change="handleFeeItemCodeChange"
> >
@@ -339,31 +337,63 @@ export default {
values.englishName = this.getFeeItemCodeSuffix(values.englishName, values.feeCategory); values.englishName = this.getFeeItemCodeSuffix(values.englishName, values.feeCategory);
return values; return values;
}, },
validateUnique(row) {
const rowId = String(row.id || '');
const hasDuplicate = (params, prop) => {
if (!row[prop]) {
return Promise.resolve(false);
}
return getList(1, 100, params).then(res => {
const records = res.data.data.records || [];
return records.some(
item =>
String(item[prop] || '').trim() === String(row[prop]).trim() &&
String(item.id || '') !== rowId
);
});
};
return Promise.all([
hasDuplicate({ name: row.name }, 'name'),
hasDuplicate({ englishName: row.englishName }, 'englishName'),
]).then(([nameExists, codeExists]) => {
if (nameExists) {
return Promise.reject(new Error('该费用项已存在'));
}
if (codeExists) {
return Promise.reject(new Error('该费用项代码已存在'));
}
return Promise.resolve();
});
},
handleSubmitError(error, loading) {
const uniqueMessages = ['该费用项已存在', '该费用项代码已存在'];
if (uniqueMessages.includes(error.message)) {
this.$message.warning(error.message);
}
window.console.log(error);
loading();
},
rowSave(row, done, loading) { rowSave(row, done, loading) {
submit(this.normalizeRow(row)).then( const submitRow = this.normalizeRow(row);
() => { this.validateUnique(submitRow)
.then(() => submit(submitRow))
.then(() => {
this.onLoad(this.page); this.onLoad(this.page);
this.$message({ type: 'success', message: '操作成功!' }); this.$message({ type: 'success', message: '操作成功!' });
done(); done();
}, })
error => { .catch(error => this.handleSubmitError(error, loading));
window.console.log(error);
loading();
}
);
}, },
rowUpdate(row, index, done, loading) { rowUpdate(row, index, done, loading) {
submit(this.normalizeRow(row)).then( const submitRow = this.normalizeRow(row);
() => { this.validateUnique(submitRow)
.then(() => submit(submitRow))
.then(() => {
this.onLoad(this.page); this.onLoad(this.page);
this.$message({ type: 'success', message: '操作成功!' }); this.$message({ type: 'success', message: '操作成功!' });
done(); done();
}, })
error => { .catch(error => this.handleSubmitError(error, loading));
window.console.log(error);
loading();
}
);
}, },
rowDel(row) { rowDel(row) {
this.$confirm('确定将选择数据删除?', { this.$confirm('确定将选择数据删除?', {
+3 -1
View File
@@ -110,7 +110,9 @@
maxlength="24" maxlength="24"
placeholder="请输入码头标识" placeholder="请输入码头标识"
@input="handleTerminalCodeChange" @input="handleTerminalCodeChange"
/> >
<template v-if="form.parentCode" #prepend>{{ form.parentCode }}</template>
</el-input>
</div> </div>
</template> </template>
<template #menu="{ row, index }"> <template #menu="{ row, index }">
+12 -6
View File
@@ -358,7 +358,10 @@ export default {
formslot: true, formslot: true,
minWidth: 130, minWidth: 130,
overHidden: false, overHidden: false,
rules: [{ validator: validateLongitude, trigger: 'blur' }], rules: [
{ required: true, message: '请输入经度', trigger: 'blur' },
{ validator: validateLongitude, trigger: 'blur' },
],
}, },
{ {
label: '纬度', label: '纬度',
@@ -366,7 +369,10 @@ export default {
formslot: true, formslot: true,
minWidth: 130, minWidth: 130,
overHidden: false, overHidden: false,
rules: [{ validator: validateLatitude, trigger: 'blur' }], rules: [
{ required: true, message: '请输入纬度', trigger: 'blur' },
{ validator: validateLatitude, trigger: 'blur' },
],
}, },
{ {
label: '行政区划编号', label: '行政区划编号',
@@ -661,14 +667,14 @@ export default {
return row; return row;
}, },
validateCoordinateFields(row) { validateCoordinateFields(row) {
const longitudeMessage = getCoordinateValidationMessage(row.longitude, 'longitude'); const longitudeMessage = getCoordinateValidationMessage(row.longitude, 'longitude', true);
if (longitudeMessage) { if (longitudeMessage) {
this.$message.warning('经度范围不正确'); this.$message.warning(longitudeMessage);
return false; return false;
} }
const latitudeMessage = getCoordinateValidationMessage(row.latitude, 'latitude'); const latitudeMessage = getCoordinateValidationMessage(row.latitude, 'latitude', true);
if (latitudeMessage) { if (latitudeMessage) {
this.$message.warning('纬度范围不正确'); this.$message.warning(latitudeMessage);
return false; return false;
} }
return true; return true;
@@ -18,25 +18,22 @@
<el-row :gutter="24"> <el-row :gutter="24">
<el-col v-for="field in formFields" :key="field.prop" :span="field.span || 6"> <el-col v-for="field in formFields" :key="field.prop" :span="field.span || 6">
<el-form-item :label="field.label" :prop="field.prop"> <el-form-item :label="field.label" :prop="field.prop">
<el-select <el-input
v-if="field.type === 'contract'" v-if="field.type === 'contract'"
v-model="form.contractId" v-model="form.contractName"
filterable readonly
remote
clearable
:remote-method="loadContractOptions"
:loading="contractLoading"
:disabled="!editable || details.length > 0" :disabled="!editable || details.length > 0"
placeholder="请选择合同" placeholder="请选择合同"
@change="handleContractChange"
> >
<el-option <template #append>
v-for="item in contractOptions" <el-button
:key="item.id" :disabled="!editable || details.length > 0"
:label="`${item.contractName}${item.contractNo || '-'}`" @click="openContractDialog"
:value="item.id" >
/> 选择
</el-select> </el-button>
</template>
</el-input>
<el-date-picker <el-date-picker
v-else-if="field.type === 'date'" v-else-if="field.type === 'date'"
v-model="form[field.prop]" v-model="form[field.prop]"
@@ -126,6 +123,9 @@
<el-input-number <el-input-number
v-else-if="editable && column.prop === 'adjustAmount'" v-else-if="editable && column.prop === 'adjustAmount'"
v-model="row.adjustAmount" v-model="row.adjustAmount"
:class="{
'pre-settlement-editor__negative-amount': Number(row.adjustAmount || 0) < 0,
}"
:precision="2" :precision="2"
:controls="false" :controls="false"
@change="recalculateSummaryRow(row)" @change="recalculateSummaryRow(row)"
@@ -136,7 +136,13 @@
maxlength="50" maxlength="50"
show-word-limit show-word-limit
/> />
<span v-else-if="isSummaryMoney(column.prop)"> <span
v-else-if="isSummaryMoney(column.prop)"
:class="{
'pre-settlement-editor__negative-amount':
column.prop === 'adjustAmount' && Number(row.adjustAmount || 0) < 0,
}"
>
{{ formatMoney(row[column.prop], form.currency) }} {{ formatMoney(row[column.prop], form.currency) }}
</span> </span>
<span v-else-if="column.prop === 'feeType'"> <span v-else-if="column.prop === 'feeType'">
@@ -393,7 +399,9 @@
<template v-if="!pageMode" #footer> <template v-if="!pageMode" #footer>
<el-button @click="visible = false">取消</el-button> <el-button @click="visible = false">取消</el-button>
<el-button v-if="editable" type="primary" plain :loading="saving" @click="saveDraft(false)">保存</el-button> <el-button v-if="editable" type="primary" plain :loading="saving" @click="saveDraft(false)"
>保存</el-button
>
<el-button <el-button
v-if="editable && hasPermission('pre_settlement_submit')" v-if="editable && hasPermission('pre_settlement_submit')"
type="primary" type="primary"
@@ -405,7 +413,9 @@
</template> </template>
<div v-if="pageMode" class="pre-settlement-editor__page-actions"> <div v-if="pageMode" class="pre-settlement-editor__page-actions">
<el-button @click="visible = false">取消</el-button> <el-button @click="visible = false">取消</el-button>
<el-button v-if="editable" type="primary" plain :loading="saving" @click="saveDraft(false)">保存</el-button> <el-button v-if="editable" type="primary" plain :loading="saving" @click="saveDraft(false)"
>保存</el-button
>
<el-button <el-button
v-if="editable && hasPermission('pre_settlement_submit')" v-if="editable && hasPermission('pre_settlement_submit')"
type="primary" type="primary"
@@ -492,6 +502,164 @@
</template> </template>
</el-dialog> </el-dialog>
<el-dialog
v-model="contractDialog.visible"
title="选择合同"
width="92%"
append-to-body
destroy-on-close
class="pre-settlement-contract-dialog"
>
<el-form
:model="contractDialog.query"
inline
label-position="right"
label-width="auto"
class="pre-settlement-editor__contract-filter"
@submit.prevent
>
<el-form-item label="合同编号">
<el-input
v-model="contractDialog.query.contractNo"
clearable
placeholder="请输入"
@keyup.enter="loadContractRows"
/>
</el-form-item>
<el-form-item label="合同名称">
<el-input
v-model="contractDialog.query.contractName"
clearable
placeholder="请输入"
@keyup.enter="loadContractRows"
/>
</el-form-item>
<el-form-item label="所属项目">
<el-input
v-model="contractDialog.query.projectName"
clearable
placeholder="请输入"
@keyup.enter="loadContractRows"
/>
</el-form-item>
<el-form-item label="所属组织">
<el-input
v-model="contractDialog.query.organizationName"
clearable
placeholder="请输入"
@keyup.enter="loadContractRows"
/>
</el-form-item>
<el-form-item label="合同类别">
<el-select v-model="contractDialog.query.contractCategory" clearable placeholder="请选择">
<el-option
v-for="item in contractCategoryOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="签约类型">
<el-select v-model="contractDialog.query.signType" clearable placeholder="请选择">
<el-option
v-for="item in signTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="生效类型">
<el-select v-model="contractDialog.query.effectiveType" clearable placeholder="请选择">
<el-option
v-for="item in effectiveTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="合同阶段">
<el-select v-model="contractDialog.query.contractStage" clearable placeholder="请选择">
<el-option
v-for="item in contractStageOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item v-if="contractDialog.expanded" label="审核状态">
<el-select v-model="contractDialog.query.approvalStatus" clearable placeholder="请选择">
<el-option
v-for="item in contractApprovalStatusOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item class="pre-settlement-editor__contract-filter-actions">
<el-button type="primary" @click="searchContractRows">查询</el-button>
<el-button @click="resetContractQuery">重置</el-button>
<el-button text @click="contractDialog.expanded = !contractDialog.expanded">
{{ contractDialog.expanded ? '收起' : '展开' }}
</el-button>
</el-form-item>
</el-form>
<el-table
v-loading="contractDialog.loading"
:data="contractDialog.rows"
border
highlight-current-row
@current-change="contractDialog.current = $event"
@row-dblclick="selectContract"
>
<el-table-column type="index" label="序号" width="64" align="center" />
<el-table-column
v-for="column in contractSelectionColumns"
:key="column.prop"
:prop="column.prop"
:label="column.label"
:min-width="column.minWidth"
align="center"
show-overflow-tooltip
>
<template #default="{ row }">
<span v-if="column.prop === 'organizationName'">{{
row.organizationName || row.deptName || '-'
}}</span>
<span v-else-if="column.prop === 'contractCategory'">{{
contractCategoryName(row.contractCategory)
}}</span>
<span v-else-if="column.prop === 'signType'">{{ signTypeName(row.signType) }}</span>
<span v-else>{{ displayValue(row[column.prop]) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" width="90" fixed="right" align="center">
<template #default="{ row }">
<el-link type="primary" @click.stop="selectContract(row)">选择</el-link>
</template>
</el-table-column>
</el-table>
<div class="pre-settlement-editor__pagination">
<el-pagination
v-model:current-page="contractDialog.page.current"
v-model:page-size="contractDialog.page.size"
:total="contractDialog.page.total"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
@current-change="loadContractRows"
@size-change="handleContractSizeChange"
/>
</div>
<template #footer>
<el-button @click="contractDialog.visible = false">取消</el-button>
<el-button type="primary" @click="selectContract(contractDialog.current)">确定</el-button>
</template>
</el-dialog>
<el-dialog <el-dialog
v-model="adjustDialog.visible" v-model="adjustDialog.visible"
title="结算明细调整" title="结算明细调整"
@@ -613,7 +781,7 @@ import { mapGetters } from 'vuex';
import { import {
adjustDetail, adjustDetail,
getCandidateDetails, getCandidateDetails,
getContractOptions, getContractList,
getDetail, getDetail,
getDetailFees, getDetailFees,
getFeeOptions, getFeeOptions,
@@ -627,6 +795,13 @@ import {
preSettlementFormFields, preSettlementFormFields,
preSettlementFormRules, preSettlementFormRules,
} from '@/option/settlement/preSettlementForm'; } from '@/option/settlement/preSettlementForm';
import {
contractApprovalStatusOptions,
contractCategoryOptions,
contractStageOptions,
effectiveTypeOptions,
signTypeOptions,
} from '@/option/business/common';
import { import {
advanceColumns, advanceColumns,
candidateDetailColumns, candidateDetailColumns,
@@ -675,8 +850,45 @@ export default {
advanceTableColumns: advanceColumns, advanceTableColumns: advanceColumns,
changeTableColumns: changeRecordColumns, changeTableColumns: changeRecordColumns,
candidateColumns: candidateDetailColumns, candidateColumns: candidateDetailColumns,
contractLoading: false,
contractOptions: [], contractOptions: [],
contractCategoryOptions,
signTypeOptions,
effectiveTypeOptions,
contractStageOptions,
contractApprovalStatusOptions,
contractSelectionColumns: [
{ prop: 'contractNo', label: '合同编号', minWidth: 180 },
{ prop: 'contractName', label: '合同名称', minWidth: 220 },
{ prop: 'projectName', label: '所属项目', minWidth: 170 },
{ prop: 'organizationName', label: '所属组织', minWidth: 170 },
{ prop: 'contractCategory', label: '合同类别', minWidth: 130 },
{ prop: 'signType', label: '签约类型', minWidth: 120 },
{ prop: 'partyA', label: '甲方', minWidth: 170 },
{ prop: 'partyB', label: '乙方', minWidth: 170 },
{ prop: 'startDate', label: '开始日期', minWidth: 130 },
{ prop: 'endDate', label: '结束日期', minWidth: 130 },
{ prop: 'temporaryStartDate', label: '临时效力起', minWidth: 130 },
{ prop: 'temporaryEndDate', label: '临时效力止', minWidth: 130 },
],
contractDialog: {
visible: false,
loading: false,
expanded: false,
rows: [],
current: null,
query: {
contractNo: '',
contractName: '',
projectName: '',
organizationName: '',
contractCategory: '',
signType: '',
effectiveType: '',
contractStage: '',
approvalStatus: '',
},
page: { current: 1, size: 10, total: 0 },
},
feeOptions: [], feeOptions: [],
feeCategoryOptions: [], feeCategoryOptions: [],
transportTypeOptions: [], transportTypeOptions: [],
@@ -857,7 +1069,6 @@ export default {
}, },
async initialize() { async initialize() {
this.resetEditor(); this.resetEditor();
await this.loadContractOptions('');
await this.loadFeeOptions(); await this.loadFeeOptions();
await this.loadFeeCategoryOptions(); await this.loadFeeCategoryOptions();
if (this.pageMode) await this.loadTransportTypeOptions(); if (this.pageMode) await this.loadTransportTypeOptions();
@@ -935,6 +1146,12 @@ export default {
this.changeRecords = []; this.changeRecords = [];
this.attachments = []; this.attachments = [];
this.selectedAttachmentRows = []; this.selectedAttachmentRows = [];
this.contractOptions = [];
this.contractDialog.visible = false;
this.contractDialog.expanded = false;
this.contractDialog.rows = [];
this.contractDialog.current = null;
this.contractDialog.page = { current: 1, size: 10, total: 0 };
this.$nextTick(() => this.$refs.formRef?.clearValidate()); this.$nextTick(() => this.$refs.formRef?.clearValidate());
}, },
async loadDetail() { async loadDetail() {
@@ -969,15 +1186,85 @@ export default {
this.loading = false; this.loading = false;
} }
}, },
async loadContractOptions(keyword = '') { openContractDialog() {
this.contractLoading = true; if (!this.editable || this.details.length > 0) return;
this.contractDialog.visible = true;
this.contractDialog.expanded = false;
this.contractDialog.current = null;
this.contractDialog.page.current = 1;
this.loadContractRows();
},
async loadContractRows() {
this.contractDialog.loading = true;
try { try {
const { data } = await getContractOptions(keyword); const { data } = await getContractList(
this.contractOptions = data?.data || []; this.contractDialog.page.current,
this.contractDialog.page.size,
{ ...this.contractDialog.query }
);
const page = data?.data || {};
this.contractDialog.rows = page.records || [];
this.contractDialog.page.total = Number(page.total || 0);
} finally { } finally {
this.contractLoading = false; this.contractDialog.loading = false;
} }
}, },
searchContractRows() {
this.contractDialog.page.current = 1;
this.loadContractRows();
},
resetContractQuery() {
this.contractDialog.query = {
contractNo: '',
contractName: '',
projectName: '',
organizationName: '',
contractCategory: '',
signType: '',
effectiveType: '',
contractStage: '',
approvalStatus: '',
};
this.searchContractRows();
},
handleContractSizeChange() {
this.contractDialog.page.current = 1;
this.loadContractRows();
},
selectContract(row) {
if (!row) {
this.$message.warning('请选择合同');
return;
}
const contract = {
...row,
deptId: row.deptId || row.organizationId,
deptName: row.deptName || row.organizationName,
partyA: row.partyA || row.payerName,
partyB: row.partyB || row.payeeName,
settlementType:
row.settlementType || (row.contractCategory === '客户合同' ? 'receivable' : 'payable'),
};
const index = this.contractOptions.findIndex(item => String(item.id) === String(contract.id));
if (index >= 0) this.contractOptions.splice(index, 1, contract);
else this.contractOptions.push(contract);
this.handleContractChange(contract.id);
this.contractDialog.visible = false;
this.contractDialog.current = null;
this.$refs.formRef?.clearValidate('contractId');
},
contractCategoryName(value) {
return (
this.contractCategoryOptions.find(item => String(item.value) === String(value))?.label ||
this.displayValue(value)
);
},
signTypeName(value) {
return (
this.signTypeOptions.find(item => String(item.value) === String(value))?.label ||
this.displayValue(value)
);
},
async loadFeeOptions() { async loadFeeOptions() {
const { data } = await getFeeOptions(); const { data } = await getFeeOptions();
this.feeOptions = data?.data || []; this.feeOptions = data?.data || [];
@@ -993,6 +1280,7 @@ export default {
handleContractChange(id) { handleContractChange(id) {
const contract = this.contractOptions.find(item => String(item.id) === String(id)); const contract = this.contractOptions.find(item => String(item.id) === String(id));
if (!contract) { if (!contract) {
this.form.contractId = '';
this.form.contractNo = ''; this.form.contractNo = '';
this.form.contractName = ''; this.form.contractName = '';
this.form.projectId = ''; this.form.projectId = '';
@@ -1004,6 +1292,7 @@ export default {
this.summaryFees = []; this.summaryFees = [];
return; return;
} }
this.form.contractId = contract.id;
this.form.contractNo = contract.contractNo; this.form.contractNo = contract.contractNo;
this.form.contractName = contract.contractName; this.form.contractName = contract.contractName;
this.form.projectId = contract.projectId; this.form.projectId = contract.projectId;
@@ -1166,9 +1455,7 @@ export default {
this.details.forEach(row => { this.details.forEach(row => {
const feeItems = this.parseFeeItems(row.feeItemsJson || row.feeItems); const feeItems = this.parseFeeItems(row.feeItemsJson || row.feeItems);
const feeItemEntries = Object.entries(feeItems); const feeItemEntries = Object.entries(feeItems);
feeItemEntries.forEach(([feeItem, amount]) => feeItemEntries.forEach(([feeItem, amount]) => appendSummary(feeItem, amount, row.feeType));
appendSummary(feeItem, amount, row.feeType)
);
let knownAmount = feeItemEntries.reduce( let knownAmount = feeItemEntries.reduce(
(total, [, amount]) => total + Number(amount || 0), (total, [, amount]) => total + Number(amount || 0),
@@ -1186,11 +1473,7 @@ export default {
} }
const totalAmount = Number( const totalAmount = Number(
row.settlementAmountTax ?? row.settlementAmountTax ?? row.totalAmount ?? row.afterAmount ?? row.settlementAmount ?? 0
row.totalAmount ??
row.afterAmount ??
row.settlementAmount ??
0
); );
const residualAmount = Number((totalAmount - knownAmount).toFixed(2)); const residualAmount = Number((totalAmount - knownAmount).toFixed(2));
if (Math.abs(residualAmount) >= 0.005) { if (Math.abs(residualAmount) >= 0.005) {
@@ -1621,6 +1904,39 @@ export default {
margin-left: auto; margin-left: auto;
} }
&__contract-filter {
display: grid !important;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 8px 24px;
margin-bottom: 16px;
:deep(.el-form-item) {
display: flex;
min-width: 0;
margin-bottom: 0;
}
:deep(.el-form-item__content) {
flex: 1;
min-width: 0;
}
:deep(.el-input),
:deep(.el-select) {
width: 100%;
}
}
&__contract-filter-actions {
grid-column: 1 / -1;
width: 100%;
justify-content: flex-end;
:deep(.el-form-item__content) {
justify-content: flex-end;
}
}
&__attachment-actions { &__attachment-actions {
display: flex; display: flex;
align-items: center; align-items: center;
@@ -1649,6 +1965,14 @@ export default {
font-weight: 600; font-weight: 600;
} }
&__negative-amount {
color: var(--el-color-danger);
}
:deep(.pre-settlement-editor__negative-amount .el-input__inner) {
color: var(--el-color-danger);
}
&__pagination { &__pagination {
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
@@ -150,7 +150,7 @@
</el-table> </el-table>
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="变更记录" name="change"> <el-tab-pane label="变更记录" name="change">
<el-table v-loading="changeDialog.loading" :data="changeRows" border> <el-table v-loading="latestChangeLoading" :data="latestChangeRows" border>
<el-table-column type="index" label="序号" width="64" align="center" /> <el-table-column type="index" label="序号" width="64" align="center" />
<el-table-column <el-table-column
v-for="column in changeRecordColumns" v-for="column in changeRecordColumns"
@@ -161,17 +161,17 @@
align="center" align="center"
show-overflow-tooltip show-overflow-tooltip
/> />
<el-table-column label="操作" width="120" fixed="right" align="center">
<template #default>
<el-link type="primary" @click="openChangeRecordsDialog">查看详情</el-link>
</template>
</el-table-column>
</el-table> </el-table>
<div class="settlement-detail-page__pagination"> <div
<el-pagination v-if="!latestChangeLoading && !latestChangeRows.length"
v-model:current-page="changePage.current" class="settlement-detail-page__empty"
v-model:page-size="changePage.size" >
:total="changePage.total" 暂无变更记录
:page-sizes="[10, 20, 50]"
layout="total, sizes, prev, pager, next, jumper"
@current-change="loadChangeRecords"
@size-change="handleChangeSizeChange"
/>
</div> </div>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
@@ -182,6 +182,37 @@
</section> </section>
</teleport> </teleport>
<el-dialog
v-model="changeRecordsDialog.visible"
title="变更记录详情"
width="88%"
append-to-body
>
<el-table v-loading="changeRecordsDialog.loading" :data="changeRows" border>
<el-table-column type="index" label="序号" width="64" align="center" />
<el-table-column
v-for="column in changeRecordColumns"
:key="column.prop"
:prop="column.prop"
:label="column.label"
:min-width="column.minWidth"
align="center"
show-overflow-tooltip
/>
</el-table>
<div class="settlement-detail-page__pagination">
<el-pagination
v-model:current-page="changePage.current"
v-model:page-size="changePage.size"
:total="changePage.total"
:page-sizes="[10, 20, 50]"
layout="total, sizes, prev, pager, next, jumper"
@current-change="loadChangeRecords"
@size-change="handleChangeSizeChange"
/>
</div>
</el-dialog>
<teleport to="body"> <teleport to="body">
<section <section
v-if="adjustDialog.visible" v-if="adjustDialog.visible"
@@ -365,19 +396,12 @@
inputmode="decimal" inputmode="decimal"
@input="value => handleAdjustFeeItemInput(row, column.feeItemName, value)" @input="value => handleAdjustFeeItemInput(row, column.feeItemName, value)"
/> />
<el-input
v-else-if="column.prop === 'remark'"
v-model="row.remark"
clearable
maxlength="200"
placeholder="请输入备注"
/>
<el-input <el-input
v-else-if="column.prop === 'changeReason'" v-else-if="column.prop === 'changeReason'"
v-model="row.changeReason" v-model="row.changeReason"
clearable clearable
maxlength="300" maxlength="300"
placeholder="请输入变更原因" placeholder="请输入调整原因"
/> />
<span v-else-if="column.prop === 'adjustAmountText'"> <span v-else-if="column.prop === 'adjustAmountText'">
{{ fixedTwoDecimals(row.adjustAmount) }} {{ fixedTwoDecimals(row.adjustAmount) }}
@@ -545,22 +569,16 @@
label-width="120px" label-width="120px"
> >
<el-form-item label="运单合同" required> <el-form-item label="运单合同" required>
<el-select <el-input
v-model="generateQuery.contractId" :model-value="generateContractName"
class="settlement-detail-page__generate-select" class="settlement-detail-page__generate-select"
filterable readonly
clearable
:loading="contractLoading"
placeholder="请选择合同" placeholder="请选择合同"
@change="handleGenerateContractChange"
> >
<el-option <template #append>
v-for="item in contractOptions" <el-button @click="openGenerateContractDialog">选择</el-button>
:key="item.id" </template>
:label="item.contractName" </el-input>
:value="item.id"
/>
</el-select>
</el-form-item> </el-form-item>
<el-form-item label="计费方案" required> <el-form-item label="计费方案" required>
<el-select <el-select
@@ -673,6 +691,180 @@
</el-button> </el-button>
</template> </template>
</el-dialog> </el-dialog>
<el-dialog
v-model="generateContractDialog.visible"
title="选择合同"
width="92%"
append-to-body
destroy-on-close
class="settlement-detail-page__contract-dialog"
>
<el-form
:model="generateContractDialog.query"
label-position="right"
label-width="auto"
class="settlement-detail-page__contract-filter"
@submit.prevent
>
<el-form-item label="合同编号">
<el-input
v-model="generateContractDialog.query.contractNo"
clearable
placeholder="请输入"
@keyup.enter="loadGenerateContracts"
/>
</el-form-item>
<el-form-item label="合同名称">
<el-input
v-model="generateContractDialog.query.contractName"
clearable
placeholder="请输入"
@keyup.enter="loadGenerateContracts"
/>
</el-form-item>
<el-form-item label="所属项目">
<el-input
v-model="generateContractDialog.query.projectName"
clearable
placeholder="请输入"
@keyup.enter="loadGenerateContracts"
/>
</el-form-item>
<el-form-item label="所属组织">
<el-input
v-model="generateContractDialog.query.organizationName"
clearable
placeholder="请输入"
@keyup.enter="loadGenerateContracts"
/>
</el-form-item>
<el-form-item label="合同类别">
<el-select :model-value="generateContractCategory" disabled placeholder="请选择">
<el-option
v-for="item in contractCategoryOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="签约类型">
<el-select v-model="generateContractDialog.query.signType" clearable placeholder="请选择">
<el-option
v-for="item in signTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="生效类型">
<el-select
v-model="generateContractDialog.query.effectiveType"
clearable
placeholder="请选择"
>
<el-option
v-for="item in effectiveTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="合同阶段">
<el-select
v-model="generateContractDialog.query.contractStage"
clearable
placeholder="请选择"
>
<el-option
v-for="item in contractStageOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item v-if="generateContractDialog.expanded" label="审核状态">
<el-select
v-model="generateContractDialog.query.approvalStatus"
clearable
placeholder="请选择"
>
<el-option
v-for="item in contractApprovalStatusOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item class="settlement-detail-page__contract-filter-actions">
<el-button type="primary" @click="searchGenerateContracts">查询</el-button>
<el-button @click="resetGenerateContractQuery">重置</el-button>
<el-button
text
@click="generateContractDialog.expanded = !generateContractDialog.expanded"
>
{{ generateContractDialog.expanded ? '收起' : '展开' }}
</el-button>
</el-form-item>
</el-form>
<el-table
v-loading="generateContractDialog.loading"
:data="generateContractDialog.rows"
border
highlight-current-row
@current-change="generateContractDialog.current = $event"
@row-dblclick="selectGenerateContract"
>
<el-table-column type="index" label="序号" width="64" align="center" />
<el-table-column
v-for="column in contractSelectionColumns"
:key="column.prop"
:prop="column.prop"
:label="column.label"
:min-width="column.minWidth"
align="center"
show-overflow-tooltip
>
<template #default="{ row }">
<span v-if="column.prop === 'organizationName'">{{
row.organizationName || row.deptName || '-'
}}</span>
<span v-else-if="column.prop === 'contractCategory'">{{
contractCategoryName(row.contractCategory)
}}</span>
<span v-else-if="column.prop === 'signType'">{{ signTypeName(row.signType) }}</span>
<span v-else>{{ displayValue(row[column.prop]) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" width="90" fixed="right" align="center">
<template #default="{ row }">
<el-link type="primary" @click.stop="selectGenerateContract(row)">选择</el-link>
</template>
</el-table-column>
</el-table>
<div class="settlement-detail-page__pagination">
<el-pagination
v-model:current-page="generateContractDialog.page.current"
v-model:page-size="generateContractDialog.page.size"
:total="generateContractDialog.page.total"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
@current-change="loadGenerateContracts"
@size-change="handleGenerateContractSizeChange"
/>
</div>
<template #footer>
<el-button @click="generateContractDialog.visible = false">取消</el-button>
<el-button type="primary" @click="selectGenerateContract(generateContractDialog.current)">
确定
</el-button>
</template>
</el-dialog>
</basic-container> </basic-container>
</template> </template>
@@ -698,6 +890,28 @@ import { exportBlob } from '@/api/common';
import { getDictionary } from '@/api/system/dictbiz'; import { getDictionary } from '@/api/system/dictbiz';
import { downloadXls } from '@/utils/util'; import { downloadXls } from '@/utils/util';
import { createSettlementTransfer } from '@/utils/settlement-transfer'; import { createSettlementTransfer } from '@/utils/settlement-transfer';
import {
contractApprovalStatusOptions,
contractCategoryOptions,
contractStageOptions,
effectiveTypeOptions,
signTypeOptions,
} from '@/option/business/common';
const contractSelectionColumns = [
{ prop: 'contractNo', label: '合同编号', minWidth: 180 },
{ prop: 'contractName', label: '合同名称', minWidth: 220 },
{ prop: 'projectName', label: '所属项目', minWidth: 170 },
{ prop: 'organizationName', label: '所属组织', minWidth: 170 },
{ prop: 'contractCategory', label: '合同类别', minWidth: 130 },
{ prop: 'signType', label: '签约类型', minWidth: 120 },
{ prop: 'partyA', label: '甲方', minWidth: 170 },
{ prop: 'partyB', label: '乙方', minWidth: 170 },
{ prop: 'startDate', label: '开始日期', minWidth: 130 },
{ prop: 'endDate', label: '结束日期', minWidth: 130 },
{ prop: 'temporaryStartDate', label: '临时效力起', minWidth: 130 },
{ prop: 'temporaryEndDate', label: '临时效力止', minWidth: 130 },
];
const ADJUST_BILLING_ELEMENTS = [ const ADJUST_BILLING_ELEMENTS = [
'按重量', '按重量',
@@ -777,15 +991,22 @@ export default {
priceUnitRequest: null, priceUnitRequest: null,
changeRows: [], changeRows: [],
changePage: { current: 1, size: 10, total: 0 }, changePage: { current: 1, size: 10, total: 0 },
changeDialog: { loading: false }, latestChangeRows: [],
latestChangeLoading: false,
changeRecordsDialog: { visible: false, loading: false },
updateFeeDialog: { visible: false, submitting: false, row: null }, updateFeeDialog: { visible: false, submitting: false, row: null },
updateFeeForm: { contractId: '', billingPlanId: '' }, updateFeeForm: { contractId: '', billingPlanId: '' },
updateFeeRules: { updateFeeRules: {
contractId: [{ required: true, message: '请选择更新范围', trigger: 'change' }], contractId: [{ required: true, message: '请选择更新范围', trigger: 'change' }],
}, },
contractOptions: [], contractOptions: [],
contractLoading: false,
updateContractOptions: [], updateContractOptions: [],
contractCategoryOptions,
signTypeOptions,
effectiveTypeOptions,
contractStageOptions,
contractApprovalStatusOptions,
contractSelectionColumns,
transportTypeOptions: [], transportTypeOptions: [],
billingPlanOptions: [], billingPlanOptions: [],
transferDialog: { visible: false, loading: false, submitting: false }, transferDialog: { visible: false, loading: false, submitting: false },
@@ -796,6 +1017,25 @@ export default {
transferPage: { current: 1, size: 10, total: 0 }, transferPage: { current: 1, size: 10, total: 0 },
generateDialog: { visible: false, loading: false }, generateDialog: { visible: false, loading: false },
generateQuery: {}, generateQuery: {},
generateContractDialog: {
visible: false,
loading: false,
expanded: false,
rows: [],
current: null,
query: {
contractNo: '',
contractName: '',
projectName: '',
organizationName: '',
contractCategory: '',
signType: '',
effectiveType: '',
contractStage: '',
approvalStatus: '',
},
page: { current: 1, size: 10, total: 0 },
},
generateRows: [], generateRows: [],
generateSelection: [], generateSelection: [],
generatePage: { current: 1, size: 10, total: 0 }, generatePage: { current: 1, size: 10, total: 0 },
@@ -805,6 +1045,30 @@ export default {
}; };
}, },
computed: { computed: {
generateContractName() {
const contract = this.contractOptions.find(
item => String(item.id) === String(this.generateQuery.contractId)
);
return contract?.contractName || '';
},
generateContractCategory() {
return this.settlementType === 'payable' ? '承运商合同' : '客户合同';
},
contractCategoryName(value) {
return (
this.contractCategoryOptions.find(item => String(item.value) === String(value))?.label ||
this.formatCell(value)
);
},
signTypeName(value) {
return (
this.signTypeOptions.find(item => String(item.value) === String(value))?.label ||
this.formatCell(value)
);
},
displayValue(value) {
return this.formatCell(value);
},
settlementTypeLabel() { settlementTypeLabel() {
if (this.settlementType === 'receivable') return '应收'; if (this.settlementType === 'receivable') return '应收';
if (this.settlementType === 'payable') return '应付'; if (this.settlementType === 'payable') return '应付';
@@ -830,14 +1094,31 @@ export default {
column => this.settlementType !== 'receivable' || column.prop !== 'preSettlementNo' column => this.settlementType !== 'receivable' || column.prop !== 'preSettlementNo'
); );
const totalIndex = columns.findIndex(column => column.prop === 'totalAmountText'); const totalIndex = columns.findIndex(column => column.prop === 'totalAmountText');
const dynamicColumns = this.tableFeeItemNames.map((name, index) => ({ const dynamicColumns = this.isPayable
label: name, ? [
prop: `tableFeeItem${index}`, {
feeItemName: name, label: '运输费',
dynamic: true, prop: 'tableFreightAmount',
minWidth: 130, feeSummaryType: 'freight',
align: 'right', minWidth: 130,
})); align: 'right',
},
{
label: '其他费用',
prop: 'tableOtherFeeAmount',
feeSummaryType: 'other',
minWidth: 130,
align: 'right',
},
]
: this.tableFeeItemNames.map((name, index) => ({
label: name,
prop: `tableFeeItem${index}`,
feeItemName: name,
dynamic: true,
minWidth: 130,
align: 'right',
}));
if (totalIndex < 0) return [...columns, ...dynamicColumns]; if (totalIndex < 0) return [...columns, ...dynamicColumns];
columns.splice(totalIndex, 0, ...dynamicColumns); columns.splice(totalIndex, 0, ...dynamicColumns);
return columns; return columns;
@@ -846,12 +1127,14 @@ export default {
return [...generatePreviewColumns, ...this.dynamicFeeColumns, ...feeDetailTailColumns]; return [...generatePreviewColumns, ...this.dynamicFeeColumns, ...feeDetailTailColumns];
}, },
adjustFeeColumns() { adjustFeeColumns() {
return [ const tailColumns = feeDetailTailColumns.filter(column => column.prop !== 'remark');
...feeDetailBaseColumns, const afterAmountIndex = tailColumns.findIndex(column => column.prop === 'afterAmountText');
...this.adjustDynamicFeeColumns, tailColumns.splice(afterAmountIndex + 1, 0, {
...feeDetailTailColumns, label: '调整原因',
{ label: '变更原因', prop: 'changeReason', minWidth: 220 }, prop: 'changeReason',
]; minWidth: 220,
});
return [...feeDetailBaseColumns, ...this.adjustDynamicFeeColumns, ...tailColumns];
}, },
transportCargoTypeOptions() { transportCargoTypeOptions() {
return this.cargoTypeOptions.filter(item => item.children?.length); return this.cargoTypeOptions.filter(item => item.children?.length);
@@ -1114,6 +1397,12 @@ export default {
formatColumnValue(row, column) { formatColumnValue(row, column) {
if (column.prop === 'transportType') return this.transportTypeLabel(row[column.prop]); if (column.prop === 'transportType') return this.transportTypeLabel(row[column.prop]);
if (column.prop === 'transportQuantity') return this.fixedTwoDecimals(row[column.prop]); if (column.prop === 'transportQuantity') return this.fixedTwoDecimals(row[column.prop]);
if (column.feeSummaryType) {
return this.money(
this.summarizeTableFee(row, column.feeSummaryType),
row.currency || 'RMB'
);
}
if (column.dynamic) { if (column.dynamic) {
return this.money( return this.money(
this.normalizeFeeItems(row.feeItems)[column.feeItemName], this.normalizeFeeItems(row.feeItems)[column.feeItemName],
@@ -1137,25 +1426,80 @@ export default {
const res = await api.getList(this.page.current, this.page.size, params); const res = await api.getList(this.page.current, this.page.size, params);
const data = this.unwrapPage(res); const data = this.unwrapPage(res);
this.rows = (data.records || []).map(this.decorateRow); this.rows = (data.records || []).map(this.decorateRow);
this.tableFeeItemNames = this.collectFeeItemNames(this.rows); this.tableFeeItemNames = this.isPayable ? [] : this.collectFeeItemNames(this.rows);
this.page.total = data.total || 0; this.page.total = data.total || 0;
} finally { } finally {
this.loading = false; this.loading = false;
} }
}, },
async loadContracts() { openGenerateContractDialog() {
const contractCategory = this.settlementType === 'payable' ? '承运商合同' : '客户合同'; this.generateContractDialog.visible = true;
this.contractLoading = true; this.generateContractDialog.expanded = false;
this.generateContractDialog.current = null;
this.generateContractDialog.page.current = 1;
this.loadGenerateContracts();
},
async loadGenerateContracts() {
this.generateContractDialog.loading = true;
try { try {
const res = await getContractList(1, 999, { const res = await getContractList(
contractCategory, this.generateContractDialog.page.current,
}); this.generateContractDialog.page.size,
{
...this.generateContractDialog.query,
contractCategory: this.generateContractCategory,
approvalStatuses: 'approved,change_approved',
}
);
const data = this.unwrapPage(res); const data = this.unwrapPage(res);
this.contractOptions = data.records || []; this.generateContractDialog.rows = data.records || [];
this.generateContractDialog.page.total = Number(data.total || 0);
} finally { } finally {
this.contractLoading = false; this.generateContractDialog.loading = false;
} }
}, },
searchGenerateContracts() {
this.generateContractDialog.page.current = 1;
this.loadGenerateContracts();
},
resetGenerateContractQuery() {
this.generateContractDialog.query = {
contractNo: '',
contractName: '',
projectName: '',
organizationName: '',
contractCategory: '',
signType: '',
effectiveType: '',
contractStage: '',
approvalStatus: '',
};
this.searchGenerateContracts();
},
handleGenerateContractSizeChange() {
this.generateContractDialog.page.current = 1;
this.loadGenerateContracts();
},
selectGenerateContract(row) {
if (!row) {
this.$message.warning('请选择合同');
return;
}
const contract = {
...row,
deptId: row.deptId || row.organizationId,
deptName: row.deptName || row.organizationName,
partyA: row.partyA || row.payerName,
partyB: row.partyB || row.payeeName,
};
const index = this.contractOptions.findIndex(item => String(item.id) === String(contract.id));
if (index >= 0) this.contractOptions.splice(index, 1, contract);
else this.contractOptions.push(contract);
this.generateQuery.contractId = contract.id;
this.handleGenerateContractChange(contract.id);
this.generateContractDialog.visible = false;
this.generateContractDialog.current = null;
},
handleSearch() { handleSearch() {
this.page.current = 1; this.page.current = 1;
this.loadTable(); this.loadTable();
@@ -1177,6 +1521,7 @@ export default {
closeDetailPanel() { closeDetailPanel() {
this.detailDialog.visible = false; this.detailDialog.visible = false;
this.detailDialog.row = null; this.detailDialog.row = null;
this.changeRecordsDialog.visible = false;
}, },
closeAdjustPanel() { closeAdjustPanel() {
this.clearAdjustCalculations(); this.clearAdjustCalculations();
@@ -1525,11 +1870,31 @@ export default {
} }
}, },
handleDetailTabChange(name) { handleDetailTabChange(name) {
if (name === 'change') this.loadChangeRecords(); if (name === 'change') this.loadLatestChangeRecord();
},
async loadLatestChangeRecord() {
if (!this.detailDialog.row) return;
this.latestChangeLoading = true;
this.latestChangeRows = [];
try {
const res = await api.getChangeRecords(1, 1, {
detailId: this.detailDialog.row.id,
});
const data = this.unwrapPage(res);
this.latestChangeRows = (data.records || []).slice(0, 1);
} finally {
this.latestChangeLoading = false;
}
},
openChangeRecordsDialog() {
if (!this.detailDialog.row) return;
this.changeRecordsDialog.visible = true;
this.changePage.current = 1;
this.loadChangeRecords();
}, },
async loadChangeRecords() { async loadChangeRecords() {
if (!this.detailDialog.row) return; if (!this.detailDialog.row) return;
this.changeDialog.loading = true; this.changeRecordsDialog.loading = true;
try { try {
const res = await api.getChangeRecords(this.changePage.current, this.changePage.size, { const res = await api.getChangeRecords(this.changePage.current, this.changePage.size, {
detailId: this.detailDialog.row.id, detailId: this.detailDialog.row.id,
@@ -1538,7 +1903,7 @@ export default {
this.changeRows = data.records || []; this.changeRows = data.records || [];
this.changePage.total = data.total || 0; this.changePage.total = data.total || 0;
} finally { } finally {
this.changeDialog.loading = false; this.changeRecordsDialog.loading = false;
} }
}, },
handleChangeSizeChange() { handleChangeSizeChange() {
@@ -1792,11 +2157,26 @@ export default {
this.generateDialog.visible = true; this.generateDialog.visible = true;
this.generateQuery = {}; this.generateQuery = {};
this.contractOptions = []; this.contractOptions = [];
this.generateContractDialog.query = {
contractNo: '',
contractName: '',
projectName: '',
organizationName: '',
contractCategory: '',
signType: '',
effectiveType: '',
contractStage: '',
approvalStatus: '',
};
this.generateContractDialog.visible = false;
this.generateContractDialog.expanded = false;
this.generateContractDialog.rows = [];
this.generateContractDialog.current = null;
this.generateContractDialog.page = { current: 1, size: 10, total: 0 };
this.billingPlanOptions = []; this.billingPlanOptions = [];
this.generateRows = []; this.generateRows = [];
this.generateSelection = []; this.generateSelection = [];
this.generatePage = { current: 1, size: 10, total: 0 }; this.generatePage = { current: 1, size: 10, total: 0 };
this.loadContracts();
}, },
handleGenerateContractChange(contractId) { handleGenerateContractChange(contractId) {
const options = this.syncBillingPlanOptions(contractId); const options = this.syncBillingPlanOptions(contractId);
@@ -1911,6 +2291,7 @@ export default {
row.vehicleNumber || row.vehicleNumber ||
sourceWaybill?.vehicleNo || sourceWaybill?.vehicleNo ||
'', '',
transportQuantityText: row.transportQuantityText ?? row.transportQuantity ?? '',
...dynamic, ...dynamic,
}; };
}); });
@@ -2032,6 +2413,16 @@ export default {
return {}; return {};
} }
}, },
summarizeTableFee(row, summaryType) {
const feeItemEntries = Object.entries(this.normalizeFeeItems(row.feeItems));
const matchedEntries = feeItemEntries.filter(([name]) =>
summaryType === 'freight' ? this.isFreightFeeItem(name) : !this.isFreightFeeItem(name)
);
if (matchedEntries.length) {
return matchedEntries.reduce((total, [, amount]) => total + Number(amount || 0), 0);
}
return summaryType === 'freight' ? row.freightAmount : row.otherFeeAmount;
},
money(value, currency) { money(value, currency) {
if (value === null || value === undefined || value === '') return '-'; if (value === null || value === undefined || value === '') return '-';
return `${Number(value).toFixed(2)} ${currency}`; return `${Number(value).toFixed(2)} ${currency}`;
@@ -2160,6 +2551,12 @@ export default {
padding: 16px 0; padding: 16px 0;
} }
.settlement-detail-page__empty {
padding: 24px 0;
color: #909399;
text-align: center;
}
.settlement-detail-page__dialog-form { .settlement-detail-page__dialog-form {
padding: 16px 20px; padding: 16px 20px;
background: #fff; background: #fff;
@@ -2212,6 +2609,39 @@ export default {
width: 220px; width: 220px;
} }
.settlement-detail-page__contract-filter {
display: grid !important;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 8px 24px;
margin-bottom: 16px;
:deep(.el-form-item) {
display: flex;
min-width: 0;
margin-bottom: 0;
}
:deep(.el-form-item__content) {
flex: 1;
min-width: 0;
}
:deep(.el-input),
:deep(.el-select) {
width: 100%;
}
}
.settlement-detail-page__contract-filter-actions {
grid-column: 1 / -1;
width: 100%;
justify-content: flex-end;
:deep(.el-form-item__content) {
justify-content: flex-end;
}
}
.settlement-detail-page__transfer-form { .settlement-detail-page__transfer-form {
margin-bottom: 16px; margin-bottom: 16px;
padding: 12px 12px 4px; padding: 12px 12px 4px;