Przeglądaj źródła

feat(frontend): 会员升级页移除功能列表、权益对比列联动高亮、状态卡片紧凑化

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
asus 1 miesiąc temu
rodzic
commit
701a9da865
1 zmienionych plików z 68 dodań i 130 usunięć
  1. 68 130
      cfc-frontend/pages/membership/upgrade.vue

+ 68 - 130
cfc-frontend/pages/membership/upgrade.vue

@@ -1,17 +1,19 @@
 <template>
   <view class="upgrade-page-wrapper">
     <scroll-view class="page" scroll-y>
-      <!-- 当前会员状态卡片 -->
+      <!-- 当前会员状态 -->
       <view class="status-card">
-        <view class="status-badge" :class="isFree ? 'badge-free' : 'badge-family'">
-          <text class="badge-icon">{{ isFree ? '○' : '●' }}</text>
-          <text class="badge-text">{{ currentLevelName }}</text>
+        <view class="status-top">
+          <view class="status-badge" :class="isFree ? 'badge-free' : 'badge-family'">
+            <text class="badge-icon">{{ isFree ? '○' : '●' }}</text>
+            <text class="badge-text">{{ currentLevelName }}</text>
+          </view>
+          <text class="free-hint" v-if="isFree">开通会员解锁全部特权</text>
+          <text class="expiry-value" v-else-if="membership && membership.memberLevel !== 'FREE'">
+            {{ formatDate(membership.endDate) }} 到期
+          </text>
         </view>
         <view class="status-body" v-if="membership && membership.memberLevel !== 'FREE'">
-          <view class="expiry-row">
-            <text class="expiry-label">到期时间</text>
-            <text class="expiry-value">{{ formatDate(membership.endDate) }}</text>
-          </view>
           <view class="days-remaining" v-if="membership.isActive">
             <text class="days-num">{{ membership.daysRemaining || 0 }}</text>
             <text class="days-unit">天剩余</text>
@@ -20,30 +22,23 @@
             <text class="expired-text">会员已过期,续费恢复权益</text>
           </view>
         </view>
-        <view class="status-body" v-else>
-          <text class="free-hint">当前为免费版,开通会员解锁全部特权</text>
-        </view>
       </view>
 
       <!-- 等级切换 tabs -->
-      <view class="level-tabs" v-if="isFree">
-        <view class="level-tab" :class="selectedLevel === 'FAMILY' ? 'level-tab-active' : ''"
-          @click="selectLevel('FAMILY')">
-          <text class="level-tab-name">家庭会员</text>
-          <text class="level-tab-price">¥{{ getLevelYearlyPrice('FAMILY') }}/年</text>
-        </view>
-        <view class="level-tab" :class="selectedLevel === 'PREMIUM' ? 'level-tab-active' : ''"
-          @click="selectLevel('PREMIUM')">
-          <text class="level-tab-name">高级家庭会员</text>
-          <text class="level-tab-price">¥{{ getLevelYearlyPrice('PREMIUM') }}/年</text>
+      <view class="level-tabs" v-if="buyableLevels.length > 0">
+        <view class="level-tab" v-for="lv in buyableLevels" :key="lv.levelCode"
+          :class="selectedLevel === lv.levelCode ? 'level-tab-active' : ''"
+          @click="selectLevel(lv.levelCode)">
+          <text class="level-tab-name">{{ lv.levelName }}</text>
+          <text class="level-tab-price">¥{{ getLevelYearlyPrice(lv.levelCode) }}/年</text>
         </view>
       </view>
 
-      <!-- 家庭会员开通卡片 -->
-      <view class="upgrade-card" v-if="isFree">
+      <!-- 开通/升级卡片(免费用户开通,或已付费用户切换到其他等级时升级) -->
+      <view class="upgrade-card" v-if="isFree || selectedLevel !== currentMemberLevel">
         <view class="upgrade-header">
-          <text class="upgrade-title">{{ selectedLevel === 'FAMILY' ? '家庭会员' : '高级家庭会员' }}</text>
-          <text class="upgrade-subtitle">{{ selectedLevel === 'FAMILY' ? 'FAMILY' : 'PREMIUM' }}</text>
+          <text class="upgrade-title">{{ getSelectedLevelName() }}</text>
+          <text class="upgrade-subtitle">{{ selectedLevel }}</text>
         </view>
         <!-- 档位选择 -->
         <view class="period-tabs">
@@ -59,22 +54,10 @@
             <text class="period-original" v-if="showOriginalPrice(p.key)">¥{{ getOriginalPriceForPeriod(p.key) / 100 }}</text>
           </view>
         </view>
-        <view class="upgrade-price">
-          <text class="price-original-line" v-if="showOriginalPrice(selectedPeriod)">¥{{ getOriginalPriceForPeriod(selectedPeriod) / 100 }}</text>
-          <text class="price-symbol">¥</text>
-          <text class="price-amount">{{ getPriceForPeriod(selectedPeriod) }}</text>
-          <text class="price-unit">{{ getPeriodUnit() }}</text>
-        </view>
-        <view class="feature-list">
-          <view class="feature-item" v-for="(feature, idx) in features" :key="idx">
-            <text class="feature-check">✓</text>
-            <text class="feature-text">{{ feature }}</text>
-          </view>
-        </view>
         <button class="btn-upgrade" @click="goNext">下一步</button>
       </view>
 
