| | |
| | | </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"> |
| | | <image class="hero-image" :src="merchantHero" mode="aspectFill" @error="replaceHeroImage"></image> |
| | | <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> |
| | | </swiper> |
| | | <view class="hero-shade"></view> |
| | | </view> |
| | | |
| | |
| | | <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> |
| | |
| | | </view> |
| | | <view v-else class="merchant-title-row"> |
| | | <text class="merchant-title">{{merchantName}}</text> |
| | | <view class="merchant-tags"><text class="coupon-tag">{{merchantPrimaryTag}}</text><text v-if="merchantDiscount != ''" class="discount-tag">{{merchantDiscount}}</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 class="service-tags"><text v-if="!isParkingMerchant && !isMedicalMerchant">{{merchantServiceLabel}}</text><text>{{applicableCards}}</text></view> |
| | | <view v-if="!isParkingMerchant" class="merchant-address-row"> |
| | | <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"> |
| | | <image class="address-icon" src="/static/benefits-assets/map-pin.svg"></image> |
| | | <text>{{merchantAddress}}</text> |
| | | <view class="merchant-actions"><view :class="merchantLocation.latitude > 0 && merchantLocation.longitude > 0 ? 'round-action' : 'round-action is-disabled'" @tap="openMap"><image src="/static/merchant-assets/navigation.svg"></image></view><view :class="merchantPhone != '' ? 'round-action' : 'round-action is-disabled'" @tap="callMerchant"><image src="/static/merchant-assets/phone.svg"></image></view></view> |
| | |
| | | </view> |
| | | |
| | | <view v-if="!merchantError" class="tabs"> |
| | | <view class="tab-item" :class="currentTab == 'primary' ? 'active' : ''" @tap="changeTab('primary')"><text>{{isMedicalMerchant ? '预约' : (groupBuyingEnabled ? '消费购买' : '使用说明')}}</text><view class="tab-line"></view></view> |
| | | <view class="tab-item" :class="currentTab == 'primary' ? 'active' : ''" @tap="changeTab('primary')"><text>{{isMedicalMerchant ? '预约' : (isParkingMerchant && parkingBenefitText == '付费' ? '使用说明' : (showUsageInstructions ? '权益内容' : '消费购买'))}}</text><view class="tab-line"></view></view> |
| | | <view class="tab-item" :class="currentTab == 'rights' ? 'active' : ''" @tap="changeTab('rights')"><text>{{isParkingMerchant ? '停车场地址' : (isTransportMerchant ? '适用门店' : '权益详情')}}</text><view class="tab-line"></view></view> |
| | | </view> |
| | | |
| | |
| | | <view class="medical-package-item" v-for="item in visibleMedicalPackages" :key="item.id" hover-class="package-item-active" @tap="openMedicalPackage(item)"> |
| | | <image class="medical-package-image" :src="item.image" mode="aspectFill" @error="replaceImage(item)"></image> |
| | | <view class="medical-package-info"> |
| | | <text class="medical-package-name">{{item.name}}</text> |
| | | <view class="medical-package-bottom"> |
| | | <view class="medical-open-row"><text class="medical-open-label">开放时间</text><text class="medical-open-value">{{item.openTime}}</text></view> |
| | | <view class="medical-package-top"> |
| | | <text class="medical-package-name">{{item.name}}</text> |
| | | <view class="medical-view-button"><text>查看</text></view> |
| | | </view> |
| | | <view class="medical-open-row"><text class="medical-open-label">开放时间</text><text class="medical-open-value">{{item.openTime}}</text></view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view v-else-if="showUsageInstructions" class="usage-panel"><text>{{usageInstructions}}</text></view> |
| | | <view v-else-if="groupBuyingEnabled" class="package-list"> |
| | | <view v-if="listLoading" class="list-state"><view class="loading-spinner"></view><text>正在加载可用套餐</text></view> |
| | | <view v-else-if="listError" class="list-state error-state"><text>套餐列表加载失败</text><text class="retry-text" @tap="loadMerchantLists">重新加载</text></view> |
| | |
| | | <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 class="coupon-tag">券</text><text class="discount-tag">{{item.discount}}</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> |
| | |
| | | <view v-if="listLoading" class="list-state"><view class="loading-spinner"></view><text>正在加载适用门店</text></view> |
| | | <view v-else-if="listError" class="list-state error-state"><text>适用门店加载失败</text><text class="retry-text" @tap="loadMerchantLists">重新加载</text></view> |
| | | <view v-else-if="listLoaded && applicableStores.length == 0" class="list-state"><text>暂无适用门店</text></view> |
| | | <view v-if="listLoaded && applicableStores.length > 0" class="store-table-header"><view class="store-head-name"><text>适用门店名称</text></view><view class="store-head-address"><text>详细地址</text></view><view class="store-head-action"></view></view> |
| | | <view v-if="listLoaded && applicableStores.length > 0" class="store-table-header"><view class="store-head-name"><text>{{isParkingMerchant ? '停车场名称' : '适用门店名称'}}</text></view><view class="store-head-address"><text>详细地址</text></view><view class="store-head-action"></view></view> |
| | | <view class="store-row" v-for="item in applicableStores" :key="item.id"> |
| | | <view class="store-name-cell"><text>{{item.name}}</text></view> |
| | | <view class="store-address-cell"><text>{{item.address}}</text></view> |
| | | <view class="store-name-cell"><view class="store-cell-content"><text>{{item.name}}</text></view></view> |
| | | <view class="store-address-cell"><view class="store-cell-content"><text>{{item.address}}</text></view></view> |
| | | <view :class="item.canNavigate === false ? 'store-location is-disabled' : 'store-location'" @tap="openStoreLocation(item)"><image src="/static/merchant-assets/store-pin-blue.svg" mode="aspectFit"></image></view> |
| | | </view> |
| | | </view> |
| | | <view v-else-if="!merchantError && isMedicalMerchant" class="medical-usage-panel"><text>{{usageInstructions}}</text></view> |
| | | <view v-else-if="!merchantError && isMedicalMerchant" class="medical-usage-panel"><text>{{merchantRightsText}}</text></view> |
| | | <view v-else-if="!merchantError" class="rights-panel"> |
| | | <text class="rights-title">人才卡专属权益</text> |
| | | <text class="rights-text">{{merchantRightsText}}</text> |
| | | <view class="rule-row"><text>适用卡种</text><text>{{applicableCards}}</text></view> |
| | | <view class="rule-row"><text>使用方式</text><text>到店出示人才码</text></view> |
| | | </view> |
| | | </view> |
| | | <view class="content-bottom"></view> |
| | |
| | | <view v-if="isParkingMerchant && parkingActionMode == 'talentCode'" class="talent-code-button" @tap="showBottomVoucher"><image src="/static/merchant-assets/qr-code.svg"></image><text>出示人才码</text></view> |
| | | <view v-else-if="isParkingMerchant" class="talent-code-button" @tap="openPlatePage"><text>填写车牌</text></view> |
| | | <view v-else-if="isTransportMerchant" class="talent-code-button" @tap="openMobileDialog"><text>填写手机号</text></view> |
| | | <view v-else-if="isApartmentMerchant" class="talent-code-button" :class="advisorMobile == '' ? 'talent-code-button-disabled' : ''" @tap="contactSpecialist"><image src="/static/merchant-assets/headset-white.svg"></image><text>联系专员</text></view> |
| | | <view v-else class="talent-code-button" @tap="showBottomVoucher"><image :src="hasFreeVoucher ? '/static/merchant-assets/coupon.svg' : '/static/merchant-assets/qr-code.svg'"></image><text>{{hasFreeVoucher ? '出示免费券' : '出示人才码'}}</text></view> |
| | | </view> |
| | | |
| | |
| | | |
| | | <script setup lang="uts"> |
| | | import { apiEnvironment, isApiConfigured } from '../../config/api.uts' |
| | | import { getFuelMerchantDetail, getMedicalPackages, getMerchantDetail, getParkingMerchantDetail, getProducts, 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' |
| | | |
| | |
| | | const isFuelMerchant = ref(false) |
| | | const isParkingMerchant = ref(false) |
| | | const isMedicalMerchant = ref(false) |
| | | // 后台停车权益配置:plate=填写车牌,talentCode=出示人才码。 |
| | | // 当前人才等级的停车权益:免费填写车牌,付费出示人才码。 |
| | | const parkingActionMode = ref('plate') |
| | | // 后台车牌表单配置:withParking=需搜索停车场,simple=仅填写车牌。 |
| | | const parkingPlateMode = ref('simple') |
| | |
| | | const merchantPhone = ref('08123335555') |
| | | const merchantId = ref('merchant-food-default') |
| | | const merchantType = ref('food') |
| | | const isApartmentMerchant = computed(() : boolean => merchantType.value == 'apartment') |
| | | const advisorMobile = ref('') |
| | | const merchantName = ref('百杏厨房(中环天地店)') |
| | | const merchantHero = ref('/static/food-assets/food-1.jpg') |
| | | const fallbackImage = '/static/common/image-fallback.svg' |
| | | const replaceHeroImage = () => { merchantHero.value = fallbackImage } |
| | | const replaceImage = (item: any) => { if (item.image != fallbackImage) item.image = fallbackImage } |
| | | const merchantPrimaryTag = ref('券') |
| | | const merchantDiscount = ref('8.8折') |
| | | const currentCardLevel = ref('') |
| | | const merchantHero = ref('') |
| | | const merchantHeroImages = ref<string[]>([]) |
| | | const displayedHeroImages = computed((): string[] => merchantHeroImages.value.length > 0 ? merchantHeroImages.value : [merchantHero.value]) |
| | | const fallbackImage = '' |
| | | const replaceHeroImage = (index: number) => { |
| | | if (merchantHeroImages.value.length == 0) { merchantHero.value = ''; return } |
| | | if (index >= 0 && index < merchantHeroImages.value.length) merchantHeroImages.value[index] = '' |
| | | } |
| | | const replaceImage = (item: any) => { if (item.image != '') item.image = '' } |
| | | // 非团购服务仍沿用各自的静态标签;餐饮团购标签由接口的券与最低折扣决定。 |
| | | const merchantPrimaryTag = ref('') |
| | | const merchantHasCoupon = ref(false) |
| | | const merchantDiscount = ref('') |
| | | const merchantConsumeDiscount = ref('') |
| | | 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('免费') |
| | | const talentId = ref('') |
| | | const talentCardId = ref('') |
| | | const fuelRegistrationKey = ref('') |
| | | const merchantRightsText = ref('持卓越人才卡到店消费可享指定菜品及套餐 8.8 折优惠,具体可用时段、适用门店和使用规则以商户现场说明为准。') |
| | | // 后台商户配置:false 时不展示套餐团购,主标签切换为“使用说明”。 |
| | |
| | | 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[]>([]) |
| | | const listLoading = ref(true) |
| | | const listLoaded = ref(false) |
| | | const listError = ref(false) |
| | | // 餐饮商家没有上架商品时,改为展示当前人才卡等级可用的到店消费说明。 |
| | | const showUsageInstructions = computed(() => !isMedicalMerchant.value && ((isParkingMerchant.value && parkingBenefitText.value == '付费') || !groupBuyingEnabled.value || (merchantType.value == 'food' && listLoaded.value && visiblePackages.value.length == 0))) |
| | | const failOnce = ref(false) |
| | | const forceEmpty = ref(false) |
| | | let parkingDetailLoaded = false |
| | | const normalizeCardLevel = (value: any): string => { |
| | | const level = value == null ? '' : ('' + value).trim().toUpperCase() |
| | | if (level.indexOf('卓越') >= 0 || level == 'EXCELLENT') return 'EXCELLENT' |
| | | if (level.indexOf('杰出') >= 0 || level == 'OUTSTANDING') return 'OUTSTANDING' |
| | | if (level.indexOf('菁英') >= 0 || level.indexOf('精英') >= 0 || level == 'ELITE') return 'ELITE' |
| | | 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 [] |
| | | if (value instanceof Array) return value as any[] |
| | | if (value.rights instanceof Array) return value.rights as any[] |
| | | if (value.items instanceof Array) return value.items as any[] |
| | | const text = '' + value |
| | | if (text.trim() == '') return [] |
| | | try { |
| | | const parsed = JSON.parse(text) |
| | | if (parsed instanceof Array) return parsed as any[] |
| | | if (parsed != null && parsed.rights instanceof Array) return parsed.rights as any[] |
| | | if (parsed != null && parsed.items instanceof Array) return parsed.items as any[] |
| | | return [] |
| | | } catch (error) { |
| | | return [] |
| | | } |
| | | } |
| | | const rightLevel = (right: any): any => right != null && right.level != null ? right.level : (right != null && right.cardLevel != null ? right.cardLevel : (right != null ? right.card_level : null)) |
| | | const rightDetail = (right: any): any => { |
| | | if (right == null) return '' |
| | | if (right.detail != null && right.detail != '') return right.detail |
| | | if (right.benefitDetail != null && right.benefitDetail != '') return right.benefitDetail |
| | | if (right.rightsText != null && right.rightsText != '') return right.rightsText |
| | | if (right.benefit_text != null && right.benefit_text != '') return right.benefit_text |
| | | return '' |
| | | } |
| | | const applyMedicalRightsConfig = (data: any) => { |
| | | if (data != null && data.cardLevel != null && data.cardLevel != '') currentCardLevel.value = data.cardLevel as string |
| | | const cardLevel = currentCardLevel.value |
| | | const rights = parseRightsConfig(data != null ? data.merchantRightsConfig : null) |
| | | // Use the configured detail for every merchant type; keep the API fallback when absent. |
| | | if (!isMedicalMerchant.value && rights.length == 0) return |
| | | 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 |
| | | } |
| | | if (disabledMatched == null) disabledMatched = item |
| | | } |
| | | if (matched != null) { |
| | | const tag = matched.tag != null && matched.tag != '' ? matched.tag as string : '' |
| | | if (tag != '') applicableCards.value = tag |
| | | const configuredDetail = rightDetail(matched) |
| | | merchantRightsText.value = configuredDetail != null && configuredDetail != '' |
| | | ? configuredDetail as string |
| | | : (data != null && data.rightsText != null && data.rightsText != '' ? data.rightsText as string : '请咨询商户') |
| | | return |
| | | } |
| | | if (rights.length > 0) { |
| | | const tag = disabledMatched != null && disabledMatched.tag != null && disabledMatched.tag != '' ? disabledMatched.tag as string : '' |
| | | applicableCards.value = tag |
| | | merchantRightsText.value = disabledMatched != null ? '当前人才卡暂未开通该权益' : '当前人才卡暂未配置该权益' |
| | | return |
| | | } |
| | | if (disabledMatched != null) { |
| | | const tag = disabledMatched.tag != null && disabledMatched.tag != '' ? disabledMatched.tag as string : '' |
| | | applicableCards.value = tag |
| | | merchantRightsText.value = '当前人才卡暂未开通该权益' |
| | | return |
| | | } |
| | | merchantRightsText.value = data != null && data.rightsText != null && data.rightsText != '' ? data.rightsText as string : '当前人才卡暂未配置该权益' |
| | | if (data != null && data.applicableCards != null && data.applicableCards != '') { |
| | | applicableCards.value = data.applicableCards as string |
| | | } |
| | | } |
| | | |
| | | onLoad((options: any) => { |
| | | // #ifdef MP-WEIXIN |
| | |
| | | merchantType.value = 'medical' |
| | | isMedicalMerchant.value = true |
| | | merchantName.value = name |
| | | merchantHero.value = '/static/stay-assets/hotel-hampton.jpg' |
| | | merchantHero.value = '' |
| | | merchantPrimaryTag.value = '免费' |
| | | merchantDiscount.value = '' |
| | | merchantAddress.value = '攀枝花市东区益康街34号4号综合3楼体检中心' |
| | | merchantLocation.value = { latitude: 26.5856, longitude: 101.7184, name: name, address: merchantAddress.value } |
| | | groupBuyingEnabled.value = false |
| | | applicableCards.value = '请稍候' |
| | | merchantRightsText.value = '正在加载权益详情' |
| | | usageInstructions.value = '①预约时间每年度5-6月,体检时间每年度6月1日-10月31日。\n②体检时请带上本人二代居民身份证。\n③检查前三天请注意休息,勿熬夜,以清淡饮食为宜,勿饮酒、忌高脂、高胆固醇食物、不吃保健品。检查前一日晚上十二点以后,请完全禁食(包括饮水)。\n④体检当天请着宽松衣物,不要穿连衣裙、连裤袜。做X线检查时,宜穿棉布内衣,勿穿带有金属纽扣的衣服、文胸;请摘去项链、手机、钢笔、钥匙等金属物品。\n⑤自费项目:体检当天,体检对象与体检医院沟通后确定,体检医院给予八折收费优惠。' |
| | | } else if (options['type'] == 'transport') { |
| | | merchantType.value = 'transport' |
| | |
| | | if (routedMerchantType == 'parking' || name.indexOf('停车') >= 0) { |
| | | isParkingMerchant.value = true |
| | | merchantName.value = '停车服务' |
| | | merchantHero.value = '/static/home-assets/city-intro.jpg' |
| | | merchantHero.value = '' |
| | | merchantPrimaryTag.value = '免费' |
| | | merchantDiscount.value = '' |
| | | merchantAddress.value = '攀枝花市东区渡口公司管辖停车场' |
| | |
| | | usageInstructions.value = '登记信息后可享受渡口公司管辖的49处停车场免费停车服务' |
| | | } else { |
| | | merchantName.value = name == '加油服务' ? '中国石油东区新福路加油站' : name |
| | | merchantHero.value = '/static/home-assets/top.jpg' |
| | | merchantHero.value = '' |
| | | merchantPrimaryTag.value = '券' |
| | | merchantDiscount.value = '9.4折' |
| | | merchantAddress.value = '四川省攀枝花市东区新福路20号附3-21号' |
| | |
| | | isHotelMerchant.value = true |
| | | merchantType.value = 'hotel' |
| | | merchantName.value = name |
| | | merchantHero.value = routedImage == '' ? '/static/stay-assets/hotel-hampton.jpg' : routedImage |
| | | merchantDiscount.value = routedDiscount == '' ? '6折' : routedDiscount |
| | | hasFreeVoucher.value = merchantDiscount.value.indexOf('免费') >= 0 |
| | | merchantPrimaryTag.value = hasFreeVoucher.value ? '券' : '惠' |
| | | merchantHero.value = routedImage |
| | | merchantDiscount.value = routedDiscount |
| | | merchantConsumeDiscount.value = '' |
| | | hasFreeVoucher.value = false |
| | | merchantPrimaryTag.value = '券' |
| | | merchantAddress.value = routedAddress == '' ? '西区清香坪北街51号' : routedAddress |
| | | merchantLocation.value = { latitude: 26.5856, longitude: 101.7184, name: name, address: merchantAddress.value } |
| | | packages.value = hotelPackages |
| | |
| | | usageInstructions.value = '到达景区后出示人才码,根据景区后台配置的权益规则核验使用;开放时间、适用区域及预约要求以景区现场说明为准。' |
| | | merchantRightsText.value = '持卓越人才卡可享该景区配置的专属游览权益,具体优惠内容、开放时间、预约方式和使用限制以景区说明为准。' |
| | | } else if (routedMerchantType == 'apartment') { |
| | | merchantPrimaryTag.value = '惠' |
| | | merchantPrimaryTag.value = '券' |
| | | groupBuyingEnabled.value = false |
| | | merchantServiceLabel.value = '住宿服务' |
| | | usageInstructions.value = '到店后出示人才码并按人才公寓或驿站的后台配置提交入住申请,房源、入住时长及审核要求以服务方说明为准。' |
| | |
| | | } |
| | | } |
| | | } |
| | | const session = getSession() |
| | | if (session != null && session.talentId != null) talentId.value = session.talentId as string |
| | | if (talentId.value == '' && apiEnvironment == 'development') talentId.value = '2' |
| | | if (isFuelMerchant.value || isParkingMerchant.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 : '' |
| | | currentCardLevel.value = card != null && card.level != null ? card.level as string : '' |
| | | loadTransportMerchantDetail() |
| | | }, () => { loadTransportMerchantDetail() }) |
| | | } else if (isFuelMerchant.value || isParkingMerchant.value) { |
| | | loadTransportMerchantDetail() |
| | | } else { |
| | | if ((isHotelMerchant.value || isApartmentMerchant.value) && talentId.value != '') { |
| | | getTalentCardProfile(talentId.value, (data: any) => { |
| | | const card = data != null ? data.card : null |
| | | talentCardId.value = card != null && card.id != null ? '' + card.id : talentCardId.value |
| | | currentCardLevel.value = card != null && card.level != null ? card.level as string : currentCardLevel.value |
| | | const advisor: any = data != null ? data.advisor : null |
| | | advisorMobile.value = advisor != null && advisor.mobile != null ? advisor.mobile as string : '' |
| | | }, () => {}) |
| | | } |
| | | loadRemoteMerchant() |
| | | loadMerchantLists() |
| | | } |
| | |
| | | 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, |
| | |
| | | const stores: any[] = data != null && data.applicableStores instanceof Array ? data.applicableStores as any[] : [] |
| | | applicableStores.value = stores.map((item: any) => ({ |
| | | id: '' + item.id, |
| | | name: item.storeName != null ? item.storeName as string : '', |
| | | name: item.storeName != null ? item.storeName as string : (item.name != null ? item.name as string : ''), |
| | | address: item.address != null ? item.address as string : '', |
| | | latitude: item.latitude != null ? Number(item.latitude) : 0, |
| | | longitude: item.longitude != null ? Number(item.longitude) : 0, |
| | | canNavigate: item.canNavigate === true || Number(item.canNavigate) == 1 |
| | | canNavigate: (item.canNavigate === true || Number(item.canNavigate) == 1) |
| | | && Number(item.latitude) > 0 && Number(item.longitude) > 0 |
| | | })) |
| | | merchantLoading.value = false |
| | | listLoading.value = false |
| | |
| | | listLoading.value = true |
| | | listLoaded.value = false |
| | | listError.value = false |
| | | getParkingMerchantDetail(merchantId.value, (data: any) => { |
| | | getParkingMerchantDetail(merchantId.value, talentCardId.value, (data: any) => { |
| | | merchantName.value = data != null && data.merchantName != null ? data.merchantName as string : '' |
| | | merchantHero.value = data != null && data.coverUrl != null && data.coverUrl != '' ? resolveApiUrl(data.coverUrl as string) : fallbackImage |
| | | merchantAddress.value = data != null && data.address != null ? data.address as string : '' |
| | |
| | | merchantAvailable.value = data != null && (data.available === true || Number(data.available) == 1) |
| | | 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.benefitText != null ? data.benefitText as string : '免费停车' |
| | | applicableCards.value = data != null && data.applicableCards != null ? data.applicableCards as string : '请咨询商户' |
| | | usageInstructions.value = data != null && data.usageInstructions != null ? data.usageInstructions as string : '请咨询商户' |
| | | parkingBenefitText.value = data != null && data.parkingType == 'free' ? '免费' : '付费' |
| | | 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() |
| | | : '' |
| | | usageInstructions.value = parkingRightDetail != '' |
| | | ? parkingRightDetail |
| | | : (data != null && data.rightsText != null && data.rightsText != '' ? data.rightsText as string : '请咨询商户') |
| | | merchantRightsText.value = data != null && data.rightsText != null ? data.rightsText as string : '请咨询商户' |
| | | parkingActionMode.value = data != null && data.actionMode == 'talentCode' ? 'talentCode' : 'plate' |
| | | parkingActionMode.value = data != null && data.parkingType == 'paid' ? 'talentCode' : 'plate' |
| | | parkingPlateMode.value = data != null && data.plateFormMode == 'withParking' ? 'withParking' : 'simple' |
| | | parkingBenefitId.value = data != null && data.benefit != null && data.benefit.benefitId != null ? '' + data.benefit.benefitId : '' |
| | | groupBuyingEnabled.value = false |
| | |
| | | } |
| | | onShow(() => { |
| | | if (isParkingMerchant.value && parkingDetailLoaded) loadParkingMerchant() |
| | | // 酒店:从免费券页/核销后返回时刷新剩余次数,按钮在「出示免费券 / 出示人才码」之间切换。 |
| | | refreshHotelVoucherAvailability() |
| | | }) |
| | | const loadRemoteMerchant = () => { |
| | | if (isFuelMerchant.value || isParkingMerchant.value) { loadTransportMerchantDetail(); return } |
| | | if (!isApiConfigured() || !/^\d+$/.test(merchantId.value)) return |
| | | merchantLoading.value = true |
| | | merchantError.value = false |
| | | getMerchantDetail(merchantId.value, (data: any) => { |
| | | getMerchantDetail(merchantId.value, talentId.value, (data: any) => { |
| | | merchantName.value = data != null && data.merchantName != null ? data.merchantName as string : merchantName.value |
| | | merchantHero.value = data != null && data.coverUrl != null && data.coverUrl != '' ? resolveApiUrl(data.coverUrl as string) : merchantHero.value |
| | | const heroRecords: any[] = data != null && data.heroImages instanceof Array ? data.heroImages as any[] : [] |
| | | const nextHeroes: string[] = [] |
| | | for (let index = 0; index < heroRecords.length; index++) { |
| | | const value = '' + heroRecords[index] |
| | | if (value != '') nextHeroes.push(resolveApiUrl(value)) |
| | | } |
| | | if (nextHeroes.length == 0) nextHeroes.push(merchantHero.value) |
| | | merchantHeroImages.value = nextHeroes |
| | | merchantAddress.value = data != null && data.address != null ? data.address as string : merchantAddress.value |
| | | const rawType = data != null && data.merchantType != null ? data.merchantType as string : merchantType.value |
| | | merchantType.value = rawType == '餐饮服务' ? 'food' : rawType |
| | | merchantType.value = rawType == '餐饮服务' ? 'food' : (rawType == '酒店服务' ? 'hotel' : ((rawType == '公寓租赁' || rawType == '公寓租赁服务') ? 'apartment' : rawType)) |
| | | merchantAvailable.value = data == null || data.available !== false |
| | | merchantPhone.value = data != null && data.contactMobile != null ? data.contactMobile as string : '' |
| | | merchantDiscount.value = data != null && data.discountText != null ? data.discountText as string : merchantDiscount.value |
| | | merchantServiceLabel.value = data != null && data.serviceLabel != null && data.serviceLabel != '' ? data.serviceLabel as string : merchantServiceLabel.value |
| | | 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 : '' |
| | | merchantRightsText.value = data != null && data.rightsText != null && data.rightsText != '' ? data.rightsText as string : merchantRightsText.value |
| | | usageInstructions.value = data != null && data.usageInstructions != null && data.usageInstructions != '' ? data.usageInstructions as string : usageInstructions.value |
| | | groupBuyingEnabled.value = data == null || data.groupBuyingEnabled == null ? groupBuyingEnabled.value : data.groupBuyingEnabled === true || Number(data.groupBuyingEnabled) == 1 |
| | | if (data != null && data.cardLevel != null && data.cardLevel != '') currentCardLevel.value = data.cardLevel as string |
| | | applyMedicalRightsConfig(data) |
| | | // 普通自助洗车复用餐饮商家的团单展示;外链洗车不会进入此通用详情页。 |
| | | const isNormalCarWash = merchantType.value == 'carWash' |
| | | groupBuyingEnabled.value = merchantType.value == 'food' |
| | | || merchantType.value == 'scenic' |
| | | || merchantType.value == 'hotel' |
| | | || merchantType.value == 'dental' |
| | | || merchantType.value == 'medical' |
| | | || isNormalCarWash |
| | | if (merchantType.value == 'culture') { |
| | | merchantPrimaryTag.value = merchantHasCoupon.value ? '券' : '权益' |
| | | } |
| | | isHotelMerchant.value = merchantType.value == 'hotel' |
| | | hasFreeVoucher.value = merchantDiscount.value.indexOf('免费') >= 0 |
| | | merchantConsumeDiscount.value = '' |
| | | if (isHotelMerchant.value) { |
| | | 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() : '' |
| | | merchantConsumeDiscount.value = hotelStoreConsume && consumeType == '折扣' && consumeDiscount != '' |
| | | ? consumeDiscount + '折' |
| | | : (hotelStoreConsume && consumeType == '优惠' && consumeCoupon != '' ? '减' + consumeCoupon : '') |
| | | } |
| | | hasFreeVoucher.value = isHotelMerchant.value && data != null && data.rightsStatus != null && String(data.rightsStatus) == 'AVAILABLE' && (data.freeVoucherEnabled === true || Number(data.freeVoucherEnabled) == 1) |
| | | if (hasFreeVoucher.value) { |
| | | 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) => { |
| | | const card = profile != null ? profile.card : null |
| | | talentCardId.value = card != null && card.id != null ? '' + card.id : talentCardId.value |
| | | if (card != null && card.level != null && card.level != '') currentCardLevel.value = card.level as string |
| | | }, () => {}) |
| | | } |
| | | merchantLocation.value = { |
| | | latitude: data != null && data.latitude != null ? Number(data.latitude) : 0, |
| | | longitude: data != null && data.longitude != null ? Number(data.longitude) : 0, |
| | |
| | | return |
| | | } |
| | | if (isApiConfigured() && /^\d+$/.test(merchantId.value) && !isMedicalMerchant.value && !isTransportMerchant.value) { |
| | | getProducts(merchantId.value, 1, 20, (data: any) => { |
| | | getProducts(merchantId.value, talentId.value, 1, 20, (data: any) => { |
| | | const records: any[] = data != null && data.records instanceof Array ? data.records as any[] : [] |
| | | const remotePackages: any[] = [] |
| | | for (let index = 0; index < records.length; index++) { |
| | | const item: any = records[index] |
| | | const sale = item.salePrice != null ? Number(item.salePrice) : 0 |
| | | const original = item.originalPrice != null ? Number(item.originalPrice) : 0 |
| | | const configuredSale = item.configuredSalePrice != null ? Number(item.configuredSalePrice) : sale |
| | | const configuredOriginal = item.configuredOriginalPrice != null ? Number(item.configuredOriginalPrice) : original |
| | | const stock = item.stock != null ? Number(item.stock) : -1 |
| | | remotePackages.push({ id: '' + item.id, name: item.productName != null ? item.productName as string : '人才专享套餐', discount: sale > 0 && original > sale ? '' + Math.round(sale / original * 100) / 10 + '折' : '人才专享', price: '' + sale, originalPrice: original > 0 ? '' + original : '', image: item.coverUrl != null && item.coverUrl != '' ? resolveApiUrl(item.coverUrl as string) : fallbackImage, available: stock != 0 }) |
| | | let displayPrice = '' + sale |
| | | if (isHotelMerchant.value) { |
| | | const rangeMin = configuredSale > 0 ? configuredSale : configuredOriginal |
| | | const rangeMax = configuredOriginal > 0 ? configuredOriginal : rangeMin |
| | | displayPrice = rangeMin > 0 && rangeMax > 0 && rangeMin != rangeMax ? '' + rangeMin + '-' + rangeMax : '' + rangeMin |
| | | } |
| | | remotePackages.push({ id: '' + item.id, name: item.productName != null ? item.productName as string : '人才专享套餐', discount: sale > 0 && original > sale ? '' + Math.round(sale / original * 100) / 10 + '折' : '人才专享', price: displayPrice, originalPrice: original > 0 ? '' + original : '', image: item.coverUrl != null && item.coverUrl != '' ? resolveApiUrl(item.coverUrl as string) : fallbackImage, available: stock != 0 }) |
| | | } |
| | | visiblePackages.value = remotePackages |
| | | visibleMedicalPackages.value = [] |
| | |
| | | listLoading.value = false |
| | | listError.value = true |
| | | }) |
| | | return |
| | | } |
| | | if (apiEnvironment != 'development') { |
| | | listLoading.value = false |
| | | listError.value = true |
| | | return |
| | | } |
| | | setTimeout(() => { |
| | |
| | | } |
| | | uni.makePhoneCall({ phoneNumber: merchantPhone.value }) |
| | | } |
| | | const contactSpecialist = () => { |
| | | if (advisorMobile.value == '') { |
| | | uni.showToast({ title: '暂未配置服务专员', icon: 'none' }) |
| | | return |
| | | } |
| | | uni.makePhoneCall({ phoneNumber: advisorMobile.value, fail: () => uni.showToast({ title: '暂时无法拨打电话', icon: 'none' }) }) |
| | | } |
| | | const openStoreLocation = (item: any) => { |
| | | if (item.canNavigate === false || Number(item.latitude) <= 0 || Number(item.longitude) <= 0) { |
| | | uni.showToast({ title: '该门店暂未配置位置', icon: 'none' }) |
| | |
| | | + encodeURIComponent(merchantName.value) |
| | | + '&hotelAddress=' + encodeURIComponent(merchantAddress.value) |
| | | + '&roomName=' + encodeURIComponent(item.name) |
| | | + '&merchantId=' + encodeURIComponent(merchantId.value) |
| | | + '&productId=' + encodeURIComponent('' + item.id) |
| | | + '&contactMobile=' + encodeURIComponent(merchantPhone.value) |
| | | }) |
| | | return |
| | | } |
| | |
| | | } |
| | | const openMedicalPackage = (item: any) => { |
| | | 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) }) |
| | | 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 (hasFreeVoucher.value) { |
| | | uni.navigateTo({ url: '/pages/free-benefit/free-benefit?merchantId=' + encodeURIComponent(merchantId.value) }) |
| | | if (isHotelMerchant.value && hasFreeVoucher.value) { |
| | | if (talentId.value != '' && talentCardId.value == '') { |
| | | getTalentCardProfile(talentId.value, (data: any) => { |
| | | const card = data != null ? data.card : null |
| | | talentCardId.value = card != null && card.id != null ? '' + card.id : talentCardId.value |
| | | uni.navigateTo({ url: '/pages/free-benefit/free-benefit?merchantId=' + encodeURIComponent(merchantId.value) + '&talentId=' + encodeURIComponent(talentId.value) + '&cardId=' + encodeURIComponent(talentCardId.value) }) |
| | | }, () => { |
| | | uni.navigateTo({ url: '/pages/free-benefit/free-benefit?merchantId=' + encodeURIComponent(merchantId.value) + '&talentId=' + encodeURIComponent(talentId.value) + '&cardId=' + encodeURIComponent(talentCardId.value) }) |
| | | }) |
| | | return |
| | | } |
| | | uni.navigateTo({ url: '/pages/free-benefit/free-benefit?merchantId=' + encodeURIComponent(merchantId.value) + '&talentId=' + encodeURIComponent(talentId.value) + '&cardId=' + encodeURIComponent(talentCardId.value) }) |
| | | return |
| | | } |
| | | uni.navigateTo({ url: '/pages/talent-code/talent-code?talentId=' + encodeURIComponent(talentId.value) + '&merchantId=' + encodeURIComponent(merchantId.value) + '&scene=' + encodeURIComponent(isParkingMerchant.value ? 'parking' : 'merchant') }) |
| | |
| | | + '&benefitId=' + encodeURIComponent(parkingBenefitId.value) |
| | | + '&mode=' + encodeURIComponent(parkingPlateMode.value) |
| | | + '&name=' + encodeURIComponent(merchantName.value) |
| | | + '&talentCardId=' + encodeURIComponent(talentCardId.value) |
| | | }) |
| | | } |
| | | </script> |
| | |
| | | .page { width: 100%; height: 100vh; background-color: #F6F6F6; } |
| | | .content { height: calc(100% - 150rpx); height: calc(100% - 150rpx - env(safe-area-inset-bottom)); background-color: #F6F6F6; } |
| | | .hero { height: 445rpx; position: relative; overflow: hidden; background-color: #3B3028; } |
| | | .hero-swiper { width: 100%; height: 445rpx; } |
| | | .hero-image { position: absolute; left: 0; top: 0; width: 100%; height: 445rpx; } |
| | | .hero-shade { position: absolute; left: 0; right: 0; top: 0; height: 190rpx; background-image: linear-gradient(180deg, rgba(0,0,0,.58) 0%, rgba(0,0,0,0) 100%); } |
| | | .custom-nav { position: fixed; z-index: 20; left: 0; right: 0; top: 0; padding-bottom: 20rpx; } |
| | |
| | | .buy-button { display: flex; width: 132rpx; height: 57rpx; border-radius: 29rpx; background-image: linear-gradient(90deg, #FF5733 0%, #FF8A20 100%); justify-content: center; align-items: center; } |
| | | .buy-button text { color: #FFFFFF; font-size: 27rpx; } |
| | | .rights-panel { margin: 16rpx 25rpx 0; padding: 28rpx 26rpx; border-radius: 15rpx; background-color: #FFFFFF; } |
| | | .rights-title { display: block; color: #303030; font-size: 32rpx; font-weight: 700; } |
| | | .rights-text { display: block; margin-top: 18rpx; color: #777F8B; font-size: 27rpx; line-height: 43rpx; } |
| | | .rule-row { display: flex; min-height: 70rpx; margin-top: 9rpx; border-top-width: 1rpx; border-top-style: solid; border-top-color: #F0F0F0; flex-direction: row; align-items: center; justify-content: space-between; color: #7D838C; font-size: 26rpx; } |
| | | .rule-row text:last-child { color: #3C424A; } |
| | | .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; box-sizing: border-box; padding: 17rpx 13rpx; border-right-width: 1rpx; border-right-style: solid; border-right-color: #E7ECF4; align-items: center; justify-content: flex-start; } |
| | | .store-address-cell { display: flex; min-width: 0; flex: 1; box-sizing: border-box; padding: 17rpx 13rpx; align-items: center; justify-content: flex-start; } |
| | | .store-name-cell text, .store-address-cell text { width: 100%; color: #48566A; font-size: 21rpx; line-height: 31rpx; text-align: left; } |
| | | .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; } |
| | | .store-cell-content { display: flex; width: 100%; min-width: 0; min-height: 92rpx; box-sizing: border-box; padding: 13rpx; flex-direction: row; align-items: center; } |
| | | .store-cell-content text { width: 100%; color: #48566A; font-size: 21rpx; line-height: 31rpx; text-align: left; } |
| | | .store-location { display: flex; width: 72rpx; flex-shrink: 0; justify-content: center; align-items: center; } |
| | | .store-location image { width: 34rpx; height: 34rpx; box-sizing: border-box; padding: 4rpx; border-width: 2rpx; border-style: solid; border-color: #8BB9FF; border-radius: 50%; background-color: #EEF6FF; } |
| | | .store-location.is-disabled { opacity: .4; } |
| | |
| | | .medical-package-item:last-child { margin-bottom: 0; } |
| | | .medical-package-image { width: 168rpx; height: 168rpx; border-radius: 14rpx; flex-shrink: 0; } |
| | | .medical-package-info { display: flex; min-width: 0; flex: 1; padding-left: 23rpx; flex-direction: column; } |
| | | .medical-package-name { color: #292929; font-size: 31rpx; font-weight: 700; } |
| | | .medical-package-bottom { display: flex; margin-top: auto; flex-direction: row; align-items: center; justify-content: space-between; } |
| | | .medical-open-row { display: flex; flex-direction: row; align-items: center; } |
| | | .medical-open-label { color: #8A919C; font-size: 24rpx; } |
| | | .medical-open-value { 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%); justify-content: center; align-items: center; } |
| | | .medical-package-top { display: flex; min-width: 0; flex-direction: row; align-items: center; justify-content: space-between; } |
| | | .medical-package-name { min-width: 0; flex: 1; margin-right: 16rpx; color: #292929; font-size: 31rpx; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } |
| | | .medical-open-row { display: flex; min-width: 0; margin-top: auto; flex-direction: row; align-items: center; } |
| | | .medical-open-label { color: #8A919C; font-size: 24rpx; flex-shrink: 0; } |
| | | .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; } |
| | | .talent-code-button-disabled { background-image: none; background-color: #C7CDD5; } |
| | | .talent-code-button image { width: 29rpx; height: 29rpx; margin-right: 14rpx; } |
| | | .talent-code-button text { color: #FFFFFF; font-size: 31rpx; font-weight: 500; } |
| | | .mobile-dialog-mask { display: flex; position: fixed; z-index: 100; left: 0; right: 0; top: 0; bottom: 0; background-color: rgba(24,29,38,.48); justify-content: center; align-items: center; } |