|
|
@@ -0,0 +1,498 @@
|
|
|
+<template>
|
|
|
+ <view class="my-page">
|
|
|
+ <!-- 自定义头部 -->
|
|
|
+ <view class="custom-header" :style="{ paddingTop: statusBarHeight + 'px' }">
|
|
|
+ <view class="header-content">
|
|
|
+ <text class="header-title">{{ t('common.mine.title') }}</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 页面内容 -->
|
|
|
+ <view class="page-body">
|
|
|
+ <!-- 用户名片区域 -->
|
|
|
+ <view class="user-card">
|
|
|
+ <image
|
|
|
+ class="avatar"
|
|
|
+ :src="userInfo.avatar"
|
|
|
+ mode="aspectFill"
|
|
|
+ :class="{ loading: loading }"
|
|
|
+ @error="handleAvatarError"
|
|
|
+ />
|
|
|
+ <text class="nickname" :class="{ loading: loading }">{{ displayNickname }}</text>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 功能列表 -->
|
|
|
+ <view class="function-list">
|
|
|
+ <!-- 基本信息 -->
|
|
|
+ <view class="list-item" @click="handleBasicInfo">
|
|
|
+ <view class="item-left">
|
|
|
+ <text class="item-icon">👤</text>
|
|
|
+ <text class="item-label">{{ t('common.mine.basicInfo') }}</text>
|
|
|
+ </view>
|
|
|
+ <text class="item-arrow">›</text>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 文件管理 -->
|
|
|
+ <view class="list-item" @click="handleFileManage">
|
|
|
+ <view class="item-left">
|
|
|
+ <text class="item-icon">📁</text>
|
|
|
+ <text class="item-label">{{ t('common.mine.fileManage') }}</text>
|
|
|
+ </view>
|
|
|
+ <text class="item-arrow">›</text>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 审核管理 -->
|
|
|
+ <view class="list-item" @click="handleAuditManage">
|
|
|
+ <view class="item-left">
|
|
|
+ <text class="item-icon">📋</text>
|
|
|
+ <text class="item-label">{{ t('common.mine.auditManage') }}</text>
|
|
|
+ </view>
|
|
|
+ <text class="item-arrow">›</text>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 语言切换 -->
|
|
|
+ <view class="list-item">
|
|
|
+ <view class="item-left">
|
|
|
+ <text class="item-icon">🌐</text>
|
|
|
+ <text class="item-label">{{ t('common.mine.languageSwitch') }}</text>
|
|
|
+ </view>
|
|
|
+ <view class="item-right">
|
|
|
+ <text class="language-text">{{ currentLanguage }}</text>
|
|
|
+ <switch
|
|
|
+ :checked="isEnglish"
|
|
|
+ @change="handleLanguageChange"
|
|
|
+ color="#007aff"
|
|
|
+ />
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 协议说明 -->
|
|
|
+ <view class="list-item" @click="handleProtocol">
|
|
|
+ <view class="item-left">
|
|
|
+ <text class="item-icon">📄</text>
|
|
|
+ <text class="item-label">{{ t('common.mine.protocol') }}</text>
|
|
|
+ </view>
|
|
|
+ <text class="item-arrow">›</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 退出登录按钮 -->
|
|
|
+ <view class="logout-section">
|
|
|
+ <button class="logout-btn" @click="handleLogout">{{ t('common.mine.logout') }}</button>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+import { ref, computed, onMounted } from 'vue'
|
|
|
+import { useI18n } from 'vue-i18n'
|
|
|
+import { useUserStore } from '@/store/index'
|
|
|
+import { useLocaleStore } from '@/store/locale'
|
|
|
+import { getUserInfo as getUserInfoAPI } from '@/apis/auth'
|
|
|
+
|
|
|
+const { t, locale } = useI18n()
|
|
|
+const userStore = useUserStore()
|
|
|
+const localeStore = useLocaleStore()
|
|
|
+
|
|
|
+// 状态栏高度
|
|
|
+const statusBarHeight = ref(0)
|
|
|
+
|
|
|
+// 用户信息
|
|
|
+const userInfo = ref({
|
|
|
+ avatar: '/static/default-avatar.svg',
|
|
|
+ nickname: ''
|
|
|
+})
|
|
|
+
|
|
|
+// 加载状态
|
|
|
+const loading = ref(false)
|
|
|
+
|
|
|
+// 当前语言显示
|
|
|
+const currentLanguage = computed(() => {
|
|
|
+ return locale.value === 'zh-CN' ? t('common.language.zh') : t('common.language.en')
|
|
|
+})
|
|
|
+
|
|
|
+// 显示的昵称(带加载状态)
|
|
|
+const displayNickname = computed(() => {
|
|
|
+ if (loading.value) {
|
|
|
+ return t('common.mine.loading')
|
|
|
+ }
|
|
|
+ return userInfo.value.nickname || t('common.mine.defaultNickname')
|
|
|
+})
|
|
|
+
|
|
|
+// 是否英文
|
|
|
+const isEnglish = computed(() => {
|
|
|
+ return locale.value === 'en-US'
|
|
|
+})
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ // 获取系统信息
|
|
|
+ const systemInfo = uni.getSystemInfoSync()
|
|
|
+ statusBarHeight.value = systemInfo.statusBarHeight || 0
|
|
|
+
|
|
|
+ // 获取用户信息
|
|
|
+ fetchUserInfo()
|
|
|
+
|
|
|
+ console.log('我的内容组件已加载')
|
|
|
+})
|
|
|
+
|
|
|
+// 头像加载失败处理
|
|
|
+const handleAvatarError = () => {
|
|
|
+ console.log('头像加载失败,使用默认头像')
|
|
|
+ userInfo.value.avatar = '/static/default-avatar.svg'
|
|
|
+}
|
|
|
+
|
|
|
+// 获取用户信息
|
|
|
+const fetchUserInfo = async () => {
|
|
|
+ try {
|
|
|
+ loading.value = true
|
|
|
+
|
|
|
+ // 调用 API 获取用户信息
|
|
|
+ const response = await getUserInfoAPI()
|
|
|
+
|
|
|
+ if (response && response.data) {
|
|
|
+ // 更新用户信息
|
|
|
+ userInfo.value = {
|
|
|
+ avatar: response.data.avatar || '/static/default-avatar.svg',
|
|
|
+ nickname: response.data.nickname || ''
|
|
|
+ }
|
|
|
+
|
|
|
+ // 同步更新 store
|
|
|
+ userStore.setUserInfo(response.data)
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('获取用户信息失败:', error)
|
|
|
+
|
|
|
+ // 如果API失败,尝试从本地store获取
|
|
|
+ const storedUserInfo = userStore.userInfo
|
|
|
+ if (storedUserInfo && storedUserInfo.nickname) {
|
|
|
+ userInfo.value = {
|
|
|
+ avatar: storedUserInfo.avatar || '/static/default-avatar.svg',
|
|
|
+ nickname: storedUserInfo.nickname
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ userInfo.value = {
|
|
|
+ avatar: '/static/default-avatar.svg',
|
|
|
+ nickname: ''
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ uni.showToast({
|
|
|
+ title: t('common.mine.getUserInfoFailed'),
|
|
|
+ icon: 'none',
|
|
|
+ duration: 2000
|
|
|
+ })
|
|
|
+ } finally {
|
|
|
+ loading.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 基本信息
|
|
|
+const handleBasicInfo = () => {
|
|
|
+ uni.showToast({
|
|
|
+ title: '基本信息',
|
|
|
+ icon: 'none'
|
|
|
+ })
|
|
|
+ // TODO: 跳转到基本信息页面
|
|
|
+}
|
|
|
+
|
|
|
+// 文件管理
|
|
|
+const handleFileManage = () => {
|
|
|
+ uni.showToast({
|
|
|
+ title: '文件管理',
|
|
|
+ icon: 'none'
|
|
|
+ })
|
|
|
+ // TODO: 跳转到文件管理页面
|
|
|
+}
|
|
|
+
|
|
|
+// 审核管理
|
|
|
+const handleAuditManage = () => {
|
|
|
+ uni.showToast({
|
|
|
+ title: '审核管理',
|
|
|
+ icon: 'none'
|
|
|
+ })
|
|
|
+ // TODO: 跳转到审核管理页面
|
|
|
+}
|
|
|
+
|
|
|
+// 语言切换
|
|
|
+const handleLanguageChange = (e) => {
|
|
|
+ const isChecked = e.detail.value
|
|
|
+ const newLocale = isChecked ? 'en-US' : 'zh-CN'
|
|
|
+
|
|
|
+ const success = localeStore.setLocale(newLocale)
|
|
|
+
|
|
|
+ if (success) {
|
|
|
+ // 延迟一下让语言切换生效后再显示提示
|
|
|
+ setTimeout(() => {
|
|
|
+ uni.showToast({
|
|
|
+ title: t('common.language.switchSuccess'),
|
|
|
+ icon: 'success',
|
|
|
+ duration: 1500
|
|
|
+ })
|
|
|
+ }, 100)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 协议说明
|
|
|
+const handleProtocol = () => {
|
|
|
+ uni.showToast({
|
|
|
+ title: '协议说明',
|
|
|
+ icon: 'none'
|
|
|
+ })
|
|
|
+ // TODO: 跳转到协议说明页面
|
|
|
+}
|
|
|
+
|
|
|
+// 退出登录
|
|
|
+const handleLogout = () => {
|
|
|
+ uni.showModal({
|
|
|
+ title: t('common.button.confirm'),
|
|
|
+ content: t('common.mine.logoutConfirm'),
|
|
|
+ confirmText: t('common.button.confirm'),
|
|
|
+ cancelText: t('common.button.cancel'),
|
|
|
+ success: (res) => {
|
|
|
+ if (res.confirm) {
|
|
|
+ // 清除本地token和用户信息缓存
|
|
|
+ userStore.logout()
|
|
|
+
|
|
|
+ // 显示退出成功提示
|
|
|
+ uni.showToast({
|
|
|
+ title: t('common.mine.logoutSuccess'),
|
|
|
+ icon: 'success',
|
|
|
+ duration: 1500
|
|
|
+ })
|
|
|
+
|
|
|
+ // 延迟跳转到登录页
|
|
|
+ setTimeout(() => {
|
|
|
+ uni.reLaunch({
|
|
|
+ url: '/pages/login/login'
|
|
|
+ })
|
|
|
+ }, 1500)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+.my-page {
|
|
|
+ width: 100%;
|
|
|
+ min-height: 100vh;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ background: linear-gradient(180deg, #f0f4ff 0%, #f8f9fa 100%);
|
|
|
+
|
|
|
+ // 自定义头部
|
|
|
+ .custom-header {
|
|
|
+ position: fixed;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ background-color: #ffffff;
|
|
|
+ border-bottom: 1rpx solid #e5e5e5;
|
|
|
+ z-index: 100;
|
|
|
+
|
|
|
+ .header-content {
|
|
|
+ height: 88rpx;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+
|
|
|
+ .header-title {
|
|
|
+ font-size: 32rpx;
|
|
|
+ font-weight: 500;
|
|
|
+ color: #000000;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 页面内容
|
|
|
+ .page-body {
|
|
|
+ flex: 1;
|
|
|
+ margin-top: 88rpx;
|
|
|
+ padding: 40rpx;
|
|
|
+
|
|
|
+ // 用户名片区域
|
|
|
+ .user-card {
|
|
|
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
|
+ border-radius: 24rpx;
|
|
|
+ padding: 80rpx 40rpx 60rpx;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ margin-bottom: 40rpx;
|
|
|
+ box-shadow: 0 8rpx 24rpx rgba(102, 126, 234, 0.3);
|
|
|
+ position: relative;
|
|
|
+ overflow: hidden;
|
|
|
+
|
|
|
+ // 背景装饰
|
|
|
+ &::before {
|
|
|
+ content: '';
|
|
|
+ position: absolute;
|
|
|
+ top: -50%;
|
|
|
+ right: -20%;
|
|
|
+ width: 400rpx;
|
|
|
+ height: 400rpx;
|
|
|
+ background: rgba(255, 255, 255, 0.1);
|
|
|
+ border-radius: 50%;
|
|
|
+ }
|
|
|
+
|
|
|
+ &::after {
|
|
|
+ content: '';
|
|
|
+ position: absolute;
|
|
|
+ bottom: -30%;
|
|
|
+ left: -10%;
|
|
|
+ width: 300rpx;
|
|
|
+ height: 300rpx;
|
|
|
+ background: rgba(255, 255, 255, 0.08);
|
|
|
+ border-radius: 50%;
|
|
|
+ }
|
|
|
+
|
|
|
+ .avatar {
|
|
|
+ width: 140rpx;
|
|
|
+ height: 140rpx;
|
|
|
+ border-radius: 70rpx;
|
|
|
+ margin-bottom: 24rpx;
|
|
|
+ border: 6rpx solid rgba(255, 255, 255, 0.3);
|
|
|
+ box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.2);
|
|
|
+ position: relative;
|
|
|
+ z-index: 1;
|
|
|
+ transition: opacity 0.3s;
|
|
|
+
|
|
|
+ &.loading {
|
|
|
+ opacity: 0.5;
|
|
|
+ animation: pulse 1.5s ease-in-out infinite;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .nickname {
|
|
|
+ font-size: 40rpx;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #ffffff;
|
|
|
+ text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.2);
|
|
|
+ position: relative;
|
|
|
+ z-index: 1;
|
|
|
+ transition: opacity 0.3s;
|
|
|
+
|
|
|
+ &.loading {
|
|
|
+ opacity: 0.7;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 功能列表
|
|
|
+ .function-list {
|
|
|
+ background-color: #ffffff;
|
|
|
+ border-radius: 20rpx;
|
|
|
+ overflow: hidden;
|
|
|
+ margin-bottom: 40rpx;
|
|
|
+ box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08);
|
|
|
+
|
|
|
+ .list-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ padding: 36rpx 40rpx;
|
|
|
+ border-bottom: 1rpx solid #f0f0f0;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+ position: relative;
|
|
|
+
|
|
|
+ &:last-child {
|
|
|
+ border-bottom: none;
|
|
|
+ }
|
|
|
+
|
|
|
+ &:active {
|
|
|
+ background-color: #f8f9ff;
|
|
|
+ transform: scale(0.98);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 左侧渐变条
|
|
|
+ &::before {
|
|
|
+ content: '';
|
|
|
+ position: absolute;
|
|
|
+ left: 0;
|
|
|
+ top: 50%;
|
|
|
+ transform: translateY(-50%);
|
|
|
+ width: 6rpx;
|
|
|
+ height: 60%;
|
|
|
+ background: linear-gradient(180deg, #667eea 0%, #764ba2 100%);
|
|
|
+ border-radius: 0 6rpx 6rpx 0;
|
|
|
+ opacity: 0;
|
|
|
+ transition: opacity 0.3s;
|
|
|
+ }
|
|
|
+
|
|
|
+ &:active::before {
|
|
|
+ opacity: 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ .item-left {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+
|
|
|
+ .item-icon {
|
|
|
+ font-size: 44rpx;
|
|
|
+ margin-right: 28rpx;
|
|
|
+ filter: drop-shadow(0 2rpx 4rpx rgba(0, 0, 0, 0.1));
|
|
|
+ }
|
|
|
+
|
|
|
+ .item-label {
|
|
|
+ font-size: 30rpx;
|
|
|
+ color: #333333;
|
|
|
+ font-weight: 500;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .item-right {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 20rpx;
|
|
|
+
|
|
|
+ .language-text {
|
|
|
+ font-size: 26rpx;
|
|
|
+ color: #667eea;
|
|
|
+ font-weight: 500;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .item-arrow {
|
|
|
+ font-size: 48rpx;
|
|
|
+ color: #d0d0d0;
|
|
|
+ font-weight: 300;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 退出登录
|
|
|
+ .logout-section {
|
|
|
+ padding: 0;
|
|
|
+
|
|
|
+ .logout-btn {
|
|
|
+ width: 100%;
|
|
|
+ height: 96rpx;
|
|
|
+ background: linear-gradient(135deg, #ff6b6b 0%, #ee5a6f 100%);
|
|
|
+ border-radius: 20rpx;
|
|
|
+ border: none;
|
|
|
+ font-size: 32rpx;
|
|
|
+ color: #ffffff;
|
|
|
+ font-weight: 600;
|
|
|
+ box-shadow: 0 6rpx 20rpx rgba(255, 107, 107, 0.3);
|
|
|
+ letter-spacing: 2rpx;
|
|
|
+
|
|
|
+ &:active {
|
|
|
+ opacity: 0.9;
|
|
|
+ transform: scale(0.98);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 加载动画
|
|
|
+@keyframes pulse {
|
|
|
+ 0%, 100% {
|
|
|
+ opacity: 0.5;
|
|
|
+ }
|
|
|
+ 50% {
|
|
|
+ opacity: 0.8;
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|