refactor: 迁移所有剩余页面到 LoadMoreList 组件
- message 页面:添加下拉刷新功能 - product-center 页面:保留搜索、tabs、计划书弹窗 - material-list 页面:保留分类缓存、搜索防抖 - search 页面:保留双列表、自动 tab 切换、三种状态 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Showing
4 changed files
with
179 additions
and
99 deletions
This diff is collapsed. Click to expand it.
| 1 | +<!-- | ||
| 2 | + * @Date: 2026-02-08 | ||
| 3 | + * @Description: 我的消息页 - 使用 LoadMoreList 组件重构版本 | ||
| 4 | +--> | ||
| 1 | <template> | 5 | <template> |
| 2 | - <view class="min-h-screen bg-[#F9FAFB] pb-safe"> | 6 | + <LoadMoreList |
| 3 | - <NavHeader title="我的消息" /> | 7 | + :list="currentList" |
| 4 | - | 8 | + :page="currentPage" |
| 5 | - <!-- 列表区域 --> | 9 | + :page-size="pageSize" |
| 6 | - <view class="p-4"> | 10 | + :has-more="hasMore" |
| 7 | - <template v-if="messageList.length > 0"> | 11 | + :loading="loading" |
| 8 | - <view | 12 | + :loading-more="loadingMore" |
| 9 | - v-for="item in messageList" | 13 | + :enable-pull-down-refresh="true" |
| 10 | - :key="item.id" | 14 | + key-field="id" |
| 11 | - class="bg-white rounded-xl p-4 mb-3 shadow-sm active:opacity-70 transition-opacity" | 15 | + @load-more="handleLoadMore" |
| 12 | - @tap="handleItemClick(item)" | 16 | + @refresh="handleRefresh" |
| 13 | - > | 17 | + > |
| 14 | - <view class="flex justify-between items-start mb-2"> | 18 | + <!-- 头部 --> |
| 15 | - <view class="flex-1 mr-2"> | 19 | + <template #header> |
| 16 | - <view class="text-base font-bold text-gray-900 line-clamp-1"> | 20 | + <NavHeader title="我的消息" /> |
| 17 | - {{ item.title }} | 21 | + </template> |
| 18 | - </view> | 22 | + |
| 23 | + <!-- 列表项 --> | ||
| 24 | + <template #item="{ item }"> | ||
| 25 | + <view | ||
| 26 | + class="message-item bg-white rounded-xl p-4 mb-3 shadow-sm active:opacity-70 transition-opacity" | ||
| 27 | + @tap="handleItemClick(item)" | ||
| 28 | + > | ||
| 29 | + <view class="flex justify-between items-start mb-2"> | ||
| 30 | + <view class="flex-1 mr-2"> | ||
| 31 | + <view class="text-base font-bold text-gray-900 line-clamp-1"> | ||
| 32 | + {{ item.title }} | ||
| 19 | </view> | 33 | </view> |
| 20 | - <text class="text-xs text-gray-400 shrink-0 mt-1"> | ||
| 21 | - {{ item.create_time }} | ||
| 22 | - </text> | ||
| 23 | - </view> | ||
| 24 | - | ||
| 25 | - <view class="text-sm text-gray-600 line-clamp-2 leading-relaxed"> | ||
| 26 | - {{ item.intro || item.content || '暂无简介' }} | ||
| 27 | </view> | 34 | </view> |
| 35 | + <text class="text-xs text-gray-400 shrink-0 mt-1"> | ||
| 36 | + {{ item.create_time }} | ||
| 37 | + </text> | ||
| 28 | </view> | 38 | </view> |
| 29 | 39 | ||
| 30 | - <!-- 加载更多/没有更多 --> | 40 | + <view class="text-sm text-gray-600 line-clamp-2 leading-relaxed"> |
| 31 | - <view class="py-4 text-center text-[24rpx] text-gray-400"> | 41 | + {{ item.intro || item.content || '暂无简介' }} |
| 32 | - <text v-if="loading">加载中...</text> | ||
| 33 | - <text v-else-if="!hasMore">没有更多了</text> | ||
| 34 | - <text v-else>上拉加载更多</text> | ||
| 35 | </view> | 42 | </view> |
| 36 | - </template> | 43 | + </view> |
| 37 | - | 44 | + </template> |
| 38 | - <!-- 空状态 --> | 45 | + |
| 39 | - <nut-empty | 46 | + <!-- 空状态 --> |
| 40 | - v-else-if="!loading && messageList.length === 0" | 47 | + <template #empty> |
| 41 | - description="暂无消息" | 48 | + <nut-empty description="暂无消息" image="empty" /> |
| 42 | - image="empty" | 49 | + </template> |
| 43 | - /> | 50 | + </LoadMoreList> |
| 44 | - </view> | ||
| 45 | - </view> | ||
| 46 | </template> | 51 | </template> |
| 47 | 52 | ||
| 48 | <script setup> | 53 | <script setup> |
| 49 | import { ref } from 'vue' | 54 | import { ref } from 'vue' |
| 50 | -import { useLoad, usePullDownRefresh, useReachBottom, stopPullDownRefresh } from '@tarojs/taro' | 55 | +import { useLoad } from '@tarojs/taro' |
| 51 | import { useGo } from '@/hooks/useGo' | 56 | import { useGo } from '@/hooks/useGo' |
| 57 | +import LoadMoreList from '@/components/LoadMoreList' | ||
| 52 | import NavHeader from '@/components/NavHeader.vue' | 58 | import NavHeader from '@/components/NavHeader.vue' |
| 53 | import { myListAPI } from '@/api/news' | 59 | import { myListAPI } from '@/api/news' |
| 54 | import { mockMessageListAPI } from '@/utils/mockData' | 60 | import { mockMessageListAPI } from '@/utils/mockData' |
| ... | @@ -58,91 +64,165 @@ const USE_MOCK_DATA = process.env.NODE_ENV === 'development' | ... | @@ -58,91 +64,165 @@ const USE_MOCK_DATA = process.env.NODE_ENV === 'development' |
| 58 | 64 | ||
| 59 | const go = useGo() | 65 | const go = useGo() |
| 60 | 66 | ||
| 61 | -const messageList = ref([]) | 67 | +/** |
| 62 | -const page = ref(1) | 68 | + * 当前列表数据 |
| 63 | -const limit = ref(10) | 69 | + * @type {Ref<Array<any>>} |
| 70 | + */ | ||
| 71 | +const currentList = ref([]) | ||
| 72 | + | ||
| 73 | +/** | ||
| 74 | + * 当前页码(从1开始) | ||
| 75 | + * @type {Ref<number>} | ||
| 76 | + */ | ||
| 77 | +const currentPage = ref(1) | ||
| 78 | + | ||
| 79 | +/** | ||
| 80 | + * 每页数量 | ||
| 81 | + * @type {number} | ||
| 82 | + */ | ||
| 83 | +const pageSize = 10 | ||
| 84 | + | ||
| 85 | +/** | ||
| 86 | + * 是否还有更多数据 | ||
| 87 | + * @type {Ref<boolean>} | ||
| 88 | + */ | ||
| 64 | const hasMore = ref(true) | 89 | const hasMore = ref(true) |
| 65 | -const loading = ref(false) | ||
| 66 | 90 | ||
| 67 | /** | 91 | /** |
| 68 | - * @description 加载消息列表 | 92 | + * 首次加载状态 |
| 69 | - * @param {boolean} refresh 是否刷新 | 93 | + * @type {Ref<boolean>} |
| 70 | */ | 94 | */ |
| 71 | -const fetchMessageList = async (refresh = false) => { | 95 | +const loading = ref(false) |
| 72 | - if (loading.value) return | ||
| 73 | - | ||
| 74 | - if (refresh) { | ||
| 75 | - page.value = 1 | ||
| 76 | - hasMore.value = true | ||
| 77 | - } else if (!hasMore.value) { | ||
| 78 | - return | ||
| 79 | - } | ||
| 80 | 96 | ||
| 81 | - loading.value = true | 97 | +/** |
| 98 | + * 加载更多状态 | ||
| 99 | + * @type {Ref<boolean>} | ||
| 100 | + */ | ||
| 101 | +const loadingMore = ref(false) | ||
| 82 | 102 | ||
| 103 | +/** | ||
| 104 | + * 获取消息列表 | ||
| 105 | + * | ||
| 106 | + * @param {Object} params - 请求参数 | ||
| 107 | + * @param {number} params.page - 页码(从1开始) | ||
| 108 | + * @param {number} params.limit - 每页数量 | ||
| 109 | + * @param {boolean} isLoadMore - 是否为加载更多 | ||
| 110 | + * @returns {Promise<void>} | ||
| 111 | + */ | ||
| 112 | +const fetchMessageList = async (params = {}, isLoadMore = false) => { | ||
| 83 | try { | 113 | try { |
| 114 | + // 如果是加载更多,使用 loadingMore 状态,否则使用 loading 状态 | ||
| 115 | + if (isLoadMore) { | ||
| 116 | + loadingMore.value = true | ||
| 117 | + } else { | ||
| 118 | + loading.value = true | ||
| 119 | + } | ||
| 120 | + | ||
| 121 | + console.log('[Message] 请求参数:', params) | ||
| 84 | console.log('[Message] 使用 Mock 数据:', USE_MOCK_DATA) | 122 | console.log('[Message] 使用 Mock 数据:', USE_MOCK_DATA) |
| 85 | 123 | ||
| 86 | // 根据开关选择使用真实 API 或 Mock 数据 | 124 | // 根据开关选择使用真实 API 或 Mock 数据 |
| 87 | const res = USE_MOCK_DATA | 125 | const res = USE_MOCK_DATA |
| 88 | - ? await mockMessageListAPI({ | 126 | + ? await mockMessageListAPI(params) |
| 89 | - page: page.value, | 127 | + : await myListAPI(params) |
| 90 | - limit: limit.value | 128 | + |
| 91 | - }) | 129 | + if (res.code === 1 && res.data) { |
| 92 | - : await myListAPI({ | 130 | + console.log('[Message] 数据:', res.data) |
| 93 | - page: page.value, | 131 | + |
| 94 | - limit: limit.value | 132 | + // 处理列表数据 |
| 95 | - }) | 133 | + if (res.data.list?.length) { |
| 96 | - | 134 | + const listData = res.data.list |
| 97 | - if (res.code === 1) { | 135 | + |
| 98 | - const list = res.data?.list || [] | 136 | + if (isLoadMore) { |
| 99 | - | 137 | + // 加载更多:追加数据 |
| 100 | - if (refresh) { | 138 | + currentList.value = [...currentList.value, ...listData] |
| 101 | - messageList.value = list | 139 | + } else { |
| 140 | + // 首次加载或刷新:替换数据 | ||
| 141 | + currentList.value = listData | ||
| 142 | + } | ||
| 143 | + | ||
| 144 | + // 判断是否还有更多数据 | ||
| 145 | + // 如果返回的数据量少于请求的量,说明没有更多了 | ||
| 146 | + hasMore.value = listData.length >= params.limit | ||
| 102 | } else { | 147 | } else { |
| 103 | - messageList.value = [...messageList.value, ...list] | 148 | + // 没有数据了 |
| 104 | - } | 149 | + if (isLoadMore) { |
| 105 | - | 150 | + hasMore.value = false |
| 106 | - if (list.length < limit.value) { | 151 | + } else { |
| 107 | - hasMore.value = false | 152 | + currentList.value = [] |
| 108 | - } else { | 153 | + } |
| 109 | - page.value++ | ||
| 110 | } | 154 | } |
| 155 | + } else { | ||
| 156 | + console.error('[Message] API 返回错误:', res.msg) | ||
| 111 | } | 157 | } |
| 112 | - } catch (err) { | 158 | + } catch (error) { |
| 113 | - console.error('获取消息列表失败:', err) | 159 | + console.error('[Message] 获取消息列表失败:', error) |
| 114 | } finally { | 160 | } finally { |
| 115 | - loading.value = false | 161 | + if (isLoadMore) { |
| 116 | - if (refresh) { | 162 | + loadingMore.value = false |
| 117 | - stopPullDownRefresh() | 163 | + } else { |
| 164 | + loading.value = false | ||
| 118 | } | 165 | } |
| 119 | } | 166 | } |
| 120 | } | 167 | } |
| 121 | 168 | ||
| 122 | /** | 169 | /** |
| 123 | - * @description 跳转到详情页 | 170 | + * 页面加载时获取数据 |
| 124 | - * @param {Object} item 消息对象 | ||
| 125 | */ | 171 | */ |
| 126 | -const handleItemClick = (item) => { | 172 | +useLoad(async (options) => { |
| 127 | - go('/pages/message-detail/index', { id: item.id }) | 173 | + console.log('[Message] 页面参数:', options) |
| 128 | -} | ||
| 129 | 174 | ||
| 130 | -// 页面加载 | 175 | + // 重置分页状态 |
| 131 | -useLoad(() => { | 176 | + currentPage.value = 1 |
| 132 | - fetchMessageList(true) | 177 | + hasMore.value = true |
| 133 | -}) | ||
| 134 | 178 | ||
| 135 | -// 下拉刷新 | 179 | + // 获取消息列表 |
| 136 | -usePullDownRefresh(() => { | 180 | + await fetchMessageList({ page: 1, limit: pageSize }) |
| 137 | - fetchMessageList(true) | ||
| 138 | }) | 181 | }) |
| 139 | 182 | ||
| 140 | -// 上拉加载更多 | 183 | +/** |
| 141 | -useReachBottom(() => { | 184 | + * 处理加载更多事件 |
| 142 | - fetchMessageList() | 185 | + * |
| 143 | -}) | 186 | + * @param {number} page - 下一页页码 |
| 187 | + * @returns {Promise<void>} | ||
| 188 | + */ | ||
| 189 | +const handleLoadMore = async (page) => { | ||
| 190 | + console.log('[Message] 加载更多,页码:', page) | ||
| 191 | + | ||
| 192 | + // 更新页码 | ||
| 193 | + currentPage.value = page | ||
| 194 | + | ||
| 195 | + // 加载下一页数据 | ||
| 196 | + await fetchMessageList( | ||
| 197 | + { page: page, limit: pageSize }, | ||
| 198 | + true // 标记为加载更多 | ||
| 199 | + ) | ||
| 200 | +} | ||
| 201 | + | ||
| 202 | +/** | ||
| 203 | + * 处理下拉刷新事件 | ||
| 204 | + */ | ||
| 205 | +const handleRefresh = async () => { | ||
| 206 | + console.log('[Message] 下拉刷新') | ||
| 207 | + | ||
| 208 | + // 重置分页状态 | ||
| 209 | + currentPage.value = 1 | ||
| 210 | + hasMore.value = true | ||
| 211 | + | ||
| 212 | + // 刷新数据 | ||
| 213 | + await fetchMessageList({ page: 1, limit: pageSize }) | ||
| 214 | +} | ||
| 215 | + | ||
| 216 | +/** | ||
| 217 | + * 跳转到详情页 | ||
| 218 | + * | ||
| 219 | + * @param {Object} item - 消息对象 | ||
| 220 | + */ | ||
| 221 | +const handleItemClick = (item) => { | ||
| 222 | + go('/pages/message-detail/index', { id: item.id }) | ||
| 223 | +} | ||
| 144 | </script> | 224 | </script> |
| 145 | 225 | ||
| 146 | <style lang="less"> | 226 | <style lang="less"> |
| 147 | -/* Scoped styles if needed */ | 227 | +/* LoadMoreList 组件已内置样式,此处无需额外样式 */ |
| 148 | </style> | 228 | </style> | ... | ... |
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
-
Please register or login to post a comment