开发工具:IDEA
服务器:Tomcat9.0, jdk1.8
项目构建:maven
数据库:mysql5.7
系统分前后台,项目采用前后端分离
前端技术:vue+elementUI
服务端技术:springboot+mybatis
本系统功能包括:
一、前台功能:
1、用户注册模块:用户可以输入用户名、密码、昵称、姓名、手机来 进行注册。
2、用户登录模块:用户可以根据用户名、密码进行登录。
3、前台首页模块:包括广告、房间信息、酒店新闻。
4、酒店新闻模块:展示酒店标题,创建时间,详情。
5、酒店预订模块:展示了客房的详情以及评价,用户输入入住日期以及入住天数进行预订。
6、用户信息模块:展示了用户的头像、昵称、姓名、手机号码、性别并可进行修改。
7、个人订单模块:展示了全部订单、待付款订单、待入住、已入住,已退房的客房信息。
二、后台功能:
1、管理员登录模块:管理员可以根据用户名、密码进行登录。
2、统计分析模块:管理员可以直观的查看近一周的客房数量、订单数量、用户数量。
3、会员管理模块:管理员可以查看用户的基本信息。
4、广告管理模块:管理员可以对酒店广告进行新增修改删除。
5、分类管理模块:管理员可以对客房的分类进行新增修改删除。
6、客房管理模块: 管理员可以对客房信息行增删改查。
7、房间管理模块:管理员可以查看目前所有房间的状态并对其进行增删改查。
8、订单管理模块:管理员可以找到用户提交的预订信息并进行开房和退房、查看的操作。
9、评价管理模块:管理员可以对用户的评价进行查询删除。
10、新闻管理模块:管理员可以对用户端新闻进行增删改查。
11、管理员管理模块:管理员可以对管理员的账号行增删改查。
文档截图:
前台截图:
后台截图:
java
package com.wfuhui.modules.order.controller;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.wfuhui.common.utils.DateUtils;
import com.wfuhui.common.utils.Query;
import com.wfuhui.common.utils.R;
import com.wfuhui.modules.room.service.HouseService;
import com.wfuhui.modules.member.service.MemberService;
import com.wfuhui.modules.order.entity.OrderEntity;
import com.wfuhui.modules.order.service.OrderService;
/**
* 订单
*
*/
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private OrderService orderService;
@Autowired
private HouseService roomService;
@Autowired
private MemberService memberService;
/**
* 列表
*/
@RequestMapping("/list")
public R list(@RequestParam Map<String, Object> params){
//查询列表数据
Query query = new Query(params);
List<OrderEntity> orderList = orderService.queryList(query);
int total = orderService.queryTotal(query);
return R.ok().put("rows", orderList).put("total", total);
}
/**
* 信息
*/
@RequestMapping("/info/{id}")
public R info(@PathVariable("id") Integer id){
OrderEntity order = orderService.queryObject(id);
return R.ok().put("order", order);
}
/**
* 保存
*/
@RequestMapping("/save")
public R save(@RequestBody OrderEntity order){
orderService.save(order);
return R.ok();
}
/**
* 修改
*/
@RequestMapping("/update")
public R update(@RequestBody OrderEntity order){
orderService.update(order);
return R.ok();
}
/**
* 删除
*/
@RequestMapping("/delete")
public R delete(@RequestBody Integer[] ids){
orderService.deleteBatch(ids);
return R.ok();
}
/**
* 预订房间
* @param orderId
* @param roomNumber
* @return
*/
@RequestMapping("/orderRoom/{id}")
public R orderRoom(@PathVariable("id")Integer id, String roomNumber) {
OrderEntity order = new OrderEntity();
order.setId(id);
order.setOrderStatus(3);
order.setRoomNumber(roomNumber);
orderService.orderRoom(order);
return R.ok();
}
/**
* 退房
* @param orderId
* @param roomNumber
* @return
*/
@RequestMapping("/returnRoom/{id}")
public R returnRoom(@PathVariable("id")Integer id) {
OrderEntity order = new OrderEntity();
order.setId(id);
order.setOrderStatus(4);
orderService.returnRoom(order);
//减少已售
orderService.delHouseVolume(order.getId());
return R.ok();
}
}
html
<template>
<div>
<el-container>
<el-header><mainHeader></mainHeader>
</el-header>
<el-container>
<mainSidebar :active="active"></mainSidebar>
<el-main>
<div v-if="showList">
<el-form :inline="true" :model="q" class="demo-form-inline">
<el-form-item label="客房名称">
<el-input v-model="q.houseName" placeholder="客房名称"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="query">查询</el-button>
<el-button type="success" @click="add">新增</el-button>
<el-button type="warning" @click="update">修改</el-button>
<el-button type="danger" @click="del">删除</el-button>
</el-form-item>
</el-form>
<el-table
:data="houseList"
style="width: 100%"
@selection-change="handleSelectionChange">
<el-table-column
type="selection"
width="55">
</el-table-column>
<el-table-column
prop="picUrl"
label="图片"
>
<template slot-scope="scope">
<el-image
style="width: 100px; height: 80px"
:src="scope.row.picUrl"></el-image>
</template>
</el-table-column>
<el-table-column
prop="houseName"
label="客房名称"
>
</el-table-column>
<el-table-column
prop="category.categoryName"
label="分类">
</el-table-column>
<el-table-column
prop="price"
label="价格">
</el-table-column>
<el-table-column
prop="stock"
label="房间数量">
</el-table-column>
<el-table-column
prop="createTime"
label="创建时间">
</el-table-column>
</el-table>
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="q.page"
:page-sizes="[10, 50, 100]"
:page-size="q.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination>
</div>
<div v-if="!showList">
<el-form :model="house" label-width="120px">
<el-form-item label="图片">
<el-upload
class="avatar-uploader"
action="http://127.0.0.1:10001/api/fileupload/upload"
list-type="picture-card"
multiple
:file-list="fileList"
:on-success="handleAvatarSuccess"
:on-remove="imgRemove"
:before-upload="beforeAvatarUpload">
<i class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
</el-form-item>
<el-form-item label="客房名称">
<el-input v-model="house.houseName"></el-input>
</el-form-item>
<el-form-item label="分类">
<el-select v-model="house.categoryId" placeholder="请选择">
<el-option
v-for="item in categoryList"
:key="item.id"
:label="item.categoryName"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="价格">
<el-input v-model="house.price"></el-input>
</el-form-item>
<el-form-item label="房间数量">
<el-input v-model="house.stock"></el-input>
</el-form-item>
<el-form-item label="详情">
<quill-editor ref="text" v-model="house.describe" :options="editorOption" style="height: 300px; margin-bottom: 50px;" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">保存</el-button>
<el-button @click="cancel">取消</el-button>
</el-form-item>
</el-form>
</div>
</el-main>
</el-container>
</el-container>
</div>
</template>
<script>
import { quillEditor } from 'vue-quill-editor'
import {quillRedefine} from 'vue-quill-editor-upload'
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.bubble.css'
import 'quill/dist/quill.snow.css'
import mainHeader from '../../../components/admin-main-header'
import mainSidebar from '../../../components/admin-main-sidebar'
export default {
name: "House",
data() {
return {
total: 0,
houseList: [],
categoryList: [],
q: {
houseName: '',
page: this.currentPage,
limit: this.pageSize
},
asideStyle: {
height: '500px'
},
house: {
picUrl: ''
},
active: '5',
showList: true,
editorOption: {},
fileList: [],
ids: []
};
},
components: {
quillEditor,
quillRedefine,
mainHeader,
mainSidebar
},
methods:{
handleSizeChange(e){
//console.log(e)
this.q.limit = e;
this.query();
},
handleCurrentChange(e){
//console.log(e)
this.q.page = e;
this.query();
},
query(){
var that = this;
this.$axios.get(this.domain + '/house/list',
{ headers:{ token: localStorage.getItem("atoken") },
params: that.q
}).then(function(res){
if(res.data.code == 0){//成功
that.houseList = res.data.rows
that.total = res.data.total
}else{
}
})
},
queryCategory(){
var that = this;
this.$axios.get(this.domain + '/category/listAll',
{
headers:{ token: localStorage.getItem("atoken") }
}).then(function(res){
if(res.data.code == 0){//成功
that.categoryList = res.data.categoryList
}else{
}
})
},
queryHouse(id){
var that = this;
this.$axios.get(this.domain + '/house/info/'+id,
{
headers:{ token: localStorage.getItem("atoken") }
}).then(function(res){
if(res.data.code == 0){//成功
that.house = res.data.house
that.initImage(res.data.house.picUrls)
}else{
}
})
},
initImage(picUrls){
this.fileList=[];
let urlList = picUrls.map(function(item, index){
return {url: picUrls[index]}
});
for (let url in urlList){
this.fileList.push(urlList[url]);
}
},
onSubmit(e){
var that = this;
var data = this.house;
data.picUrls = this.fileList.map(function(item, index){
return item.url
})
data.picUrl = data.picUrls[0]
var action = data.id == null ? "save" : "update";
this.$axios.post(this.domain + '/house/'+action,
data,
{
headers: {'token': localStorage.getItem("atoken")}
}
).then(function(res){
//console.log(res)
if(res.data.code == 0){//成功
that.showList = true;
that.query();
}else{
that.errorMsg = res.data.msg;
that.$message.error(res.data.msg);
}
})
},
add(){
this.showList = false
this.fileList = []
this.house = {
picUrls: []
}
},
cancel(){
this.showList = true
},
update(){
if(this.ids.length != 1){
this.$message.info("请选择一条数据");
return;
}
this.showList = false;
this.queryHouse(this.ids[0]);
},
del(){
if(this.ids.length == 0){
this.$message.info("请选择数据");
return;
}
var that = this;
this.$axios.post(this.domain + '/house/delete',
that.ids, {
headers: {'token': localStorage.getItem("atoken")}
}).then(function(res){
//console.log(res)
if(res.data.code == 0){//成功
that.query();
}else{
that.errorMsg = res.data.msg;
that.$message.error(res.data.msg);
}
})
},
handleAvatarSuccess(e){
this.fileList.push({
url: e.url
});
},
imgRemove(file, fileList){
this.fileList = fileList;
},
beforeAvatarUpload(e){
},
handleSelectionChange(e){
var ids = [];
for(var i = 0; i < e.length; i++){
ids.push(e[i].id)
}
this.ids = ids;
}
},
created(){
var docHeight = document.documentElement.clientHeight;
this.asideStyle.height = docHeight - 76 + "px";
var user = localStorage.getItem("auser");
if(user){
this.user = JSON.parse(user);
}else{
this.$router.push("admin_login");
}
this.query();
this.queryCategory();
this.editorOption = quillRedefine(
{
// 图片上传的设置
uploadConfig: {
action: this.domain + '/api/fileupload/upload', // 必填参数 图片上传地址
size: 500, // 可选参数 图片限制大小,单位为Kb, 1M = 1024Kb
accept: 'image/png, image/gif, image/jpeg, image/bmp, image/x-icon', // 可选参数 可上传的图片格式
// 必选参数 res是一个函数,函数接收的response为上传成功时服务器返回的数据
// 你必须把返回的数据中所包含的图片地址 return 回去
res: (respnse) => {
return respnse.url
},
name: 'file' // 图片上传参数名
}
}
)
//console.log(this.editorOption)
}
};
</script>
<style scoped>
</style>