From 366d304ab6fb0f4aaca1a27e8fefe93587c2e958 Mon Sep 17 00:00:00 2001
From: zjf <1799650030@qq.com>
Date: Tue, 20 Oct 2026 11:50:51 +0800
Subject: [PATCH] =?UTF-8?q?=E9=85=92=E5=BA=97=E4=BC=98=E5=8C=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/pages/hotel/list.vue | 81 ++++-
src/pages/tabbar/travel.vue | 99 ++++++-
src/sheep/api/hotel.ts | 15 +
.../components/hotel/hotel-guest-sheet.vue | 278 ++++++++++++++++++
4 files changed, 467 insertions(+), 6 deletions(-)
create mode 100644 src/sheep/components/hotel/hotel-guest-sheet.vue
diff --git a/src/pages/hotel/list.vue b/src/pages/hotel/list.vue
index 713e6ae..4885a1f 100644
--- a/src/pages/hotel/list.vue
+++ b/src/pages/hotel/list.vue
@@ -52,13 +52,21 @@
{{ formatMd(query.checkOut) }}
-
- 共住
- {{ nightCount }}晚
+
+ {{ nightCount }}晚 · 间数人数
+ {{ guestSummaryShort }}
+
+
当前目的地
@@ -276,7 +284,11 @@
@scrolltolower="loadMore"
>
- 加载中…
+
+
+ 正在努力查找酒店中
+ 请稍候…
+
暂无符合条件的酒店
@@ -319,7 +331,10 @@
- 加载更多…
+
+
+ 正在努力查找更多酒店…
+
没有更多了
@@ -333,10 +348,12 @@ import dayjs from "dayjs"
import { hotelApi } from "@@/api"
import { router } from "@@/index"
import { filePath } from "@@/utils/file"
+import HotelGuestSheet from "@@/components/hotel/hotel-guest-sheet.vue"
const statusBarHeight = ref(20)
const scrollHeight = ref("100vh")
const refreshing = ref(false)
+const guestVisible = ref(false)
const panel = ref<"" | "area" | "price" | "score" | "more">("")
const filterTabs = [
@@ -366,10 +383,12 @@ const query = reactive({
checkOut: dayjs().add(1, "day").format("YYYY-MM-DD"),
rooms: 1,
adults: 1,
+ children: 0,
})
const listCheckOutMin = computed(() => dayjs(query.checkIn).add(1, "day").format("YYYY-MM-DD"))
const listCheckOutMax = computed(() => dayjs(query.checkIn).add(MAX_STAY_NIGHTS, "day").format("YYYY-MM-DD"))
+const guestSummaryShort = computed(() => `${query.rooms}间${query.adults + query.children}人`)
const normalizeListDate = (value: string) => dayjs(String(value || "").replace(/\//g, "-")).format("YYYY-MM-DD")
@@ -391,6 +410,7 @@ const onListCheckInConfirm = (value: string) => {
query.checkOut = dayjs(query.checkIn).add(1, "day").format("YYYY-MM-DD")
}
clampListStayRange(true)
+ reload()
}
const onListCheckOutConfirm = (value: string) => {
@@ -403,6 +423,26 @@ const onListCheckOutConfirm = (value: string) => {
}
query.checkOut = d
clampListStayRange(true)
+ reload()
+}
+
+const onGuestConfirm = (v: { rooms: number; adults: number; children: number }) => {
+ query.rooms = v.rooms
+ query.adults = v.adults
+ query.children = v.children
+ hotelApi
+ .saveSearchPref({
+ rooms: query.rooms,
+ adults: query.adults,
+ children: query.children,
+ nights: nightCount.value,
+ city: query.city,
+ keyword: query.keyword,
+ checkIn: query.checkIn,
+ checkOut: query.checkOut,
+ })
+ .catch(() => {})
+ reload()
}
const filter = reactive({
@@ -895,6 +935,11 @@ const fetchPage = async (reset = false) => {
minPrice: filter.minPrice != null ? filter.minPrice : undefined,
maxPrice: filter.maxPrice != null ? filter.maxPrice : undefined,
facilityTags: filter.facilityTags.length ? filter.facilityTags.join(",") : undefined,
+ checkIn: query.checkIn || undefined,
+ checkOut: query.checkOut || undefined,
+ rooms: query.rooms || undefined,
+ adults: query.adults || undefined,
+ children: query.children ?? undefined,
})
const data = res?.data || {}
const rows = data.records || []
@@ -930,6 +975,8 @@ const goDetail = (item: any) => {
checkIn: query.checkIn,
checkOut: query.checkOut,
rooms: String(query.rooms),
+ adults: String(query.adults),
+ children: String(query.children),
})
}
@@ -942,6 +989,7 @@ onLoad((opt: any) => {
query.checkOut = opt?.checkOut || query.checkOut
query.rooms = Number(opt?.rooms || 1) || 1
query.adults = Number(opt?.adults || 1) || 1
+ query.children = Math.max(0, Number(opt?.children || 0) || 0)
clampListStayRange(false)
layout()
loadMeta()
@@ -1278,6 +1326,29 @@ onLoad((opt: any) => {
font-size: 26rpx;
color: #94a3b8;
}
+.ht-loading {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: 120rpx 32rpx 80rpx;
+ gap: 16rpx;
+}
+.ht-loading--more {
+ padding: 32rpx 0 48rpx;
+ flex-direction: row;
+ gap: 12rpx;
+}
+.ht-loading__title {
+ margin-top: 8rpx;
+ font-size: 30rpx;
+ font-weight: 600;
+ color: #334155;
+}
+.ht-loading__sub {
+ font-size: 24rpx;
+ color: #94a3b8;
+}
.ht-hint {
width: 100%;
font-size: 22rpx;
diff --git a/src/pages/tabbar/travel.vue b/src/pages/tabbar/travel.vue
index a14cac2..897f092 100644
--- a/src/pages/tabbar/travel.vue
+++ b/src/pages/tabbar/travel.vue
@@ -56,6 +56,14 @@
+
+
+ 间数人数
+ {{ guestSummary }}
+
+
+
+
+
+
@@ -121,7 +137,9 @@ import { onShow } from "@dcloudio/uni-app"
import dayjs from "dayjs"
import { router } from "@@/index"
import { useTabbarStore } from "@@/stores/tabbar"
+import hotelApi from "@@/api/hotel"
import TIndexAddress from "@@/components/address/t-index-address/t-index-address.vue"
+import HotelGuestSheet from "@@/components/hotel/hotel-guest-sheet.vue"
const DATE_PICKER_MIN = dayjs().format("YYYY-MM-DD")
const DATE_PICKER_MAX = dayjs().add(365, "day").format("YYYY-MM-DD")
@@ -133,12 +151,17 @@ const hotCities = ["北京", "上海", "广州", "深圳", "杭州", "成都", "
const cityVisible = ref(false)
const cityKeyword = ref("")
+const guestVisible = ref(false)
+const prefLoaded = ref(false)
const state = reactive({
city: "",
keyword: "",
checkIn: dayjs().format("YYYY-MM-DD"),
checkOut: dayjs().add(1, "day").format("YYYY-MM-DD"),
+ rooms: 1,
+ adults: 1,
+ children: 0,
})
const nightCount = computed(() => {
@@ -146,6 +169,10 @@ const nightCount = computed(() => {
return n > 0 ? n : 1
})
+const guestSummary = computed(
+ () => `${state.rooms}间 ${state.adults}成人 ${state.children}儿童`,
+)
+
const checkOutPickerMin = computed(() => dayjs(state.checkIn).add(1, "day").format("YYYY-MM-DD"))
const checkOutPickerMax = computed(() => dayjs(state.checkIn).add(MAX_STAY_NIGHTS, "day").format("YYYY-MM-DD"))
@@ -213,13 +240,63 @@ const clearCity = () => {
state.city = ""
}
+const onGuestConfirm = (v: { rooms: number; adults: number; children: number }) => {
+ state.rooms = v.rooms
+ state.adults = v.adults
+ state.children = v.children
+}
+
+const persistSearchPref = async () => {
+ try {
+ await hotelApi.saveSearchPref({
+ rooms: state.rooms,
+ adults: state.adults,
+ children: state.children,
+ nights: nightCount.value,
+ city: state.city,
+ keyword: state.keyword,
+ checkIn: state.checkIn,
+ checkOut: state.checkOut,
+ })
+ } catch {
+ /* 未登录或接口失败不阻断搜索 */
+ }
+}
+
+const loadSearchPref = async () => {
+ if (prefLoaded.value) return
+ try {
+ const res: any = await hotelApi.getSearchPref()
+ const d = res?.data
+ if (!d) return
+ state.rooms = Math.max(1, Number(d.rooms) || 1)
+ state.adults = Math.max(1, Number(d.adults) || 1)
+ state.children = Math.max(0, Number(d.children) || 0)
+ if (d.city) state.city = String(d.city)
+ // 晚数习惯:若库里有 nights,且当前仍是默认 1 晚,则按习惯回填离店
+ const nights = Math.max(1, Math.min(MAX_STAY_NIGHTS, Number(d.nights) || 1))
+ const today = dayjs().format("YYYY-MM-DD")
+ if (state.checkIn === today && nightCount.value === 1 && nights > 1) {
+ state.checkOut = dayjs(state.checkIn).add(nights, "day").format("YYYY-MM-DD")
+ }
+ prefLoaded.value = true
+ } catch {
+ prefLoaded.value = true
+ }
+}
+
const goSearch = () => {
+ clampStayRange(true)
+ // 习惯落库异步,不阻塞跳转与列表请求
+ persistSearchPref()
router.jump("/pages/hotel/list", {
city: state.city,
keyword: state.keyword,
checkIn: state.checkIn,
checkOut: state.checkOut,
- rooms: "1",
+ rooms: String(state.rooms),
+ adults: String(state.adults),
+ children: String(state.children),
})
}
@@ -230,6 +307,7 @@ const goOrders = () => {
onShow(() => {
// 从酒店列表等业务页返回时,确保原生底栏重新显示
useTabbarStore().showTabbar("/pages/tabbar/travel")
+ loadSearchPref()
})
@@ -347,6 +425,25 @@ onShow(() => {
color: #1570ef;
font-size: 22rpx;
}
+.ht-guest-row {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 20rpx 0;
+ border-bottom: 1rpx solid #f1f5f9;
+}
+.ht-guest-row__lab {
+ display: block;
+ font-size: 22rpx;
+ color: #94a3b8;
+}
+.ht-guest-row__val {
+ display: block;
+ margin-top: 6rpx;
+ font-size: 30rpx;
+ font-weight: 600;
+ color: #0f172a;
+}
.ht-keyword-wrap {
position: relative;
margin-top: 20rpx;
diff --git a/src/sheep/api/hotel.ts b/src/sheep/api/hotel.ts
index a05646f..1bab888 100644
--- a/src/sheep/api/hotel.ts
+++ b/src/sheep/api/hotel.ts
@@ -33,6 +33,21 @@ export default {
params,
})
},
+ /** 当前用户酒店搜索习惯 */
+ getSearchPref: (): Promise => {
+ return request({
+ url: "/supermarket/app/hotel/search-pref",
+ method: "GET",
+ })
+ },
+ /** 保存酒店搜索习惯 */
+ saveSearchPref: (data: any): Promise => {
+ return request({
+ url: "/supermarket/app/hotel/search-pref",
+ method: "POST",
+ data,
+ })
+ },
/** 酒店详情 */
detail: (id: string | number): Promise => {
return request({
diff --git a/src/sheep/components/hotel/hotel-guest-sheet.vue b/src/sheep/components/hotel/hotel-guest-sheet.vue
new file mode 100644
index 0000000..9621d9b
--- /dev/null
+++ b/src/sheep/components/hotel/hotel-guest-sheet.vue
@@ -0,0 +1,278 @@
+
+
+
+
+ 请选择房间和住客人数
+
+
+
+
+
+
+
+ 间数
+
+ −
+ {{ draft.rooms }}
+ +
+
+
+
+ 成人数
+
+ −
+ {{ draft.adults }}
+ +
+
+
+
+
+ 儿童数
+ 0-17岁
+
+
+ −
+ {{ draft.children }}
+ +
+
+
+
+
+ 重要提示
+ 1、儿童年龄请按照入住酒店当天实际年龄计算。
+ 2、当前按总人数筛选可住房型;成人/儿童标签后续再细化。
+
+
+
+
+
+
+
+
+
+
+