553 lines
12 KiB
Plaintext
553 lines
12 KiB
Plaintext
<template>
|
||
<view class="page">
|
||
<scroll-view class="page-scroll" scroll-y>
|
||
<!-- 用户信息 -->
|
||
<view class="user-section">
|
||
<view class="user-card">
|
||
<view class="avatar-wrapper">
|
||
<image
|
||
class="user-avatar"
|
||
:src="userInfo.avatar || '/static/images/default-avatar.png'"
|
||
mode="aspectFill"
|
||
></image>
|
||
</view>
|
||
<view class="user-info">
|
||
<text class="user-name">{{ userInfo.nickName || '点击登录' }}</text>
|
||
<text class="user-phone" v-if="userInfo.phone">{{ userInfo.phone }}</text>
|
||
</view>
|
||
<view class="user-action" @click="handleLogin" v-if="!isLoggedIn">
|
||
<text class="action-text">登录</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 功能菜单 -->
|
||
<view class="menu-section">
|
||
<view class="menu-group">
|
||
<view class="menu-item" @click="goToBookingList">
|
||
<view class="menu-left">
|
||
<text class="menu-icon">📋</text>
|
||
<text class="menu-text">我的预约</text>
|
||
</view>
|
||
<view class="menu-right">
|
||
<text class="menu-badge" v-if="bookingCount > 0">{{ bookingCount }}</text>
|
||
<text class="menu-arrow">›</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="menu-item" @click="goToFavorites">
|
||
<view class="menu-left">
|
||
<text class="menu-icon">❤️</text>
|
||
<text class="menu-text">我的收藏</text>
|
||
</view>
|
||
<view class="menu-right">
|
||
<text class="menu-badge" v-if="favoriteCount > 0">{{ favoriteCount }}</text>
|
||
<text class="menu-arrow">›</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="menu-group">
|
||
<view class="menu-item" @click="goToAbout">
|
||
<view class="menu-left">
|
||
<text class="menu-icon">🏠</text>
|
||
<text class="menu-text">关于我们</text>
|
||
</view>
|
||
<view class="menu-right">
|
||
<text class="menu-arrow">›</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="menu-item" @click="callService">
|
||
<view class="menu-left">
|
||
<text class="menu-icon">📞</text>
|
||
<text class="menu-text">联系客服</text>
|
||
</view>
|
||
<view class="menu-right">
|
||
<text class="menu-value">400-888-8888</text>
|
||
<text class="menu-arrow">›</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="menu-item" @click="openFeedback">
|
||
<view class="menu-left">
|
||
<text class="menu-icon">💬</text>
|
||
<text class="menu-text">意见反馈</text>
|
||
</view>
|
||
<view class="menu-right">
|
||
<text class="menu-arrow">›</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="menu-group">
|
||
<view class="menu-item" @click="checkUpdate">
|
||
<view class="menu-left">
|
||
<text class="menu-icon">🔄</text>
|
||
<text class="menu-text">检查更新</text>
|
||
</view>
|
||
<view class="menu-right">
|
||
<text class="menu-value">v1.0.0</text>
|
||
<text class="menu-arrow">›</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="menu-item" @click="clearCache">
|
||
<view class="menu-left">
|
||
<text class="menu-icon">🧹</text>
|
||
<text class="menu-text">清除缓存</text>
|
||
</view>
|
||
<view class="menu-right">
|
||
<text class="menu-value">{{ cacheSize }}</text>
|
||
<text class="menu-arrow">›</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 退出登录 -->
|
||
<view class="logout-section" v-if="isLoggedIn">
|
||
<view class="logout-btn" @click="handleLogout">
|
||
<text class="logout-text">退出登录</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 底部间距 -->
|
||
<view class="bottom-space"></view>
|
||
</scroll-view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup lang="uts">
|
||
import { STORAGE_KEYS } from '@/utils/config.uts'
|
||
import { wechatLogin } from '@/api/index.uts'
|
||
|
||
// 用户信息类型
|
||
type UserInfo = {
|
||
id : string
|
||
nickName : string
|
||
avatar : string
|
||
phone : string
|
||
}
|
||
|
||
// 用户信息
|
||
const userInfo = ref<UserInfo>({
|
||
id: '',
|
||
nickName: '',
|
||
avatar: '',
|
||
phone: ''
|
||
})
|
||
|
||
// 是否登录
|
||
const isLoggedIn = ref(false)
|
||
|
||
// 预约数量
|
||
const bookingCount = ref(0)
|
||
|
||
// 收藏数量
|
||
const favoriteCount = ref(0)
|
||
|
||
// 缓存大小
|
||
const cacheSize = ref('0KB')
|
||
|
||
// 检查登录状态
|
||
const checkLoginStatus = () => {
|
||
const token = uni.getStorageSync(STORAGE_KEYS.TOKEN) as string
|
||
isLoggedIn.value = token != ''
|
||
|
||
if (isLoggedIn.value) {
|
||
// 获取用户信息
|
||
const info = uni.getStorageSync(STORAGE_KEYS.USER_INFO) as UTSJSONObject | null
|
||
if (info != null) {
|
||
userInfo.value = {
|
||
id: (info['id'] ?? '') as string,
|
||
nickName: (info['nickName'] ?? '') as string,
|
||
avatar: (info['avatar'] ?? '') as string,
|
||
phone: (info['phone'] ?? '') as string
|
||
} as UserInfo
|
||
}
|
||
}
|
||
}
|
||
|
||
// 获取缓存大小
|
||
const getCacheSize = () => {
|
||
uni.getStorageInfo({
|
||
success: (res) => {
|
||
const size = res.currentSize
|
||
if (size < 1024) {
|
||
cacheSize.value = `${size}KB`
|
||
} else {
|
||
cacheSize.value = `${(size / 1024).toFixed(2)}MB`
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
// 获取统计数据
|
||
const fetchStats = () => {
|
||
// 获取预约数量(可以从后端API获取)
|
||
bookingCount.value = 0
|
||
|
||
// 获取收藏数量
|
||
const favorites = uni.getStorageSync(STORAGE_KEYS.FAVORITES) as string[]
|
||
favoriteCount.value = favorites ? favorites.length : 0
|
||
}
|
||
|
||
// 登录
|
||
const handleLogin = () => {
|
||
// #ifdef MP-WEIXIN
|
||
// 必须在用户点击事件中直接调用 getUserProfile
|
||
uni.getUserProfile({
|
||
desc: '用于完善用户资料',
|
||
success: (profileRes) => {
|
||
// 在授权成功的回调中再调用 uni.login 获取 code
|
||
uni.login({
|
||
provider: 'weixin',
|
||
success: async (loginRes) => {
|
||
const code = loginRes.code
|
||
try {
|
||
// 调用后端微信登录接口
|
||
const res = await wechatLogin(code)
|
||
|
||
if (res.code === 0 && res.data != null) {
|
||
const data = res.data as UTSJSONObject
|
||
const token = data['access_token'] as string
|
||
const userDataObj = data['user'] as UTSJSONObject
|
||
|
||
// 保存 token
|
||
uni.setStorageSync(STORAGE_KEYS.TOKEN, token)
|
||
|
||
// 保存用户信息
|
||
userInfo.value = {
|
||
id: String(userDataObj['id']),
|
||
nickName: profileRes.userInfo.nickName,
|
||
avatar: profileRes.userInfo.avatarUrl,
|
||
phone: (userDataObj['phone'] ?? '') as string
|
||
} as UserInfo
|
||
|
||
uni.setStorageSync(STORAGE_KEYS.USER_INFO, {
|
||
id: userDataObj['id'],
|
||
nickName: profileRes.userInfo.nickName,
|
||
avatar: profileRes.userInfo.avatarUrl,
|
||
phone: userDataObj['phone']
|
||
} as UTSJSONObject)
|
||
|
||
isLoggedIn.value = true
|
||
|
||
uni.showToast({
|
||
title: '登录成功',
|
||
icon: 'success'
|
||
})
|
||
|
||
// 刷新统计数据
|
||
fetchStats()
|
||
} else {
|
||
throw new Error(res.message)
|
||
}
|
||
} catch (error) {
|
||
console.error('登录失败', error)
|
||
uni.showToast({
|
||
title: '登录失败,请重试',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
},
|
||
fail: () => {
|
||
uni.showToast({
|
||
title: '登录失败',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
})
|
||
},
|
||
fail: () => {
|
||
uni.showToast({
|
||
title: '授权失败',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
})
|
||
// #endif
|
||
|
||
// #ifndef MP-WEIXIN
|
||
uni.showToast({
|
||
title: '请在微信小程序中登录',
|
||
icon: 'none'
|
||
})
|
||
// #endif
|
||
}
|
||
|
||
// 退出登录
|
||
const handleLogout = () => {
|
||
uni.showModal({
|
||
title: '提示',
|
||
content: '确定要退出登录吗?',
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
uni.removeStorageSync(STORAGE_KEYS.TOKEN)
|
||
uni.removeStorageSync(STORAGE_KEYS.USER_INFO)
|
||
isLoggedIn.value = false
|
||
userInfo.value = {
|
||
id: '',
|
||
nickName: '',
|
||
avatar: '',
|
||
phone: ''
|
||
} as UserInfo
|
||
|
||
uni.showToast({
|
||
title: '已退出登录',
|
||
icon: 'success'
|
||
})
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
// 跳转预约列表
|
||
const goToBookingList = () => {
|
||
if (!isLoggedIn.value) {
|
||
uni.showToast({
|
||
title: '请先登录',
|
||
icon: 'none'
|
||
})
|
||
return
|
||
}
|
||
uni.navigateTo({
|
||
url: '/pages/user/booking-list'
|
||
})
|
||
}
|
||
|
||
// 跳转收藏列表
|
||
const goToFavorites = () => {
|
||
if (!isLoggedIn.value) {
|
||
uni.showToast({
|
||
title: '请先登录',
|
||
icon: 'none'
|
||
})
|
||
return
|
||
}
|
||
uni.navigateTo({
|
||
url: '/pages/user/favorites'
|
||
})
|
||
}
|
||
|
||
// 关于我们
|
||
const goToAbout = () => {
|
||
uni.navigateTo({
|
||
url: '/pages/about/index'
|
||
})
|
||
}
|
||
|
||
// 联系客服
|
||
const callService = () => {
|
||
uni.makePhoneCall({
|
||
phoneNumber: '400-888-8888',
|
||
fail: () => {
|
||
uni.showToast({
|
||
title: '拨打电话失败',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
})
|
||
}
|
||
|
||
// 意见反馈
|
||
const openFeedback = () => {
|
||
// #ifdef MP-WEIXIN
|
||
// 微信小程序可以使用feedback
|
||
// #endif
|
||
uni.showToast({
|
||
title: '功能开发中',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
|
||
// 检查更新
|
||
const checkUpdate = () => {
|
||
uni.showToast({
|
||
title: '已是最新版本',
|
||
icon: 'success'
|
||
})
|
||
}
|
||
|
||
// 清除缓存
|
||
const clearCache = () => {
|
||
uni.showModal({
|
||
title: '提示',
|
||
content: '确定要清除缓存吗?',
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
uni.clearStorage({
|
||
success: () => {
|
||
cacheSize.value = '0KB'
|
||
uni.showToast({
|
||
title: '清除成功',
|
||
icon: 'success'
|
||
})
|
||
}
|
||
})
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
onShow(() => {
|
||
checkLoginStatus()
|
||
getCacheSize()
|
||
})
|
||
</script>
|
||
|
||
<style lang="scss">
|
||
.page {
|
||
flex: 1;
|
||
background-color: #f5f5f5;
|
||
}
|
||
|
||
.page-scroll {
|
||
flex: 1;
|
||
}
|
||
|
||
/* 用户信息 */
|
||
.user-section {
|
||
background: linear-gradient(135deg, #D4A574 0%, #B8895A 100%);
|
||
padding: 48rpx 32rpx 64rpx;
|
||
}
|
||
|
||
.user-card {
|
||
flex-direction: row;
|
||
align-items: center;
|
||
}
|
||
|
||
.avatar-wrapper {
|
||
width: 120rpx;
|
||
height: 120rpx;
|
||
border-radius: 50%;
|
||
overflow: hidden;
|
||
border-width: 4rpx;
|
||
border-style: solid;
|
||
border-color: rgba(255, 255, 255, 0.5);
|
||
}
|
||
|
||
.user-avatar {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
.user-info {
|
||
flex: 1;
|
||
margin-left: 24rpx;
|
||
}
|
||
|
||
.user-name {
|
||
font-size: 36rpx;
|
||
font-weight: 600;
|
||
color: #ffffff;
|
||
}
|
||
|
||
.user-phone {
|
||
font-size: 26rpx;
|
||
color: rgba(255, 255, 255, 0.8);
|
||
margin-top: 8rpx;
|
||
}
|
||
|
||
.user-action {
|
||
background-color: rgba(255, 255, 255, 0.2);
|
||
padding: 16rpx 32rpx;
|
||
border-radius: 999rpx;
|
||
}
|
||
|
||
.action-text {
|
||
font-size: 28rpx;
|
||
color: #ffffff;
|
||
}
|
||
|
||
/* 菜单区域 */
|
||
.menu-section {
|
||
margin-top: -32rpx;
|
||
padding: 0 24rpx;
|
||
}
|
||
|
||
.menu-group {
|
||
background-color: #ffffff;
|
||
border-radius: 16rpx;
|
||
margin-bottom: 24rpx;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.menu-item {
|
||
flex-direction: row;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 32rpx;
|
||
border-bottom-width: 1rpx;
|
||
border-bottom-style: solid;
|
||
border-bottom-color: #EBEEF5;
|
||
}
|
||
|
||
.menu-item:last-child {
|
||
border-bottom-width: 0;
|
||
}
|
||
|
||
.menu-left {
|
||
flex-direction: row;
|
||
align-items: center;
|
||
}
|
||
|
||
.menu-icon {
|
||
font-size: 40rpx;
|
||
margin-right: 24rpx;
|
||
}
|
||
|
||
.menu-text {
|
||
font-size: 30rpx;
|
||
color: #333333;
|
||
}
|
||
|
||
.menu-right {
|
||
flex-direction: row;
|
||
align-items: center;
|
||
}
|
||
|
||
.menu-value {
|
||
font-size: 26rpx;
|
||
color: #909399;
|
||
margin-right: 8rpx;
|
||
}
|
||
|
||
.menu-badge {
|
||
background-color: #F56C6C;
|
||
color: #ffffff;
|
||
font-size: 22rpx;
|
||
padding: 4rpx 12rpx;
|
||
border-radius: 999rpx;
|
||
margin-right: 8rpx;
|
||
}
|
||
|
||
.menu-arrow {
|
||
font-size: 32rpx;
|
||
color: #909399;
|
||
}
|
||
|
||
/* 退出登录 */
|
||
.logout-section {
|
||
padding: 24rpx;
|
||
}
|
||
|
||
.logout-btn {
|
||
background-color: #ffffff;
|
||
border-radius: 16rpx;
|
||
padding: 28rpx 0;
|
||
align-items: center;
|
||
}
|
||
|
||
.logout-text {
|
||
font-size: 30rpx;
|
||
color: #F56C6C;
|
||
}
|
||
|
||
/* 底部间距 */
|
||
.bottom-space {
|
||
height: 120rpx;
|
||
}
|
||
</style>
|