feat(JoinFamily): 添加家庭列表分页加载功能并优化布局
重构家庭选择弹窗布局,使用flexbox替代固定高度计算 添加分页加载功能,包括加载更多按钮和没有更多数据提示 移除不再需要的高度计算逻辑,简化代码结构
Showing
1 changed file
with
101 additions
and
45 deletions
| ... | @@ -80,14 +80,14 @@ | ... | @@ -80,14 +80,14 @@ |
| 80 | closeable | 80 | closeable |
| 81 | @close="closeFamilySelector" | 81 | @close="closeFamilySelector" |
| 82 | > | 82 | > |
| 83 | - <view class="family-selector-container"> | 83 | + <view class="family-selector-container h-full flex flex-col"> |
| 84 | <!-- 标题 --> | 84 | <!-- 标题 --> |
| 85 | - <view class="text-lg font-bold text-center py-4 border-b border-gray-100"> | 85 | + <view class="text-lg font-bold text-center py-4 border-b border-gray-100 flex-shrink-0"> |
| 86 | 选择要加入的家庭 | 86 | 选择要加入的家庭 |
| 87 | </view> | 87 | </view> |
| 88 | 88 | ||
| 89 | <!-- 搜索框 --> | 89 | <!-- 搜索框 --> |
| 90 | - <view class="p-4"> | 90 | + <view class="p-4 flex-shrink-0"> |
| 91 | <nut-searchbar | 91 | <nut-searchbar |
| 92 | v-model="searchKeyword" | 92 | v-model="searchKeyword" |
| 93 | placeholder="搜索家庭名称" | 93 | placeholder="搜索家庭名称" |
| ... | @@ -97,11 +97,10 @@ | ... | @@ -97,11 +97,10 @@ |
| 97 | </view> | 97 | </view> |
| 98 | 98 | ||
| 99 | <!-- 家庭列表 --> | 99 | <!-- 家庭列表 --> |
| 100 | - <view class="flex-1 px-4 pb-4"> | 100 | + <view class="flex-1 px-4 pb-4 overflow-hidden"> |
| 101 | <view | 101 | <view |
| 102 | ref="familyListContainer" | 102 | ref="familyListContainer" |
| 103 | - class="space-y-3 overflow-y-auto" | 103 | + class="h-full space-y-3 overflow-y-auto" |
| 104 | - :style="{ maxHeight: familyListHeight + 'rpx' }" | ||
| 105 | > | 104 | > |
| 106 | <view | 105 | <view |
| 107 | v-for="family in filteredFamilies" | 106 | v-for="family in filteredFamilies" |
| ... | @@ -138,11 +137,29 @@ | ... | @@ -138,11 +137,29 @@ |
| 138 | <Check size="20" /> | 137 | <Check size="20" /> |
| 139 | </view> | 138 | </view> |
| 140 | </view> | 139 | </view> |
| 140 | + | ||
| 141 | + <!-- 加载更多按钮 --> | ||
| 142 | + <view v-if="hasMoreData && !searchKeyword" class="text-center py-4"> | ||
| 143 | + <nut-button | ||
| 144 | + @click="loadMoreFamilies" | ||
| 145 | + :loading="isLoadingMore" | ||
| 146 | + type="default" | ||
| 147 | + size="small" | ||
| 148 | + plain | ||
| 149 | + > | ||
| 150 | + {{ isLoadingMore ? '加载中...' : '加载更多' }} | ||
| 151 | + </nut-button> | ||
| 152 | + </view> | ||
| 153 | + | ||
| 154 | + <!-- 没有更多数据提示 --> | ||
| 155 | + <view v-if="!hasMoreData && totalFamilies.length > 0 && !searchKeyword" class="text-center py-4 text-gray-500 text-sm"> | ||
| 156 | + 没有更多家庭了 | ||
| 157 | + </view> | ||
| 141 | </view> | 158 | </view> |
| 142 | </view> | 159 | </view> |
| 143 | 160 | ||
| 144 | <!-- 底部按钮 --> | 161 | <!-- 底部按钮 --> |
| 145 | - <view class="flex gap-3 p-4 border-t border-gray-100"> | 162 | + <view class="flex gap-3 p-4 border-t border-gray-100 flex-shrink-0"> |
| 146 | <nut-button | 163 | <nut-button |
| 147 | @click="closeFamilySelector" | 164 | @click="closeFamilySelector" |
| 148 | class="flex-1" | 165 | class="flex-1" |
| ... | @@ -189,7 +206,14 @@ const searchKeyword = ref(''); | ... | @@ -189,7 +206,14 @@ const searchKeyword = ref(''); |
| 189 | const selectedFamilyId = ref(''); | 206 | const selectedFamilyId = ref(''); |
| 190 | const mockFamilies = ref([]); | 207 | const mockFamilies = ref([]); |
| 191 | const familyListContainer = ref(null); | 208 | const familyListContainer = ref(null); |
| 192 | -const familyListHeight = ref(400); // 默认高度 | 209 | +// 移除不再需要的familyListHeight变量,因为现在使用flexbox布局 |
| 210 | + | ||
| 211 | +// 分页相关数据 | ||
| 212 | +const currentPage = ref(0); | ||
| 213 | +const pageSize = ref(10); | ||
| 214 | +const hasMoreData = ref(true); | ||
| 215 | +const isLoadingMore = ref(false); | ||
| 216 | +const totalFamilies = ref([]); | ||
| 193 | 217 | ||
| 194 | const handleInputChange = (index, value) => { | 218 | const handleInputChange = (index, value) => { |
| 195 | // 允许输入多个字符,但只保留第一个有效字符(汉字、数字、大小写字母),兼容输入法 | 219 | // 允许输入多个字符,但只保留第一个有效字符(汉字、数字、大小写字母),兼容输入法 |
| ... | @@ -297,40 +321,11 @@ const closeFamilySelector = () => { | ... | @@ -297,40 +321,11 @@ const closeFamilySelector = () => { |
| 297 | searchKeyword.value = '' | 321 | searchKeyword.value = '' |
| 298 | } | 322 | } |
| 299 | 323 | ||
| 300 | -/** | 324 | +// 监听弹窗显示状态,重置选中状态 |
| 301 | - * 计算家庭列表容器的可用高度 | ||
| 302 | - */ | ||
| 303 | -const calculateFamilyListHeight = async () => { | ||
| 304 | - try { | ||
| 305 | - // 获取系统信息 | ||
| 306 | - const systemInfo = await Taro.getSystemInfo() | ||
| 307 | - const windowHeight = systemInfo.windowHeight | ||
| 308 | - | ||
| 309 | - // 弹窗高度为70vh | ||
| 310 | - const popupHeight = windowHeight * 0.8 | ||
| 311 | - | ||
| 312 | - // 减去固定元素的高度(估算值,单位px转rpx需要乘以2) | ||
| 313 | - const titleHeight = 60 * 2 // 标题区域 | ||
| 314 | - const searchHeight = 80 * 2 // 搜索区域 | ||
| 315 | - const buttonHeight = 80 * 2 // 底部按钮区域 | ||
| 316 | - const padding = 32 * 2 // 内边距 | ||
| 317 | - | ||
| 318 | - // 计算可用高度(px转rpx) | ||
| 319 | - const availableHeight = (popupHeight - (titleHeight + searchHeight + buttonHeight + padding) / 2) | ||
| 320 | - | ||
| 321 | - // 设置最小高度和最大高度 | ||
| 322 | - familyListHeight.value = Math.max(200, Math.min(availableHeight * 2, 800)) | ||
| 323 | - } catch (error) { | ||
| 324 | - console.error('计算高度失败:', error) | ||
| 325 | - familyListHeight.value = 400 // 使用默认值 | ||
| 326 | - } | ||
| 327 | -} | ||
| 328 | - | ||
| 329 | -// 监听弹窗显示状态,动态计算高度 | ||
| 330 | watch(showFamilySelector, async (newVal) => { | 325 | watch(showFamilySelector, async (newVal) => { |
| 331 | if (newVal) { | 326 | if (newVal) { |
| 332 | await nextTick() | 327 | await nextTick() |
| 333 | - await calculateFamilyListHeight() | 328 | + // 移除高度计算逻辑,现在使用flexbox自动布局 |
| 334 | } | 329 | } |
| 335 | }) | 330 | }) |
| 336 | 331 | ||
| ... | @@ -378,18 +373,28 @@ const handleJoinFamily = async () => { | ... | @@ -378,18 +373,28 @@ const handleJoinFamily = async () => { |
| 378 | const motto = mottoChars.value.join('') | 373 | const motto = mottoChars.value.join('') |
| 379 | 374 | ||
| 380 | try { | 375 | try { |
| 381 | - // 调用API查询家庭 | 376 | + // 重置分页数据 |
| 377 | + currentPage.value = 0; | ||
| 378 | + hasMoreData.value = true; | ||
| 379 | + totalFamilies.value = []; | ||
| 380 | + | ||
| 381 | + // 调用API查询家庭(第一页) | ||
| 382 | const { code, data } = await searchFamilyByPassphraseAPI({ | 382 | const { code, data } = await searchFamilyByPassphraseAPI({ |
| 383 | passphrase: motto, | 383 | passphrase: motto, |
| 384 | - page: 0, | 384 | + page: currentPage.value, |
| 385 | - limit: 9999 | 385 | + limit: pageSize.value |
| 386 | }) | 386 | }) |
| 387 | 387 | ||
| 388 | let families = []; | 388 | let families = []; |
| 389 | 389 | ||
| 390 | if (code) { | 390 | if (code) { |
| 391 | families = data; | 391 | families = data; |
| 392 | - console.log('查询家庭:', { motto, role: selectedRole.value, families }) | 392 | + totalFamilies.value = families; |
| 393 | + | ||
| 394 | + // 检查是否还有更多数据 | ||
| 395 | + hasMoreData.value = families.length === pageSize.value; | ||
| 396 | + | ||
| 397 | + console.log('查询家庭:', { motto, role: selectedRole.value, families, hasMore: hasMoreData.value }) | ||
| 393 | 398 | ||
| 394 | if (families.length === 0) { | 399 | if (families.length === 0) { |
| 395 | Taro.showToast({ | 400 | Taro.showToast({ |
| ... | @@ -406,7 +411,7 @@ const handleJoinFamily = async () => { | ... | @@ -406,7 +411,7 @@ const handleJoinFamily = async () => { |
| 406 | if (family.is_kicked) { | 411 | if (family.is_kicked) { |
| 407 | // 被踢出状态,显示选择弹窗让用户知道 | 412 | // 被踢出状态,显示选择弹窗让用户知道 |
| 408 | showFamilySelector.value = true; | 413 | showFamilySelector.value = true; |
| 409 | - mockFamilies.value = families; | 414 | + mockFamilies.value = totalFamilies.value; |
| 410 | } else { | 415 | } else { |
| 411 | // 未被踢出,直接加入 | 416 | // 未被踢出,直接加入 |
| 412 | const joinFamily = await joinFamilyAPI({ | 417 | const joinFamily = await joinFamilyAPI({ |
| ... | @@ -430,7 +435,7 @@ const handleJoinFamily = async () => { | ... | @@ -430,7 +435,7 @@ const handleJoinFamily = async () => { |
| 430 | } else { | 435 | } else { |
| 431 | // 多个家庭,显示选择弹窗 | 436 | // 多个家庭,显示选择弹窗 |
| 432 | showFamilySelector.value = true | 437 | showFamilySelector.value = true |
| 433 | - mockFamilies.value = families | 438 | + mockFamilies.value = totalFamilies.value |
| 434 | } | 439 | } |
| 435 | } | 440 | } |
| 436 | } catch (error) { | 441 | } catch (error) { |
| ... | @@ -441,6 +446,57 @@ const handleJoinFamily = async () => { | ... | @@ -441,6 +446,57 @@ const handleJoinFamily = async () => { |
| 441 | }) | 446 | }) |
| 442 | } | 447 | } |
| 443 | }; | 448 | }; |
| 449 | + | ||
| 450 | +// 加载更多家庭数据 | ||
| 451 | +const loadMoreFamilies = async () => { | ||
| 452 | + if (isLoadingMore.value || !hasMoreData.value) return; | ||
| 453 | + | ||
| 454 | + isLoadingMore.value = true; | ||
| 455 | + | ||
| 456 | + try { | ||
| 457 | + const motto = mottoChars.value.join(''); | ||
| 458 | + currentPage.value += 1; | ||
| 459 | + | ||
| 460 | + const { code, data } = await searchFamilyByPassphraseAPI({ | ||
| 461 | + passphrase: motto, | ||
| 462 | + page: currentPage.value, | ||
| 463 | + limit: pageSize.value | ||
| 464 | + }); | ||
| 465 | + | ||
| 466 | + if (code && data) { | ||
| 467 | + // 合并新数据到现有数据 | ||
| 468 | + totalFamilies.value = [...totalFamilies.value, ...data]; | ||
| 469 | + | ||
| 470 | + // 检查是否还有更多数据 | ||
| 471 | + hasMoreData.value = data.length === pageSize.value; | ||
| 472 | + | ||
| 473 | + // 更新mockFamilies用于显示 | ||
| 474 | + mockFamilies.value = totalFamilies.value; | ||
| 475 | + | ||
| 476 | + console.log('加载更多家庭:', { | ||
| 477 | + page: currentPage.value, | ||
| 478 | + newCount: data.length, | ||
| 479 | + totalCount: totalFamilies.value.length, | ||
| 480 | + hasMore: hasMoreData.value | ||
| 481 | + }); | ||
| 482 | + } else { | ||
| 483 | + hasMoreData.value = false; | ||
| 484 | + Taro.showToast({ | ||
| 485 | + title: '加载失败', | ||
| 486 | + icon: 'none' | ||
| 487 | + }); | ||
| 488 | + } | ||
| 489 | + } catch (error) { | ||
| 490 | + console.error('加载更多家庭失败:', error); | ||
| 491 | + currentPage.value -= 1; // 回退页码 | ||
| 492 | + Taro.showToast({ | ||
| 493 | + title: '加载失败,请重试', | ||
| 494 | + icon: 'none' | ||
| 495 | + }); | ||
| 496 | + } finally { | ||
| 497 | + isLoadingMore.value = false; | ||
| 498 | + } | ||
| 499 | +}; | ||
| 444 | </script> | 500 | </script> |
| 445 | <style lang="less"> | 501 | <style lang="less"> |
| 446 | @import './index.less'; | 502 | @import './index.less'; | ... | ... |
-
Please register or login to post a comment