b07205be8c
# Conflicts: # src/views/settlement/transport-reconciliation.vue
1406 lines
48 KiB
Vue
1406 lines
48 KiB
Vue
<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 text :icon="searchExpanded ? ArrowUp : ArrowDown" @click="toggleSearch">
|
|
{{ searchExpanded ? '收起' : '展开' }}
|
|
</el-button>
|
|
</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" plain @click="openUpdateFeeDialog">更新费用</el-button>
|
|
<el-button type="primary" plain @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 displayTableColumns"
|
|
: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>{{ formatColumnValue(row, column) }}</span>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="操作" width="120" fixed="right" align="center">
|
|
<template #default="{ row }">
|
|
<div class="settlement-detail-page__actions">
|
|
<el-link v-if="row.settlementStatus === 'pending'" type="primary" @click="openAdjustDialog(row)">
|
|
调整
|
|
</el-link>
|
|
<el-link v-if="row.settlementStatus === 'pending'" type="danger" @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 type="border-card" style="background-color: #ffffff" 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 }">{{ formatDetailCell(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="adjustDialog.visible"
|
|
title="调整费用"
|
|
width="96%"
|
|
append-to-body
|
|
destroy-on-close
|
|
>
|
|
<div class="settlement-detail-page__adjust-toolbar">
|
|
<el-button type="primary" plain @click="addManualAdjustRow">新增费用项</el-button>
|
|
</div>
|
|
<el-table v-loading="adjustDialog.loading" :data="adjustRows" border>
|
|
<el-table-column type="index" label="序号" width="64" align="center" />
|
|
<el-table-column label="手工费用项目" min-width="180" align="center">
|
|
<template #default="{ row }">
|
|
<el-input
|
|
v-if="row.manualFee"
|
|
v-model="row.feeItemName"
|
|
clearable
|
|
placeholder="请输入收费或扣费项目"
|
|
/>
|
|
<span v-else>-</span>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="收费/扣费" width="130" align="center">
|
|
<template #default="{ row }">
|
|
<el-select
|
|
v-if="row.manualFee"
|
|
v-model="row.feeType"
|
|
placeholder="请选择"
|
|
@change="recalculateManualAdjustRow(row)"
|
|
>
|
|
<el-option label="收费" value="charge" />
|
|
<el-option label="扣费" value="deduct" />
|
|
</el-select>
|
|
<span v-else>-</span>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="手工金额" width="150" align="right">
|
|
<template #default="{ row }">
|
|
<el-input-number
|
|
v-if="row.manualFee"
|
|
v-model="row.amount"
|
|
:min="0"
|
|
:precision="2"
|
|
controls-position="right"
|
|
@change="recalculateManualAdjustRow(row)"
|
|
/>
|
|
<span v-else>-</span>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="操作" width="90" align="center">
|
|
<template #default="{ row }">
|
|
<el-link
|
|
v-if="row.manualFee && !row.id"
|
|
type="danger"
|
|
@click="removeManualAdjustRow(row)"
|
|
>
|
|
删除
|
|
</el-link>
|
|
<span v-else>-</span>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column
|
|
v-for="column in adjustFeeColumns"
|
|
:key="column.prop || column.feeItemName"
|
|
:label="column.label"
|
|
:min-width="column.minWidth"
|
|
:align="column.align || 'center'"
|
|
show-overflow-tooltip
|
|
>
|
|
<template #default="{ row }">
|
|
<el-input-number
|
|
v-if="column.prop === 'transportQuantityText' && !row.manualFee"
|
|
v-model="row.transportQuantity"
|
|
:min="0"
|
|
:precision="2"
|
|
controls-position="right"
|
|
@change="recalculateAdjustRow(row)"
|
|
/>
|
|
<el-input-number
|
|
v-else-if="column.prop === 'mileage' && !row.manualFee"
|
|
v-model="row.mileage"
|
|
:min="0"
|
|
:precision="2"
|
|
controls-position="right"
|
|
@change="recalculateAdjustRow(row)"
|
|
/>
|
|
<el-input-number
|
|
v-else-if="column.prop === 'freightAmount' && !row.manualFee"
|
|
v-model="row.freightAmount"
|
|
:min="0"
|
|
:precision="2"
|
|
controls-position="right"
|
|
@change="recalculateAdjustRow(row, 'freight')"
|
|
/>
|
|
<el-input-number
|
|
v-else-if="column.dynamic && !row.manualFee"
|
|
v-model="row.feeItems[column.feeItemName]"
|
|
:min="0"
|
|
:precision="2"
|
|
controls-position="right"
|
|
@change="recalculateAdjustRow(row, column.feeItemName)"
|
|
/>
|
|
<el-input
|
|
v-else-if="column.prop === 'remark'"
|
|
v-model="row.remark"
|
|
clearable
|
|
maxlength="200"
|
|
placeholder="请输入备注"
|
|
/>
|
|
<span v-else-if="column.prop === 'adjustAmountText'">
|
|
{{ fixedTwoDecimals(row.adjustAmount) }}
|
|
</span>
|
|
<span v-else-if="column.prop === 'afterAmountText'">
|
|
{{ fixedTwoDecimals(row.afterAmount) }}
|
|
</span>
|
|
<span v-else>{{ formatDetailCell(row, column.prop) }}</span>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
<template #footer>
|
|
<el-button @click="adjustDialog.visible = false">取消</el-button>
|
|
<el-button type="primary" :loading="adjustDialog.submitting" @click="saveAdjustFee">
|
|
保存
|
|
</el-button>
|
|
</template>
|
|
</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="请选择合同"
|
|
@change="handleUpdateContractChange"
|
|
>
|
|
<el-option
|
|
v-for="item in updateContractOptions"
|
|
: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
|
|
:disabled="!updateFeeForm.contractId"
|
|
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 @click="updateFeeDialog.visible = false">取消</el-button>
|
|
<el-button type="primary" :loading="updateFeeDialog.submitting" @click="confirmUpdateFee">
|
|
提交
|
|
</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"
|
|
@change="loadTransferCandidates"
|
|
>
|
|
<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
|
|
>
|
|
<template #default="{ row }">{{ formatColumnValue(row, column) }}</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
<template #footer>
|
|
<el-button @click="transferDialog.visible = false">取消</el-button>
|
|
<el-button type="primary" :loading="transferDialog.submitting" @click="submitTransfer">
|
|
提交
|
|
</el-button>
|
|
</template>
|
|
</el-dialog>
|
|
|
|
<el-dialog v-model="generateDialog.visible" title="生成费用" width="88%" append-to-body>
|
|
<el-form
|
|
:model="generateQuery"
|
|
class="settlement-detail-page__generate-search"
|
|
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>{{ formatColumnValue(row, column) }}</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 @click="generateDialog.visible = false">取消</el-button>
|
|
<el-button type="primary" @click="openGeneratePreview">生成费用</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 @click="previewDialog.visible = false">取消</el-button>
|
|
<el-button type="primary" @click="generateDialog.visible = true">上一步</el-button>
|
|
<el-button type="primary" :loading="previewDialog.submitting" @click="submitGenerateFee">
|
|
提交
|
|
</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 { getDictionary } from '@/api/system/dictbiz';
|
|
import { downloadXls } from '@/utils/util';
|
|
|
|
export default {
|
|
props: {
|
|
settlementType: {
|
|
type: String,
|
|
default: '',
|
|
},
|
|
},
|
|
data() {
|
|
return {
|
|
ArrowDown,
|
|
ArrowUp,
|
|
Refresh,
|
|
Setting,
|
|
searchFields,
|
|
tableColumns,
|
|
tableFeeItemNames: [],
|
|
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: [],
|
|
adjustDialog: { visible: false, loading: false, submitting: false, row: null },
|
|
adjustRows: [],
|
|
adjustDynamicFeeColumns: [],
|
|
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: [],
|
|
updateContractOptions: [],
|
|
transportTypeOptions: [],
|
|
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, 4);
|
|
},
|
|
feeDetailColumns() {
|
|
return [...feeDetailBaseColumns, ...this.dynamicFeeColumns, ...feeDetailTailColumns];
|
|
},
|
|
displayTableColumns() {
|
|
const columns = [...this.tableColumns];
|
|
const totalIndex = columns.findIndex(column => column.prop === 'totalAmountText');
|
|
const dynamicColumns = this.tableFeeItemNames.map((name, index) => ({
|
|
label: name,
|
|
prop: `tableFeeItem${index}`,
|
|
feeItemName: name,
|
|
dynamic: true,
|
|
minWidth: 130,
|
|
align: 'right',
|
|
}));
|
|
if (totalIndex < 0) return [...columns, ...dynamicColumns];
|
|
columns.splice(totalIndex, 0, ...dynamicColumns);
|
|
return columns;
|
|
},
|
|
previewColumns() {
|
|
return [...generatePreviewColumns, ...this.dynamicFeeColumns, ...feeDetailTailColumns];
|
|
},
|
|
adjustFeeColumns() {
|
|
return [
|
|
...feeDetailBaseColumns,
|
|
...this.adjustDynamicFeeColumns,
|
|
...feeDetailTailColumns,
|
|
];
|
|
},
|
|
},
|
|
mounted() {
|
|
this.loadTransportTypeOptions();
|
|
this.loadContracts();
|
|
this.loadTable();
|
|
},
|
|
methods: {
|
|
async loadTransportTypeOptions() {
|
|
const res = await getDictionary({ code: 'transport_type' });
|
|
const records = res.data?.data || [];
|
|
this.transportTypeOptions = records.map(item => ({
|
|
label: item.dictValue || item.label || item.name,
|
|
value: item.dictKey || item.value || item.dictValue || item.name,
|
|
}));
|
|
},
|
|
transportTypeLabel(value) {
|
|
if (value === null || value === undefined || value === '') return '-';
|
|
return (
|
|
this.transportTypeOptions.find(item => String(item.value) === String(value))?.label || value
|
|
);
|
|
},
|
|
formatColumnValue(row, column) {
|
|
if (column.prop === 'transportType') return this.transportTypeLabel(row[column.prop]);
|
|
if (column.dynamic) {
|
|
return this.money(this.normalizeFeeItems(row.feeItems)[column.feeItemName], row.currency || 'RMB');
|
|
}
|
|
return this.formatDetailCell(row, column.prop);
|
|
},
|
|
async loadTable() {
|
|
this.loading = true;
|
|
this.tableFeeItemNames = [];
|
|
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.tableFeeItemNames = this.collectFeeItemNames(this.rows);
|
|
this.page.total = data.total || 0;
|
|
} finally {
|
|
this.loading = false;
|
|
}
|
|
},
|
|
async loadContracts() {
|
|
const contractCategory =
|
|
this.settlementType === 'payable' ? '承运商合同' : '客户合同';
|
|
const res = await getContractList(1, 999, {
|
|
approvalStatus: 'approved',
|
|
contractCategory,
|
|
});
|
|
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 openAdjustDialog(row) {
|
|
this.adjustDialog = { ...this.adjustDialog, visible: true, row, loading: true };
|
|
try {
|
|
const res = await api.getFeeDetail(row.id);
|
|
const data = res.data?.data || res.data || res || {};
|
|
this.adjustDynamicFeeColumns = (data.feeItemNames || []).map(name => ({
|
|
label: name,
|
|
feeItemName: name,
|
|
dynamic: true,
|
|
minWidth: 150,
|
|
align: 'right',
|
|
}));
|
|
this.adjustRows = (data.records || []).map(item => {
|
|
const feeItems = {};
|
|
(data.feeItemNames || []).forEach(name => {
|
|
feeItems[name] = Number(item.feeItems?.[name] || 0);
|
|
});
|
|
const adjusted = {
|
|
...item,
|
|
transportQuantity: Number(item.transportQuantity || 0),
|
|
mileage:
|
|
item.mileage === null || item.mileage === undefined || Number(item.mileage) === -1
|
|
? null
|
|
: Number(item.mileage),
|
|
freightAmount: Number(item.freightAmount || 0),
|
|
originalAmount: Number(item.originalAmount || 0),
|
|
feeItems,
|
|
manualFee: item.billingFactor === '手工调整',
|
|
feeItemName:
|
|
item.billingFactor === '手工调整' ? Object.keys(feeItems)[0] || '' : '',
|
|
feeType: item.billingType === '手工扣费' ? 'deduct' : 'charge',
|
|
amount:
|
|
item.billingFactor === '手工调整'
|
|
? Math.abs(Number(item.afterAmount || 0))
|
|
: 0,
|
|
};
|
|
if (adjusted.manualFee) this.recalculateManualAdjustRow(adjusted);
|
|
else this.recalculateAdjustRow(adjusted);
|
|
return adjusted;
|
|
});
|
|
} finally {
|
|
this.adjustDialog.loading = false;
|
|
}
|
|
},
|
|
addManualAdjustRow() {
|
|
this.adjustRows.push({
|
|
id: null,
|
|
manualFee: true,
|
|
feeItemName: '',
|
|
feeType: 'charge',
|
|
amount: 0,
|
|
transportQuantity: null,
|
|
mileage: null,
|
|
freightAmount: 0,
|
|
originalAmount: 0,
|
|
feeItems: {},
|
|
remark: '',
|
|
});
|
|
},
|
|
removeManualAdjustRow(row) {
|
|
const index = this.adjustRows.indexOf(row);
|
|
if (index >= 0) this.adjustRows.splice(index, 1);
|
|
},
|
|
recalculateManualAdjustRow(row) {
|
|
const amount = Number(row.amount || 0);
|
|
row.afterAmount = Number((row.feeType === 'deduct' ? -amount : amount).toFixed(2));
|
|
row.adjustAmount = Number((row.afterAmount - Number(row.originalAmount || 0)).toFixed(2));
|
|
},
|
|
fixedTwoDecimals(value) {
|
|
return Number(value || 0).toFixed(2);
|
|
},
|
|
recalculateAdjustRow(row, changedField) {
|
|
if (changedField && changedField !== 'freight' && this.isFreightFeeItem(changedField)) {
|
|
row.freightAmount = Number(row.feeItems[changedField] || 0);
|
|
}
|
|
if (changedField === 'freight') {
|
|
const freightItem = Object.keys(row.feeItems).find(this.isFreightFeeItem);
|
|
if (freightItem) row.feeItems[freightItem] = Number(row.freightAmount || 0);
|
|
}
|
|
const feeItemTotal = Object.values(row.feeItems).reduce(
|
|
(total, value) => total + Number(value || 0),
|
|
0
|
|
);
|
|
const hasFreightItem = Object.keys(row.feeItems).some(this.isFreightFeeItem);
|
|
row.afterAmount = Number((hasFreightItem ? feeItemTotal : Number(row.freightAmount || 0) + feeItemTotal).toFixed(2));
|
|
row.adjustAmount = Number((row.afterAmount - Number(row.originalAmount || 0)).toFixed(2));
|
|
},
|
|
isFreightFeeItem(name) {
|
|
return String(name || '').includes('运费') || String(name || '').includes('运输费');
|
|
},
|
|
async saveAdjustFee() {
|
|
if (!this.adjustRows.length || !this.adjustDialog.row) {
|
|
this.$message.warning('没有可调整的费用明细');
|
|
return;
|
|
}
|
|
const invalidManualRow = this.adjustRows.find(
|
|
row => row.manualFee && (!String(row.feeItemName || '').trim() || Number(row.amount || 0) <= 0)
|
|
);
|
|
if (invalidManualRow) {
|
|
this.$message.warning('请完整填写手工费用项目和金额');
|
|
return;
|
|
}
|
|
this.adjustDialog.submitting = true;
|
|
try {
|
|
await api.adjustFee({
|
|
detailId: this.adjustDialog.row.id,
|
|
rows: this.adjustRows.map(row => ({
|
|
id: row.id,
|
|
transportQuantity: row.transportQuantity,
|
|
mileage: row.mileage,
|
|
freightAmount: row.freightAmount,
|
|
feeItems: row.feeItems,
|
|
manualFee: row.manualFee === true,
|
|
feeItemName: row.feeItemName,
|
|
feeType: row.feeType,
|
|
amount: row.amount,
|
|
remark: row.remark,
|
|
})),
|
|
});
|
|
this.$message.success('保存成功');
|
|
this.adjustDialog.visible = false;
|
|
await this.loadTable();
|
|
} finally {
|
|
this.adjustDialog.submitting = false;
|
|
}
|
|
},
|
|
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();
|
|
},
|
|
async openUpdateFeeDialog(row) {
|
|
await this.loadUpdateFeeContracts();
|
|
this.updateFeeDialog = { ...this.updateFeeDialog, visible: true, row: row || null };
|
|
this.updateFeeForm = {
|
|
settlementType: this.settlementType || undefined,
|
|
contractId: row?.contractId || '',
|
|
billingPlanId: '',
|
|
};
|
|
this.handleUpdateContractChange(this.updateFeeForm.contractId);
|
|
},
|
|
async loadUpdateFeeContracts() {
|
|
const res = await api.getUpdateFeeContracts({
|
|
settlementType: this.settlementType || undefined,
|
|
});
|
|
this.updateContractOptions = res.data?.data || [];
|
|
},
|
|
handleUpdateContractChange(contractId) {
|
|
this.updateFeeForm.billingPlanId = '';
|
|
if (!contractId) {
|
|
this.billingPlanOptions = [];
|
|
return;
|
|
}
|
|
const options = this.syncBillingPlanOptions(contractId);
|
|
const selected = options.find(item => item.defaultPlan) || options[0];
|
|
this.updateFeeForm.billingPlanId = selected?.id || '';
|
|
},
|
|
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;
|
|
this.transferSelection = [];
|
|
try {
|
|
const params = this.buildRequestParams(
|
|
this.normalizeQuery(
|
|
this.transferQuery,
|
|
'generateDateRange',
|
|
'generateStartDate',
|
|
'generateEndDate'
|
|
)
|
|
);
|
|
const res = await api.getTransferCandidates(1, 50, {
|
|
...params,
|
|
settlementStatus: 'pending',
|
|
settlementType: this.settlementType || undefined,
|
|
settlementBillType: this.transferForm.settlementBillType,
|
|
});
|
|
const data = this.unwrapPage(res);
|
|
this.transferRows = (data.records || [])
|
|
.filter(row => this.isTransferCandidate(row))
|
|
.map(this.decorateRow);
|
|
} finally {
|
|
this.transferDialog.loading = false;
|
|
}
|
|
},
|
|
isTransferCandidate(row) {
|
|
const hasValue = value => {
|
|
if (Array.isArray(value)) return value.length > 0;
|
|
return value !== null && value !== undefined && String(value).trim() !== '';
|
|
};
|
|
const normalizeSettlementType = value => {
|
|
const normalized = String(value || '').trim().toLowerCase();
|
|
if (normalized === '应收') return 'receivable';
|
|
if (normalized === '应付') return 'payable';
|
|
return normalized;
|
|
};
|
|
const rowSettlementType = normalizeSettlementType(
|
|
row.settlementType || row.settlementTypeName
|
|
);
|
|
const expectedSettlementType = normalizeSettlementType(this.settlementType);
|
|
const matchesSettlementType =
|
|
!expectedSettlementType || rowSettlementType === expectedSettlementType;
|
|
const status = String(row.settlementStatus || '').trim().toLowerCase();
|
|
const isClosed =
|
|
[row.closed, row.isClosed, row.closeFlag, row.closedFlag].some(
|
|
value => value === true || String(value).trim().toLowerCase() === 'true'
|
|
) ||
|
|
['closed', 'close', '已关闭'].includes(status);
|
|
const hasPreSettlement = [
|
|
row.preSettlementId,
|
|
row.preSettlementIds,
|
|
row.preSettlementNo,
|
|
row.preSettlementNos,
|
|
].some(hasValue);
|
|
const hasFormalSettlement = [
|
|
row.formalSettlementId,
|
|
row.formalSettlementIds,
|
|
row.formalSettlementNo,
|
|
row.formalSettlementNos,
|
|
].some(hasValue);
|
|
return (
|
|
matchesSettlementType &&
|
|
['pending', '待结算'].includes(status) &&
|
|
!isClosed &&
|
|
!hasPreSettlement &&
|
|
!hasFormalSettlement
|
|
);
|
|
},
|
|
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 contracts = [...this.updateContractOptions, ...this.contractOptions];
|
|
const contract = contracts.find(item => String(item.id) === String(contractId));
|
|
const plans = this.parseJson(contract?.billingPlanJson);
|
|
this.billingPlanOptions = plans.map((item, index) => ({
|
|
id: item.id || item.planId || item.name || `plan-${index}`,
|
|
name: item.name || item.planName || item.billingPlanName || `计费方案${index + 1}`,
|
|
defaultPlan:
|
|
item.defaultPlan === true || String(item.defaultPlan).toLowerCase() === 'true',
|
|
}));
|
|
return this.billingPlanOptions;
|
|
},
|
|
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 ||
|
|
'-',
|
|
};
|
|
},
|
|
collectFeeItemNames(rows) {
|
|
const names = [];
|
|
(rows || []).forEach(row => {
|
|
Object.keys(this.normalizeFeeItems(row.feeItems)).forEach(name => {
|
|
if (!names.includes(name)) names.push(name);
|
|
});
|
|
});
|
|
return names;
|
|
},
|
|
normalizeFeeItems(value) {
|
|
if (value && typeof value === 'object' && !Array.isArray(value)) return value;
|
|
if (typeof value !== 'string' || !value.trim()) return {};
|
|
try {
|
|
const parsed = JSON.parse(value);
|
|
return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
|
|
} catch (error) {
|
|
return {};
|
|
}
|
|
},
|
|
money(value, currency) {
|
|
if (value === null || value === undefined || value === '') return '-';
|
|
return `${Number(value).toFixed(2)} ${currency}`;
|
|
},
|
|
formatDetailCell(row, prop) {
|
|
if (
|
|
prop === 'mileage' &&
|
|
(row?.[prop] === null ||
|
|
row?.[prop] === undefined ||
|
|
row?.[prop] === '' ||
|
|
Number(row[prop]) === -1)
|
|
) {
|
|
return '';
|
|
}
|
|
return this.formatCell(row?.[prop]);
|
|
},
|
|
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.04);
|
|
|
|
:deep(.el-form-item__label) {
|
|
min-width: 160px;
|
|
white-space: nowrap;
|
|
}
|
|
}
|
|
|
|
.settlement-detail-page__search-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(4, 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__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;
|
|
background: #fff;
|
|
|
|
: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;
|
|
background: #fff;
|
|
border-radius: 6px;
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
|
|
|
:deep(.el-form-item) {
|
|
margin-bottom: 16px;
|
|
|
|
&:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
}
|
|
|
|
:deep(.el-select) {
|
|
width: 100%;
|
|
}
|
|
}
|
|
|
|
.settlement-detail-page__adjust-toolbar {
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.settlement-detail-page__generate-search {
|
|
margin-bottom: 16px;
|
|
padding: 12px 12px 4px;
|
|
background: #fff;
|
|
border-radius: 6px;
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
|
}
|
|
|
|
.settlement-detail-page__transfer-form {
|
|
margin-bottom: 16px;
|
|
padding: 12px 12px 4px;
|
|
background: #fff;
|
|
border-radius: 6px;
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
|
}
|
|
|
|
@media screen and (max-width: 1200px) {
|
|
.settlement-detail-page__search-grid {
|
|
grid-template-columns: repeat(2, minmax(220px, 1fr));
|
|
}
|
|
}
|
|
|
|
@media screen and (max-width: 760px) {
|
|
.settlement-detail-page__search-grid {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
}
|
|
|
|
/* 详情弹窗 tabs 白底 */
|
|
.settlement-detail-dialog {
|
|
:deep(.el-tabs__content) {
|
|
background: #fff;
|
|
padding: 16px;
|
|
border-radius: 0 0 6px 6px;
|
|
}
|
|
|
|
:deep(.el-tab-pane) {
|
|
background: transparent;
|
|
}
|
|
}
|
|
</style>
|