Procházet zdrojové kódy

refactor(wisdom): 重构智页面布局并提取推荐阅读为共享组件

- 添加认知雷达图(RadarChart)取代旧的认知摘要区,展示六维认知评分
- 重排页面区块顺序:RadarChart → 关系图谱 → 快捷入口 → 功能入口
- 移除WisdomTips组件引用及五个废弃区块(cognition-summary, highlight-card, tips-list, quick-actions, bottom-spacer)
- 创建DimensionArticles共享组件,遵循DimensionTasks/DimensionActivities/DimensionProducts模式
- 智页面推荐阅读区域改用DimensionArticles组件,去除模板重复
Xiaogang Liao před 2 měsíci
rodič
revize
a5ea05eb6d

+ 139 - 0
cfc-frontend/components/DimensionArticles.vue

@@ -0,0 +1,139 @@
+<template>
+  <view class="section" v-if="displayArticles && displayArticles.length > 0">
+    <view class="section-header">
+      <text class="section-title">推荐阅读</text>
+      <text class="section-more" v-if="displayArticles.length > 0" @click="$emit('moreArticles')">更多 ›</text>
+    </view>
+    <view class="article-list">
+      <view class="article-card" v-for="article in displayArticles" :key="article.id" @click="$emit('articleClick', article)">
+        <view class="article-category" :style="{ background: article.categoryColor || defaultColors[0] }">
+          <text class="article-category-text">{{ article.category || '推荐文章' }}</text>
+        </view>
+        <text class="article-title">{{ article.title }}</text>
+        <text class="article-summary" v-if="article.summary">{{ article.summary }}</text>
+        <view class="article-meta">
+          <view class="meta-item">
+            <text class="meta-icon">&#x1F4D6;</text>
+            <text class="meta-text">{{ article.views || '0' }}</text>
+          </view>
+          <view class="meta-item">
+            <text class="meta-icon">&#x1F44D;</text>
+            <text class="meta-text">{{ article.likes || '0' }}</text>
+          </view>
+          <text class="article-date" v-if="article.date">{{ article.date }}</text>
+        </view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script>
+var defaultGradientColors = [
+  'linear-gradient(135deg, #6366F1, #818CF8)',
+  'linear-gradient(135deg, #8B5CF6, #A78BFA)',
+  'linear-gradient(135deg, #5B9BD5, #8FC5E8)',
+  'linear-gradient(135deg, #10B981, #34D399)',
+  'linear-gradient(135deg, #F97316, #FB923C)'
+]
+
+export default {
+  props: {
+    dimensionCode: { type: String, required: true },
+    articles: { type: Array, default: function() { return [] } },
+    isLoggedIn: { type: Boolean, default: false },
+    colors: { type: Array, default: function() { return defaultGradientColors } }
+  },
+  computed: {
+    defaultColors: function() {
+      return defaultGradientColors
+    },
+    displayArticles: function() {
+      return this.articles
+    }
+  }
+}
+</script>
+
+<style scoped>
+.section {
+  margin: 20rpx 20rpx;
+}
+.section-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 20rpx;
+}
+.section-title {
+  font-size: 30rpx;
+  font-weight: bold;
+  color: #333;
+}
+.section-more {
+  font-size: 24rpx;
+  color: #999;
+}
+.article-list {
+  display: flex;
+  flex-direction: column;
+}
+.article-card {
+  background: #fff;
+  border-radius: 20rpx;
+  padding: 28rpx 24rpx;
+  box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
+  margin-bottom: 20rpx;
+}
+.article-card:active {
+  opacity: 0.8;
+}
+.article-category {
+  display: inline-block;
+  padding: 4rpx 18rpx;
+  border-radius: 20rpx;
+  margin-bottom: 14rpx;
+}
+.article-category-text {
+  font-size: 20rpx;
+  color: #fff;
+  font-weight: 500;
+}
+.article-title {
+  font-size: 28rpx;
+  font-weight: bold;
+  color: #333;
+  display: block;
+  margin-bottom: 10rpx;
+}
+.article-summary {
+  font-size: 24rpx;
+  color: #888;
+  line-height: 1.5;
+  display: block;
+  margin-bottom: 20rpx;
+}
+.article-meta {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+}
+.meta-item {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  margin-right: 24rpx;
+}
+.meta-icon {
+  font-size: 24rpx;
+  margin-right: 6rpx;
+}
+.meta-text {
+  font-size: 22rpx;
+  color: #999;
+}
+.article-date {
+  font-size: 22rpx;
+  color: #bbb;
+  margin-left: auto;
+}
+</style>

