学习springBoot框架-开发一个酒店管理系统,来熟悉springboot框架语法~
想快速掌握一个框架,就是要不停的写项目,看别人的项目,让自己学习到的编程知识学以致用。今天就给大家分享我最近使用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 {@Autowiredprivate HotelMapper hotelMapper;@Autowiredprivate HotelBuildingMapper hotelBuildingMapper;@Autowiredprivate RoomMapper roomMapper;@Autowiredprivate MemberMapper memberMapper;@Autowiredprivate RoomRegistrationMapper roomRegistrationMapper;@Autowiredprivate DeviceRepairMapper deviceRepairMapper;@Autowiredprivate 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"><!-- 入住登记月度统计图表(近6个月) --><div class="chart-item"><div class="chart-title">入住登记月度统计(近6个月)</div><div ref="checkInMonthlyChart" class="chart"></div></div><!-- 安全检查月度统计图表(近6个月) --><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} <br/>{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} <br/>{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 {@ResourceIRoomRegistrationService 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-optionv-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-optionv-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-optionv-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-optionv-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-switchv-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-pickerv-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-pickerv-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-pickerv-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-pickerv-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: '', // 当前登记记录IDguestName: '',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.dataif (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}// 调用续期APIrenewRegistration(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.memberNamethis.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 = falsethis.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,可以从零基础开始尝试搭建一个系统。你也可以参考这个系统,并结合自己的想法,开发出一个更完善的管理系统。希望对你有所帮助。
为了更好的帮助到学习编程,但是没有想法的小伙伴,我把我写的这个项目搭建了一个预览地址,方便大家预览参考~
https://test.wwwoop.com/?s=jiu-dian-guan-li&no=Hotel-001&rand=0.4133917792569839