Files
account/resources/mobile/pages/ucenter/index/index.vue
2026-01-19 13:07:02 +08:00

680 lines
15 KiB
Vue

<template>
<un-pages
:show-nav-bar="true"
nav-bar-title="我的"
:show-back="false"
:show-tab-bar="true"
@tab-change="handleTabChange"
>
<view class="page-content">
<!-- 用户信息头部 -->
<view class="user-header">
<view class="header-bg"></view>
<view class="user-avatar">
<image v-if="userInfo.avatar" :src="userInfo.avatar" class="avatar-image" mode="aspectFill"></image>
<uni-icons v-else type="person-filled" size="70" color="#fff"></uni-icons>
</view>
<view class="user-info">
<text class="user-name">{{ userInfo.username || '未登录' }}</text>
<text class="user-nickname" v-if="isLogin && userInfo.nickname">{{ userInfo.nickname }}</text>
<text class="user-desc" v-else>点击登录体验更多功能</text>
</view>
<view class="edit-profile" @tap="handleEditProfile" v-if="isLogin">
<uni-icons type="compose" size="18" color="rgba(255,255,255,0.8)"></uni-icons>
</view>
</view>
<!-- 数据统计卡片 -->
<view class="stats-section" v-if="isLogin">
<view class="stat-item">
<text class="stat-value">{{ stats.billCount || 0 }}</text>
<text class="stat-label">账单数</text>
</view>
<view class="stat-divider"></view>
<view class="stat-item">
<text class="stat-value">{{ stats.days || 0 }}</text>
<text class="stat-label">记账天数</text>
</view>
<view class="stat-divider"></view>
<view class="stat-item">
<text class="stat-value">{{ stats.familyMembers || 0 }}</text>
<text class="stat-label">家庭成员</text>
</view>
</view>
<!-- 快捷功能 -->
<view class="quick-section">
<view class="section-title">快捷功能</view>
<view class="quick-grid">
<view class="quick-item" @tap="navigateTo('/pages/account/bill/add')">
<view class="quick-icon" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);">
<uni-icons type="plus" size="24" color="#fff"></uni-icons>
</view>
<text class="quick-text">记一笔</text>
</view>
<view class="quick-item" @tap="navigateTo('/pages/family/index')">
<view class="quick-icon" style="background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);">
<text class="emoji-icon">👥</text>
</view>
<text class="quick-text">家庭管理</text>
</view>
<view class="quick-item" @tap="navigateTo('/pages/account/statistics/index')">
<view class="quick-icon" style="background: linear-gradient(135deg, #4ECDC4 0%, #44A08D 100%);">
<uni-icons type="color-filled" size="24" color="#fff"></uni-icons>
</view>
<text class="quick-text">统计分析</text>
</view>
<view class="quick-item" @tap="handleExport">
<view class="quick-icon" style="background: linear-gradient(135deg, #fa709a 0%, #fee140 100%);">
<uni-icons type="download" size="24" color="#fff"></uni-icons>
</view>
<text class="quick-text">数据导出</text>
</view>
</view>
</view>
<!-- 功能菜单 -->
<view class="menu-section">
<view class="section-title">设置</view>
<view class="menu-list">
<view class="menu-item" @tap="navigateTo('/pages/ucenter/profile/index')">
<view class="menu-left">
<view class="menu-icon-wrapper">
<uni-icons type="gear" size="20" color="#667eea"></uni-icons>
</view>
<view class="menu-content">
<text class="menu-text">个人设置</text>
<text class="menu-desc">修改个人信息</text>
</view>
</view>
<uni-icons type="right" size="16" color="#ccc"></uni-icons>
</view>
<view class="menu-item" @tap="navigateTo('/pages/ucenter/help/index')">
<view class="menu-left">
<view class="menu-icon-wrapper">
<uni-icons type="help" size="20" color="#667eea"></uni-icons>
</view>
<view class="menu-content">
<text class="menu-text">帮助中心</text>
<text class="menu-desc">常见问题解答</text>
</view>
</view>
<uni-icons type="right" size="16" color="#ccc"></uni-icons>
</view>
<view class="menu-item" @tap="navigateTo('/pages/ucenter/about/index')">
<view class="menu-left">
<view class="menu-icon-wrapper">
<uni-icons type="info" size="20" color="#667eea"></uni-icons>
</view>
<view class="menu-content">
<text class="menu-text">关于我们</text>
<text class="menu-desc">版本信息</text>
</view>
</view>
<uni-icons type="right" size="16" color="#ccc"></uni-icons>
</view>
<view class="menu-item" @tap="handleClearCache" v-if="false">
<view class="menu-left">
<view class="menu-icon-wrapper">
<uni-icons type="trash" size="20" color="#667eea"></uni-icons>
</view>
<view class="menu-content">
<text class="menu-text">清除缓存</text>
<text class="menu-desc">释放存储空间</text>
</view>
</view>
<text class="cache-size">{{ cacheSize }}</text>
</view>
</view>
</view>
<!-- 登录/退出按钮 -->
<view class="auth-section">
<button class="auth-btn login-btn" @tap="handleLogin" v-if="!isLogin">
立即登录
</button>
<button class="auth-btn logout-btn" @tap="handleLogout" v-else>
退出登录
</button>
</view>
</view>
</un-pages>
</template>
<script>
import { isLogin, logout } from '@/utils/auth.js'
export default {
data() {
return {
userInfo: this.$store.state.user.userInfo,
isLogin: false,
stats: {
billCount: 0,
days: 0,
familyMembers: 0
},
cacheSize: '0 MB'
}
},
onLoad() {
this.checkLoginStatus()
this.calculateCacheSize()
},
onShow() {
this.checkLoginStatus()
},
methods: {
// 检查登录状态
async checkLoginStatus() {
this.isLogin = isLogin()
if (this.isLogin) {
await this.loadUserInfo()
this.loadUserStats()
} else {
this.userInfo = {}
}
},
// 加载用户信息
async loadUserInfo() {
try {
const result = await this.$api.auth.info.get()
if (result && result.code === 1) {
this.userInfo = result.data || {}
// 更新本地存储
uni.setStorageSync('userInfo', this.userInfo)
// 更新 Vuex store
this.$store.commit('setUserInfo', this.userInfo)
} else {
// 如果接口失败,使用本地存储的数据
this.userInfo = uni.getStorageSync('userInfo') || {}
}
} catch (error) {
console.error('加载用户信息失败:', error)
// 如果接口失败,使用本地存储的数据
this.userInfo = uni.getStorageSync('userInfo') || {}
}
},
// 加载用户统计数据
async loadUserStats() {
try {
const result = await this.$api.statistics.dashboard.get({
data_type: 'family'
})
if (result && result.code === 1) {
this.stats.billCount = result.data?.bill_count || 0
this.stats.days = result.data?.days || 0
this.stats.familyMembers = result.data?.family_members || 0
}
} catch (error) {
console.error('加载统计数据失败', error)
// 失败时保持默认值
}
},
// 计算缓存大小
calculateCacheSize() {
try {
const info = uni.getStorageInfoSync()
const size = (info.currentSize / 1024).toFixed(2)
this.cacheSize = `${size} MB`
} catch (e) {
this.cacheSize = '0 MB'
}
},
// 页面跳转
navigateTo(url) {
if (!this.isLogin) {
uni.navigateTo({
url: '/pages/ucenter/login/index'
})
return
}
// 直接跳转,不显示"功能开发中"
uni.navigateTo({
url: url
})
},
// 编辑个人资料
handleEditProfile() {
uni.navigateTo({
url: '/pages/ucenter/profile/edit'
})
},
// 数据导出
handleExport() {
if (!this.isLogin) {
uni.navigateTo({
url: '/pages/ucenter/login/index'
})
return
}
uni.showToast({
title: '功能开发中',
icon: 'none',
duration: 2000
})
},
// 清除缓存
handleClearCache() {
uni.showModal({
title: '提示',
content: '确定要清除缓存吗?',
success: (res) => {
if (res.confirm) {
try {
uni.clearStorageSync()
this.calculateCacheSize()
uni.showToast({
title: '缓存已清除',
icon: 'success'
})
} catch (e) {
uni.showToast({
title: '清除失败',
icon: 'none'
})
}
}
}
})
},
// 登录
handleLogin() {
uni.navigateTo({
url: '/pages/ucenter/login/index'
})
},
// 退出登录
handleLogout() {
uni.showModal({
title: '提示',
content: '确定要退出登录吗?',
success: (res) => {
if (res.confirm) {
logout()
this.checkLoginStatus()
}
}
})
},
// Tab切换
handleTabChange(path) {
console.log('Tab changed to:', path)
}
}
}
</script>
<style lang="scss" scoped>
.page-content {
padding: 30rpx;
background: linear-gradient(180deg, #F5F7FA 0%, #FFFFFF 100%);
position: relative;
&::before {
content: '';
position: absolute;
top: -50rpx;
right: -80rpx;
width: 280rpx;
height: 280rpx;
background: radial-gradient(circle, rgba(102, 126, 234, 0.08) 0%, transparent 70%);
border-radius: 50%;
z-index: 0;
}
> view {
position: relative;
z-index: 1;
}
}
.user-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 24rpx;
padding: 60rpx 40rpx;
margin: 20rpx 0 30rpx;
display: flex;
align-items: center;
position: relative;
overflow: hidden;
box-shadow: 0 8rpx 24rpx rgba(102, 126, 234, 0.35);
animation: fadeInDown 0.6s ease-out;
.header-bg {
position: absolute;
top: -50%;
right: -50%;
width: 100%;
height: 100%;
background: radial-gradient(circle, rgba(255, 255, 255, 0.1) 0%, transparent 70%);
}
.user-avatar {
width: 120rpx;
height: 120rpx;
background: rgba(255, 255, 255, 0.25);
border-radius: 60rpx;
display: flex;
align-items: center;
justify-content: center;
margin-right: 28rpx;
position: relative;
backdrop-filter: blur(10rpx);
border: 3rpx solid rgba(255, 255, 255, 0.3);
box-shadow: 0 8rpx 20rpx rgba(0, 0, 0, 0.15);
.avatar-image {
width: 120rpx;
height: 120rpx;
border-radius: 60rpx;
object-fit: cover;
}
}
.user-info {
flex: 1;
display: flex;
flex-direction: column;
position: relative;
z-index: 1;
.user-name {
font-size: 36rpx;
font-weight: bold;
color: #fff;
margin-bottom: 12rpx;
text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
}
.user-nickname {
font-size: 26rpx;
color: rgba(255, 255, 255, 0.85);
font-weight: 500;
margin-top: 8rpx;
}
.user-desc {
font-size: 26rpx;
color: rgba(255, 255, 255, 0.9);
font-weight: 500;
}
}
.edit-profile {
padding: 16rpx;
background: rgba(255, 255, 255, 0.2);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
position: relative;
z-index: 1;
transition: all 0.3s ease;
&:active {
transform: scale(0.95);
background: rgba(255, 255, 255, 0.3);
}
}
}
.stats-section {
background: #fff;
border-radius: 24rpx;
padding: 40rpx 30rpx;
margin-bottom: 30rpx;
display: flex;
align-items: center;
box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.08);
animation: fadeInUp 0.6s ease-out 0.1s both;
.stat-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
transition: transform 0.3s ease;
&:active {
transform: scale(0.95);
}
.stat-value {
font-size: 48rpx;
font-weight: bold;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin-bottom: 8rpx;
}
.stat-label {
font-size: 26rpx;
color: #999;
font-weight: 500;
}
}
.stat-divider {
width: 2rpx;
height: 60rpx;
background: #f0f0f0;
margin: 0 20rpx;
}
}
.quick-section {
margin-bottom: 30rpx;
animation: fadeInUp 0.6s ease-out 0.2s both;
.section-title {
font-size: 28rpx;
font-weight: bold;
color: #333;
margin-bottom: 20rpx;
padding-left: 8rpx;
}
.quick-grid {
background: #fff;
border-radius: 24rpx;
padding: 30rpx;
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20rpx;
box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.08);
.quick-item {
display: flex;
flex-direction: column;
align-items: center;
transition: transform 0.3s ease;
&:active {
transform: translateY(-4rpx);
}
.quick-icon {
width: 96rpx;
height: 96rpx;
border-radius: 24rpx;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 16rpx;
box-shadow: 0 6rpx 16rpx rgba(0, 0, 0, 0.12);
.emoji-icon {
font-size: 44rpx;
}
}
.quick-text {
font-size: 24rpx;
color: #333;
font-weight: 500;
}
}
}
}
.menu-section {
margin-bottom: 30rpx;
animation: fadeInUp 0.6s ease-out 0.3s both;
.section-title {
font-size: 28rpx;
font-weight: bold;
color: #333;
margin-bottom: 20rpx;
padding-left: 8rpx;
}
.menu-list {
background: #fff;
border-radius: 24rpx;
overflow: hidden;
box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.08);
.menu-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 32rpx 30rpx;
border-bottom: 1rpx solid #f5f5f5;
transition: all 0.3s ease;
&:last-child {
border-bottom: none;
}
&:active {
background: #f8f8f8;
}
.menu-left {
display: flex;
align-items: center;
flex: 1;
.menu-icon-wrapper {
width: 64rpx;
height: 64rpx;
background: rgba(102, 126, 234, 0.1);
border-radius: 16rpx;
display: flex;
align-items: center;
justify-content: center;
margin-right: 20rpx;
}
.menu-content {
display: flex;
flex-direction: column;
.menu-text {
font-size: 30rpx;
color: #333;
font-weight: 500;
margin-bottom: 6rpx;
}
.menu-desc {
font-size: 24rpx;
color: #999;
}
}
}
.cache-size {
font-size: 24rpx;
color: #999;
font-weight: 500;
}
}
}
}
.auth-section {
margin-top: 40rpx;
padding: 0 20rpx;
animation: fadeInUp 0.6s ease-out 0.4s both;
.auth-btn {
width: 100%;
height: 96rpx;
border-radius: 48rpx;
font-size: 32rpx;
font-weight: bold;
display: flex;
align-items: center;
justify-content: center;
border: none;
box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
&::after {
border: none;
}
&:active {
transform: scale(0.98);
}
}
.login-btn {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #fff;
box-shadow: 0 8rpx 24rpx rgba(102, 126, 234, 0.4);
}
.logout-btn {
background: #fff;
color: #f56c6c;
border: 2rpx solid #f56c6c;
box-shadow: 0 6rpx 20rpx rgba(245, 108, 108, 0.15);
&:active {
background: #fef0f0;
}
}
}
@keyframes fadeInDown {
from {
opacity: 0;
transform: translateY(-30rpx);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30rpx);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>