Quellcode durchsuchen

feat(web-admin): viewport-fill list height for CRM menu pages

Wire contract, quote, customer, product, and price-letter lists to useListTableViewportHeight.

Co-authored-by: Cursor <cursoragent@cursor.com>
zlwu vor 2 Monaten
Ursprung
Commit
6f7461d009

+ 18 - 3
web-admin/src/views/crm/contract/ContractListPage.vue

@@ -156,10 +156,13 @@
 
 
       <div class="list-body list-wrap">      <!-- 操作按钮栏 + 数据表格 -->
       <div class="list-body list-wrap">      <!-- 操作按钮栏 + 数据表格 -->
         <div ref="contractTableWrapRef" class="contract-table-wrap">
         <div ref="contractTableWrapRef" class="contract-table-wrap">
-        <ListTable table-key="crm-contract"
+        <ListTable
+          ref="contractTableRef"
+          table-key="crm-contract"
           v-loading="loading"
           v-loading="loading"
           class="list-table"
           class="list-table"
           stripe
           stripe
+          :height="tableHeight"
           :data="contractList"
           :data="contractList"
           row-key="id"
           row-key="id"
           @selection-change="handleSelectionChange"
           @selection-change="handleSelectionChange"
@@ -465,7 +468,9 @@
         />
         />
         <el-table-column v-if="isColumnVisible('remark')" label="备注" align="center" prop="remark" min-width="200" show-overflow-tooltip />
         <el-table-column v-if="isColumnVisible('remark')" label="备注" align="center" prop="remark" min-width="200" show-overflow-tooltip />
         </ListTable>
         </ListTable>
+        </div>
 
 
+        <div ref="contractBelowRef">
         <ListColumnStatsRow
         <ListColumnStatsRow
           ref="contractStatsRowRef"
           ref="contractStatsRowRef"
           storage-key="crm-contract-column-stats"
           storage-key="crm-contract-column-stats"
@@ -474,7 +479,6 @@
           :table-wrap-el="contractTableWrapRef"
           :table-wrap-el="contractTableWrapRef"
           :get-cell-value="getContractStatCellValue"
           :get-cell-value="getContractStatCellValue"
         />
         />
-        </div>
 
 
         <div class="pagination list-pager-bar">
         <div class="pagination list-pager-bar">
           <div class="list-pager-selected">
           <div class="list-pager-selected">
@@ -492,6 +496,7 @@
             @current-change="getList"
             @current-change="getList"
           />
           />
         </div>
         </div>
+        </div>
       </div>
       </div>
     </section>
     </section>
 
 
@@ -526,7 +531,7 @@
 </template>
 </template>
 
 
 <script setup lang="ts" name="ContractListPage">
 <script setup lang="ts" name="ContractListPage">