-      <!-- 续费卡片 -->
+      <!-- 续费卡片(已付费用户保持当前等级) -->
       <view class="upgrade-card" v-else>
         <view class="upgrade-header">
           <text class="upgrade-title">续费会员</text>
@@ -94,12 +77,6 @@
             <text class="period-original" v-if="showOriginalPrice(p.key)">¥{{ getOriginalPriceForPeriod(p.key) / 100 }}</text>
           </view>
         </view>
-        <view class="upgrade-price">
-          <text class="price-original-line" v-if="showOriginalPrice(selectedPeriod)">¥{{ getOriginalPriceForPeriod(selectedPeriod) / 100 }}</text>
-          <text class="price-symbol">¥</text>
-          <text class="price-amount">{{ getPriceForPeriod(selectedPeriod) }}</text>
-          <text class="price-unit">{{ getPeriodUnit() }}</text>
-        </view>
         <button class="btn-upgrade btn-renew" @click="goNext">下一步</button>
       </view>
 
@@ -110,14 +87,14 @@
           <view class="compare-row header-row">
             <text class="compare-cell cell-feature">权益项</text>
             <text class="compare-cell cell-free">免费</text>
-            <text class="compare-cell cell-family active">家庭会员</text>
-            <text class="compare-cell cell-premium active">高级会员</text>
+            <text class="compare-cell cell-family" :class="selectedLevel === 'FAMILY' ? 'active' : ''">家庭会员</text>
+            <text class="compare-cell cell-premium" :class="selectedLevel === 'PREMIUM' ? 'active' : ''">高级会员</text>
           </view>
           <view class="compare-row" v-for="(item, idx) in compareData" :key="idx">
             <text class="compare-cell cell-feature">{{ item.name }}</text>
             <text class="compare-cell cell-free">{{ item.free }}</text>
-            <text class="compare-cell cell-family active">{{ item.family }}</text>
-            <text class="compare-cell cell-premium active">{{ item.premium }}</text>
+            <text class="compare-cell cell-family" :class="selectedLevel === 'FAMILY' ? 'active' : ''">{{ item.family }}</text>
+            <text class="compare-cell cell-premium" :class="selectedLevel === 'PREMIUM' ? 'active' : ''">{{ item.premium }}</text>
           </view>
         </view>
       </view>
