| | |
| | | <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> |
| | |
| | | <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> |
| | |
| | | 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 => { |
| | |
| | | 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) }) } |
| | |
| | | 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> |