Sfoglia il codice sorgente

feat(cf): 小程序推广中心切换到 CF 分佣接口(团队规模/比例/流水/钱包)

Sisyphus 2 settimane fa
parent
commit
e26ab73969

+ 51 - 109
cfc-frontend/pages/promotion/commission.vue

@@ -4,63 +4,53 @@
     <view class="header-card">
       <view class="header-row">
         <view class="header-item">
-          <text class="header-label">累计佣金</text>
-          <text class="header-value orange" v-if="summary">{{ formatPriceWithSymbol(summary.totalCommission) }}</text>
-          <text class="header-value orange" v-else>{{ formatPriceWithSymbol(0) }}</text>
+          <text class="header-label">累计获取</text>
+          <text class="header-value orange" v-if="summary">{{ cfToYuan(summary.totalEarned) }}</text>
+          <text class="header-value orange" v-else>¥0.00</text>
         </view>
         <view class="header-item">
-          <text class="header-label">可提现</text>
-          <text class="header-value blue" v-if="summary">{{ formatPriceWithSymbol(summary.availableAmount) }}</text>
-          <text class="header-value blue" v-else>{{ formatPriceWithSymbol(0) }}</text>
+          <text class="header-label">可用余额</text>
+          <text class="header-value blue" v-if="summary">{{ cfToYuan(summary.available) }}</text>
+          <text class="header-value blue" v-else>¥0.00</text>
         </view>
         <view class="header-item">
-          <text class="header-label">待结算</text>
-          <text class="header-value green" v-if="summary">{{ formatPriceWithSymbol(summary.pendingAmount) }}</text>
-          <text class="header-value green" v-else>{{ formatPriceWithSymbol(0) }}</text>
+          <text class="header-label">冻结</text>
+          <text class="header-value green" v-if="summary">{{ cfToYuan(summary.frozen) }}</text>
+          <text class="header-value green" v-else>¥0.00</text>
         </view>
       </view>
     </view>
 
-    <!-- Tab 切换 -->
-    <view class="tab-bar">
-      <view class="tab-item" :class="currentTab === 'all' ? 'tab-active' : ''" @click="switchTab('all')">
-        <text class="tab-text">全部</text>
-      </view>
-      <view class="tab-item" :class="currentTab === 'settled' ? 'tab-active' : ''" @click="switchTab('settled')">
-        <text class="tab-text">已结算</text>
-      </view>
-      <view class="tab-item" :class="currentTab === 'pending' ? 'tab-active' : ''" @click="switchTab('pending')">
-        <text class="tab-text">待结算</text>
-      </view>
-    </view>
-
-    <!-- 佣金列表 -->
+    <!-- CF 流水列表 -->
     <view class="list-section">
       <view class="list-item" v-for="item in list" :key="item.id">
         <view class="item-left">
-          <text :class="['type-tag', item.commissionType === 'member' ? 'tag-orange' : 'tag-blue']">
-            {{ item.commissionType === 'member' ? '会员佣金' : '商品佣金' }}
+          <text :class="['type-tag', isPositive(item.type) ? 'tag-orange' : 'tag-blue']">
+            {{ getTypeText(item.type) }}
           </text>
-          <text class="item-desc">{{ getOrderDesc(item) }}</text>
+          <text class="item-desc">{{ item.remark || 'CF值变动' }}</text>
         </view>
         <view class="item-right">
-          <text class="item-amount">+{{ formatPriceWithSymbol(item.commissionAmount) }}</text>
-          <text class="item-status" :class="item.status === 'settled' ? 'status-settled' : item.status === 'cancelled' ? 'status-cancelled' : 'status-pending'">{{ getStatusText(item.status) }}</text>
+          <text :class="['item-amount', isPositive(item.type) ? 'amount-positive' : 'amount-negative']">{{ getAmountText(item) }}</text>
+          <text class="item-status">{{ getRefText(item.refType) }}</text>
           <text class="item-time">{{ formatTime(item.createdAt) }}</text>
         </view>
       </view>
       <view class="empty-state" v-if="!loading && list.length === 0">
-        <text>暂无佣金记录</text>
+        <text>暂无 CF 流水记录</text>
       </view>
       <view class="loading-state" v-if="loading">
         <text>加载中...</text>
       </view>
