goPay_per.vue 7.39 KB
<template>
  <view class="container">
    <view class="content">
      <view class="card">
        <view class="yearRow">
          <view class="label">缴费年限</view>
          <view class="control">
            <image v-if="form.payYear > 1" :src="config.baseUrl_api + '/fs/static/dd_02.png'" class="icon"
                   mode="widthFix"
                   @click="minusYear"></image>
            <image v-else :src="config.baseUrl_api + '/fs/static/dd_02_g.png'" class="icon" mode="widthFix"></image>
            <text class="num">{{ form.payYear }}</text>
            <image v-if="form.payYear < 5" :src="config.baseUrl_api + '/fs/static/btn_03.png'" class="icon"
                   mode="widthFix"
                   @click="plusYear"></image>
            <image v-else :src="config.baseUrl_api + '/fs/static/btn_03_g.png'" class="icon" mode="widthFix"></image>
          </view>
        </view>
      </view>
      
      <view class="card ">
        <view class="row ">
          <text class="label">费用合计</text>
          <text class="value red">{{ form.payYear * memberFee }}</text>
        </view>
      
      </view>
      
      <view class="payRow ">
        <radio-group @change="onPayTypeChange">
          <label class="radioItem">
            <radio :checked="payType === '1'" class="custom-radio" value="1"/>
            <view class="payInfo">
              <image :src="config.baseUrl_api + '/fs/static/min.png'" class="icon" mode="widthFix"></image>
              <text>民生付</text>
            </view>
          </label>
        </radio-group>
      </view>
      
      <view class="totalRow ">
        <text class="label">支付费用合计</text>
        <text class="value redBig">{{ memberTotalFee }}</text>
      </view>
    
    </view>
    
    <view class="bottomBtn">
      <button :loading="isPaying" class="payBtn" @click="handelPay">立即支付 ¥{{ memberTotalFee }}</button>
    </view>
  
  </view>
</template>

<script setup>
import {
  ref,
  computed,
  onMounted
} from 'vue'
import {
  onLoad
} from '@dcloudio/uni-app';
import to from 'await-to-js'
import * as api from '@/common/api.js'
import {
  minShengPay
} from '@/common/pay.js'
import config from '@/config.js'

const form = ref({
  payYear: 1
})

// 支付方式
const payType = ref('1')
const isPaying = ref(false)

// 费用与优惠
const memberFee = ref(0)
const memberTotalFee = computed(() => {
  return memberFee.value * form.value.payYear
  
})
onLoad((options) => {
  if (options.baseFormData) {
    const data = JSON.parse(decodeURIComponent(options.baseFormData))
    form.value = {
      ...data,
      payYear: 1 // 年限默认1
    }
  }
  // 初始化接口
  getMyMemberCertUnitFeeApi()
})


// 减年限
const minusYear = () => {
  if (form.value.payYear > 1) {
    form.value.payYear--
  }
}

// 加年限(最大 5 年)
const plusYear = () => {
  if (form.value.payYear < 5) {
    form.value.payYear++
  }
}

// 支付方式切换
const onPayTypeChange = (e) => {
  payType.value = e.detail.value
}

const handelPay = async () => {
  if (memberTotalFee.value <= 0) {
    uni.showToast({
      title: '支付金额异常',
      icon: 'none'
    })
    return
  }
  
  // 显示 loading
  uni.showLoading({
    title: '创建订单...',
    mask: true
  })
  isPaying.value = true
  form.value.validityDate = undefined
  // 拼接完整参数
  const postData = {
    ...form.value,
    payYear: form.value.payYear,
    payType: payType.value,
    totalFee: memberTotalFee.value,
  }
  
  // 创建订单
  const [orderErr, orderRes] = await to(api.insertSinglePay(postData))
  uni.hideLoading()
  
  if (orderErr) {
    isPaying.value = false
    // uni.showToast({
    // 	title: '创建订单失败',
    // 	icon: 'none'
    // })
    return
  }
  
  if (!orderRes.data?.orderId) {
    isPaying.value = false
    uni.showToast({
      title: '订单创建异常',
      icon: 'none'
    })
    return
  }
  
  // 调起支付
  const [payErr] = await to(minShengPay(orderRes.data.orderId, orderRes.data.payResult.encryptedData))
  
  isPaying.value = false
  
  // 支付失败不跳转
  if (payErr) {
    return
  }
  
  // 支付成功,跳转页面
  uni.redirectTo({
    url: `/personal/sucPay?orderId=${orderRes.data.orderId}`
  })
}


