4.6接接口和样式

This commit is contained in:
DESKTOP-RQ919RC\Pc
2025-05-14 18:52:31 +08:00
parent 4b84dd558e
commit d201c8892f
22 changed files with 1064 additions and 690 deletions

View File

@@ -74,7 +74,7 @@
<img class="icon" src="@/assets/img/apartmentDetail/yellow-diamond.png" />
<div class="name">{{ company.title }}</div>
<div class="full-name flex1">{{ info.propaganda }}</div>
<div class="more flexacenter">
<div class="more flexacenter" @click="handleClickNav('eleseEle')">
同品牌
<img class="icon" src="@/assets/img/publicImage/black-arrow.svg" />
</div>
@@ -94,23 +94,17 @@
<div class="figure flexacenter">
距离
<div class="school">城大</div>
1.3km
<div class="btn">[切换院校]</div>
<div class="school">{{ distancePitch.alias }}</div>
{{ distancePitch.distance }}km
<div class="btn" @click="openSelectSchool">[切换院校]</div>
</div>
<div class="vehicle flexflex">
<div class="item flexacenter">
<img class="icon" src="@/assets/img/publicImage/walk-icon.png" />
2min
</div>
<div class="item flexacenter">
<img class="icon" src="@/assets/img/publicImage/walk-icon.png" />
2min
</div>
<div class="item flexacenter">
<img class="icon" src="@/assets/img/publicImage/walk-icon.png" />
2min
<div class="item flexacenter" v-for="(item, index) in distancePitch.obj" :key="index">
<img v-if="index == 'walking'" class="icon" src="@/assets/img/apartmentDetail/walk-icon.png" />
<img v-if="index == 'driving'" class="icon" src="@/assets/img/apartmentDetail/taxi-icon.png" />
<img v-if="index == 'transit'" class="icon" src="@/assets/img/apartmentDetail/subway-icon.png" />
{{ item }}
</div>
</div>
</div>
@@ -139,18 +133,18 @@
<img v-else alt="收藏图标" class="transmit-icon" src="@/assets/img/detail/collect.png" />
收藏
</div>
<div class="btn-item transmit-btn flexcenter" @click="handleTransmit">
<!-- <div class="btn-item transmit-btn flexcenter" @click="handleTransmit">
<img alt="转发图标" class="transmit-icon" src="@/assets/img/publicImage/transmit-icon.png" />
转发
<transmit-btn :qrcode="qrcode" :title="info['title']" type="apartment"></transmit-btn>
</div>
</div> -->
<div class="btn-item consult-btn flexcenter" @click="modificationContact">咨询</div>
</div>
</div>
</div>
<div class="details-box flexflex">
<div class="details-left flex1" ref="detailsLeft">
<div class="special-offer" v-if="info.promotionalactivities">
<div class="special-offer" v-if="info.promotionalactivities" ref="specialEle">
<img class="special-bj" src="@/assets/img/publicImage/special-bj.svg" />
<img class="head" src="@/assets/img/publicImage/special-title.png" />
<img class="gift" src="@/assets/img/publicImage/special-gift.png" />
@@ -167,7 +161,7 @@
<img class="bottom-orange" src="@/assets/img/publicImage/special-bottom-orange.svg" />
</div>
<div class="remark">
<div class="remark" ref="inspectEle">
<div class="head flexacenter">
<div class="text">寄托实地考察</div>
<div class="more flexacenter">
@@ -188,7 +182,7 @@
</div>
</div>
<div class="remark">
<div class="remark" ref="followEle">
<div class="head flexacenter">
<div class="text">寄托回访</div>
<div class="more flexacenter">
@@ -235,7 +229,7 @@
<img class="icon" src="@/assets/img/apartmentDetail/collect-hollow-black.svg" />
收藏
</div>
<div v-if="item.status == 1" class="consult flexcenter">
<div v-if="item.status == 1" class="consult flexcenter" @click="modificationContact">
<img class="icon" src="@/assets/img/apartmentDetail/consult-icon.png" />
咨询
</div>
@@ -288,7 +282,7 @@
</div>
<!-- 费用说明 -->
<div class="details-item cost" v-if="costList.length > 0">
<div class="details-item cost" v-if="costList.length > 0" ref="costEle">
<div class="details-header flexacenter">
<img class="icon" src="@/assets/img/apartmentDetail/cost-icon.png" />
费用说明
@@ -319,7 +313,7 @@
<img class="icon" src="@/assets/img/apartmentDetail/bus-icon.svg" />
{{ info.location || "交通" }}
</div>
<view-map :latlng="{ latitude: info['coordinate'][0], longitude: info['coordinate'][1] }" :name="info['address']"></view-map>
<view-map ref="viewMapRef" :latlng="{ latitude: info['coordinate'][0], longitude: info['coordinate'][1] }" :name="info['address']"></view-map>
<template v-if="false">
<el-popover :width="814" trigger="click" popper-style="padding: 0" :show-arrow="false" v-model:visible="showDistance">
@@ -429,43 +423,46 @@
</div>
<!-- 公寓设施 -->
<div class="details-item apartment-facilities" v-if="info['facilities']" ref="facilitiesEle">
<div class="details-item apartment-facilities" :class="{ hide: isHideFacilities }" v-if="facilitylist.length > 0" ref="facilitiesEle">
<div class="details-header flexacenter">
<img class="icon" src="@/assets/img/apartmentDetail/apartment-facilities.svg" />
公寓设施
</div>
<div class="facility-box">
<div class="item" v-for="item in 4">
<div class="head">公用设施6</div>
<div class="item" v-for="(item, index) in facilitylist" :key="index">
<div class="head">{{ item.name }}{{ item.label.length }}</div>
<div class="label flexflex">
<div class="label-item flexacenter" v-for="item in 4">
<div class="label-item flexacenter" v-for="(item, index) in item.label" :key="index">
<img class="icon" src="@/assets/img/apartmentDetail/tick-circle-baby-blue.svg" />
无线网络
{{ item }}
</div>
</div>
<div class="img-box flexflex">
<div class="img-item" v-for="item in 4">
<img class="icon" src="https://oss.x-php.com/Zvt57TuJSUvkyhw-xG7Y2l-d-JktdHnqqsgFptxhcq_cQnrld6R3X1dHBq_D-81qNDQyOQ~~" />
<div class="name">洗衣房</div>
<div class="img-box flexflex" v-if="item.images.length > 0">
<div class="img-item" v-for="(item, index) in item.images" :key="index" @click="openFacilitiesImg(item.img[0])">
<img class="icon" :src="item.img[0]" />
<div class="name">{{ item.name }}</div>
</div>
</div>
</div>
<div class="bottom-btn flexcenter">
展开全部
<img class="icon" src="@/assets/img/publicImage/arrow-black-solid.svg" />
</div>
</div>
<div class="bottom-btn flexcenter" v-if="isHideFacilities" @click="openFacilities">
展开全部
<img class="icon" src="@/assets/img/publicImage/arrow-black-solid.svg" />
</div>
</div>
<!-- 房源详情 -->
<div class="details-item" v-if="info['message']" ref="messageEle">
<div class="details-item details-message" :class="{ hide: isHideDetails }" v-if="info['message']" ref="messageEle">
<div class="details-header flexacenter">
<img class="icon" src="@/assets/img/apartmentDetail/listing-details.png" />
房源详情
</div>
<div class="text" v-html="info['message']"></div>
<div class="bottom-btn flexcenter" v-if="isHideDetails" @click="openDetails">
展开全部
<img class="icon" src="@/assets/img/publicImage/arrow-black-solid.svg" />
</div>
</div>
<!-- 公寓设施 -->
@@ -525,38 +522,14 @@
同品牌其他公寓
</div>
<div class="same-brand-list" v-if="dualBrandList.length != 0">
<div class="same-brand-item" v-for="(item, index) in dualBrandList" @click="gobrand(item)" :key="index">
<div class="same-brand-header">
<img class="same-brand-img" v-lazy="item['image']" />
<div class="apartment-name ellipsis">{{ item["title"] }}</div>
<div class="apartment-synopsis ellipsis">{{ item["propaganda"] }}</div>
</div>
<same-brand-item v-for="(item, index) in dualBrandList" :item="item" :key="index"></same-brand-item>
<div class="site flexacenter">
<img class="site-icon" src="@/assets/img/publicImage/location-icon.png" />
<div class="site-text ellipsis">{{ item["address"] }}</div>
</div>
<div class="price-box">
<div class="price-item flexacenter" v-for="(it, i) in item['roomlist']" :key="i">
<div class="room-name ellipsis flex1">{{ it["name"] }}</div>
<div class="flexacenter">
<div class="unit">HK$</div>
<div class="quantity">{{ it["price"] }}</div>
/
</div>
</div>
</div>
<div class="same-brand-quantity flexcenter">
<div class="quantity">{{ item["roomnum"] }}</div>
个房型
<img class="same-brand-quantity-icon" src="@/assets/img/publicImage/black-arrow.svg" />
</div>
</div>
<div class="same-brand-title like flexcenter" v-if="likeList.length != 0">
<img class="same-brand-icon" src="@/assets/img/apartmentDetail/same-brand-recommendation.png" />
猜你喜欢
</div>
<same-brand-item v-for="(item, index) in likeList" :item="item" :key="index"></same-brand-item>
</div>
</div>
</div>
@@ -595,11 +568,52 @@
</div>
<footerpage></footerpage>
<back-to-top></back-to-top>
<!-- 选择院校弹窗 -->
<el-dialog v-model="isSelectSchool" width="600" class="selectSchoolPop" :show-close="false">
<div class="title-box dis-f al-item jus-x">
选择院校
<img src="@/assets/img/detail/close.png" class="close-icon" @click="isSelectSchool = false" />
</div>
<div class="site flexacenter" bind:tap="to_map">
<img class="site-img" src="@/assets/img/apartmentDetail/orientation.png" />
<div class="site-text flex1">{{ info.address }}</div>
<div class="btn flexacenter" @click="openMap">
地图
<img class="btn-img" src="@/assets/img/apartmentDetail/arrow-black.svg" />
</div>
</div>
<div class="select-list" ref="selectSchoolRef">
<div class="select-item flexflex" :class="['item' + item.sid, { pitch: distancePitch.sid == item.sid }]" v-for="(item, index) in distanceList" :key="index" @click.stop="selectSchool(item.sid)">
<div class="select-icon flexcenter">
<img class="select-img" src="@/assets/img/apartmentDetail/u1834.png" />
</div>
<div class="select-info flex1">
<div class="select-name">{{ item.name }}</div>
<div class="distance flexacenter">
距离
<div class="abbreviation">{{ item.alias }}</div>
{{ item.distance }}km
</div>
<div class="vehicle flexflex flex1">
<div class="item flexcenter" v-for="(item, index) in item.obj" :key="index">
<img v-if="index == 'walking'" class="vehicle-icon" src="@/assets/img/apartmentDetail/walk-icon.png" />
<img v-else-if="index == 'driving'" class="vehicle-icon" src="@/assets/img/apartmentDetail/taxi-icon.png" />
<img v-else class="vehicle-icon" src="@/assets/img/apartmentDetail/subway-icon.png" />
{{ item }}
</div>
</div>
</div>
<img class="tick" v-if="distancePitch.sid == item.sid" src="@/assets/img/apartmentDetail/tick-circle-red.svg" />
</div>
</div>
</el-dialog>
</template>
<script setup>
import { ref, onMounted, onUnmounted, toRefs, watch, getCurrentInstance, nextTick } from "vue";
import { ElMessage, valueEquals } from "element-plus";
import { ElMessage, ElDialog } from "element-plus";
import { useStore } from "vuex";
import pageTopBar from "../components/pageTopBar/pageTopBar.vue";
@@ -610,6 +624,8 @@ import backToTop from "@/components/public/backToTop.vue";
import imageWatch from "@/components/detail/imageWatch.vue";
import phoneqrcode from "@/components/public/phoneQRcode.vue";
import groupqrcode from "@/components/public/group-QRcode.vue";
import sameBrandItem from "@/components/apartment/sameBrandItem.vue";
import api from "@/utils/api";
import { useRouter, useRoute } from "vue-router";
@@ -624,7 +640,7 @@ watch(route, () => {
roomList.value = [];
carouselsconfig.value = { lives: {}, videos: {}, attachment: {} };
navList.value = [];
navTab.value = "roomEle";
navTab.value = "specialEle";
dualBrandList.value = [];
contactReservationState.value = false;
customerservicelist.value = [];
@@ -636,6 +652,12 @@ watch(route, () => {
carouselIndex.value = 0;
allCarouselsData.value = [];
isHideFacilities.value = false;
facilitylist.value = [];
costList.value = [];
isHideDetails.value = false;
// mediaBtnstate.value = {}
init();
distanceSchool();
@@ -644,7 +666,7 @@ watch(route, () => {
let { uniqid } = router.currentRoute.value.query;
let pitchSchool = route.query.school || 0;
import { copyToClipboard, metersToKilometers, secondsToHoursMinutes } from "@/utils/util.js";
import { copyToClipboard, metersToKilometers, secondsToHoursMinutes, calculateDuration, calculateDistance } from "@/utils/util.js";
const { proxy } = getCurrentInstance();
const store = useStore();
@@ -726,12 +748,20 @@ const facilityKeyName = {
};
const facilityArr = ["public", "service", "sport", "outdoor", "security", "room"]; // 公寓设施 顺序
let facilitylist = ref([]);
let costList = ref([]);
let pitchScreenSchool = ref(0); // 选择的学校
onMounted(() => {
init();
distanceSchool();
let value = localStorage.getItem("apartmentPitchValue");
if (value) {
value = JSON.parse(value);
pitchScreenSchool = value.school;
}
});
const init = () => {
@@ -772,10 +802,12 @@ const init = () => {
});
const facility = data.info.facility;
console.log("facility", facility);
if (facility) {
let facilitylist = [];
facility["public"][0].images = ["https://oss.x-php.com/Zvt57TuJSUvkyhw-xG7Y2l-c-5skdX7qqsgFptxhXa6QWi2uePJ5Bg8WFLPIqoYV7MtZCjvF5wr_-kU8uRQ0NDI5", "https://oss.x-php.com/Zvt57TuJSUvkyhw-xG7Y2l-c-5skfHvqqsgFptxhXa6QWi2uePJ5Bg8WFLPIqoYV7MsIAzvG5Ar_-kU8uRQ0NDI5"];
facility["public"][1].images = ["https://oss.x-php.com/Zvt57TuJSUvkyhw-xG7Y2l-c-5skdXfqqsgFptxhXa6QWi2uePJ5Bg8WFLPIqoYV7MtdDG6W5Q7_-kU8uRQ0NDI5", "https://oss.x-php.com/Zvt57TuJSUvkyhw-xG7Y2l-c-5skfXnqqsgFptxhXa6QWi2uePJ5Bg8WFLPIqoYV7MtcA2vF5A7_-kU8uRQ0NDI5"];
let list = [];
facilityArr.forEach((key) => {
const target = facility[key];
let label = [];
@@ -792,7 +824,7 @@ const init = () => {
}
if (label.length > 0) {
facilitylist.push({
list.push({
key,
name: facilityKeyName[key],
label,
@@ -801,7 +833,7 @@ const init = () => {
}
});
console.log("facilitylist", facilitylist);
facilitylist.value = list;
}
const feedescription = data.info.feedescription;
@@ -815,9 +847,6 @@ const init = () => {
});
}
});
console.log("list", list);
costList.value = list;
}
@@ -832,12 +861,82 @@ const init = () => {
document.title = data?.info?.title || "港校租房-品牌公寓详情";
nextTick(() => handleNavData());
nextTick(() => {
handleNavData();
getFacilitiesHeight();
getDetailsHeight();
getLikeList();
getMapDistance();
if (!data.info.promotionalactivities) navTab.value = "inspectEle";
});
if (data.withsameapartments > 0) dualBrandData();
});
};
let isHideDetails = ref(false);
// 获取详情的高度
const getDetailsHeight = () => {
let target = messageEle.value;
if (target) {
let height = target.offsetHeight;
if (height > 820) isHideDetails.value = true;
}
};
// 切换 详情 显示
const openDetails = () => {
isHideDetails.value = !isHideDetails.value;
let target = messageEle.value;
let header = target.querySelector(".details-header");
let box = target.querySelector(".text");
if (target) target.style.height = header.offsetHeight + box.offsetHeight + "px";
};
let isHideFacilities = ref(false);
// 获取设施的高度
const getFacilitiesHeight = () => {
let target = facilitiesEle.value;
if (target) {
let height = target.offsetHeight;
if (height > 820) isHideFacilities.value = true;
}
};
// 切换 设施 显示
const openFacilities = () => {
isHideFacilities.value = !isHideFacilities.value;
let target = facilitiesEle.value;
let header = target.querySelector(".details-header");
let box = target.querySelector(".facility-box");
if (target) target.style.height = header.offsetHeight + box.offsetHeight + "px";
};
// 点击打开设施图片预览
const openFacilitiesImg = (current) => {
const list = facilitylist.value || [];
let urls = [];
list.forEach((element) => {
element.images.forEach((ele) => {
ele.img.forEach((imageurl) => {
urls.push({
imageurl,
thumbnail: imageurl,
type: "attachment",
});
});
});
});
const index = urls.findIndex((item) => item.imageurl == current);
cloaseImageShow(urls, index, "facilities");
};
let carouselsconfig = ref({ lives: {}, videos: {}, attachment: {} });
// 处理 轮播图大图的索引 tab
@@ -879,34 +978,51 @@ const roomEle = ref(null);
const specialEle = ref(null);
const addressEle = ref(null);
const messageEle = ref(null);
const costEle = ref(null);
const facilitiesEle = ref(null);
const lifeEle = ref(null);
const companyEle = ref(null);
const eleseEle = ref(null);
const inspectEle = ref(null); // 实地考察 节点
const followEle = ref(null); // 寄托回访 节点
const viewMapRef = ref(null); // 地图组件
let navconfig = [
{
name: "房间类型",
value: "roomEle",
},
{
name: "优惠活动",
value: "specialEle",
},
{
name: "位置与交通",
value: "addressEle",
name: "实地考察",
value: "inspectEle",
},
{
name: "房源详情",
value: "messageEle",
name: "寄托回访",
value: "followEle",
},
{
name: "房间类型",
value: "roomEle",
},
{
name: "费用说明",
value: "costEle",
},
{
name: "交通",
value: "addressEle",
},
{
name: "公寓设施",
value: "facilitiesEle",
},
{
name: "生活",
name: "房源详情",
value: "messageEle",
},
{
name: "生活配套",
value: "lifeEle",
},
{
@@ -916,7 +1032,7 @@ let navconfig = [
];
let navList = ref([]);
let navTab = ref("roomEle");
let navTab = ref("specialEle");
// 处理 navList 数据
const handleNavData = () => {
@@ -1041,8 +1157,6 @@ onUnmounted(() => {
window.removeEventListener("scroll", handleScroll);
});
const gobrand = (item) => router.push(`/apartmentDetail?uniqid=${item.uniqid}`);
const handleScroll = () => {
if (Math.random() > 0.3) return;
for (let i = 0; i < navList.value.length; i++) {
@@ -1175,6 +1289,86 @@ const getClass = (index) => {
[`item${index}`]: true,
};
};
let likeList = ref([]); // 猜你喜欢 数据
// 获取猜你喜欢 数据
const getLikeList = () => {
proxy.$get("https://api.gter.net/v1/apartment/lists", { token, guess: 1 }).then((res) => {
if (res.code != 200) return;
let data = res.data;
likeList.value = data.data;
});
};
let isSelectSchool = ref(false); // 选择学校状态
let distancePitch = ref({});
let distanceList = ref([]);
const getMapDistance = () => {
proxy.$get("https://api.gter.net/v1/apartment/mapDistance", { token }).then((res) => {
if (res.code != 200) return;
const data = res.data || [];
const sid = pitchScreenSchool;
const distance = data.distance || [];
distance.sort((a, b) => a.distance - b.distance);
let arr = [];
let pitch = null;
distance.forEach((element) => {
let obj = {};
element.distance = calculateDistance(element.distance);
if (element.walking) obj["walking"] = calculateDuration(element.walking.duration);
if (element.transit) obj["transit"] = calculateDuration(element.transit.duration);
if (element.driving) obj["driving"] = calculateDuration(element.driving.duration);
const target = {
name: element.name,
distance: element.distance,
alias: element.alias,
sid: element.sid,
obj,
};
arr.push(target);
if (element.sid == sid) pitch = target;
});
if (pitch == null) pitch = arr[0];
distanceList.value = arr;
distancePitch.value = pitch;
});
};
const selectSchoolRef = ref(null);
const openSelectSchool = () => {
isSelectSchool.value = true;
nextTick(() => {
const item = selectSchoolRef.value.querySelector(`.item${distancePitch.value.sid}`);
if (!item) return;
selectSchoolRef.value.scrollTo({ top: item.offsetTop - 20, behavior: "smooth" });
});
};
const selectSchool = (sid) => {
let list = distanceList.value;
distancePitch.value = list.find((item) => item.sid == sid);
isSelectSchool.value = false;
// 修改本地存储
let pitchValue = localStorage.getItem("apartmentPitchValue");
if (pitchValue) {
pitchValue = JSON.parse(pitchValue);
pitchValue.school = sid;
localStorage.setItem("apartmentPitchValue", JSON.stringify(pitchValue));
}
};
const openMap = () => {
viewMapRef.value.showPop();
isSelectSchool.value = false;
};
</script>
<style lang="less" scoped>
@@ -1547,4 +1741,159 @@ const getClass = (index) => {
.el-popper.is-light {
border-radius: 10px !important;
}
.selectSchoolPop {
border-radius: 16px;
overflow: hidden;
.el-dialog__header {
display: none;
}
.el-dialog__body {
padding: 0;
.title-box {
font-family: "PingFangSC-Semibold", "PingFang SC Semibold", "PingFang SC", sans-serif;
font-weight: 650;
font-style: normal;
font-size: 25px;
line-height: 26px;
color: #000000;
text-align: center;
padding-top: 25px;
padding-bottom: 16px;
.close-icon {
width: 20px;
height: 20px;
position: absolute;
top: 10px;
right: 10px;
cursor: pointer;
}
}
.site {
height: 35px;
background-color: rgba(242, 242, 242, 1);
margin: 0 20px 24px;
border-radius: 210px;
padding-left: 10px;
.site-img {
width: 16px;
height: 16px;
margin-right: 6px;
}
.site-text {
font-size: 14px;
color: #000000;
}
.btn {
font-size: 13px;
color: #555555;
padding: 0 10px;
height: 100%;
cursor: pointer;
.btn-img {
width: 6px;
height: 11px;
margin-left: 8px;
}
}
}
.select-list {
max-height: 500px;
overflow: auto;
position: relative;
.select-item {
padding: 10px 15px;
border-top: 1px dotted #d7d7d7;
cursor: pointer;
&.pitch {
background-color: rgba(245, 244, 249, 1);
.select-icon {
background-color: #fddf6d;
}
}
&:last-of-type {
border-radius: 0 0 16px 16px;
}
.select-icon {
width: 30px;
height: 30px;
border-radius: 50%;
background-color: #f2f2f2;
align-self: flex-start;
margin-right: 10px;
.select-img {
width: 20px;
height: 20px;
}
}
.select-info {
.select-name {
font-family: "PingFangSC-Semibold", "PingFang SC Semibold", "PingFang SC", sans-serif;
font-weight: 650;
font-style: normal;
font-size: 16px;
color: #000000;
}
.distance {
font-size: 14px;
color: #555555;
margin-top: 7px;
.abbreviation {
font-family: "PingFangSC-Semibold", "PingFang SC Semibold", "PingFang SC", sans-serif;
font-weight: 650;
font-style: normal;
color: #000000;
margin: 0 4px;
}
}
.vehicle {
flex-wrap: wrap;
.item {
height: 20px;
line-height: 20px;
background-color: rgba(255, 255, 255, 0);
border: 1px solid rgba(127, 127, 127, 1);
border-radius: 30px;
font-size: 14px;
color: #333333;
width: fit-content;
padding: 0 5px;
margin-top: 5px;
margin-right: 10px;
.vehicle-icon {
width: 16px;
height: 16px;
margin-right: 5px;
}
}
}
}
.tick {
width: 20px;
height: 20px;
align-self: center;
}
}
}
}
}
</style>

View File

@@ -36,36 +36,36 @@
</template>
<script setup>
import pageTopBar from "../../components/pageTopBar/pageTopBar.vue"
import seachModule from "@/components/apartment/seachModule.vue"
import apartmentItem from "@/components/public/apartment-item.vue"
import haveQuestions from "@/components/public/have-questions.vue"
import pageFooter from "@/components/footer/footer.vue"
import emptyDuck from "@/components/public/empty-duck.vue"
import circleBtn from "@/components/public/circle-btn.vue"
import { ref, onMounted, onUnmounted, watch, getCurrentInstance, nextTick, reactive } from "vue"
import { ElLoading } from "element-plus"
import Masonry from "masonry-layout"
import { useRoute, useRouter } from "vue-router"
import backToTop from "@/components/public/backToTop.vue"
import store from "../../store/index"
import api from "../../utils/api"
import { ElMessage } from "element-plus"
import pageTopBar from "../../components/pageTopBar/pageTopBar.vue";
import seachModule from "@/components/apartment/seachModule.vue";
import apartmentItem from "@/components/public/apartment-item.vue";
import haveQuestions from "@/components/public/have-questions.vue";
import pageFooter from "@/components/footer/footer.vue";
import emptyDuck from "@/components/public/empty-duck.vue";
import circleBtn from "@/components/public/circle-btn.vue";
import { ref, onMounted, onUnmounted, watch, getCurrentInstance, nextTick, reactive } from "vue";
import { ElLoading } from "element-plus";
import Masonry from "masonry-layout";
import { useRoute, useRouter } from "vue-router";
import backToTop from "@/components/public/backToTop.vue";
import store from "../../store/index";
import api from "../../utils/api";
import { ElMessage } from "element-plus";
const { proxy } = getCurrentInstance()
const { proxy } = getCurrentInstance();
const route = useRoute()
const route = useRoute();
const props = defineProps({
item: Object,
})
});
const gridContainer = ref(null)
const gridContainer = ref(null);
let list = ref([])
let listCount = ref(0) // 列表数量
let list = ref([]);
let listCount = ref(0); // 列表数量
let masonryInstance = null
let masonryInstance = null;
// let masonryInstanceMore = null
onMounted(() => {
@@ -73,59 +73,65 @@ onMounted(() => {
masonryInstance = new Masonry(gridContainer.value, {
itemSelector: ".item",
gutter: 22,
})
});
// masonryInstanceMore = new Masonry(moreShowList.value, {
// itemSelector: ".item",
// gutter: 20,
// })
pitchValue.value = store.state.apartmentPitchValue
if (route.query["companyid"]) pitchValue.value["companyid"] = route.query["companyid"]
// pitchValue.value = store.getters.getApartmentPitchValue
const value = localStorage.getItem("apartmentPitchValue");
if (value) pitchValue.value = JSON.parse(value);
if (route.query["companyid"]) pitchValue.value["companyid"] = route.query["companyid"];
// banner() // 获取轮播图数据
getData() // 获取列表数据
getData(); // 获取列表数据
window.addEventListener("scroll", handleScroll)
})
window.addEventListener("scroll", handleScroll);
});
onUnmounted(() => {
window.removeEventListener("scroll", handleScroll)
window.removeEventListener("scroll", getMoreScroll, true)
})
window.removeEventListener("scroll", handleScroll);
window.removeEventListener("scroll", getMoreScroll, true);
});
let loading = null // 加载
let requestLoading = false // 接口加载中
let page = 1
let loading = null; // 加载
let requestLoading = false; // 接口加载中
let page = 1;
const getData = () => {
if (page == 0 || requestLoading) return
if (page == 0 || requestLoading) return;
loading = ElLoading.service({
lock: true,
text: "Loading",
background: "rgba(0, 0, 0, 0.7)",
})
});
requestLoading = true
requestLoading = true;
localStorage.setItem("apartmentPitchValue", JSON.stringify(pitchValue.value));
proxy
.$post("/tenement/pc/api/apartment", {
.$post("https://api.gter.net/v1/apartment/lists", {
limit: 10,
page,
...pitchValue.value,
})
.then(res => {
if (res.code != 200) return
let data = res.data
list.value = list.value.concat(data.data || [])
page = data.page * data.limit >= data.count ? 0 : page + 1
.then((res) => {
if (res.code != 200) return;
let data = res.data;
list.value = list.value.concat(data.data || []);
page = data.page * data.limit >= data.count ? 0 : page + 1;
listCount = data.count
listCount = data.count;
nextTick(() => {
masonryInstance.reloadItems()
masonryInstance.layout()
loading.close()
requestLoading = false
})
masonryInstance.reloadItems();
masonryInstance.layout();
loading.close();
requestLoading = false;
});
// 小于20 代表筛选 数据不够了,需要加载更多的 随便在这里初始化 更多的 瀑布流 HTML
// if (data.data.length < 20 && (pitchValue.value["school"] || pitchValue.value["location"].length != 0)) {
@@ -133,18 +139,18 @@ const getData = () => {
// getMoreList()
// }
})
.catch(err => {
loading.close()
requestLoading = false
})
}
.catch((err) => {
loading.close();
requestLoading = false;
});
};
const handleScroll = () => {
const scrollHeight = document.documentElement.scrollHeight
const clientHeight = document.documentElement.clientHeight
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop
if (scrollTop + clientHeight >= scrollHeight - 350) getData()
}
const scrollHeight = document.documentElement.scrollHeight;
const clientHeight = document.documentElement.clientHeight;
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
if (scrollTop + clientHeight >= scrollHeight - 350) getData();
};
let pitchValue = ref({
companyid: 0,
@@ -155,84 +161,85 @@ let pitchValue = ref({
school: 0,
roomlistings: 0,
keyword: "",
})
tag: "",
});
// 筛选组件的参数的中转
const handleTransfer = data => {
const handleTransfer = (data) => {
for (const key in data.value) {
if (data.value[key] != pitchValue.value[key]) {
pitchValue.value = { ...data.value }
store.state.apartmentPitchValue = pitchValue.value
page = 1
list.value = []
getData()
break
pitchValue.value = { ...data.value };
store.state.apartmentPitchValue = pitchValue.value;
page = 1;
list.value = [];
getData();
break;
}
}
}
};
let moreShowList = ref(null)
let moreState = ref(false) // 更多列表 显示状态
let morePage = ref(1)
let moreList = ref([])
let moreLoading = ref(false)
let moreShowList = ref(null);
let moreState = ref(false); // 更多列表 显示状态
let morePage = ref(1);
let moreList = ref([]);
let moreLoading = ref(false);
// 加载更多数据列表
const getMoreList = () => {
if (morePage.value == 0 || moreLoading.value) return
moreLoading.value = true
if (morePage.value == 0 || moreLoading.value) return;
moreLoading.value = true;
let postData = {
page: morePage.value,
...pitchValue.value,
}
};
api.getMoreLists(postData).then(res => {
if (res.code != 200) return
const data = res.data
if (!data.data) return
api.getMoreLists(postData).then((res) => {
if (res.code != 200) return;
const data = res.data;
if (!data.data) return;
moreList.value = moreList.value.concat(data.data)
moreState.value = true
if (data.data && data.data.length < data.limit) morePage.value = 0
moreList.value = moreList.value.concat(data.data);
moreState.value = true;
if (data.data && data.data.length < data.limit) morePage.value = 0;
else {
morePage.value++
window.addEventListener("scroll", getMoreScroll, true)
morePage.value++;
window.addEventListener("scroll", getMoreScroll, true);
}
nextTick(() => {
masonryInstanceMore.reloadItems()
masonryInstanceMore.layout()
moreLoading.value = false
})
})
}
masonryInstanceMore.reloadItems();
masonryInstanceMore.layout();
moreLoading.value = false;
});
});
};
const getMoreScroll = () => {
let body = document.documentElement ? document.documentElement : document.body ? document.body : document.querySelector(".element")
let scrollTop = body.scrollTop
let clientHeight = body.clientHeight
let offsetHeight = body.offsetHeight
if (scrollTop + clientHeight >= offsetHeight - 200 && !moreLoading.value) getMoreList()
}
let body = document.documentElement ? document.documentElement : document.body ? document.body : document.querySelector(".element");
let scrollTop = body.scrollTop;
let clientHeight = body.clientHeight;
let offsetHeight = body.offsetHeight;
if (scrollTop + clientHeight >= offsetHeight - 200 && !moreLoading.value) getMoreList();
};
// 处理列表的点击收藏
const handlecollect = uniqid => {
let targetIndex = 0
let token = ""
const handlecollect = (uniqid) => {
let targetIndex = 0;
let token = "";
list.value.forEach((element, index) => {
if (element.uniqid == uniqid) {
targetIndex = index
token = element.token || ""
targetIndex = index;
token = element.token || "";
}
})
});
api.apartmentCollection({ token }).then(res => {
if (res.code != 200) return
const data = res.data
list.value[targetIndex]["iscollect"] = data.status
ElMessage.success(res.message)
})
}
api.apartmentCollection({ token }).then((res) => {
if (res.code != 200) return;
const data = res.data;
list.value[targetIndex]["iscollect"] = data.status;
ElMessage.success(res.message);
});
};
</script>
<style lang="less" scoped>
.screen-box {

View File

@@ -38,15 +38,15 @@
<template v-if="user['uid'] && user.identity != 0">
<img class="" src="@/assets/img/publicImage/cut-off-rule.svg" />
<div class="operate-item flexcenter identity">
<div class="operate-item-shell flexcenter" :class="{ 'intermediary': user.identity == 1, 'personage': user.identity == -1 }">
<div class="operate-item-shell flexcenter" :class="{ intermediary: user.identity == 1, personage: user.identity == -1 }">
<img class="operate-icon" src="@/assets/img/publicImage/intermediary-icon.png" />
</div>
<div class="operate-text">{{ identityObj[user.identity || -1] }}</div>
<div v-if="user.identity == 1" class="btn-qrcode">
<authentication-info :type="2" style="height: 217px;"></authentication-info>
<authentication-info :type="2" style="height: 217px"></authentication-info>
</div>
<div v-else class="btn-qrcode">
<authentication-info :type="1" style="height: 217px;"></authentication-info>
<authentication-info :type="1" style="height: 217px"></authentication-info>
</div>
</div>
</template>
@@ -54,8 +54,8 @@
</div>
<div class="tab-box flexacenter">
<div class="tab-item flexcenter" :class="{ 'pitch': tabState == 'fav' }" @click="cutTab('fav')">我的收藏</div>
<div class="tab-item flexcenter" :class="{ 'pitch': tabState == 'publish' }" @click="cutTab('publish')">我的发布</div>
<div class="tab-item flexcenter" :class="{ pitch: tabState == 'fav' }" @click="cutTab('fav')">我的收藏</div>
<div class="tab-item flexcenter" :class="{ pitch: tabState == 'publish' }" @click="cutTab('publish')">我的发布</div>
</div>
</div>
@@ -73,6 +73,10 @@
<div class="list wid1200 flexflex" v-show="tabState == 'fav'" ref="gridContainer">
<div class="item" v-for="(item, index) in favData['list']" :key="item.id" @click="goApartmentDetail(item)">
<div v-if="item.collectiontype == 'apartment'" class="apartment-item">
<div class="apartment-top flexacenter">
<img class="icon" src="@/assets/img/publicImage/apartment-icon.png" />
<img class="title" src="@/assets/img/publicImage/apartment-title.png" />
</div>
<div class="apartment-header flexflex">
<img class="apartment-header-img" :src="item.image || item.imageurl" />
<div class="apartment-header-content flexflex">
@@ -125,9 +129,7 @@
<empty-duck></empty-duck>
</div>
<div class="bottom-tps" v-if="(tabState == 'fav' && favData['list'].length != 0 && favData['page'] == 0) || (tabState == 'publish' && publishData['list'].length != 0 && publishData['page'] == 0)">
- 到底了 -
</div>
<div class="bottom-tps" v-if="(tabState == 'fav' && favData['list'].length != 0 && favData['page'] == 0) || (tabState == 'publish' && publishData['list'].length != 0 && publishData['page'] == 0)">- 到底了 -</div>
<!-- 有疑问 -->
<have-questions></have-questions>
@@ -141,271 +143,272 @@
</template>
<script setup>
import pageTopBar from "../components/pageTopBar/pageTopBar.vue"
import systematicNotificationPop from "@/components/user/systematic-notification-pop.vue"
import haveQuestions from "@/components/public/have-questions.vue"
import pageFooter from "@/components/footer/footer.vue"
import publicListItem from "@/components/public/public-list-item.vue"
import emptyDuck from "@/components/public/empty-duck.vue"
import choosingIdentity from "@/components/edit/choosingIdentity.vue"
import backToTop from "@/components/public/backToTop.vue"
import authenticationInfo from "@/components/seachModule/authenticationInfo.vue"
import pageTopBar from "../components/pageTopBar/pageTopBar.vue";
import systematicNotificationPop from "@/components/user/systematic-notification-pop.vue";
import haveQuestions from "@/components/public/have-questions.vue";
import pageFooter from "@/components/footer/footer.vue";
import publicListItem from "@/components/public/public-list-item.vue";
import emptyDuck from "@/components/public/empty-duck.vue";
import choosingIdentity from "@/components/edit/choosingIdentity.vue";
import backToTop from "@/components/public/backToTop.vue";
import authenticationInfo from "@/components/seachModule/authenticationInfo.vue";
import { ref, reactive, onMounted, onUnmounted, getCurrentInstance, nextTick, pushScopeId } from "vue"
import { useRoute, useRouter } from "vue-router"
import { ref, reactive, onMounted, onUnmounted, getCurrentInstance, nextTick, pushScopeId } from "vue";
import { useRoute, useRouter } from "vue-router";
import { ElLoading, ElMessage } from "element-plus"
import Masonry from "masonry-layout"
import store from "@/store/index"
import { ElLoading, ElMessage } from "element-plus";
import Masonry from "masonry-layout";
import store from "@/store/index";
const { proxy } = getCurrentInstance()
const { proxy } = getCurrentInstance();
const route = useRoute()
let router = useRouter()
const route = useRoute();
let router = useRouter();
const gridContainer = ref(null)
const gridContainerpublish = ref(null)
let masonryInstance = null
let masonryInstancepublish = null
const gridContainer = ref(null);
const gridContainerpublish = ref(null);
let masonryInstance = null;
let masonryInstancepublish = null;
onMounted(() => {
masonryInstance = new Masonry(gridContainer.value, {
itemSelector: ".item",
gutter: 20,
})
});
masonryInstancepublish = new Masonry(gridContainerpublish.value, {
itemSelector: ".item",
gutter: 20,
})
});
init()
init();
window.addEventListener("scroll", handleScroll)
})
window.addEventListener("scroll", handleScroll);
});
let systematicState = ref(false) // 系统通知
let user = ref({})
let count = ref({}) // 发布和收藏的数量
let newmessagenum = ref(0)
let validityidentity = ref("")
let tabState = ref("publish") // fav publish
let systematicState = ref(false); // 系统通知
let user = ref({});
let count = ref({}); // 发布和收藏的数量
let newmessagenum = ref(0);
let validityidentity = ref("");
let tabState = ref("publish"); // fav publish
const identityObj = {
1: "中介认证",
"-1": "房源认证",
}
};
// 打开消息提醒 并且 删除未读消息数
const handleOpenMessage = () => {
systematicState.value = true
newmessagenum.value = 0
}
systematicState.value = true;
newmessagenum.value = 0;
};
const init = () => {
proxy
.$post("/tenement/pc/api/user")
.then(res => {
if (res.code != 200) return
let data = res.data
.then((res) => {
if (res.code != 200) return;
let data = res.data;
if (!route.query["tab"]) {
if (data.count["publish"] > 0) tabState.value = "publish"
else if (data.count["fav"] == 0) tabState.value = "publish"
else tabState.value = "fav"
} else tabState.value = route.query["tab"]
if (data.count["publish"] > 0) tabState.value = "publish";
else if (data.count["fav"] == 0) tabState.value = "publish";
else tabState.value = "fav";
} else tabState.value = route.query["tab"];
if (tabState.value == "publish") getPublishData()
else getFavData()
// tabState.value = "fav"
if (data.user?.uin == 4171117) tabState.value = "fav";
if (tabState.value == "publish") getPublishData();
else getFavData();
// getFavData()
user.value = data.user
count.value = data.count
newmessagenum.value = data.newmessagenum
store.state.user["messagenum"] = data.newmessagenum
user.value = data.user;
count.value = data.count;
newmessagenum.value = data.newmessagenum;
store.state.user["messagenum"] = data.newmessagenum;
validityidentity.value = data.validityidentity
validityidentity.value = data.validityidentity;
})
.finally(() => {})
}
.finally(() => {});
};
let loading = ElLoading.service({
lock: true,
text: "Loading",
background: "rgba(0, 0, 0, 0.7)",
visible: false,
})
});
let publishData = ref({
page: 1,
list: [],
})
});
let stat = ref({}) // 我的发布的详细数量
let stat = ref({}); // 我的发布的详细数量
// 获取发布数据
const getPublishData = () => {
if (publishData.value["page"] == 0 || loading["visible"].value) return
if (publishData.value["page"] == 0 || loading["visible"].value) return;
loading = ElLoading.service({
lock: true,
text: "Loading",
background: "rgba(0, 0, 0, 0.7)",
})
});
proxy
.$post("/tenement/pc/api/user/publishList", {
page: publishData.value["page"],
})
.then(res => {
if (res.code != 200) return
let data = res.data
.then((res) => {
if (res.code != 200) return;
let data = res.data;
// data.data[0]['verifiedstatus'] = 1
stat.value = data["stat"]
publishData.value["page"] = data["page"] * data["limit"] >= data["count"] ? 0 : data["page"] + 1
publishData.value["list"] = publishData.value["list"].concat(data["data"] || [])
stat.value = data["stat"];
publishData.value["page"] = data["page"] * data["limit"] >= data["count"] ? 0 : data["page"] + 1;
publishData.value["list"] = publishData.value["list"].concat(data["data"] || []);
nextTick(() => {
masonryInstancepublish.reloadItems()
masonryInstancepublish.layout()
loading.close()
})
masonryInstancepublish.reloadItems();
masonryInstancepublish.layout();
loading.close();
});
})
.finally(() => {
loading.close()
})
}
loading.close();
});
};
let favData = ref({
page: 1,
list: [],
})
});
// 获取收藏数据
const getFavData = () => {
if (favData.value["page"] == 0 || loading["visible"].value) return
if (favData.value["page"] == 0 || loading["visible"].value) return;
loading = ElLoading.service({
lock: true,
text: "Loading",
background: "rgba(0, 0, 0, 0.7)",
})
});
proxy
// .$post("/tenement/pc/api/user/favList", {
.$post("/tenement/pc/api/user/collectionList", {
page: favData.value["page"],
})
.then(res => {
if (res.code != 200) return
let data = res.data
favData.value["page"] = data["page"] * data["limit"] >= data["count"] ? 0 : data["page"] + 1
favData.value["list"] = favData.value["list"].concat(data["data"] || [])
favData.value["limit"] = data["limit"]
.then((res) => {
if (res.code != 200) return;
let data = res.data;
favData.value["page"] = data["page"] * data["limit"] >= data["count"] ? 0 : data["page"] + 1;
favData.value["list"] = favData.value["list"].concat(data["data"] || []);
favData.value["limit"] = data["limit"];
nextTick(() => {
masonryInstance.reloadItems()
masonryInstance.layout()
loading.close()
})
masonryInstance.reloadItems();
masonryInstance.layout();
loading.close();
});
})
.finally(() => {
loading.close()
})
}
loading.close();
});
};
// 切换 tab
const cutTab = value => {
tabState.value = value
if (tabState.value == "publish" && publishData.value["list"].length == 0) getPublishData()
else if (tabState.value == "fav" && favData.value["list"].length == 0) getFavData()
}
const cutTab = (value) => {
tabState.value = value;
if (tabState.value == "publish" && publishData.value["list"].length == 0) getPublishData();
else if (tabState.value == "fav" && favData.value["list"].length == 0) getFavData();
};
// 取消收藏
let cancelCollection = data => {
let url = "/tenement/pc/api/user/operation"
if (data["collectiontype"] == "apartment") url = "/tenement/pc/api/user/apartmentCollection"
let cancelCollection = (data) => {
let url = "/tenement/pc/api/user/operation";
if (data["collectiontype"] == "apartment") url = "/tenement/pc/api/user/apartmentCollection";
proxy
.$post(url, {
token: data["token"],
})
.then(res => {
if (res.code != 200) return
favData.value.list.splice(data["index"], 1)
count.value["fav"]--
.then((res) => {
if (res.code != 200) return;
favData.value.list.splice(data["index"], 1);
count.value["fav"]--;
if (favData.value.list.length == 0 && favData.value["page"] != 0) {
favData.value["page"] = 1
getFavData()
} else refillData(data["index"])
favData.value["page"] = 1;
getFavData();
} else refillData(data["index"]);
nextTick(() => {
masonryInstance.reloadItems()
masonryInstance.layout()
loading.close()
})
})
}
masonryInstance.reloadItems();
masonryInstance.layout();
loading.close();
});
});
};
// 取消输出后重新补充一条数据
const refillData = index => {
if (favData.value["page"] == 0 || loading["visible"].value) return
const page = favData.value["page"] - 1
const refillData = (index) => {
if (favData.value["page"] == 0 || loading["visible"].value) return;
const page = favData.value["page"] - 1;
proxy
.$post("/tenement/pc/api/user/favList", {
page,
limit: favData.value["limit"],
})
.then(res => {
if (res.code != 200) return
let data = res.data["data"] || []
favData.value["list"].push(data[data.length - 1])
.then((res) => {
if (res.code != 200) return;
let data = res.data["data"] || [];
favData.value["list"].push(data[data.length - 1]);
nextTick(() => {
masonryInstance.reloadItems()
masonryInstance.layout()
})
})
}
masonryInstance.reloadItems();
masonryInstance.layout();
});
});
};
// 点击下架都修改状态
const undercarriage = (index, status) => {
stat.value["listing"]--
stat.value["offshelf"]++
publishData.value["list"][index].status = status
stat.value["listing"]--;
stat.value["offshelf"]++;
publishData.value["list"][index].status = status;
nextTick(() => {
masonryInstance.reloadItems()
masonryInstance.layout()
})
}
masonryInstance.reloadItems();
masonryInstance.layout();
});
};
// 点击顶上去后改 数据 状态
const goUp = index => (publishData.value["list"][index].isding = 1)
const goUp = (index) => (publishData.value["list"][index].isding = 1);
// 点击删除
const handleDelete = (index, status) => {
if (status == 0) stat.value["draft"]--
else if (status == 1) stat.value["listing"]--
else stat.value["offshelf"]--
count.value["publish"]--
publishData.value["list"].splice(index, 1)
if (status == 0) stat.value["draft"]--;
else if (status == 1) stat.value["listing"]--;
else stat.value["offshelf"]--;
count.value["publish"]--;
publishData.value["list"].splice(index, 1);
nextTick(() => {
masonryInstancepublish.reloadItems()
masonryInstancepublish.layout()
loading.close()
})
}
masonryInstancepublish.reloadItems();
masonryInstancepublish.layout();
loading.close();
});
};
// 监听滚动到底部
const handleScroll = () => {
if (!user.value["uid"]) return
const scrollHeight = document.documentElement.scrollHeight
const clientHeight = document.documentElement.clientHeight
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop
if (!user.value["uid"]) return;
const scrollHeight = document.documentElement.scrollHeight;
const clientHeight = document.documentElement.clientHeight;
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
if (scrollTop + clientHeight >= scrollHeight - 350) {
if (tabState.value == "publish") getPublishData()
else getFavData()
if (tabState.value == "publish") getPublishData();
else getFavData();
}
}
};
// 跳转公寓详情页
const goApartmentDetail = item => router.push(`/apartmentDetail?uniqid=${item.uniqid}`)
const goApartmentDetail = (item) => router.push(`/apartmentDetail?uniqid=${item.uniqid}`);
onUnmounted(() => {
window.removeEventListener("scroll", handleScroll)
})
window.removeEventListener("scroll", handleScroll);
});
</script>
<style lang="less" scoped>
@@ -682,7 +685,7 @@ onUnmounted(() => {
.apartment-item {
width: 590px;
height: 360px;
padding: 20px;
padding: 0 0 20px;
background-color: rgba(255, 255, 255, 1);
border-radius: 16px;
-moz-box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.0784313725490196);
@@ -690,6 +693,25 @@ onUnmounted(() => {
box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.0784313725490196);
margin-bottom: 20px;
.apartment-top {
// width: 590px;
height: 32px;
background: linear-gradient(93.1045259092644deg, rgba(253, 218, 85, 1) 0%, rgba(229, 215, 190, 1) 50%, rgba(203, 254, 191, 1) 100%);
border-radius: 15px 15px 0 0;
padding-left: 11px;
margin-bottom: 23px;
.icon {
width: 18px;
height: 18px;
margin-right: 6px;
}
.title {
width: 77px;
height: 18px;
}
}
.apartment-header {
margin-bottom: 20px;
.apartment-header-img {