Initial commit: MSH System\n\n- msh_single_uniapp: Vue 2 + UniApp 前端(微信小程序/H5/App/支付宝小程序)\n- msh_crmeb_22: Spring Boot 2.2 后端(C端API/管理端/业务逻辑)\n- models-integration: AI服务集成(Coze/KieAI/腾讯ASR)\n- docs: 产品文档与设计稿

This commit is contained in:
2026-02-28 05:40:21 +08:00
commit 14d29d51c0
2182 changed files with 482509 additions and 0 deletions

View File

@@ -0,0 +1,615 @@
<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>