404 lines
8.9 KiB
Vue
404 lines
8.9 KiB
Vue
<script setup>
|
|
import { ref, computed } from "vue";
|
|
import { useUserStore } from "@/store/user";
|
|
import { resolveImageUrl } from "@/utils/image";
|
|
import { getMyStats } from "@/api/user";
|
|
import { getUnreadCount } from "@/api/notification";
|
|
|
|
const userStore = useUserStore();
|
|
const isLoggedIn = computed(() => userStore.isLoggedIn);
|
|
const user = computed(() => userStore.userInfo);
|
|
const stats = ref({ spot_count: 0, approved_count: 0, favorite_count: 0, rating_received: 0 });
|
|
const unreadCount = ref(0);
|
|
|
|
const goLogin = () => {
|
|
uni.navigateTo({ url: "/pages/login/index" });
|
|
};
|
|
|
|
const handleLogout = () => {
|
|
uni.showModal({
|
|
title: "提示",
|
|
content: "确定要退出登录吗?",
|
|
success: (res) => {
|
|
if (res.confirm) {
|
|
userStore.logout();
|
|
}
|
|
},
|
|
});
|
|
};
|
|
|
|
const menuItems = [
|
|
{ label: "编辑资料", icon: "gear-filled", action: "profile" },
|
|
{ label: "我的收藏", icon: "heart-filled", action: "favorites" },
|
|
{ label: "我的投稿", icon: "location-filled", action: "my-spots" },
|
|
{ label: "约拍/活动", icon: "calendar-filled", action: "activity-hub" },
|
|
{ label: "会员中心", icon: "vip-filled", action: "membership" },
|
|
{ label: "积分概览", icon: "star-filled", action: "points" },
|
|
{ label: "消息通知", icon: "chat-filled", action: "notifications" },
|
|
{ label: "设置", icon: "gear", action: "settings" },
|
|
];
|
|
|
|
const handleMenu = (action) => {
|
|
switch (action) {
|
|
case "profile":
|
|
uni.navigateTo({ url: "/pages/mine/profile" });
|
|
break;
|
|
case "favorites":
|
|
uni.navigateTo({ url: "/pages/mine/favorites" });
|
|
break;
|
|
case "my-spots":
|
|
uni.navigateTo({ url: "/pages/mine/my-spots" });
|
|
break;
|
|
case "points":
|
|
uni.navigateTo({ url: "/pages/mine/points" });
|
|
break;
|
|
case "activity-hub":
|
|
uni.switchTab({ url: "/pages/activity/index" });
|
|
break;
|
|
case "membership":
|
|
uni.navigateTo({ url: "/pages/mine/membership" });
|
|
break;
|
|
case "notifications":
|
|
uni.switchTab({ url: "/pages/mine/notifications" });
|
|
break;
|
|
case "settings":
|
|
uni.navigateTo({ url: "/pages/mine/settings" });
|
|
break;
|
|
}
|
|
};
|
|
|
|
import { onShow } from "@dcloudio/uni-app";
|
|
|
|
onShow(async () => {
|
|
if (isLoggedIn.value) {
|
|
userStore.fetchUserInfo();
|
|
try {
|
|
const s = await getMyStats();
|
|
stats.value = s;
|
|
} catch (e) { /* ignore */ }
|
|
try {
|
|
const r = await getUnreadCount();
|
|
unreadCount.value = r.count || 0;
|
|
} catch (e) { /* ignore */ }
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<view class="mine-page">
|
|
<view v-if="isLoggedIn" class="user-card">
|
|
<view class="avatar-area">
|
|
<image
|
|
v-if="user?.avatar_url"
|
|
class="avatar"
|
|
:src="resolveImageUrl(user.avatar_url)"
|
|
mode="aspectFill"
|
|
/>
|
|
<view v-else class="avatar avatar-default">
|
|
<uni-icons type="person" size="28" color="#ffffff" class="avatar-icon" />
|
|
</view>
|
|
</view>
|
|
<view class="user-info">
|
|
<text class="nickname">{{ user?.nickname || "加载中..." }}</text>
|
|
<text v-if="user?.bio" class="user-bio">{{ user.bio }}</text>
|
|
<view class="user-meta">
|
|
<view v-if="user?.city" class="meta-item"><uni-icons type="location" size="14" color="rgba(255,255,255,0.8)" /> {{ user.city }}</view>
|
|
<view v-if="user?.identity" class="identity-badge">
|
|
<text class="identity-text">{{ user.identity }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<view v-else class="login-card">
|
|
<view class="login-avatar">
|
|
<uni-icons type="person" size="28" color="#6366f1" class="login-avatar-icon" />
|
|
</view>
|
|
<text class="login-hint">请先登录</text>
|
|
<button class="login-btn" @tap="goLogin">立即登录</button>
|
|
</view>
|
|
|
|
<view v-if="isLoggedIn" class="stats-card">
|
|
<view class="stat-item" @tap="handleMenu('my-spots')">
|
|
<text class="stat-num">{{ stats.spot_count }}</text>
|
|
<text class="stat-label">投稿</text>
|
|
</view>
|
|
<view class="stat-item" @tap="handleMenu('favorites')">
|
|
<text class="stat-num">{{ stats.favorite_count }}</text>
|
|
<text class="stat-label">收藏</text>
|
|
</view>
|
|
<view class="stat-item">
|
|
<text class="stat-num">{{ stats.rating_received }}</text>
|
|
<text class="stat-label">获赞</text>
|
|
</view>
|
|
<view class="stat-item">
|
|
<text class="stat-num">{{ stats.approved_count }}</text>
|
|
<text class="stat-label">通过</text>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="menu-card">
|
|
<view
|
|
v-for="(item, idx) in menuItems"
|
|
:key="idx"
|
|
class="menu-item"
|
|
@tap="handleMenu(item.action)"
|
|
>
|
|
<view class="menu-left">
|
|
<uni-icons :type="item.icon" size="22" color="#6366f1" class="menu-icon" />
|
|
<text class="menu-label">{{ item.label }}</text>
|
|
</view>
|
|
<view class="menu-right">
|
|
<view v-if="item.action === 'notifications' && unreadCount > 0" class="badge">
|
|
<text class="badge-text">{{ unreadCount > 99 ? '99+' : unreadCount }}</text>
|
|
</view>
|
|
<uni-icons type="right" size="16" color="#cbd5e1" class="menu-arrow" />
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<view v-if="isLoggedIn" class="logout-area">
|
|
<button class="logout-btn" @tap="handleLogout">退出登录</button>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.mine-page {
|
|
min-height: 100vh;
|
|
background: #f5f6fa;
|
|
padding: 0 32rpx;
|
|
padding-top: 32rpx;
|
|
}
|
|
|
|
.user-card {
|
|
background: linear-gradient(135deg, #6366f1, #818cf8);
|
|
border-radius: 24rpx;
|
|
padding: 40rpx 32rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
margin-bottom: 32rpx;
|
|
}
|
|
|
|
.avatar-area {
|
|
margin-right: 24rpx;
|
|
}
|
|
|
|
.avatar {
|
|
width: 120rpx;
|
|
height: 120rpx;
|
|
border-radius: 60rpx;
|
|
border: 4rpx solid rgba(255, 255, 255, 0.3);
|
|
}
|
|
|
|
.avatar-default {
|
|
background: rgba(255, 255, 255, 0.25);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.avatar-icon {
|
|
font-size: 56rpx;
|
|
}
|
|
|
|
.user-info {
|
|
flex: 1;
|
|
}
|
|
|
|
.nickname {
|
|
font-size: 36rpx;
|
|
font-weight: 700;
|
|
color: #ffffff;
|
|
display: block;
|
|
margin-bottom: 8rpx;
|
|
}
|
|
|
|
.user-bio {
|
|
font-size: 24rpx;
|
|
color: rgba(255, 255, 255, 0.75);
|
|
display: block;
|
|
margin-bottom: 8rpx;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
max-width: 400rpx;
|
|
}
|
|
|
|
.user-meta {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 16rpx;
|
|
}
|
|
|
|
.meta-item {
|
|
font-size: 24rpx;
|
|
color: rgba(255, 255, 255, 0.8);
|
|
}
|
|
|
|
.identity-badge {
|
|
background: rgba(255, 255, 255, 0.2);
|
|
padding: 4rpx 16rpx;
|
|
border-radius: 8rpx;
|
|
}
|
|
|
|
.identity-text {
|
|
font-size: 22rpx;
|
|
color: #ffffff;
|
|
}
|
|
|
|
.login-card {
|
|
background: #ffffff;
|
|
border-radius: 24rpx;
|
|
padding: 60rpx 40rpx;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
margin-bottom: 32rpx;
|
|
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
|
|
}
|
|
|
|
.login-avatar {
|
|
width: 120rpx;
|
|
height: 120rpx;
|
|
border-radius: 60rpx;
|
|
background: #e0e7ff;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin-bottom: 20rpx;
|
|
}
|
|
|
|
.login-avatar-icon {
|
|
font-size: 56rpx;
|
|
}
|
|
|
|
.login-hint {
|
|
font-size: 30rpx;
|
|
color: #64748b;
|
|
margin-bottom: 32rpx;
|
|
}
|
|
|
|
.login-btn {
|
|
width: 320rpx;
|
|
height: 80rpx;
|
|
line-height: 80rpx;
|
|
background: #6366f1;
|
|
color: #ffffff;
|
|
font-size: 30rpx;
|
|
font-weight: 600;
|
|
border-radius: 40rpx;
|
|
border: none;
|
|
}
|
|
|
|
.login-btn::after {
|
|
border: none;
|
|
}
|
|
|
|
.stats-card {
|
|
display: flex;
|
|
background: #ffffff;
|
|
border-radius: 16rpx;
|
|
padding: 28rpx 0;
|
|
margin-bottom: 24rpx;
|
|
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
|
|
}
|
|
.stat-item {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 8rpx;
|
|
}
|
|
.stat-num {
|
|
font-size: 36rpx;
|
|
font-weight: 700;
|
|
color: #1e293b;
|
|
}
|
|
.stat-label {
|
|
font-size: 22rpx;
|
|
color: #94a3b8;
|
|
}
|
|
|
|
.menu-right {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8rpx;
|
|
}
|
|
.badge {
|
|
background: #ef4444;
|
|
min-width: 32rpx;
|
|
height: 32rpx;
|
|
border-radius: 16rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 0 8rpx;
|
|
}
|
|
.badge-text {
|
|
font-size: 20rpx;
|
|
color: #fff;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.menu-card {
|
|
background: #ffffff;
|
|
border-radius: 16rpx;
|
|
overflow: hidden;
|
|
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
|
|
margin-bottom: 32rpx;
|
|
}
|
|
|
|
.menu-item {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 32rpx;
|
|
border-bottom: 1rpx solid #f1f5f9;
|
|
}
|
|
|
|
.menu-item:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.menu-left {
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.menu-icon {
|
|
font-size: 36rpx;
|
|
margin-right: 20rpx;
|
|
}
|
|
|
|
.menu-label {
|
|
font-size: 30rpx;
|
|
color: #1e293b;
|
|
}
|
|
|
|
.menu-arrow {
|
|
font-size: 36rpx;
|
|
color: #cbd5e1;
|
|
}
|
|
|
|
.logout-area {
|
|
padding: 16rpx 0 48rpx;
|
|
}
|
|
|
|
.logout-btn {
|
|
width: 100%;
|
|
height: 84rpx;
|
|
line-height: 84rpx;
|
|
background: #ffffff;
|
|
color: #ef4444;
|
|
font-size: 30rpx;
|
|
border-radius: 16rpx;
|
|
border: none;
|
|
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
|
|
}
|
|
|
|
.logout-btn::after {
|
|
border: none;
|
|
}
|
|
</style>
|