想快速掌握一个框架,就是要不停的写项目,看别人的项目,让自己学习到的编程知识学以致用。今天给大家分享最近使用springboot2.7 开发的一个前端后分离项目:酒店管理系统,来练习自己的编程技术。
java的版本是:21
springboot版本是:2.7
数据库操作:mybatis-plus
前端使用的是 vue2 + element-ui
mysql:8
写这个项目主要是练习从0到1自己搭建一个项目并完成需求开发。因为是练习项目,功能做的也不是很多,主要做了:首页统计 酒店管理 楼宇管理 房间管理 会员管理 开房登记 登记管理 设备维修
安全检查 管理员管理。
接下来跟大家分享一些页面效果:
首页:
后端代码:
package com.jsonll.base.controller;
import com.jsonll.base.core.R;
import com.jsonll.base.mapper.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 首页控制器
*/
@RestController
@RequestMapping(\"/home\")
public class HomeController extends BaseController {
@Autowired
private HotelMapper hotelMapper;
@Autowired
private HotelBuildingMapper hotelBuildingMapper;
@Autowired
private RoomMapper roomMapper;
@Autowired
private MemberMapper memberMapper;
@Autowired
private RoomRegistrationMapper roomRegistrationMapper;
@Autowired
private DeviceRepairMapper deviceRepairMapper;
@Autowired
private SafetyInspectionMapper safetyInspectionMapper;
/**
* 获取首页统计数据
* @return 统计数据
*/
@GetMapping(\"data\")
public R data(){
Map<String, Object> result = new HashMap();
// 酒店数量
long hotelCount = hotelMapper.selectCount(null);
// 楼宇数量
long buildingCount = hotelBuildingMapper.selectCount(null);
// 房间数量
long roomCount = roomMapper.selectCount(null);
// 会员数量
long memberCount = memberMapper.selectCount(null);
// 房间状态统计
List<Map<String, Object>> roomStatusStats = roomMapper.getRoomStatusStats();
// 入住登记统计(按月)
List<Map<String, Object>> checkInMonthlyStats = roomRegistrationMapper.getCheckInMonthlyStats();
// 设备维修统计
List<Map<String, Object>> repairStatusStats = deviceRepairMapper.getRepairStatusStats();
// 安全检查统计(按月)
List<Map<String, Object>> safetyMonthlyStats = safetyInspectionMapper.getSafetyMonthlyStats();
result.put(\"hotelCount\", hotelCount);
result.put(\"buildingCount\", buildingCount);
result.put(\"roomCount\", roomCount);
result.put(\"memberCount\", memberCount);
result.put(\"roomStatusStats\", roomStatusStats);
result.put(\"checkInMonthlyStats\", checkInMonthlyStats);
result.put(\"repairStatusStats\", repairStatusStats);
result.put(\"safetyMonthlyStats\", safetyMonthlyStats);
return R.successData(result);
}
}
前端代码:
<template>
<div class=\"home-container\">
<div class=\"count-cards\">
<div class=\"count-card\">
<div class=\"card-icon\">
<i class=\"el-icon-office-building\"></i>
</div>
<div class=\"card-content\">
<div class=\"card-value\">{{ statsData.hotelCount }}</div>
<div class=\"card-title\">酒店数量</div>
</div>
</div>
<div class=\"count-card\">
<div class=\"card-icon\">
<i class=\"el-icon-school\"></i>
</div>
<div class=\"card-content\">
<div class=\"card-value\">{{ statsData.buildingCount }}</div>
<div class=\"card-title\">楼宇数量</div>
</div>
</div>
<div class=\"count-card\">
<div class=\"card-icon\">
<i class=\"el-icon-house\"></i>
</div>
<div class=\"card-content\">
<div class=\"card-value\">{{ statsData.roomCount }}</div>
<div class=\"card-title\">房间数量</div>
</div>
</div>
<div class=\"count-card\">
<div class=\"card-icon\">
<i class=\"el-icon-user\"></i>
</div>
<div class=\"card-content\">
<div class=\"card-value\">{{ statsData.memberCount }}</div>
<div class=\"card-title\">会员数量</div>
</div>
</div>
</div>
<div class=\"chart-container\">
<div class=\"chart-left\">
<div class=\"chart-item\">
<div class=\"chart-title\">房间状态统计</div>
<div ref=\"roomStatusChart\" class=\"chart\"></div>
</div>
<div class=\"chart-item\">
<div class=\"chart-title\">设备维修状态统计</div>
<div ref=\"repairStatusChart\" class=\"chart\"></div>
</div>
</div>
<div class=\"chart-right\">
<div class=\"chart-item\">
<div class=\"chart-title\">入住登记月度统计(近6个月)</div>
<div ref=\"checkInMonthlyChart\" class=\"chart\"></div>
</div>
<div class=\"chart-item\">
<div class=\"chart-title\">安全检查月度统计(近6个月)</div>
<div ref=\"safetyMonthlyChart\" class=\"chart\"></div>
</div>
</div>
</div>
</div>
</template>
<script>
// 引入echarts
import * as echarts from \'echarts\'
import { getHomeData } from \'@/api/home\'
export default {
name: \'Home\',
data() {
return {
// 图表实例
roomStatusChartInstance: null,
checkInMonthlyChartInstance: null,
repairStatusChartInstance: null,
safetyMonthlyChartInstance: null,
// 统计数据
statsData: {
hotelCount: 0,
buildingCount: 0,
roomCount: 0,
memberCount: 0,
roomStatusStats: [],
checkInMonthlyStats: [],
repairStatusStats: [],
safetyMonthlyStats: []
}
}
},
mounted() {
// 初始化图表
this.initCharts()
// 获取数据
this.fetchData()
},
methods: {
// 初始化所有图表
initCharts() {
// 初始化房间状态图表
this.roomStatusChartInstance = echarts.init(this.$refs.roomStatusChart)
// 初始化入住登记月度图表
this.checkInMonthlyChartInstance = echarts.init(this.$refs.checkInMonthlyChart)
// 初始化设备维修状态图表
this.repairStatusChartInstance = echarts.init(this.$refs.repairStatusChart)
// 初始化安全检查月度图表
this.safetyMonthlyChartInstance = echarts.init(this.$refs.safetyMonthlyChart)
// 监听窗口大小变化,调整图表大小
window.addEventListener(\'resize\', this.resizeCharts)
},
// 调整所有图表大小
resizeCharts() {
this.roomStatusChartInstance && this.roomStatusChartInstance.resize()
this.checkInMonthlyChartInstance && this.checkInMonthlyChartInstance.resize()
this.repairStatusChartInstance && this.repairStatusChartInstance.resize()
this.safetyMonthlyChartInstance && this.safetyMonthlyChartInstance.resize()
},
// 获取统计数据
async fetchData() {
try {
const res = await getHomeData()
if (res.code === 1000 && res.data) {
this.statsData = res.data
// 更新图表
this.updateCharts()
}
} catch (error) {
console.error(\'获取首页数据失败\', error)
}
},
// 更新所有图表
updateCharts() {
this.updateRoomStatusChart()
this.updateCheckInMonthlyChart()
this.updateRepairStatusChart()
this.updateSafetyMonthlyChart()
},
// 更新房间状态图表
updateRoomStatusChart() {
// 房间状态映射
const statusMap = {
1: \'空闲\',
2: \'入住中\',
3: \'维修中\'
}
// 处理数据
const data = this.statsData.roomStatusStats.map(item => {
return {
name: statusMap[item.status] || `状态${item.status}`,
value: item.count
}
})
// 设置图表配置
const option = {
tooltip: {
trigger: \'item\',
formatter: \'{a}
{b}: {c} ({d}%)\'
},
legend: {
orient: \'vertical\',
left: 10,
data: data.map(item => item.name)
},
series: [
{
name: \'房间状态\',
type: \'pie\',
radius: [\'50%\', \'70%\'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 10,
borderColor: \'#fff\',
borderWidth: 2
},
label: {
show: false,
position: \'center\'
},
emphasis: {
label: {
show: true,
fontSize: \'18\',
fontWeight: \'bold\'
}
},
labelLine: {
show: false
},
data: data
}
]
}
// 更新图表
this.roomStatusChartInstance.setOption(option)
},
// 更新入住登记月度图表
updateCheckInMonthlyChart() {
// 处理数据
const months = this.statsData.checkInMonthlyStats.map(item => item.month)
const counts = this.statsData.checkInMonthlyStats.map(item => item.count)
// 设置图表配置
const option = {
tooltip: {
trigger: \'axis\',
axisPointer: {
type: \'shadow\'
}
},
grid: {
left: \'3%\',
right: \'4%\',
bottom: \'3%\',
containLabel: true
},
xAxis: {
type: \'category\',
data: months,
axisTick: {
alignWithLabel: true
}
},
yAxis: {
type: \'value\'
},
series: [
{
name: \'入住登记数\',
type: \'bar\',
barWidth: \'60%\',
data: counts,
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: \'#83bff6\' },
{ offset: 0.5, color: \'#188df0\' },
{ offset: 1, color: \'#188df0\' }
])
}
}
]
}
// 更新图表
this.checkInMonthlyChartInstance.setOption(option)
},
// 更新设备维修状态图表
updateRepairStatusChart() {
// 维修状态映射
const statusMap = {
1: \'正在维修\',
2: \'已维修\',
3: \'放弃维修\'
}
// 处理数据
const data = this.statsData.repairStatusStats.map(item => {
return {
name: statusMap[item.status] || `状态${item.status}`,
value: item.count
}
})
// 设置图表配置
const option = {
tooltip: {
trigger: \'item\',
formatter: \'{a}
{b}: {c} ({d}%)\'
},
legend: {
orient: \'vertical\',
left: 10,
data: data.map(item => item.name)
},
series: [
{
name: \'维修状态\',
type: \'pie\',
radius: \'50%\',
data: data,
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: \'rgba(0, 0, 0, 0.5)\'
}
}
}
]
}
// 更新图表
this.repairStatusChartInstance.setOption(option)
},
// 更新安全检查月度图表
updateSafetyMonthlyChart() {
// 处理数据
const months = this.statsData.safetyMonthlyStats.map(item => item.month)
const counts = this.statsData.safetyMonthlyStats.map(item => item.count)
// 设置图表配置
const option = {
tooltip: {
trigger: \'axis\'
},
grid: {
left: \'3%\',
right: \'4%\',
bottom: \'3%\',
containLabel: true
},
xAxis: {
type: \'category\',
boundaryGap: false,
data: months
},
yAxis: {
type: \'value\'
},
series: [
{
name: \'安全检查数\',
type: \'line\',
stack: \'总量\',
areaStyle: {},
emphasis: {
focus: \'series\'
},
data: counts
}
]
}
// 更新图表
this.safetyMonthlyChartInstance.setOption(option)
}
},
beforeDestroy() {
// 移除窗口大小变化监听
window.removeEventListener(\'resize\', this.resizeCharts)
// 销毁图表实例
this.roomStatusChartInstance && this.roomStatusChartInstance.dispose()
this.checkInMonthlyChartInstance && this.checkInMonthlyChartInstance.dispose()
this.repairStatusChartInstance && this.repairStatusChartInstance.dispose()
this.safetyMonthlyChartInstance && this.safetyMonthlyChartInstance.dispose()
}
}
</script>
<style lang=\"scss\" scoped>
.home-container {
padding: 20px;
// 数量统计卡片样式
.count-cards {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
margin-bottom: 20px;
.count-card {
width: 23%;
background-color: #fff;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
padding: 20px;
display: flex;
align-items: center;
margin-bottom: 15px;
.card-icon {
width: 60px;
height: 60px;
border-radius: 50%;
background-color: #f0f9eb;
display: flex;
justify-content: center;
align-items: center;
margin-right: 15px;
i {
font-size: 30px;
color: #67c23a;
}
}
&:nth-child(2) .card-icon {
background-color: #f2f6fc;
i {
color: #409eff;
}
}
&:nth-child(3) .card-icon {
background-color: #fdf6ec;
i {
color: #e6a23c;
}
}
&:nth-child(4) .card-icon {
background-color: #fef0f0;
i {
color: #f56c6c;
}
}
.card-content {
flex: 1;
.card-value {
font-size: 24px;
font-weight: bold;
color: #333;
line-height: 1.2;
}
.card-title {
font-size: 14px;
color: #999;
margin-top: 5px;
}
}
}
}
.chart-container {
display: flex;
justify-content: space-between;
.chart-left {
width: 38%;
.chart-item {
height: 400px;
margin-bottom: 20px;
background-color: #fff;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
padding: 20px;
.chart-title {
font-size: 18px;
font-weight: bold;
margin-bottom: 15px;
color: #333;
}
.chart {
width: 100%;
height: calc(100% - 35px);
}
}
}
.chart-right {
width: 60%;
.chart-item {
height: 400px;
margin-bottom: 20px;
background-color: #fff;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
padding: 20px;
.chart-title {
font-size: 18px;
font-weight: bold;
margin-bottom: 15px;
color: #333;
}
.chart {
width: 100%;
height: calc(100% - 35px);
}
}
}
}
}
@media screen and (max-width: 1200px) {
.home-container .chart-container .chart-item {
width: 100%;
}
.home-container .count-cards .count-card {
width: 48%;
}
}
@media screen and (max-width: 768px) {
.home-container .count-cards .count-card {
width: 100%;
}
}
</style>
登记入住页面效果:
package com.jsonll.base.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.jsonll.base.core.R;
import com.jsonll.base.entity.RoomRegistration;
import com.jsonll.base.request.RegistrationRequest;
import com.jsonll.base.service.IRoomRegistrationService;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.Map;
/**
* 房间登记 控制器
*/
@RestController
@RequestMapping(\"/registration\")
public class RoomRegistrationController {
@Resource
IRoomRegistrationService roomRegistrationService;
/**
* 分页查询房间登记列表
*/
@PostMapping(\"/page\")
public R page(@RequestBody RegistrationRequest request) {
Page<RoomRegistration> page = roomRegistrationService.pageList(request);
return R.successData(page);
}
/**
* 登记入住
*/
@PostMapping(\"/register\")
public R register(@RequestBody RegistrationRequest request) {
boolean result = roomRegistrationService.register(request);
return result ? R.success() : R.error(\"登记入住失败\");
}
/**
* 获取房间当前有效的登记信息
*/
@GetMapping(\"/getCurrentRegistration/{roomId}\")
public R getCurrentRegistration(@PathVariable Integer roomId) {
RoomRegistration registration = roomRegistrationService.getCurrentRegistration(roomId);
return R.successData(registration);
}
/**
* 续期入住
*/
@PostMapping(\"/renew\")
public R renew(@RequestBody RegistrationRequest request) {
boolean result = roomRegistrationService.renew(request);
return result ? R.success() : R.error(\"续期入住失败\");
}
/**
* 退房
*/
@PostMapping(\"/checkout/{roomId}\")
public R checkout(@PathVariable Integer roomId) {
boolean result = roomRegistrationService.checkout(roomId);
return result ? R.success() : R.error(\"退房失败\");
}
/**
* 获取房间登记详情(包含子表数据)
*/
@GetMapping(\"/detail/{id}\")
public R getDetail(@PathVariable Integer id) {
Map<String, Object> detailMap = roomRegistrationService.getRegistrationDetail(id);
return R.successData(detailMap);
}
}
前端代码:
<template>
<div class=\"checkin-container\">
<div class=\"search-container\">
<el-form :inline=\"true\" :model=\"searchForm\" class=\"search-form\">
<el-form-item label=\"酒店\">
<el-select v-model=\"searchForm.hotelId\" placeholder=\"请选择酒店\" @change=\"handleHotelChange\">
<el-option
v-for=\"item in hotelOptions\"
:key=\"item.id\"
:label=\"item.hotelName\"
:value=\"item.id\"
/>
</el-select>
</el-form-item>
<el-form-item label=\"楼宇\">
<el-select v-model=\"searchForm.buildingId\" placeholder=\"请选择楼宇\" @change=\"handleBuildingChange\" :disabled=\"!searchForm.hotelId\">
<el-option
v-for=\"item in buildingOptions\"
:key=\"item.id\"
:label=\"item.buildingName\"
:value=\"item.id\"
/>
</el-select>
</el-form-item>
<el-form-item label=\"楼层\">
<el-select v-model=\"searchForm.floorId\" placeholder=\"请选择楼层\" @change=\"handleSearch\" :disabled=\"!searchForm.buildingId\">
<el-option
v-for=\"item in floorOptions\"
:key=\"item.id\"
:label=\"item.floorName\"
:value=\"item.id\"
/>
</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>
</div>
<div class=\"room-container\">
<div class=\"room-list\">
<div
v-for=\"room in roomList\"
:key=\"room.id\"
class=\"room-item\"
:class=\"getRoomStatusClass(room.roomStatus)\"
>
<div class=\"room-icon\">
<i class=\"el-icon-house\"></i>
</div>
<div class=\"room-info\">
<div class=\"room-number\">{{ room.roomNumber }}</div>
<div class=\"room-name\">{{ room.roomName }}</div>
<div class=\"room-status\">{{ getRoomStatusText(room.roomStatus) }}</div>
<div class=\"room-features\">
<span class=\"feature-item\">
<i class=\"el-icon-sunny\"></i>
{{ room.isSouth==1?\'朝南\' : \'非朝南\' }}
</span>
<span class=\"feature-item\">
<i class=\"el-icon-view\"></i>
{{ room.hasWindow === 1 ? \'有窗\' : \'无窗\' }}
</span>
</div>
</div>
<div class=\"room-actions\">
<el-button
v-if=\"room.roomStatus == 1\"
type=\"primary\"
size=\"mini\"
@click=\"handleRegister(room)\"
>登记</el-button>
<template v-if=\"room.roomStatus == 2\">
<el-button type=\"warning\" size=\"mini\" @click=\"handleRenew(room)\">续期</el-button>
<el-button type=\"danger\" size=\"mini\" @click=\"handleCheckout(room)\">退房</el-button>
</template>
</div>
</div>
<div v-if=\"roomList.length === 0\" class=\"no-data\">
<span>暂无房间数据</span>
</div>
</div>
</div>
<el-dialog title=\"房间登记\" :visible.sync=\"registerDialogVisible\" width=\"900px\">
<el-form :model=\"registerForm\" :rules=\"registerRules\" ref=\"registerForm\" label-width=\"100px\" class=\"register-form\">
<el-row :gutter=\"20\">
<el-col :span=\"24\">
<el-form-item label=\"登记类型\" prop=\"registrationType\">
<el-radio-group v-model=\"registerForm.registrationType\">
<el-radio :label=\"1\">临时入驻</el-radio>
<el-radio :label=\"2\">会员入驻</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter=\"20\" v-if=\"registerForm.registrationType === 2\">
<el-col :span=\"12\">
<el-form-item label=\"会员\" prop=\"memberId\">
<el-select v-model=\"registerForm.memberId\" placeholder=\"请选择会员\" @change=\"handleMemberChange\" filterable>
<el-option
v-for=\"item in memberOptions\"
:key=\"item.id\"
:label=\"item.memberName\"
:value=\"item.id\"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter=\"20\">
<el-col :span=\"12\">
<el-form-item label=\"入住人姓名\" prop=\"guestName\">
<el-input v-model=\"registerForm.guestName\" placeholder=\"请输入入住人姓名\"></el-input>
</el-form-item>
</el-col>
<el-col :span=\"12\">
<el-form-item label=\"入住人电话\" prop=\"guestPhone\">
<el-input v-model=\"registerForm.guestPhone\" placeholder=\"请输入入住人电话\"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter=\"20\">
<el-col :span=\"12\">
<el-form-item label=\"身份证\" prop=\"idCard\">
<el-input v-model=\"registerForm.idCard\" placeholder=\"请输入入住人身份证\"></el-input>
</el-form-item>
</el-col>
<el-col :span=\"12\">
<el-form-item label=\"是否早餐\" prop=\"needBreakfast\">
<el-switch
v-model=\"registerForm.needBreakfast\"
:active-value=\"1\"
:inactive-value=\"0\"
></el-switch>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter=\"20\">
<el-col :span=\"12\">
<el-form-item label=\"入住开始时间\" prop=\"checkInTime\">
<el-date-picker
v-model=\"registerForm.checkInTime\"
type=\"datetime\"
placeholder=\"选择入住开始时间\"
style=\"width: 100%\"
></el-date-picker>
</el-form-item>
</el-col>
<el-col :span=\"12\">
<el-form-item label=\"入住到期时间\" prop=\"checkOutTime\">
<el-date-picker
v-model=\"registerForm.checkOutTime\"
type=\"datetime\"
placeholder=\"选择入住到期时间\"
style=\"width: 100%\"
></el-date-picker>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter=\"20\">
<el-col :span=\"24\">
<el-form-item label=\"随行人员\">
<div v-for=\"(companion, index) in registerForm.companions\" :key=\"index\" class=\"companion-item\">
<el-input v-model=\"companion.name\" placeholder=\"姓名\" style=\"width: 200px; margin-right: 10px;\"></el-input>
<el-input v-model=\"companion.idCard\" placeholder=\"身份证\" style=\"width: 300px; margin-right: 10px;\"></el-input>
<el-button type=\"danger\" icon=\"el-icon-delete\" circle @click=\"removeCompanion(index)\"></el-button>
</div>
<el-button type=\"primary\" icon=\"el-icon-plus\" @click=\"addCompanion\">添加随行人员</el-button>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter=\"20\">
<el-col :span=\"24\">
<el-form-item label=\"备注\">
<el-input type=\"textarea\" v-model=\"registerForm.remarks\" placeholder=\"请输入备注信息\"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter=\"20\">
<el-col :span=\"8\">
<el-form-item label=\"订单金额\">
<el-input-number v-model=\"registerForm.orderAmount\" :precision=\"2\" :step=\"10\" :min=\"0\"></el-input-number>
</el-form-item>
</el-col>
<el-col :span=\"8\">
<el-form-item label=\"支付金额\">
<el-input-number v-model=\"registerForm.paymentAmount\" :precision=\"2\" :step=\"10\" :min=\"0\"></el-input-number>
</el-form-item>
</el-col>
<el-col :span=\"8\">
<el-form-item label=\"优惠金额\">
<el-input-number v-model=\"registerForm.discountAmount\" :precision=\"2\" :step=\"10\" :min=\"0\"></el-input-number>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div slot=\"footer\" class=\"dialog-footer\">
<el-button @click=\"registerDialogVisible = false\">取 消</el-button>
<el-button type=\"primary\" @click=\"submitRegister\">确 定</el-button>
</div>
</el-dialog>
<el-dialog title=\"房间续期\" :visible.sync=\"renewDialogVisible\" width=\"1200px\">
<el-form :model=\"renewForm\" :rules=\"renewRules\" ref=\"renewForm\" label-width=\"120px\" class=\"renew-form\">
<el-row :gutter=\"20\">
<el-col :span=\"24\">
<el-form-item label=\"入住人姓名\">
<el-input v-model=\"renewForm.guestName\" disabled></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter=\"20\">
<el-col :span=\"24\">
<el-form-item label=\"入住人联系电话\">
<el-input v-model=\"renewForm.guestPhone\" disabled></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter=\"20\">
<el-col :span=\"24\">
<el-form-item label=\"入住开始时间\">
<el-date-picker
v-model=\"renewForm.checkInTime\"
type=\"datetime\"
placeholder=\"选择入住开始时间\"
style=\"width: 100%\"
disabled
></el-date-picker>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter=\"20\">
<el-col :span=\"24\">
<el-form-item label=\"入住到期时间\" prop=\"checkOutTime\">
<el-date-picker
v-model=\"renewForm.checkOutTime\"
type=\"datetime\"
placeholder=\"选择入住到期时间\"
style=\"width: 100%\"
></el-date-picker>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter=\"20\">
<el-col :span=\"8\">
<el-form-item label=\"订单金额\">
<el-input-number v-model=\"renewForm.orderAmount\" :precision=\"2\" :step=\"10\" :min=\"0\"></el-input-number>
</el-form-item>
</el-col>
<el-col :span=\"8\">
<el-form-item label=\"支付金额\">
<el-input-number v-model=\"renewForm.paymentAmount\" :precision=\"2\" :step=\"10\" :min=\"0\"></el-input-number>
</el-form-item>
</el-col>
<el-col :span=\"8\">
<el-form-item label=\"优惠金额\">
<el-input-number v-model=\"renewForm.discountAmount\" :precision=\"2\" :step=\"10\" :min=\"0\"></el-input-number>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div slot=\"footer\" class=\"dialog-footer\">
<el-button @click=\"renewDialogVisible = false\">取 消</el-button>
<el-button type=\"primary\" @click=\"submitRenew\">确 定</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { getRoomList, registerRoom, getMemberList, getHotelList, getBuildingList, getFloorList, getCurrentRegistration, renewRegistration, checkoutRoom } from \'@/api/registration\'
import { parseTime } from \'@/utils\'
export default {
name: \'Checkin\',
data() {
return {
// 搜索表单
searchForm: {
hotelId: \'\',
buildingId: \'\',
floorId: \'\'
},
// 下拉选项
hotelOptions: [],
buildingOptions: [],
floorOptions: [],
memberOptions: [],
// 房间列表
roomList: [],
// 登记弹窗
registerDialogVisible: false,
// 续期弹窗
renewDialogVisible: false,
// 登记表单
registerForm: {
registrationType: 1, // 1临时入驻 2会员入驻
memberId: null,
guestName: \'\',
guestPhone: \'\',
roomId: null,
checkInTime: parseTime(new Date(), \'{y}-{m}-{d} {h}:{i}:{s}\'),
checkOutTime: \'\',
idCard: \'\',
companions: [],
remarks: \'\',
needBreakfast: 0,
orderAmount: 0,
paymentAmount: 0,
discountAmount: 0
},
// 续期表单
renewForm: {
id: \'\', // 当前登记记录ID
guestName: \'\',
guestPhone: \'\',
checkInTime: \'\',
checkOutTime: \'\',
orderAmount: 0,
paymentAmount: 0,
discountAmount: 0
},
// 表单验证规则
registerRules: {
guestName: [
{ required: true, message: \'请输入入住人姓名\', trigger: \'blur\' }
],
guestPhone: [
{ required: true, message: \'请输入入住人电话\', trigger: \'blur\' },
{ pattern: /^1[3-9]d{9}$/, message: \'请输入正确的手机号码\', trigger: \'blur\' }
],
idCard: [
{ required: true, message: \'请输入入住人身份证\', trigger: \'blur\' },
],
checkInTime: [
{ required: true, message: \'请选择入住时间\', trigger: \'change\' }
],
checkOutTime: [
{ required: true, message: \'请选择到期时间\', trigger: \'change\' }
],
memberId: [
{ required: true, message: \'请选择会员\', trigger: \'change\' }
]
},
// 续期表单验证规则
renewRules: {
checkOutTime: [
{ required: true, message: \'请选择入住到期时间\', trigger: \'change\' }
]
}
}
},
created() {
this.fetchHotelList();
this.handleSearch();
},
methods: {
// 获取酒店列表
fetchHotelList() {
getHotelList().then(response => {
if (response.code === 1000) {
this.hotelOptions = response.data.records || []
}
})
},
// 获取楼宇列表
fetchBuildingList(hotelId) {
getBuildingList({ hotelId }).then(response => {
if (response.code === 1000) {
this.buildingOptions = response.data.records || []
}
})
},
// 获取楼层列表
fetchFloorList(buildingId) {
getFloorList({ buildingId }).then(response => {
if (response.code === 1000) {
this.floorOptions = response.data.records || []
}
})
},
// 获取房间列表
fetchRoomList() {
const params = { ...this.searchForm }
getRoomList(params).then(response => {
if (response.code === 1000) {
this.roomList = response.data.records || []
}
})
},
// 获取会员列表
fetchMemberList() {
getMemberList().then(response => {
if (response.code === 1000) {
this.memberOptions = response.data.records || []
}
})
},
// 酒店选择变化
handleHotelChange(val) {
this.searchForm.buildingId = \'\'
this.searchForm.floorId = \'\'
this.buildingOptions = []
this.floorOptions = []
if (val) {
this.fetchBuildingList(val)
// 选择酒店后自动触发房间查询
this.fetchRoomList()
}
},
// 楼宇选择变化
handleBuildingChange(val) {
this.searchForm.floorId = \'\'
this.floorOptions = []
if (val) {
this.fetchFloorList(val)
// 选择楼宇后自动触发房间查询
this.fetchRoomList()
}
},
// 搜索
handleSearch() {
this.fetchRoomList()
},
// 重置搜索
resetSearch() {
this.searchForm = {
hotelId: \'\',
buildingId: \'\',
floorId: \'\'
}
this.buildingOptions = []
this.floorOptions = []
this.roomList = []
},
// 获取房间状态样式类
getRoomStatusClass(status) {
// 将字符串类型的状态转换为数字
const statusNum = parseInt(status)
switch (statusNum) {
case 1: return \'room-free\'
case 2: return \'room-occupied\'
case 3: return \'room-maintenance\'
default: return \'\'
}
},
// 获取房间状态文本
getRoomStatusText(status) {
// 将字符串类型的状态转换为数字
const statusNum = parseInt(status)
switch (statusNum) {
case 1: return \'空闲\'
case 2: return \'入住中\'
case 3: return \'维修中\'
default: return \'未知\'
}
},
// 处理登记
handleRegister(room) {
this.registerForm = {
registrationType: 1,
memberId: null,
guestName: \'\',
guestPhone: \'\',
roomId: room.id,
checkInTime: parseTime(new Date(), \'{y}-{m}-{d} {h}:{i}:{s}\'),
checkOutTime: \'\',
idCard: \'\',
companions: [],
remarks: \'\',
needBreakfast: 0
}
this.fetchMemberList()
this.registerDialogVisible = true
},
// 处理续期
handleRenew(room) {
console.log(\'续期按钮被点击\', room)
this.currentRoom = room
// 获取当前房间的登记信息
getCurrentRegistration(room.id).then(response => {
if (response.code === 1000) {
const registration = response.data
if (registration) {
// 填充续期表单
this.renewForm = {
id: registration.id,
guestName: registration.guestName,
guestPhone: registration.guestPhone,
roomId: registration.roomId,
checkInTime: registration.checkInTime,
checkOutTime: new Date(registration.checkOutTime),
orderAmount: 0,
paymentAmount: 0,
discountAmount: 0
}
// 显示续期弹窗
this.renewDialogVisible = true
} else {
this.$message.warning(\'该房间没有有效的登记信息\')
}
} else {
this.$message.error(response.msg || \'获取登记信息失败\')
}
}).catch(err => {
console.error(\'获取登记信息失败\', err)
this.$message.error(\'获取登记信息失败\')
})
},
// 提交续期表单
submitRenew() {
this.$refs.renewForm.validate(valid => {
if (valid) {
// 检查续期时间是否大于当前时间
const now = new Date()
if (new Date(this.renewForm.checkOutTime) <= now) {
this.$message.warning(\'续期时间必须大于当前时间\')
return
}
// 构建续期请求参数
const renewRequest = {
id: this.renewForm.id,
roomId: this.currentRoom.id,
checkOutTime: parseTime(this.renewForm.checkOutTime, \'{y}-{m}-{d} {h}:{i}:{s}\'),
orderAmount: this.renewForm.orderAmount,
paymentAmount: this.renewForm.paymentAmount,
discountAmount: this.renewForm.discountAmount
}
// 调用续期API
renewRegistration(renewRequest).then(response => {
if (response.code === 1000) {
this.$message.success(\'房间续期成功\')
this.renewDialogVisible = false
// 刷新房间列表
this.fetchRoomList()
} else {
this.$message.error(response.msg || \'房间续期失败\')
}
}).catch(err => {
console.error(\'房间续期失败\', err)
this.$message.error(\'房间续期失败\')
})
} else {
return false
}
})
},
// 处理退房
handleCheckout(room) {
// 显示确认对话框
this.$confirm(`确定要为${room.roomNumber}房间办理退房吗?`, \'退房确认\', {
confirmButtonText: \'确定\',
cancelButtonText: \'取消\',
type: \'warning\'
}).then(() => {
// 用户确认退房,调用退房接口
checkoutRoom(room.id).then(response => {
if (response.code === 1000) {
this.$message.success(\'退房成功\')
// 刷新房间列表
this.fetchRoomList()
} else {
this.$message.error(response.msg || \'退房失败\')
}
}).catch(err => {
console.error(\'退房失败\', err)
this.$message.error(\'退房失败\')
})
}).catch(() => {
// 用户取消退房
this.$message.info(\'已取消退房操作\')
})
},
// 会员选择变化
handleMemberChange(memberId) {
if (memberId) {
const member = this.memberOptions.find(item => item.id === memberId)
if (member) {
this.registerForm.guestName = member.memberName
this.registerForm.guestPhone = member.contact
}
}
},
// 添加随行人员
addCompanion() {
this.registerForm.companions.push({ name: \'\', idCard: \'\' })
},
// 移除随行人员
removeCompanion(index) {
this.registerForm.companions.splice(index, 1)
},
// 提交登记
submitRegister() {
this.$refs.registerForm.validate(valid => {
if (valid) {
// 处理随行人员数据
const companions = this.registerForm.companions.filter(item => item.name && item.idCard)
const params = {
...this.registerForm,
companions: JSON.stringify(companions)
}
params.checkOutTime=parseTime(params.checkOutTime, \'{y}-{m}-{d} {h}:{i}:{s}\'),
registerRoom(params).then(response => {
if (response.code === 1000) {
this.$message.success(\'登记成功\')
this.registerDialogVisible = false
this.fetchRoomList() // 刷新房间列表
} else {
this.$message.error(response.msg || \'登记失败\')
}
})
}
})
}
}
}
</script>
<style scoped>
.checkin-container {
height: 100%;
display: flex;
flex-direction: column;
}
.search-container {
padding: 15px;
background-color: #fff;
border-radius: 4px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
margin-bottom: 15px;
}
.room-container {
flex: 1;
background-color: #fff;
border-radius: 4px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
padding: 15px;
overflow-y: auto;
}
.room-list {
display: flex;
flex-wrap: wrap;
gap: 15px;
}
.room-item {
width: 220px;
height: 220px;
border-radius: 8px;
padding: 15px;
display: flex;
flex-direction: column;
justify-content: space-between;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
transition: all 0.3s;
position: relative;
overflow: hidden;
}
.room-item:hover {
transform: translateY(-5px);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
}
.room-free {
background-color: #f0f9eb;
border: 1px solid #e1f3d8;
}
.room-occupied {
background-color: #fef0f0;
border: 1px solid #fde2e2;
}
.room-maintenance {
background-color: #f4f4f5;
border: 1px solid #e9e9eb;
}
.room-icon {
text-align: center;
margin-bottom: 10px;
}
.room-icon i {
font-size: 28px;
color: #409EFF;
}
.room-info {
text-align: center;
}
.room-number {
font-size: 18px;
font-weight: bold;
margin-bottom: 5px;
}
.room-name {
font-size: 14px;
color: #606266;
margin-bottom: 5px;
}
.room-status {
display: inline-block;
padding: 2px 8px;
font-size: 12px;
border-radius: 10px;
background-color: #f0f0f0;
margin-bottom: 8px;
}
.room-features {
display: flex;
justify-content: center;
gap: 10px;
margin-top: 5px;
font-size: 12px;
}
.feature-item {
display: flex;
align-items: center;
color: #606266;
}
.feature-item i {
margin-right: 3px;
color: #409EFF;
}
.room-free .room-status {
background-color: #67c23a;
color: #fff;
}
.room-occupied .room-status {
background-color: #f56c6c;
color: #fff;
}
.room-maintenance .room-status {
background-color: #909399;
color: #fff;
}
.room-actions {
display: flex;
justify-content: center;
gap: 10px;
margin-top: 10px;
}
.no-data {
width: 100%;
height: 200px;
display: flex;
justify-content: center;
align-items: center;
color: #909399;
}
.companion-item {
display: flex;
margin-bottom: 10px;
align-items: center;
}
.companion-input {
margin-right: 10px;
}
</style>
如果你是刚开始学习 Java,可以从零基础开始尝试搭建一个系统。你也可以参考这个系统,并结合自己的想法,开发出一个更完善的管理系统。希望对你有所帮助
为了更好的帮助到学习编程,但是没有想法的小伙伴,我把我写的这个项目搭建了一个预览地址,方便大家预览参考~
test.wwwoop.com/?s=jiu-dian…



还没有评论呢,快来抢沙发~