AssetManager.UniApp/pages/me/me.vue
niannian zheng dc857a98a3 refactor(auth): 重构用户认证逻辑并迁移至Pinia存储
将微信静默登录逻辑从App.vue迁移至Pinia的user store,实现状态集中管理
全局注册api模块并优化请求重试机制,增强错误处理和日志记录
移除App.vue中的冗余代码,简化页面组件的数据获取方式
2026-02-26 13:33:57 +08:00

181 lines
5.9 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="page-container">
<view class="profile-header">
<view class="avatar-container">
<!-- 如果有头像URL使用图片显示 -->
<image v-if="userInfo.avatar" :src="userInfo.avatar" class="avatar-image" mode="aspectFill"></image>
<view v-else class="avatar-circle"></view>
<view class="online-badge"></view>
</view>
<text class="user-name">{{ userInfo.userName || '加载中...' }}</text>
<text class="user-info">会员等级: {{ userInfo.memberLevel || '普通会员' }} | 连续运行 {{ userInfo.runningDays || 0 }}天</text>
<text v-if="userInfo.email" class="user-email">{{ userInfo.email }}</text>
</view>
<view class="stats-grid">
<view class="stat-card">
<text class="stat-label">已记录事件</text>
<text class="stat-val">{{ userStats.signalsCaptured?.toLocaleString() || 0 }}</text>
</view>
<view class="stat-card">
<text class="stat-label">胜率</text>
<text class="stat-val">{{ userStats.winRate || 0 }}%</text>
</view>
<view class="stat-card">
<text class="stat-label">总交易数</text>
<text class="stat-val">{{ userStats.totalTrades || 0 }}</text>
</view>
<view class="stat-card">
<text class="stat-label">平均收益</text>
<text class="stat-val">{{ userStats.averageProfit || 0 }}%</text>
</view>
</view>
<view class="menu-list">
<view class="menu-item">
<view class="flex-row items-center gap-3">
<uni-icons type="checkbox" size="20" color="#9CA3AF"></uni-icons>
<text class="menu-text">账户安全中心</text>
</view>
<uni-icons type="right" size="16" color="#D1D5DB"></uni-icons>
</view>
<view class="menu-item">
<view class="flex-row items-center gap-3">
<uni-icons type="gear" size="20" color="#9CA3AF"></uni-icons>
<text class="menu-text">全局执行偏好</text>
</view>
<uni-icons type="right" size="16" color="#D1D5DB"></uni-icons>
</view>
<view class="menu-item">
<view class="flex-row items-center gap-3">
<uni-icons type="staff" size="20" color="#EF4444"></uni-icons>
<text class="menu-text text-red">退出登录</text>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref, onMounted, getCurrentInstance } from 'vue';
// 用户信息
const userInfo = ref({
userName: '',
memberLevel: '',
runningDays: 0
});
// 用户统计数据
const userStats = ref({
signalsCaptured: 0,
winRate: 0
});
// 获取全局api对象
const getApi = () => {
const instance = getCurrentInstance();
return instance?.appContext.config.globalProperties.$api;
};
// 从后端API获取用户信息的函数
const fetchUserInfo = async () => {
try {
const api = getApi();
if (!api || !api.user) {
console.error('API模块未加载');
return;
}
const response = await api.user.getUserInfo();
if (response.code === 200) {
userInfo.value = response.data;
console.log('用户信息获取成功:', response.data);
}
} catch (error) {
console.error('获取用户信息失败:', error);
}
};
// 从后端API获取用户统计数据的函数
const fetchUserStats = async () => {
try {
const api = getApi();
if (!api || !api.user) {
console.error('API模块未加载');
return;
}
const response = await api.user.getUserStats();
if (response.code === 200) {
userStats.value = response.data;
console.log('用户统计数据获取成功:', response.data);
}
} catch (error) {
console.error('获取用户统计数据失败:', error);
}
};
// 页面加载时获取数据
onMounted(async () => {
console.log('个人中心页面加载,开始获取数据...');
await Promise.all([
fetchUserInfo(),
fetchUserStats()
]);
});
</script>
<style scoped>
.page-container {
min-height: 100vh;
padding: 40rpx;
padding-top: 100rpx;
background-color: #F9FAFB;
/* 浅灰色背景,与其他页面保持一致 */
}
.profile-header { display: flex; flex-direction: column; align-items: center; margin-bottom: 60rpx; }
.avatar-container { position: relative; margin-bottom: 24rpx; }
.avatar-circle { width: 160rpx; height: 160rpx; border-radius: 50%; background-color: #E5E7EB; border: 4rpx solid #fff; box-shadow: 0 10rpx 20rpx rgba(0,0,0,0.1); }
.avatar-image { width: 160rpx; height: 160rpx; border-radius: 50%; border: 4rpx solid #fff; box-shadow: 0 10rpx 20rpx rgba(0,0,0,0.1); }
.online-badge { width: 32rpx; height: 32rpx; background-color: #10B981; border: 4rpx solid #fff; border-radius: 50%; position: absolute; bottom: 8rpx; right: 8rpx; }
.user-name { font-size: 40rpx; font-weight: 700; color: #111827; }
.user-info { font-size: 24rpx; color: #9CA3AF; margin-top: 8rpx; }
.user-email { font-size: 20rpx; color: #6B7280; margin-top: 4rpx; }
.stats-grid { display: flex; gap: 32rpx; margin-bottom: 64rpx; }
.stat-card {
flex: 1;
background: #FFFFFF;
padding: 32rpx;
border-radius: 32rpx;
text-align: center;
border: 1rpx solid #E5E7EB;
box-shadow: 0 12rpx 32rpx rgba(0, 0, 0, 0.06);
transition: all 0.2s ease;
}
.stat-card:active {
transform: translateY(2rpx);
box-shadow: 0 6rpx 16rpx rgba(0, 0, 0, 0.04);
}
.stat-label { font-size: 20rpx; color: #9CA3AF; display: block; margin-bottom: 8rpx; }
.stat-val { font-size: 36rpx; font-weight: 700; color: #111827; }
.menu-list {
background: #FFFFFF;
border-radius: 32rpx;
padding: 0 32rpx;
border: 1rpx solid #E5E7EB;
box-shadow: 0 12rpx 32rpx rgba(0, 0, 0, 0.06);
}
.menu-item { display: flex; justify-content: space-between; align-items: center; padding: 32rpx 0; border-bottom: 1rpx solid #F3F4F6; }
.menu-item:last-child { border-bottom: none; }
.menu-text { font-size: 28rpx; font-weight: 500; color: #374151; margin-left: 20rpx; }
.text-red { color: #EF4444; }
</style>