|
@@ -1,1426 +1,1420 @@
|
|
|
-<template>
|
|
|
|
|
- <view class="container">
|
|
|
|
|
- <!-- 登录浮层(全局组件,页面内弹出底部登录) -->
|
|
|
|
|
- <login-sheet ref="loginSheet" @success="onSheetLoginSuccess" />
|
|
|
|
|
- <!-- 未登录 -->
|
|
|
|
|
- <view v-if="!isLoggedIn">
|
|
|
|
|
- <view class="login-prompt">
|
|
|
|
|
- <text class="prompt-icon">👤</text>
|
|
|
|
|
- <text class="prompt-title">登录浠艾福</text>
|
|
|
|
|
- <text class="prompt-desc">登录后查看个人资料、积分记录、能量数据与家庭管理</text>
|
|
|
|
|
- <button class="login-btn" @click="goLogin">登录 / 注册</button>
|
|
|
|
|
- <text class="skip-link" @click="goSkip">暂不登录,先逛逛 ›</text>
|
|
|
|
|
- </view>
|
|
|
|
|
-
|
|
|
|
|
- <view class="philosophy-card">
|
|
|
|
|
- <text class="philosophy-title">照顾家庭,从照顾好自己开始</text>
|
|
|
|
|
- <text class="philosophy-desc">你的状态,就是家的状态。先安顿好自己,才有余力温暖家人。</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- </view>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 已登录 -->
|
|
|
|
|
- <template v-else>
|
|
|
|
|
- <!-- 退出切换 -->
|
|
|
|
|
- <view class="exit-switch" v-if="isSwitchedChild" @click="onExitSwitch">
|
|
|
|
|
- <text>🔄 退出切换</text>
|
|
|
|
|
- </view>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 身份卡 -->
|
|
|
|
|
- <view class="user-card">
|
|
|
|
|
- <view class="user-card-left">
|
|
|
|
|
- <view class="avatar-wrap" @click="goToEditInfo">
|
|
|
|
|
- <image v-if="userInfo.avatar" class="avatar-img" :src="userInfo.avatar" mode="aspectFill" />
|
|
|
|
|
- <text v-else class="avatar-text">{{ nickname.charAt(0) || '👤' }}</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="user-meta">
|
|
|
|
|
- <view class="user-name-row">
|
|
|
|
|
- <text class="nickname">{{ nickname || '未设置昵称' }}</text>
|
|
|
|
|
- <text v-if="roleLabel" class="role-tag">{{ roleLabel }}</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="user-sub" v-if="memberLevel">
|
|
|
|
|
- <view class="level-badge" :class="'level-' + memberLevel">{{ memberLevelText }}</view>
|
|
|
|
|
- <text v-if="isLifetime" class="days-left">永久有效</text>
|
|
|
|
|
- <text v-else-if="membershipDays > 0 && membershipDays <= 30" class="days-left">剩余 {{ membershipDays }} 天</text>
|
|
|
|
|
- <view v-if="hasUpgradeSpace" class="upgrade-btn" @click="goUpgrade">
|
|
|
|
|
- <text class="upgrade-btn-text">{{ upgradeBtnText }}</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="user-sub" v-else>
|
|
|
|
|
- <text class="days-left">开通会员解锁更多权益</text>
|
|
|
|
|
- <view class="upgrade-btn" @click="goUpgrade">
|
|
|
|
|
- <text class="upgrade-btn-text">立即开通</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- </view>
|
|
|
|
|
- </view>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="user-card-right" @click="goToPoints">
|
|
|
|
|
- <text class="points-num">{{ totalPoints }}</text>
|
|
|
|
|
- <text class="points-label">积分</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- </view>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 我的分享海报浮动按钮(纯 CSS 二维码图标,避免 emoji 渲染成计算器) -->
|
|
|
|
|
- <view class="share-qr-float" @click="showShareQr">
|
|
|
|
|
- <view class="qr-glyph">
|
|
|
|
|
- <view class="qr-eye qr-eye-tl"></view>
|
|
|
|
|
- <view class="qr-eye qr-eye-tr"></view>
|
|
|
|
|
- <view class="qr-eye qr-eye-bl"></view>
|
|
|
|
|
- <view class="qr-mid"></view>
|
|
|
|
|
- <view class="qr-cluster">
|
|
|
|
|
- <view class="qr-pip"></view>
|
|
|
|
|
- <view class="qr-pip"></view>
|
|
|
|
|
- <view class="qr-pip"></view>
|
|
|
|
|
- <view class="qr-pip"></view>
|
|
|
|
|
- </view>
|
|
|
|
|
- </view>
|
|
|
|
|
- </view>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 家族成员速览 -->
|
|
|
|
|
- <view class="family-strip" v-if="familyMembers.length > 0">
|
|
|
|
|
- <view class="family-strip-header">
|
|
|
|
|
- <text class="family-strip-title">我的家庭</text>
|
|
|
|
|
- <text class="family-strip-count">{{ familyMembers.length }} 位成员</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="family-avatars">
|
|
|
|
|
- <view
|
|
|
|
|
- v-for="member in familyMembers"
|
|
|
|
|
- :key="member.id"
|
|
|
|
|
- class="family-avatar-item"
|
|
|
|
|
- @click="goMemberDetail(member)"
|
|
|
|
|
- >
|
|
|
|
|
- <view class="family-avatar-wrap">
|
|
|
|
|
- <image v-if="member.avatar" class="family-avatar-img" :src="member.avatar" mode="aspectFill" />
|
|
|
|
|
- <text v-else class="family-avatar-text">{{ member.nickname ? member.nickname.charAt(0) : '?' }}</text>
|
|
|
|
|
- <view class="family-avatar-tag" :class="'tag-' + (member.effectiveRole || member.source || 'member')">{{ member.roleLabel || memberRoleLabel(member.effectiveRole) }}</view>
|
|
|
|
|
- </view>
|
|
|
|
|
- <text class="family-avatar-name">{{ member.nickname || member.realName || '成员' }}</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="family-avatar-item" @click="goToFamily">
|
|
|
|
|
- <view class="family-avatar-wrap family-avatar-add">
|
|
|
|
|
- <text class="family-avatar-plus">+</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <text class="family-avatar-name">添加</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- </view>
|
|
|
|
|
- </view>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 个人成就统计 -->
|
|
|
|
|
- <view class="achievement-section">
|
|
|
|
|
- <view class="section-header-row">
|
|
|
|
|
- <text class="section-title">🏆 我的成就</text>
|
|
|
|
|
- <text class="section-sub">持续成长,看见改变</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="achievement-grid">
|
|
|
|
|
- <view class="ach-item" @click="goToPoints">
|
|
|
|
|
- <view class="ach-icon-wrap" style="background:rgba(249,115,22,0.12)">
|
|
|
|
|
- <text class="ach-icon">💰</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <text class="ach-num">{{ totalPoints }}</text>
|
|
|
|
|
- <text class="ach-label">累计积分</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="ach-item" @click="goToStreak">
|
|
|
|
|
- <view class="ach-icon-wrap" style="background:rgba(255,140,66,0.12)">
|
|
|
|
|
- <text class="ach-icon">🔥</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <text class="ach-num">{{ streakDays }}</text>
|
|
|
|
|
- <text class="ach-label">连续打卡</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="ach-item" @click="goToTasks">
|
|
|
|
|
- <view class="ach-icon-wrap" style="background:rgba(99,102,241,0.12)">
|
|
|
|
|
- <text class="ach-icon">✅</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <text class="ach-num">{{ completedTasks }}</text>
|
|
|
|
|
- <text class="ach-label">完成任务</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="ach-item" @click="goToWishes">
|
|
|
|
|
- <view class="ach-icon-wrap" style="background:rgba(245,158,11,0.12)">
|
|
|
|
|
- <text class="ach-icon">🎁</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <text class="ach-num">{{ rewardsEarned }}</text>
|
|
|
|
|
- <text class="ach-label">获得奖励</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="ach-item" @click="goToBadges">
|
|
|
|
|
- <view class="ach-icon-wrap" style="background:rgba(236,72,153,0.12)">
|
|
|
|
|
- <text class="ach-icon">🏅</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <text class="ach-num">{{ badgesCollected }}</text>
|
|
|
|
|
- <text class="ach-label">收集勋章</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="ach-item" @click="goToGrowth">
|
|
|
|
|
- <view class="ach-icon-wrap" style="background:rgba(16,185,129,0.12)">
|
|
|
|
|
- <text class="ach-icon">📈</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <text class="ach-num">{{ growthRecords }}</text>
|
|
|
|
|
- <text class="ach-label">成长记录</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="ach-item" @click="goToChallenges">
|
|
|
|
|
- <view class="ach-icon-wrap" style="background:rgba(255,107,157,0.12)">
|
|
|
|
|
- <text class="ach-icon">🎯</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <text class="ach-num">{{ challengesJoined }}</text>
|
|
|
|
|
- <text class="ach-label">参加挑战</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="ach-item" @click="goToHealthReports">
|
|
|
|
|
- <view class="ach-icon-wrap" style="background:rgba(14,165,233,0.12)">
|
|
|
|
|
- <text class="ach-icon">📋</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <text class="ach-num">{{ healthReports }}</text>
|
|
|
|
|
- <text class="ach-label">上传报告</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="ach-item" @click="goToWealthCenter">
|
|
|
|
|
- <view class="ach-icon-wrap" style="background:rgba(245,158,11,0.12)">
|
|
|
|
|
- <text class="ach-icon">💰</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <text class="ach-num">{{ referralEarnings }}</text>
|
|
|
|
|
- <text class="ach-label">推广收益</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="ach-item" @click="goTeamTree">
|
|
|
|
|
- <view class="ach-icon-wrap" style="background:rgba(16,185,129,0.12)">
|
|
|
|
|
- <text class="ach-icon">🌳</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <text class="ach-num">{{ teamMembers }}</text>
|
|
|
|
|
- <text class="ach-label">我的团队</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- </view>
|
|
|
|
|
- </view>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 我的订单(电商风格) -->
|
|
|
|
|
- <view class="order-section">
|
|
|
|
|
- <view class="order-section-header">
|
|
|
|
|
- <text class="section-title">🛒 我的订单</text>
|
|
|
|
|
- <text class="order-all-link" @click="goToOrders">全部订单 ›</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="order-card">
|
|
|
|
|
- <view class="order-grid">
|
|
|
|
|
- <view class="order-grid-item" @click="goToOrderStatus('pending')">
|
|
|
|
|
- <view class="order-icon-wrap">
|
|
|
|
|
- <text class="order-icon">💳</text>
|
|
|
|
|
- <text class="order-badge" v-if="orderPending > 0">{{ orderPending }}</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <text class="order-grid-text">待付款</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="order-grid-item" @click="goToOrderStatus('paid')">
|
|
|
|
|
- <view class="order-icon-wrap">
|
|
|
|
|
- <text class="order-icon">📦</text>
|
|
|
|
|
- <text class="order-badge" v-if="orderUnpaid > 0">{{ orderUnpaid }}</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <text class="order-grid-text">待发货</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="order-grid-item" @click="goToOrderStatus('shipped')">
|
|
|
|
|
- <view class="order-icon-wrap">
|
|
|
|
|
- <text class="order-icon">🚚</text>
|
|
|
|
|
- <text class="order-badge" v-if="orderShipped > 0">{{ orderShipped }}</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <text class="order-grid-text">待收货</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="order-grid-item" @click="goToOrderStatus('refunding')">
|
|
|
|
|
- <view class="order-icon-wrap">
|
|
|
|
|
- <text class="order-icon">🔧</text>
|
|
|
|
|
- <text class="order-badge" v-if="orderAfterSale > 0">{{ orderAfterSale }}</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <text class="order-grid-text">售后</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="order-grid-item" @click="goToOrders">
|
|
|
|
|
- <view class="order-icon-wrap">
|
|
|
|
|
- <text class="order-icon">📋</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <text class="order-grid-text">全部</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="order-extra-row">
|
|
|
|
|
- <view class="order-extra-item" @click="goToCart">
|
|
|
|
|
- <text class="order-extra-icon">🛒</text>
|
|
|
|
|
- <text class="order-extra-text">购物车</text>
|
|
|
|
|
- <text class="order-extra-badge" v-if="cartCount > 0">{{ cartCount > 99 ? '99+' : cartCount }}</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="order-extra-divider"></view>
|
|
|
|
|
- <view class="order-extra-item" @click="goToAddress">
|
|
|
|
|
- <text class="order-extra-icon">📍</text>
|
|
|
|
|
- <text class="order-extra-text">收货地址</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- </view>
|
|
|
|
|
- </view>
|
|
|
|
|
- </view>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 规划师服务中心入口(仅规划师可见) -->
|
|
|
|
|
- <view class="service-center-section" v-if="role === 'teacher' && teacherStatus === 'approved'">
|
|
|
|
|
- <view class="service-center-card" @click="goToServiceCenter">
|
|
|
|
|
- <view class="service-center-icon">👨🏫</view>
|
|
|
|
|
- <view class="service-center-info">
|
|
|
|
|
- <text class="service-center-title">服务中心</text>
|
|
|
|
|
- <text class="service-center-desc">待处理任务:{{ pendingTasks }} 条 · 消息:{{ unreadMessages }} 条</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <text class="service-center-arrow">›</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- </view>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 规划师待审核提示 -->
|
|
|
|
|
- <view class="service-center-section" v-if="role === 'teacher' && teacherStatus !== 'approved'">
|
|
|
|
|
- <view class="service-center-card disabled-card">
|
|
|
|
|
- <view class="service-center-icon">👨🏫</view>
|
|
|
|
|
- <view class="service-center-info">
|
|
|
|
|
- <text class="service-center-title">服务中心</text>
|
|
|
|
|
- <text class="service-center-desc">{{ teacherStatus === 'pending' ? '申请审核中,暂未开放' : teacherStatus === 'rejected' ? '申请未通过,请联系管理员' : '规划师服务' }}</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- </view>
|
|
|
|
|
- </view>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 设置 + 退出 -->
|
|
|
|
|
- <view class="bottom-menu-section">
|
|
|
|
|
- <view class="menu-row" @click="goToMessageHistory">
|
|
|
|
|
- <text class="menu-row-icon">📨</text>
|
|
|
|
|
- <text class="menu-row-text">历史消息</text>
|
|
|
|
|
- <text class="menu-row-arrow">›</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="menu-row" @click="goToSettings">
|
|
|
|
|
- <text class="menu-row-icon">⚙️</text>
|
|
|
|
|
- <text class="menu-row-text">设置</text>
|
|
|
|
|
- <text class="menu-row-arrow">›</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="logout-btn" @click="logout">
|
|
|
|
|
- <text class="logout-text">退出登录</text>
|
|
|
|
|
- </view>
|
|
|
|
|
- </view>
|
|
|
|
|
-
|
|
|
|
|
- </template>
|
|
|
|
|
- <!-- 我的分享海报:复用文章/活动/商品同一套 ContentSharePoster 组件(canvas 绘制 + 保存到相册) -->
|
|
|
|
|
- <ContentSharePoster
|
|
|
|
|
- :show="showSharePoster"
|
|
|
|
|
- :title="shareHeadline"
|
|
|
|
|
- :energyData="posterEnergyData"
|
|
|
|
|
- :brandFallback="true"
|
|
|
|
|
- :qrCodeBase64="posterQrCode || ''"
|
|
|
|
|
- typeLabel="邀友加入"
|
|
|
|
|
- :summary="posterSummary"
|
|
|
|
|
- ctaText="长按识别二维码 · 加入浠艾福"
|
|
|
|
|
- :inviteText="posterInviteText"
|
|
|
|
|
- @close="closeSharePoster"
|
|
|
|
|
- />
|
|
|
|
|
- <FloatingAvatar />
|
|
|
|
|
- </view>
|
|
|
|
|
-</template>
|
|
|
|
|
-
|
|
|
|
|
-<script>
|
|
|
|
|
-import FloatingAvatar from '../../components/AIFloatingAvatar.vue'
|
|
|
|
|
-import ContentSharePoster from '../../components/ContentSharePoster.vue'
|
|
|
|
|
-import {
|
|
|
|
|
- getChildren, getFamilyMemberList,
|
|
|
|
|
- getTodayTasksByCategory, getChallengeList, getMyMembership,
|
|
|
|
|
- verifyPassword, getPointsBalance, getUserInfo,
|
|
|
|
|
- cartCount, getOrderCounts, getReferralSummary,
|
|
|
|
|
- getProfileStats, getMyReferrer, getTeamConsumption,
|
|
|
|
|
- getTeacherInfo, getTeacherPendingTasks, getTeacherUnreadCount,
|
|
|
|
|
- getReferralCode, extractReferralCode, getShareQrCode, getEnergySandbox,
|
|
|
|
|
- vendorStatus
|
|
|
|
|
-} from '../../utils/api.js'
|
|
|
|
|
-
|
|
|
|
|
-export default {
|
|
|
|
|
- components: { FloatingAvatar, ContentSharePoster },
|
|
|
|
|
- data() {
|
|
|
|
|
- return {
|
|
|
|
|
- nickname: '',
|
|
|
|
|
- role: 'parent',
|
|
|
|
|
- isSwitchedChild: false,
|
|
|
|
|
- userInfo: {},
|
|
|
|
|
- memberLevel: null,
|
|
|
|
|
- membershipDays: 0,
|
|
|
|
|
- totalPoints: 0,
|
|
|
|
|
- referralEarnings: 0,
|
|
|
|
|
- teamMembers: 0,
|
|
|
|
|
- // 个人成就
|
|
|
|
|
- completedTasks: 0,
|
|
|
|
|
- streakDays: 0,
|
|
|
|
|
- rewardsEarned: 0,
|
|
|
|
|
- badgesCollected: 0,
|
|
|
|
|
- growthRecords: 0,
|
|
|
|
|
- challengesJoined: 0,
|
|
|
|
|
- healthReports: 0,
|
|
|
|
|
- // 家庭
|
|
|
|
|
- familyMembers: [],
|
|
|
|
|
- // 商城
|
|
|
|
|
- orderPending: 0,
|
|
|
|
|
- orderUnpaid: 0,
|
|
|
|
|
- orderShipped: 0,
|
|
|
|
|
- orderAfterSale: 0,
|
|
|
|
|
- cartCount: 0,
|
|
|
|
|
- // 待办
|
|
|
|
|
- pendingTasks: 0,
|
|
|
|
|
- unreadMessages: 0,
|
|
|
|
|
- teacherStatus: 'pending',
|
|
|
|
|
- isVendor: false,
|
|
|
|
|
- profileProgress: 0,
|
|
|
|
|
- myReferrer: null,
|
|
|
|
|
- // 分享推广码
|
|
|
|
|
- showSharePoster: false,
|
|
|
|
|
- referralCode: '',
|
|
|
|
|
- posterQrCode: '',
|
|
|
|
|
- posterEnergyData: null,
|
|
|
|
|
- referralCodeLoaded: false,
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- computed: {
|
|
|
|
|
- isLoggedIn() {
|
|
|
|
|
- return !!uni.getStorageSync('token')
|
|
|
|
|
- },
|
|
|
|
|
- roleLabel() {
|
|
|
|
|
- var map = { parent: '', child: '成员', teacher: '规划师' }
|
|
|
|
|
- return map[this.role] || '用户'
|
|
|
|
|
- },
|
|
|
|
|
- memberLevelText() {
|
|
|
|
|
- var map = { FREE: '免费用户', BASIC: '体验会员', FAMILY: '家庭会员', PREMIUM: '高级会员', LIFETIME: '终身会员' }
|
|
|
|
|
- return map[this.memberLevel] || this.memberLevel || ''
|
|
|
|
|
- },
|
|
|
|
|
- isLifetime() {
|
|
|
|
|
- return this.memberLevel === 'LIFETIME'
|
|
|
|
|
- },
|
|
|
|
|
- hasUpgradeSpace() {
|
|
|
|
|
- return this.memberLevel === 'FREE' || this.memberLevel === 'FAMILY'
|
|
|
|
|
- },
|
|
|
|
|
- upgradeBtnText() {
|
|
|
|
|
- return this.hasUpgradeSpace ? '升级' : '立即开通'
|
|
|
|
|
- },
|
|
|
|
|
- // 分享海报标题:XXX邀请你关注家庭健康(昵称过长自动截断)
|
|
|
|
|
- shareHeadline() {
|
|
|
|
|
- var name = (this.nickname || '').trim()
|
|
|
|
|
- if (!name) return '邀请你关注家庭健康'
|
|
|
|
|
- if (name.length > 8) name = name.substring(0, 8) + '…'
|
|
|
|
|
- return name + '邀请你关注家庭健康'
|
|
|
|
|
- },
|
|
|
|
|
- posterSummary() {
|
|
|
|
|
- return '身体 · 心理 · 智慧 · 行动 · 财富,一家专注全家健康的成长平台'
|
|
|
|
|
- },
|
|
|
|
|
- posterInviteText() {
|
|
|
|
|
- var name = (this.nickname || '').trim()
|
|
|
|
|
- if (!name) return '扫码加入浠艾福,一起照顾好这个家'
|
|
|
|
|
- if (name.length > 8) name = name.substring(0, 8) + '…'
|
|
|
|
|
- return '「' + name + '」邀请你加入浠艾福'
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- onShow() {
|
|
|
|
|
- if (!this.isLoggedIn) {
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
- var role = uni.getStorageSync('currentRole') || uni.getStorageSync('role') || 'parent'
|
|
|
|
|
- this.role = role
|
|
|
|
|
- this.isSwitchedChild = uni.getStorageSync('isSwitchedChild') || false
|
|
|
|
|
- this.nickname = (uni.getStorageSync('userInfo') || {}).nickname || ''
|
|
|
|
|
-
|
|
|
|
|
- // 服务商身份判断(成就区「我的团队」入口按此分发)
|
|
|
|
|
- this.checkVendorStatus()
|
|
|
|
|
-
|
|
|
|
|
- // 规划师加载额外数据
|
|
|
|
|
- if (role === 'teacher') {
|
|
|
|
|
- this.loadTeacherStatus()
|
|
|
|
|
- this.loadTeacherData()
|
|
|
|
|
- } else {
|
|
|
|
|
- this.loadAllData()
|
|
|
|
|
- this.loadMyReferrer()
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- methods: {
|
|
|
|
|
- // 服务商身份:vendorStatus 返回 approved 即为已通过审核的服务商
|
|
|
|
|
- checkVendorStatus() {
|
|
|
|
|
- var self = this
|
|
|
|
|
- vendorStatus().then(function(res) {
|
|
|
|
|
- self.isVendor = !!(res && res.data && res.data.vendorStatus === 'approved')
|
|
|
|
|
- }).catch(function() {
|
|
|
|
|
- self.isVendor = false
|
|
|
|
|
- })
|
|
|
|
|
- },
|
|
|
|
|
- loadTeacherStatus() {
|
|
|
|
|
- var self = this
|
|
|
|
|
- getTeacherInfo().then(function(res) {
|
|
|
|
|
- if (res && res.code === 200 && res.data) {
|
|
|
|
|
- self.teacherStatus = res.data.verificationStatus || res.data.status || 'pending'
|
|
|
|
|
- }
|
|
|
|
|
- }).catch(function() {})
|
|
|
|
|
- },
|
|
|
|
|
- loadTeacherData() {
|
|
|
|
|
- var self = this
|
|
|
|
|
- // 并行加载待处理任务和未读消息
|
|
|
|
|
- getTeacherPendingTasks().then(function(res) {
|
|
|
|
|
- if (res && res.data && Array.isArray(res.data)) {
|
|
|
|
|
- self.pendingTasks = res.data.length
|
|
|
|
|
- } else if (res && res.data && res.data.pendingCount != null) {
|
|
|
|
|
- self.pendingTasks = res.data.pendingCount
|
|
|
|
|
- }
|
|
|
|
|
- }).catch(function() {})
|
|
|
|
|
- getTeacherUnreadCount().then(function(res) {
|
|
|
|
|
- if (res && res.data != null) {
|
|
|
|
|
- self.unreadMessages = res.data
|
|
|
|
|
- }
|
|
|
|
|
- }).catch(function() {})
|
|
|
|
|
- },
|
|
|
|
|
- loadAllData() {
|
|
|
|
|
- this.loadUserInfo()
|
|
|
|
|
- this.loadMembership()
|
|
|
|
|
- this.loadPoints()
|
|
|
|
|
- this.loadFamily()
|
|
|
|
|
- this.loadTasks()
|
|
|
|
|
- this.loadChallenges()
|
|
|
|
|
- this.loadOrderCounts()
|
|
|
|
|
- this.loadCartCount()
|
|
|
|
|
- this.loadProfileStats()
|
|
|
|
|
- this.loadReferralEarnings()
|
|
|
|
|
- this.loadTeamInfo()
|
|
|
|
|
- },
|
|
|
|
|
- loadUserInfo() {
|
|
|
|
|
- var self = this
|
|
|
|
|
- getUserInfo().then(function(res) {
|
|
|
|
|
- if (res && res.code === 200 && res.data) {
|
|
|
|
|
- self.userInfo = res.data
|
|
|
|
|
- if (!self.nickname) self.nickname = res.data.nickname || ''
|
|
|
|
|
- }
|
|
|
|
|
- }).catch(function() {})
|
|
|
|
|
- },
|
|
|
|
|
- loadMembership() {
|
|
|
|
|
- var self = this
|
|
|
|
|
- getMyMembership().then(function(res) {
|
|
|
|
|
- if (res && res.data) {
|
|
|
|
|
- self.memberLevel = res.data.memberLevel || res.data.levelCode || null
|
|
|
|
|
- self.membershipDays = res.data.membershipDays || res.data.daysRemaining || 0
|
|
|
|
|
- }
|
|
|
|
|
- }).catch(function() {})
|
|
|
|
|
- },
|
|
|
|
|
- loadPoints() {
|
|
|
|
|
- // 累计积分由 loadProfileStats 汇总家庭成员积分,此处不再单独覆盖
|
|
|
|
|
- },
|
|
|
|
|
- loadFamily() {
|
|
|
|
|
- var self = this
|
|
|
|
|
- getFamilyMemberList({ visibleOnly: true }).then(function(res) {
|
|
|
|
|
- var data = res && res.data ? res.data : []
|
|
|
|
|
- self.familyMembers = Array.isArray(data) ? data : []
|
|
|
|
|
- }).catch(function() {
|
|
|
|
|
- self.familyMembers = []
|
|
|
|
|
- })
|
|
|
|
|
- },
|
|
|
|
|
- loadTasks() {
|
|
|
|
|
- var self = this
|
|
|
|
|
- var memberId = uni.getStorageSync('currentChildId') || uni.getStorageSync('userId')
|
|
|
|
|
- if (!memberId) return
|
|
|
|
|
- getTodayTasksByCategory(memberId, 'action').then(function(res) {
|
|
|
|
|
- if (res && res.data) {
|
|
|
|
|
- self.pendingTasks = Array.isArray(res.data) ? res.data.length : 0
|
|
|
|
|
- }
|
|
|
|
|
- }).catch(function() {})
|
|
|
|
|
- // fallback to storage
|
|
|
|
|
- if (!self.pendingTasks) {
|
|
|
|
|
- self.pendingTasks = parseInt(uni.getStorageSync('pendingTaskCount') || 0)
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- loadChallenges() {
|
|
|
|
|
- var self = this
|
|
|
|
|
- getChallengeList({}).then(function(res) {
|
|
|
|
|
- if (res && res.data) {
|
|
|
|
|
- // 不在此处使用,仅确保请求
|
|
|
|
|
- }
|
|
|
|
|
- }).catch(function() {})
|
|
|
|
|
- },
|
|
|
|
|
- loadOrderCounts() {
|
|
|
|
|
- var self = this
|
|
|
|
|
- getOrderCounts().then(function(res) {
|
|
|
|
|
- if (res && res.data) {
|
|
|
|
|
- self.orderPending = res.data.pending || 0
|
|
|
|
|
- self.orderUnpaid = res.data.paid || 0
|
|
|
|
|
- self.orderShipped = res.data.shipped || 0
|
|
|
|
|
- self.orderAfterSale = res.data.refunding || 0
|
|
|
|
|
- }
|
|
|
|
|
- }).catch(function() {})
|
|
|
|
|
- },
|
|
|
|
|
- loadCartCount() {
|
|
|
|
|
- var self = this
|
|
|
|
|
- cartCount().then(function(res) {
|
|
|
|
|
- if (res && res.data != null) {
|
|
|
|
|
- self.cartCount = res.data || 0
|
|
|
|
|
- }
|
|
|
|
|
- }).catch(function() {})
|
|
|
|
|
- },
|
|
|
|
|
- loadProfileStats() {
|
|
|
|
|
- var self = this
|
|
|
|
|
- getProfileStats().then(function(res) {
|
|
|
|
|
- if (res && res.code === 200 && res.data) {
|
|
|
|
|
- var d = res.data
|
|
|
|
|
- if (d.totalPoints != null) self.totalPoints = d.totalPoints
|
|
|
|
|
- if (d.streakDays != null) self.streakDays = d.streakDays
|
|
|
|
|
- if (d.completedTasks != null) self.completedTasks = d.completedTasks
|
|
|
|
|
- if (d.rewardsEarned != null) self.rewardsEarned = d.rewardsEarned
|
|
|
|
|
- if (d.badgesCollected != null) self.badgesCollected = d.badgesCollected
|
|
|
|
|
- if (d.growthRecords != null) self.growthRecords = d.growthRecords
|
|
|
|
|
- if (d.challengesJoined != null) self.challengesJoined = d.challengesJoined
|
|
|
|
|
- if (d.healthReports != null) self.healthReports = d.healthReports
|
|
|
|
|
- }
|
|
|
|
|
- }).catch(function() {})
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- loadReferralEarnings() {
|
|
|
|
|
- var self = this
|
|
|
|
|
- getReferralSummary().then(function(res) {
|
|
|
|
|
- if (res && res.code === 200 && res.data) {
|
|
|
|
|
- self.referralEarnings = res.data.totalEarnings || 0
|
|
|
|
|
- }
|
|
|
|
|
- }).catch(function() {})
|
|
|
|
|
- },
|
|
|
|
|
- loadTeamInfo() {
|
|
|
|
|
- var self = this
|
|
|
|
|
- getTeamConsumption(3).then(function(res) {
|
|
|
|
|
- if (res && res.data && res.data.totalMembers != null) {
|
|
|
|
|
- self.teamMembers = res.data.totalMembers
|
|
|
|
|
- }
|
|
|
|
|
- }).catch(function() {})
|
|
|
|
|
- },
|
|
|
|
|
- async loadMyReferrer() {
|
|
|
|
|
- try {
|
|
|
|
|
- var memberId = uni.getStorageSync('currentMemberId') || uni.getStorageSync('currentChildId') || 0
|
|
|
|
|
- var res = await getMyReferrer(memberId)
|
|
|
|
|
- if (res && res.code === 200 && res.data && res.data.hasReferrer) {
|
|
|
|
|
- this.myReferrer = res.data
|
|
|
|
|
- }
|
|
|
|
|
- } catch (e) {
|
|
|
|
|
- console.error('获取推荐人失败', e)
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- // ===== 分享海报(复用 ContentSharePoster,与文章/活动/商品同一套生成逻辑)=====
|
|
|
|
|
- async showShareQr() {
|
|
|
|
|
- uni.showLoading({ title: '生成海报中...' })
|
|
|
|
|
- try {
|
|
|
|
|
- if (!this.referralCodeLoaded) {
|
|
|
|
|
- await this.loadReferralCode()
|
|
|
|
|
- }
|
|
|
|
|
- var scene = 'ref=' + (this.referralCode || '')
|
|
|
|
|
- if (scene.length > 32) {
|
|
|
|
|
- scene = scene.substring(0, 32)
|
|
|
|
|
- }
|
|
|
|
|
- // 首页路径,扫码后进入首页并绑定推荐人
|
|
|
|
|
- // 注意:微信 getwxacodeunlimit 的 page 不能带前导斜杠,否则报 errcode 41030 invalid page rid
|
|
|
|
|
- var res = await getShareQrCode('pages/index-home/index', scene)
|
|
|
|
|
- if (res && res.code === 200 && res.data && res.data.qrCodeBase64) {
|
|
|
|
|
- this.posterQrCode = res.data.qrCodeBase64
|
|
|
|
|
- // 必须在 showSharePoster=true 之前取到数据:组件 watch 到 show 后 300ms 即开始绘制
|
|
|
|
|
- await this.loadPosterEnergy()
|
|
|
|
|
- this.showSharePoster = true
|
|
|
|
|
- } else {
|
|
|
|
|
- uni.showToast({ title: '生成失败,请重试', icon: 'none' })
|
|
|
|
|
- }
|
|
|
|
|
- } catch (e) {
|
|
|
|
|
- console.error('生成分享海报失败', e)
|
|
|
|
|
- uni.showToast({ title: '生成失败,请重试', icon: 'none' })
|
|
|
|
|
- } finally {
|
|
|
|
|
- uni.hideLoading()
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- closeSharePoster() {
|
|
|
|
|
- this.showSharePoster = false
|
|
|
|
|
- },
|
|
|
|
|
- // 五维能量数据卡数据(方案C):家庭聚合视角;未解锁全景/取数失败时置空,海报降级为品牌卡
|
|
|
|
|
- loadPosterEnergy() {
|
|
|
|
|
- var self = this
|
|
|
|
|
- return getEnergySandbox('family').then(function(res) {
|
|
|
|
|
- self.posterEnergyData = self.mapPosterEnergy(res)
|
|
|
|
|
- }).catch(function(e) {
|
|
|
|
|
- console.error('获取五维能量失败,海报降级为品牌卡', e)
|
|
|
|
|
- self.posterEnergyData = null
|
|
|
|
|
- })
|
|
|
|
|
- },
|
|
|
|
|
- mapPosterEnergy(res) {
|
|
|
|
|
- if (!res || res.code !== 200 || !res.data) return null
|
|
|
|
|
- // locked=true 表示用户未解锁全景,此时不展示任何数据,改为品牌卡
|
|
|
|
|
- if (res.data.locked) return null
|
|
|
|
|
- // 只认家庭聚合视角;仅当接口返回的是裸 DTO(无 personalData/familyData 包装)时才直接取 res.data
|
|
|
|
|
- var d = res.data.familyData || null
|
|
|
|
|
- if (!d) {
|
|
|
|
|
- if (res.data.personalData) return null
|
|
|
|
|
- d = res.data
|
|
|
|
|
- }
|
|
|
|
|
- if (!d || typeof d !== 'object') return null
|
|
|
|
|
- var defs = [
|
|
|
|
|
- { key: 'body', label: '身', color: '#FF8C42' },
|
|
|
|
|
- { key: 'mind', label: '心', color: '#FF6B9D' },
|
|
|
|
|
- { key: 'wisdom', label: '智', color: '#6366F1' },
|
|
|
|
|
- { key: 'action', label: '行', color: '#10B981' },
|
|
|
|
|
- { key: 'wealth', label: '富', color: '#F59E0B' }
|
|
|
|
|
- ]
|
|
|
|
|
- var dims = []
|
|
|
|
|
- var sum = 0
|
|
|
|
|
- for (var i = 0; i < defs.length; i++) {
|
|
|
|
|
- var raw = d[defs[i].key + 'Score']
|
|
|
|
|
- if (raw === null || raw === undefined || raw === '' || isNaN(raw)) continue
|
|
|
|
|
- var n = Number(raw)
|
|
|
|
|
- if (n < 0) n = 0
|
|
|
|
|
- if (n > 100) n = 100
|
|
|
|
|
- sum += n
|
|
|
|
|
- dims.push({ key: defs[i].key, label: defs[i].label, score: n, color: defs[i].color })
|
|
|
|
|
- }
|
|
|
|
|
- if (dims.length === 0) return null
|
|
|
|
|
- var overall = d.overallScore
|
|
|
|
|
- if (overall === null || overall === undefined || overall === '' || isNaN(overall)) {
|
|
|
|
|
- overall = Math.round(sum / dims.length)
|
|
|
|
|
- }
|
|
|
|
|
- return { label: '我们家的能量', overall: Number(overall), dims: dims }
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- async loadReferralCode() {
|
|
|
|
|
- if (this.referralCodeLoaded) return
|
|
|
|
|
- var self = this
|
|
|
|
|
- try {
|
|
|
|
|
- var res = await getReferralCode()
|
|
|
|
|
- self.referralCode = extractReferralCode(res) || ''
|
|
|
|
|
- } catch (e) {
|
|
|
|
|
- console.error('获取推广码失败', e)
|
|
|
|
|
- } finally {
|
|
|
|
|
- self.referralCodeLoaded = true
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- goToServiceCenter() {
|
|
|
|
|
- uni.navigateTo({ url: '/pages/service-center/index' })
|
|
|
|
|
- },
|
|
|
|
|
- goTeamTree() {
|
|
|
|
|
- // 服务商 → 服务商团队;非服务商 → 引导申请
|
|
|
|
|
- if (this.isVendor) {
|
|
|
|
|
- uni.navigateTo({ url: '/pages/vendor/team-system/team-system' })
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
- uni.showModal({
|
|
|
|
|
- title: '提示',
|
|
|
|
|
- content: '您尚未申请任何服务角色,是否立即申请?',
|
|
|
|
|
- confirmText: '去申请',
|
|
|
|
|
- cancelText: '取消',
|
|
|
|
|
- success: function(res) {
|
|
|
|
|
- if (res.confirm) {
|
|
|
|
|
- uni.navigateTo({ url: '/pages/profile/service-role-apply' })
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
- },
|
|
|
|
|
- goLogin: function() {
|
|
|
|
|
- this.openLoginSheet()
|
|
|
|
|
- },
|
|
|
|
|
- // 登录浮层成功回调:刷新个人资料与登录后数据
|
|
|
|
|
- onLoginSuccess: function() {
|
|
|
|
|
- if (this.loadUserInfo) this.loadUserInfo()
|
|
|
|
|
- },
|
|
|
|
|
- goSkip: function() {
|
|
|
|
|
- uni.switchTab({ url: '/pages/index-home/index' })
|
|
|
|
|
- },
|
|
|
|
|
- goToEditInfo: function() {
|
|
|
|
|
- uni.navigateTo({ url: '/pages/user-edit/user-edit' })
|
|
|
|
|
- },
|
|
|
|
|
- goToPoints: function() {
|
|
|
|
|
- uni.navigateTo({ url: '/pages/points/points' })
|
|
|
|
|
- },
|
|
|
|
|
- goToStreak: function() {
|
|
|
|
|
- var memberId = uni.getStorageSync('currentMemberId') || uni.getStorageSync('currentChildId')
|
|
|
|
|
- var url = '/pages/stats/completion-rate'
|
|
|
|
|
- if (memberId) {
|
|
|
|
|
- url += '?memberId=' + memberId
|
|
|
|
|
- } else {
|
|
|
|
|
- url += '?familyView=1'
|
|
|
|
|
- }
|
|
|
|
|
- uni.navigateTo({ url: url })
|
|
|
|
|
- },
|
|
|
|
|
- goToTasks: function() {
|
|
|
|
|
- uni.navigateTo({ url: '/pages/tasks/tasks' })
|
|
|
|
|
- },
|
|
|
|
|
- goToWishes: function() {
|
|
|
|
|
- uni.navigateTo({ url: '/pages/wishes/index' })
|
|
|
|
|
- },
|
|
|
|
|
- goToBadges: function() {
|
|
|
|
|
- uni.navigateTo({ url: '/pages/rewards/badge' })
|
|
|
|
|
- },
|
|
|
|
|
- goToGrowth: function() {
|
|
|
|
|
- uni.navigateTo({ url: '/pages/growth-main/index' })
|
|
|
|
|
- },
|
|
|
|
|
- goToChallenges: function() {
|
|
|
|
|
- uni.navigateTo({ url: '/pages/health/challenge-manage' })
|
|
|
|
|
- },
|
|
|
|
|
- goToHealthReports: function() {
|
|
|
|
|
- uni.navigateTo({ url: '/pages/health/report-list' })
|
|
|
|
|
- },
|
|
|
|
|
- goToWealthCenter: function() {
|
|
|
|
|
- uni.navigateTo({ url: '/pages/promotion/index' })
|
|
|
|
|
- },
|
|
|
|
|
- goUpgrade: function() {
|
|
|
|
|
- uni.navigateTo({ url: '/pages/membership/upgrade' })
|
|
|
|
|
- },
|
|
|
|
|
- goToFamily: function() {
|
|
|
|
|
- uni.navigateTo({ url: '/pages/profile-extra/family-members' })
|
|
|
|
|
- },
|
|
|
|
|
- goMemberDetail: function(member) {
|
|
|
|
|
- uni.navigateTo({ url: '/pages/home-pages/member-home-detail?memberId=' + member.id })
|
|
|
|
|
- },
|
|
|
|
|
- goToAppointments: function() {
|
|
|
|
|
- uni.navigateTo({ url: '/pages/parent/appointments' })
|
|
|
|
|
- },
|
|
|
|
|
- goToOrders: function() {
|
|
|
|
|
- uni.navigateTo({ url: '/pages/shop/order-list/order-list' })
|
|
|
|
|
- },
|
|
|
|
|
- goToOrderStatus: function(status) {
|
|
|
|
|
- uni.navigateTo({ url: '/pages/shop/order-list/order-list?status=' + status })
|
|
|
|
|
- },
|
|
|
|
|
- goToCart: function() {
|
|
|
|
|
- uni.navigateTo({ url: '/pages/shop/cart/cart' })
|
|
|
|
|
- },
|
|
|
|
|
- goToAddress: function() {
|
|
|
|
|
- uni.navigateTo({ url: '/pages/shop/address/address' })
|
|
|
|
|
- },
|
|
|
|
|
- goToMessageHistory: function() {
|
|
|
|
|
- uni.navigateTo({ url: '/pages/profile/message-history' })
|
|
|
|
|
- },
|
|
|
|
|
- goToSettings: function() {
|
|
|
|
|
- uni.showActionSheet({
|
|
|
|
|
- itemList: ['完善信息', '家庭成员', '隐私政策', '用户协议', '我的画像', '提醒设置'],
|
|
|
|
|
- success: function(res) {
|
|
|
|
|
- var urls = {
|
|
|
|
|
- 0: '/pages/user-edit/user-edit',
|
|
|
|
|
- 1: '/pages/profile-extra/family-members',
|
|
|
|
|
- 2: '/pages/policy/privacy',
|
|
|
|
|
- 3: '/pages/policy/agreement',
|
|
|
|
|
- 4: '/pages/profile/portrait-edit',
|
|
|
|
|
- 5: '/pages/reminder-config/index'
|
|
|
|
|
- }
|
|
|
|
|
- if (urls[res.tapIndex]) {
|
|
|
|
|
- uni.navigateTo({ url: urls[res.tapIndex] })
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
- },
|
|
|
|
|
- memberRoleLabel: function(role) {
|
|
|
|
|
- var map = { parent: '家长', child: '成员', teacher: '规划师' }
|
|
|
|
|
- return map[role] || '成员'
|
|
|
|
|
- },
|
|
|
|
|
- onExitSwitch: function() {
|
|
|
|
|
- var self = this
|
|
|
|
|
- uni.showModal({
|
|
|
|
|
- title: '退出切换',
|
|
|
|
|
- content: '请输入家长密码以确认退出',
|
|
|
|
|
- editable: true,
|
|
|
|
|
- success: function(res) {
|
|
|
|
|
- if (res.confirm && res.content) {
|
|
|
|
|
- verifyPassword(res.content).then(function(result) {
|
|
|
|
|
- if (result.code === 200) {
|
|
|
|
|
- self.$store.commit('switchBackToParent')
|
|
|
|
|
- self.isSwitchedChild = false
|
|
|
|
|
- self.role = 'parent'
|
|
|
|
|
- uni.showToast({ title: '已退出切换', icon: 'success' })
|
|
|
|
|
- uni.reLaunch({ url: '/pages/index-home/index' })
|
|
|
|
|
- } else {
|
|
|
|
|
- uni.showToast({ title: '密码错误', icon: 'none' })
|
|
|
|
|
- }
|
|
|
|
|
- }).catch(function() {
|
|
|
|
|
- uni.showToast({ title: '验证失败', icon: 'none' })
|
|
|
|
|
- })
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
- },
|
|
|
|
|
- logout: function() {
|
|
|
|
|
- uni.showModal({
|
|
|
|
|
- title: '确认退出',
|
|
|
|
|
- content: '确定要退出登录吗?',
|
|
|
|
|
- success: function(res) {
|
|
|
|
|
- if (res.confirm) {
|
|
|
|
|
- this.$store.commit('logout')
|
|
|
|
|
- uni.reLaunch({ url: '/pages/index-home/index' })
|
|
|
|
|
- }
|
|
|
|
|
- }.bind(this)
|
|
|
|
|
- })
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-</script>
|
|
|
|
|
-
|
|
|
|
|
-<style scoped lang="scss">
|
|
|
|
|
-// ===== 页面级局部调色板(单页专有色阶;同值才映射全局令牌,约定见 uni.scss §14) =====
|
|
|
|
|
-$c-text-gray-400: $facade-text-gray-400;
|
|
|
|
|
-$c-fill-2b: #F2F2F2;
|
|
|
|
|
-$c-wisdom-mid: #4F46E5; // indigo-600
|
|
|
|
|
-$c-danger-ant: #FF4D4F; // Ant Design 红
|
|
|
|
|
-$c-pink: #EC4899; // pink-500
|
|
|
|
|
-$c-pink-deep: #DB2777; // pink-600
|
|
|
|
|
-$c-indigo: $accent-indigo; // indigo-500(rgba 渐变用)
|
|
|
|
|
-
|
|
|
|
|
-.container {
|
|
|
|
|
- min-height: 100vh;
|
|
|
|
|
- background: $facade-fill-warm-gray;
|
|
|
|
|
- padding-bottom: calc(160rpx + env(safe-area-inset-bottom));
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/* 登录引导 */
|
|
|
|
|
-.login-prompt {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-direction: column;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- padding: 200rpx 60rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.prompt-icon { font-size: 100rpx; margin-bottom: 30rpx; }
|
|
|
|
|
-.prompt-title { font-size: 40rpx; font-weight: bold; color: $facade-text-strong; margin-bottom: 16rpx; }
|
|
|
|
|
-.prompt-desc { font-size: 26rpx; color: $facade-text-muted; margin-bottom: 20rpx; }
|
|
|
|
|
-.login-btn {
|
|
|
|
|
- background: linear-gradient(135deg, $brand-primary, $brand-primary-light);
|
|
|
|
|
- color: $app-surface;
|
|
|
|
|
- border-radius: 50rpx;
|
|
|
|
|
- font-size: 30rpx;
|
|
|
|
|
- padding: 24rpx 80rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.skip-link {
|
|
|
|
|
- font-size: 26rpx;
|
|
|
|
|
- color: $facade-text-muted;
|
|
|
|
|
- margin-top: 24rpx;
|
|
|
|
|
- text-decoration: underline;
|
|
|
|
|
-}
|
|
|
|
|
-.philosophy-card {
|
|
|
|
|
- margin: 0 40rpx 48rpx;
|
|
|
|
|
- background: linear-gradient(135deg, $facade-brand-tint, rgba($brand-primary, 0.06));
|
|
|
|
|
- border-radius: 20rpx;
|
|
|
|
|
- padding: 28rpx 32rpx;
|
|
|
|
|
- border-left: 6rpx solid $brand-primary;
|
|
|
|
|
-}
|
|
|
|
|
-.philosophy-title {
|
|
|
|
|
- font-size: 28rpx;
|
|
|
|
|
- font-weight: bold;
|
|
|
|
|
- color: $facade-text-strong;
|
|
|
|
|
- display: block;
|
|
|
|
|
- margin-bottom: 8rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.philosophy-desc {
|
|
|
|
|
- font-size: 24rpx;
|
|
|
|
|
- color: $facade-text-faint;
|
|
|
|
|
- line-height: 1.6;
|
|
|
|
|
- display: block;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/* 退出切换 */
|
|
|
|
|
-.exit-switch {
|
|
|
|
|
- margin: 0 30rpx 16rpx;
|
|
|
|
|
- background: linear-gradient(135deg, $brand-primary, $brand-primary-light);
|
|
|
|
|
- color: $app-surface;
|
|
|
|
|
- padding: 16rpx;
|
|
|
|
|
- border-radius: 12rpx;
|
|
|
|
|
- text-align: center;
|
|
|
|
|
- font-size: 26rpx;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/* 身份卡 */
|
|
|
|
|
-.user-card {
|
|
|
|
|
- margin: 24rpx 30rpx 20rpx;
|
|
|
|
|
- background: linear-gradient(135deg, $brand-primary 0%, $brand-primary-light 100%);
|
|
|
|
|
- border-radius: 24rpx;
|
|
|
|
|
- padding: 32rpx 32rpx;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- justify-content: space-between;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- box-shadow: 0 8rpx 32rpx rgba($c-indigo, 0.3);
|
|
|
|
|
-}
|
|
|
|
|
-.user-card-left {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- flex: 1;
|
|
|
|
|
-}
|
|
|
|
|
-.avatar-wrap {
|
|
|
|
|
- width: 100rpx;
|
|
|
|
|
- height: 100rpx;
|
|
|
|
|
- border-radius: 50%;
|
|
|
|
|
- background: rgba($app-surface, 0.2);
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: center;
|
|
|
|
|
- margin-right: 20rpx;
|
|
|
|
|
- overflow: hidden;
|
|
|
|
|
- border: 3rpx solid rgba($app-surface, 0.3);
|
|
|
|
|
-}
|
|
|
|
|
-.avatar-img {
|
|
|
|
|
- width: 100rpx;
|
|
|
|
|
- height: 100rpx;
|
|
|
|
|
- border-radius: 50%;
|
|
|
|
|
-}
|
|
|
|
|
-.avatar-text {
|
|
|
|
|
- font-size: 44rpx;
|
|
|
|
|
- color: $app-surface;
|
|
|
|
|
-}
|
|
|
|
|
-.user-meta { flex: 1; }
|
|
|
|
|
-.user-name-row {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- gap: 12rpx;
|
|
|
|
|
- margin-bottom: 8rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.nickname {
|
|
|
|
|
- font-size: 34rpx;
|
|
|
|
|
- font-weight: bold;
|
|
|
|
|
- color: $app-surface;
|
|
|
|
|
-}
|
|
|
|
|
-.role-tag {
|
|
|
|
|
- font-size: 20rpx;
|
|
|
|
|
- background: rgba($app-surface, 0.2);
|
|
|
|
|
- color: $app-surface;
|
|
|
|
|
- padding: 4rpx 14rpx;
|
|
|
|
|
- border-radius: 20rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.referrer-line {
|
|
|
|
|
- font-size: 24rpx;
|
|
|
|
|
- color: rgba($app-surface, 0.7);
|
|
|
|
|
- margin-bottom: 6rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.user-sub {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- gap: 12rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.level-badge {
|
|
|
|
|
- font-size: 22rpx;
|
|
|
|
|
- padding: 6rpx 20rpx;
|
|
|
|
|
- border-radius: 22rpx;
|
|
|
|
|
- font-weight: bold;
|
|
|
|
|
- border: 2rpx solid rgba($app-surface, 0.35);
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- gap: 6rpx;
|
|
|
|
|
- box-sizing: border-box;
|
|
|
|
|
- height: 40rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.level-badge::before {
|
|
|
|
|
- content: '👑';
|
|
|
|
|
- font-size: 18rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.level-FREE { background: linear-gradient(135deg, $c-text-gray-400, $facade-text-slate); color: $app-surface; }
|
|
|
|
|
-.level-FREE::before { content: ''; }
|
|
|
|
|
-.level-FAMILY { background: linear-gradient(135deg, $dim-wealth, $status-warning-deep); color: $app-surface; }
|
|
|
|
|
-.level-PREMIUM { background: linear-gradient(135deg, $dim-wisdom, $c-wisdom-mid); color: $app-surface; }
|
|
|
|
|
-.level-LIFETIME {
|
|
|
|
|
- background: linear-gradient(135deg, $c-pink, $c-pink-deep);
|
|
|
|
|
- color: $app-surface;
|
|
|
|
|
- box-shadow: 0 4rpx 16rpx rgba($c-pink, 0.45);
|
|
|
|
|
- border-color: rgba($app-surface, 0.55);
|
|
|
|
|
-}
|
|
|
|
|
-.days-left {
|
|
|
|
|
- font-size: 20rpx;
|
|
|
|
|
- color: rgba($app-surface, 0.85);
|
|
|
|
|
-}
|
|
|
|
|
-.upgrade-btn {
|
|
|
|
|
- background: rgba($app-surface, 0.22);
|
|
|
|
|
- border: 2rpx solid rgba($app-surface, 0.6);
|
|
|
|
|
- border-radius: 20rpx;
|
|
|
|
|
- padding: 6rpx 20rpx;
|
|
|
|
|
- flex-shrink: 0;
|
|
|
|
|
- box-sizing: border-box;
|
|
|
|
|
- height: 40rpx;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
-}
|
|
|
|
|
-.upgrade-btn-text {
|
|
|
|
|
- font-size: 20rpx;
|
|
|
|
|
- font-weight: bold;
|
|
|
|
|
- color: $app-surface;
|
|
|
|
|
- line-height: 1;
|
|
|
|
|
-}
|
|
|
|
|
-.user-card-right {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-direction: column;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- gap: 6rpx;
|
|
|
|
|
- background: rgba($app-surface, 0.15);
|
|
|
|
|
- padding: 12rpx 24rpx;
|
|
|
|
|
- border-radius: 14rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.points-num {
|
|
|
|
|
- font-size: 36rpx;
|
|
|
|
|
- font-weight: bold;
|
|
|
|
|
- color: $app-surface;
|
|
|
|
|
-}
|
|
|
|
|
-.points-label {
|
|
|
|
|
- font-size: 20rpx;
|
|
|
|
|
- color: rgba($app-surface, 0.7);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/* 家庭成员速览 */
|
|
|
|
|
-.family-strip {
|
|
|
|
|
- margin: 0 30rpx 20rpx;
|
|
|
|
|
- background: $app-surface;
|
|
|
|
|
- border-radius: 20rpx;
|
|
|
|
|
- padding: 20rpx 24rpx 16rpx;
|
|
|
|
|
- box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
|
|
|
|
|
-}
|
|
|
|
|
-.family-strip-header {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- justify-content: space-between;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- margin-bottom: 14rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.family-strip-title {
|
|
|
|
|
- font-size: 26rpx;
|
|
|
|
|
- font-weight: bold;
|
|
|
|
|
- color: $facade-text-strong;
|
|
|
|
|
-}
|
|
|
|
|
-.family-strip-count {
|
|
|
|
|
- font-size: 22rpx;
|
|
|
|
|
- color: $facade-text-muted;
|
|
|
|
|
-}
|
|
|
|
|
-.family-avatars {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-direction: row;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- gap: 16rpx;
|
|
|
|
|
- overflow-x: auto;
|
|
|
|
|
- padding-bottom: 4rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.family-avatar-item {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-direction: column;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- gap: 6rpx;
|
|
|
|
|
- flex-shrink: 0;
|
|
|
|
|
-}
|
|
|
|
|
-.family-avatar-wrap {
|
|
|
|
|
- width: 72rpx;
|
|
|
|
|
- height: 72rpx;
|
|
|
|
|
- border-radius: 50%;
|
|
|
|
|
- background: $facade-fill;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: center;
|
|
|
|
|
- overflow: hidden;
|
|
|
|
|
- position: relative;
|
|
|
|
|
-}
|
|
|
|
|
-.family-avatar-img {
|
|
|
|
|
- width: 72rpx;
|
|
|
|
|
- height: 72rpx;
|
|
|
|
|
- border-radius: 50%;
|
|
|
|
|
-}
|
|
|
|
|
-.family-avatar-text {
|
|
|
|
|
- font-size: 28rpx;
|
|
|
|
|
- color: $facade-text-body;
|
|
|
|
|
-}
|
|
|
|
|
-.family-avatar-wrap.family-avatar-add {
|
|
|
|
|
- background: $facade-fill-warm-gray;
|
|
|
|
|
- border: 2rpx dashed $facade-text-ghost;
|
|
|
|
|
-}
|
|
|
|
|
-.family-avatar-plus {
|
|
|
|
|
- font-size: 36rpx;
|
|
|
|
|
- color: $facade-text-muted;
|
|
|
|
|
-}
|
|
|
|
|
-.family-avatar-tag {
|
|
|
|
|
- position: absolute;
|
|
|
|
|
- bottom: -4rpx;
|
|
|
|
|
- right: -4rpx;
|
|
|
|
|
- font-size: 14rpx;
|
|
|
|
|
- padding: 2rpx 6rpx;
|
|
|
|
|
- border-radius: 8rpx;
|
|
|
|
|
- color: $app-surface;
|
|
|
|
|
-}
|
|
|
|
|
-.family-avatar-tag.tag-parent { background: $dim-wisdom; }
|
|
|
|
|
-.family-avatar-tag.tag-child { background: $brand-primary; }
|
|
|
|
|
-.family-avatar-tag.tag-teacher { background: $dim-action; }
|
|
|
|
|
-.family-avatar-tag.tag-member { background: $c-text-gray-400; }
|
|
|
|
|
-.family-avatar-name {
|
|
|
|
|
- font-size: 20rpx;
|
|
|
|
|
- color: $facade-text-body;
|
|
|
|
|
- white-space: nowrap;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/* 个人成就 */
|
|
|
|
|
-.achievement-section {
|
|
|
|
|
- margin: 0 30rpx 20rpx;
|
|
|
|
|
- background: $app-surface;
|
|
|
|
|
- border-radius: 20rpx;
|
|
|
|
|
- padding: 24rpx;
|
|
|
|
|
- box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
|
|
|
|
|
-}
|
|
|
|
|
-.section-header-row {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- justify-content: space-between;
|
|
|
|
|
- align-items: baseline;
|
|
|
|
|
- margin-bottom: 16rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.section-title {
|
|
|
|
|
- font-size: 28rpx;
|
|
|
|
|
- font-weight: bold;
|
|
|
|
|
- color: $facade-text-strong;
|
|
|
|
|
-}
|
|
|
|
|
-.section-sub {
|
|
|
|
|
- font-size: 22rpx;
|
|
|
|
|
- color: $facade-text-muted;
|
|
|
|
|
-}
|
|
|
|
|
-.achievement-grid {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-wrap: wrap;
|
|
|
|
|
- gap: 0;
|
|
|
|
|
-}
|
|
|
|
|
-.ach-item {
|
|
|
|
|
- width: 25%;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-direction: column;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- gap: 6rpx;
|
|
|
|
|
- padding: 10rpx 0;
|
|
|
|
|
-}
|
|
|
|
|
-.ach-icon-wrap {
|
|
|
|
|
- width: 64rpx;
|
|
|
|
|
- height: 64rpx;
|
|
|
|
|
- border-radius: 50%;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: center;
|
|
|
|
|
- margin-bottom: 4rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.ach-icon {
|
|
|
|
|
- font-size: 32rpx;
|
|
|
|
|
- line-height: 1;
|
|
|
|
|
-}
|
|
|
|
|
-.ach-num {
|
|
|
|
|
- font-size: 28rpx;
|
|
|
|
|
- font-weight: bold;
|
|
|
|
|
- color: $facade-text-strong;
|
|
|
|
|
- line-height: 1;
|
|
|
|
|
-}
|
|
|
|
|
-.ach-label {
|
|
|
|
|
- font-size: 20rpx;
|
|
|
|
|
- color: $facade-text-muted;
|
|
|
|
|
- line-height: 1;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/* 我的订单(电商风格) */
|
|
|
|
|
-.order-section {
|
|
|
|
|
- margin: 0 30rpx 20rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.order-section-header {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: space-between;
|
|
|
|
|
- margin-bottom: 14rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.order-all-link {
|
|
|
|
|
- font-size: 24rpx;
|
|
|
|
|
- color: $facade-text-muted;
|
|
|
|
|
-}
|
|
|
|
|
-.order-card {
|
|
|
|
|
- background: $app-surface;
|
|
|
|
|
- border-radius: 20rpx;
|
|
|
|
|
- padding: 24rpx 12rpx;
|
|
|
|
|
- box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
|
|
|
|
|
-}
|
|
|
|
|
-.order-grid {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-wrap: wrap;
|
|
|
|
|
-}
|
|
|
|
|
-.order-grid-item {
|
|
|
|
|
- width: 20%;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-direction: column;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- gap: 10rpx;
|
|
|
|
|
- padding: 8rpx 0;
|
|
|
|
|
-}
|
|
|
|
|
-.order-icon-wrap {
|
|
|
|
|
- position: relative;
|
|
|
|
|
- width: 64rpx;
|
|
|
|
|
- height: 64rpx;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: center;
|
|
|
|
|
-}
|
|
|
|
|
-.order-icon {
|
|
|
|
|
- font-size: 40rpx;
|
|
|
|
|
- line-height: 1;
|
|
|
|
|
-}
|
|
|
|
|
-.order-badge {
|
|
|
|
|
- position: absolute;
|
|
|
|
|
- top: -8rpx;
|
|
|
|
|
- right: -14rpx;
|
|
|
|
|
- min-width: 30rpx;
|
|
|
|
|
- height: 30rpx;
|
|
|
|
|
- line-height: 30rpx;
|
|
|
|
|
- border-radius: 15rpx;
|
|
|
|
|
- background: $c-danger-ant;
|
|
|
|
|
- color: $app-surface;
|
|
|
|
|
- font-size: 18rpx;
|
|
|
|
|
- text-align: center;
|
|
|
|
|
- padding: 0 6rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.order-grid-text {
|
|
|
|
|
- font-size: 22rpx;
|
|
|
|
|
- color: $facade-text-body;
|
|
|
|
|
- line-height: 1;
|
|
|
|
|
-}
|
|
|
|
|
-.order-extra-row {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- margin-top: 20rpx;
|
|
|
|
|
- padding-top: 20rpx;
|
|
|
|
|
- border-top: 1rpx solid $c-fill-2b;
|
|
|
|
|
-}
|
|
|
|
|
-.order-extra-item {
|
|
|
|
|
- flex: 1;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: center;
|
|
|
|
|
- gap: 8rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.order-extra-icon {
|
|
|
|
|
- font-size: 28rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.order-extra-text {
|
|
|
|
|
- font-size: 24rpx;
|
|
|
|
|
- color: $facade-text-strong;
|
|
|
|
|
-}
|
|
|
|
|
-.order-extra-badge {
|
|
|
|
|
- min-width: 30rpx;
|
|
|
|
|
- height: 30rpx;
|
|
|
|
|
- line-height: 30rpx;
|
|
|
|
|
- border-radius: 15rpx;
|
|
|
|
|
- background: $brand-primary;
|
|
|
|
|
- color: $app-surface;
|
|
|
|
|
- font-size: 18rpx;
|
|
|
|
|
- text-align: center;
|
|
|
|
|
- padding: 0 6rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.order-extra-divider {
|
|
|
|
|
- width: 1rpx;
|
|
|
|
|
- height: 30rpx;
|
|
|
|
|
- background: $c-fill-2b;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/* 规划师服务中心入口 */
|
|
|
|
|
-.service-center-section {
|
|
|
|
|
- margin: 0 30rpx 20rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.service-center-card {
|
|
|
|
|
- background: linear-gradient(135deg, $brand-primary 0%, $brand-primary-light 100%);
|
|
|
|
|
- border-radius: 20rpx;
|
|
|
|
|
- padding: 24rpx;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- gap: 20rpx;
|
|
|
|
|
- box-shadow: 0 4rpx 16rpx rgba($brand-primary, 0.25);
|
|
|
|
|
-}
|
|
|
|
|
-.service-center-card.disabled-card {
|
|
|
|
|
- background: linear-gradient(135deg, $uni-border-color-light, $app-text-subtle);
|
|
|
|
|
- box-shadow: none;
|
|
|
|
|
- opacity: 0.8;
|
|
|
|
|
-}
|
|
|
|
|
-.service-center-icon {
|
|
|
|
|
- font-size: 48rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.service-center-info {
|
|
|
|
|
- flex: 1;
|
|
|
|
|
-}
|
|
|
|
|
-.service-center-title {
|
|
|
|
|
- font-size: 30rpx;
|
|
|
|
|
- font-weight: bold;
|
|
|
|
|
- color: $app-surface;
|
|
|
|
|
- display: block;
|
|
|
|
|
-}
|
|
|
|
|
-.service-center-desc {
|
|
|
|
|
- font-size: 22rpx;
|
|
|
|
|
- color: rgba($app-surface, 0.8);
|
|
|
|
|
- margin-top: 6rpx;
|
|
|
|
|
- display: block;
|
|
|
|
|
-}
|
|
|
|
|
-.service-center-card.disabled-card .service-center-title {
|
|
|
|
|
- color: $app-surface;
|
|
|
|
|
-}
|
|
|
|
|
-.service-center-card.disabled-card .service-center-desc {
|
|
|
|
|
- color: rgba($app-surface, 0.9);
|
|
|
|
|
-}
|
|
|
|
|
-.service-center-arrow {
|
|
|
|
|
- font-size: 40rpx;
|
|
|
|
|
- color: $app-surface;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/* 设置 + 退出 */
|
|
|
|
|
-.bottom-menu-section {
|
|
|
|
|
- margin: 0 30rpx;
|
|
|
|
|
- padding-bottom: 20rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.menu-row {
|
|
|
|
|
- width: 100%;
|
|
|
|
|
- background: $app-surface;
|
|
|
|
|
- border-radius: 16rpx;
|
|
|
|
|
- padding: 24rpx;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- gap: 14rpx;
|
|
|
|
|
- box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.04);
|
|
|
|
|
- margin-bottom: 20rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.menu-row-icon {
|
|
|
|
|
- font-size: 30rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.menu-row-text {
|
|
|
|
|
- flex: 1;
|
|
|
|
|
- font-size: 26rpx;
|
|
|
|
|
- color: $facade-text-strong;
|
|
|
|
|
-}
|
|
|
|
|
-.menu-row-arrow {
|
|
|
|
|
- font-size: 28rpx;
|
|
|
|
|
- color: $facade-text-ghost;
|
|
|
|
|
-}
|
|
|
|
|
-.logout-btn {
|
|
|
|
|
- width: 100%;
|
|
|
|
|
- background: $app-surface;
|
|
|
|
|
- border-radius: 16rpx;
|
|
|
|
|
- padding: 24rpx;
|
|
|
|
|
- text-align: center;
|
|
|
|
|
- box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.04);
|
|
|
|
|
-}
|
|
|
|
|
-.logout-text {
|
|
|
|
|
- font-size: 26rpx;
|
|
|
|
|
- color: $c-danger-ant;
|
|
|
|
|
- font-weight: 500;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/* 分享推广码浮动按钮 */
|
|
|
|
|
-.share-qr-float {
|
|
|
|
|
- position: fixed;
|
|
|
|
|
- top: 180rpx;
|
|
|
|
|
- right: 30rpx;
|
|
|
|
|
- width: 80rpx;
|
|
|
|
|
- height: 80rpx;
|
|
|
|
|
- border-radius: 50%;
|
|
|
|
|
- background: $app-surface;
|
|
|
|
|
- box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.15);
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: center;
|
|
|
|
|
- z-index: 99;
|
|
|
|
|
-}
|
|
|
|
|
-/* 二维码图标(纯 CSS 绘制,不依赖 emoji 字体) */
|
|
|
|
|
-.qr-glyph {
|
|
|
|
|
- position: relative;
|
|
|
|
|
- width: 46rpx;
|
|
|
|
|
- height: 46rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.qr-eye {
|
|
|
|
|
- position: absolute;
|
|
|
|
|
- width: 16rpx;
|
|
|
|
|
- height: 16rpx;
|
|
|
|
|
- border: 4rpx solid $uni-text-color;
|
|
|
|
|
- border-radius: 3rpx;
|
|
|
|
|
- box-sizing: border-box;
|
|
|
|
|
-}
|
|
|
|
|
-.qr-eye-tl { left: 0; top: 0; }
|
|
|
|
|
-.qr-eye-tr { right: 0; top: 0; }
|
|
|
|
|
-.qr-eye-bl { left: 0; bottom: 0; }
|
|
|
|
|
-.qr-mid {
|
|
|
|
|
- position: absolute;
|
|
|
|
|
- left: 20rpx;
|
|
|
|
|
- top: 20rpx;
|
|
|
|
|
- width: 7rpx;
|
|
|
|
|
- height: 7rpx;
|
|
|
|
|
- background: $uni-text-color;
|
|
|
|
|
- border-radius: 1rpx;
|
|
|
|
|
-}
|
|
|
|
|
-.qr-cluster {
|
|
|
|
|
- position: absolute;
|
|
|
|
|
- right: 0;
|
|
|
|
|
- bottom: 0;
|
|
|
|
|
- width: 18rpx;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-wrap: wrap;
|
|
|
|
|
-}
|
|
|
|
|
-.qr-pip {
|
|
|
|
|
- width: 7rpx;
|
|
|
|
|
- height: 7rpx;
|
|
|
|
|
- margin: 1rpx;
|
|
|
|
|
- background: $uni-text-color;
|
|
|
|
|
- border-radius: 1rpx;
|
|
|
|
|
-}
|
|
|
|
|
-</style>
|
|
|
|
|
|
|
+<template>
|
|
|
|
|
+ <view class="container">
|
|
|
|
|
+ <!-- 登录浮层(全局组件,页面内弹出底部登录) -->
|
|
|
|
|
+ <login-sheet ref="loginSheet" @success="onSheetLoginSuccess" />
|
|
|
|
|
+ <!-- 未登录 -->
|
|
|
|
|
+ <view v-if="!isLoggedIn">
|
|
|
|
|
+ <view class="login-prompt">
|
|
|
|
|
+ <text class="prompt-icon">👤</text>
|
|
|
|
|
+ <text class="prompt-title">登录浠艾福</text>
|
|
|
|
|
+ <text class="prompt-desc">登录后查看个人资料、积分记录、能量数据与家庭管理</text>
|
|
|
|
|
+ <button class="login-btn" @click="goLogin">登录 / 注册</button>
|
|
|
|
|
+ <text class="skip-link" @click="goSkip">暂不登录,先逛逛 ›</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <view class="philosophy-card">
|
|
|
|
|
+ <text class="philosophy-title">照顾家庭,从照顾好自己开始</text>
|
|
|
|
|
+ <text class="philosophy-desc">你的状态,就是家的状态。先安顿好自己,才有余力温暖家人。</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 已登录 -->
|
|
|
|
|
+ <template v-else>
|
|
|
|
|
+ <!-- 退出切换 -->
|
|
|
|
|
+ <view class="exit-switch" v-if="isSwitchedChild" @click="onExitSwitch">
|
|
|
|
|
+ <text>🔄 退出切换</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 身份卡 -->
|
|
|
|
|
+ <view class="user-card">
|
|
|
|
|
+ <view class="user-card-left">
|
|
|
|
|
+ <view class="avatar-wrap" @click="goToEditInfo">
|
|
|
|
|
+ <image v-if="userInfo.avatar" class="avatar-img" :src="userInfo.avatar" mode="aspectFill" />
|
|
|
|
|
+ <text v-else class="avatar-text">{{ nickname.charAt(0) || '👤' }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="user-meta">
|
|
|
|
|
+ <view class="user-name-row">
|
|
|
|
|
+ <text class="nickname">{{ nickname || '未设置昵称' }}</text>
|
|
|
|
|
+ <text v-if="roleLabel" class="role-tag">{{ roleLabel }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="user-sub" v-if="memberLevel">
|
|
|
|
|
+ <view class="level-badge" :class="'level-' + memberLevel">{{ memberLevelText }}</view>
|
|
|
|
|
+ <text v-if="isLifetime" class="days-left">永久有效</text>
|
|
|
|
|
+ <text v-else-if="membershipDays > 0 && membershipDays <= 30" class="days-left">剩余 {{ membershipDays }} 天</text>
|
|
|
|
|
+ <view v-if="hasUpgradeSpace" class="upgrade-btn" @click="goUpgrade">
|
|
|
|
|
+ <text class="upgrade-btn-text">{{ upgradeBtnText }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="user-sub" v-else>
|
|
|
|
|
+ <text class="days-left">开通会员解锁更多权益</text>
|
|
|
|
|
+ <view class="upgrade-btn" @click="goUpgrade">
|
|
|
|
|
+ <text class="upgrade-btn-text">立即开通</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="user-card-right" @click="goToPoints">
|
|
|
|
|
+ <text class="points-num">{{ totalPoints }}</text>
|
|
|
|
|
+ <text class="points-label">积分</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 我的分享海报浮动按钮(纯 CSS 二维码图标,避免 emoji 渲染成计算器) -->
|
|
|
|
|
+ <view class="share-qr-float" @click="showShareQr">
|
|
|
|
|
+ <view class="qr-glyph">
|
|
|
|
|
+ <view class="qr-eye qr-eye-tl"></view>
|
|
|
|
|
+ <view class="qr-eye qr-eye-tr"></view>
|
|
|
|
|
+ <view class="qr-eye qr-eye-bl"></view>
|
|
|
|
|
+ <view class="qr-mid"></view>
|
|
|
|
|
+ <view class="qr-cluster">
|
|
|
|
|
+ <view class="qr-pip"></view>
|
|
|
|
|
+ <view class="qr-pip"></view>
|
|
|
|
|
+ <view class="qr-pip"></view>
|
|
|
|
|
+ <view class="qr-pip"></view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 家族成员速览 -->
|
|
|
|
|
+ <view class="family-strip" v-if="familyMembers.length > 0">
|
|
|
|
|
+ <view class="family-strip-header">
|
|
|
|
|
+ <text class="family-strip-title">我的家庭</text>
|
|
|
|
|
+ <text class="family-strip-count">{{ familyMembers.length }} 位成员</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="family-avatars">
|
|
|
|
|
+ <view
|
|
|
|
|
+ v-for="member in familyMembers"
|
|
|
|
|
+ :key="member.id"
|
|
|
|
|
+ class="family-avatar-item"
|
|
|
|
|
+ @click="goMemberDetail(member)"
|
|
|
|
|
+ >
|
|
|
|
|
+ <view class="family-avatar-wrap">
|
|
|
|
|
+ <image v-if="member.avatar" class="family-avatar-img" :src="member.avatar" mode="aspectFill" />
|
|
|
|
|
+ <text v-else class="family-avatar-text">{{ member.nickname ? member.nickname.charAt(0) : '?' }}</text>
|
|
|
|
|
+ <view class="family-avatar-tag" :class="'tag-' + (member.effectiveRole || member.source || 'member')">{{ member.roleLabel || memberRoleLabel(member.effectiveRole) }}</view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <text class="family-avatar-name">{{ member.nickname || member.realName || '成员' }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="family-avatar-item" @click="goToFamily">
|
|
|
|
|
+ <view class="family-avatar-wrap family-avatar-add">
|
|
|
|
|
+ <text class="family-avatar-plus">+</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <text class="family-avatar-name">添加</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 个人成就统计 -->
|
|
|
|
|
+ <view class="achievement-section">
|
|
|
|
|
+ <view class="section-header-row">
|
|
|
|
|
+ <text class="section-title">🏆 我的成就</text>
|
|
|
|
|
+ <text class="section-sub">持续成长,看见改变</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="achievement-grid">
|
|
|
|
|
+ <view class="ach-item" @click="goToPoints">
|
|
|
|
|
+ <view class="ach-icon-wrap" style="background:rgba(249,115,22,0.12)">
|
|
|
|
|
+ <text class="ach-icon">💰</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <text class="ach-num">{{ totalPoints }}</text>
|
|
|
|
|
+ <text class="ach-label">累计积分</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="ach-item" @click="goToStreak">
|
|
|
|
|
+ <view class="ach-icon-wrap" style="background:rgba(255,140,66,0.12)">
|
|
|
|
|
+ <text class="ach-icon">🔥</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <text class="ach-num">{{ streakDays }}</text>
|
|
|
|
|
+ <text class="ach-label">连续打卡</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="ach-item" @click="goToTasks">
|
|
|
|
|
+ <view class="ach-icon-wrap" style="background:rgba(99,102,241,0.12)">
|
|
|
|
|
+ <text class="ach-icon">✅</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <text class="ach-num">{{ completedTasks }}</text>
|
|
|
|
|
+ <text class="ach-label">完成任务</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="ach-item" @click="goToWishes">
|
|
|
|
|
+ <view class="ach-icon-wrap" style="background:rgba(245,158,11,0.12)">
|
|
|
|
|
+ <text class="ach-icon">🎁</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <text class="ach-num">{{ rewardsEarned }}</text>
|
|
|
|
|
+ <text class="ach-label">获得奖励</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="ach-item" @click="goToBadges">
|
|
|
|
|
+ <view class="ach-icon-wrap" style="background:rgba(236,72,153,0.12)">
|
|
|
|
|
+ <text class="ach-icon">🏅</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <text class="ach-num">{{ badgesCollected }}</text>
|
|
|
|
|
+ <text class="ach-label">收集勋章</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="ach-item" @click="goToGrowth">
|
|
|
|
|
+ <view class="ach-icon-wrap" style="background:rgba(16,185,129,0.12)">
|
|
|
|
|
+ <text class="ach-icon">📈</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <text class="ach-num">{{ growthRecords }}</text>
|
|
|
|
|
+ <text class="ach-label">成长记录</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="ach-item" @click="goToChallenges">
|
|
|
|
|
+ <view class="ach-icon-wrap" style="background:rgba(255,107,157,0.12)">
|
|
|
|
|
+ <text class="ach-icon">🎯</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <text class="ach-num">{{ challengesJoined }}</text>
|
|
|
|
|
+ <text class="ach-label">参加挑战</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="ach-item" @click="goToHealthReports">
|
|
|
|
|
+ <view class="ach-icon-wrap" style="background:rgba(14,165,233,0.12)">
|
|
|
|
|
+ <text class="ach-icon">📋</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <text class="ach-num">{{ healthReports }}</text>
|
|
|
|
|
+ <text class="ach-label">上传报告</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="ach-item" @click="goToWealthCenter">
|
|
|
|
|
+ <view class="ach-icon-wrap" style="background:rgba(245,158,11,0.12)">
|
|
|
|
|
+ <text class="ach-icon">💰</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <text class="ach-num">{{ referralEarnings }}</text>
|
|
|
|
|
+ <text class="ach-label">推广收益</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="ach-item" @click="goTeamTree">
|
|
|
|
|
+ <view class="ach-icon-wrap" style="background:rgba(16,185,129,0.12)">
|
|
|
|
|
+ <text class="ach-icon">🌳</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <text class="ach-num">{{ teamMembers }}</text>
|
|
|
|
|
+ <text class="ach-label">我的团队</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 我的订单(电商风格) -->
|
|
|
|
|
+ <view class="order-section">
|
|
|
|
|
+ <view class="order-section-header">
|
|
|
|
|
+ <text class="section-title">🛒 我的订单</text>
|
|
|
|
|
+ <text class="order-all-link" @click="goToOrders">全部订单 ›</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="order-card">
|
|
|
|
|
+ <view class="order-grid">
|
|
|
|
|
+ <view class="order-grid-item" @click="goToOrderStatus('pending')">
|
|
|
|
|
+ <view class="order-icon-wrap">
|
|
|
|
|
+ <text class="order-icon">💳</text>
|
|
|
|
|
+ <text class="order-badge" v-if="orderPending > 0">{{ orderPending }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <text class="order-grid-text">待付款</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="order-grid-item" @click="goToOrderStatus('paid')">
|
|
|
|
|
+ <view class="order-icon-wrap">
|
|
|
|
|
+ <text class="order-icon">📦</text>
|
|
|
|
|
+ <text class="order-badge" v-if="orderUnpaid > 0">{{ orderUnpaid }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <text class="order-grid-text">待发货</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="order-grid-item" @click="goToOrderStatus('shipped')">
|
|
|
|
|
+ <view class="order-icon-wrap">
|
|
|
|
|
+ <text class="order-icon">🚚</text>
|
|
|
|
|
+ <text class="order-badge" v-if="orderShipped > 0">{{ orderShipped }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <text class="order-grid-text">待收货</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="order-grid-item" @click="goToOrderStatus('refunding')">
|
|
|
|
|
+ <view class="order-icon-wrap">
|
|
|
|
|
+ <text class="order-icon">🔧</text>
|
|
|
|
|
+ <text class="order-badge" v-if="orderAfterSale > 0">{{ orderAfterSale }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <text class="order-grid-text">售后</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="order-grid-item" @click="goToOrders">
|
|
|
|
|
+ <view class="order-icon-wrap">
|
|
|
|
|
+ <text class="order-icon">📋</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <text class="order-grid-text">全部</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="order-extra-row">
|
|
|
|
|
+ <view class="order-extra-item" @click="goToCart">
|
|
|
|
|
+ <text class="order-extra-icon">🛒</text>
|
|
|
|
|
+ <text class="order-extra-text">购物车</text>
|
|
|
|
|
+ <text class="order-extra-badge" v-if="cartCount > 0">{{ cartCount > 99 ? '99+' : cartCount }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="order-extra-divider"></view>
|
|
|
|
|
+ <view class="order-extra-item" @click="goToAddress">
|
|
|
|
|
+ <text class="order-extra-icon">📍</text>
|
|
|
|
|
+ <text class="order-extra-text">收货地址</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 规划师服务中心入口(仅规划师可见) -->
|
|
|
|
|
+ <view class="service-center-section" v-if="role === 'teacher' && teacherStatus === 'approved'">
|
|
|
|
|
+ <view class="service-center-card" @click="goToServiceCenter">
|
|
|
|
|
+ <view class="service-center-icon">👨🏫</view>
|
|
|
|
|
+ <view class="service-center-info">
|
|
|
|
|
+ <text class="service-center-title">服务中心</text>
|
|
|
|
|
+ <text class="service-center-desc">待处理任务:{{ pendingTasks }} 条 · 消息:{{ unreadMessages }} 条</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <text class="service-center-arrow">›</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 规划师待审核提示 -->
|
|
|
|
|
+ <view class="service-center-section" v-if="role === 'teacher' && teacherStatus !== 'approved'">
|
|
|
|
|
+ <view class="service-center-card disabled-card">
|
|
|
|
|
+ <view class="service-center-icon">👨🏫</view>
|
|
|
|
|
+ <view class="service-center-info">
|
|
|
|
|
+ <text class="service-center-title">服务中心</text>
|
|
|
|
|
+ <text class="service-center-desc">{{ teacherStatus === 'pending' ? '申请审核中,暂未开放' : teacherStatus === 'rejected' ? '申请未通过,请联系管理员' : '规划师服务' }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 设置 + 退出 -->
|
|
|
|
|
+ <view class="bottom-menu-section">
|
|
|
|
|
+ <view class="menu-row" @click="goToMessageHistory">
|
|
|
|
|
+ <text class="menu-row-icon">📨</text>
|
|
|
|
|
+ <text class="menu-row-text">历史消息</text>
|
|
|
|
|
+ <text class="menu-row-arrow">›</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="menu-row" @click="goToSettings">
|
|
|
|
|
+ <text class="menu-row-icon">⚙️</text>
|
|
|
|
|
+ <text class="menu-row-text">设置</text>
|
|
|
|
|
+ <text class="menu-row-arrow">›</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="logout-btn" @click="logout">
|
|
|
|
|
+ <text class="logout-text">退出登录</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ </template>
|
|
|
|
|
+ <!-- 我的分享海报:复用文章/活动/商品同一套 ContentSharePoster 组件(canvas 绘制 + 保存到相册) -->
|
|
|
|
|
+ <ContentSharePoster
|
|
|
|
|
+ :show="showSharePoster"
|
|
|
|
|
+ :title="shareHeadline"
|
|
|
|
|
+ :energyData="posterEnergyData"
|
|
|
|
|
+ :brandFallback="true"
|
|
|
|
|
+ :qrCodeBase64="posterQrCode || ''"
|
|
|
|
|
+ typeLabel="邀友加入"
|
|
|
|
|
+ :summary="posterSummary"
|
|
|
|
|
+ ctaText="长按识别二维码 · 加入浠艾福"
|
|
|
|
|
+ :inviteText="posterInviteText"
|
|
|
|
|
+ @close="closeSharePoster"
|
|
|
|
|
+ />
|
|
|
|
|
+ <FloatingAvatar />
|
|
|
|
|
+ </view>
|
|
|
|
|
+</template>
|
|
|
|
|
+
|
|
|
|
|
+<script>
|
|
|
|
|
+import FloatingAvatar from '../../components/AIFloatingAvatar.vue'
|
|
|
|
|
+import ContentSharePoster from '../../components/ContentSharePoster.vue'
|
|
|
|
|
+import {
|
|
|
|
|
+ getChildren, getFamilyMemberList,
|
|
|
|
|
+ getTodayTasksByCategory, getChallengeList, getMyMembership,
|
|
|
|
|
+ verifyPassword, getPointsBalance, getUserInfo,
|
|
|
|
|
+ cartCount, getOrderCounts, getReferralSummary,
|
|
|
|
|
+ getProfileStats, getMyReferrer, getTeamConsumption,
|
|
|
|
|
+ getTeacherInfo, getTeacherPendingTasks, getTeacherUnreadCount,
|
|
|
|
|
+ getReferralCode, extractReferralCode, getShareQrCode, getEnergySandbox,
|
|
|
|
|
+ vendorStatus
|
|
|
|
|
+} from '../../utils/api.js'
|
|
|
|
|
+
|
|
|
|
|
+export default {
|
|
|
|
|
+ components: { FloatingAvatar, ContentSharePoster },
|
|
|
|
|
+ data() {
|
|
|
|
|
+ return {
|
|
|
|
|
+ nickname: '',
|
|
|
|
|
+ role: 'parent',
|
|
|
|
|
+ isSwitchedChild: false,
|
|
|
|
|
+ userInfo: {},
|
|
|
|
|
+ memberLevel: null,
|
|
|
|
|
+ membershipDays: 0,
|
|
|
|
|
+ totalPoints: 0,
|
|
|
|
|
+ referralEarnings: 0,
|
|
|
|
|
+ teamMembers: 0,
|
|
|
|
|
+ // 个人成就
|
|
|
|
|
+ completedTasks: 0,
|
|
|
|
|
+ streakDays: 0,
|
|
|
|
|
+ rewardsEarned: 0,
|
|
|
|
|
+ badgesCollected: 0,
|
|
|
|
|
+ growthRecords: 0,
|
|
|
|
|
+ challengesJoined: 0,
|
|
|
|
|
+ healthReports: 0,
|
|
|
|
|
+ // 家庭
|
|
|
|
|
+ familyMembers: [],
|
|
|
|
|
+ // 商城
|
|
|
|
|
+ orderPending: 0,
|
|
|
|
|
+ orderUnpaid: 0,
|
|
|
|
|
+ orderShipped: 0,
|
|
|
|
|
+ orderAfterSale: 0,
|
|
|
|
|
+ cartCount: 0,
|
|
|
|
|
+ // 待办
|
|
|
|
|
+ pendingTasks: 0,
|
|
|
|
|
+ unreadMessages: 0,
|
|
|
|
|
+ teacherStatus: 'pending',
|
|
|
|
|
+ isVendor: false,
|
|
|
|
|
+ profileProgress: 0,
|
|
|
|
|
+ myReferrer: null,
|
|
|
|
|
+ // 分享推广码
|
|
|
|
|
+ showSharePoster: false,
|
|
|
|
|
+ referralCode: '',
|
|
|
|
|
+ posterQrCode: '',
|
|
|
|
|
+ posterEnergyData: null,
|
|
|
|
|
+ referralCodeLoaded: false,
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ computed: {
|
|
|
|
|
+ isLoggedIn() {
|
|
|
|
|
+ return !!uni.getStorageSync('token')
|
|
|
|
|
+ },
|
|
|
|
|
+ roleLabel() {
|
|
|
|
|
+ var map = { parent: '', child: '成员', teacher: '规划师' }
|
|
|
|
|
+ return map[this.role] || '用户'
|
|
|
|
|
+ },
|
|
|
|
|
+ memberLevelText() {
|
|
|
|
|
+ var map = { FREE: '免费用户', BASIC: '体验会员', FAMILY: '家庭会员', PREMIUM: '高级会员', LIFETIME: '终身会员' }
|
|
|
|
|
+ return map[this.memberLevel] || this.memberLevel || ''
|
|
|
|
|
+ },
|
|
|
|
|
+ isLifetime() {
|
|
|
|
|
+ return this.memberLevel === 'LIFETIME'
|
|
|
|
|
+ },
|
|
|
|
|
+ hasUpgradeSpace() {
|
|
|
|
|
+ return this.memberLevel === 'FREE' || this.memberLevel === 'FAMILY'
|
|
|
|
|
+ },
|
|
|
|
|
+ upgradeBtnText() {
|
|
|
|
|
+ return this.hasUpgradeSpace ? '升级' : '立即开通'
|
|
|
|
|
+ },
|
|
|
|
|
+ // 分享海报标题:XXX邀请你关注家庭健康(昵称过长自动截断)
|
|
|
|
|
+ shareHeadline() {
|
|
|
|
|
+ var name = (this.nickname || '').trim()
|
|
|
|
|
+ if (!name) return '邀请你关注家庭健康'
|
|
|
|
|
+ if (name.length > 8) name = name.substring(0, 8) + '…'
|
|
|
|
|
+ return name + '邀请你关注家庭健康'
|
|
|
|
|
+ },
|
|
|
|
|
+ posterSummary() {
|
|
|
|
|
+ return '身体 · 心理 · 智慧 · 行动 · 财富,一家专注全家健康的成长平台'
|
|
|
|
|
+ },
|
|
|
|
|
+ posterInviteText() {
|
|
|
|
|
+ var name = (this.nickname || '').trim()
|
|
|
|
|
+ if (!name) return '扫码加入浠艾福,一起照顾好这个家'
|
|
|
|
|
+ if (name.length > 8) name = name.substring(0, 8) + '…'
|
|
|
|
|
+ return '「' + name + '」邀请你加入浠艾福'
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ onShow() {
|
|
|
|
|
+ if (!this.isLoggedIn) {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ var role = uni.getStorageSync('currentRole') || uni.getStorageSync('role') || 'parent'
|
|
|
|
|
+ this.role = role
|
|
|
|
|
+ this.isSwitchedChild = uni.getStorageSync('isSwitchedChild') || false
|
|
|
|
|
+ this.nickname = (uni.getStorageSync('userInfo') || {}).nickname || ''
|
|
|
|
|
+
|
|
|
|
|
+ // 服务商身份判断(成就区「我的团队」入口按此分发)
|
|
|
|
|
+ this.checkVendorStatus()
|
|
|
|
|
+
|
|
|
|
|
+ // 规划师加载额外数据
|
|
|
|
|
+ if (role === 'teacher') {
|
|
|
|
|
+ this.loadTeacherStatus()
|
|
|
|
|
+ this.loadTeacherData()
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.loadAllData()
|
|
|
|
|
+ this.loadMyReferrer()
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ methods: {
|
|
|
|
|
+ // 服务商身份:vendorStatus 返回 approved 即为已通过审核的服务商
|
|
|
|
|
+ checkVendorStatus() {
|
|
|
|
|
+ var self = this
|
|
|
|
|
+ vendorStatus().then(function(res) {
|
|
|
|
|
+ self.isVendor = !!(res && res.data && res.data.vendorStatus === 'approved')
|
|
|
|
|
+ }).catch(function() {
|
|
|
|
|
+ self.isVendor = false
|
|
|
|
|
+ })
|
|
|
|
|
+ },
|
|
|
|
|
+ loadTeacherStatus() {
|
|
|
|
|
+ var self = this
|
|
|
|
|
+ getTeacherInfo().then(function(res) {
|
|
|
|
|
+ if (res && res.code === 200 && res.data) {
|
|
|
|
|
+ self.teacherStatus = res.data.verificationStatus || res.data.status || 'pending'
|
|
|
|
|
+ }
|
|
|
|
|
+ }).catch(function() {})
|
|
|
|
|
+ },
|
|
|
|
|
+ loadTeacherData() {
|
|
|
|
|
+ var self = this
|
|
|
|
|
+ // 并行加载待处理任务和未读消息
|
|
|
|
|
+ getTeacherPendingTasks().then(function(res) {
|
|
|
|
|
+ if (res && res.data && Array.isArray(res.data)) {
|
|
|
|
|
+ self.pendingTasks = res.data.length
|
|
|
|
|
+ } else if (res && res.data && res.data.pendingCount != null) {
|
|
|
|
|
+ self.pendingTasks = res.data.pendingCount
|
|
|
|
|
+ }
|
|
|
|
|
+ }).catch(function() {})
|
|
|
|
|
+ getTeacherUnreadCount().then(function(res) {
|
|
|
|
|
+ if (res && res.data != null) {
|
|
|
|
|
+ self.unreadMessages = res.data
|
|
|
|
|
+ }
|
|
|
|
|
+ }).catch(function() {})
|
|
|
|
|
+ },
|
|
|
|
|
+ loadAllData() {
|
|
|
|
|
+ this.loadUserInfo()
|
|
|
|
|
+ this.loadMembership()
|
|
|
|
|
+ this.loadPoints()
|
|
|
|
|
+ this.loadFamily()
|
|
|
|
|
+ this.loadTasks()
|
|
|
|
|
+ this.loadChallenges()
|
|
|
|
|
+ this.loadOrderCounts()
|
|
|
|
|
+ this.loadCartCount()
|
|
|
|
|
+ this.loadProfileStats()
|
|
|
|
|
+ this.loadReferralEarnings()
|
|
|
|
|
+ this.loadTeamInfo()
|
|
|
|
|
+ },
|
|
|
|
|
+ loadUserInfo() {
|
|
|
|
|
+ var self = this
|
|
|
|
|
+ getUserInfo().then(function(res) {
|
|
|
|
|
+ if (res && res.code === 200 && res.data) {
|
|
|
|
|
+ self.userInfo = res.data
|
|
|
|
|
+ if (!self.nickname) self.nickname = res.data.nickname || ''
|
|
|
|
|
+ }
|
|
|
|
|
+ }).catch(function() {})
|
|
|
|
|
+ },
|
|
|
|
|
+ loadMembership() {
|
|
|
|
|
+ var self = this
|
|
|
|
|
+ getMyMembership().then(function(res) {
|
|
|
|
|
+ if (res && res.data) {
|
|
|
|
|
+ self.memberLevel = res.data.memberLevel || res.data.levelCode || null
|
|
|
|
|
+ self.membershipDays = res.data.membershipDays || res.data.daysRemaining || 0
|
|
|
|
|
+ }
|
|
|
|
|
+ }).catch(function() {})
|
|
|
|
|
+ },
|
|
|
|
|
+ loadPoints() {
|
|
|
|
|
+ // 累计积分由 loadProfileStats 汇总家庭成员积分,此处不再单独覆盖
|
|
|
|
|
+ },
|
|
|
|
|
+ loadFamily() {
|
|
|
|
|
+ var self = this
|
|
|
|
|
+ getFamilyMemberList({ visibleOnly: true }).then(function(res) {
|
|
|
|
|
+ var data = res && res.data ? res.data : []
|
|
|
|
|
+ self.familyMembers = Array.isArray(data) ? data : []
|
|
|
|
|
+ }).catch(function() {
|
|
|
|
|
+ self.familyMembers = []
|
|
|
|
|
+ })
|
|
|
|
|
+ },
|
|
|
|
|
+ loadTasks() {
|
|
|
|
|
+ var self = this
|
|
|
|
|
+ var memberId = uni.getStorageSync('currentChildId') || uni.getStorageSync('userId')
|
|
|
|
|
+ if (!memberId) return
|
|
|
|
|
+ getTodayTasksByCategory(memberId, 'action').then(function(res) {
|
|
|
|
|
+ if (res && res.data) {
|
|
|
|
|
+ self.pendingTasks = Array.isArray(res.data) ? res.data.length : 0
|
|
|
|
|
+ }
|
|
|
|
|
+ }).catch(function() {})
|
|
|
|
|
+ // fallback to storage
|
|
|
|
|
+ if (!self.pendingTasks) {
|
|
|
|
|
+ self.pendingTasks = parseInt(uni.getStorageSync('pendingTaskCount') || 0)
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ loadChallenges() {
|
|
|
|
|
+ var self = this
|
|
|
|
|
+ getChallengeList({}).then(function(res) {
|
|
|
|
|
+ if (res && res.data) {
|
|
|
|
|
+ // 不在此处使用,仅确保请求
|
|
|
|
|
+ }
|
|
|
|
|
+ }).catch(function() {})
|
|
|
|
|
+ },
|
|
|
|
|
+ loadOrderCounts() {
|
|
|
|
|
+ var self = this
|
|
|
|
|
+ getOrderCounts().then(function(res) {
|
|
|
|
|
+ if (res && res.data) {
|
|
|
|
|
+ self.orderPending = res.data.pending || 0
|
|
|
|
|
+ self.orderUnpaid = res.data.paid || 0
|
|
|
|
|
+ self.orderShipped = res.data.shipped || 0
|
|
|
|
|
+ self.orderAfterSale = res.data.refunding || 0
|
|
|
|
|
+ }
|
|
|
|
|
+ }).catch(function() {})
|
|
|
|
|
+ },
|
|
|
|
|
+ loadCartCount() {
|
|
|
|
|
+ var self = this
|
|
|
|
|
+ cartCount().then(function(res) {
|
|
|
|
|
+ if (res && res.data != null) {
|
|
|
|
|
+ self.cartCount = res.data || 0
|
|
|
|
|
+ }
|
|
|
|
|
+ }).catch(function() {})
|
|
|
|
|
+ },
|
|
|
|
|
+ loadProfileStats() {
|
|
|
|
|
+ var self = this
|
|
|
|
|
+ getProfileStats().then(function(res) {
|
|
|
|
|
+ if (res && res.code === 200 && res.data) {
|
|
|
|
|
+ var d = res.data
|
|
|
|
|
+ if (d.totalPoints != null) self.totalPoints = d.totalPoints
|
|
|
|
|
+ if (d.streakDays != null) self.streakDays = d.streakDays
|
|
|
|
|
+ if (d.completedTasks != null) self.completedTasks = d.completedTasks
|
|
|
|
|
+ if (d.rewardsEarned != null) self.rewardsEarned = d.rewardsEarned
|
|
|
|
|
+ if (d.badgesCollected != null) self.badgesCollected = d.badgesCollected
|
|
|
|
|
+ if (d.growthRecords != null) self.growthRecords = d.growthRecords
|
|
|
|
|
+ if (d.challengesJoined != null) self.challengesJoined = d.challengesJoined
|
|
|
|
|
+ if (d.healthReports != null) self.healthReports = d.healthReports
|
|
|
|
|
+ }
|
|
|
|
|
+ }).catch(function() {})
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ loadReferralEarnings() {
|
|
|
|
|
+ var self = this
|
|
|
|
|
+ getReferralSummary().then(function(res) {
|
|
|
|
|
+ if (res && res.code === 200 && res.data) {
|
|
|
|
|
+ self.referralEarnings = res.data.totalEarnings || 0
|
|
|
|
|
+ }
|
|
|
|
|
+ }).catch(function() {})
|
|
|
|
|
+ },
|
|
|
|
|
+ loadTeamInfo() {
|
|
|
|
|
+ var self = this
|
|
|
|
|
+ getTeamConsumption(3).then(function(res) {
|
|
|
|
|
+ if (res && res.data && res.data.totalMembers != null) {
|
|
|
|
|
+ self.teamMembers = res.data.totalMembers
|
|
|
|
|
+ }
|
|
|
|
|
+ }).catch(function() {})
|
|
|
|
|
+ },
|
|
|
|
|
+ async loadMyReferrer() {
|
|
|
|
|
+ try {
|
|
|
|
|
+ var memberId = uni.getStorageSync('currentMemberId') || uni.getStorageSync('currentChildId') || 0
|
|
|
|
|
+ var res = await getMyReferrer(memberId)
|
|
|
|
|
+ if (res && res.code === 200 && res.data && res.data.hasReferrer) {
|
|
|
|
|
+ this.myReferrer = res.data
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error('获取推荐人失败', e)
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ // ===== 分享海报(复用 ContentSharePoster,与文章/活动/商品同一套生成逻辑)=====
|
|
|
|
|
+ async showShareQr() {
|
|
|
|
|
+ uni.showLoading({ title: '生成海报中...' })
|
|
|
|
|
+ try {
|
|
|
|
|
+ if (!this.referralCodeLoaded) {
|
|
|
|
|
+ await this.loadReferralCode()
|
|
|
|
|
+ }
|
|
|
|
|
+ var scene = 'ref=' + (this.referralCode || '')
|
|
|
|
|
+ if (scene.length > 32) {
|
|
|
|
|
+ scene = scene.substring(0, 32)
|
|
|
|
|
+ }
|
|
|
|
|
+ // 首页路径,扫码后进入首页并绑定推荐人
|
|
|
|
|
+ // 注意:微信 getwxacodeunlimit 的 page 不能带前导斜杠,否则报 errcode 41030 invalid page rid
|
|
|
|
|
+ var res = await getShareQrCode('pages/index-home/index', scene)
|
|
|
|
|
+ if (res && res.code === 200 && res.data && res.data.qrCodeBase64) {
|
|
|
|
|
+ this.posterQrCode = res.data.qrCodeBase64
|
|
|
|
|
+ // 必须在 showSharePoster=true 之前取到数据:组件 watch 到 show 后 300ms 即开始绘制
|
|
|
|
|
+ await this.loadPosterEnergy()
|
|
|
|
|
+ this.showSharePoster = true
|
|
|
|
|
+ } else {
|
|
|
|
|
+ uni.showToast({ title: '生成失败,请重试', icon: 'none' })
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error('生成分享海报失败', e)
|
|
|
|
|
+ uni.showToast({ title: '生成失败,请重试', icon: 'none' })
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ uni.hideLoading()
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ closeSharePoster() {
|
|
|
|
|
+ this.showSharePoster = false
|
|
|
|
|
+ },
|
|
|
|
|
+ // 五维能量数据卡数据(方案C):家庭聚合视角;未解锁全景/取数失败时置空,海报降级为品牌卡
|
|
|
|
|
+ loadPosterEnergy() {
|
|
|
|
|
+ var self = this
|
|
|
|
|
+ return getEnergySandbox('family').then(function(res) {
|
|
|
|
|
+ self.posterEnergyData = self.mapPosterEnergy(res)
|
|
|
|
|
+ }).catch(function(e) {
|
|
|
|
|
+ console.error('获取五维能量失败,海报降级为品牌卡', e)
|
|
|
|
|
+ self.posterEnergyData = null
|
|
|
|
|
+ })
|
|
|
|
|
+ },
|
|
|
|
|
+ mapPosterEnergy(res) {
|
|
|
|
|
+ if (!res || res.code !== 200 || !res.data) return null
|
|
|
|
|
+ // locked=true 表示用户未解锁全景,此时不展示任何数据,改为品牌卡
|
|
|
|
|
+ if (res.data.locked) return null
|
|
|
|
|
+ // 只认家庭聚合视角;仅当接口返回的是裸 DTO(无 personalData/familyData 包装)时才直接取 res.data
|
|
|
|
|
+ var d = res.data.familyData || null
|
|
|
|
|
+ if (!d) {
|
|
|
|
|
+ if (res.data.personalData) return null
|
|
|
|
|
+ d = res.data
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!d || typeof d !== 'object') return null
|
|
|
|
|
+ var defs = [
|
|
|
|
|
+ { key: 'body', label: '身', color: BrandColors.body },
|
|
|
|
|
+ { key: 'mind', label: '心', color: BrandColors.mind },
|
|
|
|
|
+ { key: 'wisdom', label: '智', color: BrandColors.wisdom },
|
|
|
|
|
+ { key: 'action', label: '行', color: BrandColors.action },
|
|
|
|
|
+ { key: 'wealth', label: '富', color: BrandColors.wealth }
|
|
|
|
|
+ ]
|
|
|
|
|
+ var dims = []
|
|
|
|
|
+ var sum = 0
|
|
|
|
|
+ for (var i = 0; i < defs.length; i++) {
|
|
|
|
|
+ var raw = d[defs[i].key + 'Score']
|
|
|
|
|
+ if (raw === null || raw === undefined || raw === '' || isNaN(raw)) continue
|
|
|
|
|
+ var n = Number(raw)
|
|
|
|
|
+ if (n < 0) n = 0
|
|
|
|
|
+ if (n > 100) n = 100
|
|
|
|
|
+ sum += n
|
|
|
|
|
+ dims.push({ key: defs[i].key, label: defs[i].label, score: n, color: defs[i].color })
|
|
|
|
|
+ }
|
|
|
|
|
+ if (dims.length === 0) return null
|
|
|
|
|
+ var overall = d.overallScore
|
|
|
|
|
+ if (overall === null || overall === undefined || overall === '' || isNaN(overall)) {
|
|
|
|
|
+ overall = Math.round(sum / dims.length)
|
|
|
|
|
+ }
|
|
|
|
|
+ return { label: '我们家的能量', overall: Number(overall), dims: dims }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ async loadReferralCode() {
|
|
|
|
|
+ if (this.referralCodeLoaded) return
|
|
|
|
|
+ var self = this
|
|
|
|
|
+ try {
|
|
|
|
|
+ var res = await getReferralCode()
|
|
|
|
|
+ self.referralCode = extractReferralCode(res) || ''
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error('获取推广码失败', e)
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ self.referralCodeLoaded = true
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ goToServiceCenter() {
|
|
|
|
|
+ uni.navigateTo({ url: '/pages/service-center/index' })
|
|
|
|
|
+ },
|
|
|
|
|
+ goTeamTree() {
|
|
|
|
|
+ // 服务商 → 服务商团队;非服务商 → 引导申请
|
|
|
|
|
+ if (this.isVendor) {
|
|
|
|
|
+ uni.navigateTo({ url: '/pages/vendor/team-system/team-system' })
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ uni.showModal({
|
|
|
|
|
+ title: '提示',
|
|
|
|
|
+ content: '您尚未申请任何服务角色,是否立即申请?',
|
|
|
|
|
+ confirmText: '去申请',
|
|
|
|
|
+ cancelText: '取消',
|
|
|
|
|
+ success: function(res) {
|
|
|
|
|
+ if (res.confirm) {
|
|
|
|
|
+ uni.navigateTo({ url: '/pages/profile/service-role-apply' })
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ },
|
|
|
|
|
+ goLogin: function() {
|
|
|
|
|
+ this.openLoginSheet()
|
|
|
|
|
+ },
|
|
|
|
|
+ // 登录浮层成功回调:刷新个人资料与登录后数据
|
|
|
|
|
+ onLoginSuccess: function() {
|
|
|
|
|
+ if (this.loadUserInfo) this.loadUserInfo()
|
|
|
|
|
+ },
|
|
|
|
|
+ goSkip: function() {
|
|
|
|
|
+ uni.switchTab({ url: '/pages/index-home/index' })
|
|
|
|
|
+ },
|
|
|
|
|
+ goToEditInfo: function() {
|
|
|
|
|
+ uni.navigateTo({ url: '/pages/user-edit/user-edit' })
|
|
|
|
|
+ },
|
|
|
|
|
+ goToPoints: function() {
|
|
|
|
|
+ uni.navigateTo({ url: '/pages/points/points' })
|
|
|
|
|
+ },
|
|
|
|
|
+ goToStreak: function() {
|
|
|
|
|
+ var memberId = uni.getStorageSync('currentMemberId') || uni.getStorageSync('currentChildId')
|
|
|
|
|
+ var url = '/pages/stats/completion-rate'
|
|
|
|
|
+ if (memberId) {
|
|
|
|
|
+ url += '?memberId=' + memberId
|
|
|
|
|
+ } else {
|
|
|
|
|
+ url += '?familyView=1'
|
|
|
|
|
+ }
|
|
|
|
|
+ uni.navigateTo({ url: url })
|
|
|
|
|
+ },
|
|
|
|
|
+ goToTasks: function() {
|
|
|
|
|
+ uni.navigateTo({ url: '/pages/tasks/tasks' })
|
|
|
|
|
+ },
|
|
|
|
|
+ goToWishes: function() {
|
|
|
|
|
+ uni.navigateTo({ url: '/pages/wishes/index' })
|
|
|
|
|
+ },
|
|
|
|
|
+ goToBadges: function() {
|
|
|
|
|
+ uni.navigateTo({ url: '/pages/rewards/badge' })
|
|
|
|
|
+ },
|
|
|
|
|
+ goToGrowth: function() {
|
|
|
|
|
+ uni.navigateTo({ url: '/pages/growth-main/index' })
|
|
|
|
|
+ },
|
|
|
|
|
+ goToChallenges: function() {
|
|
|
|
|
+ uni.navigateTo({ url: '/pages/health/challenge-manage' })
|
|
|
|
|
+ },
|
|
|
|
|
+ goToHealthReports: function() {
|
|
|
|
|
+ uni.navigateTo({ url: '/pages/health/report-list' })
|
|
|
|
|
+ },
|
|
|
|
|
+ goToWealthCenter: function() {
|
|
|
|
|
+ uni.navigateTo({ url: '/pages/promotion/index' })
|
|
|
|
|
+ },
|
|
|
|
|
+ goUpgrade: function() {
|
|
|
|
|
+ uni.navigateTo({ url: '/pages/membership/upgrade' })
|
|
|
|
|
+ },
|
|
|
|
|
+ goToFamily: function() {
|
|
|
|
|
+ uni.navigateTo({ url: '/pages/profile-extra/family-members' })
|
|
|
|
|
+ },
|
|
|
|
|
+ goMemberDetail: function(member) {
|
|
|
|
|
+ uni.navigateTo({ url: '/pages/home-pages/member-home-detail?memberId=' + member.id })
|
|
|
|
|
+ },
|
|
|
|
|
+ goToAppointments: function() {
|
|
|
|
|
+ uni.navigateTo({ url: '/pages/parent/appointments' })
|
|
|
|
|
+ },
|
|
|
|
|
+ goToOrders: function() {
|
|
|
|
|
+ uni.navigateTo({ url: '/pages/shop/order-list/order-list' })
|
|
|
|
|
+ },
|
|
|
|
|
+ goToOrderStatus: function(status) {
|
|
|
|
|
+ uni.navigateTo({ url: '/pages/shop/order-list/order-list?status=' + status })
|
|
|
|
|
+ },
|
|
|
|
|
+ goToCart: function() {
|
|
|
|
|
+ uni.navigateTo({ url: '/pages/shop/cart/cart' })
|
|
|
|
|
+ },
|
|
|
|
|
+ goToAddress: function() {
|
|
|
|
|
+ uni.navigateTo({ url: '/pages/shop/address/address' })
|
|
|
|
|
+ },
|
|
|
|
|
+ goToMessageHistory: function() {
|
|
|
|
|
+ uni.navigateTo({ url: '/pages/profile/message-history' })
|
|
|
|
|
+ },
|
|
|
|
|
+ goToSettings: function() {
|
|
|
|
|
+ uni.showActionSheet({
|
|
|
|
|
+ itemList: ['完善信息', '家庭成员', '隐私政策', '用户协议', '我的画像', '提醒设置'],
|
|
|
|
|
+ success: function(res) {
|
|
|
|
|
+ var urls = {
|
|
|
|
|
+ 0: '/pages/user-edit/user-edit',
|
|
|
|
|
+ 1: '/pages/profile-extra/family-members',
|
|
|
|
|
+ 2: '/pages/policy/privacy',
|
|
|
|
|
+ 3: '/pages/policy/agreement',
|
|
|
|
|
+ 4: '/pages/profile/portrait-edit',
|
|
|
|
|
+ 5: '/pages/reminder-config/index'
|
|
|
|
|
+ }
|
|
|
|
|
+ if (urls[res.tapIndex]) {
|
|
|
|
|
+ uni.navigateTo({ url: urls[res.tapIndex] })
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ },
|
|
|
|
|
+ memberRoleLabel: function(role) {
|
|
|
|
|
+ var map = { parent: '家长', child: '成员', teacher: '规划师' }
|
|
|
|
|
+ return map[role] || '成员'
|
|
|
|
|
+ },
|
|
|
|
|
+ onExitSwitch: function() {
|
|
|
|
|
+ var self = this
|
|
|
|
|
+ uni.showModal({
|
|
|
|
|
+ title: '退出切换',
|
|
|
|
|
+ content: '请输入家长密码以确认退出',
|
|
|
|
|
+ editable: true,
|
|
|
|
|
+ success: function(res) {
|
|
|
|
|
+ if (res.confirm && res.content) {
|
|
|
|
|
+ verifyPassword(res.content).then(function(result) {
|
|
|
|
|
+ if (result.code === 200) {
|
|
|
|
|
+ self.$store.commit('switchBackToParent')
|
|
|
|
|
+ self.isSwitchedChild = false
|
|
|
|
|
+ self.role = 'parent'
|
|
|
|
|
+ uni.showToast({ title: '已退出切换', icon: 'success' })
|
|
|
|
|
+ uni.reLaunch({ url: '/pages/index-home/index' })
|
|
|
|
|
+ } else {
|
|
|
|
|
+ uni.showToast({ title: '密码错误', icon: 'none' })
|
|
|
|
|
+ }
|
|
|
|
|
+ }).catch(function() {
|
|
|
|
|
+ uni.showToast({ title: '验证失败', icon: 'none' })
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ },
|
|
|
|
|
+ logout: function() {
|
|
|
|
|
+ uni.showModal({
|
|
|
|
|
+ title: '确认退出',
|
|
|
|
|
+ content: '确定要退出登录吗?',
|
|
|
|
|
+ success: function(res) {
|
|
|
|
|
+ if (res.confirm) {
|
|
|
|
|
+ this.$store.commit('logout')
|
|
|
|
|
+ uni.reLaunch({ url: '/pages/index-home/index' })
|
|
|
|
|
+ }
|
|
|
|
|
+ }.bind(this)
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+</script>
|
|
|
|
|
+
|
|
|
|
|
+<style scoped lang="scss">
|
|
|
|
|
+// ===== 页面级局部调色板(单页专有色阶;同值才映射全局令牌,约定见 uni.scss §14) =====
|
|
|
|
|
+$c-text-gray-400: $facade-text-gray-400;
|
|
|
|
|
+$c-fill-2b: #F2F2F2;
|
|
|
|
|
+.container {
|
|
|
|
|
+ min-height: 100vh;
|
|
|
|
|
+ background: $facade-fill-warm-gray;
|
|
|
|
|
+ padding-bottom: calc(160rpx + env(safe-area-inset-bottom));
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 登录引导 */
|
|
|
|
|
+.login-prompt {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ padding: 200rpx 60rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.prompt-icon { font-size: 100rpx; margin-bottom: 30rpx; }
|
|
|
|
|
+.prompt-title { font-size: 40rpx; font-weight: bold; color: $facade-text-strong; margin-bottom: 16rpx; }
|
|
|
|
|
+.prompt-desc { font-size: 26rpx; color: $facade-text-muted; margin-bottom: 20rpx; }
|
|
|
|
|
+.login-btn {
|
|
|
|
|
+ background: linear-gradient(135deg, $brand-primary, $brand-primary-light);
|
|
|
|
|
+ color: $app-surface;
|
|
|
|
|
+ border-radius: 50rpx;
|
|
|
|
|
+ font-size: 30rpx;
|
|
|
|
|
+ padding: 24rpx 80rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.skip-link {
|
|
|
|
|
+ font-size: 26rpx;
|
|
|
|
|
+ color: $facade-text-muted;
|
|
|
|
|
+ margin-top: 24rpx;
|
|
|
|
|
+ text-decoration: underline;
|
|
|
|
|
+}
|
|
|
|
|
+.philosophy-card {
|
|
|
|
|
+ margin: 0 40rpx 48rpx;
|
|
|
|
|
+ background: linear-gradient(135deg, $facade-brand-tint, rgba($brand-primary, 0.06));
|
|
|
|
|
+ border-radius: 20rpx;
|
|
|
|
|
+ padding: 28rpx 32rpx;
|
|
|
|
|
+ border-left: 6rpx solid $brand-primary;
|
|
|
|
|
+}
|
|
|
|
|
+.philosophy-title {
|
|
|
|
|
+ font-size: 28rpx;
|
|
|
|
|
+ font-weight: bold;
|
|
|
|
|
+ color: $facade-text-strong;
|
|
|
|
|
+ display: block;
|
|
|
|
|
+ margin-bottom: 8rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.philosophy-desc {
|
|
|
|
|
+ font-size: 24rpx;
|
|
|
|
|
+ color: $facade-text-faint;
|
|
|
|
|
+ line-height: 1.6;
|
|
|
|
|
+ display: block;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 退出切换 */
|
|
|
|
|
+.exit-switch {
|
|
|
|
|
+ margin: 0 30rpx 16rpx;
|
|
|
|
|
+ background: linear-gradient(135deg, $brand-primary, $brand-primary-light);
|
|
|
|
|
+ color: $app-surface;
|
|
|
|
|
+ padding: 16rpx;
|
|
|
|
|
+ border-radius: 12rpx;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+ font-size: 26rpx;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 身份卡 */
|
|
|
|
|
+.user-card {
|
|
|
|
|
+ margin: 24rpx 30rpx 20rpx;
|
|
|
|
|
+ background: linear-gradient(135deg, $brand-primary 0%, $brand-primary-light 100%);
|
|
|
|
|
+ border-radius: 24rpx;
|
|
|
|
|
+ padding: 32rpx 32rpx;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ box-shadow: 0 8rpx 32rpx rgba($c-indigo, 0.3);
|
|
|
|
|
+}
|
|
|
|
|
+.user-card-left {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+}
|
|
|
|
|
+.avatar-wrap {
|
|
|
|
|
+ width: 100rpx;
|
|
|
|
|
+ height: 100rpx;
|
|
|
|
|
+ border-radius: 50%;
|
|
|
|
|
+ background: rgba($app-surface, 0.2);
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ margin-right: 20rpx;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ border: 3rpx solid rgba($app-surface, 0.3);
|
|
|
|
|
+}
|
|
|
|
|
+.avatar-img {
|
|
|
|
|
+ width: 100rpx;
|
|
|
|
|
+ height: 100rpx;
|
|
|
|
|
+ border-radius: 50%;
|
|
|
|
|
+}
|
|
|
|
|
+.avatar-text {
|
|
|
|
|
+ font-size: 44rpx;
|
|
|
|
|
+ color: $app-surface;
|
|
|
|
|
+}
|
|
|
|
|
+.user-meta { flex: 1; }
|
|
|
|
|
+.user-name-row {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 12rpx;
|
|
|
|
|
+ margin-bottom: 8rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.nickname {
|
|
|
|
|
+ font-size: 34rpx;
|
|
|
|
|
+ font-weight: bold;
|
|
|
|
|
+ color: $app-surface;
|
|
|
|
|
+}
|
|
|
|
|
+.role-tag {
|
|
|
|
|
+ font-size: 20rpx;
|
|
|
|
|
+ background: rgba($app-surface, 0.2);
|
|
|
|
|
+ color: $app-surface;
|
|
|
|
|
+ padding: 4rpx 14rpx;
|
|
|
|
|
+ border-radius: 20rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.referrer-line {
|
|
|
|
|
+ font-size: 24rpx;
|
|
|
|
|
+ color: rgba($app-surface, 0.7);
|
|
|
|
|
+ margin-bottom: 6rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.user-sub {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 12rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.level-badge {
|
|
|
|
|
+ font-size: 22rpx;
|
|
|
|
|
+ padding: 6rpx 20rpx;
|
|
|
|
|
+ border-radius: 22rpx;
|
|
|
|
|
+ font-weight: bold;
|
|
|
|
|
+ border: 2rpx solid rgba($app-surface, 0.35);
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 6rpx;
|
|
|
|
|
+ box-sizing: border-box;
|
|
|
|
|
+ height: 40rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.level-badge::before {
|
|
|
|
|
+ content: '👑';
|
|
|
|
|
+ font-size: 18rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.level-FREE { background: linear-gradient(135deg, $c-text-gray-400, $facade-text-slate); color: $app-surface; }
|
|
|
|
|
+.level-FREE::before { content: ''; }
|
|
|
|
|
+.level-FAMILY { background: linear-gradient(135deg, $dim-wealth, $status-warning-deep); color: $app-surface; }
|
|
|
|
|
+.level-PREMIUM { background: linear-gradient(135deg, $dim-wisdom, $dim-wisdom-deep); color: $app-surface; }
|
|
|
|
|
+.level-LIFETIME {
|
|
|
|
|
+ background: linear-gradient(135deg, $accent-pink, $accent-pink-deep);
|
|
|
|
|
+ color: $app-surface;
|
|
|
|
|
+ box-shadow: 0 4rpx 16rpx rgba($accent-pink, 0.45);
|
|
|
|
|
+ border-color: rgba($app-surface, 0.55);
|
|
|
|
|
+}
|
|
|
|
|
+.days-left {
|
|
|
|
|
+ font-size: 20rpx;
|
|
|
|
|
+ color: rgba($app-surface, 0.85);
|
|
|
|
|
+}
|
|
|
|
|
+.upgrade-btn {
|
|
|
|
|
+ background: rgba($app-surface, 0.22);
|
|
|
|
|
+ border: 2rpx solid rgba($app-surface, 0.6);
|
|
|
|
|
+ border-radius: 20rpx;
|
|
|
|
|
+ padding: 6rpx 20rpx;
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+ box-sizing: border-box;
|
|
|
|
|
+ height: 40rpx;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+}
|
|
|
|
|
+.upgrade-btn-text {
|
|
|
|
|
+ font-size: 20rpx;
|
|
|
|
|
+ font-weight: bold;
|
|
|
|
|
+ color: $app-surface;
|
|
|
|
|
+ line-height: 1;
|
|
|
|
|
+}
|
|
|
|
|
+.user-card-right {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 6rpx;
|
|
|
|
|
+ background: rgba($app-surface, 0.15);
|
|
|
|
|
+ padding: 12rpx 24rpx;
|
|
|
|
|
+ border-radius: 14rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.points-num {
|
|
|
|
|
+ font-size: 36rpx;
|
|
|
|
|
+ font-weight: bold;
|
|
|
|
|
+ color: $app-surface;
|
|
|
|
|
+}
|
|
|
|
|
+.points-label {
|
|
|
|
|
+ font-size: 20rpx;
|
|
|
|
|
+ color: rgba($app-surface, 0.7);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 家庭成员速览 */
|
|
|
|
|
+.family-strip {
|
|
|
|
|
+ margin: 0 30rpx 20rpx;
|
|
|
|
|
+ background: $app-surface;
|
|
|
|
|
+ border-radius: 20rpx;
|
|
|
|
|
+ padding: 20rpx 24rpx 16rpx;
|
|
|
|
|
+ box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
|
|
|
|
|
+}
|
|
|
|
|
+.family-strip-header {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ margin-bottom: 14rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.family-strip-title {
|
|
|
|
|
+ font-size: 26rpx;
|
|
|
|
|
+ font-weight: bold;
|
|
|
|
|
+ color: $facade-text-strong;
|
|
|
|
|
+}
|
|
|
|
|
+.family-strip-count {
|
|
|
|
|
+ font-size: 22rpx;
|
|
|
|
|
+ color: $facade-text-muted;
|
|
|
|
|
+}
|
|
|
|
|
+.family-avatars {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: row;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 16rpx;
|
|
|
|
|
+ overflow-x: auto;
|
|
|
|
|
+ padding-bottom: 4rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.family-avatar-item {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 6rpx;
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+}
|
|
|
|
|
+.family-avatar-wrap {
|
|
|
|
|
+ width: 72rpx;
|
|
|
|
|
+ height: 72rpx;
|
|
|
|
|
+ border-radius: 50%;
|
|
|
|
|
+ background: $facade-fill;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+}
|
|
|
|
|
+.family-avatar-img {
|
|
|
|
|
+ width: 72rpx;
|
|
|
|
|
+ height: 72rpx;
|
|
|
|
|
+ border-radius: 50%;
|
|
|
|
|
+}
|
|
|
|
|
+.family-avatar-text {
|
|
|
|
|
+ font-size: 28rpx;
|
|
|
|
|
+ color: $facade-text-body;
|
|
|
|
|
+}
|
|
|
|
|
+.family-avatar-wrap.family-avatar-add {
|
|
|
|
|
+ background: $facade-fill-warm-gray;
|
|
|
|
|
+ border: 2rpx dashed $facade-text-ghost;
|
|
|
|
|
+}
|
|
|
|
|
+.family-avatar-plus {
|
|
|
|
|
+ font-size: 36rpx;
|
|
|
|
|
+ color: $facade-text-muted;
|
|
|
|
|
+}
|
|
|
|
|
+.family-avatar-tag {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ bottom: -4rpx;
|
|
|
|
|
+ right: -4rpx;
|
|
|
|
|
+ font-size: 14rpx;
|
|
|
|
|
+ padding: 2rpx 6rpx;
|
|
|
|
|
+ border-radius: 8rpx;
|
|
|
|
|
+ color: $app-surface;
|
|
|
|
|
+}
|
|
|
|
|
+.family-avatar-tag.tag-parent { background: $dim-wisdom; }
|
|
|
|
|
+.family-avatar-tag.tag-child { background: $brand-primary; }
|
|
|
|
|
+.family-avatar-tag.tag-teacher { background: $dim-action; }
|
|
|
|
|
+.family-avatar-tag.tag-member { background: $c-text-gray-400; }
|
|
|
|
|
+.family-avatar-name {
|
|
|
|
|
+ font-size: 20rpx;
|
|
|
|
|
+ color: $facade-text-body;
|
|
|
|
|
+ white-space: nowrap;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 个人成就 */
|
|
|
|
|
+.achievement-section {
|
|
|
|
|
+ margin: 0 30rpx 20rpx;
|
|
|
|
|
+ background: $app-surface;
|
|
|
|
|
+ border-radius: 20rpx;
|
|
|
|
|
+ padding: 24rpx;
|
|
|
|
|
+ box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
|
|
|
|
|
+}
|
|
|
|
|
+.section-header-row {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ align-items: baseline;
|
|
|
|
|
+ margin-bottom: 16rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.section-title {
|
|
|
|
|
+ font-size: 28rpx;
|
|
|
|
|
+ font-weight: bold;
|
|
|
|
|
+ color: $facade-text-strong;
|
|
|
|
|
+}
|
|
|
|
|
+.section-sub {
|
|
|
|
|
+ font-size: 22rpx;
|
|
|
|
|
+ color: $facade-text-muted;
|
|
|
|
|
+}
|
|
|
|
|
+.achievement-grid {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-wrap: wrap;
|
|
|
|
|
+ gap: 0;
|
|
|
|
|
+}
|
|
|
|
|
+.ach-item {
|
|
|
|
|
+ width: 25%;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 6rpx;
|
|
|
|
|
+ padding: 10rpx 0;
|
|
|
|
|
+}
|
|
|
|
|
+.ach-icon-wrap {
|
|
|
|
|
+ width: 64rpx;
|
|
|
|
|
+ height: 64rpx;
|
|
|
|
|
+ border-radius: 50%;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ margin-bottom: 4rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.ach-icon {
|
|
|
|
|
+ font-size: 32rpx;
|
|
|
|
|
+ line-height: 1;
|
|
|
|
|
+}
|
|
|
|
|
+.ach-num {
|
|
|
|
|
+ font-size: 28rpx;
|
|
|
|
|
+ font-weight: bold;
|
|
|
|
|
+ color: $facade-text-strong;
|
|
|
|
|
+ line-height: 1;
|
|
|
|
|
+}
|
|
|
|
|
+.ach-label {
|
|
|
|
|
+ font-size: 20rpx;
|
|
|
|
|
+ color: $facade-text-muted;
|
|
|
|
|
+ line-height: 1;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 我的订单(电商风格) */
|
|
|
|
|
+.order-section {
|
|
|
|
|
+ margin: 0 30rpx 20rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.order-section-header {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ margin-bottom: 14rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.order-all-link {
|
|
|
|
|
+ font-size: 24rpx;
|
|
|
|
|
+ color: $facade-text-muted;
|
|
|
|
|
+}
|
|
|
|
|
+.order-card {
|
|
|
|
|
+ background: $app-surface;
|
|
|
|
|
+ border-radius: 20rpx;
|
|
|
|
|
+ padding: 24rpx 12rpx;
|
|
|
|
|
+ box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
|
|
|
|
|
+}
|
|
|
|
|
+.order-grid {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-wrap: wrap;
|
|
|
|
|
+}
|
|
|
|
|
+.order-grid-item {
|
|
|
|
|
+ width: 20%;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 10rpx;
|
|
|
|
|
+ padding: 8rpx 0;
|
|
|
|
|
+}
|
|
|
|
|
+.order-icon-wrap {
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ width: 64rpx;
|
|
|
|
|
+ height: 64rpx;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+}
|
|
|
|
|
+.order-icon {
|
|
|
|
|
+ font-size: 40rpx;
|
|
|
|
|
+ line-height: 1;
|
|
|
|
|
+}
|
|
|
|
|
+.order-badge {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ top: -8rpx;
|
|
|
|
|
+ right: -14rpx;
|
|
|
|
|
+ min-width: 30rpx;
|
|
|
|
|
+ height: 30rpx;
|
|
|
|
|
+ line-height: 30rpx;
|
|
|
|
|
+ border-radius: 15rpx;
|
|
|
|
|
+ background: $status-danger-2;
|
|
|
|
|
+ color: $app-surface;
|
|
|
|
|
+ font-size: 18rpx;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+ padding: 0 6rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.order-grid-text {
|
|
|
|
|
+ font-size: 22rpx;
|
|
|
|
|
+ color: $facade-text-body;
|
|
|
|
|
+ line-height: 1;
|
|
|
|
|
+}
|
|
|
|
|
+.order-extra-row {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ margin-top: 20rpx;
|
|
|
|
|
+ padding-top: 20rpx;
|
|
|
|
|
+ border-top: 1rpx solid $c-fill-2b;
|
|
|
|
|
+}
|
|
|
|
|
+.order-extra-item {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ gap: 8rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.order-extra-icon {
|
|
|
|
|
+ font-size: 28rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.order-extra-text {
|
|
|
|
|
+ font-size: 24rpx;
|
|
|
|
|
+ color: $facade-text-strong;
|
|
|
|
|
+}
|
|
|
|
|
+.order-extra-badge {
|
|
|
|
|
+ min-width: 30rpx;
|
|
|
|
|
+ height: 30rpx;
|
|
|
|
|
+ line-height: 30rpx;
|
|
|
|
|
+ border-radius: 15rpx;
|
|
|
|
|
+ background: $brand-primary;
|
|
|
|
|
+ color: $app-surface;
|
|
|
|
|
+ font-size: 18rpx;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+ padding: 0 6rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.order-extra-divider {
|
|
|
|
|
+ width: 1rpx;
|
|
|
|
|
+ height: 30rpx;
|
|
|
|
|
+ background: $c-fill-2b;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 规划师服务中心入口 */
|
|
|
|
|
+.service-center-section {
|
|
|
|
|
+ margin: 0 30rpx 20rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.service-center-card {
|
|
|
|
|
+ background: linear-gradient(135deg, $brand-primary 0%, $brand-primary-light 100%);
|
|
|
|
|
+ border-radius: 20rpx;
|
|
|
|
|
+ padding: 24rpx;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 20rpx;
|
|
|
|
|
+ box-shadow: 0 4rpx 16rpx rgba($brand-primary, 0.25);
|
|
|
|
|
+}
|
|
|
|
|
+.service-center-card.disabled-card {
|
|
|
|
|
+ background: linear-gradient(135deg, $uni-border-color-light, $app-text-subtle);
|
|
|
|
|
+ box-shadow: none;
|
|
|
|
|
+ opacity: 0.8;
|
|
|
|
|
+}
|
|
|
|
|
+.service-center-icon {
|
|
|
|
|
+ font-size: 48rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.service-center-info {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+}
|
|
|
|
|
+.service-center-title {
|
|
|
|
|
+ font-size: 30rpx;
|
|
|
|
|
+ font-weight: bold;
|
|
|
|
|
+ color: $app-surface;
|
|
|
|
|
+ display: block;
|
|
|
|
|
+}
|
|
|
|
|
+.service-center-desc {
|
|
|
|
|
+ font-size: 22rpx;
|
|
|
|
|
+ color: rgba($app-surface, 0.8);
|
|
|
|
|
+ margin-top: 6rpx;
|
|
|
|
|
+ display: block;
|
|
|
|
|
+}
|
|
|
|
|
+.service-center-card.disabled-card .service-center-title {
|
|
|
|
|
+ color: $app-surface;
|
|
|
|
|
+}
|
|
|
|
|
+.service-center-card.disabled-card .service-center-desc {
|
|
|
|
|
+ color: rgba($app-surface, 0.9);
|
|
|
|
|
+}
|
|
|
|
|
+.service-center-arrow {
|
|
|
|
|
+ font-size: 40rpx;
|
|
|
|
|
+ color: $app-surface;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 设置 + 退出 */
|
|
|
|
|
+.bottom-menu-section {
|
|
|
|
|
+ margin: 0 30rpx;
|
|
|
|
|
+ padding-bottom: 20rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.menu-row {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ background: $app-surface;
|
|
|
|
|
+ border-radius: 16rpx;
|
|
|
|
|
+ padding: 24rpx;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 14rpx;
|
|
|
|
|
+ box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.04);
|
|
|
|
|
+ margin-bottom: 20rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.menu-row-icon {
|
|
|
|
|
+ font-size: 30rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.menu-row-text {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ font-size: 26rpx;
|
|
|
|
|
+ color: $facade-text-strong;
|
|
|
|
|
+}
|
|
|
|
|
+.menu-row-arrow {
|
|
|
|
|
+ font-size: 28rpx;
|
|
|
|
|
+ color: $facade-text-ghost;
|
|
|
|
|
+}
|
|
|
|
|
+.logout-btn {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ background: $app-surface;
|
|
|
|
|
+ border-radius: 16rpx;
|
|
|
|
|
+ padding: 24rpx;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+ box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.04);
|
|
|
|
|
+}
|
|
|
|
|
+.logout-text {
|
|
|
|
|
+ font-size: 26rpx;
|
|
|
|
|
+ color: $status-danger-2;
|
|
|
|
|
+ font-weight: 500;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 分享推广码浮动按钮 */
|
|
|
|
|
+.share-qr-float {
|
|
|
|
|
+ position: fixed;
|
|
|
|
|
+ top: 180rpx;
|
|
|
|
|
+ right: 30rpx;
|
|
|
|
|
+ width: 80rpx;
|
|
|
|
|
+ height: 80rpx;
|
|
|
|
|
+ border-radius: 50%;
|
|
|
|
|
+ background: $app-surface;
|
|
|
|
|
+ box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.15);
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ z-index: 99;
|
|
|
|
|
+}
|
|
|
|
|
+/* 二维码图标(纯 CSS 绘制,不依赖 emoji 字体) */
|
|
|
|
|
+.qr-glyph {
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ width: 46rpx;
|
|
|
|
|
+ height: 46rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.qr-eye {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ width: 16rpx;
|
|
|
|
|
+ height: 16rpx;
|
|
|
|
|
+ border: 4rpx solid $uni-text-color;
|
|
|
|
|
+ border-radius: 3rpx;
|
|
|
|
|
+ box-sizing: border-box;
|
|
|
|
|
+}
|
|
|
|
|
+.qr-eye-tl { left: 0; top: 0; }
|
|
|
|
|
+.qr-eye-tr { right: 0; top: 0; }
|
|
|
|
|
+.qr-eye-bl { left: 0; bottom: 0; }
|
|
|
|
|
+.qr-mid {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ left: 20rpx;
|
|
|
|
|
+ top: 20rpx;
|
|
|
|
|
+ width: 7rpx;
|
|
|
|
|
+ height: 7rpx;
|
|
|
|
|
+ background: $uni-text-color;
|
|
|
|
|
+ border-radius: 1rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.qr-cluster {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ right: 0;
|
|
|
|
|
+ bottom: 0;
|
|
|
|
|
+ width: 18rpx;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-wrap: wrap;
|
|
|
|
|
+}
|
|
|
|
|
+.qr-pip {
|
|
|
|
|
+ width: 7rpx;
|
|
|
|
|
+ height: 7rpx;
|
|
|
|
|
+ margin: 1rpx;
|
|
|
|
|
+ background: $uni-text-color;
|
|
|
|
|
+ border-radius: 1rpx;
|
|
|
|
|
+}
|
|
|
|
|
+</style>
|