This commit is contained in:
yvan 2025-08-13 21:17:24 +08:00
parent c60787e9d4
commit 58ac884203
1 changed files with 853 additions and 98 deletions

View File

@ -1,58 +1,295 @@
<template> <template>
<view class="container"> <view class="profile-container">
<view class="header"> <!-- 用户信息卡片 -->
<text class="title">我的</text> <view class="user-info-card">
<view class="user-avatar-section">
<view class="avatar-wrapper">
<u-avatar
:src="userInfo.avatarUrl || ''"
:text="userInfo.nickName ? userInfo.nickName.charAt(0) : '👤'"
size="100"
shape="circle"
bg-color="linear-gradient(135deg, #FF8A80, #FFB6C1)"
color="white"
font-size="40"
></u-avatar>
<view class="online-status" v-if="userInfo.nickName">
<view class="status-dot"></view>
</view>
</view>
</view>
<view class="user-info-section">
<view class="user-name">{{ userInfo.nickName || '点击登录' }}</view>
<view class="user-status">{{ userInfo.nickName ? '已登录' : '未登录' }}</view>
<view class="login-days" v-if="userInfo.nickName">已使用 {{ loginDays }} </view>
</view>
<view class="user-action" @click="handleUserAction">
<text class="action-icon">{{ userInfo.nickName ? '⚙️' : '👋' }}</text>
</view>
</view> </view>
<view class="user-info" v-if="userInfo"> <!-- 宠物概览统计卡片 -->
<image class="avatar" :src="userInfo.avatarUrl || '/static/default-avatar.png'" mode="aspectFill"></image> <view class="stats-card">
<text class="nickname">{{ userInfo.nickName || '未登录' }}</text> <view class="stats-header">
<text class="stats-title">我的宠物</text>
<view class="stats-action" @click="navigateTo('/pages/pets/pets')">
<text class="action-text">管理</text>
<text class="action-arrow"></text>
</view>
</view>
<view class="stats-grid">
<view class="stat-item" @click="navigateTo('/pages/pets/pets')">
<view class="stat-number">{{ petStats.petCount }}</view>
<view class="stat-label">宠物</view>
<view class="stat-icon">🐱</view>
</view>
<view class="stat-item" @click="navigateTo('/pages/pets/pet-records')">
<view class="stat-number">{{ petStats.recordCount }}</view>
<view class="stat-label">记录</view>
<view class="stat-icon">📝</view>
</view>
<view class="stat-item" @click="showReminders">
<view class="stat-number">{{ petStats.reminderCount }}</view>
<view class="stat-label">提醒</view>
<view class="stat-icon"></view>
</view>
</view>
</view> </view>
<view class="login-section" v-if="!userInfo"> <!-- 家庭管理卡片 -->
<button class="login-btn" @click="wxLogin" open-type="getUserInfo" @getuserinfo="getUserInfo"> <view class="family-card">
微信登录 <view class="card-header">
</button> <view class="header-left">
<text class="card-icon">👨👩👧👦</text>
<text class="card-title">我的家庭</text>
</view>
<view class="header-right" @click="navigateToFamily">
<text class="member-count">{{ familyStats.memberCount }}</text>
<text class="action-arrow"></text>
</view>
</view>
<view class="family-actions">
<view class="action-btn" @click="inviteFamily">
<text class="btn-icon"></text>
<text class="btn-text">邀请成员</text>
</view>
<view class="action-btn" @click="navigateToFamily">
<text class="btn-icon">👥</text>
<text class="btn-text">家庭设置</text>
</view>
</view>
</view> </view>
<view class="menu-list"> <!-- 领养管理卡片 -->
<view class="menu-item" @click="navigateTo('/pages/profile/settings')"> <view class="adoption-card">
<text class="menu-text">设置</text> <view class="card-header">
<text class="menu-arrow">></text> <view class="header-left">
<text class="card-icon">🏠</text>
<text class="card-title">领养管理</text>
</view>
<view class="header-right" @click="navigateTo('/pages/adoption/my-adoption')">
<text class="action-arrow"></text>
</view>
</view>
<view class="adoption-stats">
<view class="adoption-item" @click="navigateTo('/pages/adoption/my-published')">
<view class="adoption-number">{{ adoptionStats.publishedCount }}</view>
<view class="adoption-label">已发布</view>
</view>
<view class="adoption-item" @click="navigateTo('/pages/adoption/my-applications')">
<view class="adoption-number">{{ adoptionStats.applicationCount }}</view>
<view class="adoption-label">已申请</view>
</view>
</view>
<view class="publish-btn" @click="navigateTo('/pages/adoption/publish')">
<text class="publish-icon">📝</text>
<text class="publish-text">发布领养信息</text>
</view>
</view>
<!-- 设置功能卡片 -->
<view class="settings-card">
<view class="settings-list">
<view class="setting-item" @click="navigateTo('/pages/profile/notifications')">
<view class="setting-left">
<text class="setting-icon">🔔</text>
<text class="setting-text">消息通知</text>
</view>
<view class="setting-right">
<text class="setting-badge" v-if="notificationCount > 0">{{ notificationCount }}</text>
<text class="setting-arrow"></text>
</view>
</view>
<view class="setting-item" @click="navigateTo('/pages/profile/privacy')">
<view class="setting-left">
<text class="setting-icon">🔒</text>
<text class="setting-text">隐私设置</text>
</view>
<view class="setting-right">
<text class="setting-arrow"></text>
</view>
</view>
<view class="setting-item" @click="navigateTo('/pages/profile/feedback')">
<view class="setting-left">
<text class="setting-icon">💬</text>
<text class="setting-text">意见反馈</text>
</view>
<view class="setting-right">
<text class="setting-arrow"></text>
</view>
</view>
<view class="setting-item" @click="navigateTo('/pages/profile/about')">
<view class="setting-left">
<text class="setting-icon"></text>
<text class="setting-text">关于我们</text>
</view>
<view class="setting-right">
<text class="setting-arrow"></text>
</view>
</view> </view>
<view class="menu-item" @click="navigateTo('/pages/profile/about')">
<text class="menu-text">关于我们</text>
<text class="menu-arrow">></text>
</view> </view>
</view> </view>
</view> </view>
</template> </template>
<script> <script>
export default { import { reactive, ref, onMounted, computed } from 'vue'
data() {
return {
userInfo: null
}
},
onLoad() {
this.checkLogin()
},
methods: {
checkLogin() {
//
const userInfo = uni.getStorageSync('userInfo')
if (userInfo) {
this.userInfo = userInfo
}
},
wxLogin() { export default {
name: 'ProfilePage',
setup() {
//
const userInfo = ref({
nickName: '',
avatarUrl: ''
})
const loginDays = ref(0)
const notificationCount = ref(3)
//
const petStats = reactive({
petCount: 0,
recordCount: 0,
reminderCount: 0
})
//
const familyStats = reactive({
memberCount: 1
})
//
const adoptionStats = reactive({
publishedCount: 0,
applicationCount: 0
})
//
const initPage = () => {
checkLogin()
loadUserStats()
}
const checkLogin = () => {
//
const savedUserInfo = uni.getStorageSync('userInfo')
if (savedUserInfo) {
userInfo.value = savedUserInfo
calculateLoginDays()
}
}
const calculateLoginDays = () => {
const loginDate = uni.getStorageSync('loginDate')
if (loginDate) {
const now = new Date()
const login = new Date(loginDate)
const diffTime = Math.abs(now - login)
loginDays.value = Math.ceil(diffTime / (1000 * 60 * 60 * 24))
}
}
const loadUserStats = () => {
try {
//
const pets = uni.getStorageSync('pets') || []
petStats.petCount = pets.length
// -
let totalRecords = 0
pets.forEach(pet => {
const petRecords = uni.getStorageSync(`petRecords_${pet.id}`) || []
totalRecords += petRecords.length
})
petStats.recordCount = totalRecords
//
const today = new Date()
const reminders = uni.getStorageSync('reminders') || []
const pendingReminders = reminders.filter(reminder => {
const reminderDate = new Date(reminder.date)
return reminderDate <= today && !reminder.completed
})
petStats.reminderCount = pendingReminders.length
//
const familyData = uni.getStorageSync('familyData') || { members: [] }
familyStats.memberCount = familyData.members.length || 1
//
const adoptionData = uni.getStorageSync('adoptionData') || {
published: [],
applications: []
}
adoptionStats.publishedCount = adoptionData.published.length
adoptionStats.applicationCount = adoptionData.applications.length
//
const notifications = uni.getStorageSync('notifications') || []
const unreadNotifications = notifications.filter(n => !n.read)
notificationCount.value = unreadNotifications.length
} catch (error) {
console.error('加载用户统计数据失败:', error)
//
petStats.petCount = 0
petStats.recordCount = 0
petStats.reminderCount = 0
familyStats.memberCount = 1
adoptionStats.publishedCount = 0
adoptionStats.applicationCount = 0
}
}
const handleUserAction = () => {
if (userInfo.value.nickName) {
//
navigateTo('/pages/profile/user-info')
} else {
//
wxLogin()
}
}
const wxLogin = () => {
uni.login({ uni.login({
provider: 'weixin', provider: 'weixin',
success: (res) => { success: (res) => {
console.log('登录成功', res) console.log('登录成功', res)
// //
userInfo.value = {
nickName: '宠物主人',
avatarUrl: ''
}
uni.setStorageSync('userInfo', userInfo.value)
uni.setStorageSync('loginDate', new Date().toISOString())
calculateLoginDays()
uni.showToast({
title: '登录成功',
icon: 'success'
})
}, },
fail: (err) => { fail: (err) => {
console.error('登录失败', err) console.error('登录失败', err)
@ -62,87 +299,487 @@ export default {
}) })
} }
}) })
}, }
getUserInfo(e) { const navigateTo = (url) => {
if (e.detail.userInfo) { uni.navigateTo({
this.userInfo = e.detail.userInfo url,
uni.setStorageSync('userInfo', e.detail.userInfo) fail: () => {
uni.showToast({
title: '页面开发中',
icon: 'none'
})
}
})
}
const navigateToFamily = () => {
navigateTo('/pages/profile/family')
}
const inviteFamily = () => {
uni.showModal({
title: '邀请家庭成员',
content: '通过微信分享邀请码邀请家人加入',
confirmText: '分享邀请',
success: (res) => {
if (res.confirm) {
uni.showToast({
title: '功能开发中',
icon: 'none'
})
}
}
})
}
const showReminders = () => {
if (petStats.reminderCount > 0) {
navigateTo('/pages/profile/reminders')
} else {
uni.showToast({
title: '暂无提醒事项',
icon: 'none'
})
}
}
//
const refreshData = () => {
loadUserStats()
}
//
const onShow = () => {
refreshData()
}
//
const onPullDownRefresh = () => {
refreshData()
setTimeout(() => {
uni.stopPullDownRefresh()
}, 1000)
}
//
const formatNumber = (num) => {
if (num >= 1000) {
return (num / 1000).toFixed(1) + 'k'
}
return num.toString()
}
//
const checkNewFeatures = () => {
const lastVersion = uni.getStorageSync('lastAppVersion') || '1.0.0'
const currentVersion = '1.1.0' //
if (lastVersion !== currentVersion) {
//
setTimeout(() => {
uni.showModal({
title: '发现新功能',
content: '新增了家庭共享和领养管理功能,快来体验吧!',
confirmText: '去看看',
success: (res) => {
if (res.confirm) {
uni.setStorageSync('lastAppVersion', currentVersion)
}
}
})
}, 2000)
}
}
//
onMounted(() => {
initPage()
checkNewFeatures()
})
return {
userInfo,
loginDays,
notificationCount,
petStats,
familyStats,
adoptionStats,
handleUserAction,
navigateTo,
navigateToFamily,
inviteFamily,
showReminders,
refreshData,
formatNumber,
onShow,
onPullDownRefresh
} }
}, },
navigateTo(url) { //
uni.navigateTo({ url }) onShow() {
} this.onShow()
},
onPullDownRefresh() {
this.onPullDownRefresh()
} }
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.container { .profile-container {
background: linear-gradient(135deg, #FF8A80 0%, #FFB6C1 25%, #FECFEF 50%, #F8BBD9 100%); background: linear-gradient(135deg, #FF8A80 0%, #FFB6C1 25%, #FECFEF 50%, #F8BBD9 100%);
min-height: 100vh; min-height: 100vh;
padding-bottom: 40rpx;
padding-top: 40rpx;
} }
.user-info { /* 用户信息卡片 */
.user-info-card {
background: rgba(255, 255, 255, 0.95); background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(20rpx); backdrop-filter: blur(20rpx);
margin: 20rpx 30rpx; margin: 0 30rpx 24rpx 30rpx;
border-radius: 24rpx; border-radius: 24rpx;
padding: 40rpx 30rpx; padding: 32rpx;
display: flex; display: flex;
align-items: center; align-items: center;
box-shadow: 0 8rpx 32rpx rgba(255, 138, 128, 0.2); box-shadow: 0 8rpx 32rpx rgba(255, 138, 128, 0.2);
border: 1rpx solid rgba(255, 255, 255, 0.3); border: 1rpx solid rgba(255, 255, 255, 0.3);
transition: all 0.3s ease;
.avatar { &:active {
width: 120rpx; transform: scale(0.98);
height: 120rpx;
border-radius: 60rpx;
margin-right: 30rpx;
box-shadow: 0 4rpx 16rpx rgba(255, 138, 128, 0.2);
} }
.nickname { .user-avatar-section {
font-size: 32rpx; .avatar-wrapper {
position: relative;
.online-status {
position: absolute;
bottom: 8rpx;
right: 8rpx;
width: 24rpx;
height: 24rpx;
background: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
.status-dot {
width: 16rpx;
height: 16rpx;
background: #4CAF50;
border-radius: 50%;
}
}
}
}
.user-info-section {
flex: 1;
margin-left: 24rpx;
.user-name {
font-size: 36rpx;
font-weight: 600; font-weight: 600;
color: #333333;
margin-bottom: 8rpx;
}
.user-status {
font-size: 24rpx;
color: #FF8A80; color: #FF8A80;
margin-bottom: 4rpx;
}
.login-days {
font-size: 22rpx;
color: #999999;
}
}
.user-action {
.action-icon {
font-size: 32rpx;
}
} }
} }
.login-section { /* 宠物概览统计卡片 */
.stats-card {
background: rgba(255, 255, 255, 0.95); background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(20rpx); backdrop-filter: blur(20rpx);
margin: 20rpx 30rpx; margin: 0 30rpx 24rpx 30rpx;
border-radius: 24rpx; border-radius: 24rpx;
padding: 40rpx 30rpx; padding: 32rpx;
box-shadow: 0 8rpx 32rpx rgba(255, 138, 128, 0.2); box-shadow: 0 8rpx 32rpx rgba(255, 138, 128, 0.2);
border: 1rpx solid rgba(255, 255, 255, 0.3); border: 1rpx solid rgba(255, 255, 255, 0.3);
.login-btn { .stats-header {
background: linear-gradient(135deg, #FF8A80 0%, #FFB6C1 100%); display: flex;
color: white; justify-content: space-between;
border-radius: 24rpx; align-items: center;
margin-bottom: 24rpx;
.stats-title {
font-size: 32rpx; font-size: 32rpx;
box-shadow: 0 4rpx 16rpx rgba(255, 138, 128, 0.4); font-weight: 600;
border: none; color: #333333;
}
.stats-action {
display: flex;
align-items: center;
.action-text {
font-size: 24rpx;
color: #FF8A80;
margin-right: 8rpx;
}
.action-arrow {
font-size: 24rpx;
color: #FF8A80;
}
}
}
.stats-grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 24rpx;
.stat-item {
text-align: center;
padding: 24rpx 16rpx;
border-radius: 16rpx;
background: rgba(255, 138, 128, 0.05);
transition: all 0.3s ease;
&:active {
transform: scale(0.95);
background: rgba(255, 138, 128, 0.1);
}
.stat-number {
font-size: 40rpx;
font-weight: 700;
color: #FF8A80;
margin-bottom: 8rpx;
}
.stat-label {
font-size: 22rpx;
color: #666666;
margin-bottom: 8rpx;
}
.stat-icon {
font-size: 28rpx;
}
}
} }
} }
.menu-list { /* 家庭管理卡片 */
.family-card {
background: rgba(255, 255, 255, 0.95); background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(20rpx); backdrop-filter: blur(20rpx);
margin: 20rpx 30rpx; margin: 0 30rpx 24rpx 30rpx;
border-radius: 24rpx;
padding: 32rpx;
box-shadow: 0 8rpx 32rpx rgba(255, 138, 128, 0.2);
border: 1rpx solid rgba(255, 255, 255, 0.3);
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24rpx;
.header-left {
display: flex;
align-items: center;
.card-icon {
font-size: 32rpx;
margin-right: 12rpx;
}
.card-title {
font-size: 32rpx;
font-weight: 600;
color: #333333;
}
}
.header-right {
display: flex;
align-items: center;
.member-count {
font-size: 24rpx;
color: #FF8A80;
margin-right: 8rpx;
}
.action-arrow {
font-size: 24rpx;
color: #FF8A80;
}
}
}
.family-actions {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16rpx;
.action-btn {
display: flex;
flex-direction: column;
align-items: center;
padding: 24rpx 16rpx;
border-radius: 16rpx;
background: rgba(255, 138, 128, 0.05);
transition: all 0.3s ease;
&:active {
transform: scale(0.95);
background: rgba(255, 138, 128, 0.1);
}
.btn-icon {
font-size: 32rpx;
margin-bottom: 8rpx;
}
.btn-text {
font-size: 22rpx;
color: #666666;
}
}
}
}
/* 领养管理卡片 */
.adoption-card {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(20rpx);
margin: 0 30rpx 24rpx 30rpx;
border-radius: 24rpx;
padding: 32rpx;
box-shadow: 0 8rpx 32rpx rgba(255, 138, 128, 0.2);
border: 1rpx solid rgba(255, 255, 255, 0.3);
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24rpx;
.header-left {
display: flex;
align-items: center;
.card-icon {
font-size: 32rpx;
margin-right: 12rpx;
}
.card-title {
font-size: 32rpx;
font-weight: 600;
color: #333333;
}
}
.header-right {
.action-arrow {
font-size: 24rpx;
color: #FF8A80;
}
}
}
.adoption-stats {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16rpx;
margin-bottom: 24rpx;
.adoption-item {
text-align: center;
padding: 20rpx 16rpx;
border-radius: 16rpx;
background: rgba(255, 138, 128, 0.05);
transition: all 0.3s ease;
&:active {
transform: scale(0.95);
background: rgba(255, 138, 128, 0.1);
}
.adoption-number {
font-size: 36rpx;
font-weight: 700;
color: #FF8A80;
margin-bottom: 8rpx;
}
.adoption-label {
font-size: 22rpx;
color: #666666;
}
}
}
.publish-btn {
display: flex;
align-items: center;
justify-content: center;
padding: 20rpx;
border-radius: 16rpx;
background: linear-gradient(135deg, #FF8A80, #FFB6C1);
transition: all 0.3s ease;
&:active {
transform: scale(0.98);
}
.publish-icon {
font-size: 24rpx;
margin-right: 8rpx;
}
.publish-text {
font-size: 26rpx;
color: white;
font-weight: 500;
}
}
}
/* 设置功能卡片 */
.settings-card {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(20rpx);
margin: 0 30rpx 24rpx 30rpx;
border-radius: 24rpx; border-radius: 24rpx;
box-shadow: 0 8rpx 32rpx rgba(255, 138, 128, 0.2); box-shadow: 0 8rpx 32rpx rgba(255, 138, 128, 0.2);
border: 1rpx solid rgba(255, 255, 255, 0.3); border: 1rpx solid rgba(255, 255, 255, 0.3);
overflow: hidden; overflow: hidden;
.menu-item { .settings-list {
.setting-item {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 40rpx 30rpx; padding: 32rpx;
border-bottom: 1rpx solid rgba(255, 255, 255, 0.3); border-bottom: 1rpx solid rgba(255, 255, 255, 0.3);
transition: all 0.3s ease; transition: all 0.3s ease;
@ -151,20 +788,138 @@ export default {
} }
&:active { &:active {
background: rgba(255, 138, 128, 0.1); background: rgba(255, 138, 128, 0.05);
} }
.menu-text { .setting-left {
display: flex;
align-items: center;
.setting-icon {
font-size: 32rpx;
margin-right: 16rpx;
}
.setting-text {
font-size: 30rpx; font-size: 30rpx;
color: #333333; color: #333333;
font-weight: 500; font-weight: 500;
} }
}
.menu-arrow { .setting-right {
font-size: 28rpx; display: flex;
align-items: center;
.setting-badge {
background: #FF8A80;
color: white;
font-size: 20rpx;
padding: 4rpx 12rpx;
border-radius: 20rpx;
margin-right: 12rpx;
min-width: 32rpx;
text-align: center;
}
.setting-arrow {
font-size: 24rpx;
color: #FF8A80; color: #FF8A80;
font-weight: 600; }
}
} }
} }
} }
/* 响应式设计 */
@media (max-width: 375px) {
.profile-container {
.user-info-card {
margin: 0 20rpx 20rpx 20rpx;
padding: 24rpx;
}
.stats-card,
.family-card,
.adoption-card,
.settings-card {
margin: 0 20rpx 20rpx 20rpx;
padding: 24rpx;
}
.stats-grid {
gap: 16rpx;
.stat-item {
padding: 20rpx 12rpx;
.stat-number {
font-size: 36rpx;
}
}
}
.family-actions {
gap: 12rpx;
.action-btn {
padding: 20rpx 12rpx;
}
}
}
}
/* 加载状态 */
.loading-state {
display: flex;
justify-content: center;
align-items: center;
padding: 60rpx;
.loading-text {
font-size: 24rpx;
color: #999999;
margin-left: 16rpx;
}
}
/* 空状态 */
.empty-state {
text-align: center;
padding: 60rpx 40rpx;
.empty-icon {
font-size: 80rpx;
margin-bottom: 16rpx;
opacity: 0.5;
}
.empty-text {
font-size: 24rpx;
color: #999999;
line-height: 1.5;
}
}
/* 动画效果 */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20rpx);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.profile-container > view {
animation: fadeIn 0.5s ease-out;
}
.profile-container > view:nth-child(1) { animation-delay: 0.1s; }
.profile-container > view:nth-child(2) { animation-delay: 0.2s; }
.profile-container > view:nth-child(3) { animation-delay: 0.3s; }
.profile-container > view:nth-child(4) { animation-delay: 0.4s; }
.profile-container > view:nth-child(5) { animation-delay: 0.5s; }
</style> </style>