Ver Fonte

feat: 商城页新增会员升级营销触点 — 首页/购物车/结算页/支付页升级提醒

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Xiaogang Liao há 2 meses atrás
pai
commit
b0c44883e1

+ 46 - 1
cfc-frontend/pages/shop/cart/cart.vue

@@ -61,6 +61,13 @@
       </view>
     </scroll-view>
 
+    <!-- Member Upgrade Hint -->
+    <view v-if="!isMember && items.length > 0" class="member-hint" @click="goUpgrade">
+      <text class="member-hint-icon">👑</text>
+      <text class="member-hint-text">开通家庭会员享全场商品折扣</text>
+      <text class="member-hint-action">去开通 ›</text>
+    </view>
+
     <view class="bottom-bar" v-if="items.length > 0">
       <view class="bottom-left">
         <text class="selected-count">已选 {{ selectedCount }} 件</text>
@@ -78,6 +85,7 @@
 
 <script>
 import config from '@/config.js'
+import { getMyMembership } from '@/utils/api.js'
 
 export default {
   data() {
@@ -85,7 +93,8 @@ export default {
       items: [],
       loading: false,
       refreshing: false,
-      userId: null
+      userId: null,
+      isMember: false
     }
   },
   computed: {
@@ -115,6 +124,7 @@ export default {
   },
   onLoad() {
     this.userId = uni.getStorageSync('userId')
+    this.checkMemberStatus()
     this.loadCart()
   },
   onShow() {
@@ -123,6 +133,17 @@ export default {
     }
   },
   methods: {
+    checkMemberStatus() {
+      var that = this
+      getMyMembership().then(function(res) {
+        if (res.data && res.data.memberLevel) {
+          that.isMember = res.data.memberLevel !== 'FREE'
+        }
+      }).catch(function() {})
+    },
+    goUpgrade() {
+      uni.navigateTo({ url: '/pages/membership/upgrade' })
+    },
     loadCart() {
       if (this.loading) return
       this.loading = true
@@ -460,4 +481,28 @@ export default {
   background: #ddd;
   color: #999;
 }
+
+/* Member Upgrade Hint */
+.member-hint {
+  display: flex;
+  align-items: center;
+  padding: 20rpx 30rpx;
+  background: linear-gradient(135deg, #FFF7ED, #FFEDD5);
+  border-top: 2rpx solid #FED7AA;
+  border-bottom: 2rpx solid #FED7AA;
+}
+.member-hint-icon {
+  font-size: 32rpx;
+  margin-right: 12rpx;
+}
+.member-hint-text {
+  flex: 1;
+  font-size: 26rpx;
+  color: #C2410C;
+}
+.member-hint-action {
+  font-size: 26rpx;
+  color: #F97316;
+  font-weight: 500;
+}
 </style>

+ 47 - 2
cfc-frontend/pages/shop/checkout/checkout.vue

@@ -78,6 +78,15 @@
         </view>
       </view>
 
+      <!-- Member Upgrade Hint -->
+      <view v-if="!isMember" class="section member-upgrade-section" @click="goUpgrade">
+        <view class="member-upgrade-row">
+          <text class="member-upgrade-icon">👑</text>
+          <text class="member-upgrade-text">开通家庭会员享全场商品会员专享价</text>
+          <text class="member-upgrade-arrow">›</text>
+        </view>
+      </view>
+
       <!-- Coupon Section -->
       <view class="section coupon-section" @click="openCouponPicker">
         <view class="info-row">
@@ -184,7 +193,7 @@
 
 <script>
 import config from '@/config.js'
-import { getCouponList, consigneeList, getProductRequiredFields } from '../../../utils/api.js'
+import { getCouponList, consigneeList, getProductRequiredFields, getMyMembership } from '../../../utils/api.js'
 
 export default {
   data() {
@@ -205,7 +214,8 @@ export default {
       showCouponPicker: false,
       pointsUsed: 0,
       userTotalPoints: 0,
-      pointsLoading: false
+      pointsLoading: false,
+      isMember: false
     }
   },
   computed: {
@@ -263,6 +273,7 @@ export default {
       this.items = [productData]
       this.calcAmount()
     }
+    this.checkMemberStatus()
     this.loadDefaultConsignee()
     this.loadRequiredFields()
     this.loadCoupons()
@@ -384,6 +395,17 @@ export default {
         }
       })
     },
+    checkMemberStatus() {
+      var that = this
+      getMyMembership().then(function(res) {
+        if (res.data && res.data.memberLevel) {
+          that.isMember = res.data.memberLevel !== 'FREE'
+        }
+      }).catch(function() {})
+    },
+    goUpgrade() {
+      uni.navigateTo({ url: '/pages/membership/upgrade' })
+    },
     goAddConsignee() {
       uni.navigateTo({ url: '/pages/shop/consignee-edit/consignee-edit' })
     },
@@ -699,6 +721,29 @@ export default {
   margin-left: 8rpx;
 }
 
+/* ===== Member Upgrade Hint ===== */
+.member-upgrade-section {
+  position: relative;
+}
+.member-upgrade-row {
+  display: flex;
+  align-items: center;
+}
+.member-upgrade-icon {
+  font-size: 32rpx;
+  margin-right: 12rpx;
+}
+.member-upgrade-text {
+  flex: 1;
+  font-size: 26rpx;
+  color: #F97316;
+  font-weight: 500;
+}
+.member-upgrade-arrow {
+  font-size: 32rpx;
+  color: #F97316;
+}
+
 /* ===== Coupon Picker Modal ===== */
 .modal-mask {
   position: fixed;

+ 67 - 2
cfc-frontend/pages/shop/index/index.vue

@@ -1,5 +1,17 @@
 <template>
   <view class="container">
+    <!-- Member Upgrade Banner -->
+    <view v-if="!isMember" class="member-banner" @click="goUpgrade">
+      <view class="member-banner-content">
+        <text class="member-banner-icon">👑</text>
+        <view class="member-banner-texts">
+          <text class="member-banner-title">开通家庭会员</text>
+          <text class="member-banner-desc">享专属折扣与成长服务</text>
+        </view>
+      </view>
+      <text class="member-banner-action">立即开通 ›</text>
+    </view>
+
     <!-- Level 1 分类:2列横排网格 -->
     <view class="cat-level1">
       <view
@@ -101,7 +113,7 @@
 </template>
 
 <script>
-import { goodsCategory, productList } from '@/utils/api.js'
+import { goodsCategory, productList, getMyMembership } from '@/utils/api.js'
 export default {
   data() {
     return {
@@ -115,15 +127,18 @@ export default {
       loadingMore: false,
       refreshing: false,
       noMore: false,
-      isLoggedIn: false
+      isLoggedIn: false,
+      isMember: false
     }
   },
   onLoad() {
+    this.checkMemberStatus()
     this.isLoggedIn = !!uni.getStorageSync('token')
     this.loadCategories()
     this.loadProducts()
   },
   onShow() {
+    this.checkMemberStatus()
     this.isLoggedIn = !!uni.getStorageSync('token')
   },
   methods: {
@@ -205,6 +220,17 @@ export default {
         this.loadingMore = false
       })
     },
+    checkMemberStatus() {
+      var that = this
+      getMyMembership().then(function(res) {
+        if (res.data && res.data.memberLevel) {
+          that.isMember = res.data.memberLevel !== 'FREE'
+        }
+      }).catch(function() {})
+    },
+    goUpgrade() {
+      uni.navigateTo({ url: '/pages/membership/upgrade' })
+    },
     goDetail(id) {
       uni.navigateTo({ url: '/pages/shop/detail/detail?id=' + id })
     }
@@ -277,6 +303,45 @@ export default {
   font-weight: bold;
 }
 
+/* Member Upgrade Banner */
+.member-banner {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  background: linear-gradient(135deg, #FFF7ED, #FFEDD5);
+  margin: 16rpx;
+  padding: 20rpx 24rpx;
+  border-radius: 16rpx;
+  border: 2rpx solid #FED7AA;
+}
+.member-banner-content {
+  display: flex;
+  align-items: center;
+}
+.member-banner-icon {
+  font-size: 40rpx;
+  margin-right: 16rpx;
+}
+.member-banner-texts {
+  display: flex;
+  flex-direction: column;
+}
+.member-banner-title {
+  font-size: 28rpx;
+  font-weight: bold;
+  color: #C2410C;
+}
+.member-banner-desc {
+  font-size: 22rpx;
+  color: #F97316;
+  margin-top: 4rpx;
+}
+.member-banner-action {
+  font-size: 26rpx;
+  color: #F97316;
+  font-weight: 500;
+}
+
 /* Level 2 子分类:横向滚动chips */
 .cat-level2-scroll {
   background: #fff;

+ 185 - 1
cfc-frontend/pages/shop/payment/payment.vue

@@ -54,12 +54,45 @@
         </button>
         <text class="cancel-link" @click="onCancelOrder">取消订单</text>
       </view>
+
+      <!-- Post-payment Upgrade Card -->
+      <view v-if="showUpgradeCard" class="upgrade-card">
+        <view class="upgrade-card-header">
+          <text class="upgrade-card-icon">🎉</text>
+          <text class="upgrade-card-title">支付成功!开通家庭会员更划算</text>
+        </view>
+        <view class="upgrade-card-body">
+          <view class="upgrade-benefit-row">
+            <text class="upgrade-benefit-icon">✓</text>
+            <text class="upgrade-benefit-text">全场商品享受会员专享价</text>
+          </view>
+          <view class="upgrade-benefit-row">
+            <text class="upgrade-benefit-icon">✓</text>
+            <text class="upgrade-benefit-text">不限次一对一成长咨询服务</text>
+          </view>
+          <view class="upgrade-benefit-row">
+            <text class="upgrade-benefit-icon">✓</text>
+            <text class="upgrade-benefit-text">专属家庭成长计划与测评报告</text>
+          </view>
+          <view class="upgrade-price-row">
+            <text class="upgrade-price-label">仅需</text>
+            <text class="upgrade-price-value">{{ formatPriceWithSymbol(familyYearlyPrice * 100) }}</text>
+            <text class="upgrade-price-unit">/年</text>
+            <text class="upgrade-price-original" v-if="familyYearlyPrice > 0">{{ formatPriceWithSymbol((familyYearlyPrice * 1.5 * 100).toFixed(0)) }}</text>
+          </view>
+        </view>
+        <view class="upgrade-card-action">
+          <button class="upgrade-btn" @click="goUpgrade">立即开通</button>
+          <text class="upgrade-skip" @click="showUpgradeCard = false">暂不开通</text>
+        </view>
+      </view>
     </scroll-view>
   </view>
 </template>
 
 <script>
 import config from '@/config.js'
+import { getMyMembership } from '@/utils/api.js'
 
 export default {
   data() {
@@ -71,7 +104,9 @@ export default {
       loading: false,
       timer: null,
       pointsUsed: 0,
-      pointsUsed: 0
+      isMember: false,
+      showUpgradeCard: false,
+      familyYearlyPrice: '365'
     }
   },
   computed: {
@@ -82,6 +117,8 @@ export default {
     }
   },
   onLoad(options) {
+    this.checkMemberStatus()
+    this.checkMembershipLevels()
     if (options.orderNo) {
       this.orderNo = options.orderNo
     }
@@ -100,6 +137,44 @@ export default {
     }
   },
   methods: {
+    checkMemberStatus() {
+      var that = this
+      getMyMembership().then(function(res) {
+        if (res.data && res.data.memberLevel) {
+          that.isMember = res.data.memberLevel !== 'FREE'
+        }
+      }).catch(function() {})
+    },
+    checkMembershipLevels() {
+      var that = this
+      uni.request({
+        url: config.api('/api/membership/levels'),
+        method: 'POST',
+        header: { 'Authorization': 'Bearer ' + uni.getStorageSync('token') },
+        success: function(res) {
+          if (res.data && res.data.code === 200 && res.data.data) {
+            var levels = res.data.data
+            for (var i = 0; i < levels.length; i++) {
+              if (levels[i].levelCode === 'FAMILY' && levels[i].priceYearly > 0) {
+                that.familyYearlyPrice = (levels[i].priceYearly / 100).toFixed(0)
+                break
+              }
+            }
+          }
+        }
+      })
+    },
+    handlePaymentSuccess() {
+      var that = this
+      if (!this.isMember) {
+        setTimeout(function() {
+          that.showUpgradeCard = true
+        }, 2000)
+      }
+    },
+    goUpgrade() {
+      uni.navigateTo({ url: '/pages/membership/upgrade' })
+    },
     startCountdown() {
       var that = this
       this.timer = setInterval(function() {
@@ -145,6 +220,7 @@ export default {
                 success: function(payRes) {
                   if (payRes.data && payRes.data.code === 200) {
                     uni.showToast({ title: '支付成功', icon: 'success' })
+                    that.handlePaymentSuccess()
                     setTimeout(function() {
                       uni.redirectTo({
                         url: '/pages/shop/order-list/order-list?tab=paid'
@@ -178,6 +254,7 @@ export default {
                     }
                   })
                   uni.showToast({ title: '支付成功', icon: 'success' })
+                  that.handlePaymentSuccess()
                   setTimeout(function() {
                     uni.redirectTo({
                       url: '/pages/shop/order-list/order-list?tab=paid'
@@ -371,4 +448,111 @@ export default {
   .points-value {
     color: #F97316;
   }
+
+  /* Upgrade Card */
+  .upgrade-card {
+    margin: 30rpx 20rpx;
+    background: linear-gradient(135deg, #FFF7ED, #FFEDD5);
+    border-radius: 20rpx;
+    padding: 30rpx;
+    border: 2rpx solid #FED7AA;
+    animation: fadeInUp 0.5s ease;
+  }
+  .upgrade-card-header {
+    display: flex;
+    align-items: center;
+    margin-bottom: 20rpx;
+  }
+  .upgrade-card-icon {
+    font-size: 40rpx;
+    margin-right: 12rpx;
+  }
+  .upgrade-card-title {
+    font-size: 30rpx;
+    font-weight: bold;
+    color: #333;
+  }
+  .upgrade-card-body {
+    margin-bottom: 24rpx;
+  }
+  .upgrade-benefit-row {
+    display: flex;
+    align-items: center;
+    padding: 8rpx 0;
+  }
+  .upgrade-benefit-icon {
+    font-size: 24rpx;
+    color: #22C55E;
+    margin-right: 12rpx;
+    font-weight: bold;
+  }
+  .upgrade-benefit-text {
+    font-size: 26rpx;
+    color: #666;
+  }
+  .upgrade-price-row {
+    display: flex;
+    align-items: baseline;
+    margin-top: 16rpx;
+    padding-top: 16rpx;
+    border-top: 1rpx solid #FED7AA;
+  }
+  .upgrade-price-label {
+    font-size: 26rpx;
+    color: #999;
+    margin-right: 8rpx;
+  }
+  .upgrade-price-value {
+    font-size: 48rpx;
+    font-weight: bold;
+    color: #F97316;
+  }
+  .upgrade-price-unit {
+    font-size: 24rpx;
+    color: #999;
+    margin-left: 6rpx;
+  }
+  .upgrade-price-original {
+    font-size: 24rpx;
+    color: #ccc;
+    text-decoration: line-through;
+    margin-left: 12rpx;
+  }
+  .upgrade-card-action {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+  }
+  .upgrade-btn {
+    width: 100%;
+    height: 80rpx;
+    line-height: 80rpx;
+    background: linear-gradient(135deg, #F97316, #FB923C);
+    color: #fff;
+    font-size: 30rpx;
+    font-weight: bold;
+    border-radius: 40rpx;
+    border: none;
+    text-align: center;
+  }
+  .upgrade-btn::after {
+    border: none;
+  }
+  .upgrade-skip {
+    margin-top: 16rpx;
+    font-size: 24rpx;
+    color: #999;
+    padding: 8rpx;
+  }
+
+  @keyframes fadeInUp {
+    from {
+      opacity: 0;
+      transform: translateY(30rpx);
+    }
+    to {
+      opacity: 1;
+      transform: translateY(0);
+    }
+  }
 </style>