qrCodeSearch.vue 6.47 KB
<!--
 * @Date: 2024-01-16 10:06:47
 * @LastEditors: hookehuyr hookehuyr@gmail.com
 * @LastEditTime: 2026-01-14 21:57:40
 * @FilePath: /xyxBooking-weapp/src/components/qrCodeSearch.vue
 * @Description: 预约码卡组件
-->
<template>
  <view class="qr-code-page">
    <view v-if="userinfo.qr_code" class="show-qrcode">
      <view class="qrcode-content">
        <view class="user-info">{{ userinfo.name }}&nbsp;{{ userinfo.id }}</view>
        <view class="user-qrcode">
          <view class="left">
            <!-- <image src="https://cdn.ipadbiz.cn/xys/booking/%E5%B7%A6@2x.png"> -->
          </view>
          <view class="center">
            <image :src="userinfo.qr_code_url" mode="aspectFit" />
            <view
              v-if="useStatus === STATUS_CODE.CANCELED || useStatus === STATUS_CODE.USED"
              class="qrcode-used"
            >
              <view class="overlay"></view>
              <text class="status-text">二维码{{ qr_code_status[useStatus] }}</text>
            </view>
          </view>
          <view class="right">
            <!-- <image src="https://cdn.ipadbiz.cn/xys/booking/%E5%8F%B3@2x.png"> -->
          </view>
        </view>
        <view style="color: red; margin-top: 32rpx">{{ userinfo.datetime }}</view>
      </view>
    </view>
    <view v-else class="no-qrcode">
      <image
        src="https://cdn.ipadbiz.cn/xys/booking/%E6%9A%82%E6%97%A0@2x.png"
        style="width: 320rpx; height: 320rpx"
      />
      <view class="no-qrcode-title">您还没有预约过今天参观</view>
    </view>
  </view>
</template>

<script setup>
import { ref, onMounted, watch, onUnmounted } from 'vue'
import { formatDatetime } from '@/utils/tools'
import { qrcodeStatusAPI, queryQrCodeAPI } from '@/api/index'
import BASE_URL from '@/utils/config'

const props = defineProps({
  id: {
    type: String,
    default: ''
  },
  id_type: {
    type: Number,
    default: 1
  }
})

const userinfo = ref({})

const replaceMiddleCharacters = input_string => {
  if (!input_string || input_string.length < 15) {
    return input_string
  }
  const start = Math.floor((input_string.length - 8) / 2)
  const end = start + 8
  const replacement = '*'.repeat(8)
  return input_string.substring(0, start) + replacement + input_string.substring(end)
}

const formatId = id => replaceMiddleCharacters(id)

const useStatus = ref('0')
const is_loading = ref(false)
let is_destroyed = false

const qr_code_status = {
  1: '未激活',
  3: '待使用',
  5: '被取消',
  7: '已使用'
}

const STATUS_CODE = {
  APPLY: '1',
  SUCCESS: '3',
  CANCELED: '5',
  USED: '7'
}

const build_qr_code_url = qr_code => {
  if (!qr_code) {
    return ''
  }
  return `${BASE_URL}/admin?m=srv&a=get_qrcode&key=${encodeURIComponent(String(qr_code))}`
}

/**
 * @description: 格式化预约码卡数据
 * @param {*} raw 原始数据
 * @return {*} 格式化后的数据
 */

const normalize_item = raw => {
  if (!raw || typeof raw !== 'object') {
    return null
  }
  const qr_code = raw.qr_code ? String(raw.qr_code) : ''
  const id_number = raw.id_number ? String(raw.id_number) : ''
  return {
    ...raw,
    qr_code,
    qr_code_url: build_qr_code_url(qr_code),
    datetime: formatDatetime({ begin_time: raw.begin_time, end_time: raw.end_time }),
    id: formatId(id_number)
  }
}

/**
 * @description: 重置状态
 */

const reset_state = () => {
  userinfo.value = {}
  useStatus.value = '0'
}

/**
 * @description: 加载预约码卡状态
 * @param {*} qr_code 预约码
 * @return {*} 状态码
 */

const load_qr_code_status = async qr_code => {
  if (!qr_code) {
    return
  }
  const res = await qrcodeStatusAPI({ qr_code })
  if (is_destroyed) {
    return
  }
  if (!res || res.code !== 1) {
    return
  }
  const status = res?.data?.status
  if (status === undefined || status === null) {
    return
  }
  useStatus.value = String(status)
}

/**
 * @description: 加载预约码卡信息
 * @param {*} id_number 身份证号
 * @return {*} 预约码卡信息
 */

const load_qr_code_info = async id_number => {
  const id = String(id_number || '').trim()
  if (!id) {
    reset_state()
    return
  }

  is_loading.value = true
  const params = { id_number: id }
  if (props.id_type) {
    params.id_type = props.id_type
  }
  const res = await queryQrCodeAPI(params)
  if (is_destroyed) {
    return
  }
  is_loading.value = false

  if (!res || res.code !== 1 || !res.data) {
    reset_state()
    return
  }

  const raw = Array.isArray(res.data) ? res.data[0] : res.data
  const item = normalize_item(raw)
  if (!item || !item.qr_code) {
    reset_state()
    return
  }

  userinfo.value = item
  await load_qr_code_status(item.qr_code)
}

onUnmounted(() => {
  is_destroyed = true
})

onMounted(() => {
  load_qr_code_info(props.id)
})

watch(
  () => [props.id, props.id_type],
  ([val]) => {
    if (is_loading.value) {
      return
    }
    load_qr_code_info(val)
  }
)
</script>

<style lang="less">
.qr-code-page {
  .qrcode-content {
    padding: 32rpx 0;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    background-color: #fff;
    border-radius: 16rpx;
    box-shadow: 0 0 29rpx 0 rgba(106, 106, 106, 0.27);

    .user-info {
      color: #a6a6a6;
      font-size: 37rpx;
      margin-top: 16rpx;
      margin-bottom: 16rpx;
    }
    .user-qrcode {
      display: flex;
      align-items: center;
      .center {
        border: 2rpx solid #d1d1d1;
        border-radius: 40rpx;
        padding: 16rpx;
        position: relative;
        image {
          width: 480rpx;
          height: 480rpx;
        }
        .qrcode-used {
          position: absolute;
          top: 0;
          left: 0;
          right: 0;
          bottom: 0;
          border-radius: 40rpx;
          overflow: hidden;

          .overlay {
            width: 100%;
            height: 100%;
            background-image: url('https://cdn.ipadbiz.cn/xys/booking/southeast.jpeg');
            background-size: contain;
            opacity: 0.9;
          }

          .status-text {
            color: #a67939;
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            font-size: 38rpx;
            white-space: nowrap;
            font-weight: bold;
            z-index: 10;
          }
        }
      }
    }
  }

  .no-qrcode {
    display: flex;
    justify-content: center;
    align-items: center;
    flex-direction: column;
    margin-bottom: 32rpx;

    .no-qrcode-title {
      color: #a67939;
      font-size: 34rpx;
    }
  }
}
</style>