酒店详情

This commit is contained in:
zjf committed 2026-10-13 17:31:41 +08:00
1 parent 0958d8dded
commit d441fffc19
1 file changed
+53 -7
+53 -7
View File
@@ -21,7 +21,7 @@
<image v-else class="ht-hero__img" :src="banners[0] || placeholderCover" mode="aspectFill" />
</view>
<view class="ht-body pb-180rpx">
<view class="ht-body" :style="{ paddingBottom: bodyPadBottom }">
<!-- 基本信息(上浮卡片) -->
<view class="ht-card ht-card--main">
<view class="ht-name">{{ hotel.hotelName }}</view>
@@ -87,8 +87,8 @@
</view>
</scroll-view>
<!-- 底部起价 -->
<view v-if="hotel && roomCards.length" class="ht-bottom">
<!-- 底部起价:iOS/Android 用 safeAreaInsets 垫高,避免 Home 条/手势条遮挡 -->
<view v-if="showBottomBar" class="ht-bottom" :style="{ paddingBottom: bottomBarPadBottom }">
<view class="ht-bottom__price">
<text class="ht-unit">起价</text>
<text class="ht-yen">¥</text>
@@ -112,6 +112,7 @@ const STAR_MAP: Record<number, string> = { 1: "民宿", 2: "经济", 3: "舒适"
const placeholderCover = "/static/page/blue-bg.png"
const statusBarHeight = ref(20)
const safeBottomPx = ref(0)
const scrollHeight = ref("100vh")
const hotelId = ref("")
const MAX_STAY_NIGHTS = 30
@@ -123,6 +124,13 @@ const hotel = ref<any>(null)
const products = ref<any[]>([])
const rooms = ref<any[]>([])
/** 底部栏内容高度(上内边距 + 按钮 + 下内边距),不含安全区 */
const BOTTOM_BAR_INNER_RPX = 16 + 84 + 16
/** 无底栏时内容区额外留白 */
const BODY_PAD_BASE_RPX = 24
/** 有底栏时内容区额外缓冲,避免最后一张房型贴边 */
const BODY_PAD_EXTRA_RPX = 24
const nightCount = computed(() => {
const n = dayjs(checkOut.value).diff(dayjs(checkIn.value), "day")
return n > 0 ? n : 1
@@ -212,6 +220,26 @@ const minPrice = computed(() => {
return nums.length ? Math.min(...nums) : null
})
const showBottomBar = computed(() => !!(hotel.value && roomCards.value.length))
const rpx2px = (rpx: number) => {
try {
return uni.upx2px(rpx)
} catch {
return Math.round(rpx / 2)
}
}
/** 滚动区底部留白:底栏高度 + 安全区,避免 iOS Home 条 / Android 手势条遮住房型 */
const bodyPadBottom = computed(() => {
if (showBottomBar.value) {
return `${rpx2px(BOTTOM_BAR_INNER_RPX + BODY_PAD_EXTRA_RPX) + safeBottomPx.value}px`
}
return `${rpx2px(BODY_PAD_BASE_RPX) + safeBottomPx.value}px`
})
const bottomBarPadBottom = computed(() => `${rpx2px(16) + safeBottomPx.value}px`)
const formatMd = (ymd: string) => dayjs(ymd).format("M月D日")
const formatPrice = (v: any) => {
const n = Number(v)
@@ -226,9 +254,21 @@ const facilityIcon = (name: string) => {
return "checkbox-circle-line"
}
const readSafeBottom = (sys: any) => {
const inset = sys?.safeAreaInsets?.bottom
if (typeof inset === "number" && inset >= 0) return inset
const area = sys?.safeArea
if (area && typeof sys?.screenHeight === "number") {
const bottom = sys.screenHeight - (area.bottom || 0)
if (bottom > 0 && bottom < 80) return bottom
}
return 0
}
const layout = () => {
const sys = uni.getSystemInfoSync()
statusBarHeight.value = sys.statusBarHeight || 20
safeBottomPx.value = readSafeBottom(sys)
scrollHeight.value = `${sys.windowHeight || 667}px`
}
@@ -255,7 +295,6 @@ const load = async () => {
const book = (p: any) => {
if (!p?.productCode) return
// 页面跳转 query 只带关键键;名称/地址/封面等进确认页后用详情接口补齐,避免整单信息挂在 URL 上
router.jump("/pages/hotel/confirm", {
hotelId: hotelId.value,
productCode: p.productCode,
@@ -272,7 +311,6 @@ onLoad((opt: any) => {
checkIn.value = opt?.checkIn || checkIn.value
checkOut.value = opt?.checkOut || checkOut.value
roomsCount.value = Number(opt?.rooms || 1) || 1
// 连住最多 30 晚,避免超长区间带入确认页
const nights = dayjs(checkOut.value).diff(dayjs(checkIn.value), "day")
if (nights > MAX_STAY_NIGHTS) {
checkOut.value = dayjs(checkIn.value).add(MAX_STAY_NIGHTS, "day").format("YYYY-MM-DD")
@@ -320,9 +358,12 @@ onLoad((opt: any) => {
}
.ht-body {
margin-top: -36rpx;
padding: 0 24rpx 24rpx;
padding-left: 24rpx;
padding-right: 24rpx;
/* padding-bottom 由 bodyPadBottom 动态计算(底栏 + iOS/Android 安全区) */
position: relative;
z-index: 2;
box-sizing: border-box;
}
.ht-card {
background: #fff;
@@ -492,11 +533,16 @@ onLoad((opt: any) => {
right: 0;
bottom: 0;
z-index: 20;
padding: 16rpx 28rpx calc(16rpx + env(safe-area-inset-bottom));
padding-top: 16rpx;
padding-left: 28rpx;
padding-right: 28rpx;
padding-bottom: calc(16rpx + constant(safe-area-inset-bottom));
padding-bottom: calc(16rpx + env(safe-area-inset-bottom));
background: #fff;
display: flex;
align-items: center;
gap: 20rpx;
box-sizing: border-box;
box-shadow: 0 -4rpx 20rpx rgba(15, 23, 42, 0.06);
}
.ht-bottom__price {