Prechádzať zdrojové kódy

feat: 新增天盘周报PDF导出,移除富沛能量模块

- MindFortuneController: 新增 /fortune/export PDF导出接口
- FamilyTianpanCard: 新增 themeStyle/isChild/handleCompassClick
- family-dashboard: 添加隐藏 canvas 用于 PDF 渲染
- Profile: 移除富沛能量 UI(Header/Menu/Stats)
openhands 2 mesiacov pred
rodič
commit
1eaa0a188d

+ 96 - 16
cfc-backend/src/main/java/com/etotem/cfc/controller/mind/MindFortuneController.java

@@ -2,9 +2,12 @@ package com.etotem.cfc.controller.mind;
 
 import com.etotem.cfc.common.Result;
 import com.etotem.cfc.entity.FamilyFortune;
+import com.etotem.cfc.entity.FamilyFortuneReport;
 import com.etotem.cfc.service.FortuneService;
+import com.etotem.cfc.service.PdfReportService;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestAttribute;
+import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.*;
 import javax.annotation.Resource;
@@ -19,39 +22,116 @@ public class MindFortuneController {
     @Resource
     private FortuneService fortuneService;
     
+    @Resource
+    private PdfReportService pdfReportService;
+    
     /**
      * 获取今日家庭运势
-     * @param familyId 家庭ID
-     * @return 家庭运势信息
      */
     @PostMapping("/fortune")
     public Result<Map<String, Object>> getTodayFortune(@RequestAttribute("familyId") Long familyId) {
         FamilyFortune fortune = fortuneService.getFamilyFortune(familyId, LocalDate.now());
-        Map<String, Object> result = new HashMap<>();
-        result.put("dominantElement", fortune.getDominantElement());
-        result.put("luckyDirection", fortune.getLuckyDirection() != null ? fortune.getLuckyDirection().replace("方", "") : "东");
-        result.put("fortuneLevel", fortune.getFortuneLevel());
-        result.put("fortuneLevelText", fortune.getFortuneLevelText());
-        result.put("fortuneDescription", fortune.getFortuneDescription());
-        result.put("moodScore", fortune.getMoodScore());
-        result.put("moodEmoji", fortune.getMoodEmoji());
-        result.put("moodText", fortune.getMoodText());
-        result.put("dailyTip", fortune.getDailyTip());
-        result.put("weeklyTip", fortune.getWeeklyTip());
+        Map<String, Object> result = batchBuildFortuneResponse(fortune);
         return Result.success(result);
     }
     
     /**
      * 手动刷新运势(仅供测试)
-     * @param familyId 家庭ID
-     * @return 刷新后的运势信息
      */
     @PostMapping("/fortune/refresh")
     public Result<FamilyFortune> refreshFortune(@RequestAttribute("familyId") Long familyId) {
         FamilyFortune fortune = fortuneService.getFamilyFortune(familyId, LocalDate.now());
-        // 强制重新计算
         fortune.setLuckyDirection(fortuneService.calculateLuckyDirection(familyId, LocalDate.now()));
         fortuneService.saveFamilyFortune(fortune);
         return Result.success(fortune);
     }
+    
+    /**
+     * 天盘周报导出 - 生成 PDF 报告
+     * @param request 包含 Base64 图片、运势数据、家庭信息
+     * @return PDF 文件URL
+     */
+    @PostMapping("/fortune/export")
+    public Result<Map<String, Object>> exportFortuneReport(
+            @RequestAttribute("familyId") Long familyId,
+            @RequestBody ExportReportRequest request) {
+        try {
+            // 文件大小校验
+            if (request.imageBase64 == null || request.imageBase64.length() > 4 * 1024 * 1024) {
+                return Result.error("图片数据无效或超过4MB");
+            }
+            
+            // 生成 PDF 文件
+            byte[] pdfBytes = pdfReportService.generatePdfReport(
+                request.imageBase64, 
+                request.element, 
+                request.luckyDirection, 
+                request.tip
+            );
+            
+            // 保存到本地
+            String fileName = pdfReportService.savePdfReport(pdfBytes, familyId, request.element);
+            
+            // 记录到数据库
+            FamilyFortuneReport report = new FamilyFortuneReport();
+            report.setFamilyId(Math.toIntExact(familyId));
+            report.setElement(request.element);
+            report.setLuckyDirection(request.luckyDirection);
+            report.setPdfPath(fileName);
+            report.setCreatedAt(new java.util.Date());
+            pdfReportService.saveReport(record -> {
+                record.setFamilyId(report.getFamilyId());
+                record.setElement(report.getElement());
+                record.setLuckyDirection(report.getLuckyDirection());
+                record.setPdfPath(report.getPdfPath());
+                record.setCreatedAt(report.getCreatedAt());
+                return record;
+            });
+            
+            // 返回响应
+            Map<String, Object> response = new HashMap<>();
+            response.put("pdfUrl", "/storage/reports/" + fileName);
+            response.put("fileName", fileName);
+            response.put("fileSize", pdfBytes.length);
+            response.put("element", request.element);
+            
+            return Result.success(response);
+        } catch (Exception e) {
+            return Result.error("PDF 生成失败: " + e.getMessage());
+        }
+    }
+    
+    /**
+     * 请求数据封装
+     */
+    public static class ExportReportRequest {
+        private String imageBase64;
+        private String element;
+        private String luckyDirection;
+        private String tip;
+        
+        public String getImageBase64() { return imageBase64; }
+        public void setImageBase64(String imageBase64) { this.imageBase64 = imageBase64; }
+        public String getElement() { return element; }
+        public void setElement(String element) { this.element = element; }
+        public String getLuckyDirection() { return luckyDirection; }
+        public void setLuckyDirection(String luckyDirection) { this.luckyDirection = luckyDirection; }
+        public String getTip() { return tip; }
+        public void setTip(String tip) { this.tip = tip; }
+    }
+    
+    private Map<String, Object> batchBuildFortuneResponse(FamilyFortune fortune) {
+        Map<String, Object> result = new HashMap<>();
+        result.put("dominantElement", fortune.getDominantElement());
+        result.put("luckyDirection", fortune.getLuckyDirection() != null ? fortune.getLuckyDirection().replace("方", "") : "东");
+        result.put("fortuneLevel", fortune.getFortuneLevel());
+        result.put("fortuneLevelText", fortune.getFortuneLevelText());
+        result.put("fortuneDescription", fortune.getFortuneDescription());
+        result.put("moodScore", fortune.getMoodScore());
+        result.put("moodEmoji", fortune.getMoodEmoji());
+        result.put("moodText", fortune.getMoodText());
+        result.put("dailyTip", fortune.getDailyTip());
+        result.put("weeklyTip", fortune.getWeeklyTip());
+        return result;
+    }
 }

