rentease-web/src/views/rental/Detail.vue

608 lines
22 KiB
Vue
Raw Normal View History

2026-03-02 12:29:23 +00:00
<template>
<div class="rental-detail">
<el-card>
<template slot="header">
<div class="card-header">
<span>房屋详情</span>
<div class="action-buttons">
<el-button v-if="room.status === 'empty'" type="primary" @click="handleRent">租房</el-button>
<el-button v-if="room.status === 'rented'" type="warning" @click="handleCheckout">退房</el-button>
<el-button v-if="room.status === 'empty'" type="info" @click="handleCleaning">打扫</el-button>
<el-button v-if="room.status === 'empty'" type="danger" @click="handleMaintenance">维修</el-button>
<el-button v-if="room.status === 'cleaning' || room.status === 'maintenance'" type="success" @click="handleComplete">完成</el-button>
<el-button type="primary" @click="goBack">返回</el-button>
</div>
</div>
</template>
<div v-loading="isLoading" class="room-info-section">
<h2>{{ room.apartmentName }} - {{ room.roomNumber }}</h2>
<div class="room-basic-info">
<div class="info-item">
<span class="label">面积:</span>
<span class="value">{{ room.area }}</span>
</div>
<div class="info-item">
<span class="label">租金:</span>
<span class="value">¥{{ room.price }}/</span>
</div>
<div class="info-item">
<span class="label">状态:</span>
<span class="value">
<el-tag :type="getStatusType(room.status)">{{ getStatusText(room.status) }}</el-tag>
</span>
</div>
<div class="info-item">
<span class="label">创建时间:</span>
<span class="value">{{ room.createTime }}</span>
</div>
</div>
</div>
<el-tabs v-model="activeTab">
<el-tab-pane label="租赁档案" name="rental">
<el-table :data="rentalHistory" style="width: 100%">
<el-table-column prop="tenantName" label="租客" width="120"></el-table-column>
<el-table-column prop="startDate" label="开始日期" width="150"></el-table-column>
<el-table-column prop="endDate" label="结束日期" width="150"></el-table-column>
<el-table-column prop="rent" label="租金(元/月)" width="120"></el-table-column>
<el-table-column prop="deposit" label="押金(元)" width="120"></el-table-column>
<el-table-column prop="remark" label="备注" min-width="150"></el-table-column>
<el-table-column prop="status" label="状态" width="100">
<template slot-scope="scope">
<el-tag :type="scope.row.status === 'active' ? 'success' : 'danger'">
{{ scope.row.status === 'active' ? '在租' : '已到期' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" width="180"></el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="水费记录" name="water">
<div class="section-header">
<el-button type="primary" size="small" @click="handleAddWaterBill">添加水费</el-button>
</div>
<el-table :data="waterBills" style="width: 100%">
<el-table-column prop="startDate" label="开始日期" width="150"></el-table-column>
<el-table-column prop="endDate" label="结束日期" width="150"></el-table-column>
<el-table-column prop="startReading" label="起始度数" width="120"></el-table-column>
<el-table-column prop="endReading" label="结束度数" width="120"></el-table-column>
<el-table-column prop="usage" label="用水量(吨)" width="120"></el-table-column>
<el-table-column prop="unitPrice" label="单价(元/吨)" width="120"></el-table-column>
<el-table-column prop="amount" label="费用(元)" width="100"></el-table-column>
<el-table-column prop="status" label="状态" width="100">
<template slot-scope="scope">
<el-tag :type="scope.row.status === 'paid' ? 'success' : 'warning'">
{{ scope.row.status === 'paid' ? '已支付' : '未支付' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="150">
<template slot-scope="scope">
<el-button type="primary" size="small" @click="handleEditWaterBill(scope.row)">编辑</el-button>
<el-button type="danger" size="small" @click="handleDeleteWaterBill(scope.row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="电费记录" name="electricity">
<div class="section-header">
<el-button type="primary" size="small" @click="handleAddElectricityBill">添加电费</el-button>
</div>
<el-table :data="electricityBills" style="width: 100%">
<el-table-column prop="startDate" label="开始日期" width="150"></el-table-column>
<el-table-column prop="endDate" label="结束日期" width="150"></el-table-column>
<el-table-column prop="startReading" label="起始度数" width="120"></el-table-column>
<el-table-column prop="endReading" label="结束度数" width="120"></el-table-column>
<el-table-column prop="usage" label="用电量(度)" width="120"></el-table-column>
<el-table-column prop="unitPrice" label="单价(元/度)" width="120"></el-table-column>
<el-table-column prop="amount" label="费用(元)" width="100"></el-table-column>
<el-table-column prop="status" label="状态" width="100">
<template slot-scope="scope">
<el-tag :type="scope.row.status === 'paid' ? 'success' : 'warning'">
{{ scope.row.status === 'paid' ? '已支付' : '未支付' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="150">
<template slot-scope="scope">
<el-button type="primary" size="small" @click="handleEditElectricityBill(scope.row)">编辑</el-button>
<el-button type="danger" size="small" @click="handleDeleteElectricityBill(scope.row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
</el-tabs>
<!-- 水费编辑对话框 -->
<el-dialog title="编辑水费" :visible.sync="waterBillDialogVisible" width="500px">
<el-form :model="waterBillForm" :rules="waterBillRules" ref="waterBillForm">
<el-form-item label="开始日期" prop="startDate">
<el-date-picker v-model="waterBillForm.startDate" type="date" placeholder="选择开始日期" style="width: 100%"></el-date-picker>
</el-form-item>
<el-form-item label="结束日期" prop="endDate">
<el-date-picker v-model="waterBillForm.endDate" type="date" placeholder="选择结束日期" style="width: 100%"></el-date-picker>
</el-form-item>
<el-form-item label="起始度数" prop="startReading">
<el-input v-model.number="waterBillForm.startReading" placeholder="请输入起始度数"></el-input>
</el-form-item>
<el-form-item label="结束度数" prop="endReading">
<el-input v-model.number="waterBillForm.endReading" placeholder="请输入结束度数"></el-input>
</el-form-item>
<el-form-item label="单价(元/吨)" prop="unitPrice">
<el-input v-model.number="waterBillForm.unitPrice" placeholder="请输入单价"></el-input>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="waterBillForm.status" placeholder="请选择状态">
<el-option label="未支付" value="unpaid"></el-option>
<el-option label="已支付" value="paid"></el-option>
</el-select>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="waterBillDialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleSaveWaterBill">保存</el-button>
</span>
</el-dialog>
<!-- 电费编辑对话框 -->
<el-dialog title="编辑电费" :visible.sync="electricityBillDialogVisible" width="500px">
<el-form :model="electricityBillForm" :rules="electricityBillRules" ref="electricityBillForm">
<el-form-item label="开始日期" prop="startDate">
<el-date-picker v-model="electricityBillForm.startDate" type="date" placeholder="选择开始日期" style="width: 100%"></el-date-picker>
</el-form-item>
<el-form-item label="结束日期" prop="endDate">
<el-date-picker v-model="electricityBillForm.endDate" type="date" placeholder="选择结束日期" style="width: 100%"></el-date-picker>
</el-form-item>
<el-form-item label="起始度数" prop="startReading">
<el-input v-model.number="electricityBillForm.startReading" placeholder="请输入起始度数"></el-input>
</el-form-item>
<el-form-item label="结束度数" prop="endReading">
<el-input v-model.number="electricityBillForm.endReading" placeholder="请输入结束度数"></el-input>
</el-form-item>
<el-form-item label="单价(元/度)" prop="unitPrice">
<el-input v-model.number="electricityBillForm.unitPrice" placeholder="请输入单价"></el-input>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="electricityBillForm.status" placeholder="请选择状态">
<el-option label="未支付" value="unpaid"></el-option>
<el-option label="已支付" value="paid"></el-option>
</el-select>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="electricityBillDialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleSaveElectricityBill">保存</el-button>
</span>
</el-dialog>
</el-card>
</div>
</template>
<script>
import { roomApi, rentalApi, apartmentApi, waterBillApi, electricityBillApi } from '../../api/api'
export default {
name: 'RentalDetail',
data() {
return {
room: {},
rentals: [],
apartments: [],
rentalHistory: [],
waterBills: [],
electricityBills: [],
isLoading: false,
activeTab: 'rental',
waterBillDialogVisible: false,
electricityBillDialogVisible: false,
waterBillForm: {
id: '',
roomId: '',
startDate: '',
endDate: '',
startReading: '',
endReading: '',
unitPrice: '',
status: 'unpaid'
},
electricityBillForm: {
id: '',
roomId: '',
startDate: '',
endDate: '',
startReading: '',
endReading: '',
unitPrice: '',
status: 'unpaid'
},
waterBillRules: {
startDate: [{ required: true, message: '请选择开始日期', trigger: 'change' }],
endDate: [{ required: true, message: '请选择结束日期', trigger: 'change' }],
startReading: [{ required: true, message: '请输入起始度数', trigger: 'blur' }],
endReading: [{ required: true, message: '请输入结束度数', trigger: 'blur' }],
unitPrice: [{ required: true, message: '请输入单价', trigger: 'blur' }],
status: [{ required: true, message: '请选择状态', trigger: 'change' }]
},
electricityBillRules: {
startDate: [{ required: true, message: '请选择开始日期', trigger: 'change' }],
endDate: [{ required: true, message: '请选择结束日期', trigger: 'change' }],
startReading: [{ required: true, message: '请输入起始度数', trigger: 'blur' }],
endReading: [{ required: true, message: '请输入结束度数', trigger: 'blur' }],
unitPrice: [{ required: true, message: '请输入单价', trigger: 'blur' }],
status: [{ required: true, message: '请选择状态', trigger: 'change' }]
}
}
},
mounted() {
this.loadData()
},
methods: {
async loadData() {
this.isLoading = true
try {
const roomId = this.$route.params.id
// 加载房间数据
const roomResponse = await roomApi.getById(roomId)
this.room = roomResponse
// 加载公寓数据
const apartmentsResponse = await apartmentApi.getAll()
this.apartments = apartmentsResponse.data || apartmentsResponse
// 添加公寓名称
if (this.room.apartmentId) {
const apartment = this.apartments.find(a => a.id == this.room.apartmentId)
this.room.apartmentName = apartment ? apartment.name : ''
}
// 加载租房数据
const rentalsResponse = await rentalApi.getAll()
this.rentals = rentalsResponse.data || rentalsResponse
// 加载水费数据
const waterBillsResponse = await waterBillApi.getAll({ roomId })
this.waterBills = waterBillsResponse.data || waterBillsResponse
// 加载电费数据
const electricityBillsResponse = await electricityBillApi.getAll({ roomId })
this.electricityBills = electricityBillsResponse.data || electricityBillsResponse
// 加载租赁历史
this.loadRentalHistory()
} catch (error) {
this.$message.error('加载数据失败')
} finally {
this.isLoading = false
}
},
loadRentalHistory() {
const roomId = this.$route.params.id
this.rentalHistory = this.rentals
.filter(r => r.roomId == roomId)
.map(rental => {
return {
...rental,
tenantName: rental.Tenant ? rental.Tenant.name : ''
}
})
},
getStatusType(status) {
switch (status) {
case 'empty': return ''
case 'rented': return 'success'
case 'soon_expire': return 'warning'
case 'expired': return 'danger'
case 'cleaning': return 'info'
case 'maintenance': return 'danger'
default: return ''
}
},
getStatusText(status) {
switch (status) {
case 'empty': return '空房'
case 'rented': return '在租'
case 'soon_expire': return '即将到期'
case 'expired': return '到期'
case 'cleaning': return '打扫中'
case 'maintenance': return '维修中'
default: return status
}
},
handleRent() {
this.$router.push(`/rental/add?roomId=${this.room.id}`)
},
async handleCheckout() {
this.$confirm('确定要退房吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
try {
// 找到当前房间的租房记录
const roomId = this.$route.params.id
const rental = this.rentals.find(r => r.roomId == roomId && r.status === 'active')
if (rental) {
// 更新租房记录状态
await rentalApi.update(rental.id, { status: 'expired' })
// 更新房间状态
await roomApi.update(roomId, { status: 'empty' })
this.$message.success('退房成功')
this.loadData()
} else {
this.$message.error('未找到活跃的租房记录')
}
} catch (error) {
this.$message.error('退房失败')
}
}).catch(() => {
// 取消退房
})
},
async handleCleaning() {
this.$confirm('确定要标记为打扫中吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'info'
}).then(async () => {
try {
const roomId = this.$route.params.id
await roomApi.update(roomId, { status: 'cleaning' })
this.$message.success('标记为打扫中成功')
this.loadData()
} catch (error) {
this.$message.error('操作失败')
}
}).catch(() => {
// 取消操作
})
},
async handleMaintenance() {
this.$confirm('确定要标记为维修中吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'danger'
}).then(async () => {
try {
const roomId = this.$route.params.id
await roomApi.update(roomId, { status: 'maintenance' })
this.$message.success('标记为维修中成功')
this.loadData()
} catch (error) {
this.$message.error('操作失败')
}
}).catch(() => {
// 取消操作
})
},
async handleComplete() {
this.$confirm('确定要完成打扫/维修并将状态改为空房吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'success'
}).then(async () => {
try {
const roomId = this.$route.params.id
await roomApi.update(roomId, { status: 'empty' })
this.$message.success('操作成功,房间状态已改为空房')
this.loadData()
} catch (error) {
this.$message.error('操作失败')
}
}).catch(() => {
// 取消操作
})
},
handleAddWaterBill() {
this.waterBillForm = {
id: '',
roomId: this.$route.params.id,
startDate: '',
endDate: '',
startReading: '',
endReading: '',
unitPrice: '',
status: 'unpaid'
}
this.waterBillDialogVisible = true
},
handleEditWaterBill(bill) {
this.waterBillForm = {
...bill,
startDate: bill.startDate ? new Date(bill.startDate) : '',
endDate: bill.endDate ? new Date(bill.endDate) : ''
}
this.waterBillDialogVisible = true
},
async handleSaveWaterBill() {
try {
const roomId = this.$route.params.id
if (this.waterBillForm.id) {
// 更新水费记录
await waterBillApi.update(this.waterBillForm.id, this.waterBillForm)
this.$message.success('水费记录更新成功')
} else {
// 创建水费记录
await waterBillApi.create({
...this.waterBillForm,
roomId
})
this.$message.success('水费记录添加成功')
}
this.waterBillDialogVisible = false
this.loadData()
} catch (error) {
this.$message.error('操作失败')
}
},
async handleDeleteWaterBill(id) {
this.$confirm('确定要删除这条水费记录吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'danger'
}).then(async () => {
try {
await waterBillApi.delete(id)
this.$message.success('水费记录删除成功')
this.loadData()
} catch (error) {
this.$message.error('删除失败')
}
}).catch(() => {
// 取消删除
})
},
handleAddElectricityBill() {
this.electricityBillForm = {
id: '',
roomId: this.$route.params.id,
startDate: '',
endDate: '',
startReading: '',
endReading: '',
unitPrice: '',
status: 'unpaid'
}
this.electricityBillDialogVisible = true
},
handleEditElectricityBill(bill) {
this.electricityBillForm = {
...bill,
startDate: bill.startDate ? new Date(bill.startDate) : '',
endDate: bill.endDate ? new Date(bill.endDate) : ''
}
this.electricityBillDialogVisible = true
},
async handleSaveElectricityBill() {
try {
const roomId = this.$route.params.id
if (this.electricityBillForm.id) {
// 更新电费记录
await electricityBillApi.update(this.electricityBillForm.id, this.electricityBillForm)
this.$message.success('电费记录更新成功')
} else {
// 创建电费记录
await electricityBillApi.create({
...this.electricityBillForm,
roomId
})
this.$message.success('电费记录添加成功')
}
this.electricityBillDialogVisible = false
this.loadData()
} catch (error) {
this.$message.error('操作失败')
}
},
async handleDeleteElectricityBill(id) {
this.$confirm('确定要删除这条电费记录吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'danger'
}).then(async () => {
try {
await electricityBillApi.delete(id)
this.$message.success('电费记录删除成功')
this.loadData()
} catch (error) {
this.$message.error('删除失败')
}
}).catch(() => {
// 取消删除
})
},
goBack() {
this.$router.push('/rental/list')
}
}
}
</script>
<style scoped>
.rental-detail {
padding: 20px 0;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.room-info-section {
margin-bottom: 30px;
padding-bottom: 20px;
border-bottom: 1px solid #e4e7ed;
}
.room-info-section h2 {
margin-bottom: 20px;
font-size: 24px;
font-weight: 500;
}
.room-basic-info {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 15px;
}
.info-item {
display: flex;
align-items: center;
}
.info-item .label {
width: 80px;
color: #606266;
}
.info-item .value {
font-weight: 500;
}
.rental-history-section {
margin-bottom: 30px;
padding-bottom: 20px;
border-bottom: 1px solid #e4e7ed;
}
.rental-history-section h3 {
margin-bottom: 15px;
font-size: 18px;
font-weight: 500;
}
.water-bill-section {
margin-bottom: 30px;
padding-bottom: 20px;
border-bottom: 1px solid #e4e7ed;
}
.electricity-bill-section {
margin-bottom: 30px;
padding-bottom: 20px;
border-bottom: 1px solid #e4e7ed;
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
.section-header h3 {
font-size: 18px;
font-weight: 500;
margin: 0;
}
.action-buttons {
display: flex;
gap: 10px;
}
</style>