소스 검색

feat(web-admin): viewport-fill list height for pl transport menus

Wire 运输管理 list pages (plans, IO orders, freight panels, vehicles,
personnel, and related lists) to useListTableViewportHeight so table body
scrolls with sticky header and pager stays near the content bottom.

Co-authored-by: Cursor <cursoragent@cursor.com>
zlwu 2 달 전
부모
커밋
c577edc597
35개의 변경된 파일과 1334개의 추가작업 그리고 788개의 파일을 삭제
  1. 14 0
      web-admin/graphify-out/.graphify_labels.json
  2. 11 2
      web-admin/graphify-out/2026-09-01/.graphify_labels.json
  3. 263 198
      web-admin/graphify-out/2026-09-01/GRAPH_REPORT.md
  4. 112 112
      web-admin/graphify-out/2026-09-01/graph.json
  5. 260 227
      web-admin/graphify-out/GRAPH_REPORT.md
  6. 112 112
      web-admin/graphify-out/graph.json
  7. 22 5
      web-admin/src/views/pl/_shared/IoOrderListPage.vue
  8. 21 2
      web-admin/src/views/pl/carrier/index.vue
  9. 21 2
      web-admin/src/views/pl/driver/index.vue
  10. 2 2
      web-admin/src/views/pl/freight-approval/index.vue
  11. 2 2
      web-admin/src/views/pl/freight-disburse/index.vue
  12. 19 5
      web-admin/src/views/pl/freight/components/ApprovalDonePanel.vue
  13. 19 5
      web-admin/src/views/pl/freight/components/ApprovalPendingPanel.vue
  14. 19 5
      web-admin/src/views/pl/freight/components/DisbursePanel.vue
  15. 19 5
      web-admin/src/views/pl/freight/components/SettledPanel.vue
  16. 19 5
      web-admin/src/views/pl/freight/components/SettlementBillPanel.vue
  17. 19 5
      web-admin/src/views/pl/freight/components/UnsettledPlanPanel.vue
  18. 2 2
      web-admin/src/views/pl/freight/index.vue
  19. 30 13
      web-admin/src/views/pl/inventory/document-reminder.vue
  20. 21 2
      web-admin/src/views/pl/inventory/ledger.vue
  21. 21 2
      web-admin/src/views/pl/personnel/index.vue
  22. 16 3
      web-admin/src/views/pl/plan-archive/index.vue
  23. 40 24
      web-admin/src/views/pl/plan-cert/index.vue
  24. 37 21
      web-admin/src/views/pl/plan-contract/index.vue
  25. 19 3
      web-admin/src/views/pl/plan/index.vue
  26. 21 2
      web-admin/src/views/pl/self-vehicle/index.vue
  27. 21 2
      web-admin/src/views/pl/transport-address/index.vue
  28. 21 2
      web-admin/src/views/pl/transport/index.vue
  29. 17 3
      web-admin/src/views/pl/vehicle-maintenance-apply/index.vue
  30. 17 3
      web-admin/src/views/pl/vehicle-maintenance-process/index.vue
  31. 21 2
      web-admin/src/views/pl/vehicle-recharge-apply/index.vue
  32. 21 2
      web-admin/src/views/pl/vehicle-recharge-record/index.vue
  33. 17 3
      web-admin/src/views/pl/vehicle-urea-apply/index.vue
  34. 17 3
      web-admin/src/views/pl/vehicle-urea-disburse/index.vue
  35. 21 2
      web-admin/src/views/pl/vehicle/index.vue

+ 14 - 0
web-admin/graphify-out/.graphify_labels.json

@@ -271,24 +271,35 @@
   "269": "Community 269",
   "270": "Community 270",
   "271": "SettingDrawer.vue",
+  "272": "customerPool.ts",
+  "273": "handleConfirmApproval",
+  "274": "pl-plan-stop-product-arrange-required.ts",
   "275": "index.js",
   "276": "tabs.ts",
   "277": "handleKeyDown",
   "278": "index.vue",
+  "279": "handleDisburse",
   "280": "Community 280",
   "281": "index.js",
+  "282": "product.ts",
   "283": "Community 283",
   "284": "Community 284",
   "285": "Community 285",
   "286": "Community 286",
+  "287": "view-fields.ts",
+  "288": "syncStatsColumnWidths",
   "289": "handleEdit",
   "290": "index.js",
+  "291": "crm-owner-candidates.ts",
   "292": "Community 292",
   "293": "index.vue",
   "294": "menuFavorite.ts",
   "295": "Community 295",
   "296": "Community 296",
+  "297": "index.vue",
+  "298": "index.vue",
   "299": "zh_CN.js",
+  "300": "index.vue",
   "301": "ProcessStatusTag.vue",
   "302": "Community 302",
   "303": "Community 303",
@@ -300,6 +311,8 @@
   "309": "Community 309",
   "310": "Community 310",
   "311": "handleExportPdf",
+  "312": "loadUserOptions",
+  "313": "loadUserOptions",
   "314": "assignmentMatchesFilter",
   "315": "Community 315",
   "316": "Community 316",
@@ -329,6 +342,7 @@
   "340": "Community 340",
   "341": "Community 341",
   "342": "error-handle.ts",
+  "343": "v2.4.2.1 - 2025-06-16",
   "344": "ArtException.vue",
   "346": "Community 346",
   "347": "Community 347",

+ 11 - 2
web-admin/graphify-out/2026-09-01/.graphify_labels.json

@@ -193,7 +193,9 @@
   "191": "formatYmd",
   "192": "prepareLineAliasAndBrandForSubmit",
   "193": "crm-product-display.ts",
+  "194": "index.vue",
   "195": "Community 195",
+  "196": "ClaimDialog.vue",
   "197": "v2.4.2.1 - 2025-06-16",
   "198": "fetchRoleOptions",
   "199": "colors.ts",
@@ -204,9 +206,11 @@
   "204": "DbCrmCustomerSelect.vue",
   "205": "loadDefaultCustomerOptions",
   "206": "onDrop",
+  "207": "SettingActions.vue",
   "208": "index.vue",
   "209": "Community 209",
   "210": "Community 210",
+  "211": "navigation.ts",
   "212": "index.js",
   "213": "Community 213",
   "214": "Community 214",
@@ -215,6 +219,7 @@
   "217": "index.js",
   "218": "price-letter.ts",
   "219": "applyBrandSelectChange",
+  "220": "index.vue",
   "221": "index.js",
   "222": "index.vue",
   "223": "Community 223",
@@ -226,8 +231,10 @@
   "229": "useClientColumnFilters.ts",
   "230": "customerPool.ts",
   "231": "handlePublish",
+  "232": "index.vue",
   "233": "Community 233",
   "234": "Community 234",
+  "235": "index.vue",
   "236": "handleEdit",
   "237": "handleDisburse",
   "238": "ViewKvTableCell.vue",
@@ -236,11 +243,13 @@
   "241": "handleExportPdf",
   "242": "Community 242",
   "243": "Community 243",
+  "244": "SettingItem.vue",
   "245": "index.js",
   "246": "fetchData",
   "247": "Community 247",
   "248": "Community 248",
   "249": "SettingItem.vue",
+  "250": "v2.5.7 - 2025-09-14",
   "251": "handleDisburse",
   "252": "index.js",
   "253": "Community 253",
@@ -253,10 +262,12 @@
   "260": "Community 260",
   "261": "Community 261",
   "262": "index.js",
+  "263": "handleSubmit",
   "264": "syncStatsColumnWidths",
   "265": "Community 265",
   "266": "crypto.ts",
   "267": "fetchData",
+  "268": "zh_CN.js",
   "269": "Community 269",
   "270": "Community 270",
   "271": "SettingDrawer.vue",
@@ -270,7 +281,6 @@
   "284": "Community 284",
   "285": "Community 285",
   "286": "Community 286",
-  "288": "loadUserOptions",
   "289": "handleEdit",
   "290": "index.js",
   "292": "Community 292",
@@ -279,7 +289,6 @@
   "295": "Community 295",
   "296": "Community 296",
   "299": "zh_CN.js",
-  "300": "form.vue",
   "301": "ProcessStatusTag.vue",
   "302": "Community 302",
   "303": "Community 303",

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 263 - 198
web-admin/graphify-out/2026-09-01/GRAPH_REPORT.md


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 112 - 112
web-admin/graphify-out/2026-09-01/graph.json


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 260 - 227
web-admin/graphify-out/GRAPH_REPORT.md


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 112 - 112
web-admin/graphify-out/graph.json


+ 22 - 5
web-admin/src/views/pl/_shared/IoOrderListPage.vue

@@ -78,7 +78,9 @@
 
       <div class="list-body list-wrap">
         <div ref="ioOrderTableWrapRef" class="table-scroll bx-list-table-wrap">
-          <ListTable :table-key="`pl-${kind}-io-order-list`"
+          <ListTable
+            ref="ioOrderTableRef" :table-key="`pl-${kind}-io-order-list`"
+            :height="tableHeight"
             class="list-table"
             :data="rows"
             v-loading="loading"
@@ -212,6 +214,8 @@
           <el-table-column prop="actualQtyTotal" label="实际数量合计" min-width="120" align="right" />
           <el-table-column prop="siteFeedbackTime" label="最近提交" min-width="160" show-overflow-tooltip />
         </ListTable>
+          </div>
+        <div ref="ioOrderBelowRef">
           <ListColumnStatsRow
             ref="ioOrderStatsRowRef"
             :storage-key="`pl-${kind}-column-stats`"
@@ -220,9 +224,8 @@
             :table-wrap-el="ioOrderTableWrapRef"
             :get-cell-value="getIoOrderStatCellValue"
           />
-        </div>
 
-        <div class="pagination list-pager-bar">
+          <div class="pagination list-pager-bar">
           <div class="list-pager-selected">
             已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
           </div>
@@ -237,6 +240,7 @@
             @size-change="fetchData"
           />
         </div>
+        </div>
       </div>
     </section>
 
@@ -427,7 +431,7 @@
 </template>
 
 <script setup lang="ts">
