Bläddra i källkod

fix(miniapp): 修复 reminder-config 页 :key 表达式导致非 h5 编译失败

liaoxg 11 timmar sedan
förälder
incheckning
37824ebbb6

+ 3 - 1
cfc-frontend/components/LoginGuideCard.vue

@@ -26,7 +26,9 @@ export default {
   },
   methods: {
     goLogin: function() {
-      uni.navigateTo({ url: '/pages/login/login' })
+      // 走全局登录浮层(mixin 会沿 $parent 链找到宿主页面的 <login-sheet>);
+      // 宿主页面未挂浮层时 mixin 自动回退整页登录
+      this.openLoginSheet()
     }
   }
 }

+ 6 - 0
cfc-frontend/main.js

@@ -3,12 +3,18 @@ import Vue from 'vue'
 import store from './store'
 import FamilyEmptyState from './components/FamilyEmptyState/FamilyEmptyState.vue'
 import CustomTabBar from './custom-tab-bar/index.vue'
+import LoginSheet from './components/login-sheet.vue'
+import loginSheetMixin from './mixins/login-sheet.js'
 import { formatPriceWithSymbol, formatPrice, formatRate, formatScore } from '@/utils/format'
 
 Vue.config.productionTip = false
 
 Vue.component('FamilyEmptyState', FamilyEmptyState)
 Vue.component('custom-tab-bar', CustomTabBar)
+// 全局登录浮层:任意页面模板可直接使用 <login-sheet ref="loginSheet" @success="onSheetLoginSuccess" />,
+// 并借助全局 mixin 的 this.openLoginSheet() 弹出(未挂浮层的页面自动回退整页登录)
+Vue.component('login-sheet', LoginSheet)
+Vue.mixin(loginSheetMixin)
 
 Vue.prototype.$store = store
 Vue.prototype.formatPriceWithSymbol = formatPriceWithSymbol

+ 26 - 5
cfc-frontend/pages/action-detail/index.vue

@@ -1,5 +1,7 @@
 <template>
   <view class="container">
+    <!-- 登录浮层(全局组件,页面内弹出底部登录) -->
+    <login-sheet ref="loginSheet" @success="onSheetLoginSuccess" />
     <!-- Tabbar 切换过渡页 -->
     <tab-transition v-if="showTabTransition" dimCode="action" ref="tabTransition" @close="showTabTransition = false" />
 
@@ -27,6 +29,20 @@
       <text class="guidance-text">{{ monthlyGuidance }}</text>
     </view>
 
+    <!-- 游客价值卡(统一组件:维度色 CTA + 价值预览) -->
+    <DimensionVisitorCard
+      v-if="!isLoggedIn"
+      dimCode="action"
+      card-title="行维度 · 关系滋养"
+      :benefits="[
+        '家庭关系图谱与关键人分析',
+        '章鱼图 / 珍珠图能力资产',
+        '家庭关系质量与改善建议'
+      ]"
+      cta-text="登录绘制关系网络"
+      secondary-text="了解关系维度"
+      secondary-url="/pages/action-detail/dimension-intro" />
+
     <!-- 登录后:五维能量条 -->
     <FamilyEnergyBar
       v-if="isLoggedIn"
@@ -250,13 +266,14 @@ import DimensionTasks from '../../components/DimensionTasks.vue'
 import DimensionActivities from '../../components/DimensionActivities.vue'
 import DimensionProducts from '../../components/DimensionProducts.vue'
 import DimensionIntroCard from '../../components/DimensionIntroCard.vue'
