Browse Source

Revert "feat: 屏蔽在线开通会员功能,改为管理员后台设置"

This reverts commit 7f3725c70e600021875f61c198c9c0ee527a63b7.
liaoxg 3 months ago
parent
commit
868ed5230b

+ 370 - 18
cfc-frontend/pages/membership/plans.vue

@@ -1,31 +1,383 @@
 <template>
-  <view class="container">
-    <view class="notice-card">
-      <text class="notice-icon">👑</text>
-      <text class="notice-title">会员功能</text>
-      <text class="notice-desc">会员级别由管理员在后台统一设置,暂不支持在线开通。</text>
-      <text class="notice-desc">如有疑问请联系客服。</text>
-      <button class="back-btn" @click="goBack">返回</button>
+  <scroll-view class="container" scroll-y>
+    <view class="plans-header">
+      <text class="header-title">会员方案</text>
+      <text class="header-subtitle">全家人的健康守护</text>
     </view>
-  </view>
+
+    <view class="plans-list">
+      <!-- 免费卡片(静态) -->
+      <view class="plan-card plan-l0">
+        <view class="plan-header">
+          <text class="plan-name">注册会员</text>
+          <text class="plan-tag">免费</text>
+        </view>
+        <view class="plan-price">
+          <text class="price-amount">¥0</text>
+          <text class="price-unit">/永久</text>
+        </view>
+        <view class="plan-features">
+          <view class="feature-row" v-for="(feat, idx) in l0Features" :key="idx">
+            <text class="feature-icon">✓</text>
+            <text class="feature-text">{{ feat }}</text>
+          </view>
+        </view>
+        <button class="btn-action" disabled>当前等级</button>
+      </view>
+
+      <!-- 付费等级卡片(从后端动态加载) -->
+      <view class="plan-card" v-for="level in levels" :key="level.levelCode"
+        :class="{'active': level.recommended}">
+        <view class="plan-badge" v-if="level.recommended">推荐</view>
+        <view class="plan-badge premium" v-else-if="level.levelCode === 'PREMIUM'">至尊</view>
+        <view class="plan-header">
+          <text class="plan-name">{{ level.levelName }}</text>
+          <text class="plan-tag">{{ level.levelCode === 'FAMILY' ? '年付会员' : '年付/月付' }}</text>
+        </view>
+        <view class="plan-price">
+          <text class="price-amount">{{ formatPrice(level.yearly) }}</text>
+          <text class="price-unit">/年</text>
+        </view>
+        <view class="plan-features" v-if="level.levelDesc">
+          <view class="feature-row" v-for="(feat, idx) in parseFeatures(level.levelDesc)" :key="idx">
+            <text class="feature-icon">✓</text>
+            <text class="feature-text">{{ feat }}</text>
+          </view>
+        </view>
+        <button class="btn-action" :class="level.recommended ? 'btn-primary' : 'btn-premium'"
+          @click="purchasePlan(level.levelCode)">
+          {{ level.levelCode === 'FAMILY' ? '立即开通' : '立即订阅' }}
+        </button>
+      </view>
+    </view>
+
+    <view class="benefits-section">
+      <text class="section-title">会员权益详解</text>
+      <view class="benefits-grid">
+        <view class="benefit-item" v-for="(item, idx) in benefits" :key="idx">
+          <view class="benefit-icon" :style="'background:' + item.bg">
+            <text class="benefit-emoji">{{ item.icon }}</text>
+          </view>
+          <text class="benefit-name">{{ item.name }}</text>
+          <text class="benefit-desc">{{ item.desc }}</text>
+        </view>
+      </view>
+    </view>
+
+    <view class="faq-section">
+      <text class="section-title">常见问题</text>
+      <view class="faq-item" v-for="(faq, idx) in faqs" :key="idx">
+        <text class="faq-question">{{ faq.q }}</text>
+        <text class="faq-answer">{{ faq.a }}</text>
+      </view>
+    </view>
+  </scroll-view>
 </template>
 
 <script>
