Files
-----/scoring/pages/my/my.vue
2025-11-11 17:07:13 +08:00

496 lines
11 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">
<!-- 用户信息区域 -->
<view class="user-info">
<view class="background">
</view>
<view class="info-content">
<view class="top-user-name" :style="{'height': topHeight, 'line-height': topHeight}">
{{userInfo.nickName}}
</view>
<view class="avatars">
<image class="avatar-img" :src="userInfo.avatarUrl ? userInfo.avatarUrl : 'https://pic.rmb.bdstatic.com/bjh/down/1742bc3845cbbcf0c78c01eb59bb1c1a.jpeg'" @click="updateAvatar"></image>
</view>
<view class="hello-text">
<p>你好{{userInfo.nickName}}</p>
<p>欢迎使用计分小程序</p>
</view>
</view>
</view>
<!-- 数据统计区域 -->
<view class="data-section">
<view class="data-item" @click="navigateToHistory">
<text class="data-label">总对局数</text>
<text class="data-number">{{totalGames || 0}}</text>
</view>
<view class="data-divider"></view>
<view class="data-item" @click="navigateToHistory">
<text class="data-label">胜利局数</text>
<text class="data-number">{{totalWins || 0}}</text>
</view>
<view class="data-divider"></view>
<view class="data-item" @click="navigateToHistory">
<text class="data-label">胜率</text>
<text class="data-number">{{winRate || '0%'}}</text>
</view>
</view>
<!-- 功能菜单区域 -->
<view class="menu-section">
<!-- 第一组菜单 -->
<view class="menu-group">
<view class="menu-item" @click="navigateToFavorites">
<view class="menu-left">
<image src="/static/favorites.png" alt="收藏的对局" class="menu-icon" mode="aspectFit"></image>
<text class="menu-text">收藏的对局</text>
</view>
<uni-icons type="right" size="24" color="#999"></uni-icons>
</view>
</view>
<!-- 第二组菜单 -->
<view class="menu-group">
<view class="menu-item" @click="openSettings">
<view class="menu-left">
<image src="/static/setting.png" alt="设置" class="menu-icon" mode="aspectFit"></image>
<text class="menu-text">设置</text>
</view>
<uni-icons type="right" size="24" color="#999"></uni-icons>
</view>
<view class="menu-item" @click="openFeedback">
<view class="menu-left">
<image src="/static/feedback.png" alt="帮助与反馈" class="menu-icon" mode="aspectFit"></image>
<text class="menu-text">帮助与反馈</text>
</view>
<uni-icons type="right" size="24" color="#999"></uni-icons>
</view>
<view class="menu-item" @click="aboutUs">
<view class="menu-left">
<image src="/static/about.png" alt="关于我们" class="menu-icon" mode="aspectFit"></image>
<text class="menu-text">关于我们</text>
</view>
<uni-icons type="right" size="24" color="#999"></uni-icons>
</view>
</view>
</view>
<!-- 版本信息 -->
<view class="version-info">
<text>版本 {{version || '1.0.0'}}</text>
</view>
</view>
</template>
<script setup>
import { onMounted, onUnmounted, ref, getCurrentInstance } from 'vue'
import { onLoad, onShow, onHide } from '@dcloudio/uni-app'
import { getOpenId } from '@/utils/wxutils.js'
import { register, login } from '@/api/user.js'
const proxy = getCurrentInstance().proxy;
// 小程序胶囊高度
const topHeight = ref(proxy.$StaticValue.getTopHeight());
// 用户信息相关
const userInfo = ref({});
const totalGames = ref(0);
const totalWins = ref(0);
const winRate = ref('0%');
const unreadCount = ref(0);
const version = ref('1.0.0');
// 获取用户信息
const getUserInfo = async () => {
// 从本地获取用户信息
userInfo.value = proxy.$StaticValue.getUserInfo();
if(!userInfo.value?.userId) {
// 如果没有登录, 获取到当前用户的openid
const openIdRes = await getOpenId();
const openId = openIdRes.openid;
userInfo.value = {
openId: openId,
nickName: '用户'+openId.substring(0, 6)
};
register(userInfo.value).then(res => {
// 注册成功就执行登录
login(userInfo.value).then(loginRes => {
userInfo.value = loginRes.data;
// 存储用户信息到本地
proxy.$StaticValue.setUserInfo(userInfo.value);
// 获取用户统计数据
fetchUserStats();
});
});
} else {
// 已登录,获取用户统计数据
fetchUserStats();
}
}
// 获取用户统计数据
const fetchUserStats = () => {
// 实际项目中应该调用API获取真实数据
// 这里使用模拟数据
totalGames.value = 42;
totalWins.value = 28;
winRate.value = '66.7%';
unreadCount.value = 2;
// 实际API调用示例
// getUserStats(userInfo.value.userId).then(res => {
// totalGames.value = res.data.totalGames;
// totalWins.value = res.data.totalWins;
// winRate.value = res.data.winRate;
// unreadCount.value = res.data.unreadCount;
// });
}
// 更新头像
const updateAvatar = () => {
/* #ifdef MP-WEIXIN */
wx.chooseMedia({
count: 1,
mediaType: ['image'],
sourceType: ['album', 'camera'],
sizeType: ['compressed'],
success: (res) => {
if (res.tempFiles && res.tempFiles.length > 0) {
const tempFilePath = res.tempFiles[0].tempFilePath;
// 预览选择的头像
wx.previewImage({
urls: [tempFilePath],
success: () => {
// 更新头像
userInfo.value.avatarUrl = tempFilePath;
// 在实际项目中应该上传头像到服务器
proxy.$StaticValue.setUserInfo(userInfo.value);
uni.showToast({
title: '头像更新成功',
icon: 'success'
});
}
});
}
},
fail: (err) => {
console.log('选择头像失败', err);
if (err.errMsg !== 'chooseMedia:fail cancel') {
uni.showToast({
title: '选择头像失败',
icon: 'none'
});
}
}
});
/* #endif */
/* #ifndef MP-WEIXIN */
uni.chooseImage({
count: 1,
success: (res) => {
if (res.tempFilePaths && res.tempFilePaths.length > 0) {
userInfo.value.avatarUrl = res.tempFilePaths[0];
proxy.$StaticValue.setUserInfo(userInfo.value);
uni.showToast({
title: '头像更新成功',
icon: 'success'
});
}
}
});
/* #endif */
}
// 导航到历史对局页面
const navigateToHistory = () => {
uni.navigateTo({
url: '/pages/history-game/history-game'
});
}
// 导航到收藏对局页面
const navigateToFavorites = () => {
// 如果收藏页面不存在,可以创建一个或跳转到历史页面并筛选
uni.navigateTo({
url: '/pages/my/favorites'
});
}
// 打开设置页面
const openSettings = () => {
uni.navigateTo({
url: '/pages/my/setting'
});
}
// 打开帮助与反馈
const openFeedback = () => {
uni.navigateTo({
url: '/pages/my/feedback'
});
}
// 关于我们
const aboutUs = () => {
uni.showModal({
title: '关于计分小程序',
content: '计分小程序 v' + version.value + '\n\n一款便捷的计分工具支持多种游戏模式。\n\n© 2023 计分小程序团队',
showCancel: false,
confirmText: '确定'
});
}
// 退出登录
const logout = () => {
uni.showModal({
title: '确认退出',
content: '确定要退出登录吗?',
success: (res) => {
if (res.confirm) {
// 清除本地存储的用户信息
proxy.$StaticValue.clearUserInfo();
// 重置用户信息
userInfo.value = {};
totalGames.value = 0;
totalWins.value = 0;
winRate.value = '0%';
// 重新获取用户信息(会自动重新登录)
getUserInfo();
uni.showToast({
title: '已退出登录',
icon: 'success'
});
}
}
});
}
onMounted(() => {
getUserInfo();
// 获取版本信息
/* #ifdef MP-WEIXIN */
const appInfo = wx.getAccountInfoSync();
version.value = appInfo.miniProgram.version || '1.0.0';
/* #endif */
});
onShow(() => {
// 每次显示页面时刷新数据
fetchUserStats();
});
</script>
<style lang="less" scoped>
.my {
width: 750rpx;
margin: 0;
padding: 0;
min-height: 100vh;
background-color: #f8f8f8;
}
/* 用户信息区域样式 */
.user-info {
--user-height: 500rpx;
width: 750rpx;
height: var(--user-height);
position: relative;
.background {
width: 100%;
height: 100%;
background-image: linear-gradient(rgb(87, 255, 87), rgb(144, 251, 144));
position: absolute;
top: 0;
left: 0;
z-index: 0;
}
.info-content {
width: 100%;
height: 100%;
position: relative;
z-index: 1;
padding-top: calc(var(--top-margin) + 20rpx);
.top-user-name {
width: 100%;
font-size: 40rpx;
font-weight: bold;
color: #000000;
padding: 0 20rpx;
box-sizing: border-box;
ttext-align: center;
}
.avatars {
width: 100%;
display: flex;
justify-content: center;
margin-top: 20rpx;
.avatar-img {
width: 150rpx;
height: 150rpx;
border-radius: 75rpx;
border: 4rpx solid #ffffff;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
// 添加点击效果
&:active {
opacity: 0.8;
transform: scale(0.95);
}
}
}
.hello-text {
width: 100%;
text-align: center;
margin-top: 20rpx;
font-size: 30rpx;
color: #000000;
p {
margin: 5rpx 0;
}
}
}
}
/* 数据统计区域样式 */
.data-section {
width: 100%;
height: 160rpx;
background-color: #ffffff;
margin-top: -30rpx;
border-radius: 20rpx;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
display: flex;
align-items: center;
justify-content: space-around;
padding: 0 40rpx;
margin-bottom: 30rpx;
.data-item {
flex: 1;
text-align: center;
.data-number {
font-size: 40rpx;
font-weight: bold;
color: #333;
display: block;
margin-bottom: 10rpx;
}
.data-label {
font-size: 24rpx;
color: #666;
}
// 添加点击效果
&:active {
opacity: 0.7;
}
}
.data-divider {
width: 1rpx;
height: 80rpx;
background-color: #eee;
}
}
/* 功能菜单区域样式 */
.menu-section {
width: 100%;
padding: 0 20rpx;
box-sizing: border-box;
.menu-group {
background-color: #ffffff;
border-radius: 16rpx;
margin-bottom: 30rpx;
overflow: hidden;
.menu-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 30rpx;
height: 100rpx;
border-bottom: 1rpx solid #f5f5f5;
// 最后一个菜单项不需要底边框
&:last-child {
border-bottom: none;
}
.menu-left {
display: flex;
align-items: center;
.menu-icon {
width: 40rpx;
height: 40rpx;
vertical-align: middle;
}
.menu-text {
font-size: 32rpx;
color: #333;
margin-left: 20rpx;
}
}
.menu-right {
display: flex;
align-items: center;
.menu-badge {
background-color: #FF3B30;
color: #ffffff;
font-size: 20rpx;
padding: 2rpx 10rpx;
border-radius: 10rpx;
margin-right: 10rpx;
min-width: 20rpx;
text-align: center;
}
}
// 添加点击效果
&:active {
background-color: #f8f8f8;
}
}
}
}
/* 退出登录按钮样式 */
.logout-section {
width: 100%;
padding: 0 40rpx;
box-sizing: border-box;
margin-bottom: 40rpx;
.logout-btn {
width: 100%;
height: 90rpx;
line-height: 90rpx;
border-radius: 45rpx;
font-size: 34rpx;
background-color: #ffffff;
color: #FF3B30;
border: 1rpx solid #FF3B30;
}
}
/* 版本信息样式 */
.version-info {
width: 100%;
text-align: center;
padding-bottom: 60rpx;
text {
font-size: 24rpx;
color: #999;
}
}
/* 安全区域适配 */
/* #ifdef MP-WEIXIN */
.my {
padding-bottom: calc(30rpx + env(safe-area-inset-bottom));
}
/* #endif */
</style>