Эх сурвалжийг харах

feat(frontend): add article placeholder cards on action home page

User 2 сар өмнө
parent
commit
9c3e7c06e9

+ 43 - 19
cfc-frontend/pages/index/index.vue

@@ -131,27 +131,44 @@
         <text class="section-title">📖 推荐阅读</text>
         <text class="section-more" @click="goArticles">更多 ›</text>
       </view>
-      <view v-if="articlesLoading" class="loading-state">
-        <text class="loading-text">加载中...</text>
-      </view>
-      <view v-else-if="indexArticles.length === 0" class="empty-state" style="padding: 40rpx 0;">
-        <text class="empty-text">暂无文章</text>
-      </view>
-      <view class="article-list" v-else>
-        <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 class="article-list">
+        <view v-if="articlesLoading" class="loading-state" style="padding: 40rpx 0;">
+          <text class="loading-text">加载中...</text>
+        </view>
+        <!-- 无数据时显示占位卡片 -->
+        <template v-else-if="indexArticles.length === 0">
+          <view class="article-card" v-for="i in 3" :key="'ph-' + i">
+            <view class="article-category" :style="{ background: gradientColors[(i-1) % gradientColors.length] }">
+              <text class="article-category-text">推荐文章</text>
+            </view>
+            <text class="article-title">暂无推荐文章</text>
+            <text class="article-summary">精彩内容即将上线,敬请期待</text>
+            <view class="article-meta">
+              <view class="meta-item">
+                <text class="meta-icon">&#x1F4D6;</text>
+                <text class="meta-text">0</text>
+              </view>
+              <text class="article-date">--</text>
+            </view>
           </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>
+        </template>
+        <!-- 有数据时显示文章卡片 -->
+        <template v-else>
+          <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>
-            <text class="article-date">{{ article.date }}</text>
           </view>
-        </view>
+        </template>
       </view>
     </view>
 
@@ -268,7 +285,14 @@ export default {
       loading: false,
       dimensionActivities: [],
       indexArticles: [],
-      articlesLoading: false
+      articlesLoading: false,
+      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)'
+      ]
     }
   },
   onLoad(options) {

+ 175 - 11
cfc-frontend/pages/index/parent-index.vue

@@ -248,6 +248,54 @@
         <text class="error-state-text">商品推荐加载失败</text>
       </view>
 
+      <!-- ===== 推荐阅读 ===== -->
+      <view class="article-section animate-fade-in animate-stagger-12">
+        <view class="article-header">
+          <text class="section-title">📖 推荐阅读</text>
+          <text class="article-more" @click="goArticles">更多 ›</text>
+        </view>
+        <view class="article-list">
+          <!-- 加载中 -->
+          <view v-if="articlesLoading" class="loading-state" style="padding: 40rpx 0;">
+            <text class="loading-text">加载中...</text>
+          </view>
+          <!-- 无数据时显示占位卡片 -->
+          <template v-else-if="indexArticles.length === 0">
+            <view class="article-card" v-for="i in 3" :key="'ph-' + i">
+              <view class="article-category" :style="{ background: gradientColors[(i-1) % gradientColors.length] }">
+                <text class="article-category-text">推荐文章</text>
+              </view>
+              <text class="article-title">暂无推荐文章</text>
+              <text class="article-summary">精彩内容即将上线,敬请期待</text>
+              <view class="article-meta">
+                <view class="meta-item">
+                  <text class="meta-icon">📖</text>
+                  <text class="meta-text">0</text>
+                </view>
+                <text class="article-date">--</text>
+              </view>
+            </view>
+          </template>
+          <!-- 有数据时显示文章卡片 -->
+          <template v-else>
+            <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">📖</text>
+                  <text class="meta-text">{{ article.views }}</text>
+                </view>
+                <text class="article-date">{{ article.date }}</text>
+              </view>
+            </view>
+          </template>
+        </view>
+      </view>
+
       <!-- ===== h) 待审核任务 ===== -->
       <view class="review-section animate-fade-in animate-stagger-11" v-if="pendingReviewTasks.length > 0">
         <view class="section-title">待审核任务</view>
@@ -293,7 +341,7 @@
               <view class="action-icon act-child">
                 <text class="action-icon-text">👨‍👩‍👧</text>
               </view>