+import { getMembershipPlans } from '../../utils/api.js'
+
 export default {
+  data() {
+    return {
+      levels: [],
+      l0Features: [
+        '每日 3 次 AI 问答',
+        '基础健康百科',
+        '社区内容浏览'
+      ],
+      benefits: [
+        { icon: '🤖', name: 'AI 健康顾问', desc: '7×24 小时智能问答', bg: '#DBEAFE' },
+        { icon: '👨‍👩‍👧‍👦', name: '家庭覆盖', desc: '一次付费全家守护', bg: '#D1FAE5' },
+        { icon: '👨‍🏫', name: '专属管家', desc: '认证专业人员服务', bg: '#FEF3C7' },
+        { icon: '🛒', name: '商品折扣', desc: '会员专享优惠价', bg: '#FCE7F3' },
+        { icon: '💰', name: '消费返利', desc: 'P 点制终身返利', bg: '#FFEDD5' },
+        { icon: '📈', name: '推广佣金', desc: 'R0-R4 阶梯奖励', bg: '#E0E7FF' }
+      ],
+      faqs: [
+        { q: '家庭会员适合谁?', a: '适合有 1-2 个孩子的家庭,追求性价比,希望获得 AI 健康顾问 + 认证管家服务' },
+        { q: '高级家庭会员与家庭会员的区别?', a: '高级会员享资深管家 (1:30 配比)、深度 AI 分析、专家会诊、更高折扣和返利加成' },
+        { q: '可以取消吗?', a: '可以,取消后当前月结束后降级为免费用户,健康档案保留 30 天' },
+        { q: '消费返利如何计算?', a: '被推荐人消费 P 点总和 × 推广人等级返利率,高级会员额外 +5% 加成' }
+      ]
+    }
+  },
+  onLoad() {
+    this.loadPlans()
+  },
   methods: {
-    goBack() {
-      uni.navigateBack()
+    loadPlans() {
+      var self = this
+      getMembershipPlans().then(function(res) {
+        if (res.code === 200 && Array.isArray(res.data)) {
+          self.levels = res.data
+        }
+      }).catch(function(e) {
+        console.error('获取方案失败', e)
+      })
+    },
+    formatPrice(cents) {
+      if (!cents && cents !== 0) return '¥0'
+      return '¥' + (cents / 100).toLocaleString()
+    },
+    parseFeatures(features) {
+      if (!features) return []
+      if (Array.isArray(features)) return features
+      try { return JSON.parse(features) } catch (e) { return [] }
+    },
+    purchasePlan(levelCode) {
+      uni.navigateTo({ url: '/pages/membership/upgrade?level=' + levelCode })
     }
   }
 }
 </script>
 
-<style>
-.container { min-height: 100vh; background: #f5f7fa; display: flex; align-items: center; justify-content: center; padding: 60rpx; }
-.notice-card { background: #fff; border-radius: 24rpx; padding: 60rpx 40rpx; text-align: center; width: 100%; }
-.notice-icon { font-size: 80rpx; }
-.notice-title { font-size: 36rpx; font-weight: 700; color: #333; margin-top: 24rpx; }
-.notice-desc { font-size: 28rpx; color: #999; margin-top: 16rpx; line-height: 1.6; }
-.back-btn { margin-top: 48rpx; width: 60%; height: 80rpx; line-height: 80rpx; background: linear-gradient(135deg, #5B9BD5, #3A7CC4); color: #fff; font-size: 30rpx; border-radius: 40rpx; border: none; }
-.back-btn::after { border: none; }
+<style scoped>
+.container {
+  padding: 30rpx;
+  min-height: 100vh;
+  background: linear-gradient(180deg, #FFF7ED 0%, #FEF3C7 100%);
+}
+
+.plans-header {
+  text-align: center;
+  padding: 60rpx 0 40rpx;
+}
+.header-title {
+  font-size: 44rpx;
+  font-weight: bold;
+  color: #1E293B;
+  display: block;
+}
+.header-subtitle {
+  font-size: 28rpx;
+  color: #64748B;
+  margin-top: 12rpx;
+  display: block;
+}
+
+.plans-list {
+  display: flex;
+  flex-direction: column;
+  gap: 30rpx;
+  margin-bottom: 40rpx;
+}
+
+.plan-card {
+  background: #fff;
+  border-radius: 24rpx;
+  padding: 40rpx;
+  position: relative;
+  border: 2rpx solid #FED7AA;
+  box-shadow: 0 4rpx 20rpx rgba(249,115,22,0.08);
+}
+
+.plan-card.active {
+  border-color: #F97316;
+  background: linear-gradient(135deg, #FFF7ED 0%, #FEF3C7 100%);
+  transform: scale(1.02);
+}
+
+.plan-badge {
+  position: absolute;
+  top: -16rpx;
+  right: 40rpx;
+  background: #F97316;
+  color: #fff;
+  padding: 8rpx 24rpx;
+  border-radius: 9999rpx;
+  font-size: 22rpx;
+  font-weight: 600;
+}
+
+.plan-badge.premium {
+  background: linear-gradient(135deg, #8B5CF6 0%, #EC4899 100%);
+}
+
+.plan-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 20rpx;
+}
+
+.plan-name {
+  font-size: 32rpx;
+  font-weight: bold;
+  color: #1E293B;
+}
+
+.plan-tag {
+  font-size: 22rpx;
+  color: #64748B;
+  background: #F1F5F9;
+  padding: 6rpx 16rpx;
+  border-radius: 8rpx;
+}
+
+.plan-price {
+  text-align: center;
+  margin: 24rpx 0;
+}
+
+.price-amount {
+  font-size: 56rpx;
+  font-weight: bold;
+  color: #F97316;
+}
+
+.price-unit {
+  font-size: 24rpx;
+  color: #94A3B8;
+  margin-left: 8rpx;
+}
+
+.plan-features {
+  margin: 24rpx 0;
+}
+
+.feature-row {
+  display: flex;
+  align-items: center;
+  padding: 12rpx 0;
+}
+
+.feature-icon {
+  width: 40rpx;
+  height: 40rpx;
+  line-height: 40rpx;
+  text-align: center;
+  background: #10B981;
+  color: #fff;
+  border-radius: 50%;
+  font-size: 20rpx;
+  margin-right: 12rpx;
+  flex-shrink: 0;
+}
+
+.feature-text {
+  font-size: 26rpx;
+  color: #334155;
+  flex: 1;
+}
+
+.btn-action {
+  width: 100%;
+  height: 80rpx;
+  line-height: 80rpx;
+  background: #E5E7EB;
+  color: #9CA3AF;
+  font-size: 28rpx;
+  border-radius: 40rpx;
+  border: none;
+  margin-top: 20rpx;
+}
+
+.btn-action:disabled {
+  opacity: 0.6;
+}
+
+.btn-primary {
+  background: linear-gradient(135deg, #F97316 0%, #EA580C 100%);
+  color: #fff;
+  font-weight: 600;
+}
+
+.btn-premium {
+  background: linear-gradient(135deg, #8B5CF6 0%, #EC4899 100%);
+  color: #fff;
+  font-weight: 600;
+}
+
+.btn-action::after {
+  border: none;
+}
+
+.benefits-section {
+  background: #fff;
+  border-radius: 24rpx;
+  padding: 30rpx;
+  margin-bottom: 30rpx;
+  box-shadow: 0 4rpx 20rpx rgba(249,115,22,0.08);
+}
+
+.section-title {
+  font-size: 32rpx;
+  font-weight: bold;
+  color: #1E293B;
+  display: block;
+  margin-bottom: 24rpx;
+}
+
+.benefits-grid {
+  display: flex;
+  flex-wrap: wrap;
+}
+
+.benefit-item {
+  width: 50%;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  padding: 24rpx 16rpx;
+  box-sizing: border-box;
+}
+
+.benefit-icon {
+  width: 80rpx;
+  height: 80rpx;
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-bottom: 16rpx;
+}
+
+.benefit-emoji {
+  font-size: 40rpx;
+}
+
+.benefit-name {
+  font-size: 26rpx;
+  font-weight: 600;
+  color: #1E293B;
+  margin-bottom: 8rpx;
+}
+
+.benefit-desc {
+  font-size: 22rpx;
+  color: #94A3B8;
+  text-align: center;
+}
+
+.faq-section {
+  background: #fff;
+  border-radius: 24rpx;
+  padding: 30rpx;
+  box-shadow: 0 4rpx 20rpx rgba(249,115,22,0.08);
+}
+
+.faq-item {
+  padding: 20rpx 0;
+  border-bottom: 1rpx solid #F1F5F9;
+}
+
+.faq-item:last-child {
+  border-bottom: none;
+}
+
+.faq-question {
+  font-size: 26rpx;
+  font-weight: 600;
+  color: #1E293B;
+  display: block;
+  margin-bottom: 8rpx;
+}
+
+.faq-answer {
+  font-size: 24rpx;
+  color: #64748B;
+  line-height: 1.6;
+  display: block;
+}
 </style>

+ 4 - 30
cfc-frontend/pages/membership/upgrade.vue

@@ -1,34 +1,8 @@
 <template>
-  <view class="container">
-    <view class="notice-card">
-      <text class="notice-icon">🔒</text>
-      <text class="notice-title">开通会员</text>
-      <text class="notice-desc">会员级别由管理员在后台统一设置,暂不支持在线开通。</text>
-      <text class="notice-desc">如有疑问请联系客服。</text>
-      <button class="back-btn" @click="goBack">返回</button>
-    </view>
-  </view>
-</template>
-
-<script>
-export default {
-  methods: {
-    goBack() {
-      uni.navigateBack()
-    }
-  }
-}
-</script>
-
-<style>
-.container { min-height: 100vh; background: #f5f7fa; display: flex; align-items: center; justify-content: center; padding: 60rpx; }
-.notice-card { background: #fff; border-radius: 24rpx; padding: 60rpx 40rpx; text-align: center; width: 100%; }
-.notice-icon { font-size: 80rpx; }
-.notice-title { font-size: 36rpx; font-weight: 700; color: #333; margin-top: 24rpx; }
-.notice-desc { font-size: 28rpx; color: #999; margin-top: 16rpx; line-height: 1.6; }
-.back-btn { margin-top: 48rpx; width: 60%; height: 80rpx; line-height: 80rpx; background: linear-gradient(135deg, #5B9BD5, #3A7CC4); color: #fff; font-size: 30rpx; border-radius: 40rpx; border: none; }
-.back-btn::after { border: none; }
-</style>
+  <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>

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

@@ -173,7 +173,7 @@ export default {
       }
     },
     goToMembership() {
-      uni.navigateTo({ url: '/pages/membership/benefits' })
+      uni.navigateTo({ url: '/pages/membership/index' })
     },
     goToVendorCenter() {
       uni.navigateTo({ url: '/pages/vendor/center' })