gterFang/src/views/user.vue
2024-04-17 18:40:28 +08:00

833 lines
28 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<pageTopBar></pageTopBar>
<div class="user-box">
<div class="info-box flexacenter">
<div class="info-left flexacenter flex1">
<a href="https://bbs.gter.net/account.php?a=avatar" target="_blank">
<img class="info-user-icon" :src="user['avatar'] || store.state.user['avatar'] || require('@/assets/img/publicImage/defaultAvatar.png')" />
</a>
<div class="info-user-box">
<div class="info-user-top flexacenter">
<a href="https://bbs.gter.net/account.php?a=info" target="_blank" class="info-user-username">{{ user["nickname"] }}</a>
<a href="https://bbs.gter.net/account.php?a=info" target="_blank">
<img class="info-user-edit" src="@/assets/img/publicImage/edit-pen.png" />
</a>
<img class="info-user-certifying" v-if="user.intermediary == 1" src="@/assets/img/publicImage/certifying-agent.png" />
<div class="info-user-validity" v-if="validityidentity">(有效期至{{ validityidentity }}</div>
</div>
<div class="info-user-bottom flexacenter">UID{{ user["uid"] }}</div>
</div>
</div>
<div class="info-right flexacenter">
<div class="operate-item message flexcenter" @click="handleOpenMessage">
<div class="operate-item-shell flexcenter">
<div class="newmessagenum flexcenter" v-if="newmessagenum > 0">{{ newmessagenum }}</div>
<img class="operate-icon" src="@/assets/img/publicImage/message-icon.svg" />
</div>
<div class="operate-text">消息提醒</div>
</div>
<img class="" src="@/assets/img/publicImage/cut-off-rule.svg" />
<div class="operate-item flexcenter add">
<div class="operate-item-shell flexcenter">
<img class="operate-icon" src="@/assets/img/publicImage/add-icon.svg" />
</div>
<div class="operate-text">发布房源</div>
<choosing-identity></choosing-identity>
</div>
<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 }">
<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>
</div>
<div v-else class="btn-qrcode">
<authentication-info :type="1" style="height: 217px;"></authentication-info>
</div>
</div>
</template>
</div>
</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>
</div>
<div class="quantity wid1200" v-if="tabState == 'fav'">
共收藏 <b>{{ count["fav"] }}</b> 个房源
</div>
<div class="quantity wid1200 flexacenter" v-else>
共 <b>{{ count["publish"] }}</b> 条房源(上架 {{ stat["listing"] }}
<div class="longString">|</div>
草稿 {{ stat["draft"] }}
<div class="longString">|</div>
下架 {{ stat["offshelf"] }}
</div>
<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-header flexflex">
<img class="apartment-header-img" :src="item.image || item.imageurl" />
<div class="apartment-header-content flexflex flex1">
<div class="apartment-header-title">{{ item.title }}</div>
<div class="apartment-header-tips">{{ item.propaganda }}</div>
<div class="apartment-header-types flex">
<div v-for="(it, i) in item.othertags" :key="i" class="apartment-header-types-item flexcenter">{{ it }}</div>
</div>
</div>
</div>
<div class="apartment-type-box flexflex flex1">
<div class="apartment-type-list flexflex flex1">
<div class="apartment-type-list-item flexacenter" v-for="(it, index) in item.roomlist" :key="index">
<div class="apartment-type-list-item-name flex1">{{ it.name }}</div>
<div class="apartment-type-list-item-price flexacenter">
<div class="unit">HK$</div>
<div class="value">{{ it.price }}</div>
/
</div>
</div>
</div>
<div class="apartment-type-total flexcenter">
<div class="apartment-type-total-value flexcenter">{{ item.roomnum }}</div>
个房型
<img class="apartment-type-arrow" src="@/assets/img/publicImage/black-arrow.svg" />
</div>
</div>
<div class="apartment-bottom flexacenter">
<div class="apartment-bottom-location flexacenter flex1">
<img class="apartment-bottom-icon" src="@/assets/img/publicImage/location-icon.png" />
{{ item.address }}
</div>
<div class="apartment-bottom-btn flexacenter" @click.stop="cancelCollection(item)">
<img class="apartment-bottom-btn-icon" src="@/assets/img/publicImage/black-circle-cross.svg" />
取消收藏
</div>
</div>
</div>
<public-list-item v-else :item="item" :index="index" @cancelCollection="cancelCollection"></public-list-item>
</div>
</div>
<div class="list wid1200 flexflex" v-show="tabState == 'publish'" ref="gridContainerpublish">
<div class="item" v-for="(item, index) in publishData['list']" :key="item.id">
<public-list-item :item="item" :index="index" @cancelCollection="cancelCollection" :ispublish="true" @goUp="goUp" @undercarriage="undercarriage" @handleDelete="handleDelete"></public-list-item>
</div>
</div>
<div class="empty-box flexcenter wid1200" v-if="(tabState == 'fav' && favData['list'].length == 0) || (tabState == 'publish' && publishData['list'].length == 0)">
<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>
<!-- 有疑问 -->
<have-questions></have-questions>
<!-- 页底 -->
<page-footer></page-footer>
<!-- 系统通知弹窗 -->
<systematic-notification-pop v-if="systematicState" @close="systematicState = false"></systematic-notification-pop>
<back-to-top></back-to-top>
</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 { 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"
const { proxy } = getCurrentInstance()
const route = useRoute()
let router = useRouter()
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()
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
const identityObj = {
1: "中介认证",
"-1": "房源认证",
}
// 打开消息提醒 并且 删除未读消息数
const handleOpenMessage = () => {
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
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 (tabState.value == 'publish') getPublishData()
// else getFavData()
tabState.value = "fav"
getFavData()
user.value = data.user
count.value = data.count
newmessagenum.value = data.newmessagenum
store.state.user["messagenum"] = data.newmessagenum
validityidentity.value = data.validityidentity
})
.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({}) // 我的发布的详细数量
// 获取发布数据
const getPublishData = () => {
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
// 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"] || [])
nextTick(() => {
masonryInstancepublish.reloadItems()
masonryInstancepublish.layout()
loading.close()
})
})
.finally(() => {
loading.close()
})
}
let favData = ref({
page: 1,
list: [],
})
// 获取收藏数据
const getFavData = () => {
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"]
nextTick(() => {
masonryInstance.reloadItems()
masonryInstance.layout()
loading.close()
})
})
.finally(() => {
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()
}
// 取消收藏
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"]--
if (favData.value.list.length == 0 && favData.value["page"] != 0) {
favData.value["page"] = 1
getFavData()
} else refillData(data["index"])
nextTick(() => {
masonryInstance.reloadItems()
masonryInstance.layout()
loading.close()
})
})
}
// 取消输出后重新补充一条数据
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])
nextTick(() => {
masonryInstance.reloadItems()
masonryInstance.layout()
})
})
}
// 点击下架都修改状态
const undercarriage = (index, status) => {
stat.value["listing"]--
stat.value["offshelf"]++
publishData.value["list"][index].status = status
nextTick(() => {
masonryInstance.reloadItems()
masonryInstance.layout()
})
}
// 点击顶上去后改 数据 状态
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)
nextTick(() => {
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 (scrollTop + clientHeight >= scrollHeight - 350) {
if (tabState.value == "publish") getPublishData()
else getFavData()
}
}
// 跳转公寓详情页
const goApartmentDetail = item => router.push(`/apartmentDetail?uniqid=${item.uniqid}`)
onUnmounted(() => {
window.removeEventListener("scroll", handleScroll)
})
</script>
<style lang="less" scoped>
* {
font-family: "PingFangSC-Regular", "PingFang SC", sans-serif;
}
.user-box {
width: 1200px;
height: 238px;
margin: 30px auto 26px;
font-size: 14px;
background: linear-gradient(0deg, rgba(214, 236, 255, 1) -4%, rgba(232, 244, 255, 1) 34%, rgba(176, 216, 255, 1) 131%);
border-radius: 16px;
-moz-box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.0784313725490196);
-webkit-box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.0784313725490196);
box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.0784313725490196);
.info-box {
height: 160px;
border-bottom: 1px solid #ddeeff;
.info-left {
padding-left: 40px;
.info-user-icon {
width: 80px;
height: 80px;
border-radius: 50%;
}
.info-user-box {
margin-left: 20px;
.info-user-top {
height: 28px;
.info-user-username {
font-size: 16px;
color: #333;
}
.info-user-edit {
width: 16px;
height: 16px;
margin-left: 5px;
cursor: pointer;
margin-right: 29px;
}
.info-user-certifying {
width: 85px;
height: 20px;
}
.info-user-validity {
color: #aaa;
font-size: 13px;
}
}
.info-user-bottom {
height: 24px;
color: #7f7f7f;
}
}
}
.info-right {
.operate-item:hover .btn-qrcode {
display: block;
}
.operate-item {
flex-direction: column;
padding: 0 52px;
position: relative;
cursor: pointer;
.operate-item-shell {
width: 40px;
height: 40px;
border-radius: 20px;
margin-bottom: 7px;
}
&.message {
.operate-item-shell {
position: relative;
background-color: rgba(80, 227, 194, 1);
border-radius: 8px;
-moz-box-shadow: 0px 0px 5px rgba(80, 227, 194, 1);
-webkit-box-shadow: 0px 0px 5px rgba(80, 227, 194, 1);
box-shadow: 0px 0px 5px rgba(80, 227, 194, 1);
.operate-icon {
width: 32px;
height: 28px;
}
.newmessagenum {
position: absolute;
top: 0;
right: 0;
transform: translate(50%, -50%);
width: 16px;
height: 16px;
font-size: 13px;
background: #f95d5d;
color: #ffffff;
border-radius: 50%;
}
}
}
&.add {
.operate-item-shell {
background-color: rgba(253, 223, 109, 1);
-moz-box-shadow: 0px 0px 5px rgba(253, 223, 109, 1);
-webkit-box-shadow: 0px 0px 5px rgba(253, 223, 109, 1);
box-shadow: 0px 0px 5px rgba(253, 223, 109, 1);
.operate-icon {
width: 16px;
height: 16px;
}
}
}
&.identity {
.operate-item-shell {
&.intermediary {
background-color: rgba(46, 207, 226, 1);
-moz-box-shadow: 0px 0px 5px rgba(46, 207, 226, 1);
-webkit-box-shadow: 0px 0px 5px rgba(46, 207, 226, 1);
box-shadow: 0px 0px 5px rgba(46, 207, 226, 1);
}
&.personage {
background-color: rgba(171, 169, 255, 1);
-moz-box-shadow: 0px 0px 5px rgba(123, 121, 255, 1);
-webkit-box-shadow: 0px 0px 5px rgba(123, 121, 255, 1);
box-shadow: 0px 0px 5px rgba(123, 121, 255, 1);
}
}
.operate-icon {
width: 22px;
height: 22px;
}
}
.operate-text {
color: #555;
font-size: 14px;
height: 24px;
}
}
}
}
.tab-box {
height: 78px;
padding-left: 20px;
.tab-item {
width: 160px;
height: 48px;
background-color: rgba(205, 227, 247, 1);
border-radius: 8px;
font-size: 18px;
color: #555555;
margin-right: 10px;
cursor: pointer;
&:hover {
color: #000;
}
&.pitch {
background-color: rgba(98, 177, 255, 1);
border-radius: 8px;
font-family: "PingFangSC-Semibold", "PingFang SC Semibold", "PingFang SC", sans-serif;
font-weight: 650;
font-style: normal;
font-size: 18px;
color: #ffffff;
position: relative;
&::after {
content: "";
position: absolute;
bottom: -5px;
width: 14px;
height: 8px;
background-image: url("@/assets/img/publicImage/green-arrow-below.svg");
}
}
}
}
}
.quantity {
margin: 0 auto;
font-size: 14px;
color: #555;
margin-bottom: 24px;
b {
color: #000;
margin: 0 4px;
}
.longString {
font-weight: 400;
color: #d7d7d7;
margin: 0 4px;
}
}
.list {
margin: 0 auto;
flex-wrap: wrap;
justify-content: space-between;
.item {
cursor: pointer;
}
}
.empty-box {
background-color: #ffffff;
height: 500px;
margin: 0 auto;
justify-content: center;
align-items: center;
border-radius: 16px;
}
.bottom-tps {
font-weight: 400;
font-size: 14px;
color: #555555;
text-align: center;
padding: 30px 0;
}
.btn-qrcode {
position: absolute;
right: 155px;
top: 74px;
width: 360px;
z-index: 66;
display: none;
// margin: 0 auto;
.bj {
width: 100%;
box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.2784313725490196);
border-radius: 10px;
}
.QR-code {
position: absolute;
width: 110px;
height: 110px;
border-radius: 50%;
bottom: 88px;
left: 50%;
transform: translateX(-50%);
}
}
.apartment-item {
width: 590px;
height: 360px;
padding: 20px;
background-color: rgba(255, 255, 255, 1);
border-radius: 16px;
-moz-box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.0784313725490196);
-webkit-box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.0784313725490196);
box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.0784313725490196);
margin-bottom: 20px;
.apartment-header {
margin-bottom: 20px;
.apartment-header-img {
width: 209px;
height: 140px;
border-radius: 15px;
margin-right: 20px;
}
.apartment-header-content {
flex-direction: column;
.apartment-header-title {
font-weight: 650;
font-size: 20px;
color: #000000;
margin-bottom: 10px;
}
.apartment-header-tips {
color: #aaaaaa;
font-size: 14px;
margin-bottom: 20px;
}
.apartment-header-types {
height: 76px;
overflow: hidden;
.apartment-header-types-item {
display: inline-flex;
height: 28px;
padding: 0 12px;
background-color: rgba(224, 240, 255, 1);
border-radius: 5px;
color: #447eb3;
font-size: 13px;
margin-right: 10px;
margin-bottom: 10px;
}
}
}
}
.apartment-type-box {
width: 550px;
background-color: rgba(246, 246, 246, 1);
border-radius: 12px;
.apartment-type-list {
flex-direction: column;
border-right: 1px solid #ebebeb;
.apartment-type-list-item {
padding: 0 10px;
height: 54px;
&:not(:last-of-type) {
border-bottom: 1px solid #ebebeb;
}
.apartment-type-list-item-name {
font-size: 15px;
color: #000000;
}
.apartment-type-list-item-price {
.unit {
font-family: "Arial-Black", "Arial Black", sans-serif;
font-weight: 900;
color: #000000;
font-size: 14px;
margin-right: 5px;
}
.value {
font-family: "Arial-Black", "Arial Black", sans-serif;
font-weight: 900;
font-size: 20px;
color: #f95d5d;
margin-right: 5px;
}
font-size: 14px;
color: #555555;
}
}
}
.apartment-type-total {
font-size: 14px;
color: #555555;
width: 191px;
.apartment-type-total-value {
font-weight: 650;
font-size: 14px;
color: #000000;
line-height: 18px;
background-color: #fddf6d;
border-radius: 9px;
padding: 0 8px;
margin: 0 8px;
}
.apartment-type-arrow {
width: 7px;
height: 12px;
margin-left: 8px;
}
}
}
.apartment-bottom {
height: 58px;
.apartment-bottom-location {
font-size: 15px;
color: #555555;
.apartment-bottom-icon {
width: 18px;
height: 18px;
margin-right: 5px;
}
}
.apartment-bottom-btn {
color: #333333;
font-size: 14px;
height: 58px;
&:hover .apartment-bottom-btn-icon {
transform: rotate(360deg);
}
.apartment-bottom-btn-icon {
width: 16px;
height: 16px;
margin-right: 3px;
transition: all 1s;
}
}
}
}
</style>