+import DimensionVisitorCard from '../../components/DimensionVisitorCard.vue'
 import FloatingAvatar from '../../components/AIFloatingAvatar.vue'
 import RadarChart from '../../components/RadarChart.vue'
 import DimensionSubDims from '../../components/DimensionSubDims.vue'
 import { getVisibleSections, getEnergyOverview, getChildren, getFamilyEnergySandbox, getEnergySandbox, getFamilyMemberList, getOctopusTentacles, getPearlResources, deletePearlResource, getAbilityMap, getRadarData, getPearlReminders, getOctopusEffects, getOctopusAnalysisSummary, getOctopusExpansionSuggest, getOctopusEffectKeyPersons } from '../../utils/api.js'
 
 export default {
-  components: { TabTransition, PageBanner, FamilyEnergyBar, FamilyRelationGraph, PearlDiagram, AbilityDiagram, OctopusDiagram, FamilyMemberStrip, DimensionTasks, DimensionActivities, DimensionProducts, DimensionIntroCard, FloatingAvatar, RadarChart, DimensionSubDims },
+  components: { TabTransition, PageBanner, FamilyEnergyBar, FamilyRelationGraph, PearlDiagram, AbilityDiagram, OctopusDiagram, FamilyMemberStrip, DimensionTasks, DimensionActivities, DimensionProducts, DimensionIntroCard, FloatingAvatar, RadarChart, DimensionSubDims, DimensionVisitorCard },
   data() {
     return {
       isLoggedIn: false,
@@ -658,13 +675,17 @@ export default {
     sectionVisible: function(key) {
       return this.visibleSections.length === 0 || this.visibleSections.indexOf(key) !== -1
     },
+    // 登录浮层成功回调:刷新行动维度数据
+    onLoginSuccess: function() {
+      this.loadDashboardData()
+    },
     onFuncClick: function(item) {
       if (item.needLogin && !this.isLoggedIn) {
         uni.showModal({
           title: '提示',
           content: '请先登录后使用此功能',
-          success: function(res) {
-            if (res.confirm) uni.navigateTo({ url: '/pages/login/login' })
+          success: (res) => {
+            if (res.confirm) this.openLoginSheet()
           }
         })
         return
@@ -913,7 +934,7 @@ export default {
   flex-direction: row;
   align-items: center;
   gap: 8rpx;
-  border-left: 6rpx solid #10B981;
+  border-left: 8rpx solid #10B981;
   padding-left: 16rpx;
 }
 .help-icon {
@@ -1246,7 +1267,7 @@ export default {
   padding: 16rpx 20rpx;
   background: linear-gradient(135deg, #F0FFF4, #E6FFF2);
   border-radius: 12rpx;
-  border-left: 4rpx solid #10B981;
+  border-left: 8rpx solid #10B981;
   margin-top: 16rpx;
 }
 .portrait-tip-text {

+ 13 - 4
cfc-frontend/pages/activity/activity-detail/activity-detail.vue

@@ -1,5 +1,7 @@
 <template>
   <view class="container">
+    <!-- 登录浮层(全局组件,页面内弹出底部登录) -->
+    <login-sheet ref="loginSheet" @success="onSheetLoginSuccess" />
     <!-- 加载状态 -->
     <view v-if="loading" class="loading-wrap">
       <view class="loading-spinner"></view>
@@ -436,7 +438,7 @@ export default {
 
       // 未登录则跳转登录
       if (!uni.getStorageSync('token')) {
-        uni.navigateTo({ url: '/pages/login/login' })
+        self.openLoginSheet()
         return
       }
 
@@ -487,7 +489,7 @@ export default {
       var self = this
       // 未登录则跳转登录
       if (!uni.getStorageSync('token')) {
-        uni.navigateTo({ url: '/pages/login/login' })
+        self.openLoginSheet()
         return
       }
       // Open registration form modal
@@ -499,7 +501,7 @@ export default {
       var self = this
       // 未登录则跳转登录
       if (!uni.getStorageSync('token')) {
-        uni.navigateTo({ url: '/pages/login/login' })
+        self.openLoginSheet()
         return
       }
       uni.showModal({
@@ -672,12 +674,19 @@ export default {
       this.registrants = []
       this.familyMembers = []
     },
+    // 登录浮层成功回调:刷新活动详情与签到状态
+    onLoginSuccess: function() {
+      if (this.activity && this.activity.id) {
+        this.loadDetail(this.activity.id)
+      }
+      if (this.loadFeedbackStatus) this.loadFeedbackStatus()
+    },
     // ===== 分享海报 =====
     async generatePoster() {
       if (!this.activity || !this.activity.id) return
       // 未登录则跳转登录
       if (!uni.getStorageSync('token')) {
-        uni.navigateTo({ url: '/pages/login/login' })
+        this.openLoginSheet()
         return
       }
       uni.showLoading({ title: '生成海报中...' })

+ 3 - 1
cfc-frontend/pages/article-center/article-detail.vue

@@ -1,5 +1,7 @@
 <template>
   <view class="detail-container">
+    <!-- 登录浮层(全局组件,页面内弹出底部登录) -->
+    <login-sheet ref="loginSheet" @success="onSheetLoginSuccess" />
     <view v-if="loading" class="loading-wrap">
       <view class="loading-spinner"></view>
       <text class="loading-text">加载中...</text>
@@ -271,7 +273,7 @@ export default {
     async startQuiz() {
       if (!this.isLogin) {
         uni.showToast({ title: '登录后即可完成知识测评', icon: 'none' })
-        setTimeout(function() { uni.navigateTo({ url: '/pages/login/login' }) }, 1500)
+        setTimeout(() => { this.openLoginSheet() }, 1500)
         return
       }
       var memberId = this.getActiveMemberId()

+ 7 - 1
cfc-frontend/pages/article-center/index.vue

@@ -1,5 +1,7 @@
 <template>
   <view class="ac-container">
+    <!-- 登录浮层(全局组件,页面内弹出底部登录) -->
+    <login-sheet ref="loginSheet" @success="onSheetLoginSuccess" />
     <!-- 自定义导航栏 -->
     <view class="ac-nav">
       <view class="ac-nav-left" @click="onBack">
@@ -259,7 +261,7 @@ onLoad(options) {
       if (isLoggedIn) {
         uni.navigateTo({ url: '/pages/article-center/article-edit' })
       } else {
-        uni.navigateTo({ url: '/pages/login/login?redirect=' + encodeURIComponent('/pages/article-center/index') })
+        this.openLoginSheet({ redirect: '/pages/article-center/index' })
       }
     },
     goExplore() {
@@ -269,6 +271,10 @@ onLoad(options) {
       this.articles = []
       this.loadArticles()
     },
+    // 登录浮层成功回调:刷新文章列表(登录后可见编辑入口等)
+    onLoginSuccess() {
+      this.loadArticles()
+    },
     formatDate(dateStr) {
       if (!dateStr) return ''
       return dateStr.slice(0, 10)

+ 40 - 83
cfc-frontend/pages/body-detail/index.vue

@@ -1,11 +1,18 @@
 <template>
   <view class="body-container">
+    <!-- 登录浮层(全局组件,页面内弹出底部登录) -->
+    <login-sheet ref="loginSheet" @success="onSheetLoginSuccess" />
     <!-- Tabbar 切换过渡页 -->
     <tab-transition v-if="showTabTransition" dimCode="body" ref="tabTransition" @close="showTabTransition = false" />
 
     <!-- 品牌头部 -->
     <PageBanner theme="body" tagline="科学管理全家健康" quote="身体健康是幸福的基础" />
 
+    <!-- 品牌定位语(登录前显示) -->
+    <view class="brand-strap" v-if="!isLoggedIn">
+      <text class="brand-strap-text">科学管理全家健康</text>
+    </view>
+
     <!-- 身维度画像 - 总体说明 -->
     <view class="dimension-portrait-section" v-if="isLoggedIn && bodyPortrait">
       <view class="section-header">
@@ -205,18 +212,19 @@
       </view>
     </view>
 
-    <!-- 游客主区域 -->
-    <view v-else class="visitor-body">
-      <view class="visitor-intro">
-        <text class="visitor-icon">🌿</text>
-        <text class="visitor-title">欢迎来到身维度</text>
-        <text class="visitor-desc">登录后查看身高体重、血压、心率等身体指标及趋势报告</text>
-      </view>
-      <view class="visitor-actions">
-        <button class="visitor-btn-primary" @click="goLogin">立即登录</button>
-        <button class="visitor-btn-secondary" @click="goReportList">查看报告</button>
-      </view>
-    </view>
+    <!-- 游客价值卡(统一组件:维度色 CTA + 价值预览) -->
+    <DimensionVisitorCard
+      v-else
+      dimCode="body"
+      card-title="身维度 · 全家健康管理"
+      :benefits="[
+        '身高体重 / 血压 / 心率趋势一目了然',
+        '肠道菌群健康与疾病风险预警',
+        '家庭成员健康画像对比'
+      ]"
+      cta-text="立即登录查看健康概览"
+      secondary-text="查看健康报告"
+      secondary-url="/pages/health/report-list" />
 
     <view class="bottom-spacer"></view>
     <FloatingAvatar />
@@ -230,6 +238,7 @@ import FamilyEnergyBar from '../../components/FamilyEnergyBar.vue'
 import RadarChart from '../../components/RadarChart.vue'
 import FloatingAvatar from '../../components/AIFloatingAvatar.vue'
 import DimensionIntroCard from '../../components/DimensionIntroCard.vue'
+import DimensionVisitorCard from '../../components/DimensionVisitorCard.vue'
 import FamilyMemberStrip from '../../components/FamilyMemberStrip.vue'
 
 import {
@@ -247,7 +256,7 @@ import {
 } from '../../utils/api.js'
 
 export default {
-  components: { TabTransition, PageBanner, FamilyEnergyBar, RadarChart, FloatingAvatar, FamilyMemberStrip, DimensionIntroCard },
+  components: { TabTransition, PageBanner, FamilyEnergyBar, RadarChart, FloatingAvatar, FamilyMemberStrip, DimensionIntroCard, DimensionVisitorCard },
   data() {
     return {
       isLoggedIn: false,
@@ -545,7 +554,7 @@ export default {
       uni.navigateTo({ url: '/pages/health/report-upload' })
     },
     goToLogin: function() {
-      uni.navigateTo({ url: '/pages/login/login' })
+      this.openLoginSheet()
     },
     goDimensionDetail: function(dim) {
       var url = '/pages/body-detail/dimension-detail?dim=' + dim.key
@@ -556,8 +565,8 @@ export default {
         uni.showModal({
           title: '提示',
           content: '请先登录后使用此功能',
-          success: function(res) {
-            if (res.confirm) uni.navigateTo({ url: '/pages/login/login' })
+          success: (res) => {
+            if (res.confirm) this.openLoginSheet()
           }
         })
         return
@@ -601,7 +610,11 @@ export default {
       return n.toFixed(1)
     },
     goLogin: function() {
-      uni.navigateTo({ url: '/pages/login/login' })
+      this.openLoginSheet()
+    },
+    // 登录浮层成功回调:刷新身维度数据
+    onLoginSuccess: function() {
+      this.loadDashboardData()
     },
     goReportList: function() {
       uni.navigateTo({ url: '/pages/health/report-list' })
@@ -785,6 +798,7 @@ export default {
   align-items: center;
   padding: 24rpx 24rpx 16rpx;
   border-bottom: 1rpx solid #F5F5F5;
+  border-left: 8rpx solid #FF8C42;
 }
 
 .section-title {
@@ -1093,7 +1107,7 @@ export default {
   padding: 16rpx 20rpx;
   background: linear-gradient(135deg, #FFF7ED, #FFF0E4);
   border-radius: 12rpx;
-  border-left: 4rpx solid #FF8C42;
+  border-left: 8rpx solid #FF8C42;
 }
 .portrait-tip-text {
   font-size: 24rpx;
@@ -1120,74 +1134,17 @@ export default {
   color: #FF8C42;
 }
 
-/* ─── 游客区域 ─── */
-.visitor-body {
-  flex: 1;
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  justify-content: center;
-  padding: 60rpx 32rpx;
-}
-
-.visitor-intro {
-  text-align: center;
-  margin-bottom: 48rpx;
-}
-
-.visitor-icon {
-  font-size: 80rpx;
-  margin-bottom: 20rpx;
-  display: block;
-}
-
-.visitor-title {
-  font-size: 36rpx;
-  font-weight: 700;
-  color: #FF8C42;
-  margin-bottom: 12rpx;
-  display: block;
-}
-
-.visitor-desc {
-  font-size: 24rpx;
-  color: #888;
-  line-height: 1.6;
-}
-
-.visitor-actions {
-  display: flex;
-  gap: 20rpx;
-  width: 100%;
-  max-width: 560rpx;
-}
-
-.visitor-btn-primary {
-  flex: 1;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  background: linear-gradient(135deg, #FF8C42, #FF6B1A);
-  border-radius: 48rpx;
-  padding: 24rpx 0;
-  color: #FFFFFF;
-  font-size: 28rpx;
-  font-weight: 600;
-  box-shadow: 0 4rpx 16rpx rgba(255, 107, 26, 0.35);
+/* ─── 品牌定位语(登录前) ─── */
+.brand-strap {
+  margin: 0 24rpx 16rpx;
+  background: linear-gradient(135deg, #FFF1E6, rgba(255, 140, 66, 0.10));
+  border-radius: 16rpx;
+  padding: 20rpx 24rpx;
 }
-
-.visitor-btn-secondary {
-  flex: 1;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  background-color: #FFFFFF;
-  border-radius: 48rpx;
-  padding: 24rpx 0;
+.brand-strap-text {
+  font-size: 26rpx;
   color: #FF8C42;
-  font-size: 28rpx;
   font-weight: 600;
-  border: 2rpx solid #FF8C42;
 }
 
 .bottom-spacer {

+ 17 - 5
cfc-frontend/pages/discover-detail/product-detail/product-detail.vue

@@ -1,5 +1,7 @@
 <template>
   <view class="container">
+    <!-- 通用登录浮层 -->
+    <login-sheet ref="loginSheet" @success="onSheetLoginSuccess" />
     <view v-if="loading" class="loading-wrap">
       <text class="loading-text">加载中...</text>
     </view>
@@ -536,10 +538,20 @@ export default {
       this.$set(item, '_selected', !item._selected)
     },
 
+    // 登录成功:刷新商品详情(会员价等以登录态为准)
+    onLoginSuccess: function() {
+      if (this.product && this.product.id) {
+        this.loadDetail(this.product.id)
+      }
+      if (!this.referralCode) {
+        this.loadReferralCode()
+      }
+    },
+
     onBuy: function() {
       var userId = uni.getStorageSync('userId')
       if (!userId) {
-        uni.navigateTo({ url: '/pages/login/login' })
+        this.openLoginSheet()
         return
       }
       var price = this.actualPrice
@@ -587,7 +599,7 @@ export default {
     onAddToCart: function() {
       var userId = uni.getStorageSync('userId')
       if (!userId) {
-        uni.navigateTo({ url: '/pages/login/login' })
+        this.openLoginSheet()
         return
       }
       uni.showLoading({ title: '加入购物车...' })
@@ -885,7 +897,7 @@ export default {
   display: flex;
   align-items: center;
   padding: 16rpx 24rpx;
-  background: linear-gradient(135deg, #667eea22, #764ba222);
+  background: linear-gradient(135deg, #F9731622, #FB923C22);
   border-radius: 16rpx;
   margin-top: 12rpx;
 }
@@ -895,7 +907,7 @@ export default {
 }
 .cf-hint-text {
   font-size: 24rpx;
-  color: #667eea;
+  color: #F97316;
   font-weight: 500;
 }
 
@@ -1129,7 +1141,7 @@ export default {
   font-size: 40rpx;
   margin-right: 16rpx;
   flex-shrink: 0;
-  background: linear-gradient(135deg, #667eea22, #764ba222);
+  background: linear-gradient(135deg, #F9731622, #FB923C22);
   border-radius: 12rpx;
 }
 .gift-select-info {

+ 8 - 1
cfc-frontend/pages/discover/index.vue

@@ -1,5 +1,7 @@
 <template>
   <view class="discover-container">
+    <!-- 通用登录浮层 -->
+    <login-sheet ref="loginSheet" @success="onSheetLoginSuccess" />
 <!-- 五行沙盘预览 -->
     <wuxing-sandbox mode="preview" badgeText="登录查看完整报告" @point-click="handleLogin" />
 
@@ -205,7 +207,12 @@ export default {
       uni.navigateTo({ url: '/pages/discover-detail/product-detail/product-detail?id=' + id })
     },
     handleLogin() {
-      uni.navigateTo({ url: '/pages/login/login?redirect=' + encodeURIComponent('/pages/discover/index') })
+      this.openLoginSheet({ redirect: '/pages/discover/index' })
+    },
+    // 登录成功:刷新推荐列表
+    onLoginSuccess() {
+      this.loadProducts()
+      this.loadFeaturedArticles()
     },
     async loadFeaturedArticles() {
       try {

+ 17 - 10
cfc-frontend/pages/energy/index.vue

@@ -1,5 +1,7 @@
 <template>
   <view class="container">
+    <!-- 登录浮层(全局组件,页面内弹出底部登录) -->
+    <login-sheet ref="loginSheet" @success="onSheetLoginSuccess" />
     <!-- 顶部导航栏 -->
     <view class="nav-bar" :style="{ background: navGradient }">
       <view class="nav-back" @click="goBack">
@@ -74,7 +76,7 @@
           :avg-scores="radarFamilyScores"
           :population-scores="[]"
           fillColor="rgba(102,126,234,0.2)"
-          strokeColor="#667eea"
+          strokeColor="#F97316"
           gridColor="rgba(102,126,234,0.15)"
           labelColor="#888"
           :width="580"
@@ -143,7 +145,7 @@ import { getFamilyEnergySandbox, getFamilyMemberList } from '../../utils/api.js'
 
 var DIM_CONFIG = [
   { code: 'body',    label: '身', icon: '💪', color: '#10B981', gradient: 'linear-gradient(135deg,#10B981,#34D399)' },
-  { code: 'mind',    label: '心', icon: '❤️', color: '#8B5CF6', gradient: 'linear-gradient(135deg,#8B5CF6,#A78BFA)' },
+  { code: 'mind',    label: '心', icon: '❤️', color: '#F97316', gradient: 'linear-gradient(135deg,#F97316,#FB923C)' },
   { code: 'wisdom',  label: '智', icon: '🧠', color: '#F59E0B', gradient: 'linear-gradient(135deg,#F59E0B,#FBBF24)' },
   { code: 'action',  label: '行', icon: '⚡', color: '#F97316', gradient: 'linear-gradient(135deg,#F97316,#FB923C)' },
   { code: 'wealth',  label: '富', icon: '💎', color: '#3B82F6', gradient: 'linear-gradient(135deg,#3B82F6,#60A5FA)' }
@@ -200,9 +202,9 @@ export default {
       return '👤'
     },
     avatarGradient() {
-      if (this.currentView === 'family') return 'linear-gradient(135deg,#667eea,#764ba2)'
+      if (this.currentView === 'family') return 'linear-gradient(135deg,#F97316,#FB923C)'
       if (this.currentMember) return this.memberColor(this.currentMember)
-      return 'linear-gradient(135deg,#8B5CF6,#A78BFA)'
+      return 'linear-gradient(135deg,#F97316,#FB923C)'
     },
     navTitle() { return this.currentMemberName + ' · 五维能量' },
     currentMemberIcon() {
@@ -216,12 +218,12 @@ export default {
       return this.currentMember.name || '我的'
     },
     navGradient() {
-      if (this.currentView === 'family') return 'linear-gradient(135deg,#667eea,#764ba2)'
+      if (this.currentView === 'family') return 'linear-gradient(135deg,#F97316,#FB923C)'
       if (this.currentMember) {
         var dim = DIM_CONFIG[0]
         return dim.gradient
       }
-      return 'linear-gradient(135deg,#667eea,#764ba2)'
+      return 'linear-gradient(135deg,#F97316,#FB923C)'
     },
     pageTitle() { return this.navTitle },
     radarDims() {
@@ -264,13 +266,18 @@ export default {
       this.isLoggedIn = !!uni.getStorageSync('token')
     },
     goLogin() {
-      uni.navigateTo({ url: '/pages/login/login' })
+      this.openLoginSheet()
+    },
+    // 登录浮层成功回调:刷新登录态与能量数据
+    onLoginSuccess() {
+      this.checkLogin()
+      if (this.isLoggedIn) this.loadData()
     },
     goBack() {
       uni.navigateBack({ fail: function() { uni.switchTab({ url: '/pages/index-home/index' }) } })
     },
     memberColor(member) {
-      if (!member) return 'linear-gradient(135deg,#8B5CF6,#A78BFA)'
+      if (!member) return 'linear-gradient(135deg,#F97316,#FB923C)'
       var types = { parent: '#F59E0B', child: '#10B981', teacher: '#3B82F6' }
       var t = member.memberType || 'parent'
       var colors = {
@@ -492,7 +499,7 @@ export default {
   width: 280rpx;
   height: 80rpx;
   line-height: 80rpx;
-  background: linear-gradient(135deg, #667eea, #764ba2);
+  background: linear-gradient(135deg, #F97316, #FB923C);
   color: #fff;
   border-radius: 40rpx;
   font-size: 28rpx;
@@ -526,7 +533,7 @@ export default {
 .overview-name { font-size: 32rpx; font-weight: 600; color: #fff; display: block; }
 .overview-role { font-size: 22rpx; color: rgba(255,255,255,0.5); margin-top: 4rpx; display: block; }
 .overview-score { text-align: right; }
-.score-value { font-size: 48rpx; font-weight: 700; color: #667eea; display: block; line-height: 1; }
+.score-value { font-size: 48rpx; font-weight: 700; color: #F97316; display: block; line-height: 1; }
 .score-label { font-size: 20rpx; color: rgba(255,255,255,0.4); }
 /* ===== 五维能量条 ===== */
 .dims-row {

+ 10 - 0
cfc-frontend/pages/growth-main/index.vue

@@ -1,5 +1,7 @@
 <template>
   <view class="container">
+    <!-- 通用登录浮层 -->
+    <login-sheet ref="loginSheet" @success="onSheetLoginSuccess" />
     <view v-if="!isLoggedIn">
       <!-- 品牌定位语 -->
       <view class="brand-strap"><text class="brand-strap-text">看见改变正在发生</text></view>
@@ -325,6 +327,14 @@ export default {
     goMoreProducts: function() { uni.navigateTo({ url: '/pages/shop/index/index' }) },
     goMoreArticles: function() { uni.navigateTo({ url: '/pages/article-center/index?dimension=action' }) },
     goPage: function(url) { uni.navigateTo({ url: url }) },
+    // 登录成功:刷新登录态数据(镜像 onShow 已登录分支)
+    onLoginSuccess: function() {
+      this.isLoggedIn = true
+      this.memberId = uni.getStorageSync('currentChildId') || uni.getStorageSync('currentMemberId') || uni.getStorageSync('userId') || ''
+      var now = new Date()
+      this.todayDate = now.getFullYear() + '-' + this.pad(now.getMonth() + 1) + '-' + this.pad(now.getDate())
+      this.loadAllData()
+    },
     formatDateKey: function(dateVal) {
       if (!dateVal) return ''
       var d = parseDate(dateVal)

+ 16 - 1
cfc-frontend/pages/health-main/index.vue

@@ -1,5 +1,7 @@
 <template>
   <view class="container">
+    <!-- 通用登录浮层 -->
+    <login-sheet ref="loginSheet" @success="onSheetLoginSuccess" />
     <block v-if="!isLoggedIn">
       <!-- 品牌定位语 -->
       <view class="brand-strap"><text class="brand-strap-text">科学管理全家健康数据</text></view>
@@ -471,6 +473,19 @@ export default {
     },
     switchTab: function(code) { this.currentTab = code },
     goPage: function(url) { uni.navigateTo({ url: url }) },
+    // 登录成功:刷新登录态数据(镜像 onShow 已登录分支)
+    onLoginSuccess: function() {
+      this.isLoggedIn = true
+      var stored = uni.getStorageSync('currentChildId') || uni.getStorageSync('currentMemberId') || ''
+      if (stored) {
+        var n = parseInt(stored, 10)
+        this.selectedMemberId = isNaN(n) ? stored : n
+        this.selectedMemberName = uni.getStorageSync('currentChildName') || '成员'
+      }
+      this.loadHealthData()
+      this.loadPlanList()
+      this.loadPendingSurveys()
+    },
     goQuiz: function(code) {
       var url = '/pages/body-detail/dimension-questionnaire?dim=' + code
       if (this.selectedMemberId) url += '&memberId=' + this.selectedMemberId
@@ -721,7 +736,7 @@ export default {
 .feat-icon { font-size: 44rpx; display: block; margin-bottom: 10rpx; }
 .feat-title { font-size: 26rpx; font-weight: 600; color: #333; display: block; margin-bottom: 4rpx; }
 .feat-desc { font-size: 22rpx; color: #999; display: block; }
-.self-test-card { margin: 0 24rpx 16rpx; background: linear-gradient(135deg, #667eea, #764ba2); border-radius: 20rpx; padding: 24rpx 28rpx; }
+.self-test-card { margin: 0 24rpx 16rpx; background: linear-gradient(135deg, #F97316, #FB923C); border-radius: 20rpx; padding: 24rpx 28rpx; }
 .self-test-head { display: flex; align-items: center; }
 .self-test-icon { font-size: 40rpx; margin-right: 16rpx; }
 .self-test-info { flex: 1; }

+ 168 - 156
cfc-frontend/pages/index-home/index.vue

@@ -1,5 +1,7 @@
 <template>
 <view>
+  <!-- 通用登录浮层 -->
+  <login-sheet ref="loginSheet" @success="onSheetLoginSuccess" />
   <!-- 冷启动 splash overlay — 覆盖首页直到数据加载完毕(tab 切回时不显示) -->
   <view class="splash-overlay" :class="{'fade-out': splashOverlayFading}" v-if="splashOverlay || splashOverlayFading">
     <view class="splash-overlay-bg">
@@ -99,17 +101,10 @@
     <!-- ② 发现区 — 上传报告 -->
 
     <FamilyFeed
-      :items="familyFeed"
+      :items="feedItems"
       :notices="notices"
-      :showHeader="true" />
-
-    <!-- 能量/积分/CF值变更通知 - 从下滑动展示 -->
-    <view v-if="showEnergyBadge || showPointsBadge || showCfBadge" class="change-notification-bar" :class="{ 'visible': isNotificationVisible }" @click="handleNotificationClick">
-      <view v-if="showEnergyBadge" class="change-item energy" @click.stop="onEnergyClick">{{ formatEnergyNotification }}</view>
-      <view v-if="showPointsBadge" class="change-item points" @click.stop="onPointsClick">{{ formatPointsNotification }}</view>
-      <view v-if="showCfBadge" class="change-item cf" @click.stop="onCfClick">{{ formatCfNotification }}</view>
-      <view class="change-dismiss" @click="dismissAll">✕</view>
-    </view>
+      :showHeader="true"
+      @viewed="onFeedItemViewed" />
 
     <!-- ④ 内容区 — 推荐文章 -->
 
@@ -430,7 +425,7 @@ import ReportUploadCard from '../../components/report-upload-card.vue'
 import OnboardingProgressBar from '../../components/OnboardingProgressBar.vue'
 import OnboardingGuide from '../../components/OnboardingGuide.vue'
 import ConfigurableGuide from '../../components/ConfigurableGuide.vue'
-import { acceptParentInvite, getActivityList, getFeaturedArticles, getEnergySandbox, getEnergyOverview, getFamilyMemberList, getChallengeList, getGuideConfig, getGuidePopupList, consumeGuidePopup, switchToFamilyMember, getMyMembership, updateChallengeProgress, respondChallenge, getTodayTasks, completeTask as completeTaskApi, getTodayParentTasks, completeParentTask as completeParentTaskApi, getTaskHistory, getNotices, getNotificationList, getSurveyStatus, getSelfCheckStatus, getReferralCode, extractReferralCode, getAppLibrary, reportAppEnter, getEnergyLogs, getPointsLogs, getCfList, getCfSummary } from '../../utils/api.js'
+import { acceptParentInvite, getActivityList, getFeaturedArticles, getEnergySandbox, getEnergyOverview, getFamilyMemberList, getChallengeList, getGuideConfig, getGuidePopupList, consumeGuidePopup, switchToFamilyMember, getMyMembership, updateChallengeProgress, respondChallenge, getTodayTasks, completeTask as completeTaskApi, getTodayParentTasks, completeParentTask as completeParentTaskApi, getTaskHistory, getNotices, getNotificationList, readNotification, getSurveyStatus, getSelfCheckStatus, getReferralCode, extractReferralCode, getAppLibrary, reportAppEnter, getEnergyLogs, getPointsLogs, getCfList, getCfSummary } from '../../utils/api.js'
 import { findAppByKey, getShortcutableApps } from '../../utils/app-library.js'
 import nav from '../../utils/nav.js'
 import config from '@/config.js'
@@ -462,6 +457,9 @@ components: {
     return {
       currentRole: _currentRole,
 
+      // 已查看的动态提醒键(点击后从动态中移除;持久化,重启/刷新后不再出现)
+      viewedFeedKeys: (uni.getStorageSync('viewedFeedKeys') || []).slice(0, 200),
+
       // splash loading overlay(冷启动时展示,数据加载完关闭)
       splashOverlay: _isFirstLaunch,
       splashOverlayFading: false,
@@ -482,7 +480,7 @@ components: {
       gradientColors: [
         'linear-gradient(135deg, #10B981, #34D399)',
         'linear-gradient(135deg, #5B9BD5, #8FC5E8)',
-        'linear-gradient(135deg, #8B5CF6, #C084FC)',
+        'linear-gradient(135deg, #F59E0B, #FBBF24)',
         'linear-gradient(135deg, #F97316, #FB923C)',
         'linear-gradient(135deg, #6366F1, #818CF8)'
       ],
@@ -527,15 +525,10 @@ membershipDays: 0,
       appUsage: {},
       quickLoaded: false,
 
-      // 能量/积分/CF值变更通知
+      // 能量/积分/CF值变更记录(合并展示于首页动态提醒,带具体值)
       energyMessages: [],
       pointsMessages: [],
-      cfMessages: [],
-      showEnergyBadge: false,
-      showPointsBadge: false,
-      showCfBadge: false,
-      // 通知栏可见性(从下往上滑入)
-      isNotificationVisible: false
+      cfMessages: []
 
 
     }
@@ -693,36 +686,100 @@ title: '添加家庭成员',
         color: 'gray'
       }
     },
-    // ===== 能量/积分/CF值变更通知格式化(标题带具体数量) =====
-    formatEnergyNotification: function() {
-      var total = 0
-      var arr = this.energyMessages || []
-      for (var i = 0; i < arr.length; i++) {
-        total += (arr[i].amount || 0)
-      }
-      var sign = total > 0 ? '+' : ''
-      var first = arr.length > 0 ? arr[0].description : '能量变动'
-      return '⚡ 能量 ' + sign + total + ' · ' + first
-    },
-    formatPointsNotification: function() {
-      var total = 0
-      var arr = this.pointsMessages || []
-      for (var i = 0; i < arr.length; i++) {
-        total += (arr[i].amount || 0)
+    // ===== 首页动态提醒列表(含具体值)=====
+    // 顺序:今日能量/积分/CF 变更(带数值)→ 家庭动态(通知/公告/任务完成)
+    feedItems: function() {
+      var list = []
+      // 1) 今日能量/积分/CF 变更(每条携带获取到的具体数值)
+      var groups = [
+        { arr: this.energyMessages, icon: '⚡', unit: '能量' },
+        { arr: this.pointsMessages, icon: '🪙', unit: '积分' },
+        { arr: this.cfMessages, icon: '💰', unit: 'CF' }
+      ]
+      for (var g = 0; g < groups.length; g++) {
+        var grp = groups[g]
+        var arr = grp.arr || []
+        for (var i = 0; i < arr.length; i++) {
+          var m = arr[i] || {}
+          var amount = m.amount || 0
+          var amountStr = (amount > 0 ? '+' : '') + amount + grp.unit
+          var desc = m.description || m.title || (grp.unit + '变动')
+          // 能量描述不带数值 → 末位拼接;积分/CF 描述本身已含数值,避免重复
+          var lineDesc = desc
+          if (grp.unit === '能量' && amount) {
+            lineDesc = desc + ' ' + amountStr
+          }
+          list.push({
+            text: grp.icon + ' ' + lineDesc,
+            time: m.time || '',
+            type: m.type || 'change',
+            title: m.title || (grp.unit + '变动'),
+            content: lineDesc,
+            amount: '',
+            unread: false,
+            _feedKey: m.id || ''
+          })
+        }
       }
-      var sign = total > 0 ? '+' : ''
-      var first = arr.length > 0 ? arr[0].description : '积分变动'
-      return '🪙 积分 ' + sign + total + ' · ' + first
-    },
-    formatCfNotification: function() {
-      var total = 0
-      var arr = this.cfMessages || []
-      for (var i = 0; i < arr.length; i++) {
-        total += (arr[i].amount || 0)
+      // 2) 家庭动态(公告/通知/任务完成,任务完成已带 +N 分具体值)
+      var feed = this.familyFeed || []
+      for (var j = 0; j < feed.length; j++) {
+        var f = feed[j]
+        if (!f) continue
+        if (f.type === 'task') {
+          list.push({
+            text: f.text || '',
+            time: f.time || '',
+            type: 'task',
+            title: (f.who || '成员') + ' 完成「' + (f.taskTitle || '任务') + '」',
+            content: '',
+            amount: f.pointsText || '+' + (f.points || 0) + '分',
+            unread: false,
+            _feedKey: f.id || ''
+          })
+        } else if (f.type === 'notification') {
+          var nn = f.notice || {}
+          list.push({
+            text: f.text || '',
+            time: f.time || '',
+            type: 'notification',
+            title: nn.title || f.text || '通知',
+            content: nn.content || '',
+            amount: '',
+            unread: !!f.unread,
+            _feedKey: 'notification-' + (nn.id !== undefined ? nn.id : ''),
+            _srcId: nn.id || ''
+          })
+        } else if (f.type === 'notice') {
+          var nb = f.notice || {}
+          list.push({
+            text: f.text || '',
+            time: f.time || '',
+            type: 'notice',
+            title: nb.title || f.text || '公告',
+            content: nb.content || '',
+            amount: '',
+            unread: false,
+            _feedKey: 'notice-' + (nb.id !== undefined ? nb.id : '')
+          })
+        } else {
+          list.push({
+            text: f.text || '',
+            time: f.time || '',
+            type: f.type || 'feed',
+            title: f.text || '',
+            content: '',
+            amount: '',
+            unread: false,
+            _feedKey: f._feedKey || ''
+          })
+        }
       }
-      var sign = total > 0 ? '+' : ''
-      var first = arr.length > 0 ? arr[0].description : 'CF值变动'
-      return '💰 CF值 ' + sign + total + ' · ' + first
+      // 已查看过的动态不再展示(点击后从提醒中移除),只保留前 20 条
+      var viewed = this.viewedFeedKeys || []
+      return list.filter(function(item) {
+        return !item._feedKey || viewed.indexOf(item._feedKey) === -1
+      }).slice(0, 20)
     }
   },
   onLoad(options) {
@@ -994,7 +1051,7 @@ title: '添加家庭成员',
           var gradientColors = [
             'linear-gradient(135deg, #10B981, #34D399)',
             'linear-gradient(135deg, #5B9BD5, #8FC5E8)',
-            'linear-gradient(135deg, #8B5CF6, #C084FC)',
+            'linear-gradient(135deg, #F59E0B, #FBBF24)',
             'linear-gradient(135deg, #F97316, #FB923C)',
             'linear-gradient(135deg, #6366F1, #818CF8)'
           ]
@@ -1079,9 +1136,15 @@ title: '添加家庭成员',
             timeStr = t.completedAt.substring(11, 16)
           }
           return {
+            id: 'task-' + (t.id !== undefined ? t.id : (t.taskId !== undefined ? t.taskId : '')),
             text: '✅ ' + (child.nickname || '小名') + ' 完成「' + (t.title || '任务') + '」 +' + points + '分',
             time: timeStr,
-            type: 'task'
+            type: 'task',
+            // 补充详情字段(首页动态提醒点击弹窗使用)
+            who: child.nickname || '小名',
+            taskTitle: t.title || '任务',
+            points: points,
+            pointsText: '+' + points + '分'
           }
         })
         self.familyFeed = feed.slice(0, 10)
@@ -1108,13 +1171,19 @@ title: '添加家庭成员',
               notice: n
             }
           })
-          self.notices = notices
+          // 已查看过的公告不再进入动态(历史消息页仍可查)
+          var viewed = self.viewedFeedKeys || []
+          self.notices = notices.filter(function(item) {
+            var raw = item.notice || {}
+            var key = 'notice-' + (raw.id !== undefined ? raw.id : '')
+            return viewed.indexOf(key) === -1
+          })
           // 合并 notices 到 familyFeed(优先显示)
-          self.familyFeed = notices.concat(self.familyFeed).slice(0, 10)
+          self.familyFeed = self.notices.concat(self.familyFeed).slice(0, 10)
         }
       }).catch(function() {})
     },
-    // W3: 用户通知入动态(仅显示未读,已读不计入 familyFeed 顶部)
+    // W3: 用户通知入动态(未读优先显示在 familyFeed 顶部)
     loadNotifications: function() {
       var self = this
       getNotificationList().then(function(res) {
@@ -1135,12 +1204,39 @@ title: '添加家庭成员',
               notice: n
             }
           })
-          // 仅显示未读通知在 familyFeed 顶部(最多10条)
+          // 只将「未读」通知合并到动态(已读通知归入历史消息页,不再回到动态)
           var unread = notifications.filter(function(n) { return n.unread })
-          self.familyFeed = unread.slice(0, 10)
+          self.familyFeed = unread.concat(self.familyFeed).slice(0, 10)
         }
       }).catch(function() {})
     },
+    // 动态提醒点击查看后:从动态中移除 + 系统通知同步标记已读(历史消息页仍可查)
+    onFeedItemViewed: function(item) {
+      if (!item || !item._feedKey) return
+      var key = item._feedKey
+      if (this.viewedFeedKeys.indexOf(key) === -1) {
+        this.viewedFeedKeys.push(key)
+        if (this.viewedFeedKeys.length > 200) {
+          this.viewedFeedKeys = this.viewedFeedKeys.slice(-200)
+        }
+        uni.setStorageSync('viewedFeedKeys', this.viewedFeedKeys)
+      }
+      // 公告同时从 notices 数组中移除(组件会经 notices prop 展示)
+      if (item.type === 'notice') {
+        var notices = this.notices || []
+        for (var ni = 0; ni < notices.length; ni++) {
+          var raw = notices[ni] && notices[ni].notice || {}
+          if (raw.id !== undefined && ('notice-' + raw.id) === key) {
+            notices.splice(ni, 1)
+            break
+          }
+        }
+      }
+      // 系统通知:服务端标记已读(下次加载不再进入动态)
+      if (item.type === 'notification' && item._srcId) {
+        readNotification({ id: item._srcId }).catch(function() {})
+      }
+    },
     // ===== 能量/积分/CF值变更通知 =====
     loadEnergyPointsCFChanges: function() {
       var self = this
@@ -1150,9 +1246,6 @@ title: '添加家庭成员',
         self.energyMessages = []
         self.pointsMessages = []
         self.cfMessages = []
-        self.showEnergyBadge = false
-        self.showPointsBadge = false
-        self.showCfBadge = false
         return
       }
       
@@ -1169,30 +1262,21 @@ title: '添加家庭成员',
         // 处理能量变更
         if (energyRes) {
           self.energyMessages = energyRes.messages
-          self.showEnergyBadge = energyRes.showBadge
         }
         
         // 处理积分变更
         if (pointsRes) {
           self.pointsMessages = pointsRes.messages
-          self.showPointsBadge = pointsRes.showBadge
         }
         
         // 处理CF值变更
         if (cfRes) {
           self.cfMessages = cfRes.messages
-          self.showCfBadge = cfRes.showBadge
         }
-        
-        // 通知栏滑入动画:数据加载完成后触发从下往上的展示
-        self.isNotificationVisible = true
       }).catch(function() {
         self.energyMessages = []
         self.pointsMessages = []
         self.cfMessages = []
-        self.showEnergyBadge = false
-        self.showPointsBadge = false
-        self.showCfBadge = false
       })
     },
     
@@ -1253,12 +1337,8 @@ title: '添加家庭成员',
             }
           }
           
-          // 存储到实例属性(供其他方法使用,或可单独暴露)
-          // 注意:这里的 messages 仅在该方法调用后有效,实际建议使用单独的数据存储
+          // 存储到实例属性(合并展示于首页动态提醒)
           self.energyMessages = messages
-          self.energyChangeTotal = totalAmount
-          self.energyChangeCount = count
-          self.showEnergyBadge = count > 0
         }
       }).catch(function() {})
     },
@@ -1323,9 +1403,6 @@ title: '添加家庭成员',
           }
           
           self.pointsMessages = messages
-          self.pointsChangeTotal = totalAmount
-          self.pointsChangeCount = count
-          self.showPointsBadge = count > 0
         }
       }).catch(function() {})
     },
@@ -1385,9 +1462,6 @@ title: '添加家庭成员',
               }
               
               self.cfMessages = messages
-              self.cfChangeTotal = totalAmount
-              self.cfChangeCount = count
-              self.showCfBadge = count > 0
             }
           }).catch(function() {})
         }
@@ -1409,43 +1483,14 @@ title: '添加家庭成员',
              date.getMonth() === today.getMonth() &&
              date.getFullYear() === today.getFullYear()
     },
-    
-    // ===== 能量/积分/CF值变更通知点击处理 =====
-    // 点击通知内容 → 跳转详情页 + 隐藏通知(点击后不再显示)
-    handleNotificationClick: function() {
-      // 点击空白区域:若有多个通知,跳转第一个可见的
-      if (this.showEnergyBadge) {
-        this.onEnergyClick()
-      } else if (this.showPointsBadge) {
-        this.onPointsClick()
-      } else if (this.showCfBadge) {
-        this.onCfClick()
-      } else {
-        this.dismissAll()
-      }
-    },
-    onEnergyClick: function() {
-      var memberId = this.currentChildId || uni.getStorageSync('currentMemberId') || ''
-      this.dismissAll()
-      uni.navigateTo({ url: '/pages/energy/detail?memberId=' + memberId })
-    },
-    onPointsClick: function() {
-      this.dismissAll()
-      uni.navigateTo({ url: '/pages/rewards/rewards' })
-    },
-    onCfClick: function() {
-      this.dismissAll()
-      uni.navigateTo({ url: '/pages/promotion/index' })
-    },
-    // 关闭按钮:仅隐藏,不跳转
-    dismissAll: function() {
-      this.isNotificationVisible = false
-      this.showEnergyBadge = false
-      this.showPointsBadge = false
-      this.showCfBadge = false
-      this.energyMessages = []
-      this.pointsMessages = []
-      this.cfMessages = []
+// ===== 时间格式化 =====
+    // 时间格式化 yyyy-MM-dd HH:mm(iOS 安全:经 utils/format.js parseDate 归一化)
+    formatTime: function(dateStr) {
+      var d = parseDate(dateStr)
+      if (!d) return ''
+      function pad2(n) { return n < 10 ? '0' + n : '' + n }
+      return d.getFullYear() + '-' + pad2(d.getMonth() + 1) + '-' + pad2(d.getDate()) +
+        ' ' + pad2(d.getHours()) + ':' + pad2(d.getMinutes())
     },
     quickCompleteTask: function(task) {
       var self = this
@@ -1829,7 +1874,7 @@ title: '添加家庭成员',
           var gradientColors = [
             'linear-gradient(135deg, #10B981, #34D399)',
             'linear-gradient(135deg, #5B9BD5, #8FC5E8)',
-            'linear-gradient(135deg, #8B5CF6, #C084FC)',
+            'linear-gradient(135deg, #F59E0B, #FBBF24)',
             'linear-gradient(135deg, #F97316, #FB923C)',
             'linear-gradient(135deg, #6366F1, #818CF8)'
           ]
@@ -1869,7 +1914,11 @@ title: '添加家庭成员',
       uni.navigateTo({ url: '/pages/article-center/article-detail?id=' + id })
     },
     handleLogin() {
-      uni.navigateTo({ url: '/pages/login/login?redirect=' + encodeURIComponent('/pages/index-home/index') })
+      this.openLoginSheet({ redirect: '/pages/index-home/index' })
+    },
+    // 登录成功:刷新首页登录态数据
+    onLoginSuccess() {
+      this.loadLoggedInData()
     },
     async handleParentInvite(code) {
       // 延迟显示,等待登录完成
@@ -2331,7 +2380,7 @@ title: '添加家庭成员',
 }
 .section-more {
   font-size: 24rpx;
-  color: #5B9BD5;
+  color: #F97316;
 }
 .empty-state {
   display: flex;
@@ -2658,7 +2707,7 @@ title: '添加家庭成员',
 /* ===== ① 了解区 — 自测大卡(引导"从了解开始") ===== */
 .self-test-card {
   margin: 20rpx 30rpx 16rpx;
-  background: linear-gradient(135deg, #667eea, #764ba2);
+  background: linear-gradient(135deg, #F97316, #FB923C);
   border-radius: 20rpx;
   padding: 24rpx 28rpx;
 }
@@ -2726,7 +2775,7 @@ title: '添加家庭成员',
 .guide-bar-purple .guide-bar-icon-wrap { background: rgba(139,92,246,0.12); }
 .guide-bar-purple .guide-bar-title { color: #4C1D95; }
 .guide-bar-purple .guide-bar-sub { color: #6D28D9; }
-.guide-bar-purple .guide-bar-btn { background: linear-gradient(135deg, #8B5CF6, #A78BFA); color: #fff; }
+.guide-bar-purple .guide-bar-btn { background: linear-gradient(135deg, #F97316, #FB923C); color: #fff; }
 
 .guide-bar-gray { background: #F8FAFC; border: 1rpx solid rgba(100,116,139,0.15); }
 .guide-bar-gray .guide-bar-icon-wrap { background: rgba(100,116,139,0.10); }
@@ -2860,41 +2909,4 @@ title: '添加家庭成员',
   border: none;
 }
 .self-test-badge { display: inline-block; align-self: flex-start; margin-top: 8rpx; font-size: 20rpx; color: #F97316; background: rgba(249,115,22,0.1); padding: 4rpx 14rpx; border-radius: 999rpx; }
-
-/* ===== 能量/积分/CF值变更通知条(从下滑入动画) ===== */
-.change-notification-bar {
-  position: fixed;
-  bottom: -80rpx;
-  left: 30rpx;
-  right: 30rpx;
-  padding: 16rpx 24rpx;
-  background: linear-gradient(135deg, #FFF7ED, #FFEDD5);
-  border-radius: 16rpx;
-  box-shadow: 0 2rpx 8rpx rgba(249,115,22,0.12);
-  transition: bottom 0.3s cubic-bezier(0.16,1,0.3,1) forwards;
-  z-index: 100;
-}
-.change-notification-bar.visible {
-  bottom: 0;
-  animation: slide-up-notice 0.4s cubic-bezier(0.16,1,0.3,1) forwards;
-}
-@keyframes slide-up-notice {
-  from { opacity: 0; transform: translateY(80rpx); }
-  to   { opacity: 1; transform: translateY(0); }
-}
-.change-item {
-  font-size: 22rpx;
-  font-weight: 500;
-  color: #333;
-}
-.change-item.energy { color: #F97316; }
-.change-item.points { color: #10B981; }
-.change-item.cf { color: #6366F1; }
-.change-dismiss {
-  margin-left: auto;
-  font-size: 24rpx;
-  color: #999;
-  flex-shrink: 0;
-  cursor: pointer;
-}
 </style>

+ 22 - 62
cfc-frontend/pages/invite/join.vue

@@ -1,5 +1,7 @@
 <template>
   <view class="page">
+    <!-- 通用登录浮层 -->
+    <login-sheet ref="loginSheet" @success="onSheetLoginSuccess" />
     <!-- 加载中 -->
     <view class="loading" v-if="loading">
       <view class="loading-icon">⏳</view>
@@ -82,7 +84,7 @@
 </template>
 
 <script>
-import { validateInvitation, validateFamilyCode, checkUserFamily, bindReferral, getMyJoinRequest } from '@/utils/api.js'
+import { validateInvitation, validateFamilyCode, checkUserFamily, bindReferral } from '@/utils/api.js'
 
 export default {
   data() {
@@ -99,7 +101,6 @@ export default {
       isLoggedIn: false,
       inviteInfo: null,
       familyStatus: null,
-      joinRequest: null,
       error: '',
       // 动态文案
       tipText: '',
@@ -225,11 +226,13 @@ export default {
             this.$store && this.$store.commit('setFamilyId', res.data.familyId)
             uni.setStorageSync('familyId', res.data.familyId)
           }
-          // 已在目标家庭
-          if (this.inviteInfo && this.familyStatus.inFamily &&
+          // 已在目标家庭 -> 直接进首页
+          if (this.inviteInfo && this.familyStatus.inFamily && 
               this.familyStatus.familyId === this.inviteInfo.familyId) {
-            // 检查加入请求状态
-            await this.checkJoinRequestStatus()
+            this.setRedirectInfo(true)
+            this.loading = false
+            // 延迟跳转,让用户看到成功提示
+            setTimeout(() => this.goHome(), 1000)
             return
           }
           // 不在目标家庭 -> 去完善个人信息页提交申请
@@ -246,45 +249,6 @@ export default {
       }
     },
 
-    // 检查加入请求状态
-    async checkJoinRequestStatus() {
-      try {
-        var reqRes = await getMyJoinRequest()
-        if (reqRes && reqRes.data) {
-          var req = reqRes.data
-          if (req && req.status === 'pending') {
-            // 已提交申请但等待审批
-            this.tipText = '您的加入申请已提交,等待邀请人同意'
-            this.btnText = '查看申请状态'
-            this.joinRequest = req
-            this.loading = false
-            return
-          } else if (req && req.status === 'approved') {
-            // 已批准,直接进入成员管理页面
-            this.loading = false
-            uni.redirectTo({
-              url: '/pages/profile-extra/family-members'
-            })
-            return
-          } else if (req && req.status === 'rejected') {
-            // 已拒绝
-            this.tipText = '您的加入申请已被拒绝,如需重新申请请联系邀请人'
-            this.btnText = '返回首页'
-            this.loading = false
-            return
-          }
-        }
-        // 无请求记录,视为已是正式成员
-        this.setRedirectInfo(true)
-        this.loading = false
-      } catch (e) {
-        console.log('检查加入请求状态失败', e)
-        // 降级处理:直接进入首页
-        this.setRedirectInfo(true)
-        this.loading = false
-      }
-    },
-
     setRedirectInfo(isInTargetFamily) {
       if (isInTargetFamily) {
         this.tipText = '您已是该家庭成员'
@@ -300,17 +264,9 @@ export default {
       if (this.redirecting) return
       this.redirecting = true
 
-      // 已在目标家庭且请求已批准 -> 直接进入成员管理页面
+      // 已在目标家庭 -> 直接进首页
       if (this.inviteInfo && this.familyStatus && this.familyStatus.inFamily &&
           this.familyStatus.familyId === this.inviteInfo.familyId) {
-        if (this.joinRequest && this.joinRequest.status === 'approved') {
-          this.redirecting = false
-          uni.redirectTo({
-            url: '/pages/profile-extra/family-members'
-          })
-          return
-        }
-        // 已在家庭但请求 pending,不重复跳转,直接返回首页
         this.redirecting = false
         this.goHome()
         return
@@ -340,18 +296,22 @@ export default {
       uni.reLaunch({ url: '/pages/index-home/index' })
     },
 
-    // 登录页跳转(带上邀请码)
+    // 打开登录浮层(家庭邀请码已存 pendingFamilyInviteCode,登录成功后走家庭邀请流程)
     goLogin() {
-      uni.navigateTo({ 
-        url: '/pages/login/login?familyInviteCode=' + this.familyInviteCode 
-      })
+      this.openLoginSheet()
     },
 
-    // 推广码场景:去登录
+    // 推广码场景:打开登录浮层(refCode 交给登录浮层静默绑定)
     goLoginWithRef() {
-      uni.navigateTo({ 
-        url: '/pages/login/login?refCode=' + this.refCode 
-      })
+      this.openLoginSheet({ refCode: this.refCode })
+    },
+
+    // 登录成功:刷新登录态并重新校验邀请
+    onLoginSuccess() {
+      this.checkLoginStatus()
+      if (this.familyInviteCode) {
+        this.validateFamilyInvite(this.familyInviteCode)
+      }
     },
 
     // 推广码绑定

+ 26 - 517
cfc-frontend/pages/login/login.vue

@@ -1,540 +1,49 @@
 <template>
-  <view class="login-container">
-    <!-- 顶部品牌区 -->
-    <view class="brand-section">
-      <image class="logo" src="/static/logo.png" mode="aspectFit" @error="handleImageError"></image>
-      <text class="app-name">浠艾福</text>
-      <text class="identity">您的专属家庭主动健康管理师</text>
-      <text class="slogan">家里每个人的状态,你真的看见了?</text>
-    </view>
-
-    <!-- 主动健康理念 + 闭环 -->
-    <view class="concept-card">
-      <view class="concept-desc">
-        不等问题发生,先一步<text class="concept-highlight">识别风险 · 主动干预 · 持续评估</text>,把全家健康管理融入每一天。
-      </view>
-      <view class="loop-row">
-        <view class="loop-pill loop-pill-indigo"><text>🔍</text><text class="loop-pill-text">提前识别</text></view>
-        <text class="loop-arrow">→</text>
-        <view class="loop-pill loop-pill-green"><text>🛡️</text><text class="loop-pill-text">风险管控</text></view>
-        <text class="loop-arrow">→</text>
-        <view class="loop-pill loop-pill-amber"><text>⚡</text><text class="loop-pill-text">主动干预</text></view>
-        <text class="loop-arrow">→</text>
-        <view class="loop-pill loop-pill-red"><text>📊</text><text class="loop-pill-text">效果评估</text></view>
-      </view>
-    </view>
-
-    <!-- 五维标签 -->
-    <view class="dims-row">
-      <view class="dim-tag dim-tag-body"><text>🌏</text><text>身·健康</text></view>
-      <view class="dim-tag dim-tag-wisdom"><text>⚡</text><text>智·赋能</text></view>
-      <view class="dim-tag dim-tag-wealth"><text>💧</text><text>富·创富</text></view>
-      <view class="dim-tag dim-tag-action"><text>🌿</text><text>行·和谐</text></view>
-      <view class="dim-tag dim-tag-mind"><text>🔥</text><text>心·大爱</text></view>
-    </view>
-
-    <!-- 登录表单 -->
-    <view class="login-form">
-      <view class="phone-section">
-        <!--
-          disabled 必须同时受 agreed 和 loading 约束。
-          open-type="getPhoneNumber" 在「点击那一刻」就由渲染层向微信发起授权请求,
-          早于 @getphonenumber 回调执行 —— 也就是说每一次点击都会消耗一次平台调用配额,
-          JS 里的 if (!this.agreed) return 只能弹 toast,拦不住已经发生的调用。
-          未同意协议时按钮若仍可点,用户会因「点了没反应」而反复点击,最终触发微信限频:
-          [渲染层错误] invoke getPhoneNumber too frequently。
-          故未勾选协议时直接禁用按钮,让无效点击根本不会发生。
-        -->
-        <button
-          class="phone-btn"
-          open-type="getPhoneNumber"
-          @getphonenumber="getPhoneNumber"
-          @tap="onPhoneBtnTap"
-          :loading="loading"
-          :disabled="!agreed || loading || tapLock"
-        >
-          <text class="phone-btn-icon">📱</text>
-          <text>手机号快捷登录</text>
-        </button>
-      </view>
-
-      <!-- 暂不登录 -->
-      <view class="skip-section">
-        <text class="skip-btn" @click="handleSkipLogin">暂不登录,先逛逛</text>
-      </view>
-
-      <view class="agreement">
-        <checkbox-group @change="onAgreementChange">
-          <label>
-            <checkbox value="agree" :checked="agreed" />
-            <text class="agreement-text">我已阅读并同意</text>
-          </label>
-        </checkbox-group>
-        <text class="link" @click="showAgreement">《用户协议》</text>
-        <text class="agreement-text">和</text>
-        <text class="link" @click="showPrivacy">《隐私政策》</text>
-      </view>
-    </view>
+  <view class="login-page">
+    <!--
+      兜底整页登录壳:全部登录逻辑已迁移至全局组件 login-sheet(components/login-sheet.vue)。
+      历史整页登录的 URL 参数(?inviteCode=&inviteType=&refCode=&redirect=)在 onLoad 透传给浮层。
+      standalone 模式语义:登录成功(无跳转需求)/ 暂不登录 → 回首页,保持旧整页登录行为。
+      触发场景:登出流程的 reLaunch 到本页、或宿主页面未挂载浮层时的 openLoginSheet() 回退。
+    -->
+    <login-sheet ref="loginSheet" :standalone="true" />
   </view>
 </template>
 
 <script>
-import { wechatPhoneLogin, checkTeamInviteStatus, joinTeamByInvite, bindReferralIfNotSelf } from '../../utils/api.js'
-
 export default {
   data() {
     return {
-      loading: false,
-      // tapLock:在「点击」瞬间上锁,用于关闭授权弹窗弹出前的连点窗口(见 onPhoneBtnTap)
-      tapLock: false,
-      agreed: false,
-      nickname: '',
-      avatar: '',
-      redirectUrl: ''
+      _sheetOptions: {}
     }
   },
   onLoad(options) {
-      // 处理邀请码
-      if (options.inviteCode) {
-        uni.setStorageSync('inviteCode', options.inviteCode)
-        uni.setStorageSync('inviteType', options.inviteType || 'family')
-      }
-      // 处理推荐码(refCode):来自 join.vue 的推广绑定流程,推荐码统一存 refCode storage
-      if (options.refCode) {
-        uni.setStorageSync('refCode', options.refCode)
-      }
-      // 存储登录后跳转地址
-      if (options.redirect) {
-        this.redirectUrl = decodeURIComponent(options.redirect)
-      }
-      // 注意:不在 onLoad 预取 login code —— 微信 code 一次性有效(5分钟且用过即废),
-      // 预取后如果用户隔几分钟再点击授权,code 已失效会报 invalid code (40029)。
-      // 正确做法:getPhoneNumber 中用户点击授权时实时获取。
-    },
-  onUnload() {
-    // 清理 tap 锁兜底定时器,避免登录成功 reLaunch 后定时器仍在已销毁页面上置值
-    if (this._tapLockTimer) {
-      clearTimeout(this._tapLockTimer)
-      this._tapLockTimer = null
+    this._sheetOptions = {
+      inviteCode: (options && options.inviteCode) || '',
+      inviteType: (options && options.inviteType) || '',
+      refCode: (options && options.refCode) || '',
+      redirect: (options && options.redirect) || ''
     }
   },
-  methods: {
-    handleImageError(e) {
-      console.log('Logo 加载失败,使用默认显示')
-    },
-    onAgreementChange(e) {
-      this.agreed = e.detail.value.length > 0
-    },
-    showAgreement() {
-      uni.navigateTo({
-        url: '/pages/policy/agreement'
-      })
-    },
-    showPrivacy() {
-      uni.navigateTo({
-        url: '/pages/policy/privacy'
-      })
-    },
-    handleSkipLogin() {
-      // 不登录直接进入首页
-      uni.switchTab({ url: '/pages/index-home/index' })
-    },
-    // 点击瞬间上锁,关闭「授权弹窗弹出前」的连点窗口。
-    //
-    // 为什么不能只在 getPhoneNumber 里判 loading:
-    // open-type="getPhoneNumber" 在点击那一刻就由渲染层向微信发起了授权调用,
-    // @getphonenumber 回调要等用户在系统弹窗里点完才返回。这中间几百毫秒内
-    // loading 还是 false、按钮仍可点,用户「以为没反应」就连点,
-    // 每次点击都真实消耗一次平台调用配额 → invoke getPhoneNumber too frequently。
-    // 在 @tap 上锁能让按钮在一个渲染周期内变为不可点,早于弹窗出现。
-    onPhoneBtnTap() {
-      if (this.loading || this.tapLock) return
-      this.tapLock = true
-      // 兜底:若因系统异常导致 getphonenumber 始终不回调,3 秒后强制解锁,
-      // 避免按钮永久锁死导致无法再登录。
-      if (this._tapLockTimer) clearTimeout(this._tapLockTimer)
-      this._tapLockTimer = setTimeout(() => {
-        this.tapLock = false
-        this._tapLockTimer = null
-      }, 3000)
-    },
-    getPhoneNumber: function(e) {
-      // 授权流程已结束,释放 tap 锁与兜底定时器,按钮恢复可点。
-      // 注意:这里原有的 `if (this.loading) return` 已移除 —— loading 在本函数内才置 true,
-      // 保留它会把首个合法点击也拦掉;防重复消费改由下面的 tapLock 承担。
-      if (this._tapLockTimer) {
-        clearTimeout(this._tapLockTimer)
-        this._tapLockTimer = null
-      }
-      this.tapLock = false
-
-      if (!this.agreed) {
-        uni.showToast({ title: '请先同意用户协议', icon: 'none' })
-        return
-      }
-      if (!e.detail || !e.detail.code) {
-        uni.showToast({ title: '授权失败,请重试', icon: 'none' })
-        return
-      }
-      var self = this
-      self.loading = true
-      // 点击授权时实时获取 login code(微信 code 一次性有效,不能复用旧 code)
-      uni.login({
-        provider: 'weixin',
-        success: function(loginRes) {
-          if (!loginRes.code) {
-            uni.showToast({ title: '网络异常,请重试', icon: 'none' })
-            self.loading = false
-            return
-          }
-          wechatPhoneLogin({ code: loginRes.code, phoneCode: e.detail.code }).then(function(res) {
-            self.completeLogin(res.data)
-          }).catch(function(err) {
-            uni.showToast({ title: err.message || '登录失败', icon: 'none' })
-            self.loading = false
-          })
-        },
-        fail: function() {
-          uni.showToast({ title: '网络异常,请重试', icon: 'none' })
-          self.loading = false
-        }
-      })
-    },
-    completeLogin: function(data) {
-      var self = this
-      uni.setStorageSync('token', data.token)
-      uni.setStorageSync('userId', data.userId)
-      uni.setStorageSync('role', data.role)
-      uni.setStorageSync('currentRole', data.role)
-      uni.setStorageSync('isSwitchedChild', false)
-      uni.setStorageSync('isSwitchedTeacher', false)
-      if (data.openid) uni.setStorageSync('openid', data.openid)
-      // familyId 必须持久化:家庭在注册时已由后端创建,缺失会导致页面误判无家庭而重复创建
-      uni.setStorageSync('userInfo', { nickname: data.nickname, userId: data.userId, familyId: data.familyId, vendorType: data.vendorType, activityProviderStatus: data.activityProviderStatus })
-      if (data.phone) uni.setStorageSync('phone', data.phone)
-      if (data.teacherStatus) uni.setStorageSync('teacherStatus', data.teacherStatus)
-      if (data.teacherRejectReason) uni.setStorageSync('teacherRejectReason', data.teacherRejectReason)
-      self.$store.commit('setFamilyId', data.familyId)
-      uni.showToast({ title: '登录成功', icon: 'success' })
-      self.loading = false
-      // 注册零填写:新用户(needsRoleSelect)不再跳信息填写页,直接进首页
-      // 家庭邀请衔接:有 pendingFamilyInviteCode(join.vue 写入)→ 直接进 join-form 填真实姓名+家庭身份
-      var pendingInvite = uni.getStorageSync('pendingFamilyInviteCode')
-      if (pendingInvite) {
-        uni.redirectTo({ url: '/pages/invite/join-form?familyInviteCode=' + encodeURIComponent(pendingInvite) })
-      } else {
-        self.handlePostLoginRouting()
-      }
-    },
-    handlePostLoginRouting() {
-      const inviteCode = uni.getStorageSync('inviteCode')
-      const inviteType = uni.getStorageSync('inviteType')
-      const refCode = uni.getStorageSync('refCode')
-
-      if (inviteCode && inviteType === 'team') {
-        this.handleTeamInviteFlow(inviteCode)
-      } else if (refCode) {
-        this.handleReferralBind(refCode)
-      } else if (inviteCode) {
-        // 兼容:历史 storage inviteCode 承载推荐码的残留
-        this.handleReferralBind(inviteCode)
-      } else {
-        this.navigateToHome()
-      }
-    },
-    async handleReferralBind(refCode) {
-      try {
-var res = await bindReferralIfNotSelf(refCode)
-        if (res && res.code === 200) {
-          console.log('登录后绑定推荐人成功', refCode)
-        }
-      } catch (e) {
-        // 后端 Result.error(code=500) → request() 已 toast「已绑定推荐人,不可修改」等业务消息
-        if (e && e.code !== undefined && e.code !== 401) {
-          console.log('登录后推荐码业务拒绝', refCode, e.message || '')
-        } else {
-          console.log('登录后推荐码绑定失败', e)
-        }
-      }
-      uni.removeStorageSync('refCode')
-      uni.removeStorageSync('inviteCode')
-      uni.removeStorageSync('inviteType')
-      this.navigateToHome()
-    },
-    async handleTeamInviteFlow(inviteCode) {
-      try {
-        const res = await checkTeamInviteStatus(inviteCode)
-        const status = res.data.status
-
-        if (status === 'not_planner') {
-          uni.redirectTo({ url: `/pages/guide/register/index?inviteCode=${inviteCode}` })
-        } else if (status === 'not_in_team') {
-          uni.showModal({
-            title: '加入团队',
-            content: '是否加入该规划师团队?',
-            success: async (confirmRes) => {
-              if (confirmRes.confirm) {
-                try {
-                  await joinTeamByInvite(inviteCode)
-                  uni.showToast({ title: '加入成功', icon: 'success' })
-                  uni.removeStorageSync('inviteCode')
-                  uni.removeStorageSync('inviteType')
-                  this.navigateToHome()
-                } catch (e) {
-                  uni.showToast({ title: e.message || '加入失败', icon: 'none' })
-                }
-              } else {
-                this.navigateToHome()
-              }
-            }
-          })
-        } else if (status === 'already_in_team') {
-          uni.showModal({
-            title: '加入新团队',
-            content: '您已在其他团队中,离开当前团队并加入新团队?',
-            success: async (confirmRes) => {
-              if (confirmRes.confirm) {
-                try {
-                  await joinTeamByInvite(inviteCode)
-                  uni.showToast({ title: '加入成功', icon: 'success' })
-                  uni.removeStorageSync('inviteCode')
-                  uni.removeStorageSync('inviteType')
-                  this.navigateToHome()
-                } catch (e) {
-                  uni.showToast({ title: e.message || '加入失败', icon: 'none' })
-                }
-              } else {
-                this.navigateToHome()
-              }
-            }
-          })
-        } else {
-          this.navigateToHome()
-        }
-      } catch (e) {
-        uni.showToast({ title: e.message || '处理邀请失败', icon: 'none' })
-        this.navigateToHome()
-      }
-    },
-    navigateToHome() {
-      if (this.redirectUrl) {
-        const redirect = this.redirectUrl
-        this.redirectUrl = ''
-        // 注意: redirectTo 不支持跳转 tabBar 页面,必须用 switchTab
-        // 只有 pages.json tabBar.list 注册的页面才能用 switchTab
-        var tabBarPages = ['/pages/index-home/index', '/pages/wealth/index']
-        if (tabBarPages.indexOf(redirect) !== -1) {
-          uni.switchTab({ url: redirect })
-        } else {
-          uni.redirectTo({ url: redirect })
-        }
-      } else {
-        uni.switchTab({ url: '/pages/index-home/index' })
-      }
+  mounted() {
+    // 页面渲染完成后自动弹出浮层(子组件先于父组件 mounted,$refs 可用)
+    if (this.$refs.loginSheet) {
+      this.$refs.loginSheet.open(this._sheetOptions)
+    }
+  },
+  onUnload() {
+    // 清理 tap 锁兜底定时器,避免登录成功跳转后定时器仍在已销毁组件上置值(组件自身也会清理)
+    if (this.$refs && this.$refs.loginSheet && this.$refs.loginSheet._tapLockTimer) {
+      clearTimeout(this.$refs.loginSheet._tapLockTimer)
+      this.$refs.loginSheet._tapLockTimer = null
     }
   }
 }
 </script>
 
 <style scoped>
-.login-container {
+.login-page {
   min-height: 100vh;
   background: linear-gradient(180deg, #FFF9F0 0%, #FFEEDB 100%);
-  padding: 40rpx 36rpx 32rpx;
-  display: flex;
-  flex-direction: column;
-}
-
-/* ===== 顶部品牌区 ===== */
-.brand-section {
-  text-align: center;
-  margin-bottom: 32rpx;
-}
-
-.logo {
-  width: 104rpx;
-  height: 104rpx;
-  border-radius: 52rpx;
-  background: #fff;
-  box-shadow: 0 8rpx 24rpx rgba(249,115,22,0.2);
-}
-
-.app-name {
-  display: block;
-  font-size: 44rpx;
-  font-weight: bold;
-  color: #7C2D12;
-  margin-top: 14rpx;
-  letter-spacing: 6rpx;
-}
-
-.identity {
-  display: block;
-  font-size: 26rpx;
-  font-weight: 600;
-  color: #C2410C;
-  margin-top: 10rpx;
-}
-
-.slogan {
-  display: block;
-  font-size: 24rpx;
-  color: #9A3412;
-  margin-top: 8rpx;
-}
-
-/* ===== 主动健康理念 + 闭环 ===== */
-.concept-card {
-  background: #fff;
-  border-radius: 20rpx;
-  padding: 26rpx 28rpx;
-  margin-bottom: 24rpx;
-  box-shadow: 0 4rpx 16rpx rgba(249,115,22,0.08);
-}
-
-.concept-desc {
-  font-size: 24rpx;
-  color: #4B5563;
-  line-height: 1.6;
-  margin-bottom: 20rpx;
-}
-
-.concept-highlight {
-  color: #B45309;
-  font-weight: 600;
-}
-
-.loop-row {
-  display: flex;
-  align-items: center;
-  gap: 6rpx;
-}
-
-.loop-pill {
-  flex: 1;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  gap: 4rpx;
-  flex-direction: column;
-  font-size: 20rpx;
-  padding: 10rpx 4rpx;
-  border-radius: 12rpx;
-  line-height: 1.3;
-}
-
-.loop-pill-text {
-  font-size: 20rpx;
-  font-weight: 500;
-}
-
-.loop-pill-indigo { background: #EEF2FF; color: #4F46E5; }
-.loop-pill-green { background: #D1FAE5; color: #047857; }
-.loop-pill-amber { background: #FEF3C7; color: #B45309; }
-.loop-pill-red { background: #FEE2E2; color: #B91C1C; }
-
-.loop-arrow {
-  font-size: 24rpx;
-  color: #D1D5DB;
-  flex-shrink: 0;
-}
-
-/* ===== 五维标签 ===== */
-.dims-row {
-  display: flex;
-  justify-content: center;
-  gap: 8rpx;
-  margin-bottom: 24rpx;
-  flex-wrap: wrap;
-}
-
-.dim-tag {
-  display: flex;
-  align-items: center;
-  gap: 4rpx;
-  font-size: 20rpx;
-  color: #fff;
-  font-weight: 500;
-  padding: 8rpx 14rpx;
-  border-radius: 24rpx;
-  white-space: nowrap;
-}
-
-.dim-tag-body { background: #F97316; }
-.dim-tag-wisdom { background: #6366F1; }
-.dim-tag-wealth { background: #D97706; }
-.dim-tag-action { background: #10B981; }
-.dim-tag-mind { background: #EC4899; }
-
-/* ===== 登录表单 ===== */
-.login-form {
-  background: #fff;
-  border-radius: 20rpx;
-  padding: 36rpx 36rpx 28rpx;
-  margin-top: 48rpx;
-  box-shadow: 0 4rpx 24rpx rgba(0,0,0,0.06);
-}
-
-.phone-section {
-  margin-bottom: 24rpx;
-}
-
-.phone-btn {
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  gap: 12rpx;
-  width: 100%;
-  height: 88rpx;
-  background: #07c160;
-  color: #fff;
-  border-radius: 44rpx;
-  font-size: 32rpx;
-  font-weight: 500;
-  border: none;
-}
-
-.phone-btn::after {
-  border: none;
-}
-
-.phone-btn-icon {
-  font-size: 36rpx;
-}
-
-/* ===== 跳过登录 ===== */
-.skip-section {
-  text-align: center;
-  margin-bottom: 20rpx;
-}
-
-.skip-btn {
-  font-size: 26rpx;
-  color: #F97316;
-  font-weight: 500;
-  padding: 10rpx 20rpx;
-  background: rgba(249, 115, 22, 0.08);
-  border-radius: 30rpx;
-}
-
-/* ===== 协议 ===== */
-.agreement {
-  margin-top: 4rpx;
-  text-align: center;
-  font-size: 22rpx;
-  line-height: 1.6;
-}
-
-.agreement checkbox {
-  transform: scale(0.8);
-}
-
-.agreement-text {
-  color: #9CA3AF;
-}
-
-.link {
-  color: #5B9BD5;
-  text-decoration: underline;
 }
 </style>

+ 51 - 13
cfc-frontend/pages/mind-detail/index.vue

@@ -1,5 +1,7 @@
 <template>
   <view class="container">
+    <!-- 通用登录浮层 -->
+    <login-sheet ref="loginSheet" @success="onSheetLoginSuccess" />
     <!-- Tabbar 切换过渡页 -->
     <tab-transition v-if="showTabTransition" dimCode="mind" ref="tabTransition" @close="showTabTransition = false" />
 
@@ -37,6 +39,20 @@
       <text class="guidance-text">{{ monthlyGuidance }}</text>
     </view>
 
+    <!-- 游客价值卡(统一组件:维度色 CTA + 价值预览) -->
+    <DimensionVisitorCard
+      v-if="!isLoggedIn"
+      dimCode="mind"
+      card-title="心维度 · 心理成长"
+      :benefits="[
+        '心理测评 EQ 报告与情绪画像',
+        '情绪与肠胃关联解读',
+        '家庭关系质量分析'
+      ]"
+      cta-text="登录查看心理画像"
+      secondary-text="了解心理维度"
+      secondary-url="/pages/mind-detail/dimension-intro" />
+
         <!-- 登录后:五维能量条 -->
         <FamilyEnergyBar
           v-if="isLoggedIn"
@@ -217,6 +233,7 @@
 import TabTransition from '../../components/tab-transition.vue'
 import PageBanner from '../../components/PageBanner.vue'
 import DimensionIntroCard from '../../components/DimensionIntroCard.vue'
+import DimensionVisitorCard from '../../components/DimensionVisitorCard.vue'
 import RadarChart from '../../components/RadarChart.vue'
 import FamilyEnergyBar from '../../components/FamilyEnergyBar.vue'
 import PsychCrisisBanner from '@/pages/mind-detail/components/PsychCrisisBanner.vue'
@@ -229,7 +246,7 @@ import config from '../../config.js'
 import { parseDate } from '../../utils/format.js'
 
 export default {
-components: { TabTransition, PageBanner, RadarChart, FamilyEnergyBar, PsychCrisisBanner, FloatingAvatar, FamilyTianpanCard, FamilyMemberStrip, DimensionIntroCard },
+components: { TabTransition, PageBanner, RadarChart, FamilyEnergyBar, PsychCrisisBanner, FloatingAvatar, FamilyTianpanCard, FamilyMemberStrip, DimensionIntroCard, DimensionVisitorCard },
   data() {
     return {
       isLoggedIn: false,
@@ -542,7 +559,7 @@ var descList = descMap[this.dominantElement] || descMap.wood
         { key: 'conscientiousnessScore', label: '尽责性', color: '#F59E0B', desc: '做事有条理、自律程度' },
         { key: 'extraversionScore', label: '外向性', color: '#10B981', desc: '社交活跃与外向程度' },
         { key: 'agreeablenessScore', label: '宜人性', color: '#FF8C42', desc: '与人相处的和谐程度' },
-        { key: 'neuroticismScore', label: '神经质', color: '#8B5CF6', desc: '情绪波动程度(反向)' }
+        { key: 'neuroticismScore', label: '神经质', color: '#F97316', desc: '情绪波动程度(反向)' }
       ]
       var result = { overall: 0, tierLabel: '', summary: '', dims: [], tip: '' }
       var total = 0
@@ -866,7 +883,7 @@ var descList = descMap[this.dominantElement] || descMap.wood
         uni.showModal({
           title: '提示',
           content: '请先登录后使用此功能',
-          success: function(res) { if (res.confirm) uni.navigateTo({ url: '/pages/login/login' }) }
+          success: (res) => { if (res.confirm) this.openLoginSheet() }
         })
         return
       }
@@ -1032,7 +1049,28 @@ var descList = descMap[this.dominantElement] || descMap.wood
       uni.pageScrollTo({ selector: '#' + section, duration: 300 })
     },
     goAssessment: function() { uni.navigateTo({ url: '/pages/assessment/apply' }) },
-    goLogin: function() { uni.navigateTo({ url: '/pages/login/login' }) },
+    goLogin: function() { this.openLoginSheet() },
+    // 登录成功:刷新登录态数据(镜像 onShow 已登录分支)
+    onLoginSuccess: function() {
+      this.isLoggedIn = true
+      this.role = uni.getStorageSync('currentRole') || uni.getStorageSync('role') || null
+      this.currentChildId = (this.$store.state && this.$store.state.currentChildId) || uni.getStorageSync('currentChildId') || null
+      this.userName = uni.getStorageSync('nickname') || uni.getStorageSync('userName') || '家长'
+      this.loadSectionConfig()
+      this.loadChildren()
+      this.loadFamilyMembersVisible()
+      this.loadSandboxData()
+      this.loadMindRadarPopulationData()
+      this.loadEmiData()
+      this.loadGutData()
+      this.loadEnergyData()
+      this.loadTraditionalCultureData()
+      this.loadContacts()
+      this.loadHealthAlerts()
+      this.loadMilestones()
+      this.loadDanReports()
+      this._watchPageReady()
+    },
 
     // ===== 传统文化数据 =====
     // ===== 联系人 / 关系健康 =====
@@ -1162,7 +1200,7 @@ var descList = descMap[this.dominantElement] || descMap.wood
   align-items: center;
   margin: 20rpx 30rpx 10rpx;
   padding: 16rpx 24rpx;
-  background: linear-gradient(135deg, #8B5CF6, #A78BFA);
+  background: linear-gradient(135deg, #F97316, #FB923C);
   border-radius: 20rpx;
   box-shadow: 0 4rpx 16rpx rgba(139,92,246,0.25);
 }
@@ -1235,7 +1273,7 @@ var descList = descMap[this.dominantElement] || descMap.wood
 }
 .mind-report-arrow {
   font-size: 36rpx;
-  color: #C084FC;
+  color: #FB923C;
   margin-left: 10rpx;
   flex-shrink: 0;
 }
@@ -1358,7 +1396,7 @@ var descList = descMap[this.dominantElement] || descMap.wood
   margin-bottom: 24rpx;
 }
 .test-btn {
-  background: #8B5CF6;
+  background: #F97316;
   padding: 8rpx 28rpx;
   border-radius: 30rpx;
 }
@@ -1646,7 +1684,7 @@ var descList = descMap[this.dominantElement] || descMap.wood
 .bazi-pillar-value {
   font-size: 32rpx;
   font-weight: 700;
-  color: #8B5CF6;
+  color: #F97316;
   background: #F5F0FF;
   padding: 10rpx 14rpx;
   border-radius: 10rpx;
@@ -1830,7 +1868,7 @@ var descList = descMap[this.dominantElement] || descMap.wood
 .culture-dual-value {
   font-size: 28rpx;
   font-weight: 700;
-  color: #8B5CF6;
+  color: #F97316;
 }
 .culture-empty {
   text-align: center;
@@ -2226,7 +2264,7 @@ var descList = descMap[this.dominantElement] || descMap.wood
   text-align: center;
   padding: 12rpx;
   font-size: 24rpx;
-  color: #8B5CF6;
+  color: #F97316;
   font-weight: 500;
   background: #F5F3FF;
   border-radius: 12rpx;
@@ -2303,7 +2341,7 @@ var descList = descMap[this.dominantElement] || descMap.wood
   padding: 20rpx 28rpx;
   background: linear-gradient(135deg, #FFF0F5, rgba(255,107,157,0.12));
   border-radius: 16rpx;
-  border-left: 4rpx solid #FF6B9D;
+  border-left: 8rpx solid #FF6B9D;
 }
 .brand-strap-text {
   font-size: 26rpx;
@@ -2317,7 +2355,7 @@ var descList = descMap[this.dominantElement] || descMap.wood
   padding: 20rpx 28rpx;
   background: linear-gradient(135deg, #FFF0F5, rgba(255,107,157,0.08));
   border-radius: 16rpx;
-  border-left: 4rpx solid #FF6B9D;
+  border-left: 8rpx solid #FF6B9D;
   display: flex;
   align-items: center;
   gap: 12rpx;
@@ -2417,7 +2455,7 @@ var descList = descMap[this.dominantElement] || descMap.wood
   padding: 16rpx 20rpx;
   background: linear-gradient(135deg, #FFF0F5, #FDF2F7);
   border-radius: 12rpx;
-  border-left: 4rpx solid #FF6B9D;
+  border-left: 8rpx solid #FF6B9D;
   margin-top: 16rpx;
 }
 .portrait-tip-text {

+ 12 - 6
cfc-frontend/pages/profile-main/profile.vue

@@ -1,5 +1,7 @@
 <template>
   <view class="container">
+    <!-- 登录浮层(全局组件,页面内弹出底部登录) -->
+    <login-sheet ref="loginSheet" @success="onSheetLoginSuccess" />
     <!-- 未登录 -->
     <view v-if="!isLoggedIn">
       <view class="login-prompt">
@@ -690,7 +692,11 @@ export default {
       })
     },
     goLogin: function() {
-      uni.navigateTo({ url: '/pages/login/login' })
+      this.openLoginSheet()
+    },
+    // 登录浮层成功回调:刷新个人资料与登录后数据
+    onLoginSuccess: function() {
+      if (this.loadUserInfo) this.loadUserInfo()
     },
     goSkip: function() {
       uni.switchTab({ url: '/pages/index-home/index' })
@@ -840,7 +846,7 @@ export default {
 .prompt-title { font-size: 40rpx; font-weight: bold; color: #333; margin-bottom: 16rpx; }
 .prompt-desc { font-size: 26rpx; color: #999; margin-bottom: 20rpx; }
 .login-btn {
-  background: linear-gradient(135deg, #667eea, #764ba2);
+  background: linear-gradient(135deg, #F97316, #FB923C);
   color: #fff;
   border-radius: 50rpx;
   font-size: 30rpx;
@@ -876,7 +882,7 @@ export default {
 /* 退出切换 */
 .exit-switch {
   margin: 0 30rpx 16rpx;
-  background: linear-gradient(135deg, #667eea, #764ba2);
+  background: linear-gradient(135deg, #F97316, #FB923C);
   color: #fff;
   padding: 16rpx;
   border-radius: 12rpx;
@@ -887,7 +893,7 @@ export default {
 /* 身份卡 */
 .user-card {
   margin: 24rpx 30rpx 20rpx;
-  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+  background: linear-gradient(135deg, #F97316 0%, #FB923C 100%);
   border-radius: 24rpx;
   padding: 32rpx 32rpx;
   display: flex;
@@ -1268,13 +1274,13 @@ export default {
   margin: 0 30rpx 20rpx;
 }
 .service-center-card {
-  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+  background: linear-gradient(135deg, #F97316 0%, #FB923C 100%);
   border-radius: 20rpx;
   padding: 24rpx;
   display: flex;
   align-items: center;
   gap: 20rpx;
-  box-shadow: 0 4rpx 16rpx rgba(102,126,234,0.25);
+  box-shadow: 0 4rpx 16rpx rgba(249,115,22,0.25);
 }
 .service-center-card.disabled-card {
   background: linear-gradient(135deg, #e2e8f0, #94a3b8);

+ 7 - 3
cfc-frontend/pages/profile/index.vue

@@ -1,5 +1,7 @@
 <template>
   <view class="container">
+    <!-- 登录浮层(全局组件,页面内弹出底部登录) -->
+    <login-sheet ref="loginSheet" @success="onSheetLoginSuccess" />
     <view v-if="!isLoggedIn" class="login-prompt">
       <view class="prompt-icon">👤</view>
       <text class="prompt-title">登录浠艾福</text>
@@ -104,9 +106,11 @@ export default {
       uni.navigateTo({ url: '/pages/wealth/index' })
     },
     goLogin() {
-      uni.navigateTo({
-        url: '/pages/login/login'
-      })
+      this.openLoginSheet()
+    },
+    // 登录浮层成功回调:加载登录后数据(推荐码等)
+    onLoginSuccess() {
+      if (this.isLoggedIn) this.loadReferralCode()
     },
     goToEditInfo() {
       uni.navigateTo({

+ 2 - 2
cfc-frontend/pages/reminder-config/index.vue

@@ -16,7 +16,7 @@
       <view class="cfg-card">
         <text class="cfg-card-title">提醒渠道</text>
         <view class="channel-list">
-          <view class="channel-item" v-for="ch in channels" :key="ch.type"
+          <view class="channel-item" v-for="ch in channels" :key="ch.type + '_' + index"
             :class="{
               'channel-selected': isSelected(ch.type),
               'channel-disabled': !ch.available
@@ -67,7 +67,7 @@
         </view>
         <text class="cfg-card-desc">任务正常完成时,发送表扬鼓励消息</text>
         <view class="channel-list" v-if="completionPraiseEnabled === 1" style="margin-top:20rpx">
-          <view class="channel-item" v-for="ch in channels" :key="'p-'+ch.type"
+          <view class="channel-item" v-for="(ch, pIndex) in channels" :key="'p-' + pIndex"
             :class="{
               'channel-selected': isPraiseSelected(ch.type),
               'channel-disabled': !ch.available

+ 15 - 5
cfc-frontend/pages/wealth-cf/index.vue

@@ -1,5 +1,7 @@
 <template>
   <view class="container">
+    <!-- 登录浮层(全局组件,页面内弹出底部登录) -->
+    <login-sheet ref="loginSheet" @success="onSheetLoginSuccess" />
     <!-- 未登录 -->
     <template v-if="!isLoggedIn">
       <view class="empty-state">
@@ -129,7 +131,15 @@ export default {
       this.isLoggedIn = !!token
     },
     goLogin() {
-      uni.navigateTo({ url: '/pages/login/login' })
+      this.openLoginSheet()
+    },
+    // 登录浮层成功回调:刷新登录态、余额与流水
+    onLoginSuccess() {
+      this.checkLogin()
+      if (this.isLoggedIn) {
+        this.loadBalance()
+        this.loadLogs()
+      }
     },
     async loadBalance() {
       try {
@@ -266,7 +276,7 @@ export default {
   width: 280rpx;
   height: 80rpx;
   line-height: 80rpx;
-  background: linear-gradient(135deg, #667eea, #764ba2);
+  background: linear-gradient(135deg, #F97316, #FB923C);
   color: #fff;
   border-radius: 40rpx;
   font-size: 28rpx;
@@ -275,7 +285,7 @@ export default {
 .balance-card {
   margin: 30rpx;
   padding: 40rpx 30rpx;
-  background: linear-gradient(135deg, #667eea, #764ba2);
+  background: linear-gradient(135deg, #F97316, #FB923C);
   border-radius: 24rpx;
   color: #fff;
 }
@@ -353,7 +363,7 @@ export default {
 }
 .section-more {
   font-size: 24rpx;
-  color: #667eea;
+  color: #F97316;
 }
 .log-list {
   background: #fff;
@@ -470,7 +480,7 @@ export default {
 }
 .coupon-price {
   font-size: 26rpx;
-  color: #667eea;
+  color: #F97316;
   font-weight: 600;
   white-space: nowrap;
 }

+ 34 - 43
cfc-frontend/pages/wealth/index.vue

@@ -1,8 +1,13 @@
 <template>
   <view class="container">
+    <!-- 通用登录浮层 -->
+    <login-sheet ref="loginSheet" @success="onSheetLoginSuccess" />
     <!-- TabTransition overlay -->
     <tab-transition v-if="showTabTransition" dimCode="wealth" ref="tabTransition" @close="showTabTransition = false" />
 
+    <!-- 品牌头部 -->
+    <PageBanner theme="wealth" tagline="家庭财富能量 · 利他创富" quote="利他创富 · 富足人生" />
+
     <!-- 未登录时显示精简内容 -->
     <template v-if="!isLoggedIn">
       <!-- 品牌定位语 -->
@@ -16,16 +21,18 @@
         <text class="guidance-text">{{ monthlyGuidance }}</text>
       </view>
 
-      <!-- 登录引导 -->
-      <view class="login-guide-section">
-        <LoginGuideCard
-          title="解锁财富能量与推广收益"
-          :items="[
-            '查看家庭财富能量仪表盘与推广收益',
-            '管理邀请返利,开通会员享专属折扣',
-            '浏览商城精选商品,一键加入购物车'
-          ]" />
-      </view>
+      <!-- 游客价值卡(统一组件:维度色 CTA + 价值预览) -->
+      <DimensionVisitorCard
+        dimCode="wealth"
+        card-title="富维度 · 利他创富"
+        :benefits="[
+          '家庭财富能量仪表盘与推广收益',
+          '邀请返利与会员专属折扣',
+          '商城精选好物一键下单'
+        ]"
+        cta-text="登录解锁财富能量"
+        secondary-text="逛逛商城"
+        secondary-url="/pages/shop/index/index" />
 
       <!-- 商城精选预览(无需登录) -->
       <view class="section">
@@ -261,6 +268,7 @@ import BodyWealthAlert from '@/pages/wealth/components/BodyWealthAlert.vue'
 import FamilyMemberStrip from '../../components/FamilyMemberStrip.vue'
 import FamilyEnergyBar from '../../components/FamilyEnergyBar.vue'
 import DimensionIntroCard from '../../components/DimensionIntroCard.vue'
+import DimensionVisitorCard from '../../components/DimensionVisitorCard.vue'
 import DimensionSubDims from '../../components/DimensionSubDims.vue'
 import {
   getChildren,
@@ -281,7 +289,7 @@ import nav from '../../utils/nav.js'
 import config from '../../config.js'
 
 export default {
-  components: { TabTransition, WealthEnergyRing, WealthSubScores, BodyWealthAlert, FamilyMemberStrip, FamilyEnergyBar, DimensionIntroCard, DimensionSubDims },
+  components: { TabTransition, WealthEnergyRing, WealthSubScores, BodyWealthAlert, FamilyMemberStrip, FamilyEnergyBar, DimensionIntroCard, DimensionSubDims, DimensionVisitorCard },
   data() {
     return {
       showTabTransition: true,
@@ -506,7 +514,19 @@ export default {
       return map[status] || status || '未知'
     },
     goLogin() {
-      uni.navigateTo({ url: '/pages/login/login' })
+      this.openLoginSheet()
+    },
+    // 登录成功:刷新登录态数据(镜像 onShow 已登录分支)
+    onLoginSuccess() {
+      this.isLoggedIn = true
+      this.loadChildren()
+      this.loadWealthDetail()
+      this.loadWealthData()
+      this.loadChildData()
+      this.loadFamilyMembersVisible()
+      this.loadInsuranceList()
+      this.loadSandboxData()
+      this.loadEnergyData()
     },
     _watchPageReady() {
       var self = this
@@ -1035,35 +1055,6 @@ export default {
 }
 
 /* 未登录 */
-.login-guide-section {
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  padding: 60rpx 40rpx;
-  margin: 30rpx;
-  background: #fff;
-  border-radius: 20rpx;
-  box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
-}
-.login-guide-text {
-  font-size: 26rpx;
-  color: #999;
-  text-align: center;
-  margin-bottom: 30rpx;
-  line-height: 1.6;
-}
-.login-guide-section .login-btn {
-  width: 60%;
-  height: 80rpx;
-  line-height: 80rpx;
-  background: linear-gradient(135deg, #5B9BD5, #3A7CC4);
-  color: #fff;
-  font-size: 30rpx;
-  font-weight: bold;
-  border-radius: 40rpx;
-  text-align: center;
-  border: none;
-}
 .login-prompt .login-btn::after {
   border: none;
 }
@@ -1074,7 +1065,7 @@ export default {
   padding: 20rpx 28rpx;
   background: linear-gradient(135deg, #FFFBEB, rgba(245,158,11,0.12));
   border-radius: 16rpx;
-  border-left: 4rpx solid #F59E0B;
+  border-left: 8rpx solid #F59E0B;
 }
 .brand-strap-text {
   font-size: 26rpx;
@@ -1305,7 +1296,7 @@ export default {
   padding: 16rpx 20rpx;
   background: linear-gradient(135deg, #FFF7ED, #FFF3E8);
   border-radius: 12rpx;
-  border-left: 4rpx solid #F59E0B;
+  border-left: 8rpx solid #F59E0B;
   margin-top: 16rpx;
 }
 .portrait-tip-text {

+ 38 - 5
cfc-frontend/pages/wisdom-detail/index.vue

@@ -1,8 +1,13 @@
 <template>
   <view class="wisdom-container">
+    <!-- 通用登录浮层 -->
+    <login-sheet ref="loginSheet" @success="onSheetLoginSuccess" />
     <!-- Tabbar 切换过渡页 -->
     <tab-transition v-if="showTabTransition" dimCode="wisdom" ref="tabTransition" @close="showTabTransition = false" />
 
+    <!-- 品牌头部 -->
+    <PageBanner theme="wisdom" tagline="认知赋能 · 提升学习效率" quote="认知赋能 · 智慧人生" />
+
     <!-- 品牌定位语(登录前显示) -->
     <view class="brand-strap" v-if="!isLoggedIn">
       <text class="brand-strap-text">提升全家认知能力与学习效率</text>
@@ -41,6 +46,20 @@
       <text class="guidance-text">{{ monthlyGuidance }}</text>
     </view>
 
+    <!-- 游客价值卡(统一组件:维度色 CTA + 价值预览) -->
+    <DimensionVisitorCard
+      v-if="!isLoggedIn"
+      dimCode="wisdom"
+      card-title="智维度 · 认知赋能"
+      :benefits="[
+        '全家认知能力评估与强项分析',
+        '学习解读与认知训练建议',
+        '认知趋势与同龄对比'
+      ]"
+      cta-text="登录做认知测评"
+      secondary-text="了解认知维度"
+      secondary-url="/pages/wisdom-detail/dimension-intro" />
+
     <!-- 4. 五维能量条(登录后) -->
     <FamilyEnergyBar
       v-if="isLoggedIn"
@@ -221,11 +240,12 @@ import FamilyEnergyBar from '../../components/FamilyEnergyBar.vue'
 import FloatingAvatar from '../../components/AIFloatingAvatar.vue'
 import FamilyMemberStrip from '../../components/FamilyMemberStrip.vue'
 import DimensionIntroCard from '../../components/DimensionIntroCard.vue'
+import DimensionVisitorCard from '../../components/DimensionVisitorCard.vue'
 import { getVisibleSections, getChildren, getFamilyEnergySandbox, getEnergySandbox, getAssessmentLatestResult, getDanReportByChild, getGutCognitionIndicators, getLatestIndicators, getRadarData } from '../../utils/api.js'
 import { parseDate } from '../../utils/format.js'
 
 export default {
-  components: { TabTransition, RadarChart, FamilyEnergyBar, FloatingAvatar, FamilyMemberStrip, DimensionIntroCard },
+  components: { TabTransition, RadarChart, FamilyEnergyBar, FloatingAvatar, FamilyMemberStrip, DimensionIntroCard, DimensionVisitorCard },
   data() {
     return {
       isLoggedIn: false,
@@ -586,7 +606,7 @@ export default {
         uni.showModal({
           title: '提示',
           content: '请先登录后使用此功能',
-          success: function(res) { if (res.confirm) uni.navigateTo({ url: '/pages/login/login' }) }
+          success: (res) => { if (res.confirm) this.openLoginSheet() }
         })
         return
       }
@@ -595,6 +615,19 @@ export default {
       }
     },
     scrollToSection: function(section) {},
+    // 登录成功:刷新登录态数据(镜像 onShow 已登录分支)
+    onLoginSuccess: function() {
+      this.isLoggedIn = true
+      this.role = uni.getStorageSync('currentRole') || uni.getStorageSync('role') || null
+      this.userName = uni.getStorageSync('nickname') || uni.getStorageSync('userName') || '家长'
+      this.loadSectionConfig()
+      this.loadChildren()
+      this.loadFamilyMembersVisible()
+      this.loadCognitiveReport()
+      this.loadDanReports()
+      this.loadGutCognitionData()
+      this._watchPageReady()
+    },
     goCognitiveReport: function() {
       uni.navigateTo({ url: '/pages/wisdom-detail/cognitive-report' })
     },
@@ -920,7 +953,7 @@ export default {
   padding: 24rpx 28rpx;
   background: linear-gradient(135deg, #F0F0FF, rgba(99,102,241,0.16));
   border-radius: 16rpx;
-  border-left: 4rpx solid #6366F1;
+  border-left: 8rpx solid #6366F1;
   box-shadow: 0 2rpx 12rpx rgba(99,102,241,0.12);
 }
 .brand-strap-text {
@@ -936,7 +969,7 @@ export default {
   padding: 20rpx 28rpx;
   background: linear-gradient(135deg, #F0F0FF, rgba(99,102,241,0.08));
   border-radius: 16rpx;
-  border-left: 4rpx solid #6366F1;
+  border-left: 8rpx solid #6366F1;
   display: flex;
   align-items: center;
   gap: 12rpx;
@@ -1198,7 +1231,7 @@ export default {
   padding: 16rpx 20rpx;
   background: linear-gradient(135deg, #F0F4FF, #E8F1FE);
   border-radius: 12rpx;
-  border-left: 4rpx solid #6366F1;
+  border-left: 8rpx solid #6366F1;
   margin-top: 16rpx;
 }
 .portrait-tip-text {

+ 8 - 2
cfc-frontend/pages/wisdom-detail/member-wisdom-detail.vue

@@ -1,5 +1,7 @@
 <template>
   <view class="detail-container">
+    <!-- 登录浮层(全局组件,页面内弹出底部登录) -->
+    <login-sheet ref="loginSheet" @success="onSheetLoginSuccess" />
     <!-- 顶部导航 -->
 
     <!-- 五维能量条 -->
@@ -128,7 +130,7 @@ export default {
       if (token) {
         uni.navigateTo({ url: '/pages/discover-detail/product-detail/product-detail?id=' + id })
       } else {
-        uni.navigateTo({ url: '/pages/login/login' })
+        this.openLoginSheet()
       }
     },
     goMoreProducts: function() {
@@ -139,11 +141,15 @@ export default {
       if (token) {
         uni.navigateTo({ url: '/pages/article-center/article-detail/article-detail?id=' + id })
       } else {
-        uni.navigateTo({ url: '/pages/login/login' })
+        this.openLoginSheet()
       }
     },
     goMoreArticles: function() {
       uni.navigateTo({ url: '/pages/article-center/index' })
+    },
+    // 登录浮层成功回调:刷新成员心智数据
+    onLoginSuccess: function() {
+      if (this.loadMemberData) this.loadMemberData()
     }
   }
 }