Support filtered and selected export across freight settlement board panels. Co-authored-by: Cursor <cursoragent@cursor.com>
332 lines
9.9 KiB
Vue
332 lines
9.9 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"
|
|
/>
|
|
<ListExportDropdown
|
|
:export-fn="freightApi.export"
|
|
:get-filter-params="getSettlementExportFilterParams"
|
|
:has-selection="true"
|
|
:selected-ids="selectedRows.map((r) => r.id)"
|
|
:visible-columns="settlementExportColumns"
|
|
filename="已结算.xlsx"
|
|
/>
|
|
|
|
<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">
|
|
<div ref="settledTableWrapRef" class="table-scroll bx-list-table-wrap">
|
|
<ListTable table-key="pl-freight-components-settled-panel"
|
|
class="list-table"
|
|
:data="settlementList"
|
|
v-loading="loading"
|
|
stripe
|
|
row-key="id"
|
|
@row-dblclick="handleView"
|
|
@selection-change="handleSelectionChange"
|
|
@header-dragend="() => settledStatsRowRef?.resync?.()"
|
|
>
|
|
<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('disburseAmount')"
|
|
prop="disburseAmount"
|
|
label="发放金额(元)"
|
|
min-width="130"
|
|
align="right"
|
|
>
|
|
<template #default="{ row }">{{
|
|
row.disburseAmount ? formatAmount(row.disburseAmount) : '—'
|
|
}}</template>
|
|
</el-table-column>
|
|
<el-table-column
|
|
v-if="isColumnVisible('disburseTime')"
|
|
prop="disburseTime"
|
|
label="发放时间"
|
|
min-width="160"
|
|
align="center"
|
|
>
|
|
<template #default="{ row }">{{
|
|
row.disburseTime ? formatDateTime(row.disburseTime) : '—'
|
|
}}</template>
|
|
</el-table-column>
|
|
<el-table-column
|
|
v-if="isColumnVisible('settlementStatus')"
|
|
prop="settlementStatus"
|
|
label="结算状态"
|
|
min-width="100"
|
|
align="center"
|
|
>
|
|
<template #default="{ row }">
|
|
<el-tag :type="getSettlementStatusTagType(row.settlementStatus)" size="small">
|
|
{{ getSettlementStatusLabel(row.settlementStatus) }}
|
|
</el-tag>
|
|
</template>
|
|
</el-table-column>
|
|
</ListTable>
|
|
<ListColumnStatsRow
|
|
ref="settledStatsRowRef"
|
|
storage-key="pl-freight-settled-column-stats"
|
|
:columns="settledStatsRowColumns"
|
|
:data="settlementList"
|
|
:table-wrap-el="settledTableWrapRef"
|
|
:get-cell-value="getSettledStatCellValue"
|
|
/>
|
|
</div>
|
|
|
|
<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, 100, 500]"
|
|
layout="total, sizes, prev, pager, next"
|
|
@size-change="handleSizeChange"
|
|
@current-change="handlePageChange"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<PlFreightSettlementViewDialog ref="viewDialogRef" />
|
|
</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 ListExportDropdown from '@/components/business/list-export-dropdown/index.vue'
|
|
import ListColumnStatsRow from '@/components/business/list-column-stats-row/index.vue'
|
|
import type { StatsRowColumn } from '@/utils/list-column-stats'
|
|
import { useDbInstantSearch } from '@/composables/useDbInstantSearch'
|
|
import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
|
|
import { RefreshLeft, Search } from '@element-plus/icons-vue'
|
|
import { freightApi, type FreightSettlement } from '@/api/pl'
|
|
import PlFreightSettlementViewDialog from '@/components/business/pl-freight-settlement-view-dialog/index.vue'
|
|
import {
|
|
getSettlementStatusLabel,
|
|
getSettlementStatusTagType
|
|
} from '@/views/pl/freight/freight-status'
|
|
import { formatMoneyDisplay } from '@/utils/money-format'
|
|
import { formatDateTime } from '@/utils/format'
|
|
|
|
const listColumnDefs = [
|
|
{ prop: 'totalFreight', label: '运费合计(元)' },
|
|
{ prop: 'disburseAmount', label: '发放金额(元)' },
|
|
{ prop: 'disburseTime', label: '发放时间' },
|
|
{ prop: 'settlementStatus', label: '结算状态' }
|
|
]
|
|
|
|
const {
|
|
visibleColumns,
|
|
checkAll,
|
|
isIndeterminate,
|
|
isColumnVisible,
|
|
resetToDefault
|
|
} = usePersistedListColumns('pl-freight-settled-columns', listColumnDefs)
|
|
|
|
const settledTableWrapRef = ref<HTMLElement | null>(null)
|
|
const settledStatsRowRef = ref<{ resync?: () => void } | null>(null)
|
|
|
|
const settledStatsRowColumns = computed((): StatsRowColumn[] => {
|
|
const cols: StatsRowColumn[] = [
|
|
{ key: '__selection', interactive: false },
|
|
{ key: '__index', interactive: false },
|
|
{ key: 'settlementNo', interactive: true, prop: 'settlementNo' }
|
|
]
|
|
for (const def of listColumnDefs) {
|
|
if (!isColumnVisible(def.prop)) continue
|
|
cols.push({
|
|
key: def.prop,
|
|
interactive: true,
|
|
prop: def.prop,
|
|
money: def.prop === 'totalFreight' || def.prop === 'disburseAmount'
|
|
})
|
|
}
|
|
return cols
|
|
})
|
|
|
|
function getSettledStatCellValue(row: FreightSettlement, prop: string): unknown {
|
|
if (prop === 'disburseAmount') return row.disburseAmount || 0
|
|
if (prop === 'disburseTime') {
|
|
return row.disburseTime ? formatDateTime(row.disburseTime) : ''
|
|
}
|
|
if (prop === 'settlementStatus') return getSettlementStatusLabel(row.settlementStatus)
|
|
return (row as any)?.[prop]
|
|
}
|
|
|
|
const loading = ref(false)
|
|
const selectedRows = ref<FreightSettlement[]>([])
|
|
|
|
function getSettlementExportFilterParams() {
|
|
return {
|
|
tab: 'settled',
|
|
q: quickSearchKeyword.value.trim() || searchForm.q || undefined,
|
|
}
|
|
}
|
|
|
|
const settlementExportColumns = computed(() => {
|
|
const cols = [...visibleColumns.value]
|
|
if (!cols.includes('settlementNo')) cols.unshift('settlementNo')
|
|
return cols
|
|
})
|
|
|
|
const settlementList = ref<FreightSettlement[]>([])
|
|
const viewDialogRef = ref<InstanceType<typeof PlFreightSettlementViewDialog>>()
|
|
const quickSearchKeyword = ref('')
|
|
|
|
const searchForm = reactive({ q: '' })
|
|
|
|
const pagination = reactive({
|
|
page: 1,
|
|
pageSize: 20,
|
|
total: 0
|
|
})
|
|
|
|
function handleSelectionChange(rows: FreightSettlement[]) {
|
|
selectedRows.value = rows
|
|
}
|
|
|
|
function formatAmount(amount: number) {
|
|
if (!amount) return '¥0.00'
|
|
return formatMoneyDisplay(amount)
|
|
}
|
|
|
|
async function fetchData() {
|
|
loading.value = true
|
|
try {
|
|
const res = await freightApi.list({
|
|
tab: 'settled',
|
|
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) {
|
|
viewDialogRef.value?.open(row.id)
|
|
}
|
|
|
|
function refresh() {
|
|
fetchData()
|
|
}
|
|
|
|
useDbInstantSearch(() => ({ q: searchForm.q }), handleSearch)
|
|
|
|
onMounted(() => {
|
|
fetchData()
|
|
})
|
|
|
|
defineExpose({ refresh })
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
.bx-list-table-wrap {
|
|
min-width: 0;
|
|
}
|
|
</style>
|