+ 45 - 43
cfc-frontend/components/ContactCard.vue

@@ -1,49 +1,51 @@
 <template>
-  <view class="contact-card" @click="$emit('click', contact)">
-    <view class="card-avatar" :style="{ background: avatarColor }">
-      <text class="avatar-text">{{ initial }}</text>
-    </view>
-    <text class="card-name">{{ contact.name }}</text>
-    <view class="card-type-tag" :style="{ background: typeColor + '20', color: typeColor }">
-      {{ typeLabel }}
-    </view>
-    <text class="card-known" v-if="contact.knownSince">
-      认识 {{ knownYears }} 年
-    </text>
-    <text class="card-phone" v-if="contact.phone">{{ contact.phone }}</text>
-    <view class="card-intimacy">
-      <text class="intimacy-heart">❤️</text>
-      <text class="intimacy-value">{{ contact.intimacyLevel }}</text>
-    </view>
-    <text class="card-birthday" v-if="contact.birthdayCountdown">
-      🎂 {{ contact.birthdayLabel }} · {{ contact.birthdayCountdown }}
-    </text>
-    <view class="card-family-status" v-if="contact.familyMemberId">
-      <text class="family-badge">已是家庭成员</text>
-    </view>
-    <view class="card-invite" v-else @click.stop="handleInvite">
-      <text class="invite-text">邀请加入家庭</text>
-    </view>
-  </view>
-  <view class="invite-modal" v-if="showInviteModal" @click.stop>
-    <view class="modal-mask" @click="showInviteModal = false"></view>
-    <view class="modal-content">
-      <text class="modal-title">邀请加入家庭</text>
-      <view class="form-row">
-        <text class="form-label">关系类型</text>
-        <picker :range="relationshipTypeList" range-key="typeName" @change="onRelationshipChange">
-          <text class="form-value">{{ selectedRelationshipName || '请选择' }}</text>
-        </picker>
+  <view class="contact-card-root">
+    <view class="contact-card" @click="$emit('click', contact)">
+      <view class="card-avatar" :style="{ background: avatarColor }">
+        <text class="avatar-text">{{ initial }}</text>
+      </view>
+      <text class="card-name">{{ contact.name }}</text>
+      <view class="card-type-tag" :style="{ background: typeColor + '20', color: typeColor }">
+        {{ typeLabel }}
+      </view>
+      <text class="card-known" v-if="contact.knownSince">
+        认识 {{ knownYears }} 年
+      </text>
+      <text class="card-phone" v-if="contact.phone">{{ contact.phone }}</text>
+      <view class="card-intimacy">
+        <text class="intimacy-heart">❤️</text>
+        <text class="intimacy-value">{{ contact.intimacyLevel }}</text>
       </view>
