Files
msh-system/msh_single_uniapp/pages/tool/my-profile.vue
msh-agent 560d4de275 fix(ui): 我的页继续瘦身 + 计算结果页客服按钮统一 + 历史页标题运行时回写
my-profile.vue:
- 整段移除「工具与服务」(邀请有礼当前未上线,剩它一项无意义)
- 同步清理 iconGift / goToInvite 残留

calculator-result.vue:
- 「联系专业营养师」按钮 MP-WEIXIN 端改用 <button open-type='contact'>
- 与 welcome-gift / customer-service 统一行为,零 JS API 依赖
- 非小程序端保留原 contactNutritionist 兜底
- contact-btn-mp 重置 button 默认 line-height/border 视觉一致

calculator-history.vue:
- onLoad 中 uni.setNavigationBarTitle 强制回写「我的计算记录」
- 兜底部分开发者工具/旧编译缓存把 UTF-8 标题渲染成乱码的场景

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 03:44:04 +08:00

452 lines
9.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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>
<!-- 我的健康 -->
<view class="section-card">
<view class="section-title">我的健康</view>
<view class="menu-list">
<view class="menu-item" @click="goToDietRecord">
<text class="menu-icon">📊</text>
<text class="menu-text">饮食记录</text>
<view class="menu-badge">{{ dietRecordCount }}</view>
</view>
<!-- 食谱计算历史入口test-0415 反馈2-2 后的产品落地 -->
<view class="menu-item" @click="goToCalculatorHistory">
<text class="menu-icon">🧮</text>
<text class="menu-text">食谱计算历史</text>
</view>
</view>
</view>
<!-- 工具与服务当前所有入口邀请有礼暂未上线整段隐藏 -->
<!-- 退出登录按钮 -->
<view class="logout-btn" @click="handleLogout">
<image class="logout-icon" :src="iconLogout" mode="aspectFit"></image>
<text>退出登录</text>
</view>
<!-- 版本号关于我们入口已隐藏版本信息直接展示在底部 -->
<view class="version-tip">
<text class="version-tip-text">v2.0</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',
iconLogout: 'https://www.figma.com/api/mcp/asset/a4d5597f-0287-4411-a173-1e62db3c6c9f',
userInfo: {
name: '慢友小张',
stage: '透析期',
id: '1001'
},
stats: {
checkin: 0,
points: 0
},
dietRecordCount: 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
};
this.dietRecordCount = streakData.totalCheckins || this.dietRecordCount;
} catch (error) {
console.error('加载个人统计失败:', error);
}
},
// 仅保留已实现入口的方法未开发功能项已从模板移除避免点击只弹「开发中」toast
goToCheckin() {
uni.navigateTo({ url: '/pages/tool/checkin' })
},
goToPoints() {
uni.navigateTo({ url: '/pages/tool/points-rules' })
},
goToDietRecord() {
uni.navigateTo({ url: '/pages/tool/dietary-records' })
},
goToCalculatorHistory() {
uni.navigateTo({ url: '/pages/tool/calculator-history' })
},
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;
}
}
/* 退出登录按钮 */
.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;
}
}
/* 版本号(替代原「关于我们」入口) */
.version-tip {
text-align: center;
padding: 20rpx 0;
}
.version-tip-text {
font-size: 22rpx;
color: #9fa5c0;
}
/* 底部安全距离 */
.safe-bottom {
height: 40rpx;
}
</style>