index.vue 7.99 KB
<template>
  <view class="pay-confirm-page">
    <view class="hero-panel" :style="hero_panel_style">
      <text class="page-title">常住随用</text>

      <view class="amount-card" :style="amount_card_style">
        <text class="amount-label">支付金额:</text>
        <view class="amount-value-group">
          <text class="amount-value">{{ display_amount }}</text>
          <text v-if="has_amount" class="amount-unit">元</text>
        </view>
      </view>

      <text class="blessing-text">感恩随喜~</text>
    </view>

    <view class="footer-area">
      <text v-if="status_text" class="status-text">{{ status_text }}</text>

      <button
        class="pay-button"
        :style="pay_button_style"
        hover-class="pay-button-hover"
        :loading="pay_loading"
        :disabled="!order_id || pay_loading"
        @tap="handlePay"
      >
        立即支付
      </button>
    </view>
  </view>
</template>

<script setup>
import { computed, ref, watch } from 'vue'
import Taro, { useLoad } from '@tarojs/taro'
import { useWechatMiniPay } from '@/composables/useWechatMiniPay'
import { getVersionedImageAssetByName, preloadImageAssetVersion } from '@/utils/assetUrl'
import { redirectAfterPaySuccess } from '@/utils/paySuccessRedirect'

const order_id = ref('')
const amount = ref('')
const status_text = ref('')
const navigating_after_pay_success = ref(false)
const mini_program_env_version = ref('')

const {
  pay_loading,
  last_result_text,
  pay_by_order_id,
} = useWechatMiniPay()

const display_amount = computed(() => {
  const normalized_amount = String(amount.value || '').trim().replace(/元$/, '').trim()
  return normalized_amount || '--'
})

const has_amount = computed(() => display_amount.value !== '--')
const hero_panel_style = computed(() => ({
  background: `url('${getVersionedImageAssetByName('bgg@2x.png')}') center top / 100% 100% no-repeat`,
}))
const amount_card_style = computed(() => ({
  background: `url('${getVersionedImageAssetByName('money_bg@2x.png')}') center / 100% 100% no-repeat`,
}))
const pay_button_style = computed(() => ({
  background: `url('${getVersionedImageAssetByName('btnn@2x.png')}') center / 100% 100% no-repeat`,
}))

const is_release_env = computed(() => mini_program_env_version.value === 'release')

const getMiniProgramEnvVersion = () => {
  try {
    const account_info = Taro.getAccountInfoSync?.()
    return String(account_info?.miniProgram?.envVersion || account_info?.envVersion || '').trim().toLowerCase()
  } catch (error) {
    console.warn('获取小程序环境版本失败:', error)
    return ''
  }
}

const formatReleaseStatusText = (raw_text) => {
  const normalized_text = String(raw_text || '').trim()
  if (!normalized_text) return ''

  const lower_text = normalized_text.toLowerCase()

  if (normalized_text === '正在请求支付参数...') return normalized_text
  if (normalized_text === '已获取支付参数,准备拉起微信支付弹框...') return normalized_text
  if (normalized_text === '支付成功,正在跳转...') return normalized_text
  if (normalized_text.includes('支付成功')) return normalized_text

  if (lower_text.includes('cancel') || normalized_text.includes('取消支付')) {
    return '您已取消本次支付,如需继续,可重新点击支付。'
  }

  if (
    normalized_text.includes('未授权')
    || normalized_text.includes('授权失败')
    || normalized_text.includes('静默授权失败')
    || normalized_text.includes('登录')
  ) {
    return '当前登录状态需要更新,请重新进入后再试。'
  }

  if (normalized_text.includes('缺少订单编号') || normalized_text.includes('订单 ID')) {
    return '支付信息不完整,请返回上一页后重新发起。'
  }

  if (
    normalized_text.includes('获取支付参数失败')
    || normalized_text.includes('拉起支付失败')
    || normalized_text.includes('支付未完成')
    || normalized_text.includes('失败')
    || normalized_text.includes('错误')
    || normalized_text.includes('异常')
  ) {
    return '支付暂时没有完成,请稍后重试;如多次失败,请联系寺院工作人员协助处理。'
  }

  return '当前操作暂时没有完成,请稍后再试。'
}

