377 lines
7.7 KiB
Plaintext
377 lines
7.7 KiB
Plaintext
<template>
|
||
<view class="page-wrapper">
|
||
<view class="top-section">
|
||
<view class="language-switch">
|
||
<button class="language-btn" @click="toggleLanguage">
|
||
{{ currentLocale === 'zh-CN' ? 'EN' : '中文' }}
|
||
</button>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="main-section">
|
||
<scroll-view direction="vertical" class="user-center-container">
|
||
<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 != null && profile.username != null ? profile.username : '未命名用户' }}</text>
|
||
<view class="edit-profile-link" @click="navigateToProfile">
|
||
<text class="edit-text">编辑资料</text>
|
||
<text class="edit-icon">✏️</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="stats-container">
|
||
<view class="stat-item">
|
||
<text class="stat-value">{{ userStats.trainings }}</text>
|
||
<text class="stat-label">训练</text>
|
||
</view>
|
||
<view class="stat-divider"></view>
|
||
<view class="stat-item">
|
||
<text class="stat-value">{{ userStats.points }}</text>
|
||
<text class="stat-label">积分</text>
|
||
</view>
|
||
<view class="stat-divider"></view>
|
||
<view class="stat-item">
|
||
<text class="stat-value">{{ userStats.streak }}</text>
|
||
<text class="stat-label">连续</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="menu-sections">
|
||
<view class="menu-section">
|
||
<view class="section-header">
|
||
<text class="section-title">设置</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">应用设置</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">关于</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">通知</text>
|
||
<text class="menu-arrow">></text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<button class="logout-button" @click="showLogoutConfirm">退出登录</button>
|
||
</scroll-view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup lang="uts">
|
||
import { ref } from 'vue'
|
||
import { onShow } from '@dcloudio/uni-app'
|
||
import { supabaseService } from '@/utils/supabaseService.uts'
|
||
|
||
type ProfileType = {
|
||
id: string
|
||
username: string | null
|
||
email: string | null
|
||
avatar_url: string | null
|
||
}
|
||
|
||
type UserStatsType = {
|
||
trainings: number
|
||
points: number
|
||
streak: number
|
||
}
|
||
|
||
const profile = ref<ProfileType | null>(null)
|
||
const userStats = ref<UserStatsType>({
|
||
trainings: 0,
|
||
points: 0,
|
||
streak: 0
|
||
} as UserStatsType)
|
||
const currentLocale = ref<string>('zh-CN')
|
||
const userAvatar = ref<string>('/static/images/default-product.png')
|
||
|
||
const toggleLanguage = (): void => {
|
||
if (currentLocale.value === 'zh-CN') {
|
||
currentLocale.value = 'en-US'
|
||
} else {
|
||
currentLocale.value = 'zh-CN'
|
||
}
|
||
uni.showToast({
|
||
title: '语言已切换',
|
||
icon: 'success'
|
||
})
|
||
}
|
||
|
||
const loadProfile = async (): Promise<void> => {
|
||
try {
|
||
const res = await supabaseService.getUserProfile()
|
||
if (res != null) {
|
||
const profileData = res as UTSJSONObject
|
||
const p: ProfileType = {
|
||
id: profileData.getString('id') ?? '',
|
||
username: profileData.getString('username'),
|
||
email: profileData.getString('email'),
|
||
avatar_url: profileData.getString('avatar_url')
|
||
} as ProfileType
|
||
profile.value = p
|
||
|
||
if (p.avatar_url != null && p.avatar_url != '') {
|
||
userAvatar.value = p.avatar_url
|
||
}
|
||
}
|
||
} catch (e) {
|
||
console.error('加载用户资料失败:', e)
|
||
}
|
||
}
|
||
|
||
const loadUserStats = (): void => {
|
||
userStats.value = {
|
||
trainings: 12,
|
||
points: 480,
|
||
streak: 5
|
||
} as UserStatsType
|
||
}
|
||
|
||
const navigateToProfile = (): void => {
|
||
uni.navigateTo({
|
||
url: '/pages/user/profile'
|
||
})
|
||
}
|
||
|
||
const navigateTo = (url: string): void => {
|
||
const implementedPages: Array<string> = ['/pages/user/profile']
|
||
let found = false
|
||
for (let i: number = 0; i < implementedPages.length; i++) {
|
||
if (implementedPages[i] == url) {
|
||
found = true
|
||
break
|
||
}
|
||
}
|
||
|
||
if (found) {
|
||
uni.navigateTo({ url: url })
|
||
} else {
|
||
uni.showToast({
|
||
title: '功能开发中',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
}
|
||
|
||
const handleLogout = (): void => {
|
||
logout()
|
||
uni.removeStorageSync('userInfo')
|
||
|
||
uni.showToast({
|
||
title: '已退出登录',
|
||
icon: 'success'
|
||
})
|
||
|
||
setTimeout(() => {
|
||
uni.switchTab({
|
||
url: '/pages/main/profile'
|
||
})
|
||
}, 1000)
|
||
}
|
||
|
||
const showLogoutConfirm = (): void => {
|
||
uni.showModal({
|
||
title: '提示',
|
||
content: '确定要退出登录吗?',
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
handleLogout()
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
onShow(() => {
|
||
loadProfile()
|
||
loadUserStats()
|
||
})
|
||
</script>
|
||
|
||
<style>
|
||
.page-wrapper {
|
||
flex: 1;
|
||
background-color: #f5f5f5;
|
||
}
|
||
|
||
.top-section {
|
||
padding: 10px 15px;
|
||
background-color: #fff;
|
||
}
|
||
|
||
.language-switch {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
}
|
||
|
||
.language-btn {
|
||
font-size: 12px;
|
||
padding: 5px 15px;
|
||
background-color: #f0f0f0;
|
||
border-radius: 15px;
|
||
}
|
||
|
||
.main-section {
|
||
flex: 1;
|
||
}
|
||
|
||
.user-center-container {
|
||
flex: 1;
|
||
}
|
||
|
||
.user-header {
|
||
background-color: #fff;
|
||
padding: 20px 15px;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.user-info {
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
}
|
||
|
||
.user-avatar {
|
||
width: 60px;
|
||
height: 60px;
|
||
border-radius: 30px;
|
||
background-color: #eee;
|
||
}
|
||
|
||
.user-details {
|
||
margin-left: 15px;
|
||
flex: 1;
|
||
}
|
||
|
||
.user-name {
|
||
font-size: 18px;
|
||
font-weight: bold;
|
||
color: #333;
|
||
}
|
||
|
||
.edit-profile-link {
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
margin-top: 5px;
|
||
}
|
||
|
||
.edit-text {
|
||
font-size: 12px;
|
||
color: #007aff;
|
||
}
|
||
|
||
.edit-icon {
|
||
font-size: 12px;
|
||
margin-left: 5px;
|
||
}
|
||
|
||
.stats-container {
|
||
display: flex;
|
||
flex-direction: row;
|
||
justify-content: space-around;
|
||
margin-top: 20px;
|
||
padding-top: 15px;
|
||
border-top: 1px solid #eee;
|
||
}
|
||
|
||
.stat-item {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
}
|
||
|
||
.stat-value {
|
||
font-size: 20px;
|
||
font-weight: bold;
|
||
color: #333;
|
||
}
|
||
|
||
.stat-label {
|
||
font-size: 12px;
|
||
color: #999;
|
||
margin-top: 5px;
|
||
}
|
||
|
||
.stat-divider {
|
||
width: 1px;
|
||
height: 30px;
|
||
background-color: #eee;
|
||
}
|
||
|
||
.menu-sections {
|
||
background-color: #fff;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.menu-section {
|
||
padding: 15px;
|
||
}
|
||
|
||
.section-header {
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.section-title {
|
||
font-size: 14px;
|
||
color: #999;
|
||
}
|
||
|
||
.section-items {
|
||
background-color: #fff;
|
||
}
|
||
|
||
.menu-item {
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
padding: 12px 0;
|
||
border-bottom: 1px solid #f5f5f5;
|
||
}
|
||
|
||
.menu-item:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.menu-icon {
|
||
width: 30px;
|
||
font-size: 18px;
|
||
}
|
||
|
||
.menu-text {
|
||
flex: 1;
|
||
font-size: 14px;
|
||
color: #333;
|
||
}
|
||
|
||
.menu-arrow {
|
||
font-size: 14px;
|
||
color: #ccc;
|
||
}
|
||
|
||
.logout-button {
|
||
margin: 20px 15px;
|
||
background-color: #ff4444;
|
||
color: #fff;
|
||
font-size: 16px;
|
||
padding: 12px;
|
||
border-radius: 8px;
|
||
text-align: center;
|
||
}
|
||
</style>
|