397 lines
8.3 KiB
Vue
397 lines
8.3 KiB
Vue
<template>
|
|
<view class="page">
|
|
<view class="profile-header">
|
|
<view class="profile-info">
|
|
<view class="profile-avatar">
|
|
<text class="avatar-text">{{ avatarText }}</text>
|
|
</view>
|
|
<view class="profile-detail">
|
|
<text class="profile-name">{{ user?.nickname || user?.name || '云泽用户' }}</text>
|
|
<text class="profile-id">{{ user?.account ? `账号: ${user.account}` : `ID: ${userId}` }}</text>
|
|
</view>
|
|
<view class="profile-edit" @tap="onEdit">
|
|
<FaIcon name="pen-to-square" color="#ffffff" :size="16" />
|
|
</view>
|
|
</view>
|
|
|
|
<view class="profile-stats">
|
|
<view class="profile-stat" v-for="item in profileStats" :key="item.label">
|
|
<text class="stat-num">{{ item.value }}</text>
|
|
<text class="stat-label">{{ item.label }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="main-scroll" style="margin-top:10rpx">
|
|
<view class="menu-group" v-for="(group, gIndex) in menuGroups" :key="gIndex">
|
|
<view
|
|
class="menu-item"
|
|
v-for="item in group"
|
|
:key="item.title"
|
|
@tap="onMenuTap(item)"
|
|
>
|
|
<view class="menu-left">
|
|
<view class="menu-icon">
|
|
<FaIcon :name="item.icon" color="#3c9cff" :size="18" />
|
|
</view>
|
|
<text class="menu-title">{{ item.title }}</text>
|
|
</view>
|
|
<view class="menu-right">
|
|
<text v-if="item.badge" class="menu-badge">{{ item.badge }}</text>
|
|
<FaIcon name="chevron-right" color="#c0c4cc" :size="12" />
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="logout-btn" hover-class="logout-btn-hover" @tap.stop="handleLogout">
|
|
<text class="logout-text">退出登录</text>
|
|
</view>
|
|
|
|
<view class="bottom-space" />
|
|
</view>
|
|
|
|
<AppTabbar current="profile" />
|
|
</view>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, computed, onMounted } from 'vue'
|
|
import { getUser, setUser, logout, isLoggedIn } from '@/utils/auth.js'
|
|
import { getCurrentUser, logoutApi } from '@/api/auth.js'
|
|
import AppTabbar from '@/components/AppTabbar.vue'
|
|
|
|
const user = ref(null)
|
|
|
|
const avatarText = computed(() => {
|
|
const name = user.value?.nickname || user.value?.name || '云'
|
|
return name.charAt(0).toUpperCase()
|
|
})
|
|
|
|
const userId = computed(() => {
|
|
if (user.value?.phone) {
|
|
return user.value.phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2')
|
|
}
|
|
if (user.value?.account) return user.value.account
|
|
if (user.value?.id) return String(user.value.id)
|
|
return '-'
|
|
})
|
|
|
|
const profileStats = ref([
|
|
{ label: '关注', value: '128' },
|
|
{ label: '粉丝', value: '256' },
|
|
{ label: '获赞', value: '1.2k' }
|
|
])
|
|
|
|
const menuGroups = ref([
|
|
[
|
|
{ title: '我的订单', icon: 'clipboard-list' },
|
|
{ title: '我的收藏', icon: 'star' },
|
|
{ title: '浏览历史', icon: 'clock-rotate-left' }
|
|
],
|
|
[
|
|
{ title: '消息通知', icon: 'bell', badge: '3' },
|
|
{ title: '账号安全', icon: 'lock' },
|
|
{ title: '隐私设置', icon: 'eye' }
|
|
],
|
|
[
|
|
{ title: '帮助中心', icon: 'circle-question' },
|
|
{ title: '关于我们', icon: 'circle-info' },
|
|
{ title: '设置', icon: 'gear' }
|
|
]
|
|
])
|
|
|
|
async function loadUser() {
|
|
user.value = getUser()
|
|
try {
|
|
const data = await getCurrentUser()
|
|
if (data) {
|
|
const prev = getUser() || {}
|
|
const merged = {
|
|
...prev,
|
|
...data,
|
|
nickname: data.name || data.nickname || data.account || prev.nickname || '云泽用户'
|
|
}
|
|
setUser(merged)
|
|
user.value = merged
|
|
}
|
|
} catch {
|
|
// 401 时 request 会跳转登录
|
|
user.value = getUser()
|
|
}
|
|
}
|
|
|
|
onMounted(() => {
|
|
if (!isLoggedIn()) {
|
|
uni.reLaunch({ url: '/pages/login/login' })
|
|
return
|
|
}
|
|
loadUser()
|
|
})
|
|
|
|
function onEdit() {
|
|
uni.showToast({ title: '编辑资料', icon: 'none' })
|
|
}
|
|
|
|
function onMenuTap(item) {
|
|
if (item.title === '账号安全') {
|
|
uni.navigateTo({ url: '/pages/login/forget' })
|
|
return
|
|
}
|
|
uni.showToast({ title: item.title, icon: 'none' })
|
|
}
|
|
|
|
function handleLogout() {
|
|
uni.showModal({
|
|
title: '提示',
|
|
content: '确定要退出登录吗?',
|
|
confirmText: '退出',
|
|
cancelText: '取消',
|
|
success(res) {
|
|
if (!res.confirm) return
|
|
// 先清本地并跳转,避免接口超时/失败导致“退出无效”
|
|
logout()
|
|
// 后端无状态退出,失败可忽略(不阻塞)
|
|
logoutApi().catch(() => {})
|
|
uni.reLaunch({ url: '/pages/login/login' })
|
|
}
|
|
})
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
@import '@/src/styles/page-common.scss';
|
|
|
|
.page {
|
|
@include page-shell;
|
|
}
|
|
|
|
.profile-header {
|
|
flex-shrink: 0;
|
|
padding: calc(var(--status-bar-height, 44px) + 24rpx) $page-padding-x 40rpx;
|
|
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.profile-header::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: -50%;
|
|
right: -50%;
|
|
width: 400rpx;
|
|
height: 400rpx;
|
|
border-radius: 50%;
|
|
background: rgba(255, 255, 255, 0.1);
|
|
pointer-events: none;
|
|
}
|
|
|
|
.profile-header::after {
|
|
content: '';
|
|
position: absolute;
|
|
bottom: -30%;
|
|
left: -20%;
|
|
width: 350rpx;
|
|
height: 350rpx;
|
|
border-radius: 50%;
|
|
background: rgba(255, 255, 255, 0.08);
|
|
pointer-events: none;
|
|
}
|
|
|
|
.profile-info {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 24rpx;
|
|
position: relative;
|
|
z-index: 1;
|
|
}
|
|
|
|
.profile-avatar {
|
|
width: 120rpx;
|
|
height: 120rpx;
|
|
border-radius: 50%;
|
|
background: linear-gradient(135deg, rgba(255, 255, 255, 0.25) 0%, rgba(255, 255, 255, 0.15) 100%);
|
|
border: 3rpx solid rgba(255, 255, 255, 0.4);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
flex-shrink: 0;
|
|
box-shadow: 0 12rpx 32rpx rgba(0, 0, 0, 0.15);
|
|
}
|
|
|
|
.avatar-text {
|
|
font-size: 44rpx;
|
|
font-weight: 600;
|
|
color: #ffffff;
|
|
}
|
|
|
|
.profile-detail {
|
|
flex: 1;
|
|
min-width: 0;
|
|
}
|
|
|
|
.profile-name {
|
|
display: block;
|
|
font-size: 36rpx;
|
|
font-weight: 600;
|
|
color: #ffffff;
|
|
}
|
|
|
|
.profile-id {
|
|
display: block;
|
|
font-size: 24rpx;
|
|
color: rgba(255, 255, 255, 0.75);
|
|
margin-top: 6rpx;
|
|
}
|
|
|
|
.profile-edit {
|
|
width: 60rpx;
|
|
height: 60rpx;
|
|
border-radius: 50%;
|
|
background: rgba(255, 255, 255, 0.15);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
|
|
&:active {
|
|
background: rgba(255, 255, 255, 0.25);
|
|
}
|
|
}
|
|
|
|
.profile-stats {
|
|
display: flex;
|
|
justify-content: space-around;
|
|
margin-top: 40rpx;
|
|
padding-top: 32rpx;
|
|
border-top: 1rpx solid rgba(255, 255, 255, 0.2);
|
|
position: relative;
|
|
z-index: 1;
|
|
}
|
|
|
|
.profile-stat {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
}
|
|
|
|
.stat-num {
|
|
font-size: 36rpx;
|
|
font-weight: 600;
|
|
color: #ffffff;
|
|
}
|
|
|
|
.stat-label {
|
|
font-size: 24rpx;
|
|
color: rgba(255, 255, 255, 0.75);
|
|
margin-top: 6rpx;
|
|
}
|
|
|
|
.main-scroll {
|
|
@include scroll-body;
|
|
margin-top: -20rpx;
|
|
}
|
|
|
|
.menu-group {
|
|
@include card;
|
|
margin-bottom: 24rpx;
|
|
overflow: hidden;
|
|
border-radius: 16rpx;
|
|
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
|
|
transition: box-shadow 0.3s ease;
|
|
}
|
|
|
|
.menu-group:active {
|
|
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.12);
|
|
}
|
|
|
|
.menu-item {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 32rpx 28rpx;
|
|
transition: background 0.2s ease, transform 0.2s ease;
|
|
|
|
& + & {
|
|
border-top: 1rpx solid $color-divider;
|
|
}
|
|
|
|
&:active {
|
|
background: $color-bg-page;
|
|
transform: translateX(4rpx);
|
|
}
|
|
}
|
|
|
|
.menu-left {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 20rpx;
|
|
flex: 1;
|
|
min-width: 0;
|
|
}
|
|
|
|
.menu-icon {
|
|
width: 56rpx;
|
|
height: 56rpx;
|
|
border-radius: 12rpx;
|
|
background: linear-gradient(135deg, rgba(79, 172, 254, 0.1) 0%, rgba(0, 242, 254, 0.1) 100%);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.menu-title {
|
|
font-size: 30rpx;
|
|
font-weight: 500;
|
|
color: $color-text;
|
|
}
|
|
|
|
.menu-right {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12rpx;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.menu-badge {
|
|
font-size: 20rpx;
|
|
font-weight: 600;
|
|
color: #ffffff;
|
|
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
|
|
padding: 6rpx 14rpx;
|
|
border-radius: 12rpx;
|
|
min-width: 36rpx;
|
|
text-align: center;
|
|
line-height: 1.4;
|
|
box-shadow: 0 4rpx 12rpx rgba(79, 172, 254, 0.3);
|
|
}
|
|
|
|
.logout-btn {
|
|
@include card;
|
|
height: 100rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin-bottom: 12rpx;
|
|
border-radius: 16rpx;
|
|
background: linear-gradient(135deg, rgba(245, 108, 108, 0.1) 0%, rgba(245, 108, 108, 0.05) 100%);
|
|
border: 1rpx solid rgba(245, 108, 108, 0.2);
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.logout-btn-hover {
|
|
background: linear-gradient(135deg, rgba(245, 108, 108, 0.2) 0%, rgba(245, 108, 108, 0.1) 100%) !important;
|
|
box-shadow: 0 4rpx 16rpx rgba(245, 108, 108, 0.15) !important;
|
|
}
|
|
|
|
.logout-text {
|
|
font-size: 32rpx;
|
|
font-weight: 600;
|
|
color: #f56c6c;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.bottom-space {
|
|
height: 48rpx;
|
|
}
|
|
</style>
|