+      <view class="loadmore-state" v-if="!loading && list.length > 0 && !hasMore">
+        <text>没有更多了</text>
+      </view>
     </view>
   </view>
 </template>
 
 <script>
-import { getCommissionList, getCommissionSummary, getCommissionStats } from '../../utils/api.js'
+import { getCfSummary, getCfList } from '../../utils/api.js'
 import { parseDate } from '../../utils/format.js'
 
 export default {
@@ -70,8 +60,7 @@ export default {
       summary: {},
       page: 1,
       hasMore: true,
-      loading: false,
-      currentTab: 'all'
+      loading: false
     }
   },
   onLoad() {
@@ -85,35 +74,20 @@ export default {
     }
   },
   methods: {
-    switchTab(tab) {
-      if (this.currentTab === tab) return
-      this.currentTab = tab
-      this.page = 1
-      this.list = []
-      this.loadList()
-    },
     async loadSummary() {
       try {
-        const res = await getCommissionSummary()
+        var res = await getCfSummary()
         if (res.data) {
           this.summary = res.data
         }
       } catch (e) {
-        console.error('获取佣金总览失败', e)
+        console.error('获取 CF 总览失败', e)
       }
     },
     async loadList() {
       this.loading = true
       try {
-        var page = this.page
-        var size = 20
-        var status = ''
-        if (this.currentTab === 'settled') {
-          status = 'settled'
-        } else if (this.currentTab === 'pending') {
-          status = 'pending'
-        }
-        const res = await getCommissionList(page, size, status)
+        var res = await getCfList(this.page, 20)
         if (res.data && res.data.records) {
           this.list = this.page === 1 ? res.data.records : this.list.concat(res.data.records)
           this.hasMore = res.data.records.length >= 20
@@ -121,27 +95,30 @@ export default {
           this.hasMore = false
         }
       } catch (e) {
-        console.error('获取佣金明细失败', e)
+        console.error('获取 CF 流水失败', e)
       }
       this.loading = false
     },
-    getOrderDesc(item) {
-      var typeMap = {
-        membership: '会员',
-        product: '商品',
-        assessment: '测评',
-        package: '任务模板'
-      }
-      return (typeMap[item.orderType] || item.orderType) + '订单 #' + item.orderId
+    isPositive(type) {
+      return type === 'earn' || type === 'unfreeze'
     },
-    getStatusText(status) {
-      var map = { settled: '已结算', pending: '待结算', cancelled: '已取消' }
-      return map[status] || status || '待结算'
+    getAmountText(item) {
+      var prefix = this.isPositive(item.type) ? '+' : '-'
+      return prefix + this.cfToYuan(item.amount)
     },
-    getStatusClass(status) {
-      if (status === 'settled') return 'status-settled'
-      if (status === 'cancelled') return 'status-cancelled'
-      return 'status-pending'
+    getTypeText(type) {
+      var map = { earn: '获得', spend: '支出', freeze: '冻结', unfreeze: '解冻', withdraw: '提现' }
+      return map[type] || type || '变动'
+    },
+    getRefText(refType) {
+      var map = { product_order: '商品订单', activity: '活动', withdrawal: '提现', migration: '迁移', cf_transfer_out: '转让转出', cf_transfer_in: '转让转入' }
+      return map[refType] || ''
+    },
+    cfToYuan(val) {
+      if (val === null || val === undefined) return '¥0.00'
+      var num = Number(val)
+      if (isNaN(num)) return '¥0.00'
+      return '¥' + (num / 100).toFixed(2)
     },
     formatTime(time) {
       if (!time) return ''
@@ -187,33 +164,6 @@ export default {
 .header-value.blue { color: #0EA5E9; }
 .header-value.green { color: #10B981; }
 
-/* ===== Tab切换 ===== */
-.tab-bar {
-  display: flex;
-  background: #fff;
-  border-radius: 16rpx;
-  padding: 8rpx;
-  margin-bottom: 24rpx;
-  box-shadow: 0 2rpx 8rpx rgba(249,115,22,0.06);
-}
-.tab-item {
-  flex: 1;
-  text-align: center;
-  padding: 16rpx 0;
-  border-radius: 12rpx;
-}
-.tab-active {
-  background: #F97316;
-}
-.tab-active .tab-text {
-  color: #fff;
-  font-weight: 600;
-}
-.tab-text {
-  font-size: 26rpx;
-  color: #64748B;
-}
-
 .list-section {
   background: #fff;
   border-radius: 20rpx;
@@ -258,35 +208,27 @@ export default {
 .item-amount {
   font-size: 30rpx;
   font-weight: bold;
-  color: #F97316;
   display: block;
 }
+.amount-positive {
+  color: #F97316;
+}
+.amount-negative {
+  color: #94A3B8;
+}
 .item-status {
   font-size: 20rpx;
-  padding: 2rpx 10rpx;
-  border-radius: 6rpx;
+  color: #94A3B8;
   display: inline-block;
   margin-top: 4rpx;
 }
-.status-settled {
-  color: #10B981;
-  background: #D1FAE5;
-}
-.status-pending {
-  color: #D97706;
-  background: #FEF3C7;
-}
-.status-cancelled {
-  color: #94A3B8;
-  background: #F1F5F9;
-}
 .item-time {
   font-size: 20rpx;
   color: #94A3B8;
   display: block;
   margin-top: 4rpx;
 }
-.empty-state, .loading-state {
+.empty-state, .loading-state, .loadmore-state {
   text-align: center;
   padding: 60rpx;
   color: #94A3B8;

+ 16 - 17
cfc-frontend/pages/promotion/index.vue

@@ -115,21 +115,21 @@
       </view>
       <view class="earnings-body">
         <view class="earning-item">
-          <text class="earning-label">累计佣金</text>
-          <text class="earning-value orange" v-if="summary">{{ formatPriceWithSymbol(summary.totalCommission) }}</text>
-          <text class="earning-value orange" v-else>{{ formatPriceWithSymbol(0) }}</text>
+          <text class="earning-label">累计获取</text>
+          <text class="earning-value orange" v-if="summary">{{ cfToYuan(summary.totalEarned) }}</text>
+          <text class="earning-value orange" v-else>¥0.00</text>
         </view>
         <view class="earning-divider"></view>
         <view class="earning-item">
-          <text class="earning-label">可提现</text>
-          <text class="earning-value blue" v-if="summary">{{ formatPriceWithSymbol(summary.availableAmount) }}</text>
-          <text class="earning-value blue" v-else>{{ formatPriceWithSymbol(0) }}</text>
+          <text class="earning-label">可用余额</text>
+          <text class="earning-value blue" v-if="summary">{{ cfToYuan(summary.available) }}</text>
+          <text class="earning-value blue" v-else>¥0.00</text>
         </view>
         <view class="earning-divider"></view>
         <view class="earning-item">
-          <text class="earning-label">待结算</text>
-          <text class="earning-value green" v-if="summary">{{ formatPriceWithSymbol(summary.pendingAmount) }}</text>
-          <text class="earning-value green" v-else>{{ formatPriceWithSymbol(0) }}</text>
+          <text class="earning-label">冻结</text>
+          <text class="earning-value green" v-if="summary">{{ cfToYuan(summary.frozen) }}</text>
+          <text class="earning-value green" v-else>¥0.00</text>
         </view>
       </view>
     </view>
@@ -206,7 +206,7 @@
 </template>
 
 <script>
-import { getReferralSummary, getCommissionSummary, getFamilyEarningsSummary, getFamilyEarningsMembers, getFamilyEarningsRecords, distributeToMember, toggleMemberBalance, withdrawFamilyEarnings, getInviteQrCode, getFamilyMemberList, getReferralCode } from '../../utils/api.js'
+import { getReferralSummary, getCfSummary, getFamilyEarningsSummary, getFamilyEarningsMembers, getFamilyEarningsRecords, distributeToMember, toggleMemberBalance, withdrawFamilyEarnings, getInviteQrCode, getFamilyMemberList, getReferralCode } from '../../utils/api.js'
 import SharePoster from '@/components/SharePoster.vue'
 
 export default {
@@ -313,13 +313,13 @@ export default {
         console.error('获取邀请统计失败', e)
       }
       try {
-        var comRes = await getCommissionSummary()
+        var comRes = await getCfSummary()
         if (comRes.data) {
           this.summary = comRes.data
-          this.availableAmount = comRes.data.availableAmount || '0.00'
+          this.availableAmount = comRes.data.available || '0.00'
         }
       } catch (e) {
-        console.error('获取佣金总览失败', e)
+        console.error('获取 CF 总览失败', e)
       }
     },
     async loadFamilyData() {
@@ -414,12 +414,11 @@ export default {
       if (isNaN(num)) return '0.00'
       return (num / 100).toFixed(2)
     },
-    formatPriceWithSymbol(val) {
-      if (!val && val !== 0) return '¥0.00'
+    cfToYuan(val) {
+      if (val === null || val === undefined) return '¥0.00'
       var num = Number(val)
       if (isNaN(num)) return '¥0.00'
-      // val 已经是元单位(后端返回的 commission summary 是元)
-      return '¥' + Number(num).toFixed(2)
+      return '¥' + (num / 100).toFixed(2)
     },
     // 管理员操作
     toggleMemberAccess(m) {

+ 12 - 12
cfc-frontend/pages/promotion/team.vue

@@ -3,21 +3,21 @@
     <!-- 团队统计 -->
     <view class="stats-card">
       <view class="stats-item">
-        <text class="stats-num" v-if="teamStats">{{ teamStats.l1Count || 0 }}</text>
+        <text class="stats-num" v-if="cfRate">{{ cfRate.totalTeamSize || 0 }}</text>
         <text class="stats-num" v-else>0</text>
-        <text class="stats-label">直推(L1)</text>
+        <text class="stats-label">团队规模</text>
       </view>
       <view class="stats-divider"></view>
       <view class="stats-item">
-        <text class="stats-num" v-if="teamStats">{{ teamStats.l2Count || 0 }}</text>
-        <text class="stats-num" v-else>0</text>
-        <text class="stats-label">间推(L2)</text>
+        <text class="stats-num" v-if="cfRate">{{ (cfRate.ratePercent || 0) + '%' }}</text>
+        <text class="stats-num" v-else>0%</text>
+        <text class="stats-label">返佣比例</text>
       </view>
       <view class="stats-divider"></view>
       <view class="stats-item">
-        <text class="stats-num" v-if="teamStats">{{ formatPriceWithSymbol(teamStats.totalCommission) }}</text>
-        <text class="stats-num" v-else>{{ formatPriceWithSymbol(0) }}</text>
-        <text class="stats-label">团队佣金</text>
+        <text class="stats-num" v-if="cfRate && cfRate.tierName">{{ cfRate.tierName }}</text>
+        <text class="stats-num" v-else>未入档</text>
+        <text class="stats-label">当前档位</text>
       </view>
     </view>
 
@@ -64,7 +64,7 @@
 </template>
 
 <script>
-import { getCommissionTeam, getTeamList } from '../../utils/api.js'
+import { getCfRate, getTeamList } from '../../utils/api.js'
 import { parseDate } from '../../utils/format.js'
 
 export default {
@@ -76,7 +76,7 @@ export default {
       level: 'L1',
       loading: false,
       hasMore: true,
-      teamStats: null
+      cfRate: null
     }
   },
   onLoad() {
@@ -92,9 +92,9 @@ export default {
   methods: {
     async loadTeamStats() {
       try {
-        var res = await getCommissionTeam(1, 1)
+        var res = await getCfRate()
         if (res.data) {
-          this.teamStats = res.data
+          this.cfRate = res.data
         }
       } catch (e) {
         console.error('获取团队统计失败', e)

+ 9 - 0
cfc-frontend/utils/api.js

@@ -1829,6 +1829,15 @@ export const getCommissionBalance = () => {
   return request('/api/commission/balance', 'POST')
 }
 
+// CF 分佣
+export const getCfRate = () => request('/api/commission/cf/rate', 'POST')
+export const getCfSummary = () => request('/api/commission/cf/summary', 'POST')
+export const getCfList = (page, size) => request('/api/commission/cf/list', 'POST', { page, size })
+export const getFamilyCoupons = (page, size) => request('/api/coupon/family/list', 'POST', { page, size })
+// CF 转让
+export const sendCfTransfer = (toUserId, amount) => request('/api/cf/transfer/send', 'POST', { toUserId, amount })
+export const getCfTransferList = (page, size) => request('/api/cf/transfer/list', 'POST', { page, size })
+
 // ===== 财富维度 · 财商打卡 =====
 export const getCheckinList = (data) => {
   return request('/api/wealth/checkin/list', 'POST', data)