feat: 重构用户中心页面UI

This commit is contained in:
Ray.Hao
2026-03-06 22:46:26 +08:00
parent deef7b1b5b
commit 8628415351
3 changed files with 675 additions and 345 deletions

View File

@@ -1,7 +1,7 @@
<template>
<view class="page-container dark:text-[var(--wot-color-text)]">
<view class="page-container">
<!-- 用户信息卡片 -->
<view class="user-profile" :style="{ paddingTop: navbar.totalHeight.value + 'px' }">
<view class="user-profile" :style="{ paddingTop: navbar.statusBarHeight.value + 'px' }">
<view class="blur-bg" />
<view class="user-info">
@@ -16,6 +16,9 @@
<block v-if="isLogin">
<view class="nickname">
{{ userInfo!.nickname || "匿名用户" }}
<text v-if="genderLabel" class="gender-badge" :class="genderClass">
{{ genderLabel }}
</text>
</view>
<view class="user-id">ID: {{ userInfo?.username || "0000000" }}</view>
</block>
@@ -35,10 +38,13 @@
<!-- 操作按钮区域 - 避开胶囊按钮 -->
<view
class="action-buttons"
:style="{ marginRight: navbar.menuButtonRightGap.value + 8 + 'px' }"
:style="{
right: navbar.menuButtonRightGap.value + 8 + 'px',
top: actionButtonsTop,
}"
>
<view class="action-btn" @click="navigateToSettings">
<wd-icon name="setting1" size="22" color="#fff" />
<wd-icon name="setting" size="22" color="#fff" />
</view>
<view v-if="isLogin" class="action-btn relative" @click="navigateToSection('messages')">
<wd-icon name="notification" size="22" color="#fff" />
@@ -48,135 +54,156 @@
</view>
</view>
<!-- 数据统计 -->
<wd-card custom-class="stats-card">
<wd-grid :column="3" border>
<wd-grid-item @click="navigateToSection('wallet')">
<view class="stats-item">
<view class="mb-8rpx text-36rpx font-600">0.00</view>
<view class="text-26rpx text-gray-500">我的余额</view>
</view>
</wd-grid-item>
<wd-grid-item @click="navigateToSection('favorites')">
<view class="stats-item">
<view class="mb-8rpx text-36rpx font-600">0</view>
<view class="text-26rpx text-gray-500">我的收藏</view>
</view>
</wd-grid-item>
<wd-grid-item @click="navigateToSection('history')">
<view class="stats-item">
<view class="mb-8rpx text-36rpx font-600">0</view>
<view class="text-26rpx text-gray-500">浏览历史</view>
</view>
</wd-grid-item>
</wd-grid>
</wd-card>
<!-- 常用工具 -->
<wd-card>
<template #header>
<view class="flex-start">
<wd-icon name="tools" size="18" :color="currentThemeColor" />
<text class="ml-12rpx text-28rpx font-600">常用工具</text>
<view class="section-card">
<view class="section-header">
<view class="section-title">
<wd-icon name="user" size="18" :color="currentThemeColor" />
<text class="title-text">资料卡片</text>
</view>
</template>
<wd-grid :column="4" border>
<wd-grid-item @click="navigateToProfile">
<view class="flex-col-center py-20rpx">
<view class="tool-icon mb-12rpx">
<wd-icon name="user" size="24" :color="currentThemeColor" />
</view>
<view class="text-24rpx">个人资料</view>
</view>
<view class="profile-grid">
<view class="profile-tile">
<view class="tile-icon" :style="{ backgroundColor: currentThemeColor + '15' }">
<wd-icon name="secured" size="20" :color="currentThemeColor" />
</view>
</wd-grid-item>
<wd-grid-item @click="navigateToFAQ">
<view class="flex-col-center py-20rpx">
<view class="mb-12rpx">
<wd-icon name="help-circle" size="24" :color="currentThemeColor" />
</view>
<view class="text-24rpx">常见问题</view>
<view class="tile-content">
<text class="tile-label">角色</text>
<text class="tile-value">{{ roleText }}</text>
</view>
</wd-grid-item>
<wd-grid-item @click="handleQuestionFeedback">
<view class="flex-col-center py-20rpx">
<view class="mb-12rpx">
<wd-icon name="check-circle" size="24" :color="currentThemeColor" />
</view>
<view class="text-24rpx">问题反馈</view>
</view>
</wd-grid-item>
<wd-grid-item @click="navigateToAbout">
<view class="flex-col-center py-20rpx">
<view class="mb-12rpx">
<wd-icon name="info-circle" size="24" :color="currentThemeColor" />
</view>
<view class="text-24rpx">关于我们</view>
</view>
</wd-grid-item>
</wd-grid>
</wd-card>
<!-- 推荐服务 -->
<wd-card>
<template #header>
<view class="flex-start">
<wd-icon name="star" size="18" :color="currentThemeColor" />
<text class="ml-12rpx text-28rpx font-600">推荐服务</text>
</view>
</template>
<view class="profile-tile">
<view class="tile-icon" :style="{ backgroundColor: currentThemeColor + '15' }">
<wd-icon name="goods" size="20" :color="currentThemeColor" />
</view>
<view class="tile-content">
<text class="tile-label">部门</text>
<text class="tile-value">{{ deptText }}</text>
</view>
</view>
<view class="profile-tile">
<view class="tile-icon" :style="{ backgroundColor: currentThemeColor + '15' }">
<wd-icon name="phone" size="20" :color="currentThemeColor" />
</view>
<view class="tile-content">
<text class="tile-label">手机</text>
<text class="tile-value">{{ mobileText }}</text>
</view>
</view>
<view class="profile-tile">
<view class="tile-icon" :style="{ backgroundColor: currentThemeColor + '15' }">
<wd-icon name="mail" size="20" :color="currentThemeColor" />
</view>
<view class="tile-content">
<text class="tile-label">邮箱</text>
<text class="tile-value">{{ emailText }}</text>
</view>
</view>
</view>
</view>
<wd-cell-group>
<wd-cell
title="会员中心"
label="解锁更多特权"
is-link
custom-class="service-cell"
@click="navigateToSection('services', 'vip')"
>
<template #icon>
<view class="service-icon mr-20rpx">
<wd-icon name="dong" size="22" :color="currentThemeColor" />
</view>
</template>
</wd-cell>
<view class="section-card">
<view class="section-header">
<view class="section-title">
<wd-icon name="setting" size="18" :color="currentThemeColor" />
<text class="title-text">快捷入口</text>
</view>
</view>
<view class="quick-grid">
<view class="quick-item" @click="navigateToProfile">
<view class="quick-icon" :style="{ backgroundColor: currentThemeColor + '15' }">
<wd-icon name="user" size="24" :color="currentThemeColor" />
</view>
<text class="quick-text">个人资料</text>
</view>
<view v-if="isLogin" class="quick-item" @click="navigateToAccount">
<view class="quick-icon" :style="{ backgroundColor: currentThemeColor + '15' }">
<wd-icon name="secured" size="24" :color="currentThemeColor" />
</view>
<text class="quick-text">账号安全</text>
</view>
<view class="quick-item" @click="navigateToTheme">
<view class="quick-icon" :style="{ backgroundColor: currentThemeColor + '15' }">
<wd-icon name="color" size="24" :color="currentThemeColor" />
</view>
<text class="quick-text">主题设置</text>
</view>
<view class="quick-item" @click="navigateToSettings">
<view class="quick-icon" :style="{ backgroundColor: currentThemeColor + '15' }">
<wd-icon name="setting" size="24" :color="currentThemeColor" />
</view>
<text class="quick-text">更多设置</text>
</view>
</view>
</view>
<wd-cell
title="优惠券"
label="查看我的优惠券"
is-link
custom-class="service-cell"
@click="navigateToSection('services', 'coupon')"
>
<template #icon>
<view class="service-icon mr-20rpx">
<wd-icon name="discount" size="22" :color="currentThemeColor" />
</view>
</template>
</wd-cell>
<view class="section-card">
<view class="section-header">
<view class="section-title">
<wd-icon name="setting" size="18" :color="currentThemeColor" />
<text class="title-text">系统设置</text>
</view>
</view>
<view class="menu-list">
<view class="menu-item" @click="navigateToUserAgreement">
<wd-icon name="note" size="20" color="#666" />
<text class="menu-text">用户协议</text>
<wd-icon name="arrow-right" size="16" color="#ccc" />
</view>
<view class="menu-item" @click="navigateToAbout">
<wd-icon name="info-circle" size="20" color="#666" />
<text class="menu-text">关于我们</text>
<wd-icon name="arrow-right" size="16" color="#ccc" />
</view>
<view class="menu-item" @click="navigateToNetworkTest">
<wd-icon name="wifi" size="20" color="#666" />
<text class="menu-text">网络测试</text>
<wd-icon name="arrow-right" size="16" color="#ccc" />
</view>
<view class="menu-item" @click="handleClearCache">
<wd-icon name="delete" size="20" color="#666" />
<text class="menu-text">清空缓存</text>
<text class="menu-value">{{ cacheSize }}</text>
</view>
</view>
</view>
<wd-cell
title="邀请有礼"
label="邀请好友得奖励"
is-link
custom-class="service-cell"
@click="navigateToSection('services', 'invite')"
>
<template #icon>
<view class="service-icon mr-20rpx">
<wd-icon name="share" size="22" :color="currentThemeColor" />
</view>
</template>
</wd-cell>
</wd-cell-group>
</wd-card>
<view class="section-card">
<view class="section-header">
<view class="section-title">
<wd-icon name="help-circle" size="18" :color="currentThemeColor" />
<text class="title-text">帮助与支持</text>
</view>
</view>
<view class="menu-list">
<view class="menu-item" @click="navigateToFAQ">
<wd-icon name="question-circle" size="20" color="#666" />
<text class="menu-text">常见问题</text>
<wd-icon name="arrow-right" size="16" color="#ccc" />
</view>
<view class="menu-item" @click="handleQuestionFeedback">
<wd-icon name="edit" size="20" color="#666" />
<text class="menu-text">问题反馈</text>
<wd-icon name="arrow-right" size="16" color="#ccc" />
</view>
</view>
</view>
<!-- 退出登录按钮 -->
<view v-if="isLogin" class="flex-center mt-20rpx">
<wd-button custom-class="logout-button" plain @click="handleLogout">退出登录</wd-button>
<view v-if="isLogin" class="logout-section">
<view class="logout-btn" @click="handleLogout">
<text class="logout-text">退出登录</text>
</view>
</view>
<wd-toast />
<wd-loading
v-if="clearing"
v-model="clearing"
text="正在清理..."
mask
custom-class="loading-center"
/>
</view>
</template>
@@ -189,6 +216,7 @@
</route>
<script lang="ts" setup>
import { onLoad } from "@dcloudio/uni-app";
import { useToast } from "wot-design-uni";
import { useUserStore, useThemeStore } from "@/store";
import { useRouter } from "uni-mini-router";
@@ -207,6 +235,74 @@ const router = useRouter();
// 使用导航栏 HookTabBar 页面)
const navbar = useNavbar({ hasTabbar: true });
const navContentHeight = computed(() => {
const height = navbar.totalHeight.value - navbar.statusBarHeight.value;
return `${height > 0 ? height : 0}px`;
});
const actionButtonsTop = computed(() => {
const navHeight = navbar.totalHeight.value - navbar.statusBarHeight.value;
return `${navbar.statusBarHeight.value + navHeight / 2}px`;
});
const roleText = computed(() => {
if (!userInfo.value) return "-";
return userInfo.value.roleName || userInfo.value.roles?.join("、") || "-";
});
const deptText = computed(() => userInfo.value?.deptName || "-");
const mobileText = computed(() => userInfo.value?.mobile || "-");
const emailText = computed(() => userInfo.value?.email || "-");
const genderValue = computed(() => (userInfo.value as any)?.gender);
const genderLabel = computed(() => {
if (genderValue.value === 1) return "♂";
if (genderValue.value === 2) return "♀";
return "";
});
const genderClass = computed(() => {
if (genderValue.value === 1) return "gender-badge--male";
if (genderValue.value === 2) return "gender-badge--female";
return "";
});
const clearing = ref(false);
const cacheSize = ref<any>("-");
const formatSize = (size: number) => {
if (size < 1024) {
return size + "B";
} else if (size < 1024 * 1024) {
return (size / 1024).toFixed(2) + "KB";
} else {
return (size / 1024 / 1024).toFixed(2) + "MB";
}
};
const getCacheSize = async () => {
try {
// #ifdef MP-WEIXIN
const res = await uni.getStorageInfo();
cacheSize.value = formatSize(res.currentSize);
// #endif
// #ifdef H5
cacheSize.value = formatSize(
Object.keys(localStorage).reduce((size, key) => size + localStorage[key].length, 0)
);
// #endif
if (!cacheSize.value) {
cacheSize.value = "0B";
}
} catch (error) {
console.error("获取缓存大小失败:", error);
cacheSize.value = "-";
}
};
onLoad(() => {
getCacheSize();
});
// 登录
const navigateToLoginPage = () => {
const pages = getCurrentPages();
@@ -253,11 +349,47 @@ const navigateToSettings = () => {
router.push({ path: "/pages/mine/settings/index" });
};
const navigateToAccount = () => {
if (!isLogin.value) {
navigateToLoginPage();
return;
}
router.push({ path: "/pages/mine/settings/account/index" });
};
const navigateToTheme = () => {
router.push({ path: "/pages/mine/settings/theme/index" });
};
const navigateToUserAgreement = () => {
router.push({ path: "/pages/mine/settings/agreement/index" });
};
const navigateToNetworkTest = () => {
router.push({ path: "/pages/mine/settings/network/index" });
};
// 问题反馈
const handleQuestionFeedback = () => {
router.push({ path: "/pages/mine/feedback/index" });
};
const handleClearCache = async () => {
if (clearing.value) return;
try {
clearing.value = true;
await new Promise((resolve) => setTimeout(resolve, 800));
await uni.clearStorage();
await getCacheSize();
toast.show("清理成功");
} catch {
toast.show("清理失败");
} finally {
clearing.value = false;
}
};
// 导航到各个板块
const navigateToSection = (section: string, subSection?: string) => {
console.log(`导航到: ${section}${subSection ? ` - ${subSection}` : ""}`);
@@ -270,6 +402,11 @@ const navigateToSection = (section: string, subSection?: string) => {
</script>
<style lang="scss" scoped>
.page-container {
min-height: 100vh;
background: #f5f7fa;
}
// 用户信息卡片
.user-profile {
position: relative;
@@ -290,34 +427,60 @@ const navigateToSection = (section: string, subSection?: string) => {
z-index: 1;
display: flex;
align-items: center;
padding: 30rpx;
min-height: v-bind(navContentHeight);
padding: 32rpx 32rpx 40rpx;
padding-right: 200rpx;
.avatar-container {
position: relative;
.avatar {
width: 120rpx;
height: 120rpx;
border: 4rpx solid rgba(255, 255, 255, 0.8);
width: 128rpx;
height: 128rpx;
border: 4rpx solid rgba(255, 255, 255, 0.9);
border-radius: 50%;
box-shadow: 0 6rpx 16rpx rgba(0, 0, 0, 0.1);
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.15);
}
}
.user-details {
flex: 1;
margin-left: 24rpx;
margin-left: 28rpx;
.nickname {
margin-bottom: 8rpx;
font-size: 34rpx;
font-weight: bold;
margin-bottom: 10rpx;
font-size: 36rpx;
font-weight: 700;
color: #fff;
.gender-badge {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 36rpx;
height: 36rpx;
padding: 0 12rpx;
margin-left: 12rpx;
font-size: 22rpx;
font-weight: 700;
line-height: 36rpx;
color: #fff;
background-color: rgba(255, 255, 255, 0.25);
border-radius: 999px;
}
.gender-badge--male {
background-color: rgba(64, 158, 255, 0.5);
}
.gender-badge--female {
background-color: rgba(255, 99, 132, 0.5);
}
}
.user-id {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.8);
color: rgba(255, 255, 255, 0.85);
}
.login-prompt {
@@ -329,18 +492,21 @@ const navigateToSection = (section: string, subSection?: string) => {
// 操作按钮区域
.action-buttons {
position: absolute;
right: 0;
display: flex;
gap: 16rpx;
margin-left: auto;
transform: translateY(-50%);
}
.action-btn {
display: flex;
align-items: center;
justify-content: center;
width: 70rpx;
height: 70rpx;
background-color: rgba(255, 255, 255, 0.25);
width: 72rpx;
height: 72rpx;
background-color: rgba(255, 255, 255, 0.28);
backdrop-filter: blur(8px);
border-radius: 50%;
}
@@ -363,37 +529,198 @@ const navigateToSection = (section: string, subSection?: string) => {
}
}
:deep(.stats-card) {
margin: 20rpx 30rpx !important;
// 通用卡片样式
.section-card {
padding: 28rpx;
margin: 24rpx 28rpx;
background: #fff;
border-radius: 20rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
}
// 服务图标
.service-icon {
.section-header {
margin-bottom: 24rpx;
}
.section-title {
display: flex;
gap: 12rpx;
align-items: center;
}
.title-text {
font-size: 30rpx;
font-weight: 600;
color: #1f2937;
}
// 资料卡片网格
.profile-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20rpx;
}
.profile-tile {
display: flex;
align-items: center;
padding: 20rpx;
background: #f9fafb;
border-radius: 14rpx;
transition: background 0.2s;
&:active {
background: #f3f4f6;
}
}
.tile-icon {
display: flex;
flex-shrink: 0;
align-items: center;
justify-content: center;
width: 48rpx;
height: 48rpx;
margin-right: 16rpx;
border-radius: 12rpx;
}
.tile-content {
flex: 1;
overflow: hidden;
}
.tile-label {
display: block;
margin-bottom: 6rpx;
overflow: hidden;
font-size: 22rpx;
color: #9ca3af;
text-overflow: ellipsis;
white-space: nowrap;
}
.tile-value {
display: block;
overflow: hidden;
font-size: 28rpx;
font-weight: 600;
color: #1f2937;
text-overflow: ellipsis;
white-space: nowrap;
}
// 快捷入口网格
.quick-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 16rpx;
}
.quick-item {
display: flex;
flex-direction: column;
align-items: center;
padding: 20rpx 8rpx;
transition: opacity 0.2s;
&:active {
opacity: 0.7;
}
}
.quick-icon {
display: flex;
align-items: center;
justify-content: center;
width: 80rpx;
height: 80rpx;
background-color: var(--wot-color-bg-light);
width: 64rpx;
height: 64rpx;
margin-bottom: 12rpx;
border-radius: 16rpx;
}
.quick-text {
font-size: 24rpx;
color: #374151;
}
// 菜单列表
.menu-list {
display: flex;
flex-direction: column;
gap: 2rpx;
overflow: hidden;
background: #f9fafb;
border-radius: 14rpx;
}
.menu-item {
display: flex;
align-items: center;
padding: 28rpx 24rpx;
background: #fff;
transition: background 0.2s;
&:active {
background: #f9fafb;
}
&:not(:last-child) {
border-bottom: 1rpx solid #f3f4f6;
}
}
.menu-text {
flex: 1;
margin-left: 20rpx;
font-size: 28rpx;
color: #374151;
}
.menu-value {
font-size: 26rpx;
color: #9ca3af;
}
// 退出登录
.logout-section {
padding: 40rpx 28rpx 80rpx;
}
.logout-btn {
display: flex;
align-items: center;
justify-content: center;
height: 88rpx;
background: #fff;
border-radius: 16rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
transition: opacity 0.2s;
&:active {
opacity: 0.8;
}
}
.logout-text {
font-size: 30rpx;
font-weight: 500;
color: #ef4444;
}
// 加载遮罩
:deep(.loading-center) {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: rgba(0, 0, 0, 0.6);
border-radius: 12rpx;
}
// 登录按钮样式
:deep(.btn-login) {
font-size: 24rpx !important;
border-radius: 20rpx !important;
}
// 退出登录按钮样式
:deep(.logout-button) {
width: 80% !important;
height: 80rpx !important;
font-size: 32rpx !important;
font-weight: bold !important;
border-radius: 40rpx !important;
&:active {
opacity: 0.8 !important;
}
}
</style>