1、项目背景
1.1需求分析
和团委老师交流时得知,目前学校使用的教室管理(预约)系统使用比较繁琐。于是希望制作一款简单易用的教室预约管理系统。
1.2项目要求
1.教室/场地空闲情况的查看。
2.学生提交对于空闲教室/场地的预约申请。
3.老师审批对于空闲教室/场地的预约申请。
4.当老师提交预约申请时,若教室/场地未被占用则申请自动通过。
1.3设计思路
学生端:
1.学生用户预约教室/场地时从微信小程序页面提交姓名、学号以及需要预约的教室/场地编号、用途和待审批老师的工号。
2.微信小程序与数据库建立连接,服务器将请求数据存入数据库表格,将预约目的发送给对应工号的老师,返回教室/场地状态。
3.小程序通过url获取数据库信息,用户得知是否预约成功。
教师端:
1.老师用户预约教室/场地时从微信小程序页面提交姓名、工号以及需要预约的教室/场地。
2.微信小程序微信小程序与数据库建立连接,服务器将请求数据存入数据库表格,检验此时制定教室/场地的使用状态,若未被占用,则数据库向小程序发送申请通过的数据,完成预约。
1.4系统特点
系统具有实时性、可靠性,且方便快捷;学生和教师的权限不同,学生和老师都能查看教室/场地空闲情况和提交预约申请,教师有权对学生提交的预约申请进行审批。
2、成果展示
基于微信小程序的教室管理系统
3、具体实现
3.1前端
数据存储:
小程序首页效果图:
index.wxml
<view class="first_line">
<view class="searchbox">
<input placeholder="请输入" bindinput="getInput" value="{{search_val}}"></input>
<icon type="cancel" bindtap="now_cancel" color="grey" size="30" wx:if="{{isCancel}}"></icon>
<view class="btn blue" wx:if="{{isSearch}}" bindtap="now_search">
<text>搜索</text>
</view>
</view>
</view>
<view>
<text>预约空间:</text>
</view>
<view class="hengxiang_button">
<view class="long_button">
<text>教室:</text>
</view>
<view class=long_button">
<navigator url="../building_N/building_N">
<text>N楼</text>
</navigator>
</view>
<view class="long_button">
<text bindtap="click_NULL">S楼</text>
</view>
<view class="long_button">
<text bindtap="click_NULL">s1楼</text>
</view>
<view class="long_button">
<text bindtap="click_NULL">s2楼</text>
</view>
<view class=long_button">
<text bindtap="click_NULL">s3楼</text>
</view>
</view>
<view class="tab"></view>
<view class='fenlei'>
<text>周次</text>
<!-- 下拉框 -->
<view class='select_box'>
<view class='select' catchtap='selectTaps1'>
<text class='select_text'>第{{selectDatas1[indexs1]}}周</text>
<!-- <image class='words_img' src='../../images/sanjiao.png'></image> -->
<image class='select_img {{shows1&&"select_img_rotate"}}' src='../../icons/倒三角形.png'></image>
</view>
<view class='option_box' style='height:{{shows1?(selectDatas1.length>5?300:selectDatas1.length*60):0}}rpx;'>
<text class='option' style='{{indexs1==selectDatas1.length-1&&"border:0;"}}' wx:for='{{selectDatas1}}' wx:key='this' data-index='{{index}}' catchtap='optionTaps1'>第{{item}}周</text>
</view>
</view>
</view>
<view class="tab"></view>
<view class='fenlei'>
<text>星期</text>
<!-- 下拉框 -->
<view class='select_box'>
<view class='select' catchtap='selectTaps2'>
<text class='select_text'>星期{{selectDatas2[indexs2]}}</text>
<!-- <image class='words_img' src='../../images/sanjiao.png'></image> -->
<image class='select_img {{shows2&&"select_img_rotate"}}' src='../../icons/倒三角形.png'></image>
</view>
<view class='option_box' style='height:{{shows2?(selectDatas2.length>5?300:selectDatas2.length*60):0}}rpx;'>
<text class='option' style='{{indexs2==selectDatas2.length-1&&"border:0;"}}' wx:for='{{selectDatas2}}' wx:key='this' data-index='{{index}}' catchtap='optionTaps2'>星期{{item}}</text>
</view>
</view>
</view>
<view class="tab"></view>
<view class='fenlei'>
<text>节次</text>
<!-- 下拉框 -->
<view class='select_box'>
<view class='select' catchtap='selectTaps3'>
<text class='select_text'>第{{selectDatas3[indexs3]}}节课</text>
<!-- <image class='words_img' src='../../images/sanjiao.png'></image> -->
<image class='select_img {{shows3&&"select_img_rotate"}}' src='../../icons/倒三角形.png'></image>
</view>
<view class='option_box' style='height:{{shows3?(selectDatas3.length>5?300:selectDatas3.length*60):0}}rpx;'>
<text class='option' style='{{indexs3==selectDatas3.length-1&&"border:0;"}}' wx:for='{{selectDatas3}}' wx:key='this' data-index='{{index}}' catchtap='optionTaps3'>第{{item}}节课</text>
</view>
</view>
</view>
<view class="btn blue" bindtap="now_search_time">
<text>搜索</text>
</view>
<view class="tab"></view>
index.wxss
.first_line{
display: flex;
justify-content: center;
}
.tab{
display: flex;
height: 10rpx;
}
.searchbox{
display: flex;
flex-direction: row;
border:1px solid #fc86ad;
border-radius: 20px;
width: max-content;
height:43px;
align-items: center;
}
input{
margin-left: 12px;
font-size: 20px;
width:150px;
}
.btn {
display: inline-block;
margin: 1px;
padding: 0.7em 1em;
background:transparent;
border: 1px;
border-radius: 20px;
font-weight: 200;
text-align: center;
box-shadow: 2px 2px 4px #aaa,0px 1px 2px #fc86ad;
}
.btn text {
background: -webkit-linear-gradient(left, #aaa, #fc86ad);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.hengxiang_button{
flex-direction: column;
justify-self: center;
width: auto;
}
.long_button{
display: inline-flex;
margin: 1px;
padding: 0.7em 1em;
background:transparent;
border: 1px;
border-radius: 20px;
font-weight: 200;
text-align: center;
box-shadow: 1px 2px 8px #aaa,1px 1px 2px #fc86ad;
}
.tab{
display: flex;
height: 30rpx;
}
.fenlei{
margin: 0 25rpx;
height: 90rpx;
line-height: 90rpx;
border-bottom: 1rpx solid #e6e6e6;
display: flex;
align-items: center;
}
.fenlei text{
font-size: 30rpx;
color: #999999;
margin-left: 15rpx;
}
/* 下拉框 */
.select_box {
background: #fff;
width: 620rpx;
/* margin: 0 auto; */
height: 90rpx;
line-height: 90rpx;
text-align: left;
position: relative;
}
.select {
box-sizing: border-box;
width: 100%;
height: 86rpx;
/* border: 1px solid #efefef; */
border-radius: 8rpx;
display: flex;
align-items: center;
padding: 0 20rpx;
}
.select_text {
font-size: 28rpx;
flex: 1;
color: rgb(102, 102, 102);
line-height: 86rpx;
height: 86rpx;
}
.select_img {
width: 40rpx;
height: 40rpx;
display: block;
transition: transform 0.3s;
}
.select_img_rotate {
transform: rotate(180deg);
}
.option_box {
position: absolute;
top: 86rpx;
width: 100%;
/* border: 1px solid #efefef; */
box-sizing: border-box;
height: 0;
overflow-y: auto;
border-top: 0;
background: #fff;
transition: height 0.3s;
z-index: 100;
}
.option {
display: block;
line-height: 40rpx;
font-size: 28rpx;
border-bottom: 1px solid #efefef;
padding: 10rpx;
color: rgb(102, 102, 102);
}
index.js
Page({
data: {
isSearch:false,
isCancel:false,
search_val:'',
/*学生数组 */
StudentID:[],
StudentName:[],
StudentPassword:[],
shows1:false, //控制下拉列表的显示隐藏,false隐藏、true显示
shows2:false,
shows3:false,
selectDatas1: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16], //下拉列表的数据
selectDatas2: [1,2,3,4,5,6,7],
selectDatas3: [1,2,3,4,5,6,7,8,9,10],
indexs1: 0, //选择的下拉列 表下标,
indexs2: 0,
indexs3: 0,
Weeks:'',
Weekdays:'',
Times:'',
},
getInput:function(e)
{
//获取搜索框输入的内容
this.setData({
search_val:e.detail.value,
})
//将搜索内容存入缓存
wx.setStorageSync(
"search_room",e.detail.value
);
if(this.data.search_val.length>0){
this.setData({
isSearch:true,
isCancel:true,
})
}else{
this.setData({
isSearch:false,
isCancel:false,
})
}
},
click_NULL:function(){
wx.showModal({
title: '功能尚未开发',
content: '敬请期待!',
success: function (res) {
if (res.confirm) {
// console.log('用户点击确定')
wx.removeStorageSync(Object);
//页面跳转
wx.redirectTo({
url: '../index/index',
})
} else if (res.cancel) {
console.log('用户点击取消')
}
}
})
},
now_search:function()
{
wx.showModal({
title: '功能尚未开发',
content: '敬请期待!',
success: function (res) {
if (res.confirm) {
// console.log('用户点击确定')
wx.removeStorageSync(Object);
//页面跳转
wx.redirectTo({
url: '../index/index',
})
} else if (res.cancel) {
console.log('用户点击取消')
}
}
})
this.setData({
isSearch:true,
isCancel:true,
})
},
now_cancel:function()
{
this.setData({
search_val:'',
isSearch:false,
isCancel:false,
})
},
// 点击下拉显示框
selectTaps1() {
this.setData({
shows1: !this.data.shows1,
});
},
selectTaps2() {
this.setData({
shows2: !this.data.shows2,
});
},
selectTaps3() {
this.setData({
shows3: !this.data.shows3,
});
},
// 点击下拉列表
optionTaps1(e) {
let Indexs = e.currentTarget.dataset.index; //获取点击的下拉列表的下标
console.log(Indexs)
this.setData({
indexs1: Indexs,
shows1: !this.data.shows1,
Weeks:Indexs+1,
});
wx.setStorageSync('Weeks', Indexs+1);
},
optionTaps2(e) {
let Indexs = e.currentTarget.dataset.index; //获取点击的下拉列表的下标
console.log(Indexs)
this.setData({
indexs2: Indexs,
shows2: !this.data.shows2,
Weekdays:Indexs+1,
});
wx.setStorageSync('Weekdays', Indexs+1);
},
optionTaps3(e) {
let Indexs = e.currentTarget.dataset.index; //获取点击的下拉列表的下标
console.log(Indexs)
this.setData({
indexs3: Indexs,
shows3: !this.data.shows3,
Times:Indexs+1,
});
wx.setStorageSync('Times', Indexs+1);
},
now_search_time(){
wx.request({
url: 'https://abc.charlieqyq.top:29999/RoomQuery.php',
data:{
'Weeks':this.data.Weeks,
'Weekdays':this.data.Weekdays,
'Times':this.data.Times,
},
success:function(res)
{
console.log(res.data)
//将教室情况存入缓存
wx.setStorage({
data: res.data,
key: 'roominfo',
})
wx.navigateTo({
url: '../search_result/search_result',
})
}
})
},
/*页面开始加载,就会触发的生命周期事件*/
onLoad: function (options) {
}
})
index.json
{
"enablePullDownRefresh":true,
"navigationBarBackgroundColor": "#fc86ad",
"navigationBarTextStyle": "black",
"navigationBarTitleText": "教室管理系统(学生版)",
"backgroundColor": "#eeeeee",
"backgroundTextStyle": "light"
}
3.2后端
服务器详细配置如下:
内核版本:Linux 5.4.73-1-pve #1 SMP PVE 5.4.73-1
PVE管理器:pve-manager/6.3-2/22f57405
CPU:16x Intel® Xeon® CPU E5-2630L v3
内存:16G(8Gx2)
虚拟机详细配置如下:
虚拟机分配:2 Core,2048M RAM,10GB硬盘
虚拟机系统:Ubuntu 20.04.2 LTS(Py3.7.9)
虚拟机平台:宝塔面板 免费版 7.5.1
虚拟机软件:Apache 2.4.46,MySQL 5.7.33,PHP-5.6,phpMyAdmin 4.4
所用软件:
数据库设计:
接口设计:
接口名称 | 功能 |
---|---|
Update.php | 修改对应申请编号的申请记录 |
RoomQuery.php | 查询所选节次、星期、周次可用的教室 |
Approval.php | 审批对应申请编号的申请记录 |
AppliQuery.php | 查询申请ID对应的所有申请记录 |
TeacherQuery.php | 查询审批教师为ID的所有申请记录 |
ApplicationUpload.php | 提交申请信息 |
TeacherLogin.php | 验证教师工号和登录密码是否正确 |
StudentLogin.php | 验证学生学号和登录密码是否正确 |