shinnosuke
4 天以前 5ce0d76154bf4c1d70543476cdfad9a30195b0f8
rencaika/pages/merchant/merchant.uvue
@@ -10,7 +10,7 @@
         </view>
      </view>
      <scroll-view :class="'content' + (isMedicalMerchant ? ' content-full' : '')" scroll-y @scroll="handleScroll">
      <scroll-view :show-scrollbar="false" :class="'content' + (isMedicalMerchant ? ' content-full' : '')" scroll-y @scroll="handleScroll">
         <view class="hero">
            <swiper class="hero-swiper" :indicator-dots="displayedHeroImages.length > 1" indicator-color="rgba(255,255,255,.55)" indicator-active-color="#FFFFFF" circular="true">
               <swiper-item v-for="(imageUrl, index) in displayedHeroImages" :key="index"><image class="hero-image" :src="imageUrl" mode="aspectFill" @error="replaceHeroImage(index)"></image></swiper-item>
@@ -25,7 +25,7 @@
               <view v-if="!merchantAvailable" class="merchant-unavailable"><text>{{merchantUnavailableReason}}</text></view>
               <view v-if="isParkingMerchant" class="parking-title-block">
                  <view class="parking-title-row"><text class="merchant-title">{{merchantName}}</text><view class="round-action" @tap="callMerchant"><image src="/static/merchant-assets/phone.svg"></image></view></view>
                  <text class="parking-free">{{parkingBenefitText}}</text>
                  <text class="parking-free">{{merchantServiceLabel != '' ? merchantServiceLabel : parkingBenefitText}}</text>
               </view>
               <view v-else-if="isMedicalMerchant" class="merchant-title-row">
                  <text class="merchant-title">{{merchantName}}</text>
@@ -33,7 +33,7 @@
               </view>
               <view v-else class="merchant-title-row">
                  <text class="merchant-title">{{merchantName}}</text>
                  <view class="merchant-tags"><text v-if="merchantHasCoupon && !isFuelMerchant" class="coupon-tag">券</text><text v-if="merchantDiscount != '' && !isFuelMerchant" class="discount-tag">{{merchantDiscount}}</text><text v-if="isHotelMerchant && merchantConsumeDiscount != ''" class="discount-tag">{{merchantConsumeDiscount}}</text></view>
                  <view v-if="merchantDisplayTags.length > 0" class="merchant-tags"><text v-for="tag in merchantDisplayTags" :key="tag" :class="tag == '券' ? 'coupon-tag' : 'discount-tag'">{{tag}}</text></view>
               </view>
               <view v-if="(!isFuelMerchant && !isHotelMerchant && merchantServiceLabel != '') || applicableCards != ''" class="service-tags"><text v-if="!isParkingMerchant && !isMedicalMerchant && !isFuelMerchant && !isHotelMerchant && merchantServiceLabel != ''">{{merchantServiceLabel}}</text><text v-if="applicableCards != ''">{{applicableCards}}</text></view>
               <view v-if="!isParkingMerchant && merchantType != 'apartment'" class="merchant-address-row">
@@ -73,7 +73,7 @@
                     <image class="package-image" :src="item.image" mode="aspectFill" @error="replaceImage(item)"></image>
                     <view class="package-info">
                        <text class="package-name">{{item.name}}</text>
                        <view class="package-tags"><text v-if="merchantHasCoupon" class="coupon-tag">券</text><text v-if="merchantDiscount != ''" class="discount-tag">{{merchantDiscount}}</text><text v-if="isHotelMerchant && merchantConsumeDiscount != ''" class="discount-tag">{{merchantConsumeDiscount}}</text></view>
                        <view class="package-tags"><text v-for="tag in merchantDisplayTags" :key="tag" :class="tag == '券' ? 'coupon-tag' : 'discount-tag'">{{tag}}</text></view>
                        <view class="package-bottom"><view class="price-row"><text class="price-symbol">¥</text><text class="price">{{item.price}}</text><text v-if="!isHotelMerchant" class="old-price">¥{{item.originalPrice}}</text></view><view class="buy-button"><text>{{item.available === false ? '已售罄' : (isHotelMerchant ? '预约' : '购买')}}</text></view></view>
                     </view>
                  </view>
