通知统计
This commit is contained in:
parent
75bc71b532
commit
4576eef238
|
|
@ -14,6 +14,27 @@
|
||||||
<i class="el-icon-s-fold" style="font-size: 24px; color: white;"></i>
|
<i class="el-icon-s-fold" style="font-size: 24px; color: white;"></i>
|
||||||
</el-button>
|
</el-button>
|
||||||
<h1 class="app-title">租房管理系统</h1>
|
<h1 class="app-title">租房管理系统</h1>
|
||||||
|
<!-- 通知模块 -->
|
||||||
|
<el-dropdown trigger="click" @command="handleNotificationCommand" class="notification-dropdown">
|
||||||
|
<div class="notification-icon">
|
||||||
|
<i class="el-icon-bell" style="font-size: 20px; color: white;"></i>
|
||||||
|
<span v-if="notificationCount > 0" class="notification-badge">{{ notificationCount }}</span>
|
||||||
|
</div>
|
||||||
|
<el-dropdown-menu slot="dropdown">
|
||||||
|
<el-dropdown-item command="viewAll">
|
||||||
|
查看所有通知
|
||||||
|
<span v-if="notificationCount > 0" class="notification-badge-small">{{ notificationCount }}</span>
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item divided command="expired">
|
||||||
|
已到期房间
|
||||||
|
<span v-if="expiredCount > 0" class="notification-badge-small">{{ expiredCount }}</span>
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="soonExpire">
|
||||||
|
即将到期房间
|
||||||
|
<span v-if="soonExpireCount > 0" class="notification-badge-small">{{ soonExpireCount }}</span>
|
||||||
|
</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</el-dropdown>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<el-dropdown @command="handleUserCommand">
|
<el-dropdown @command="handleUserCommand">
|
||||||
|
|
@ -129,6 +150,7 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { getUserInfo, getUserMenus, clearAuth } from '@/utils/auth'
|
import { getUserInfo, getUserMenus, clearAuth } from '@/utils/auth'
|
||||||
|
import { statisticsApi } from '@/api/api'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'MainLayout',
|
name: 'MainLayout',
|
||||||
|
|
@ -139,7 +161,11 @@ export default {
|
||||||
drawerVisible: false,
|
drawerVisible: false,
|
||||||
drawerWidth: '70%',
|
drawerWidth: '70%',
|
||||||
username: '',
|
username: '',
|
||||||
menuList: []
|
menuList: [],
|
||||||
|
// 通知相关数据
|
||||||
|
expiredCount: 0,
|
||||||
|
soonExpireCount: 0,
|
||||||
|
notificationShown: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|
@ -148,6 +174,8 @@ export default {
|
||||||
this.loadUserInfo()
|
this.loadUserInfo()
|
||||||
this.loadMenus()
|
this.loadMenus()
|
||||||
this.setActiveMenu()
|
this.setActiveMenu()
|
||||||
|
// 获取通知数据
|
||||||
|
this.getNotificationData()
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
window.removeEventListener('resize', this.checkDevice)
|
window.removeEventListener('resize', this.checkDevice)
|
||||||
|
|
@ -155,6 +183,12 @@ export default {
|
||||||
watch: {
|
watch: {
|
||||||
'$route': 'setActiveMenu'
|
'$route': 'setActiveMenu'
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
// 计算总通知数
|
||||||
|
notificationCount() {
|
||||||
|
return this.expiredCount + this.soonExpireCount
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
checkDevice() {
|
checkDevice() {
|
||||||
const width = window.innerWidth
|
const width = window.innerWidth
|
||||||
|
|
@ -258,6 +292,81 @@ export default {
|
||||||
clearAuth()
|
clearAuth()
|
||||||
this.$message.success('退出成功')
|
this.$message.success('退出成功')
|
||||||
this.$router.push('/login')
|
this.$router.push('/login')
|
||||||
|
},
|
||||||
|
// 获取通知数据
|
||||||
|
async getNotificationData() {
|
||||||
|
try {
|
||||||
|
// 调用真实API获取Dashboard统计数据
|
||||||
|
const response = await statisticsApi.getDashboardStats()
|
||||||
|
const data = response.data || response
|
||||||
|
|
||||||
|
// 更新通知数据
|
||||||
|
this.expiredCount = data.expiredRoomCount || 0
|
||||||
|
this.soonExpireCount = data.soonExpireRoomCount || 0
|
||||||
|
|
||||||
|
// 登录成功后提示(只显示一次)
|
||||||
|
if (!this.notificationShown && (this.expiredCount > 0 || this.soonExpireCount > 0)) {
|
||||||
|
this.showNotification()
|
||||||
|
this.notificationShown = true
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取通知数据失败:', error)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 显示通知
|
||||||
|
showNotification() {
|
||||||
|
// 分别显示已到期和即将到期的通知
|
||||||
|
if (this.expiredCount > 0) {
|
||||||
|
this.$notify({
|
||||||
|
title: '已到期提醒',
|
||||||
|
message: `有 ${this.expiredCount} 个房间已到期`,
|
||||||
|
type: 'error',
|
||||||
|
position: 'top-right',
|
||||||
|
offset: 0,
|
||||||
|
onClick: () => this.handleNotificationClick('expired')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.soonExpireCount > 0) {
|
||||||
|
this.$notify({
|
||||||
|
title: '即将到期提醒',
|
||||||
|
message: `有 ${this.soonExpireCount} 个房间即将到期`,
|
||||||
|
type: 'warning',
|
||||||
|
position: 'top-right',
|
||||||
|
offset: this.expiredCount > 0 ? 80 : 0,
|
||||||
|
onClick: () => this.handleNotificationClick('soonExpire')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 处理通知点击
|
||||||
|
handleNotificationClick(type) {
|
||||||
|
// 跳转到租房管理页面并带上相应参数
|
||||||
|
const timestamp = Date.now()
|
||||||
|
switch (type) {
|
||||||
|
case 'expired':
|
||||||
|
this.$router.push(`/rental/list?subStatus=expired&t=${timestamp}`)
|
||||||
|
break
|
||||||
|
case 'soonExpire':
|
||||||
|
this.$router.push(`/rental/list?subStatus=soon_expire&t=${timestamp}`)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 处理通知命令
|
||||||
|
handleNotificationCommand(command) {
|
||||||
|
switch (command) {
|
||||||
|
case 'viewAll':
|
||||||
|
// 查看所有通知
|
||||||
|
this.$router.push('/rental/list')
|
||||||
|
break
|
||||||
|
case 'expired':
|
||||||
|
// 查看已到期房间
|
||||||
|
this.$router.push('/rental/list?subStatus=expired')
|
||||||
|
break
|
||||||
|
case 'soonExpire':
|
||||||
|
// 查看即将到期房间
|
||||||
|
this.$router.push('/rental/list?subStatus=soon_expire')
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -342,6 +451,53 @@ export default {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 通知模块样式 */
|
||||||
|
.notification-dropdown {
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-icon {
|
||||||
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 50%;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-icon:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-badge {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
background-color: #f56c6c;
|
||||||
|
color: white;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
min-width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
line-height: 18px;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 9px;
|
||||||
|
padding: 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-badge-small {
|
||||||
|
background-color: #f56c6c;
|
||||||
|
color: white;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
min-width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
line-height: 16px;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 0 4px;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
/* 侧边栏样式 */
|
/* 侧边栏样式 */
|
||||||
.app-aside {
|
.app-aside {
|
||||||
background-color: #f0f2f5;
|
background-color: #f0f2f5;
|
||||||
|
|
|
||||||
|
|
@ -123,14 +123,46 @@
|
||||||
class="stats-table"
|
class="stats-table"
|
||||||
>
|
>
|
||||||
<el-table-column prop="apartment" label="公寓" min-width="100"></el-table-column>
|
<el-table-column prop="apartment" label="公寓" min-width="100"></el-table-column>
|
||||||
<el-table-column prop="empty" label="空房" width="70"></el-table-column>
|
<el-table-column prop="empty" label="空房" width="70">
|
||||||
<el-table-column prop="reserved" label="预订" width="70"></el-table-column>
|
<template slot-scope="scope">
|
||||||
<el-table-column prop="rented" label="在租" width="70"></el-table-column>
|
<span class="clickable-cell" @click="navigateToRentalList(scope.row.apartmentId, 'status', 'empty')">{{ scope.row.empty }}</span>
|
||||||
<el-table-column prop="soon_expire" label="即将到期" width="90"></el-table-column>
|
</template>
|
||||||
<el-table-column prop="expired" label="已到期" width="70"></el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="cleaning" label="打扫中" width="70"></el-table-column>
|
<el-table-column prop="reserved" label="预订" width="70">
|
||||||
<el-table-column prop="maintenance" label="维修中" width="70"></el-table-column>
|
<template slot-scope="scope">
|
||||||
<el-table-column prop="total" label="总数" width="70"></el-table-column>
|
<span class="clickable-cell" @click="navigateToRentalList(scope.row.apartmentId, 'status', 'reserved')">{{ scope.row.reserved }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="rented" label="在租" width="70">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span class="clickable-cell" @click="navigateToRentalList(scope.row.apartmentId, 'status', 'rented')">{{ scope.row.rented }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="soon_expire" label="即将到期" width="90">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span class="clickable-cell" @click="navigateToRentalList(scope.row.apartmentId, 'subStatus', 'soon_expire')">{{ scope.row.soon_expire }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="expired" label="已到期" width="70">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span class="clickable-cell" @click="navigateToRentalList(scope.row.apartmentId, 'subStatus', 'expired')">{{ scope.row.expired }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="cleaning" label="打扫中" width="70">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span class="clickable-cell" @click="navigateToRentalList(scope.row.apartmentId, 'otherStatus', 'cleaning')">{{ scope.row.cleaning }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="maintenance" label="维修中" width="70">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span class="clickable-cell" @click="navigateToRentalList(scope.row.apartmentId, 'otherStatus', 'maintenance')">{{ scope.row.maintenance }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="total" label="总数" width="70">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span class="clickable-cell" @click="navigateToRentalList(scope.row.apartmentId)">{{ scope.row.total }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|
@ -215,6 +247,17 @@ export default {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return sums;
|
return sums;
|
||||||
|
},
|
||||||
|
navigateToRentalList(apartmentId, paramType, paramValue) {
|
||||||
|
const timestamp = Date.now()
|
||||||
|
let query = `t=${timestamp}`
|
||||||
|
if (apartmentId) {
|
||||||
|
query += `&apartmentId=${apartmentId}`
|
||||||
|
}
|
||||||
|
if (paramType && paramValue) {
|
||||||
|
query += `&${paramType}=${paramValue}`
|
||||||
|
}
|
||||||
|
this.$router.push(`/rental/list?${query}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -291,6 +334,17 @@ export default {
|
||||||
min-width: 800px;
|
min-width: 800px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.clickable-cell {
|
||||||
|
cursor: pointer;
|
||||||
|
color: #409EFF;
|
||||||
|
transition: color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clickable-cell:hover {
|
||||||
|
color: #66b1ff;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
/* 移动端适配 */
|
/* 移动端适配 */
|
||||||
@media screen and (max-width: 768px) {
|
@media screen and (max-width: 768px) {
|
||||||
.welcome-card {
|
.welcome-card {
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,13 @@
|
||||||
<el-table-column prop="endDate" label="结束日期" min-width="100"></el-table-column>
|
<el-table-column prop="endDate" label="结束日期" min-width="100"></el-table-column>
|
||||||
<el-table-column prop="rent" label="租金(元)" min-width="100"></el-table-column>
|
<el-table-column prop="rent" label="租金(元)" min-width="100"></el-table-column>
|
||||||
<el-table-column prop="deposit" label="押金(元)" min-width="90"></el-table-column>
|
<el-table-column prop="deposit" label="押金(元)" min-width="90"></el-table-column>
|
||||||
|
<el-table-column prop="refundedDeposit" label="已退押金(元)" min-width="100">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span :class="{ 'text-success': scope.row.refundedDeposit > 0 }">
|
||||||
|
{{ scope.row.refundedDeposit || 0 }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column prop="remark" label="备注" min-width="120"></el-table-column>
|
<el-table-column prop="remark" label="备注" min-width="120"></el-table-column>
|
||||||
<el-table-column prop="status" label="状态" min-width="80">
|
<el-table-column prop="status" label="状态" min-width="80">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
|
|
@ -181,6 +188,9 @@
|
||||||
<el-form-item label="押金(元)" prop="deposit">
|
<el-form-item label="押金(元)" prop="deposit">
|
||||||
<el-input v-model.number="rentalForm.deposit" placeholder="请输入押金"></el-input>
|
<el-input v-model.number="rentalForm.deposit" placeholder="请输入押金"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="已退押金(元)" prop="refundedDeposit">
|
||||||
|
<el-input v-model.number="rentalForm.refundedDeposit" placeholder="请输入已退押金"></el-input>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item label="状态" prop="status">
|
<el-form-item label="状态" prop="status">
|
||||||
<el-select v-model="rentalForm.status" placeholder="请选择状态" style="width: 100%">
|
<el-select v-model="rentalForm.status" placeholder="请选择状态" style="width: 100%">
|
||||||
<el-option label="有效" value="active"></el-option>
|
<el-option label="有效" value="active"></el-option>
|
||||||
|
|
@ -258,6 +268,7 @@ export default {
|
||||||
endDate: '',
|
endDate: '',
|
||||||
rent: '',
|
rent: '',
|
||||||
deposit: '',
|
deposit: '',
|
||||||
|
refundedDeposit: '',
|
||||||
status: 'active',
|
status: 'active',
|
||||||
remark: ''
|
remark: ''
|
||||||
},
|
},
|
||||||
|
|
@ -328,9 +339,8 @@ export default {
|
||||||
const roomResponse = await roomApi.getById(roomId)
|
const roomResponse = await roomApi.getById(roomId)
|
||||||
this.room = roomResponse
|
this.room = roomResponse
|
||||||
await this.loadRentalHistory()
|
await this.loadRentalHistory()
|
||||||
if (this.activeTab === 'water') {
|
// 无论当前tab是什么,都加载水费数据,以便退房时检查
|
||||||
await this.loadWaterBills()
|
await this.loadWaterBills()
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.$message.error('加载数据失败')
|
this.$message.error('加载数据失败')
|
||||||
} finally {
|
} finally {
|
||||||
|
|
@ -395,10 +405,6 @@ export default {
|
||||||
case 'empty': return ''
|
case 'empty': return ''
|
||||||
case 'reserved': return 'warning'
|
case 'reserved': return 'warning'
|
||||||
case 'rented': return 'success'
|
case 'rented': return 'success'
|
||||||
case 'soon_expire': return 'warning'
|
|
||||||
case 'expired': return 'danger'
|
|
||||||
case 'cleaning': return 'info'
|
|
||||||
case 'maintenance': return 'danger'
|
|
||||||
default: return ''
|
default: return ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -407,10 +413,6 @@ export default {
|
||||||
case 'empty': return '空房'
|
case 'empty': return '空房'
|
||||||
case 'reserved': return '预订'
|
case 'reserved': return '预订'
|
||||||
case 'rented': return '在租'
|
case 'rented': return '在租'
|
||||||
case 'soon_expire': return '即将到期'
|
|
||||||
case 'expired': return '到期'
|
|
||||||
case 'cleaning': return '打扫中'
|
|
||||||
case 'maintenance': return '维修中'
|
|
||||||
default: return status
|
default: return status
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -498,28 +500,66 @@ export default {
|
||||||
}).catch(() => {})
|
}).catch(() => {})
|
||||||
},
|
},
|
||||||
async handleCheckout() {
|
async handleCheckout() {
|
||||||
|
console.log('开始退房操作')
|
||||||
if (!this.room.id) {
|
if (!this.room.id) {
|
||||||
this.$message.error('房间信息加载失败,无法进行退房操作')
|
this.$message.error('房间信息加载失败,无法进行退房操作')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$confirm('确定要退房吗?', '提示', {
|
const roomId = this.$route.params.id
|
||||||
|
console.log('房间ID:', roomId)
|
||||||
|
const rental = this.rentalHistory.find(r => r.roomId == roomId && r.status === 'active')
|
||||||
|
console.log('找到的租房记录:', rental)
|
||||||
|
|
||||||
|
if (!rental) {
|
||||||
|
this.$message.error('未找到活跃的租房记录')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查押金情况
|
||||||
|
let depositMessage = ''
|
||||||
|
console.log('押金金额:', rental.deposit)
|
||||||
|
console.log('已退押金:', rental.refundedDeposit)
|
||||||
|
const deposit = parseFloat(rental.deposit) || 0
|
||||||
|
const refundedDeposit = parseFloat(rental.refundedDeposit) || 0
|
||||||
|
console.log('处理后的押金:', deposit)
|
||||||
|
console.log('处理后的已退押金:', refundedDeposit)
|
||||||
|
if (deposit > 0 && refundedDeposit === 0) {
|
||||||
|
depositMessage = `押金 ${deposit} 元尚未退还,`
|
||||||
|
console.log('押金提示信息:', depositMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查水费情况
|
||||||
|
let waterMessage = ''
|
||||||
|
console.log('水费列表:', this.waterBills)
|
||||||
|
const pendingWaterBills = (this.waterBills || []).filter(bill =>
|
||||||
|
(bill.status === 'unbilled' || bill.status === 'unpaid')
|
||||||
|
)
|
||||||
|
console.log('未处理水费:', pendingWaterBills)
|
||||||
|
|
||||||
|
if (pendingWaterBills.length > 0) {
|
||||||
|
const totalWaterAmount = pendingWaterBills.reduce((sum, bill) => sum + (bill.amount || 0), 0)
|
||||||
|
waterMessage = `存在 ${pendingWaterBills.length} 笔未处理水费,总金额 ${totalWaterAmount} 元,`
|
||||||
|
console.log('水费提示信息:', waterMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成确认信息
|
||||||
|
let confirmMessage = '确定要退房吗?'
|
||||||
|
if (depositMessage || waterMessage) {
|
||||||
|
confirmMessage = `注意:${depositMessage}${waterMessage}确定要退房吗?`
|
||||||
|
}
|
||||||
|
console.log('最终确认信息:', confirmMessage)
|
||||||
|
|
||||||
|
this.$confirm(confirmMessage, '提示', {
|
||||||
confirmButtonText: '确定',
|
confirmButtonText: '确定',
|
||||||
cancelButtonText: '取消',
|
cancelButtonText: '取消',
|
||||||
type: 'warning'
|
type: 'warning'
|
||||||
}).then(async () => {
|
}).then(async () => {
|
||||||
try {
|
try {
|
||||||
const roomId = this.$route.params.id
|
await rentalApi.update(rental.id, { status: 'expired' })
|
||||||
const rental = this.rentalHistory.find(r => r.roomId == roomId && r.status === 'active')
|
await roomApi.update(roomId, { status: 'empty', subStatus: 'normal' })
|
||||||
|
this.$message.success('退房成功')
|
||||||
if (rental) {
|
this.loadData()
|
||||||
await rentalApi.update(rental.id, { status: 'expired' })
|
|
||||||
await roomApi.update(roomId, { status: 'empty' })
|
|
||||||
this.$message.success('退房成功')
|
|
||||||
this.loadData()
|
|
||||||
} else {
|
|
||||||
this.$message.error('未找到活跃的租房记录')
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('退房失败:', error)
|
console.error('退房失败:', error)
|
||||||
this.$message.error('退房失败:' + (error.message || '未知错误'))
|
this.$message.error('退房失败:' + (error.message || '未知错误'))
|
||||||
|
|
@ -679,8 +719,42 @@ export default {
|
||||||
this.$message.success('租赁记录添加成功')
|
this.$message.success('租赁记录添加成功')
|
||||||
|
|
||||||
if (this.renewingRentalId) {
|
if (this.renewingRentalId) {
|
||||||
await rentalApi.update(this.renewingRentalId, { status: 'expired' })
|
// 直接获取原租赁记录的完整信息
|
||||||
this.$message.success('原租赁记录状态已更新为已到期')
|
try {
|
||||||
|
const response = await rentalApi.getById(this.renewingRentalId)
|
||||||
|
const oldRental = response
|
||||||
|
|
||||||
|
if (oldRental) {
|
||||||
|
console.log('原租赁记录:', oldRental)
|
||||||
|
const deposit = parseFloat(oldRental.deposit) || 0
|
||||||
|
if (deposit > 0) {
|
||||||
|
// 更新原租赁记录:状态改为已到期,押金设为0,添加备注
|
||||||
|
const newRemark = (oldRental.remark ? oldRental.remark + ';' : '') + `押金${deposit}元已转移至下一租赁周期`
|
||||||
|
console.log('新备注:', newRemark)
|
||||||
|
|
||||||
|
await rentalApi.update(this.renewingRentalId, {
|
||||||
|
status: 'expired',
|
||||||
|
deposit: 0,
|
||||||
|
refundedDeposit: 0,
|
||||||
|
remark: newRemark
|
||||||
|
})
|
||||||
|
this.$message.success('原租赁记录状态已更新为已到期,押金已转移')
|
||||||
|
} else {
|
||||||
|
// 押金为0,只更新状态
|
||||||
|
await rentalApi.update(this.renewingRentalId, { status: 'expired' })
|
||||||
|
this.$message.success('原租赁记录状态已更新为已到期')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 未找到原租赁记录,只更新状态
|
||||||
|
await rentalApi.update(this.renewingRentalId, { status: 'expired' })
|
||||||
|
this.$message.success('原租赁记录状态已更新为已到期')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取原租赁记录失败:', error)
|
||||||
|
// 即使获取失败,也要更新状态
|
||||||
|
await rentalApi.update(this.renewingRentalId, { status: 'expired' })
|
||||||
|
this.$message.success('原租赁记录状态已更新为已到期')
|
||||||
|
}
|
||||||
this.renewingRentalId = null
|
this.renewingRentalId = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -811,6 +885,11 @@ export default {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.text-success {
|
||||||
|
color: #67c23a;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
/* 移动端适配 */
|
/* 移动端适配 */
|
||||||
@media screen and (max-width: 768px) {
|
@media screen and (max-width: 768px) {
|
||||||
.card-header {
|
.card-header {
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,16 @@ export default {
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
paginationLayout() {
|
paginationLayout() {
|
||||||
return this.isMobile ? 'prev, pager, next' : 'total, sizes, prev, pager, next, jumper'
|
return this.isMobile ? 'prev, pager, next' : 'total, sizes, prev, pager, next'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
'$route': {
|
||||||
|
handler() {
|
||||||
|
this.initFromQuery()
|
||||||
|
this.loadData()
|
||||||
|
},
|
||||||
|
deep: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
<el-table-column prop="status" label="状态" min-width="100"></el-table-column>
|
<el-table-column prop="status" label="状态" min-width="100"></el-table-column>
|
||||||
<el-table-column prop="count" label="数量" min-width="80">
|
<el-table-column prop="count" label="数量" min-width="80">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<span class="count-value">{{ scope.row.count }}</span>
|
<span class="count-value clickable-cell" @click="handleCountClick(scope.row)">{{ scope.row.count }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="percentage" label="占比" min-width="100">
|
<el-table-column prop="percentage" label="占比" min-width="100">
|
||||||
|
|
@ -66,6 +66,34 @@ export default {
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.$message.error('加载房间状态统计数据失败')
|
this.$message.error('加载房间状态统计数据失败')
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
handleCountClick(row) {
|
||||||
|
const timestamp = Date.now()
|
||||||
|
let query = `t=${timestamp}`
|
||||||
|
|
||||||
|
// 根据状态映射到对应的查询参数
|
||||||
|
const statusMap = {
|
||||||
|
'空房': 'empty',
|
||||||
|
'预订': 'reserved',
|
||||||
|
'在租': 'rented',
|
||||||
|
'即将到期': 'soon_expire',
|
||||||
|
'已到期': 'expired',
|
||||||
|
'打扫中': 'cleaning',
|
||||||
|
'维修中': 'maintenance'
|
||||||
|
}
|
||||||
|
|
||||||
|
const statusKey = statusMap[row.status]
|
||||||
|
if (statusKey) {
|
||||||
|
if (['empty', 'reserved', 'rented'].includes(statusKey)) {
|
||||||
|
query += `&status=${statusKey}`
|
||||||
|
} else if (['soon_expire', 'expired'].includes(statusKey)) {
|
||||||
|
query += `&subStatus=${statusKey}`
|
||||||
|
} else {
|
||||||
|
query += `&otherStatus=${statusKey}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$router.push(`/rental/list?${query}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -101,6 +129,16 @@ export default {
|
||||||
color: #409EFF;
|
color: #409EFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.clickable-cell {
|
||||||
|
cursor: pointer;
|
||||||
|
transition: color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clickable-cell:hover {
|
||||||
|
color: #66b1ff;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
.total-count {
|
.total-count {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,22 @@
|
||||||
<span class="amount-value">¥{{ scope.row.amount }}</span>
|
<span class="amount-value">¥{{ scope.row.amount }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column prop="depositReceived" label="已收押金(元)" min-width="150">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span class="amount-value">¥{{ scope.row.depositReceived || 0 }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="depositRefunded" label="已退押金(元)" min-width="150">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span class="amount-value">¥{{ scope.row.depositRefunded || 0 }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
</div>
|
</div>
|
||||||
<div class="total-amount">
|
<div class="total-amount">
|
||||||
<el-tag size="medium" type="primary">总租金收入:{{ totalAmount }} 元</el-tag>
|
<el-tag size="medium" type="primary">总租金收入:{{ totalAmount }} 元</el-tag>
|
||||||
|
<el-tag size="medium" type="success" style="margin-left: 10px;">已收押金:{{ totalDepositReceived }} 元</el-tag>
|
||||||
|
<el-tag size="medium" type="info" style="margin-left: 10px;">已退押金:{{ totalDepositRefunded }} 元</el-tag>
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -39,6 +51,12 @@ export default {
|
||||||
computed: {
|
computed: {
|
||||||
totalAmount() {
|
totalAmount() {
|
||||||
return this.rentData.reduce((sum, item) => sum + item.amount, 0)
|
return this.rentData.reduce((sum, item) => sum + item.amount, 0)
|
||||||
|
},
|
||||||
|
totalDepositReceived() {
|
||||||
|
return this.rentData.reduce((sum, item) => sum + (item.depositReceived || 0), 0)
|
||||||
|
},
|
||||||
|
totalDepositRefunded() {
|
||||||
|
return this.rentData.reduce((sum, item) => sum + (item.depositRefunded || 0), 0)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue