Просмотр исходного кода

feat(frontend): add 热门活动 and 推荐阅读 sections to index and body pages

User 2 месяцев назад
Родитель
Сommit
4dde9023f8
2 измененных файлов с 406 добавлено и 3 удалено
  1. 127 1
      cfc-frontend/pages/body/index.vue
  2. 279 2
      cfc-frontend/pages/index/index.vue

+ 127 - 1
cfc-frontend/pages/body/index.vue

@@ -201,6 +201,30 @@
       @productClick="goProductDetail"
       @moreProducts="goMoreProducts" />
 
+    <!-- 推荐阅读(无条件展示) -->
+    <view class="section" v-if="bodyArticles.length > 0">
+      <view class="section-header">
+        <text class="section-title">📖 推荐阅读</text>
+        <text class="section-more" @click="goMoreArticles">更多 ›</text>
+      </view>
+      <view class="article-list">
+        <view class="article-card" v-for="article in bodyArticles" :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>
+            <text class="article-date">{{ article.date }}</text>
+          </view>
+        </view>
+      </view>
+    </view>
+
     <!-- 健康小贴士 -->
     <view class="section" v-if="sectionVisible('health_tips')">
       <view class="section-header">
@@ -247,7 +271,7 @@ import DimensionActivities from '../../components/DimensionActivities.vue'
 import DimensionProducts from '../../components/DimensionProducts.vue'
 import FamilyRelationGraph from '../../components/FamilyRelationGraph.vue'
 import RadarChart from '../../components/RadarChart.vue'
-import { getVisibleSections, getEnergyOverview, getChildren, getTodayTasksByCategory, getActivityList, getProductsByDomain, getFamilyEnergySandbox, getVisibleFamilyMembers, getDimensionOverview } from '../../utils/api.js'
+import { getVisibleSections, getEnergyOverview, getChildren, getTodayTasksByCategory, getActivityList, getProductsByDomain, getFamilyEnergySandbox, getVisibleFamilyMembers, getDimensionOverview, getFeaturedArticles } from '../../utils/api.js'
 import config from '../../config.js'
 
 var BASE_URL = config.api('')
