586 lines
15 KiB
Vue
586 lines
15 KiB
Vue
<template>
|
|
<view class="page">
|
|
<u-navbar title="酒店" :border-bottom="false" :background="bgc" back-icon-color="#262B37" title-color='#262B37'
|
|
title-size='36' height='46' id="navbar">
|
|
</u-navbar>
|
|
|
|
<!-- 搜索栏 -->
|
|
<view class="search-container">
|
|
<view class="search-bar">
|
|
<image class="search-icon" src="https://api.ccttiot.com/smartmeter/img/static/uAO3fdmrrHTzqbVx9hYd" mode=""></image>
|
|
<input class="search-input" placeholder="搜索酒店、民宿" v-model="searchKeyword" @confirm="handleSearch" />
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 筛选器 -->
|
|
<view class="filter-container">
|
|
<view class="filter-item-wrapper">
|
|
<view class="filter-item" @tap="showAreaPicker">
|
|
<text :class="{active: showArea}">{{ selectedArea }}</text>
|
|
<text class="arrow" :class="{ 'arrow-up': showArea }">▼</text>
|
|
</view>
|
|
</view>
|
|
<view class="filter-item-wrapper">
|
|
<view class="filter-item" @tap="showSortPicker">
|
|
<text :class="{active: showSort}">{{ selectedSort }}</text>
|
|
<text class="arrow" :class="{ 'arrow-up': showSort }">▼</text>
|
|
</view>
|
|
</view>
|
|
<view class="filter-item-wrapper">
|
|
<view class="filter-item" @tap="showPricePicker">
|
|
<text :class="{active: showPrice}">{{ selectedPrice }}</text>
|
|
<text class="arrow" :class="{ 'arrow-up': showPrice }">▼</text>
|
|
</view>
|
|
</view>
|
|
<view class="filter-item-wrapper">
|
|
<view class="filter-item" @tap="showFilterPicker">
|
|
<text :class="{active: showFilter}">筛选</text>
|
|
<text class="arrow" :class="{ 'arrow-up': showFilter }">▼</text>
|
|
</view>
|
|
</view>
|
|
<!-- 下拉菜单容器 -->
|
|
<view class="dropdown-container" v-if="showArea || showSort || showPrice || showFilter">
|
|
<!-- 区域/位置下拉菜单 -->
|
|
<view class="dropdown-menu" v-if="showArea">
|
|
<view class="dropdown-item" :class="{selected: selectedArea===item.name}" v-for="(item, index) in areaOptions" :key="index" @tap="selectArea(item)">
|
|
<text>{{ item.name }}</text>
|
|
</view>
|
|
</view>
|
|
<!-- 综合排序下拉菜单 -->
|
|
<view class="dropdown-menu" v-if="showSort">
|
|
<view class="dropdown-item" :class="{selected: selectedSort===item.name}" v-for="(item, index) in sortOptions" :key="index" @tap="selectSort(item)">
|
|
<text>{{ item.name }}</text>
|
|
</view>
|
|
</view>
|
|
<!-- 价格下拉菜单 -->
|
|
<view class="dropdown-menu" v-if="showPrice">
|
|
<view class="dropdown-item" :class="{selected: selectedPrice===item.name}" v-for="(item, index) in priceOptions" :key="index" @tap="selectPrice(item)">
|
|
<text>{{ item.name }}</text>
|
|
</view>
|
|
</view>
|
|
<!-- 筛选下拉菜单 -->
|
|
<view class="dropdown-menu" v-if="showFilter">
|
|
<view class="dropdown-item" :class="{selected: selectedFilter===item.name}" v-for="(item, index) in filterOptions" :key="index" @tap="selectFilter(item)">
|
|
<text>{{ item.name }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 酒店列表 -->
|
|
<scroll-view class="hotel-list" @scrolltolower="handqixing" scroll-y refresher-enabled @refresherrefresh="onRefresh" :refresher-triggered="isRefreshing" refresher-default-style="black">
|
|
<!-- 遮罩层 -->
|
|
<view class="dropdown-mask" v-if="showArea || showSort || showPrice || showFilter" @tap="closeAllDropdowns"></view>
|
|
<view class="hotel-item" v-for="(item, index) in hotelList" :key="index" @click="goToDetail(item)">
|
|
<view class="hotel-image">
|
|
<image :src="getFirstImage(item.picture)" mode="aspectFill"></image>
|
|
</view>
|
|
<view class="hotel-info">
|
|
<view class="hotel-name">{{ item.name }}</view>
|
|
<view class="hotel-rating">
|
|
<view class="rating-score">{{ item.rating || '4.9' }}</view>
|
|
<view class="rating-text" v-if="item.tags && item.tags.length > 0" v-for="(val,indexs) in item.tags" :key="indexs">{{val}}</view>
|
|
</view>
|
|
<view class="hotel-location">
|
|
<text>据您直线{{ item.distance || '--' }}公里</text>
|
|
<text v-if="item.address"> · {{ item.address.length > 10 ? item.address.slice(0,10) + '...' : item.address }}</text>
|
|
</view>
|
|
<view class="hotel-price" style="display: flex;justify-content: space-between;">
|
|
<text></text>
|
|
<view class="" style="display: flex;align-items: center;">
|
|
<!-- <text class="original-price" v-if="item.originalPrice">¥{{ item.originalPrice }}</text> -->
|
|
<text class="current-price" style="margin-left: 24rpx;">¥{{ item.priceMin || '0' }} <text style="color: #333;font-size: 24rpx;font-weight: 400;">起</text> </text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
<view class="no-more">
|
|
当前没有更多酒店了...
|
|
</view>
|
|
</scroll-view>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
export default {
|
|
data() {
|
|
return {
|
|
bgc: {
|
|
backgroundColor: "#fff",
|
|
},
|
|
searchKeyword: '',
|
|
showArea: false,
|
|
showSort: false,
|
|
showPrice: false,
|
|
showFilter: false,
|
|
// 选中项显示
|
|
selectedArea: '区域/位置',
|
|
selectedSort: '综合排序',
|
|
selectedPrice: '价格',
|
|
selectedFilter: '筛选',
|
|
// 筛选选项
|
|
areaOptions: [
|
|
{name:'全部',
|
|
value:''}
|
|
],
|
|
sortOptions: [
|
|
{name: '综合排序', value: ''},
|
|
{name: '距离最近', value: 'distance_asc'},
|
|
{name: '评分最高', value: 'rating_desc'},
|
|
{name: '价格最低', value: 'price_asc'},
|
|
{name: '价格最高', value: 'price_desc'}
|
|
],
|
|
priceOptions: [{name: '价格', value: ''},
|
|
{name: '100以下', value: '0,100'},
|
|
{name: '100-200', value: '100,200'},
|
|
{name: '200-300', value: '200,300'},
|
|
{name: '300以上', value: '300'}],
|
|
filterOptions: [
|
|
{name: '全部', value: ''},
|
|
],
|
|
hotelList: [],
|
|
isRefreshing: false,
|
|
pageNum: 1,
|
|
total: 0,
|
|
lat: '',
|
|
lng: '',
|
|
selectedAreaValue: '',
|
|
selectedSortValue: '',
|
|
selectedPriceValue: '',
|
|
selectedFilterValue: ''
|
|
}
|
|
},
|
|
onLoad() {
|
|
this.gettiaojian()
|
|
this.getshuaixuan()
|
|
// 初始化筛选选项
|
|
// 获取位置信息
|
|
uni.getLocation({
|
|
type: 'gcj02',
|
|
isHighAccuracy: true,
|
|
accuracy: 'best',
|
|
success: (res) => {
|
|
this.lat = res.latitude
|
|
this.lng = res.longitude
|
|
this.getlist()
|
|
},
|
|
fail: (err) => {
|
|
console.error('获取位置失败:', err)
|
|
this.getlist()
|
|
}
|
|
})
|
|
},
|
|
onShow() {
|
|
// 页面显示时关闭所有下拉菜单
|
|
this.closeAllDropdowns()
|
|
},
|
|
methods: {
|
|
// 请求酒店筛选条件
|
|
getshuaixuan(){
|
|
this.$u.get(`/system/dict/data/type/hotel_type`).then(res =>{
|
|
if(res.code == 200){
|
|
res.data.forEach(item =>{
|
|
this.filterOptions.push({
|
|
name:item.dictLabel,
|
|
value:item.dictValue
|
|
})
|
|
})
|
|
}
|
|
})
|
|
},
|
|
// 请求酒店位置条件
|
|
gettiaojian(){
|
|
this.$u.get(`/system/dict/data/type/hotel_location`).then(res =>{
|
|
if(res.code == 200){
|
|
res.data.forEach(item =>{
|
|
this.areaOptions.push({
|
|
name:item.dictLabel,
|
|
value:item.dictValue
|
|
})
|
|
})
|
|
}
|
|
})
|
|
},
|
|
// 获取第一张图片
|
|
getFirstImage(pictureString) {
|
|
if (!pictureString) return '';
|
|
// 如果包含逗号,取第一张图片
|
|
if (pictureString.includes(',')) {
|
|
return pictureString.split(',')[0].trim();
|
|
}
|
|
return pictureString.trim();
|
|
},
|
|
// 请求酒店列表
|
|
getlist() {
|
|
this.$u.get(`/app/hotel/list?keyword=${this.searchKeyword}&pageNum=${this.pageNum}&pageSize=20&currLon=${this.lng}&currLat=${this.lat}&priceRange=${this.selectedPriceValue}&sortBy=${this.selectedSortValue}&location=${this.selectedAreaValue}&type=${this.selectedFilterValue}`).then((res) => {
|
|
if(res.code == 200){
|
|
this.total = res.total
|
|
if(this.pageNum == 1){
|
|
this.pageNum++
|
|
this.hotelList = res.rows
|
|
}else{
|
|
this.pageNum++
|
|
this.hotelList = this.hotelList.concat(res.rows)
|
|
}
|
|
}
|
|
})
|
|
},
|
|
// 上拉加载更多
|
|
handqixing() {
|
|
if (this.hotelList.length < this.total) {
|
|
this.getlist()
|
|
}
|
|
},
|
|
// 下拉刷新列表
|
|
onRefresh() {
|
|
this.isRefreshing = true
|
|
this.pageNum = 1
|
|
this.getlist()
|
|
setTimeout(() => {
|
|
this.isRefreshing = false
|
|
}, 1000)
|
|
},
|
|
// 点击搜索查询列表
|
|
handleSearch() {
|
|
this.pageNum = 1
|
|
this.getlist()
|
|
},
|
|
// 点击展示下拉菜单 - 区域
|
|
showAreaPicker() {
|
|
if (this.showArea) {
|
|
this.showArea = false
|
|
} else {
|
|
this.closeAllDropdowns()
|
|
this.showArea = true
|
|
}
|
|
},
|
|
// 点击展示下拉菜单 - 排序
|
|
showSortPicker() {
|
|
if (this.showSort) {
|
|
this.showSort = false
|
|
} else {
|
|
this.closeAllDropdowns()
|
|
this.showSort = true
|
|
}
|
|
},
|
|
// 点击展示下拉菜单 - 价格
|
|
showPricePicker() {
|
|
if (this.showPrice) {
|
|
this.showPrice = false
|
|
} else {
|
|
this.closeAllDropdowns()
|
|
this.showPrice = true
|
|
}
|
|
},
|
|
// 点击展示下拉菜单 - 筛选
|
|
showFilterPicker() {
|
|
if (this.showFilter) {
|
|
this.showFilter = false
|
|
} else {
|
|
this.closeAllDropdowns()
|
|
this.showFilter = true
|
|
}
|
|
},
|
|
// 点击隐藏下拉菜单
|
|
closeAllDropdowns() {
|
|
this.showArea = false
|
|
this.showSort = false
|
|
this.showPrice = false
|
|
this.showFilter = false
|
|
},
|
|
// 点击选择区域
|
|
selectArea(item) {
|
|
this.selectedArea = item.name
|
|
this.selectedAreaValue = item.value
|
|
this.showArea = false
|
|
this.pageNum = 1
|
|
this.getlist()
|
|
},
|
|
// 点击选择排序
|
|
selectSort(item) {
|
|
this.selectedSort = item.name
|
|
this.selectedSortValue = item.value
|
|
this.showSort = false
|
|
if (this.selectedSortValue == 'distance_asc') {
|
|
uni.getLocation({
|
|
type: 'gcj02',
|
|
isHighAccuracy: true,
|
|
accuracy: 'best',
|
|
success: (res) => {
|
|
this.lat = res.latitude
|
|
this.lng = res.longitude
|
|
this.pageNum = 1
|
|
this.getlist()
|
|
},
|
|
fail: (err) => {
|
|
console.error('获取位置失败:', err)
|
|
this.lat = ''
|
|
this.lng = ''
|
|
this.pageNum = 1
|
|
this.getlist()
|
|
}
|
|
})
|
|
} else {
|
|
this.pageNum = 1
|
|
this.getlist()
|
|
}
|
|
},
|
|
// 点击选择价格
|
|
selectPrice(item) {
|
|
this.selectedPrice = item.name
|
|
this.selectedPriceValue = item.value
|
|
this.showPrice = false
|
|
this.pageNum = 1
|
|
this.getlist()
|
|
},
|
|
// 点击选择筛选
|
|
selectFilter(item) {
|
|
this.selectedFilter = item.name
|
|
this.selectedFilterValue = item.value
|
|
this.showFilter = false
|
|
this.pageNum = 1
|
|
this.getlist()
|
|
},
|
|
// 跳转到详情页
|
|
goToDetail(item) {
|
|
uni.navigateTo({
|
|
url: '/page_fenbao/jiudian/jiudianxq?id=' + item.id
|
|
})
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
::v-deep .u-icon__icon,
|
|
::v-deep .u-title {
|
|
padding-bottom: 22rpx !important;
|
|
}
|
|
page {
|
|
background: #fff;
|
|
}
|
|
.page {
|
|
background: #fff;
|
|
min-height: 100vh;
|
|
}
|
|
// 搜索栏样式
|
|
.search-container {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 20rpx 30rpx;
|
|
background: #fff;
|
|
gap: 20rpx;
|
|
z-index: 1002;
|
|
.search-bar {
|
|
flex: 1;
|
|
display: flex;
|
|
align-items: center;
|
|
background: #f5f5f5;
|
|
border-radius: 50rpx;
|
|
padding: 16rpx 30rpx;
|
|
gap: 20rpx;
|
|
.search-icon {
|
|
width: 32rpx;
|
|
height: 32rpx;
|
|
}
|
|
.search-input {
|
|
flex: 1;
|
|
font-size: 28rpx;
|
|
color: #333;
|
|
border: none;
|
|
background: transparent;
|
|
outline: none;
|
|
&::placeholder {
|
|
color: #999;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// 筛选器样式
|
|
.filter-container {
|
|
position: relative;
|
|
display: flex;
|
|
background: #fff;
|
|
padding: 0 30rpx 20rpx;
|
|
gap: 20rpx;
|
|
z-index: 1002;
|
|
box-shadow: 0 1rpx 4rpx rgba(0, 0, 0, 0.1);
|
|
.filter-item-wrapper {
|
|
flex: 1;
|
|
.filter-item {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 10rpx 0;
|
|
font-size: 28rpx;
|
|
color: #333;
|
|
border-bottom: 2rpx solid transparent;
|
|
transition: all 0.3s ease;
|
|
text {
|
|
&.active {
|
|
color: #1EC28B;
|
|
font-weight: 600;
|
|
}
|
|
}
|
|
.arrow {
|
|
font-size: 20rpx;
|
|
color: #999;
|
|
transition: transform 0.3s ease;
|
|
margin-left: 10rpx;
|
|
}
|
|
.arrow-up {
|
|
transform: rotate(180deg);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// 下拉菜单容器样式
|
|
.dropdown-container {
|
|
position: fixed;
|
|
top: 366rpx;
|
|
left: 0;
|
|
background: #fff;
|
|
border-radius: 0 0 20rpx 20rpx;
|
|
z-index: 1001;
|
|
overflow: hidden;
|
|
width: 100%;
|
|
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.1);
|
|
.dropdown-menu {
|
|
width: 100%;
|
|
.dropdown-item {
|
|
padding: 30rpx 20rpx;
|
|
font-size: 28rpx;
|
|
color: #333;
|
|
border-bottom: 1rpx solid #f0f0f0;
|
|
transition: all 0.3s ease;
|
|
background: #fff;
|
|
position: relative;
|
|
&:last-child {
|
|
border-bottom: none;
|
|
}
|
|
&:active {
|
|
background: #1EC28B;
|
|
color: #fff;
|
|
}
|
|
&.selected {
|
|
color: #1EC28B;
|
|
font-weight: 600;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// 酒店列表样式
|
|
.hotel-list {
|
|
background: #fff;
|
|
width: 750rpx;
|
|
height: 77vh;
|
|
overflow: scroll;
|
|
margin: auto;
|
|
margin-top: 20rpx;
|
|
.hotel-item {
|
|
display: flex;
|
|
background: #fff;
|
|
border-radius: 10rpx;
|
|
width: 730rpx;
|
|
margin: auto;
|
|
margin-bottom: 30rpx;
|
|
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.08);
|
|
overflow: hidden;
|
|
.hotel-image {
|
|
width: 180rpx;
|
|
height: 232rpx;
|
|
flex-shrink: 0;
|
|
border-radius: 10rpx;
|
|
overflow: hidden;
|
|
image {
|
|
width: 100%;
|
|
height: 100%;
|
|
display: block;
|
|
}
|
|
}
|
|
.hotel-info {
|
|
flex: 1;
|
|
padding: 24rpx 20rpx;
|
|
padding-left: 32rpx;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: space-between;
|
|
.hotel-name {
|
|
font-size: 32rpx;
|
|
font-weight: 600;
|
|
color: #262B37;
|
|
margin-bottom: 15rpx;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
.hotel-rating {
|
|
display: flex;
|
|
align-items: center;
|
|
margin-bottom: 15rpx;
|
|
gap: 10rpx;
|
|
flex-wrap: wrap;
|
|
.rating-score {
|
|
background: #1EC28B;
|
|
color: #fff;
|
|
font-size: 24rpx;
|
|
font-weight: 600;
|
|
padding: 6rpx 14rpx;
|
|
border-radius: 6rpx;
|
|
margin-right: 8rpx;
|
|
}
|
|
.rating-text {
|
|
background: #E8FFFB;
|
|
color: #1EC28B;
|
|
font-size: 22rpx;
|
|
padding: 6rpx 14rpx;
|
|
border-radius: 6rpx;
|
|
}
|
|
}
|
|
.hotel-location {
|
|
font-size: 24rpx;
|
|
color: #999;
|
|
margin-bottom: 15rpx;
|
|
line-height: 1.5;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
.hotel-price {
|
|
display: flex;
|
|
align-items: baseline;
|
|
gap: 10rpx;
|
|
width: 490rpx;
|
|
.original-price {
|
|
font-size: 24rpx;
|
|
color: #999;
|
|
text-decoration: line-through;
|
|
}
|
|
.current-price {
|
|
font-size: 32rpx;
|
|
color: #FF0000;
|
|
font-weight: 600;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.no-more {
|
|
margin-top: 30rpx;
|
|
text-align: center;
|
|
color: #ccc;
|
|
font-size: 24rpx;
|
|
padding-bottom: 30rpx;
|
|
}
|
|
}
|
|
// 遮罩层样式
|
|
.dropdown-mask {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background: rgba(0, 0, 0, 0.3);
|
|
z-index: 1;
|
|
}
|
|
</style>
|