+ 80 - 274
cfc-frontend/pages/wisdom/index.vue

@@ -17,7 +17,6 @@
           '记录每日成长,获取智慧能量',
           '参与认知训练,提升综合能力'
         ]" />
-      <WisdomTips />
     </template>
 
     <!-- 3. 五维能量条(登录后) -->
@@ -27,7 +26,22 @@
       :sandboxData="sandboxData"
       :dualDimension="dualDimension" />
 
-    <!-- 4. 家庭成员关系图谱(登录后) -->
+    <!-- 4. 认知雷达图(登录后) -->
+    <view class="section" v-if="isLoggedIn && cognitiveReport">
+      <view class="section-header">
+        <text class="section-title">认知雷达</text>
+        <text class="section-more" @click="goCognitiveReport">查看详情 ›</text>
+      </view>
+      <RadarChart
+        :dimensions="cognitiveDimensions"
+        :scores="cognitiveScores"
+        :childName="activeChildName"
+        fillColor="#6366F1"
+        gridColor="#E0E7FF"
+        labelColor="#3730A3" />
+    </view>
+
+    <!-- 5. 家庭成员关系图谱(登录后) -->
     <FamilyRelationGraph
       v-if="isLoggedIn && familyMembersVisible.length > 0"
       dimensionCode="wisdom"
@@ -37,7 +51,7 @@
       :intimacyMap="intimacyMapForGraph"
       @memberTap="goMemberDetail" />
 
-    <!-- 5. 用户快捷入口 -->
+    <!-- 6. 用户快捷入口 -->
     <UserQuickEntry
       v-if="isLoggedIn"
       :userName="userName"
@@ -47,7 +61,7 @@
       @childChanged="onChildChanged"
       @scrollTo="scrollToSection" />
 
-    <!-- 6. 功能入口 -->
+    <!-- 7. 功能入口 -->
     <view class="func-section" v-if="sectionVisible('func_entries')">
       <view class="func-grid">
         <view class="func-item" v-for="item in funcList" :key="item.label" @click="onFuncClick(item)">
@@ -59,59 +73,7 @@
       </view>
     </view>
 
-    <!-- 7. 认知摘要区(登录后) -->
-    <view class="section" v-if="isLoggedIn">
-      <view class="section-header">
-        <text class="section-title">认知概览</text>
-        <text class="section-more" @click="goCognitiveReport">查看详情 ›</text>
-      </view>
-      <view class="cognition-summary" v-if="cognitiveReport">
-        <view class="summary-main">
-          <text class="summary-score">{{ cognitiveReport.overallScore || '--' }}</text>
-          <text class="summary-label">综合认知评分</text>
-        </view>
-        <view class="summary-detail">
-          
-          <text class="summary-min" v-if="lowestDimension">
-            待提升: {{ lowestDimension.label }} ({{ lowestDimension.score }})
-          </text>
-        </view>
-      </view>
-      <view class="cognition-empty" v-else>
-        <text class="empty-tip">暂无认知测评数据</text>
-        <text class="empty-sub">完成DAN评估后查看详细报告</text>
-      </view>
-    </view>
-
-    <!-- 8. 自己出题入口卡(登录后) -->
-    <view class="section highlight-card" v-if="isLoggedIn" @click="goSelfQuiz">
-      <view class="highlight-card-inner">
-        <view class="highlight-card-left">
-          <text class="highlight-icon">✏️</text>
-        </view>
-        <view class="highlight-card-right">
-          <text class="highlight-title">自己出题自己答</text>
-          <text class="highlight-desc">元认知训练:自己出题、自己判分,提升学习能力</text>
-        </view>
-        <text class="highlight-arrow">›</text>
-      </view>
-    </view>
-
-    <!-- 9. 训练中心入口卡(登录后) -->
-    <view class="section highlight-card" v-if="isLoggedIn" @click="goTrainingHub">
-      <view class="highlight-card-inner">
-        <view class="highlight-card-left">
-          <text class="highlight-icon">🧠</text>
-        </view>
-        <view class="highlight-card-right">
-          <text class="highlight-title">认知训练中心</text>
-          <text class="highlight-desc">舒尔特方格 · 猜数字 — 多维度认知训练游戏</text>
-        </view>
-        <text class="highlight-arrow">›</text>
-      </view>
-    </view>
-
-    <!-- 10. 维度任务 -->
+    <!-- 8. 维度任务 -->
     <DimensionTasks
       v-if="isLoggedIn"
       dimensionCode="wisdom"
