index.vue 5.76 KB
<!--
 * @Date: 2024-01-16 13:19:23
 * @LastEditors: hookehuyr hookehuyr@gmail.com
 * @LastEditTime: 2026-01-24 12:46:06
 * @FilePath: /xyxBooking-weapp/src/pages/bookingDetail/index.vue
 * @Description: 预约记录详情
-->
<template>
  <view class="booking-detail-page">
    <qrCode
      ref="qr_code_ref"
      :status="qrCodeStatus"
      type="detail"
      :payId="pay_id"
      @status-change="handleQrCodeStatusChange"
    ></qrCode>
    <view v-if="billInfo.pay_id" class="detail-wrapper">
      <view class="detail-item">
        <view>参访时间:</view>
        <view>{{ billInfo?.datetime }}</view>
      </view>
      <view class="detail-item">
        <view>参访人数:</view>
        <view>{{ billInfo?.total_qty }} 人</view>
      </view>
      <view class="detail-item">
        <view>支付金额:</view>
        <view>¥ {{ billInfo?.total_amt }}</view>
      </view>
      <view class="detail-item">
        <view>下单时间:</view>
        <view>{{ billInfo?.order_time }}</view>
      </view>
      <view class="detail-item">
        <view>订单编号:</view>
        <view>{{ billInfo?.pay_id }}</view>
      </view>
      <view class="detail-item">
        <view>订单状态:</view>
        <view>{{ qrCodeStatusText }}</view>
      </view>
    </view>
    <view style="height: 160rpx"></view>
    <!-- 正式按钮 -->
    <view
      v-if="billInfo.status === CodeStatus.SUCCESS && billInfo.show_cancel_reserve === 1"
      class="cancel-wrapper"
    >
      <view @tap="cancelBooking" class="cancel-btn">取消预约</view>
    </view>
    <view v-else-if="billInfo.status === CodeStatus.USED" class="cancel-wrapper">
      <view class="cancel-btn disabled">订单已核销,无法取消</view>
    </view>
  </view>
</template>

<script setup>
import { ref, computed } from 'vue'
import Taro, { useDidShow, useDidHide, useRouter as useTaroRouter } from '@tarojs/taro'
import qrCode from '@/components/qrCode'
import { billInfoAPI, icbcRefundAPI } from '@/api/index'
import { formatDatetime, get_bill_status_text } from '@/utils/tools'
import { refresh_offline_booking_cache } from '@/composables/useOfflineBookingCache'
import { showConfirm, showSuccess, showError, showLoading, hideLoading } from '@/utils/toast'

const router = useTaroRouter()

const pay_id = ref('')
const qrCodeStatus = ref('')
const billInfo = ref({})
const qr_code_ref = ref(null)

/**
 * @description 预约码状态枚举(与后端约定)
 * @readonly
 */
const CodeStatus = {
  APPLY: '1',
  PAYING: '2',
  SUCCESS: '3',
  CANCEL: '5',
  CANCELED: '7',
  USED: '9',
  REFUNDING: '11'
}

/**
 * @description 订单状态文案
 * @returns {string} 状态文案
 */
const qrCodeStatusText = computed(() => {
  return get_bill_status_text(billInfo.value?.status)
})

/**
 * @description 处理二维码状态变化
 * - 当二维码被核销时,刷新订单信息以更新页面状态
 * @param {string} newStatus 新的二维码状态(来自 qrCode 组件)
 * @returns {Promise<void>} 无返回值
 */
const handleQrCodeStatusChange = async newStatus => {
  // ⚠️ 注意:qrCode 组件的 USED = '7',但 CodeStatus.USED = '9'
  // 这里需要兼容两种情况
  const isUsedStatus = newStatus === CodeStatus.USED || newStatus === '7'

  if (isUsedStatus && pay_id.value) {
    // 调用 API 刷新订单信息
    const { code, data } = await billInfoAPI({ pay_id: pay_id.value })
    if (code) {
      data.datetime = data && formatDatetime(data)
      data.order_time = data.created_time ? data.created_time.slice(0, -3) : ''
      billInfo.value = data
    }
  }
}

/**
 * @description 取消预约
 * - 成功后刷新离线缓存(保证弱网/离线模式数据一致)
 * @returns {Promise<void>} 无返回值
 */
const cancelBooking = async () => {
  const confirm = await showConfirm('是否取消预约?', '温馨提示')

  if (confirm) {
    showLoading('取消中...')
    const { code } = await icbcRefundAPI({ pay_id: pay_id.value })
    hideLoading()
    if (code) {
      showSuccess('取消成功')
      try {
        await refresh_offline_booking_cache({ force: true })
        // eslint-disable-next-line no-empty
      } catch (e) {}
      Taro.navigateBack()
    } else {
      showError('取消失败')
    }
  }
}

useDidShow(async () => {
  qr_code_ref.value?.start_polling?.()
  pay_id.value = router.params.pay_id
  if (pay_id.value) {
    const { code, data } = await billInfoAPI({ pay_id: pay_id.value })
    if (code) {
      data.datetime = data && formatDatetime(data)
      data.order_time = data.created_time ? data.created_time.slice(0, -3) : ''
      billInfo.value = data
    }
  }
})

useDidHide(() => {
  qr_code_ref.value?.stop_polling?.()
})
</script>

<style lang="less">
.booking-detail-page {
  min-height: 100vh;
  background-color: #f6f6f6;
  padding: 32rpx;

  .detail-wrapper {
    background-color: #fff;
    border-radius: 16rpx;
    padding: 32rpx;
    margin-top: 32rpx;
    box-shadow: 0 0 29rpx 0 rgba(106, 106, 106, 0.1);

    .detail-item {
      display: flex;
      justify-content: space-between;
      margin-bottom: 26rpx;
      color: #333;
      font-size: 30rpx;

      &:last-child {
        margin-bottom: 0;
      }

      view:first-child {
        color: #999;
        width: 160rpx;
      }
      view:last-child {
        flex: 1;
        text-align: right;
      }
    }
  }

  .cancel-wrapper {
    position: fixed;
    bottom: 0;
    left: 0;
    width: 750rpx;
    background-color: #fff;
    padding: 32rpx;
    box-sizing: border-box;

    .cancel-btn {
      background-color: #fff;
      color: #a67939;
      border: 2rpx solid #a67939;
      text-align: center;
      padding: 26rpx 0;
      border-radius: 16rpx;
      font-size: 35rpx;

      &.disabled {
        color: #999;
        border-color: #999;
        background-color: #f5f5f5;
      }
    }
  }
}
</style>