shinnosuke
4 天以前 5ce0d76154bf4c1d70543476cdfad9a30195b0f8
rencaika/pages/medical-package/medical-package.uvue
@@ -1,7 +1,7 @@
<template>
   <view class="page">
      <view class="custom-nav" :class="isSolidNav ? 'is-solid' : ''"><view class="custom-nav-bg"></view><view class="safe-space" :style="'height:' + navTop + 'px'"></view><view class="nav-row" :style="'height:' + navHeight + 'px;padding-right:' + navRight + 'px'"><view class="back-button" @tap="goBack"><image src="/static/food-assets/arrow-left-white.svg"></image></view><text class="nav-title">{{packageName}}</text><view class="capsule-space" :style="'width:' + navWidth + 'px'"></view></view></view>
      <scroll-view class="content" scroll-y @scroll="handleScroll"><view class="hero"><image class="hero-image" :src="heroImage" mode="aspectFill" @error="replaceHero"></image><view class="hero-shade"></view></view><view class="package-panel">
      <scroll-view :show-scrollbar="false" class="content" scroll-y @scroll="handleScroll"><view class="hero"><image class="hero-image" :src="heroImage" mode="aspectFill" @error="replaceHero"></image><view class="hero-shade"></view></view><view class="package-panel">
         <view v-if="detailLoading" class="detail-state"><view class="loading-spinner"></view><text>正在加载体检套餐</text></view>
         <view v-else-if="detailError" class="detail-state error-state"><text>体检套餐加载失败</text><text class="retry-text" @tap="loadDetail">重新加载</text></view>
         <view v-else>
@@ -9,6 +9,7 @@
            <view class="tabs"><view class="tab-item active"><text>详情</text><view class="tab-line"></view></view></view>
            <view v-if="itemsLoading" class="list-state"><view class="loading-spinner"></view><text>正在加载检查项目</text></view><view v-else-if="itemsError" class="list-state error-state"><text>检查项目加载失败</text><text class="retry-text" @tap="loadItems">重新加载</text></view><view v-else-if="itemsLoaded && packageItems.length == 0" class="list-state"><text>暂无体检检查项目</text></view>
            <view v-if="itemsLoaded && packageItems.length > 0" class="check-table"><view class="check-head"><view class="check-cell cell-item"><text>检查项目</text></view><view class="check-cell cell-meaning"><text>项目意义</text></view><view class="check-cell cell-gender"><text>男</text></view><view class="check-cell cell-gender cell-gender-end"><text>女</text></view></view><view class="check-row" v-for="item in packageItems" :key="item.id"><view class="check-cell cell-item"><text>{{item.name}}</text></view><view class="check-cell cell-meaning"><text>{{item.meaning}}</text></view><view class="check-cell cell-gender"><text>{{item.male ? '√' : ''}}</text></view><view class="check-cell cell-gender cell-gender-end"><text>{{item.female ? '√' : ''}}</text></view></view></view>
            <view v-if="detailImages.length > 0" class="detail-images"><image v-for="(imageUrl, index) in detailImages" :key="imageUrl + index" class="detail-image" :src="imageUrl" mode="widthFix" @error="dropDetailImage(index)"></image></view>
         </view><view class="content-bottom"></view>
      </view></scroll-view>
      <view class="action-bar"><view :class="canBook ? 'booking-button' : 'booking-button booking-disabled'" hover-class="booking-button-active" @tap="goBooking"><text>{{packageAvailable ? '预约' : '暂不可预约'}}</text></view></view>
