index.vue 7.35 KB
<!--
 * @Date: 2026-01-08 13:01:20
 * @LastEditors: hookehuyr hookehuyr@gmail.com
 * @LastEditTime: 2026-02-02 13:36:30
 * @FilePath: /xyxBooking-weapp/src/pages/verificationResult/index.vue
 * @Description: 核销结果页面
-->
<template>
  <view class="min-h-screen bg-gray-100 p-4 verify-page">
    <view class="rounded-2xl bg-white p-4 shadow-sm">
      <view class="flex items-center">
        <view class="flex h-14 w-14 items-center justify-center rounded-full bg-amber-50">
          <icon :type="status_icon_type" size="30" :color="status_icon_color" />
        </view>
        <view class="ml-4 flex-1">
          <view class="text-lg font-semibold text-gray-900">{{ status_title }}</view>
          <!-- <view class="mt-1 text-sm text-gray-500">{{ msg }}</view> -->
        </view>
      </view>
    </view>

    <!-- <view class="mt-4 rounded-2xl bg-white p-5 shadow-sm">
            <view class="text-xs text-gray-500">核销权限</view>
            <view class="mt-2 text-sm font-medium text-gray-900">{{ can_redeem_text }}</view>
        </view> -->

    <view class="mt-4 rounded-2xl bg-white p-5 shadow-sm">
      <view class="text-sm text-gray-500 mb-4">核销记录信息</view>

      <template v-if="verify_info && Object.keys(verify_info).length > 0">
        <view
          class="flex justify-between items-center py-3 border-b border-gray-50 border-solid last:border-0"
        >
          <view class="text-gray-500 text-base">姓名</view>
          <view class="text-gray-900 text-lg font-medium">{{
            verify_info.person_name || '-'
          }}</view>
        </view>

        <view
          class="flex justify-between items-center py-3 border-b border-gray-50 border-solid last:border-0"
        >
          <view class="text-gray-500 text-base">证件号码</view>
          <view class="text-gray-900 text-lg font-medium">{{
            formatIdNumber(verify_info.id_number)
          }}</view>
        </view>

        <view
          class="flex justify-between items-center py-3 border-b border-gray-50 border-solid last:border-0"
        >
          <view class="text-gray-500 text-base">状态</view>
          <view class="text-amber-600 text-lg font-medium">{{ verify_info.status || '-' }}</view>
        </view>

        <view
          class="flex justify-between items-center py-3 border-b border-gray-50 border-solid last:border-0"
        >
          <view class="text-gray-500 text-base">预约开始</view>
          <view class="text-gray-900 text-lg font-medium">{{ verify_info.begin_time || '-' }}</view>
        </view>

        <view
          class="flex justify-between items-center py-3 border-b border-gray-50 border-solid last:border-0"
        >
          <view class="text-gray-500 text-base">预约结束</view>
          <view class="text-gray-900 text-lg font-medium">{{ verify_info.end_time || '-' }}</view>
        </view>
      </template>
      <view v-else-if="verify_status === 'fail'" class="py-2">
        <view class="text-gray-500 text-base mb-2">失败原因</view>
        <view class="text-red-500 text-lg font-medium break-all">{{ msg }}</view>
      </view>
      <!-- <view v-else-if="verify_code" class="mt-2 break-all whitespace-pre-wrap text-lg font-medium text-gray-900">{{ verify_code }}</view> -->
      <view v-else class="mt-2 text-base text-gray-400">暂无核销信息</view>
    </view>

    <view class="verify-footer">
      <nut-button
        block
        color="#A67939"
        :loading="verify_status === 'verifying'"
        :disabled="verify_status === 'verifying'"
        class="verify-btn"
        @tap="start_scan_and_verify"
      >
        <view class="flex items-center justify-center w-full">
          <IconFont name="photograph" size="52rpx" />
        </view>
      </nut-button>
      <view class="mt-4 text-center text-sm text-gray-400">扫描预约码二维码进行核销</view>
    </view>
  </view>
</template>

<script setup>
import { ref, computed } from 'vue'
import Taro, { useRouter, useDidShow } from '@tarojs/taro'
import { verifyTicketAPI, checkRedeemPermissionAPI } from '@/api/redeem'
import { IconFont } from '@nutui/icons-vue-taro'
import { mainStore } from '@/stores/main'
import { useReplace } from '@/hooks/useGo'
import { mask_id_number } from '@/utils/tools'
import { showError, showLoading, hideLoading } from '@/utils/toast'

const router = useRouter()
const verify_code = ref('')
const verify_info = ref({}) // 新增:存储核销返回的详细信息
const verify_status = ref('idle')
const msg = ref('请点击下方按钮进行核销')
const store = mainStore()
const replace = useReplace()

const formatIdNumber = id => mask_id_number(id, { keep_start: 6, keep_end: 4 })

const status_title = computed(() => {
  if (verify_status.value === 'verifying') {
    return '核销中'
  }
  if (verify_status.value === 'success') {
    return '核销成功'
  }
  if (verify_status.value === 'fail') {
    return '核销失败'
  }
  return '核销'
})

const status_icon_type = computed(() => {
  if (verify_status.value === 'verifying') {
    return 'waiting'
  }
  if (verify_status.value === 'success') {
    return 'success'
  }
  if (verify_status.value === 'fail') {
    return 'cancel'
  }
  return 'info'
})

const status_icon_color = computed(() => {
  if (verify_status.value === 'fail') {
    return '#E24A4A'
  }
  return '#A67939'
})

// const can_redeem_text = computed(() => {
//     if (store?.appUserInfo?.can_redeem === true) return '已授权'
//     return '未授权'
// })

/**
 * @description: 核销预约码
 * @param {string} code
 * @return {void}
 */

const verify_ticket = async code => {
  if (!code) {
    return
  }
  if (verify_status.value === 'verifying') {
    return
  }

  verify_code.value = code
  verify_status.value = 'verifying'
  msg.value = '核销中...'

  showLoading('核销中...')
  try {
    const res = await verifyTicketAPI({ qr_code: code })
    if (res?.code === 1) {
      verify_status.value = 'success'
      msg.value = res?.msg || '核销成功'
      // 保存核销返回的详细信息
      verify_info.value = res?.data || {}
      return
    }
    verify_status.value = 'fail'
    msg.value = res?.msg || '核销失败'
    verify_info.value = {}
  } catch (e) {
    verify_status.value = 'fail'
    msg.value = '核销失败'
    verify_info.value = {}
    showError(msg.value)
  } finally {
    hideLoading()
  }
}

useDidShow(async () => {
  const permission_res = await checkRedeemPermissionAPI()
  if (permission_res?.code !== 1) {
    replace('volunteerLogin')
    return
  }

  if (permission_res?.data) {
    store.changeUserInfo(permission_res.data)
  }
  if (permission_res?.data?.can_redeem !== true) {
    replace('volunteerLogin')
    return
  }

  const code = router?.params?.result || ''
  if (code && verify_code.value !== code) {
    await verify_ticket(code)
  }
})

const start_scan_and_verify = () => {
  Taro.scanCode({
    success: res => {
      verify_ticket(res?.result || '')
    },
    fail: () => {}
  })
}
</script>

<style lang="less">
.verify-page {
  padding-bottom: 220rpx;
}

.verify-footer {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  width: 750rpx;
  padding: 24rpx 32rpx calc(24rpx + env(safe-area-inset-bottom));
  background-color: #ffffff;
  box-sizing: border-box;
  box-shadow: 0 -10rpx 8rpx 0 rgba(0, 0, 0, 0.08);
}

.verify-btn {
  font-size: 36rpx;
  height: 104rpx;
  line-height: 104rpx;
  border-radius: 16rpx;
  font-weight: 600;
}
</style>