@@ -296,6 +320,7 @@ export default {
       dimensionTasks: [],
       dimensionActivities: [],
       dimensionProducts: [],
+      bodyArticles: [],
       dimensionData: null,
       funcList: [
         { icon: '\u{1F3C3}', label: '运动', needLogin: true, page: '' },
@@ -358,6 +383,8 @@ export default {
     // 游客也能浏览活动和商品
     this.loadDimensionActivities()
     this.loadDimensionProducts()
+    // 游客也能浏览文章
+    this.loadFeaturedArticles()
   },
   methods: {
     loadSectionConfig: function() {
@@ -492,6 +519,32 @@ export default {
         }
       }).catch(function() { self.dimensionProducts = [] })
     },
+    loadFeaturedArticles: function() {
+      var self = this
+      getFeaturedArticles({ size: 5 }).then(function(res) {
+        if (res.code === 200 && res.data) {
+          var gradientColors = [
+            'linear-gradient(135deg, #FF8C42, #FFB074)',
+            'linear-gradient(135deg, #FF6B9D, #FF9DBD)',
+            'linear-gradient(135deg, #6366F1, #818CF8)',
+            'linear-gradient(135deg, #10B981, #34D399)',
+            'linear-gradient(135deg, #F59E0B, #FBBF24)'
+          ]
+          var list = Array.isArray(res.data) ? res.data : (res.data.records || [])
+          self.bodyArticles = list.map(function(item, index) {
+            return {
+              id: item.id,
+              category: item.categoryName || '身体健康',
+              categoryColor: gradientColors[index % gradientColors.length],
+              title: item.title || '',
+              summary: item.summary || '',
+              views: item.viewCount ? String(item.viewCount) : '0',
+              date: item.publishedAt ? item.publishedAt.slice(0, 10) : ''
+            }
+          })
+        }
+      }.bind(this)).catch(function() {})
+    },
     onChildChanged: function(child) {
       this.activeChildId = child.childId
       this.activeChildName = child.name || child.nickname || '孩子'
@@ -524,6 +577,12 @@ export default {
     goMoreProducts: function() {
       uni.navigateTo({ url: '/pages/discover/index?type=product&domain=body' })
     },
+    goArticleDetail: function(id) {
+      uni.navigateTo({ url: '/pages/mind-detail/article-detail?id=' + id })
+    },
+    goMoreArticles: function() {
+      uni.navigateTo({ url: '/pages/mind-detail/articles' })
+    },
     goTasks: function() { uni.switchTab({ url: '/pages/tasks/tasks' }) },
     goMemberDetail: function(member) {
       if (!member || !member.memberId) return
@@ -944,4 +1003,71 @@ export default {
   color: #CCC;
   flex-shrink: 0;
 }
+
+/* ===== 推荐阅读 ===== */
+.article-list {
+  display: flex;
+  flex-direction: column;
+  gap: 16rpx;
+}
+.article-card {
+  background: #fff;
+  border-radius: 16rpx;
+  padding: 24rpx;
+  box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
+}
+.article-card:active {
+  opacity: 0.8;
+}
+.article-category {
+  display: inline-block;
+  padding: 4rpx 14rpx;
+  border-radius: 8rpx;
+  margin-bottom: 10rpx;
+}
+.article-category-text {
+  font-size: 20rpx;
+  color: #fff;
+}
+.article-title {
+  display: block;
+  font-size: 28rpx;
+  font-weight: bold;
+  color: #333;
+  margin-bottom: 8rpx;
+  line-height: 1.4;
+}
+.article-summary {
+  display: block;
+  font-size: 24rpx;
+  color: #888;
+  line-height: 1.5;
+  margin-bottom: 12rpx;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  display: -webkit-box;
+  -webkit-line-clamp: 2;
+  -webkit-box-orient: vertical;
+}
+.article-meta {
+  display: flex;
+  align-items: center;
+  gap: 16rpx;
+}
+.meta-item {
+  display: flex;
+  align-items: center;
+  gap: 4rpx;
+}
+.meta-icon {
+  font-size: 24rpx;
+}
+.meta-text {
+  font-size: 22rpx;
+  color: #999;
+}
+.article-date {
+  font-size: 22rpx;
+  color: #bbb;
+}
 </style>

+ 279 - 2
cfc-frontend/pages/index/index.vue

@@ -54,6 +54,57 @@
       </view>
     </view>
 
+    <!-- 热门活动(无条件展示) -->
+    <view class="section">
+      <view class="section-header">
+        <text class="section-title">🔥 热门活动</text>
+        <text class="section-more" @click="goActivities">查看更多 ›</text>
+      </view>
+      <view v-if="dimensionActivities.length === 0" class="empty-state" style="padding: 40rpx 0;">
+        <text class="empty-text">暂无活动</text>
+      </view>
+      <view class="activity-list" v-else>
+        <view class="activity-card" v-for="act in dimensionActivities" :key="act.id" @click="goActivityDetail(act)">
+          <image class="activity-cover" :src="act.coverImage || '/static/default-activity.png'" mode="aspectFill" />
+          <view class="activity-info">
+            <text class="activity-name line-clamp-1">{{ act.title }}</text>
+            <view class="activity-meta">
+              <text class="activity-time">{{ act.startTime }}</text>
+            </view>
+            <view class="activity-bottom">
+              <text class="activity-status" :class="'status-' + (act.status || 'upcoming')">{{ act.status === 'ongoing' ? '进行中' : act.status === 'ended' ? '已结束' : '即将开始' }}</text>
+              <text class="activity-fee" v-if="act.price && act.price > 0">¥{{ (act.price / 100).toFixed(0) }}</text>
+              <text class="activity-fee fee-free" v-else>免费</text>
+            </view>
+          </view>
+        </view>
+      </view>
+    </view>
+
+    <!-- 推荐阅读(无条件展示) -->
+    <view class="section" v-if="indexArticles.length > 0">
+      <view class="section-header">
+        <text class="section-title">📖 推荐阅读</text>
+        <text class="section-more" @click="goArticles">更多 ›</text>
+      </view>
+      <view class="article-list">
+        <view class="article-card" v-for="article in indexArticles" :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>
+            <text class="article-date">{{ article.date }}</text>
+          </view>
+        </view>
+      </view>
+    </view>
+
     <!-- 五维全景幸福矩阵 -->
     <view class="why-section">
       <view class="section-header">
@@ -114,7 +165,7 @@ import ChildIndex from './child-index.vue'
 import PageBanner from '../../components/PageBanner.vue'
 import WuxingSandbox from '../../components/wuxing-sandbox.vue'
 import TabTransition from '../../components/tab-transition.vue'
-import { acceptParentInvite, productList } from '../../utils/api.js'
+import { acceptParentInvite, productList, getActivityList, getFeaturedArticles } from '../../utils/api.js'
 
 var DIMENSIONS = [
   { code: 'body', name: '身', icon: '🌏', color: '#8D6E63', bgColor: 'rgba(141,110,99,0.15)', title: '主动健康', desc: '用最适合的方法,从根源调理家人体质,畅享高质量长寿生活' },
@@ -153,7 +204,9 @@ export default {
       ],
       currentType: '',
       products: [],
-      loading: false
+      loading: false,
+      dimensionActivities: [],
+      indexArticles: []
     }
   },
   onLoad(options) {
@@ -181,6 +234,8 @@ export default {
       if (!this.isLoggedIn()) {
         this.currentRole = ''
         this.loadProducts()
+        this.loadDimensionActivities()
+        this.loadFeaturedArticles()
         return
       }
       this.syncRoleAndTabBar()
@@ -239,6 +294,53 @@ export default {
         this.loading = false
       }.bind(this))
     },
+    loadDimensionActivities: function() {
+      var self = this
+      getActivityList({ dimensionCode: '', page: 1, size: 3 }).then(function(res) {
+        if (res && res.data) {
+          var list = Array.isArray(res.data) ? res.data : (res.data.records || [])
+          self.dimensionActivities = list.slice(0, 3)
+        }
+      }.bind(this)).catch(function() {})
+    },
+    loadFeaturedArticles: function() {
+      var self = this
+      getFeaturedArticles({ size: 5 }).then(function(res) {
+        if (res.code === 200 && res.data) {
+          var gradientColors = [
+            'linear-gradient(135deg, #10B981, #34D399)',
+            'linear-gradient(135deg, #5B9BD5, #8FC5E8)',
+            'linear-gradient(135deg, #8B5CF6, #C084FC)',
+            'linear-gradient(135deg, #F97316, #FB923C)',
+            'linear-gradient(135deg, #6366F1, #818CF8)'
+          ]
+          var list = Array.isArray(res.data) ? res.data : (res.data.records || [])
+          self.indexArticles = list.map(function(item, index) {
+            return {
+              id: item.id,
+              category: item.categoryName || '热门文章',
+              categoryColor: gradientColors[index % gradientColors.length],
+              title: item.title || '',
+              summary: item.summary || '',
+              views: item.viewCount ? String(item.viewCount) : '0',
+              date: item.publishedAt ? item.publishedAt.slice(0, 10) : ''
+            }
+          })
+        }
+      }.bind(this)).catch(function() {})
+    },
+    goActivities: function() {
+      uni.navigateTo({ url: '/pages/discover/index?type=activity' })
+    },
+    goActivityDetail: function(act) {
+      uni.navigateTo({ url: '/pages/discover-detail/activity-detail/activity-detail?id=' + act.id })
+    },
+    goArticles: function() {
+      uni.navigateTo({ url: '/pages/mind-detail/articles' })
+    },
+    goArticleDetail: function(id) {
+      uni.navigateTo({ url: '/pages/mind-detail/article-detail?id=' + id })
+    },
     onTypeChange(value) {
       this.currentType = value
       this.loadProducts()
@@ -536,4 +638,179 @@ export default {
 .fade-leave-to {
   opacity: 0;
 }
+
+/* ===== 通用区块样式 ===== */
+.section {
+  margin: 20rpx 30rpx;
+}
+.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: #5B9BD5;
+}
+.empty-state {
+  display: flex;
+  justify-content: center;
+  padding: 60rpx 0;
+}
+.empty-text {
+  font-size: 26rpx;
+  color: #999;
+}
+
+/* ===== 活动列表 ===== */
+.activity-list {
+  display: flex;
+  flex-direction: column;
+  gap: 16rpx;
+}
+.activity-card {
+  background: #fff;
+  border-radius: 16rpx;
+  overflow: hidden;
+  display: flex;
+  flex-direction: row;
+  box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
+}
+.activity-card:active {
+  opacity: 0.8;
+}
+.activity-cover {
+  width: 200rpx;
+  height: 160rpx;
+  flex-shrink: 0;
+  background: #f0f0f0;
+}
+.activity-info {
+  flex: 1;
+  padding: 16rpx;
+  display: flex;
+  flex-direction: column;
+  justify-content: space-between;
+}
+.activity-name {
+  font-size: 28rpx;
+  font-weight: bold;
+  color: #333;
+  margin-bottom: 8rpx;
+}
+.line-clamp-1 {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+.activity-meta {
+  display: flex;
+  align-items: center;
+  margin-bottom: 8rpx;
+}
+.activity-time {
+  font-size: 22rpx;
+  color: #999;
+}
+.activity-bottom {
+  display: flex;
+  align-items: center;
+  gap: 12rpx;
+}
+.activity-status {
+  font-size: 20rpx;
+  padding: 2rpx 10rpx;
+  border-radius: 8rpx;
+  background: #E8F5E9;
+  color: #2E7D32;
+}
+.activity-status.status-upcoming {
+  background: #E3F2FD;
+  color: #1565C0;
+}
+.activity-status.status-ended {
+  background: #F5F5F5;
+  color: #999;
+}
+.activity-fee {
+  font-size: 24rpx;
+  color: #F97316;
+  font-weight: bold;
+}
+.activity-fee.fee-free {
+  color: #4CAF50;
+}
+
+/* ===== 文章列表 ===== */
+.article-list {
+  display: flex;
+  flex-direction: column;
+  gap: 16rpx;
+}
+.article-card {
+  background: #fff;
+  border-radius: 16rpx;
+  padding: 24rpx;
+  box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
+}
+.article-card:active {
+  opacity: 0.8;
+}
+.article-category {
+  display: inline-block;
+  padding: 4rpx 14rpx;
+  border-radius: 8rpx;
+  margin-bottom: 10rpx;
+}
+.article-category-text {
+  font-size: 20rpx;
+  color: #fff;
+}
+.article-title {
+  display: block;
+  font-size: 28rpx;
+  font-weight: bold;
+  color: #333;
+  margin-bottom: 8rpx;
+  line-height: 1.4;
+}
+.article-summary {
+  display: block;
+  font-size: 24rpx;
+  color: #888;
+  line-height: 1.5;
+  margin-bottom: 12rpx;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  display: -webkit-box;
+  -webkit-line-clamp: 2;
+  -webkit-box-orient: vertical;
+}
+.article-meta {
+  display: flex;
+  align-items: center;
+  gap: 16rpx;
+}
+.meta-item {
+  display: flex;
+  align-items: center;
+  gap: 4rpx;
+}
+.meta-icon {
+  font-size: 24rpx;
+}
+.meta-text {
+  font-size: 22rpx;
+  color: #999;
+}
+.article-date {
+  font-size: 22rpx;
+  color: #bbb;
+}
 </style>