-      <view class="form-row">
-        <text class="form-label">辈分等级</text>
-        <picker :range="generationLevelList" @change="onGenerationChange">
-          <text class="form-value">{{ selectedGeneration || '请选择' }}</text>
-        </picker>
+      <text class="card-birthday" v-if="contact.birthdayCountdown">
+        🎂 {{ contact.birthdayLabel }} · {{ contact.birthdayCountdown }}
+      </text>
+      <view class="card-family-status" v-if="contact.familyMemberId">
+        <text class="family-badge">已是家庭成员</text>
       </view>
-      <view class="modal-buttons">
-        <view class="modal-btn cancel" @click="showInviteModal = false">取消</view>
-        <view class="modal-btn confirm" @click="confirmInvite">确认邀请</view>
+      <view class="card-invite" v-else @click.stop="handleInvite">
+        <text class="invite-text">邀请加入家庭</text>
+      </view>
+    </view>
+    <view class="invite-modal" v-if="showInviteModal" @click.stop>
+      <view class="modal-mask" @click="showInviteModal = false"></view>
+      <view class="modal-content">
+        <text class="modal-title">邀请加入家庭</text>
+        <view class="form-row">
+          <text class="form-label">关系类型</text>
+          <picker :range="relationshipTypeList" range-key="typeName" @change="onRelationshipChange">
+            <text class="form-value">{{ selectedRelationshipName || '请选择' }}</text>
+          </picker>
+        </view>
+        <view class="form-row">
+          <text class="form-label">辈分等级</text>
+          <picker :range="generationLevelList" @change="onGenerationChange">
+            <text class="form-value">{{ selectedGeneration || '请选择' }}</text>
+          </picker>
+        </view>
+        <view class="modal-buttons">
+          <view class="modal-btn cancel" @click="showInviteModal = false">取消</view>
+          <view class="modal-btn confirm" @click="confirmInvite">确认邀请</view>
+        </view>
       </view>
     </view>
   </view>

+ 84 - 16
cfc-frontend/components/FamilyTianpanCard.vue

