837 lines
20 KiB
Vue
837 lines
20 KiB
Vue
<template>
|
||
<view class="panel-container">
|
||
<view
|
||
v-if="bannerList.length"
|
||
id="content-start"
|
||
class="compact-banner-wrap"
|
||
:style="{ top: 0 }"
|
||
>
|
||
<!-- 收缩轮播图 -->
|
||
<view class="compact-swiper-container">
|
||
<swiper
|
||
class="compact-swiper"
|
||
circular
|
||
:current="currentBannerIndex"
|
||
@change="handleBannerChange"
|
||
>
|
||
<swiper-item v-for="(item, index) in bannerList" :key="index">
|
||
<image :src="item.image" mode="aspectFill" class="compact-swiper__image"></image>
|
||
<view class="compact-swiper__mask"></view>
|
||
</swiper-item>
|
||
</swiper>
|
||
</view>
|
||
|
||
<!-- 搜索栏 -->
|
||
<view class="search-header tn-flex tn-flex-row-between tn-flex-col-center">
|
||
<view class="back-btn tn-flex tn-flex-col-center tn-flex-row-center" @click="$emit('collapse-to-hero')">
|
||
<text class="tn-icon-up nav-icon"></text>
|
||
</view>
|
||
<view class="search-box tn-flex tn-flex-col-center">
|
||
<text class="tn-icon-search search-icon"></text>
|
||
<text class="search-placeholder">{{ $tr('搜索区域、公寓或房型') }}</text>
|
||
</view>
|
||
<view
|
||
class="sort-trigger tn-flex tn-flex-col-center tn-flex-row-center"
|
||
:class="{ 'sort-trigger--active': selectedSortKey !== 'default' }"
|
||
@click="showSortActionSheet"
|
||
>
|
||
<text class="sort-trigger__text">{{ currentSortLabel }}</text>
|
||
<text class="tn-icon-down sort-trigger__arrow"></text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="quick-filter-card">
|
||
<view class="quick-filter-head tn-flex tn-flex-row-between tn-flex-col-center">
|
||
<view class="quick-filter-title-box tn-flex tn-flex-col-center">
|
||
<view class="quick-filter-dot"></view>
|
||
<text class="quick-filter-title">{{ $tr('标签筛选') }}</text>
|
||
</view>
|
||
<text class="quick-filter-current">{{ displayTag(currentTagLabel) }}</text>
|
||
</view>
|
||
|
||
<!-- 快捷标签栏 (横向滚动) -->
|
||
<scroll-view class="quick-tags-scroll" scroll-x :show-scrollbar="false">
|
||
<view class="quick-tags-wrap tn-flex tn-flex-col-center">
|
||
<view
|
||
class="quick-tag"
|
||
:class="{ 'quick-tag--active': !selectedTag }"
|
||
@click="$emit('select-tag', '')"
|
||
>
|
||
{{ $tr('全部') }}
|
||
</view>
|
||
<view
|
||
v-for="tag in availableFilterTags"
|
||
:key="tag"
|
||
class="quick-tag"
|
||
:class="{ 'quick-tag--active': selectedTag === tag }"
|
||
@click="$emit('select-tag', tag)"
|
||
>
|
||
{{ displayTag(tag) }}
|
||
</view>
|
||
</view>
|
||
</scroll-view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="content-section">
|
||
<view class="content-panel">
|
||
|
||
<view
|
||
v-if="isAreaLoading || roomLoadError || areaLoadError"
|
||
class="room-status-card tn-margin-bottom-lg"
|
||
>
|
||
<view class="room-status-card__title">
|
||
{{ isAreaLoading ? $tr('区域加载中') : $tr('区域状态') }}
|
||
</view>
|
||
<view class="room-status-card__desc">
|
||
{{ roomLoadError || areaLoadError || $tr('暂无符合筛选条件的区域') }}
|
||
</view>
|
||
</view>
|
||
|
||
<view v-if="!isAreaLoading && !displayAreaSections.length" class="empty-state-card tn-margin-bottom-lg">
|
||
<view class="empty-state-card__title">{{ bannerList.length ? $tr('暂无匹配房源') : $tr('暂无轮播图') }}</view>
|
||
<view class="empty-state-card__desc">
|
||
{{ bannerList.length ? $tr('当前筛选条件下暂无可用房源') : $tr('暂无可展示的轮播图') }}
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 小区/区域卡片 -->
|
||
<view v-for="area in displayAreaSections" :key="area.code" class="store-card">
|
||
<!-- 小区头部信息 -->
|
||
<view class="store-header" @click="openStoreDetail(area)">
|
||
<view class="store-title-bar tn-flex tn-flex-row-between tn-flex-col-center">
|
||
<view class="store-title tn-text-ellipsis">{{ getAreaName(area) }}</view>
|
||
<view class="store-detail-btn tn-flex tn-flex-col-center">
|
||
<text class="btn-text">{{ $tr('查看详情') }}</text>
|
||
<text class="tn-icon-right btn-icon"></text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="store-tags tn-flex tn-flex-wrap">
|
||
<view v-for="tag in getDisplayTags(area)" :key="tag" class="store-tag">{{ displayTag(tag) }}</view>
|
||
</view>
|
||
|
||
<view class="store-location-info tn-flex tn-flex-row-between tn-flex-col-center">
|
||
<view class="store-address tn-flex tn-flex-col-center tn-text-ellipsis">
|
||
<view class="address-icon-box tn-flex tn-flex-col-center tn-flex-row-center">
|
||
<text class="tn-icon-location location-icon"></text>
|
||
</view>
|
||
<text class="address-text">{{ getAreaAddress(area) || defaultAreaAddress(getAreaName(area)) }}</text>
|
||
</view>
|
||
<view class="store-distance">{{ $tr('附近') }}</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 横向滚动房型列表 -->
|
||
<scroll-view class="room-scroll" scroll-x :show-scrollbar="false">
|
||
<view class="room-list-wrap tn-flex">
|
||
<view
|
||
v-for="(roomType, idx) in area.roomTypes"
|
||
:key="roomType.code"
|
||
class="room-item"
|
||
@click="$emit('open-room-type', area, roomType)"
|
||
>
|
||
<view class="room-image-box">
|
||
<image
|
||
:src="roomType.images && roomType.images.length ? roomType.images[0] : ''"
|
||
mode="aspectFill"
|
||
class="room-image"
|
||
></image>
|
||
<view class="room-tag-overlay" v-if="roomType.payMethod">{{ getPayMethodText(roomType.payMethod) }}</view>
|
||
</view>
|
||
|
||
<view class="room-info">
|
||
<view class="room-name tn-text-ellipsis">{{ formatRoomTypeName(getRoomTypeName(roomType)) }}</view>
|
||
<view class="room-price-row tn-flex tn-flex-row-between tn-flex-col-bottom">
|
||
<view class="price-content">
|
||
<text class="price-symbol">¥</text>
|
||
<text class="price-val">{{ formatDisplayPrice(roomType.price) || $tr('待定') }}</text>
|
||
<text class="price-unit">{{ $tr('元/月起') }}</text>
|
||
</view>
|
||
<view class="room-go-icon tn-flex tn-flex-col-center tn-flex-row-center">
|
||
<text class="tn-icon-right"></text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</scroll-view>
|
||
</view>
|
||
|
||
<!-- 底部安全区占位,防止最后一张卡片被 tabBar 遮挡 -->
|
||
<view class="safe-area-bottom-padding"></view>
|
||
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import { localizeHousingTag, localizePayMethodText } from '../../../utils/select-room-normalizers'
|
||
|
||
export default {
|
||
name: 'SelectRoomAreaPanel',
|
||
props: {
|
||
isBannerLoading: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
bannerLoadError: {
|
||
type: String,
|
||
default: ''
|
||
},
|
||
bannerLoadedFromApi: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
currentBanner: {
|
||
type: Object,
|
||
default: () => ({})
|
||
},
|
||
selectedAreaCode: {
|
||
type: String,
|
||
default: ''
|
||
},
|
||
areaList: {
|
||
type: Array,
|
||
default: () => []
|
||
},
|
||
currentAreaLabel: {
|
||
type: String,
|
||
default: ''
|
||
},
|
||
selectedTag: {
|
||
type: String,
|
||
default: ''
|
||
},
|
||
currentTagLabel: {
|
||
type: String,
|
||
default: '全部标签'
|
||
},
|
||
availableFilterTags: {
|
||
type: Array,
|
||
default: () => []
|
||
},
|
||
selectedSortKey: {
|
||
type: String,
|
||
default: 'default'
|
||
},
|
||
currentSortLabel: {
|
||
type: String,
|
||
default: ''
|
||
},
|
||
isAreaLoading: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
roomLoadError: {
|
||
type: String,
|
||
default: ''
|
||
},
|
||
areaLoadError: {
|
||
type: String,
|
||
default: ''
|
||
},
|
||
isLogin: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
userInfo: {
|
||
type: Object,
|
||
default: null
|
||
},
|
||
displayAreaSections: {
|
||
type: Array,
|
||
default: () => []
|
||
},
|
||
bannerList: {
|
||
type: Array,
|
||
default: () => []
|
||
},
|
||
currentBannerIndex: {
|
||
type: Number,
|
||
default: 0
|
||
},
|
||
isActive: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
language: {
|
||
type: String,
|
||
default: 'zh'
|
||
},
|
||
compactBannerTop: {
|
||
type: Number,
|
||
default: 88
|
||
}
|
||
},
|
||
data() {
|
||
return {}
|
||
},
|
||
methods: {
|
||
displayTag(tag = '') {
|
||
const text = String(tag || '').trim()
|
||
if (text === '全部标签' && this.language === 'en') {
|
||
return 'All Tags'
|
||
}
|
||
return localizeHousingTag(text, this.language)
|
||
},
|
||
formatRoomTypeName(name) {
|
||
const normalizedName = String(name || '')
|
||
return normalizedName.replace(/^[^·]+·/, '')
|
||
},
|
||
getAreaName(area = {}) {
|
||
if (this.language === 'en') {
|
||
return area.nameEnDisplay || area.nameEn || area.name || ''
|
||
}
|
||
return area.name || ''
|
||
},
|
||
getAreaAddress(area = {}) {
|
||
if (this.language === 'en') {
|
||
return area.addressEnDisplay || area.addressEn || area.address || ''
|
||
}
|
||
return area.address || ''
|
||
},
|
||
getRoomTypeName(roomType = {}) {
|
||
if (this.language === 'en') {
|
||
return roomType.nameEnDisplay || roomType.nameEn || roomType.name || ''
|
||
}
|
||
return roomType.name || ''
|
||
},
|
||
getPayMethodText(payMethod = '') {
|
||
return localizePayMethodText(payMethod, this.language)
|
||
},
|
||
formatDisplayPrice(price) {
|
||
if (!price) {
|
||
return ''
|
||
}
|
||
const normalizedPrice = String(price).replace(/,/g, '')
|
||
const matched = normalizedPrice.match(/(\d+(?:\.\d+)?)/)
|
||
return matched ? matched[1] : ''
|
||
},
|
||
getDisplayTags(area) {
|
||
const areaTags = Array.isArray(area.tags) ? area.tags : []
|
||
if (areaTags.length) {
|
||
return areaTags.slice(0, 4)
|
||
}
|
||
|
||
const roomTypeTags = (area.roomTypes || [])
|
||
.flatMap((roomType) => roomType.tags || [])
|
||
.filter(Boolean)
|
||
|
||
return [...new Set(roomTypeTags)].slice(0, 4)
|
||
},
|
||
showTagActionSheet() {
|
||
const tagOptions = [this.$tr('全部标签'), ...this.availableFilterTags]
|
||
if (!tagOptions.length) {
|
||
return
|
||
}
|
||
uni.showActionSheet({
|
||
itemList: tagOptions.map((tag) => this.displayTag(tag)),
|
||
success: ({ tapIndex }) => {
|
||
const selectedTag = tapIndex === 0 ? '' : tagOptions[tapIndex]
|
||
this.$emit('select-tag', selectedTag)
|
||
}
|
||
})
|
||
},
|
||
showSortActionSheet() {
|
||
const sortOptions = [
|
||
{ label: this.$tr('默认排序'), value: 'default' },
|
||
{ label: this.$tr('价格从低到高'), value: 'price-asc' },
|
||
{ label: this.$tr('价格从高到低'), value: 'price-desc' },
|
||
{ label: this.$tr('名称排序'), value: 'name-asc' }
|
||
]
|
||
uni.showActionSheet({
|
||
itemList: sortOptions.map((item) => item.label),
|
||
success: ({ tapIndex }) => {
|
||
const selected = sortOptions[tapIndex]
|
||
if (selected) {
|
||
this.$emit('select-sort', selected.value)
|
||
}
|
||
}
|
||
})
|
||
},
|
||
handleBannerChange(e) {
|
||
if (!this.isActive) {
|
||
return
|
||
}
|
||
const nextIndex = Number(e && e.detail && e.detail.current)
|
||
if (!Number.isFinite(nextIndex)) {
|
||
return
|
||
}
|
||
this.$emit('banner-change', nextIndex)
|
||
},
|
||
openStoreDetail(area) {
|
||
uni.navigateTo({
|
||
url: `/pkgExplore/store_detail/store_detail?storeData=${encodeURIComponent(JSON.stringify(area))}`
|
||
})
|
||
},
|
||
defaultAreaAddress(areaName = '') {
|
||
if (this.language === 'en') {
|
||
return `${this.$tr('位于')}${areaName} CBD`
|
||
}
|
||
return `${this.$tr('位于')}${areaName}核心商圈`
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.panel-container {
|
||
min-height: 100vh;
|
||
background-color: transparent;
|
||
}
|
||
.content-panel {
|
||
padding: 24rpx;
|
||
box-sizing: border-box;
|
||
}
|
||
/* 顶部搜索栏包装 */
|
||
.compact-banner-wrap {
|
||
position: sticky;
|
||
z-index: 99;
|
||
padding: 0 0 24rpx;
|
||
overflow: hidden;
|
||
background-color: #fff;
|
||
}
|
||
|
||
.compact-swiper-container {
|
||
width: 100%;
|
||
height: 420rpx;
|
||
position: relative;
|
||
}
|
||
|
||
.compact-swiper {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
.compact-swiper__image {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
.compact-swiper__mask {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: linear-gradient(to bottom,
|
||
rgba(0,0,0,0.15) 0%,
|
||
rgba(0,0,0,0) 30%,
|
||
rgba(255,255,255,0) 60%,
|
||
rgba(255,255,255,1) 100%
|
||
);
|
||
}
|
||
|
||
.search-header {
|
||
position: relative;
|
||
z-index: 100;
|
||
margin-top: -96rpx;
|
||
min-height: 92rpx;
|
||
background: rgba(255, 255, 255, 0.96);
|
||
backdrop-filter: blur(12px);
|
||
-webkit-backdrop-filter: blur(12px);
|
||
border-radius: 46rpx;
|
||
padding: 0 36rpx;
|
||
box-sizing: border-box;
|
||
margin-left: 24rpx;
|
||
margin-right: 24rpx;
|
||
border: 1rpx solid rgba(255, 255, 255, 0.8);
|
||
box-shadow:
|
||
0 10rpx 30rpx rgba(0, 0, 0, 0.05),
|
||
0 4rpx 10rpx rgba($brand-primary, 0.05);
|
||
}
|
||
|
||
.back-btn {
|
||
width: 60rpx;
|
||
height: 60rpx;
|
||
flex-shrink: 0;
|
||
color: #333;
|
||
margin-right: 20rpx;
|
||
background: #f8f9fa;
|
||
border-radius: 50%;
|
||
border: 1rpx solid rgba(0, 0, 0, 0.03);
|
||
}
|
||
|
||
.nav-icon {
|
||
font-size: 28rpx;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.search-box {
|
||
flex: 1;
|
||
color: #333;
|
||
}
|
||
|
||
.search-icon {
|
||
font-size: 34rpx;
|
||
margin-right: 16rpx;
|
||
line-height: 1;
|
||
color: $brand-primary;
|
||
}
|
||
|
||
.search-placeholder {
|
||
font-size: 28rpx;
|
||
color: #a0a7b5;
|
||
letter-spacing: 1rpx;
|
||
font-weight: 400;
|
||
}
|
||
|
||
.sort-trigger {
|
||
max-width: 200rpx;
|
||
min-width: 144rpx;
|
||
height: 64rpx;
|
||
padding: 0 24rpx;
|
||
flex-shrink: 0;
|
||
border-radius: 32rpx;
|
||
border: 1rpx solid rgba($brand-primary, 0.12);
|
||
background: #f0f7ff;
|
||
color: #0C6E8B;
|
||
box-sizing: border-box;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.sort-trigger--active {
|
||
background: $brand-primary;
|
||
color: #ffffff;
|
||
border-color: $brand-primary;
|
||
box-shadow: 0 4rpx 12rpx rgba($brand-primary, 0.2);
|
||
}
|
||
|
||
.sort-trigger__text {
|
||
max-width: 128rpx;
|
||
font-size: 24rpx;
|
||
font-weight: 600;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.sort-trigger__arrow {
|
||
margin-left: 10rpx;
|
||
font-size: 20rpx;
|
||
line-height: 1;
|
||
opacity: 0.8;
|
||
}
|
||
|
||
.quick-filter-card {
|
||
margin: 18rpx 24rpx 0;
|
||
padding: 22rpx 0 18rpx;
|
||
border-radius: 28rpx;
|
||
background: rgba(255, 255, 255, 0.96);
|
||
border: 1rpx solid rgba(255, 255, 255, 0.88);
|
||
box-shadow:
|
||
0 12rpx 28rpx rgba(15, 23, 42, 0.05),
|
||
0 6rpx 16rpx rgba($brand-primary, 0.06);
|
||
}
|
||
|
||
.quick-filter-head {
|
||
padding: 0 24rpx 14rpx;
|
||
}
|
||
|
||
.quick-filter-title-box {
|
||
color: #1E293B;
|
||
}
|
||
|
||
.quick-filter-dot {
|
||
width: 12rpx;
|
||
height: 12rpx;
|
||
margin-right: 12rpx;
|
||
border-radius: 50%;
|
||
background: linear-gradient(135deg, $brand-primary 0%, #76B8FF 100%);
|
||
box-shadow: 0 0 0 6rpx rgba($brand-primary, 0.08);
|
||
}
|
||
|
||
.quick-filter-title {
|
||
font-size: 24rpx;
|
||
font-weight: 600;
|
||
color: #334155;
|
||
letter-spacing: 1rpx;
|
||
}
|
||
|
||
.quick-filter-current {
|
||
max-width: 220rpx;
|
||
font-size: 22rpx;
|
||
color: #94A3B8;
|
||
text-align: right;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
/* 横向快捷标签 */
|
||
.quick-tags-scroll {
|
||
background-color: transparent;
|
||
padding: 0;
|
||
width: 100%;
|
||
}
|
||
|
||
.quick-tags-wrap {
|
||
padding: 0 24rpx;
|
||
}
|
||
|
||
.quick-tag {
|
||
flex-shrink: 0;
|
||
min-width: 96rpx;
|
||
text-align: center;
|
||
background: #F8FBFF;
|
||
color: #475569;
|
||
font-size: 24rpx;
|
||
padding: 12rpx 26rpx;
|
||
border-radius: 999rpx;
|
||
margin-right: 16rpx;
|
||
font-weight: 500;
|
||
border: 1rpx solid rgba($brand-primary, 0.08);
|
||
box-shadow: inset 0 1rpx 0 rgba(255, 255, 255, 0.8);
|
||
}
|
||
|
||
.quick-tag--active {
|
||
background: linear-gradient(135deg, rgba($brand-primary, 0.16) 0%, rgba(118, 184, 255, 0.24) 100%);
|
||
color: #0C6E8B;
|
||
border-color: rgba($brand-primary, 0.22);
|
||
box-shadow: 0 8rpx 18rpx rgba($brand-primary, 0.10);
|
||
}
|
||
|
||
/* 小区卡片整体 */
|
||
.store-card {
|
||
background: rgba(255, 255, 255, 0.95);
|
||
backdrop-filter: blur(20rpx);
|
||
-webkit-backdrop-filter: blur(20rpx);
|
||
border-radius: 32rpx;
|
||
padding: 32rpx;
|
||
margin-bottom: 32rpx;
|
||
box-shadow: 0 12rpx 32rpx rgba($brand-primary, 0.08);
|
||
border: 1rpx solid rgba(255, 255, 255, 1);
|
||
}
|
||
|
||
/* 小区头部信息 */
|
||
.store-header {
|
||
margin-bottom: 32rpx;
|
||
}
|
||
|
||
.store-title-bar {
|
||
min-width: 0;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.store-title {
|
||
font-size: 38rpx;
|
||
font-weight: 700;
|
||
color: #1A1D24;
|
||
max-width: 75%;
|
||
letter-spacing: 1rpx;
|
||
}
|
||
|
||
.store-detail-btn {
|
||
background: rgba($brand-primary, 0.08);
|
||
padding: 8rpx 20rpx;
|
||
border-radius: 24rpx;
|
||
color: $brand-primary;
|
||
|
||
.btn-text {
|
||
font-size: 22rpx;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.btn-icon {
|
||
font-size: 20rpx;
|
||
margin-left: 4rpx;
|
||
}
|
||
}
|
||
|
||
.store-tags {
|
||
margin-bottom: 24rpx;
|
||
gap: 12rpx;
|
||
}
|
||
|
||
.store-tag {
|
||
color: #0C6E8B;
|
||
background: #F0F9FF;
|
||
font-size: 22rpx;
|
||
padding: 6rpx 18rpx;
|
||
border-radius: 12rpx;
|
||
font-weight: 500;
|
||
border: 1rpx solid rgba($brand-primary, 0.1);
|
||
}
|
||
|
||
.store-location-info {
|
||
color: #64748B;
|
||
font-size: 24rpx;
|
||
background: #F8FAFC;
|
||
padding: 16rpx 24rpx;
|
||
border-radius: 20rpx;
|
||
border: 1rpx solid rgba(0, 0, 0, 0.02);
|
||
}
|
||
|
||
.store-address {
|
||
flex: 1;
|
||
min-width: 0;
|
||
margin-right: 24rpx;
|
||
}
|
||
|
||
.address-icon-box {
|
||
width: 44rpx;
|
||
height: 44rpx;
|
||
background: #fff;
|
||
border-radius: 12rpx;
|
||
margin-right: 12rpx;
|
||
box-shadow: 0 4rpx 10rpx rgba(0, 0, 0, 0.03);
|
||
}
|
||
|
||
.location-icon {
|
||
font-size: 26rpx;
|
||
color: $brand-primary;
|
||
}
|
||
|
||
.address-text {
|
||
min-width: 0;
|
||
color: #475569;
|
||
font-weight: 500;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.store-distance {
|
||
color: #1E293B;
|
||
font-weight: 600;
|
||
font-size: 22rpx;
|
||
}
|
||
|
||
/* 横向滚动房型列表 */
|
||
.room-scroll {
|
||
width: 100%;
|
||
}
|
||
|
||
.room-list-wrap {
|
||
gap: 16rpx;
|
||
padding-bottom: 12rpx;
|
||
}
|
||
|
||
.room-item {
|
||
width: 240rpx;
|
||
flex-shrink: 0;
|
||
background: #ffffff;
|
||
border-radius: 16rpx;
|
||
padding-bottom: 16rpx;
|
||
box-shadow: 0 6rpx 16rpx rgba(0, 0, 0, 0.03);
|
||
overflow: hidden;
|
||
border: 1rpx solid #F1F5F9;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.room-item:active {
|
||
transform: translateY(-2rpx);
|
||
box-shadow: 0 10rpx 24rpx rgba($brand-primary, 0.1);
|
||
}
|
||
|
||
.room-image-box {
|
||
width: 100%;
|
||
height: 180rpx;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.room-image {
|
||
width: 100%;
|
||
height: 100%;
|
||
background-color: #F8FAFC;
|
||
transition: transform 0.4s ease;
|
||
}
|
||
|
||
.room-tag-overlay {
|
||
position: absolute;
|
||
top: 12rpx;
|
||
right: 12rpx;
|
||
max-width: calc(100% - 24rpx);
|
||
background: rgba(0, 0, 0, 0.6);
|
||
backdrop-filter: blur(8rpx);
|
||
color: #fff;
|
||
font-size: 18rpx;
|
||
padding: 4rpx 10rpx;
|
||
border-radius: 999rpx;
|
||
font-weight: 500;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.room-info {
|
||
width: 100%;
|
||
padding: 16rpx 16rpx 0;
|
||
}
|
||
|
||
.room-name {
|
||
font-size: 26rpx;
|
||
color: #1E293B;
|
||
font-weight: 600;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
|
||
.room-price-row {
|
||
width: 100%;
|
||
}
|
||
|
||
.price-symbol {
|
||
font-size: 20rpx;
|
||
color: #EF4444;
|
||
font-weight: 700;
|
||
margin-right: 2rpx;
|
||
}
|
||
|
||
.price-val {
|
||
color: #EF4444;
|
||
font-size: 32rpx;
|
||
font-weight: 800;
|
||
line-height: 1;
|
||
}
|
||
|
||
.price-unit {
|
||
color: #94A3B8;
|
||
font-size: 18rpx;
|
||
margin-left: 4rpx;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.room-go-icon {
|
||
width: 32rpx;
|
||
height: 32rpx;
|
||
background: #F8FAFC;
|
||
border-radius: 50%;
|
||
color: #94A3B8;
|
||
font-size: 18rpx;
|
||
border: 1rpx solid #F1F5F9;
|
||
}
|
||
|
||
.safe-area-bottom-padding {
|
||
height: calc(140rpx + env(safe-area-inset-bottom));
|
||
width: 100%;
|
||
}
|
||
|
||
.room-status-card,
|
||
.empty-state-card {
|
||
background: rgba(255, 255, 255, 0.8);
|
||
backdrop-filter: blur(10rpx);
|
||
border-radius: 20rpx;
|
||
padding: 32rpx;
|
||
text-align: center;
|
||
border: 1rpx solid rgba($brand-primary, 0.1);
|
||
box-shadow: 0 4rpx 16rpx rgba($brand-primary, 0.04);
|
||
}
|
||
|
||
.room-status-card__title,
|
||
.empty-state-card__title {
|
||
font-size: 30rpx;
|
||
font-weight: 600;
|
||
color: #333;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
|
||
.room-status-card__desc,
|
||
.empty-state-card__desc {
|
||
font-size: 26rpx;
|
||
color: #666;
|
||
}
|
||
</style>
|