616 lines
14 KiB
Vue
616 lines
14 KiB
Vue
<template>
|
||
<view class="my-profile-page">
|
||
<!-- 用户信息 -->
|
||
<view class="profile-header">
|
||
<view class="user-info-section">
|
||
<view class="user-avatar">
|
||
<text class="avatar-icon">👤</text>
|
||
</view>
|
||
<view class="user-details">
|
||
<view class="user-name-row">
|
||
<text class="user-name">{{ userInfo.name }}</text>
|
||
<view class="verified-badge">
|
||
<image class="verified-icon" :src="iconVerified" mode="aspectFit"></image>
|
||
</view>
|
||
</view>
|
||
<view class="user-tags">
|
||
<view class="user-tag">{{ userInfo.stage }}</view>
|
||
<text class="user-id">ID: {{ userInfo.id }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 内容区域 -->
|
||
<scroll-view class="content-scroll" scroll-y>
|
||
<!-- 统计数据卡片 -->
|
||
<view class="stats-card">
|
||
<view class="stat-item" @click="goToCheckin">
|
||
<text class="stat-value">{{ stats.checkin }}</text>
|
||
<text class="stat-label">打卡</text>
|
||
</view>
|
||
<view class="stat-item" @click="goToPoints">
|
||
<text class="stat-value">{{ stats.points }}</text>
|
||
<text class="stat-label">积分</text>
|
||
</view>
|
||
<view class="stat-item" @click="goToFollowing">
|
||
<text class="stat-value">{{ stats.following }}</text>
|
||
<text class="stat-label">关注</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 我的健康 -->
|
||
<view class="section-card">
|
||
<view class="section-title">我的健康</view>
|
||
<view class="menu-list">
|
||
<view class="menu-item" @click="goToHealthRecord">
|
||
<text class="menu-icon">🏥</text>
|
||
<text class="menu-text">健康档案</text>
|
||
</view>
|
||
<view class="menu-item" @click="goToDietRecord">
|
||
<text class="menu-icon">📊</text>
|
||
<text class="menu-text">饮食记录</text>
|
||
<view class="menu-badge">{{ dietRecordCount }}次</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 我的内容 -->
|
||
<view class="section-card">
|
||
<view class="section-title">我的内容</view>
|
||
<view class="menu-list">
|
||
<view class="menu-item" @click="goToMyLikes">
|
||
<image class="menu-icon-img" :src="iconLike" mode="aspectFit"></image>
|
||
<text class="menu-text">我的点赞</text>
|
||
<view class="menu-badge">{{ myLikesCount }}</view>
|
||
</view>
|
||
<view class="menu-item" @click="goToMyCollections">
|
||
<image class="menu-icon-img" :src="iconCollection" mode="aspectFit"></image>
|
||
<text class="menu-text">我的收藏</text>
|
||
<view class="menu-badge">{{ myCollectionsCount }}</view>
|
||
</view>
|
||
<view class="menu-item" @click="goToMyRecipes">
|
||
<text class="menu-icon">📝</text>
|
||
<text class="menu-text">我的食谱</text>
|
||
<view class="menu-badge">{{ myRecipesCount }}</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 工具与服务 -->
|
||
<view class="section-card">
|
||
<view class="section-title">工具与服务</view>
|
||
<view class="menu-list">
|
||
<view class="menu-item" @click="goToNutritionist">
|
||
<text class="menu-icon">👨⚕️</text>
|
||
<text class="menu-text">我的营养师</text>
|
||
<view class="menu-badge online">在线</view>
|
||
</view>
|
||
<view class="menu-item" @click="goToInvite">
|
||
<image class="menu-icon-img" :src="iconGift" mode="aspectFit"></image>
|
||
<text class="menu-text">邀请有礼</text>
|
||
</view>
|
||
<view class="menu-item" @click="goToNotifications">
|
||
<image class="menu-icon-img" :src="iconNotification" mode="aspectFit"></image>
|
||
<text class="menu-text">消息通知</text>
|
||
<view class="menu-badge">{{ notificationCount }}</view>
|
||
</view>
|
||
<view class="menu-item" @click="goToNutritionGuide">
|
||
<text class="menu-icon">🥗</text>
|
||
<text class="menu-text">营养指导</text>
|
||
</view>
|
||
<view class="menu-item" @click="goToMedicationGuide">
|
||
<text class="menu-icon">💊</text>
|
||
<text class="menu-text">用药指导</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 设置 -->
|
||
<view class="section-card">
|
||
<view class="section-title">设置</view>
|
||
<view class="menu-list">
|
||
<view class="menu-item" @click="goToAccountSettings">
|
||
<image class="menu-icon-img" :src="iconSettings" mode="aspectFit"></image>
|
||
<text class="menu-text">账号设置</text>
|
||
</view>
|
||
<view class="menu-item" @click="goToPrivacySettings">
|
||
<image class="menu-icon-img" :src="iconPrivacy" mode="aspectFit"></image>
|
||
<text class="menu-text">隐私设置</text>
|
||
</view>
|
||
<view class="menu-item" @click="goToHelp">
|
||
<image class="menu-icon-img" :src="iconHelp" mode="aspectFit"></image>
|
||
<text class="menu-text">帮助与反馈</text>
|
||
</view>
|
||
<view class="menu-item" @click="goToAbout">
|
||
<text class="menu-icon">ℹ️</text>
|
||
<text class="menu-text">关于我们</text>
|
||
<text class="version-text">v2.0</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 退出登录按钮 -->
|
||
<view class="logout-btn" @click="handleLogout">
|
||
<image class="logout-icon" :src="iconLogout" mode="aspectFit"></image>
|
||
<text>退出登录</text>
|
||
</view>
|
||
|
||
<!-- 底部安全距离 -->
|
||
<view class="safe-bottom"></view>
|
||
</scroll-view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import { getUserPoints, getCheckinStreak } from '@/api/tool.js';
|
||
import { mapGetters } from 'vuex';
|
||
|
||
export default {
|
||
computed: {
|
||
...mapGetters({ storeUserInfo: 'userInfo', uid: 'uid', isLogin: 'isLogin' })
|
||
},
|
||
data() {
|
||
return {
|
||
iconVerified: 'https://www.figma.com/api/mcp/asset/e06ebfe5-bf8d-40e7-9b5f-a395c823127b',
|
||
iconLike: 'https://www.figma.com/api/mcp/asset/dd7b88ff-b674-48b4-80f9-059f6205709e',
|
||
iconCollection: 'https://www.figma.com/api/mcp/asset/3e07edd4-5f53-4d38-8de9-ef920713385f',
|
||
iconGift: 'https://www.figma.com/api/mcp/asset/afaeb0a1-7276-4a8c-87c0-ba4dc4ce3eba',
|
||
iconNotification: 'https://www.figma.com/api/mcp/asset/5b415d37-7e7d-4069-b714-cc8dad364bb4',
|
||
iconSettings: 'https://www.figma.com/api/mcp/asset/a6107d1c-4643-4a9c-aa57-2f90ba8c125c',
|
||
iconPrivacy: 'https://www.figma.com/api/mcp/asset/e72b9b89-6013-4c6f-b19f-95b9042f03bb',
|
||
iconHelp: 'https://www.figma.com/api/mcp/asset/1d05ae21-1147-4e48-83ac-3357447cc199',
|
||
iconLogout: 'https://www.figma.com/api/mcp/asset/a4d5597f-0287-4411-a173-1e62db3c6c9f',
|
||
userInfo: {
|
||
name: '慢友小张',
|
||
stage: '透析期',
|
||
id: '1001'
|
||
},
|
||
stats: {
|
||
checkin: 0,
|
||
points: 0,
|
||
following: 0
|
||
},
|
||
dietRecordCount: 0,
|
||
myLikesCount: 0,
|
||
myCollectionsCount: 0,
|
||
myRecipesCount: 0,
|
||
notificationCount: 0
|
||
}
|
||
},
|
||
onLoad() {
|
||
this.syncUserInfo();
|
||
this.loadProfileStats();
|
||
},
|
||
onShow() {
|
||
this.syncUserInfo();
|
||
this.loadProfileStats();
|
||
},
|
||
methods: {
|
||
syncUserInfo() {
|
||
if (this.isLogin && this.storeUserInfo) {
|
||
this.userInfo = {
|
||
name: this.storeUserInfo.nickname || '慢友小张',
|
||
stage: this.storeUserInfo.level || '透析期',
|
||
id: this.uid || '1001'
|
||
}
|
||
}
|
||
},
|
||
async loadProfileStats() {
|
||
try {
|
||
const [pointsRes, streakRes] = await Promise.all([
|
||
getUserPoints().catch(() => ({ data: {} })),
|
||
getCheckinStreak().catch(() => ({ data: {} }))
|
||
]);
|
||
const pointsData = pointsRes.data || {};
|
||
const streakData = streakRes.data || {};
|
||
this.stats = {
|
||
checkin: streakData.totalCheckins || streakData.currentStreak || this.stats.checkin,
|
||
points: pointsData.points || pointsData.totalPoints || this.stats.points,
|
||
following: pointsData.followingCount || this.stats.following
|
||
};
|
||
this.dietRecordCount = streakData.totalCheckins || this.dietRecordCount;
|
||
} catch (error) {
|
||
console.error('加载个人统计失败:', error);
|
||
}
|
||
},
|
||
goToCheckin() {
|
||
uni.navigateTo({
|
||
url: '/pages/tool/checkin'
|
||
})
|
||
},
|
||
goToPoints() {
|
||
uni.navigateTo({
|
||
url: '/pages/tool/points-rules'
|
||
})
|
||
},
|
||
goToFollowing() {
|
||
uni.showToast({
|
||
title: '关注页面开发中',
|
||
icon: 'none'
|
||
})
|
||
},
|
||
goToHealthRecord() {
|
||
uni.showToast({
|
||
title: '健康档案功能开发中',
|
||
icon: 'none'
|
||
})
|
||
},
|
||
goToDietRecord() {
|
||
uni.navigateTo({
|
||
url: '/pages/tool/dietary-records'
|
||
})
|
||
},
|
||
goToMyLikes() {
|
||
uni.showToast({
|
||
title: '我的点赞功能开发中',
|
||
icon: 'none'
|
||
})
|
||
},
|
||
goToMyCollections() {
|
||
uni.showToast({
|
||
title: '我的收藏功能开发中',
|
||
icon: 'none'
|
||
})
|
||
},
|
||
goToMyRecipes() {
|
||
uni.showToast({
|
||
title: '我的食谱功能开发中',
|
||
icon: 'none'
|
||
})
|
||
},
|
||
goToNutritionist() {
|
||
uni.showToast({
|
||
title: '我的营养师功能开发中',
|
||
icon: 'none'
|
||
})
|
||
},
|
||
goToInvite() {
|
||
uni.navigateTo({
|
||
url: '/pages/tool/invite-rewards'
|
||
})
|
||
},
|
||
goToNotifications() {
|
||
uni.showToast({
|
||
title: '消息通知功能开发中',
|
||
icon: 'none'
|
||
})
|
||
},
|
||
goToNutritionGuide() {
|
||
uni.showToast({
|
||
title: '营养指导功能开发中',
|
||
icon: 'none'
|
||
})
|
||
},
|
||
goToMedicationGuide() {
|
||
uni.showToast({
|
||
title: '用药指导功能开发中',
|
||
icon: 'none'
|
||
})
|
||
},
|
||
goToAccountSettings() {
|
||
uni.showToast({
|
||
title: '账号设置功能开发中',
|
||
icon: 'none'
|
||
})
|
||
},
|
||
goToPrivacySettings() {
|
||
uni.showToast({
|
||
title: '隐私设置功能开发中',
|
||
icon: 'none'
|
||
})
|
||
},
|
||
goToHelp() {
|
||
uni.showToast({
|
||
title: '帮助与反馈功能开发中',
|
||
icon: 'none'
|
||
})
|
||
},
|
||
goToAbout() {
|
||
uni.showToast({
|
||
title: '关于我们功能开发中',
|
||
icon: 'none'
|
||
})
|
||
},
|
||
handleLogout() {
|
||
uni.showModal({
|
||
title: '提示',
|
||
content: '确定要退出登录吗?',
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
// 清除本地存储的登录信息
|
||
uni.removeStorageSync('token')
|
||
uni.removeStorageSync('userInfo')
|
||
uni.removeStorageSync('uid')
|
||
uni.removeStorageSync('expires_time')
|
||
|
||
// 清除 Vuex 状态
|
||
this.$store.commit('LOGOUT')
|
||
|
||
uni.showToast({
|
||
title: '退出登录成功',
|
||
icon: 'success'
|
||
})
|
||
|
||
// 跳转到登录页或首页
|
||
setTimeout(() => {
|
||
uni.reLaunch({
|
||
url: '/pages/index/index'
|
||
})
|
||
}, 1500)
|
||
}
|
||
}
|
||
})
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.my-profile-page {
|
||
min-height: 100vh;
|
||
background: linear-gradient(180deg, #f8f9fa 0%, #e9ecef 100%);
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
/* 顶部导航栏和用户信息 */
|
||
.profile-header {
|
||
background: linear-gradient(135deg, #ff8c52 0%, #ff7f50 50%, #ff6b35 100%);
|
||
padding-top: 0;
|
||
position: relative;
|
||
overflow: hidden;
|
||
|
||
&::before {
|
||
content: '';
|
||
position: absolute;
|
||
right: -80rpx;
|
||
top: -80rpx;
|
||
width: 384rpx;
|
||
height: 384rpx;
|
||
background: rgba(255, 255, 255, 0.1);
|
||
border-radius: 50%;
|
||
filter: blur(60rpx);
|
||
}
|
||
|
||
&::after {
|
||
content: '';
|
||
position: absolute;
|
||
left: -80rpx;
|
||
bottom: -80rpx;
|
||
width: 320rpx;
|
||
height: 320rpx;
|
||
background: rgba(0, 0, 0, 0.05);
|
||
border-radius: 50%;
|
||
filter: blur(40rpx);
|
||
}
|
||
}
|
||
|
||
.user-info-section {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 1rpx;
|
||
padding: calc(var(--status-bar-height, 0) + 1rpx) 32rpx 32rpx;
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
|
||
.user-avatar {
|
||
width: 128rpx;
|
||
height: 128rpx; margin-right: 24rpx;
|
||
background: #ffffff;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
box-shadow: 0 40rpx 50rpx rgba(0, 0, 0, 0.1), 0 16rpx 20rpx rgba(0, 0, 0, 0.1);
|
||
|
||
.avatar-icon {
|
||
font-size: 60rpx;
|
||
}
|
||
}
|
||
|
||
.user-details {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 20rpx;
|
||
}
|
||
|
||
.user-name-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 16rpx;
|
||
|
||
.user-name {
|
||
font-size: 36rpx;
|
||
color: #ffffff;
|
||
font-weight: 500;
|
||
text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.15);
|
||
}
|
||
|
||
.verified-badge {
|
||
width: 32rpx;
|
||
height: 32rpx;
|
||
background: #ff6b35;
|
||
border: 2rpx solid #ffffff;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
|
||
.verified-icon {
|
||
width: 20rpx;
|
||
height: 20rpx;
|
||
}
|
||
}
|
||
}
|
||
|
||
.user-tags {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 16rpx;
|
||
|
||
.user-tag {
|
||
background: rgba(255, 255, 255, 0.2);
|
||
border: 1rpx solid rgba(255, 255, 255, 0.3);
|
||
border-radius: 50rpx;
|
||
padding: 4rpx 20rpx;
|
||
font-size: 24rpx;
|
||
color: #ffffff;
|
||
}
|
||
|
||
.user-id {
|
||
font-size: 24rpx;
|
||
color: rgba(255, 255, 255, 0.8);
|
||
text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
||
}
|
||
}
|
||
|
||
/* 内容滚动区域 */
|
||
.content-scroll {
|
||
flex: 1;
|
||
padding: 0;
|
||
}
|
||
|
||
/* 统计数据卡片 */
|
||
.stats-card {
|
||
background: #ffffff;
|
||
border-radius: 32rpx;
|
||
padding: 20rpx;
|
||
margin: 32rpx 32rpx 0;
|
||
display: flex;
|
||
justify-content: space-around;
|
||
box-shadow: 0 20rpx 30rpx rgba(0, 0, 0, 0.1), 0 8rpx 12rpx rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.stat-item {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 8rpx;
|
||
flex: 1;
|
||
|
||
.stat-value {
|
||
font-size: 40rpx;
|
||
color: #1e2939;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.stat-label {
|
||
font-size: 24rpx;
|
||
color: #6a7282;
|
||
}
|
||
}
|
||
|
||
/* 通用卡片样式 */
|
||
.section-card {
|
||
background: #ffffff;
|
||
border: 1rpx solid #e8eaed;
|
||
border-radius: 24rpx;
|
||
margin: 32rpx;
|
||
padding: 24rpx;
|
||
}
|
||
|
||
.section-title {
|
||
font-size: 28rpx;
|
||
color: #9fa5c0;
|
||
margin-bottom: 16rpx;
|
||
padding-left: 16rpx;
|
||
}
|
||
|
||
.menu-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8rpx;
|
||
}
|
||
|
||
.menu-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 24rpx;
|
||
padding: 8rpx 16rpx;
|
||
border-radius: 16rpx;
|
||
min-height: 80rpx;
|
||
transition: background-color 0.2s;
|
||
|
||
&:active {
|
||
background: #f8f9fa;
|
||
}
|
||
}
|
||
|
||
.menu-icon {
|
||
font-size: 40rpx;
|
||
width: 40rpx;
|
||
height: 40rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.menu-icon-img {
|
||
width: 40rpx;
|
||
height: 40rpx;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.menu-text {
|
||
flex: 1;
|
||
font-size: 28rpx;
|
||
color: #2e3e5c;
|
||
}
|
||
|
||
.menu-badge {
|
||
background: linear-gradient(180deg, #fff5f0 0%, #ffe8dc 100%);
|
||
border: 1rpx solid rgba(255, 107, 53, 0.3);
|
||
border-radius: 50rpx;
|
||
padding: 4rpx 16rpx;
|
||
font-size: 24rpx;
|
||
color: #ff6b35;
|
||
flex-shrink: 0;
|
||
|
||
&.online {
|
||
background: linear-gradient(180deg, #fff5f0 0%, #ffe8dc 100%);
|
||
border: 1rpx solid rgba(255, 107, 53, 0.3);
|
||
color: #ff6b35;
|
||
}
|
||
}
|
||
|
||
.version-text {
|
||
font-size: 24rpx;
|
||
color: #9fa5c0;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
/* 退出登录按钮 */
|
||
.logout-btn {
|
||
background: #ffffff;
|
||
border-radius: 24rpx;
|
||
height: 96rpx;
|
||
margin: 0 32rpx 32rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 16rpx;
|
||
box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.1);
|
||
|
||
.logout-icon {
|
||
width: 40rpx;
|
||
height: 40rpx;
|
||
}
|
||
|
||
text {
|
||
font-size: 32rpx;
|
||
color: #364153;
|
||
font-weight: 500;
|
||
}
|
||
}
|
||
|
||
/* 底部安全距离 */
|
||
.safe-bottom {
|
||
height: 40rpx;
|
||
}
|
||
</style>
|
||
|