-              <text class="action-label">孩子管理</text>
+              <text class="action-label">成员管理</text>
             </view>
             <view class="action-item" @click="goToRewards">
               <view class="action-icon act-reward">
@@ -301,12 +349,6 @@
               </view>
               <text class="action-label">奖励中心</text>
             </view>
-            <view class="action-item" @click="inviteFriend">
-              <view class="action-icon act-invite">
-                <text class="action-icon-text">📤</text>
-              </view>
-              <text class="action-label">邀请家人</text>
-            </view>
           </view>
         </PlayfulCard>
       </view>
@@ -315,7 +357,7 @@
 </template>
 
 <script>
-import { getFamilyMembers, getChildren, getPendingReviewTasks, getPendingWishes, approveTask, rejectTask, getChildCompletionStats, getParentDashboard, getFamilyEnergySandbox, getEnergyOverview, getVisibleFamilyMembers, getActivityList, getProductsByDomain, getContactList } from '../../utils/api.js'
+import { getFamilyMembers, getChildren, getPendingReviewTasks, getPendingWishes, approveTask, rejectTask, getChildCompletionStats, getParentDashboard, getFamilyEnergySandbox, getEnergyOverview, getVisibleFamilyMembers, getActivityList, getProductsByDomain, getContactList, getFeaturedArticles } from '../../utils/api.js'
 import PageBanner from '../../components/PageBanner.vue'
 import DimensionActivities from '../../components/DimensionActivities.vue'
 import DimensionProducts from '../../components/DimensionProducts.vue'
@@ -367,7 +409,17 @@ export default {
       errorActivities: false,
       errorProducts: false,
       contactList: [],
-      showImportModal: false
+      showImportModal: false,
+      // 推荐阅读
+      indexArticles: [],
+      articlesLoading: false,
+      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)'
+      ]
     }
   },
   computed: {
@@ -629,6 +681,8 @@ export default {
         this.loadFamilyMembersVisible()
         // 加载外部联系人
         this.loadContacts()
+        // 加载推荐阅读
+        this.loadFeaturedArticles()
 
         // 加载五维能量概览(账本模式)
         var overviewChildId = this.currentChildId
@@ -747,8 +801,7 @@ export default {
       })
     },
     goToTaskList() { uni.switchTab({ url: '/pages/tasks/tasks' }) },
-    manageChildren() { uni.navigateTo({ url: '/pages/profile/children' }) },
-    inviteFriend() { uni.navigateTo({ url: '/pages/parent/invite/index' }) },
+    manageChildren() { uni.navigateTo({ url: '/pages/profile/family-members' }) },
 
     loadContacts: function() {
       var self = this
@@ -772,6 +825,37 @@ export default {
     onImportSuccess: function() {
       this.showImportModal = false
       this.loadContacts()
+    },
+    // ===== 推荐阅读 =====
+    loadFeaturedArticles: function() {
+      var self = this
+      this.articlesLoading = true
+      getFeaturedArticles({ size: 5 }).then(function(res) {
+        self.articlesLoading = false
+        if (res && res.code === 200) {
+          var rawList = res.data && (Array.isArray(res.data) ? res.data : (res.data.data || res.data.records || []))
+          var list = Array.isArray(rawList) ? rawList : []
+          self.indexArticles = list.map(function(item, index) {
+            return {
+              id: item.id,
+              category: item.categoryName || item.category || '热门文章',
+              categoryColor: self.gradientColors[index % self.gradientColors.length],
+              title: item.title || '',
+              summary: item.summary || '',
+              views: item.viewCount ? String(item.viewCount) : '0',
+              date: item.publishedAt ? item.publishedAt.slice(0, 10) : ''
+            }
+          })
+        }
+      }).catch(function() {
+        self.articlesLoading = false
+      })
+    },
+    goArticles: function() {
+      uni.navigateTo({ url: '/pages/mind-detail/articles' })
+    },
+    goArticleDetail: function(id) {
+      uni.navigateTo({ url: '/pages/mind-detail/article-detail?id=' + id })
     }
   }
 }
@@ -1293,4 +1377,84 @@ export default {
   font-size: 26rpx;
   color: #ccc;
 }
+
+/* ===== 推荐阅读 ===== */
+.article-section {
+  margin-bottom: 48rpx;
+}
+.article-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: var(--space-md);
+}
+.article-more {
+  font-size: 26rpx;
+  color: var(--color-primary, #F97316);
+}
+.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>