const setStatusText = (raw_text) => {
  const normalized_text = String(raw_text || '').trim()
  if (!normalized_text) {
    status_text.value = ''
    return
  }

  status_text.value = is_release_env.value
    ? formatReleaseStatusText(normalized_text)
    : normalized_text
}

watch(last_result_text, (value) => {
  if (!pay_loading.value) {
    setStatusText(value)
  }
})

const goUserWebviewAfterPaySuccess = async () => {
  if (navigating_after_pay_success.value) return

  navigating_after_pay_success.value = true

  try {
    setStatusText('支付成功,正在跳转...')

    const redirect_result = await redirectAfterPaySuccess()
    if (redirect_result?.reason === 'missing-user-menu') {
      setStatusText('支付成功,未找到“我的”菜单,正在返回首页...')
      return
    }

    if (redirect_result?.reason === 'empty-user-link') {
      setStatusText('支付成功,但“我的”菜单地址为空,正在返回首页...')
      return
    }

    if (redirect_result?.target === 'home') {
      setStatusText('支付成功,但菜单获取失败,正在返回首页...')
    }
  } catch (error) {
    setStatusText('支付成功,但菜单获取失败,正在返回首页...')
  } finally {
    navigating_after_pay_success.value = false
  }
}

const handlePay = async () => {
  if (!order_id.value || pay_loading.value) return

  status_text.value = ''

  const pay_res = await pay_by_order_id(order_id.value, {
    auto_auth: true,
    on_status: (text) => {
      setStatusText(text)
    },
  })

  if (pay_res?.status === 'success') {
    await goUserWebviewAfterPaySuccess()
  }
}

useLoad((options) => {
  mini_program_env_version.value = getMiniProgramEnvVersion()

  preloadImageAssetVersion().catch((error) => {
    console.error('支付确认页预加载图片版本配置失败:', error)
  })

  order_id.value = String(options?.order_id || '').trim()
  amount.value = String(options?.amount || options?.money || '').trim()

  if (!order_id.value) {
    setStatusText('缺少订单编号,暂时无法发起支付。')
  }
})
</script>

<style lang="less">
.pay-confirm-page {
  min-height: 100vh;
  box-sizing: border-box;
  padding: 0 0 calc(72rpx + env(safe-area-inset-bottom));
  background: #fff;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}

.hero-panel {
  min-height: 612rpx;
  padding: 132rpx 48rpx 88rpx;
  box-sizing: border-box;
}

.page-title {
  display: block;
  text-align: center;
  font-size: 55rpx;
  line-height: 1.2;
  font-weight: 600;
  color: #2f3133;
}

.amount-card {
  margin-top: 84rpx;
  width: 100%;
  min-height: 145rpx;
  padding: 0 40rpx;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24rpx;
}

.amount-label {
  flex-shrink: 0;
  font-size: 30rpx;
  color: #353535;
}

.amount-value-group {
  display: flex;
  align-items: baseline;
  justify-content: flex-end;
  flex: 1;
  min-width: 0;
  color: #252525;
}

.amount-value {
  max-width: 100%;
  font-size: 72rpx;
  line-height: 1;
  font-weight: 500;
  text-align: right;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.amount-unit {
  margin-left: 14rpx;
  font-size: 30rpx;
  line-height: 1;
}

.blessing-text {
  display: block;
  margin-top: 72rpx;
  text-align: center;
  font-size: 32rpx;
  line-height: 1.4;
  color: #3a3a3a;
}

.footer-area {
  padding: 0 84rpx;
  box-sizing: border-box;
}

.status-text {
  display: block;
  margin-bottom: 28rpx;
  text-align: center;
  font-size: 22rpx;
  line-height: 1.7;
  color: #8a6b45;
}

.pay-button {
  width: 100%;
  height: 90rpx;
  line-height: 90rpx;
  padding: 0;
  border: 0;
  border-radius: 0;
  font-size: 34rpx;
  font-weight: 500;
  color: #fff6eb;

  &::after {
    border: 0;
  }

  &[disabled] {
    opacity: 0.72;
    color: #fff6eb;
  }
}

.pay-button-hover {
  opacity: 0.92;
}
</style>