Files
shanxia2.0/web-admin/src/views/pl/freight/components/DisbursePanel.vue
T
2026-08-03 12:48:52 +08:00

298 lines
8.8 KiB
Vue

<template>
<div class="freight-tab-panel">
<div class="filter-block">
<el-form :model="searchForm" class="filter-form" @submit.prevent>
<div class="filter-grid">
<el-form-item label="结算单号">
<el-input
v-model="searchForm.q"
placeholder="搜索结算单号"
clearable
@keyup.enter="handleSearch"
/>
</el-form-item>
</div>
<div class="filter-actions">
<div class="filter-actions-end">
<el-button class="btn-ghost" :loading="loading" @click="handleReset">
<el-icon><RefreshLeft /></el-icon>重置
</el-button>
<el-button class="btn-main" type="primary" :loading="loading" @click="handleSearch">
<el-icon><Search /></el-icon>搜索
</el-button>
</div>
</div>
</el-form>
</div>
<div class="list-toolbar">
<div class="list-toolbar-row">
<ListQuickSearchInput
v-model="quickSearchKeyword"
placeholder="结算单号"
@search="handleQuickSearch"
@clear="handleQuickSearchClear"
/>
<el-button
class="btn-main"
type="primary"
:disabled="selectedRows.length !== 1"
@click="handleToolbarDisburse"
>
<el-icon><Money /></el-icon>发放登记
</el-button>
<ListToolbarEndActions
:loading="loading"
:column-defs="listColumnDefs"
v-model:visible-columns="visibleColumns"
v-model:check-all="checkAll"
:is-indeterminate="isIndeterminate"
@refresh="fetchData"
@reset-columns="resetToDefault"
/>
</div>
</div>
<div class="list-body list-wrap">
<ListTable table-key="pl-freight-components-disburse-panel"
class="list-table"
:data="settlementList"
v-loading="loading"
stripe
row-key="id"
@selection-change="handleSelectionChange"
@row-dblclick="handleView"
>
<el-table-column type="selection" min-width="55" align="center" header-align="center" />
<el-table-column
type="index"
label="序号"
min-width="68"
align="center"
header-align="center"
:index="(index) => (pagination.page - 1) * pagination.pageSize + index + 1"
/>
<el-table-column prop="settlementNo" label="结算单号" min-width="180" show-overflow-tooltip>
<template #default="{ row }">
<el-link type="primary" @click="handleView(row)">{{ row.settlementNo }}</el-link>
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('totalFreight')"
prop="totalFreight"
label="运费合计(元)"
min-width="130"
align="right"
>
<template #default="{ row }">{{ formatAmount(row.totalFreight) }}</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('planCount')"
prop="planCount"
label="关联计划数"
min-width="110"
align="center"
/>
<el-table-column
v-if="isColumnVisible('licensePlateSummary')"
prop="licensePlateSummary"
label="车牌摘要"
min-width="140"
show-overflow-tooltip
/>
<el-table-column
v-if="isColumnVisible('approvalFinishedTime')"
prop="approvalFinishedTime"
label="审批完成时间"
min-width="160"
align="center"
>
<template #default="{ row }">{{
row.approvalFinishedTime ? formatDateTime(row.approvalFinishedTime) : '—'
}}</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('submitterName')"
prop="submitterName"
label="提交人"
min-width="100"
align="center"
>
<template #default="{ row }">{{ row.submitterName || '—' }}</template>
</el-table-column>
</ListTable>
<div class="pagination list-pager-bar">
<div class="list-pager-selected">
已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
</div>
<el-pagination
v-model:current-page="pagination.page"
v-model:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next"
@size-change="handleSizeChange"
@current-change="handlePageChange"
/>
</div>
</div>
<PlFreightSettlementViewDialog ref="viewDialogRef">
<template v-if="viewingRow" #hero-actions>
<el-button class="btn-main" type="primary" @click="handleDisburse(viewingRow)">
<el-icon><Money /></el-icon>发放登记
</el-button>
</template>
</PlFreightSettlementViewDialog>
<PlFreightDisburseDialog ref="disburseDialogRef" @success="handleDisburseSuccess" />
</div>
</template>
<script setup lang="ts">
import ListQuickSearchInput from '@/components/business/list-quick-search-input/index.vue'
import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'
import { useDbInstantSearch } from '@/composables/useDbInstantSearch'
import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
import { Money, RefreshLeft, Search } from '@element-plus/icons-vue'
import { freightApi, type FreightSettlement } from '@/api/pl'
import PlFreightDisburseDialog from '@/components/business/pl-freight-disburse-dialog/index.vue'
import PlFreightSettlementViewDialog from '@/components/business/pl-freight-settlement-view-dialog/index.vue'
import { formatMoneyDisplay } from '@/utils/money-format'
import { formatDateTime } from '@/utils/format'
const listColumnDefs = [
{ prop: 'totalFreight', label: '运费合计(元)' },
{ prop: 'planCount', label: '关联计划数' },
{ prop: 'licensePlateSummary', label: '车牌摘要' },
{ prop: 'approvalFinishedTime', label: '审批完成时间' },
{ prop: 'submitterName', label: '提交人' }
]
const {
visibleColumns,
checkAll,
isIndeterminate,
isColumnVisible,
resetToDefault
} = usePersistedListColumns('pl-freight-disburse-columns', listColumnDefs)
const emit = defineEmits<{ disbursed: [] }>()
const loading = ref(false)
const settlementList = ref<FreightSettlement[]>([])
const selectedRows = ref<FreightSettlement[]>([])
const viewingRow = ref<FreightSettlement | null>(null)
const viewDialogRef = ref<InstanceType<typeof PlFreightSettlementViewDialog>>()
const disburseDialogRef = ref<InstanceType<typeof PlFreightDisburseDialog>>()
const quickSearchKeyword = ref('')
const searchForm = reactive({ q: '' })
const pagination = reactive({
page: 1,
pageSize: 20,
total: 0
})
function formatAmount(amount: number) {
if (!amount) return '¥0.00'
return formatMoneyDisplay(amount)
}
function handleSelectionChange(selection: FreightSettlement[]) {
selectedRows.value = selection
}
async function fetchData() {
loading.value = true
try {
const res = await freightApi.list({
tab: 'disburse',
q: quickSearchKeyword.value.trim() || searchForm.q || undefined,
page: pagination.page,
pageSize: pagination.pageSize
})
const data = res.data ?? res
settlementList.value = data.list || []
pagination.total = data.total || 0
} finally {
loading.value = false
}
}
function handleSearch() {
quickSearchKeyword.value = ''
pagination.page = 1
fetchData()
}
function handleReset() {
searchForm.q = ''
quickSearchKeyword.value = ''
pagination.page = 1
fetchData()
}
function handleQuickSearch() {
pagination.page = 1
fetchData()
}
function handleQuickSearchClear() {
quickSearchKeyword.value = ''
pagination.page = 1
fetchData()
}
function handlePageChange() {
fetchData()
}
function handleSizeChange() {
pagination.page = 1
fetchData()
}
function handleView(row: FreightSettlement) {
viewingRow.value = row
viewDialogRef.value?.open(row.id)
}
function handleDisburse(row: FreightSettlement) {
disburseDialogRef.value?.open(row.id)
}
function handleToolbarDisburse() {
if (selectedRows.value.length !== 1) return
handleDisburse(selectedRows.value[0])
}
function syncViewingRowFromList() {
if (!viewingRow.value?.id) return
const updated = settlementList.value.find(row => row.id === viewingRow.value!.id)
if (updated) viewingRow.value = updated
}
async function handleDisburseSuccess() {
await fetchData()
syncViewingRowFromList()
await viewDialogRef.value?.reload()
emit('disbursed')
}
function refresh() {
fetchData()
}
useDbInstantSearch(() => ({ q: searchForm.q }), handleSearch)
onMounted(() => {
fetchData()
})
defineExpose({ refresh })
</script>