hookehuyr

feat: 实现 LoadMoreList 动态高度计算并修复双重滚动问题

### 新增功能
- LoadMoreList 组件支持动态高度测量
  - 使用 Taro.createSelectorQuery() 运行时测量
  - 新增 hasFooter prop(TabBar 页面)
  - 新增 extraBottomSpace prop(固定按钮等)
  - 支持响应式监听 props 变化
  - 支持页面生命周期重新测量(useDidShow)
  - 自动处理底部安全区域

### 修复
- 修复 4 个页面的双重滚动问题
  - feedback-list, favorites, material-list, product-center
  - 添加页面容器 height: 100vh 和 overflow: hidden
- 移除所有调试 console.log,保持代码整洁

### 配置更新
- feedback-list: extraBottomSpace=280(固定按钮)
- favorites: hasFooter=false
- material-list: hasFooter=false
- product-center: hasFooter=false
- search: hasFooter=false
- message: hasFooter=false
- week-hot-material: hasFooter=false

### 文档
- CHANGELOG.md: 记录双重滚动问题修复
- lessons-learned.md: 添加坑 5(双重滚动问题)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
...@@ -5,6 +5,57 @@ ...@@ -5,6 +5,57 @@
5 5
6 --- 6 ---
7 7
8 +## [2026-02-08] - 修复 LoadMoreList 页面双重滚动问题
9 +
10 +### 修复
11 +- 修复 4 个使用 LoadMoreList 组件的页面出现双重滚动问题
12 + - 页面级和 scroll-view 都可以滚动,导致用户体验混乱
13 + - `src/pages/feedback-list/index.vue`
14 + - `src/pages/favorites/index.vue`
15 + - `src/pages/material-list/index.vue`
16 + - `src/pages/product-center/index.vue`
17 +- 在页面容器添加 `height: 100vh``overflow: hidden`
18 + - TailwindCSS: `h-screen overflow-hidden`
19 + - Less: `height: 100vh; overflow: hidden;`
20 +
21 +### 问题原因
22 +- 页面容器未设置固定高度(使用 `min-height` 或未设置)
23 +- 页面容器未禁用溢出(缺少 `overflow: hidden`
24 +- 导致页面级和组件级滚动同时生效
25 +
26 +### 解决方案
27 +- 页面容器设置固定高度:`height: 100vh`(或 `h-screen`
28 +- 页面容器禁用溢出:`overflow: hidden`(或 `overflow-hidden`
29 +- 让 LoadMoreList 内部的 scroll-view 处理所有滚动
30 +- 固定元素(导航栏、搜索栏)放在 `#header` 插槽中
31 +
32 +### 文档
33 +-`docs/lessons-learned.md` 中添加"LoadMoreList 页面的双重滚动问题"记录
34 + - 说明问题表现、原因分析、解决方案
35 + - 提供最佳实践和检查清单
36 + - 列出修复的 4 个页面和已正确的 3 个页面
37 +
38 +### 收益
39 +- ✅ 消除双重滚动,提升用户体验
40 +- ✅ 固定元素(导航栏、搜索栏)始终可见
41 +- ✅ 统一 LoadMoreList 页面的滚动行为
42 +- ✅ 为所有使用 LoadMoreList 的页面提供标准模式
43 +
44 +---
45 +
46 +**详细信息**
47 +- **影响文件**:
48 + - `src/pages/feedback-list/index.vue`(修复双重滚动)
49 + - `src/pages/favorites/index.vue`(修复双重滚动)
50 + - `src/pages/material-list/index.vue`(修复双重滚动)
51 + - `src/pages/product-center/index.vue`(修复双重滚动)
52 + - `docs/lessons-learned.md`(添加经验教训)
53 +- **技术栈**: Vue 3, Taro 4, TailwindCSS, Less
54 +- **测试状态**: ✅ 已通过
55 +- **备注**: 共检查 7 个使用 LoadMoreList 的页面,修复 4 个,3 个已正确
56 +
57 +---
58 +
8 ## [2026-02-08] - 修复 LoadMoreList 组件底部 padding 堆叠问题 59 ## [2026-02-08] - 修复 LoadMoreList 组件底部 padding 堆叠问题
9 60
10 ### 修复 61 ### 修复
......
...@@ -583,6 +583,137 @@ padding: [top] [right] [bottom] [left]; ...@@ -583,6 +583,137 @@ padding: [top] [right] [bottom] [left];
583 - **第 1 次**: 在 material-list 页面发现底部 padding 过高 583 - **第 1 次**: 在 material-list 页面发现底部 padding 过高
584 - **教训**: ⚠️ **LESS 嵌套选择器中,修饰符类的属性会与基础类堆叠,需要覆盖整个属性而不是只写子属性** 584 - **教训**: ⚠️ **LESS 嵌套选择器中,修饰符类的属性会与基础类堆叠,需要覆盖整个属性而不是只写子属性**
585 585
586 +### ❌ 坑 5: LoadMoreList 页面的双重滚动问题 ⭐ 2026-02-08 新增
587 +
588 +**问题描述**:
589 +
590 +使用 `LoadMoreList` 组件的页面出现双重滚动问题:
591 +- 整个页面可以滚动
592 +- LoadMoreList 内部的 scroll-view 也可以滚动
593 +- 用户滚动时体验混乱
594 +
595 +**错误代码**:
596 +```vue
597 +<!-- ❌ 页面容器没有限制高度和溢出 -->
598 +<template>
599 + <view class="bg-[#F9FAFB]">
600 + <LoadMoreList
601 + :list="currentList"
602 + :page="currentPage"
603 + @load-more="handleLoadMore"
604 + >
605 + <!-- 列表项 -->
606 + </LoadMoreList>
607 + </view>
608 +</template>
609 +```
610 +
611 +```less
612 +// ❌ 缺少高度和溢出控制
613 +.feedback-list {
614 + min-height: 100vh; // ❌ 使用 min-height 而非 height
615 + // ❌ 缺少 overflow: hidden
616 +}
617 +```
618 +
619 +**错误表现**:
620 +- 页面级和组件级都可以滚动
621 +- 用户滚动时不确定是哪个在滚动
622 +- 固定元素(如导航栏、筛选栏)可能随页面滚动消失
623 +
624 +**原因分析**:
625 +1. **页面容器未限制高度**: 使用 `min-height: 100vh` 或没有设置高度
626 +2. **未禁用页面级溢出**: 缺少 `overflow: hidden`
627 +3. **滚动上下文混乱**: 浏览器无法确定应该滚动哪个容器
628 +
629 +**解决方案**: 在页面容器添加 `height: 100vh` 和 `overflow: hidden`
630 +
631 +```vue
632 +<!-- ✅ 正确:页面容器固定高度并禁用溢出 -->
633 +<template>
634 + <!-- ✅ TailwindCSS 方式 -->
635 + <view class="h-screen overflow-hidden bg-[#F9FAFB]">
636 + <LoadMoreList
637 + :list="currentList"
638 + :page="currentPage"
639 + @load-more="handleLoadMore"
640 + >
641 + <!-- 列表项 -->
642 + </LoadMoreList>
643 + </view>
644 +</template>
645 +```
646 +
647 +```less
648 +// ✅ Less 方式
649 +.feedback-list {
650 + height: 100vh; // ✅ 固定高度
651 + overflow: hidden; // ✅ 禁用页面级滚动
652 +}
653 +```
654 +
655 +**关键点**:
656 +- ✅ 页面容器必须设置 `height: 100vh`(固定高度)
657 +- ✅ 页面容器必须设置 `overflow: hidden`(禁用页面级滚动)
658 +- ✅ 让 LoadMoreList 内部的 scroll-view 处理所有滚动
659 +- ✅ 如果页面有固定顶部(如导航栏、搜索栏),放在 LoadMoreList 的 `#header` 插槽中
660 +
661 +**修复的页面**(共 4 个):
662 +1. `src/pages/feedback-list/index.vue`
663 +2. `src/pages/favorites/index.vue`
664 +3. `src/pages/material-list/index.vue`
665 +4. `src/pages/product-center/index.vue`
666 +
667 +**已正确的页面**(共 3 个):
668 +1. `src/pages/search/index.vue`(已正确配置)
669 +2. `src/pages/message/index.vue`(LoadMoreList 是根元素)
670 +3. `src/pages/week-hot-material/index.vue`(LoadMoreList 是根元素)
671 +
672 +**最佳实践**:
673 +```vue
674 +<!-- ✅ 推荐的 LoadMoreList 页面结构 -->
675 +<template>
676 + <!-- 外层容器:固定高度,禁用溢出 -->
677 + <view class="h-screen overflow-hidden bg-[#F9FAFB]">
678 + <LoadMoreList
679 + :list="currentList"
680 + :page="currentPage"
681 + :page-size="pageSize"
682 + :has-more="hasMore"
683 + :loading="loading"
684 + :loading-more="loadingMore"
685 + key-field="id"
686 + @load-more="handleLoadMore"
687 + >
688 + <!-- 固定头部(可选) -->
689 + <template #header>
690 + <view class="sticky top-0 bg-white z-10">
691 + <NavHeader title="页面标题" />
692 + <SearchBar v-model="searchValue" />
693 + </view>
694 + </template>
695 +
696 + <!-- 列表项 -->
697 + <template #item="{ item }">
698 + <ProductCard :product="item" />
699 + </template>
700 + </LoadMoreList>
701 + </view>
702 +</template>
703 +```
704 +
705 +**检查清单**:
706 +使用 LoadMoreList 组件时,确认:
707 +- [ ] 页面容器设置了 `height: 100vh`(或 `h-screen`)
708 +- [ ] 页面容器设置了 `overflow: hidden`(或 `overflow-hidden`)
709 +- [ ] 固定元素(导航栏、搜索栏)放在 `#header` 插槽中
710 +- [ ] 只有 LoadMoreList 内部的 scroll-view 可以滚动
711 +
712 +**适用场景**:
713 +- ✅ 所有使用 LoadMoreList 组件的分页列表页面
714 +- ✅ 需要固定顶部元素的滚动页面
715 +- ✅ 需要防止双重滚动的任何场景
716 +
586 --- 717 ---
587 718
588 ### ⚠️ 双设计宽度系统 719 ### ⚠️ 双设计宽度系统
......
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
31 <template> 31 <template>
32 <view class="load-more-list" :class="{ 'has-header': showHeader }"> 32 <view class="load-more-list" :class="{ 'has-header': showHeader }">
33 <!-- 可选固定头部 --> 33 <!-- 可选固定头部 -->
34 - <view v-if="showHeader" class="load-more-header"> 34 + <view v-if="showHeader" class="load-more-header" ref="headerRef">
35 <slot name="header"></slot> 35 <slot name="header"></slot>
36 </view> 36 </view>
37 37
...@@ -127,8 +127,9 @@ ...@@ -127,8 +127,9 @@
127 </template> 127 </template>
128 128
129 <script setup> 129 <script setup>
130 -import { computed } from 'vue' 130 +import { computed, ref, onMounted, watch, nextTick as vueNextTick } from 'vue'
131 -import { usePullDownRefresh, stopPullDownRefresh } from '@tarojs/taro' 131 +import Taro from '@tarojs/taro'
132 +import { usePullDownRefresh, stopPullDownRefresh, useDidShow } from '@tarojs/taro'
132 133
133 /** 134 /**
134 * 通用加载更多列表组件 135 * 通用加载更多列表组件
...@@ -298,6 +299,28 @@ const props = defineProps({ ...@@ -298,6 +299,28 @@ const props = defineProps({
298 enableScrollLoad: { 299 enableScrollLoad: {
299 type: Boolean, 300 type: Boolean,
300 default: true 301 default: true
302 + },
303 +
304 + /**
305 + * 页面是否有底部导航(TabBar 等)
306 + * @type {boolean}
307 + * @default false
308 + * @description 如果页面有底部导航(如 TabBar),设为 true 会自动预留底部空间
309 + */
310 + hasFooter: {
311 + type: Boolean,
312 + default: false
313 + },
314 +
315 + /**
316 + * 额外底部空间(rpx)
317 + * @type {number}
318 + * @default 0
319 + * @description 额外预留的底部空间(单位:rpx),用于固定按钮等非标准底部元素
320 + */
321 + extraBottomSpace: {
322 + type: Number,
323 + default: 0
301 } 324 }
302 }) 325 })
303 326
...@@ -323,17 +346,118 @@ const emit = defineEmits({ ...@@ -323,17 +346,118 @@ const emit = defineEmits({
323 const displayList = computed(() => props.list || []) 346 const displayList = computed(() => props.list || [])
324 347
325 /** 348 /**
349 + * 动态测量的头部高度(px)
350 + * @description 使用 Taro.createSelectorQuery() 在运行时测量实际头部高度
351 + */
352 +const headerHeight = ref(0)
353 +
354 +/**
355 + * 动态测量的底部导航高度(px)
356 + * @description 如果页面有 TabBar 等底部导航,预留底部空间
357 + */
358 +const footerHeight = ref(0)
359 +
360 +/**
361 + * 是否已完成高度测量
362 + * @description 避免重复测量
363 + */
364 +const heightMeasured = ref(false)
365 +
366 +/**
367 + * 测量头部和底部高度
368 + * @description 使用 Taro.createSelectorQuery() 动态测量页面元素高度
369 + */
370 +const measureHeaderHeight = async () => {
371 + if (heightMeasured.value) return
372 +
373 + try {
374 + // 获取系统信息
375 + const systemInfo = await Taro.getSystemInfo()
376 + const { windowHeight, screenHeight, safeArea } = systemInfo
377 +
378 + // 计算底部安全区域高度
379 + const bottomSafeArea = screenHeight - safeArea.bottom
380 +
381 + /**
382 + * 计算底部高度
383 + * @description 处理底部导航栏、安全区域和额外底部空间
384 + */
385 + const calculateFooterHeight = () => {
386 + let totalFooterHeight = 0
387 +
388 + if (props.hasFooter) {
389 + // 假设 TabBar 高度约为 50px(小程序标准 TabBar 高度)
390 + totalFooterHeight = 50 + bottomSafeArea
391 + } else {
392 + // 无底部导航,只考虑安全区域
393 + totalFooterHeight = bottomSafeArea
394 + }
395 +
396 + // 添加额外底部空间(将 rpx 转换为 px:750rpx 设计宽度标准,rpx / 2 = px)
397 + if (props.extraBottomSpace > 0) {
398 + const extraBottomSpacePx = props.extraBottomSpace / 2
399 + totalFooterHeight += extraBottomSpacePx
400 + }
401 +
402 + footerHeight.value = totalFooterHeight
403 + heightMeasured.value = true
404 + }
405 +
406 + let totalHeaderHeight = 0
407 +
408 + // 如果有头部,测量头部实际高度
409 + if (props.showHeader) {
410 + await vueNextTick() // 等待 DOM 渲染完成
411 +
412 + const query = Taro.createSelectorQuery()
413 + query.select('.load-more-header').boundingClientRect()
414 + query.exec((res) => {
415 + if (res && res[0]) {
416 + totalHeaderHeight = res[0].height
417 + } else {
418 + // 如果测量失败,使用默认估算值
419 + totalHeaderHeight = 178 // 356rpx ≈ 178px
420 + }
421 +
422 + headerHeight.value = totalHeaderHeight
423 + calculateFooterHeight() // 现在可以正确调用了
424 + })
425 + } else {
426 + // 无头部
427 + calculateFooterHeight()
428 + }
429 + } catch (err) {
430 + console.error('[LoadMoreList] 测量高度失败:', err)
431 + // 测量失败时使用默认值
432 + headerHeight.value = props.showHeader ? 178 : 0
433 + footerHeight.value = props.hasFooter ? 80 : 34 // 约50px TabBar + 30px安全区域
434 + heightMeasured.value = true
435 + }
436 +}
437 +
438 +// 组件挂载后测量高度
439 +onMounted(() => {
440 + measureHeaderHeight()
441 +})
442 +
443 +/**
326 * scroll-view 高度 444 * scroll-view 高度
327 - * @description 动态计算 scroll-view 的高度 445 + * @description 动态计算 scroll-view 的高度,使用运行时测量的实际高度
328 */ 446 */
329 const scrollHeight = computed(() => { 447 const scrollHeight = computed(() => {
330 - // 头部高度估算(rpx 单位) 448 + if (!heightMeasured.value) {
331 - // NavHeader(约88rpx) + SearchBar(约120rpx) + Tabs(约88rpx) + ResultCount(约60rpx) ≈ 356rpx 449 + // 未完成测量时,使用默认估算值
332 - const headerHeight = props.showHeader ? '356rpx' : '0rpx' 450 + const defaultHeaderHeight = props.showHeader ? '178px' : '0px' // 356rpx ≈ 178px
451 + const defaultFooterHeight = props.hasFooter ? '80px' : '34px'
452 + return `calc(100vh - ${defaultHeaderHeight} - ${defaultFooterHeight})`
453 + }
333 454
334 - // 使用 calc() 计算剩余高度 455 + // 使用测量到的实际高度(转换为 px)
335 - // 100vh 是视口高度,减去头部高度 456 + const headerPx = headerHeight.value
336 - return `calc(100vh - ${headerHeight})` 457 + const footerPx = footerHeight.value
458 +
459 + // 计算滚动高度:视口高度 - 头部高度 - 底部高度
460 + return `calc(100vh - ${headerPx}px - ${footerPx}px)`
337 }) 461 })
338 462
339 /** 463 /**
...@@ -356,20 +480,11 @@ function getAnimationDelay(index) { ...@@ -356,20 +480,11 @@ function getAnimationDelay(index) {
356 * @description 当 scroll-view 滚动到底部时触发 480 * @description 当 scroll-view 滚动到底部时触发
357 */ 481 */
358 const handleScrollToLower = () => { 482 const handleScrollToLower = () => {
359 - console.log('[LoadMoreList] scroll-view 触底事件触发', {
360 - loadingMore: props.loadingMore,
361 - loading: props.loading,
362 - hasMore: props.hasMore,
363 - page: props.page
364 - })
365 -
366 // 如果正在加载或没有更多数据,不执行 483 // 如果正在加载或没有更多数据,不执行
367 if (props.loadingMore || props.loading || !props.hasMore) { 484 if (props.loadingMore || props.loading || !props.hasMore) {
368 - console.log('[LoadMoreList] 跳过加载:正在加载或没有更多数据')
369 return 485 return
370 } 486 }
371 487
372 - console.log('[LoadMoreList] 触底加载更多,当前页:', props.page, '下一页:', props.page + 1)
373 const nextPage = props.page + 1 488 const nextPage = props.page + 1
374 emit('load-more', nextPage) 489 emit('load-more', nextPage)
375 } 490 }
...@@ -380,11 +495,28 @@ const handleScrollToLower = () => { ...@@ -380,11 +495,28 @@ const handleScrollToLower = () => {
380 */ 495 */
381 if (props.enablePullDownRefresh) { 496 if (props.enablePullDownRefresh) {
382 usePullDownRefresh(() => { 497 usePullDownRefresh(() => {
383 - console.log('[LoadMoreList] 下拉刷新')
384 emit('refresh') 498 emit('refresh')
385 stopPullDownRefresh() 499 stopPullDownRefresh()
386 }) 500 })
387 } 501 }
502 +
503 +/**
504 + * 监听 showHeader、hasFooter 和 extraBottomSpace 变化
505 + * @description 当头部或底部配置变化时,重新测量高度
506 + */
507 +watch([() => props.showHeader, () => props.hasFooter, () => props.extraBottomSpace], () => {
508 + heightMeasured.value = false
509 + measureHeaderHeight()
510 +})
511 +
512 +/**
513 + * 页面显示时重新测量高度
514 + * @description 确保在页面显示时高度计算正确(处理从其他页面返回的情况)
515 + */
516 +useDidShow(() => {
517 + heightMeasured.value = false
518 + measureHeaderHeight()
519 +})
388 </script> 520 </script>
389 521
390 <style lang="less"> 522 <style lang="less">
...@@ -392,10 +524,6 @@ if (props.enablePullDownRefresh) { ...@@ -392,10 +524,6 @@ if (props.enablePullDownRefresh) {
392 background-color: #F9FAFB; 524 background-color: #F9FAFB;
393 height: 100vh; 525 height: 100vh;
394 overflow: hidden; 526 overflow: hidden;
395 -
396 - &.has-header {
397 - padding-bottom: calc(160rpx + env(safe-area-inset-bottom));
398 - }
399 } 527 }
400 528
401 .load-more-header { 529 .load-more-header {
...@@ -419,8 +547,9 @@ if (props.enablePullDownRefresh) { ...@@ -419,8 +547,9 @@ if (props.enablePullDownRefresh) {
419 &.scrollable { 547 &.scrollable {
420 // 高度通过 :style 动态绑定 548 // 高度通过 :style 动态绑定
421 box-sizing: border-box; 549 box-sizing: border-box;
422 - // 重置基础 padding,只保留顶部和左右,底部使用特殊计算值 550 + // 重置基础 padding,只保留顶部和左右,底部预留空间 + 安全区域
423 - padding: 32rpx 32rpx calc(160rpx + env(safe-area-inset-bottom)); 551 + // 确保在所有设备(包括刘海屏)上"没有更多了"文字都能完整显示
552 + padding: 32rpx 32rpx calc(env(safe-area-inset-bottom));
424 } 553 }
425 } 554 }
426 555
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
3 * @Description: 我的收藏 - 使用 LoadMoreList 组件重构版本 3 * @Description: 我的收藏 - 使用 LoadMoreList 组件重构版本
4 --> 4 -->
5 <template> 5 <template>
6 - <view class="h-screen bg-gray-50 flex flex-col"> 6 + <view class="h-screen bg-gray-50 flex flex-col overflow-hidden">
7 <view class="bg-gray-50 z-10"> 7 <view class="bg-gray-50 z-10">
8 <NavHeader title="我的收藏" /> 8 <NavHeader title="我的收藏" />
9 </view> 9 </view>
...@@ -17,6 +17,8 @@ ...@@ -17,6 +17,8 @@
17 :loading="loading" 17 :loading="loading"
18 :loading-more="loadingMore" 18 :loading-more="loadingMore"
19 key-field="meta_id" 19 key-field="meta_id"
20 + :show-header="false"
21 + :has-footer="false"
20 @load-more="handleLoadMore" 22 @load-more="handleLoadMore"
21 > 23 >
22 <!-- 列表项 --> 24 <!-- 列表项 -->
......
...@@ -15,6 +15,9 @@ ...@@ -15,6 +15,9 @@
15 :loading="loading" 15 :loading="loading"
16 :loading-more="loadingMore" 16 :loading-more="loadingMore"
17 key-field="id" 17 key-field="id"
18 + :show-header="false"
19 + :has-footer="false"
20 + :extra-bottom-space="280"
18 @load-more="handleLoadMore" 21 @load-more="handleLoadMore"
19 > 22 >
20 <!-- 列表项 --> 23 <!-- 列表项 -->
...@@ -314,9 +317,10 @@ onMounted(() => { ...@@ -314,9 +317,10 @@ onMounted(() => {
314 317
315 <style lang="less"> 318 <style lang="less">
316 .feedback-list { 319 .feedback-list {
317 - min-height: 100vh; 320 + height: 100vh;
321 + overflow: hidden;
318 background-color: #f9fafb; 322 background-color: #f9fafb;
319 - padding-bottom: 160rpx; // 为固定按钮留出空间 323 + // padding-bottom: 160rpx; // ❌ 已移除:LoadMoreList 已经通过 extraBottomSpace=114 预留了 57px 空间
320 324
321 .feedback-item { 325 .feedback-item {
322 background: white; 326 background: white;
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
3 * @Description: 资料/文档列表页 - 使用 LoadMoreList 组件重构版本 3 * @Description: 资料/文档列表页 - 使用 LoadMoreList 组件重构版本
4 --> 4 -->
5 <template> 5 <template>
6 - <view class="bg-[#F9FAFB]"> 6 + <view class="h-screen overflow-hidden bg-[#F9FAFB]">
7 <LoadMoreList 7 <LoadMoreList
8 :list="currentList" 8 :list="currentList"
9 :page="currentPage" 9 :page="currentPage"
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
12 :loading="loading" 12 :loading="loading"
13 :loading-more="loadingMore" 13 :loading-more="loadingMore"
14 key-field="meta_id" 14 key-field="meta_id"
15 + :has-footer="false"
15 @load-more="handleLoadMore" 16 @load-more="handleLoadMore"
16 > 17 >
17 <!-- 头部:导航 + 搜索 + Tabs --> 18 <!-- 头部:导航 + 搜索 + Tabs -->
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
12 :loading-more="loadingMore" 12 :loading-more="loadingMore"
13 :enable-pull-down-refresh="true" 13 :enable-pull-down-refresh="true"
14 key-field="id" 14 key-field="id"
15 + :has-footer="false"
15 @load-more="handleLoadMore" 16 @load-more="handleLoadMore"
16 @refresh="handleRefresh" 17 @refresh="handleRefresh"
17 > 18 >
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
3 * @Description: 产品中心 - 使用 LoadMoreList 组件重构版本 3 * @Description: 产品中心 - 使用 LoadMoreList 组件重构版本
4 --> 4 -->
5 <template> 5 <template>
6 - <view class="bg-[#F9FAFB]"> 6 + <view class="h-screen overflow-hidden bg-[#F9FAFB]">
7 <!-- 计划书弹窗 --> 7 <!-- 计划书弹窗 -->
8 <view v-if="showPlanPopup && selectedProduct"> 8 <view v-if="showPlanPopup && selectedProduct">
9 <PlanFormContainer 9 <PlanFormContainer
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
22 :loading="loading" 22 :loading="loading"
23 :loading-more="loadingMore" 23 :loading-more="loadingMore"
24 key-field="id" 24 key-field="id"
25 + :has-footer="false"
25 @load-more="handleLoadMore" 26 @load-more="handleLoadMore"
26 > 27 >
27 <!-- 头部:导航 + 搜索 + Tabs --> 28 <!-- 头部:导航 + 搜索 + Tabs -->
...@@ -30,7 +31,7 @@ ...@@ -30,7 +31,7 @@
30 <NavHeader title="产品中心" /> 31 <NavHeader title="产品中心" />
31 32
32 <!-- Search Bar --> 33 <!-- Search Bar -->
33 - <view class="px-[24rpx] py-[16rpx] bg-white"> 34 + <view class="px-[24rpx] py-[16rpx] bg-[#F9FAFB]">
34 <SearchBar 35 <SearchBar
35 v-model="searchValue" 36 v-model="searchValue"
36 placeholder="搜索产品名称..." 37 placeholder="搜索产品名称..."
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
14 :loading-more="loadingMore" 14 :loading-more="loadingMore"
15 :show-header="true" 15 :show-header="true"
16 :enable-scroll-load="shouldEnableScrollLoad" 16 :enable-scroll-load="shouldEnableScrollLoad"
17 + :has-footer="false"
17 key-field="id" 18 key-field="id"
18 @load-more="handleLoadMore" 19 @load-more="handleLoadMore"
19 > 20 >
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
11 :loading="loading" 11 :loading="loading"
12 :loading-more="loadingMore" 12 :loading-more="loadingMore"
13 key-field="meta_id" 13 key-field="meta_id"
14 + :has-footer="false"
14 @load-more="handleLoadMore" 15 @load-more="handleLoadMore"
15 > 16 >
16 <!-- 头部 --> 17 <!-- 头部 -->
......