@@ -119,48 +81,30 @@
       @taskClick="onTaskClick"
       @moreTasks="goTasks" />
 
-    <!-- 11. 维度活动 -->
+    <!-- 9. 维度活动 -->
     <DimensionActivities
       dimensionCode="wisdom"
       :activities="dimensionActivities"
       @activityClick="goActivityDetail"
       @moreActivities="goMoreActivities" />
 
-    <!-- 12. 维度商品 -->
+    <!-- 10. 维度商品 -->
     <DimensionProducts
       dimensionCode="wisdom"
       :products="dimensionProducts"
       @productClick="goProductDetail"
       @moreProducts="goMoreProducts" />
 
-    <!-- 推荐阅读 -->
-    <view class="section" v-if="sectionVisible('recommended_reading') && wisdomArticles.length > 0">
-      <view class="section-header">
-        <text class="section-title">推荐阅读</text>
-      </view>
-      <view class="article-list">
-        <view class="article-card" v-for="article in wisdomArticles" :key="article.id" @click="goArticleDetail(article.id)">
-          <view class="article-category" :style="{ background: article.categoryColor }">
-            <text class="article-category-text">{{ article.category }}</text>
-          </view>
-          <text class="article-title">{{ article.title }}</text>
-          <text class="article-summary">{{ article.summary }}</text>
-          <view class="article-meta">
-            <view class="meta-item">
-              <text class="meta-icon">&#x1F4D6;</text>
-              <text class="meta-text">{{ article.views }}</text>
-            </view>
-            <view class="meta-item">
-              <text class="meta-icon">&#x1F44D;</text>
-              <text class="meta-text">{{ article.likes }}</text>
-            </view>
-            <text class="article-date">{{ article.date }}</text>
-          </view>
-        </view>
-      </view>
-    </view>
+    <!-- 11. 推荐阅读 -->
+    <DimensionArticles
+      v-if="sectionVisible('recommended_reading')"
+      dimensionCode="wisdom"
+      :articles="wisdomArticles"
+      :isLoggedIn="isLoggedIn"
+      @articleClick="goArticleDetail"
+      @moreArticles="goMoreArticles" />
 
-    <!-- 13. 智慧小贴士 -->
+    <!-- 12. 智慧小贴士 -->
     <view class="section" v-if="sectionVisible('wisdom_tips')">
       <view class="section-header">
         <text class="section-title">智慧小贴士</text>
@@ -173,7 +117,7 @@
       </view>
     </view>
 
-    <!-- 14. 快捷操作按钮 -->
+    <!-- 13. 快捷操作按钮 -->
     <view class="section quick-actions" v-if="isLoggedIn">
       <view class="action-btn" @click="goCognitiveReport">
         <text class="action-icon">📊</text>
@@ -202,17 +146,18 @@
 import TabTransition from '../../components/tab-transition.vue'
 import PageBanner from '../../components/PageBanner.vue'
 import LoginGuideCard from '../../components/LoginGuideCard.vue'