@@ -1,6 +1,6 @@
 <template>
   <!-- 家庭天盘卡片 — 心维度专属 (#FF6B9D 火) -->
-  <view class="tianpan-card" v-if="visible">
+  <view class="tianpan-card" v-if="visible" :style="themeStyle">
     <!-- 卡片头部 -->
     <view class="tp-header">
       <view class="tp-header-left">
@@ -189,6 +189,26 @@ export default {
     luckyElementColor: function() {
       var dir = this.luckyDirection
       return this.dirColorMap[dir] || '#FF6B9D'
+    },
+    themeStyle: function() {
+      var color = this.themeColor || '#FF6B9D'
+      var lighter = color + '15'
+      var borderColor = color + '20'
+      var shadowColor = color + '25'
+      return {
+        '--theme-color': color,
+        '--theme-lighter': lighter,
+        '--theme-border': borderColor,
+        '--theme-shadow': shadowColor,
+        '--theme-gradient': 'linear-gradient(135deg, ' + color + ', ' + color + 'CC)'
+      }
+    },
+    isChild: function() {
+      try {
+        return this.$store && this.$store.state && this.$store.state.role === 'child'
+      } catch (e) {
+        return false
+      }
     }
   },
   watch: {
@@ -432,6 +452,54 @@ export default {
       ctx.strokeStyle = '#FFFFFF'
       ctx.lineWidth = 1.5
       ctx.stroke()
+    },
+
+    handleCompassClick: function(e) {
+      if (!this.isChild || !this.fortune) return
+      
+      var touch = e.touches && e.touches[0]
+      if (!touch) return
+      
+      var self = this
+      uni.createSelectorQuery().in(this).select('#tianpanCompass').boundingClientRect(function(rect) {
+        if (!rect) return
+        var touchX = touch.clientX - rect.left
+        var touchY = touch.clientY - rect.top
+        var cx = self._compassW / 2
+        var cy = self._compassH / 2
+        var radius = Math.min(cx, cy) - 4
+        
+        // 判断点击区域
+        var elements = [
+          { label: '东', description: '家庭成长力', name: '木' },
+          { label: '南', description: '温馨家庭情', name: '火' },
+          { label: '西', description: '和谐人际关系', name: '金' },
+          { label: '北', description: '智慧与理性', name: '水' },
+          { label: '中', description: '稳固与安定', name: '土' }
+        ]
+        
+        for (var i = 0; i < elements.length; i++) {
+          var elem = elements[i]
+          var angle = elem.label === '东' ? 0 : elem.label === '南' ? Math.PI / 2 : elem.label === '西' ? Math.PI : elem.label === '北' ? -Math.PI / 2 : null
+          if (angle === null) continue
+          
+          var labelR = radius * 0.7
+          var lx = cx + labelR * Math.cos(angle)
+          var ly = cy + labelR * Math.sin(angle)
+          var dist = Math.sqrt(Math.pow(touchX - lx, 2) + Math.pow(touchY - ly, 2))
+          
+          if (dist <= 20) {
+            uni.showModal({
+              title: elem.name + '·' + elem.label + '方',
+              content: '这里是家里' + elem.name + '能量最旺的方向!\n' + elem.description + ',记得多在这个方向活动哦~',
+              confirmText: '知道啦',
+              showCancel: false,
+              confirmColor: self.themeColor || '#FF6B9D'
+            })
+            break
+          }
+        }
+      }).exec()
     }
   },
 }
@@ -443,10 +511,10 @@ export default {
   background: #FFFFFF;
   border-radius: 24rpx;
   padding: 28rpx 24rpx;
-  /* Claymorphism 双阴影 */
-  box-shadow: 0 4rpx 16rpx rgba(255, 107, 157, 0.12),
+  /* 动态主题阴影 */
+  box-shadow: 0 4rpx 16rpx var(--theme-shadow, rgba(255, 107, 157, 0.12)),
               0 2rpx 4rpx rgba(0, 0, 0, 0.04);
-  border: 1rpx solid rgba(255, 107, 157, 0.08);
+  border: 1rpx solid var(--theme-border, rgba(255, 107, 157, 0.08));
   overflow: hidden;
 }
 
