瀏覽代碼

feat(frontend): 五维页面推荐组件统一——推荐阅读改用ArticleBookshelf、商品推荐改用DimensionProductList

- action/body/index-home/mind/wisdom/wealth 六页推荐阅读统一为 ArticleBookshelf 书册组件
- mind/wisdom 商品推荐迁移至 DimensionProductList(支持未登录空态)
- body 移除商品推荐 v-if 登录守卫,组件内部处理未登录态
- DimensionProductList familyId 改为可选,无 familyId 时不请求接口展示引导文案
- wealth 页新增推荐阅读区块(未登录/已登录两区块)
Xiaogang Liao 1 月之前
父節點
當前提交
f3e31f3c47

+ 7 - 2
cfc-frontend/components/DimensionProductList.vue

@@ -19,7 +19,7 @@
       </view>
     </view>
     <view class="empty-state" v-else-if="!loading">
-      <text class="empty-text">暂无推荐商品</text>
+      <text class="empty-text">{{ familyId ? '暂无推荐商品' : '登录后查看推荐商品' }}</text>
     </view>
     <view class="loading-state" v-if="loading">
       <text class="loading-text">加载中...</text>
@@ -33,7 +33,7 @@ import { getDimensionProducts, clickRepurchaseReminder } from '../utils/api.js'
 export default {
   props: {
     dimensionCode: { type: String, required: true },
-    familyId: { type: [Number, String], required: true },
+    familyId: { type: [Number, String], default: null },
     memberScores: { type: Object, default: function() { return {} } },
     title: { type: String, default: '为你推荐' }
   },
@@ -44,6 +44,11 @@ export default {
     }
   },
   mounted: function() {
+    // 无 familyId(未登录/未选择孩子)时不请求推荐接口,展示引导文案
+    if (!this.familyId) {
+      this.loading = false
+      return
+    }
     this.loadProducts()
   },
   methods: {

+ 3 - 4
cfc-frontend/pages/action/index.vue

@@ -76,8 +76,7 @@
       @moreProducts="goMoreProducts" />
 
     <!-- 推荐阅读 -->
-    <DimensionArticles
-      dimensionCode="action"
+    <ArticleBookshelf
       :articles="actionArticles"
       :isLoggedIn="isLoggedIn"
       @articleClick="goArticleDetail"
@@ -156,7 +155,7 @@ import FamilyEnergyBar from '../../components/FamilyEnergyBar.vue'
 import DimensionTasks from '../../components/DimensionTasks.vue'
 import DimensionActivities from '../../components/DimensionActivities.vue'
 import DimensionProducts from '../../components/DimensionProducts.vue'
-import DimensionArticles from '../../components/DimensionArticles.vue'
+import ArticleBookshelf from '../../components/ArticleBookshelf.vue'
 import CircleCard from '../../components/CircleCard.vue'
 import CircleDetail from '../../components/CircleDetail.vue'
 import FamilyRelationGraph from '../../components/FamilyRelationGraph.vue'
@@ -168,7 +167,7 @@ import { getVisibleSections, getEnergyOverview, getFamilyEnergySandbox, getToday
 
 export default {
   mixins: [recommendMixin],
-  components: { TabTransition, PageBanner, LoginGuideCard, FamilyEnergyBar, DimensionTasks, DimensionActivities, DimensionProducts, DimensionArticles, FamilyRelationGraph, WuxingSandbox, FamilyMemberStrip, CircleCard, CircleDetail, RecommendedSection },
+  components: { TabTransition, PageBanner, LoginGuideCard, FamilyEnergyBar, DimensionTasks, DimensionActivities, DimensionProducts, ArticleBookshelf, FamilyRelationGraph, WuxingSandbox, FamilyMemberStrip, CircleCard, CircleDetail, RecommendedSection },
   data() {
     return {
       showTabTransition: true,

+ 7 - 13
cfc-frontend/pages/body/index.vue

@@ -108,14 +108,12 @@
 
     <!-- 商品推荐 -->
     <DimensionProductList
-      v-if="isLoggedIn && activeChildId"
       dimensionCode="body"
       :familyId="activeChildId"
       title="为你推荐" />
 
     <!-- 推荐阅读 -->
-    <DimensionArticles
-      dimensionCode="body"
+    <ArticleBookshelf
       :articles="bodyArticles"
       :isLoggedIn="isLoggedIn"
       @articleClick="goArticleDetail"
@@ -142,9 +140,8 @@ import LoginGuideCard from '../../components/LoginGuideCard.vue'
 import FamilyEnergyBar from '../../components/FamilyEnergyBar.vue'
 import DimensionTasks from '../../components/DimensionTasks.vue'
 import DimensionActivities from '../../components/DimensionActivities.vue'
-import DimensionProducts from '../../components/DimensionProducts.vue'
 import DimensionProductList from '../../components/DimensionProductList.vue'
-import DimensionArticles from '../../components/DimensionArticles.vue'
+import ArticleBookshelf from '../../components/ArticleBookshelf.vue'
 import RadarChart from '../../components/RadarChart.vue'
 import AIFloatingAvatar from '../../components/AIFloatingAvatar.vue'
 import FamilyMemberStrip from '../../components/FamilyMemberStrip.vue'
@@ -154,7 +151,7 @@ import { getVisibleSections, getEnergyOverview, getChildren, getTodayTasksByCate
 
 export default {
   mixins: [recommendMixin],
-  components: { TabTransition, PageBanner, LoginGuideCard, FamilyEnergyBar, DimensionTasks, DimensionActivities, DimensionProducts, DimensionProductList, DimensionArticles, RadarChart, AIFloatingAvatar, FamilyMemberStrip, RecommendedSection },
+  components: { TabTransition, PageBanner, LoginGuideCard, FamilyEnergyBar, DimensionTasks, DimensionActivities, DimensionProductList, ArticleBookshelf, RadarChart, AIFloatingAvatar, FamilyMemberStrip, RecommendedSection },
   data() {
     return {
       isLoggedIn: false,
@@ -402,7 +399,10 @@ export default {
               title: item.title || '',
               summary: item.summary || '',
               views: item.viewCount ? String(item.viewCount) : '0',
-              date: item.publishedAt ? item.publishedAt.slice(0, 10) : ''
+              date: item.publishedAt ? item.publishedAt.slice(0, 10) : '',
+              relatedDimensions: item.relatedDimensions,
+              coverImage: item.coverImage,
+              author: item.author
             }
           })
         }
@@ -438,12 +438,6 @@ export default {
     goMoreActivities: function() {
       uni.navigateTo({ url: '/pages/activity/index' })
     },
-    goProductDetail: function(prod) {
-      uni.navigateTo({ url: '/pages/discover-detail/product-detail/product-detail?id=' + prod.id })
-    },
-    goMoreProducts: function() {
-      uni.navigateTo({ url: '/pages/shop/index' })
-    },
     goArticleDetail: function(article) {
       var id = article.id || article
       uni.navigateTo({ url: '/pages/article-center/article-detail?id=' + id })

+ 3 - 4
cfc-frontend/pages/index-home/index.vue

@@ -102,8 +102,7 @@
       @activityClick="goActivityDetail"
       @moreActivities="goMoreActivities" />
 
-    <DimensionArticles
-      dimensionCode="action"
+    <ArticleBookshelf
       :articles="actionArticles"
       :isLoggedIn="true"
       @articleClick="goArticleDetail"
@@ -356,7 +355,7 @@ import FamilyEnergyBar from '../../components/FamilyEnergyBar.vue'
 import DimensionTasks from '../../components/DimensionTasks.vue'
 import DimensionActivities from '../../components/DimensionActivities.vue'
 import DimensionProducts from '../../components/DimensionProducts.vue'
-import DimensionArticles from '../../components/DimensionArticles.vue'
+import ArticleBookshelf from '../../components/ArticleBookshelf.vue'
 import CircleCard from '../../components/CircleCard.vue'
 import CircleDetail from '../../components/CircleDetail.vue'
 import LoginGuideCard from '../../components/LoginGuideCard.vue'
@@ -384,7 +383,7 @@ export default {
     DimensionTasks,
     DimensionActivities,
     DimensionProducts,
-    DimensionArticles,
+    ArticleBookshelf,
     CircleCard,
     CircleDetail,
     LoginGuideCard,

+ 9 - 30
cfc-frontend/pages/mind/index.vue

@@ -99,8 +99,7 @@
     </view>
 
     <!-- 推荐阅读 -->
-    <DimensionArticles
-      dimensionCode="mind"
+    <ArticleBookshelf
       :articles="emotionArticles"
       :isLoggedIn="isLoggedIn"
       @articleClick="goArticleDetail"
@@ -121,11 +120,10 @@
       @activityClick="goActivityDetail"
       @moreActivities="goMoreActivities" />
 
-    <DimensionProducts
-      :dimensionCode="currentDimensionCode"
-      :products="dimensionProducts"
-      @productClick="goProductDetail"
-      @moreProducts="goMoreProducts" />
+    <DimensionProductList
+      dimensionCode="mind"
+      :familyId="currentChildId"
+      title="为你推荐" />
 
     <!-- ===== Phase 1.2: 心理告警横幅 ===== -->
     <PsychCrisisBanner v-if="isLoggedIn" :childId="currentChildId" source="screening" />
@@ -174,20 +172,20 @@ import RadarChart from '../../components/RadarChart.vue'
 import FamilyEnergyBar from '../../components/FamilyEnergyBar.vue'
 import DimensionTasks from '../../components/DimensionTasks.vue'
 import DimensionActivities from '../../components/DimensionActivities.vue'
-import DimensionProducts from '../../components/DimensionProducts.vue'
-import DimensionArticles from '../../components/DimensionArticles.vue'
+import DimensionProductList from '../../components/DimensionProductList.vue'
+import ArticleBookshelf from '../../components/ArticleBookshelf.vue'
 import PsychCrisisBanner from '../../components/PsychCrisisBanner.vue'
 import AIFloatingAvatar from '../../components/AIFloatingAvatar.vue'
 import FamilyTianpanCard from '../../components/FamilyTianpanCard.vue'
 import FamilyMemberStrip from '../../components/FamilyMemberStrip.vue'
-import { getVisibleSections, getFeaturedArticles, getEmiReport, getFamilyEnergySandbox, getTodayTasksByCategory, getActivityList, getProductsByDomain, getChildren, getVisibleFamilyMembers, getEnergyOverview, getContactList, getHealthAlerts, getMilestones, getDanReportByChild, getFamilyFortune } from '../../utils/api.js'
+import { getVisibleSections, getFeaturedArticles, getEmiReport, getFamilyEnergySandbox, getTodayTasksByCategory, getActivityList, getChildren, getVisibleFamilyMembers, getEnergyOverview, getContactList, getHealthAlerts, getMilestones, getDanReportByChild, getFamilyFortune } from '../../utils/api.js'
 import config from '../../config.js'
 import RecommendedSection from '../../components/RecommendedSection.vue'
 import recommendMixin from '../../mixins/recommend-mixin'
 
 export default {
   mixins: [recommendMixin],
-components: { TabTransition, PageBanner, RadarChart, LoginGuideCard, FamilyEnergyBar, DimensionTasks, DimensionActivities, DimensionProducts, DimensionArticles, PsychCrisisBanner, AIFloatingAvatar, FamilyTianpanCard, FamilyMemberStrip, RecommendedSection },
+components: { TabTransition, PageBanner, RadarChart, LoginGuideCard, FamilyEnergyBar, DimensionTasks, DimensionActivities, DimensionProductList, ArticleBookshelf, PsychCrisisBanner, AIFloatingAvatar, FamilyTianpanCard, FamilyMemberStrip, RecommendedSection },
   data() {
     return {
       isLoggedIn: false,
@@ -253,7 +251,6 @@ components: { TabTransition, PageBanner, RadarChart, LoginGuideCard, FamilyEnerg
       sandboxData: null,
       dimensionTasks: [],
       dimensionActivities: [],
-      dimensionProducts: [],
       contactList: [],
       healthAlerts: [],
       milestones: [],
@@ -458,7 +455,6 @@ components: { TabTransition, PageBanner, RadarChart, LoginGuideCard, FamilyEnerg
     }
     // 游客也能浏览活动和商品
     this.loadDimensionActivities()
-    this.loadDimensionProducts()
     this.loadRecommendations('mind')
     this.loadFamilyFortune()
     this._watchPageReady()
@@ -791,7 +787,6 @@ components: { TabTransition, PageBanner, RadarChart, LoginGuideCard, FamilyEnerg
     loadDimensionData: function() {
       this.loadDimensionTasks()
       this.loadDimensionActivities()
-      this.loadDimensionProducts()
       if (this.familyMembersVisible.length > 0) {
         this.loadCompatibilityData()
       }
@@ -841,16 +836,6 @@ components: { TabTransition, PageBanner, RadarChart, LoginGuideCard, FamilyEnerg
         }
       }).catch(function() { self.dimensionActivities = [] })
     },
-    loadDimensionProducts: function() {
-      var self = this
-      var dimCode = self.currentDimensionCode
-      getProductsByDomain(dimCode, 1, 4).then(function(res) {
-        if (res && res.data) {
-          var list = Array.isArray(res.data) ? res.data : (res.data.records || [])
-          self.dimensionProducts = list.slice(0, 4)
-        }
-      }).catch(function() { self.dimensionProducts = [] })
-    },
     onFamilyMemberSelect: function(member) {
       this.selectedMemberId = member.id
     },
@@ -905,12 +890,6 @@ components: { TabTransition, PageBanner, RadarChart, LoginGuideCard, FamilyEnerg
     goMoreActivities: function() {
       uni.navigateTo({ url: '/pages/activity/index' })
     },
-    goProductDetail: function(prod) {
-      if (prod && prod.id) uni.navigateTo({ url: '/pages/discover-detail/product-detail/product-detail?id=' + prod.id })
-    },
-    goMoreProducts: function() {
-      uni.navigateTo({ url: '/pages/shop/index' })
-    },
     goTasks: function() { uni.switchTab({ url: '/pages/tasks/tasks' }) },
     goFamilyMembers: function() {
       uni.navigateTo({ url: '/pages/profile-extra/family-members' })

+ 50 - 3
cfc-frontend/pages/wealth/index.vue

@@ -14,6 +14,13 @@
         dimensionCode="wealth"
         title="为你推荐" />
 
+      <!-- 推荐阅读 -->
+      <ArticleBookshelf
+        :articles="wealthArticles"
+        :loading="articlesLoading"
+        @articleClick="goArticleDetail"
+        @moreArticles="goMoreArticles" />
+
       <!-- 登录引导 -->
       <view class="login-guide-section">
         <text class="login-guide-text">登录后查看财富能量、推广收益、邀请返利等更多内容</text>
@@ -165,6 +172,13 @@
         :familyId="activeChildId"
         title="为你推荐" />
 
+      <!-- 推荐阅读 -->
+      <ArticleBookshelf
+        :articles="wealthArticles"
+        :loading="articlesLoading"
+        @articleClick="goArticleDetail"
+        @moreArticles="goMoreArticles" />
+
       <!-- 个性化推荐 -->
       <RecommendedSection
         v-if="recommendItems.length > 0"
@@ -195,6 +209,7 @@
 <script>
 import TabTransition from '../../components/tab-transition.vue'
 import PageBanner from '../../components/PageBanner.vue'
+import ArticleBookshelf from '../../components/ArticleBookshelf.vue'
 import DimensionProductList from '../../components/DimensionProductList.vue'
 import WealthSubScores from '../../components/WealthSubScores.vue'
 import BodyWealthAlert from '../../components/BodyWealthAlert.vue'
@@ -208,12 +223,13 @@ import {
   getReferralCode,
   getReferralSummary,
   getCommissionSummary,
-  getVisibleFamilyMembers
+  getVisibleFamilyMembers,
+  getFeaturedArticles
 } from '../../utils/api.js'
 
 export default {
   mixins: [recommendMixin],
-  components: { TabTransition, PageBanner, DimensionProductList, WealthSubScores, BodyWealthAlert, FamilyMemberStrip, RecommendedSection },
+  components: { TabTransition, PageBanner, DimensionProductList, WealthSubScores, BodyWealthAlert, FamilyMemberStrip, RecommendedSection, ArticleBookshelf },
   data() {
     return {
       showTabTransition: true,
@@ -250,7 +266,9 @@ export default {
       },
       children: [],
       familyMembersVisible: [],
-      selectedMemberId: null
+      selectedMemberId: null,
+      wealthArticles: [],
+      articlesLoading: false
     }
   },
   computed: {
@@ -285,6 +303,7 @@ export default {
     this.loadWealthData()
     this.loadFamilyMembersVisible()
     this.loadRecommendations('wealth')
+    this.loadWealthArticles()
     this._watchPageReady()
   },
   methods: {
@@ -294,6 +313,34 @@ export default {
     goMoreRecommend: function() {
       uni.navigateTo({ url: '/pages/discover/index' })
     },
+    loadWealthArticles: function() {
+      var self = this
+      self.articlesLoading = true
+      getFeaturedArticles({ size: 5 }).then(function(res) {
+        if (res.code === 200 && res.data) {
+          var list = Array.isArray(res.data) ? res.data : (res.data.records || [])
+          var result = []
+          for (var i = 0; i < list.length; i++) {
+            var a = list[i]
+            if (a.relatedDimensions && a.relatedDimensions.indexOf('wealth') !== -1) {
+              result.push(a)
+            }
+          }
+          self.wealthArticles = result.length > 0 ? result.slice(0, 5) : list.slice(0, 5)
+        }
+      }).catch(function() {
+        self.wealthArticles = []
+      }).then(function() {
+        self.articlesLoading = false
+      })
+    },
+    goArticleDetail: function(article) {
+      var id = article && article.id ? article.id : article
+      uni.navigateTo({ url: '/pages/article-center/article-detail?id=' + id })
+    },
+    goMoreArticles: function() {
+      uni.navigateTo({ url: '/pages/article-center/index?dimension=wealth' })
+    },
     loadFamilyMembersVisible() {
       var self = this
       getVisibleFamilyMembers().then(function(res) {

+ 7 - 22
cfc-frontend/pages/wisdom/index.vue

@@ -99,16 +99,14 @@
       @moreActivities="goMoreActivities" />
 
     <!-- 9. 维度商品 -->
-    <DimensionProducts
+    <DimensionProductList
       dimensionCode="wisdom"
-      :products="dimensionProducts"
-      @productClick="goProductDetail"
-      @moreProducts="goMoreProducts" />
+      :familyId="activeChildId"
+      title="为你推荐" />
 
     <!-- 10. 推荐阅读 -->
-    <DimensionArticles
+    <ArticleBookshelf
       v-if="sectionVisible('recommended_reading')"
-      dimensionCode="wisdom"
       :articles="wisdomArticles"
       :isLoggedIn="isLoggedIn"
       @articleClick="goArticleDetail"
@@ -156,8 +154,8 @@ import RadarChart from '../../components/RadarChart.vue'
 import FamilyEnergyBar from '../../components/FamilyEnergyBar.vue'
 import DimensionTasks from '../../components/DimensionTasks.vue'
 import DimensionActivities from '../../components/DimensionActivities.vue'
-import DimensionProducts from '../../components/DimensionProducts.vue'
-import DimensionArticles from '../../components/DimensionArticles.vue'
+import DimensionProductList from '../../components/DimensionProductList.vue'
+import ArticleBookshelf from '../../components/ArticleBookshelf.vue'
 import AIFloatingAvatar from '../../components/AIFloatingAvatar.vue'
 import FamilyMemberStrip from '../../components/FamilyMemberStrip.vue'
 import RecommendedSection from '../../components/RecommendedSection.vue'
@@ -166,7 +164,7 @@ import { getVisibleSections, getChildren, getTodayTasksByCategory, getActivityLi
 
 export default {
   mixins: [recommendMixin],
-  components: { TabTransition, PageBanner, LoginGuideCard, RadarChart, FamilyEnergyBar, DimensionTasks, DimensionActivities, DimensionProducts, DimensionArticles, AIFloatingAvatar, FamilyMemberStrip, RecommendedSection },
+  components: { TabTransition, PageBanner, LoginGuideCard, RadarChart, FamilyEnergyBar, DimensionTasks, DimensionActivities, DimensionProductList, ArticleBookshelf, AIFloatingAvatar, FamilyMemberStrip, RecommendedSection },
   data() {
     return {
       isLoggedIn: false,
@@ -407,19 +405,6 @@ export default {
       if (!act || !act.id) return
       uni.navigateTo({ url: '/pages/activity/activity-detail/activity-detail?id=' + act.id })
     },
-    goProductDetail: function(prod) {
-      if (prod && prod.id) {
-        var token = uni.getStorageSync('token')
-        if (token) {
-          uni.navigateTo({ url: '/pages/discover-detail/product-detail/product-detail?id=' + prod.id })
-        } else {
-          uni.navigateTo({ url: '/pages/login/login?redirect=' + encodeURIComponent('/pages/wisdom/index') })
-        }
-      }
-    },
-    goMoreProducts: function() {
-      uni.navigateTo({ url: '/pages/shop/index' })
-    },
     parseRelatedDimensions: function(val) {
       if (!val) return []
       if (Array.isArray(val)) return val