-import WisdomTips from '../../components/WisdomTips.vue'
+import RadarChart from '../../components/RadarChart.vue'
 import FamilyEnergyBar from '../../components/FamilyEnergyBar.vue'
 import UserQuickEntry from '../../components/UserQuickEntry.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 FamilyRelationGraph from '../../components/FamilyRelationGraph.vue'
 import { getVisibleSections, getChildren, getTodayTasksByCategory, getActivityList, getProductsByDomain, getFamilyEnergySandbox, getAssessmentLatestResult, getFeaturedArticles } from '../../utils/api.js'
 
 export default {
-  components: { TabTransition, PageBanner, LoginGuideCard, WisdomTips, FamilyEnergyBar, UserQuickEntry, DimensionTasks, DimensionActivities, DimensionProducts, FamilyRelationGraph },
+  components: { TabTransition, PageBanner, LoginGuideCard, RadarChart, FamilyEnergyBar, UserQuickEntry, DimensionTasks, DimensionActivities, DimensionProducts, DimensionArticles, FamilyRelationGraph },
   data() {
     return {
       isLoggedIn: false,
@@ -227,23 +172,17 @@ export default {
       dualDimension: null,
       visibleSections: [],
       cognitiveReport: null,
-      lowestDimension: null,
       dimensionTasks: [],
       dimensionActivities: [],
       dimensionProducts: [],
       articles: [],
+      wisdomTips: [],
       funcList: [
         { icon: '\u{1F4CA}', label: '认知报告', needLogin: true, page: '/pages/wisdom/cognitive-report' },
         { icon: '\u{270F}\uFE0F', label: '自己出题', needLogin: true, page: '/pages/wisdom/self-quiz' },
         { icon: '\u{1F9E0}', label: '训练中心', needLogin: true, page: '/pages/wisdom/training-hub' },
         { icon: '\u{1F3AF}', label: '测评预约', needLogin: false, page: '/pages/assessment/apply' },
         { icon: '\u{1F4D6}', label: '阅读天地', needLogin: false, page: '/pages/mind/articles' }
-      ],
-      wisdomTips: [
-        { id: 1, title: '认知发展的关键期', summary: '儿童认知发展存在关键期窗口,抓住每个阶段的核心能力培养机会。' },
-        { id: 2, title: '元认知训练的意义', summary: '教会孩子"学会如何学习",通过自我监控和反思提升学习效率。' },
-        { id: 3, title: '专注力的培养方法', summary: '从短时间专注起步,逐步延长专注时长,配合舒尔特方格等训练工具。' },
-        { id: 4, title: '多维度认知刺激', summary: '综合运用视、听、触等多种感官刺激,促进大脑神经网络全面发展。' }
       ]
     }
   },
@@ -277,6 +216,25 @@ export default {
       }
       if (result.length === 0) return this.articles.slice(0, 5)
       return result.slice(0, 5)
+    },
+    cognitiveDimensions: function() {
+      return [
+        { label: '感知能力', key: 'perceptionScore' },
+        { label: '专注力', key: 'focusScore' },
+        { label: '记忆力', key: 'memoryScore' },
+        { label: '逻辑思维', key: 'logicScore' },
+        { label: '空间思维', key: 'spatialScore' },
+        { label: '加工速度', key: 'processingSpeedScore' }
+      ]
+    },
+    cognitiveScores: function() {
+      if (!this.cognitiveReport) return []
+      var dims = this.cognitiveDimensions
+      var scores = []
+      for (var i = 0; i < dims.length; i++) {
+        scores.push(this.cognitiveReport[dims[i].key] || 0)
+      }
+      return scores
     }
   },
   onShow() {
@@ -368,30 +326,9 @@ export default {
       getAssessmentLatestResult(childId).then(function(res) {
         if (res.code === 200 && res.data) {
           self.cognitiveReport = res.data
-          self.findLowestDimension(res.data)
         }
       }).catch(function(e) {})
     },
-    findLowestDimension: function(report) {
-      var dims = [
-        { key: 'perceptionScore', label: '感知能力' },
-        { key: 'focusScore', label: '专注力' },
-        { key: 'memoryScore', label: '记忆力' },
-        { key: 'logicScore', label: '逻辑思维' },
-        { key: 'spatialScore', label: '空间思维' },
-        { key: 'processingSpeedScore', label: '加工速度' }
-      ]
-      var lowest = null
-      var minVal = Infinity
-      for (var i = 0; i < dims.length; i++) {
-        var val = report[dims[i].key]
-        if (val !== null && val !== undefined && val < minVal) {
-          minVal = val
-          lowest = { label: dims[i].label, score: val }
-        }
-      }
-      this.lowestDimension = lowest
-    },
     onChildChanged: function(childId) {
       this.activeChildId = childId
       uni.setStorageSync('currentChildId', childId)
@@ -511,8 +448,11 @@ export default {
         }
       }).catch(function() {})
     },
-    goArticleDetail: function(id) {
-      uni.navigateTo({ url: '/pages/wisdom/article-detail?id=' + id })
+    goMoreArticles: function() {
+      uni.navigateTo({ url: '/pages/mind/articles' })
+    },
+    goArticleDetail: function(article) {
+      uni.navigateTo({ url: '/pages/wisdom/article-detail?id=' + (article.id || article) })
     }
   }
 }
@@ -594,199 +534,65 @@ export default {
   color: #999;
 }
 
