Files
akmon/pages/user/center.uvue
2026-01-20 08:04:15 +08:00

686 lines
16 KiB
Plaintext
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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-wrapper">
<!-- Top section with language switch -->
<view class="top-section"> <view class="language-switch">
<button class="language-btn" @click="toggleLanguage">
{{ currentLocale === 'zh-CN' ? 'EN' : '中' }}
</button>
</view>
</view>
<!-- Main content section -->
<view class="main-section">
<scroll-view direction="vertical" class="user-center-container">
<!-- Header with user info -->
<view class="user-header">
<view class="user-info">
<image class="user-avatar" :src="userAvatar" mode="aspectFill"></image>
<view class="user-details">
<text class="user-name">{{ profile.username ?? $t('user.center.unnamed') }}</text>
<view class="edit-profile-link" @click="navigateToProfile">
<text class="edit-text">{{ $t('user.center.edit_profile') }}</text>
<text class="edit-icon">✏️</text>
</view>
</view>
</view>
<!-- User stats -->
<view class="stats-container">
<view class="stat-item">
<text class="stat-value">{{ userStats.trainings }}</text>
<text class="stat-label">{{ $t('user.center.trainings') }}</text>
</view>
<view class="stat-divider"></view>
<view class="stat-item">
<text class="stat-value">{{ userStats.points }}</text>
<text class="stat-label">{{ $t('user.center.points') }}</text>
</view>
<view class="stat-divider"></view>
<view class="stat-item">
<text class="stat-value">{{ userStats.streak }}</text>
<text class="stat-label">{{ $t('user.center.streak') }}</text>
</view>
</view>
</view>
<!-- Main menu sections -->
<view class="menu-sections">
<!-- Training section -->
<view class="menu-section">
<view class="section-header">
<text class="section-title">{{ $t('user.center.training') }}</text>
</view>
<view class="section-items">
<view class="menu-item" @click="navigateTo('/pages/training/records')"> <view class="menu-icon training-records">📊</view>
<text class="menu-text">{{ $t('user.center.training_records') }}</text>
<text class="menu-arrow">></text>
</view>
<view class="menu-item" @click="navigateTo('/pages/training/plans')">
<view class="menu-icon training-plans">📋</view>
<text class="menu-text">{{ $t('user.center.training_plans') }}</text>
<text class="menu-arrow">></text>
</view>
<view class="menu-item" @click="navigateTo('/pages/training/reports')">
<view class="menu-icon reports">📈</view>
<text class="menu-text">{{ $t('user.center.reports') }}</text>
<text class="menu-arrow">></text>
</view>
</view>
</view>
<!-- Account section -->
<view class="menu-section">
<view class="section-header">
<text class="section-title">{{ $t('user.center.account') }}</text>
</view>
<view class="section-items"> <view class="menu-item" @click="navigateTo('/pages/user/profile')">
<view class="menu-icon profile">👤</view>
<text class="menu-text">{{ $t('user.center.profile') }}</text>
<text class="menu-arrow">></text>
</view>
<view class="menu-item" @click="navigateTo('/pages/user/devices')">
<view class="menu-icon devices">📱</view>
<text class="menu-text">{{ $t('user.center.devices') }}</text>
<text class="menu-arrow">></text>
</view>
<view class="menu-item" @click="navigateTo('/pages/user/notifications')">
<view class="menu-icon notifications">🔔</view>
<text class="menu-text">{{ $t('user.center.notifications') }}</text>
<text class="menu-arrow">></text>
</view>
</view>
</view>
<!-- Settings section -->
<view class="menu-section">
<view class="section-header">
<text class="section-title">{{ $t('user.center.settings') }}</text>
</view>
<view class="section-items"> <view class="menu-item" @click="navigateTo('/pages/settings/app')">
<view class="menu-icon app-settings">⚙️</view>
<text class="menu-text">{{ $t('user.center.app_settings') }}</text>
<text class="menu-arrow">></text>
</view>
<view class="menu-item" @click="navigateTo('/pages/settings/about')">
<view class="menu-icon about"></view>
<text class="menu-text">{{ $t('user.center.about') }}</text>
<text class="menu-arrow">></text>
</view>
</view>
</view> <!-- Logout button -->
<button class="logout-button" @click="showLogoutConfirm">
{{ $t('user.center.logout') }}
</button>
</view>
</scroll-view>
</view>
<!-- Bottom section -->
<view class="bottom-section">
<!-- Footer content or spacing -->
</view>
</view>
</template>
<script lang="uts">
import { AkReqOptions, AkReqResponse, AkReqError } from '@/uni_modules/ak-req/index.uts';
import supa from '@/components/supadb/aksupainstance.uts'
import type { UserProfile,UserStats } from './types.uts';
import { setUserProfile } from '@/utils/store.uts';
import { AkSupaSelectOptions } from '@/components/supadb/aksupa.uts'
import { switchLocale, getCurrentLocale } from '@/utils/utils.uts';
export default {
data() {
return {
isLoading: true,
userStats: {
trainings: 0,
points: 0,
streak: 0
} as UserStats,
profile: {
username: '',
email: ''
} as UserProfile,
userAvatar: '/static/default-avatar.png',
currentLocale: getCurrentLocale()
};
},
onLoad() {
},
onShow() {
this.loadProfile();
this.loadUserStats();
}, methods: {
toggleLanguage() {
const newLocale = this.currentLocale === 'zh-CN' ? 'en-US' : 'zh-CN';
switchLocale(newLocale);
this.currentLocale = newLocale;
uni.showToast({
title: this.$t('user.center.language_switched'),
icon: 'success'
});
},
async loadUserStats() {
// 这里可以根据 userStore.profile.id 拉取真实数据
this.userStats = {
trainings: 12,
points: 480,
streak: 5
};
},
async loadProfile() {
const user = supa.user;
if (user==null || user.email=='') {
console.log('null user:',user)
this.profile.email = '';
return;
}
const filter = `id.eq.${user.id}`;
const options = { single: true } as AkSupaSelectOptions;
const { data, error } = await supa.select('ak_users', filter, options);
// 判断 data 是否为数组且为空
if(Array.isArray(data) && data.length> 0) {
console.log(data)
let prodata= data[0] as UTSJSONObject;
this.profile = {
id: user.id as string,
username: prodata.getString("username")??"",
email: prodata.getString("email")??"",
gender: prodata.getString("gender"),
birthday: prodata.getString("birthday"),
height_cm: prodata.getNumber("height_cm"),
weight_kg: prodata.getNumber("weight_kg"),
bio: prodata.getString("bio"),
avatar_url: prodata.getString("avatar_url")??'/static/logo.png',
preferred_language: prodata.getString("preferred_language"),
}
this.userAvatar = this.profile.avatar_url!!;
}
else {
this.profile.id= user.getString("id");
this.profile.username = user.getString("username")??"";
this.profile.email = user.getString("email")??"";
let newProfile = new UTSJSONObject(this.profile);
const insertResult = await supa.from('ak_users').insert(newProfile).execute();
console.log(insertResult)
if (insertResult.error==null) {
setUserProfile(this.profile);
}
}
},
navigateToProfile() {
uni.navigateTo({
url: '/pages/user/profile'
});
},
navigateTo(url: string) {
const implementedPages = ['/pages/user/profile'];
if (implementedPages.includes(url)) {
uni.navigateTo({ url });
} else {
uni.showToast({
title: 'Coming soon',
icon: 'none'
});
}
},
showLogoutConfirm() {
uni.showModal({
title: this.$t('user.center.logout_confirm_title'),
content: this.$t('user.center.logout_confirm_message'),
cancelText: this.$t('user.center.cancel'),
confirmText: this.$t('user.center.confirm'),
success: (res) => {
if (res.confirm) {
this.handleLogout();
}
}
});
},
async handleLogout() {
try {
await supa.signOut();
uni.showToast({
title: this.$t('user.center.logout_success'),
icon: 'success'
});
setTimeout(() => {
uni.reLaunch({
url: '/pages/user/login'
});
}, 1000);
} catch (err) {
console.error('Logout error:', err);
uni.showToast({
title: this.$t('user.center.logout_error'),
icon: 'none'
});
}
}
}
};
</script>
<style>
/* Page wrapper for full screen utilization */
.page-wrapper {
/* #ifdef APP-PLUS */ display: flex;
flex-direction: column;
/* #endif */
/* #ifndef APP-PLUS *//* #endif */
background-color: #f8f9fa;
}
/* Top section - Fixed header */
.top-section {
/* #ifdef APP-PLUS */
flex: 0 0 auto;
height: 100rpx;
/* #endif */ /* #ifndef APP-PLUS */
height: 120rpx;
/* #endif */
position: relative;
background-image: linear-gradient(to right, #2196f3, #03a9f4);
}
/* Main content section - Scrollable */
.main-section {
/* #ifdef APP-PLUS */
flex: 1;
overflow: hidden;
/* #endif */
/* #ifndef APP-PLUS */
flex-grow: 1;
/* #endif */
}
/* Bottom section - Fixed footer */
.bottom-section {
/* #ifdef APP-PLUS */
flex: 0 0 auto;
height: 50rpx;
/* #endif */
/* #ifndef APP-PLUS */
height: 60rpx;
/* #endif */
background-color: #f8f9fa;
}
.user-center-container {
/* #ifdef APP-PLUS */ /* #endif */
/* #ifndef APP-PLUS *//* #endif */
background-color: #f8f9fa;
/* #ifdef APP-PLUS */
padding-bottom: 40rpx;
/* #endif */
/* #ifndef APP-PLUS */
padding-bottom: 50rpx;
/* #endif */
}
/* Language switch button */
.language-switch {
position: absolute;
/* #ifdef APP-PLUS */
top: 20rpx;
right: 30rpx;
/* #endif */
/* #ifndef APP-PLUS */
top: 30rpx;
right: 40rpx;
/* #endif */
z-index: 10;
}
/* User header */
.user-header {
background-image: linear-gradient(to right, #2196f3, #03a9f4);
/* #ifdef APP-PLUS */
padding: 30rpx 30rpx 25rpx;
border-bottom-left-radius: 25rpx;
border-bottom-right-radius: 25rpx;
/* #endif */
/* #ifndef APP-PLUS */
padding: 60rpx 40rpx 30rpx;
border-bottom-left-radius: 30rpx;
border-bottom-right-radius: 30rpx;
/* #endif */
color: #fff;
box-shadow: 0 10rpx 20rpx rgba(3, 169, 244, 0.2);
}
.user-info {
display: flex;
align-items: center;
flex-direction: row;
}
.user-avatar {
/* #ifdef APP-PLUS */
width: 90rpx;
height: 90rpx;
border-radius: 45rpx;
border: 3rpx solid #fff;
/* #endif */
/* #ifndef APP-PLUS */
width: 120rpx;
height: 120rpx;
border-radius: 60rpx;
border: 4rpx solid #fff;
/* #endif */
background-color: #fff;
}
.user-details {
flex: 1;
/* #ifdef APP-PLUS */
margin-left: 20rpx;
/* #endif */
/* #ifndef APP-PLUS */
margin-left: 30rpx;
/* #endif */
}
.user-name {
/* #ifdef APP-PLUS */
font-size: 28rpx;
margin-bottom: 8rpx;
/* #endif */
/* #ifndef APP-PLUS */
font-size: 36rpx;
margin-bottom: 10rpx;
/* #endif */
font-weight: bold;
}
.edit-profile-link {
display: flex;
align-items: center;
}
.edit-text {
/* #ifdef APP-PLUS */
font-size: 20rpx;
/* #endif */
/* #ifndef APP-PLUS */
font-size: 24rpx;
/* #endif */
color: rgba(255, 255, 255, 0.9);
}
.edit-icon {
/* #ifdef APP-PLUS */
font-size: 20rpx;
margin-left: 5rpx;
/* #endif */
/* #ifndef APP-PLUS */
font-size: 24rpx;
margin-left: 6rpx;
/* #endif */
}
/* Stats container */
.stats-container {
display: flex;
flex-direction: row;
/* #ifdef APP-PLUS */
margin-top: 30rpx;
border-radius: 12rpx;
padding: 15rpx 0;
/* #endif */
/* #ifndef APP-PLUS */
margin-top: 40rpx;
border-radius: 15rpx;
padding: 20rpx 0;
/* #endif */
background-color: rgba(255, 255, 255, 0.15);
}
.stat-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
}
.stat-value {
/* #ifdef APP-PLUS */
font-size: 24rpx;
/* #endif */
/* #ifndef APP-PLUS */
font-size: 32rpx;
/* #endif */
font-weight: bold;
}
.stat-label {
/* #ifdef APP-PLUS */
font-size: 20rpx;
margin-top: 4rpx;
/* #endif */
/* #ifndef APP-PLUS */
font-size: 24rpx;
margin-top: 6rpx;
/* #endif */
opacity: 0.9;
}
.stat-divider {
width: 2rpx;
background-color: rgba(255, 255, 255, 0.3);
/* #ifdef APP-PLUS */
margin: 8rpx 0;
/* #endif */
/* #ifndef APP-PLUS */
margin: 10rpx 0;
/* #endif */
}
/* Menu sections */
.menu-sections {
/* #ifdef APP-PLUS */
padding: 30rpx 25rpx;
/* #endif */
/* #ifndef APP-PLUS */
padding: 40rpx 30rpx;
/* #endif */
}
.menu-section {
background-color: #fff;
/* #ifdef APP-PLUS */
border-radius: 15rpx;
margin-bottom: 20rpx;
/* #endif */
/* #ifndef APP-PLUS */
border-radius: 20rpx;
margin-bottom: 30rpx;
/* #endif */
overflow: hidden;
box-shadow: 0 5rpx 15rpx rgba(0, 0, 0, 0.05);
}
.section-header {
/* #ifdef APP-PLUS */
padding: 20rpx;
/* #endif */
/* #ifndef APP-PLUS */
padding: 30rpx;
/* #endif */
border-bottom: 2rpx solid #f0f0f0;
}
.section-title {
/* #ifdef APP-PLUS */
font-size: 22rpx;
/* #endif */
/* #ifndef APP-PLUS */
font-size: 28rpx;
/* #endif */
font-weight: bold;
color: #333;
}
.section-items {
/* #ifdef APP-PLUS */
padding: 0 10rpx;
/* #endif */
/* #ifndef APP-PLUS */
padding: 0 15rpx;
/* #endif */
}
.menu-item {
display: flex;
flex-direction: row;
align-items: center;
/* #ifdef APP-PLUS */
padding: 20rpx 10rpx;
/* #endif */
/* #ifndef APP-PLUS */
padding: 30rpx 15rpx;
/* #endif */
border-bottom: 2rpx solid #f8f8f8;
}
.menu-item:last-child {
border-bottom: none;
flex-direction: row;
}
.menu-icon {
/* #ifdef APP-PLUS */
width: 45rpx;
height: 45rpx;
border-radius: 22rpx;
margin-right: 15rpx;
font-size: 24rpx;
/* #endif */
/* #ifndef APP-PLUS */
width: 60rpx;
height: 60rpx;
border-radius: 30rpx;
margin-right: 20rpx;
font-size: 30rpx;
/* #endif */
display: flex;
align-items: center;
justify-content: center;
}
.menu-text {
flex: 1;
/* #ifdef APP-PLUS */
font-size: 22rpx;
/* #endif */
/* #ifndef APP-PLUS */
font-size: 28rpx;
/* #endif */
color: #333;
}
.menu-arrow {
/* #ifdef APP-PLUS */
font-size: 28rpx;
/* #endif */
/* #ifndef APP-PLUS */
font-size: 36rpx;
/* #endif */
color: #ccc;
}
/* Menu icons with different colors */
.training-records {
background-color: #e3f2fd;
}
.training-plans {
background-color: #e8f5e9;
}
.reports {
background-color: #f3e5f5;
}
.profile {
background-color: #e1f5fe;
}
.devices {
background-color: #fffde7;
}
.notifications {
background-color: #fff3e0;
}
.app-settings {
background-color: #e0f2f1;
}
.about {
background-color: #f5f5f5;
}
/* Logout button */
.logout-button {
width: 100%;
/* #ifdef APP-PLUS */
height: 70rpx;
font-size: 26rpx;
margin: 20rpx 0 15rpx;
border-radius: 35rpx;
/* #endif */
/* #ifndef APP-PLUS */
height: 90rpx;
font-size: 32rpx;
margin: 30rpx 0 20rpx;
border-radius: 45rpx;
/* #endif */
background-color: #fff; color: #f44336;
font-weight: normal;
border: 2rpx solid #fce4ec;
text-align: center;
}
/* Center row utility class */
.center-row {
display: flex;
flex-direction: row;
align-items: center; /* 如有需<E69C89>?*/
}
.language-btn {
/* #ifdef APP-PLUS */
width: 60rpx;
height: 60rpx;
border-radius: 30rpx;
font-size: 22rpx;
/* #endif */
/* #ifndef APP-PLUS */
width: 80rpx;
height: 80rpx;
border-radius: 40rpx;
font-size: 28rpx;
/* #endif */ background-color: rgba(33, 150, 243, 0.8);
color: #fff;
font-weight: normal;
border: 2rpx solid rgba(255, 255, 255, 0.3);
text-align: center;
box-shadow: 0 4rpx 12rpx rgba(33, 150, 243, 0.3);
}
</style>