feat(pl): 调整加油登记列表排序、必填项和顶部汇总区。

按登记日期倒序列表并增加列统计,期初公里数改为必填,车辆统计卡片改为待开发。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
wuzilongandCursor committed 2026-11-07 17:51:57 +08:00
1 parent 574e2d9b31
commit af8d4d90fa
6 files changed
+155 -72

No files matched your search

@@ -2,16 +2,7 @@
<template>
<div class="page-container">
<section class="panel">
<FuelPlateMonthCards
v-if="SHOW_FUEL_PLATE_MONTH_CARDS && !isMineMode"
:month="summaryMonth"
:items="plateSummaryItems"
:loading="plateSummaryLoading"
:error="plateSummaryError"
:selected-plate="selectedPlateFromFilter"
@update:month="handleSummaryMonthChange"
@select="handlePlateCardSelect"
/>
<div v-if="!isMineMode" class="fuel-summary-pending">待开发</div>
<div class="list-toolbar">
<div class="list-toolbar-row">
@@ -50,9 +41,11 @@
</div>
<div class="list-body list-wrap">
<div class="table-scroll bx-list-table-wrap">
<div ref="fuelTableWrapRef" class="table-scroll bx-list-table-wrap">
<ListTable
ref="fuelTableRef"
:table-key="tableKey"
:height="isMineMode ? undefined : tableHeight"
class="list-table"
:data="tableData"
v-loading="loading"
@@ -60,6 +53,7 @@
row-key="id"
@selection-change="handleSelectionChange"
@row-dblclick="handleView"
@header-dragend="() => fuelStatsRowRef?.resync?.()"
>
<el-table-column type="selection" width="55" align="center" />
<el-table-column
@@ -319,7 +313,16 @@
</el-table-column>
</ListTable>
</div>
<div>
<div ref="fuelBelowRef">
<ListColumnStatsRow
v-if="!isMineMode"
ref="fuelStatsRowRef"
storage-key="pl-vehicle-fuel-summary-column-stats"
:columns="fuelStatsRowColumns"
:data="tableData"
:table-wrap-el="fuelTableWrapRef"
:get-cell-value="getFuelStatCellValue"
/>
<div class="pagination list-pager-bar">
<div class="list-pager-selected">
已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
@@ -503,7 +506,10 @@ import {
overflowEditItem,
overflowExportPlaceholder
} from '@/utils/list-toolbar-overflow-items'
import ListColumnStatsRow from '@/components/business/list-column-stats-row/index.vue'
import type { StatsRowColumn } from '@/utils/list-column-stats'
import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
import { useListTableViewportHeight } from '@/composables/useListTableViewportHeight'
import { useServerColumnFilters } from '@/composables/useServerColumnFilters'
import { plVehicleFuelRegisterColumnFilterDefs } from '@/views/pl/vehicle-fuel-register/columnFilterDefs'
import {
@@ -521,10 +527,8 @@ import {
vehicleFuelRegisterApi,
type PlanDocumentAttachmentItem,
type Vehicle,
type VehicleFuelPlateMonthItem,
type VehicleFuelRegister
} from '@/api/pl'
import FuelPlateMonthCards from '@/views/pl/vehicle-fuel-register/FuelPlateMonthCards.vue'
import { pickBxData, pickBxPageResult } from '@/utils/http/bx'
import { fetchAllPagedRecords } from '@/utils/fetch-paged-list'
import { fetchUserOptions } from '@/api/admin'
@@ -556,8 +560,6 @@ const props = withDefaults(
)
const isMineMode = computed(() => props.mode === 'mine')
/** 加油信息汇总 — 列表上方按车当月/当季百公里油耗卡片 */
const SHOW_FUEL_PLATE_MONTH_CARDS = true
const tableKey = computed(() =>
isMineMode.value ? 'pl-vehicle-fuel-register' : 'pl-vehicle-fuel-summary'
)
@@ -661,52 +663,49 @@ const vehicleOptions = ref<Vehicle[]>([])
const vehicleTrailerLookup = computed(() => buildVehicleTrailerLookup(vehicleOptions.value))
const quickSearchKeyword = ref('')
const plateSummaryItems = ref<VehicleFuelPlateMonthItem[]>([])
const plateSummaryLoading = ref(false)
const plateSummaryError = ref('')
function currentYearMonth() {
const now = new Date()
return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`
}
const summaryMonth = ref(currentYearMonth())
function handleSummaryMonthChange(month: string) {
summaryMonth.value = month || currentYearMonth()
fetchPlateSummary()
}
const selectedPlateFromFilter = computed(() => {
const s = filters.licensePlate as { text?: string } | undefined
return String(s?.text || '').trim()
const fuelTableWrapRef = ref<HTMLElement | null>(null)
const fuelBelowRef = ref<HTMLElement | null>(null)
const fuelTableRef = ref<any>(null)
const fuelStatsRowRef = ref<{ resync?: () => void } | null>(null)
const { tableHeight } = useListTableViewportHeight({
tableEl: fuelTableWrapRef,
pagerEl: fuelBelowRef,
tableRef: fuelTableRef,
enabled: computed(() => !isMineMode.value)
})
async function fetchPlateSummary() {
if (!SHOW_FUEL_PLATE_MONTH_CARDS || isMineMode.value) return
plateSummaryLoading.value = true
plateSummaryError.value = ''
try {
const res = await vehicleFuelRegisterApi.plateMonthSummary(summaryMonth.value)
const data = (res as { data?: { items?: VehicleFuelPlateMonthItem[] } }).data ?? res
plateSummaryItems.value = (data as { items?: VehicleFuelPlateMonthItem[] }).items || []
} catch (e: unknown) {
plateSummaryItems.value = []
plateSummaryError.value =
e && typeof e === 'object' && 'message' in e
? String((e as { message?: string }).message || '加载车辆汇总失败')
: '加载车辆汇总失败'
} finally {
plateSummaryLoading.value = false
const fuelStatsRowColumns = computed((): StatsRowColumn[] => {
const cols: StatsRowColumn[] = [
{ key: '__selection', interactive: false },
{ key: 'seqNo', interactive: false },
{ key: 'registerNo', interactive: true, prop: 'registerNo' }
]
for (const def of listColumnDefs) {
if (!isColumnVisible(def.prop)) continue
cols.push({
key: def.prop,
interactive: def.prop !== 'attachmentUrls',
prop: def.prop
})
}
}
return cols
})
function handlePlateCardSelect(plate: string | null) {
if (!plate) {
setFilterState('licensePlate', null)
return
function getFuelStatCellValue(row: VehicleFuelRegister, prop: string): unknown {
if (prop === 'fuelCategory') return getPlFuelCategoryLabel(row.fuelCategory)
if (prop === 'licensePlate') {
return formatPlVehiclePlateDisplay(row.licensePlate, undefined, vehicleTrailerLookup.value)
}
setFilterState('licensePlate', { text: plate, selected: [] })
if (prop === 'status') return getPlFuelRegisterStatusLabel(row.status)
if (prop === 'odometerKm') return row.odometerKm
if (prop === 'fuelLiters') return row.fuelLiters
if (prop === 'fuelTons') {
if (row.fuelTons != null) return Number(row.fuelTons)
if (row.fuelLiters != null) return Number(row.fuelLiters) / 1176
return null
}
if (prop === 'attachmentUrls') return ''
return (row as unknown as Record<string, unknown>)[prop]
}
const pagination = reactive({ page: 1, pageSize: 20, total: 0 })
@@ -734,6 +733,23 @@ const formRules = {
registerDate: [{ required: true, message: '请选择日期', trigger: 'change' }],
registrantId: [{ required: true, message: '请选择登记人', trigger: 'change' }],
licensePlate: [{ required: true, message: '请选择车牌号', trigger: 'change' }],
odometerKm: [
{
required: true,
trigger: 'change',
validator: (_rule: unknown, value: number | null, callback: (e?: Error) => void) => {
if (value === null || value === undefined || Number.isNaN(Number(value))) {
callback(new Error('请输入期初公里数'))
return
}
if (Number(value) < 0) {
callback(new Error('期初公里数不能为负数'))
return
}
callback()
}
}
],
fuelLiters: [{ required: true, message: '请输入加油升数', trigger: 'blur' }]
}
@@ -871,7 +887,6 @@ async function fetchData() {
function handleToolbarRefresh() {
fetchData()
fetchPlateSummary()
}
function handleSearch() {
@@ -1074,7 +1089,6 @@ async function handleSubmit() {
}
dialogVisible.value = false
await fetchData()
await fetchPlateSummary()
if (editing) await refreshViewIfOpen(formData.id)
await promptCopyAfterSave(saved, editing)
} finally {
@@ -1098,7 +1112,6 @@ async function handleCancel(row: VehicleFuelRegister) {
)
ElMessage.success('已作废')
fetchData()
fetchPlateSummary()
} finally {
cancelLoadingMap[row.id] = false
}
@@ -1136,7 +1149,6 @@ async function handleRestoreMany(rows: VehicleFuelRegister[]) {
ElMessage.warning(`成功恢复 ${successCount} 条,${failedCount} 条失败`)
}
await fetchData()
await fetchPlateSummary()
} finally {
restoreLoading.value = false
for (const row of targets) restoreLoadingMap[row.id] = false
@@ -1146,6 +1158,19 @@ async function handleRestoreMany(rows: VehicleFuelRegister[]) {
onMounted(async () => {
await Promise.all([loadVehicleOptions(), loadUserOptions()])
fetchData()
fetchPlateSummary()
})
</script>
<style scoped lang="scss">
.fuel-summary-pending {
margin: 16px 16px 0;
padding: 28px 16px;
border: 1px dashed var(--mint-border, #e2e8f0);
border-radius: 10px;
background: #fff;
color: var(--mint-text-muted, #64748b);
font-size: 14px;
line-height: 22px;
text-align: center;
}
</style>