-/* ===== 认知概览 ===== */
-.cognition-summary {
-  background: linear-gradient(135deg, #FFD700, #FFA500);
-  border-radius: 20rpx;
-  padding: 30rpx;
-  display: flex;
-  flex-direction: row;
-  align-items: center;
-}
-
-.summary-main {
-  text-align: center;
-  margin-right: 30rpx;
-  padding-right: 30rpx;
-  border-right: 2rpx solid rgba(255,255,255,0.3);
-}
-
-.summary-score {
-  font-size: 60rpx;
-  font-weight: bold;
-  color: #fff;
-  display: block;
-}
-
-.summary-label {
-  font-size: 22rpx;
-  color: rgba(255,255,255,0.85);
-  display: block;
-  margin-top: 4rpx;
-}
-
-.summary-detail {
-  flex: 1;
-}
-
-.summary-dan {
-  font-size: 28rpx;
-  color: #fff;
-  font-weight: bold;
-  display: block;
-  margin-bottom: 8rpx;
-}
-
-.summary-min {
-  font-size: 24rpx;
-  color: rgba(255,255,255,0.85);
-  display: block;
-}
-
-.cognition-empty {
-  background: #fff;
-  border-radius: 16rpx;
-  padding: 40rpx;
-  text-align: center;
-  box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
-}
-
-.empty-tip {
-  font-size: 26rpx;
-  color: #999;
-  display: block;
-}
-
-.empty-sub {
-  font-size: 22rpx;
-  color: #ccc;
-  margin-top: 6rpx;
-  display: block;
-}
-
-/* ===== 高亮卡片 ===== */
-.highlight-card {
-  margin: 20rpx;
-}
-
-.highlight-card-inner {
-  display: flex;
-  flex-direction: row;
-  align-items: center;
-  background: linear-gradient(135deg, #FFF8E1, #fff);
-  border: 2rpx solid #FFD700;
-  border-radius: 20rpx;
-  padding: 24rpx;
-}
-
-.highlight-card:active {
-  opacity: 0.8;
-}
-
-.highlight-card-left {
-  width: 72rpx;
-  height: 72rpx;
-  border-radius: 18rpx;
-  background: #FFD700;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  margin-right: 20rpx;
-}
-
-.highlight-icon {
-  font-size: 36rpx;
-}
-
-.highlight-card-right {
-  flex: 1;
-}
-
-.highlight-title {
-  font-size: 28rpx;
-  font-weight: bold;
-  color: #B8860B;
-  display: block;
-  margin-bottom: 4rpx;
-}
-
-.highlight-desc {
-  font-size: 22rpx;
-  color: #999;
-  display: block;
-}
-
-.highlight-arrow {
-  font-size: 36rpx;
-  color: #ccc;
-  margin-left: 10rpx;
-}
-
-/* ===== 小贴士 ===== */
+/* ===== 智慧小贴士 ===== */
 .tips-list {
   display: flex;
   flex-direction: column;
 }
-
 .tip-card {
   background: #fff;
   border-radius: 16rpx;
-  padding: 24rpx;
-  margin-bottom: 16rpx;
+  padding: 24rpx 20rpx;
   box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
-  border-left: 6rpx solid #FFD700;
+  margin-bottom: 16rpx;
 }
-
 .tip-title {
-  font-size: 26rpx;
+  font-size: 28rpx;
   font-weight: bold;
   color: #333;
   display: block;
   margin-bottom: 8rpx;
 }
-
 .tip-summary {
   font-size: 24rpx;
-  color: #999;
+  color: #888;
   line-height: 1.5;
   display: block;
 }
 
-/* ===== 快捷操作 ===== */
+/* ===== 快捷操作按钮 ===== */
 .quick-actions {
   display: flex;
   flex-direction: row;
-  justify-content: space-around;
-  margin: 20rpx;
+  flex-wrap: wrap;
+  justify-content: space-between;
 }
-
 .action-btn {
-  display: flex;
-  flex-direction: column;
-  align-items: center;
+  width: 48%;
   background: #fff;
   border-radius: 16rpx;
-  padding: 20rpx 30rpx;
+  padding: 24rpx 0;
   box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
-  flex: 1;
-  margin: 0 8rpx;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  margin-bottom: 20rpx;
 }
-
 .action-btn:active {
-  opacity: 0.7;
+  opacity: 0.8;
 }
-
 .action-icon {
-  font-size: 36rpx;
-  margin-bottom: 8rpx;
+  font-size: 40rpx;
+  margin-bottom: 10rpx;
 }
-
 .action-text {
-  font-size: 22rpx;
+  font-size: 26rpx;
   color: #333;
+  font-weight: 500;
 }
 
+/* ===== 底部占位 ===== */
 .bottom-spacer {
-  height: 20rpx;
+  height: 120rpx;
 }
 </style>