// 获取会员费
async function getMyMemberCertUnitFeeApi() {
  const res = await api.getZtxFeeConfig()
  memberFee.value = Number(res.data.personMemberFee || 1500)
}
</script>

<style scoped>
.container {
  min-height: 100vh;
  background-color: #f7f7f7;
}

.content {
  padding: 20rpx 20rpx 120rpx;
}

.card {
  background: #fff;
  border-radius: 8rpx;
  padding: 25rpx 20rpx;
  margin-bottom: 20rpx;
}

.yearRow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 20rpx;
}

.yearRow .label {
  font-size: 28rpx;
  color: #333;
}

.yearRow .control {
  display: flex;
  align-items: center;
}

.control image {
  width: 50rpx;
  height: 50rpx;
}

.yearRow .num {
  font-size: 28rpx;
  color: #333;
  min-width: 80rpx;
  text-align: center;
  margin: 0 10rpx;
}

.row {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.row .label {
  font-size: 28rpx;
  color: #333;
}

.row .value {
  font-size: 30rpx;
  color: #C4121B;
  font-weight: 500;
}

.hintRow {
  display: flex;
  align-items: flex-start;
  font-size: 24rpx;
  line-height: 1.4;
}

.hintRow .hintText {
  color: #FF8124;
  flex: 1;
  margin-top: 10rpx;
}

.deductRow {
  background: #fff;
  padding: 20rpx 20rpx;
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 10rpx;
  border-radius: 8rpx;
}

.deductRow .label {
  font-size: 28rpx;
  color: #333;
}

.deductRow .value {
  font-size: 30rpx;
  color: #C4121B;
}

.payRow {
  background: #fff;
  border-radius: 8rpx;
  padding: 20rpx 20rpx;
  margin-bottom: 20rpx;
}

.radioItem {
  display: flex;
  align-items: center;
}

.payInfo {
  display: flex;
  align-items: center;
  margin-left: 15rpx;
}

.payInfo .icon {
  width: 40rpx;
  height: 40rpx;
  margin-right: 10rpx;
}

.payInfo text {
  font-size: 28rpx;
  color: #333;
}

.totalRow {
  background: #fff;
  border-radius: 8rpx;
  padding: 20rpx 20rpx;
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: 10rpx;
}

.totalRow .label {
  font-size: 28rpx;
  color: #333;
}

.redBig {
  font-size: 32rpx;
  color: #C4121B;
  font-weight: bold;
}

.bottomBtn {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  padding: 20rpx 20rpx;
  background: #fff;
  border-top: 1rpx solid #eee;
}

.payBtn {
  width: 100%;
  height: 88rpx;
  line-height: 88rpx;
  background-color: #C4121B;
  color: #fff;
  border-radius: 8rpx;
  font-size: 32rpx;
  text-align: center;
  border: none;
}

.payBtn[disabled] {
  background-color: #ccc;
  color: #999;
}

.red {
  color: #C4121B;
}

.icon {
  width: 30px;
}

::v-deep .custom-radio .wx-radio-input {
  width: 30rpx;
  height: 30rpx;
  border-radius: 50%;
  border: 2rpx solid #ccc;
}

::v-deep .custom-radio .wx-radio-input.wx-radio-input-checked {
  border-color: #C4121B !important;
  background: #C4121B !important;
}
</style>