<template>
<view class="u-wrap">
<view class="u-search-box" :style="{ paddingTop: safeAreaInsets?.top + 'px' }">
<view class="u-search-inner">
<u-icon name="search" color="#909399" :size="28"></u-icon>
<text class="u-search-text">搜索</text>
</view>
</view>
<view class="u-menu-wrap">
<scroll-view scroll-y scroll-with-animation class="u-tab-view menu-scroll-view" :scroll-top="scrollTop"
:scroll-into-view="itemId">
<view v-for="(item, index) in tabbar" :key="index" class="u-tab-item"
:class="[current == index ? 'u-tab-item-active' : '']" @tap.stop="swichMenu(index)">
<text class="u-line-1">{{ item.name }}</text>
</view>
</scroll-view>
<scroll-view :scroll-top="scrollRightTop" scroll-y scroll-with-animation class="right-box menuHeight"
@scroll="rightScroll">
<view class="page-view">
<view class="class-item menuList" :id="'item' + index" v-for="(item, index) in tabbar" :key="index">
<view class="item-title">
<text>{{ item.name }}</text>
</view>
<view class="item-container">
<view v-if="item.name == '价格范围'" class="roomPrice">
<view class="priceMsg search-item">
<text :style="{ color: price >= 20 ? '#3DC5FB' : 'black' }">0</text>
<text :style="{ color: price >= 20 ? '#3DC5FB' : 'black' }">¥100</text>
<text :style="{ color: price >= 40 ? '#3DC5FB' : 'black' }">¥200</text>
<text :style="{ color: price >= 60 ? '#3DC5FB' : 'black' }">¥300</text>
<text :style="{ color: price >= 80 ? '#3DC5FB' : 'black' }">¥400</text>
<text :style="{ color: price >= 100 ? '#3DC5FB' : 'black' }">¥500+</text>
</view>
<u-slider v-model="price" step="20" @change="changePrice"></u-slider>
</view>
<view v-if="item.name == '房源户型' && searchObj" class="roomType search-item">
<view v-for="item in housType" :key="item">
<view
:class="{ 'typeList-activ': searchObj.housTypeVal == item.val, typeList: true }"
@click="choiceHouseType(item.val)">
{{ item.name }}
</view>
</view>
</view>
<view v-if="item.name == '出租类型' && searchObj" class="roomType search-item">
<view :class="{ 'typeList-activ': searchObj.houseType.rentMode == 1, typeList: true }"
@click="searchObj.houseType.rentMode = 1">
<text>独享整套</text>
</view>
<view :class="{ 'typeList-activ': searchObj.houseType.rentMode == 2, typeList: true }"
@click="searchObj.houseType.rentMode = 2">
<text>单间出租</text>
</view>
</view>
<view v-if="item.name == '设施' && searchObj" class="roomType search-item">
<view
:class="{ 'typeList-activ': searchObj.otherArr.includes(item.id), typeList: true }"
v-for="item in facilitiesArr" :key="item.id" @click="appendOtherList(item.id)">
{{ item.name }}
</view>
<view :class="{ 'typeList-activ': searchObj.otherArr.includes(1049), menuList: true }"
@click="appendOtherList(1049)">
洗发水/沐浴露
</view>
</view>
<view v-if="item.name == '服务' && searchObj" class="roomType search-item">
<view v-for="item in serveArr" :key="item.id"
:class="{ 'typeList-activ': searchObj.otherArr.includes(item.id), menuList: true }"
@click="appendOtherList(item.id)">
{{ item.name }}
</view>
</view>
<view v-if="item.name == '用途' && searchObj" class="roomType search-item">
<view @click="searchRequreArr(item.id)" :class="{
'typeList-activ': addrequeryArr.includes(item.id) || searchObj.addrequeryArr.includes(item.id),
typeList: true,
}" v-for="item in requireArr" :key="item.id">
{{ item.name }}
</view>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
<view class="detail-btn">
<view>
<u-button @click="cancellation" :custom-style="{
width: '187.5rpx',
height: '97.22rpx',
borderRadius: '18rpx',
}">取消</u-button>
</view>
<view>
<u-button :custom-style="{
width: '458.33rpx',
height: '97.22rpx',
background: '#ff960c',
borderRadius: '18rpx',
color: '#fff',
}" @click="sendInfo">确定</u-button>
</view>
</view>
</view>
</template>
<script setup>
import {
ref,
onMounted,
nextTick
} from 'vue';
import {
onLoad,
onShow,
onReady
} from '@dcloudio/uni-app';
import {
getCurrentInstance
} from 'vue';
const props = defineProps({
searchObj: {
type:Object,
default:()=>{
return {
priceRange:[100],
housTypeVal:1,
houseType:{
rentMode:1
},
otherArr:[1035,1047,1049,1029,1041],
addrequeryArr:[1068,1069]
}
}
},
});
const menuCompoments = ref(null);
const currentInstance = ref();
const emit = defineEmits(['closePopup', 'sendSearObj']);
const scrollTop = ref(0);
const oldScrollTop = ref(0);
const current = ref(0);
const menuHeight = ref(0);
const menuItemHeight = ref(0);
const itemId = ref('');
const arr = ref([]);
const scrollRightTop = ref(0);
const timer = ref(null);
const {
safeAreaInsets
} = uni.getSystemInfoSync();
const price = ref(20);
const searchObj = ref();
const housTypeVal = ref();
const addrequeryArr = ref([1078,1069]);
const tabbar = ref([{
name: '价格范围'
}, {
name: '房源户型'
}, {
name: '出租类型'
}, {
name: '设施'
}, {
name: '服务'
}, {
name: '用途'
}]);
const housType = ref([{
name: '1室',
val: 1
},
{
name: '1室1厅',
val: 1.1
},
{
name: '2室',
val: 2
},
{
name: '4室',
val: 4
},
{
name: '2室1厅',
val: 2.1
},
{
name: '3室',
val: 3
},
]);
const requireArr = ref([{
id: 1068,
name: '接待婴儿'
},
{
id: 1069,
name: '接待儿童'
},
{
id: 1070,
name: '接待老人'
},
{
id: 1071,
name: '接待外宾'
},
{
id: 1072,
name: '允许吸烟'
},
{
id: 1073,
name: '携带宠物'
},
{
id: 1074,
name: '允许做饭'
},
{
id: 1075,
name: '允许聚会'
},
{
id: 1076,
name: '商业拍摄'
},
]);
const facilitiesArr = ref([{
id: 1035,
name: '电梯'
},
{
id: 1047,
name: '暖气'
},
{
id: 1031,
name: '电视'
},
{
id: 1036,
name: '厨房'
},
{
id: 1037,
name: '书房'
},
{
id: 1038,
name: '阳台'
},
{
id: 1048,
name: '浴巾'
},
{
id: 1050,
name: '电吹风'
},
]);
const serveArr = ref([{
id: 1029,
name: '无线网络'
},
{
id: 1032,
name: '独立卫浴'
},
{
id: 1041,
name: '提供餐饮'
},
{
id: 1039,
name: '免费停车位'
},
{
id: 1040,
name: '付费停车位'
},
{
id: 1027,
name: '可洗热水澡'
},
{
id: 1042,
name: '支团建会议'
},
]);
onMounted(() => {
currentInstance.value = getCurrentInstance();
getMenuItemTop();
if (props.searchObj) searchObj.value = props.searchObj;
dateEcho();
});
const swichMenu = async (index) => {
if (arr.value.length === 0) {
await getMenuItemTop();
}
if (index === current.value) return;
scrollRightTop.value = oldScrollTop.value;
nextTick(() => {
scrollRightTop.value = arr.value[index];
current.value = index;
leftMenuStatus(index);
});
};
const getElRect = async (elClass, dataVal) => {
return new Promise((resolve) => {
const query = uni.createSelectorQuery().in(currentInstance.value);
query
.select('.' + elClass)
.boundingClientRect((rect) => {
if (!rect) {
setTimeout(() => {
getElRect(elClass, dataVal);
}, 10);
return;
}
if (dataVal == 'menuHeight') menuHeight.value = rect.height;
if (dataVal == 'menuItemHeight') menuItemHeight.value = rect.height;
dataVal = rect.height;
resolve();
})
.exec();
});
};
const observer = () => {
tabbar.value.map((val, index) => {
let observer = uni.createIntersectionObserver(currentInstance.value);
observer
.relativeTo('.right-box', {
top: 0,
})
.observe('#item' + index, (res) => {
if (res.intersectionRatio > 0) {
let id = res.id.substring(4);
leftMenuStatus(id);
}
});
});
};
const leftMenuStatus = async (index) => {
current.value = index;
if (menuHeight.value === 0 || menuItemHeight.value === 0) {
await getElRect('menu-scroll-view', 'menuHeight');
await getElRect('u-tab-item', 'menuItemHeight');
}
scrollTop.value = index * menuItemHeight.value + menuItemHeight.value / 2 - menuHeight.value / 2;
};
const getMenuItemTop = () => {
return new Promise((resolve) => {
let selectorQuery = uni.createSelectorQuery().in(currentInstance.value);
setTimeout(() => {
selectorQuery
.selectAll('.search-item')
.boundingClientRect((rects) => {
if (!rects.length) {
setTimeout(() => {
getMenuItemTop();
}, 10);
return;
}
rects.forEach((rect) => {
arr.value.push(rect.top - rects[0].top);
resolve();
});
})
.exec();
}, 100);
});
};
const rightScroll = async (e) => {
oldScrollTop.value = e.detail.scrollTop;
if (arr.value.length === 0) {
await getMenuItemTop();
}
if (timer.value) return;
if (!menuHeight.value) {
await getElRect('menu-scroll-view', 'menuHeight');
}
setTimeout(() => {
timer.value = null;
let scrollHeight = e.detail.scrollTop;
for (let i = 0; i < arr.value.length; i++) {
let height1 = Math.floor(arr.value[i]);
let height2 = Math.floor(arr.value[i + 1]);
if (scrollHeight >= height1 && scrollHeight < height2) {
leftMenuStatus(i);
return;
}
if (!height2) {
leftMenuStatus(5);
}
}
}, 10);
};
const dateEcho = () => {
price.value = props.searchObj.priceRange[1] / 500;
};
const changePrice = () => {
searchObj.value.priceRange = [0, (price.value / 2) * 1000];
if (price.value === 100) searchObj.value.priceRange = [0, 9999];
};
const cancellation = () => {
emit('closePopup', false);
};
const sendInfo = () => {
emit('sendSearObj', searchObj.value);
};
const choiceHouseType = (val) => {
housTypeVal.value = val;
searchObj.value.houseType.bedRoomCount = +val.toFixed(0);
searchObj.value.housTypeVal = val;
};
const searchRequreArr = (val) => {
if (!addrequeryArr.value.includes(val)) {
addrequeryArr.value.push(val);
} else {
addrequeryArr.value = addrequeryArr.value.filter((item) => item !== val);
}
searchObj.value.requireArr = addrequeryArr.value.map((item) => ({
id: String(item),
checked: '1',
}));
searchObj.value.addrequeryArr = addrequeryArr.value;
};
const appendOtherList = (val) => {
if (!searchObj.value.otherArr.includes(val)) {
searchObj.value.otherArr.push(val);
} else {
searchObj.value.otherArr = searchObj.value.otherArr.filter((item) => item !== val);
}
};
</script>
<style lang="scss" scoped>
.u-wrap {
height: calc(100vh);
height: calc(100vh - var(--window-top));
display: flex;
flex-direction: column;
box-sizing: border-box;
padding-top: 20rpx;
background-color: #f6f6f6;
}
.u-search-box {
display: none;
}
.u-menu-wrap {
flex: 1;
display: flex;
overflow: hidden;
}
.u-search-inner {
background-color: rgb(234, 234, 234);
border-radius: 100rpx;
display: flex;
align-items: center;
padding: 10rpx 16rpx;
}
.u-search-text {
font-size: 26rpx;
color: $u-tips-color;
margin-left: 10rpx;
}
.u-tab-view {
width: 200rpx;
height: 100%;
}
.u-tab-item {
height: 110rpx;
background: #f6f6f6;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: center;
font-size: 26rpx;
color: #444;
font-weight: 400;
line-height: 1;
}
.u-tab-item-active {
position: relative;
color: #000;
font-size: 30rpx;
font-weight: 600;
background: #fff;
}
.u-tab-item-active::before {
content: '';
position: absolute;
border-left: 4px solid $u-primary;
height: 32rpx;
left: 0;
top: 39rpx;
}
.u-tab-view {
height: 100%;
}
.right-box {
background-color: rgb(250, 250, 250);
}
.page-view {
padding: 16rpx;
}
.class-item {
margin-bottom: 30rpx;
background-color: #fff;
padding: 16rpx;
border-radius: 8rpx;
}
.class-item:last-child {
min-height: 100vh;
}
.item-title {
font-size: 26rpx;
color: $u-main-color;
font-weight: bold;
margin-bottom: 20rpx;
}
.item-menu-name {
font-weight: normal;
font-size: 24rpx;
color: $u-main-color;
}
.item-container {
display: flex;
flex-wrap: wrap;
.roomPrice {
width: 100%;
.priceMsg {
display: flex;
justify-content: space-between;
font-size: 25rpx;
margin-left: 34.72rpx;
}
}
}
.thumb-box {
width: 33.333333%;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
margin-top: 20rpx;
}
.item-menu-image {
width: 120rpx;
height: 120rpx;
}
.roomType {
display: flex;
flex-wrap: wrap;
.typeList {
width: 152.78rpx;
height: 69.44rpx;
margin: 0 20.83rpx 13.89rpx 0;
display: flex;
justify-content: center;
align-items: center;
background-color: #f0f1f3;
font-size: 14px;
font-weight: 300;
}
.menuList {
box-sizing: border-box;
margin: 0 20.83rpx 13.89rpx 0;
display: flex;
justify-content: center;
align-items: center;
background-color: #f0f1f3;
font-size: 14px;
font-weight: 300;
padding: 22.92rpx;
}
.typeList-activ {
background-image: url(https://rizuwang-1316974425.cos.ap-beijing.myqcloud.com/mobilePics/home/background.png);
background-repeat: no-repeat;
background-position-x: 5px;
background-size: 22.22rpx 22.22rpx;
background-position: bottom right;
background-color: #dcf5ff;
}
}
.detail-btn {
margin-top: 50rpx;
margin-bottom: 20rpx;
display: flex;
justify-content: space-around;
}
</style>