@@ -156,14 +133,13 @@ export default {
       membership: null,
       levels: [],
       records: [],
-      features: ['折扣购物', '家庭管理', '推荐佣金', '专属活动', '任务特权', '成长报告'],
       isFree: true,
       currentLevelName: '免费用户',
       compareData: [
-        { name: '商品折扣', free: '普通价格', family: '9折', premium: '8折', lifetime: '全场最低价' },
+        { name: '商品折扣', free: '普通价格', family: '会员折扣', premium: '会员折扣', lifetime: '会员折扣' },
         { name: '家庭管理', free: '基础功能', family: '全部功能', premium: '全部功能+优先响应', lifetime: '全部功能+专属管家' },
         { name: '管家服务', free: '—', family: '—', premium: '每周一次', lifetime: '专属管家7×24h' },
-        { name: '推荐佣金', free: '不可参与', family: '最高15%', premium: '最高20%', lifetime: '最高25%+团队分红' },
+        { name: '推荐佣金', free: '不可参与', family: '可参与', premium: '可参与', lifetime: '可参与' },
         { name: '专属活动', free: '部分参与', family: '全部参与', premium: '优先参与', lifetime: 'VIP专属+定制活动' },
         { name: '成长报告', free: '月度', family: '每周+深度', premium: '每日+深度+专家解读', lifetime: '实时+AI专家深度解读' },
         { name: '任务配额', free: '每日5个', family: '不限量', premium: '不限量+优先审核', lifetime: '不限量+专属通道' }
@@ -175,10 +151,17 @@ export default {
       ],
       selectedPeriod: 'yearly',
       selectedLevel: 'FAMILY',
+      currentMemberLevel: 'FREE',
       joinRules: [],
     }
   },
   computed: {
+    buyableLevels() {
+      var self = this
+      return this.levels.filter(function(lv) {
+        return lv.levelCode !== 'FREE' && lv.levelCode !== 'LIFETIME' && lv.levelCode !== 'PROVIDER'
+      })
+    },
     visiblePeriods() {
       var self = this
       return this.periods.filter(function(p) {
@@ -215,6 +198,16 @@ export default {
       }
       return map[code] || code || ''
     },
+    getSelectedLevelName() {
+      var name = ''
+      for (var i = 0; i < this.levels.length; i++) {
+        if (this.levels[i].levelCode === this.selectedLevel) {
+          name = this.levels[i].levelName
+          break
+        }
+      }
+      return name || this.getLevelName(this.selectedLevel)
+    },
     getJoinRuleKey(rule, idx) {
       return rule.id + '-' + idx
     },
@@ -231,11 +224,14 @@ export default {
         this.membership = membershipRes.data
         if (this.membership && this.membership.memberLevel && this.membership.memberLevel !== 'FREE') {
           this.isFree = false
+          this.currentMemberLevel = this.membership.memberLevel
           this.currentLevelName = this.membership.levelName || '家庭会员'
           this.selectedLevel = this.membership.memberLevel
         } else {
           this.isFree = true
+          this.currentMemberLevel = 'FREE'
           this.currentLevelName = '免费用户'
+          this.selectedLevel = 'FAMILY'
         }
       } catch (e) {
         console.error('加载会员信息失败', e)
@@ -284,14 +280,6 @@ export default {
       if (periodKey === 'yearly') return plan.yearly ? (plan.yearly / 100).toFixed(0) : '0'
       return '0'
     },
-    getPeriodUnit: function() {
-      for (var i = 0; i < this.periods.length; i++) {
-        if (this.periods[i].key === this.selectedPeriod) {
-          return this.periods[i].unit
-        }
-      }
-      return '/年'
-    },
     goNext: function() {
       var levelCode = this.selectedLevel
       var period = this.selectedPeriod
@@ -339,8 +327,13 @@ export default {
 .status-card {
   background: linear-gradient(135deg, #F97316 0%, #EA580C 100%);
   border-radius: 20rpx;
-  padding: 40rpx;
-  margin-bottom: 30rpx;
+  padding: 26rpx 30rpx;
+  margin-bottom: 24rpx;
+}
+.status-top {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
 }
 .status-badge {
   display: inline-flex;
@@ -369,51 +362,42 @@ export default {
   font-weight: 600;
 }
 .status-body {
-  margin-top: 24rpx;
-}
-.expiry-row {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-}
-.expiry-label {
-  font-size: 26rpx;
-  color: rgba(255,255,255,0.8);
+  margin-top: 18rpx;
 }
 .expiry-value {
-  font-size: 28rpx;
+  font-size: 26rpx;
   color: #fff;
   font-weight: 500;
+  flex-shrink: 0;
 }
 .days-remaining {
   display: flex;
   align-items: baseline;
   justify-content: center;
-  margin-top: 20rpx;
+  margin-top: 14rpx;
 }
 .days-num {
-  font-size: 56rpx;
+  font-size: 44rpx;
   font-weight: bold;
   color: #FEF3C7;
 }
 .days-unit {
-  font-size: 26rpx;
+  font-size: 24rpx;
   color: rgba(255,255,255,0.7);
   margin-left: 8rpx;
 }
 .expired-msg {
   text-align: center;
-  margin-top: 16rpx;
+  margin-top: 12rpx;
 }
 .expired-text {
-  font-size: 26rpx;
+  font-size: 24rpx;
   color: #FEF3C7;
 }
 .free-hint {
-  font-size: 26rpx;
+  font-size: 24rpx;
   color: rgba(255,255,255,0.85);
-  text-align: center;
-  display: block;
+  flex-shrink: 0;
 }
 .lifetime-hint {
   font-size: 26rpx;
@@ -447,50 +431,6 @@ export default {
   margin-top: 6rpx;
   display: block;
 }
-.upgrade-price {
-  text-align: center;
-  margin: 24rpx 0;
-}
-.price-symbol {
-  font-size: 28rpx;
-  color: #F97316;
-  vertical-align: top;
-}
-.price-amount {
-  font-size: 64rpx;
-  font-weight: bold;
-  color: #F97316;
-  line-height: 1;
-}
-.price-unit {
-  font-size: 26rpx;
-  color: #94A3B8;
-  margin-left: 6rpx;
-}
-.feature-list {
-  margin: 24rpx 0;
-}
-.feature-item {
-  display: flex;
-  align-items: center;
-  padding: 12rpx 0;
-}
-.feature-check {
-  width: 40rpx;
-  height: 40rpx;
-  line-height: 40rpx;
-  text-align: center;
-  background: #F97316;
-  color: #fff;
-  border-radius: 50%;
-  font-size: 22rpx;
-  margin-right: 16rpx;
-  flex-shrink: 0;
-}
-.feature-text {
-  font-size: 28rpx;
-  color: #1E293B;
-}
 .btn-upgrade {
   width: 100%;
   height: 88rpx;
@@ -562,10 +502,12 @@ export default {
   color: #94A3B8;
 }
 .cell-family.active {
+  background: #FFEDD5;
   color: #F97316;
   font-weight: 600;
 }
 .cell-premium.active {
+  background: #EDE9FE;
   color: #8B5CF6;
   font-weight: 600;
 }
@@ -587,9 +529,11 @@ export default {
   color: #64748B;
 }
 .header-row .cell-family.active {
+  background: #FFEDD5;
   color: #F97316;
 }
 .header-row .cell-premium.active {
+  background: #EDE9FE;
   color: #8B5CF6;
 }
 
@@ -906,10 +850,4 @@ export default {
   text-decoration: line-through;
   margin-top: 2rpx;
 }
-.price-original-line {
-  font-size: 26rpx;
-  color: #94A3B8;
-  text-decoration: line-through;
-  margin-right: 12rpx;
-}
 </style>