-  import { computed, onMounted, reactive, ref } from 'vue'
+  import { computed, onMounted, reactive, ref, nextTick } from 'vue'
   import { Check, Close, Edit, Link, RefreshLeft, Search } from '@element-plus/icons-vue'
   import AttachmentUploadField from '@/components/business/attachment-upload-field/index.vue'
   import BrandQtyChipsField, {
@@ -437,6 +441,7 @@
   import ListExportDropdown from '@/components/business/list-export-dropdown/index.vue'
   import ListToolbarDateShortcuts from '@/components/core/list-toolbar-date-shortcuts/index.vue'
   import ListColumnStatsRow from '@/components/business/list-column-stats-row/index.vue'
+import { useListTableViewportHeight } from '@/composables/useListTableViewportHeight'
   import type { StatsRowColumn } from '@/utils/list-column-stats'
   import ViewKvTable from '@/components/business/system-view-dialog/ViewKvTable.vue'
   import type { ViewField } from '@/components/business/system-view-dialog/types'
@@ -464,6 +469,15 @@
   const api = props.kind === 'inbound' ? inboundOrderApi : outboundOrderApi
 
   const ioOrderTableWrapRef = ref<HTMLElement | null>(null)
+  const ioOrderBelowRef = ref<HTMLElement | null>(null)
+  const ioOrderTableRef = ref<any>(null)
+  const { tableHeight, recalc: recalcTableHeight } = useListTableViewportHeight({
+tableEl: ioOrderTableWrapRef,
+    pagerEl: ioOrderBelowRef,
+    tableRef: ioOrderTableRef
+
+
+  })
   const ioOrderStatsRowRef = ref<{ resync?: () => void } | null>(null)
 
   const ioOrderStatsRowColumns = computed((): StatsRowColumn[] => [
@@ -854,7 +868,10 @@
     ElMessage.success('已复制现场链接')
   }
 
-  onMounted(fetchData)
+  onMounted(() => {
+  fetchData()
+  nextTick(() => recalcTableHeight())
+})
 </script>
 
 <style lang="scss">

+ 21 - 2
web-admin/src/views/pl/carrier/index.vue

@@ -85,7 +85,10 @@
       </div>
 
       <div class="list-body list-wrap">
-        <ListTable table-key="pl-carrier"
+        <div ref="carrierTableWrapRef" class="table-scroll bx-list-table-wrap">
+          <ListTable
+            ref="carrierTableRef"
+            :height="tableHeight" table-key="pl-carrier"
           class="list-table"
           :data="carrierList"
           v-loading="loading"
@@ -189,7 +192,10 @@
           />
         </ListTable>
 
-        <div class="pagination list-pager-bar">
+        
+        </div>
+        <div ref="carrierBelowRef">
+          <div class="pagination list-pager-bar">
           <div class="list-pager-selected">
             已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
           </div>
@@ -204,6 +210,7 @@
             @current-change="handlePageChange"
           />
         </div>
+        </div>
       </div>
     </section>
 
@@ -332,6 +339,7 @@ import ListToolbarEndActions from '@/components/business/list-toolbar-end-action
 import ListExportDropdown from '@/components/business/list-export-dropdown/index.vue'
 import ListQuickSearchInput from '@/components/business/list-quick-search-input/index.vue'
 import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
+import { useListTableViewportHeight } from '@/composables/useListTableViewportHeight'
 import { useDbInstantSearch } from '@/composables/useDbInstantSearch'
 import { useListMediaActive } from '@/composables/useListMediaActive'
 import { pickBxPageResult } from '@/utils/http/bx'
@@ -359,6 +367,15 @@ const {
   resetToDefault
 } = usePersistedListColumns('pl-carrier-list-columns-v2', listColumnDefs)
 
+const carrierTableWrapRef = ref<HTMLElement | null>(null)
+const carrierBelowRef = ref<HTMLElement | null>(null)
+const carrierTableRef = ref<any>(null)
+const { tableHeight, recalc: recalcTableHeight } = useListTableViewportHeight({
+tableEl: carrierTableWrapRef,
+  pagerEl: carrierBelowRef,
+  tableRef: carrierTableRef
+})
+
 const submitLoading = ref(false)
 const carrierList = ref<Carrier[]>([])
 const selectedRows = ref<Carrier[]>([])
@@ -619,5 +636,7 @@ useDbInstantSearch(
 
 onMounted(() => {
   fetchData()
+
+  nextTick(() => recalcTableHeight())
 })
 </script>

+ 21 - 2
web-admin/src/views/pl/driver/index.vue

@@ -80,7 +80,10 @@
 
       <!-- 数据表格 -->
       <div class="list-body list-wrap">
-        <ListTable table-key="pl-driver"
+        <div ref="driverTableWrapRef" class="table-scroll bx-list-table-wrap">
+          <ListTable
+            ref="driverTableRef"
+            :height="tableHeight" table-key="pl-driver"
           class="list-table"
           :data="driverList"
           v-loading="loading"
@@ -163,7 +166,10 @@
           />
         </ListTable>
 
-        <div class="pagination list-pager-bar">
+        
+        </div>
+        <div ref="driverBelowRef">
+          <div class="pagination list-pager-bar">
           <div class="list-pager-selected">
             已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
           </div>
@@ -178,6 +184,7 @@
             @current-change="handlePageChange"
           />
         </div>
+        </div>
       </div>
     </section>
 
@@ -296,6 +303,7 @@ import ListQuickSearchInput from '@/components/business/list-quick-search-input/
 import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'
 import ListExportDropdown from '@/components/business/list-export-dropdown/index.vue'
 import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
+import { useListTableViewportHeight } from '@/composables/useListTableViewportHeight'
 import { useDbInstantSearch } from '@/composables/useDbInstantSearch'
 import { pickBxPageResult } from '@/utils/http/bx'
 import { DEFAULT_LIST_PAGE_SIZE } from '@/utils/table/tableConfig'
@@ -330,6 +338,15 @@ const {
   ]
 })
 
+const driverTableWrapRef = ref<HTMLElement | null>(null)
+const driverBelowRef = ref<HTMLElement | null>(null)
+const driverTableRef = ref<any>(null)
+const { tableHeight, recalc: recalcTableHeight } = useListTableViewportHeight({
+tableEl: driverTableWrapRef,
+  pagerEl: driverBelowRef,
+  tableRef: driverTableRef
+})
+
 const selectedRows = ref<Driver[]>([])
 
 function getDriverExportFilterParams() {
@@ -614,6 +631,8 @@ useDbInstantSearch(() => ({ q: searchForm.q }), handleSearch)
 
 onMounted(() => {
   fetchData()
+
+  nextTick(() => recalcTableHeight())
 })
 </script>
 

+ 2 - 2
web-admin/src/views/pl/freight-approval/index.vue

@@ -7,10 +7,10 @@
         class="panel-tabs"
         @tab-change="handleTabChange"
       >
-        <el-tab-pane label="待审批" name="pending">
+        <el-tab-pane label="待审批" name="pending" lazy>
           <ApprovalPendingPanel ref="pendingRef" />
         </el-tab-pane>
-        <el-tab-pane label="已审批" name="done">
+        <el-tab-pane label="已审批" name="done" lazy>
           <ApprovalDonePanel ref="doneRef" />
         </el-tab-pane>
       </el-tabs>

+ 2 - 2
web-admin/src/views/pl/freight-disburse/index.vue

@@ -7,10 +7,10 @@
         class="panel-tabs"
         @tab-change="handleTabChange"
       >
-        <el-tab-pane label="待发放" name="disburse">
+        <el-tab-pane label="待发放" name="disburse" lazy>
           <DisbursePanel ref="disburseRef" @disbursed="handleDisbursed" />
         </el-tab-pane>
-        <el-tab-pane label="已发放" name="settled">
+        <el-tab-pane label="已发放" name="settled" lazy>
           <SettledPanel ref="settledRef" />
         </el-tab-pane>
       </el-tabs>

+ 19 - 5
web-admin/src/views/pl/freight/components/ApprovalDonePanel.vue

@@ -63,8 +63,10 @@
 
     <div class="list-body list-wrap">
       <div ref="doneTableWrapRef" class="table-scroll bx-list-table-wrap">
-        <ListTable table-key="pl-freight-components-approval-done-panel"
-          class="list-table"
+        <ListTable
+            ref="doneTableRef" table-key="pl-freight-components-approval-done-panel"
+          :height="tableHeight"
+            class="list-table"
           :data="settlementList"
           v-loading="loading"
           stripe
@@ -144,7 +146,9 @@
           <template #default="{ row }">{{ row.submitterName || '—' }}</template>
         </el-table-column>
       </ListTable>
-        <ListColumnStatsRow
+        </div>
+        <div ref="doneBelowRef">
+          <ListColumnStatsRow
           ref="doneStatsRowRef"
           storage-key="pl-freight-approval-done-column-stats"
           :columns="doneStatsRowColumns"
@@ -152,9 +156,8 @@
           :table-wrap-el="doneTableWrapRef"
           :get-cell-value="getDoneStatCellValue"
         />
-      </div>
 
-      <div class="pagination list-pager-bar">
+          <div class="pagination list-pager-bar">
           <div class="list-pager-selected">
             已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
           </div>
@@ -169,6 +172,7 @@
           @current-change="handlePageChange"
         />
       </div>
+        </div>
     </div>
 
     <PlFreightSettlementViewDialog ref="viewDialogRef" />
@@ -183,6 +187,7 @@ import ListColumnStatsRow from '@/components/business/list-column-stats-row/inde
 import type { StatsRowColumn } from '@/utils/list-column-stats'
 import { useDbInstantSearch } from '@/composables/useDbInstantSearch'
 import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
+import { useListTableViewportHeight } from '@/composables/useListTableViewportHeight'
 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'
@@ -211,6 +216,13 @@ const {
 } = usePersistedListColumns('pl-freight-approval-done-columns', listColumnDefs)
 
 const doneTableWrapRef = ref<HTMLElement | null>(null)
+const doneBelowRef = ref<HTMLElement | null>(null)
+const doneTableRef = ref<any>(null)
+const { tableHeight, recalc: recalcTableHeight } = useListTableViewportHeight({
+tableEl: doneTableWrapRef,
+  pagerEl: doneBelowRef,
+  tableRef: doneTableRef
+})
 const doneStatsRowRef = ref<{ resync?: () => void } | null>(null)
 
 const doneStatsRowColumns = computed((): StatsRowColumn[] => {
@@ -349,6 +361,8 @@ useDbInstantSearch(
 
 onMounted(() => {
   fetchData()
+
+  nextTick(() => recalcTableHeight())
 })
 
 defineExpose({ refresh })

+ 19 - 5
web-admin/src/views/pl/freight/components/ApprovalPendingPanel.vue

@@ -81,8 +81,10 @@
 
     <div class="list-body list-wrap">
       <div ref="pendingTableWrapRef" class="table-scroll bx-list-table-wrap">
-        <ListTable table-key="pl-freight-components-approval-pending-panel"
-          class="list-table"
+        <ListTable
+            ref="pendingTableRef" table-key="pl-freight-components-approval-pending-panel"
+          :height="tableHeight"
+            class="list-table"
           :data="settlementList"
           v-loading="loading"
           stripe
@@ -156,7 +158,9 @@
           <template #default="{ row }">{{ formatDateTime(row.createTime) }}</template>
         </el-table-column>
       </ListTable>
-        <ListColumnStatsRow
+        </div>
+        <div ref="pendingBelowRef">
+          <ListColumnStatsRow
           ref="pendingStatsRowRef"
           storage-key="pl-freight-approval-pending-column-stats"
           :columns="pendingStatsRowColumns"
@@ -164,9 +168,8 @@
           :table-wrap-el="pendingTableWrapRef"
           :get-cell-value="getPendingStatCellValue"
         />
-      </div>
 
-      <div class="pagination list-pager-bar">
+          <div class="pagination list-pager-bar">
           <div class="list-pager-selected">
             已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
           </div>
@@ -181,6 +184,7 @@
           @current-change="handlePageChange"
         />
       </div>
+        </div>
     </div>
 
     <PlFreightSettlementViewDialog ref="viewDialogRef">
@@ -215,6 +219,7 @@ import ListColumnStatsRow from '@/components/business/list-column-stats-row/inde
 import type { StatsRowColumn } from '@/utils/list-column-stats'
 import { useDbInstantSearch } from '@/composables/useDbInstantSearch'
 import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
+import { useListTableViewportHeight } from '@/composables/useListTableViewportHeight'
 import { CircleCheck, CircleClose, RefreshLeft, Search } from '@element-plus/icons-vue'
 import { freightApi, type FreightSettlement } from '@/api/pl'
 import PlFreightApprovalDialog from '@/components/business/pl-freight-approval-dialog/index.vue'
@@ -240,6 +245,13 @@ const {
 } = usePersistedListColumns('pl-freight-approval-pending-columns', listColumnDefs)
 
 const pendingTableWrapRef = ref<HTMLElement | null>(null)
+const pendingBelowRef = ref<HTMLElement | null>(null)
+const pendingTableRef = ref<any>(null)
+const { tableHeight, recalc: recalcTableHeight } = useListTableViewportHeight({
+tableEl: pendingTableWrapRef,
+  pagerEl: pendingBelowRef,
+  tableRef: pendingTableRef
+})
 const pendingStatsRowRef = ref<{ resync?: () => void } | null>(null)
 
 const pendingStatsRowColumns = computed((): StatsRowColumn[] => {
@@ -403,6 +415,8 @@ useDbInstantSearch(() => ({ q: searchForm.q }), handleSearch)
 
 onMounted(() => {
   fetchData()
+
+  nextTick(() => recalcTableHeight())
 })
 
 defineExpose({ refresh })

+ 19 - 5
web-admin/src/views/pl/freight/components/DisbursePanel.vue

@@ -65,8 +65,10 @@
 
     <div class="list-body list-wrap">
       <div ref="disburseTableWrapRef" class="table-scroll bx-list-table-wrap">
-        <ListTable table-key="pl-freight-components-disburse-panel"
-          class="list-table"
+        <ListTable
+            ref="disburseTableRef" table-key="pl-freight-components-disburse-panel"
+          :height="tableHeight"
+            class="list-table"
           :data="settlementList"
           v-loading="loading"
           stripe
@@ -133,7 +135,9 @@
           <template #default="{ row }">{{ row.submitterName || '—' }}</template>
         </el-table-column>
       </ListTable>
-        <ListColumnStatsRow
+        </div>
+        <div ref="disburseBelowRef">
+          <ListColumnStatsRow
           ref="disburseStatsRowRef"
           storage-key="pl-freight-disburse-column-stats"
           :columns="disburseStatsRowColumns"
@@ -141,9 +145,8 @@
           :table-wrap-el="disburseTableWrapRef"
           :get-cell-value="getDisburseStatCellValue"
         />
-      </div>
 
-      <div class="pagination list-pager-bar">
+          <div class="pagination list-pager-bar">
           <div class="list-pager-selected">
             已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
           </div>
@@ -158,6 +161,7 @@
           @current-change="handlePageChange"
         />
       </div>
+        </div>
     </div>
 
     <PlFreightSettlementViewDialog ref="viewDialogRef">
@@ -179,6 +183,7 @@ import ListColumnStatsRow from '@/components/business/list-column-stats-row/inde
 import type { StatsRowColumn } from '@/utils/list-column-stats'
 import { useDbInstantSearch } from '@/composables/useDbInstantSearch'
 import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
+import { useListTableViewportHeight } from '@/composables/useListTableViewportHeight'
 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'
@@ -205,6 +210,13 @@ const {
 const emit = defineEmits<{ disbursed: [] }>()
 
 const disburseTableWrapRef = ref<HTMLElement | null>(null)
+const disburseBelowRef = ref<HTMLElement | null>(null)
+const disburseTableRef = ref<any>(null)
+const { tableHeight, recalc: recalcTableHeight } = useListTableViewportHeight({
+tableEl: disburseTableWrapRef,
+  pagerEl: disburseBelowRef,
+  tableRef: disburseTableRef
+})
 const disburseStatsRowRef = ref<{ resync?: () => void } | null>(null)
 
 const disburseStatsRowColumns = computed((): StatsRowColumn[] => {
@@ -357,6 +369,8 @@ useDbInstantSearch(() => ({ q: searchForm.q }), handleSearch)
 
 onMounted(() => {
   fetchData()
+
+  nextTick(() => recalcTableHeight())
 })
 
 defineExpose({ refresh })

+ 19 - 5
web-admin/src/views/pl/freight/components/SettledPanel.vue

@@ -57,8 +57,10 @@
 
     <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"
+        <ListTable
+            ref="settledTableRef" table-key="pl-freight-components-settled-panel"
+          :height="tableHeight"
+            class="list-table"
           :data="settlementList"
           v-loading="loading"
           stripe
@@ -126,7 +128,9 @@
           </template>
         </el-table-column>
       </ListTable>
-        <ListColumnStatsRow
+        </div>
+        <div ref="settledBelowRef">
+          <ListColumnStatsRow
           ref="settledStatsRowRef"
           storage-key="pl-freight-settled-column-stats"
           :columns="settledStatsRowColumns"
@@ -134,9 +138,8 @@
           :table-wrap-el="settledTableWrapRef"
           :get-cell-value="getSettledStatCellValue"
         />
-      </div>
 
-      <div class="pagination list-pager-bar">
+          <div class="pagination list-pager-bar">
           <div class="list-pager-selected">
             已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
           </div>
@@ -151,6 +154,7 @@
           @current-change="handlePageChange"
         />
       </div>
+        </div>
     </div>
 
     <PlFreightSettlementViewDialog ref="viewDialogRef" />
@@ -165,6 +169,7 @@ import ListColumnStatsRow from '@/components/business/list-column-stats-row/inde
 import type { StatsRowColumn } from '@/utils/list-column-stats'
 import { useDbInstantSearch } from '@/composables/useDbInstantSearch'
 import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
+import { useListTableViewportHeight } from '@/composables/useListTableViewportHeight'
 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'
@@ -191,6 +196,13 @@ const {
 } = usePersistedListColumns('pl-freight-settled-columns', listColumnDefs)
 
 const settledTableWrapRef = ref<HTMLElement | null>(null)
+const settledBelowRef = ref<HTMLElement | null>(null)
+const settledTableRef = ref<any>(null)
+const { tableHeight, recalc: recalcTableHeight } = useListTableViewportHeight({
+tableEl: settledTableWrapRef,
+  pagerEl: settledBelowRef,
+  tableRef: settledTableRef
+})
 const settledStatsRowRef = ref<{ resync?: () => void } | null>(null)
 
 const settledStatsRowColumns = computed((): StatsRowColumn[] => {
@@ -319,6 +331,8 @@ useDbInstantSearch(() => ({ q: searchForm.q }), handleSearch)
 
 onMounted(() => {
   fetchData()
+
+  nextTick(() => recalcTableHeight())
 })
 
 defineExpose({ refresh })

+ 19 - 5
web-admin/src/views/pl/freight/components/SettlementBillPanel.vue

@@ -136,8 +136,10 @@
 
     <div class="list-body list-wrap">
       <div ref="billsTableWrapRef" class="table-scroll bx-list-table-wrap">
-        <ListTable table-key="pl-freight-components-settlement-bill-panel"
-          class="list-table"
+        <ListTable
+            ref="billsTableRef" table-key="pl-freight-components-settlement-bill-panel"
+          :height="tableHeight"
+            class="list-table"
           :data="settlementList"
           v-loading="loading"
           stripe
@@ -246,7 +248,9 @@
           <template #default="{ row }">{{ formatDateTime(row.createTime) }}</template>
         </el-table-column>
       </ListTable>
-        <ListColumnStatsRow
+        </div>
+        <div ref="billsBelowRef">
+          <ListColumnStatsRow
           ref="billsStatsRowRef"
           storage-key="pl-freight-settlement-bill-column-stats"
           :columns="billsStatsRowColumns"
@@ -254,9 +258,8 @@
           :table-wrap-el="billsTableWrapRef"
           :get-cell-value="getBillsStatCellValue"
         />
-      </div>
 
-      <div class="pagination list-pager-bar">
+          <div class="pagination list-pager-bar">
           <div class="list-pager-selected">
             已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
           </div>
@@ -271,6 +274,7 @@
           @current-change="handlePageChange"
         />
       </div>
+        </div>
     </div>
 
     <PlFreightSettlementViewDialog ref="viewDialogRef">
@@ -334,6 +338,7 @@ import ListColumnStatsRow from '@/components/business/list-column-stats-row/inde
 import type { StatsRowColumn } from '@/utils/list-column-stats'
 import { useDbInstantSearch } from '@/composables/useDbInstantSearch'
 import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
+import { useListTableViewportHeight } from '@/composables/useListTableViewportHeight'
 import {
   CircleClose,
   Edit,
@@ -376,6 +381,13 @@ const {
 } = usePersistedListColumns('pl-freight-settlement-bill-columns', listColumnDefs)
 
 const billsTableWrapRef = ref<HTMLElement | null>(null)
+const billsBelowRef = ref<HTMLElement | null>(null)
+const billsTableRef = ref<any>(null)
+const { tableHeight, recalc: recalcTableHeight } = useListTableViewportHeight({
+tableEl: billsTableWrapRef,
+  pagerEl: billsBelowRef,
+  tableRef: billsTableRef
+})
 const billsStatsRowRef = ref<{ resync?: () => void } | null>(null)
 
 const billsStatsRowColumns = computed((): StatsRowColumn[] => {
@@ -610,6 +622,8 @@ useDbInstantSearch(
 onMounted(() => {
   loadCarrierList()
   fetchData()
+
+  nextTick(() => recalcTableHeight())
 })
 
 defineExpose({ refresh })

+ 19 - 5
web-admin/src/views/pl/freight/components/UnsettledPlanPanel.vue

@@ -94,8 +94,10 @@
 
     <div class="list-body list-wrap">
       <div ref="unsettledTableWrapRef" class="table-scroll bx-list-table-wrap">
-        <ListTable table-key="pl-freight-components-unsettled-plan-panel"
-          class="list-table"
+        <ListTable
+            ref="unsettledTableRef" table-key="pl-freight-components-unsettled-plan-panel"
+          :height="tableHeight"
+            class="list-table"
           :data="planList"
           v-loading="loading"
           stripe
@@ -198,7 +200,9 @@
           }}</template>
         </el-table-column>
       </ListTable>
-        <ListColumnStatsRow
+        </div>
+        <div ref="unsettledBelowRef">
+          <ListColumnStatsRow
           ref="unsettledStatsRowRef"
           storage-key="pl-freight-unsettled-plan-column-stats"
           :columns="unsettledStatsRowColumns"
@@ -206,9 +210,8 @@
           :table-wrap-el="unsettledTableWrapRef"
           :get-cell-value="getUnsettledStatCellValue"
         />
-      </div>
 
-      <div class="pagination list-pager-bar">
+          <div class="pagination list-pager-bar">
           <div class="list-pager-selected">
             已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
           </div>
@@ -223,6 +226,7 @@
           @current-change="handlePageChange"
         />
       </div>
+        </div>
     </div>
 
     <PlPlanViewDialog v-model:visible="planViewVisible" :plan-id="viewPlanId" />
@@ -237,6 +241,7 @@ import ListColumnStatsRow from '@/components/business/list-column-stats-row/inde
 import type { StatsRowColumn } from '@/utils/list-column-stats'
 import { useDbInstantSearch } from '@/composables/useDbInstantSearch'
 import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
+import { useListTableViewportHeight } from '@/composables/useListTableViewportHeight'
 import { DocumentAdd, RefreshLeft, Search } from '@element-plus/icons-vue'
 import { freightApi, vehicleApi, type UnsettledPlanItem, type Vehicle } from '@/api/pl'
 import { usePlRemoteCarrierOptions } from '@/composables/usePlRemoteOptions'
@@ -270,6 +275,13 @@ const {
 } = usePersistedListColumns('pl-freight-unsettled-plan-columns', listColumnDefs)
 
 const unsettledTableWrapRef = ref<HTMLElement | null>(null)
+const unsettledBelowRef = ref<HTMLElement | null>(null)
+const unsettledTableRef = ref<any>(null)
+const { tableHeight, recalc: recalcTableHeight } = useListTableViewportHeight({
+tableEl: unsettledTableWrapRef,
+  pagerEl: unsettledBelowRef,
+  tableRef: unsettledTableRef
+})
 const unsettledStatsRowRef = ref<{ resync?: () => void } | null>(null)
 
 const unsettledStatsRowColumns = computed((): StatsRowColumn[] => {
@@ -474,6 +486,8 @@ onMounted(() => {
   loadCarrierList()
   loadVehicleOptions()
   fetchData()
+
+  nextTick(() => recalcTableHeight())
 })
 
 defineExpose({ refresh })

+ 2 - 2
web-admin/src/views/pl/freight/index.vue

@@ -7,10 +7,10 @@
         class="panel-tabs"
         @tab-change="handleTabChange"
       >
-        <el-tab-pane label="未结算计划" name="unsettled">
+        <el-tab-pane label="未结算计划" name="unsettled" lazy>
           <UnsettledPlanPanel ref="unsettledRef" @created="handleSettlementCreated" />
         </el-tab-pane>
-        <el-tab-pane label="结算单" name="bills">
+        <el-tab-pane label="结算单" name="bills" lazy>
           <SettlementBillPanel ref="billsRef" />
         </el-tab-pane>
       </el-tabs>

+ 30 - 13
web-admin/src/views/pl/inventory/document-reminder.vue

@@ -65,14 +65,18 @@
       </div>
 
       <div class="list-body list-wrap">
-        <ListTable table-key="pl-inventory-document-reminder"
-          class="list-table"
-          :data="displayReminderList"
-          v-loading="loading"
-          stripe
-          row-key="id"
-          @selection-change="handleSelectionChange"
-        >
+        <div ref="docReminderTableWrapRef" class="table-scroll bx-list-table-wrap">
+          <ListTable
+            ref="docReminderTableRef"
+            table-key="pl-inventory-document-reminder"
+            class="list-table"
+            :height="tableHeight"
+            :data="displayReminderList"
+            v-loading="loading"
+            stripe
+            row-key="id"
+            @selection-change="handleSelectionChange"
+          >
           <el-table-column type="selection" width="55" align="center" />
           <el-table-column type="index" label="序号" width="68" align="center" header-align="center" />
           <el-table-column
@@ -154,12 +158,14 @@
             show-overflow-tooltip
           />
         </ListTable>
-      </div>
-
-      <div class="pagination list-pager-bar">
-          <div class="list-pager-selected">
-            已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
+        </div>
+        <div ref="docReminderBelowRef">
+          <div class="pagination list-pager-bar">
+            <div class="list-pager-selected">
+              已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
+            </div>
           </div>
+        </div>
       </div>
     </section>
   </div>
@@ -172,6 +178,7 @@ import ListExportDropdown from '@/components/business/list-export-dropdown/index
 import { CircleCheck, RefreshLeft, Search } from '@element-plus/icons-vue'
 import { inventoryApi, type DocumentReminder } from '@/api/pl'
 import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
+import { useListTableViewportHeight } from '@/composables/useListTableViewportHeight'
 import { useDbInstantSearch } from '@/composables/useDbInstantSearch'
 import { refreshMenuBadges } from '@/utils/menu-badge-registry'
 
@@ -193,6 +200,15 @@ const {
   resetToDefault
 } = usePersistedListColumns('pl-inventory-document-reminder-list-columns', listColumnDefs)
 
+const docReminderTableWrapRef = ref<HTMLElement | null>(null)
+const docReminderBelowRef = ref<HTMLElement | null>(null)
+const docReminderTableRef = ref<any>(null)
+const { tableHeight, recalc: recalcTableHeight } = useListTableViewportHeight({
+tableEl: docReminderTableWrapRef,
+  pagerEl: docReminderBelowRef,
+  tableRef: docReminderTableRef
+})
+
 const loading = ref(false)
 const resolveLoading = ref(false)
 const reminderSourceList = ref<DocumentReminder[]>([])
@@ -337,5 +353,6 @@ useDbInstantSearch(() => ({ status: searchForm.status }), handleSearch)
 
 onMounted(() => {
   fetchData()
+  nextTick(() => recalcTableHeight())
 })
 </script>

+ 21 - 2
web-admin/src/views/pl/inventory/ledger.vue

@@ -58,7 +58,10 @@
       </div>
 
       <div class="list-body list-wrap">
-        <ListTable table-key="pl-inventory-ledger"
+        <div ref="ledgerTableWrapRef" class="table-scroll bx-list-table-wrap">
+          <ListTable
+            ref="ledgerTableRef"
+            :height="tableHeight" table-key="pl-inventory-ledger"
           class="list-table"
           :data="inventoryList"
           v-loading="loading"
@@ -163,7 +166,10 @@
           </el-table-column>
         </ListTable>
 
-        <div class="pagination list-pager-bar">
+        
+        </div>
+        <div ref="ledgerBelowRef">
+          <div class="pagination list-pager-bar">
           <div class="list-pager-selected">
             已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
           </div>
@@ -178,6 +184,7 @@
             @current-change="handlePageChange"
           />
         </div>
+        </div>
       </div>
     </section>
   </div>
@@ -190,6 +197,7 @@ import ListExportDropdown from '@/components/business/list-export-dropdown/index
 import { RefreshLeft, Search } from '@element-plus/icons-vue'
 import { inventoryApi, type InventoryRecord } from '@/api/pl'
 import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
+import { useListTableViewportHeight } from '@/composables/useListTableViewportHeight'
 import { useDbInstantSearch } from '@/composables/useDbInstantSearch'
 
 const listColumnDefs = [
@@ -210,6 +218,15 @@ const {
   resetToDefault
 } = usePersistedListColumns('pl-inventory-ledger-list-columns', listColumnDefs)
 
+const ledgerTableWrapRef = ref<HTMLElement | null>(null)
+const ledgerBelowRef = ref<HTMLElement | null>(null)
+const ledgerTableRef = ref<any>(null)
+const { tableHeight, recalc: recalcTableHeight } = useListTableViewportHeight({
+tableEl: ledgerTableWrapRef,
+  pagerEl: ledgerBelowRef,
+  tableRef: ledgerTableRef
+})
+
 const loading = ref(false)
 const selectedRows = ref<any[]>([])
 
@@ -297,6 +314,8 @@ useDbInstantSearch(() => ({ productName: searchForm.productName }), handleSearch
 
 onMounted(() => {
   fetchData()
+
+  nextTick(() => recalcTableHeight())
 })
 </script>
 

+ 21 - 2
web-admin/src/views/pl/personnel/index.vue

@@ -92,7 +92,10 @@
       </div>
 
       <div class="list-body list-wrap">
-        <ListTable table-key="pl-personnel"
+        <div ref="personnelTableWrapRef" class="table-scroll bx-list-table-wrap">
+          <ListTable
+            ref="personnelTableRef"
+            :height="tableHeight" table-key="pl-personnel"
           class="list-table"
           :data="personnelRows"
           v-loading="loading"
@@ -262,7 +265,10 @@
           />
         </ListTable>
 
-        <div class="pagination list-pager-bar">
+        
+        </div>
+        <div ref="personnelBelowRef">
+          <div class="pagination list-pager-bar">
           <div class="list-pager-selected">
             已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
           </div>
@@ -277,6 +283,7 @@
             @current-change="handlePageChange"
           />
         </div>
+        </div>
       </div>
     </section>
 
@@ -439,6 +446,7 @@ import ListToolbarEndActions from '@/components/business/list-toolbar-end-action
 import ListExportDropdown from '@/components/business/list-export-dropdown/index.vue'
 import ListQuickSearchInput from '@/components/business/list-quick-search-input/index.vue'
 import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
+import { useListTableViewportHeight } from '@/composables/useListTableViewportHeight'
 import { useDbInstantSearch } from '@/composables/useDbInstantSearch'
 import { useListMediaActive } from '@/composables/useListMediaActive'
 import { pickBxPageResult } from '@/utils/http/bx'
@@ -504,6 +512,15 @@ const {
   resetToDefault
 } = usePersistedListColumns('pl-personnel-list-columns-v3', listColumnDefs)
 
+const personnelTableWrapRef = ref<HTMLElement | null>(null)
+const personnelBelowRef = ref<HTMLElement | null>(null)
+const personnelTableRef = ref<any>(null)
+const { tableHeight, recalc: recalcTableHeight } = useListTableViewportHeight({
+tableEl: personnelTableWrapRef,
+  pagerEl: personnelBelowRef,
+  tableRef: personnelTableRef
+})
+
 const submitLoading = ref(false)
 const personnelRows = ref<Personnel[]>([])
 const selectedRows = ref<Personnel[]>([])
@@ -959,6 +976,8 @@ useDbInstantSearch(
 
 onMounted(() => {
   fetchData()
+
+  nextTick(() => recalcTableHeight())
 })
 </script>
 

+ 16 - 3
web-admin/src/views/pl/plan-archive/index.vue

@@ -141,7 +141,9 @@
       <div class="list-body list-wrap">
         <div ref="archiveTableWrapRef" class="table-scroll bx-list-table-wrap">
           <ListTable
+            ref="archiveTableRef"
             table-key="pl-plan-archive"
+            :height="tableHeight"
             class="list-table pl-plan-merge-list-table"
             :data="displayRows"
             v-loading="loading"
@@ -539,6 +541,8 @@
               </template>
             </el-table-column>
           </ListTable>
+          </div>
+        <div ref="archiveBelowRef">
           <ListColumnStatsRow
             ref="archiveStatsRowRef"
             storage-key="pl-plan-archive-column-stats"
@@ -547,9 +551,8 @@
             :table-wrap-el="archiveTableWrapRef"
             :get-cell-value="getArchiveStatCellValue"
           />
-        </div>
 
-        <div class="pagination list-pager-bar">
+          <div class="pagination list-pager-bar">
           <div class="list-pager-selected">
             已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
           </div>
@@ -564,6 +567,7 @@
             @current-change="handlePageChange"
           />
         </div>
+        </div>
       </div>
     </section>
 
@@ -603,6 +607,7 @@ import ListColumnStatsRow from '@/components/business/list-column-stats-row/inde
 import type { StatsRowColumn } from '@/utils/list-column-stats'
 import { useContractDetailDialog } from '@/composables/useContractDetailDialog'
 import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
+import { useListTableViewportHeight } from '@/composables/useListTableViewportHeight'
 import { usePlPlanMergeListTable } from '@/composables/usePlPlanMergeListTable'
 import {
   ArrowDown,
@@ -820,7 +825,13 @@ function getArchiveStatCellValue(row: PlPlanListDisplayRow, prop: string): unkno
 }
 
 const archiveTableWrapRef = ref<HTMLElement | null>(null)
-
+const archiveBelowRef = ref<HTMLElement | null>(null)
+const archiveTableRef = ref<any>(null)
+const { tableHeight, recalc: recalcTableHeight } = useListTableViewportHeight({
+tableEl: archiveTableWrapRef,
+  pagerEl: archiveBelowRef,
+  tableRef: archiveTableRef
+})
 function getPlanArchiveExportFilterParams() {
   const keyword = quickSearch.value.trim()
   return {
@@ -1038,6 +1049,8 @@ useDbInstantSearch(
 
 onMounted(() => {
   fetchData()
+
+  nextTick(() => recalcTableHeight())
 })
 </script>
 

+ 40 - 24
web-admin/src/views/pl/plan-cert/index.vue

@@ -145,17 +145,20 @@
 
       <!-- 数据表格 -->
       <div class="list-body list-wrap">
-        <ListTable
-          table-key="pl-plan-cert"
-          class="list-table pl-plan-merge-list-table"
-          :data="displayRows"
-          v-loading="loading"
-          row-key="rowKey"
-          :span-method="planListSpanMethod"
-          :row-class-name="getPlPlanListRowClassName"
-          @selection-change="handleDisplaySelectionChange"
-          @row-dblclick="handleDisplayRowDblClick"
-        >
+        <div ref="planCertTableWrapRef" class="table-scroll bx-list-table-wrap">
+          <ListTable
+            ref="planCertTableRef"
+            table-key="pl-plan-cert"
+            class="list-table pl-plan-merge-list-table"
+            :height="tableHeight"
+            :data="displayRows"
+            v-loading="loading"
+            row-key="rowKey"
+            :span-method="planListSpanMethod"
+            :row-class-name="getPlPlanListRowClassName"
+            @selection-change="handleDisplaySelectionChange"
+            @row-dblclick="handleDisplayRowDblClick"
+          >
           <el-table-column
             type="selection"
             width="55"
@@ -454,21 +457,23 @@
             </template>
           </el-table-column>
         </ListTable>
+        </div>
+        <div ref="planCertBelowRef">
+          <div class="pagination list-pager-bar">
+            <div class="list-pager-selected">
+              已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
+            </div>
 
-        <div class="pagination list-pager-bar">
-          <div class="list-pager-selected">
-            已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
+            <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>
-
-          <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>
     </section>
@@ -536,6 +541,7 @@
   } from '@/composables/usePlToxicProductCertCheck'
   import { useContractDetailDialog } from '@/composables/useContractDetailDialog'
   import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
+  import { useListTableViewportHeight } from '@/composables/useListTableViewportHeight'
   import { usePlPlanMergeListTable } from '@/composables/usePlPlanMergeListTable'
   import PlPlanViewDialog from '@/components/business/pl-plan-view-dialog/index.vue'
   import PlPlanTypeTabs from '@/components/business/pl-plan-type-tabs/index.vue'
@@ -618,6 +624,15 @@
   const { visibleColumns, checkAll, isIndeterminate, isColumnVisible, resetToDefault } =
     usePersistedListColumns('pl-plan-cert-list-columns-v4', listColumnDefs)
 
+  const planCertTableWrapRef = ref<HTMLElement | null>(null)
+  const planCertBelowRef = ref<HTMLElement | null>(null)
+  const planCertTableRef = ref<any>(null)
+  const { tableHeight, recalc: recalcTableHeight } = useListTableViewportHeight({
+tableEl: planCertTableWrapRef,
+    pagerEl: planCertBelowRef,
+    tableRef: planCertTableRef
+  })
+
   const selectedRows = ref<Plan[]>([])
 
   const pagination = reactive({
@@ -1001,6 +1016,7 @@
 
   onMounted(() => {
     fetchData()
+    nextTick(() => recalcTableHeight())
   })
 </script>
 

+ 37 - 21
web-admin/src/views/pl/plan-contract/index.vue

@@ -86,15 +86,18 @@
       </div>
 
       <div class="list-body list-wrap">
-        <el-table
-          class="list-table pl-plan-merge-list-table"
-          :data="displayRows"
-          v-loading="loading"
-          row-key="rowKey"
-          :span-method="planListSpanMethod"
-          :row-class-name="getPlPlanListRowClassName"
-          @row-dblclick="handleDisplayRowDblClick"
-        >
+        <div ref="planContractTableWrapRef" class="table-scroll bx-list-table-wrap">
+          <el-table
+            ref="planContractTableRef"
+            class="list-table pl-plan-merge-list-table"
+            :height="tableHeight"
+            :data="displayRows"
+            v-loading="loading"
+            row-key="rowKey"
+            :span-method="planListSpanMethod"
+            :row-class-name="getPlPlanListRowClassName"
+            @row-dblclick="handleDisplayRowDblClick"
+          >
           <el-table-column
             type="index"
             label="序号"
@@ -318,18 +321,20 @@
             </template>
           </el-table-column>
         </el-table>
-
-        <div class="pagination list-pager-bar">
-          <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, jumper"
-            background
-            @current-change="handlePageChange"
-            @size-change="handleSizeChange"
-          />
+        </div>
+        <div ref="planContractBelowRef">
+          <div class="pagination list-pager-bar">
+            <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, jumper"
+              background
+              @current-change="handlePageChange"
+              @size-change="handleSizeChange"
+            />
+          </div>
         </div>
       </div>
     </section>
@@ -444,6 +449,7 @@ import ListToolbarDateShortcuts from '@/components/core/list-toolbar-date-shortc
 import { useContractDetailDialog } from '@/composables/useContractDetailDialog'
 import { useListDateShortcut } from '@/composables/useListDateShortcut'
 import { usePlPlanMergeListTable } from '@/composables/usePlPlanMergeListTable'
+import { useListTableViewportHeight } from '@/composables/useListTableViewportHeight'
 import {
   getPlPlanFollowStatusLabel,
   getPlPlanFollowStatusTagType
@@ -505,6 +511,15 @@ const pagination = reactive({
   total: 0
 })
 
+const planContractTableWrapRef = ref<HTMLElement | null>(null)
+const planContractBelowRef = ref<HTMLElement | null>(null)
+const planContractTableRef = ref<any>(null)
+const { tableHeight, recalc: recalcTableHeight } = useListTableViewportHeight({
+tableEl: planContractTableWrapRef,
+  pagerEl: planContractBelowRef,
+  tableRef: planContractTableRef
+})
+
 const planListForDisplay = computed(() => tableData.value as unknown as import('@/api/pl').Plan[])
 
 const visiblePlanMergeProps = computed(() => [...PLAN_MERGE_COLUMN_PROPS])
@@ -803,6 +818,7 @@ async function handleSaveStopContracts() {
 
 onMounted(() => {
   fetchData()
+  nextTick(() => recalcTableHeight())
 })
 </script>
 

+ 19 - 3
web-admin/src/views/pl/plan/index.vue

@@ -191,7 +191,9 @@
       <div class="list-body list-wrap">
         <div ref="planTableWrapRef" class="table-scroll bx-list-table-wrap">
           <ListTable
+            ref="planTableRef"
             table-key="pl-plan"
+            :height="tableHeight"
             class="list-table pl-plan-merge-list-table"
             :data="displayRows"
             v-loading="loading"
@@ -548,6 +550,8 @@
               </template>
             </el-table-column>
           </ListTable>
+          </div>
+        <div ref="planBelowRef">
           <ListColumnStatsRow
             ref="planStatsRowRef"
             storage-key="pl-plan-column-stats-v2"
@@ -556,9 +560,8 @@
             :table-wrap-el="planTableWrapRef"
             :get-cell-value="getDisplayStatCellValue"
           />
-        </div>
 
-        <div class="pagination list-pager-bar pl-plan-list-pager-bar">
+          <div class="pagination list-pager-bar pl-plan-list-pager-bar">
           <div class="list-pager-selected">
             已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
           </div>
@@ -572,6 +575,7 @@
             @current-change="handlePageChange"
           />
         </div>
+        </div>
       </div>
     </section>
 
@@ -774,6 +778,7 @@
   import type { StatsRowColumn } from '@/utils/list-column-stats'
   import { useContractDetailDialog } from '@/composables/useContractDetailDialog'
   import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
+import { useListTableViewportHeight } from '@/composables/useListTableViewportHeight'
   import { usePlPlanArrangeActions } from '@/composables/usePlPlanArrangeActions'
   import PlPlanListCertCell from '@/components/business/pl-plan-list-cert-cell/index.vue'
   import PlPlanListProductCell from '@/components/business/pl-plan-list-product-cell/index.vue'
@@ -891,6 +896,15 @@
   )
 
   const planTableWrapRef = ref<HTMLElement | null>(null)
+  const planBelowRef = ref<HTMLElement | null>(null)
+  const planTableRef = ref<any>(null)
+  const { tableHeight, recalc: recalcTableHeight } = useListTableViewportHeight({
+tableEl: planTableWrapRef,
+    pagerEl: planBelowRef,
+    tableRef: planTableRef
+
+
+  })
   const planStatsRowRef = ref<{ resync?: () => void } | null>(null)
 
   const {
@@ -1535,7 +1549,9 @@
     loadForceFollowPermission()
     fetchData()
     openArrangeFromQuery()
-  })
+  
+  nextTick(() => recalcTableHeight())
+})
 
   async function openArrangeFromQuery() {
     const arrangeId = route.query.arrange as string | undefined

+ 21 - 2
web-admin/src/views/pl/self-vehicle/index.vue

@@ -103,7 +103,10 @@
       </div>
 
       <div class="list-body list-wrap">
-        <ListTable table-key="pl-self-vehicle"
+        <div ref="selfVehicleTableWrapRef" class="table-scroll bx-list-table-wrap">
+          <ListTable
+            ref="selfVehicleTableRef"
+            :height="tableHeight" table-key="pl-self-vehicle"
           class="list-table"
           :data="vehicleList"
           v-loading="loading"
@@ -237,7 +240,10 @@
           />
         </ListTable>
 
-        <div class="pagination list-pager-bar">
+        
+        </div>
+        <div ref="selfVehicleBelowRef">
+          <div class="pagination list-pager-bar">
           <div class="list-pager-selected">
             已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
           </div>
@@ -252,6 +258,7 @@
             @current-change="handlePageChange"
           />
         </div>
+        </div>
       </div>
     </section>
 
@@ -472,6 +479,7 @@ import ListExportDropdown from '@/components/business/list-export-dropdown/index
 import ListQuickSearchInput from '@/components/business/list-quick-search-input/index.vue'
 import AttachmentUploadField from '@/components/business/attachment-upload-field/index.vue'
 import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
+import { useListTableViewportHeight } from '@/composables/useListTableViewportHeight'
 import { Check, Close, Delete, Download, Edit, Plus, RefreshLeft, Search, Van } from '@element-plus/icons-vue'
 import {
   vehicleApi,
@@ -576,6 +584,15 @@ const {
   resetToDefault
 } = usePersistedListColumns('pl-self-vehicle-list-columns-v3', listColumnDefs)
 
+const selfVehicleTableWrapRef = ref<HTMLElement | null>(null)
+const selfVehicleBelowRef = ref<HTMLElement | null>(null)
+const selfVehicleTableRef = ref<any>(null)
+const { tableHeight, recalc: recalcTableHeight } = useListTableViewportHeight({
+tableEl: selfVehicleTableWrapRef,
+  pagerEl: selfVehicleBelowRef,
+  tableRef: selfVehicleTableRef
+})
+
 const {
   list: personnelOptions,
   loading: personnelSelectLoading,
@@ -1180,6 +1197,8 @@ useDbInstantSearch(
 onMounted(() => {
   loadCarrierOptions()
   fetchData()
+
+  nextTick(() => recalcTableHeight())
 })
 </script>
 

+ 21 - 2
web-admin/src/views/pl/transport-address/index.vue

@@ -83,7 +83,10 @@
       </div>
 
       <div class="list-body list-wrap">
-        <ListTable table-key="pl-transport-address"
+        <div ref="transportAddressTableWrapRef" class="table-scroll bx-list-table-wrap">
+          <ListTable
+            ref="transportAddressTableRef"
+            :height="tableHeight" table-key="pl-transport-address"
           class="list-table"
           :data="addressRows"
           v-loading="loading"
@@ -160,7 +163,10 @@
           />
         </ListTable>
 
-        <div class="pagination list-pager-bar">
+        
+        </div>
+        <div ref="transportAddressBelowRef">
+          <div class="pagination list-pager-bar">
           <div class="list-pager-selected">
             已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
           </div>
@@ -175,6 +181,7 @@
             @size-change="handleSizeChange"
           />
         </div>
+        </div>
       </div>
     </section>
 
@@ -280,6 +287,7 @@ import ListToolbarEndActions from '@/components/business/list-toolbar-end-action
 import ListExportDropdown from '@/components/business/list-export-dropdown/index.vue'
 import SystemViewDialog, { type ViewSection } from '@/components/business/system-view-dialog/index.vue'
 import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
+import { useListTableViewportHeight } from '@/composables/useListTableViewportHeight'
 import { useDbInstantSearch } from '@/composables/useDbInstantSearch'
 import { pickBxPageResult } from '@/utils/http/bx'
 import { DEFAULT_LIST_PAGE_SIZE } from '@/utils/table/tableConfig'
@@ -305,6 +313,15 @@ const {
   isColumnVisible,
   resetToDefault
 } = usePersistedListColumns('pl-transport-address-list-columns', listColumnDefs)
+
+const transportAddressTableWrapRef = ref<HTMLElement | null>(null)
+const transportAddressBelowRef = ref<HTMLElement | null>(null)
+const transportAddressTableRef = ref<any>(null)
+const { tableHeight, recalc: recalcTableHeight } = useListTableViewportHeight({
+tableEl: transportAddressTableWrapRef,
+  pagerEl: transportAddressBelowRef,
+  tableRef: transportAddressTableRef
+})
 const submitLoading = ref(false)
 const addressRows = ref<TransportAddress[]>([])
 const selectedRows = ref<TransportAddress[]>([])
@@ -613,5 +630,7 @@ useDbInstantSearch(
 
 onMounted(() => {
   fetchData()
+
+  nextTick(() => recalcTableHeight())
 })
 </script>

+ 21 - 2
web-admin/src/views/pl/transport/index.vue

@@ -84,7 +84,10 @@
       </div>
 
       <div class="list-body list-wrap">
-        <ListTable table-key="pl-transport"
+        <div ref="transportTableWrapRef" class="table-scroll bx-list-table-wrap">
+          <ListTable
+            ref="transportTableRef"
+            :height="tableHeight" table-key="pl-transport"
           class="list-table"
           :data="transportList"
           v-loading="loading"
@@ -204,7 +207,10 @@
           </el-table-column>
         </ListTable>
 
-        <div class="pagination list-pager-bar">
+        
+        </div>
+        <div ref="transportBelowRef">
+          <div class="pagination list-pager-bar">
           <div class="list-pager-selected">
             已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
           </div>
@@ -219,6 +225,7 @@
             @current-change="handlePageChange"
           />
         </div>
+        </div>
       </div>
     </section>
 
@@ -261,6 +268,7 @@ import ListQuickSearchInput from '@/components/business/list-quick-search-input/
 import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'
 import ListExportDropdown from '@/components/business/list-export-dropdown/index.vue'
 import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
+import { useListTableViewportHeight } from '@/composables/useListTableViewportHeight'
 import { useDbInstantSearch } from '@/composables/useDbInstantSearch'
 import { formatPlVehiclePlateDisplay } from '@/utils/pl-display'
 import { formatMoneyDisplay } from '@/utils/money-format'
@@ -289,6 +297,15 @@ const {
   resetToDefault
 } = usePersistedListColumns('pl-transport-list-columns', listColumnDefs)
 
+const transportTableWrapRef = ref<HTMLElement | null>(null)
+const transportBelowRef = ref<HTMLElement | null>(null)
+const transportTableRef = ref<any>(null)
+const { tableHeight, recalc: recalcTableHeight } = useListTableViewportHeight({
+tableEl: transportTableWrapRef,
+  pagerEl: transportBelowRef,
+  tableRef: transportTableRef
+})
+
 const detailLoading = ref(false)
 const qrcodeLoading = ref(false)
 const transportList = ref<Transport[]>([])
@@ -554,6 +571,8 @@ useDbInstantSearch(
 
 onMounted(() => {
   fetchData()
+
+  nextTick(() => recalcTableHeight())
 })
 </script>
 

+ 17 - 3
web-admin/src/views/pl/vehicle-maintenance-apply/index.vue

@@ -109,7 +109,9 @@
 
       <div class="list-body list-wrap">
         <div ref="maintApplyTableWrapRef" class="table-scroll bx-list-table-wrap">
-          <ListTable table-key="pl-vehicle-maintenance-apply"
+          <ListTable
+            ref="maintApplyTableRef" table-key="pl-vehicle-maintenance-apply"
+            :height="tableHeight"
             class="list-table"
             :data="tableData"
             v-loading="loading"
@@ -209,6 +211,8 @@
             align="center"
           />
         </ListTable>
+          </div>
+        <div ref="maintApplyBelowRef">
           <ListColumnStatsRow
             ref="maintApplyStatsRowRef"
             storage-key="pl-vehicle-maintenance-apply-column-stats"
@@ -217,9 +221,8 @@
             :table-wrap-el="maintApplyTableWrapRef"
             :get-cell-value="getMaintApplyStatCellValue"
           />
-        </div>
 
-        <div class="pagination list-pager-bar">
+          <div class="pagination list-pager-bar">
           <div class="list-pager-selected">
             已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
           </div>
@@ -234,6 +237,7 @@
             @current-change="fetchData"
           />
         </div>
+        </div>
       </div>
     </section>
 
@@ -384,6 +388,7 @@ import ListColumnStatsRow from '@/components/business/list-column-stats-row/inde
 import type { StatsRowColumn } from '@/utils/list-column-stats'
 import { useDbInstantSearch } from '@/composables/useDbInstantSearch'
 import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
+import { useListTableViewportHeight } from '@/composables/useListTableViewportHeight'
 import {
   Check,
   CircleClose,
@@ -439,6 +444,13 @@ const {
 } = usePersistedListColumns('pl-vehicle-maintenance-apply-list-columns', listColumnDefs)
 
 const maintApplyTableWrapRef = ref<HTMLElement | null>(null)
+const maintApplyBelowRef = ref<HTMLElement | null>(null)
+const maintApplyTableRef = ref<any>(null)
+const { tableHeight, recalc: recalcTableHeight } = useListTableViewportHeight({
+tableEl: maintApplyTableWrapRef,
+  pagerEl: maintApplyBelowRef,
+  tableRef: maintApplyTableRef
+})
 const maintApplyStatsRowRef = ref<{ resync?: () => void } | null>(null)
 
 const maintApplyStatsRowColumns = computed((): StatsRowColumn[] => {
@@ -841,6 +853,8 @@ useDbInstantSearch(
 onMounted(async () => {
   await Promise.all([loadVehicleOptions(), loadUserOptions()])
   fetchData()
+
+  nextTick(() => recalcTableHeight())
 })
 </script>
 

+ 17 - 3
web-admin/src/views/pl/vehicle-maintenance-process/index.vue

@@ -124,7 +124,9 @@
 
       <div class="list-body list-wrap">
         <div ref="maintProcessTableWrapRef" class="table-scroll bx-list-table-wrap">
-          <ListTable table-key="pl-vehicle-maintenance-process"
+          <ListTable
+            ref="maintProcessTableRef" table-key="pl-vehicle-maintenance-process"
+            :height="tableHeight"
             class="list-table"
             :data="tableData"
             v-loading="loading"
@@ -255,6 +257,8 @@
             <template #default="{ row }">{{ row.handlerName || '—' }}</template>
           </el-table-column>
         </ListTable>
+          </div>
+        <div ref="maintProcessBelowRef">
           <ListColumnStatsRow
             ref="maintProcessStatsRowRef"
             storage-key="pl-vehicle-maintenance-process-column-stats"
@@ -263,9 +267,8 @@
             :table-wrap-el="maintProcessTableWrapRef"
             :get-cell-value="getMaintProcessStatCellValue"
           />
-        </div>
 
-        <div class="pagination list-pager-bar">
+          <div class="pagination list-pager-bar">
           <div class="list-pager-selected">
             已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
           </div>
@@ -280,6 +283,7 @@
             @current-change="fetchData"
           />
         </div>
+        </div>
       </div>
     </section>
 
@@ -428,6 +432,7 @@ import ListColumnStatsRow from '@/components/business/list-column-stats-row/inde
 import type { StatsRowColumn } from '@/utils/list-column-stats'
 import { useDbInstantSearch } from '@/composables/useDbInstantSearch'
 import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
+import { useListTableViewportHeight } from '@/composables/useListTableViewportHeight'
 import { Check, Close, Delete, Edit, Plus, RefreshLeft, Search, Tools } from '@element-plus/icons-vue'
 import { fetchUserOptions } from '@/api/admin'
 import {
@@ -491,6 +496,13 @@ const {
 } = usePersistedListColumns('pl-vehicle-maintenance-process-list-columns', listColumnDefs)
 
 const maintProcessTableWrapRef = ref<HTMLElement | null>(null)
+const maintProcessBelowRef = ref<HTMLElement | null>(null)
+const maintProcessTableRef = ref<any>(null)
+const { tableHeight, recalc: recalcTableHeight } = useListTableViewportHeight({
+tableEl: maintProcessTableWrapRef,
+  pagerEl: maintProcessBelowRef,
+  tableRef: maintProcessTableRef
+})
 const maintProcessStatsRowRef = ref<{ resync?: () => void } | null>(null)
 
 const maintProcessStatsRowColumns = computed((): StatsRowColumn[] => {
@@ -934,6 +946,8 @@ useDbInstantSearch(
 
 onMounted(() => {
   fetchData()
+
+  nextTick(() => recalcTableHeight())
 })
 </script>
 

+ 21 - 2
web-admin/src/views/pl/vehicle-recharge-apply/index.vue

@@ -96,7 +96,10 @@
       </div>
 
       <div class="list-body list-wrap">
-        <ListTable table-key="pl-vehicle-recharge-apply"
+        <div ref="rechargeApplyTableWrapRef" class="table-scroll bx-list-table-wrap">
+          <ListTable
+            ref="rechargeApplyTableRef"
+            :height="tableHeight" table-key="pl-vehicle-recharge-apply"
           class="list-table"
           :data="tableData"
           v-loading="loading"
@@ -189,7 +192,10 @@
           />
         </ListTable>
 
-        <div class="pagination list-pager-bar">
+        
+        </div>
+        <div ref="rechargeApplyBelowRef">
+          <div class="pagination list-pager-bar">
           <div class="list-pager-selected">
             已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
           </div>
@@ -204,6 +210,7 @@
             @current-change="fetchData"
           />
         </div>
+        </div>
       </div>
     </section>
 
@@ -320,6 +327,7 @@ import ListToolbarEndActions from '@/components/business/list-toolbar-end-action
 import ListExportDropdown from '@/components/business/list-export-dropdown/index.vue'
 import { useDbInstantSearch } from '@/composables/useDbInstantSearch'
 import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
+import { useListTableViewportHeight } from '@/composables/useListTableViewportHeight'
 import {
   Check,
   CircleClose,
@@ -372,6 +380,15 @@ const {
   resetToDefault
 } = usePersistedListColumns('pl-vehicle-recharge-apply-list-columns', listColumnDefs)
 
+const rechargeApplyTableWrapRef = ref<HTMLElement | null>(null)
+const rechargeApplyBelowRef = ref<HTMLElement | null>(null)
+const rechargeApplyTableRef = ref<any>(null)
+const { tableHeight, recalc: recalcTableHeight } = useListTableViewportHeight({
+tableEl: rechargeApplyTableWrapRef,
+  pagerEl: rechargeApplyBelowRef,
+  tableRef: rechargeApplyTableRef
+})
+
 const currentUserId = computed(() => {
   const info = userStore.info as Record<string, unknown> | undefined
   return String(info?.id || info?.userId || '')
@@ -697,5 +714,7 @@ useDbInstantSearch(
 onMounted(async () => {
   await Promise.all([loadVehicleOptions(), loadUserOptions()])
   fetchData()
+
+  nextTick(() => recalcTableHeight())
 })
 </script>

+ 21 - 2
web-admin/src/views/pl/vehicle-recharge-record/index.vue

@@ -126,7 +126,10 @@
       </div>
 
       <div class="list-body list-wrap">
-        <ListTable table-key="pl-vehicle-recharge-record"
+        <div ref="rechargeRecordTableWrapRef" class="table-scroll bx-list-table-wrap">
+          <ListTable
+            ref="rechargeRecordTableRef"
+            :height="tableHeight" table-key="pl-vehicle-recharge-record"
           class="list-table"
           :data="tableData"
           v-loading="loading"
@@ -270,7 +273,10 @@
           </el-table-column>
         </ListTable>
 
-        <div class="pagination list-pager-bar">
+        
+        </div>
+        <div ref="rechargeRecordBelowRef">
+          <div class="pagination list-pager-bar">
           <div class="list-pager-selected">
             已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
           </div>
@@ -285,6 +291,7 @@
             @current-change="fetchData"
           />
         </div>
+        </div>
       </div>
     </section>
 
@@ -429,6 +436,7 @@ import ListToolbarEndActions from '@/components/business/list-toolbar-end-action
 import ListExportDropdown from '@/components/business/list-export-dropdown/index.vue'
 import { useDbInstantSearch } from '@/composables/useDbInstantSearch'
 import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
+import { useListTableViewportHeight } from '@/composables/useListTableViewportHeight'
 import { Check, Close, Delete, Edit, Money, Plus, RefreshLeft, Search } from '@element-plus/icons-vue'
 import {
   vehicleApi,
@@ -482,6 +490,15 @@ const {
   resetToDefault
 } = usePersistedListColumns('pl-vehicle-recharge-record-list-columns', listColumnDefs)
 
+const rechargeRecordTableWrapRef = ref<HTMLElement | null>(null)
+const rechargeRecordBelowRef = ref<HTMLElement | null>(null)
+const rechargeRecordTableRef = ref<any>(null)
+const { tableHeight, recalc: recalcTableHeight } = useListTableViewportHeight({
+tableEl: rechargeRecordTableWrapRef,
+  pagerEl: rechargeRecordBelowRef,
+  tableRef: rechargeRecordTableRef
+})
+
 const submitLoading = ref(false)
 const deleteLoadingMap = reactive<Record<string, boolean>>({})
 const tableData = ref<VehicleRechargeDisburseBoardItem[]>([])
@@ -842,6 +859,8 @@ useDbInstantSearch(
 
 onMounted(() => {
   fetchData()
+
+  nextTick(() => recalcTableHeight())
 })
 </script>
 

+ 17 - 3
web-admin/src/views/pl/vehicle-urea-apply/index.vue

@@ -91,7 +91,9 @@
 
       <div class="list-body list-wrap">
         <div ref="ureaApplyTableWrapRef" class="table-scroll bx-list-table-wrap">
-          <ListTable table-key="pl-vehicle-urea-apply"
+          <ListTable
+            ref="ureaApplyTableRef" table-key="pl-vehicle-urea-apply"
+            :height="tableHeight"
             class="list-table"
             :data="tableData"
             v-loading="loading"
@@ -194,6 +196,8 @@
             align="center"
           />
         </ListTable>
+          </div>
+        <div ref="ureaApplyBelowRef">
           <ListColumnStatsRow
             ref="ureaApplyStatsRowRef"
             storage-key="pl-vehicle-urea-apply-column-stats"
@@ -202,9 +206,8 @@
             :table-wrap-el="ureaApplyTableWrapRef"
             :get-cell-value="getUreaApplyStatCellValue"
           />
-        </div>
 
-        <div class="pagination list-pager-bar">
+          <div class="pagination list-pager-bar">
           <div class="list-pager-selected">
             已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
           </div>
@@ -219,6 +222,7 @@
             @current-change="fetchData"
           />
         </div>
+        </div>
       </div>
     </section>
 
@@ -349,6 +353,7 @@ import ListColumnStatsRow from '@/components/business/list-column-stats-row/inde
 import type { StatsRowColumn } from '@/utils/list-column-stats'
 import { useDbInstantSearch } from '@/composables/useDbInstantSearch'
 import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
+import { useListTableViewportHeight } from '@/composables/useListTableViewportHeight'
 import {
   Check,
   CircleClose,
@@ -414,6 +419,13 @@ const {
 } = usePersistedListColumns('pl-vehicle-urea-apply-list-columns', listColumnDefs)
 
 const ureaApplyTableWrapRef = ref<HTMLElement | null>(null)
+const ureaApplyBelowRef = ref<HTMLElement | null>(null)
+const ureaApplyTableRef = ref<any>(null)
+const { tableHeight, recalc: recalcTableHeight } = useListTableViewportHeight({
+tableEl: ureaApplyTableWrapRef,
+  pagerEl: ureaApplyBelowRef,
+  tableRef: ureaApplyTableRef
+})
 const ureaApplyStatsRowRef = ref<{ resync?: () => void } | null>(null)
 
 const ureaApplyStatsRowColumns = computed((): StatsRowColumn[] => {
@@ -767,6 +779,8 @@ useDbInstantSearch(
 onMounted(async () => {
   await Promise.all([loadVehicleOptions(), loadUserOptions()])
   fetchData()
+
+  nextTick(() => recalcTableHeight())
 })
 </script>
 

+ 17 - 3
web-admin/src/views/pl/vehicle-urea-disburse/index.vue

@@ -106,7 +106,9 @@
 
       <div class="list-body list-wrap">
         <div ref="ureaDisburseTableWrapRef" class="table-scroll bx-list-table-wrap">
-          <ListTable table-key="pl-vehicle-urea-disburse"
+          <ListTable
+            ref="ureaDisburseTableRef" table-key="pl-vehicle-urea-disburse"
+            :height="tableHeight"
             class="list-table"
             :data="tableData"
             v-loading="loading"
@@ -240,6 +242,8 @@
             <template #default="{ row }">{{ row.handlerName || '—' }}</template>
           </el-table-column>
         </ListTable>
+          </div>
+        <div ref="ureaDisburseBelowRef">
           <ListColumnStatsRow
             ref="ureaDisburseStatsRowRef"
             storage-key="pl-vehicle-urea-disburse-column-stats"
@@ -248,9 +252,8 @@
             :table-wrap-el="ureaDisburseTableWrapRef"
             :get-cell-value="getUreaDisburseStatCellValue"
           />
-        </div>
 
-        <div class="pagination list-pager-bar">
+          <div class="pagination list-pager-bar">
           <div class="list-pager-selected">
             已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
           </div>
@@ -265,6 +268,7 @@
             @current-change="fetchData"
           />
         </div>
+        </div>
       </div>
     </section>
 
@@ -399,6 +403,7 @@ import ListColumnStatsRow from '@/components/business/list-column-stats-row/inde
 import type { StatsRowColumn } from '@/utils/list-column-stats'
 import { useDbInstantSearch } from '@/composables/useDbInstantSearch'
 import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
+import { useListTableViewportHeight } from '@/composables/useListTableViewportHeight'
 import { Check, Close, Delete, Edit, Money, Plus, RefreshLeft, Search } from '@element-plus/icons-vue'
 import {
   vehicleApi,
@@ -459,6 +464,13 @@ const {
 } = usePersistedListColumns('pl-vehicle-urea-disburse-list-columns', listColumnDefs)
 
 const ureaDisburseTableWrapRef = ref<HTMLElement | null>(null)
+const ureaDisburseBelowRef = ref<HTMLElement | null>(null)
+const ureaDisburseTableRef = ref<any>(null)
+const { tableHeight, recalc: recalcTableHeight } = useListTableViewportHeight({
+tableEl: ureaDisburseTableWrapRef,
+  pagerEl: ureaDisburseBelowRef,
+  tableRef: ureaDisburseTableRef
+})
 const ureaDisburseStatsRowRef = ref<{ resync?: () => void } | null>(null)
 
 const ureaDisburseStatsRowColumns = computed((): StatsRowColumn[] => {
@@ -863,6 +875,8 @@ useDbInstantSearch(
 
 onMounted(() => {
   fetchData()
+
+  nextTick(() => recalcTableHeight())
 })
 </script>
 

+ 21 - 2
web-admin/src/views/pl/vehicle/index.vue

@@ -108,7 +108,10 @@
       </div>
 
       <div class="list-body list-wrap">
-        <ListTable table-key="pl-vehicle"
+        <div ref="vehicleTableWrapRef" class="table-scroll bx-list-table-wrap">
+          <ListTable
+            ref="vehicleTableRef"
+            :height="tableHeight" table-key="pl-vehicle"
           class="list-table"
           :data="vehicleList"
           v-loading="loading"
@@ -244,7 +247,10 @@
           />
         </ListTable>
 
-        <div class="pagination list-pager-bar">
+        
+        </div>
+        <div ref="vehicleBelowRef">
+          <div class="pagination list-pager-bar">
           <div class="list-pager-selected">
             已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
           </div>
@@ -259,6 +265,7 @@
             @current-change="handlePageChange"
           />
         </div>
+        </div>
       </div>
     </section>
 
@@ -490,6 +497,7 @@ import ListExportDropdown from '@/components/business/list-export-dropdown/index
 import ListQuickSearchInput from '@/components/business/list-quick-search-input/index.vue'
 import AttachmentUploadField from '@/components/business/attachment-upload-field/index.vue'
 import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
+import { useListTableViewportHeight } from '@/composables/useListTableViewportHeight'
 import { Check, Close, Delete, DocumentCopy, Download, Edit, Plus, RefreshLeft, Search, Van } from '@element-plus/icons-vue'
 import {
   vehicleApi,
@@ -588,6 +596,15 @@ const {
   resetToDefault
 } = usePersistedListColumns('pl-vehicle-list-columns-v3', listColumnDefs)
 
+const vehicleTableWrapRef = ref<HTMLElement | null>(null)
+const vehicleBelowRef = ref<HTMLElement | null>(null)
+const vehicleTableRef = ref<any>(null)
+const { tableHeight, recalc: recalcTableHeight } = useListTableViewportHeight({
+tableEl: vehicleTableWrapRef,
+  pagerEl: vehicleBelowRef,
+  tableRef: vehicleTableRef
+})
+
 const {
   list: personnelOptions,
   loading: personnelSelectLoading,
@@ -1245,6 +1262,8 @@ useDbInstantSearch(
 onMounted(() => {
   loadCarrierOptions()
   fetchData()
+
+  nextTick(() => recalcTableHeight())
 })
 </script>
 

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.