Browse Source

feat: 新增支付结果页 result.vue(成功态+赠券/失败态+续付)

Xiaogang Liao 2 months ago
parent
commit
fe8d91e467
1 changed files with 286 additions and 0 deletions
  1. 286 0
      cfc-frontend/pages/membership/result.vue

+ 286 - 0
cfc-frontend/pages/membership/result.vue

@@ -0,0 +1,286 @@
+<template>
+  <view class="result-page">
+    <!-- 成功态 -->
+    <view class="result-body" v-if="status === 'success'">
+      <view class="result-icon success">
+        <text class="icon-text">✅</text>
+      </view>
+      <text class="result-title">开通成功</text>
+      <text class="result-sub">{{ levelName }} {{ levelCode }}</text>
+      <view class="info-card">
+        <view class="info-row">
+          <text class="info-label">有效期至</text>
+          <text class="info-value">{{ expireText }}</text>
+        </view>
+        <view class="info-row" v-if="giftCoupons.length > 0">
+          <text class="info-label">开通赠券</text>
+          <text class="info-value gift">{{ giftCouponsText }}</text>
+        </view>
+      </view>
+      <view class="btn-group">
+        <button class="btn-primary" @click="goBenefits">查看我的权益</button>
+        <button class="btn-secondary" @click="goHome">返回会员中心</button>
+      </view>
+    </view>
+
+    <!-- 失败态 -->
+    <view class="result-body" v-else>
+      <view class="result-icon fail">
+        <text class="icon-text">❌</text>
+      </view>
+      <text class="result-title">支付未完成</text>
+      <text class="result-sub">{{ failReason }}</text>
+      <view class="btn-group">
+        <button class="btn-primary" v-if="canContinue" @click="continuePay">继续支付</button>
+        <button class="btn-primary" v-else @click="retryPay">重新支付</button>
+        <button class="btn-secondary" @click="goUpgrade">返回套餐选择</button>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script>
+import { getMyMembership, getJoinCouponRules, nextOrder, requestVirtualPayment } from '../../utils/api.js'
+import { parseDate } from '../../utils/format.js'
+
+export default {
+  data() {
+    return {
+      orderNo: '',
+      status: 'success',
+      levelName: '会员',
+      levelCode: '',
+      expireText: '',
+      giftCoupons: [],
+      failReason: '支付未完成,请重试',
+      canContinue: false,
+      nextPayParams: null
+    }
+  },
+  computed: {
+    giftCouponsText: function() {
+      var self = this
+      return this.giftCoupons.map(function(r) {
+        return '「' + r.name + '」×' + (r.grantQuantity || 1)
+      }).join('、')
+    }
+  },
+  onLoad: function(options) {
+    this.orderNo = options.orderNo || ''
+    this.status = options.status || 'success'
+    if (this.status === 'success') {
+      this.loadSuccess()
+    } else {
+      this.loadFail()
+    }
+  },
+  methods: {
+    loadSuccess: function() {
+      var self = this
+      // 轮询确认开通
+      var times = 0
+      var timer = setInterval(function() {
+        times++
+        getMyMembership().then(function(res) {
+          var m = res.data || {}
+          var mem = m.membership
+          if (mem && mem.isActive) {
+            clearInterval(timer)
+            self.levelCode = mem.levelCode || ''
+            self.levelName = mem.levelName || self.levelName
+            self.expireText = self.formatDate(mem.endDate)
+            self.loadGiftCoupons()
+          } else if (times >= 8) {
+            clearInterval(timer)
+            self.loadGiftCoupons()
+          }
+        }).catch(function() {
+          if (times >= 8) {
+            clearInterval(timer)
+            self.loadGiftCoupons()
+          }
+        })
+      }, 1500)
+    },
+    loadGiftCoupons: function() {
+      var self = this
+      getJoinCouponRules().then(function(res) {
+        var rules = res.data || []
+        self.giftCoupons = rules.filter(function(r) {
+          return r.grantLevelCode === self.levelCode
+        })
+      }).catch(function() {})
+    },
+    loadFail: function() {
+      var self = this
+      if (!this.orderNo) {
+        this.failReason = '订单信息缺失'
+        return
+      }
+      // 尝试获取下一期支付参数(拆单未付清时可用)
+      nextOrder({ orderNo: this.orderNo }).then(function(res) {
+        var nd = res.data
+        if (nd && nd.done) {
+          self.failReason = '订单已全部支付,请稍后查看会员状态'
+          self.canContinue = false
+        } else if (nd && nd.orderInfo && nd.sign) {
+          self.canContinue = true
+          self.nextPayParams = nd
+          self.failReason = '第 ' + (nd.currentPeriod || 1) + ' 期尚未支付'
+        } else {
+          self.failReason = '支付未完成,请重试'
+          self.canContinue = false
+        }
+      }).catch(function() {
+        self.failReason = '支付未完成,请重试'
+      })
+    },
+    continuePay: function() {
+      var self = this
+      if (!this.nextPayParams) {
+        this.retryPay()
+        return
+      }
+      requestVirtualPayment(this.nextPayParams, {
+        success: function() {
+          uni.redirectTo({ url: '/pages/membership/result?orderNo=' + self.orderNo + '&status=success' })
+        },
+        fail: function(err) {
+          var errCode = err && err.errCode
+          if (errCode === -2) {
+            uni.showToast({ title: '支付已取消', icon: 'none' })
+          } else {
+            uni.showToast({ title: '支付未完成', icon: 'none' })
+          }
+        }
+      })
+    },
+    retryPay: function() {
+      uni.redirectTo({ url: '/pages/membership/upgrade' })
+    },
+    goBenefits: function() {
+      uni.redirectTo({ url: '/pages/membership/benefits' })
+    },
+    goHome: function() {
+      uni.switchTab({ url: '/pages/index/index' })
+    },
+    goUpgrade: function() {
+      uni.redirectTo({ url: '/pages/membership/upgrade' })
+    },
+    formatDate: function(date) {
+      if (!date) return ''
+      var d = parseDate(date)
+      if (!d) return ''
+      return d.getFullYear() + '-' + String(d.getMonth() + 1).padStart(2, '0') + '-' + String(d.getDate()).padStart(2, '0')
+    }
+  }
+}
+</script>
+
+<style scoped>
+.result-page {
+  min-height: 100vh;
+  background: #FFF7ED;
+  display: flex;
+  justify-content: center;
+  padding: 120rpx 30rpx 60rpx;
+  box-sizing: border-box;
+}
+.result-body {
+  width: 100%;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+}
+.result-icon {
+  width: 160rpx;
+  height: 160rpx;
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-bottom: 32rpx;
+}
+.result-icon.success {
+  background: #D1FAE5;
+}
+.result-icon.fail {
+  background: #FEE2E2;
+}
+.icon-text {
+  font-size: 80rpx;
+}
+.result-title {
+  font-size: 40rpx;
+  font-weight: bold;
+  color: #1E293B;
+  display: block;
+  margin-bottom: 12rpx;
+}
+.result-sub {
+  font-size: 28rpx;
+  color: #64748B;
+  display: block;
+  margin-bottom: 48rpx;
+}
+.info-card {
+  width: 100%;
+  background: #FFFFFF;
+  border-radius: 20rpx;
+  padding: 30rpx;
+  box-shadow: 0 4rpx 24rpx rgba(249, 115, 22, 0.08);
+  margin-bottom: 60rpx;
+}
+.info-row {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 16rpx 0;
+}
+.info-label {
+  font-size: 28rpx;
+  color: #64748B;
+}
+.info-value {
+  font-size: 28rpx;
+  color: #1E293B;
+  font-weight: 600;
+}
+.info-value.gift {
+  color: #F97316;
+}
+.btn-group {
+  width: 100%;
+  display: flex;
+  flex-direction: column;
+}
+.btn-primary {
+  width: 100%;
+  height: 96rpx;
+  line-height: 96rpx;
+  background: linear-gradient(135deg, #F97316 0%, #EA580C 100%);
+  color: #fff;
+  font-size: 32rpx;
+  font-weight: 600;
+  border-radius: 48rpx;
+  border: none;
+  margin-bottom: 24rpx;
+}
+.btn-primary::after {
+  border: none;
+}
+.btn-secondary {
+  width: 100%;
+  height: 96rpx;
+  line-height: 96rpx;
+  background: #FFFFFF;
+  color: #F97316;
+  font-size: 32rpx;
+  font-weight: 600;
+  border-radius: 48rpx;
+  border: 2rpx solid #FED7AA;
+}
+.btn-secondary::after {
+  border: none;
+}
+</style>