fix(material): 修复滚动加载无法触发的问题
移除 h-screen flex-col 布局,改用页面级滚动 - NavHeader 和搜索栏使用 sticky top-0 固定 - 列表容器使用页面级滚动(移除 overflow-y-auto) - 删除 mock 数据相关代码 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Showing
4 changed files
with
21 additions
and
207 deletions
| 1 | +/* | ||
| 2 | + * @Date: 2026-02-06 18:10:17 | ||
| 3 | + * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 4 | + * @LastEditTime: 2026-02-06 18:12:50 | ||
| 5 | + * @FilePath: /manulife-weapp/src/api/file.js | ||
| 6 | + * @Description: 文件相关 API | ||
| 7 | + */ | ||
| 1 | import { fn, fetch } from '@/api/fn'; | 8 | import { fn, fetch } from '@/api/fn'; |
| 2 | 9 | ||
| 3 | const Api = { | 10 | const Api = { |
| ... | @@ -62,7 +69,9 @@ const Api = { | ... | @@ -62,7 +69,9 @@ const Api = { |
| 62 | * }; | 69 | * }; |
| 63 | * }>} | 70 | * }>} |
| 64 | */ | 71 | */ |
| 65 | -export const fileListAPI = (params) => fn(fetch.get(Api.FileList, params)); | 72 | +export const fileListAPI = (params) => { |
| 73 | + return fn(fetch.get(Api.FileList, params)); | ||
| 74 | +}; | ||
| 66 | 75 | ||
| 67 | /** | 76 | /** |
| 68 | * @description 本周热门资料 | 77 | * @description 本周热门资料 |
| ... | @@ -86,4 +95,6 @@ export const fileListAPI = (params) => fn(fetch.get(Api.FileList, params)); | ... | @@ -86,4 +95,6 @@ export const fileListAPI = (params) => fn(fetch.get(Api.FileList, params)); |
| 86 | * }; | 95 | * }; |
| 87 | * }>} | 96 | * }>} |
| 88 | */ | 97 | */ |
| 89 | -export const weekHotAPI = (params) => fn(fetch.get(Api.WeekHot, params)); | 98 | +export const weekHotAPI = (params) => { |
| 99 | + return fn(fetch.get(Api.WeekHot, params)); | ||
| 100 | +}; | ... | ... |
| 1 | import { fn, fetch } from '@/api/fn'; | 1 | import { fn, fetch } from '@/api/fn'; |
| 2 | -import { mockListAPI, mockDetailAPI } from './mock/product'; | ||
| 3 | - | ||
| 4 | -// ⚠️ Mock 数据开关 - 设置为 true 使用 mock 数据,false 使用真实 API | ||
| 5 | -const USE_MOCK_DATA = false; | ||
| 6 | 2 | ||
| 7 | const Api = { | 3 | const Api = { |
| 8 | Detail: '/srv/?a=get_product&t=detail', | 4 | Detail: '/srv/?a=get_product&t=detail', |
| ... | @@ -50,10 +46,6 @@ const Api = { | ... | @@ -50,10 +46,6 @@ const Api = { |
| 50 | * }>} | 46 | * }>} |
| 51 | */ | 47 | */ |
| 52 | export const detailAPI = (params) => { | 48 | export const detailAPI = (params) => { |
| 53 | - // 如果开启 Mock 数据,返回 mock 数据 | ||
| 54 | - if (USE_MOCK_DATA) { | ||
| 55 | - return mockDetailAPI(params); | ||
| 56 | - } | ||
| 57 | return fn(fetch.get(Api.Detail, params)); | 49 | return fn(fetch.get(Api.Detail, params)); |
| 58 | }; | 50 | }; |
| 59 | 51 | ||
| ... | @@ -98,9 +90,5 @@ export const detailAPI = (params) => { | ... | @@ -98,9 +90,5 @@ export const detailAPI = (params) => { |
| 98 | * }>} | 90 | * }>} |
| 99 | */ | 91 | */ |
| 100 | export const listAPI = (params) => { | 92 | export const listAPI = (params) => { |
| 101 | - // 如果开启 Mock 数据,返回 mock 数据 | ||
| 102 | - if (USE_MOCK_DATA) { | ||
| 103 | - return mockListAPI(params); | ||
| 104 | - } | ||
| 105 | return fn(fetch.get(Api.List, params)); | 93 | return fn(fetch.get(Api.List, params)); |
| 106 | }; | 94 | }; | ... | ... |
src/api/mock/product.js
deleted
100644 → 0
| 1 | -/** | ||
| 2 | - * @description Mock 数据 - 产品中心 | ||
| 3 | - * @note 用于测试滚动加载更多功能 | ||
| 4 | - */ | ||
| 5 | - | ||
| 6 | -// Mock 分类数据 | ||
| 7 | -export const mockCategories = [ | ||
| 8 | - { id: 1, name: '寿险' }, | ||
| 9 | - { id: 2, name: '健康险' }, | ||
| 10 | - { id: 3, name: '意外险' }, | ||
| 11 | - { id: 4, name: '年金险' }, | ||
| 12 | - { id: 5, name: '重疾险' } | ||
| 13 | -] | ||
| 14 | - | ||
| 15 | -// Mock 标签数据 | ||
| 16 | -const mockTags = [ | ||
| 17 | - { id: 1, name: '热销', bg_color: '#FEF3C7', text_color: '#92400E' }, | ||
| 18 | - { id: 2, name: '新品', bg_color: '#DBEAFE', text_color: '#1E40AF' }, | ||
| 19 | - { id: 3, name: '推荐', bg_color: '#D1FAE5', text_color: '#065F46' }, | ||
| 20 | - { id: 4, name: '限时', bg_color: '#FEE2E2', text_color: '#991B1B' } | ||
| 21 | -] | ||
| 22 | - | ||
| 23 | -// 生成单个产品数据 | ||
| 24 | -const generateProduct = (id) => { | ||
| 25 | - const recommendTypes = ['normal', 'hot'] | ||
| 26 | - const recommend = recommendTypes[Math.floor(Math.random() * recommendTypes.length)] | ||
| 27 | - | ||
| 28 | - // 随机选择 1-2 个标签 | ||
| 29 | - const tagCount = Math.floor(Math.random() * 2) + 1 | ||
| 30 | - const tags = [] | ||
| 31 | - for (let i = 0; i < tagCount; i++) { | ||
| 32 | - const tag = mockTags[Math.floor(Math.random() * mockTags.length)] | ||
| 33 | - if (!tags.find(t => t.id === tag.id)) { | ||
| 34 | - tags.push(tag) | ||
| 35 | - } | ||
| 36 | - } | ||
| 37 | - | ||
| 38 | - // 随机选择 1-2 个分类 | ||
| 39 | - const categoryCount = Math.floor(Math.random() * 2) + 1 | ||
| 40 | - const categories = [] | ||
| 41 | - for (let i = 0; i < categoryCount; i++) { | ||
| 42 | - const category = mockCategories[Math.floor(Math.random() * mockCategories.length)] | ||
| 43 | - if (!categories.find(c => c.id === category.id)) { | ||
| 44 | - categories.push(category) | ||
| 45 | - } | ||
| 46 | - } | ||
| 47 | - | ||
| 48 | - return { | ||
| 49 | - id: id, | ||
| 50 | - product_name: `测试产品 ${id} - ${categories.map(c => c.name).join('+')}`, | ||
| 51 | - recommend: recommend, | ||
| 52 | - form_sn: `product_form_${id}`, | ||
| 53 | - created_time: '2025-12-01 12:00:00', | ||
| 54 | - categories: categories, | ||
| 55 | - tags: tags, | ||
| 56 | - // 使用 Lorem Picsum 随机图片服务(基于产品 ID 确保图片固定) | ||
| 57 | - cover_image: `https://picsum.photos/300/200?random=${id}` | ||
| 58 | - } | ||
| 59 | -} | ||
| 60 | - | ||
| 61 | -// 生成产品列表 | ||
| 62 | -const generateProductList = (page, limit, cid = null) => { | ||
| 63 | - const start = page * limit | ||
| 64 | - const end = start + limit | ||
| 65 | - | ||
| 66 | - // 如果指定了分类,只返回该分类的产品 | ||
| 67 | - let filteredProducts = [] | ||
| 68 | - if (cid) { | ||
| 69 | - // 为每个分类生成固定数量的产品 | ||
| 70 | - const categoryProducts = [] | ||
| 71 | - for (let i = 1; i <= 50; i++) { | ||
| 72 | - const product = generateProduct(i) | ||
| 73 | - // 强制该产品属于指定分类 | ||
| 74 | - product.categories = mockCategories.find(c => String(c.id) === String(cid)) | ||
| 75 | - ? [mockCategories.find(c => String(c.id) === String(cid))] | ||
| 76 | - : [{ id: parseInt(cid), name: '测试分类' }] | ||
| 77 | - categoryProducts.push(product) | ||
| 78 | - } | ||
| 79 | - filteredProducts = categoryProducts | ||
| 80 | - } else { | ||
| 81 | - // 全部产品 | ||
| 82 | - for (let i = 1; i <= 100; i++) { | ||
| 83 | - filteredProducts.push(generateProduct(i)) | ||
| 84 | - } | ||
| 85 | - } | ||
| 86 | - | ||
| 87 | - const total = filteredProducts.length | ||
| 88 | - const list = filteredProducts.slice(start, end) | ||
| 89 | - | ||
| 90 | - return { | ||
| 91 | - list, | ||
| 92 | - total, | ||
| 93 | - hasMore: end < total | ||
| 94 | - } | ||
| 95 | -} | ||
| 96 | - | ||
| 97 | -/** | ||
| 98 | - * Mock 产品列表 API | ||
| 99 | - * @param {Object} params 请求参数 | ||
| 100 | - * @param {string} params.page 页码(从 0 开始) | ||
| 101 | - * @param {string} params.limit 每页数量 | ||
| 102 | - * @param {string} params.cid 分类 ID(可选) | ||
| 103 | - * @param {string} params.keyword 搜索关键词(可选) | ||
| 104 | - * @returns {Promise} 模拟 API 响应 | ||
| 105 | - */ | ||
| 106 | -export const mockListAPI = (params) => { | ||
| 107 | - return new Promise((resolve) => { | ||
| 108 | - // 模拟网络延迟(300-800ms) | ||
| 109 | - const delay = Math.floor(Math.random() * 500) + 300 | ||
| 110 | - | ||
| 111 | - setTimeout(() => { | ||
| 112 | - const page = parseInt(params.page) || 0 | ||
| 113 | - const limit = parseInt(params.limit) || 10 | ||
| 114 | - const cid = params.cid || null | ||
| 115 | - const keyword = params.keyword || '' | ||
| 116 | - | ||
| 117 | - let result = generateProductList(page, limit, cid) | ||
| 118 | - | ||
| 119 | - // 如果有搜索关键词,过滤产品 | ||
| 120 | - if (keyword) { | ||
| 121 | - result.list = result.list.filter(p => | ||
| 122 | - p.product_name.includes(keyword) | ||
| 123 | - ) | ||
| 124 | - // 搜索时重新计算总数 | ||
| 125 | - result.total = result.list.length + Math.floor(Math.random() * 20) | ||
| 126 | - } | ||
| 127 | - | ||
| 128 | - resolve({ | ||
| 129 | - code: 1, | ||
| 130 | - msg: 'success', | ||
| 131 | - data: { | ||
| 132 | - categories: mockCategories, | ||
| 133 | - list: result.list, | ||
| 134 | - total: result.total | ||
| 135 | - } | ||
| 136 | - }) | ||
| 137 | - }, delay) | ||
| 138 | - }) | ||
| 139 | -} | ||
| 140 | - | ||
| 141 | -/** | ||
| 142 | - * Mock 产品详情 API | ||
| 143 | - * @param {Object} params 请求参数 | ||
| 144 | - * @param {string} params.i 产品 ID | ||
| 145 | - * @returns {Promise} 模拟 API 响应 | ||
| 146 | - */ | ||
| 147 | -export const mockDetailAPI = (params) => { | ||
| 148 | - return new Promise((resolve) => { | ||
| 149 | - const delay = Math.floor(Math.random() * 500) + 300 | ||
| 150 | - | ||
| 151 | - setTimeout(() => { | ||
| 152 | - const id = parseInt(params.i) || 1 | ||
| 153 | - const product = generateProduct(id) | ||
| 154 | - | ||
| 155 | - // 添加额外的详情字段 | ||
| 156 | - product.product_description = `这是产品 ${id} 的详细描述。\n\n产品特点:\n1. 保障全面\n2. 灵活配置\n3. 理赔便捷` | ||
| 157 | - product.documents = [ | ||
| 158 | - { | ||
| 159 | - file_url: 'https://example.com/file1.pdf', | ||
| 160 | - file_name: '产品条款.pdf', | ||
| 161 | - file_size: '1024000', | ||
| 162 | - file_size_formatted: '1.0 MB' | ||
| 163 | - }, | ||
| 164 | - { | ||
| 165 | - file_url: 'https://example.com/file2.pdf', | ||
| 166 | - file_name: '产品说明.pdf', | ||
| 167 | - file_size: '512000', | ||
| 168 | - file_size_formatted: '512 KB' | ||
| 169 | - } | ||
| 170 | - ] | ||
| 171 | - product.status = 'active' | ||
| 172 | - product.created_by = 1 | ||
| 173 | - product.updated_by = 1 | ||
| 174 | - product.updated_time = '2025-12-01 12:00:00' | ||
| 175 | - | ||
| 176 | - resolve({ | ||
| 177 | - code: 1, | ||
| 178 | - msg: 'success', | ||
| 179 | - data: product | ||
| 180 | - }) | ||
| 181 | - }, delay) | ||
| 182 | - }) | ||
| 183 | -} |
| ... | @@ -3,8 +3,9 @@ | ... | @@ -3,8 +3,9 @@ |
| 3 | * @Description: 资料列表页 - 已改造为 NutTabs 版本 | 3 | * @Description: 资料列表页 - 已改造为 NutTabs 版本 |
| 4 | --> | 4 | --> |
| 5 | <template> | 5 | <template> |
| 6 | - <view class="h-screen bg-[#F9FAFB] flex flex-col"> | 6 | + <view class="bg-[#F9FAFB]"> |
| 7 | - <view class="bg-[#F9FAFB] z-10"> | 7 | + <!-- 固定在顶部的导航和搜索 --> |
| 8 | + <view class="bg-[#F9FAFB] sticky top-0 z-10"> | ||
| 8 | <NavHeader :title="pageTitle" /> | 9 | <NavHeader :title="pageTitle" /> |
| 9 | 10 | ||
| 10 | <view class="px-[32rpx] mt-[32rpx] mb-[24rpx]"> | 11 | <view class="px-[32rpx] mt-[32rpx] mb-[24rpx]"> |
| ... | @@ -18,10 +19,7 @@ | ... | @@ -18,10 +19,7 @@ |
| 18 | :show-clear="true" | 19 | :show-clear="true" |
| 19 | /> | 20 | /> |
| 20 | </view> | 21 | </view> |
| 21 | - </view> | ||
| 22 | 22 | ||
| 23 | - <!-- Tabs Container --> | ||
| 24 | - <view class="flex-1 min-h-0 flex flex-col"> | ||
| 25 | <!-- 动态显示 Tabs(仅在有分类时显示) --> | 23 | <!-- 动态显示 Tabs(仅在有分类时显示) --> |
| 26 | <nut-tabs v-if="hasCategories" v-model="activeTabId"> | 24 | <nut-tabs v-if="hasCategories" v-model="activeTabId"> |
| 27 | <!-- 自定义标签栏 --> | 25 | <!-- 自定义标签栏 --> |
| ... | @@ -41,12 +39,13 @@ | ... | @@ -41,12 +39,13 @@ |
| 41 | </view> | 39 | </view> |
| 42 | </template> | 40 | </template> |
| 43 | </nut-tabs> | 41 | </nut-tabs> |
| 42 | + </view> | ||
| 44 | 43 | ||
| 45 | - <!-- 列表容器(独立于 nut-tab-pane) --> | 44 | + <!-- 列表容器 - 页面级滚动 --> |
| 46 | <view | 45 | <view |
| 47 | v-if="listVisible" | 46 | v-if="listVisible" |
| 48 | :key="listRenderKey" | 47 | :key="listRenderKey" |
| 49 | - class="flex-1 min-h-0 overflow-y-auto px-[32rpx] pb-[calc(160rpx+env(safe-area-inset-bottom))] box-border" | 48 | + class="px-[32rpx] pb-[calc(160rpx+env(safe-area-inset-bottom))] box-border" |
| 50 | > | 49 | > |
| 51 | <view class="flex flex-col gap-[24rpx]"> | 50 | <view class="flex flex-col gap-[24rpx]"> |
| 52 | <view v-for="(item, index) in currentList" :key="index" | 51 | <view v-for="(item, index) in currentList" :key="index" |
| ... | @@ -112,7 +111,6 @@ | ... | @@ -112,7 +111,6 @@ |
| 112 | </view> | 111 | </view> |
| 113 | </view> | 112 | </view> |
| 114 | </view> | 113 | </view> |
| 115 | - </view> | ||
| 116 | </template> | 114 | </template> |
| 117 | 115 | ||
| 118 | <script setup> | 116 | <script setup> |
| ... | @@ -282,9 +280,9 @@ const fetchMaterialList = async (params = {}, isLoadMore = false) => { | ... | @@ -282,9 +280,9 @@ const fetchMaterialList = async (params = {}, isLoadMore = false) => { |
| 282 | loading.value = true | 280 | loading.value = true |
| 283 | } | 281 | } |
| 284 | 282 | ||
| 285 | - // console.log('[Material List] 请求参数:', params) | 283 | + console.log('[Material List] 请求参数:', params) |
| 286 | 284 | ||
| 287 | - // 调用接口(直接调用,不使用 fn() 包装) | 285 | + // 调用接口 |
| 288 | const res = await fileListAPI(params) | 286 | const res = await fileListAPI(params) |
| 289 | 287 | ||
| 290 | if (res.code === 1 && res.data) { | 288 | if (res.code === 1 && res.data) { | ... | ... |
-
Please register or login to post a comment