hookehuyr

feat(MyFamily): 实现家庭列表页面及切换退出功能

添加家庭列表页面完整UI实现,包括:
- 家庭卡片展示封面、名称、成员信息
- 成员头像叠加显示效果
- 当前家庭标记
- 切换家庭和退出家庭功能
- 空状态和底部加入新家庭按钮
更新配置文件描述和样式文件
......@@ -14,6 +14,7 @@ declare module 'vue' {
NutActionSheet: typeof import('@nutui/nutui-taro')['ActionSheet']
NutButton: typeof import('@nutui/nutui-taro')['Button']
NutDatePicker: typeof import('@nutui/nutui-taro')['DatePicker']
NutDialog: typeof import('@nutui/nutui-taro')['Dialog']
NutImagePreview: typeof import('@nutui/nutui-taro')['ImagePreview']
NutInput: typeof import('@nutui/nutui-taro')['Input']
NutPicker: typeof import('@nutui/nutui-taro')['Picker']
......
/*
* @Date: 2025-08-29 10:51:54
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-08-29 10:52:16
* @LastEditTime: 2025-08-29 15:11:39
* @FilePath: /lls_program/src/pages/MyFamily/index.config.js
* @Description: 文件描述
* @Description: 我的家庭页面 - 配置文件
*/
export default {
navigationBarTitleText: '我的家庭',
......
.red {
color: red;
/* 我的家庭页面样式 */
/* 渐变背景覆盖层 */
.bg-gradient-to-t {
background: linear-gradient(to top, rgba(0, 0, 0, 0.6), transparent);
}
/* 成员头像叠加效果 */
.avatar-overlap {
display: flex;
}
.avatar-overlap .avatar-item:not(:first-child) {
margin-left: -10rpx;
}
/* 兼容Tailwind的-space-x-2类名 */
.-space-x-2 > * + * {
margin-left: -10rpx !important;
}
/* 确保头像圆形显示 */
.rounded-full {
border-radius: 50%;
}
/* 按钮悬停效果 */
.bg-blue-500:active {
background-color: #2563eb;
}
.bg-red-500:active {
background-color: #dc2626;
}
/* 卡片阴影效果 */
.shadow-sm {
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
}
/* 固定底部按钮样式 */
.fixed {
position: fixed;
}
/* 空状态图标居中 */
.text-center {
text-align: center;
}
/* 响应式间距 */
.space-y-4 > * + * {
margin-top: 16rpx;
}
.gap-3 {
gap: 12rpx;
}
/* 文本省略 */
.line-clamp-2 {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
......
<!--
* @Date: 2022-09-19 14:11:06
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-08-29 12:47:26
* @LastEditTime: 2025-08-29 15:27:43
* @FilePath: /lls_program/src/pages/MyFamily/index.vue
* @Description: 文件描述
* @Description: 我的家庭页面 - 展示用户加入的家庭列表
-->
<template>
<div class="red">{{ str }}</div>
<view class="min-h-screen bg-gray-50 pb-20">
<!-- 家庭列表 -->
<view class="px-4 py-4 space-y-4">
<view
v-for="family in familyList"
:key="family.id"
class="bg-white rounded-lg overflow-hidden shadow-sm border border-gray-100"
>
<!-- 家庭封面图和基本信息 -->
<view class="relative">
<!-- 当前家庭标记 -->
<view
v-if="family.isCurrent"
class="absolute top-2 right-2 bg-blue-500 text-white text-xs px-2 py-1 rounded-sm z-10"
>
当前家庭
</view>
<!-- 封面图 -->
<image
:src="family.coverImage"
class="w-full h-44 object-cover"
mode="aspectFill"
/>
<!-- 家庭名称和大家长信息覆盖层 -->
<view class="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/60 to-transparent p-4">
<view class="text-white font-bold text-lg mb-1">{{ family.name }}</view>
<view class="text-white/90 text-sm">大家长:{{ family.ownerName }}</view>
</view>
</view>
<!-- 成员信息和操作按钮 -->
<view class="p-4">
<!-- 成员头像和数量 -->
<view class="flex items-center justify-between mb-4">
<view class="flex items-center">
<!-- 成员头像叠加效果 -->
<view class="avatar-overlap">
<image
v-for="(member, index) in family.members.slice(0, 4)"
:key="member.id"
:src="member.avatar"
class="avatar-item w-8 h-8 rounded-full border-2 border-white object-cover"
:style="{ zIndex: 10 - index }"
/>
<!-- 更多成员数量显示 -->
<view
v-if="family.members.length > 4"
class="w-8 h-8 rounded-full bg-gray-300 border-2 border-white flex items-center justify-center text-xs text-gray-600"
:style="{ zIndex: 6 }"
>
+{{ family.members.length - 4 }}
</view>
</view>
<!-- 总成员数 -->
<view class="ml-3 text-sm text-gray-600">
{{ family.members.length }} 位家庭成员
</view>
</view>
</view>
<!-- 操作按钮 -->
<view class="flex gap-3 justify-end">
<view
v-if="!family.isCurrent"
@tap="switchToFamily(family.id)"
class="px-4 py-2 bg-blue-500 text-white text-sm rounded-lg"
>
切换到此家庭
</view>
<view
@tap="exitFamily(family.id)"
class="px-4 py-2 bg-red-500 text-white text-sm rounded-lg"
>
退出家庭
</view>
</view>
</view>
</view>
<!-- 空状态 -->
<view v-if="familyList.length === 0" class="text-center py-12">
<view class="text-gray-400 mb-4">
<Home size="48" />
</view>
<view class="text-gray-500 mb-2">您还没有加入任何家庭</view>
<view class="text-gray-400 text-sm">点击下方按钮加入家庭,开始健康之旅</view>
</view>
</view>
<!-- 底部固定按钮 -->
<view class="fixed bottom-0 left-0 right-0 bg-white border-t border-gray-100 p-4 z-10">
<view
@tap="joinNewFamily"
class="w-full bg-blue-500 text-white text-center py-3 rounded-lg font-medium"
>
+ 加入新家庭
</view>
</view>
<!-- 确认弹窗已替换为Taro.showModal -->
</view>
</template>
<script setup>
// import '@tarojs/taro/html.css'
import { ref } from "vue";
import "./index.less";
import { ref, onMounted } from 'vue';
import Taro from '@tarojs/taro';
import { Home } from '@nutui/icons-vue-taro';
import './index.less';
// 响应式数据
const familyList = ref([]);
/**
* 初始化页面数据
*/
const initPageData = () => {
// 模拟家庭数据
familyList.value = [
{
id: 1,
name: '幸福之家',
ownerName: '张明明',
coverImage: 'https://images.unsplash.com/photo-1511895426328-dc8714191300?w=400&h=200&fit=crop',
isCurrent: 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: '李志强',
coverImage: 'https://images.unsplash.com/photo-1502086223501-7ea6ecd79368?w=400&h=200&fit=crop',
isCurrent: 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: '王芳',
coverImage: 'https://images.unsplash.com/photo-1502086223501-7ea6ecd79368?w=400&h=200&fit=crop',
isCurrent: 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' }
]
},
];
};
/**
* 切换到指定家庭
* @param {number} familyId - 家庭ID
*/
const switchToFamily = (familyId) => {
const family = familyList.value.find(f => f.id === familyId);
if (!family) return;
Taro.showModal({
title: '切换家庭',
content: `确定要切换到「${family.name}」吗?`,
success: (res) => {
if (res.confirm) {
// 切换家庭逻辑 - 先清除所有当前标记,再设置新的当前家庭
familyList.value = familyList.value.map(f => ({
...f,
isCurrent: f.id === familyId
}));
console.log('切换家庭后的列表:', familyList.value);
Taro.showToast({
title: '切换成功',
icon: 'success'
});
}
}
});
};
/**
* 退出家庭
* @param {number} familyId - 家庭ID
*/
const exitFamily = (familyId) => {
const family = familyList.value.find(f => f.id === familyId);
if (!family) return;
Taro.showModal({
title: '退出家庭',
content: `确定要退出「${family.name}」吗?退出后将无法查看该家庭的相关信息。`,
success: (res) => {
if (res.confirm) {
// 退出家庭逻辑
const exitingFamily = familyList.value.find(f => f.id === familyId);
if (exitingFamily?.isCurrent) {
// 如果退出的是当前家庭,需要返回我的页面
familyList.value = familyList.value.filter(f => f.id !== familyId);
Taro.showToast({
title: '已退出家庭',
icon: 'success'
});
// 延迟返回我的页面
setTimeout(() => {
Taro.navigateBack();
}, 1500);
} else {
// 退出非当前家庭
familyList.value = familyList.value.filter(f => f.id !== familyId);
Taro.showToast({
title: '已退出家庭',
icon: 'success'
});
}
}
}
});
};
/**
* 加入新家庭
*/
const joinNewFamily = () => {
Taro.navigateTo({
url: '/pages/JoinFamily/index'
});
};
// 定义响应式数据
const str = ref('我的家庭')
// 页面加载时初始化数据
onMounted(() => {
initPageData();
});
</script>
<script>
export default {
name: "MyFamily",
name: 'MyFamily',
};
</script>
......
/*
* @Date: 2022-09-19 14:11:06
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-08-27 20:41:05
* @LastEditTime: 2025-08-29 14:38:56
* @FilePath: /lls_program/src/utils/config.js
* @Description: 环境配置文件 - 根据小程序运行环境自动切换API地址
*/
......@@ -25,10 +25,10 @@ function getBaseUrl() {
baseUrl = 'https://oa-dev.onwall.cn'; // 体验版暂时使用开发环境
break;
case 'release': // 正式版
baseUrl = 'https://jiangedianlv.onwall.cn';
baseUrl = 'https://walk.onwall.cn';
break;
default: // 未知环境,默认使用正式版地址
baseUrl = 'https://jiangedianlv.onwall.cn';
baseUrl = 'https://walk.onwall.cn';
}
// eslint-disable-next-line no-console
......