This commit is contained in:
parent
0a839183bc
commit
058aa4333d
12
src/App.vue
12
src/App.vue
|
|
@ -35,14 +35,6 @@
|
|||
<i class="el-icon-key"></i>
|
||||
<span>租房管理</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="tenant-list">
|
||||
<i class="el-icon-user"></i>
|
||||
<span>租客档案</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="contract-list">
|
||||
<i class="el-icon-document"></i>
|
||||
<span>合同档案</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="rent-statistics">
|
||||
<i class="el-icon-data-analysis"></i>
|
||||
<span>租金统计</span>
|
||||
|
|
@ -83,10 +75,6 @@ export default {
|
|||
'room-add': '/room/add',
|
||||
'rental-list': '/rental/list',
|
||||
'rental-add': '/rental/add',
|
||||
'tenant-list': '/tenant/list',
|
||||
'tenant-add': '/tenant/add',
|
||||
'contract-list': '/contract/list',
|
||||
'contract-add': '/contract/add',
|
||||
'rent-statistics': '/statistics/rent',
|
||||
'room-statistics': '/statistics/room'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,38 +78,6 @@ const routes = [
|
|||
name: 'RentalDetail',
|
||||
component: () => import('../views/rental/Detail.vue')
|
||||
},
|
||||
// 租客管理
|
||||
{
|
||||
path: '/tenant/list',
|
||||
name: 'TenantList',
|
||||
component: () => import('../views/tenant/List.vue')
|
||||
},
|
||||
{
|
||||
path: '/tenant/add',
|
||||
name: 'TenantAdd',
|
||||
component: () => import('../views/tenant/Add.vue')
|
||||
},
|
||||
{
|
||||
path: '/tenant/edit/:id',
|
||||
name: 'TenantEdit',
|
||||
component: () => import('../views/tenant/Edit.vue')
|
||||
},
|
||||
// 合同管理
|
||||
{
|
||||
path: '/contract/list',
|
||||
name: 'ContractList',
|
||||
component: () => import('../views/contract/List.vue')
|
||||
},
|
||||
{
|
||||
path: '/contract/add',
|
||||
name: 'ContractAdd',
|
||||
component: () => import('../views/contract/Add.vue')
|
||||
},
|
||||
{
|
||||
path: '/contract/edit/:id',
|
||||
name: 'ContractEdit',
|
||||
component: () => import('../views/contract/Edit.vue')
|
||||
},
|
||||
// 统计分析
|
||||
{
|
||||
path: '/statistics/rent',
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<div class="dashboard">
|
||||
<el-card class="welcome-card">
|
||||
<h2>欢迎使用租房管理系统</h2>
|
||||
<p>本系统提供区域管理、房源管理、租客管理、合同管理和统计分析等功能</p>
|
||||
<p>本系统提供区域管理、房源管理、租房管理和统计分析等功能</p>
|
||||
</el-card>
|
||||
|
||||
<el-row :gutter="20" style="margin-top: 20px;">
|
||||
|
|
@ -39,28 +39,7 @@
|
|||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8">
|
||||
<el-card class="stat-card">
|
||||
<div class="stat-item">
|
||||
<el-icon class="el-icon-user"></el-icon>
|
||||
<div class="stat-info">
|
||||
<div class="stat-value">{{ tenantCount }}</div>
|
||||
<div class="stat-label">租客数量</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8">
|
||||
<el-card class="stat-card">
|
||||
<div class="stat-item">
|
||||
<el-icon class="el-icon-document"></el-icon>
|
||||
<div class="stat-info">
|
||||
<div class="stat-value">{{ contractCount }}</div>
|
||||
<div class="stat-label">合同数量</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
|
||||
<el-col :xs="24" :sm="12" :md="8">
|
||||
<el-card class="stat-card">
|
||||
<div class="stat-item">
|
||||
|
|
@ -83,6 +62,28 @@
|
|||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8">
|
||||
<el-card class="stat-card">
|
||||
<div class="stat-item">
|
||||
<el-icon class="el-icon-s-finance"></el-icon>
|
||||
<div class="stat-info">
|
||||
<div class="stat-value">{{ soonExpireRoomCount }}</div>
|
||||
<div class="stat-label">即将到期</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8">
|
||||
<el-card class="stat-card">
|
||||
<div class="stat-item">
|
||||
<el-icon class="el-icon-s-finance"></el-icon>
|
||||
<div class="stat-info">
|
||||
<div class="stat-value">{{ expiredRoomCount }}</div>
|
||||
<div class="stat-label">已到期</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8">
|
||||
<el-card class="stat-card">
|
||||
<div class="stat-item">
|
||||
|
|
@ -113,7 +114,7 @@
|
|||
<span>区域公寓房间状态分布</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-table :data="regionApartmentHouseStats" style="width: 100%">
|
||||
<el-table :data="regionApartmentHouseStats" style="width: 100%" show-summary :summary-method="getSummary">
|
||||
<el-table-column prop="region" label="区域" width="180"></el-table-column>
|
||||
<el-table-column prop="apartment" label="公寓" width="180"></el-table-column>
|
||||
<el-table-column prop="empty" label="空房" width="80"></el-table-column>
|
||||
|
|
@ -138,10 +139,10 @@ export default {
|
|||
regionCount: 0,
|
||||
apartmentCount: 0,
|
||||
roomCount: 0,
|
||||
tenantCount: 0,
|
||||
contractCount: 0,
|
||||
emptyRoomCount: 0,
|
||||
rentedRoomCount: 0,
|
||||
soonExpireRoomCount: 0,
|
||||
expiredRoomCount: 0,
|
||||
collectedRentAmount: 0,
|
||||
collectedWaterAmount: 0,
|
||||
regionApartmentHouseStats: []
|
||||
|
|
@ -166,16 +167,40 @@ export default {
|
|||
this.regionCount = dashboardStats.regionCount
|
||||
this.apartmentCount = dashboardStats.apartmentCount
|
||||
this.roomCount = dashboardStats.roomCount
|
||||
this.tenantCount = dashboardStats.tenantCount
|
||||
this.contractCount = dashboardStats.contractCount
|
||||
this.emptyRoomCount = dashboardStats.emptyRoomCount
|
||||
this.rentedRoomCount = dashboardStats.rentedRoomCount
|
||||
this.soonExpireRoomCount = dashboardStats.soonExpireRoomCount
|
||||
this.expiredRoomCount = dashboardStats.expiredRoomCount
|
||||
this.collectedRentAmount = dashboardStats.collectedRentAmount
|
||||
this.collectedWaterAmount = dashboardStats.collectedWaterAmount
|
||||
this.regionApartmentHouseStats = regionApartmentHouseStatsResponse
|
||||
} catch (error) {
|
||||
this.$message.error('加载数据失败')
|
||||
}
|
||||
},
|
||||
getSummary(param) {
|
||||
const { columns, data } = param;
|
||||
const sums = [];
|
||||
columns.forEach((column, index) => {
|
||||
if (index === 0) {
|
||||
sums[index] = '合计';
|
||||
return;
|
||||
}
|
||||
if (index === 1) {
|
||||
sums[index] = '';
|
||||
return;
|
||||
}
|
||||
const values = data.map(item => Number(item[column.property]) || 0);
|
||||
if (values.every(value => !isNaN(value))) {
|
||||
sums[index] = values.reduce((prev, curr) => {
|
||||
const value = Number(curr) || 0;
|
||||
return prev + value;
|
||||
}, 0);
|
||||
} else {
|
||||
sums[index] = '';
|
||||
}
|
||||
});
|
||||
return sums;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,12 +46,7 @@ export default {
|
|||
{ required: true, message: '请选择区域', trigger: 'blur' }
|
||||
],
|
||||
name: [
|
||||
{ required: true, message: '请输入公寓名称', trigger: 'blur' },
|
||||
{ min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
|
||||
],
|
||||
address: [
|
||||
{ required: true, message: '请输入公寓地址', trigger: 'blur' },
|
||||
{ min: 5, max: 100, message: '长度在 5 到 100 个字符', trigger: 'blur' }
|
||||
{ required: true, message: '请输入公寓名称', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,12 +47,7 @@ export default {
|
|||
{ required: true, message: '请选择区域', trigger: 'blur' }
|
||||
],
|
||||
name: [
|
||||
{ required: true, message: '请输入公寓名称', trigger: 'blur' },
|
||||
{ min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
|
||||
],
|
||||
address: [
|
||||
{ required: true, message: '请输入公寓地址', trigger: 'blur' },
|
||||
{ min: 5, max: 100, message: '长度在 5 到 100 个字符', trigger: 'blur' }
|
||||
{ required: true, message: '请输入公寓名称', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,139 +0,0 @@
|
|||
<template>
|
||||
<div class="contract-add">
|
||||
<el-card>
|
||||
<template slot="header">
|
||||
<div class="card-header">
|
||||
<span>添加合同</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-form :model="contractForm" :rules="rules" ref="contractForm" label-width="120px">
|
||||
<el-form-item label="房间" prop="roomId">
|
||||
<el-select v-model="contractForm.roomId" placeholder="请选择房间">
|
||||
<el-option v-for="room in rooms" :key="room.id" :label="room.roomNumber" :value="room.id"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="租客" prop="tenantId">
|
||||
<el-select v-model="contractForm.tenantId" placeholder="请选择租客">
|
||||
<el-option v-for="tenant in tenants" :key="tenant.id" :label="tenant.name" :value="tenant.id"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="开始日期" prop="startDate">
|
||||
<el-date-picker v-model="contractForm.startDate" type="date" placeholder="选择开始日期" style="width: 100%"></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item label="结束日期" prop="endDate">
|
||||
<el-date-picker v-model="contractForm.endDate" type="date" placeholder="选择结束日期" style="width: 100%"></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item label="租金(元/月)" prop="rent">
|
||||
<el-input type="number" v-model="contractForm.rent" placeholder="请输入租金"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="押金(元)" prop="deposit">
|
||||
<el-input type="number" v-model="contractForm.deposit" placeholder="请输入押金"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="submitForm">提交</el-button>
|
||||
<el-button @click="resetForm">重置</el-button>
|
||||
<el-button @click="goBack">返回</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { contractApi, tenantApi, roomApi } from '../../api/api'
|
||||
|
||||
export default {
|
||||
name: 'ContractAdd',
|
||||
data() {
|
||||
return {
|
||||
contractForm: {
|
||||
houseId: '',
|
||||
tenantId: '',
|
||||
startDate: '',
|
||||
endDate: '',
|
||||
rent: '',
|
||||
deposit: ''
|
||||
},
|
||||
tenants: [],
|
||||
rooms: [],
|
||||
rules: {
|
||||
houseId: [
|
||||
{ required: true, message: '请选择房间', trigger: 'blur' }
|
||||
],
|
||||
tenantId: [
|
||||
{ required: true, message: '请选择租客', trigger: 'blur' }
|
||||
],
|
||||
startDate: [
|
||||
{ required: true, message: '请选择开始日期', trigger: 'blur' }
|
||||
],
|
||||
endDate: [
|
||||
{ required: true, message: '请选择结束日期', trigger: 'blur' }
|
||||
],
|
||||
rent: [
|
||||
{ required: true, message: '请输入租金', trigger: 'blur' },
|
||||
{ type: 'number', message: '请输入数字', trigger: 'blur' }
|
||||
],
|
||||
deposit: [
|
||||
{ required: true, message: '请输入押金', trigger: 'blur' },
|
||||
{ type: 'number', message: '请输入数字', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.loadTenants()
|
||||
this.loadRooms()
|
||||
},
|
||||
methods: {
|
||||
async loadTenants() {
|
||||
try {
|
||||
const response = await tenantApi.list()
|
||||
this.tenants = response
|
||||
} catch (error) {
|
||||
this.$message.error('加载租客数据失败')
|
||||
}
|
||||
},
|
||||
async loadRooms() {
|
||||
try {
|
||||
const response = await roomApi.list()
|
||||
this.rooms = response
|
||||
} catch (error) {
|
||||
this.$message.error('加载房间数据失败')
|
||||
}
|
||||
},
|
||||
async submitForm() {
|
||||
this.$refs.contractForm.validate(async (valid) => {
|
||||
if (valid) {
|
||||
try {
|
||||
await contractApi.create(this.contractForm)
|
||||
this.$message.success('添加成功')
|
||||
this.$router.push('/contract/list')
|
||||
} catch (error) {
|
||||
this.$message.error('添加失败')
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
},
|
||||
resetForm() {
|
||||
this.$refs.contractForm.resetFields()
|
||||
},
|
||||
goBack() {
|
||||
this.$router.push('/contract/list')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.contract-add {
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,152 +0,0 @@
|
|||
<template>
|
||||
<div class="contract-edit">
|
||||
<el-card>
|
||||
<template slot="header">
|
||||
<div class="card-header">
|
||||
<span>编辑合同</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-form :model="contractForm" :rules="rules" ref="contractForm" label-width="120px">
|
||||
<el-form-item label="房间" prop="roomId">
|
||||
<el-select v-model="contractForm.roomId" placeholder="请选择房间">
|
||||
<el-option v-for="room in rooms" :key="room.id" :label="room.roomNumber" :value="room.id"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="租客" prop="tenantId">
|
||||
<el-select v-model="contractForm.tenantId" placeholder="请选择租客">
|
||||
<el-option v-for="tenant in tenants" :key="tenant.id" :label="tenant.name" :value="tenant.id"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="开始日期" prop="startDate">
|
||||
<el-date-picker v-model="contractForm.startDate" type="date" placeholder="选择开始日期" style="width: 100%"></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item label="结束日期" prop="endDate">
|
||||
<el-date-picker v-model="contractForm.endDate" type="date" placeholder="选择结束日期" style="width: 100%"></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item label="租金(元/月)" prop="rent">
|
||||
<el-input type="number" v-model="contractForm.rent" placeholder="请输入租金"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="押金(元)" prop="deposit">
|
||||
<el-input type="number" v-model="contractForm.deposit" placeholder="请输入押金"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="submitForm">提交</el-button>
|
||||
<el-button @click="resetForm">重置</el-button>
|
||||
<el-button @click="goBack">返回</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { contractApi, tenantApi, roomApi } from '../../api/api'
|
||||
|
||||
export default {
|
||||
name: 'ContractEdit',
|
||||
data() {
|
||||
return {
|
||||
contractForm: {
|
||||
id: '',
|
||||
houseId: '',
|
||||
tenantId: '',
|
||||
startDate: '',
|
||||
endDate: '',
|
||||
rent: '',
|
||||
deposit: ''
|
||||
},
|
||||
tenants: [],
|
||||
rooms: [],
|
||||
rules: {
|
||||
houseId: [
|
||||
{ required: true, message: '请选择房间', trigger: 'blur' }
|
||||
],
|
||||
tenantId: [
|
||||
{ required: true, message: '请选择租客', trigger: 'blur' }
|
||||
],
|
||||
startDate: [
|
||||
{ required: true, message: '请选择开始日期', trigger: 'blur' }
|
||||
],
|
||||
endDate: [
|
||||
{ required: true, message: '请选择结束日期', trigger: 'blur' }
|
||||
],
|
||||
rent: [
|
||||
{ required: true, message: '请输入租金', trigger: 'blur' },
|
||||
{ type: 'number', message: '请输入数字', trigger: 'blur' }
|
||||
],
|
||||
deposit: [
|
||||
{ required: true, message: '请输入押金', trigger: 'blur' },
|
||||
{ type: 'number', message: '请输入数字', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.loadTenants()
|
||||
this.loadRooms()
|
||||
this.loadContractData()
|
||||
},
|
||||
methods: {
|
||||
async loadTenants() {
|
||||
try {
|
||||
const response = await tenantApi.list()
|
||||
this.tenants = response
|
||||
} catch (error) {
|
||||
this.$message.error('加载租客数据失败')
|
||||
}
|
||||
},
|
||||
async loadRooms() {
|
||||
try {
|
||||
const response = await roomApi.list()
|
||||
this.rooms = response
|
||||
} catch (error) {
|
||||
this.$message.error('加载房间数据失败')
|
||||
}
|
||||
},
|
||||
async loadContractData() {
|
||||
try {
|
||||
const id = this.$route.params.id
|
||||
const contract = await contractApi.getById(id)
|
||||
if (contract) {
|
||||
this.contractForm = contract
|
||||
}
|
||||
} catch (error) {
|
||||
this.$message.error('加载合同数据失败')
|
||||
}
|
||||
},
|
||||
async submitForm() {
|
||||
this.$refs.contractForm.validate(async (valid) => {
|
||||
if (valid) {
|
||||
try {
|
||||
await contractApi.update(this.contractForm.id, this.contractForm)
|
||||
this.$message.success('编辑成功')
|
||||
this.$router.push('/contract/list')
|
||||
} catch (error) {
|
||||
this.$message.error('编辑失败')
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
},
|
||||
resetForm() {
|
||||
this.loadContractData()
|
||||
},
|
||||
goBack() {
|
||||
this.$router.push('/contract/list')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.contract-edit {
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,171 +0,0 @@
|
|||
<template>
|
||||
<div class="contract-list">
|
||||
<el-card>
|
||||
<template slot="header">
|
||||
<div class="card-header">
|
||||
<span>合同档案</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-form :inline="true" :model="searchForm" class="search-form">
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="searchForm.status" placeholder="请选择状态">
|
||||
<el-option label="全部" value=""></el-option>
|
||||
<el-option label="有效" value="active"></el-option>
|
||||
<el-option label="过期" value="expired"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSearch">搜索</el-button>
|
||||
<el-button @click="resetSearch">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-table :data="contracts" style="width: 100%" v-loading="isLoading">
|
||||
<el-table-column prop="id" label="ID" width="80"></el-table-column>
|
||||
<el-table-column prop="regionName" label="区域"></el-table-column>
|
||||
<el-table-column prop="apartmentName" label="公寓"></el-table-column>
|
||||
<el-table-column prop="roomNumber" label="房间号"></el-table-column>
|
||||
<el-table-column prop="tenantName" label="租客姓名"></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="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>
|
||||
<div class="pagination" style="margin-top: 20px;">
|
||||
<el-pagination
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="total"
|
||||
:page-size="pageSize"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:current-page="currentPage"
|
||||
@current-change="handleCurrentChange"
|
||||
@size-change="handleSizeChange"
|
||||
></el-pagination>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { contractApi } from '../../api/api'
|
||||
|
||||
export default {
|
||||
name: 'ContractList',
|
||||
data() {
|
||||
return {
|
||||
contracts: [],
|
||||
total: 0,
|
||||
searchForm: {
|
||||
status: ''
|
||||
},
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
isLoading: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.loadContracts()
|
||||
},
|
||||
methods: {
|
||||
async loadContracts() {
|
||||
this.isLoading = true
|
||||
try {
|
||||
// 加载合同数据
|
||||
const params = {
|
||||
status: this.searchForm.status,
|
||||
page: this.currentPage,
|
||||
pageSize: this.pageSize
|
||||
}
|
||||
const contractsResponse = await contractApi.getAll(params)
|
||||
|
||||
// 处理合同数据,使用后端返回的关联信息
|
||||
const contracts = contractsResponse.data || contractsResponse
|
||||
this.contracts = contracts.map(contract => {
|
||||
return {
|
||||
...contract,
|
||||
roomNumber: contract.Room ? contract.Room.roomNumber : '',
|
||||
tenantName: contract.Tenant ? contract.Tenant.name : '',
|
||||
apartmentName: contract.Room && contract.Room.Apartment ? contract.Room.Apartment.name : '',
|
||||
regionName: contract.Room && contract.Room.Apartment && contract.Room.Apartment.Region ? contract.Room.Apartment.Region.name : ''
|
||||
}
|
||||
})
|
||||
this.total = contractsResponse.total || 0
|
||||
} catch (error) {
|
||||
this.$message.error('加载数据失败')
|
||||
} finally {
|
||||
this.isLoading = false
|
||||
}
|
||||
},
|
||||
handleAdd() {
|
||||
this.$router.push('/contract/add')
|
||||
},
|
||||
handleEdit(id) {
|
||||
this.$router.push(`/contract/edit/${id}`)
|
||||
},
|
||||
async handleDelete(id) {
|
||||
this.$confirm('确定要删除这个合同吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(async () => {
|
||||
try {
|
||||
await contractApi.delete(id)
|
||||
this.$message.success('删除成功')
|
||||
this.loadContracts()
|
||||
} catch (error) {
|
||||
this.$message.error('删除失败')
|
||||
}
|
||||
}).catch(() => {
|
||||
// 取消删除
|
||||
})
|
||||
},
|
||||
handleSearch() {
|
||||
this.currentPage = 1
|
||||
this.loadContracts()
|
||||
},
|
||||
resetSearch() {
|
||||
this.searchForm = {
|
||||
status: ''
|
||||
}
|
||||
this.currentPage = 1
|
||||
this.loadContracts()
|
||||
},
|
||||
handleCurrentChange(val) {
|
||||
this.currentPage = val
|
||||
this.loadContracts()
|
||||
},
|
||||
handleSizeChange(val) {
|
||||
this.pageSize = val
|
||||
this.currentPage = 1
|
||||
this.loadContracts()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.contract-list {
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.search-form {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,125 +0,0 @@
|
|||
<template>
|
||||
<div class="house-add">
|
||||
<el-card>
|
||||
<template slot="header">
|
||||
<div class="card-header">
|
||||
<span>添加房源</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-form :model="houseForm" :rules="rules" ref="houseForm" label-width="100px">
|
||||
<el-form-item label="区域" prop="regionId">
|
||||
<el-select v-model="houseForm.regionId" placeholder="请选择区域">
|
||||
<el-option v-for="region in regions" :key="region.id" :label="region.name" :value="region.id"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="地址" prop="address">
|
||||
<el-input v-model="houseForm.address" placeholder="请输入地址"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="面积(㎡)" prop="area">
|
||||
<el-input type="number" v-model="houseForm.area" placeholder="请输入面积"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="租金(元/月)" prop="price">
|
||||
<el-input type="number" v-model="houseForm.price" placeholder="请输入租金"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="houseForm.status" placeholder="请选择状态">
|
||||
<el-option label="可租" value="available"></el-option>
|
||||
<el-option label="已租" value="rented"></el-option>
|
||||
<el-option label="维护中" value="maintenance"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="submitForm">提交</el-button>
|
||||
<el-button @click="resetForm">重置</el-button>
|
||||
<el-button @click="goBack">返回</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { regionApi, houseApi } from '../../api/api'
|
||||
|
||||
export default {
|
||||
name: 'HouseAdd',
|
||||
data() {
|
||||
return {
|
||||
houseForm: {
|
||||
regionId: '',
|
||||
address: '',
|
||||
area: '',
|
||||
price: '',
|
||||
status: 'available'
|
||||
},
|
||||
regions: [],
|
||||
rules: {
|
||||
regionId: [
|
||||
{ required: true, message: '请选择区域', trigger: 'blur' }
|
||||
],
|
||||
address: [
|
||||
{ required: true, message: '请输入地址', trigger: 'blur' },
|
||||
{ min: 5, max: 100, message: '长度在 5 到 100 个字符', trigger: 'blur' }
|
||||
],
|
||||
area: [
|
||||
{ required: true, message: '请输入面积', trigger: 'blur' },
|
||||
{ type: 'number', message: '请输入数字', trigger: 'blur' }
|
||||
],
|
||||
price: [
|
||||
{ required: true, message: '请输入租金', trigger: 'blur' },
|
||||
{ type: 'number', message: '请输入数字', trigger: 'blur' }
|
||||
],
|
||||
status: [
|
||||
{ required: true, message: '请选择状态', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.loadRegions()
|
||||
},
|
||||
methods: {
|
||||
async loadRegions() {
|
||||
try {
|
||||
const response = await regionApi.list()
|
||||
this.regions = response
|
||||
} catch (error) {
|
||||
this.$message.error('加载区域数据失败')
|
||||
}
|
||||
},
|
||||
async submitForm() {
|
||||
this.$refs.houseForm.validate(async (valid) => {
|
||||
if (valid) {
|
||||
try {
|
||||
await houseApi.create(this.houseForm)
|
||||
this.$message.success('添加成功')
|
||||
this.$router.push('/house/list')
|
||||
} catch (error) {
|
||||
this.$message.error('添加失败')
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
},
|
||||
resetForm() {
|
||||
this.$refs.houseForm.resetFields()
|
||||
},
|
||||
goBack() {
|
||||
this.$router.push('/house/list')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.house-add {
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,138 +0,0 @@
|
|||
<template>
|
||||
<div class="house-edit">
|
||||
<el-card>
|
||||
<template slot="header">
|
||||
<div class="card-header">
|
||||
<span>编辑房源</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-form :model="houseForm" :rules="rules" ref="houseForm" label-width="100px">
|
||||
<el-form-item label="区域" prop="regionId">
|
||||
<el-select v-model="houseForm.regionId" placeholder="请选择区域">
|
||||
<el-option v-for="region in regions" :key="region.id" :label="region.name" :value="region.id"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="地址" prop="address">
|
||||
<el-input v-model="houseForm.address" placeholder="请输入地址"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="面积(㎡)" prop="area">
|
||||
<el-input type="number" v-model="houseForm.area" placeholder="请输入面积"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="租金(元/月)" prop="price">
|
||||
<el-input type="number" v-model="houseForm.price" placeholder="请输入租金"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="houseForm.status" placeholder="请选择状态">
|
||||
<el-option label="可租" value="available"></el-option>
|
||||
<el-option label="已租" value="rented"></el-option>
|
||||
<el-option label="维护中" value="maintenance"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="submitForm">提交</el-button>
|
||||
<el-button @click="resetForm">重置</el-button>
|
||||
<el-button @click="goBack">返回</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { regionApi, houseApi } from '../../api/api'
|
||||
|
||||
export default {
|
||||
name: 'HouseEdit',
|
||||
data() {
|
||||
return {
|
||||
houseForm: {
|
||||
id: '',
|
||||
regionId: '',
|
||||
address: '',
|
||||
area: '',
|
||||
price: '',
|
||||
status: ''
|
||||
},
|
||||
regions: [],
|
||||
rules: {
|
||||
regionId: [
|
||||
{ required: true, message: '请选择区域', trigger: 'blur' }
|
||||
],
|
||||
address: [
|
||||
{ required: true, message: '请输入地址', trigger: 'blur' },
|
||||
{ min: 5, max: 100, message: '长度在 5 到 100 个字符', trigger: 'blur' }
|
||||
],
|
||||
area: [
|
||||
{ required: true, message: '请输入面积', trigger: 'blur' },
|
||||
{ type: 'number', message: '请输入数字', trigger: 'blur' }
|
||||
],
|
||||
price: [
|
||||
{ required: true, message: '请输入租金', trigger: 'blur' },
|
||||
{ type: 'number', message: '请输入数字', trigger: 'blur' }
|
||||
],
|
||||
status: [
|
||||
{ required: true, message: '请选择状态', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.loadRegions()
|
||||
this.loadHouseData()
|
||||
},
|
||||
methods: {
|
||||
async loadRegions() {
|
||||
try {
|
||||
const response = await regionApi.list()
|
||||
this.regions = response
|
||||
} catch (error) {
|
||||
this.$message.error('加载区域数据失败')
|
||||
}
|
||||
},
|
||||
async loadHouseData() {
|
||||
try {
|
||||
const id = this.$route.params.id
|
||||
const house = await houseApi.getById(id)
|
||||
if (house) {
|
||||
this.houseForm = house
|
||||
}
|
||||
} catch (error) {
|
||||
this.$message.error('加载房源数据失败')
|
||||
}
|
||||
},
|
||||
async submitForm() {
|
||||
this.$refs.houseForm.validate(async (valid) => {
|
||||
if (valid) {
|
||||
try {
|
||||
await houseApi.update(this.houseForm.id, this.houseForm)
|
||||
this.$message.success('编辑成功')
|
||||
this.$router.push('/house/list')
|
||||
} catch (error) {
|
||||
this.$message.error('编辑失败')
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
},
|
||||
resetForm() {
|
||||
this.loadHouseData()
|
||||
},
|
||||
goBack() {
|
||||
this.$router.push('/house/list')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.house-edit {
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,200 +0,0 @@
|
|||
<template>
|
||||
<div class="house-list">
|
||||
<el-card>
|
||||
<template slot="header">
|
||||
<div class="card-header">
|
||||
<span>房源管理</span>
|
||||
<el-button type="primary" @click="handleAdd">添加房源</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<el-form :inline="true" :model="searchForm" class="search-form">
|
||||
<el-form-item label="区域">
|
||||
<el-select v-model="searchForm.regionId" placeholder="请选择区域">
|
||||
<el-option label="全部" value=""></el-option>
|
||||
<el-option v-for="region in regions" :key="region.id" :label="region.name" :value="region.id"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="searchForm.status" placeholder="请选择状态">
|
||||
<el-option label="全部" value=""></el-option>
|
||||
<el-option label="可租" value="available"></el-option>
|
||||
<el-option label="已租" value="rented"></el-option>
|
||||
<el-option label="维护中" value="maintenance"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSearch">搜索</el-button>
|
||||
<el-button @click="resetSearch">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-table :data="houses" style="width: 100%" v-loading="isLoading">
|
||||
<el-table-column prop="id" label="ID" width="80"></el-table-column>
|
||||
<el-table-column prop="regionName" label="区域"></el-table-column>
|
||||
<el-table-column prop="address" label="地址"></el-table-column>
|
||||
<el-table-column prop="area" label="面积(㎡)" width="100"></el-table-column>
|
||||
<el-table-column prop="price" label="租金(元/月)" width="120"></el-table-column>
|
||||
<el-table-column prop="status" label="状态" width="100">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="getStatusType(scope.row.status)">{{ getStatusText(scope.row.status) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="创建时间" width="180"></el-table-column>
|
||||
<el-table-column label="操作" width="150">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="small" type="primary" @click="handleEdit(scope.row.id)">编辑</el-button>
|
||||
<el-button size="small" type="danger" @click="handleDelete(scope.row.id)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="pagination" style="margin-top: 20px;">
|
||||
<el-pagination
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="total"
|
||||
:page-size="pageSize"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:current-page="currentPage"
|
||||
@current-change="handleCurrentChange"
|
||||
@size-change="handleSizeChange"
|
||||
></el-pagination>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { regionApi, houseApi } from '../../api/api'
|
||||
|
||||
export default {
|
||||
name: 'HouseList',
|
||||
data() {
|
||||
return {
|
||||
regions: [],
|
||||
houses: [],
|
||||
total: 0,
|
||||
searchForm: {
|
||||
regionId: '',
|
||||
status: ''
|
||||
},
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
isLoading: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.loadData()
|
||||
},
|
||||
methods: {
|
||||
async loadData() {
|
||||
this.isLoading = true
|
||||
try {
|
||||
// 加载区域数据
|
||||
const regionsResponse = await regionApi.getAll()
|
||||
this.regions = regionsResponse
|
||||
|
||||
// 加载房源数据
|
||||
const params = {
|
||||
regionId: this.searchForm.regionId,
|
||||
status: this.searchForm.status,
|
||||
page: this.currentPage,
|
||||
pageSize: this.pageSize
|
||||
}
|
||||
const housesResponse = await houseApi.getAll(params)
|
||||
const houses = housesResponse.data || housesResponse
|
||||
this.houses = houses.map(house => {
|
||||
const region = regionsResponse.find(r => r.id == house.regionId)
|
||||
return {
|
||||
...house,
|
||||
regionName: region ? region.name : ''
|
||||
}
|
||||
})
|
||||
this.total = housesResponse.total || 0
|
||||
} catch (error) {
|
||||
this.$message.error('加载数据失败')
|
||||
} finally {
|
||||
this.isLoading = false
|
||||
}
|
||||
},
|
||||
getStatusType(status) {
|
||||
switch (status) {
|
||||
case 'available': return 'success'
|
||||
case 'rented': return 'warning'
|
||||
case 'maintenance': return 'danger'
|
||||
default: return ''
|
||||
}
|
||||
},
|
||||
getStatusText(status) {
|
||||
switch (status) {
|
||||
case 'available': return '可租'
|
||||
case 'rented': return '已租'
|
||||
case 'maintenance': return '维护中'
|
||||
default: return status
|
||||
}
|
||||
},
|
||||
handleAdd() {
|
||||
this.$router.push('/house/add')
|
||||
},
|
||||
handleEdit(id) {
|
||||
this.$router.push(`/house/edit/${id}`)
|
||||
},
|
||||
async handleDelete(id) {
|
||||
this.$confirm('确定要删除这个房源吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(async () => {
|
||||
try {
|
||||
await houseApi.delete(id)
|
||||
this.$message.success('删除成功')
|
||||
this.loadData()
|
||||
} catch (error) {
|
||||
this.$message.error('删除失败')
|
||||
}
|
||||
}).catch(() => {
|
||||
// 取消删除
|
||||
})
|
||||
},
|
||||
handleSearch() {
|
||||
this.currentPage = 1
|
||||
this.loadData()
|
||||
},
|
||||
resetSearch() {
|
||||
this.searchForm = {
|
||||
regionId: '',
|
||||
status: ''
|
||||
}
|
||||
this.currentPage = 1
|
||||
this.loadData()
|
||||
},
|
||||
handleCurrentChange(val) {
|
||||
this.currentPage = val
|
||||
this.loadData()
|
||||
},
|
||||
handleSizeChange(val) {
|
||||
this.pageSize = val
|
||||
this.currentPage = 1
|
||||
this.loadData()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.house-list {
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.search-form {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -36,12 +36,7 @@ export default {
|
|||
},
|
||||
rules: {
|
||||
name: [
|
||||
{ required: true, message: '请输入区域名称', trigger: 'blur' },
|
||||
{ min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
|
||||
],
|
||||
description: [
|
||||
{ required: true, message: '请输入区域描述', trigger: 'blur' },
|
||||
{ min: 5, max: 100, message: '长度在 5 到 100 个字符', trigger: 'blur' }
|
||||
{ required: true, message: '请输入区域名称', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,12 +37,7 @@ export default {
|
|||
},
|
||||
rules: {
|
||||
name: [
|
||||
{ required: true, message: '请输入区域名称', trigger: 'blur' },
|
||||
{ min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
|
||||
],
|
||||
description: [
|
||||
{ required: true, message: '请输入区域描述', trigger: 'blur' },
|
||||
{ min: 5, max: 100, message: '长度在 5 到 100 个字符', trigger: 'blur' }
|
||||
{ required: true, message: '请输入区域名称', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,12 +21,6 @@
|
|||
<el-form-item label="租客姓名" prop="tenantName">
|
||||
<el-input v-model="rentalForm.tenantName" placeholder="请输入租客姓名"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="租客电话" prop="tenantPhone">
|
||||
<el-input v-model="rentalForm.tenantPhone" placeholder="请输入租客电话"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="身份证号" prop="tenantIdCard">
|
||||
<el-input v-model="rentalForm.tenantIdCard" placeholder="请输入身份证号"></el-input>
|
||||
</el-form-item>
|
||||
<el-divider>合同信息</el-divider>
|
||||
<el-form-item label="开始日期" prop="startDate">
|
||||
<el-date-picker
|
||||
|
|
@ -90,8 +84,6 @@ export default {
|
|||
rentalForm: {
|
||||
roomId: '',
|
||||
tenantName: '',
|
||||
tenantPhone: '',
|
||||
tenantIdCard: '',
|
||||
startDate: '',
|
||||
endDate: '',
|
||||
rent: '',
|
||||
|
|
@ -179,12 +171,22 @@ export default {
|
|||
try {
|
||||
await rentalApi.create(this.rentalForm)
|
||||
this.$message.success('添加成功')
|
||||
// 跳转到租房详情页面
|
||||
if (this.rentalForm.roomId) {
|
||||
this.$router.push({
|
||||
path: `/rental/detail/${this.rentalForm.roomId}`,
|
||||
query: this.returnQuery
|
||||
})
|
||||
} else {
|
||||
// 如果没有房间ID,跳转到列表页面
|
||||
this.$router.push({
|
||||
path: '/rental/list',
|
||||
query: this.returnQuery
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
this.$message.error('添加失败')
|
||||
console.error('添加失败:', error)
|
||||
this.$message.error('添加失败:' + (error.message || '未知错误'))
|
||||
}
|
||||
} else {
|
||||
this.$message.error('请填写所有必填项')
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
</div>
|
||||
</template>
|
||||
<div v-loading="isLoading" class="room-info-section">
|
||||
<h2>{{ room.Apartment.name }} - {{ room.roomNumber }}</h2>
|
||||
<h2>{{ room.Apartment ? room.Apartment.name : '' }} - {{ room.roomNumber }}</h2>
|
||||
<div class="room-basic-info">
|
||||
<div class="info-item">
|
||||
<span class="label">面积:</span>
|
||||
|
|
@ -161,12 +161,6 @@
|
|||
<el-form-item label="租客姓名" prop="tenantName">
|
||||
<el-input v-model="rentalForm.tenantName" placeholder="请输入租客姓名"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="租客电话" prop="tenantPhone">
|
||||
<el-input v-model="rentalForm.tenantPhone" placeholder="请输入租客电话"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="身份证号" prop="tenantIdCard">
|
||||
<el-input v-model="rentalForm.tenantIdCard" placeholder="请输入身份证号"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="开始日期" prop="startDate">
|
||||
<el-date-picker v-model="rentalForm.startDate" type="date" placeholder="选择开始日期" style="width: 100%"></el-date-picker>
|
||||
</el-form-item>
|
||||
|
|
@ -245,8 +239,6 @@ export default {
|
|||
id: '',
|
||||
roomId: '',
|
||||
tenantName: '',
|
||||
tenantPhone: '',
|
||||
tenantIdCard: '',
|
||||
startDate: '',
|
||||
endDate: '',
|
||||
rent: '',
|
||||
|
|
@ -338,8 +330,7 @@ export default {
|
|||
const data = response.data || response
|
||||
this.rentalHistory = data.map(rental => {
|
||||
return {
|
||||
...rental,
|
||||
tenantName: rental.Tenant ? rental.Tenant.name : ''
|
||||
...rental
|
||||
}
|
||||
})
|
||||
|
||||
|
|
@ -431,6 +422,11 @@ export default {
|
|||
})
|
||||
},
|
||||
async handleCheckout() {
|
||||
if (!this.room.id) {
|
||||
this.$message.error('房间信息加载失败,无法进行退房操作')
|
||||
return
|
||||
}
|
||||
|
||||
this.$confirm('确定要退房吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
|
|
@ -439,7 +435,7 @@ export default {
|
|||
try {
|
||||
// 找到当前房间的租房记录
|
||||
const roomId = this.$route.params.id
|
||||
const rental = this.rentals.find(r => r.roomId == roomId && r.status === 'active')
|
||||
const rental = this.rentalHistory.find(r => r.roomId == roomId && r.status === 'active')
|
||||
|
||||
if (rental) {
|
||||
// 更新租房记录状态
|
||||
|
|
@ -454,7 +450,8 @@ export default {
|
|||
this.$message.error('未找到活跃的租房记录')
|
||||
}
|
||||
} catch (error) {
|
||||
this.$message.error('退房失败')
|
||||
console.error('退房失败:', error)
|
||||
this.$message.error('退房失败:' + (error.message || '未知错误'))
|
||||
}
|
||||
}).catch(() => {
|
||||
// 取消退房
|
||||
|
|
@ -635,11 +632,26 @@ export default {
|
|||
type: 'danger'
|
||||
}).then(async () => {
|
||||
try {
|
||||
// 先获取租赁记录的详细信息
|
||||
const rental = this.rentalHistory.find(r => r.id == id)
|
||||
|
||||
// 删除租赁记录
|
||||
await rentalApi.delete(id)
|
||||
this.$message.success('租赁记录删除成功')
|
||||
|
||||
// 如果是在租状态,更新房间状态为空房
|
||||
if (rental && rental.status === 'active' && rental.roomId) {
|
||||
await roomApi.update(rental.roomId, {
|
||||
status: 'empty',
|
||||
subStatus: 'normal'
|
||||
})
|
||||
this.$message.success('房间状态已更新为空房')
|
||||
}
|
||||
|
||||
this.loadData()
|
||||
} catch (error) {
|
||||
this.$message.error('删除失败')
|
||||
console.error('删除失败:', error)
|
||||
this.$message.error('删除失败:' + (error.message || '未知错误'))
|
||||
}
|
||||
}).catch(() => {
|
||||
// 取消删除
|
||||
|
|
|
|||
|
|
@ -21,12 +21,6 @@
|
|||
<el-form-item label="租客姓名" prop="tenantName">
|
||||
<el-input v-model="rentalForm.tenantName" placeholder="请输入租客姓名"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="租客电话" prop="tenantPhone">
|
||||
<el-input v-model="rentalForm.tenantPhone" placeholder="请输入租客电话"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="身份证号" prop="tenantIdCard">
|
||||
<el-input v-model="rentalForm.tenantIdCard" placeholder="请输入身份证号"></el-input>
|
||||
</el-form-item>
|
||||
<el-divider>合同信息</el-divider>
|
||||
<el-form-item label="开始日期" prop="startDate">
|
||||
<el-date-picker
|
||||
|
|
@ -77,8 +71,6 @@ export default {
|
|||
id: '',
|
||||
roomId: '',
|
||||
tenantName: '',
|
||||
tenantPhone: '',
|
||||
tenantIdCard: '',
|
||||
startDate: '',
|
||||
endDate: '',
|
||||
rent: '',
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="rental-info" v-if="room.Rentals && room.Rentals.length > 0">
|
||||
<p>租客: {{ room.Rentals[0].Tenant.name }}</p>
|
||||
<p>租客: {{ room.Rentals[0].tenantName }}</p>
|
||||
<p>租期: {{ room.Rentals[0].startDate }} 至 {{ room.Rentals[0].endDate }}</p>
|
||||
</div>
|
||||
<div class="rental-info" v-else>
|
||||
|
|
|
|||
|
|
@ -1,93 +0,0 @@
|
|||
<template>
|
||||
<div class="tenant-add">
|
||||
<el-card>
|
||||
<template slot="header">
|
||||
<div class="card-header">
|
||||
<span>添加租客</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-form :model="tenantForm" :rules="rules" ref="tenantForm" label-width="100px">
|
||||
<el-form-item label="姓名" prop="name">
|
||||
<el-input v-model="tenantForm.name" placeholder="请输入姓名"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="电话" prop="phone">
|
||||
<el-input v-model="tenantForm.phone" placeholder="请输入电话"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="身份证号" prop="idCard">
|
||||
<el-input v-model="tenantForm.idCard" placeholder="请输入身份证号"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="submitForm">提交</el-button>
|
||||
<el-button @click="resetForm">重置</el-button>
|
||||
<el-button @click="goBack">返回</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { tenantApi } from '../../api/api'
|
||||
|
||||
export default {
|
||||
name: 'TenantAdd',
|
||||
data() {
|
||||
return {
|
||||
tenantForm: {
|
||||
name: '',
|
||||
phone: '',
|
||||
idCard: ''
|
||||
},
|
||||
rules: {
|
||||
name: [
|
||||
{ required: true, message: '请输入姓名', trigger: 'blur' },
|
||||
{ min: 2, max: 10, message: '长度在 2 到 10 个字符', trigger: 'blur' }
|
||||
],
|
||||
phone: [
|
||||
{ required: true, message: '请输入电话', trigger: 'blur' },
|
||||
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的电话号码', trigger: 'blur' }
|
||||
],
|
||||
idCard: [
|
||||
{ required: true, message: '请输入身份证号', trigger: 'blur' },
|
||||
{ pattern: /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/, message: '请输入正确的身份证号码', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async submitForm() {
|
||||
this.$refs.tenantForm.validate(async (valid) => {
|
||||
if (valid) {
|
||||
try {
|
||||
await tenantApi.create(this.tenantForm)
|
||||
this.$message.success('添加成功')
|
||||
this.$router.push('/tenant/list')
|
||||
} catch (error) {
|
||||
this.$message.error('添加失败')
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
},
|
||||
resetForm() {
|
||||
this.$refs.tenantForm.resetFields()
|
||||
},
|
||||
goBack() {
|
||||
this.$router.push('/tenant/list')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tenant-add {
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
<template>
|
||||
<div class="tenant-edit">
|
||||
<el-card>
|
||||
<template slot="header">
|
||||
<div class="card-header">
|
||||
<span>编辑租客</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-form :model="tenantForm" :rules="rules" ref="tenantForm" label-width="100px">
|
||||
<el-form-item label="姓名" prop="name">
|
||||
<el-input v-model="tenantForm.name" placeholder="请输入姓名"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="电话" prop="phone">
|
||||
<el-input v-model="tenantForm.phone" placeholder="请输入电话"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="身份证号" prop="idCard">
|
||||
<el-input v-model="tenantForm.idCard" placeholder="请输入身份证号"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="submitForm">提交</el-button>
|
||||
<el-button @click="resetForm">重置</el-button>
|
||||
<el-button @click="goBack">返回</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { tenantApi } from '../../api/api'
|
||||
|
||||
export default {
|
||||
name: 'TenantEdit',
|
||||
data() {
|
||||
return {
|
||||
tenantForm: {
|
||||
id: '',
|
||||
name: '',
|
||||
phone: '',
|
||||
idCard: ''
|
||||
},
|
||||
rules: {
|
||||
name: [
|
||||
{ required: true, message: '请输入姓名', trigger: 'blur' },
|
||||
{ min: 2, max: 10, message: '长度在 2 到 10 个字符', trigger: 'blur' }
|
||||
],
|
||||
phone: [
|
||||
{ required: true, message: '请输入电话', trigger: 'blur' },
|
||||
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的电话号码', trigger: 'blur' }
|
||||
],
|
||||
idCard: [
|
||||
{ required: true, message: '请输入身份证号', trigger: 'blur' },
|
||||
{ pattern: /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/, message: '请输入正确的身份证号码', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.loadTenantData()
|
||||
},
|
||||
methods: {
|
||||
async loadTenantData() {
|
||||
try {
|
||||
const id = this.$route.params.id
|
||||
const tenant = await tenantApi.getById(id)
|
||||
if (tenant) {
|
||||
this.tenantForm = tenant
|
||||
}
|
||||
} catch (error) {
|
||||
this.$message.error('加载租客数据失败')
|
||||
}
|
||||
},
|
||||
async submitForm() {
|
||||
this.$refs.tenantForm.validate(async (valid) => {
|
||||
if (valid) {
|
||||
try {
|
||||
await tenantApi.update(this.tenantForm.id, this.tenantForm)
|
||||
this.$message.success('编辑成功')
|
||||
this.$router.push('/tenant/list')
|
||||
} catch (error) {
|
||||
this.$message.error('编辑失败')
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
},
|
||||
resetForm() {
|
||||
this.loadTenantData()
|
||||
},
|
||||
goBack() {
|
||||
this.$router.push('/tenant/list')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tenant-edit {
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,151 +0,0 @@
|
|||
<template>
|
||||
<div class="tenant-list">
|
||||
<el-card>
|
||||
<template slot="header">
|
||||
<div class="card-header">
|
||||
<span>租客档案</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-form :inline="true" :model="searchForm" class="search-form">
|
||||
<el-form-item label="姓名">
|
||||
<el-input v-model="searchForm.name" placeholder="请输入姓名"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="电话">
|
||||
<el-input v-model="searchForm.phone" placeholder="请输入电话"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSearch">搜索</el-button>
|
||||
<el-button @click="resetSearch">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-table :data="tenants" style="width: 100%" v-loading="isLoading">
|
||||
<el-table-column prop="id" label="ID" width="80"></el-table-column>
|
||||
<el-table-column prop="name" label="姓名"></el-table-column>
|
||||
<el-table-column prop="phone" label="电话"></el-table-column>
|
||||
<el-table-column prop="idCard" label="身份证号"></el-table-column>
|
||||
<el-table-column prop="createTime" label="创建时间" width="180"></el-table-column>
|
||||
|
||||
</el-table>
|
||||
<div class="pagination" style="margin-top: 20px;">
|
||||
<el-pagination
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="total"
|
||||
:page-size="pageSize"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:current-page="currentPage"
|
||||
@current-change="handleCurrentChange"
|
||||
@size-change="handleSizeChange"
|
||||
></el-pagination>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { tenantApi } from '../../api/api'
|
||||
|
||||
export default {
|
||||
name: 'TenantList',
|
||||
data() {
|
||||
return {
|
||||
tenants: [],
|
||||
total: 0,
|
||||
searchForm: {
|
||||
name: '',
|
||||
phone: ''
|
||||
},
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
isLoading: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.loadTenants()
|
||||
},
|
||||
methods: {
|
||||
async loadTenants() {
|
||||
this.isLoading = true
|
||||
try {
|
||||
const params = {
|
||||
name: this.searchForm.name,
|
||||
phone: this.searchForm.phone,
|
||||
page: this.currentPage,
|
||||
pageSize: this.pageSize
|
||||
}
|
||||
const response = await tenantApi.getAll(params)
|
||||
this.tenants = response.data || response
|
||||
this.total = response.total || 0
|
||||
} catch (error) {
|
||||
this.$message.error('加载租客数据失败')
|
||||
} finally {
|
||||
this.isLoading = false
|
||||
}
|
||||
},
|
||||
handleAdd() {
|
||||
this.$router.push('/tenant/add')
|
||||
},
|
||||
handleEdit(id) {
|
||||
this.$router.push(`/tenant/edit/${id}`)
|
||||
},
|
||||
async handleDelete(id) {
|
||||
this.$confirm('确定要删除这个租客吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(async () => {
|
||||
try {
|
||||
await tenantApi.delete(id)
|
||||
this.$message.success('删除成功')
|
||||
this.loadTenants()
|
||||
} catch (error) {
|
||||
this.$message.error('删除失败')
|
||||
}
|
||||
}).catch(() => {
|
||||
// 取消删除
|
||||
})
|
||||
},
|
||||
handleSearch() {
|
||||
this.currentPage = 1
|
||||
this.loadTenants()
|
||||
},
|
||||
resetSearch() {
|
||||
this.searchForm = {
|
||||
name: '',
|
||||
phone: ''
|
||||
}
|
||||
this.currentPage = 1
|
||||
this.loadTenants()
|
||||
},
|
||||
handleCurrentChange(val) {
|
||||
this.currentPage = val
|
||||
this.loadTenants()
|
||||
},
|
||||
handleSizeChange(val) {
|
||||
this.pageSize = val
|
||||
this.currentPage = 1
|
||||
this.loadTenants()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tenant-list {
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.search-form {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in New Issue