@@ -123,9 +123,9 @@
<script setup lang="uts">
   import { apiEnvironment, isApiConfigured } from '../../config/api.uts'
   import { getFuelMerchantDetail, getMedicalPackages, getMerchantDetail, getParkingMerchantDetail, getProducts, getTalentCardProfile, submitFuelMobileRegistration } from '../../services/portal.uts'
   import { getBenefitQuota, getFuelMerchantDetail, getMedicalPackages, getMerchantDetail, getParkingMerchantDetail, getProducts, getTalentCardProfile, submitFuelMobileRegistration } from '../../services/portal.uts'
   import { resolveApiUrl } from '../../services/http.uts'
   import { getSession } from '../../services/session.uts'
   import { resolveTalentId } from '../../utils/talent-id.uts'
   import { safeBack } from '../../utils/navigation.uts'
   import { submitBusiness } from '../../services/business.uts'
@@ -174,17 +174,29 @@
   const merchantHasCoupon = ref(false)
   const merchantDiscount = ref('')
   const merchantConsumeDiscount = ref('')
   const normalizeHotelCardLabel = (value: string): string => {
      let label = value.trim()
      const separator = label.indexOf('|')
      if (separator >= 0) label = label.substring(0, separator).trim()
      label = label.replace('(外地)', '').replace('(本地)', '').trim()
      if (label == '') return ''
      return label.endsWith('可用') ? label : label + '可用'
   const merchantBenefitTags = ref<string[]>([])
   const merchantBenefitTagsLoaded = ref(false)
   const merchantDisplayTags = computed((): string[] => {
      if (isFuelMerchant.value || ['employment', 'finance', 'approval', 'apartment', 'housing', 'car', 'appliance', 'digital', 'telecom'].indexOf(merchantType.value) >= 0) return []
      if (merchantBenefitTagsLoaded.value) return merchantBenefitTags.value
      const tags: string[] = []
      if (merchantHasCoupon.value) tags.push('券')
      if (merchantDiscount.value != '') tags.push(merchantDiscount.value)
      if (isHotelMerchant.value && merchantConsumeDiscount.value != '') tags.push(merchantConsumeDiscount.value)
      return tags
   })
   const readBenefitTags = (data: any): string[] => {
      const tags: string[] = []
      const records: any[] = data != null && data.benefitTags instanceof Array ? data.benefitTags as any[] : []
      for (let index = 0; index < records.length; index++) {
         const tag = String(records[index]).trim()
         if (tag != '' && tags.indexOf(tag) < 0) tags.push(tag)
      }
      return tags
   }
   const merchantAddress = ref('四川省攀枝花市东区炳三区新福路20号中环天地负一层')
   const merchantServiceLabel = ref('到店消费')
   const applicableCards = ref('卓越卡可用')
   const merchantServiceLabel = ref('')
   const applicableCards = ref('')
   const fuelBenefitId = ref('')
   const parkingBenefitId = ref('')
   const parkingBenefitText = ref('免费')
@@ -215,30 +227,30 @@
   const applicableStores = ref<any[]>([])
   const packages = ref([
      { id: 'food-set-001', name: '【川味盛宴】川菜12人餐', discount: '6折', price: '138', originalPrice: '266', image: '/static/food-assets/food-1.jpg' },
      { id: 'food-set-002', name: '【川香四溢】川菜4人餐', discount: '6折', price: '118', originalPrice: '198', image: '/static/food-assets/food-2.jpg' },
      { id: 'food-set-003', name: '【欢聚时光】招牌菜6人餐', discount: '6.8折', price: '168', originalPrice: '248', image: '/static/food-assets/food-1.jpg' },
      { id: 'food-set-004', name: '【暖心小聚】特色菜双人餐', discount: '7折', price: '88', originalPrice: '126', image: '/static/food-assets/food-2.jpg' },
      { id: 'food-set-005', name: '【家庭聚餐】经典川菜8人餐', discount: '6.5折', price: '238', originalPrice: '366', image: '/static/food-assets/food-1.jpg' },
      { id: 'food-set-006', name: '【人气必点】招牌菜4人餐', discount: '6.8折', price: '128', originalPrice: '188', image: '/static/food-assets/food-2.jpg' },
      { id: 'food-set-007', name: '【周末欢聚】特色菜10人餐', discount: '6折', price: '298', originalPrice: '498', image: '/static/food-assets/food-1.jpg' },
      { id: 'food-set-008', name: '【一人食】暖心工作餐', discount: '8折', price: '39', originalPrice: '49', image: '/static/food-assets/food-2.jpg' }
      { id: 'food-set-001', name: '【川味盛宴】川菜12人餐', discount: '6折', price: '138', originalPrice: '266', image: '/static/common/image-placeholder.png' },
      { id: 'food-set-002', name: '【川香四溢】川菜4人餐', discount: '6折', price: '118', originalPrice: '198', image: '/static/common/image-placeholder.png' },
      { id: 'food-set-003', name: '【欢聚时光】招牌菜6人餐', discount: '6.8折', price: '168', originalPrice: '248', image: '/static/common/image-placeholder.png' },
      { id: 'food-set-004', name: '【暖心小聚】特色菜双人餐', discount: '7折', price: '88', originalPrice: '126', image: '/static/common/image-placeholder.png' },
      { id: 'food-set-005', name: '【家庭聚餐】经典川菜8人餐', discount: '6.5折', price: '238', originalPrice: '366', image: '/static/common/image-placeholder.png' },
      { id: 'food-set-006', name: '【人气必点】招牌菜4人餐', discount: '6.8折', price: '128', originalPrice: '188', image: '/static/common/image-placeholder.png' },
      { id: 'food-set-007', name: '【周末欢聚】特色菜10人餐', discount: '6折', price: '298', originalPrice: '498', image: '/static/common/image-placeholder.png' },
      { id: 'food-set-008', name: '【一人食】暖心工作餐', discount: '8折', price: '39', originalPrice: '49', image: '/static/common/image-placeholder.png' }
   ])
   const hotelPackages = [
      { id: 101, name: '都市视野大床房', discount: '6折', price: '588-800', originalPrice: '', image: '/static/stay-assets/hotel-hampton.jpg' },
      { id: 102, name: '山景云窗商务双床房', discount: '6折', price: '388-600', originalPrice: '', image: '/static/stay-assets/hotel-vienna.jpg' },
      { id: 103, name: '都市视野大床房', discount: '6折', price: '588-800', originalPrice: '', image: '/static/stay-assets/hotel-hampton.jpg' },
      { id: 104, name: '行政景观大床房', discount: '6折', price: '688-920', originalPrice: '', image: '/static/stay-assets/hotel-vienna.jpg' },
      { id: 105, name: '亲子主题家庭房', discount: '6.5折', price: '628-860', originalPrice: '', image: '/static/stay-assets/hotel-hampton.jpg' },
      { id: 106, name: '高层城景双床房', discount: '6折', price: '468-680', originalPrice: '', image: '/static/stay-assets/hotel-vienna.jpg' },
      { id: 107, name: '豪华行政套房', discount: '6.8折', price: '988-1280', originalPrice: '', image: '/static/stay-assets/hotel-hampton.jpg' },
      { id: 108, name: '舒适大床房', discount: '6折', price: '428-620', originalPrice: '', image: '/static/stay-assets/hotel-vienna.jpg' }
      { id: 101, name: '都市视野大床房', discount: '6折', price: '588-800', originalPrice: '', image: '/static/common/image-placeholder.png' },
      { id: 102, name: '山景云窗商务双床房', discount: '6折', price: '388-600', originalPrice: '', image: '/static/common/image-placeholder.png' },
      { id: 103, name: '都市视野大床房', discount: '6折', price: '588-800', originalPrice: '', image: '/static/common/image-placeholder.png' },
      { id: 104, name: '行政景观大床房', discount: '6折', price: '688-920', originalPrice: '', image: '/static/common/image-placeholder.png' },
      { id: 105, name: '亲子主题家庭房', discount: '6.5折', price: '628-860', originalPrice: '', image: '/static/common/image-placeholder.png' },
      { id: 106, name: '高层城景双床房', discount: '6折', price: '468-680', originalPrice: '', image: '/static/common/image-placeholder.png' },
      { id: 107, name: '豪华行政套房', discount: '6.8折', price: '988-1280', originalPrice: '', image: '/static/common/image-placeholder.png' },
      { id: 108, name: '舒适大床房', discount: '6折', price: '428-620', originalPrice: '', image: '/static/common/image-placeholder.png' }
   ]
   // 体检医院演示套餐:图片按体验版素材规则复用“享”专题两张演示图。
   // 体检医院演示套餐:本地演示数据,图片统一用兜底占位图(后台没配图时也是这张)。
   const medicalPackages = [
      { id: 1, name: 'A套餐(脑血管系统筛查)', openTime: '5-6月', image: '/static/stay-assets/hotel-hampton.jpg' },
      { id: 2, name: 'B套餐(心血管系统筛查)', openTime: '5-6月', image: '/static/stay-assets/hotel-vienna.jpg' },
      { id: 3, name: 'C套餐(消化系统筛查)', openTime: '5-6月', image: '/static/stay-assets/hotel-hampton.jpg' }
      { id: 1, name: 'A套餐(脑血管系统筛查)', openTime: '5-6月', image: '/static/common/image-placeholder.png' },
      { id: 2, name: 'B套餐(心血管系统筛查)', openTime: '5-6月', image: '/static/common/image-placeholder.png' },
      { id: 3, name: 'C套餐(消化系统筛查)', openTime: '5-6月', image: '/static/common/image-placeholder.png' }
   ]
   const visiblePackages = ref<any[]>([])
   const visibleMedicalPackages = ref<any[]>([])
@@ -258,6 +270,19 @@
      return level
   }
   const sameCardLevel = (configured: any, actual: string): boolean => normalizeCardLevel(configured) == normalizeCardLevel(actual)
   // 商家可以按“卓越人才卡(本地)/(外地)”分开配置权益;人才地区已知时不能拿地区对不上的配置来展示。
   const levelAreaOf = (value: any): string => {
      const level = value == null ? '' : ('' + value).trim().toUpperCase()
      if (level.indexOf('外地') >= 0 || level.indexOf('OUTSIDE') >= 0) return 'OUTSIDE'
      if (level.indexOf('本地') >= 0 || level.indexOf('LOCAL') >= 0) return 'LOCAL'
      return ''
   }
   const talentAreaOf = (data: any): string => {
      const raw = data != null && data.sourceAreaType != null ? ('' + data.sourceAreaType).trim().toUpperCase() : ''
      if (raw.indexOf('外地') >= 0 || raw.indexOf('OUTSIDE') >= 0) return 'OUTSIDE'
      if (raw.indexOf('本地') >= 0 || raw.indexOf('LOCAL') >= 0) return 'LOCAL'
      return ''
   }
   const isRightEnabled = (value: any): boolean => value === true || Number(value) == 1 || ('' + value).trim().toLowerCase() == 'true'
   const parseRightsConfig = (value: any): any[] => {
      if (value == null || value == '') return []
@@ -294,9 +319,12 @@
      if (cardLevel == '') return
      let matched: any = null
      let disabledMatched: any = null
      const talentArea = talentAreaOf(data)
      for (let index = 0; index < rights.length; index++) {
         const item: any = rights[index]
         if (!sameCardLevel(rightLevel(item), cardLevel)) continue
         const configuredArea = levelAreaOf(rightLevel(item))
         if (talentArea != '' && configuredArea != '' && configuredArea != talentArea) continue
         if (isRightEnabled(item != null ? item.enabled : null)) {
            matched = item
            break
@@ -440,10 +468,8 @@
            }
         }
      }
      const session = getSession()
      if (session != null && session.talentId != null) talentId.value = session.talentId as string
      if (talentId.value == '' && apiEnvironment == 'development') talentId.value = '2'
      if (isParkingMerchant.value && talentId.value != '') {
      talentId.value = resolveTalentId()
      if ((isFuelMerchant.value || isParkingMerchant.value) && talentId.value != '') {
         getTalentCardProfile(talentId.value, (data: any) => {
            const card = data != null ? data.card : null
            talentCardId.value = card != null && card.id != null ? '' + card.id : ''
@@ -482,20 +508,27 @@
      listLoading.value = true
      listLoaded.value = false
      listError.value = false
      getFuelMerchantDetail(merchantId.value, (data: any) => {
      getFuelMerchantDetail(merchantId.value, talentCardId.value, (data: any) => {
         merchantName.value = data != null && data.merchantName != null ? data.merchantName as string : ''
         merchantAddress.value = data != null && data.address != null ? data.address as string : ''
         merchantPhone.value = data != null && data.contactMobile != null ? data.contactMobile as string : ''
         merchantAvailable.value = data != null && (data.available === true || Number(data.available) == 1)
         merchantDiscount.value = data != null && data.discountText != null ? data.discountText as string : '请咨询商户'
         merchantServiceLabel.value = data != null && data.serviceLabel != null ? data.serviceLabel as string : '请咨询商户'
         applicableCards.value = data != null && data.applicableCards != null ? data.applicableCards as string : '请咨询商户'
         merchantServiceLabel.value = ''
         merchantDiscount.value = ''
         merchantBenefitTagsLoaded.value = true
         merchantBenefitTags.value = []
         applicableCards.value = data != null && data.applicableCards != null ? String(data.applicableCards) : ''
         usageInstructions.value = data != null && data.usageInstructions != null ? data.usageInstructions as string : '请咨询商户'
         merchantRightsText.value = data != null && data.rightsText != null ? data.rightsText as string : '请咨询商户'
         groupBuyingEnabled.value = false
         merchantType.value = 'fuel'
         const heroes: any[] = data != null && data.heroImages instanceof Array ? data.heroImages as any[] : []
         if (heroes.length > 0 && heroes[0].imageUrl != null && heroes[0].imageUrl != '') merchantHero.value = resolveApiUrl(heroes[0].imageUrl as string)
         if (heroes.length > 0 && heroes[0].imageUrl != null && heroes[0].imageUrl != '') {
            merchantHero.value = resolveApiUrl(heroes[0].imageUrl as string)
         } else if (data != null && data.coverUrl != null && data.coverUrl != '') {
            // 没配置详情页图片时用商家封面图兜底,避免顶部大图空白。
            merchantHero.value = resolveApiUrl(data.coverUrl as string)
         }
         fuelBenefitId.value = data != null && data.benefit != null && data.benefit.benefitId != null ? '' + data.benefit.benefitId : ''
         merchantLocation.value = {
            latitude: data != null && data.latitude != null ? Number(data.latitude) : 0,
@@ -544,7 +577,7 @@
         merchantUnavailableReason.value = data != null && data.unavailableReason != null && data.unavailableReason != '' ? data.unavailableReason as string : '该停车权益暂不可用'
         merchantServiceLabel.value = data != null && data.serviceLabel != null ? data.serviceLabel as string : '停车服务'
         parkingBenefitText.value = data != null && data.parkingType == 'free' ? '免费' : '付费'
         applicableCards.value = data != null && data.applicableCards != null ? data.applicableCards as string : '请咨询商户'
         applicableCards.value = data != null && data.applicableCards != null ? String(data.applicableCards) : ''
         const parkingRightDetail = data != null && data.merchantRight != null && data.merchantRight.detail != null
            ? String(data.merchantRight.detail).trim()
            : ''
@@ -585,6 +618,8 @@
   }
   onShow(() => {
      if (isParkingMerchant.value && parkingDetailLoaded) loadParkingMerchant()
      // 酒店:从免费券页/核销后返回时刷新剩余次数,按钮在「出示免费券 / 出示人才码」之间切换。
      refreshHotelVoucherAvailability()
   })
   const loadRemoteMerchant = () => {
      if (isFuelMerchant.value || isParkingMerchant.value) { loadTransportMerchantDetail(); return }
@@ -610,6 +645,8 @@
         merchantHasCoupon.value = data != null && (data.couponAvailable === true || Number(data.couponAvailable) == 1)
         const discountText = data != null && data.discountText != null ? data.discountText as string : ''
         merchantDiscount.value = discountText == '人才专享' ? '' : discountText
         merchantBenefitTagsLoaded.value = data != null && data.benefitTags instanceof Array
         merchantBenefitTags.value = merchantBenefitTagsLoaded.value ? readBenefitTags(data) : []
         const configuredTag = data != null && data.rightsTag != null ? '' + data.rightsTag : ''
         merchantServiceLabel.value = configuredTag != '' && data != null && data.serviceLabel != null ? data.serviceLabel as string : ''
         applicableCards.value = data != null && data.applicableCards != null ? data.applicableCards as string : ''
@@ -619,26 +656,19 @@
         applyMedicalRightsConfig(data)
         // 普通自助洗车复用餐饮商家的团单展示;外链洗车不会进入此通用详情页。
         const isNormalCarWash = merchantType.value == 'carWash'
         groupBuyingEnabled.value = merchantType.value == 'culture'
            ? false
            : isNormalCarWash
            ? true
            : (data == null || data.groupBuyingEnabled == null ? groupBuyingEnabled.value : data.groupBuyingEnabled === true || Number(data.groupBuyingEnabled) == 1)
         groupBuyingEnabled.value = merchantType.value == 'food'
            || merchantType.value == 'scenic'
            || merchantType.value == 'hotel'
            || merchantType.value == 'dental'
            || merchantType.value == 'medical'
            || isNormalCarWash
         if (merchantType.value == 'culture') {
            const cultureStoreConsume = data != null && (data.storeConsume === true || Number(data.storeConsume) == 1) && (data.consumeEnabled === true || Number(data.consumeEnabled) == 1)
            merchantServiceLabel.value = cultureStoreConsume ? '到店消费' : ''
            merchantPrimaryTag.value = merchantHasCoupon.value ? '券' : '权益'
         }
         isHotelMerchant.value = merchantType.value == 'hotel'
         merchantConsumeDiscount.value = ''
         if (isHotelMerchant.value) {
            const configuredServiceLabel = data != null && data.serviceLabel != null ? String(data.serviceLabel) : ''
            const configuredCardTag = data != null && data.applicableCards != null ? String(data.applicableCards) : ''
            const hasStoreConsumeTag = configuredCardTag.indexOf('到店消费') >= 0
            const hotelStoreConsume = (data != null && (data.storeConsume === true || Number(data.storeConsume) == 1) && (data.consumeEnabled === true || Number(data.consumeEnabled) == 1)) || configuredServiceLabel.indexOf('到店') >= 0 || hasStoreConsumeTag
            merchantServiceLabel.value = hotelStoreConsume ? '到店消费' : '住宿服务'
            const normalizedCardLabel = normalizeHotelCardLabel(configuredCardTag)
            if (normalizedCardLabel != '') applicableCards.value = normalizedCardLabel
            const hotelStoreConsume = data != null && (data.storeConsume === true || Number(data.storeConsume) == 1) && (data.consumeEnabled === true || Number(data.consumeEnabled) == 1)
            const consumeType = data != null && data.consumeType != null ? String(data.consumeType) : ''
            const consumeDiscount = data != null && data.consumeDiscount != null ? String(data.consumeDiscount).trim() : ''
            const consumeCoupon = data != null && data.consumeCoupon != null ? String(data.consumeCoupon).trim() : ''
@@ -651,6 +681,7 @@
            const freeVoucherTimes = data != null && data.freeVoucherTimes != null ? String(data.freeVoucherTimes).trim() : ''
            merchantDiscount.value = freeVoucherTimes != '' ? '免费' + freeVoucherTimes + '次' : '免费'
            merchantPrimaryTag.value = '券'
            refreshHotelVoucherAvailability()
         }
         if (isHotelMerchant.value && talentId.value != '' && talentCardId.value == '') {
            getTalentCardProfile(talentId.value, (profile: any) => {
@@ -825,6 +856,17 @@
      if (item.available === false) { uni.showToast({ title: item.unavailableReason || '该套餐暂不可预约', icon: 'none' }); return }
      uni.navigateTo({ url: '/pages/medical-package/medical-package?packageId=' + encodeURIComponent(item.id) + '&hospitalId=' + encodeURIComponent(merchantId.value) + '&name=' + encodeURIComponent(item.name) + '&hospital=' + encodeURIComponent(merchantName.value) + '&applicableCards=' + encodeURIComponent(applicableCards.value) })
   }
   // 酒店底部按钮显示「出示免费券」还是「出示人才码」,除了看后台是否启用免费券,
   // 还要看**剩余次数**:次数用完就得出示人才码,否则点进去只会看到"暂无可用免费券"。
   const refreshHotelVoucherAvailability = () => {
      if (!isHotelMerchant.value || !hasFreeVoucher.value || merchantId.value == '' || talentId.value == '') return
      getBenefitQuota('HOTEL', talentId.value, talentCardId.value, merchantId.value, (quota: any) => {
         const remaining = quota != null && quota.quotaRemaining != null ? Number(quota.quotaRemaining) : 0
         hasFreeVoucher.value = quota != null && quota.available === true && remaining > 0
      }, () => {
         // 额度查询失败时保持原判断:不因为一次额度查询失败就把按钮换成人才码。
      })
   }
   const showBottomVoucher = () => {
      if (isHotelMerchant.value && hasFreeVoucher.value) {
         if (talentId.value != '' && talentCardId.value == '') {
@@ -979,12 +1021,12 @@
   .rights-panel { margin: 16rpx 25rpx 0; padding: 28rpx 26rpx; border-radius: 15rpx; background-color: #FFFFFF; }
   .rights-text { display: block; margin-top: 0; color: #777F8B; font-size: 27rpx; line-height: 43rpx; }
   .store-panel { margin: 16rpx 25rpx 0; overflow: hidden; border-width: 2rpx; border-style: solid; border-color: #DCE7FA; border-radius: 16rpx; background-color: #FFFFFF; }
   .store-table-header { display: flex; min-height: 67rpx; background-color: #EEF4FF; flex-direction: row; align-items: center; }
   .store-head-name, .store-head-address { display: flex; height: 67rpx; box-sizing: border-box; padding: 0 13rpx; justify-content: flex-start; align-items: center; }
   .store-table-header { display: flex; height: 67rpx; background-color: #EEF4FF; flex-direction: row; align-items: center; }
   .store-head-name, .store-head-address { display: flex; height: 67rpx; position: relative; box-sizing: border-box; padding: 0; justify-content: flex-start; align-items: center; }
   .store-head-name { width: 250rpx; }
   .store-head-address { min-width: 0; flex: 1; }
   .store-head-action { width: 72rpx; height: 67rpx; flex-shrink: 0; }
   .store-head-name text, .store-head-address text { width: 100%; color: #26374F; font-size: 23rpx; font-weight: 700; text-align: left; }
   .store-head-name text, .store-head-address text { display: block; position: absolute; left: 13rpx; top: 0; right: 0; height: 67rpx; color: #26374F; font-size: 23rpx; font-weight: 700; line-height: 67rpx; text-align: left; }
   .store-row { display: flex; min-height: 92rpx; border-top-width: 1rpx; border-top-style: solid; border-top-color: #E7ECF4; flex-direction: row; align-items: stretch; }
   .store-name-cell { display: flex; width: 250rpx; min-height: 92rpx; box-sizing: border-box; border-right-width: 1rpx; border-right-style: solid; border-right-color: #E7ECF4; align-items: stretch; justify-content: flex-start; }
   .store-address-cell { display: flex; min-width: 0; min-height: 92rpx; flex: 1; box-sizing: border-box; align-items: stretch; justify-content: flex-start; }
@@ -1008,7 +1050,7 @@
   .medical-open-value { min-width: 0; flex: 1; margin-left: 14rpx; color: #FF7C2B; font-size: 26rpx; font-weight: 600; }
   .medical-view-button { display: flex; width: 132rpx; height: 57rpx; border-radius: 29rpx; background-image: linear-gradient(90deg, #FF5733 0%, #FF8A20 100%); flex-shrink: 0; justify-content: center; align-items: center; }
   .medical-view-button text { color: #FFFFFF; font-size: 27rpx; }
   .medical-usage-panel { min-height: 548rpx; margin: 16rpx 25rpx 0; box-sizing: border-box; padding: 31rpx 27rpx; border-radius: 15rpx; background-color: #EFF1F4; }
   .medical-usage-panel { margin: 16rpx 25rpx 0; box-sizing: border-box; padding: 28rpx 26rpx; border-radius: 15rpx; background-color: #FFFFFF; }
   .medical-usage-panel text { color: #777F8B; font-size: 27rpx; line-height: 43rpx; white-space: pre-wrap; }
   .action-bar { display: flex; height: 150rpx; height: calc(150rpx + env(safe-area-inset-bottom)); box-sizing: border-box; padding: 20rpx 34rpx 2rpx; padding-bottom: calc(2rpx + env(safe-area-inset-bottom)); background-color: #FFFFFF; box-shadow: 0 -5rpx 15rpx rgba(0,0,0,.06); flex-direction: column; justify-content: flex-end; }
   .talent-code-button { display: flex; width: 100%; height: 90rpx; flex-shrink: 0; border-radius: 46rpx; background-image: linear-gradient(90deg, #FF4D32 0%, #FF8A20 100%); flex-direction: row; justify-content: center; align-items: center; }