1、新增应付明细
2、新增应收明细 3、新增异常处置 4、新增风险处置 5、新增在途追踪 6、修复业务模块bug
This commit is contained in:
@@ -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>
|
||||
Reference in New Issue
Block a user