fix(admin): unify fleet driver trip and operation list layouts

This commit is contained in:
2026-10-07 12:24:21 +08:00
parent ca7f6aa414
commit 04e1d7dbfa
9 changed files with 73 additions and 44 deletions
@@ -45,19 +45,24 @@ async function download() { exporting.value = true; try { saveFleetCsv(await exp
const money = (rows: TripOperationsMoney[]) => rows.length ? rows.map(r => `${r.currency} ${r.amount.toFixed(2)}`).join(' / ') : '—'
watch([page, size], load); onMounted(load); onBeforeUnmount(() => { sequence++ })
</script>
<template><AdminPage class="admin-density"><ListPageLayout>
<LoadErrorState v-if="error" title="查询或导出失败" :message="error.message" @retry="load"><template #details><ApiErrorMeta :details="error" /></template></LoadErrorState>
<QueryToolbar v-model="filters" :fields="fields" ariaLabel="运营记录查询" @search="search" @reset="search" />
<TripOperationsCharts v-if="result" :charts="result.charts" />
<div v-if="result" class="operations-summary" aria-live="polite">
<span>实际出车 {{ result.summary.trips }} 次</span><span>在途 {{ result.summary.inTransit }} 次</span><span>货物结案 {{ result.summary.completed }} 次</span>
<span>实物托盘 {{ result.summary.physicalPallets }} 托</span><span>外接约定 {{ money(result.summary.externalAgreedAmounts) }}</span>
</div>
<p v-if="result?.summary.costs.visible">已登记直接成本:{{ result.summary.costs.totals.length ? result.summary.costs.totals.map(c => `${c.currency} ${c.directCost.toFixed(2)}`).join(' / ') : '—' }};未登记 {{ result.summary.costs.unrecordedTrips }} 趟,已登记但未能分摊 {{ result.summary.costs.unallocatedTrips }} 趟。</p>
<p>按实际发车时间查询,每趟车只计一次;货源筛选展示含该类货物的整趟车。约定金额不代表应收或实收,结案不代表车辆与司机已释放。</p>
<DataTable v-model:page="page" v-model:page-size="size" :data="result?.page.records || []" :columns="columns" :total="result?.page.total || 0" :loading="loading" row-key="tripNo" empty-text="该范围暂无实际出车记录">
<template><AdminPage class="admin-density"><ListPageLayout aria-label="出车记录">
<DataTable appearance="integrated" :stripe="false" column-settings-mode="show" :results-visible="true" preference-key="fleet-operations-table" v-model:page="page" v-model:page-size="size" :data="result?.page.records || []" :columns="columns" :total="result?.page.total || 0" :loading="loading" row-key="tripNo" empty-text="该范围暂无实际出车记录">
<template #toolbar-start><h1>出车记录</h1></template>
<template #filters><QueryToolbar v-model="filters" :fields="fields" ariaLabel="运营记录查询" preference-key="fleet-operations-query" :loading="loading" @search="search" @reset="search" /></template>
<template #notices>
<div class="operations-overview">
<LoadErrorState v-if="error" title="查询或导出失败" :message="error.message" @retry="load"><template #details><ApiErrorMeta :details="error" /></template></LoadErrorState>
<TripOperationsCharts v-if="result" :charts="result.charts" />
<div v-if="result" class="operations-summary" aria-live="polite">
<span>实际出车 <strong>{{ result.summary.trips }}</strong> 次</span><span>在途 <strong>{{ result.summary.inTransit }}</strong> 次</span><span>货物结案 <strong>{{ result.summary.completed }}</strong> 次</span>
<span>实物托盘 <strong>{{ result.summary.physicalPallets }}</strong> 托</span><span>外接约定 {{ money(result.summary.externalAgreedAmounts) }}</span>
</div>
<p v-if="result?.summary.costs.visible" class="operations-note">已登记直接成本:{{ result.summary.costs.totals.length ? result.summary.costs.totals.map(c => `${c.currency} ${c.directCost.toFixed(2)}`).join(' / ') : '—' }};未登记 {{ result.summary.costs.unrecordedTrips }} 趟,已登记但未能分摊 {{ result.summary.costs.unallocatedTrips }} 趟。</p>
<p class="operations-note">按实际发车时间查询,每趟车只计一次;货源筛选展示含该类货物的整趟车。约定金额不代表应收或实收,结案不代表车辆与司机已释放。</p>
</div>
</template>
<template #toolbar-actions><el-button v-if="access.can('platform:fleet:operations:export')" :loading="exporting" @click="download">导出CSV</el-button></template>
<template #actions="{ row }"><el-button v-if="access.can('platform:fleet:trip:list')" class="density-action" @click="router.push({ name: 'overseas-master-data-trip-detail', params: { tripNo: row.tripNo } })">详情</el-button><el-button class="density-action" @click="openRecord(row.tripNo)">实测记录</el-button></template>
<template #actions="{ row }"><div class="density-actions row-actions admin-text-actions"><el-button v-if="access.can('platform:fleet:trip:list')" class="density-action" @click="router.push({ name: 'overseas-master-data-trip-detail', params: { tripNo: row.tripNo } })">详情</el-button><el-button class="density-action" @click="openRecord(row.tripNo)">实测记录</el-button></div></template>
<template #status="{ row }">{{ row.status === 'COMPLETED' ? '货物已结案' : '在途' }}</template><template #destinations="{ row }">{{ row.destinations.map((d: TripOperationsRow['destinations'][number]) => d.label).join(' / ') }}</template>
<template #distance="{ row }">{{ row.measured?.actualDistanceMiles ?? '—' }}</template><template #fuel="{ row }">{{ row.measured?.actualFuelLitres ?? '—' }}</template>
<template #agreed="{ row }">{{ money(row.externalAgreedAmounts) }}</template><template #finance="{ row }"><TripOperationsFinanceCell :trip-no="row.tripNo" /></template>
@@ -66,4 +71,8 @@ watch([page, size], load); onMounted(load); onBeforeUnmount(() => { sequence++ }
</DataTable>
<TripOperationsRecordDialog v-model="recordOpen" :row="selected" :readonly="!access.can('platform:fleet:operations:edit')" @saved="load" />
</ListPageLayout></AdminPage></template>
<style scoped>.operations-summary { display:flex; flex-wrap:wrap; gap:var(--admin-space-4); padding:var(--admin-space-3); background:var(--admin-surface); }</style>
<style scoped>
.operations-overview { display: grid; gap: var(--admin-space-3); min-width: 0; }
.operations-summary { display: flex; flex-wrap: wrap; gap: var(--admin-space-2) var(--admin-space-6); font-variant-numeric: tabular-nums; }
.operations-note { margin: 0; color: var(--cc-muted); font-size: var(--admin-font-caption); line-height: 1.6; }
</style>