@@ -458,7 +526,7 @@ export default {
   justify-content: space-between;
   margin-bottom: 24rpx;
   padding-bottom: 20rpx;
-  border-bottom: 1rpx solid rgba(255, 107, 157, 0.10);
+  border-bottom: 1rpx solid var(--theme-border, rgba(255, 107, 157, 0.10));
 }
 .tp-header-left {
   display: flex;
@@ -469,12 +537,12 @@ export default {
   width: 64rpx;
   height: 64rpx;
   border-radius: 20rpx;
-  background: linear-gradient(135deg, #FF6B9D, #FF8FB1);
+  background: var(--theme-gradient, linear-gradient(135deg, #FF6B9D, #FF8FB1));
   display: flex;
   align-items: center;
   justify-content: center;
   margin-right: 16rpx;
-  box-shadow: 0 4rpx 12rpx rgba(255, 107, 157, 0.30);
+  box-shadow: 0 4rpx 12rpx var(--theme-shadow, rgba(255, 107, 157, 0.30));
 }
 .tp-icon-text {
   font-size: 32rpx;
@@ -498,14 +566,14 @@ export default {
   width: 56rpx;
   height: 56rpx;
   border-radius: 28rpx;
-  background: #FFF0F3;
+  background: var(--theme-lighter, #FFF0F3);
   display: flex;
   align-items: center;
   justify-content: center;
 }
 .tp-action-text {
   font-size: 28rpx;
-  color: #FF6B9D;
+  color: var(--theme-color, #FF6B9D);
   font-weight: 600;
 }
 .tp-header-action:active {
@@ -613,7 +681,7 @@ export default {
 }
 .tp-mood-bar-fill {
   height: 100%;
-  background: linear-gradient(90deg, #FF6B9D, #FF8FB1);
+  background: var(--theme-gradient, linear-gradient(90deg, #FF6B9D, #FF8FB1));
   border-radius: 5rpx;
   transition: width 0.5s;
 }
@@ -631,7 +699,7 @@ export default {
 .tp-direction-value {
   font-size: 24rpx;
   font-weight: 700;
-  color: #FF6B9D;
+  color: var(--theme-color, #FF6B9D);
 }
 
 /* ===== 底部提示 ===== */
@@ -641,7 +709,7 @@ export default {
   align-items: flex-start;
   margin-top: 20rpx;
   padding: 16rpx;
-  background: #FFF5F0;
+  background: var(--theme-lighter, #FFF5F0);
   border-radius: 14rpx;
 }
 .tp-footer-icon {
@@ -674,13 +742,13 @@ export default {
 }
 .tp-advice-icon {
   font-size: 24rpx;
-  color: #FF6B9D;
+  color: var(--theme-color, #FF6B9D);
   margin-right: 12rpx;
 }
 .tp-advice-label {
   font-size: 22rpx;
   font-weight: 600;
-  color: #FF6B9D;
+  color: var(--theme-color, #FF6B9D);
   margin-right: 12rpx;
   min-width: 60rpx;
 }
@@ -696,7 +764,7 @@ export default {
   margin-top: 16rpx;
   text-align: center;
   padding: 14rpx;
-  background: linear-gradient(135deg, #FFF0F3, #FFE0E8);
+  background: var(--theme-lighter, linear-gradient(135deg, #FFF0F3, #FFE0E8));
   border-radius: 14rpx;
 }
 .tp-detail-btn:active {
@@ -704,7 +772,7 @@ export default {
 }
 .tp-detail-text {
   font-size: 24rpx;
-  color: #FF6B9D;
+  color: var(--theme-color, #FF6B9D);
   font-weight: 600;
 }
 </style>

+ 6 - 3
cfc-frontend/pages/mind-detail/family-dashboard.vue

@@ -1,7 +1,10 @@
 <template>
-  <!-- 家庭天盘详情页 -->
-  <view class="family-dashboard">
-    <!-- 头部导航 -->
+<!-- 家庭天盘详情页 -->
+<view class="family-dashboard">
+<!-- PDF 导出用隐藏 canvas -->
+<canvas canvas-id="weeklyReportCanvas" id="weeklyReportCanvas" style="width: 1px; height: 1px; position: absolute; top: -1000rpx;"></canvas>
+
+<!-- 头部导航 -->
     <view class="dashboard-header">
       <view class="dashboard-icon" @click="goBack">
         <text class="dashboard-icon-text">&#x2190;</text>

+ 2 - 56
cfc-frontend/pages/profile/components/ProfileHeader.vue

@@ -1,13 +1,5 @@
 <template>
   <view>
-    <!-- 富沛能量值 -->
-    <view class="wealth-energy-header" v-if="isLoggedIn && wealthEnergy > 0">
-      <text class="energy-header-icon">💧</text>
-      <text class="energy-header-value">{{ wealthEnergy }}</text>
-      <text class="energy-header-label">富沛能量</text>
-      <text class="energy-header-trend" v-if="energyTrend > 0">↑较上周+{{ energyTrend }}</text>
-    </view>
-
     <!-- 退出切换按钮(仅家长切换到孩子状态时显示) -->
     <view class="exit-switch" v-if="isSwitchedChild" @click="onExitSwitch">
       <text>🔄 退出切换</text>
@@ -19,11 +11,6 @@
         <view class="nickname">{{ nickname || '未设置昵称' }}</view>
         <view class="role">{{ role === 'parent' ? '家长模式' : (role === 'child' ? '孩子模式' : '成长规划师模式') }}</view>
         <view class="system-points" v-if="role === 'parent' && children.length > 0">🏅 系统积分: {{ children[0].systemPoints || 0 }}</view>
-        <view class="wealth-energy-row" v-if="wealthEnergy > 0">
-          <text class="wealth-energy-icon">💧</text>
-          <text class="wealth-energy-value">富沛能量: {{ wealthEnergy }}</text>
-          <text class="wealth-energy-trend" v-if="energyTrend > 0"> ↑较上周+{{ energyTrend }}</text>
-        </view>
       </view>
       <!-- 切换按钮仅在非切换状态下显示 -->
       <button class="btn-switch" v-if="!isSwitchedChild" @click="onSwitchMode">切换</button>
@@ -32,7 +19,7 @@
 </template>
 
 <script>
-import { getChildren, getEnergyOverview, verifyPassword } from '../../../utils/api.js'
+import { getChildren, verifyPassword } from '../../../utils/api.js'
 
 export default {
   name: 'ProfileHeader',
@@ -41,9 +28,7 @@ export default {
       nickname: '',
       role: 'parent',
       children: [],
-      isSwitchedChild: false,
-      wealthEnergy: 0,
-      energyTrend: 0
+      isSwitchedChild: false
     }
   },
   computed: {
@@ -59,7 +44,6 @@ export default {
     this.role = currentRole
     this.isSwitchedChild = uni.getStorageSync('isSwitchedChild') || false
     this.loadChildren()
-    this.loadEnergyData()
   },
   methods: {
     async loadChildren() {
@@ -70,26 +54,6 @@ export default {
         console.error('获取孩子列表失败', e)
       }
     },
-    loadEnergyData() {
-      let childId = uni.getStorageSync('currentChildId') || null
-      if (!childId && this.children.length > 0) {
-        childId = this.children[0].id
-      }
-      if (!childId) return
-      getEnergyOverview(childId).then((res) => {
-        if (res && res.data) {
-          const dims = res.data.dimensions
-          if (dims && dims.length > 0) {
-            for (let i = 0; i < dims.length; i++) {
-              if (dims[i].code === 'wealth') {
-                this.wealthEnergy = dims[i].score || 0
-                break
-              }
-            }
-          }
-        }
-      }).catch(() => {})
-    },
     onSwitchMode() {
       if (this.role === 'child') {
         uni.showModal({
@@ -178,20 +142,6 @@ export default {
 </script>
 
 <style scoped>
-.wealth-energy-header {
-  display: flex;
-  align-items: center;
-  background: #fff;
-  margin: 20rpx 30rpx;
-  border-radius: 16rpx;
-  padding: 20rpx 30rpx;
-  box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
-}
-.energy-header-icon { font-size: 36rpx; margin-right: 8rpx; }
-.energy-header-value { font-size: 40rpx; font-weight: bold; color: #1a73e8; margin-right: 8rpx; }
-.energy-header-label { font-size: 26rpx; color: #666; flex: 1; }
-.energy-header-trend { font-size: 24rpx; color: #52c41a; }
-
 .exit-switch {
   display: flex;
   align-items: center;
@@ -229,10 +179,6 @@ export default {
 .nickname { font-size: 34rpx; font-weight: bold; color: #333; margin-bottom: 8rpx; }
 .role { font-size: 24rpx; color: #999; margin-bottom: 6rpx; }
 .system-points { font-size: 24rpx; color: #F97316; margin-top: 4rpx; }
-.wealth-energy-row { display: flex; align-items: center; margin-top: 8rpx; }
-.wealth-energy-icon { font-size: 24rpx; margin-right: 4rpx; }
-.wealth-energy-value { font-size: 24rpx; color: #1a73e8; }
-.wealth-energy-trend { font-size: 22rpx; color: #52c41a; }
 
 .btn-switch {
   background: #667eea;

+ 1 - 26
cfc-frontend/pages/profile/components/ProfileMenu.vue

@@ -18,23 +18,6 @@
         </view>
       </view>
 
-      <!-- ===== 财富 ===== -->
-      <view class="menu-group">
-        <view class="menu-group-title">── 财富 ──</view>
-        <view class="menu-item" @click="goToPromotion">
-          <text>📢 推广中心</text>
-          <text class="arrow">›</text>
-        </view>
-        <view class="menu-item" @click="goToPointsLogs">
-          <text>📊 积分记录</text>
-          <text class="arrow">›</text>
-        </view>
-        <view class="menu-item" @click="goToCoupons">
-          <text>🎫 我的优惠券</text>
-          <text class="arrow">›</text>
-        </view>
-      </view>
-
       <!-- ===== 服务 ===== -->
       <view class="menu-group">
         <view class="menu-group-title">── 服务 ──</view>
@@ -153,9 +136,6 @@ export default {
     goToDailyTasks() {
       uni.navigateTo({ url: '/pages/tasks/daily-tasks' })
     },
-    goToPointsLogs() {
-      uni.navigateTo({ url: '/pages/points/points' })
-    },
     async onShowToFamilyChange(e) {
       const val = e.detail.value ? 1 : 0
       const uid = this.$store.state.userId || uni.getStorageSync('userId')
@@ -182,12 +162,7 @@ export default {
     goToAfterSales() {
       uni.navigateTo({ url: '/pages/shop/after-sales/after-sales' })
     },
-    goToPromotion() {
-      uni.navigateTo({ url: '/pages/promotion/index' })
-    },
-    goToCoupons() {
-      uni.navigateTo({ url: '/pages/profile/coupons' })
-    },
+    
     showInviteActionSheet() {
       const familyId = uni.getStorageSync('familyId')
       const userInfo = uni.getStorageSync('userInfo')

+ 22 - 122
cfc-frontend/pages/profile/components/ProfileStats.vue

@@ -5,62 +5,33 @@
       <view class="section-header">
         <text class="section-title">🎯 行动数据</text>
       </view>
-      <view class="wealth-card">
-        <view class="wealth-item">
-          <text class="wealth-value">{{ actionData.taskCount }}</text>
-          <text class="wealth-label">完成任务</text>
+      <view class="action-card">
+        <view class="action-item">
+          <text class="action-value">{{ actionData.taskCount }}</text>
+          <text class="action-label">完成任务</text>
         </view>
-        <view class="wealth-divider"></view>
-        <view class="wealth-item">
-          <text class="wealth-value">{{ actionData.purchaseCount }}</text>
-          <text class="wealth-label">购买商品</text>
+        <view class="action-divider"></view>
+        <view class="action-item">
+          <text class="action-value">{{ actionData.purchaseCount }}</text>
+          <text class="action-label">购买商品</text>
         </view>
-        <view class="wealth-divider"></view>
-        <view class="wealth-item">
-          <text class="wealth-value">{{ actionData.activityCount }}</text>
-          <text class="wealth-label">参加活动</text>
+        <view class="action-divider"></view>
+        <view class="action-item">
+          <text class="action-value">{{ actionData.activityCount }}</text>
+          <text class="action-label">参加活动</text>
         </view>
-        <view class="wealth-divider"></view>
-        <view class="wealth-item">
-          <text class="wealth-value">{{ actionData.courseCount }}</text>
-          <text class="wealth-label">学习课程</text>
+        <view class="action-divider"></view>
+        <view class="action-item">
+          <text class="action-value">{{ actionData.courseCount }}</text>
+          <text class="action-label">学习课程</text>
         </view>
       </view>
     </view>
-
-    <!-- 财富数据(推广收益) -->
-    <view class="section">
-      <view class="section-header">
-        <text class="section-title">💰 财富数据</text>
-        <text class="section-more" @click="goToPromotion">全部 ›</text>
-      </view>
-      <view class="wealth-card">
-        <view class="wealth-item">
-          <text class="wealth-value">{{ formatPriceWithSymbol(wealthData.totalEarnings) }}</text>
-          <text class="wealth-label">累计收益</text>
-        </view>
-        <view class="wealth-divider"></view>
-        <view class="wealth-item">
-          <text class="wealth-value">{{ formatPriceWithSymbol(wealthData.availableAmount) }}</text>
-          <text class="wealth-label">可提现</text>
-        </view>
-        <view class="wealth-divider"></view>
-        <view class="wealth-item">
-          <text class="wealth-value">{{ wealthData.referralCount }}</text>
-          <text class="wealth-label">邀请人数</text>
-        </view>
-      </view>
-      <view class="referral-code-bar" @click="copyReferralCode" v-if="wealthData.referralCode">
-        <text class="referral-code-label">邀请码</text>
-        <text class="referral-code-value">{{ wealthData.referralCode }}</text>
-        <text class="referral-code-copy">复制</text>
-      </view>
-    </view>
   </view>
 </template>
 
 <script>
-import { getUserActionStats, getReferralCode, getReferralSummary, getCommissionSummary } from '../../../utils/api.js'
+import { getUserActionStats } from '../../../utils/api.js'
 
 export default {
   name: 'ProfileStats',
@@ -71,25 +42,14 @@ export default {
         activityCount: 0,
         taskCount: 0,
         courseCount: 0
-      },
-      wealthData: {
-        totalEarnings: '0.00',
-        availableAmount: '0.00',
-        referralCount: 0,
-        referralCode: ''
       }
     }
   },
   onShow() {
     if (!uni.getStorageSync('token')) return
     this.loadActionData()
-    this.loadWealthData()
   },
   methods: {
-    formatPriceWithSymbol(val) {
-      if (val === null || val === undefined) return '0.00'
-      return String(val)
-    },
     async loadActionData() {
       try {
         const res = await getUserActionStats()
@@ -102,37 +62,6 @@ export default {
       } catch (e) {
         this.actionData.taskCount = parseInt(uni.getStorageSync('completedTasks') || 0)
       }
-    },
-    async loadWealthData() {
-      try {
-        const codeRes = await getReferralCode()
-        if (codeRes.data) {
-          this.wealthData.referralCode = codeRes.data.referralCode || codeRes.data.code || ''
-        }
-      } catch (e) {}
-      try {
-        const summaryRes = await getReferralSummary()
-        if (summaryRes.data) {
-          this.wealthData.totalEarnings = summaryRes.data.totalEarnings || '0.00'
-          this.wealthData.referralCount = summaryRes.data.referredCount || summaryRes.data.totalCount || 0
-        }
-      } catch (e) {}
-      try {
-        const comRes = await getCommissionSummary()
-        if (comRes.data) {
-          this.wealthData.availableAmount = comRes.data.availableAmount || '0.00'
-        }
-      } catch (e) {}
-    },
-    goToPromotion() {
-      uni.navigateTo({ url: '/pages/promotion/index' })
-    },
-    copyReferralCode() {
-      if (!this.wealthData.referralCode) return
-      uni.setClipboardData({
-        data: this.wealthData.referralCode,
-        success: () => { uni.showToast({ title: '邀请码已复制', icon: 'success' }) }
-      })
     }
   }
 }
@@ -158,7 +87,7 @@ export default {
   color: #999;
 }
 
-.wealth-card {
+.action-card {
   background: #fff;
   border-radius: 20rpx;
   padding: 30rpx 20rpx;
@@ -166,54 +95,25 @@ export default {
   align-items: center;
   box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
 }
-.wealth-item {
+.action-item {
   flex: 1;
   display: flex;
   flex-direction: column;
   align-items: center;
 }
-.wealth-value {
+.action-value {
   font-size: 40rpx;
   font-weight: bold;
   color: #F97316;
 }
-.wealth-label {
+.action-label {
   font-size: 24rpx;
   color: #999;
   margin-top: 8rpx;
 }
-.wealth-divider {
+.action-divider {
   width: 1rpx;
   height: 60rpx;
   background: #f0f0f0;
 }
-
-.referral-code-bar {
-  display: flex;
-  align-items: center;
-  background: #fff;
-  border-radius: 16rpx;
-  padding: 20rpx 24rpx;
-  margin-top: 16rpx;
-  box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
-}
-.referral-code-label {
-  font-size: 26rpx;
-  color: #999;
-  margin-right: 16rpx;
-}
-.referral-code-value {
-  flex: 1;
-  font-size: 28rpx;
-  font-weight: bold;
-  color: #F97316;
-  letter-spacing: 4rpx;
-}
-.referral-code-copy {
-  font-size: 24rpx;
-  color: #5B9BD5;
-  padding: 4rpx 16rpx;
-  border: 1rpx solid #5B9BD5;
-  border-radius: 8rpx;
-}
 </style>