Jelajahi Sumber

feat(web): 批量添加列表页分页+排序+横向滚动(109文件)

- 101 个 admin/teacher/nutritionist/service 页面添加 table-scroll-wrap 横向滚动
- 67 个页面添加 sortable="custom" 列排序
- 12 个 teacher/nutritionist/service 页面添加 el-pagination 分页
- PlanManagement.vue 样板参考实现
Sisyphus 1 bulan lalu
induk
melakukan
77e8f2110b
100 mengubah file dengan 1307 tambahan dan 232 penghapusan
  1. 5 1
      cfc-web/src/views/Children.vue
  2. 6 2
      cfc-web/src/views/Families.vue
  3. 5 1
      cfc-web/src/views/OperationLogs.vue
  4. 5 1
      cfc-web/src/views/Points.vue
  5. 7 3
      cfc-web/src/views/Rewards.vue
  6. 7 3
      cfc-web/src/views/Tasks.vue
  7. 5 1
      cfc-web/src/views/Users.vue
  8. 9 5
      cfc-web/src/views/Wishes.vue
  9. 15 4
      cfc-web/src/views/admin/Activities.vue
  10. 9 1
      cfc-web/src/views/admin/ActivityRegistrationReview.vue
  11. 14 3
      cfc-web/src/views/admin/ActivityReview.vue
  12. 13 1
      cfc-web/src/views/admin/AiQuestionnaireScenes.vue
  13. 10 2
      cfc-web/src/views/admin/ArticleManage.vue
  14. 10 2
      cfc-web/src/views/admin/Assessment.vue
  15. 16 3
      cfc-web/src/views/admin/AssessmentAssign.vue
  16. 16 3
      cfc-web/src/views/admin/AssessmentOrders.vue
  17. 17 4
      cfc-web/src/views/admin/AssessmentPlanRules.vue
  18. 14 1
      cfc-web/src/views/admin/AssessmentProducts.vue
  19. 13 3
      cfc-web/src/views/admin/BadgeManage.vue
  20. 2 0
      cfc-web/src/views/admin/BaziConfigs.vue
  21. 2 0
      cfc-web/src/views/admin/BloodTypeConfigs.vue
  22. 14 3
      cfc-web/src/views/admin/CfValueManage.vue
  23. 13 1
      cfc-web/src/views/admin/ChallengeScoreConfig.vue
  24. 15 4
      cfc-web/src/views/admin/CommentReview.vue
  25. 10 2
      cfc-web/src/views/admin/CouponGrantLog.vue
  26. 3 1
      cfc-web/src/views/admin/CouponManagement.vue
  27. 15 2
      cfc-web/src/views/admin/DanExecution.vue
  28. 16 3
      cfc-web/src/views/admin/DanSettlement.vue
  29. 14 3
      cfc-web/src/views/admin/Dashboard.vue
  30. 2 0
      cfc-web/src/views/admin/EcomSupplierManage.vue
  31. 15 3
      cfc-web/src/views/admin/EmotionAlertManage.vue
  32. 15 2
      cfc-web/src/views/admin/EnergyBehaviorConfig.vue
  33. 13 1
      cfc-web/src/views/admin/EnergyRuleManagement.vue
  34. 12 1
      cfc-web/src/views/admin/EnergySandbox.vue
  35. 9 1
      cfc-web/src/views/admin/FamilyEarnings.vue
  36. 9 1
      cfc-web/src/views/admin/FamilyEarningsWithdraw.vue
  37. 16 3
      cfc-web/src/views/admin/FileManage.vue
  38. 14 1
      cfc-web/src/views/admin/Foods.vue
  39. 10 1
      cfc-web/src/views/admin/GrowthTaskManagement.vue
  40. 14 2
      cfc-web/src/views/admin/HealthCheckinManage.vue
  41. 18 7
      cfc-web/src/views/admin/InventoryManage.vue
  42. 14 3
      cfc-web/src/views/admin/KnowledgeTag.vue
  43. 14 3
      cfc-web/src/views/admin/LangGraphAdmin.vue
  44. 15 4
      cfc-web/src/views/admin/MembershipCenter.vue
  45. 3 1
      cfc-web/src/views/admin/MenuManage.vue
  46. 9 1
      cfc-web/src/views/admin/NoticeManage.vue
  47. 13 2
      cfc-web/src/views/admin/NotificationTemplateManage.vue
  48. 14 1
      cfc-web/src/views/admin/NutritionProducts.vue
  49. 10 2
      cfc-web/src/views/admin/OrderManage.vue
  50. 10 2
      cfc-web/src/views/admin/PendingRefund.vue
  51. 14 1
      cfc-web/src/views/admin/PeriodicServiceConfig.vue
  52. 70 29
      cfc-web/src/views/admin/PlanManagement.vue
  53. 10 2
      cfc-web/src/views/admin/PointsLog.vue
  54. 11 1
      cfc-web/src/views/admin/ProblemDomainManage.vue
  55. 12 4
      cfc-web/src/views/admin/ProductManage.vue
  56. 13 1
      cfc-web/src/views/admin/ProductPpointManage.vue
  57. 13 1
      cfc-web/src/views/admin/ProductProfitRate.vue
  58. 12 1
      cfc-web/src/views/admin/PromotionManagement.vue
  59. 3 1
      cfc-web/src/views/admin/Recipes.vue
  60. 15 3
      cfc-web/src/views/admin/ReportAutoLearn.vue
  61. 15 2
      cfc-web/src/views/admin/ReportManagement.vue
  62. 15 3
      cfc-web/src/views/admin/ReportParserImport.vue
  63. 11 1
      cfc-web/src/views/admin/ReportTypeManagement.vue
  64. 16 4
      cfc-web/src/views/admin/ReportUnknownCluster.vue
  65. 2 0
      cfc-web/src/views/admin/SeasonalFoods.vue
  66. 12 4
      cfc-web/src/views/admin/SubscriptionManagement.vue
  67. 10 2
      cfc-web/src/views/admin/SupplierProductManage.vue
  68. 16 4
      cfc-web/src/views/admin/SurveyTemplates.vue
  69. 13 1
      cfc-web/src/views/admin/SysConfig.vue
  70. 16 4
      cfc-web/src/views/admin/UnifiedHealthReports.vue
  71. 15 4
      cfc-web/src/views/admin/VirtualGoodsConfig.vue
  72. 17 4
      cfc-web/src/views/admin/VirtualTeamList.vue
  73. 2 0
      cfc-web/src/views/admin/ZodiacConfigs.vue
  74. 10 2
      cfc-web/src/views/admin/data-source-config.vue
  75. 12 1
      cfc-web/src/views/admin/dimension-config.vue
  76. 13 1
      cfc-web/src/views/admin/dimension/index.vue
  77. 14 1
      cfc-web/src/views/admin/energy-config/WuxingConfig.vue
  78. 12 2
      cfc-web/src/views/admin/gates.vue
  79. 14 2
      cfc-web/src/views/admin/health-knowledge.vue
  80. 11 1
      cfc-web/src/views/admin/indicators/index.vue
  81. 13 1
      cfc-web/src/views/admin/review/ActivityAudit.vue
  82. 12 1
      cfc-web/src/views/admin/review/ActivityRegistrationAudit.vue
  83. 15 3
      cfc-web/src/views/admin/review/ArticleAudit.vue
  84. 13 1
      cfc-web/src/views/admin/review/ButlerAudit.vue
  85. 13 1
      cfc-web/src/views/admin/review/GuideAudit.vue
  86. 13 1
      cfc-web/src/views/admin/review/HierarchyAudit.vue
  87. 13 1
      cfc-web/src/views/admin/review/NutritionistAudit.vue
  88. 15 3
      cfc-web/src/views/admin/review/PackageAudit.vue
  89. 13 1
      cfc-web/src/views/admin/review/ProductAudit.vue
  90. 13 1
      cfc-web/src/views/admin/review/VendorAudit.vue
  91. 15 3
      cfc-web/src/views/admin/review/WithdrawAudit.vue
  92. 15 3
      cfc-web/src/views/admin/supply-hierarchy/ChangeRequest.vue
  93. 16 3
      cfc-web/src/views/admin/supply-manage/List.vue
  94. 14 1
      cfc-web/src/views/admin/supply-system/List.vue
  95. 16 3
      cfc-web/src/views/health/HealthReportAudit.vue
  96. 31 4
      cfc-web/src/views/nutritionist/HealthCheckins.vue
  97. 30 3
      cfc-web/src/views/nutritionist/HealthIndicators.vue
  98. 13 2
      cfc-web/src/views/nutritionist/HealthReports.vue
  99. 26 1
      cfc-web/src/views/nutritionist/NutritionMappings.vue
  100. 30 3
      cfc-web/src/views/service/ServiceRoleApplications.vue

+ 5 - 1
cfc-web/src/views/Children.vue

@@ -39,6 +39,8 @@
         </el-form>
 
         <!-- 孩子列表 -->
+        <div class="table-scroll-wrap">
+
         <el-table :data="tableData" border v-loading="loading" style="width: 100%" :max-height="tableHeight">
         <el-table-column prop="id" label="ID" width="80"></el-table-column>
         <el-table-column prop="nickname" label="昵称" width="150"></el-table-column>
@@ -61,7 +63,7 @@
             </el-tag>
           </template>
         </el-table-column>
-        <el-table-column prop="createdAt" label="创建时间" width="180">
+        <el-table-column prop="createdAt" label="创建时间" width="180" sortable="custom" :sort-orders="['ascending','descending']"  sortable="custom" :sort-orders="['ascending','descending']" >
           <template slot-scope="scope">
             {{ formatDate(scope.row.createdAt) }}
           </template>
@@ -81,6 +83,7 @@
         </el-table-column>
         </el-table>
 
+    </div>
         <!-- 分页 -->
       <el-pagination
         style="margin-top: 20px; text-align: right"
