hookehuyr

feat(家庭管理): 添加家庭成员管理功能并优化角色显示

- 在MyFamily页面添加成员管理弹窗,支持创建者查看和移除成员
- 将JoinFamily页面的角色id从英文改为中文显示
- 为家庭创建者添加特殊标记和操作权限
- 区分退出家庭和解散家庭的不同操作
......@@ -229,16 +229,16 @@ const handleBlur = (index) => {
};
const familyRoles = [
{ id: 'husband', label: '丈夫' },
{ id: 'wife', label: '妻子' },
{ id: 'son', label: '儿子' },
{ id: 'daughter-in-law', label: '儿媳' },
{ id: 'son-in-law', label: '女婿' },
{ id: 'daughter', label: '女儿' },
{ id: 'grandson', label: '孙子' },
{ id: 'maternal-grandson', label: '外孙' },
{ id: 'granddaughter', label: '孙女' },
{ id: 'maternal-granddaughter', label: '外孙女' }
{ id: '丈夫', label: '丈夫' },
{ id: '妻子', label: '妻子' },
{ id: '儿子', label: '儿子' },
{ id: '女儿', label: '女儿' },
{ id: '女婿', label: '女婿' },
{ id: '儿媳', label: '儿媳' },
{ id: '孙子', label: '孙子' },
{ id: '外孙', label: '外孙' },
{ id: '孙女', label: '孙女' },
{ id: '外孙女', label: '外孙女' }
];
const isComplete = computed(() => {
......
<!--
* @Date: 2022-09-19 14:11:06
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-09-02 21:45:14
* @LastEditTime: 2025-09-03 00:11:18
* @FilePath: /lls_program/src/pages/MyFamily/index.vue
* @Description: 我的家庭页面 - 展示用户加入的家庭列表
-->
......@@ -24,6 +24,14 @@
当前家庭
</view>
<!-- 创建者标记 -->
<view
v-if="family.is_my"
class="absolute top-2 left-2 bg-yellow-500 text-white text-xs px-2 py-1 rounded-sm z-10"
>
创建者
</view>
<!-- 封面图 -->
<image
:src="family.avatar_url || defaultFamilyCoverSvg"
......@@ -71,6 +79,13 @@
<!-- 操作按钮 -->
<view class="flex gap-3 justify-end">
<view
v-if="family.is_my"
@tap="showMemberManagement(family)"
class="px-4 py-2 bg-green-500 text-white text-sm rounded-lg"
>
查看成员
</view>
<view
v-if="!family.is_in"
@tap="switchToFamily(family.id)"
class="px-4 py-2 bg-blue-500 text-white text-sm rounded-lg"
......@@ -78,10 +93,10 @@
切换到此家庭
</view>
<view
@tap="exitFamily(family.id)"
@tap="family.is_my ? dissolveFamily(family.id) : exitFamily(family.id)"
class="px-4 py-2 bg-red-500 text-white text-sm rounded-lg"
>
退出家庭
{{ family.is_my ? '解散家庭' : '退出家庭' }}
</view>
</view>
</view>
......@@ -106,6 +121,95 @@
</view>
</view>
<!-- 成员管理弹窗 -->
<nut-popup
v-model:visible="showMemberPopup"
position="bottom"
:style="{ height: '60%' }"
round
closeable
@close="closeMemberPopup"
>
<view class="h-full flex flex-col">
<!-- 固定标题区域 -->
<view class="p-4 pb-2 border-b border-gray-100">
<view class="text-lg font-bold mb-2 text-center">家庭成员管理</view>
<view class="text-sm text-gray-600 text-center">{{ currentFamily?.name }}</view>
</view>
<!-- 可滚动的成员列表区域 -->
<view class="flex-1 overflow-hidden">
<scroll-view :scroll-y="true" class="h-full">
<view class="px-4 py-3">
<view class="space-y-3">
<view
v-for="member in mockMembers"
:key="member.id"
class="bg-gray-50 rounded-lg p-3"
@tap="toggleMemberSelection(member.id)"
>
<view class="flex items-center">
<!-- 左侧:选择框 + 头像 + 信息 -->
<view class="flex items-center flex-1 min-w-0">
<!-- 选择框 -->
<view
class="w-5 h-5 rounded border-2 flex items-center justify-center flex-shrink-0"
:class="selectedMembers.includes(member.id) ? 'bg-blue-500 border-blue-500' : 'border-gray-300'"
>
<view
v-if="selectedMembers.includes(member.id)"
class="w-2 h-2 bg-white rounded-sm"
></view>
</view>
<!-- 头像 -->
<image
:src="member.avatar"
class="w-10 h-10 rounded-full ml-3 object-cover flex-shrink-0"
/>
<!-- 昵称和角色 -->
<view class="ml-3 flex-1 min-w-0">
<view class="font-medium text-sm truncate">{{ member.nickname }}</view>
<view class="text-xs text-gray-500 truncate">{{ member.role }}</view>
</view>
</view>
<!-- 右侧:创建者标识 -->
<view
v-if="member.is_owner"
class="ml-3 px-2 py-1 bg-yellow-100 text-yellow-600 text-xs rounded flex-shrink-0"
>
创建者
</view>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
<!-- 固定底部按钮区域 -->
<view class="p-4 pt-2 border-t border-gray-100">
<view class="flex gap-3">
<view
@tap="closeMemberPopup"
class="flex-1 py-3 bg-gray-200 text-gray-700 text-center rounded-lg"
>
关闭
</view>
<view
@tap="removeSelectedMembers"
class="flex-1 py-3 bg-red-500 text-white text-center rounded-lg"
:class="selectedMembers.length === 0 ? 'opacity-50' : ''"
>
移除 ({{ selectedMembers.length }})
</view>
</view>
</view>
</view>
</nut-popup>
<!-- 确认弹窗已替换为Taro.showModal -->
</view>
</template>
......@@ -123,6 +227,12 @@ const defaultFamilyCoverSvg = 'https://cdn.ipadbiz.cn/lls_prog/images/default-fa
// 响应式数据
const familyList = ref([]);
// 成员管理相关数据
const showMemberPopup = ref(false);
const currentFamily = ref(null);
const selectedMembers = ref([]);
const mockMembers = ref([]);
/**
* 初始化页面数据
*/
......@@ -132,43 +242,46 @@ const initPageData = async () => {
familyList.value = data;
console.warn(data);
}
// 模拟家庭数据
// familyList.value = [
// {
// id: 1,
// name: '幸福之家',
// ownerName: '张明明',
// avatar_url: 'https://images.unsplash.com/photo-1511895426328-dc8714191300?w=400&h=200&fit=crop',
// is_in: true,
// members: [
// { id: 1, avatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100&h=100&fit=crop&crop=face' },
// { id: 2, avatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100&h=100&fit=crop&crop=face' },
// { id: 3, avatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100&h=100&fit=crop&crop=face' }
// ]
// },
// {
// id: 2,
// name: '欢乐之家',
// ownerName: '李志强',
// avatar_url: 'https://images.unsplash.com/photo-1502086223501-7ea6ecd79368?w=400&h=200&fit=crop',
// is_in: false,
// members: [
// { id: 4, avatar: 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=100&h=100&fit=crop&crop=face' },
// { id: 5, avatar: 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=100&h=100&fit=crop&crop=face' }
// ]
// },
// {
// id: 3,
// name: '快乐之家',
// ownerName: '王芳',
// avatar_url: 'https://images.unsplash.com/photo-1502086223501-7ea6ecd79368?w=400&h=200&fit=crop',
// is_in: false,
// members: [
// { id: 6, avatar: 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=100&h=100&fit=crop&crop=face' },
// { id: 7, avatar: 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=100&h=100&fit=crop&crop=face' }
// ]
// },
// ];
// 模拟家庭数据 - 添加is_my字段用于测试
familyList.value = [
{
id: 1,
name: '幸福之家',
ownerName: '张明明',
avatar_url: 'https://images.unsplash.com/photo-1511895426328-dc8714191300?w=400&h=200&fit=crop',
is_in: true,
is_my: true, // 当前用户是家长,可以管理成员
members: [
{ id: 1, avatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100&h=100&fit=crop&crop=face' },
{ id: 2, avatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100&h=100&fit=crop&crop=face' },
{ id: 3, avatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100&h=100&fit=crop&crop=face' }
]
},
{
id: 2,
name: '欢乐之家',
ownerName: '李志强',
avatar_url: 'https://images.unsplash.com/photo-1502086223501-7ea6ecd79368?w=400&h=200&fit=crop',
is_in: false,
is_my: false, // 当前用户不是家长
members: [
{ id: 4, avatar: 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=100&h=100&fit=crop&crop=face' },
{ id: 5, avatar: 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=100&h=100&fit=crop&crop=face' }
]
},
{
id: 3,
name: '快乐之家',
ownerName: '王芳',
avatar_url: 'https://images.unsplash.com/photo-1502086223501-7ea6ecd79368?w=400&h=200&fit=crop',
is_in: false,
is_my: true, // 当前用户是家长,但不在此家庭
members: [
{ id: 6, avatar: 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=100&h=100&fit=crop&crop=face' },
{ id: 7, avatar: 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=100&h=100&fit=crop&crop=face' }
]
}
];
};
// 如果家庭列表存在时, 才显示加入新家庭的按钮
......@@ -250,6 +363,38 @@ const exitFamily = (familyId) => {
};
/**
* 解散家庭
* @param {number} familyId - 家庭ID
*/
const dissolveFamily = (familyId) => {
const family = familyList.value.find(f => f.id === familyId);
if (!family) return;
Taro.showModal({
title: '解散家庭',
content: `确定要解散「${family.name}」吗?\n\n解散后:\n• 所有家庭成员将被移除\n• 家庭数据将被永久删除\n• 此操作无法撤销\n\n请谨慎操作!`,
confirmText: '确认解散',
cancelText: '取消',
success: (res) => {
if (res.confirm) {
// 解散家庭逻辑
familyList.value = familyList.value.filter(f => f.id !== familyId);
Taro.showToast({
title: '家庭已解散',
icon: 'success'
});
// 延迟返回我的页面
setTimeout(() => {
Taro.navigateBack();
}, 1500);
}
}
});
};
/**
* 加入新家庭
*/
const joinNewFamily = () => {
......@@ -258,6 +403,123 @@ const joinNewFamily = () => {
});
};
/**
* 显示成员管理弹窗
* @param {Object} family - 家庭对象
*/
const showMemberManagement = (family) => {
currentFamily.value = family;
// 生成模拟成员数据
mockMembers.value = [
{
id: 1,
nickname: '张明明',
avatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100&h=100&fit=crop&crop=face',
role: '父亲',
is_owner: true
},
{
id: 2,
nickname: '李美丽',
avatar: 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=100&h=100&fit=crop&crop=face',
role: '母亲',
is_owner: false
},
{
id: 3,
nickname: '张小明',
avatar: 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=100&h=100&fit=crop&crop=face',
role: '儿子',
is_owner: false
},
{
id: 4,
nickname: '张小花',
avatar: 'https://images.unsplash.com/photo-1494790108755-2616b612b786?w=100&h=100&fit=crop&crop=face',
role: '女儿',
is_owner: false
},
{
id: 5,
nickname: '王奶奶',
avatar: 'https://images.unsplash.com/photo-1551836022-d5d88e9218df?w=100&h=100&fit=crop&crop=face',
role: '奶奶',
is_owner: false
}
];
selectedMembers.value = [];
showMemberPopup.value = true;
};
/**
* 关闭成员管理弹窗
*/
const closeMemberPopup = () => {
showMemberPopup.value = false;
currentFamily.value = null;
selectedMembers.value = [];
mockMembers.value = [];
};
/**
* 切换成员选择状态
* @param {number} memberId - 成员ID
*/
const toggleMemberSelection = (memberId) => {
const member = mockMembers.value.find(m => m.id === memberId);
// 创建者不能被选择移除
if (member?.is_owner) {
Taro.showToast({
title: '创建者不能被移除',
icon: 'none'
});
return;
}
const index = selectedMembers.value.indexOf(memberId);
if (index > -1) {
selectedMembers.value.splice(index, 1);
} else {
selectedMembers.value.push(memberId);
}
};
/**
* 移除选中的成员
*/
const removeSelectedMembers = () => {
if (selectedMembers.value.length === 0) {
Taro.showToast({
title: '请选择要移除的成员',
icon: 'none'
});
return;
}
const selectedNames = mockMembers.value
.filter(m => selectedMembers.value.includes(m.id))
.map(m => m.nickname)
.join('、');
Taro.showModal({
title: '移除成员',
content: `确定要移除「${selectedNames}」吗?移除后他们将无法访问家庭信息。`,
success: (res) => {
if (res.confirm) {
// 从模拟数据中移除选中的成员
mockMembers.value = mockMembers.value.filter(m => !selectedMembers.value.includes(m.id));
selectedMembers.value = [];
Taro.showToast({
title: '移除成功',
icon: 'success'
});
}
}
});
};
// 页面加载时初始化数据
useDidShow(() => {
initPageData();
......