6a18d3de61
- 门票Tab展示已购门票记录(可核销/已核销/已退款) - 购买次数去掉合计金额行 - 登录后直接跳profile/detail,合并setup页(不区分模式) - profile/detail增加返回首页按钮 - 约课页移除UnauthLoginPanel - 401跳转改为reLaunch到login页,不显示toast - membership/cards仅展示可用卡,恢复点击进详情 - available-sessions改为只读展示,移除预约逻辑 - profile/orders重写样式,单行状态筛选 - profile/balance重写样式,onShow自动刷新 - profile/index移除冗余菜单项(篮球门票/充值/我的预约/个人信息/切换场馆) - profile/index移除AgreementLinks,我的次卡改为我的会员卡 - pages.json移除setup页注册
517 lines
11 KiB
Vue
517 lines
11 KiB
Vue
<template>
|
|
<view class="profile-page container">
|
|
<view v-if="!session.isLoggedIn" class="user-profile-hero" @tap="login">
|
|
<view class="hero-left">
|
|
<view class="hero-avatar placeholder">
|
|
<text class="hero-placeholder-text">点击登录</text>
|
|
</view>
|
|
<view class="hero-meta">
|
|
<text class="hero-name">点击登录</text>
|
|
</view>
|
|
</view>
|
|
<view class="hero-right">
|
|
<view class="hero-arrow" />
|
|
</view>
|
|
</view>
|
|
|
|
<view v-else class="user-profile-hero" @tap="go('/pages/comprehensive/profile/detail')">
|
|
<view class="hero-left">
|
|
<image v-if="session.member?.avatar_url" :src="session.member.avatar_url" mode="aspectFill" class="hero-avatar" />
|
|
<view v-else class="hero-avatar placeholder">
|
|
<text>{{ avatarInitial }}</text>
|
|
</view>
|
|
<view class="hero-meta">
|
|
<text class="hero-name">{{ session.member?.nickname }}</text>
|
|
<view v-if="session.member?.phone" class="hero-sub-row">
|
|
<text class="hero-phone">{{ session.member.phone }}</text>
|
|
</view>
|
|
<text class="hero-balance">余额 ¥{{ balanceText }}</text>
|
|
</view>
|
|
</view>
|
|
<view class="hero-right">
|
|
<view class="hero-arrow" />
|
|
</view>
|
|
</view>
|
|
|
|
<view class="menu-group-card">
|
|
<view v-for="item in menuItems" :key="item.title" class="menu-item" @tap="go(item.url)">
|
|
<view class="item-left">
|
|
<view class="item-icon-container" :class="item.theme">
|
|
<view class="icon-order" v-if="item.icon === 'order'">
|
|
<view class="icon-order-line" />
|
|
<view class="icon-order-line short" />
|
|
</view>
|
|
<view class="icon-private" v-else-if="item.icon === 'private'">
|
|
<view class="icon-private-net" />
|
|
</view>
|
|
<view class="icon-ticket" v-else-if="item.icon === 'ticket'">
|
|
<view class="icon-ticket-dot" />
|
|
</view>
|
|
<view class="icon-profile" v-else>
|
|
<view class="icon-profile-head" />
|
|
<view class="icon-profile-body" />
|
|
</view>
|
|
</view>
|
|
<text class="menu-title">{{ item.title }}</text>
|
|
</view>
|
|
<view class="item-right">
|
|
<view class="menu-arrow" />
|
|
</view>
|
|
</view>
|
|
<view v-if="session.isLoggedIn" class="menu-item logout" @tap="logout">
|
|
<view class="item-left">
|
|
<view class="item-icon-container logout-theme">
|
|
<view class="icon-logout">
|
|
<view class="icon-logout-door" />
|
|
<view class="icon-logout-arrow" />
|
|
</view>
|
|
</view>
|
|
<text class="menu-title">退出登录</text>
|
|
</view>
|
|
<view class="item-right">
|
|
<view class="menu-arrow" />
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { computed, onMounted, ref } from 'vue'
|
|
import { useComprehensiveSessionStore } from '@/modules/comprehensive/stores/session'
|
|
import { memberApi } from '@/modules/comprehensive/api/member'
|
|
import { formatAmount } from '@/modules/comprehensive/utils/money'
|
|
|
|
const session = useComprehensiveSessionStore()
|
|
const avatarInitial = computed(() => (session.member?.nickname || '用户').slice(0, 1))
|
|
const balance = ref('0.00')
|
|
const balanceText = computed(() => formatAmount(balance.value))
|
|
|
|
const menuItems = [
|
|
{ title: '我的会员卡', url: '/pages/comprehensive/membership/cards', theme: 'profile-theme', icon: 'profile' },
|
|
{ title: '我的订单', url: '/pages/comprehensive/profile/orders', theme: 'order-theme', icon: 'order' },
|
|
{ title: '余额流水', url: '/pages/comprehensive/profile/balance', theme: 'order-theme', icon: 'order' }
|
|
]
|
|
|
|
onMounted(async () => {
|
|
await session.init()
|
|
try {
|
|
const data = await memberApi.getBalance()
|
|
balance.value = data.balance
|
|
} catch {
|
|
balance.value = '0.00'
|
|
}
|
|
})
|
|
|
|
const login = () => {
|
|
uni.navigateTo({ url: '/pages/comprehensive/profile/login' })
|
|
}
|
|
|
|
const logout = () => {
|
|
session.logout()
|
|
uni.showToast({ title: '已退出', icon: 'none' })
|
|
}
|
|
|
|
const go = (url) => {
|
|
uni.navigateTo({ url })
|
|
}
|
|
|
|
defineExpose({ onPageShow: () => {} })
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.login-entry {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
min-height: 160rpx;
|
|
padding: $spacing-lg;
|
|
border: 1rpx solid rgba(241, 245, 249, 0.9);
|
|
border-radius: $border-radius-lg;
|
|
background-color: $bg-color-main;
|
|
box-shadow: 0 10rpx 40rpx rgba(15, 23, 42, 0.02);
|
|
}
|
|
|
|
.login-entry:active {
|
|
opacity: 0.96;
|
|
transform: scale(0.99);
|
|
}
|
|
|
|
.login-entry-left {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: $spacing-md;
|
|
}
|
|
|
|
.login-entry-avatar {
|
|
width: 104rpx;
|
|
height: 104rpx;
|
|
flex-shrink: 0;
|
|
border-radius: 50%;
|
|
background: $bg-color-hover;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.login-entry-avatar.placeholder {
|
|
background: $color-primary-light;
|
|
}
|
|
|
|
.login-entry-icon {
|
|
font-size: 48rpx;
|
|
}
|
|
|
|
.login-entry-meta {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 6rpx;
|
|
}
|
|
|
|
.login-entry-title {
|
|
color: $text-color-main;
|
|
font-size: 34rpx;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.login-entry-sub {
|
|
color: $text-color-muted;
|
|
font-size: 24rpx;
|
|
}
|
|
|
|
.user-profile-hero {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
min-height: 160rpx;
|
|
padding: $spacing-lg;
|
|
border: 1rpx solid rgba(241, 245, 249, 0.9);
|
|
border-radius: $border-radius-lg;
|
|
background-color: $bg-color-main;
|
|
box-shadow: 0 10rpx 40rpx rgba(15, 23, 42, 0.02);
|
|
transition: all 0.2s ease;
|
|
}
|
|
|
|
.user-profile-hero:active {
|
|
opacity: 0.96;
|
|
transform: scale(0.99);
|
|
}
|
|
|
|
.hero-left {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: $spacing-md;
|
|
min-width: 0;
|
|
}
|
|
|
|
.hero-avatar {
|
|
width: 104rpx;
|
|
height: 104rpx;
|
|
flex-shrink: 0;
|
|
border-radius: 50%;
|
|
background: $bg-color-hover;
|
|
}
|
|
|
|
.hero-avatar.placeholder {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
color: $text-color-main;
|
|
font-size: 36rpx;
|
|
font-weight: 900;
|
|
background: $color-primary-light;
|
|
}
|
|
|
|
.hero-placeholder-text {
|
|
font-size: 20rpx;
|
|
color: $text-color-muted;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.hero-meta {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 6rpx;
|
|
min-width: 0;
|
|
}
|
|
|
|
.hero-name,
|
|
.hero-phone,
|
|
.hero-balance,
|
|
.menu-title {
|
|
display: block;
|
|
}
|
|
|
|
.hero-name {
|
|
color: $text-color-main;
|
|
font-size: 34rpx;
|
|
font-weight: 700;
|
|
line-height: 1.3;
|
|
}
|
|
|
|
.hero-phone {
|
|
color: $text-color-muted;
|
|
font-size: 24rpx;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.hero-balance {
|
|
margin-top: 4rpx;
|
|
color: $color-primary;
|
|
font-size: 24rpx;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.hero-arrow,
|
|
.menu-arrow {
|
|
width: 18rpx;
|
|
height: 18rpx;
|
|
border-top: 3rpx solid $text-color-light;
|
|
border-right: 3rpx solid $text-color-light;
|
|
transform: rotate(45deg);
|
|
}
|
|
|
|
.menu-group-card {
|
|
margin-top: $spacing-lg;
|
|
overflow: hidden;
|
|
border: 1rpx solid rgba(241, 245, 249, 0.9);
|
|
border-radius: $border-radius-lg;
|
|
background-color: $bg-color-main;
|
|
box-shadow: 0 10rpx 40rpx rgba(15, 23, 42, 0.02);
|
|
}
|
|
|
|
.menu-item {
|
|
min-height: 112rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 0 $spacing-lg;
|
|
border-bottom: 1rpx solid rgba(241, 245, 249, 0.9);
|
|
}
|
|
|
|
.menu-item:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.item-left,
|
|
.item-right {
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.item-left {
|
|
gap: $spacing-md;
|
|
}
|
|
|
|
.item-icon-container {
|
|
width: 58rpx;
|
|
height: 58rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border-radius: $border-radius-base;
|
|
background: $bg-color-soft;
|
|
color: $text-color-muted;
|
|
}
|
|
|
|
.item-icon-container.order-theme {
|
|
background: $color-primary-light;
|
|
color: $color-primary;
|
|
}
|
|
|
|
.item-icon-container.profile-theme {
|
|
background: $color-success-light;
|
|
color: $color-success;
|
|
}
|
|
|
|
.item-icon-container.private-theme {
|
|
background: rgba(14, 165, 233, 0.12);
|
|
color: #0284C7;
|
|
}
|
|
|
|
.item-icon-container.ticket-theme {
|
|
background: rgba(245, 158, 11, 0.14);
|
|
color: #D97706;
|
|
}
|
|
|
|
.item-icon-container.logout-theme {
|
|
background: $color-danger-light;
|
|
color: $color-danger;
|
|
}
|
|
|
|
.icon-order,
|
|
.icon-private,
|
|
.icon-ticket,
|
|
.icon-profile,
|
|
.icon-logout {
|
|
position: relative;
|
|
width: 32rpx;
|
|
height: 32rpx;
|
|
}
|
|
|
|
.icon-order {
|
|
border: 3rpx solid currentColor;
|
|
border-radius: 4rpx;
|
|
}
|
|
|
|
.icon-order::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: -8rpx;
|
|
left: 7rpx;
|
|
width: 14rpx;
|
|
height: 10rpx;
|
|
border: 3rpx solid currentColor;
|
|
border-bottom: none;
|
|
border-radius: 4rpx 4rpx 0 0;
|
|
background: inherit;
|
|
}
|
|
|
|
.icon-order-line {
|
|
position: absolute;
|
|
left: 7rpx;
|
|
right: 7rpx;
|
|
top: 10rpx;
|
|
height: 3rpx;
|
|
border-radius: 2rpx;
|
|
background: currentColor;
|
|
}
|
|
|
|
.icon-order-line.short {
|
|
top: 19rpx;
|
|
right: 12rpx;
|
|
}
|
|
|
|
.icon-private {
|
|
border: 3rpx solid currentColor;
|
|
border-radius: 50%;
|
|
}
|
|
|
|
.icon-private::before,
|
|
.icon-private::after {
|
|
content: '';
|
|
position: absolute;
|
|
left: 5rpx;
|
|
right: 5rpx;
|
|
height: 3rpx;
|
|
border-radius: 2rpx;
|
|
background: currentColor;
|
|
}
|
|
|
|
.icon-private::before {
|
|
top: 9rpx;
|
|
}
|
|
|
|
.icon-private::after {
|
|
bottom: 9rpx;
|
|
}
|
|
|
|
.icon-private-net {
|
|
position: absolute;
|
|
top: 3rpx;
|
|
bottom: 3rpx;
|
|
left: 12rpx;
|
|
width: 4rpx;
|
|
border-radius: 2rpx;
|
|
background: currentColor;
|
|
}
|
|
|
|
.icon-ticket {
|
|
border: 3rpx solid currentColor;
|
|
border-radius: 6rpx;
|
|
transform: rotate(-8deg);
|
|
}
|
|
|
|
.icon-ticket::before,
|
|
.icon-ticket::after {
|
|
content: '';
|
|
position: absolute;
|
|
top: 9rpx;
|
|
bottom: 9rpx;
|
|
width: 3rpx;
|
|
border-radius: 2rpx;
|
|
background: currentColor;
|
|
}
|
|
|
|
.icon-ticket::before {
|
|
left: 8rpx;
|
|
}
|
|
|
|
.icon-ticket::after {
|
|
right: 8rpx;
|
|
}
|
|
|
|
.icon-ticket-dot {
|
|
position: absolute;
|
|
top: 12rpx;
|
|
left: 13rpx;
|
|
width: 6rpx;
|
|
height: 6rpx;
|
|
border-radius: 50%;
|
|
background: currentColor;
|
|
}
|
|
|
|
.icon-profile-head {
|
|
position: absolute;
|
|
top: 3rpx;
|
|
left: 10rpx;
|
|
width: 12rpx;
|
|
height: 12rpx;
|
|
border: 3rpx solid currentColor;
|
|
border-radius: 50%;
|
|
}
|
|
|
|
.icon-profile-body {
|
|
position: absolute;
|
|
left: 5rpx;
|
|
right: 5rpx;
|
|
bottom: 3rpx;
|
|
height: 12rpx;
|
|
border: 3rpx solid currentColor;
|
|
border-radius: 16rpx 16rpx 4rpx 4rpx;
|
|
}
|
|
|
|
.icon-logout-door {
|
|
position: absolute;
|
|
left: 4rpx;
|
|
top: 4rpx;
|
|
width: 15rpx;
|
|
height: 24rpx;
|
|
border: 3rpx solid currentColor;
|
|
border-right: none;
|
|
border-radius: 4rpx 0 0 4rpx;
|
|
}
|
|
|
|
.icon-logout-arrow {
|
|
position: absolute;
|
|
right: 2rpx;
|
|
top: 14rpx;
|
|
width: 20rpx;
|
|
height: 3rpx;
|
|
border-radius: 2rpx;
|
|
background: currentColor;
|
|
}
|
|
|
|
.icon-logout-arrow::after {
|
|
content: '';
|
|
position: absolute;
|
|
right: 0;
|
|
top: -6rpx;
|
|
width: 10rpx;
|
|
height: 10rpx;
|
|
border-top: 3rpx solid currentColor;
|
|
border-right: 3rpx solid currentColor;
|
|
transform: rotate(45deg);
|
|
}
|
|
|
|
.menu-title {
|
|
color: $text-color-main;
|
|
font-size: 28rpx;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.logout .menu-title {
|
|
color: $color-danger;
|
|
}
|
|
</style>
|