-import { ref, reactive, computed, onMounted, watch } from 'vue';
+import { ref, reactive, computed, onMounted, watch, nextTick } from 'vue';
 import {
 import {
   listContract,
   listContract,
   getContract as getContractDetail,
   getContract as getContractDetail,
@@ -576,6 +581,7 @@ import ListExportDropdown from '@/components/business/list-export-dropdown/index
 import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'
 import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'
 import ListColumnStatsRow from '@/components/business/list-column-stats-row/index.vue'
 import ListColumnStatsRow from '@/components/business/list-column-stats-row/index.vue'
 import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
 import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
+import { useListTableViewportHeight } from '@/composables/useListTableViewportHeight'
 import { buildStatsRowColumns } from '@/utils/list-column-stats'
 import { buildStatsRowColumns } from '@/utils/list-column-stats'
 
 
 const props = withDefaults(
 const props = withDefaults(
@@ -1027,8 +1033,16 @@ const CONTRACT_MONEY_STAT_PROPS = new Set([
 ])
 ])
 
 
 const contractTableWrapRef = ref<HTMLElement | null>(null)
 const contractTableWrapRef = ref<HTMLElement | null>(null)
+const contractBelowRef = ref<HTMLElement | null>(null)
+const contractTableRef = ref<any>(null)
 const contractStatsRowRef = ref<{ resync?: () => void } | null>(null)
 const contractStatsRowRef = ref<{ resync?: () => void } | null>(null)
 
 
+const { tableHeight, recalc: recalcTableHeight } = useListTableViewportHeight({
+  tableEl: contractTableWrapRef,
+  pagerEl: contractBelowRef,
+  tableRef: contractTableRef
+})
+
 const contractStatsRowColumns = computed(() =>
 const contractStatsRowColumns = computed(() =>
   buildStatsRowColumns({
   buildStatsRowColumns({
     leadingSpacers: 2,
     leadingSpacers: 2,
@@ -1341,6 +1355,7 @@ onMounted(() => {
   } else {
   } else {
     handleViewChange()
     handleViewChange()
   }
   }
+  nextTick(() => recalcTableHeight())
 })
 })
 
 
 watch(formDialogVisible, (visible) => {
 watch(formDialogVisible, (visible) => {

+ 18 - 2
web-admin/src/views/crm/customer/index.vue

@@ -183,8 +183,12 @@
 
 
       <!-- 数据表格 -->
       <!-- 数据表格 -->
       <div class="list-body list-wrap">
       <div class="list-body list-wrap">
-        <ListTable table-key="crm-customer"
+        <div ref="customerTableWrapRef" class="list-table-viewport-anchor">
+        <ListTable
+          ref="customerTableRef"
+          table-key="crm-customer"
           class="list-table"
           class="list-table"
+          :height="tableHeight"
           :data="customerList"
           :data="customerList"
           v-loading="loading"
           v-loading="loading"
           stripe
           stripe
@@ -615,7 +619,8 @@
             show-overflow-tooltip
             show-overflow-tooltip
           />
           />
         </ListTable>
         </ListTable>
-        <div class="pagination list-pager-bar">
+        </div>
+        <div ref="customerPagerRef" class="pagination list-pager-bar">
           <div class="list-pager-selected">
           <div class="list-pager-selected">
             已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
             已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
           </div>
           </div>
@@ -693,6 +698,7 @@ import ListExportDropdown from '@/components/business/list-export-dropdown/index
 import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'
 import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'
 import ListQuickSearchInput from '@/components/business/list-quick-search-input/index.vue'
 import ListQuickSearchInput from '@/components/business/list-quick-search-input/index.vue'
 import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
 import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
+import { useListTableViewportHeight } from '@/composables/useListTableViewportHeight'
 
 
 const dictStore = useDictStore()
 const dictStore = useDictStore()
 
 
@@ -789,6 +795,15 @@ const {
   ]
   ]
 })
 })
 
 
+const customerTableWrapRef = ref<HTMLElement | null>(null)
+const customerPagerRef = ref<HTMLElement | null>(null)
+const customerTableRef = ref<any>(null)
+const { tableHeight, recalc: recalcTableHeight } = useListTableViewportHeight({
+  tableEl: customerTableWrapRef,
+  pagerEl: customerPagerRef,
+  tableRef: customerTableRef
+})
+
 const customerExportColumns = computed(() => {
 const customerExportColumns = computed(() => {
   const cols = [...visibleColumns.value]
   const cols = [...visibleColumns.value]
   if (!cols.includes('customerName')) cols.unshift('customerName')
   if (!cols.includes('customerName')) cols.unshift('customerName')
@@ -1025,6 +1040,7 @@ watchDebounced(
 
 
 onMounted(() => {
 onMounted(() => {
   getList()
   getList()
+  nextTick(() => recalcTableHeight())
 })
 })
 </script>
 </script>
 
 

+ 18 - 2
web-admin/src/views/crm/price-letter/index.vue

@@ -132,8 +132,12 @@
       </div>
       </div>
 
 
       <div class="list-body list-wrap">
       <div class="list-body list-wrap">
-        <ListTable table-key="crm-price-letter"
+        <div ref="priceLetterTableWrapRef" class="list-table-viewport-anchor">
+        <ListTable
+          ref="priceLetterTableRef"
+          table-key="crm-price-letter"
           class="list-table"
           class="list-table"
+          :height="tableHeight"
           :data="tableData"
           :data="tableData"
           v-loading="loading"
           v-loading="loading"
           stripe
           stripe
@@ -239,8 +243,9 @@
             header-align="center"
             header-align="center"
           />
           />
         </ListTable>
         </ListTable>
+        </div>
 
 
-        <div class="pagination list-pager-bar">
+        <div ref="priceLetterPagerRef" class="pagination list-pager-bar">
           <div class="list-pager-selected">
           <div class="list-pager-selected">
             已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
             已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
           </div>
           </div>
@@ -612,6 +617,7 @@
   } from '@/utils/crm/price-letter-template'
   } from '@/utils/crm/price-letter-template'
   import { useDictStore } from '@/store/modules/dict'
   import { useDictStore } from '@/store/modules/dict'
   import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
   import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
+  import { useListTableViewportHeight } from '@/composables/useListTableViewportHeight'
   import { formatMoneyDisplay } from '@/utils/money-format'
   import { formatMoneyDisplay } from '@/utils/money-format'
   import { $modal } from '@/utils/modal'
   import { $modal } from '@/utils/modal'
   import { fetchProductOptions } from '@/api/crm/product'
   import { fetchProductOptions } from '@/api/crm/product'
@@ -661,6 +667,15 @@
     resetToDefault
     resetToDefault
   } = usePersistedListColumns('crm-price-letter-list-columns', listColumnDefs)
   } = usePersistedListColumns('crm-price-letter-list-columns', listColumnDefs)
 
 
+  const priceLetterTableWrapRef = ref<HTMLElement | null>(null)
+  const priceLetterPagerRef = ref<HTMLElement | null>(null)
+  const priceLetterTableRef = ref<any>(null)
+  const { tableHeight, recalc: recalcTableHeight } = useListTableViewportHeight({
+    tableEl: priceLetterTableWrapRef,
+    pagerEl: priceLetterPagerRef,
+    tableRef: priceLetterTableRef
+  })
+
   const formDialogVisible = ref(false)
   const formDialogVisible = ref(false)
   const isEdit = ref(false)
   const isEdit = ref(false)
   const submitLoading = ref(false)
   const submitLoading = ref(false)
@@ -1363,6 +1378,7 @@ ${preparedHtml}
   onMounted(() => {
   onMounted(() => {
     getList()
     getList()
     searchProducts('')
     searchProducts('')
+    nextTick(() => recalcTableHeight())
   })
   })
 
 
   // 筛选区变化时清空简易检索并查询(勿把 quickSearch 放进依赖)
   // 筛选区变化时清空简易检索并查询(勿把 quickSearch 放进依赖)

+ 17 - 2
web-admin/src/views/crm/product/index.vue

@@ -128,9 +128,12 @@
 
 
       <!-- 数据表格 -->
       <!-- 数据表格 -->
       <div class="list-body list-wrap">
       <div class="list-body list-wrap">
+        <div ref="productTableWrapRef" class="list-table-viewport-anchor">
         <ListTable
         <ListTable
+          ref="productTableRef"
           table-key="crm-product"
           table-key="crm-product"
           class="list-table"
           class="list-table"
+          :height="tableHeight"
           :data="productList"
           :data="productList"
           v-loading="loading"
           v-loading="loading"
           stripe
           stripe
@@ -268,10 +271,11 @@
             </template>
             </template>
           </el-table-column>
           </el-table-column>
         </ListTable>
         </ListTable>
+        </div>
       </div>
       </div>
 
 
       <!-- 分页 -->
       <!-- 分页 -->
-      <div class="pagination list-pager-bar">
+      <div ref="productPagerRef" class="pagination list-pager-bar">
         <div class="list-pager-selected">
         <div class="list-pager-selected">
           已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
           已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
         </div>
         </div>
@@ -392,7 +396,7 @@
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-  import { ref, reactive, computed, onMounted } from 'vue'
+  import { ref, reactive, computed, onMounted, nextTick } from 'vue'
   import { ElMessage, ElMessageBox } from 'element-plus'
   import { ElMessage, ElMessageBox } from 'element-plus'
   import type { UploadFile } from 'element-plus'
   import type { UploadFile } from 'element-plus'
   import {
   import {
@@ -429,6 +433,7 @@
   import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'
   import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'
   import ListQuickSearchInput from '@/components/business/list-quick-search-input/index.vue'
   import ListQuickSearchInput from '@/components/business/list-quick-search-input/index.vue'
   import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
   import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
+  import { useListTableViewportHeight } from '@/composables/useListTableViewportHeight'
   import {
   import {
     fetchCategoryTree,
     fetchCategoryTree,
     fetchProductList,
     fetchProductList,
@@ -487,6 +492,15 @@
   const { visibleColumns, checkAll, isIndeterminate, isColumnVisible, resetToDefault } =
   const { visibleColumns, checkAll, isIndeterminate, isColumnVisible, resetToDefault } =
     usePersistedListColumns('crm-product-list-columns-v2', listColumnDefs)
     usePersistedListColumns('crm-product-list-columns-v2', listColumnDefs)
 
 
+  const productTableWrapRef = ref<HTMLElement | null>(null)
+  const productPagerRef = ref<HTMLElement | null>(null)
+  const productTableRef = ref<any>(null)
+  const { tableHeight, recalc: recalcTableHeight } = useListTableViewportHeight({
+    tableEl: productTableWrapRef,
+    pagerEl: productPagerRef,
+    tableRef: productTableRef
+  })
+
   function getProductExportFilterParams() {
   function getProductExportFilterParams() {
     const quickKeyword = quickSearchKeyword.value?.trim()
     const quickKeyword = quickSearchKeyword.value?.trim()
     return {
     return {
@@ -815,6 +829,7 @@
   onMounted(() => {
   onMounted(() => {
     getCategoryOptions()
     getCategoryOptions()
     getList()
     getList()
+    nextTick(() => recalcTableHeight())
   })
   })
 </script>
 </script>
 
 

+ 17 - 2
web-admin/src/views/crm/quote/QuoteListPage.vue

@@ -133,10 +133,13 @@
 
 
       <div class="list-body list-wrap">
       <div class="list-body list-wrap">
         <div ref="quoteTableWrapRef" class="quote-table-wrap">
         <div ref="quoteTableWrapRef" class="quote-table-wrap">
-        <ListTable table-key="crm-quote"
+        <ListTable
+          ref="quoteTableRef"
+          table-key="crm-quote"
           v-loading="loading"
           v-loading="loading"
           class="list-table"
           class="list-table"
           stripe
           stripe
+          :height="tableHeight"
           :data="quoteList"
           :data="quoteList"
           row-key="id"
           row-key="id"
           @selection-change="handleSelectionChange"
           @selection-change="handleSelectionChange"
@@ -389,7 +392,9 @@
         />
         />
         <el-table-column v-if="isColumnVisible('remark')" label="备注" align="center" prop="remark" min-width="200" show-overflow-tooltip />
         <el-table-column v-if="isColumnVisible('remark')" label="备注" align="center" prop="remark" min-width="200" show-overflow-tooltip />
         </ListTable>
         </ListTable>
+        </div>
 
 
+        <div ref="quoteBelowRef">
         <div
         <div
           ref="quoteStatsScrollRef"
           ref="quoteStatsScrollRef"
           class="quote-list-stats-row"
           class="quote-list-stats-row"
@@ -439,7 +444,6 @@
             </div>
             </div>
           </div>
           </div>
         </div>
         </div>
-        </div>
 
 
         <div class="pagination list-pager-bar">
         <div class="pagination list-pager-bar">
           <div class="list-pager-selected">
           <div class="list-pager-selected">
@@ -457,6 +461,7 @@
             @current-change="getList"
             @current-change="getList"
           />
           />
         </div>
         </div>
+        </div>
       </div>
       </div>
     </section>
     </section>
 
 
@@ -878,6 +883,7 @@ import ListToolbarDateShortcuts from '@/components/core/list-toolbar-date-shortc
 import ListExportDropdown from '@/components/business/list-export-dropdown/index.vue'
 import ListExportDropdown from '@/components/business/list-export-dropdown/index.vue'
 import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'
 import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'
 import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
 import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
+import { useListTableViewportHeight } from '@/composables/useListTableViewportHeight'
 import {
 import {
   COLUMN_STAT_METHOD_OPTIONS,
   COLUMN_STAT_METHOD_OPTIONS,
   QUOTE_MONEY_STAT_PROPS,
   QUOTE_MONEY_STAT_PROPS,
@@ -1971,6 +1977,14 @@ function pickColumnStat(prop: string, method: ColumnStatMethod | '') {
 }
 }
 
 
 const quoteTableWrapRef = ref<HTMLElement | null>(null)
 const quoteTableWrapRef = ref<HTMLElement | null>(null)
+const quoteBelowRef = ref<HTMLElement | null>(null)
+const quoteTableRef = ref<any>(null)
+
+const { tableHeight, recalc: recalcTableHeight } = useListTableViewportHeight({
+  tableEl: quoteTableWrapRef,
+  pagerEl: quoteBelowRef,
+  tableRef: quoteTableRef
+})
 const quoteStatsScrollRef = ref<HTMLElement | null>(null)
 const quoteStatsScrollRef = ref<HTMLElement | null>(null)
 let quoteStatsScrollSyncing = false
 let quoteStatsScrollSyncing = false
 let quoteHeaderResizeObserver: ResizeObserver | null = null
 let quoteHeaderResizeObserver: ResizeObserver | null = null
@@ -3053,6 +3067,7 @@ onMounted(() => {
   setTimeout(() => scheduleSyncStatsColumnWidths(), 150);
   setTimeout(() => scheduleSyncStatsColumnWidths(), 150);
   setTimeout(() => scheduleSyncStatsColumnWidths(), 400);
   setTimeout(() => scheduleSyncStatsColumnWidths(), 400);
   window.addEventListener('resize', scheduleSyncStatsColumnWidths);
   window.addEventListener('resize', scheduleSyncStatsColumnWidths);
+  nextTick(() => recalcTableHeight());
 });
 });
 
 
 onUnmounted(() => {
 onUnmounted(() => {