2026-04-20 06:23:11 +00:00
|
|
|
|
<template>
|
|
|
|
|
|
<view class="properties-page">
|
|
|
|
|
|
<!-- 自定义导航栏 -->
|
|
|
|
|
|
<view class="custom-nav safe-area-top">
|
|
|
|
|
|
<view class="nav-content">
|
2026-04-22 06:47:04 +00:00
|
|
|
|
<text class="nav-title">房态图</text>
|
2026-04-20 06:23:11 +00:00
|
|
|
|
<view class="nav-actions">
|
|
|
|
|
|
<view class="nav-btn primary" @click="addProperty">
|
|
|
|
|
|
<uni-icons type="plus" size="22" color="#FFFFFF"></uni-icons>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 公寓选择器 -->
|
2026-04-22 06:47:04 +00:00
|
|
|
|
<view class="apartment-section-header" v-if="apartments.length > 0">
|
|
|
|
|
|
<scroll-view scroll-x class="apartment-tabs" show-scrollbar="false">
|
2026-04-20 06:23:11 +00:00
|
|
|
|
<view
|
|
|
|
|
|
v-for="(apt, index) in apartments"
|
|
|
|
|
|
:key="index"
|
|
|
|
|
|
class="tab-item"
|
|
|
|
|
|
:class="{ active: selectedApartmentId === apt.id }"
|
|
|
|
|
|
@click="selectApartment(apt.id)"
|
|
|
|
|
|
>
|
|
|
|
|
|
<text class="tab-name">{{apt.name}}</text>
|
|
|
|
|
|
<text class="tab-count">{{apt.roomCount}}间</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</scroll-view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
2026-04-22 06:47:04 +00:00
|
|
|
|
<!-- 搜索栏 -->
|
|
|
|
|
|
<view class="search-bar">
|
|
|
|
|
|
<view class="search-input-wrapper">
|
|
|
|
|
|
<uni-icons type="search" size="18" color="#909399"></uni-icons>
|
|
|
|
|
|
<input
|
|
|
|
|
|
class="search-input"
|
|
|
|
|
|
type="text"
|
|
|
|
|
|
v-model="searchKeyword"
|
|
|
|
|
|
placeholder="搜索房间号"
|
|
|
|
|
|
confirm-type="search"
|
|
|
|
|
|
@confirm="onSearch"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<uni-icons v-if="searchKeyword" type="clear" size="16" color="#909399" @click="clearSearch"></uni-icons>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 状态图例 -->
|
|
|
|
|
|
<view class="status-legend">
|
|
|
|
|
|
<view class="legend-scroll">
|
|
|
|
|
|
<view
|
|
|
|
|
|
v-for="(filter, index) in statusFilters"
|
|
|
|
|
|
:key="index"
|
|
|
|
|
|
class="legend-item"
|
|
|
|
|
|
:class="{ active: currentFilter === filter.value }"
|
|
|
|
|
|
@click="setFilter(filter.value)"
|
|
|
|
|
|
>
|
|
|
|
|
|
<view class="legend-dot" :style="{ background: filter.color }"></view>
|
|
|
|
|
|
<text class="legend-text">{{filter.label}}</text>
|
|
|
|
|
|
<text class="legend-count">{{getStatusCount(filter.value)}}</text>
|
|
|
|
|
|
</view>
|
2026-04-20 06:23:11 +00:00
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
2026-04-22 06:47:04 +00:00
|
|
|
|
<!-- 房间列表 -->
|
|
|
|
|
|
<scroll-view
|
|
|
|
|
|
scroll-y
|
|
|
|
|
|
class="room-list"
|
|
|
|
|
|
@scrolltolower="loadMore"
|
|
|
|
|
|
refresher-enabled
|
|
|
|
|
|
:refresher-triggered="isRefreshing"
|
|
|
|
|
|
@refresherrefresh="onRefresh"
|
|
|
|
|
|
>
|
|
|
|
|
|
<!-- 空状态 -->
|
2026-04-20 06:23:11 +00:00
|
|
|
|
<view v-if="filteredRooms.length === 0 && !isLoading" class="empty-state">
|
|
|
|
|
|
<view class="empty-icon">
|
|
|
|
|
|
<uni-icons type="home-filled" size="80" color="#CBD5E1"></uni-icons>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<text class="empty-title">暂无房间</text>
|
|
|
|
|
|
<text class="empty-desc">点击右上角添加房间</text>
|
|
|
|
|
|
<button class="empty-btn" @click="addRoom">添加房间</button>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
2026-04-22 06:47:04 +00:00
|
|
|
|
<!-- 房间卡片网格 -->
|
|
|
|
|
|
<view v-else class="room-grid">
|
|
|
|
|
|
<view
|
|
|
|
|
|
v-for="(room, index) in filteredRooms"
|
2026-04-20 06:23:11 +00:00
|
|
|
|
:key="index"
|
|
|
|
|
|
class="room-card"
|
2026-04-22 06:47:04 +00:00
|
|
|
|
:class="getRoomStatusClass(room)"
|
2026-04-20 06:23:11 +00:00
|
|
|
|
@click="handleRoomClick(room)"
|
|
|
|
|
|
>
|
2026-04-22 06:47:04 +00:00
|
|
|
|
<text class="room-number">{{room.roomNumber}}</text>
|
2026-04-20 06:23:11 +00:00
|
|
|
|
<view class="room-info">
|
2026-04-22 06:47:04 +00:00
|
|
|
|
<text class="room-price" v-if="room.monthlyPrice">¥{{room.monthlyPrice}}/月</text>
|
|
|
|
|
|
<text class="room-tenant" v-if="room.renterName">{{room.renterName}}</text>
|
|
|
|
|
|
<text class="room-date" v-if="room.endDate">{{formatDate(room.endDate)}}到期</text>
|
2026-04-20 06:23:11 +00:00
|
|
|
|
</view>
|
2026-04-22 06:47:04 +00:00
|
|
|
|
<view class="room-status-tag">{{getRoomStatusText(room)}}</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
2026-04-20 06:23:11 +00:00
|
|
|
|
|
2026-04-22 06:47:04 +00:00
|
|
|
|
<uni-load-more v-if="filteredRooms.length > 0" :status="loadStatus"></uni-load-more>
|
2026-04-20 06:23:11 +00:00
|
|
|
|
|
2026-04-22 06:47:04 +00:00
|
|
|
|
<!-- 底部统计栏 -->
|
|
|
|
|
|
<view class="bottom-stats">
|
|
|
|
|
|
<view class="stats-content">
|
|
|
|
|
|
<view class="stat-item">
|
|
|
|
|
|
<text class="stat-value">{{currentApartmentStats.total}}</text>
|
|
|
|
|
|
<text class="stat-label">总房间</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="stat-divider"></view>
|
|
|
|
|
|
<view class="stat-item">
|
|
|
|
|
|
<text class="stat-value" style="color: #67C23A;">{{currentApartmentStats.rented}}</text>
|
|
|
|
|
|
<text class="stat-label">在租</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="stat-divider"></view>
|
|
|
|
|
|
<view class="stat-item">
|
|
|
|
|
|
<text class="stat-value" style="color: #909399;">{{currentApartmentStats.empty}}</text>
|
|
|
|
|
|
<text class="stat-label">空房</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="stat-divider"></view>
|
|
|
|
|
|
<view class="stat-item highlight">
|
|
|
|
|
|
<text class="stat-value" style="color: #409EFF;">{{currentApartmentStats.occupancyRate}}%</text>
|
|
|
|
|
|
<text class="stat-label">出租率</text>
|
2026-04-20 06:23:11 +00:00
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
2026-04-22 06:47:04 +00:00
|
|
|
|
<view class="safe-area-bottom" style="height: 120rpx;"></view>
|
2026-04-20 06:23:11 +00:00
|
|
|
|
</scroll-view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
|
import apartmentApi from '@/api/apartment.js'
|
|
|
|
|
|
import roomApi from '@/api/room.js'
|
2026-04-22 06:47:04 +00:00
|
|
|
|
import statisticsApi from '@/api/statistics.js'
|
|
|
|
|
|
|
2026-04-20 06:23:11 +00:00
|
|
|
|
export default {
|
|
|
|
|
|
data() {
|
|
|
|
|
|
return {
|
|
|
|
|
|
isLoading: false,
|
|
|
|
|
|
isRefreshing: false,
|
|
|
|
|
|
apartments: [],
|
|
|
|
|
|
selectedApartmentId: null,
|
|
|
|
|
|
rooms: [],
|
2026-04-22 06:47:04 +00:00
|
|
|
|
apartmentStats: {}, // 公寓统计数据
|
2026-04-20 06:23:11 +00:00
|
|
|
|
currentFilter: 'all',
|
|
|
|
|
|
loadStatus: 'more',
|
|
|
|
|
|
page: 1,
|
|
|
|
|
|
pageSize: 20,
|
2026-04-22 06:47:04 +00:00
|
|
|
|
searchKeyword: '',
|
2026-04-20 06:23:11 +00:00
|
|
|
|
statusFilters: [
|
|
|
|
|
|
{ label: '全部', value: 'all', color: '#409EFF' },
|
2026-04-22 06:47:04 +00:00
|
|
|
|
{ label: '空置', value: 'empty', color: '#67C23A' },
|
|
|
|
|
|
{ label: '已租', value: 'rented', color: '#409EFF' },
|
|
|
|
|
|
{ label: '即将到期', value: 'soon_expire', color: '#E6A23C' },
|
|
|
|
|
|
{ label: '已到期', value: 'expired', color: '#F56C6C' },
|
|
|
|
|
|
{ label: '预定', value: 'reserved', color: '#909399' }
|
2026-04-20 06:23:11 +00:00
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
computed: {
|
|
|
|
|
|
filteredRooms() {
|
|
|
|
|
|
let list = this.rooms
|
2026-04-22 06:47:04 +00:00
|
|
|
|
// 按状态筛选
|
2026-04-20 06:23:11 +00:00
|
|
|
|
if (this.currentFilter !== 'all') {
|
|
|
|
|
|
list = list.filter(room => room.status === this.currentFilter)
|
|
|
|
|
|
}
|
2026-04-22 06:47:04 +00:00
|
|
|
|
// 按关键词搜索
|
|
|
|
|
|
if (this.searchKeyword.trim()) {
|
|
|
|
|
|
const keyword = this.searchKeyword.trim().toLowerCase()
|
|
|
|
|
|
list = list.filter(room => room.roomNumber.toLowerCase().includes(keyword))
|
|
|
|
|
|
}
|
2026-04-20 06:23:11 +00:00
|
|
|
|
return list
|
|
|
|
|
|
},
|
|
|
|
|
|
currentApartmentStats() {
|
|
|
|
|
|
const total = this.rooms.length
|
|
|
|
|
|
const rented = this.rooms.filter(r => r.status === 'rented').length
|
|
|
|
|
|
const empty = this.rooms.filter(r => r.status === 'empty').length
|
2026-04-22 06:47:04 +00:00
|
|
|
|
const soonExpire = this.rooms.filter(r => r.status === 'soon_expire').length
|
2026-04-20 06:23:11 +00:00
|
|
|
|
return {
|
|
|
|
|
|
total,
|
|
|
|
|
|
rented,
|
|
|
|
|
|
empty,
|
2026-04-22 06:47:04 +00:00
|
|
|
|
soonExpire,
|
2026-04-20 06:23:11 +00:00
|
|
|
|
occupancyRate: total > 0 ? Math.round((rented / total) * 100) : 0
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
onLoad(options) {
|
|
|
|
|
|
this.loadApartments()
|
|
|
|
|
|
},
|
|
|
|
|
|
onShow() {
|
|
|
|
|
|
if (this.selectedApartmentId) {
|
|
|
|
|
|
this.loadRooms()
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
methods: {
|
|
|
|
|
|
async loadApartments() {
|
|
|
|
|
|
try {
|
2026-04-22 06:47:04 +00:00
|
|
|
|
// 使用统计接口获取公寓列表及统计数据
|
|
|
|
|
|
const res = await statisticsApi.getApartmentRoomStatusStats()
|
2026-04-20 06:23:11 +00:00
|
|
|
|
if (res.data) {
|
|
|
|
|
|
this.apartments = res.data.map(apt => ({
|
2026-04-22 06:47:04 +00:00
|
|
|
|
id: apt.apartmentId,
|
|
|
|
|
|
name: apt.apartment,
|
|
|
|
|
|
roomCount: apt.total || 0
|
2026-04-20 06:23:11 +00:00
|
|
|
|
}))
|
2026-04-22 06:47:04 +00:00
|
|
|
|
// 保存统计数据
|
|
|
|
|
|
this.apartmentStats = {}
|
|
|
|
|
|
res.data.forEach(apt => {
|
|
|
|
|
|
this.apartmentStats[apt.apartmentId] = {
|
|
|
|
|
|
total: apt.total || 0,
|
|
|
|
|
|
empty: apt.empty || 0,
|
|
|
|
|
|
rented: apt.rented || 0,
|
|
|
|
|
|
soonExpire: apt.soon_expire || 0,
|
|
|
|
|
|
expired: apt.expired || 0,
|
|
|
|
|
|
reserved: apt.reserved || 0
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
2026-04-20 06:23:11 +00:00
|
|
|
|
if (this.apartments.length > 0 && !this.selectedApartmentId) {
|
|
|
|
|
|
this.selectedApartmentId = this.apartments[0].id
|
|
|
|
|
|
this.loadRooms()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('加载公寓列表失败:', error)
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
async loadRooms() {
|
|
|
|
|
|
if (!this.selectedApartmentId) return
|
|
|
|
|
|
this.isLoading = true
|
|
|
|
|
|
try {
|
2026-04-22 06:47:04 +00:00
|
|
|
|
const res = await roomApi.list({
|
|
|
|
|
|
apartmentId: this.selectedApartmentId
|
2026-04-20 06:23:11 +00:00
|
|
|
|
})
|
|
|
|
|
|
if (res.data) {
|
|
|
|
|
|
const list = res.data.map(room => ({
|
|
|
|
|
|
id: room.id,
|
|
|
|
|
|
roomNumber: room.roomNumber,
|
|
|
|
|
|
area: room.area,
|
2026-04-22 06:47:04 +00:00
|
|
|
|
monthlyPrice: room.rent || room.monthlyPrice,
|
2026-04-20 06:23:11 +00:00
|
|
|
|
status: this.getRoomStatus(room),
|
2026-04-22 06:47:04 +00:00
|
|
|
|
rentalStatus: room.rentalStatus,
|
2026-04-20 06:23:11 +00:00
|
|
|
|
renterName: room.Renter?.name,
|
|
|
|
|
|
renterPhone: room.Renter?.phone,
|
|
|
|
|
|
rentalId: room.Rental?.id,
|
2026-04-22 06:47:04 +00:00
|
|
|
|
endDate: room.Rental?.endDate
|
2026-04-20 06:23:11 +00:00
|
|
|
|
}))
|
|
|
|
|
|
if (this.page === 1) {
|
|
|
|
|
|
this.rooms = list
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.rooms = [...this.rooms, ...list]
|
|
|
|
|
|
}
|
|
|
|
|
|
this.loadStatus = this.rooms.length < (res.total || 0) ? 'more' : 'noMore'
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('加载房间列表失败:', error)
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
this.isLoading = false
|
|
|
|
|
|
this.isRefreshing = false
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
getRoomStatus(room) {
|
2026-04-22 06:47:04 +00:00
|
|
|
|
// 与Web端Status.vue保持一致,直接使用room.status
|
|
|
|
|
|
return room.status || 'empty'
|
2026-04-20 06:23:11 +00:00
|
|
|
|
},
|
2026-04-22 06:47:04 +00:00
|
|
|
|
|
|
|
|
|
|
getRoomStatusClass(room) {
|
|
|
|
|
|
// 根据status和rentalStatus确定样式类,与Web端保持一致
|
|
|
|
|
|
if (room.status === 'empty') return 'status-empty'
|
|
|
|
|
|
if (room.status === 'rented') {
|
|
|
|
|
|
if (room.rentalStatus === 'expired') return 'status-expired'
|
|
|
|
|
|
if (room.rentalStatus === 'soon_expire') return 'status-soon-expire'
|
|
|
|
|
|
return 'status-rented'
|
|
|
|
|
|
}
|
|
|
|
|
|
if (room.status === 'reserved') return 'status-reserved'
|
|
|
|
|
|
return ''
|
2026-04-20 06:23:11 +00:00
|
|
|
|
},
|
|
|
|
|
|
|
2026-04-22 06:47:04 +00:00
|
|
|
|
formatDate(date) {
|
|
|
|
|
|
if (!date) return '-'
|
|
|
|
|
|
const d = new Date(date)
|
|
|
|
|
|
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`
|
2026-04-20 06:23:11 +00:00
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
selectApartment(id) {
|
|
|
|
|
|
this.selectedApartmentId = id
|
|
|
|
|
|
this.page = 1
|
|
|
|
|
|
this.loadRooms()
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
setFilter(value) {
|
|
|
|
|
|
this.currentFilter = value
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
getStatusCount(status) {
|
2026-04-22 06:47:04 +00:00
|
|
|
|
// 使用接口返回的统计数据
|
|
|
|
|
|
const stats = this.apartmentStats[this.selectedApartmentId]
|
|
|
|
|
|
if (!stats) return 0
|
|
|
|
|
|
if (status === 'all') return stats.total
|
|
|
|
|
|
const keyMap = {
|
|
|
|
|
|
'empty': 'empty',
|
|
|
|
|
|
'rented': 'rented',
|
|
|
|
|
|
'soon_expire': 'soonExpire',
|
|
|
|
|
|
'expired': 'expired',
|
|
|
|
|
|
'reserved': 'reserved'
|
|
|
|
|
|
}
|
|
|
|
|
|
return stats[keyMap[status]] || 0
|
2026-04-20 06:23:11 +00:00
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
getStatusColor(status) {
|
|
|
|
|
|
const colors = {
|
2026-04-22 06:47:04 +00:00
|
|
|
|
empty: '#67C23A',
|
|
|
|
|
|
rented: '#409EFF',
|
|
|
|
|
|
soon_expire: '#E6A23C',
|
|
|
|
|
|
expired: '#F56C6C',
|
|
|
|
|
|
reserved: '#909399'
|
2026-04-20 06:23:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
return colors[status] || '#909399'
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
getStatusText(status) {
|
2026-04-22 06:47:04 +00:00
|
|
|
|
// 保持与Web端一致的文本映射
|
2026-04-20 06:23:11 +00:00
|
|
|
|
const texts = {
|
2026-04-22 06:47:04 +00:00
|
|
|
|
empty: '空置',
|
|
|
|
|
|
rented: '已租',
|
2026-04-20 06:23:11 +00:00
|
|
|
|
soon_expire: '即将到期',
|
2026-04-22 06:47:04 +00:00
|
|
|
|
expired: '已到期',
|
|
|
|
|
|
reserved: '预定'
|
2026-04-20 06:23:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
return texts[status] || status
|
|
|
|
|
|
},
|
2026-04-22 06:47:04 +00:00
|
|
|
|
|
|
|
|
|
|
getRoomStatusText(room) {
|
|
|
|
|
|
// 与Web端Status.vue#getRoomStatusText保持一致
|
|
|
|
|
|
if (room.status === 'empty') return '空置'
|
|
|
|
|
|
if (room.status === 'rented') {
|
|
|
|
|
|
if (room.rentalStatus === 'expired') return '已到期'
|
|
|
|
|
|
if (room.rentalStatus === 'soon_expire') return '即将到期'
|
|
|
|
|
|
return '已租'
|
|
|
|
|
|
}
|
|
|
|
|
|
if (room.status === 'reserved') return '预定'
|
|
|
|
|
|
return room.status
|
|
|
|
|
|
},
|
2026-04-20 06:23:11 +00:00
|
|
|
|
|
|
|
|
|
|
handleRoomClick(room) {
|
|
|
|
|
|
uni.navigateTo({
|
|
|
|
|
|
url: `/pages/room-detail/room-detail?id=${room.id}`
|
|
|
|
|
|
})
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
rentRoom(room) {
|
|
|
|
|
|
uni.navigateTo({
|
|
|
|
|
|
url: `/pages/rental-add/rental-add?roomId=${room.id}`
|
|
|
|
|
|
})
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
reserveRoom(room) {
|
|
|
|
|
|
uni.showModal({
|
|
|
|
|
|
title: '预订房间',
|
|
|
|
|
|
content: `确定要预订房间 ${room.roomNumber} 吗?`,
|
|
|
|
|
|
success: (res) => {
|
|
|
|
|
|
if (res.confirm) {
|
|
|
|
|
|
uni.showToast({ title: '预订功能开发中', icon: 'none' })
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
confirmRent(room) {
|
|
|
|
|
|
uni.navigateTo({
|
|
|
|
|
|
url: `/pages/rental-add/rental-add?roomId=${room.id}`
|
|
|
|
|
|
})
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
createBill(room) {
|
|
|
|
|
|
uni.navigateTo({
|
|
|
|
|
|
url: `/pages/bill-add/bill-add?roomId=${room.id}&renterId=${room.renterId}`
|
|
|
|
|
|
})
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
addProperty() {
|
2026-04-22 06:47:04 +00:00
|
|
|
|
// 直接跳转到添加房间页面
|
|
|
|
|
|
this.addRoom()
|
2026-04-20 06:23:11 +00:00
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
addRoom() {
|
|
|
|
|
|
if (!this.selectedApartmentId) {
|
|
|
|
|
|
uni.showToast({ title: '请先选择公寓', icon: 'none' })
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
uni.navigateTo({
|
|
|
|
|
|
url: `/pages/room-add/room-add?apartmentId=${this.selectedApartmentId}`
|
|
|
|
|
|
})
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
onRefresh() {
|
|
|
|
|
|
this.isRefreshing = true
|
|
|
|
|
|
this.page = 1
|
|
|
|
|
|
this.loadRooms()
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
loadMore() {
|
|
|
|
|
|
if (this.loadStatus === 'noMore') return
|
|
|
|
|
|
this.loadStatus = 'loading'
|
|
|
|
|
|
this.page++
|
|
|
|
|
|
this.loadRooms()
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
showFilter() {
|
|
|
|
|
|
uni.showToast({ title: '筛选功能', icon: 'none' })
|
2026-04-22 06:47:04 +00:00
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
onSearch() {
|
|
|
|
|
|
// 搜索时不需要重新加载数据,computed 会自动过滤
|
|
|
|
|
|
uni.showToast({ title: `搜索: ${this.searchKeyword}`, icon: 'none', duration: 1000 })
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
clearSearch() {
|
|
|
|
|
|
this.searchKeyword = ''
|
2026-04-20 06:23:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
|
.properties-page {
|
|
|
|
|
|
min-height: 100vh;
|
2026-04-22 06:47:04 +00:00
|
|
|
|
background: #F5F7FA;
|
2026-04-20 06:23:11 +00:00
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 导航栏 */
|
|
|
|
|
|
.custom-nav {
|
|
|
|
|
|
background: #FFFFFF;
|
2026-04-22 06:47:04 +00:00
|
|
|
|
border-bottom: 1rpx solid #EBEEF5;
|
2026-04-20 06:23:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.nav-content {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
padding: 20rpx 32rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.nav-title {
|
|
|
|
|
|
font-size: 36rpx;
|
|
|
|
|
|
font-weight: 700;
|
2026-04-22 06:47:04 +00:00
|
|
|
|
color: #303133;
|
2026-04-20 06:23:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.nav-actions {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
gap: 16rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.nav-btn {
|
|
|
|
|
|
width: 72rpx;
|
|
|
|
|
|
height: 72rpx;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
border-radius: 50%;
|
2026-04-22 06:47:04 +00:00
|
|
|
|
background: #F5F7FA;
|
|
|
|
|
|
transition: all 0.2s;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.nav-btn:active {
|
|
|
|
|
|
transform: scale(0.95);
|
|
|
|
|
|
background: #EBEEF5;
|
2026-04-20 06:23:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.nav-btn.primary {
|
2026-04-22 06:47:04 +00:00
|
|
|
|
background: linear-gradient(135deg, #409EFF 0%, #66B1FF 100%);
|
|
|
|
|
|
box-shadow: 0 4rpx 16rpx rgba(64, 158, 255, 0.3);
|
2026-04-20 06:23:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-22 06:47:04 +00:00
|
|
|
|
/* 统计概览卡片 */
|
|
|
|
|
|
.statistics-overview {
|
|
|
|
|
|
padding: 24rpx 32rpx;
|
2026-04-20 06:23:11 +00:00
|
|
|
|
background: #FFFFFF;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-22 06:47:04 +00:00
|
|
|
|
.stats-grid {
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: repeat(4, 1fr);
|
|
|
|
|
|
gap: 16rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.stat-card {
|
|
|
|
|
|
border-radius: 16rpx;
|
|
|
|
|
|
padding: 20rpx 12rpx;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.stat-card::before {
|
|
|
|
|
|
content: '';
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
top: 0;
|
|
|
|
|
|
left: 0;
|
|
|
|
|
|
right: 0;
|
|
|
|
|
|
bottom: 0;
|
|
|
|
|
|
opacity: 0.1;
|
|
|
|
|
|
background: linear-gradient(135deg, rgba(255,255,255,0.3) 0%, transparent 100%);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.stat-card.total {
|
|
|
|
|
|
background: linear-gradient(135deg, #667EEA 0%, #764BA2 100%);
|
|
|
|
|
|
box-shadow: 0 4rpx 16rpx rgba(102, 126, 234, 0.3);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.stat-card.empty {
|
|
|
|
|
|
background: linear-gradient(135deg, #11998E 0%, #38EF7D 100%);
|
|
|
|
|
|
box-shadow: 0 4rpx 16rpx rgba(17, 153, 142, 0.3);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.stat-card.rented {
|
|
|
|
|
|
background: linear-gradient(135deg, #4FACFE 0%, #00F2FE 100%);
|
|
|
|
|
|
box-shadow: 0 4rpx 16rpx rgba(79, 172, 254, 0.3);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.stat-card.soon-expire {
|
|
|
|
|
|
background: linear-gradient(135deg, #FA709A 0%, #FEE140 100%);
|
|
|
|
|
|
box-shadow: 0 4rpx 16rpx rgba(250, 112, 154, 0.3);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.stat-icon {
|
|
|
|
|
|
margin-bottom: 8rpx;
|
|
|
|
|
|
opacity: 0.9;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.stat-content {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.stat-number {
|
|
|
|
|
|
font-size: 36rpx;
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
color: #FFFFFF;
|
|
|
|
|
|
line-height: 1.2;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.stat-label {
|
|
|
|
|
|
font-size: 22rpx;
|
|
|
|
|
|
color: rgba(255, 255, 255, 0.9);
|
|
|
|
|
|
margin-top: 4rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 搜索栏 */
|
|
|
|
|
|
.search-bar {
|
|
|
|
|
|
background: #FFFFFF;
|
|
|
|
|
|
padding: 20rpx 32rpx;
|
|
|
|
|
|
border-bottom: 1rpx solid #EBEEF5;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.search-input-wrapper {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
background: #F5F7FA;
|
|
|
|
|
|
border-radius: 32rpx;
|
|
|
|
|
|
padding: 16rpx 24rpx;
|
|
|
|
|
|
gap: 16rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.search-input {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
|
color: #303133;
|
|
|
|
|
|
height: 40rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.search-input::placeholder {
|
|
|
|
|
|
color: #909399;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 公寓选择器 */
|
|
|
|
|
|
.apartment-section-header {
|
|
|
|
|
|
background: #FFFFFF;
|
|
|
|
|
|
border-bottom: 1rpx solid #EBEEF5;
|
|
|
|
|
|
padding: 16rpx 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.apartment-tabs {
|
2026-04-20 06:23:11 +00:00
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
padding: 0 32rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.tab-item {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
padding: 16rpx 32rpx;
|
|
|
|
|
|
margin-right: 16rpx;
|
2026-04-22 06:47:04 +00:00
|
|
|
|
border-radius: 12rpx;
|
|
|
|
|
|
background: #F5F7FA;
|
|
|
|
|
|
border: 2rpx solid transparent;
|
|
|
|
|
|
transition: all 0.2s;
|
2026-04-20 06:23:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.tab-item.active {
|
2026-04-22 06:47:04 +00:00
|
|
|
|
background: #ECF5FF;
|
|
|
|
|
|
border-color: #409EFF;
|
2026-04-20 06:23:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.tab-name {
|
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
|
font-weight: 600;
|
2026-04-22 06:47:04 +00:00
|
|
|
|
color: #303133;
|
2026-04-20 06:23:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.tab-count {
|
|
|
|
|
|
font-size: 22rpx;
|
2026-04-22 06:47:04 +00:00
|
|
|
|
color: #909399;
|
2026-04-20 06:23:11 +00:00
|
|
|
|
margin-top: 4rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-22 06:47:04 +00:00
|
|
|
|
/* 状态图例 */
|
|
|
|
|
|
.status-legend {
|
|
|
|
|
|
background: #FFFFFF;
|
|
|
|
|
|
padding: 20rpx 0;
|
|
|
|
|
|
border-bottom: 1rpx solid #EBEEF5;
|
2026-04-20 06:23:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-22 06:47:04 +00:00
|
|
|
|
.legend-scroll {
|
2026-04-20 06:23:11 +00:00
|
|
|
|
display: flex;
|
2026-04-22 06:47:04 +00:00
|
|
|
|
padding: 0 32rpx;
|
2026-04-20 06:23:11 +00:00
|
|
|
|
gap: 16rpx;
|
2026-04-22 06:47:04 +00:00
|
|
|
|
flex-wrap: wrap;
|
2026-04-20 06:23:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-22 06:47:04 +00:00
|
|
|
|
.legend-item {
|
2026-04-20 06:23:11 +00:00
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 8rpx;
|
|
|
|
|
|
padding: 12rpx 20rpx;
|
2026-04-22 06:47:04 +00:00
|
|
|
|
background: #F5F7FA;
|
|
|
|
|
|
border-radius: 24rpx;
|
|
|
|
|
|
border: 2rpx solid transparent;
|
|
|
|
|
|
transition: all 0.2s;
|
2026-04-20 06:23:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-22 06:47:04 +00:00
|
|
|
|
.legend-item.active {
|
|
|
|
|
|
background: #ECF5FF;
|
|
|
|
|
|
border-color: #409EFF;
|
2026-04-20 06:23:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-22 06:47:04 +00:00
|
|
|
|
.legend-dot {
|
|
|
|
|
|
width: 16rpx;
|
|
|
|
|
|
height: 16rpx;
|
2026-04-20 06:23:11 +00:00
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-22 06:47:04 +00:00
|
|
|
|
.legend-text {
|
|
|
|
|
|
font-size: 24rpx;
|
|
|
|
|
|
color: #606266;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.legend-count {
|
2026-04-20 06:23:11 +00:00
|
|
|
|
font-size: 22rpx;
|
2026-04-22 06:47:04 +00:00
|
|
|
|
color: #909399;
|
|
|
|
|
|
background: #FFFFFF;
|
|
|
|
|
|
padding: 2rpx 10rpx;
|
|
|
|
|
|
border-radius: 10rpx;
|
|
|
|
|
|
margin-left: 4rpx;
|
2026-04-20 06:23:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-22 06:47:04 +00:00
|
|
|
|
/* 房间列表 */
|
|
|
|
|
|
.room-list {
|
2026-04-20 06:23:11 +00:00
|
|
|
|
flex: 1;
|
|
|
|
|
|
padding: 24rpx 32rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.empty-state {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
padding: 120rpx 60rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.empty-icon {
|
|
|
|
|
|
margin-bottom: 32rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.empty-title {
|
|
|
|
|
|
font-size: 32rpx;
|
|
|
|
|
|
font-weight: 600;
|
2026-04-22 06:47:04 +00:00
|
|
|
|
color: #303133;
|
2026-04-20 06:23:11 +00:00
|
|
|
|
margin-bottom: 12rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.empty-desc {
|
|
|
|
|
|
font-size: 26rpx;
|
2026-04-22 06:47:04 +00:00
|
|
|
|
color: #909399;
|
2026-04-20 06:23:11 +00:00
|
|
|
|
margin-bottom: 40rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.empty-btn {
|
2026-04-22 06:47:04 +00:00
|
|
|
|
background: linear-gradient(135deg, #409EFF 0%, #66B1FF 100%);
|
2026-04-20 06:23:11 +00:00
|
|
|
|
color: #FFFFFF;
|
2026-04-22 06:47:04 +00:00
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
padding: 16rpx 40rpx;
|
|
|
|
|
|
border-radius: 12rpx;
|
2026-04-20 06:23:11 +00:00
|
|
|
|
border: none;
|
2026-04-22 06:47:04 +00:00
|
|
|
|
box-shadow: 0 4rpx 12rpx rgba(64, 158, 255, 0.3);
|
|
|
|
|
|
line-height: 1.5;
|
2026-04-20 06:23:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-22 06:47:04 +00:00
|
|
|
|
/* 房间卡片网格 */
|
|
|
|
|
|
.room-grid {
|
2026-04-20 06:23:11 +00:00
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: repeat(2, 1fr);
|
|
|
|
|
|
gap: 20rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.room-card {
|
|
|
|
|
|
background: #FFFFFF;
|
2026-04-22 06:47:04 +00:00
|
|
|
|
border: 2rpx solid #EBEEF5;
|
|
|
|
|
|
border-radius: 16rpx;
|
2026-04-20 06:23:11 +00:00
|
|
|
|
padding: 24rpx;
|
2026-04-22 06:47:04 +00:00
|
|
|
|
transition: all 0.2s;
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
min-height: 160rpx;
|
2026-04-20 06:23:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-22 06:47:04 +00:00
|
|
|
|
.room-card:active {
|
|
|
|
|
|
transform: scale(0.98);
|
2026-04-20 06:23:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-22 06:47:04 +00:00
|
|
|
|
/* 状态样式 */
|
|
|
|
|
|
.room-card.status-empty {
|
|
|
|
|
|
border-color: #67C23A;
|
|
|
|
|
|
background: #F0F9FF;
|
2026-04-20 06:23:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-22 06:47:04 +00:00
|
|
|
|
.room-card.status-rented {
|
|
|
|
|
|
border-color: #409EFF;
|
|
|
|
|
|
background: #ECF5FF;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.room-card.status-soon-expire {
|
|
|
|
|
|
border-color: #E6A23C;
|
|
|
|
|
|
background: #FDF6EC;
|
2026-04-20 06:23:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-22 06:47:04 +00:00
|
|
|
|
.room-card.status-expired {
|
|
|
|
|
|
border-color: #F56C6C;
|
2026-04-20 06:23:11 +00:00
|
|
|
|
background: #FEF0F0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-22 06:47:04 +00:00
|
|
|
|
.room-card.status-reserved {
|
|
|
|
|
|
border-color: #909399;
|
|
|
|
|
|
background: #F4F4F5;
|
2026-04-20 06:23:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.room-number {
|
2026-04-22 06:47:04 +00:00
|
|
|
|
font-size: 32rpx;
|
2026-04-20 06:23:11 +00:00
|
|
|
|
font-weight: 700;
|
2026-04-22 06:47:04 +00:00
|
|
|
|
color: #303133;
|
|
|
|
|
|
margin-bottom: 12rpx;
|
|
|
|
|
|
display: block;
|
2026-04-20 06:23:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.room-info {
|
|
|
|
|
|
font-size: 24rpx;
|
2026-04-22 06:47:04 +00:00
|
|
|
|
color: #606266;
|
|
|
|
|
|
line-height: 1.6;
|
2026-04-20 06:23:11 +00:00
|
|
|
|
display: flex;
|
2026-04-22 06:47:04 +00:00
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 4rpx;
|
2026-04-20 06:23:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-22 06:47:04 +00:00
|
|
|
|
.room-price {
|
2026-04-20 06:23:11 +00:00
|
|
|
|
color: #F56C6C;
|
2026-04-22 06:47:04 +00:00
|
|
|
|
font-weight: 600;
|
2026-04-20 06:23:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-22 06:47:04 +00:00
|
|
|
|
.room-tenant {
|
|
|
|
|
|
color: #409EFF;
|
2026-04-20 06:23:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-22 06:47:04 +00:00
|
|
|
|
.room-date {
|
|
|
|
|
|
color: #909399;
|
2026-04-20 06:23:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-22 06:47:04 +00:00
|
|
|
|
.room-status-tag {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
top: 20rpx;
|
|
|
|
|
|
right: 20rpx;
|
|
|
|
|
|
font-size: 20rpx;
|
|
|
|
|
|
padding: 4rpx 12rpx;
|
|
|
|
|
|
border-radius: 20rpx;
|
|
|
|
|
|
background: rgba(255, 255, 255, 0.9);
|
|
|
|
|
|
color: #606266;
|
2026-04-20 06:23:11 +00:00
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-22 06:47:04 +00:00
|
|
|
|
/* 底部统计栏 */
|
|
|
|
|
|
.bottom-stats {
|
|
|
|
|
|
background: #FFFFFF;
|
|
|
|
|
|
border-top: 1rpx solid #EBEEF5;
|
|
|
|
|
|
border-radius: 16rpx;
|
|
|
|
|
|
margin: 24rpx 0;
|
|
|
|
|
|
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
|
2026-04-20 06:23:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-22 06:47:04 +00:00
|
|
|
|
.stats-content {
|
2026-04-20 06:23:11 +00:00
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: space-around;
|
2026-04-22 06:47:04 +00:00
|
|
|
|
align-items: center;
|
2026-04-20 06:23:11 +00:00
|
|
|
|
padding: 20rpx 32rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.stat-item {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
2026-04-22 06:47:04 +00:00
|
|
|
|
flex: 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.stat-item.highlight {
|
|
|
|
|
|
background: linear-gradient(135deg, #ECF5FF 0%, #F5F7FA 100%);
|
|
|
|
|
|
border-radius: 12rpx;
|
|
|
|
|
|
padding: 12rpx 0;
|
|
|
|
|
|
margin: -12rpx 0;
|
2026-04-20 06:23:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-22 06:47:04 +00:00
|
|
|
|
.stat-divider {
|
|
|
|
|
|
width: 2rpx;
|
|
|
|
|
|
height: 40rpx;
|
|
|
|
|
|
background: #EBEEF5;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.bottom-stats .stat-value {
|
2026-04-20 06:23:11 +00:00
|
|
|
|
font-size: 32rpx;
|
|
|
|
|
|
font-weight: 700;
|
2026-04-22 06:47:04 +00:00
|
|
|
|
color: #303133;
|
2026-04-20 06:23:11 +00:00
|
|
|
|
margin-bottom: 4rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-22 06:47:04 +00:00
|
|
|
|
.bottom-stats .stat-label {
|
2026-04-20 06:23:11 +00:00
|
|
|
|
font-size: 22rpx;
|
|
|
|
|
|
color: #909399;
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|