hookehuyr

feat(JoinFamily): 添加家庭列表分页加载功能并优化布局

重构家庭选择弹窗布局,使用flexbox替代固定高度计算
添加分页加载功能,包括加载更多按钮和没有更多数据提示
移除不再需要的高度计算逻辑,简化代码结构
......@@ -80,14 +80,14 @@
closeable
@close="closeFamilySelector"
>
<view class="family-selector-container">
<view class="family-selector-container h-full flex flex-col">
<!-- 标题 -->
<view class="text-lg font-bold text-center py-4 border-b border-gray-100">
<view class="text-lg font-bold text-center py-4 border-b border-gray-100 flex-shrink-0">
选择要加入的家庭
</view>
<!-- 搜索框 -->
<view class="p-4">
<view class="p-4 flex-shrink-0">
<nut-searchbar
v-model="searchKeyword"
placeholder="搜索家庭名称"
......@@ -97,11 +97,10 @@
</view>
<!-- 家庭列表 -->
<view class="flex-1 px-4 pb-4">
<view class="flex-1 px-4 pb-4 overflow-hidden">
<view
ref="familyListContainer"
class="space-y-3 overflow-y-auto"
:style="{ maxHeight: familyListHeight + 'rpx' }"
class="h-full space-y-3 overflow-y-auto"
>
<view
v-for="family in filteredFamilies"
......@@ -138,11 +137,29 @@
<Check size="20" />
</view>
</view>
<!-- 加载更多按钮 -->
<view v-if="hasMoreData && !searchKeyword" class="text-center py-4">
<nut-button
@click="loadMoreFamilies"
:loading="isLoadingMore"
type="default"
size="small"
plain
>
{{ isLoadingMore ? '加载中...' : '加载更多' }}
</nut-button>
</view>
<!-- 没有更多数据提示 -->
<view v-if="!hasMoreData && totalFamilies.length > 0 && !searchKeyword" class="text-center py-4 text-gray-500 text-sm">
没有更多家庭了
</view>
</view>
</view>
<!-- 底部按钮 -->
<view class="flex gap-3 p-4 border-t border-gray-100">
<view class="flex gap-3 p-4 border-t border-gray-100 flex-shrink-0">
<nut-button
@click="closeFamilySelector"
class="flex-1"
......@@ -189,7 +206,14 @@ const searchKeyword = ref('');
const selectedFamilyId = ref('');
const mockFamilies = ref([]);
const familyListContainer = ref(null);
const familyListHeight = ref(400); // 默认高度
// 移除不再需要的familyListHeight变量,因为现在使用flexbox布局
// 分页相关数据
const currentPage = ref(0);
const pageSize = ref(10);
const hasMoreData = ref(true);
const isLoadingMore = ref(false);
const totalFamilies = ref([]);
const handleInputChange = (index, value) => {
// 允许输入多个字符,但只保留第一个有效字符(汉字、数字、大小写字母),兼容输入法
......@@ -297,40 +321,11 @@ const closeFamilySelector = () => {
searchKeyword.value = ''
}
/**
* 计算家庭列表容器的可用高度
*/
const calculateFamilyListHeight = async () => {
try {
// 获取系统信息
const systemInfo = await Taro.getSystemInfo()
const windowHeight = systemInfo.windowHeight
// 弹窗高度为70vh
const popupHeight = windowHeight * 0.8
// 减去固定元素的高度(估算值,单位px转rpx需要乘以2)
const titleHeight = 60 * 2 // 标题区域
const searchHeight = 80 * 2 // 搜索区域
const buttonHeight = 80 * 2 // 底部按钮区域
const padding = 32 * 2 // 内边距
// 计算可用高度(px转rpx)
const availableHeight = (popupHeight - (titleHeight + searchHeight + buttonHeight + padding) / 2)
// 设置最小高度和最大高度
familyListHeight.value = Math.max(200, Math.min(availableHeight * 2, 800))
} catch (error) {
console.error('计算高度失败:', error)
familyListHeight.value = 400 // 使用默认值
}
}
// 监听弹窗显示状态,动态计算高度
// 监听弹窗显示状态,重置选中状态
watch(showFamilySelector, async (newVal) => {
if (newVal) {
await nextTick()
await calculateFamilyListHeight()
// 移除高度计算逻辑,现在使用flexbox自动布局
}
})
......@@ -378,18 +373,28 @@ const handleJoinFamily = async () => {
const motto = mottoChars.value.join('')
try {
// 调用API查询家庭
// 重置分页数据
currentPage.value = 0;
hasMoreData.value = true;
totalFamilies.value = [];
// 调用API查询家庭(第一页)
const { code, data } = await searchFamilyByPassphraseAPI({
passphrase: motto,
page: 0,
limit: 9999
page: currentPage.value,
limit: pageSize.value
})
let families = [];
if (code) {
families = data;
console.log('查询家庭:', { motto, role: selectedRole.value, families })
totalFamilies.value = families;
// 检查是否还有更多数据
hasMoreData.value = families.length === pageSize.value;
console.log('查询家庭:', { motto, role: selectedRole.value, families, hasMore: hasMoreData.value })
if (families.length === 0) {
Taro.showToast({
......@@ -406,7 +411,7 @@ const handleJoinFamily = async () => {
if (family.is_kicked) {
// 被踢出状态,显示选择弹窗让用户知道
showFamilySelector.value = true;
mockFamilies.value = families;
mockFamilies.value = totalFamilies.value;
} else {
// 未被踢出,直接加入
const joinFamily = await joinFamilyAPI({
......@@ -430,7 +435,7 @@ const handleJoinFamily = async () => {
} else {
// 多个家庭,显示选择弹窗
showFamilySelector.value = true
mockFamilies.value = families
mockFamilies.value = totalFamilies.value
}
}
} catch (error) {
......@@ -441,6 +446,57 @@ const handleJoinFamily = async () => {
})
}
};
// 加载更多家庭数据
const loadMoreFamilies = async () => {
if (isLoadingMore.value || !hasMoreData.value) return;
isLoadingMore.value = true;
try {
const motto = mottoChars.value.join('');
currentPage.value += 1;
const { code, data } = await searchFamilyByPassphraseAPI({
passphrase: motto,
page: currentPage.value,
limit: pageSize.value
});
if (code && data) {
// 合并新数据到现有数据
totalFamilies.value = [...totalFamilies.value, ...data];
// 检查是否还有更多数据
hasMoreData.value = data.length === pageSize.value;
// 更新mockFamilies用于显示
mockFamilies.value = totalFamilies.value;
console.log('加载更多家庭:', {
page: currentPage.value,
newCount: data.length,
totalCount: totalFamilies.value.length,
hasMore: hasMoreData.value
});
} else {
hasMoreData.value = false;
Taro.showToast({
title: '加载失败',
icon: 'none'
});
}
} catch (error) {
console.error('加载更多家庭失败:', error);
currentPage.value -= 1; // 回退页码
Taro.showToast({
title: '加载失败,请重试',
icon: 'none'
});
} finally {
isLoadingMore.value = false;
}
};
</script>
<style lang="less">
@import './index.less';
......