@@ -23,6 +24,8 @@
   const packageId = ref(''); const hospitalId = ref(''); const routeApplicableCards = ref(''); const detailLoading = ref(true); const detailError = ref(false); const itemsLoading = ref(false); const itemsLoaded = ref(false); const itemsError = ref(false)
   const packageName = ref(''); const heroImage = ref(''); const freeLabel = ref('免费'); const applicableCards = ref(''); const packageAvailable = ref(false); const unavailableReason = ref('该套餐暂不可预约')
   const hospitalName = ref(''); const hospitalAddress = ref(''); const hospitalPhone = ref(''); const latitude = ref(0); const longitude = ref(0); const packageItems = ref<any[]>([])
   // 商户在「图文详情」上传的图片(可能多张),按顺序铺在检查项目表格下面。
   const detailImages = ref<string[]>([])
   const canNavigate = computed(() => latitude.value > 0 && longitude.value > 0)
   const canBook = computed(() => !detailLoading.value && !detailError.value && packageAvailable.value)
   const decodeRouteText = (value: any): string => {
@@ -38,12 +41,13 @@
   const loadDetail = () => {
      if (packageId.value == '') { detailLoading.value = false; detailError.value = true; return }
      detailLoading.value = true; detailError.value = false
      getMedicalPackage(packageId.value, (data: any) => { const hospital: any = data.hospital || {}; packageName.value = data.name || ''; heroImage.value = data.heroImage != null && data.heroImage != '' ? resolveApiUrl(data.heroImage as string) : ''; freeLabel.value = data.freeLabel || '免费'; const apiApplicableCards = data.applicableCards != null ? String(data.applicableCards).trim() : ''; applicableCards.value = apiApplicableCards != '' ? apiApplicableCards : routeApplicableCards.value; packageAvailable.value = data.available === true; unavailableReason.value = data.unavailableReason || unavailableReason.value; hospitalId.value = String(hospital.hospitalId || hospitalId.value); hospitalName.value = hospital.name || ''; hospitalAddress.value = hospital.address || '医院暂未配置地址'; hospitalPhone.value = hospital.mobile || ''; latitude.value = Number(hospital.latitude); longitude.value = Number(hospital.longitude); detailLoading.value = false; loadItems() }, () => { detailLoading.value = false; detailError.value = true })
      getMedicalPackage(packageId.value, (data: any) => { const hospital: any = data.hospital || {}; packageName.value = data.name || ''; heroImage.value = data.heroImage != null && data.heroImage != '' ? resolveApiUrl(data.heroImage as string) : ''; freeLabel.value = data.freeLabel || '免费'; const apiApplicableCards = data.applicableCards != null ? String(data.applicableCards).trim() : ''; applicableCards.value = apiApplicableCards != '' ? apiApplicableCards : routeApplicableCards.value; packageAvailable.value = data.available === true; unavailableReason.value = data.unavailableReason || unavailableReason.value; hospitalId.value = String(hospital.hospitalId || hospitalId.value); hospitalName.value = hospital.name || ''; hospitalAddress.value = hospital.address || '医院暂未配置地址'; hospitalPhone.value = hospital.mobile || ''; latitude.value = Number(hospital.latitude); longitude.value = Number(hospital.longitude); const images: string[] = []; const sourceImages: any[] = data.detailImages instanceof Array ? data.detailImages as any[] : []; for (let index = 0; index < sourceImages.length; index++) { const value = sourceImages[index] == null ? '' : ('' + sourceImages[index]).trim(); if (value.length > 0) images.push(resolveApiUrl(value)) }; detailImages.value = images; detailLoading.value = false; loadItems() }, () => { detailLoading.value = false; detailError.value = true })
   }
   const loadItems = () => { if (packageId.value == '') return; itemsLoading.value = true; itemsLoaded.value = false; itemsError.value = false; getMedicalPackageItems(packageId.value, (data: any) => { packageItems.value = data instanceof Array ? data as any[] : []; itemsLoading.value = false; itemsLoaded.value = true }, () => { itemsLoading.value = false; itemsError.value = true }) }
   const goBack = () => safeBack('/pages/premium-benefits/premium-benefits')
   const handleScroll = (event: any) => { const show = event.detail.scrollTop > 64; if (isSolidNav.value != show) isSolidNav.value = show }
   const replaceHero = () => { heroImage.value = '' }
   const dropDetailImage = (index: number) => { const next = detailImages.value.slice(); next.splice(index, 1); detailImages.value = next }
   const openMap = () => { if (!canNavigate.value) { uni.showToast({ title: '医院暂未配置位置', icon: 'none' }); return }; uni.openLocation({ latitude: latitude.value, longitude: longitude.value, name: hospitalName.value, address: hospitalAddress.value, scale: 18, fail: () => uni.showToast({ title: '暂时无法打开地图导航', icon: 'none' }) }) }
   const callHospital = () => { if (hospitalPhone.value == '') { uni.showToast({ title: '医院暂未配置联系电话', icon: 'none' }); return }; uni.makePhoneCall({ phoneNumber: hospitalPhone.value, fail: () => uni.showToast({ title: '暂时无法拨打电话', icon: 'none' }) }) }
   const goBooking = () => { if (!canBook.value) { if (!detailLoading.value && !detailError.value) uni.showToast({ title: unavailableReason.value, icon: 'none' }); return }; uni.navigateTo({ url: '/pages/medical-booking/medical-booking?packageId=' + encodeURIComponent(packageId.value) + '&hospitalId=' + encodeURIComponent(hospitalId.value) + '&package=' + encodeURIComponent(packageName.value) + '&hospital=' + encodeURIComponent(hospitalName.value) }) }
@@ -83,4 +87,15 @@
      display: flex;
      flex-direction: row;
   }
   /* 商户在「图文详情」上传的多张图片:整组铺在检查项目表格下面,按原图比例完整显示。 */
   .detail-images {
      margin: 16rpx 25rpx 0;
   }
   .detail-image {
      display: block;
      width: 100%;
      margin-bottom: 16rpx;
      border-radius: 8rpx;
      background-color: #F2F4F7;
   }
</style>