xiangyixiangqin/miniapp/pages/mine/index.vue
2026-01-06 19:00:44 +08:00

677 lines
16 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-bg">
<image src="/static/title_bg.png" mode="aspectFill" class="bg-img" />
</view>
<!-- 页面加载状态 -->
<Loading type="page" :loading="pageLoading" />
<!-- 未登录状态 -->
<view class="login-card" v-if="!isLoggedIn && !pageLoading" :style="{ marginTop: (statusBarHeight + 35) + 'px' }">
<text class="login-tip">登陆后帮您更精确准备</text>
<button class="btn-login" @click="handleLogin">立即登录</button>
</view>
<!-- 已登录状态 - 用户信息区域 -->
<view class="user-section" v-else-if="isLoggedIn && !pageLoading" :style="{ marginTop: (statusBarHeight + 35) + 'px' }">
<!-- 用户信息(无背景)- 点击进入个人资料页 -->
<view class="user-info-row" @click="handlePersonalProfile">
<image
class="user-avatar"
:src="userInfo.avatar || defaultAvatar"
mode="aspectFill"
/>
<view class="user-detail">
<view class="user-name-row">
<text class="user-nickname">{{ userInfo.nickname || '用户名' }}</text>
<text class="edit-icon">✎</text>
</view>
<text class="user-xiangqin-no">相亲编号:{{ userInfo.xiangQinNo || '未设置' }}</text>
</view>
</view>
<!-- 资料卡片 -->
<view class="profile-card" v-if="!userInfo.isProfileCompleted">
<text class="tip-text">您还未填写孩子的相亲资料</text>
<button class="btn-fill" @click="handleEditProfile">立即填写</button>
</view>
<view class="profile-card completed" v-else>
<view class="profile-info">
<text class="tip-text">相亲资料已填写</text>
<text class="sub-text">可随时修改完善资料</text>
</view>
<button class="btn-edit" @click="handleEditProfile">修改资料</button>
</view>
</view>
<!-- 互动统计区域 -->
<view class="interact-section">
<view class="interact-grid">
<view class="interact-item" @click="navigateTo('/pages/interact/viewedMe')">
<view class="interact-icon viewed">
<image src="/static/ic_seen.png" mode="aspectFit" class="icon-img" />
</view>
<text class="interact-label">看过我</text>
<view class="interact-badge" v-if="interactCounts.viewedMe > 0">
<text>+{{ interactCounts.viewedMe }}</text>
</view>
</view>
<view class="interact-item" @click="navigateTo('/pages/interact/favoritedMe')">
<view class="interact-icon favorited">
<image src="/static/ic_collection.png" mode="aspectFit" class="icon-img" />
</view>
<text class="interact-label">收藏我</text>
<view class="interact-badge" v-if="interactCounts.favoritedMe > 0">
<text>+{{ interactCounts.favoritedMe }}</text>
</view>
</view>
<view class="interact-item" @click="navigateTo('/pages/interact/unlockedMe')">
<view class="interact-icon unlocked">
<image src="/static/ic_unlock.png" mode="aspectFit" class="icon-img" />
</view>
<text class="interact-label">解锁我</text>
<view class="interact-badge" v-if="interactCounts.unlockedMe > 0">
<text>+{{ interactCounts.unlockedMe }}</text>
</view>
</view>
</view>
</view>
<!-- 会员公告横幅 -->
<view class="vip-banner" @click="handleMember">
<view class="banner-content">
<text class="banner-title">会员权益升级公告</text>
<text class="banner-desc">最新会员体系重磅上线</text>
</view>
<image src="/static/logo.png" mode="aspectFit" class="banner-icon" />
</view>
<!-- 常用功能 -->
<view class="menu-section">
<text class="section-title">常用功能</text>
<view class="menu-list">
<view class="menu-item" @click="handleButler">
<image src="/static/butler.png" mode="aspectFit" class="menu-icon" />
<text class="menu-title">管家指导</text>
<text class="menu-arrow"></text>
</view>
<view class="menu-item" @click="handleRealName">
<image src="/static/real_name.png" mode="aspectFit" class="menu-icon" />
<text class="menu-title">实名认证</text>
<text class="menu-arrow"></text>
</view>
<view class="menu-item" @click="handleUserAgreement">
<image src="/static/ic_agreement1.png" mode="aspectFit" class="menu-icon" />
<text class="menu-title">用户协议</text>
<text class="menu-arrow"></text>
</view>
<view class="menu-item" @click="handlePrivacyPolicy">
<image src="/static/ic_agreement2.png" mode="aspectFit" class="menu-icon" />
<text class="menu-title">隐私协议</text>
<text class="menu-arrow"></text>
</view>
<view class="menu-item" @click="handleLogout" v-if="isLoggedIn">
<image src="/static/ic_exit.png" mode="aspectFit" class="menu-icon" />
<text class="menu-title">退出登录</text>
<text class="menu-arrow"></text>
</view>
</view>
</view>
</view>
</template>
<script>
import { ref, computed, onMounted } from 'vue'
import { useUserStore } from '@/store/user.js'
import { getMyProfile } from '@/api/profile.js'
import { login } from '@/api/auth.js'
import { getViewedMe, getFavoritedMe, getUnlockedMe } from '@/api/interact.js'
import Loading from '@/components/Loading/index.vue'
export default {
name: 'MinePage',
components: {
Loading
},
setup() {
const userStore = useUserStore()
// 页面状态
const pageLoading = ref(true)
const statusBarHeight = ref(20)
// 默认头像
const defaultAvatar = '/static/logo.png'
// 互动统计
const interactCounts = ref({
viewedMe: 0,
favoritedMe: 0,
unlockedMe: 0
})
// 获取系统信息
const getSystemInfo = () => {
uni.getSystemInfo({
success: (res) => {
statusBarHeight.value = res.statusBarHeight || 20
}
})
}
// 计算属性
const isLoggedIn = computed(() => userStore.isLoggedIn)
const userInfo = computed(() => ({
userId: userStore.userId,
nickname: userStore.nickname,
avatar: userStore.avatar,
xiangQinNo: userStore.xiangQinNo,
isProfileCompleted: userStore.isProfileCompleted,
isMember: userStore.isMember,
memberLevel: userStore.memberLevel,
isRealName: userStore.isRealName
}))
// 加载互动统计
const loadInteractCounts = async () => {
if (!userStore.isLoggedIn) return
try {
const [viewedRes, favoritedRes, unlockedRes] = await Promise.all([
getViewedMe(1, 1),
getFavoritedMe(1, 1),
getUnlockedMe(1, 1)
])
interactCounts.value = {
viewedMe: viewedRes?.data?.total || 0,
favoritedMe: favoritedRes?.data?.total || 0,
unlockedMe: unlockedRes?.data?.total || 0
}
} catch (error) {
console.error('加载互动统计失败:', error)
}
}
// 初始化页面
const initPage = async () => {
pageLoading.value = true
try {
userStore.restoreFromStorage()
if (userStore.isLoggedIn) {
await loadInteractCounts()
}
} catch (error) {
console.error('初始化页面失败:', error)
} finally {
pageLoading.value = false
}
}
// 微信登录 - 跳转到登录页
const handleLogin = () => {
uni.navigateTo({ url: '/pages/login/index' })
}
// 个人资料页
const handlePersonalProfile = () => {
uni.navigateTo({ url: '/pages/profile/personal' })
}
// 预览资料
const handlePreviewProfile = () => {
if (!userStore.isProfileCompleted) {
handleEditProfile()
return
}
uni.navigateTo({ url: '/pages/profile/preview' })
}
// 编辑资料
const handleEditProfile = () => {
uni.navigateTo({ url: '/pages/profile/edit' })
}
// 会员中心
const handleMember = () => {
uni.navigateTo({ url: '/pages/member/index' })
}
// 管家指导
const handleButler = () => {
uni.showToast({ title: '功能开发中', icon: 'none' })
}
// 实名认证
const handleRealName = () => {
uni.navigateTo({ url: '/pages/realname/index' })
}
// 用户协议
const handleUserAgreement = () => {
uni.navigateTo({ url: '/pages/agreement/index?type=user' })
}
// 隐私协议
const handlePrivacyPolicy = () => {
uni.navigateTo({ url: '/pages/agreement/index?type=privacy' })
}
// 退出登录
const handleLogout = () => {
uni.showModal({
title: '提示',
content: '确定要退出登录吗?',
success: (res) => {
if (res.confirm) {
userStore.logout()
interactCounts.value = { viewedMe: 0, favoritedMe: 0, unlockedMe: 0 }
uni.showToast({ title: '已退出登录', icon: 'success' })
// 清除所有页面,返回首页
setTimeout(() => {
uni.reLaunch({ url: '/pages/index/index' })
}, 1000)
}
}
})
}
// 导航
const navigateTo = (url) => {
uni.navigateTo({ url })
}
onMounted(() => {
getSystemInfo()
initPage()
})
return {
pageLoading,
statusBarHeight,
isLoggedIn,
userInfo,
interactCounts,
defaultAvatar,
handleLogin,
handlePersonalProfile,
handlePreviewProfile,
handleEditProfile,
handleMember,
handleButler,
handleRealName,
handleUserAgreement,
handlePrivacyPolicy,
handleLogout,
navigateTo,
loadInteractCounts
}
},
onShow() {
const userStore = useUserStore()
userStore.restoreFromStorage()
if (userStore.isLoggedIn && this.loadInteractCounts) {
this.loadInteractCounts()
}
}
}
</script>
<style lang="scss" scoped>
.mine-page {
min-height: 100vh;
background-color: #f5f6fa;
padding-bottom: 120rpx;
}
// 顶部背景图
.top-bg {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 400rpx;
z-index: 0;
.bg-img {
width: 100%;
height: 100%;
}
}
// 未登录卡片
.login-card {
position: relative;
z-index: 1;
margin: 0 32rpx;
padding: 48rpx 40rpx;
background: #fff;
border-radius: 24rpx;
text-align: center;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
.login-tip {
display: block;
font-size: 32rpx;
color: #333;
margin-bottom: 32rpx;
}
.btn-login {
width: 280rpx;
height: 80rpx;
line-height: 80rpx;
background: linear-gradient(135deg, #ffb5b5 0%, #ff9a9a 100%);
border-radius: 40rpx;
font-size: 30rpx;
color: #fff;
border: none;
&::after {
border: none;
}
}
}
// 已登录用户区域
.user-section {
position: relative;
z-index: 1;
padding: 0 32rpx;
}
// 用户信息行(无背景)
.user-info-row {
display: flex;
align-items: center;
margin-bottom: 24rpx;
.user-avatar {
width: 100rpx;
height: 100rpx;
border-radius: 50%;
margin-right: 24rpx;
border: 4rpx solid rgba(255, 255, 255, 0.5);
}
.user-detail {
flex: 1;
.user-name-row {
display: flex;
align-items: center;
margin-bottom: 8rpx;
.user-nickname {
font-size: 36rpx;
font-weight: 600;
color: #333;
margin-right: 12rpx;
}
.edit-icon {
font-size: 28rpx;
color: #999;
}
}
.user-xiangqin-no {
font-size: 26rpx;
color: #666;
}
}
}
// 资料卡片
.profile-card {
background: #fff;
border-radius: 24rpx;
padding: 48rpx 40rpx;
text-align: center;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
.tip-text {
display: block;
font-size: 32rpx;
color: #333;
margin-bottom: 32rpx;
}
.btn-fill {
width: 280rpx;
height: 80rpx;
line-height: 80rpx;
background: #FF8C95;
border-radius: 40rpx;
font-size: 30rpx;
color: #fff;
border: none;
&::after {
border: none;
}
}
}
// 已完成资料的样式
.profile-card.completed {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
padding: 32rpx 40rpx;
text-align: left;
.profile-info {
flex: 1;
.tip-text {
margin-bottom: 8rpx;
font-weight: 500;
}
.sub-text {
font-size: 26rpx;
color: #999;
}
}
.btn-edit {
flex-shrink: 0;
width: 180rpx;
height: 68rpx;
line-height: 68rpx;
background: #fff;
border: 2rpx solid #FF8C95;
border-radius: 34rpx;
font-size: 28rpx;
color: #FF8C95;
margin-left: 20rpx;
&::after {
border: none;
}
}
}
// 互动统计区域
.interact-section {
position: relative;
z-index: 1;
padding: 32rpx;
.interact-grid {
display: flex;
justify-content: space-between;
.interact-item {
display: flex;
flex-direction: column;
align-items: center;
position: relative;
.interact-icon {
width: 120rpx;
height: 120rpx;
border-radius: 24rpx;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 16rpx;
.icon-img {
width: 56rpx;
height: 56rpx;
}
&.viewed {
background: linear-gradient(135deg, #e8e0ff 0%, #d4c4ff 100%);
}
&.favorited {
background: linear-gradient(135deg, #ffe0e8 0%, #ffc4d4 100%);
}
&.unlocked {
background: linear-gradient(135deg, #fff3e0 0%, #ffe4c4 100%);
}
}
.interact-label {
font-size: 28rpx;
color: #333;
font-weight: 500;
}
.interact-badge {
position: absolute;
top: -8rpx;
right: -8rpx;
min-width: 44rpx;
height: 44rpx;
padding: 0 12rpx;
border-radius: 22rpx;
display: flex;
align-items: center;
justify-content: center;
text {
font-size: 24rpx;
color: #fff;
font-weight: 500;
}
}
&:nth-child(1) .interact-badge {
background: #9b7bff;
}
&:nth-child(2) .interact-badge {
background: #ff6b8a;
}
&:nth-child(3) .interact-badge {
background: #ffb347;
}
}
}
}
// 会员公告横幅
.vip-banner {
position: relative;
z-index: 1;
margin: 0 32rpx 32rpx;
padding: 32rpx;
background: linear-gradient(135deg, #4a3728 0%, #2d1f15 100%);
border-radius: 24rpx;
display: flex;
align-items: center;
justify-content: space-between;
.banner-content {
.banner-title {
display: block;
font-size: 34rpx;
font-weight: 600;
color: #ffd700;
margin-bottom: 8rpx;
}
.banner-desc {
display: inline-block;
font-size: 24rpx;
color: #ffd700;
background: rgba(255, 215, 0, 0.2);
padding: 6rpx 16rpx;
border-radius: 20rpx;
}
}
.banner-icon {
width: 100rpx;
height: 100rpx;
}
}
// 常用功能
.menu-section {
position: relative;
z-index: 1;
margin: 0 32rpx;
background: #fff;
border-radius: 24rpx;
padding: 32rpx;
.section-title {
display: block;
font-size: 32rpx;
font-weight: 600;
color: #333;
margin-bottom: 24rpx;
}
.menu-list {
.menu-item {
display: flex;
align-items: center;
padding: 28rpx 0;
border-bottom: 1rpx solid #f5f5f5;
&:last-child {
border-bottom: none;
}
&:active {
opacity: 0.7;
}
.menu-icon {
width: 44rpx;
height: 44rpx;
margin-right: 24rpx;
}
.menu-title {
flex: 1;
font-size: 30rpx;
color: #333;
}
.menu-arrow {
font-size: 32rpx;
color: #ccc;
}
}
}
}
</style>