Files
ShaFaFanXin/前端/pages/user/index.uvue

553 lines
12 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

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">
<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>