Files
medical-mall/pages/user/center.uvue

377 lines
7.7 KiB
Plaintext
Raw 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">
<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>