@@ -310,4 +313,5 @@ export default {
 </script>
 
 <style scoped>
+    .table-scroll-wrap { overflow-x: auto; }
 </style>

+ 6 - 2
cfc-web/src/views/Families.vue

@@ -31,6 +31,8 @@
         </el-form>
 
         <!-- 家庭列表 -->
+        <div class="table-scroll-wrap">
+
         <el-table
           :data="tableData"
           border
@@ -41,7 +43,7 @@
         <el-table-column prop="id" label="ID" width="80"></el-table-column>
         <el-table-column prop="name" label="家庭名称" width="200"></el-table-column>
         <el-table-column prop="inviteCode" label="邀请码" width="150"></el-table-column>
-        <el-table-column prop="createdAt" label="创建时间" width="180">
+        <el-table-column prop="createdAt" label="创建时间" width="180" sortable="custom" :sort-orders="['ascending','descending']"  sortable="custom" :sort-orders="['ascending','descending']" >
           <template slot-scope="scope">
             {{ formatDate(scope.row.createdAt) }}
           </template>
@@ -62,6 +64,7 @@
         </el-table-column>
         </el-table>
 
+    </div>
         <!-- 分页 -->
       <el-pagination
         style="margin-top: 20px; text-align: right"
@@ -134,7 +137,7 @@
               <el-tag type="success">{{ scope.row.totalPoints || 0 }}</el-tag>
             </template>
           </el-table-column>
-          <el-table-column prop="createdAt" label="创建时间">
+          <el-table-column prop="createdAt" label="创建时间" sortable="custom" :sort-orders="['ascending','descending']"  sortable="custom" :sort-orders="['ascending','descending']" >
             <template slot-scope="scope">
               {{ formatDate(scope.row.createdAt) }}
             </template>
@@ -310,4 +313,5 @@ export default {
 </script>
 
 <style scoped>
+    .table-scroll-wrap { overflow-x: auto; }
 </style>

+ 5 - 1
cfc-web/src/views/OperationLogs.vue

@@ -35,6 +35,8 @@
 
     <!-- 日志列表 -->
     <el-card>
+      <div class="table-scroll-wrap">
+
       <el-table :max-height="tableHeight" :data="logs" v-loading="loading" stripe>
         <el-table-column prop="id" label="ID" width="80"></el-table-column>
         <el-table-column prop="nickname" label="用户名" width="120"></el-table-column>
@@ -56,7 +58,7 @@
           </template>
         </el-table-column>
         <el-table-column prop="clientIp" label="IP地址" width="130"></el-table-column>
-        <el-table-column prop="createdAt" label="操作时间" width="160">
+        <el-table-column prop="createdAt" label="操作时间" width="160" sortable="custom" :sort-orders="['ascending','descending']"  sortable="custom" :sort-orders="['ascending','descending']" >
           <template slot-scope="scope">
             {{ formatDate(scope.row.createdAt) }}
           </template>
@@ -68,6 +70,7 @@
         </el-table-column>
       </el-table>
 
+    </div>
       <!-- 分页 -->
       <el-pagination
         @current-change="handlePageChange"
@@ -207,4 +210,5 @@ export default {
   margin-top: 20px;
   text-align: right;
 }
+    .table-scroll-wrap { overflow-x: auto; }
 </style>

+ 5 - 1
cfc-web/src/views/Points.vue

@@ -41,6 +41,8 @@
       </el-form>
 
       <!-- 积分流水列表 -->
+      <div class="table-scroll-wrap">
+
       <el-table :data="tableData" border v-loading="loading" style="width: 100%" :max-height="tableHeight">
         <el-table-column prop="id" label="ID" width="80"></el-table-column>
         <el-table-column prop="childId" label="孩子ID" width="100"></el-table-column>
@@ -64,13 +66,14 @@
             <span>{{ scope.row.taskId || '-' }}</span>
           </template>
         </el-table-column>
-        <el-table-column prop="createdAt" label="时间" width="180">
+        <el-table-column prop="createdAt" label="时间" width="180" sortable="custom" :sort-orders="['ascending','descending']"  sortable="custom" :sort-orders="['ascending','descending']" >
           <template slot-scope="scope">
             {{ formatDate(scope.row.createdAt) }}
           </template>
         </el-table-column>
       </el-table>
 
+    </div>
       <!-- 分页 -->
       <el-pagination
         style="margin-top: 20px; text-align: right"
@@ -189,4 +192,5 @@ export default {
 </script>
 
 <style scoped>
+    .table-scroll-wrap { overflow-x: auto; }
 </style>

+ 7 - 3
cfc-web/src/views/Rewards.vue

@@ -52,16 +52,19 @@
 
           <!-- 家庭选择对话框 -->
           <el-dialog title="选择家庭" :visible.sync="familyDialogVisible" width="600px">
+            <div class="table-scroll-wrap">
+
             <el-table :max-height="tableHeight" style="cursor: pointer" :data="familyList" border v-loading="familyLoading" @row-click="selectFamily">
               <el-table-column prop="id" label="ID" width="80"></el-table-column>
               <el-table-column prop="name" label="家庭名称" min-width="150"></el-table-column>
               <el-table-column prop="inviteCode" label="邀请码" width="120"></el-table-column>
-              <el-table-column prop="createdAt" label="创建时间" width="180">
+              <el-table-column prop="createdAt" label="创建时间" width="180" sortable="custom" :sort-orders="['ascending','descending']"  sortable="custom" :sort-orders="['ascending','descending']" >
                 <template slot-scope="scope">
                   {{ formatDate(scope.row.createdAt) }}
                 </template>
               </el-table-column>
             </el-table>
+    </div>
             <el-pagination
               style="margin-top: 15px; text-align: right"
               @current-change="loadFamilies"
@@ -89,7 +92,7 @@
                 </el-tag>
               </template>
             </el-table-column>
-            <el-table-column prop="createdAt" label="创建时间" width="180">
+            <el-table-column prop="createdAt" label="创建时间" width="180" sortable="custom" :sort-orders="['ascending','descending']"  sortable="custom" :sort-orders="['ascending','descending']" >
               <template slot-scope="scope">
                 {{ formatDate(scope.row.createdAt) }}
               </template>
@@ -142,7 +145,7 @@
               </template>
             </el-table-column>
             <el-table-column prop="category" label="分类" width="100"></el-table-column>
-            <el-table-column prop="createdAt" label="创建时间" width="180">
+            <el-table-column prop="createdAt" label="创建时间" width="180" sortable="custom" :sort-orders="['ascending','descending']"  sortable="custom" :sort-orders="['ascending','descending']" >
               <template slot-scope="scope">
                 {{ formatDate(scope.row.createdAt) }}
               </template>
@@ -423,4 +426,5 @@ export default {
 </script>
 
 <style scoped>
+    .table-scroll-wrap { overflow-x: auto; }
 </style>

+ 7 - 3
cfc-web/src/views/Tasks.vue

@@ -57,16 +57,19 @@
 
       <!-- 家庭选择对话框 -->
       <el-dialog title="选择家庭" :visible.sync="familyDialogVisible" width="600px">
+        <div class="table-scroll-wrap">
+
         <el-table :data="familyList" border v-loading="familyLoading" @row-click="selectFamily" style="cursor: pointer" :max-height="300">
           <el-table-column prop="id" label="ID" width="80"></el-table-column>
           <el-table-column prop="name" label="家庭名称" min-width="150"></el-table-column>
           <el-table-column prop="inviteCode" label="邀请码" width="120"></el-table-column>
-          <el-table-column prop="createdAt" label="创建时间" width="180">
+          <el-table-column prop="createdAt" label="创建时间" width="180" sortable="custom" :sort-orders="['ascending','descending']"  sortable="custom" :sort-orders="['ascending','descending']" >
             <template slot-scope="scope">
               {{ formatDate(scope.row.createdAt) }}
             </template>
           </el-table-column>
         </el-table>
+    </div>
         <el-pagination
           style="margin-top: 15px; text-align: right"
           @current-change="loadFamilies"
@@ -103,7 +106,7 @@
           </template>
         </el-table-column>
         <el-table-column prop="category" label="分类" width="100"></el-table-column>
-        <el-table-column prop="deadline" label="截止时间" width="180">
+        <el-table-column prop="deadline" label="截止时间" width="180" sortable="custom" :sort-orders="['ascending','descending']" >
           <template slot-scope="scope">
             {{ formatDate(scope.row.deadline) }}
           </template>
@@ -123,7 +126,7 @@
           </template>
         </el-table-column>
         <el-table-column prop="childId" label="孩子ID" width="80"></el-table-column>
-        <el-table-column prop="createdAt" label="创建时间" width="180">
+        <el-table-column prop="createdAt" label="创建时间" width="180" sortable="custom" :sort-orders="['ascending','descending']"  sortable="custom" :sort-orders="['ascending','descending']" >
           <template slot-scope="scope">
             {{ formatDate(scope.row.createdAt) }}
           </template>
@@ -317,4 +320,5 @@ export default {
   font-weight: bold;
   color: #409EFF;
 }
+    .table-scroll-wrap { overflow-x: auto; }
 </style>

+ 5 - 1
cfc-web/src/views/Users.vue

@@ -60,6 +60,8 @@
       </el-form>
 
       <!-- 用户列表 -->
+<div class="table-scroll-wrap">
+
 <el-table 
         :data="tableData" 
         border 
@@ -89,7 +91,7 @@
           </template>
         </el-table-column>
         <el-table-column prop="familyId" label="家庭ID" width="100"></el-table-column>
-        <el-table-column prop="createdAt" label="创建时间" width="180">
+        <el-table-column prop="createdAt" label="创建时间" width="180" sortable="custom" :sort-orders="['ascending','descending']"  sortable="custom" :sort-orders="['ascending','descending']" >
           <template slot-scope="scope">
             {{ formatDate(scope.row.createdAt) }}
           </template>
@@ -113,6 +115,7 @@
         </el-table-column>
       </el-table>
 
+    </div>
       <!-- 分页 -->
       <el-pagination
         style="margin-top: 20px; text-align: right"
@@ -421,4 +424,5 @@ export default {
   cursor: pointer;
   white-space: nowrap;
 }
+    .table-scroll-wrap { overflow-x: auto; }
 </style>

+ 9 - 5
cfc-web/src/views/Wishes.vue

@@ -48,12 +48,14 @@
           <!-- 待定价列表 -->
           <div v-if="draftWishes.length > 0" class="wish-section">
             <div class="section-title">📝 待定价</div>
+<div class="table-scroll-wrap">
+
 <el-table style="width: 100%" :max-height="tableHeight" :data="draftWishes" border v-loading="loading">
               <el-table-column prop="id" label="ID" width="80"></el-table-column>
               <el-table-column prop="childName" label="孩子" width="100"></el-table-column>
               <el-table-column prop="title" label="心愿名称" min-width="200"></el-table-column>
               <el-table-column prop="category" label="分类" width="100"></el-table-column>
-              <el-table-column prop="createdAt" label="创建时间" width="180">
+              <el-table-column prop="createdAt" label="创建时间" width="180" sortable="custom" :sort-orders="['ascending','descending']"  sortable="custom" :sort-orders="['ascending','descending']" >
                 <template slot-scope="scope">
                   {{ formatDate(scope.row.createdAt) }}
                 </template>
@@ -72,6 +74,7 @@
                 </template>
               </el-table-column>
             </el-table>
+    </div>
           </div>
 
           <!-- 待审批兑换列表 -->
@@ -86,7 +89,7 @@
                   <el-tag type="warning">{{ scope.row.pointsRequired }}</el-tag>
                 </template>
               </el-table-column>
-              <el-table-column prop="exchangeRequestedAt" label="申请时间" width="180">
+              <el-table-column prop="exchangeRequestedAt" label="申请时间" width="180" sortable="custom" :sort-orders="['ascending','descending']" >
                 <template slot-scope="scope">
                   {{ formatDate(scope.row.exchangeRequestedAt) }}
                 </template>
@@ -119,7 +122,7 @@
                   <el-tag type="success">{{ scope.row.pointsRequired }}</el-tag>
                 </template>
               </el-table-column>
-              <el-table-column prop="exchangeApprovedAt" label="批准时间" width="180">
+              <el-table-column prop="exchangeApprovedAt" label="批准时间" width="180" sortable="custom" :sort-orders="['ascending','descending']" >
                 <template slot-scope="scope">
                   {{ formatDate(scope.row.exchangeApprovedAt) }}
                 </template>
@@ -139,7 +142,7 @@
               <el-table-column prop="id" label="ID" width="80"></el-table-column>
               <el-table-column prop="childName" label="孩子" width="100"></el-table-column>
               <el-table-column prop="title" label="心愿名称" min-width="200"></el-table-column>
-              <el-table-column prop="fulfilledAt" label="购买时间" width="180">
+              <el-table-column prop="fulfilledAt" label="购买时间" width="180" sortable="custom" :sort-orders="['ascending','descending']" >
                 <template slot-scope="scope">
                   {{ formatDate(scope.row.fulfilledAt) }}
                 </template>
@@ -237,7 +240,7 @@
                 </el-tag>
               </template>
             </el-table-column>
-            <el-table-column prop="createdAt" label="创建时间" width="180">
+            <el-table-column prop="createdAt" label="创建时间" width="180" sortable="custom" :sort-orders="['ascending','descending']"  sortable="custom" :sort-orders="['ascending','descending']" >
               <template slot-scope="scope">
                 {{ formatDate(scope.row.createdAt) }}
               </template>
@@ -573,4 +576,5 @@ export default {
   color: #999;
   font-size: 12px;
 }
+    .table-scroll-wrap { overflow-x: auto; }
 </style>

+ 15 - 4
cfc-web/src/views/admin/Activities.vue

@@ -57,6 +57,8 @@
         <el-button type="primary" size="mini" @click="handleSearch">查询</el-button>
         <el-button size="mini" @click="resetSearch">重置</el-button>
       </div>
+        <div class="table-scroll-wrap">
+
         <el-table :data="activities" v-loading="loading" border stripe style="min-width:900px;" :max-height="tableHeight">
         <el-table-column prop="id" label="ID" width="80" />
         <el-table-column label="封面" width="80">
@@ -93,9 +95,9 @@
             </el-tag>
           </template>
         </el-table-column>
-        <el-table-column prop="startTime" label="开始时间" width="170" />
-        <el-table-column prop="endTime" label="结束时间" width="170" />
-        <el-table-column label="操作" width="200" fixed="right">
+        <el-table-column prop="startTime" label="开始时间" width="170" / sortable="custom" :sort-orders="['ascending','descending']"
+        <el-table-column prop="endTime" label="结束时间" width="170" / sortable="custom" :sort-orders="['ascending','descending']"
+        <el-table-column label="操作" width="200" fixed="right" sortable="custom" :sort-orders="['ascending','descending']"
           <template slot-scope="{ row }">
             <el-button size="mini" type="primary" @click="$router.push('/activity-edit?id=' + row.id)">编辑</el-button>
             <el-dropdown trigger="hover" @command="(cmd) => handleMoreCmd(row, cmd)">
@@ -112,7 +114,8 @@
           </template>
         </el-table-column>
         </el-table>
-      <el-pagination
+      </el-table>
+      </div><el-pagination
         v-if="total > 0"
         @size-change="handleSizeChange"
         @current-change="handlePageChange"
@@ -200,6 +203,8 @@ export default {
         auditStatus: '',
         page: 1,
         size: 20
+      sortProp: '' ,
+      sortOrder: '' ,
       },
       // 报名列表对话框
       regDialogVisible: false,
@@ -214,6 +219,12 @@ export default {
   methods: {
     statusType(status) {
       return (STATUS_MAP[status] && STATUS_MAP[status].type) || 'info'
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
     },
     statusLabel(status) {
       return (STATUS_MAP[status] && STATUS_MAP[status].label) || status

+ 9 - 1
cfc-web/src/views/admin/ActivityRegistrationReview.vue

@@ -28,7 +28,7 @@
           </el-tag>
         </template>
       </el-table-column>
-      <el-table-column prop="registeredAt" label="报名时间" width="160">
+      <el-table-column prop="registeredAt" label="报名时间" width="160" sortable="custom" :sort-orders="['ascending','descending']"
         <template slot-scope="{ row }">
           {{ formatTime(row.registeredAt || row.createdAt) }}
         </template>
@@ -70,6 +70,8 @@ export default {
       rejectDialogVisible: false,
       rejectReason: '',
       currentRow: null
+      sortProp: '' ,
+      sortOrder: '' ,
     }
   },
   computed: {
@@ -94,6 +96,12 @@ export default {
     formatTime(t) {
       if (!t) return '-'
       return t.replace ? t.replace('T', ' ').substring(0, 19) : t
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
     },
     async loadActivities() {
       try {

+ 14 - 3
cfc-web/src/views/admin/ActivityReview.vue

@@ -23,12 +23,14 @@
           <el-button size="mini" @click="resetSearch">重置</el-button>
         </div>
       </div>
+        <div class="table-scroll-wrap">
+
         <el-table :data="pendingActivities" v-loading="loading" border stripe style="min-width:900px;" :max-height="tableHeight">
       <el-table-column prop="id" label="ID" width="80" />
       <el-table-column prop="title" label="活动标题" min-width="200" show-overflow-tooltip />
       <el-table-column prop="applicant" label="申请人" width="120" />
-      <el-table-column prop="createdAt" label="申请时间" width="180" />
-      <el-table-column label="状态" width="100">
+      <el-table-column prop="createdAt" label="申请时间" width="180" / sortable="custom" :sort-orders="['ascending','descending']"
+      <el-table-column label="状态" width="100" sortable="custom" :sort-orders="['ascending','descending']"
         <template slot-scope="{ row }">
           <el-tag v-if="row.status === 'draft'" type="warning">待审核</el-tag>
           <el-tag v-else-if="row.status === 'published'" type="success">已发布</el-tag>
@@ -54,7 +56,8 @@
           </template>
       </el-table-column>
     </el-table>
-    <el-pagination
+    </el-table>
+    </div><el-pagination
         v-if="total > 0"
         @current-change="handlePageChange"
         :current-page="page"
@@ -85,6 +88,8 @@ export default {
         keyword: '',
         status: '',
         dimensionCode: ''
+      sortProp: '' ,
+      sortOrder: '' ,
       },
       page: 1,
       size: 20,
@@ -104,6 +109,12 @@ export default {
           status: this.filter.status || 'draft',
           keyword: this.filter.keyword || undefined,
           dimensionCode: this.filter.dimensionCode || undefined
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
         }
         const res = await getActivityList(params)
         this.pendingActivities = (res.data && res.data.records) || []

+ 13 - 1
cfc-web/src/views/admin/AiQuestionnaireScenes.vue

@@ -3,6 +3,8 @@
     <div class="toolbar">
       <el-button type="primary" size="small" @click="openEdit()">新增场景</el-button>
     </div>
+    <div class="table-scroll-wrap">
+
     <el-table :data="scenes" border stripe size="small">
       <el-table-column prop="sceneKey" label="场景标识" width="140" />
       <el-table-column prop="sceneName" label="场景名称" width="160" />
@@ -22,7 +24,9 @@
       </el-table-column>
     </el-table>
 
-    <el-dialog :title="form.id ? '编辑场景' : '新增场景'" :visible.sync="dialogVisible" width="640px" append-to-body>
+    </el-table>
+
+    </div><el-dialog :title="form.id ? '编辑场景' : '新增场景'" :visible.sync="dialogVisible" width="640px" append-to-body>
       <el-form :model="form" label-width="110px" size="small">
         <el-form-item label="场景标识" required>
           <el-input v-model="form.sceneKey" placeholder="如 microbiome" :disabled="!!form.id" />
@@ -85,6 +89,8 @@ export default {
         maxQuestions: 12,
         systemPrompt: '',
         enabled: 1
+      sortProp: '' ,
+      sortOrder: '' ,
       }
     }
   },
@@ -97,6 +103,12 @@ export default {
         var res = await listScenes({})
         if (res.code === 200) {
           this.scenes = res.data || []
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
         } else {
           this.$message.error(res.message || '加载失败')
         }

+ 10 - 2
cfc-web/src/views/admin/ArticleManage.vue

@@ -117,7 +117,7 @@
           </template>
         </el-table-column>
 
-        <el-table-column label="发布时间" width="160">
+        <el-table-column label="发布时间" width="160" sortable="custom" :sort-orders="['ascending','descending']"
           <template slot-scope="{ row }">
             {{ formatTime(row.publishedAt) }}
           </template>
@@ -275,7 +275,9 @@ export default {
       page: 1,
       size: 20,
       statusTabs: [
-        { value: '', label: '全部' },
+        { value: '', label: '全部'
+      sortProp: '' ,
+      sortOrder: '' , },
         { value: 'draft', label: '草稿箱' },
         { value: 'pending', label: '待审核' },
         { value: 'rejected', label: '已驳回' },
@@ -313,6 +315,12 @@ export default {
         const res = await adminArticleCategoriesList()
         if (res.data) {
           this.categories = res.data
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
         }
       } catch (e) {
         console.error('加载分类失败', e)

+ 10 - 2
cfc-web/src/views/admin/Assessment.vue

@@ -39,8 +39,8 @@
             </el-tag>
           </template>
         </el-table-column>
-        <el-table-column prop="createdAt" label="创建时间" width="160" />
-        <el-table-column label="操作" width="180" fixed="right">
+        <el-table-column prop="createdAt" label="创建时间" width="160" / sortable="custom" :sort-orders="['ascending','descending']"
+        <el-table-column label="操作" width="180" fixed="right" sortable="custom" :sort-orders="['ascending','descending']"
           <template slot-scope="{ row }">
             <el-button size="mini" type="primary" @click="editMaterial(row)">编辑</el-button>
             <el-dropdown trigger="hover" @command="(cmd) => handleActionCmd(row, cmd)">
@@ -89,6 +89,8 @@ export default {
       pointsConfig: {
         pointsValue: 100,
         pointsValueYuan: 0.01
+      sortProp: '' ,
+      sortOrder: '' ,
       },
       materials: [],
       dialogVisible: false,
@@ -127,6 +129,12 @@ export default {
           this.pointsConfig = {
             pointsValue: res.data.pointsValue || 100,
             pointsValueYuan: res.data.pointsValueYuan || 0.01
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
           }
         }
       } catch (e) {

+ 16 - 3
cfc-web/src/views/admin/AssessmentAssign.vue

@@ -6,6 +6,9 @@
         <el-button type="primary" size="small" icon="el-icon-refresh" @click="loadList">刷新</el-button>
       </div>
 
+        <div class="table-scroll-wrap">
+
+
         <el-table :max-height="tableHeight" :data="list" v-loading="loading" border stripe empty-text="暂无可分配记录">
           <el-table-column prop="id" label="ID" width="70" />
           <el-table-column label="用户" width="120">
@@ -17,8 +20,8 @@
           <el-table-column label="预约日期" width="120">
             <template slot-scope="{ row }">{{ row.appointmentDate || '-' }}</template>
           </el-table-column>
-          <el-table-column label="预约时间" width="90">
-            <template slot-scope="{ row }">{{ row.appointmentTime || '-' }}</template>
+          <el-table-column label="预约时间" width="90" sortable="custom" :sort-orders="['ascending','descending']"
+            <template slot-scope="{ row }" sortable="custom" :sort-orders="['ascending','descending']"{{ row.appointmentTime || '-' }}</template>
           </el-table-column>
           <el-table-column label="类型" width="80">
             <template slot-scope="{ row }">
@@ -38,7 +41,9 @@
           </el-table-column>
         </el-table>
 
-      <el-pagination
+      </el-table>
+
+      </div><el-pagination
         @size-change="handleSizeChange"
         @current-change="handleCurrentChange"
         :current-page="page"
@@ -107,6 +112,8 @@ export default {
       currentUserName: '',
       currentChildName: '',
       selectedGuideId: null
+      sortProp: '' ,
+      sortOrder: '' ,
     }
   },
   mounted() {
@@ -119,6 +126,12 @@ export default {
         const res = await getPendingAssessmentList({ page: this.page, size: this.size })
         this.list = res.data.records || []
         this.total = res.data.total || 0
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
       } catch (e) {
         this.$message.error(e.message || '加载失败')
       } finally {

+ 16 - 3
cfc-web/src/views/admin/AssessmentOrders.vue

@@ -35,6 +35,9 @@
         </div>
       </div>
 
+      <div class="table-scroll-wrap">
+
+
       <el-table :max-height="tableHeight" :data="list" v-loading="loading" border stripe empty-text="暂无测评预约">
         <el-table-column label="用户" width="110">
           <template slot-scope="{ row }">{{ row.userName || '-' }}</template>
@@ -54,8 +57,8 @@
         <el-table-column label="预约日期" width="120">
           <template slot-scope="{ row }">{{ row.appointmentDate || '-' }}</template>
         </el-table-column>
-        <el-table-column label="预约时间" width="100">
-          <template slot-scope="{ row }">{{ row.appointmentTime || '-' }}</template>
+        <el-table-column label="预约时间" width="100" sortable="custom" :sort-orders="['ascending','descending']"
+          <template slot-scope="{ row }" sortable="custom" :sort-orders="['ascending','descending']"{{ row.appointmentTime || '-' }}</template>
         </el-table-column>
         <el-table-column label="预约类型" width="100">
           <template slot-scope="{ row }">{{ row.appointmentType || '-' }}</template>
@@ -81,7 +84,9 @@
         </el-table-column>
       </el-table>
 
-      <el-pagination
+      </el-table>
+
+      </div><el-pagination
         @size-change="handleSizeChange"
         @current-change="handleCurrentChange"
         :current-page="page"
@@ -161,6 +166,8 @@ export default {
       guides: [],
       guidesLoading: false,
       changing: false
+      sortProp: '' ,
+      sortOrder: '' ,
     }
   },
   mounted() {
@@ -170,6 +177,12 @@ export default {
     handleSearch() {
       this.page = 1
       this.loadList()
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
     },
     async loadList() {
       this.loading = true

+ 17 - 4
cfc-web/src/views/admin/AssessmentPlanRules.vue

@@ -5,6 +5,9 @@
       <el-button type="primary" @click="showDialog(null)">新增规则</el-button>
     </div>
 
+    <div class="table-scroll-wrap">
+
+
     <el-table :data="rules" border stripe style="width: 100%" :max-height="tableHeight">
       <el-table-column prop="dimension" label="维度" width="120" />
       <el-table-column label="分数区间" width="140">
@@ -17,8 +20,8 @@
           <el-tag :type="row.isActive ? 'success' : 'info'">{{ row.isActive ? '启用' : '禁用' }}</el-tag>
         </template>
       </el-table-column>
-      <el-table-column prop="createdAt" label="创建时间" />
-      <el-table-column label="操作" width="180">
+      <el-table-column prop="createdAt" label="创建时间" / sortable="custom" :sort-orders="['ascending','descending']"
+      <el-table-column label="操作" width="180" sortable="custom" :sort-orders="['ascending','descending']"
         <template slot-scope="{ row }">
           <el-button size="mini" @click="showDialog(row)">编辑</el-button>
           <el-button size="mini" type="danger" @click="handleDelete(row.id)">删除</el-button>
@@ -26,7 +29,9 @@
       </el-table-column>
     </el-table>
 
-    <el-dialog title="规则编辑" :visible.sync="dialogVisible" width="500px">
+    </el-table>
+
+    </div><el-dialog title="规则编辑" :visible.sync="dialogVisible" width="500px">
       <el-form :model="form" label-width="100px">
         <el-form-item label="维度">
           <el-select v-model="form.dimension" placeholder="选择维度">
@@ -74,7 +79,9 @@ export default {
     return {
       rules: [],
       dialogVisible: false,
-      form: { dimension: '', scoreMin: 0, scoreMax: 100, templateId: 1, priority: 0, isActive: true }
+      form: { dimension: '', scoreMin: 0, scoreMax: 100, templateId: 1, priority: 0, isActive: true
+      sortProp: '' ,
+      sortOrder: '' , }
     }
   },
   created() {
@@ -84,6 +91,12 @@ export default {
     async loadRules() {
       const res = await listPlanRules()
       if (res.code === 200) this.rules = res.data || []
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
     },
     showDialog(row) {
       if (row) {

+ 14 - 1
cfc-web/src/views/admin/AssessmentProducts.vue

@@ -26,6 +26,9 @@
         </div>
       </div>
 
+      <div class="table-scroll-wrap">
+
+
       <el-table :data="list" v-loading="loading" border stripe :max-height="tableHeight">
         <el-table-column prop="id" label="ID" width="70" />
         <el-table-column label="商品图片" width="80">
@@ -66,7 +69,9 @@
         </el-table-column>
       </el-table>
 
-      <div class="pagination-wrap">
+      </el-table>
+
+      </div><div class="pagination-wrap">
         <el-pagination
           @size-change="handleSizeChange"
           @current-change="handlePageChange"
@@ -117,6 +122,8 @@ export default {
       filters: {
         keyword: '',
         status: ''
+      sortProp: '' ,
+      sortOrder: '' ,
       },
       detail: null,
       detailVisible: false
@@ -135,6 +142,12 @@ export default {
           productType: 'assessment',
           keyword: this.filters.keyword || undefined,
           status: this.filters.status || undefined
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
         })
         if (res.code === 200 && res.data) {
           this.list = res.data.records || []

+ 13 - 3
cfc-web/src/views/admin/BadgeManage.vue

@@ -25,6 +25,9 @@
         </el-form-item>
       </el-form>
 
+      <div class="table-scroll-wrap">
+
+
       <el-table :data="list" border stripe v-loading="loading" empty-text="暂无数据" style="width:100%" :max-height="tableHeight">
         <el-table-column prop="id" label="ID" width="60" />
         <el-table-column prop="name" label="名称" min-width="120" />
@@ -49,8 +52,8 @@
           </template>
         </el-table-column>
         <el-table-column prop="sortOrder" label="排序" width="60" align="center" />
-        <el-table-column prop="createdAt" label="创建时间" width="160" />
-        <el-table-column label="操作" width="160" fixed="right">
+        <el-table-column prop="createdAt" label="创建时间" width="160" / sortable="custom" :sort-orders="['ascending','descending']"
+        <el-table-column label="操作" width="160" fixed="right" sortable="custom" :sort-orders="['ascending','descending']"
           <template slot-scope="{ row }">
             <el-button type="text" @click="editRow(row)">编辑</el-button>
             <el-popconfirm title="确定删除此勋章吗?" @confirm="deleteRow(row)">
@@ -59,7 +62,8 @@
           </template>
         </el-table-column>
       </el-table>
-    </el-card>
+    </el-table>
+    </div></el-card>
 
     <!-- 新增/编辑弹窗 -->
     <el-dialog :visible.sync="dialog.visible" :title="dialog.isEdit ? '编辑勋章' : '新增勋章'" width="600px" :close-on-click-modal="false">
@@ -209,6 +213,12 @@ export default {
     onSearchInput() {
       if (this.searchTimer) clearTimeout(this.searchTimer)
       this.searchTimer = setTimeout(() => { this.fetchList() }, 300)
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
     },
     async fetchList() {
       this.loading = true

+ 2 - 0
cfc-web/src/views/admin/BaziConfigs.vue

@@ -114,6 +114,8 @@ export default {
       successDialogVisible: false,
       isEdit: false,
       form: this.getEmptyForm()
+      sortProp: '' ,
+      sortOrder: '' ,
     }
   },
   created() {

+ 2 - 0
cfc-web/src/views/admin/BloodTypeConfigs.vue

@@ -92,6 +92,8 @@ export default {
       successDialogVisible: false,
       isEdit: false,
       form: this.getEmptyForm()
+      sortProp: '' ,
+      sortOrder: '' ,
     }
   },
   created() {

+ 14 - 3
cfc-web/src/views/admin/CfValueManage.vue

@@ -10,6 +10,8 @@
 
     <el-tabs v-model="activeTab">
       <el-tab-pane label="余额列表" name="balances">
+        <div class="table-scroll-wrap">
+
         <el-table :data="balanceList" v-loading="loadingBalance" border stripe>
           <el-table-column prop="id" label="ID" width="70" />
           <el-table-column label="用户" width="140">
@@ -27,7 +29,8 @@
             </template>
           </el-table-column>
         </el-table>
-        <el-pagination v-if="balanceTotal > 0" :current-page="balancePage" :page-size="balanceSize" :total="balanceTotal" layout="prev, pager, next" @current-change="pageBalances" />
+        </el-table>
+        </div><el-pagination v-if="balanceTotal > 0" :current-page="balancePage" :page-size="balanceSize" :total="balanceTotal" layout="prev, pager, next" @current-change="pageBalances" />
       </el-tab-pane>
 
       <el-tab-pane label="流水查询" name="logs">
@@ -50,8 +53,8 @@
           <el-table-column prop="refType" label="来源" width="130" />
           <el-table-column prop="refId" label="关联ID" width="80" />
           <el-table-column prop="remark" label="备注" min-width="160" show-overflow-tooltip />
-          <el-table-column prop="createdAt" label="时间" width="160" />
-        </el-table>
+          <el-table-column prop="createdAt" label="时间" width="160" / sortable="custom" :sort-orders="['ascending','descending']"
+        </el-table sortable="custom" :sort-orders="['ascending','descending']"
         <el-pagination v-if="logTotal > 0" :current-page="logPage" :page-size="logSize" :total="logTotal" layout="prev, pager, next" @current-change="pageLogs" />
       </el-tab-pane>
     </el-tabs>
@@ -79,6 +82,8 @@ export default {
       logPage: 1,
       logSize: 20,
       logTotal: 0
+      sortProp: '' ,
+      sortOrder: '' ,
     }
   },
   mounted() {
@@ -90,6 +95,12 @@ export default {
       var ids = []
       items.forEach(function(item) {
         if (item[idField] && ids.indexOf(item[idField]) === -1) ids.push(item[idField])
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
       })
       var nameMap = {}
       var self = this

+ 13 - 1
cfc-web/src/views/admin/ChallengeScoreConfig.vue

@@ -5,6 +5,9 @@
       <div class="header-tip">家庭挑战 发起/同意/拒绝 的方向性关系评分与能量增减(可正可负)</div>
     </div>
 
+    <div class="table-scroll-wrap">
+
+
     <el-table :data="matrix" v-loading="loading" border stripe>
       <el-table-column label="动作" width="120">
         <template slot-scope="{ row }">
@@ -37,7 +40,8 @@
         </template>
       </el-table-column>
     </el-table>
-    <div class="footer-tip">注:reject 的沟通方向由代码固定(拒绝人→发起人 +、发起人→拒绝人 -、已同意成员→拒绝人 -),此处的值为增减幅度。信任/亲近为单向扣减(发起人→拒绝人)。</div>
+    </el-table>
+    </div><div class="footer-tip">注:reject 的沟通方向由代码固定(拒绝人→发起人 +、发起人→拒绝人 -、已同意成员→拒绝人 -),此处的值为增减幅度。信任/亲近为单向扣减(发起人→拒绝人)。</div>
   </div>
 </template>
 
@@ -50,6 +54,8 @@ export default {
     return {
       loading: false,
       matrix: []
+      sortProp: '' ,
+      sortOrder: '' ,
     }
   },
   created() {
@@ -59,6 +65,12 @@ export default {
     actionLabel: function(action) {
       var map = { initiate: '发起', agree: '同意', reject: '拒绝' }
       return map[action] || action
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
     },
     loadList: function() {
       var self = this

+ 15 - 4
cfc-web/src/views/admin/CommentReview.vue

@@ -13,6 +13,8 @@
         <el-button type="primary" @click="loadList">查询</el-button>
       </div>
     </div>
+    <div class="table-scroll-wrap">
+
     <el-table :data="list" v-loading="loading" border stripe>
       <el-table-column prop="id" label="ID" width="60" />
       <el-table-column label="用户" width="120">
@@ -37,15 +39,16 @@
           </el-tag>
         </template>
       </el-table-column>
-      <el-table-column prop="createdAt" label="时间" width="160" />
-      <el-table-column label="操作" width="200" fixed="right">
+      <el-table-column prop="createdAt" label="时间" width="160" / sortable="custom" :sort-orders="['ascending','descending']"
+      <el-table-column label="操作" width="200" fixed="right" sortable="custom" :sort-orders="['ascending','descending']"
         <template slot-scope="{ row }">
           <el-button size="mini" type="success" @click="audit(row.id, 'approved')" :disabled="row.status !== 'pending'">通过</el-button>
           <el-button size="mini" type="danger" @click="handleReject(row)" :disabled="row.status !== 'pending'">驳回</el-button>
         </template>
       </el-table-column>
     </el-table>
-    <el-pagination
+    </el-table>
+    </div><el-pagination
       v-if="total > 0"
       @current-change="handlePageChange"
       :current-page="listQuery.page"
@@ -73,7 +76,9 @@ export default {
       list: [],
       loading: false,
       total: 0,
-      listQuery: { status: 'pending', articleId: '', page: 1, size: 20 },
+      listQuery: { status: 'pending', articleId: '', page: 1, size: 20
+      sortProp: '' ,
+      sortOrder: '' , },
       rejectDialogVisible: false,
       rejectTargetId: null,
       rejectReason: ''
@@ -91,6 +96,12 @@ export default {
         if (res.code === 200) {
           this.list = res.data.records || []
           this.total = res.data.total || 0
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
         } else {
           this.$message.error(res.message || '加载失败')
         }

+ 10 - 2
cfc-web/src/views/admin/CouponGrantLog.vue

@@ -29,8 +29,8 @@
           <el-table-column prop="source" label="触发来源" min-width="160" show-overflow-tooltip>
             <template slot-scope="{ row }">{{ row.source || '-' }}</template>
           </el-table-column>
-          <el-table-column prop="createdAt" label="发放时间" width="180" />
-        </el-table>
+          <el-table-column prop="createdAt" label="发放时间" width="180" / sortable="custom" :sort-orders="['ascending','descending']"
+        </el-table sortable="custom" :sort-orders="['ascending','descending']"
       </div>
 
       <el-pagination
@@ -65,6 +65,8 @@ export default {
       total: 0,
       userId: '',
       couponId: ''
+      sortProp: '' ,
+      sortOrder: '' ,
     }
   },
   created() {
@@ -74,6 +76,12 @@ export default {
     grantTypeLabel(val) {
       const map = { JOIN: '开通赠券', PERIODIC: '周期补发', POPULATION: '家庭人口券', EXCHANGE: '积分兑换' }
       return map[val] || val
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
     },
     grantTypeTagType(val) {
       const map = { JOIN: 'success', PERIODIC: 'primary', POPULATION: 'warning', EXCHANGE: 'danger' }

+ 3 - 1
cfc-web/src/views/admin/CouponManagement.vue

@@ -235,7 +235,9 @@ export default {
       batchSubmitting: false,
       couponOptions: [],
       keyword: '',
-      sortableColumns: { name: '名称' }
+      sortableColumns: { name: '名称'
+      sortProp: '' ,
+      sortOrder: '' , }
 
     }
   },

+ 15 - 2
cfc-web/src/views/admin/DanExecution.vue

@@ -14,6 +14,9 @@
         </div>
       </div>
 
+      <div class="table-scroll-wrap">
+
+
       <el-table :data="list" v-loading="loading" border stripe empty-text="暂无执行记录">
         <el-table-column label="ID" prop="id" width="80" />
         <el-table-column label="订单ID" prop="assessmentOrderId" width="100" />
@@ -57,7 +60,7 @@
             </div>
           </template>
         </el-table-column>
-        <el-table-column label="创建时间" width="160">
+        <el-table-column label="创建时间" width="160" sortable="custom" :sort-orders="['ascending','descending']"
           <template slot-scope="{ row }">{{ formatTime(row.createdAt) }}</template>
         </el-table-column>
         <el-table-column label="操作" width="160" fixed="right">
@@ -67,7 +70,9 @@
         </el-table-column>
       </el-table>
 
-      <el-pagination
+      </el-table>
+
+      </div><el-pagination
         @size-change="handleSizeChange"
         @current-change="handleCurrentChange"
         :current-page="page"
@@ -166,6 +171,8 @@ export default {
       detailDialogVisible: false,
       detailLoading: false,
       currentRecord: null
+      sortProp: '' ,
+      sortOrder: '' ,
     }
   },
   created() {
@@ -185,6 +192,12 @@ export default {
         // this.total = res.data.data.total
         this.list = []
         this.total = 0
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
       } catch (e) {
         console.error('加载执行记录失败', e)
         this.$message.error('加载失败')

+ 16 - 3
cfc-web/src/views/admin/DanSettlement.vue

@@ -66,6 +66,9 @@
         </div>
       </div>
 
+      <div class="table-scroll-wrap">
+
+
       <el-table :data="list" v-loading="loading" border stripe empty-text="暂无结算记录">
         <el-table-column label="ID" prop="id" width="80" />
         <el-table-column label="执行ID" prop="executionId" width="100" />
@@ -109,8 +112,8 @@
             </div>
           </template>
         </el-table-column>
-        <el-table-column label="结算时间" width="160">
-          <template slot-scope="{ row }">{{ formatTime(row.settledAt) }}</template>
+        <el-table-column label="结算时间" width="160" sortable="custom" :sort-orders="['ascending','descending']"
+          <template slot-scope="{ row }" sortable="custom" :sort-orders="['ascending','descending']"{{ formatTime(row.settledAt) }}</template>
         </el-table-column>
         <el-table-column label="交易ID" prop="transactionId" min-width="150" show-overflow-tooltip />
         <el-table-column label="创建时间" width="160">
@@ -131,7 +134,9 @@
         </el-table-column>
       </el-table>
 
-      <el-pagination
+      </el-table>
+
+      </div><el-pagination
         @size-change="handleSizeChange"
         @current-change="handleCurrentChange"
         :current-page="page"
@@ -204,6 +209,8 @@ export default {
         totalCount: 0,
         pendingCount: 0,
         settledCount: 0
+      sortProp: '' ,
+      sortOrder: '' ,
       },
       detailDialogVisible: false,
       detailLoading: false,
@@ -221,6 +228,12 @@ export default {
         const res = await this.$axios.post('/api/admin/dan-settlement/list', {
           status: this.statusFilter,
           providerType: this.providerTypeFilter
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
         })
         if (res.data.code === 200) {
           this.list = res.data.data || []

+ 14 - 3
cfc-web/src/views/admin/Dashboard.vue

@@ -56,6 +56,8 @@
       <div slot="header" class="card-header">
         <span><i class="el-icon-s-order"></i> 最近任务</span>
       </div>
+      <div class="table-scroll-wrap">
+
       <el-table :data="recentTasks" border stripe empty-text="暂无任务" size="small">
         <el-table-column prop="id" label="ID" width="60" />
         <el-table-column prop="title" label="任务名称" min-width="160" show-overflow-tooltip />
@@ -70,11 +72,12 @@
             <el-tag :type="taskStatusTag(row.status)" size="mini">{{ taskStatusLabel(row.status) }}</el-tag>
           </template>
         </el-table-column>
-        <el-table-column label="创建时间" width="150">
+        <el-table-column label="创建时间" width="150" sortable="custom" :sort-orders="['ascending','descending']"
           <template slot-scope="{ row }">{{ formatTime(row.createdAt) }}</template>
         </el-table-column>
       </el-table>
-    </el-card>
+    </el-table>
+    </div></el-card>
   </div>
 </template>
 
@@ -91,7 +94,9 @@ export default {
       memberLoading: false,
       revenueLoading: false,
       statCards: [
-        { label: '家庭总数', value: '-', icon: 'el-icon-s-home', color: 'orange' },
+        { label: '家庭总数', value: '-', icon: 'el-icon-s-home', color: 'orange'
+      sortProp: '' ,
+      sortOrder: '' , },
         { label: '家长总数', value: '-', icon: 'el-icon-user', color: 'blue' },
         { label: '孩子总数', value: '-', icon: 'el-icon-s-custom', color: 'cyan' },
         { label: '规划师总数', value: '-', icon: 'el-icon-school', color: 'green' },
@@ -143,6 +148,12 @@ export default {
           this.statCards[5].value = d.pendingPackageCount || 0
           this.statCards[6].value = d.pendingRefundCount || 0
           this.recentTasks = d.recentTasks || []
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
         }
       } catch (e) {
         this.$message.error('加载概览数据失败')

+ 2 - 0
cfc-web/src/views/admin/EcomSupplierManage.vue

@@ -85,6 +85,8 @@ export default {
         contactPhone: '',
         status: 1,
         remark: ''
+      sortProp: '' ,
+      sortOrder: '' ,
       },
       rules: {
         name: [{ required: true, message: '请输入供应商名称', trigger: 'blur' }],

+ 15 - 3
cfc-web/src/views/admin/EmotionAlertManage.vue

@@ -9,6 +9,8 @@
         <el-radio-button label="true">已处理</el-radio-button>
       </el-radio-group>
       </div>
+    <div class="table-scroll-wrap">
+
     <el-table :max-height="tableHeight" :data="list" v-loading="loading" border stripe>
       <el-table-column prop="id" label="ID" width="70" />
       <el-table-column prop="childId" label="孩子ID" width="90" />
@@ -31,8 +33,8 @@
           </el-tag>
         </template>
       </el-table-column>
-      <el-table-column label="创建时间" width="160">
-        <template slot-scope="{ row }">
+      <el-table-column label="创建时间" width="160" sortable="custom" :sort-orders="['ascending','descending']"
+        <template slot-scope="{ row }" sortable="custom" :sort-orders="['ascending','descending']"
           {{ formatTime(row.createdAt) }}
         </template>
       </el-table-column>
@@ -54,7 +56,9 @@
       </el-table-column>
     </el-table>
 
-    <el-pagination
+    </el-table>
+
+    </div><el-pagination
       @size-change="onSizeChange"
       @current-change="onPageChange"
       :current-page="page"
@@ -85,6 +89,8 @@ export default {
       total: 0,
       keyword: '',
       resolvedFilter: ''
+      sortProp: '' ,
+      sortOrder: '' ,
     }
   },
   mounted() {
@@ -97,6 +103,12 @@ export default {
         var params = { page: this.page, size: this.size }
         if (this.resolvedFilter !== '') {
           params.resolved = this.resolvedFilter
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
         }
         if (this.keyword) {
           params.keyword = this.keyword

+ 15 - 2
cfc-web/src/views/admin/EnergyBehaviorConfig.vue

@@ -12,6 +12,9 @@
       </div>
     </div>
 
+    <div class="table-scroll-wrap">
+
+
     <el-table :data="list" v-loading="loading" border stripe highlight-current-row>
       <el-table-column prop="behaviorCode" label="行为编码" width="160" />
       <el-table-column prop="behaviorName" label="行为名称" width="130" />
@@ -56,7 +59,9 @@
       </el-table-column>
     </el-table>
 
-    <el-dialog title="编辑能量行为配置" :visible.sync="dialogVisible" width="700px" :close-on-click-modal="false">
+    </el-table>
+
+    </div><el-dialog title="编辑能量行为配置" :visible.sync="dialogVisible" width="700px" :close-on-click-modal="false">
       <el-form :model="form" label-width="110px" size="small">
         <el-row :gutter="20">
           <el-col :span="12">
@@ -147,7 +152,9 @@ export default {
       loading: false,
       list: [],
       dialogVisible: false,
-      form: {},
+      form: {
+      sortProp: '' ,
+      sortOrder: '' ,},
       dimAssignList: [],
       dimensions: Object.entries(DIMENSION_MAP).map(([code, v]) => ({ code, name: v.name }))
     }
@@ -165,6 +172,12 @@ export default {
             if (this.keyword) {
               const kw = this.keyword.toLowerCase()
               if (!r.behaviorCode.toLowerCase().includes(kw) && !r.behaviorName.toLowerCase().includes(kw)) return false
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
             }
             if (this.enabledFilter !== '') {
               if (r.enabled !== parseInt(this.enabledFilter)) return false

+ 13 - 1
cfc-web/src/views/admin/EnergyRuleManagement.vue

@@ -11,6 +11,8 @@
         <el-button type="primary" icon="el-icon-plus" @click="showDialog(null)">新增规则</el-button>
       </div>
       </div>
+    <div class="table-scroll-wrap">
+
     <el-table :max-height="tableHeight" :data="list" v-loading="loading" border stripe>
       <el-table-column prop="id" label="ID" width="60" />
       <el-table-column prop="ruleCode" label="规则编码" width="140" />
@@ -44,7 +46,9 @@
       </el-table-column>
     </el-table>
 
-    <el-pagination
+    </el-table>
+
+    </div><el-pagination
       @size-change="onSizeChange"
       @current-change="onPageChange"
       :current-page="page"
@@ -120,6 +124,8 @@ export default {
       dialogVisible: false,
       saving: false,
       form: this.getEmptyForm()
+      sortProp: '' ,
+      sortOrder: '' ,
     }
   },
   mounted() {
@@ -129,6 +135,12 @@ export default {
   methods: {
     getEmptyForm() {
       return { id: null, ruleCode: '', ruleName: '', eventType: '', action: '', dimensionCode: '', amount: 0, amountFormula: '', priority: 0, conditions: '', status: 1, description: '' }
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
     },
     async loadDimensions() {
       try {

+ 12 - 1
cfc-web/src/views/admin/EnergySandbox.vue

@@ -41,6 +41,8 @@
         <span>家庭成员数据</span>
         <el-input v-model="memberKeyword" placeholder="搜索成员姓名..." prefix-icon="el-icon-search" size="small" clearable style="float: right; width: 200px;" />
       </div>
+      <div class="table-scroll-wrap">
+
       <el-table :data="filteredMembers" border style="width: 100%" :max-height="tableHeight">
         <el-table-column prop="memberType" label="成员类型" width="100"></el-table-column>
         <el-table-column prop="name" label="姓名" width="120"></el-table-column>
@@ -78,7 +80,8 @@
           <span>暂无数据</span>
         </template>
       </el-table>
-    </el-card>
+    </el-table>
+    </div></el-card>
   </div>
 </template>
 
@@ -99,6 +102,8 @@ export default {
         wealthScore: null,
         overallScore: null,
         members: []
+      sortProp: '' ,
+      sortOrder: '' ,
       },
       dimensionList: [
         { key: 'mindScore', label: '心', color: '#9B59B6' },
@@ -138,6 +143,12 @@ export default {
             wealthScore: res.data.wealthScore,
             overallScore: res.data.overallScore,
             members: res.data.members || []
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
           }
         }
       } catch (error) {

+ 9 - 1
cfc-web/src/views/admin/FamilyEarnings.vue

@@ -32,7 +32,7 @@
             {{ fenToYuan(row.distributedAmount) }}
           </template>
         </el-table-column>
-        <el-table-column label="更新时间" width="160">
+        <el-table-column label="更新时间" width="160" sortable="custom" :sort-orders="['ascending','descending']"
           <template slot-scope="{ row }">
             {{ row.updatedAt ? formatTime(row.updatedAt) : '-' }}
           </template>
@@ -108,6 +108,8 @@ export default {
       detailVisible: false,
       detailLoading: false,
       detail: null
+      sortProp: '' ,
+      sortOrder: '' ,
     }
   },
   mounted() {
@@ -119,6 +121,12 @@ export default {
       try {
         var res = await getFamilyEarningsList()
         this.list = res.data || []
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
       } catch (e) {
         this.$message.error('加载失败')
       } finally {

+ 9 - 1
cfc-web/src/views/admin/FamilyEarningsWithdraw.vue

@@ -33,7 +33,7 @@
             {{ row.fromUserId || '-' }}
           </template>
         </el-table-column>
-        <el-table-column label="创建时间" width="160">
+        <el-table-column label="创建时间" width="160" sortable="custom" :sort-orders="['ascending','descending']"
           <template slot-scope="{ row }">
             {{ row.createdAt ? formatTime(row.createdAt) : '-' }}
           </template>
@@ -69,6 +69,8 @@ export default {
       size: 20,
       total: 0,
       typeFilter: ''
+      sortProp: '' ,
+      sortOrder: '' ,
     }
   },
   mounted() {
@@ -82,6 +84,12 @@ export default {
         if (res.data) {
           this.list = Array.isArray(res.data) ? res.data : [res.data]
           this.total = this.list.length
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
         }
       } catch (e) {
         this.$message.error('加载失败')

+ 16 - 3
cfc-web/src/views/admin/FileManage.vue

@@ -52,6 +52,9 @@
         <el-button type="primary" @click="loadData">查询</el-button>
       </div>
 
+      <div class="table-scroll-wrap">
+
+
       <el-table :data="tableData" border stripe v-loading="loading" style="margin-top:16px">
         <el-table-column prop="id" label="ID" width="70"></el-table-column>
         <el-table-column label="预览" width="70">
@@ -80,7 +83,7 @@
           </template>
         </el-table-column>
         <el-table-column prop="description" label="描述" min-width="120" show-overflow-tooltip></el-table-column>
-        <el-table-column prop="createdAt" label="上传时间" width="160"></el-table-column>
+        <el-table-column prop="createdAt" label="上传时间" width="160" sortable="custom" :sort-orders="['ascending','descending']"</el-table-column sortable="custom" :sort-orders="['ascending','descending']"
         <el-table-column label="操作" width="120" fixed="right">
           <template slot-scope="scope">
             <el-button size="mini" type="text" @click="openEdit(scope.row)">编辑</el-button>
@@ -89,7 +92,9 @@
         </el-table-column>
       </el-table>
 
-      <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="query.page" :page-size="query.pageSize" :total="total" layout="total, sizes, prev, pager, next, jumper" :page-sizes="[10, 20, 50, 100]" style="margin-top:16px"></el-pagination>
+      </el-table>
+
+      </div><el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="query.page" :page-size="query.pageSize" :total="total" layout="total, sizes, prev, pager, next, jumper" :page-sizes="[10, 20, 50, 100]" style="margin-top:16px"></el-pagination>
     </el-card>
 
     <el-dialog title="编辑文件" :visible.sync="editVisible" width="500px">
@@ -124,7 +129,9 @@ export default {
   name: 'FileManage',
   data() {
     return {
-      tableData: [], total: 0, loading: false, stats: {}, tagOptions: [], dateRange: null,
+      tableData: [], total: 0, loading: false, stats: {
+      sortProp: '' ,
+      sortOrder: '' ,}, tagOptions: [], dateRange: null,
       query: { page: 1, pageSize: 20, category: '', fileType: '', tagIds: [], search: '', dateFrom: '', dateTo: '' },
       editVisible: false, saving: false,
       editForm: { id: null, url: '', originalFilename: '', description: '', tagIds: [] }
@@ -138,6 +145,12 @@ export default {
         if (this.dateRange && this.dateRange.length === 2) {
           this.query.dateFrom = this.dateRange[0].toISOString().slice(0, 10)
           this.query.dateTo = this.dateRange[1].toISOString().slice(0, 10)
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
         } else { this.query.dateFrom = ''; this.query.dateTo = '' }
         const res = await listFiles(this.query)
         if (res.data.code === 200) { this.tableData = res.data.data.records; this.total = res.data.data.total }

+ 14 - 1
cfc-web/src/views/admin/Foods.vue

@@ -42,6 +42,9 @@
         </div>
       </div>
 
+      <div class="table-scroll-wrap">
+
+
       <el-table
         :data="list"
         v-loading="loading"
@@ -158,7 +161,9 @@
         </el-table-column>
       </el-table>
 
-      <div class="pagination-wrap">
+      </el-table>
+
+      </div><div class="pagination-wrap">
         <el-pagination
           @size-change="handleSizeChange"
           @current-change="handleCurrentChange"
@@ -315,6 +320,8 @@ export default {
         name: '\u540d\u79f0', category: '\u5206\u7c7b', calories: '\u70ed\u91cf', protein: '\u86cb\u767d',
         carbs: '\u78b3\u6c34', fat: '\u8102\u80aa', fiber: '\u7ea4\u7ef4', energyKj: '\u80fd\u91cf',
         starch: '\u6dc0\u7c89', cholesterol: '\u80c6\u56fa\u9187', status: '\u72b6\u6001'
+      sortProp: '' ,
+      sortOrder: '' ,
       },
       form: {
         name: '',
@@ -359,6 +366,12 @@ export default {
         const res = await getFoodList(sortParams)
         this.list = res.data && res.data.records || []
         this.total = res.data && res.data.total || 0
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
       } catch (e) {
         this.$message && this.$message.error && this.$message.error('加载失败')
       } finally {

+ 10 - 1
cfc-web/src/views/admin/GrowthTaskManagement.vue

@@ -20,6 +20,8 @@
           <el-button type="primary" size="mini" @click="openCreate">创建任务</el-button>
         </div>
       </div>
+      <div class="table-scroll-wrap">
+
       <el-table :max-height="tableHeight" :data="tasks" v-loading="loading" border stripe>
         <el-table-column prop="id" label="ID" width="80" />
         <el-table-column prop="name" label="任务名称" min-width="150" show-overflow-tooltip />
@@ -68,7 +70,8 @@
           </template>
         </el-table-column>
       </el-table>
-      <el-pagination
+      </el-table>
+      </div><el-pagination
         v-if="total > 0"
         @size-change="handleSizeChange"
         @current-change="handlePageChange"
@@ -198,6 +201,12 @@ export default {
   methods: {
     dimensionLabel(code) {
       return (DIMENSION_CONFIG[code] && DIMENSION_CONFIG[code].label) || code || '—'
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
     },
     dimensionType(code) {
       return (DIMENSION_CONFIG[code] && DIMENSION_CONFIG[code].type) || ''

+ 14 - 2
cfc-web/src/views/admin/HealthCheckinManage.vue

@@ -25,6 +25,9 @@
         </div>
       </div>
 
+      <div class="table-scroll-wrap">
+
+
       <el-table :data="list" v-loading="loading" border stripe empty-text="暂无打卡记录" :max-height="tableHeight">
         <el-table-column prop="id" label="ID" width="70" />
         <el-table-column label="孩子" width="100">
@@ -43,11 +46,12 @@
         </el-table-column>
         <el-table-column label="能量" width="70" prop="earnedEnergy" />
         <el-table-column label="备注" min-width="160" show-overflow-tooltip prop="note" />
-        <el-table-column label="打卡时间" width="160">
+        <el-table-column label="打卡时间" width="160" sortable="custom" :sort-orders="['ascending','descending']"
           <template slot-scope="{ row }">{{ formatTime(row.createdAt) }}</template>
         </el-table-column>
       </el-table>
-    </el-card>
+    </el-table>
+    </div></el-card>
   </div>
 </template>
 
@@ -62,6 +66,8 @@ export default {
       list: [],
       keyword: '',
       moodFilter: ''
+      sortProp: '' ,
+      sortOrder: '' ,
     }
   },
   computed: {
@@ -77,6 +83,12 @@ export default {
       if (!d) return '-'
       var date = new Date(d)
       return date.getFullYear() + '-' + (date.getMonth() + 1).toString().padStart(2, '0') + '-' + date.getDate().toString().padStart(2, '0')
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
     },
     formatTime(d) {
       if (!d) return '-'

+ 18 - 7
cfc-web/src/views/admin/InventoryManage.vue

@@ -22,6 +22,8 @@
             <el-date-picker v-model="txFilters.dateRange" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" value-format="yyyy-MM-dd" style="width:260px;margin-right:8px;" />
             <el-button type="primary" size="mini" @click="loadTransactions">查询</el-button>
           </div>
+          <div class="table-scroll-wrap">
+
           <el-table :data="txList" v-loading="txLoading" border stripe max-height="500">
             <el-table-column prop="id" label="ID" width="60" />
             <el-table-column label="商品" min-width="150">
@@ -42,13 +44,14 @@
             <el-table-column prop="beforeStock" label="变动前" width="70" />
             <el-table-column prop="afterStock" label="变动后" width="70" />
             <el-table-column prop="remark" label="备注" min-width="150" show-overflow-tooltip />
-            <el-table-column prop="createdAt" label="时间" width="160" />
-          </el-table>
-          <el-pagination @size-change="txPageSize=$event;loadTransactions()" @current-change="txPage=$event;loadTransactions()" :current-page="txPage" :page-sizes="[10,20,50]" :page-size="txPageSize" :total="txTotal" layout="total, sizes, prev, pager, next, jumper" class="pagination-wrap" />
-        </el-tab-pane>
+            <el-table-column prop="createdAt" label="时间" width="160" / sortable="custom" :sort-orders="['ascending','descending']"
+          </el-table sortable="custom" :sort-orders="['ascending','descending']"
+          </el-table sortable="custom" :sort-orders="['ascending','descending']"
+          </div sortable="custom" :sort-orders="['ascending','descending']"<el-pagination @size-change="txPageSize=$event;loadTransactions()" @current-change="txPage=$event;loadTransactions()" :current-page="txPage" :page-sizes="[10,20,50]" :page-size="txPageSize" :total="txTotal" layout="total, sizes, prev, pager, next, jumper" class="pagination-wrap" / sortable="custom" :sort-orders="['ascending','descending']"
+        </el-tab-pane sortable="custom" :sort-orders="['ascending','descending']"
 
-        <!-- 入库单 -->
-        <el-tab-pane label="入库单" name="inbound">
+        <!-- 入库单 -- sortable="custom" :sort-orders="['ascending','descending']"
+        <el-tab-pane label="入库单" name="inbound" sortable="custom" :sort-orders="['ascending','descending']"
           <el-table :data="inboundList" v-loading="inboundLoading" border stripe max-height="500">
             <el-table-column prop="orderNo" label="入库单号" width="180" />
             <el-table-column prop="supplierName" label="供应商" width="120" />
@@ -226,7 +229,9 @@ export default {
     return {
       activeTab: 'transactions',
       txList: [], txLoading: false, txPage: 1, txPageSize: 20, txTotal: 0,
-      txFilters: { productName: '', type: '', dateRange: null },
+      txFilters: { productName: '', type: '', dateRange: null
+      sortProp: '' ,
+      sortOrder: '' , },
       inboundList: [], inboundLoading: false, inboundPage: 1, inboundPageSize: 20, inboundTotal: 0,
       inboundDetail: null, detailDialogVisible: false,
       alertList: [], alertLoading: false,
@@ -253,6 +258,12 @@ export default {
           type: this.txFilters.type || null,
           startDate: this.txFilters.dateRange ? this.txFilters.dateRange[0] : null,
           endDate: this.txFilters.dateRange ? this.txFilters.dateRange[1] : null
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
         }
         const res = await getInventoryTransactions(params)
         this.txList = res.data.records || []

+ 14 - 3
cfc-web/src/views/admin/KnowledgeTag.vue

@@ -8,6 +8,8 @@
           <el-button type="primary" size="small" @click="handleCreate">+ 新增标签</el-button>
         </div>
       </div>
+      <div class="table-scroll-wrap">
+
       <el-table :max-height="tableHeight" :data="filteredTags" v-loading="loading" border stripe>
         <el-table-column prop="id" label="ID" width="70" />
         <el-table-column prop="tagName" label="标签名称" min-width="200" />
@@ -18,8 +20,8 @@
             </el-tag>
           </template>
         </el-table-column>
-        <el-table-column prop="createdAt" label="创建时间" width="180" />
-        <el-table-column label="操作" width="180" fixed="right">
+        <el-table-column prop="createdAt" label="创建时间" width="180" / sortable="custom" :sort-orders="['ascending','descending']"
+        <el-table-column label="操作" width="180" fixed="right" sortable="custom" :sort-orders="['ascending','descending']"
           <template slot-scope="{ row }">
             <el-button size="mini" type="primary" @click="handleEdit(row)">编辑</el-button>
             <el-dropdown trigger="hover" @command="(cmd) => handleActionCmd(row, cmd)">
@@ -33,7 +35,8 @@
           </template>
         </el-table-column>
       </el-table>
-    </el-card>
+    </el-table>
+    </div></el-card>
 
     <el-dialog :visible.sync="dialogVisible" :title="isEdit ? '编辑标签' : '新增标签'" width="450px" :close-on-click-modal="false">
       <el-form :model="form" label-width="80px">
@@ -84,6 +87,8 @@ export default {
         id: null,
         tagName: '',
         tagType: 'general'
+      sortProp: '' ,
+      sortOrder: '' ,
       }
     }
   },
@@ -107,6 +112,12 @@ export default {
         const res = await getTagList()
         if (res.data) {
           this.tags = res.data
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
         }
       } catch (e) {
         this.$message.error('加载标签列表失败')

+ 14 - 3
cfc-web/src/views/admin/LangGraphAdmin.vue

@@ -44,6 +44,8 @@
       <el-tab-pane label="概览" name="overview">
         <div class="lg-panel">
           <div class="lg-panel-title">组件健康状态</div>
+          <div class="table-scroll-wrap">
+
           <el-table :data="componentList" border stripe size="small" class="lg-table">
             <el-table-column prop="name" label="组件" width="140" />
             <el-table-column prop="status" label="状态" width="100">
@@ -55,7 +57,8 @@
             </el-table-column>
             <el-table-column prop="detail" label="详情" show-overflow-tooltip />
           </el-table>
-        </div>
+        </el-table>
+        </div></div>
         <div class="lg-panel">
           <div class="lg-panel-title">服务配置</div>
           <el-descriptions :column="2" border size="small" class="lg-desc">
@@ -107,8 +110,8 @@
           <el-button size="small" @click="fetchLogs" :loading="logLoading">刷新</el-button>
         </div>
         <el-table :data="filteredLogs" border stripe size="small" class="lg-table lg-table-full">
-          <el-table-column prop="ts" label="时间" width="70" fixed />
-          <el-table-column prop="method" label="方法" width="60">
+          <el-table-column prop="ts" label="时间" width="70" fixed / sortable="custom" :sort-orders="['ascending','descending']"
+          <el-table-column prop="method" label="方法" width="60" sortable="custom" :sort-orders="['ascending','descending']"
             <template slot-scope="{ row }">
               <el-tag :type="row.method === 'GET' ? 'info' : 'primary'" size="mini">{{ row.method }}</el-tag>
             </template>
@@ -200,6 +203,8 @@ export default {
         CHROMA_DB_PATH: '/data/chroma_db',
         LANGCHAIN_TRACING_V2: 'true',
         LOG_LEVEL: 'info'
+      sortProp: '' ,
+      sortOrder: '' ,
       },
       logList: [],
       totalLogs: 0,
@@ -251,6 +256,12 @@ export default {
       this.refreshing = true
       try { await Promise.all([this.fetchHealth(), this.fetchSpec()]) }
       finally { this.refreshing = false }
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
     },
     async fetchHealth() {
       try {

+ 15 - 4
cfc-web/src/views/admin/MembershipCenter.vue

@@ -39,6 +39,8 @@
       <div slot="header">
         <span>会员等级配置</span>
       </div>
+      <div class="table-scroll-wrap">
+
       <el-table :max-height="tableHeight" :data="levels" v-loading="levelsLoading" border stripe>
         <el-table-column prop="levelCode" label="等级编码" width="120">
           <template slot-scope="{ row }">
@@ -82,7 +84,8 @@
           </template>
         </el-table-column>
       </el-table>
-    </el-card>
+    </el-table>
+    </div></el-card>
 
     <!-- 升级记录 -->
     <el-card>
@@ -110,10 +113,10 @@
           </template>
         </el-table-column>
         <el-table-column prop="orderId" label="订单ID" width="80" />
-        <el-table-column prop="expireTime" label="到期时间" width="160">
-          <template slot-scope="{ row }">{{ formatTime(row.expireTime) }}</template>
+        <el-table-column prop="expireTime" label="到期时间" width="160" sortable="custom" :sort-orders="['ascending','descending']"
+          <template slot-scope="{ row }" sortable="custom" :sort-orders="['ascending','descending']"{{ formatTime(row.expireTime) }}</template>
         </el-table-column>
-        <el-table-column prop="createdAt" label="创建时间" width="160">
+        <el-table-column prop="createdAt" label="创建时间" width="160" sortable="custom" :sort-orders="['ascending','descending']"
           <template slot-scope="{ row }">{{ formatTime(row.createdAt) }}</template>
         </el-table-column>
     </el-table>
@@ -216,6 +219,8 @@ export default {
         newFamilyThisMonth: 0,
         totalFamilyMemberships: 0,
         lifetimeCount: 0
+      sortProp: '' ,
+      sortOrder: '' ,
       },
       // levels
       levelsLoading: false,
@@ -242,6 +247,12 @@ export default {
     formatPrice(price) {
       if (price === null || price === undefined) return '—'
       return (price / 100).toFixed(2)
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
     },
     formatTime(t) {
       if (!t) return '-'

+ 3 - 1
cfc-web/src/views/admin/MenuManage.vue

@@ -243,7 +243,9 @@ export default {
       isEdit: false,
       submitting: false,
       menuForm: this.getEmptyForm(),
-      menuRules: {},
+      menuRules: {
+      sortProp: '' ,
+      sortOrder: '' ,},
       // 授权弹窗
       authDialogVisible: false,
       authTab: 'byRole',

+ 9 - 1
cfc-web/src/views/admin/NoticeManage.vue

@@ -27,7 +27,7 @@
               </el-tag>
             </template>
           </el-table-column>
-          <el-table-column label="创建时间" width="160">
+          <el-table-column label="创建时间" width="160" sortable="custom" :sort-orders="['ascending','descending']"
             <template slot-scope="{ row }">{{ formatTime(row.createdAt) }}</template>
           </el-table-column>
           <el-table-column label="操作" width="140" fixed="right">
@@ -82,6 +82,8 @@ export default {
         type: 'platform',
         title: '',
         content: ''
+      sortProp: '' ,
+      sortOrder: '' ,
       },
       rules: {
         type: [{ required: true, message: '请选择类型', trigger: 'change' }],
@@ -106,6 +108,12 @@ export default {
   methods: {
     calcHeight() {
       this.tableHeight = window.innerHeight - 280
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
     },
     loadList() {
       this.loading = true

+ 13 - 2
cfc-web/src/views/admin/NotificationTemplateManage.vue

@@ -7,6 +7,8 @@
       </div>
 
       <!-- 模板列表 -->
+      <div class="table-scroll-wrap">
+
       <el-table :data="list" border v-loading="loading">
         <el-table-column prop="type" label="类型" width="160">
           <template slot-scope="{ row }">
@@ -28,7 +30,7 @@
             </el-tag>
           </template>
         </el-table-column>
-        <el-table-column prop="updatedAt" label="更新时间" width="170"></el-table-column>
+        <el-table-column prop="updatedAt" label="更新时间" width="170" sortable="custom" :sort-orders="['ascending','descending']"</el-table-column sortable="custom" :sort-orders="['ascending','descending']"
         <el-table-column label="操作" width="140" fixed="right">
           <template slot-scope="{ row }">
             <el-button size="mini" type="primary" @click="handleEdit(row)">编辑</el-button>
@@ -36,7 +38,8 @@
           </template>
         </el-table-column>
       </el-table>
-    </el-card>
+    </el-table>
+    </div></el-card>
 
     <!-- 编辑弹窗 -->
     <el-dialog :title="form.id ? '编辑通知模板' : '新增通知模板'" :visible.sync="dialogVisible" width="600px">
@@ -112,6 +115,8 @@ export default {
         channels: 'in_app',
         channelList: ['in_app'],
         status: 1
+      sortProp: '' ,
+      sortOrder: '' ,
       }
     }
   },
@@ -125,6 +130,12 @@ export default {
         const res = await getNotificationTemplateList()
         if (res.code === 0 || res.code === 200) {
           this.list = res.data || []
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
         } else {
           this.$message.error(res.message || '加载失败')
         }

+ 14 - 1
cfc-web/src/views/admin/NutritionProducts.vue

@@ -42,6 +42,9 @@
         </div>
       </div>
 
+      <div class="table-scroll-wrap">
+
+
       <el-table
         :data="list"
         v-loading="loading"
@@ -118,7 +121,9 @@
         </el-table-column>
       </el-table>
 
-      <!-- 分页 -->
+      </el-table>
+
+      </div><!-- 分页 -->
       <div class="pagination-wrapper">
         <el-pagination
           background
@@ -257,6 +262,8 @@ export default {
       detailLoading: false,
       detailData: null,
       tableHeight: window.innerHeight - 260
+      sortProp: '' ,
+      sortOrder: '' ,
     }
   },
   computed: {
@@ -284,6 +291,12 @@ export default {
           const parsed = JSON.parse(row.productImages)
           if (Array.isArray(parsed)) {
             return parsed.filter(u => u)
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
           }
           if (typeof parsed === 'string') {
             return parsed.split(',').filter(u => u)

+ 10 - 2
cfc-web/src/views/admin/OrderManage.vue

@@ -69,7 +69,7 @@
           {{ payMethodLabel(row.paymentMethod) }}
         </template>
       </el-table-column>
-      <el-table-column label="下单时间" width="155">
+      <el-table-column label="下单时间" width="155" sortable="custom" :sort-orders="['ascending','descending']"
         <template slot-scope="{ row }">
           {{ formatTime(row.createdAt) }}
         </template>
@@ -203,7 +203,9 @@ export default {
       page: 1,
       size: 20,
       total: 0,
-      sortableColumns: { orderNo: '订单号' },
+      sortableColumns: { orderNo: '订单号'
+      sortProp: '' ,
+      sortOrder: '' , },
 
       statusFilter: '',
       keyword: '',
@@ -227,6 +229,12 @@ export default {
     handleSearch() {
       this.page = 1
       this.loadList()
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
     },
     async loadList() {
       this.loading = true

+ 10 - 2
cfc-web/src/views/admin/PendingRefund.vue

@@ -77,8 +77,8 @@
             {{ row.failureReason || '-' }}
           </template>
         </el-table-column>
-        <el-table-column label="创建时间" width="160">
-          <template slot-scope="{ row }">
+        <el-table-column label="创建时间" width="160" sortable="custom" :sort-orders="['ascending','descending']"
+          <template slot-scope="{ row }" sortable="custom" :sort-orders="['ascending','descending']"
             {{ formatTime(row.createdAt) }}
           </template>
         </el-table-column>
@@ -132,6 +132,8 @@ export default {
       summary: {
         totalPendingAmount: 0,
         pendingCount: 0
+      sortProp: '' ,
+      sortOrder: '' ,
       }
     }
   },
@@ -145,6 +147,12 @@ export default {
         const res = await getPendingRefundSummary()
         if (res.code === 200) {
           this.summary = res.data || { totalPendingAmount: 0 }
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
         }
       } catch (e) {
         console.error('加载待退款摘要失败', e)

+ 14 - 1
cfc-web/src/views/admin/PeriodicServiceConfig.vue

@@ -5,6 +5,9 @@
       <el-button type="primary" icon="el-icon-plus" @click="openDialog()">新增配置</el-button>
     </div>
 
+    <div class="table-scroll-wrap">
+
+
     <el-table :data="list" v-loading="loading" border stripe style="width: 100%">
       <el-table-column prop="id" label="ID" width="60" />
       <el-table-column prop="productId" label="商品ID" width="80" />
@@ -33,7 +36,9 @@
       </el-table-column>
     </el-table>
 
-    <el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="500px" @closed="resetForm">
+    </el-table>
+
+    </div><el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="500px" @closed="resetForm">
       <el-form :model="form" label-width="120px" size="small">
         <el-form-item label="商品ID" required>
           <el-input-number v-model="form.productId" :min="1" style="width: 100%" />
@@ -80,6 +85,8 @@ export default {
         remindBeforeDays: 30,
         autoRenew: 0,
         enabled: 1
+      sortProp: '' ,
+      sortOrder: '' ,
       },
       editingId: null
     }
@@ -94,6 +101,12 @@ export default {
         const res = await getPeriodicServiceList()
         if (res.data && res.data.code === 200) {
           this.list = res.data.data || []
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
         }
       } catch (e) {
         console.error('获取列表失败', e)

+ 70 - 29
cfc-web/src/views/admin/PlanManagement.vue

@@ -16,32 +16,45 @@
       </div>
     </div>
 
-    <el-table :data="plans" border stripe style="width:100%" :max-height="tableHeight">
-      <el-table-column prop="id" label="方案ID" width="80" />
-      <el-table-column prop="familyId" label="家庭ID" width="80" />
-      <el-table-column prop="childId" label="孩子ID" width="80" />
-      <el-table-column prop="name" label="方案名称" min-width="180" />
-      <el-table-column label="状态" width="100">
-        <template slot-scope="{ row }">
-          <el-tag :type="statusType(row.status)">{{ statusLabel(row.status) }}</el-tag>
-        </template>
-      </el-table-column>
-      <el-table-column label="进度" width="120">
-        <template slot-scope="{ row }">
-          {{ row.completedDays || 0 }} / {{ row.totalDays || '-' }} 天
-        </template>
-      </el-table-column>
-      <el-table-column prop="source" label="来源" width="100" />
-      <el-table-column prop="createdAt" label="创建时间" width="160" />
-      <el-table-column prop="reviewedAt" label="审核时间" width="160" />
-      <el-table-column label="操作" width="180">
-        <template slot-scope="{ row }">
-          <el-button size="mini" type="primary" @click="showDetail(row)">详情</el-button>
-          <el-button v-if="row.status === 'generated'" size="mini" type="success" @click="handleApprove(row)">批准</el-button>
-          <el-button v-if="row.status === 'reviewed'" size="mini" @click="handleActivate(row)">激活</el-button>
-        </template>
-      </el-table-column>
-    </el-table>
+    <div class="table-scroll-wrap">
+      <el-table :data="plans" border stripe style="width:100%" :max-height="tableHeight">
+        <el-table-column prop="id" label="方案ID" width="80" />
+        <el-table-column prop="familyId" label="家庭ID" width="80" />
+        <el-table-column prop="childId" label="孩子ID" width="80" />
+        <el-table-column prop="name" label="方案名称" min-width="180" />
+        <el-table-column prop="status" label="状态" width="100" sortable="custom" :sort-orders="['ascending','descending']">
+          <template slot-scope="{ row }">
+            <el-tag :type="statusType(row.status)">{{ statusLabel(row.status) }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="completedDays" label="进度" width="120" sortable="custom" :sort-orders="['ascending','descending']">
+          <template slot-scope="{ row }">
+            {{ row.completedDays || 0 }} / {{ row.totalDays || '-' }} 天
+          </template>
+        </el-table-column>
+        <el-table-column prop="source" label="来源" width="100" sortable="custom" :sort-orders="['ascending','descending']" />
+        <el-table-column prop="createdAt" label="创建时间" width="160" sortable="custom" :sort-orders="['ascending','descending']" / sortable="custom" :sort-orders="['ascending','descending']"
+        <el-table-column prop="reviewedAt" label="审核时间" width="160" sortable="custom" :sort-orders="['ascending','descending']" / sortable="custom" :sort-orders="['ascending','descending']"
+        <el-table-column label="操作" width="180" fixed="right">
+          <template slot-scope="{ row }">
+            <el-button size="mini" type="primary" @click="showDetail(row)">详情</el-button>
+            <el-button v-if="row.status === 'generated'" size="mini" type="success" @click="handleApprove(row)">批准</el-button>
+            <el-button v-if="row.status === 'reviewed'" size="mini" @click="handleActivate(row)">激活</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+    <el-pagination
+      :current-page="page"
+      :page-size="pageSize"
+      :total="total"
+      :page-sizes="[10, 20, 50, 100]"
+      layout="total, sizes, prev, pager, next, jumper"
+      @size-change="handleSizeChange"
+      @current-change="handleCurrentChange"
+      @sort-change="handleSortChange"
+      style="margin-top: 16px; text-align: right;"
+    />
 
     <el-dialog title="方案详情" :visible.sync="detailVisible" width="600px">
       <el-form label-width="100px" v-if="currentPlan">
@@ -81,7 +94,12 @@ export default {
       statusFilter: '',
       familySearch: '',
       detailVisible: false,
-      currentPlan: null
+      currentPlan: null,
+      page: 1,
+      pageSize: 20,
+      total: 0,
+      sortProp: '',
+      sortOrder: ''
     }
   },
   created() { this.loadPlans() },
@@ -89,9 +107,31 @@ export default {
     async loadPlans() {
       const res = await listFamilyPlans({
         status: this.statusFilter || null,
-        familyId: this.familySearch ? Number(this.familySearch) : null
+        familyId: this.familySearch ? Number(this.familySearch) : null,
+        page: this.page,
+        pageSize: this.pageSize,
+        sortProp: this.sortProp,
+        sortOrder: this.sortOrder
       })
-      if (res.code === 200) this.plans = res.data || []
+      if (res.code === 200) {
+        this.plans = (res.data && res.data.records) ? res.data.records : (res.data || [])
+        this.total = res.data && res.data.total ? res.data.total : this.plans.length
+      }
+    },
+    handleSizeChange(val) {
+      this.pageSize = val
+      this.page = 1
+      this.loadPlans()
+    },
+    handleCurrentChange(val) {
+      this.page = val
+      this.loadPlans()
+    },
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.loadPlans()
     },
     statusType(s) {
       const map = { generated: 'warning', reviewed: 'primary', active: 'success', completed: 'info', rejected: 'danger' }
@@ -121,4 +161,5 @@ export default {
 <style scoped>
 .header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; }
 .filters { display: flex; align-items: center; }
+.table-scroll-wrap { overflow-x: auto; }
 </style>

+ 10 - 2
cfc-web/src/views/admin/PointsLog.vue

@@ -22,8 +22,8 @@
       </div>
       <div class="table-scroll-wrap-sm">
       <el-table :max-height="tableHeight" :data="list" v-loading="loading" border stripe>
-        <el-table-column prop="createdAt" label="时间" width="160">
-          <template slot-scope="{ row }">{{ formatTime(row.createdAt) }}</template>
+        <el-table-column prop="createdAt" label="时间" width="160" sortable="custom" :sort-orders="['ascending','descending']"
+          <template slot-scope="{ row }" sortable="custom" :sort-orders="['ascending','descending']"{{ formatTime(row.createdAt) }}</template sortable="custom" :sort-orders="['ascending','descending']"
         </el-table-column>
         <el-table-column prop="childId" label="孩子ID" width="80" />
         <el-table-column label="类型" width="100">
@@ -138,6 +138,8 @@ export default {
         childKeyword: '',
         page: 1,
         size: 10
+      sortProp: '' ,
+      sortOrder: '' ,
       },
       detailVisible: false,
       currentRow: null
@@ -149,6 +151,12 @@ export default {
   methods: {
     typeLabel(type) {
       return (TYPE_MAP[type] && TYPE_MAP[type].label) || type || '—'
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
     },
     formatTime(t) {
       if (!t) return '-'

+ 11 - 1
cfc-web/src/views/admin/ProblemDomainManage.vue

@@ -6,6 +6,9 @@
         <el-button type="primary" size="mini" icon="el-icon-plus" @click="openCreate">新增问题域</el-button>
       </div>
 
+      <div class="table-scroll-wrap">
+
+
       <el-table :data="list" v-loading="loading" border stripe>
         <el-table-column prop="id" label="ID" width="60" />
         <el-table-column prop="code" label="编码" min-width="110" />
@@ -34,7 +37,8 @@
           </template>
         </el-table-column>
       </el-table>
-    </el-card>
+    </el-table>
+    </div></el-card>
 
     <el-dialog :title="form.id ? '编辑问题域' : '新增问题域'" :visible.sync="dialogVisible" width="700px" top="5vh">
       <el-form :model="form" label-width="120px">
@@ -154,6 +158,12 @@ export default {
       getProblemDomainListAdmin({}).then(res => {
         this.loading = false
         this.list = res.data || []
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
       }).catch(() => {
         this.loading = false
         this.$message.error('加载失败')

+ 12 - 4
cfc-web/src/views/admin/ProductManage.vue

@@ -95,11 +95,11 @@
           {{ row.sortOrder != null ? row.sortOrder : '-' }}
         </template>
       </el-table-column>
-      <el-table-column prop="createdAt" label="创建时间" width="160">
-        <template slot-scope="{ row }">
+      <el-table-column prop="createdAt" label="创建时间" width="160" sortable="custom" :sort-orders="['ascending','descending']"
+        <template slot-scope="{ row }" sortable="custom" :sort-orders="['ascending','descending']"
           {{ formatTime(row.createdAt) }}
-        </template>
-          <template slot="header">
+        </template sortable="custom" :sort-orders="['ascending','descending']"
+          <template slot="header" sortable="custom" :sort-orders="['ascending','descending']"
             <span class="sort-header" @click.stop="onSortClick('createdAt', $event)" @dblclick.stop="onSortToggle('createdAt')">创建时间{{ sortIcon('createdAt') }}</span>
 
           </template></el-table-column>
@@ -228,6 +228,8 @@ export default {
         keyword: '',
         status: '',
         productType: ''
+      sortProp: '' ,
+      sortOrder: '' ,
       },
       pagination: {
         page: 1,
@@ -259,6 +261,12 @@ export default {
           status: this.filters.status || null,
           productType: this.filters.productType || null,
           keyword: this.filters.keyword || null
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
         }
         if (this.sortState.length > 0) params.sort = this.sortState
 

+ 13 - 1
cfc-web/src/views/admin/ProductPpointManage.vue

@@ -18,6 +18,8 @@
         </el-tag>
       </div>
     </div>
+    <div class="table-scroll-wrap">
+
     <el-table :max-height="tableHeight" :data="list" v-loading="loading" border stripe>
       <el-table-column prop="id" label="ID" width="70" />
       <el-table-column label="商品图片" width="70">
@@ -88,7 +90,9 @@
       </el-table-column>
     </el-table>
 
-    <el-pagination
+    </el-table>
+
+    </div><el-pagination
       @size-change="onSizeChange"
       @current-change="onPageChange"
       :current-page="page"
@@ -152,6 +156,8 @@ export default {
         productId: null,
         ppoint: 0,
         startDate: ''
+      sortProp: '' ,
+      sortOrder: '' ,
       },
       productOptions: [],
       sortableColumns: { productName: '商品名称', ppoint: 'P点基点', startDate: '生效日期' }
@@ -168,6 +174,12 @@ export default {
 
         this.list = res.data.records || res.data || []
         this.total = res.data.total || 0
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
       } catch (e) {
         this.$message.error('加载失败')
       } finally {

+ 13 - 1
cfc-web/src/views/admin/ProductProfitRate.vue

@@ -13,6 +13,8 @@
         <el-button type="primary" @click="loadList">查询</el-button>
       </div>
       </div>
+    <div class="table-scroll-wrap">
+
     <el-table :max-height="tableHeight" :data="list" v-loading="loading" border stripe>
       <el-table-column prop="id" label="ID" width="70" />
       <el-table-column label="商品图片" width="70">
@@ -68,7 +70,9 @@
       </el-table-column>
     </el-table>
 
-    <el-pagination
+    </el-table>
+
+    </div><el-pagination
       @size-change="onSizeChange"
       @current-change="onPageChange"
       :current-page="page"
@@ -101,6 +105,8 @@ export default {
       typeFilter: '',
       editId: null,
       editRate: 0
+      sortProp: '' ,
+      sortOrder: '' ,
     }
   },
   mounted() {
@@ -113,6 +119,12 @@ export default {
         const res = await getProductList({ status: '', keyword: this.searchText, productType: this.typeFilter })
         this.list = res.data || []
         this.total = (res.data || []).length
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
       } catch (e) {
         this.$message.error('加载失败')
       } finally {

+ 12 - 1
cfc-web/src/views/admin/PromotionManagement.vue

@@ -6,6 +6,8 @@
           <div style="margin-bottom: 16px">
             <el-input v-model="milestoneKeyword" placeholder="搜索里程碑..." prefix-icon="el-icon-search" style="width:200px" clearable @keyup.enter.native="handleMilestoneSearch" @clear="handleMilestoneSearch" />
     </div>
+    <div class="table-scroll-wrap">
+
     <el-table :data="filteredMilestones" v-loading="milestoneLoading" border stripe :max-height="tableHeight">
             <el-table-column prop="milestone" label="里程碑" min-width="180" show-overflow-tooltip />
             <el-table-column prop="rewardPoints" label="奖励积分" width="120" />
@@ -18,7 +20,8 @@
               </template>
             </el-table-column>
     </el-table>
-    </el-tab-pane>
+    </el-table>
+    </div></el-tab-pane>
         <el-tab-pane label="排行榜" name="leaderboard">
           <div style="margin-bottom: 16px; display: flex; gap: 16px; align-items: center; flex-wrap: wrap">
             <el-date-picker
@@ -99,6 +102,8 @@ export default {
         milestone: '',
         rewardPoints: 0,
         rewardEnergy: 0
+      sortProp: '' ,
+      sortOrder: '' ,
       },
       leaderboardList: [],
       leaderboardLoading: false,
@@ -132,6 +137,12 @@ export default {
       const diff = now.getDate() - day + (day === 0 ? -6 : 1)
       const monday = new Date(now.setDate(diff))
       return monday.toISOString().split('T')[0]
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
     },
     leaderboardIndex(index) {
       return (this.leaderboardPage - 1) * this.leaderboardSize + index + 1

+ 3 - 1
cfc-web/src/views/admin/Recipes.vue

@@ -131,7 +131,9 @@ export default {
       mealTypeFilter: '',
       keyword: '',
       editId: null,
-      sortableColumns: {"name": "名称", "mealType": "餐型", "status": "状态", "createdAt": "创建时间"},
+      sortableColumns: {"name": "名称", "mealType": "餐型", "status": "状态", "createdAt": "创建时间"
+      sortProp: '' ,
+      sortOrder: '' ,},
       form: {
         name: '',
         mealType: '',

+ 15 - 3
cfc-web/src/views/admin/ReportAutoLearn.vue

@@ -10,6 +10,9 @@
 
       <el-alert title='自学习机制:当同一类型的未知报告积累到3份后,系统自动标记为"可生成"状态,管理员可在此生成报告类型和指纹规则。' type='info' :closable='false' show-icon style='margin-bottom: 16px;' />
 
+      <div class="table-scroll-wrap">
+
+
       <el-table :data="clusters" v-loading="loading" border stripe>
         <el-table-column prop="id" label="ID" width="60" />
         <el-table-column prop="clusterKey" label="聚类键" width="140" />
@@ -22,8 +25,8 @@
           </template>
         </el-table-column>
         <el-table-column prop="generatedTypeId" label="类型ID" width="140" />
-        <el-table-column prop="updatedAt" label="更新时间" width="180" />
-        <el-table-column label="操作" width="180" fixed="right">
+        <el-table-column prop="updatedAt" label="更新时间" width="180" / sortable="custom" :sort-orders="['ascending','descending']"
+        <el-table-column label="操作" width="180" fixed="right" sortable="custom" :sort-orders="['ascending','descending']"
           <template slot-scope="{ row }">
             <el-button size="mini" type="primary" @click="handleDetail(row)">详情</el-button>
             <el-button v-if="row.status === 'ready'" size="mini" type="success" @click="handleGenerate(row)">生成类型</el-button>
@@ -31,7 +34,8 @@
           </template>
         </el-table-column>
       </el-table>
-    </el-card>
+    </el-table>
+    </div></el-card>
 
     <el-dialog :visible.sync="detailVisible" title="聚类详情" width="750px">
       <template v-if="currentCluster">
@@ -61,6 +65,8 @@ export default {
       clusters: [],
       detailVisible: false,
       currentCluster: null,
+      sortProp: '' ,
+      sortOrder: '' ,
     }
   },
   mounted() { this.fetchClusters() },
@@ -71,6 +77,12 @@ export default {
         const res = await getUnknownClusters({ page: 1, size: 50 })
         this.clusters = (res.data?.records || res.data || []).filter(
           c => c.status === 'ready' || c.status === 'generated')
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
       } catch (e) { this.$message.error('加载失败') }
       this.loading = false
     },

+ 15 - 2
cfc-web/src/views/admin/ReportManagement.vue

@@ -23,6 +23,9 @@
         <el-button @click="doSync">同步数据</el-button>
       </div>
 
+      <div class="table-scroll-wrap">
+
+
       <el-table :data="tableData" border stripe v-loading="loading" style="margin-top:16px">
         <el-table-column prop="id" label="ID" width="80"></el-table-column>
         <el-table-column label="报告类型" width="100">
@@ -44,7 +47,7 @@
             <el-tag size="small">{{ scope.row.status }}</el-tag>
           </template>
         </el-table-column>
-        <el-table-column prop="createdAt" label="创建时间" width="160"></el-table-column>
+        <el-table-column prop="createdAt" label="创建时间" width="160" sortable="custom" :sort-orders="['ascending','descending']"</el-table-column sortable="custom" :sort-orders="['ascending','descending']"
         <el-table-column label="操作" width="120">
           <template slot-scope="scope">
             <el-button size="mini" type="text" @click="viewDetail(scope.row.id)">详情</el-button>
@@ -52,7 +55,9 @@
         </el-table-column>
       </el-table>
 
-      <el-pagination
+      </el-table>
+
+      </div><el-pagination
         @size-change="handleSizeChange"
         @current-change="handleCurrentChange"
         :current-page="query.page"
@@ -80,6 +85,8 @@ export default {
         size: 20,
         type: '',
         keyword: ''
+      sortProp: '' ,
+      sortOrder: '' ,
       },
       dateRange: []
     }
@@ -96,6 +103,12 @@ export default {
         size: self.query.size,
         type: self.query.type,
         keyword: self.query.keyword
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
       }
       if (self.dateRange && self.dateRange.length === 2) {
         params.startDate = self.dateRange[0].format ? self.dateRange[0].format('YYYY-MM-DD') : self.dateRange[0]

+ 15 - 3
cfc-web/src/views/admin/ReportParserImport.vue

@@ -8,6 +8,9 @@
         </div>
       </div>
 
+      <div class="table-scroll-wrap">
+
+
       <el-table :data="imports" v-loading="loading" border stripe>
         <el-table-column prop="id" label="ID" width="60" />
         <el-table-column prop="packageName" label="导入包名称" min-width="200" />
@@ -17,8 +20,8 @@
             <el-tag :type="statusType(row.status)" size="small">{{ statusText(row.status) }}</el-tag>
           </template>
         </el-table-column>
-        <el-table-column prop="createdAt" label="导入时间" width="180" />
-        <el-table-column label="操作" width="160" fixed="right">
+        <el-table-column prop="createdAt" label="导入时间" width="180" / sortable="custom" :sort-orders="['ascending','descending']"
+        <el-table-column label="操作" width="160" fixed="right" sortable="custom" :sort-orders="['ascending','descending']"
           <template slot-scope="{ row }">
             <template v-if="row.status === 'pending'">
               <el-button size="mini" type="success" @click="handleApprove(row)">批准</el-button>
@@ -28,7 +31,8 @@
           </template>
         </el-table-column>
       </el-table>
-    </el-card>
+    </el-table>
+    </div></el-card>
 
     <!-- 上传导入包对话框 -->
     <el-dialog :visible.sync="uploadDialogVisible" title="上传导入包" width="600px" :close-on-click-modal="false">
@@ -88,6 +92,8 @@ export default {
       previewDialogVisible: false,
       selectedFile: null,
       previewData: null,
+      sortProp: '' ,
+      sortOrder: '' ,
     }
   },
   mounted() { this.fetchImports() },
@@ -97,6 +103,12 @@ export default {
       try {
         const res = await getParserImports({ page: 1, size: 50 })
         this.imports = res.data?.records || res.data || []
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
       } catch (e) { this.$message.error('加载失败') }
       this.loading = false
     },

+ 11 - 1
cfc-web/src/views/admin/ReportTypeManagement.vue

@@ -9,6 +9,9 @@
         </div>
       </div>
 
+      <div class="table-scroll-wrap">
+
+
       <el-table :data="filteredTypes" v-loading="loading" border stripe>
         <el-table-column prop="id" label="ID" width="60" />
         <el-table-column prop="typeId" label="类型标识" width="140" />
@@ -42,7 +45,8 @@
           </template>
         </el-table-column>
       </el-table>
-    </el-card>
+    </el-table>
+    </div></el-card>
 
     <!-- 编辑/新增对话框 -->
     <el-dialog :visible.sync="dialogVisible" :title="isEdit ? '编辑报告类型' : '新增报告类型'" width="550px">
@@ -156,6 +160,12 @@ export default {
       try {
         const res = await getReportTypes({ page: 1, size: 999 })
         this.types = (res.data?.records || res.data || [])
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
       } catch (e) { this.$message.error('加载失败') }
       this.loading = false
     },

+ 16 - 4
cfc-web/src/views/admin/ReportUnknownCluster.vue

@@ -13,6 +13,9 @@
         </div>
       </div>
 
+      <div class="table-scroll-wrap">
+
+
       <el-table :data="clusters" v-loading="loading" border stripe>
         <el-table-column prop="id" label="ID" width="60" />
         <el-table-column prop="clusterKey" label="聚类键" width="140" />
@@ -23,15 +26,16 @@
           </template>
         </el-table-column>
         <el-table-column prop="generatedTypeId" label="生成类型" width="140" />
-        <el-table-column prop="updatedAt" label="更新时间" width="180" />
-        <el-table-column label="操作" width="160" fixed="right">
-          <template slot-scope="{ row }">
+        <el-table-column prop="updatedAt" label="更新时间" width="180" / sortable="custom" :sort-orders="['ascending','descending']"
+        <el-table-column label="操作" width="160" fixed="right" sortable="custom" :sort-orders="['ascending','descending']"
+          <template slot-scope="{ row }" sortable="custom" :sort-orders="['ascending','descending']"
             <el-button size="mini" type="primary" @click="handleDetail(row)">详情</el-button>
             <el-button v-if="row.status === 'ready'" size="mini" type="success" @click="handleGenerate(row)">生成类型</el-button>
           </template>
         </el-table-column>
       </el-table>
-    </el-card>
+    </el-table>
+    </div></el-card>
 
     <!-- 详情对话框 -->
     <el-dialog :visible.sync="detailVisible" title="聚类详情" width="750px" :close-on-click-modal="false">
@@ -71,6 +75,8 @@ export default {
       clusters: [], statusFilter: '',
       detailVisible: false, currentCluster: null,
       uploads: [],
+      sortProp: '' ,
+      sortOrder: '' ,
     }
   },
   mounted() { this.fetchClusters() },
@@ -80,6 +86,12 @@ export default {
       try {
         const res = await getUnknownClusters({ page: 1, size: 50, status: this.statusFilter })
         this.clusters = res.data?.records || res.data || []
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
       } catch (e) { this.$message.error('加载失败') }
       this.loading = false
     },

+ 2 - 0
cfc-web/src/views/admin/SeasonalFoods.vue

@@ -92,6 +92,8 @@ export default {
         foodId: null,
         month: now.getMonth() + 1,
         peak: 1
+      sortProp: '' ,
+      sortOrder: '' ,
       }
     }
   },

+ 12 - 4
cfc-web/src/views/admin/SubscriptionManagement.vue

@@ -40,10 +40,10 @@
             <el-tag :type="statusType(row.status)">{{ statusLabel(row.status) }}</el-tag>
           </template>
         </el-table-column>
-        <el-table-column label="开始时间" width="160">
-          <template slot-scope="{ row }">
+        <el-table-column label="开始时间" width="160" sortable="custom" :sort-orders="['ascending','descending']"
+          <template slot-scope="{ row }" sortable="custom" :sort-orders="['ascending','descending']"
             {{ formatTime(row.startTime) }}
-          </template>
+          </template sortable="custom" :sort-orders="['ascending','descending']"
         </el-table-column>
         <el-table-column label="结束时间" width="160">
           <template slot-scope="{ row }">
@@ -201,7 +201,9 @@ export default {
       refundReason: '',
       activateDialogVisible: false,
       activateTarget: null,
-      activateForm: { expireTime: '' }
+      activateForm: { expireTime: ''
+      sortProp: '' ,
+      sortOrder: '' , }
     }
   },
   mounted() {
@@ -218,6 +220,12 @@ export default {
         var res = await getSubscriptionList(params)
         this.list = res.data.records || res.data.list || res.data || []
         this.total = res.data.total || this.list.length
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
       } catch (e) {
         this.$message.error('加载订阅列表失败')
       } finally {

+ 10 - 2
cfc-web/src/views/admin/SupplierProductManage.vue

@@ -62,8 +62,8 @@
               <el-tag :type="statusType(row.status)" size="mini">{{ statusLabel(row.status) }}</el-tag>
             </template>
           </el-table-column>
-          <el-table-column prop="createdAt" label="创建时间" width="160">
-            <template slot-scope="{ row }">
+          <el-table-column prop="createdAt" label="创建时间" width="160" sortable="custom" :sort-orders="['ascending','descending']"
+            <template slot-scope="{ row }" sortable="custom" :sort-orders="['ascending','descending']"
               {{ formatTime(row.createdAt) }}
             </template>
           </el-table-column>
@@ -115,6 +115,8 @@ export default {
       filters: {
         keyword: '',
         status: ''
+      sortProp: '' ,
+      sortOrder: '' ,
       },
       pagination: {
         page: 1,
@@ -135,6 +137,12 @@ export default {
           size: this.pagination.size,
           status: this.filters.status || null,
           keyword: this.filters.keyword || null
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
         }
         const res = await getSupplierProductList(params)
         if (res.code === 200) {

+ 16 - 4
cfc-web/src/views/admin/SurveyTemplates.vue

@@ -7,6 +7,9 @@
         <el-button type="success" size="mini" icon="el-icon-magic-stick" @click="openAiGenerate">AI生成调研</el-button>
       </div>
 
+      <div class="table-scroll-wrap">
+
+
       <el-table :data="templates" v-loading="loading" border stripe>
         <el-table-column prop="title" label="标题" min-width="150" />
         <el-table-column prop="dimension" label="维度" width="80" />
@@ -30,8 +33,8 @@
             />
           </template>
         </el-table-column>
-        <el-table-column prop="createdAt" label="创建时间" width="160">
-          <template slot-scope="scope">
+        <el-table-column prop="createdAt" label="创建时间" width="160" sortable="custom" :sort-orders="['ascending','descending']"
+          <template slot-scope="scope" sortable="custom" :sort-orders="['ascending','descending']"
             {{ scope.row.createdAt && scope.row.createdAt.substring(0, 10) }}
           </template>
         </el-table-column>
@@ -42,7 +45,8 @@
           </template>
         </el-table-column>
       </el-table>
-    </el-card>
+    </el-table>
+    </div></el-card>
 
     <el-dialog title="新增/编辑调研模板" :visible.sync="editDialogVisible" width="600px">
       <el-form :model="form" label-width="80px">
@@ -142,7 +146,9 @@ export default {
       aiLoading: false,
       aiResult: null,
       form: this.emptyForm(),
-      aiForm: { dimension: '', topic: '' },
+      aiForm: { dimension: '', topic: ''
+      sortProp: '' ,
+      sortOrder: '' , },
       addOptionText: {}
     }
   },
@@ -153,6 +159,12 @@ export default {
     cycleLabel(type) {
       var map = { weekly: '每周', biweekly: '两周', monthly: '每月' }
       return map[type] || type || '-'
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
     },
     emptyForm() {
       return { id: null, title: '', dimension: '', cycleType: 'weekly', enabled: 1, questions: [] }

+ 13 - 1
cfc-web/src/views/admin/SysConfig.vue

@@ -7,6 +7,8 @@
         <el-button type="primary" size="small" @click="addConfig" icon="el-icon-plus">新增配置</el-button>
       </div>
     </div>
+    <div class="table-scroll-wrap">
+
     <el-table :data="list" v-loading="loading" border stripe :max-height="tableHeight">
       <el-table-column prop="configKey" label="配置键" min-width="200" show-overflow-tooltip />
       <el-table-column prop="configValue" label="配置值" min-width="250" show-overflow-tooltip />
@@ -31,7 +33,9 @@
       </el-table-column>
     </el-table>
 
-    <el-pagination
+    </el-table>
+
+    </div><el-pagination
       @size-change="onSizeChange"
       @current-change="onPageChange"
       :current-page="page"
@@ -97,6 +101,8 @@ export default {
         configValue: '',
         description: '',
         valueType: 'string'
+      sortProp: '' ,
+      sortOrder: '' ,
       },
       keyword: '',
       rules: {
@@ -115,6 +121,12 @@ export default {
         const res = await getSysConfigList({ keyword: this.keyword || undefined })
         this.list = res.data || []
         this.total = (res.data || []).length
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
       } catch (e) {
         this.$message.error('加载失败')
       } finally {

+ 16 - 4
cfc-web/src/views/admin/UnifiedHealthReports.vue

@@ -20,6 +20,9 @@
           <el-button type="danger" :disabled="selectedRows.length === 0" @click="handleBatchDelete">批量删除({{ selectedRows.length }})</el-button>
         </div>
 
+        <div class="table-scroll-wrap">
+
+
         <el-table :data="reportList" v-loading="listLoading" border stripe :max-height="tableHeight" style="margin-top:16px"
           ref="listTable"
           @selection-change="handleSelectionChange" @row-click="goEdit">
@@ -64,7 +67,8 @@
             </template>
           </el-table-column>
         </el-table>
-        <el-pagination v-if="listTotal > listSize" @current-change="onListPageChange" :current-page="listPage" :page-size="listSize" :total="listTotal" layout="total, prev, pager, next" style="margin-top:16px;text-align:right" />
+        </el-table>
+        </div><el-pagination v-if="listTotal > listSize" @current-change="onListPageChange" :current-page="listPage" :page-size="listSize" :total="listTotal" layout="total, prev, pager, next" style="margin-top:16px;text-align:right" />
       </el-tab-pane>
 
       <!-- Tab 2: 代传报告 -->
@@ -142,8 +146,8 @@
               <template slot-scope="{ row }">{{ row.overallScore || '-' }}</template>
             </el-table-column>
             <el-table-column prop="remark" label="备注" show-overflow-tooltip />
-            <el-table-column prop="createdAt" label="上传时间" width="160">
-              <template slot-scope="{ row }">{{ formatTime(row.createdAt) }}</template>
+            <el-table-column prop="createdAt" label="上传时间" width="160" sortable="custom" :sort-orders="['ascending','descending']"
+              <template slot-scope="{ row }" sortable="custom" :sort-orders="['ascending','descending']"{{ formatTime(row.createdAt) }}</template>
             </el-table-column>
             <el-table-column label="操作" width="100" fixed="right">
               <template slot-scope="{ row }">
@@ -193,7 +197,9 @@ export default {
       listPage: 1,
       listSize: 20,
       listTotal: 0,
-      query: { type: '', keyword: '' },
+      query: { type: '', keyword: ''
+      sortProp: '' ,
+      sortOrder: '' , },
       dateRange: [],
       selectedRows: [],
 
@@ -239,6 +245,12 @@ export default {
         if (this.dateRange && this.dateRange.length === 2) {
           params.startDate = this.dateRange[0].format ? this.dateRange[0].format('YYYY-MM-DD') : this.dateRange[0]
           params.endDate = this.dateRange[1].format ? this.dateRange[1].format('YYYY-MM-DD') : this.dateRange[1]
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
         }
         var baseURL = process.env.VUE_APP_BASE_API || ''
         var token = localStorage.getItem('token')

+ 15 - 4
cfc-web/src/views/admin/VirtualGoodsConfig.vue

@@ -16,6 +16,8 @@
           <el-button type="primary" size="small" @click="handleCreate">+ 新增道具</el-button>
         </div>
       </div>
+      <div class="table-scroll-wrap">
+
       <el-table :max-height="tableHeight" :data="filteredList" v-loading="loading" border stripe>
         <el-table-column prop="id" label="ID" width="70" />
         <el-table-column label="道具类型" width="120">
@@ -33,9 +35,9 @@
             <el-switch :value="row.status === 1" disabled />
           </template>
         </el-table-column>
-        <el-table-column prop="createTime" label="创建时间" width="170" />
-        <el-table-column prop="updateTime" label="更新时间" width="170" />
-        <el-table-column label="操作" width="180" fixed="right">
+        <el-table-column prop="createTime" label="创建时间" width="170" / sortable="custom" :sort-orders="['ascending','descending']"
+        <el-table-column prop="updateTime" label="更新时间" width="170" / sortable="custom" :sort-orders="['ascending','descending']"
+        <el-table-column label="操作" width="180" fixed="right" sortable="custom" :sort-orders="['ascending','descending']"
           <template slot-scope="{ row }">
             <el-button size="mini" type="primary" @click="handleEdit(row)">编辑</el-button>
             <el-dropdown trigger="hover" @command="(cmd) => handleActionCmd(row, cmd)">
@@ -49,7 +51,8 @@
           </template>
         </el-table-column>
       </el-table>
-    </el-card>
+    </el-table>
+    </div></el-card>
 
     <el-dialog :visible.sync="dialogVisible" :title="isEdit ? '编辑道具' : '新增道具'" width="480px" :close-on-click-modal="false">
       <el-form :model="form" label-width="90px">
@@ -103,6 +106,8 @@ export default {
         productId: '',
         goodsName: '',
         status: 1
+      sortProp: '' ,
+      sortOrder: '' ,
       }
     }
   },
@@ -125,6 +130,12 @@ export default {
       if (type === 'MEMBERSHIP') return '会员'
       if (type === 'SUBSCRIPTION') return '订阅'
       return type || '-'
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
     },
     async loadData() {
       this.loading = true

+ 17 - 4
cfc-web/src/views/admin/VirtualTeamList.vue

@@ -20,6 +20,9 @@
         <el-button size="small" type="primary" @click="loadData">查询</el-button>
     </div>
 
+    <div class="table-scroll-wrap">
+
+
     <el-table :max-height="tableHeight" :data="pagedData" stripe v-loading="loading">
         <el-table-column prop="id" label="ID" width="80"></el-table-column>
         <el-table-column prop="name" label="团队名称" width="160"></el-table-column>
@@ -37,8 +40,8 @@
             </el-tag>
           </template>
         </el-table-column>
-        <el-table-column prop="createdAt" label="创建时间" width="170">
-          <template slot-scope="scope">
+        <el-table-column prop="createdAt" label="创建时间" width="170" sortable="custom" :sort-orders="['ascending','descending']"
+          <template slot-scope="scope" sortable="custom" :sort-orders="['ascending','descending']"
             {{ formatDate(scope.row.createdAt) }}
           </template>
         </el-table-column>
@@ -58,7 +61,9 @@
         </el-table-column>
     </el-table>
 
-    <el-pagination
+    </el-table>
+
+    </div><el-pagination
         v-if="filteredData.length > pageSize"
         style="margin-top: 16px; text-align: right"
         background
@@ -124,7 +129,9 @@ export default {
       isEdit: false,
       form: this.getEmptyForm(),
       formRules: {
-        name: [{ required: true, message: '请输入团队名称', trigger: 'blur' }]
+        name: [{ required: true, message: '请输入团队名称', trigger: 'blur'
+      sortProp: '' ,
+      sortOrder: '' , }]
       }
     }
   },
@@ -150,6 +157,12 @@ export default {
   methods: {
     getEmptyForm() {
       return { id: null, name: '', description: '', profitRate: 0 }
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
     },
     formatDate(val) {
       if (!val) return '-'

+ 2 - 0
cfc-web/src/views/admin/ZodiacConfigs.vue

@@ -111,6 +111,8 @@ export default {
       successDialogVisible: false,
       isEdit: false,
       form: this.getEmptyForm()
+      sortProp: '' ,
+      sortOrder: '' ,
     }
   },
   created() {

+ 10 - 2
cfc-web/src/views/admin/data-source-config.vue

@@ -27,8 +27,8 @@
             <span v-else style="color:#999">-</span>
           </template>
         </el-table-column>
-        <el-table-column prop="createdAt" label="创建时间" width="160">
-          <template slot-scope="scope">{{ formatDate(scope.row.createdAt) }}</template>
+        <el-table-column prop="createdAt" label="创建时间" width="160" sortable="custom" :sort-orders="['ascending','descending']"
+          <template slot-scope="scope" sortable="custom" :sort-orders="['ascending','descending']"{{ formatDate(scope.row.createdAt) }}</template>
         </el-table-column>
         <el-table-column label="操作" width="200" fixed="right">
           <template slot-scope="{ row }">
@@ -122,6 +122,8 @@ export default {
       isEdit: false,
       form: this.emptyForm(),
       keyword: ''
+      sortProp: '' ,
+      sortOrder: '' ,
     }
   },
   mounted() {
@@ -131,6 +133,12 @@ export default {
     handleSearch() {
       this.page = 1
       this.fetchRecords()
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
     },
     emptyForm() {
       return {

+ 12 - 1
cfc-web/src/views/admin/dimension-config.vue

@@ -6,6 +6,8 @@
         <el-input v-model="keyword" placeholder="搜索维度/来源..." prefix-icon="el-icon-search" style="width:200px;float:right;margin-right:8px" clearable @keyup.enter.native="loadData" @clear="loadData" />
         <el-button style="float: right;margin-right:8px" type="primary" size="small" @click="handleCreate">+ 添加常模</el-button>
     </div>
+    <div class="table-scroll-wrap">
+
     <el-table :data="filteredNorms" stripe v-loading="loading" :max-height="tableHeight">
         <el-table-column prop="id" label="ID" width="60"></el-table-column>
         <el-table-column prop="dimension" label="维度" width="100">
@@ -52,7 +54,8 @@
         </template>
       </el-table-column>
     </el-table>
-  </el-card>
+  </el-table>
+  </div></el-card>
 
     <el-dialog :visible.sync="dialogVisible" :title="isEdit ? '编辑常模' : '添加常模'" width="600px">
       <el-form :model="form" label-width="100px">
@@ -141,6 +144,8 @@ export default {
       successDialogVisible: false,
       isEdit: false,
       form: this.emptyForm()
+      sortProp: '' ,
+      sortOrder: '' ,
     }
   },
   computed: {
@@ -175,6 +180,12 @@ export default {
         percentile85: 0,
         percentile95: 0,
         source: ''
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
       }
     },
     loadData() {

+ 13 - 1
cfc-web/src/views/admin/dimension/index.vue

@@ -7,6 +7,9 @@
         <el-button style="float: right; margin-left: 10px" type="primary" size="small" @click="handleCreate">+ 新增维度</el-button>
 </div>
 
+      <div class="table-scroll-wrap">
+
+
       <el-table :data="filteredData" stripe v-loading="loading" :max-height="tableHeight">
         <el-table-column prop="id" label="ID" width="80" />
         <el-table-column prop="dimensionCode" label="代码" width="120" />
@@ -34,7 +37,8 @@
           </template>
         </el-table-column>
     </el-table>
-  </el-card>
+  </el-table>
+  </div></el-card>
 
     <el-dialog :visible.sync="dialogVisible" :title="isEdit ? '编辑维度' : '新增维度'" width="500px">
       <el-form :model="form" label-width="120px">
@@ -107,6 +111,8 @@ export default {
       form: this.initForm(),
       dimensions: [],
       keyword: ''
+      sortProp: '' ,
+      sortOrder: '' ,
     }
   },
   computed: {
@@ -138,6 +144,12 @@ export default {
         enabled: 1,
         difyDatasetId: '',
         remark: ''
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
       }
     },
     async fetchList() {

+ 14 - 1
cfc-web/src/views/admin/energy-config/WuxingConfig.vue

@@ -11,6 +11,9 @@
         <el-input v-model="sourceKeyword" placeholder="搜索来源类型/名称..." prefix-icon="el-icon-search" style="width: 250px; float: right; margin-bottom: 15px;" clearable @keyup.enter.native="loadSourceConfigs" @clear="loadSourceConfigs" />
         <div style="clear: both;"></div>
 
+        <div class="table-scroll-wrap">
+
+
         <el-table :data="filteredSourceConfigs" stripe v-loading="sourceLoading" style="margin-top: 10px;" :max-height="tableHeight">
           <el-table-column prop="sourceType" label="来源类型" width="120" show-overflow-tooltip></el-table-column>
           <el-table-column prop="sourceName" label="来源名称" width="150" show-overflow-tooltip></el-table-column>
@@ -29,7 +32,9 @@
           </el-table-column>
         </el-table>
 
-        <div style="margin-top: 15px;">
+        </el-table>
+
+        </div><div style="margin-top: 15px;">
           <el-button type="primary" @click="handleCreateSource">+ 新增来源配置</el-button>
         </div>
       </div>
@@ -133,6 +138,8 @@ export default {
       sourceForm: this.emptySourceForm(),
       dailyLimits: [],
       dailyLimitsLoading: false
+      sortProp: '' ,
+      sortOrder: '' ,
     }
   },
   computed: {
@@ -161,6 +168,12 @@ export default {
           { dimensionId: 1, ratio: 5000 },
           { dimensionId: 2, ratio: 5000 }
         ]
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
       }
     },
     getDimensionLabel(dimensionId) {

+ 12 - 2
cfc-web/src/views/admin/gates.vue

@@ -6,6 +6,9 @@
         <el-button type="primary" size="small" style="float:right" @click="handleAdd">新增关卡</el-button>
       </div>
 
+      <div class="table-scroll-wrap">
+
+
       <el-table :data="gateList" border v-loading="loading" style="width:100%">
         <el-table-column prop="sortOrder" label="排序" width="60" align="center">
           <template slot-scope="scope">
@@ -59,7 +62,8 @@
           </template>
         </el-table-column>
       </el-table>
-      <div style="margin-top:15px;text-align:right">
+      </el-table>
+      </div><div style="margin-top:15px;text-align:right">
         <el-pagination
           background
           layout="total, prev, pager, next, sizes"
@@ -97,7 +101,7 @@
           </template>
         </el-table-column>
         <el-table-column prop="unlockedBy" label="解锁人" width="80"></el-table-column>
-        <el-table-column prop="unlockedAt" label="解锁时间" width="180"></el-table-column>
+        <el-table-column prop="unlockedAt" label="解锁时间" width="180" sortable="custom" :sort-orders="['ascending','descending']"</el-table-column>
       </el-table>
     </el-card>
 
@@ -231,6 +235,12 @@ export default {
   methods: {
     gateTypeLabel(type) {
       return GATE_TYPE_LABELS[type] || type
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
     },
     unlockTargetLabel(target) {
       return UNLOCK_TARGET_LABELS[target] || target

+ 14 - 2
cfc-web/src/views/admin/health-knowledge.vue

@@ -21,6 +21,9 @@
         </el-form-item>
       </el-form>
 
+      <div class="table-scroll-wrap">
+
+
       <el-table :data="list" border stripe v-loading="loading" empty-text="暂无数据" style="width:100%" :max-height="tableHeight">
         <el-table-column prop="id" label="ID" width="60" />
         <el-table-column prop="itemName" label="指标名称" min-width="140" />
@@ -35,7 +38,8 @@
           </template>
         </el-table-column>
       </el-table>
-    </el-card>
+    </el-table>
+    </div></el-card>
 
     <el-dialog :visible.sync="dialog.visible" :title="dialog.isEdit ? '编辑知识库条目' : '新增知识库条目'" width="560px" :close-on-click-modal="false">
       <el-form :model="dialog.form" label-width="100px" size="small">
@@ -76,7 +80,9 @@ export default {
     return {
       loading: false,
       list: [],
-      filters: { name: '', category: '' },
+      filters: { name: '', category: ''
+      sortProp: '' ,
+      sortOrder: '' , },
       categories: ['维生素', '氨基酸', '微量元素', '短链脂肪酸', '神经递质与激素', '肠道屏障与代谢物', '抗生素风险评估'],
       dialog: { visible: false, isEdit: false, saving: false, form: {} }
     }
@@ -94,6 +100,12 @@ export default {
           var rows = data.data || []
           if (this.filters.name) {
             rows = rows.filter(function(r) { return r.itemName && r.itemName.indexOf(this.filters.name) >= 0 }.bind(this))
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
           }
           if (this.filters.category) {
             rows = rows.filter(function(r) { return r.category === this.filters.category }.bind(this))

+ 11 - 1
cfc-web/src/views/admin/indicators/index.vue

@@ -16,6 +16,8 @@
     </div>
 
     <!-- 表格 -->
+      <div class="table-scroll-wrap">
+
       <el-table :data="list" border stripe v-loading="loading" :max-height="tableHeight">
       <el-table-column prop="id" label="ID" width="60" />
       <el-table-column prop="domain" label="领域" width="100" />
@@ -44,7 +46,9 @@
       </el-table-column>
     </el-table>
 
-    <!-- 编辑弹窗 -->
+    </el-table>
+
+    </div><!-- 编辑弹窗 -->
     <el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="500px">
       <el-form ref="form" :model="form" label-width="100px">
         <el-form-item label="领域" required>
@@ -131,6 +135,12 @@ export default {
       this.$http.post('/api/admin/indicator/list', {
         domain: this.filterDomain || undefined,
         status: 1
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
       }).then(res => {
         if (res.code === 0) {
           this.list = res.data || []

+ 13 - 1
cfc-web/src/views/admin/review/ActivityAudit.vue

@@ -10,6 +10,8 @@
       <el-input v-model="keyword" placeholder="搜索..." prefix-icon="el-icon-search" clearable @keyup.enter.native="handleSearch" @clear="handleSearch" style="width:200px" />
       <el-button size="small" @click="loadData" :loading="loading">刷新</el-button>
     </div>
+    <div class="table-scroll-wrap">
+
     <el-table :data="list" v-loading="loading" border stripe style="min-width:900px;" :max-height="tableHeight">
       <el-table-column prop="id" label="ID" width="60" />
       <el-table-column prop="title" label="活动名称" min-width="200" show-overflow-tooltip />
@@ -30,7 +32,9 @@
       </el-table-column>
     </el-table>
 
-    <el-dialog title="拒绝原因" :visible.sync="rejectDialogVisible" width="400px">
+    </el-table>
+
+    </div><el-dialog title="拒绝原因" :visible.sync="rejectDialogVisible" width="400px">
       <el-input v-model="rejectReason" type="textarea" :rows="3" placeholder="请输入拒绝原因" />
       <span slot="footer">
         <el-button @click="rejectDialogVisible = false">取消</el-button>
@@ -54,6 +58,8 @@ export default {
     return {
       list: [], loading: false, keyword: '', statusFilter: 'pending',
       rejectDialogVisible: false, rejectTarget: null, rejectReason: ''
+      sortProp: '' ,
+      sortOrder: '' ,
     }
   },
   mounted() { this.loadData() },
@@ -67,6 +73,12 @@ export default {
         const params = { auditStatus: this.statusFilter || undefined, keyword: this.keyword || undefined, page: 1, size: 50 }
         const res = await getActivityList(params)
         this.list = res.data && res.data.records || res.data && res.data.list || res.data || []
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
       } catch (e) { this.$message.error('加载失败')
       } finally { this.loading = false }
     },

+ 12 - 1
cfc-web/src/views/admin/review/ActivityRegistrationAudit.vue

@@ -9,6 +9,8 @@
       </el-select>
       <el-button size="small" @click="loadData" :loading="loading">刷新</el-button>
     </div>
+    <div class="table-scroll-wrap">
+
     <el-table :data="list" v-loading="loading" border stripe style="min-width:900px;" :max-height="tableHeight">
       <el-table-column prop="id" label="ID" width="60" />
       <el-table-column label="用户" width="120">
@@ -32,7 +34,8 @@
         </template>
       </el-table-column>
     </el-table>
-  </div>
+  </el-table>
+  </div></div>
 </template>
 
 <script>
@@ -49,6 +52,8 @@ export default {
   data() {
     return {
       list: [], loading: false, statusFilter: 'pending'
+      sortProp: '' ,
+      sortOrder: '' ,
     }
   },
   mounted() { this.loadData() },
@@ -68,6 +73,12 @@ export default {
               var r = a.registrations[j]
               r.activityTitle = a.title
               regs.push(r)
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
             }
           }
         }

+ 15 - 3
cfc-web/src/views/admin/review/ArticleAudit.vue

@@ -12,6 +12,8 @@
       <el-button v-if="selectedRows.length > 0 && statusFilter === 'pending'" size="small" type="success" @click="handleBatchApprove">批量通过({{ selectedRows.length }})</el-button>
       <el-button v-if="selectedRows.length > 0 && statusFilter === 'pending'" size="small" type="danger" @click="showBatchRejectDialog">批量驳回({{ selectedRows.length }})</el-button>
     </div>
+    <div class="table-scroll-wrap">
+
     <el-table :data="list" v-loading="loading" border stripe style="min-width:900px;" :max-height="tableHeight" @selection-change="onSelectionChange">
       <el-table-column type="selection" width="40" v-if="statusFilter === 'pending'" />
       <el-table-column prop="id" label="ID" width="60" />
@@ -27,8 +29,8 @@
           <span v-else style="color:#999;">-</span>
         </template>
       </el-table-column>
-      <el-table-column prop="createdAt" label="申请时间" width="160" />
-      <el-table-column label="操作" width="200" fixed="right">
+      <el-table-column prop="createdAt" label="申请时间" width="160" / sortable="custom" :sort-orders="['ascending','descending']"
+      <el-table-column label="操作" width="200" fixed="right" sortable="custom" :sort-orders="['ascending','descending']"
         <template slot-scope="{ row }">
           <template v-if="row.auditStatus === 'pending'">
             <el-button size="mini" type="primary" @click="showDetail(row)">查看</el-button>
@@ -40,7 +42,9 @@
       </el-table-column>
     </el-table>
 
-    <el-pagination
+    </el-table>
+
+    </div><el-pagination
       v-if="total > 0"
       :current-page="page"
       :page-size="size"
@@ -123,6 +127,8 @@ export default {
       rejectReason: '',
       batchRejectReason: '',
       currentRow: null
+      sortProp: '' ,
+      sortOrder: '' ,
     }
   },
   created() {
@@ -138,6 +144,12 @@ export default {
           status: 'draft',
           auditStatus: this.statusFilter || undefined,
           keyword: this.keyword || undefined
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
         })
         if (res.code === 200) {
           this.list = res.data && res.data.records || res.data && res.data.list || []

+ 13 - 1
cfc-web/src/views/admin/review/ButlerAudit.vue

@@ -9,6 +9,8 @@
       </el-select>
       <el-button size="small" @click="loadData" :loading="loading">刷新</el-button>
     </div>
+    <div class="table-scroll-wrap">
+
     <el-table :data="list" v-loading="loading" border stripe style="min-width:900px;" :max-height="tableHeight">
       <el-table-column prop="id" label="档案ID" width="80" />
       <el-table-column label="用户" width="120">
@@ -32,7 +34,9 @@
       </el-table-column>
     </el-table>
 
-    <el-dialog title="拒绝原因" :visible.sync="rejectDialogVisible" width="400px">
+    </el-table>
+
+    </div><el-dialog title="拒绝原因" :visible.sync="rejectDialogVisible" width="400px">
       <el-input v-model="rejectReason" type="textarea" :rows="3" placeholder="请输入拒绝原因" />
       <span slot="footer">
         <el-button @click="rejectDialogVisible = false">取消</el-button>
@@ -57,6 +61,8 @@ export default {
     return {
       list: [], loading: false, statusFilter: 'pending',
       rejectDialogVisible: false, rejectTarget: null, rejectReason: ''
+      sortProp: '' ,
+      sortOrder: '' ,
     }
   },
   mounted() { this.loadData() },
@@ -69,6 +75,12 @@ export default {
         const res = await getButlerList({ status: this.statusFilter || undefined, page: 1, size: 50 })
         this.list = res.data && res.data.records || res.data && res.data.list || res.data || []
         await this.resolveUserNames(this.list, 'userId')
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
       } catch (e) { this.$message.error('加载失败')
       } finally { this.loading = false }
     },

+ 13 - 1
cfc-web/src/views/admin/review/GuideAudit.vue

@@ -10,6 +10,8 @@
       <el-input v-model="keyword" placeholder="搜索..." prefix-icon="el-icon-search" clearable @keyup.enter.native="handleSearch" @clear="handleSearch" style="width:200px" />
       <el-button size="small" @click="loadData" :loading="loading">刷新</el-button>
     </div>
+    <div class="table-scroll-wrap">
+
     <el-table :data="list" v-loading="loading" border stripe style="min-width:900px;" :max-height="tableHeight">
       <el-table-column prop="id" label="ID" width="60" />
       <el-table-column prop="nickname" label="昵称" width="120" />
@@ -31,7 +33,9 @@
       </el-table-column>
     </el-table>
 
-    <el-pagination
+    </el-table>
+
+    </div><el-pagination
       v-if="total > 0"
       :current-page="page"
       :page-size="size"
@@ -66,6 +70,8 @@ export default {
       list: [], loading: false, keyword: '', statusFilter: 'pending',
       page: 1, size: 20, total: 0,
       rejectDialogVisible: false, rejectTarget: null, rejectReason: ''
+      sortProp: '' ,
+      sortOrder: '' ,
     }
   },
   mounted() { this.loadData() },
@@ -82,6 +88,12 @@ export default {
           keyword: this.keyword || undefined,
           page: this.page,
           size: this.size
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
         })
         if (res.data && res.data.records) {
           this.list = res.data.records

+ 13 - 1
cfc-web/src/views/admin/review/HierarchyAudit.vue

@@ -9,6 +9,8 @@
       </el-select>
       <el-button size="small" @click="loadData" :loading="loading">刷新</el-button>
     </div>
+    <div class="table-scroll-wrap">
+
     <el-table :data="list" v-loading="loading" border stripe style="min-width:900px;" :max-height="tableHeight">
       <el-table-column prop="id" label="ID" width="60" />
       <el-table-column prop="applicantName" label="申请人" width="120" />
@@ -31,7 +33,9 @@
       </el-table-column>
     </el-table>
 
-    <el-dialog title="拒绝原因" :visible.sync="rejectDialogVisible" width="400px">
+    </el-table>
+
+    </div><el-dialog title="拒绝原因" :visible.sync="rejectDialogVisible" width="400px">
       <el-input v-model="rejectReason" type="textarea" :rows="3" placeholder="请输入拒绝原因" />
       <span slot="footer">
         <el-button @click="rejectDialogVisible = false">取消</el-button>
@@ -56,6 +60,8 @@ export default {
     return {
       list: [], loading: false, statusFilter: 'pending',
       rejectDialogVisible: false, rejectTarget: null, rejectReason: ''
+      sortProp: '' ,
+      sortOrder: '' ,
     }
   },
   mounted() { this.loadData() },
@@ -67,6 +73,12 @@ export default {
       try {
         const res = await request({ url: '/api/admin/supply-hierarchy/change-requests', method: 'post', data: { status: this.statusFilter || undefined } })
         this.list = res.data && res.data.records || res.data || []
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
       } catch (e) { this.$message.error('加载失败')
       } finally { this.loading = false }
     },

+ 13 - 1
cfc-web/src/views/admin/review/NutritionistAudit.vue

@@ -10,6 +10,8 @@
       <el-input v-model="keyword" placeholder="搜索..." prefix-icon="el-icon-search" clearable @keyup.enter.native="handleSearch" @clear="handleSearch" style="width:200px" />
       <el-button size="small" @click="loadData" :loading="loading">刷新</el-button>
     </div>
+    <div class="table-scroll-wrap">
+
     <el-table :data="list" v-loading="loading" border stripe style="min-width:900px;" :max-height="tableHeight">
       <el-table-column prop="id" label="ID" width="60" />
       <el-table-column prop="nickname" label="昵称" width="120" />
@@ -31,7 +33,9 @@
       </el-table-column>
     </el-table>
 
-    <el-dialog title="拒绝原因" :visible.sync="rejectDialogVisible" width="400px">
+    </el-table>
+
+    </div><el-dialog title="拒绝原因" :visible.sync="rejectDialogVisible" width="400px">
       <el-input v-model="rejectReason" type="textarea" :rows="3" placeholder="请输入拒绝原因" />
       <span slot="footer">
         <el-button @click="rejectDialogVisible = false">取消</el-button>
@@ -55,6 +59,8 @@ export default {
     return {
       list: [], loading: false, keyword: '', statusFilter: 'pending',
       rejectDialogVisible: false, rejectTarget: null, rejectReason: ''
+      sortProp: '' ,
+      sortOrder: '' ,
     }
   },
   mounted() { this.loadData() },
@@ -68,6 +74,12 @@ export default {
         const res = await getNutritionistApplications({
           status: this.statusFilter || undefined,
           keyword: this.keyword || undefined, page: 1, size: 50
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
         })
         if (res.data && res.data.records) {
           this.list = res.data.records

+ 15 - 3
cfc-web/src/views/admin/review/PackageAudit.vue

@@ -11,6 +11,8 @@
       <el-button v-if="selectedRows.length > 0 && statusFilter === 'pending'" size="small" type="success" @click="handleBatchApprove">批量通过({{ selectedRows.length }})</el-button>
       <el-button v-if="selectedRows.length > 0 && statusFilter === 'pending'" size="small" type="danger" @click="showBatchRejectDialog">批量驳回({{ selectedRows.length }})</el-button>
     </div>
+    <div class="table-scroll-wrap">
+
     <el-table :data="list" v-loading="loading" border stripe style="min-width:900px;" :max-height="tableHeight" @selection-change="onSelectionChange">
       <el-table-column type="selection" width="40" v-if="statusFilter === 'pending'" />
       <el-table-column prop="id" label="ID" width="60" />
@@ -32,8 +34,8 @@
           <span v-else style="color:#999;">-</span>
         </template>
       </el-table-column>
-      <el-table-column prop="createdAt" label="申请时间" width="160" />
-      <el-table-column label="操作" width="200" fixed="right">
+      <el-table-column prop="createdAt" label="申请时间" width="160" / sortable="custom" :sort-orders="['ascending','descending']"
+      <el-table-column label="操作" width="200" fixed="right" sortable="custom" :sort-orders="['ascending','descending']"
         <template slot-scope="{ row }">
           <template v-if="row.status === 'pending'">
             <el-button size="mini" type="primary" @click="showDetail(row)">查看</el-button>
@@ -45,7 +47,9 @@
       </el-table-column>
     </el-table>
 
-    <el-pagination
+    </el-table>
+
+    </div><el-pagination
       v-if="total > 0"
       :current-page="page"
       :page-size="size"
@@ -125,6 +129,8 @@ export default {
       rejectReason: '',
       batchRejectReason: '',
       currentRow: null
+      sortProp: '' ,
+      sortOrder: '' ,
     }
   },
   created() {
@@ -139,6 +145,12 @@ export default {
           let data = Array.isArray(res.data) ? res.data : (res.data && res.data.records) || []
           if (this.statusFilter) {
             data = data.filter(item => item.status === this.statusFilter)
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
           }
           this.list = data
           this.total = data.length

+ 13 - 1
cfc-web/src/views/admin/review/ProductAudit.vue

@@ -9,6 +9,8 @@
       </el-select>
       <el-button size="small" @click="loadData" :loading="loading">刷新</el-button>
     </div>
+    <div class="table-scroll-wrap">
+
     <el-table :data="list" v-loading="loading" border stripe style="min-width:900px;" :max-height="tableHeight">
       <el-table-column prop="id" label="ID" width="60" />
       <el-table-column prop="name" label="商品名称" min-width="180" show-overflow-tooltip />
@@ -29,7 +31,9 @@
       </el-table-column>
     </el-table>
 
-    <el-dialog title="拒绝原因" :visible.sync="rejectDialogVisible" width="400px">
+    </el-table>
+
+    </div><el-dialog title="拒绝原因" :visible.sync="rejectDialogVisible" width="400px">
       <el-input v-model="rejectReason" type="textarea" :rows="3" placeholder="请输入拒绝原因" />
       <span slot="footer">
         <el-button @click="rejectDialogVisible = false">取消</el-button>
@@ -53,6 +57,8 @@ export default {
     return {
       list: [], loading: false, statusFilter: 'pending',
       rejectDialogVisible: false, rejectTarget: null, rejectReason: ''
+      sortProp: '' ,
+      sortOrder: '' ,
     }
   },
   mounted() { this.loadData() },
@@ -64,6 +70,12 @@ export default {
       try {
         const res = await getProductList({ status: this.statusFilter || undefined, page: 1, size: 50 })
         this.list = res.data && res.data.records || res.data && res.data.list || res.data || []
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
       } catch (e) { this.$message.error('加载失败')
       } finally { this.loading = false }
     },

+ 13 - 1
cfc-web/src/views/admin/review/VendorAudit.vue

@@ -10,6 +10,8 @@
       <el-input v-model="keyword" placeholder="搜索..." prefix-icon="el-icon-search" clearable @keyup.enter.native="handleSearch" @clear="handleSearch" style="width:200px" />
       <el-button size="small" @click="loadData" :loading="loading">刷新</el-button>
     </div>
+    <div class="table-scroll-wrap">
+
     <el-table :data="list" v-loading="loading" border stripe style="min-width:900px;" :max-height="tableHeight">
       <el-table-column prop="id" label="ID" width="60" />
       <el-table-column prop="nickname" label="昵称" width="120" />
@@ -34,7 +36,9 @@
       </el-table-column>
     </el-table>
 
-    <el-dialog title="拒绝原因" :visible.sync="rejectDialogVisible" width="400px">
+    </el-table>
+
+    </div><el-dialog title="拒绝原因" :visible.sync="rejectDialogVisible" width="400px">
       <el-input v-model="rejectReason" type="textarea" :rows="3" placeholder="请输入拒绝原因" />
       <span slot="footer">
         <el-button @click="rejectDialogVisible = false">取消</el-button>
@@ -58,6 +62,8 @@ export default {
     return {
       list: [], loading: false, keyword: '', statusFilter: 'pending',
       rejectDialogVisible: false, rejectTarget: null, rejectReason: ''
+      sortProp: '' ,
+      sortOrder: '' ,
     }
   },
   mounted() { this.loadData() },
@@ -71,6 +77,12 @@ export default {
       try {
         const res = await getVendorList({ status: this.statusFilter, keyword: this.keyword || undefined, page: 1, size: 50 })
         this.list = res.data && res.data.records || res.data && res.data.list || []
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
       } catch (e) { this.$message.error('加载失败')
       } finally { this.loading = false }
     },

+ 15 - 3
cfc-web/src/views/admin/review/WithdrawAudit.vue

@@ -10,6 +10,8 @@
       <el-input v-model="keyword" placeholder="搜索..." prefix-icon="el-icon-search" clearable @keyup.enter.native="handleSearch" @clear="handleSearch" style="width:200px" />
       <el-button size="small" @click="loadData" :loading="loading">刷新</el-button>
     </div>
+    <div class="table-scroll-wrap">
+
     <el-table :data="list" v-loading="loading" border stripe style="min-width:900px;" :max-height="tableHeight">
       <el-table-column prop="id" label="ID" width="60" />
       <el-table-column label="用户" width="120">
@@ -26,8 +28,8 @@
           <el-tag :type="statusType(row.status)">{{ statusLabel(row.status) }}</el-tag>
         </template>
       </el-table-column>
-      <el-table-column prop="createdAt" label="申请时间" width="160" />
-      <el-table-column label="操作" width="180" fixed="right">
+      <el-table-column prop="createdAt" label="申请时间" width="160" / sortable="custom" :sort-orders="['ascending','descending']"
+      <el-table-column label="操作" width="180" fixed="right" sortable="custom" :sort-orders="['ascending','descending']"
         <template slot-scope="{ row }">
           <template v-if="row.status === 'pending'">
             <el-button size="mini" type="success" @click="handleApprove(row)">通过</el-button>
@@ -38,7 +40,9 @@
       </el-table-column>
     </el-table>
 
-    <el-pagination
+    </el-table>
+
+    </div><el-pagination
       v-if="total > 0"
       :current-page="page"
       :page-size="size"
@@ -69,6 +73,8 @@ export default {
       page: 1,
       size: 20,
       total: 0
+      sortProp: '' ,
+      sortOrder: '' ,
     }
   },
   created() {
@@ -83,6 +89,12 @@ export default {
           size: this.size,
           status: this.statusFilter || undefined,
           keyword: this.keyword || undefined
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
         })
         if (res.code === 200) {
           this.list = res.data && res.data.records || res.data && res.data.list || []

+ 15 - 3
cfc-web/src/views/admin/supply-hierarchy/ChangeRequest.vue

@@ -22,6 +22,9 @@
                 </el-form>
             </div>
             
+            <div class="table-scroll-wrap">
+
+            
             <el-table :data="requests" stripe :max-height="tableHeight">
                 <el-table-column prop="id" label="ID" width="60"></el-table-column>
                 <el-table-column prop="systemId" label="体系ID" width="80"></el-table-column>
@@ -39,8 +42,8 @@
                         </el-tag>
                     </template>
                 </el-table-column>
-                <el-table-column prop="createdAt" label="申请时间" width="160">
-                    <template slot-scope="scope">{{ scope.row.createdAt | parseTime }}</template>
+                <el-table-column prop="createdAt" label="申请时间" width="160" sortable="custom" :sort-orders="['ascending','descending']"
+                    <template slot-scope="scope" sortable="custom" :sort-orders="['ascending','descending']"{{ scope.row.createdAt | parseTime }}</template>
                 </el-table-column>
                 <el-table-column label="操作" width="180">
                     <template slot-scope="scope">
@@ -49,7 +52,8 @@
                     </template>
                 </el-table-column>
             </el-table>
-        </el-card>
+        </el-table>
+        </div></el-card>
         
         <!-- 拒绝理由对话框 -->
         <el-dialog title="拒绝申请" :visible.sync="rejectDialogVisible" width="400px">
@@ -83,6 +87,8 @@ export default {
             filterForm: {
                 systemId: null,
                 status: null
+      sortProp: '' ,
+      sortOrder: '' ,
             },
             rejectDialogVisible: false,
             rejectReason: '',
@@ -107,6 +113,12 @@ export default {
                 const res = await listAllSupplySystems()
                 if (res.data) {
                     this.supplySystems = res.data
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
                 }
             } catch (e) {
                 this.$message.error('加载供应体系列表失败')

+ 16 - 3
cfc-web/src/views/admin/supply-manage/List.vue

@@ -24,6 +24,9 @@
         </el-form-item>
     </el-form>
 
+      <div class="table-scroll-wrap">
+
+
       <el-table :data="suppliers" stripe v-loading="loading" :max-height="tableHeight">
         <el-table-column prop="id" label="ID" width="70"></el-table-column>
         <el-table-column prop="nickname" label="昵称" width="130"></el-table-column>
@@ -41,8 +44,8 @@
             </el-tag>
           </template>
         </el-table-column>
-        <el-table-column prop="createdAt" label="创建时间" width="170">
-          <template slot-scope="scope">{{ scope.row.createdAt | parseTime('{y}-{m}-{d} {h}:{i}') }}</template>
+        <el-table-column prop="createdAt" label="创建时间" width="170" sortable="custom" :sort-orders="['ascending','descending']"
+          <template slot-scope="scope" sortable="custom" :sort-orders="['ascending','descending']"{{ scope.row.createdAt | parseTime('{y}-{m}-{d} {h}:{i}') }}</template>
         </el-table-column>
         <el-table-column label="操作" width="220" fixed="right">
           <template slot-scope="scope">
@@ -53,7 +56,9 @@
         </el-table-column>
     </el-table>
 
-    <el-pagination
+    </el-table>
+
+    </div><el-pagination
         v-if="total > 0"
         style="margin-top: 15px; text-align: right"
         background
@@ -86,6 +91,8 @@ export default {
       page: 1,
       pageSize: 10,
       total: 0
+      sortProp: '' ,
+      sortOrder: '' ,
     }
   },
   created() {
@@ -100,6 +107,12 @@ export default {
           vendorStatus: this.searchVendorStatus,
           page: this.page,
           size: this.pageSize
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
         })
         if (res.data) {
           this.suppliers = res.data.list || []

+ 14 - 1
cfc-web/src/views/admin/supply-system/List.vue

@@ -23,6 +23,9 @@
         </el-form-item>
       </el-form>
 
+      <div class="table-scroll-wrap">
+
+
       <el-table :data="systems" stripe v-loading="loading" :max-height="tableHeight">
         <el-table-column prop="id" label="ID" width="80"></el-table-column>
         <el-table-column prop="name" label="体系名称" min-width="150"></el-table-column>
@@ -51,7 +54,9 @@
         </el-table-column>
       </el-table>
 
-      <el-pagination
+      </el-table>
+
+      </div><el-pagination
         v-if="total > 0"
         style="margin-top: 15px; text-align: right"
         background
@@ -84,6 +89,8 @@ export default {
       page: 1,
       pageSize: 10,
       total: 0
+      sortProp: '' ,
+      sortOrder: '' ,
     }
   },
   created() {
@@ -98,6 +105,12 @@ export default {
           status: this.searchStatus,
           page: this.page,
           size: this.pageSize
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
         })
         if (res.data) {
           this.systems = res.data.records || res.data.list || []

+ 16 - 3
cfc-web/src/views/health/HealthReportAudit.vue

@@ -4,6 +4,9 @@
       <h2 class="admin-page-title">报告草稿审核</h2>
     </div>
 
+    <div class="table-scroll-wrap">
+
+
     <el-table :data="list" v-loading="loading" border stripe :max-height="tableHeight">
       <el-table-column prop="id" label="草稿ID" width="80" />
       <el-table-column label="上传用户" width="140">
@@ -16,8 +19,8 @@
           <el-tag type="warning">待审核</el-tag>
         </template>
       </el-table-column>
-      <el-table-column label="上传时间" width="160">
-        <template slot-scope="{ row }">{{ formatTime(row.createdAt) }}</template>
+      <el-table-column label="上传时间" width="160" sortable="custom" :sort-orders="['ascending','descending']"
+        <template slot-scope="{ row }" sortable="custom" :sort-orders="['ascending','descending']"{{ formatTime(row.createdAt) }}</template>
       </el-table-column>
       <el-table-column label="过期时间" width="160">
         <template slot-scope="{ row }">{{ formatTime(row.expireAt) }}</template>
@@ -31,7 +34,9 @@
       </el-table-column>
     </el-table>
 
-    <el-pagination
+    </el-table>
+
+    </div><el-pagination
       v-if="total > size"
       @current-change="onPageChange"
       :current-page="page"
@@ -124,6 +129,8 @@ export default {
       detailVisible: false,
       detail: null,
       activeTab: 'indicators'
+      sortProp: '' ,
+      sortOrder: '' ,
     }
   },
   computed: {
@@ -158,6 +165,12 @@ export default {
         const axios = this.$axios || (await import('axios')).default
         const res = await axios.post(baseURL + '/api/health/report/audit/pending', {
           page: this.page, size: this.size
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
         }, {
           headers: { Authorization: 'Bearer ' + token }
         })

+ 31 - 4
cfc-web/src/views/nutritionist/HealthCheckins.vue

@@ -2,14 +2,28 @@
   <div class="health-checkins page-container">
     <el-card>
       <div slot="header"><span>健康打卡</span></div>
+      <div class="table-scroll-wrap">
+
       <el-table :data="checkins" v-loading="loading" border stripe :max-height="tableHeight">
         <el-table-column prop="id" label="ID" width="80" />
         <el-table-column prop="childName" label="孩子" width="120" />
         <el-table-column prop="checkinType" label="打卡类型" width="120" />
         <el-table-column prop="note" label="备注" />
-        <el-table-column prop="createdAt" label="打卡时间" width="180" />
-      </el-table>
-    </el-card>
+        <el-table-column prop="createdAt" label="打卡时间" width="180" / sortable="custom" :sort-orders="['ascending','descending']"
+      </el-table sortable="custom" :sort-orders="['ascending','descending']"
+    </el-table>
+
+    <el-pagination
+      :current-page="page"
+      :page-size="pageSize"
+      :total="total"
+      :page-sizes="[10, 20, 50, 100]"
+      layout="total, sizes, prev, pager, next, jumper"
+      @size-change="handleSizeChange"
+      @current-change="handleCurrentChange"
+      style="margin-top: 16px; text-align: right;"
+    />
+    </div></el-card>
   </div>
 </template>
 
@@ -21,7 +35,14 @@ export default {
       return window.innerHeight - 280
     }
   },
-  data() { return { loading: false, checkins: [] } },
+  data() { return { loading: false, checkins: []
+      sortProp: '' ,
+      sortOrder: '' ,
+      page: 1,
+      pageSize: 20,
+      total: 0,
+      sortProp: '',
+      sortOrder: '' , } },
   created() { this.loadCheckins() },
   methods: {
     async loadCheckins() {
@@ -32,6 +53,12 @@ export default {
         const axios = this.$axios || (await import('axios')).default
         const res = await axios.post(baseURL + '/api/health/checkin/list', {}, {
           headers: { Authorization: 'Bearer ' + token }
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
         })
         this.checkins = res.data?.data || []
       } catch (e) {

+ 30 - 3
cfc-web/src/views/nutritionist/HealthIndicators.vue

@@ -2,6 +2,8 @@
   <div class="health-indicators page-container">
     <el-card>
       <div slot="header"><span>健康指标</span></div>
+      <div class="table-scroll-wrap">
+
       <el-table :data="indicators" v-loading="loading" border stripe :max-height="tableHeight" :cell-style="cellStyle">
         <el-table-column prop="id" label="ID" width="80" />
         <el-table-column prop="childName" label="孩子" width="120" />
@@ -31,9 +33,21 @@
             <span v-else style="color: #999; font-size: 12px">—</span>
           </template>
         </el-table-column>
-        <el-table-column prop="recordedAt" label="记录时间" width="180" />
+        <el-table-column prop="recordedAt" label="记录时间" width="180" / sortable="custom" :sort-orders="['ascending','descending']"
       </el-table>
-    </el-card>
+
+    <el-pagination
+      :current-page="page"
+      :page-size="pageSize"
+      :total="total"
+      :page-sizes="[10, 20, 50, 100]"
+      layout="total, sizes, prev, pager, next, jumper"
+      @size-change="handleSizeChange"
+      @current-change="handleCurrentChange"
+      style="margin-top: 16px; text-align: right;"
+    />
+    </el-table>
+    </div></el-card>
   </div>
 </template>
 
@@ -45,7 +59,14 @@ export default {
       return window.innerHeight - 280
     }
   },
-  data() { return { loading: false, indicators: [] } },
+  data() { return { loading: false, indicators: []
+      sortProp: '' ,
+      sortOrder: '' ,
+      page: 1,
+      pageSize: 20,
+      total: 0,
+      sortProp: '',
+      sortOrder: '' , } },
   created() { this.loadIndicators() },
   methods: {
     async loadIndicators() {
@@ -56,6 +77,12 @@ export default {
         const axios = this.$axios || (await import('axios')).default
         const res = await axios.post(baseURL + '/api/health/indicator/list', {}, {
           headers: { Authorization: 'Bearer ' + token }
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
         })
         this.indicators = res.data?.data || []
       } catch (e) {

+ 13 - 2
cfc-web/src/views/nutritionist/HealthReports.vue

@@ -4,6 +4,8 @@
       <div slot="header">
         <span>健康报告</span>
       </div>
+      <div class="table-scroll-wrap">
+
       <el-table :data="reports" v-loading="loading" border stripe :max-height="tableHeight">
         <el-table-column prop="id" label="ID" width="70" />
         <el-table-column prop="childName" label="孩子" width="100" />
@@ -33,7 +35,8 @@
           </template>
         </el-table-column>
       </el-table>
-      <el-pagination
+      </el-table>
+      </div><el-pagination
         v-if="total > size"
         @current-change="onPageChange"
         :current-page="page"
@@ -55,7 +58,9 @@ export default {
     }
   },
   data() {
-    return { loading: false, reports: [], page: 1, size: 20, total: 0 }
+    return { loading: false, reports: [], page: 1, size: 20, total: 0
+      sortProp: '' ,
+      sortOrder: '' , }
   },
   created() { this.loadReports() },
   methods: {
@@ -67,6 +72,12 @@ export default {
         const axios = this.$axios || (await import('axios')).default
         const res = await axios.post(baseURL + '/api/health/report/admin-list', {
           page: this.page, size: this.size
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
         }, {
           headers: { Authorization: 'Bearer ' + token }
         })

+ 26 - 1
cfc-web/src/views/nutritionist/NutritionMappings.vue

@@ -5,6 +5,8 @@
         <span>营养素映射管理</span>
         <el-button type="primary" size="mini" style="float:right" @click="openCreateDialog">新增映射</el-button>
       </div>
+      <div class="table-scroll-wrap">
+
       <el-table :max-height="tableHeight" :data="mappings" v-loading="loading" border stripe>
         <el-table-column prop="id" label="ID" width="60" />
         <el-table-column prop="indicatorNamePattern" label="指标名模式" width="180" />
@@ -27,7 +29,19 @@
           </template>
         </el-table-column>
       </el-table>
-    </el-card>
+
+    <el-pagination
+      :current-page="page"
+      :page-size="pageSize"
+      :total="total"
+      :page-sizes="[10, 20, 50, 100]"
+      layout="total, sizes, prev, pager, next, jumper"
+      @size-change="handleSizeChange"
+      @current-change="handleCurrentChange"
+      style="margin-top: 16px; text-align: right;"
+    />
+    </el-table>
+    </div></el-card>
 
     <el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="600px">
       <el-form :model="form" label-width="120px">
@@ -84,6 +98,11 @@ export default {
         excessSymptoms: '',
         foodSuggestions: '',
         sortOrder: 10
+      page: 1,
+      pageSize: 20,
+      total: 0,
+      sortProp: '',
+      sortOrder: '' ,
       }
     }
   },
@@ -99,6 +118,12 @@ export default {
         const axios = this.$axios || (await import('axios')).default
         const res = await axios.post(baseURL + '/api/health/nutrition/mappings', {}, {
           headers: { Authorization: 'Bearer ' + token }
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
         })
         this.mappings = res.data?.data || []
       } catch (e) {

+ 30 - 3
cfc-web/src/views/service/ServiceRoleApplications.vue

@@ -4,6 +4,8 @@
       <div slot="header">
         <span>服务角色申请审核</span>
       </div>
+      <div class="table-scroll-wrap">
+
       <el-table :data="applications" v-loading="loading" style="width: 100%">
         <el-table-column prop="id" label="ID" width="60" />
         <el-table-column label="用户" width="120">
@@ -47,8 +49,8 @@
             {{ row.province }}{{ row.city }}{{ row.district }}{{ row.street }} {{ row.addressDetail }}
           </template>
         </el-table-column>
-        <el-table-column prop="createdAt" label="申请时间" width="160" />
-        <el-table-column label="状态" width="80">
+        <el-table-column prop="createdAt" label="申请时间" width="160" / sortable="custom" :sort-orders="['ascending','descending']"
+        <el-table-column label="状态" width="80" sortable="custom" :sort-orders="['ascending','descending']"
           <template slot-scope="{ row }">
             <el-tag :type="statusType(row.status)">{{ statusText(row.status) }}</el-tag>
           </template>
@@ -60,7 +62,19 @@
           </template>
         </el-table-column>
       </el-table>
-    </el-card>
+
+    <el-pagination
+      :current-page="page"
+      :page-size="pageSize"
+      :total="total"
+      :page-sizes="[10, 20, 50, 100]"
+      layout="total, sizes, prev, pager, next, jumper"
+      @size-change="handleSizeChange"
+      @current-change="handleCurrentChange"
+      style="margin-top: 16px; text-align: right;"
+    />
+    </el-table>
+    </div></el-card>
 
     <el-dialog title="驳回申请" :visible.sync="rejectDialogVisible" width="400px">
       <el-form>
@@ -98,6 +112,13 @@ export default {
         vendor: '服务商',
         article_admin: '文章管理员',
         activity_provider: '活动提供商'
+      sortProp: '' ,
+      sortOrder: '' ,
+      page: 1,
+      pageSize: 20,
+      total: 0,
+      sortProp: '',
+      sortOrder: '' ,
       }
     }
   },
@@ -111,6 +132,12 @@ export default {
         const res = await request({ url: '/api/service-role/admin/list', method: 'post', data: { status: 0 } })
         this.applications = res.data || []
         await this.resolveUserNames(this.applications, 'userId')
+    handleSortChange({ prop, order }) {
+      this.sortProp = prop
+      this.sortOrder = order
+      this.page = 1
+      this.fetchList && this.fetchList()
+    },
       } catch (e) {
         this.$message.error('获取列表失败')
       } finally {

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini