mi-assessment/uniapp/pages/mine/index.vue
zpc ddf1a092b1 feat(user): 登录后获取用户信息、App启动刷新、我的页面下拉刷新
- 用户store新增fetchUserInfo action,调用/userInfo接口获取完整用户信息
- 登录页修复:LoginResponse只有token和userId,登录成功后调用fetchUserInfo获取资料
- App.vue启动时若已登录自动刷新用户信息
- 我的页面onShow时刷新用户信息,新增下拉刷新支持
- pages.json为我的页面启用enablePullDownRefresh
2026-02-20 22:56:47 +08:00

476 lines
10 KiB
Vue
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="mine-page">
<!-- 顶部渐变背景层 -->
<view class="top-gradient"></view>
<!-- 导航栏占位 -->
<view class="navbar-placeholder" :style="{ height: totalNavbarHeight + 'px' }"></view>
<!-- 页面内容 -->
<view class="page-content">
<!-- 用户信息区域 -->
<view class="user-section" @click="handleUserClick">
<image
class="user-avatar"
:src="isLoggedIn ? (userInfo.avatar || '/static/mine/icon-user.png') : '/static/mine/icon-user.png'"
mode="aspectFill"
/>
<view class="user-info">
<text class="user-name">{{ isLoggedIn ? (userInfo.nickname || '用户') : '点击登录' }}</text>
<text v-if="isLoggedIn" class="user-uid">UID: {{ userInfo.uid || '--' }}</text>
</view>
<text class="user-arrow"></text>
</view>
<!-- 常用功能 -->
<view class="menu-section">
<text class="section-title">常用功能</text>
<view class="menu-list">
<view class="menu-item" @click="goOrderList">
<image class="menu-icon" src="/static/mine/icon-order.png" mode="aspectFit" />
<text class="menu-label">我的订单</text>
<text class="menu-arrow"></text>
</view>
<view class="menu-item" @click="goAssessmentHistory">
<image class="menu-icon" src="/static/mine/icon-assessment-history.png" mode="aspectFit" />
<text class="menu-label">往期测评</text>
<text class="menu-arrow"></text>
</view>
<view class="menu-item" @click="handleContactUs">
<image class="menu-icon" src="/static/mine/icon-contact.png" mode="aspectFit" />
<text class="menu-label">联系我们</text>
<text class="menu-arrow"></text>
</view>
<view v-if="isPartner" class="menu-item" @click="goInvite">
<image class="menu-icon" src="/static/mine/icon-invite.png" mode="aspectFit" />
<text class="menu-label">邀请新用户</text>
<text class="menu-arrow"></text>
</view>
</view>
</view>
<!-- 其他功能 -->
<view class="menu-section">
<text class="section-title">其他功能</text>
<view class="menu-list">
<view class="menu-item" @click="goAbout">
<image class="menu-icon" src="/static/mine/icon-about.png" mode="aspectFit" />
<text class="menu-label">关于</text>
<text class="menu-arrow"></text>
</view>
<view class="menu-item" @click="goUserAgreement">
<image class="menu-icon" src="/static/mine/icon-user-agreement.png" mode="aspectFit" />
<text class="menu-label">用户协议</text>
<text class="menu-arrow"></text>
</view>
<view class="menu-item" @click="goPrivacyPolicy">
<image class="menu-icon" src="/static/mine/icon-privacy-policy.png" mode="aspectFit" />
<text class="menu-label">隐私协议</text>
<text class="menu-arrow"></text>
</view>
<view v-if="isLoggedIn" class="menu-item" @click="showLogoutPopup">
<image class="menu-icon" src="/static/mine/icon-logout.png" mode="aspectFit" />
<text class="menu-label">退出登录</text>
<text class="menu-arrow"></text>
</view>
</view>
</view>
<!-- 底部安全区域 -->
<view class="safe-bottom"></view>
</view>
<!-- 退出登录确认弹窗 -->
<view v-if="logoutPopupVisible" class="popup-mask" @click="hideLogoutPopup">
<view class="popup-container" @click.stop>
<view class="popup-content">
<text class="popup-title">提示</text>
<text class="popup-message">确定要退出登录吗?</text>
</view>
<view class="popup-buttons">
<view class="popup-btn cancel" @click="hideLogoutPopup">
<text>取消</text>
</view>
<view class="popup-btn confirm" @click="handleLogout">
<text>确定</text>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup>
/**
* 我的页面
* 展示用户信息和功能入口
*/
import { ref, computed, onMounted } from 'vue'
import { onShow, onPullDownRefresh } from '@dcloudio/uni-app'
import { useUserStore } from '@/store/user.js'
import { useNavbar } from '@/composables/useNavbar.js'
const userStore = useUserStore()
const { totalNavbarHeight } = useNavbar()
// 退出登录弹窗状态
const logoutPopupVisible = ref(false)
// 计算属性
const isLoggedIn = computed(() => userStore.isLoggedIn)
const isPartner = computed(() => userStore.isPartner)
const userInfo = computed(() => ({
userId: userStore.userId,
uid: userStore.uid,
nickname: userStore.nickname,
avatar: userStore.avatar
}))
/**
* 点击用户区域
*/
function handleUserClick() {
if (isLoggedIn.value) {
goProfile()
} else {
handleLogin()
}
}
/**
* 跳转登录页
*/
function handleLogin() {
uni.navigateTo({
url: '/pages/login/index'
})
}
/**
* 跳转个人资料页
*/
function goProfile() {
uni.navigateTo({
url: '/pages/mine/profile/index'
})
}
/**
* 跳转我的订单
*/
function goOrderList() {
uni.navigateTo({
url: '/pages/order/list/index'
})
}
/**
* 跳转往期测评
*/
function goAssessmentHistory() {
uni.navigateTo({
url: '/pages/assessment/history/index'
})
}
/**
* 联系我们
*/
function handleContactUs() {
uni.showModal({
title: '联系我们',
content: '如有问题请联系客服微信xxxxxx',
showCancel: false,
confirmText: '我知道了'
})
}
/**
* 跳转邀请新用户
*/
function goInvite() {
uni.navigateTo({
url: '/pages/invite/index'
})
}
/**
* 跳转关于页
*/
function goAbout() {
uni.navigateTo({
url: '/pages/about/index'
})
}
/**
* 跳转用户协议
*/
function goUserAgreement() {
uni.navigateTo({
url: '/pages/agreement/user/index'
})
}
/**
* 跳转隐私协议
*/
function goPrivacyPolicy() {
uni.navigateTo({
url: '/pages/agreement/privacy/index'
})
}
/**
* 显示退出登录弹窗
*/
function showLogoutPopup() {
logoutPopupVisible.value = true
}
/**
* 隐藏退出登录弹窗
*/
function hideLogoutPopup() {
logoutPopupVisible.value = false
}
/**
* 处理退出登录
*/
function handleLogout() {
userStore.logout()
logoutPopupVisible.value = false
uni.showToast({
title: '已退出登录',
icon: 'success'
})
}
/**
* 页面显示时刷新用户信息
*/
onShow(() => {
if (userStore.isLoggedIn) {
userStore.fetchUserInfo()
}
})
/**
* 下拉刷新
*/
onPullDownRefresh(async () => {
if (userStore.isLoggedIn) {
await userStore.fetchUserInfo()
}
uni.stopPullDownRefresh()
})
/**
* 页面加载
*/
onMounted(() => {
userStore.restoreFromStorage()
})
</script>
<style lang="scss" scoped>
@import '@/styles/variables.scss';
.mine-page {
min-height: 100vh;
background-color: #F3F3F3;
position: relative;
}
// 顶部渐变背景
.top-gradient {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 500rpx;
background: linear-gradient(177deg, #FFEFDE 1%, #F3F3F3 28%);
z-index: 0;
}
.navbar-placeholder {
width: 100%;
position: relative;
z-index: 1;
}
// 页面内容
.page-content {
padding: $spacing-lg;
padding-bottom: env(safe-area-inset-bottom);
position: relative;
z-index: 1;
}
// 用户信息区域
.user-section {
display: flex;
align-items: center;
padding: $spacing-xl 0;
margin-bottom: $spacing-lg;
.user-avatar {
width: 120rpx;
height: 120rpx;
border-radius: 50%;
margin-right: $spacing-lg;
}
.user-info {
flex: 1;
display: flex;
flex-direction: column;
.user-name {
font-size: $font-size-xl;
font-weight: $font-weight-medium;
color: $text-color;
}
.user-uid {
font-size: $font-size-sm;
color: $text-placeholder;
margin-top: 8rpx;
}
}
.user-arrow {
font-size: 40rpx;
color: $text-placeholder;
}
}
// 菜单区域
.menu-section {
margin-bottom: $spacing-xl;
.section-title {
display: block;
font-size: $font-size-lg;
font-weight: $font-weight-bold;
color: $text-color;
margin-bottom: $spacing-md;
}
.menu-list {
background-color: transparent;
}
}
// 菜单项
.menu-item {
display: flex;
align-items: center;
padding: $spacing-lg 0;
&:active {
opacity: 0.7;
}
.menu-icon {
width: 44rpx;
height: 44rpx;
margin-right: $spacing-lg;
}
.menu-label {
flex: 1;
font-size: $font-size-md;
color: $text-color;
}
.menu-arrow {
font-size: 32rpx;
color: $text-placeholder;
}
}
// 底部安全区域
.safe-bottom {
height: 40rpx;
padding-bottom: env(safe-area-inset-bottom);
}
// 退出登录弹窗
.popup-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: $bg-mask;
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.popup-container {
width: 560rpx;
background-color: $bg-white;
border-radius: $border-radius-lg;
overflow: hidden;
.popup-content {
padding: 60rpx 40rpx 40rpx;
text-align: center;
.popup-title {
display: block;
font-size: $font-size-lg;
font-weight: $font-weight-medium;
color: $text-color;
margin-bottom: $spacing-md;
}
.popup-message {
display: block;
font-size: $font-size-md;
color: $text-secondary;
}
}
.popup-buttons {
display: flex;
border-top: 1rpx solid $border-light;
.popup-btn {
flex: 1;
height: 100rpx;
display: flex;
align-items: center;
justify-content: center;
&:active {
background-color: $bg-gray;
}
text {
font-size: $font-size-lg;
}
&.cancel {
border-right: 1rpx solid $border-light;
text {
color: $text-secondary;
}
}
&.confirm {
text {
color: $primary-color;
}
}
}
}
}
</style>