1、新增应付明细

2、新增应收明细
3、新增异常处置
4、新增风险处置
5、新增在途追踪
6、修复业务模块bug
This commit is contained in:
2026-08-13 00:01:23 +08:00
parent 748dd80d4a
commit 6554ad80dd
33 changed files with 7868 additions and 1657 deletions
+12
View File
@@ -0,0 +1,12 @@
<template>
<receivable-payable-detail settlement-type="payable" />
</template>
<script>
import ReceivablePayableDetail from './receivable-payable-detail.vue';
export default {
name: 'PayableDetail',
components: { ReceivablePayableDetail },
};
</script>
@@ -0,0 +1,12 @@
<template>
<receivable-payable-detail settlement-type="receivable" />
</template>
<script>
import ReceivablePayableDetail from './receivable-payable-detail.vue';
export default {
name: 'ReceivableDetail',
components: { ReceivablePayableDetail },
};
</script>
@@ -0,0 +1,938 @@
<template>
<basic-container class="settlement-detail-page">
<section class="settlement-detail-page__search">
<el-form :model="query" label-position="right" label-width="160px" @submit.prevent>
<div class="settlement-detail-page__search-grid">
<template v-for="field in visibleSearchFields" :key="field.prop">
<el-form-item :label="field.label">
<el-date-picker
v-if="field.type === 'daterange'"
v-model="query[field.prop]"
type="daterange"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
range-separator="~"
start-placeholder="//"
end-placeholder="//"
/>
<el-input v-else v-model="query[field.prop]" clearable placeholder="请输入" />
</el-form-item>
</template>
<div class="settlement-detail-page__search-actions">
<el-button type="primary" @click="handleSearch">查询</el-button>
<el-button @click="handleReset">重置</el-button>
<el-button :icon="searchExpanded ? ArrowUp : ArrowDown" @click="toggleSearch" />
</div>
</div>
</el-form>
</section>
<section class="settlement-detail-page__table-panel">
<div class="settlement-detail-page__toolbar">
<div class="settlement-detail-page__toolbar-left">
<el-button type="primary" @click="openGenerateDialog">生成费用</el-button>
<el-button type="primary" @click="openUpdateFeeDialog">更新费用</el-button>
<el-button type="primary" @click="openTransferDialog">批量转结算</el-button>
<el-button type="primary" plain @click="handleExport">导出</el-button>
</div>
<div class="settlement-detail-page__toolbar-right">
<el-tooltip content="刷新" placement="top">
<el-button :icon="Refresh" text @click="loadTable" />
</el-tooltip>
<el-tooltip content="列设置" placement="top">
<el-button :icon="Setting" text @click="noop" />
</el-tooltip>
</div>
</div>
<el-table
v-loading="loading"
:data="rows"
border
class="settlement-detail-page__table"
@selection-change="selectionChange"
>
<el-table-column type="selection" width="52" fixed="left" align="center" />
<el-table-column type="index" label="序号" width="64" fixed="left" align="center" />
<el-table-column
v-for="column in tableColumns"
:key="column.prop"
:prop="column.prop"
:label="column.label"
:min-width="column.minWidth"
:align="column.align || 'center'"
show-overflow-tooltip
>
<template #default="{ row }">
<el-link
v-if="column.link && row[column.prop]"
type="primary"
@click="openDetailDialog(row)"
>
{{ row[column.prop] }}
</el-link>
<span v-else>{{ row[column.prop] || '-' }}</span>
</template>
</el-table-column>
<el-table-column label="操作" width="220" fixed="right" align="center">
<template #default="{ row }">
<div class="settlement-detail-page__actions">
<el-link v-if="row.settlementStatus === 'pending'" type="primary" @click="openUpdateFeeDialog(row)">
调整
</el-link>
<el-link v-if="row.settlementStatus === 'pending'" type="primary" @click="closeRow(row)">
关闭
</el-link>
<el-link v-else type="primary" disabled>-</el-link>
</div>
</template>
</el-table-column>
</el-table>
<div class="settlement-detail-page__pagination">
<el-pagination
v-model:current-page="page.current"
v-model:page-size="page.size"
:total="page.total"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
@current-change="loadTable"
@size-change="handleSizeChange"
/>
</div>
</section>
<el-dialog
v-model="detailDialog.visible"
:title="detailDialog.title"
width="92%"
append-to-body
destroy-on-close
class="settlement-detail-dialog"
>
<el-tabs v-model="detailDialog.activeTab" @tab-change="handleDetailTabChange">
<el-tab-pane label="费用明细" name="fee">
<el-table v-loading="detailDialog.loading" :data="feeRows" border>
<el-table-column type="index" label="序号" width="64" align="center" />
<el-table-column
v-for="column in feeDetailColumns"
:key="column.prop"
:prop="column.prop"
:label="column.label"
:min-width="column.minWidth"
:align="column.align || 'center'"
show-overflow-tooltip
>
<template #default="{ row }">{{ formatCell(row[column.prop]) }}</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="变更记录" name="change">
<el-table v-loading="changeDialog.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-tab-pane>
</el-tabs>
</el-dialog>
<el-dialog v-model="updateFeeDialog.visible" title="更新费用" width="620px" append-to-body>
<el-form
ref="updateFeeFormRef"
:model="updateFeeForm"
:rules="updateFeeRules"
label-position="right"
label-width="auto"
class="settlement-detail-page__dialog-form"
>
<el-form-item label="更新范围" prop="contractId">
<el-select
v-model="updateFeeForm.contractId"
clearable
filterable
placeholder="请选择合同"
>
<el-option
v-for="item in contractOptions"
:key="item.id"
:label="item.contractName"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="合同计费方案" prop="billingPlanId">
<el-select v-model="updateFeeForm.billingPlanId" clearable filterable placeholder="请选择">
<el-option
v-for="item in billingPlanOptions"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<el-button type="primary" :loading="updateFeeDialog.submitting" @click="confirmUpdateFee">
确认
</el-button>
<el-button @click="updateFeeDialog.visible = false">取消</el-button>
</template>
</el-dialog>
<el-dialog v-model="transferDialog.visible" title="批量转结算" width="86%" append-to-body>
<div class="settlement-detail-page__transfer-form">
<el-form :model="transferQuery" inline label-position="right" label-width="120px">
<el-form-item label="转结算类型">
<el-radio-group v-model="transferForm.settlementBillType">
<el-radio label="pre">预结算单</el-radio>
<el-radio label="formal">正式结算单</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<el-form :model="transferQuery" inline label-position="right" label-width="120px">
<el-form-item
v-for="field in transferSearchFields"
:key="field.prop"
:label="field.label"
>
<el-date-picker
v-if="field.type === 'daterange'"
v-model="transferQuery[field.prop]"
type="daterange"
value-format="YYYY-MM-DD"
range-separator="-"
start-placeholder="请选择"
end-placeholder="请选择"
/>
<el-input v-else v-model="transferQuery[field.prop]" clearable placeholder="请输入" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadTransferCandidates">查询</el-button>
<el-button type="primary" @click="confirmTransferSelection">确认选择</el-button>
</el-form-item>
</el-form>
</div>
<el-table
v-loading="transferDialog.loading"
:data="transferRows"
border
@selection-change="transferSelectionChange"
>
<el-table-column type="selection" width="52" align="center" />
<el-table-column type="index" label="序号" width="70" align="center" />
<el-table-column
v-for="column in tableColumns.slice(0, 13)"
:key="column.prop"
:prop="column.prop"
:label="column.label"
:min-width="column.minWidth"
align="center"
show-overflow-tooltip
/>
</el-table>
<template #footer>
<el-button type="primary" :loading="transferDialog.submitting" @click="submitTransfer">
确认
</el-button>
<el-button @click="transferDialog.visible = false">取消</el-button>
</template>
</el-dialog>
<el-dialog v-model="generateDialog.visible" title="生成费用" width="88%" append-to-body>
<el-form :model="generateQuery" inline label-position="right" label-width="120px">
<el-form-item label="运单合同" required>
<el-select
v-model="generateQuery.contractId"
filterable
clearable
placeholder="请选择合同"
@change="handleGenerateContractChange"
>
<el-option
v-for="item in contractOptions"
:key="item.id"
:label="item.contractName"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="计费方案" required>
<el-select v-model="generateQuery.billingPlanId" filterable clearable placeholder="请选择">
<el-option
v-for="item in billingPlanOptions"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="批次号">
<el-input v-model="generateQuery.batchNo" clearable placeholder="请输入" />
</el-form-item>
<el-form-item label="运单完成日期">
<el-date-picker
v-model="generateQuery.finishDateRange"
type="daterange"
value-format="YYYY-MM-DD"
range-separator="-"
start-placeholder="请选择"
end-placeholder="请选择"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadGenerateWaybills">查询</el-button>
<el-button @click="resetGenerateQuery">重置</el-button>
</el-form-item>
</el-form>
<el-table
v-loading="generateDialog.loading"
:data="generateRows"
border
@selection-change="generateSelectionChange"
>
<el-table-column type="selection" width="52" align="center" />
<el-table-column type="index" label="序号" width="70" align="center" />
<el-table-column
v-for="column in generateWaybillColumns"
:key="column.prop"
:prop="column.prop"
:label="column.label"
:min-width="column.minWidth"
align="center"
show-overflow-tooltip
>
<template #default="{ row }">
<el-link v-if="column.link && row[column.prop]" type="primary">
{{ row[column.prop] }}
</el-link>
<span v-else>{{ row[column.prop] || '-' }}</span>
</template>
</el-table-column>
</el-table>
<div class="settlement-detail-page__pagination">
<el-pagination
v-model:current-page="generatePage.current"
v-model:page-size="generatePage.size"
:total="generatePage.total"
:page-sizes="[10, 20, 50]"
layout="total, sizes, prev, pager, next, jumper"
@current-change="loadGenerateWaybills"
@size-change="handleGenerateSizeChange"
/>
</div>
<template #footer>
<el-button type="primary" @click="openGeneratePreview">生成费用</el-button>
<el-button @click="generateDialog.visible = false">取消</el-button>
</template>
</el-dialog>
<el-dialog v-model="previewDialog.visible" title="生成费用明细" width="88%" append-to-body>
<el-table v-loading="previewDialog.loading" :data="previewRows" border>
<el-table-column type="index" label="序号" width="70" align="center" />
<el-table-column
v-for="column in previewColumns"
: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="previewPage.current"
v-model:page-size="previewPage.size"
:total="previewPage.total"
:page-sizes="[10, 20, 50]"
layout="total, sizes, prev, pager, next, jumper"
@current-change="loadGeneratePreview"
@size-change="handlePreviewSizeChange"
/>
</div>
<template #footer>
<el-button type="primary" @click="generateDialog.visible = true">上一步</el-button>
<el-button type="primary" :loading="previewDialog.submitting" @click="submitGenerateFee">
确认
</el-button>
<el-button @click="previewDialog.visible = false">取消</el-button>
</template>
</el-dialog>
</basic-container>
</template>
<script>
import { ArrowDown, ArrowUp, Refresh, Setting } from '@element-plus/icons-vue';
import {
changeRecordColumns,
feeDetailBaseColumns,
feeDetailTailColumns,
generatePreviewColumns,
generateWaybillColumns,
searchFields,
settlementStatusOptions,
tableColumns,
transferSearchFields,
} from '@/option/settlement/receivable-payable-detail';
import * as api from '@/api/settlement/receivable-payable-detail';
import { getList as getContractList } from '@/api/business/contract-manage';
import { exportBlob } from '@/api/common';
import { downloadXls } from '@/utils/util';
export default {
props: {
settlementType: {
type: String,
default: '',
},
},
data() {
return {
ArrowDown,
ArrowUp,
Refresh,
Setting,
searchFields,
tableColumns,
changeRecordColumns,
transferSearchFields,
generateWaybillColumns,
loading: false,
searchExpanded: false,
query: {},
rows: [],
selection: [],
page: { current: 1, size: 10, total: 0 },
detailDialog: { visible: false, title: '费用明细', activeTab: 'fee', row: null, loading: false },
feeRows: [],
dynamicFeeColumns: [],
changeRows: [],
changePage: { current: 1, size: 10, total: 0 },
changeDialog: { loading: false },
updateFeeDialog: { visible: false, submitting: false, row: null },
updateFeeForm: { contractId: '', billingPlanId: '' },
updateFeeRules: {
contractId: [{ required: true, message: '请选择更新范围', trigger: 'change' }],
},
contractOptions: [],
billingPlanOptions: [],
transferDialog: { visible: false, loading: false, submitting: false },
transferQuery: {},
transferForm: { settlementBillType: 'formal' },
transferRows: [],
transferSelection: [],
generateDialog: { visible: false, loading: false },
generateQuery: {},
generateRows: [],
generateSelection: [],
generatePage: { current: 1, size: 10, total: 0 },
previewDialog: { visible: false, loading: false, submitting: false },
previewRows: [],
previewPage: { current: 1, size: 10, total: 0 },
};
},
computed: {
settlementTypeLabel() {
if (this.settlementType === 'receivable') return '应收';
if (this.settlementType === 'payable') return '应付';
return '应收应付';
},
visibleSearchFields() {
return this.searchExpanded ? this.searchFields : this.searchFields.slice(0, 7);
},
feeDetailColumns() {
return [...feeDetailBaseColumns, ...this.dynamicFeeColumns, ...feeDetailTailColumns];
},
previewColumns() {
return [...generatePreviewColumns, ...this.dynamicFeeColumns, ...feeDetailTailColumns];
},
},
mounted() {
this.loadContracts();
this.loadTable();
},
methods: {
async loadTable() {
this.loading = true;
try {
const params = this.buildRequestParams(
this.normalizeQuery(this.query, 'generateDateRange', 'generateStartDate', 'generateEndDate')
);
const res = await api.getList(this.page.current, this.page.size, params);
const data = this.unwrapPage(res);
this.rows = (data.records || []).map(this.decorateRow);
this.page.total = data.total || 0;
} finally {
this.loading = false;
}
},
async loadContracts() {
const res = await getContractList(1, 999, { approvalStatus: 'approved' });
const data = this.unwrapPage(res);
this.contractOptions = data.records || [];
},
handleSearch() {
this.page.current = 1;
this.loadTable();
},
handleReset() {
this.query = {};
this.handleSearch();
},
toggleSearch() {
this.searchExpanded = !this.searchExpanded;
},
handleSizeChange() {
this.page.current = 1;
this.loadTable();
},
selectionChange(selection) {
this.selection = selection;
},
async openDetailDialog(row) {
this.detailDialog = { ...this.detailDialog, visible: true, row, title: row.documentNo, activeTab: 'fee' };
await this.loadFeeDetail();
},
async loadFeeDetail() {
if (!this.detailDialog.row) return;
this.detailDialog.loading = true;
try {
const res = await api.getFeeDetail(this.detailDialog.row.id);
const data = res.data?.data || res.data || res || {};
this.dynamicFeeColumns = (data.feeItemNames || []).map((name, index) => ({
label: name,
prop: `feeItem${index}`,
minWidth: 130,
align: 'right',
}));
this.feeRows = (data.records || []).map(row => {
const dynamic = {};
(data.feeItemNames || []).forEach((name, index) => {
dynamic[`feeItem${index}`] = row.feeItems?.[name] || '';
});
return { ...row, ...dynamic };
});
} finally {
this.detailDialog.loading = false;
}
},
handleDetailTabChange(name) {
if (name === 'change') this.loadChangeRecords();
},
async loadChangeRecords() {
if (!this.detailDialog.row) return;
this.changeDialog.loading = true;
try {
const res = await api.getChangeRecords(this.changePage.current, this.changePage.size, {
detailId: this.detailDialog.row.id,
});
const data = this.unwrapPage(res);
this.changeRows = data.records || [];
this.changePage.total = data.total || 0;
} finally {
this.changeDialog.loading = false;
}
},
handleChangeSizeChange() {
this.changePage.current = 1;
this.loadChangeRecords();
},
openUpdateFeeDialog(row) {
this.updateFeeDialog = { ...this.updateFeeDialog, visible: true, row: row || null };
this.updateFeeForm = {
ids: row ? [row.id] : this.selection.map(item => item.id),
settlementType: this.settlementType || undefined,
contractId: row?.contractId || '',
billingPlanId: '',
};
this.syncBillingPlanOptions(this.updateFeeForm.contractId);
},
async confirmUpdateFee() {
await this.$refs.updateFeeFormRef.validate();
await this.$confirm(
'更新费用将使用最新的合同计费规则重新生成费用明细,确认更新费用?',
'确认提示',
{ type: 'warning' }
);
this.updateFeeDialog.submitting = true;
try {
await api.updateFee(this.updateFeeForm);
this.$message.success('更新成功');
this.updateFeeDialog.visible = false;
this.loadTable();
} finally {
this.updateFeeDialog.submitting = false;
}
},
async closeRow(row) {
await this.$confirm('确认关闭该费用明细?', '提示', { type: 'warning' });
await api.updateFee({
ids: [row.id],
closeOnly: true,
settlementType: this.settlementType || undefined,
});
this.$message.success('关闭成功');
this.loadTable();
},
openTransferDialog() {
this.transferDialog.visible = true;
this.transferForm = { settlementBillType: 'formal' };
this.transferQuery = {};
this.transferRows = [];
this.transferSelection = [];
this.loadTransferCandidates();
},
async loadTransferCandidates() {
this.transferDialog.loading = true;
try {
const params = this.buildRequestParams(
this.normalizeQuery(this.transferQuery, 'generateDateRange', 'generateStartDate', 'generateEndDate')
);
const res = await api.getTransferCandidates(1, 50, {
...params,
settlementBillType: this.transferForm.settlementBillType,
});
const data = this.unwrapPage(res);
this.transferRows = (data.records || []).map(this.decorateRow);
} finally {
this.transferDialog.loading = false;
}
},
confirmTransferSelection() {
if (!this.transferSelection.length) {
this.$message.warning('请选择需要转结算的明细');
return;
}
this.$message.success(`已选择${this.transferSelection.length}条明细`);
},
transferSelectionChange(selection) {
this.transferSelection = selection;
},
async submitTransfer() {
if (!this.transferSelection.length) {
this.$message.warning('请选择需要转结算的明细');
return;
}
this.transferDialog.submitting = true;
try {
await api.transferSettlement({
settlementType: this.settlementType || undefined,
settlementBillType: this.transferForm.settlementBillType,
ids: this.transferSelection.map(item => item.id),
});
this.$message.success('转结算成功');
this.transferDialog.visible = false;
this.loadTable();
} finally {
this.transferDialog.submitting = false;
}
},
openGenerateDialog() {
this.generateDialog.visible = true;
this.generateQuery = {};
this.generateRows = [];
this.generateSelection = [];
this.generatePage = { current: 1, size: 10, total: 0 };
},
handleGenerateContractChange(contractId) {
this.syncBillingPlanOptions(contractId);
this.generateQuery.billingPlanId = '';
},
async loadGenerateWaybills() {
if (!this.generateQuery.contractId || !this.generateQuery.billingPlanId) {
this.$message.warning('请选择运单合同和计费方案');
return;
}
this.generateDialog.loading = true;
try {
const params = this.buildRequestParams(
this.normalizeQuery(this.generateQuery, 'finishDateRange', 'finishStartDate', 'finishEndDate')
);
const res = await api.getGenerateWaybills(this.generatePage.current, this.generatePage.size, params);
const data = this.unwrapPage(res);
this.generateRows = data.records || [];
this.generatePage.total = data.total || 0;
} finally {
this.generateDialog.loading = false;
}
},
handleGenerateSizeChange() {
this.generatePage.current = 1;
this.loadGenerateWaybills();
},
resetGenerateQuery() {
this.generateQuery = {};
this.generateRows = [];
this.generateSelection = [];
this.generatePage = { current: 1, size: 10, total: 0 };
},
generateSelectionChange(selection) {
this.generateSelection = selection;
},
openGeneratePreview() {
if (!this.generateSelection.length) {
this.$message.warning('请选择需要生成费用的运单');
return;
}
this.previewDialog.visible = true;
this.generateDialog.visible = false;
this.previewPage.current = 1;
this.loadGeneratePreview();
},
async loadGeneratePreview() {
this.previewDialog.loading = true;
try {
const res = await api.getGeneratePreview(this.previewPage.current, this.previewPage.size, {
settlementType: this.settlementType || undefined,
contractId: this.generateQuery.contractId,
billingPlanId: this.generateQuery.billingPlanId,
waybillIds: this.generateSelection.map(item => item.id).join(','),
});
const data = res.data?.data || res.data || res || {};
this.dynamicFeeColumns = (data.feeItemNames || []).map((name, index) => ({
label: name,
prop: `feeItem${index}`,
minWidth: 130,
align: 'right',
}));
this.previewRows = (data.records || []).map(row => {
const dynamic = {};
(data.feeItemNames || []).forEach((name, index) => {
dynamic[`feeItem${index}`] = row.feeItems?.[name] || '';
});
return { ...row, ...dynamic };
});
this.previewPage.total = data.total || 0;
} finally {
this.previewDialog.loading = false;
}
},
handlePreviewSizeChange() {
this.previewPage.current = 1;
this.loadGeneratePreview();
},
async submitGenerateFee() {
this.previewDialog.submitting = true;
try {
await api.generateFee({
settlementType: this.settlementType || undefined,
contractId: this.generateQuery.contractId,
billingPlanId: this.generateQuery.billingPlanId,
waybillIds: this.generateSelection.map(item => item.id),
});
this.$message.success('生成成功');
this.previewDialog.visible = false;
this.loadTable();
} finally {
this.previewDialog.submitting = false;
}
},
handleExport() {
exportBlob(
'/blade-transport/receivable-payable-detail/export-receivable-payable-detail',
this.buildRequestParams(
this.normalizeQuery(this.query, 'generateDateRange', 'generateStartDate', 'generateEndDate')
),
{ feedback: true }
).then(res => {
downloadXls(
res.data,
`${this.settlementTypeLabel}明细${this.$dayjs().format('YYYY-MM-DD HH:mm:ss')}.xlsx`
);
});
},
buildRequestParams(params = {}) {
return this.settlementType ? { ...params, settlementType: this.settlementType } : params;
},
syncBillingPlanOptions(contractId) {
const contract = this.contractOptions.find(item => String(item.id) === String(contractId));
const plans = this.parseJson(contract?.billingPlanJson);
this.billingPlanOptions = plans.length
? plans.map((item, index) => ({
id: item.id || item.planId || item.name || `plan-${index}`,
name: item.name || item.planName || item.billingPlanName || `计费方案${index + 1}`,
}))
: [{ id: 'default', name: '默认计费方案' }];
},
normalizeQuery(source, rangeProp, startProp, endProp) {
const params = { ...source };
const range = params[rangeProp];
delete params[rangeProp];
if (Array.isArray(range)) {
params[startProp] = range[0];
params[endProp] = range[1];
}
return params;
},
decorateRow(row) {
const currency = row.currency || 'RMB';
return {
...row,
unitPriceText: this.money(row.unitPrice, currency),
freightAmountText: this.money(row.freightAmount, currency),
otherFeeAmountText: this.money(row.otherFeeAmount, currency),
totalAmountText: this.money(row.totalAmount, currency),
settlementStatusName:
settlementStatusOptions.find(item => item.value === row.settlementStatus)?.label ||
row.settlementStatus ||
'-',
};
},
money(value, currency) {
if (value === null || value === undefined || value === '') return '-';
return `${Number(value).toFixed(2)} ${currency}`;
},
formatCell(value) {
return value === null || value === undefined || value === '' ? '-' : value;
},
parseJson(value) {
if (!value) return [];
try {
const data = JSON.parse(value);
return Array.isArray(data) ? data : [];
} catch (error) {
return [];
}
},
unwrapPage(res) {
const data = res.data?.data || res.data || res || {};
return data.records ? data : { records: data.records || [], total: data.total || 0 };
},
noop() {},
},
};
</script>
<style scoped lang="scss">
.settlement-detail-page {
color: #303133;
}
.settlement-detail-page__search {
margin-bottom: 8px;
padding: 12px 12px 4px;
background: #fff;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
:deep(.el-form-item__label) {
min-width: 160px;
white-space: nowrap;
}
}
.settlement-detail-page__search-grid {
display: grid;
grid-template-columns: repeat(7, minmax(220px, 1fr));
gap: 8px 24px;
align-items: start;
:deep(.el-form-item) {
margin-bottom: 8px;
}
:deep(.el-input),
:deep(.el-select),
:deep(.el-date-editor) {
width: 100%;
}
}
.settlement-detail-page__search-actions {
grid-column: 1 / -1;
display: flex;
justify-content: flex-end;
gap: 8px;
margin-bottom: 8px;
}
.settlement-detail-page__table-panel {
background: #fff;
}
.settlement-detail-page__toolbar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 0;
background: transparent;
}
.settlement-detail-page__toolbar-left,
.settlement-detail-page__toolbar-right {
display: flex;
align-items: center;
gap: 8px;
}
.settlement-detail-page__table,
.settlement-detail-page :deep(.el-table) {
--el-table-border-color: #eff1f7;
:deep(th.el-table__cell) {
background: #f5f7fa;
}
:deep(.el-table__row:nth-child(even) td.el-table__cell) {
background: #fafafa;
}
}
.settlement-detail-page__actions {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 8px;
}
.settlement-detail-page__pagination {
display: flex;
justify-content: flex-end;
padding: 16px 0;
}
.settlement-detail-page__dialog-form {
padding: 16px 20px 0;
:deep(.el-form-item) {
margin-bottom: 16px;
}
:deep(.el-select) {
width: 100%;
}
}
.settlement-detail-page__transfer-form {
margin-bottom: 16px;
}
@media screen and (max-width: 1600px) {
.settlement-detail-page__search-grid {
grid-template-columns: repeat(4, minmax(220px, 1fr));
}
}
@media screen and (max-width: 900px) {
.settlement-detail-page__search-grid {
grid-template-columns: 1fr;
}
}
</style>