2023.6.16

1. 精进了一些功能
2. 修复了一些BUG
3. 整合了前端
This commit is contained in:
KaiyuanOSG 2023-06-16 23:33:04 +08:00
parent f8723d51fc
commit 9819b9b97a
48 changed files with 1920 additions and 242 deletions

View File

@ -2,6 +2,10 @@ package com.sdut.labex.Factory;
import com.alibaba.fastjson.JSONObject;
import com.sdut.labex.entity.BorrowInfo;
import com.sdut.labex.utils.UserHolder;
import lombok.extern.slf4j.Slf4j;
import java.util.Date;
/**
* File: BorrowInfoFactory
@ -9,17 +13,23 @@ import com.sdut.labex.entity.BorrowInfo;
* Author: springforest
* Description:
*/
@Slf4j
public class BorrowInfoFactory {
public static BorrowInfo createBorrowInfo(JSONObject jsonObject) {
BorrowInfo borrowInfo = new BorrowInfo();
try {
borrowInfo.setId(jsonObject.getInteger("id"));
borrowInfo.setApplyUser(jsonObject.getString("applyUser"));
borrowInfo.setTime(jsonObject.getString("time"));
borrowInfo.setApplyUser(UserHolder.getUser().getUsername());
borrowInfo.setDate(jsonObject.getDate("date"));
borrowInfo.setApplyDate(jsonObject.getDate("applyDate"));
borrowInfo.setApplyDate(new Date());
borrowInfo.setIsAdmit(jsonObject.getInteger("isAdmit"));
borrowInfo.setRoomName(jsonObject.getString("roomName"));
} catch (Exception e) {
e.printStackTrace();
log.error("json解析错误");
return null;
}
return borrowInfo;
}
}

View File

@ -2,6 +2,7 @@ package com.sdut.labex.config;
import com.sdut.labex.interceptor.TokenInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.annotation.Resource;
@ -17,10 +18,10 @@ public class MVBConfig implements WebMvcConfigurer {
@Resource
private TokenInterceptor tokenInterceptor;
//@Override
//public void addInterceptors(InterceptorRegistry registry) {
// registry.addInterceptor(tokenInterceptor)
// .excludePathPatterns("/login/**")
// .excludePathPatterns("/register/**");
//}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(tokenInterceptor)
.excludePathPatterns("/login/**")
.excludePathPatterns("/register/**");
}
}

View File

@ -10,9 +10,7 @@ import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* File: BorrowInfoController
@ -29,35 +27,13 @@ public class BorrowInfoController {
@PostMapping("/borrowInfo")
public ResVo addBorrowInfo(@RequestBody JSONObject jsonObject) {
Map<String, String> map = new HashMap<>();
map.put("name", jsonObject.getString("username"));
map.put("date", jsonObject.getString("date"));
map.put("reason", jsonObject.getString("reason"));
map.put("applyDate", jsonObject.getString("applyDate"));
map.put("roomName", jsonObject.getString("roomName"));
JSONArray timeList = jsonObject.getJSONArray("time");
boolean isAnyRoomBorrowed = false;
BorrowInfo borrowInfo = BorrowInfoFactory.createBorrowInfo(jsonObject);
List<String> list = new ArrayList<>();
for (Object item : timeList) {
map.put("time", item.toString());
int code = borrowInfoService.isBorrowed(map);
if (code == 1) {
return ResVo.error("借用失败,所选时段教室被占用");
} else if (code == 0) {
isAnyRoomBorrowed = true;
list.add(item.toString());
}
}
if (isAnyRoomBorrowed) {
for (Object item : timeList) {
map.put("time", item.toString());
borrowInfoService.borrow(map);
}
}
return ResVo.ok("借用成功");
return borrowInfoService.borrow(list, borrowInfo);
}
@PostMapping("/borrowInfo/notBorrowedYet")

View File

@ -1,5 +1,6 @@
package com.sdut.labex.controller;
import com.alibaba.fastjson.JSONObject;
import com.sdut.labex.service.TableService;
import com.sdut.labex.utils.ResVo;
import org.springframework.web.bind.annotation.*;
@ -19,9 +20,10 @@ public class TableController {
@Resource
private TableService tableService;
@GetMapping("/getUnusedTable/{date}/{roomName}")
public ResVo getUnusedTable(@PathVariable("date") String date,
@PathVariable("roomName") String roomName) {
@PostMapping("/getUnusedTable")
public ResVo getUnusedTable(@RequestBody JSONObject jsonObject) {
String roomName = jsonObject.getString("roomName");
String date = jsonObject.getString("date");
if (roomName.equals("")) {
roomName = "9教207";
}

View File

@ -21,10 +21,10 @@ public class TimeOptionController {
@Resource
private TimeOptionService timeOptionService;
@PostMapping("/timeOption")
public ResVo addTimeOption(@RequestBody JSONObject jsonObject) {
@PostMapping("/timeOption/{name}")
public ResVo addTimeOption(@PathVariable("name") String name) {
TimeOption timeOption = new TimeOption();
timeOption.setName(jsonObject.getString("name"));
timeOption.setName(name);
return timeOptionService.addTimeOption(timeOption);
}
@ -36,9 +36,9 @@ public class TimeOptionController {
return timeOptionService.updateTimeOption(timeOption);
}
@DeleteMapping("/timeOption")
public ResVo deleteTimeOption(@RequestBody JSONObject jsonObject) {
return timeOptionService.deleteTimeOption(jsonObject.getInteger("id"));
@DeleteMapping("/timeOption/{id}")
public ResVo deleteTimeOption(@PathVariable("id") Integer id) {
return timeOptionService.deleteTimeOption(id);
}
@GetMapping("/timeOption")

View File

@ -50,4 +50,16 @@ public class UserController {
User user = UserFactory.createUser(jsonObject);
return userService.update(user);
}
@PostMapping("/user/{pageNum}/{pageSize}")
public ResVo getUser(@RequestBody JSONObject jsonObject, @PathVariable("pageNum") int pageNum, @PathVariable("pageSize") int pageSize) {
User user = UserFactory.createUser(jsonObject);
return userService.getUser(pageNum, pageSize, user);
}
@PostMapping("/user")
public ResVo add(@RequestBody JSONObject jsonObject) {
User user = UserFactory.createUser(jsonObject);
return userService.add(user);
}
}

View File

@ -20,14 +20,10 @@ import java.util.Map;
public interface BorrowInfoMapper extends BaseMapper<BorrowInfo> {
public Page<BorrowInfo> queryBorrowInfoByOptions(Page<BorrowInfo> page, BorrowInfo borrowInfo);
public BorrowInfo borrow(Map<String, String> map);
public void addTimeTable(Map<String, String> map);
public List<BorrowInfo> listAllByDateAndRoom(String date, String room);
public BorrowInfo isBorrowed(@Param("roomName") String roomName, @Param("time") String time, @Param("date") String date);
public List<Room> notBorrowedYet(@Param("timeList") List<String> timeList, @Param("date") String date);
}

View File

@ -1,6 +1,7 @@
package com.sdut.labex.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.sdut.labex.entity.User;
import org.apache.ibatis.annotations.Mapper;
@ -15,4 +16,6 @@ import java.util.List;
@Mapper
public interface UserMapper extends BaseMapper<User> {
public void insertBatch(List<User> list);
public Page<User> getUser(Page<User> page, User user);
}

View File

@ -5,7 +5,6 @@ import com.sdut.labex.entity.BorrowInfo;
import com.sdut.labex.utils.ResVo;
import java.util.List;
import java.util.Map;
/**
* @author springforest
@ -14,7 +13,7 @@ import java.util.Map;
*/
public interface BorrowInfoService extends IService<BorrowInfo> {
public ResVo borrow(Map<String, String> map);
public ResVo borrow(List<String> timeList, BorrowInfo borrowInfo);
public ResVo deleteBorrowInfoById(Integer id);
@ -22,7 +21,7 @@ public interface BorrowInfoService extends IService<BorrowInfo> {
public ResVo notBorrowedYet(List<String> timeList, String date);
public int isBorrowed(Map<String, String> map);
public int isBorrowed(BorrowInfo borrowInfo);
}

View File

@ -24,4 +24,6 @@ public interface UserService extends IService<User> {
public ResVo update(User user);
public ResVo addBatch(MultipartFile file);
public ResVo getUser(int pageNum, int pageSize, User user);
}

View File

@ -1,5 +1,7 @@
package com.sdut.labex.service.impl;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sdut.labex.entity.BorrowInfo;
@ -8,14 +10,12 @@ import com.sdut.labex.mapper.BorrowInfoMapper;
import com.sdut.labex.service.BorrowInfoService;
import com.sdut.labex.utils.ResVo;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* @author springforest
@ -33,20 +33,34 @@ public class BorrowInfoServiceImpl extends ServiceImpl<BorrowInfoMapper, BorrowI
private RedisTemplate<String, Object> redisTemplate;
@Override
public ResVo borrow(Map<String, String> map) {
try {
synchronized (this) {//线程锁
//默认为通过审核(0未审核1通过2拒绝)
map.put("isAdmit", "1");
borrowInfoMapper.borrow(map);
public ResVo borrow(List<String> timeList, BorrowInfo borrowInfo) {
boolean isAnyRoomBorrowed = false;
for (String item : timeList) {
BorrowInfo newBorrowInfo = new BorrowInfo();
BeanUtil.copyProperties(borrowInfo, newBorrowInfo); // 使用BeanUtil进行属性拷贝
newBorrowInfo.setTime(item.toString());
int code = this.isBorrowed(newBorrowInfo);
if (code == 1) {
return ResVo.error("借用失败,所选时段教室被占用");
} else if (code == 0) {
isAnyRoomBorrowed = true;
}
}
if (isAnyRoomBorrowed) {
for (String item : timeList) {
BorrowInfo newBorrowInfo = new BorrowInfo();
BeanUtil.copyProperties(borrowInfo, newBorrowInfo);
newBorrowInfo.setTime(item.toString());
newBorrowInfo.setIsAdmit(1);
borrowInfoMapper.insert(newBorrowInfo);
}
} catch (Exception e) {
e.printStackTrace();
return ResVo.error("借用失败");
}
return ResVo.ok("借用成功");
}
@Override
public ResVo deleteBorrowInfoById(Integer id) {
if (borrowInfoMapper.selectById(id) == null) {
@ -75,32 +89,20 @@ public class BorrowInfoServiceImpl extends ServiceImpl<BorrowInfoMapper, BorrowI
@Override
public ResVo notBorrowedYet(List<String> timeList, String date) {
List<Room> RBI;
String key = "notBorrowedYet:" + timeList.toString() + "_" + date;
ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
//拆箱的 'redisTemplate.hasKey(key)' 可能产生 'java.lang.NullPointerException
if (Boolean.TRUE.equals(redisTemplate.hasKey(key))) {
//缓存命中
return (ResVo) valueOperations.get(key);
} else {
//缓存未命中先添加缓存
RBI = borrowInfoMapper.notBorrowedYet(timeList, date);
List<Room> RBI = borrowInfoMapper.notBorrowedYet(timeList, date);
Map<String, Object> res = new HashMap<>();
res.put("list", RBI);
//存入缓存
valueOperations.set(key, ResVo.ok(res));
//设置过期时间
redisTemplate.expire(key, 12, TimeUnit.HOURS);
return ResVo.ok(res);
}
}
@Override
public int isBorrowed(Map<String, String> map) {
String date = map.get("date");
String time = map.get("time");
String roomName = map.get("roomName");
if (borrowInfoMapper.isBorrowed(roomName, time, date) != null) {
public int isBorrowed(BorrowInfo borrowInfo) {
QueryWrapper<BorrowInfo> queryWrapper = new QueryWrapper<>();
queryWrapper.select("id").eq("room_name", borrowInfo.getRoomName())
.eq("time", borrowInfo.getTime()).eq("date", borrowInfo.getDate());
if (borrowInfoMapper.selectOne(queryWrapper) != null) {
return 1;
} else {
return 0;

View File

@ -1,5 +1,6 @@
package com.sdut.labex.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.sdut.labex.dto.TimeTable;
import com.sdut.labex.entity.BorrowInfo;
import com.sdut.labex.entity.Room;
@ -39,45 +40,37 @@ public class TableServiceImpl implements TableService {
@Override
public ResVo getUnUsedTable(String room, String date) {
List<BorrowInfo> borrowedList = borrowInfoMapper.listAllByDateAndRoom(room, date);
log.info("borrowedList: " + borrowedList.toString());
Map<String, List<String>> tempMap = new HashMap<>();
// 获取所有教室
List<Room> roomList = roomMapper.selectList(null);
// 获取所有时间按顺序
List<TimeOption> timeOptionList = timeOptionMapper.selectList(null);
QueryWrapper<BorrowInfo> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("date", date);
List<BorrowInfo> borrowedList = borrowInfoMapper.selectList(queryWrapper);
Map<String, Set<String>> tempMap = new HashMap<>();
List<Room> roomList = roomMapper.selectList(null);
List<TimeOption> timeOptionList = timeOptionMapper.selectList(new QueryWrapper<TimeOption>().orderByAsc("order_number"));
try {
for (TimeOption time : timeOptionList) {
List<String> rooms = new ArrayList<>();
Set<String> rooms = new HashSet<>();
tempMap.put(time.getName(), rooms);
}
for (BorrowInfo item : borrowedList) {
log.info(item.toString());
tempMap.get(item.getTime()).add(item.getRoomName());
}
} catch (Exception e) {
e.printStackTrace();
return ResVo.error("解析时间或记录出错");
}
Map<String, Object> map = new HashMap<>();
List<List<String>> res = new ArrayList<>();
// 获取不在borrowedList中的教室
for (TimeOption key : timeOptionList) {
List<String> rooms = new ArrayList<>();
for (Room item : roomList) {
if (!tempMap.get(key.getName()).contains(item.getName())) {
for (Room item : roomMapper.selectList(new QueryWrapper<Room>().in("name", tempMap.get(key.getName())))) {
rooms.add(item.getName());
}
}
res.add(rooms);
}
if (room != null && !room.isEmpty()) {
for (List<String> rooms : res) {
rooms.removeIf(r -> !r.equals(room));
rooms.removeIf(r -> !r.contains(room));
}
}
@ -85,6 +78,7 @@ public class TableServiceImpl implements TableService {
return ResVo.ok(map);
}
@Override
public ResVo uploadTable(MultipartFile file, String startDate, String roomName) {
try {

View File

@ -1,5 +1,6 @@
package com.sdut.labex.service.impl;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sdut.labex.entity.User;
import com.sdut.labex.mapper.UserMapper;
@ -17,6 +18,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* File: UserServiceImpl
@ -44,7 +46,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User>
map.put("id", user.getId());
map.put("role", user.getRole());
map.put("username", user.getUsername());
map.put("classId", user.getClassName());
map.put("className", user.getClassName());
map.put("department", user.getDepartment());
String token = JWTUtil.getTokenByMap(map);
map.put("token", token);
@ -121,7 +123,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User>
return ResVo.error("解析Excel出错");
}
int startRow = 0;
int startRow = 1;
int endRow = excel.totalRows;
List<User> list = new ArrayList<>();
for (int i = startRow; i < endRow; i++) {
@ -129,13 +131,20 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User>
//学号
try {
if (excel.readCell(i, 0) == null) {
break;
}
user.setId(excel.readCell(i, 0));
} catch (Exception e) {
e.printStackTrace();
return ResVo.error("Excel格式错误" + (i + 1) + "行第1列");
}
//姓名
try {
if (excel.readCell(i, 1) == null) {
return ResVo.error("Excel格式错误" + (i + 1) + "行第2列");
}
user.setUsername(excel.readCell(i, 1));
} catch (Exception e) {
e.printStackTrace();
@ -147,6 +156,9 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User>
user.setRole("student");
//班级
try {
if (excel.readCell(i, 2) == null) {
return ResVo.error("Excel格式错误" + (i + 1) + "行第3列");
}
user.setClassName(excel.readCell(i, 2));
} catch (Exception e) {
e.printStackTrace();
@ -154,6 +166,9 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User>
}
//学院
try {
if (excel.readCell(i, 3) == null) {
return ResVo.error("Excel格式错误" + (i + 1) + "行第4列");
}
user.setDepartment(excel.readCell(i, 3));
} catch (Exception e) {
e.printStackTrace();
@ -162,12 +177,24 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User>
list.add(user);
}
userMapper.deleteBatchIds(list.stream().map(User::getId).collect(Collectors.toList()));
try {
userMapper.insertBatch(list);
} catch (Exception e) {
e.printStackTrace();
return ResVo.error("批量添加失败");
}
return ResVo.ok();
return ResVo.ok("批量添加成功" + list.size() + "条数据");
}
@Override
public ResVo getUser(int pageNum, int pageSize, User user) {
Page<User> page = new Page<>(pageNum, pageSize);
userMapper.getUser(page, user);
Map<String, Object> map = new HashMap<>();
map.put("total", page.getTotal());
map.put("list", page.getRecords());
return ResVo.ok(map);
}
}

View File

@ -17,10 +17,6 @@
insert into borrow_info (apply_user, time, date, apply_date, is_admit, room_name)
VALUES (#{applyUser}, #{time}, #{date}, #{applyDate}, 1, #{roomName});
</insert>
<insert id="borrow" parameterType="map">
insert into borrow_info(name, time, date, applyDate, isAdmit, room_name)
values (#{name}, #{time}, #{date}, #{applyDate}, #{isAdmit}, #{roomName})
</insert>
<select id="queryBorrowInfoByOptions" resultType="com.sdut.labex.entity.BorrowInfo">
select *
@ -39,13 +35,7 @@
</if>
</where>
</select>
<select id="isBorrowed" resultType="com.sdut.labex.entity.BorrowInfo">
select *
from borrow_info
where date = #{date}
and time = #{time}
and roomName = #{roomName};
</select>
<select id="notBorrowedYet" resultType="com.sdut.labex.entity.Room">
SELECT *
FROM room

View File

@ -21,4 +21,15 @@
#{item.role})
</foreach>
</insert>
<select id="getUser" resultType="com.sdut.labex.entity.User" parameterType="object">
select * from user
<where>
<if test="user.id != ''">
and id = #{user.id}
</if>
<if test="user.username != ''">
and username = #{user.username}
</if>
</where>
</select>
</mapper>

0
web/babel.config.js Normal file → Executable file
View File

0
web/jsconfig.json Normal file → Executable file
View File

0
web/package-lock.json generated Normal file → Executable file
View File

0
web/package.json Normal file → Executable file
View File

0
web/public/favicon.ico Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

0
web/public/index.html Normal file → Executable file
View File

59
web/src/App.vue Normal file → Executable file
View File

@ -1,34 +1,59 @@
<template>
<img src="./assets/logo.png">
<div>
<p>
If Element Plus is successfully added to this project, you'll see an
<code v-text="'<el-button>'"></code>
below
</p>
<el-button type="primary">el-button</el-button>
<div class="background">
<router-view></router-view>
</div>
<HelloWorld msg="Welcome to Your Vue.js App"/>
<Footer/>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
import Footer from "@/components/Footer";
export default {
name: 'App',
mounted() {
document.title = "计算机科学与技术学院教室预约系统";
},
created() {
this.bodyScale()
},
methods:{
bodyScale() {
var devicewidth = document.documentElement.clientWidth; //
var scale = devicewidth / 1800; // 稿
document.body.style.zoom = scale; //
}
},
components: {
HelloWorld
Footer
}
}
</script>
<style>
.background{
display: flex;
justify-content: center;
min-width: 1024px;
min-height: 900px !important;
/*width: 1440px;*/
/*height: 900px;*/
/*display: flex;*/
/*justify-content: center;*/
/*min-width: 1440px;*/
}
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
font-family: Noto Sans SC, emoji;
/*position: absolute;*/
/*top: 0;*/
/*right: 0;*/
/*left: 0;*/
/*bottom: 0;*/
/*margin: auto;*/
/*overflow-x: hidden;*/
/*min-width: 1024px !important;*/
/*min-height: 1080px !important;*/
}
</style>

BIN
web/src/assets/img/loginBac.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

BIN
web/src/assets/img/logo.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 803 B

BIN
web/src/assets/img/logoW.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 535 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 KiB

BIN
web/src/assets/img/院标2.0.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

View File

@ -0,0 +1,21 @@
<template>
<div style="display: flex;width: 100%;justify-content: center">
<el-button style="border: 1px solid transparent" text @click="dialogVisible = true">© 2023 . All Rights Reserved | Designed by 张开源臧群彭凯 | 1.0
</el-button>
</div>
</template>
<script>
export default {
name: "Footer",
data(){
return{
dialogVisible:false,
}
}
}
</script>
<style scoped>
</style>

View File

@ -1,59 +0,0 @@
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<p>
For a guide and recipes on how to configure / customize this project,<br>
check out the
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
</p>
<h3>Installed CLI Plugins</h3>
<ul>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-router" target="_blank" rel="noopener">router</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-vuex" target="_blank" rel="noopener">vuex</a></li>
</ul>
<h3>Essential Links</h3>
<ul>
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
</ul>
<h3>Ecosystem</h3>
<ul>
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
</ul>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>

View File

@ -0,0 +1,120 @@
<template>
<!--教室使用情况表-->
<h4>可用教室</h4>
<el-table :data="tableData" border>
<el-table-column prop="date"/>
<el-table-column v-for="(val,index) in timeSet" :key="index" :label="val" align="center">
<template #default="scope">
<el-space direction="vertical">
<div v-for="(room,i) in scope.row.rooms[index]">
<el-tag size="small">{{ room }}</el-tag>
</div>
</el-space>
</template>
</el-table-column>
</el-table>
<el-form label-width="120px" :inline="true" style="margin-top: 20px;margin-bottom: -20px">
<el-form-item label="教室筛选">
<div v-for="(item,i) in rooms">
<el-tag type="warning" @click="subForm.roomName = item.name" style="cursor: pointer;margin-right: 20px;">
{{ item.name }}
</el-tag>
</div>
</el-form-item>
</el-form>
</template>
<script>
export default {
name: "Table",
props: ["msg"],
data() {
return {
rooms: [],
subForm: {
date: '',
roomName: ''
},
timeSet: ['第一节', '第二节', '第三节', '第四节', '第五节', '第六节', '第七节', '第八节', '第九节', '第十节'],
tableData: [],
}
},
watch: {
'msg.date'(newVal, oldVal) {
//
this.subForm.date = newVal;
this.init();
},
'subForm.roomName'(newVal, oldVal) {
//
this.init();
},
},
mounted() {
//
this.subForm.date = this.$props.msg.date;
this.subForm.roomName = this.$props.msg.roomName;
this.init();
this.getAllRoom();
},
methods: {
init() {
//
this.tableData = [];
//borrowFormDate
let tempDate = this.subForm.date;
for (let i = 0; i < 7; i++) {
this.tableData.push({
date: this.setTimes(tempDate, i).substring(5, 10),
rooms: []
})
this.subForm.date = this.setTimes(tempDate, i);
setTimeout(() => {
this.getTableData(i);
}, 200);
}
//
this.subForm.date = tempDate;
},
getAllRoom() {
this.$http({
method: 'get',
url: '/room'
}).then(res => {
this.rooms = res.data.list;
})
},
getTableData(offset) {
this.$http({
url: '/getUnusedTable',
method: 'post',
data: this.subForm
}).then(res => {
this.tableData[offset].rooms = res.data.list;
})
},
//
setTimes(selDate, offset) {
let dataOp = null;
if (selDate != null) {
//selDate2022-10-14
let year = parseInt(selDate.substring(0, 4));
//Date0-11
let month = parseInt(selDate.substring(5, 7)) - 1;
let day = parseInt(selDate.substring(8, 10));
dataOp = new Date(year, month, day);
} else {
//
dataOp = new Date()
}
dataOp.setDate(dataOp.getDate() + offset)
return String(dataOp.getFullYear()) + '-' + String((dataOp.getMonth() + 1) < 10 ? '0' + (dataOp.getMonth() + 1) : (dataOp.getMonth() + 1)) + '-' + ((dataOp.getDate() + 1) <= 10 ? '0' + (dataOp.getDate()) : (dataOp.getDate()));
},
}
}
</script>
<style scoped>
</style>

19
web/src/main.js Normal file → Executable file
View File

@ -3,7 +3,26 @@ import App from './App.vue'
import router from './router'
import store from './store'
import installElementPlus from './plugins/element'
import axios from "axios";
const app = createApp(App)
installElementPlus(app)
app.use(store).use(router).mount('#app')
app.config.globalProperties.$http = axios;
//接口请求的基准路径
axios.defaults.baseURL = 'http://localhost:8080/';
// axios.defaults.baseURL = 'http://211.64.28.110:8080/';
// 添加请求拦截器
axios.interceptors.request.use(config => {
// 在发送请求之前做些什么
// 判断是否存在token,如果存在将每个页面header添加token
if (sessionStorage.getItem("token")) {
config.headers.token = sessionStorage.getItem("token");
}
return config
})

0
web/src/plugins/element.js Normal file → Executable file
View File

80
web/src/router/index.js Normal file → Executable file
View File

@ -1,20 +1,64 @@
import { createRouter, createWebHashHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
const routes = [
{
path: '/',
name: 'home',
component: HomeView
redirect:'/Login'
},
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue')
path: '/Home',
name: 'Home',
redirect: '/BorrowRoom',
component: () => import('../views/Home.vue'),
//---------------------教室借用相关-----------------------------
children:[
{
path: '/BorrowRoom',
name: 'BorrowRoom',
component: () => import('../views/BorrowRoom/BorrowRoom.vue')
},
{
path: '/BorrowInfoList',
name: 'BorrowInfoList',
component: () => import('../views/BorrowRoom/RecordList.vue')
},
//---------------------管理员部分-----------------------------
{
path: '/Admit',
name: 'Admit',
component: () => import('../views/Admin/Admit.vue')
},
{
path: '/UserManage',
name: 'UserManage',
component: () => import('../views/Admin/UserManage.vue')
},
{
path: '/RoomTimeAndReasonManage',
name: 'RoomTimeAndReasonManage',
component: () => import('../views/Admin/RoomAndTimeManage.vue')
},
{
path: '/Personal',
name: 'Personal',
component: () => import('../views/Personal.vue')
},
//---------------------教师--------------------------------------------------------------------------
{
path: '/ApplyExperiment',
name: 'ApplyExperiment',
component: () => import('../views/ApplyExperiment/ApplyExperiment.vue')
}
]
},
//---------------------用户/Admin--------------------------------------------------------------------
{
path: '/Login',
name: 'Login',
component: () => import('../views/Login.vue')
},
]
const router = createRouter({
@ -22,4 +66,22 @@ const router = createRouter({
routes
})
//导航守卫
//开启前先做白名单
router.beforeEach((to, from, next) => {
//页面拦截
if (to.name !== 'Login') {
let role = window.sessionStorage.getItem("role");
// let token = window.sessionStorage.getItem("token");
if (!role) {
// return next({ name: 'Login' });
}
return next();
} else {
return next();
}
})
export default router

0
web/src/store/index.js Normal file → Executable file
View File

View File

@ -1,5 +0,0 @@
<template>
<div class="about">
<h1>This is an about page</h1>
</div>
</template>

View File

@ -0,0 +1,99 @@
<template>
<el-table :data="admitlist" stripe border v-loading="loading" :highlight-current-row="true">
<el-table-column align="center" prop="date" label="日期" width="120" />
<el-table-column align="center" prop="time" label="时间" width="120" />
<el-table-column align="center" prop="roomName" label="教室名称" width="100" />
<el-table-column align="center" prop="reason" label="用途" width="150"/>
<el-table-column align="center" prop="name" label="借用人" />
<el-table-column align="center" prop="applyDate" label="申请时间" width="180"/>
<el-table-column align="center" label="操作">
<template #default="scope">
<el-button type="success" @click="access(scope.row.id)" v-show="userInfo.role === 'admin'">允许</el-button>
<el-button type="danger" @click="deny(scope.row.id)" v-show="userInfo.role === 'admin'">拒绝</el-button>
</template>
</el-table-column>
</el-table>
</template>
<script>
import {ElMessage} from "element-plus";
export default {
name: "Admit",
data(){
return{
loading: true,
userInfo:{
userId:'',
role:'',
username:'',
userDepart:'',
},
admitlist:[],
}
},
mounted() {
this.userInfo.role = window.sessionStorage.getItem("role");
this.userInfo.userId = window.sessionStorage.getItem("userId");
this.getAllAdmit()
},
methods:{
getAllAdmit(){
this.loading = true;
this.$http({
url:'/admit',
method:'get',
}).then(({data})=>{
if (data.code !== 200){
ElMessage({
message: '信息获取失败',
type: 'error',
})
}else {
this.admitlist = data.list;
}
this.loading = false;
})
},
access(id){
this.loading = true;
this.$http({
url:'/admit/access/' + id,
method:'put',
}).then(({data})=>{
if (data.code !== 200){
ElMessage({
message: '操作失败',
type: 'error',
})
}else {
this.getAllAdmit();
}
this.loading = false;
})
},
deny(id){
this.loading = true;
this.$http({
url:'/admit/deny/' + id,
method:'put',
}).then(({data})=>{
if (data.code !== 200){
ElMessage({
message: '操作失败',
type: 'error',
})
}else {
this.getAllAdmit();
}
this.loading = false;
})
},
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,274 @@
<template>
<el-page-header content="系统管理" style="margin-bottom: 30px" @back="this.$router.push('/Login')"/>
<div class="bac" style="padding-left: 40px">
<el-space>
<!--时间管理-->
<el-card shadow="always" :v-loading="isLoading" style="width: 500px;height: 300px">
<el-space style="margin-bottom: 10px">
<el-input placeholder="时间段名称" v-model="this.timeName"/>
<el-button @click="addTime">添加时间段</el-button>
</el-space>
<el-table
:data="times"
highlight-current-row
max-height="220">
<el-table-column label="ID" prop="id"></el-table-column>
<el-table-column label="时间段名称" prop="name"></el-table-column>
<el-table-column label="操作" >
<template #default="scope">
<el-button type="danger" @click="removeTime(scope.row.id)" v-show="scope.row.id!==0">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
</el-space>
<!--教室管理-->
<el-card shadow="always" :v-loading="isLoading" style="width: 1050px;height: 450px;margin-top: 40px;">
<el-space>
<el-button @click="dialogVisible = true" style="margin-bottom: 10px">添加教室</el-button>
</el-space>
<el-table
:data="rooms"
highlight-current-row
max-height="500">
<el-table-column align="center" label="ID" prop="id" width="50px"></el-table-column>
<el-table-column align="center" label="教室名称" prop="name" ></el-table-column>
<el-table-column align="center" label="描述" prop="description" width="300px"></el-table-column>
<el-table-column align="center" label="状态">
<template #default="scope">
<el-tag v-show="scope.row.status === 1">可用</el-tag>
<el-tag type="danger" v-show="scope.row.status === 2">维护中</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" >
<template #default="scope">
<el-button type="primary" @click="getRoomFromRow(scope.row)">更新</el-button>
<el-button type="danger" @click="removeRoom(scope.row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
<!--添加教室-->
<el-dialog
v-model="dialogVisible"
title="活动管理"
width="30%"
>
<el-form
label-width="100px"
:model="roomInfo"
style="max-width: 460px"
>
<el-form-item label="教室ID">
<el-input v-model="roomInfo.id" :disabled="isUpdate"/>
</el-form-item>
<el-form-item label="教室名称">
<el-input v-model="roomInfo.name" />
</el-form-item>
<el-form-item label="描述">
<el-input v-model="roomInfo.description" />
</el-form-item>
<el-form-item label="状态">
<el-switch
v-model="roomInfo.status"
style="--el-switch-on-color: #13ce66; --el-switch-off-color: #ff4949"
active-text="可用"
inactive-text="维护"
/>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="confirmForm">确认</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script>
import {ElMessage, ElMessageBox} from "element-plus";
let formData = new FormData();
export default {
name: "RoomAndTimeManage",
data(){
return{
dialogVisible:false,
isLoading:true,
//
timeName:'',
times:[],
//
roomInfo:{
id:'',
name:'',
description:'',
status:true,
imgUrl:'',
},
rooms:[],
isUpdate:false,
}
},
mounted() {
this.getAllTime();
this.getAllRoom();
},
methods:{
//
getAllTime(){
this.$http({
method:'get',
url:'/timeOption'
}).then(({data}) =>{
this.times = data.list;
})
},
addTime(){
this.$http({
method:'post',
url:'/timeOption/' + this.timeName,
}).then(({data})=>{
if (data.code ===200){
//
this.timer = setTimeout(() => { //
this.getAllTime();
this.timeName = '';
}, 100);
}
})
},
removeTime(id){
this.$http({
method:'delete',
url:'/timeOption/' + id,
}).then(({data})=>{
if (data.code === 200){
ElMessage({
type: 'success',
message: '删除成功',
})
this.getAllTime();
}
})
},
//
getAllRoom(){
this.$http({
method:'get',
url:'/room'
}).then(res =>{
this.rooms = res.data.list;
})
},
confirmForm(){
if (this.isUpdate) {
this.updateRoom();
}else {
this.addRoom()
}
},
addRoom(){
if (this.roomInfo.status){
this.roomInfo.status = '1';
}else {
this.roomInfo.status = '2';
}
this.$http({
method:'post',
url:'/room',
data:this.roomInfo
}).then(({data})=>{
if (data.code ===200){
ElMessage({
type: 'success',
message: '添加成功',
})
}else {
ElMessage({
type: 'error',
message: data.msg,
})
}
})
this.dialogVisible = false;
this.roomInfo.status = true;
this.roomInfo.name = '';
this.roomInfo.imgUrl = '';
this.roomInfo.description = '';
this.isUpdate = false;
//
this.timer = setTimeout(() => { //
this.getAllTime();
this.getAllRoom();
}, 100);
},
removeRoom(id){
this.$http({
method:'delete',
url:'/room/' + id,
}).then(({data})=>{
if (data.code === 200){
ElMessage({
type: 'success',
message: '删除成功',
})
this.getAllRoom();
}
})
},
getRoomFromRow(row) {
this.isUpdate = true;
this.roomInfo = JSON.parse(JSON.stringify(row));
this.dialogVisible = true;
this.roomInfo.status = this.roomInfo.status === 1;
},
updateRoom(){
if (this.roomInfo.status){
this.roomInfo.status = '1';
}else {
this.roomInfo.status = '2';
}
this.$http({
method: 'put',
url: '/room',
data: this.roomInfo
}).then(({data}) => {
if (data.code === 200) {
ElMessage({
type: 'success',
message: '修改成功',
})
} else {
ElMessage({
type: 'error',
message: data.msg,
})
}
})
this.timer = setTimeout(() => { //
this.getAllTime();
this.getAllRoom();
}, 200);
this.dialogVisible = false;
this.roomInfo.status = true;
this.roomInfo.name = '';
this.roomInfo.imgUrl = '';
this.roomInfo.description = '';
this.isUpdate = false;
},
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,222 @@
<template>
<el-page-header content="用户管理" style="margin-bottom: 30px" @back="this.$router.push('/Login')"/>
<el-card style="max-width: 1200px;margin:0 auto;background-color: #f9fafb">
<div style="display: flex;justify-content: space-between;align-items: center">
<el-space>
<el-button type="primary" style="margin-right: 10px;" @click="dialogVisibleForAdd = true">添加成员</el-button>
<el-upload :on-change="fileChange" :show-file-list="false" :auto-upload="false">
<el-button type="warning" :loading="loading">上传</el-button>
</el-upload>
</el-space>>
</div>
<!--表格展示-->
<div style="margin-top: 20px">
<el-table :data="userList" border stripe v-loading="loading">
<el-table-column align="center" prop="id" label="ID"/>
<el-table-column align="center" prop="username" label="姓名" />
<el-table-column align="center" prop="className" label="班级"/>
<el-table-column align="center" prop="department" label="学院"/>
<el-table-column align="center" prop="role" label="身份"/>
<el-table-column align="center" label="操作">
<template #default="scope">
<el-button type="primary" @click="update(scope.row)">
<span style="margin-left: 3px">更新</span>
</el-button>
<el-button type="danger" @click="remove(scope.row.id)">
<span style="margin-left: 3px">删除</span>
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<el-pagination
@size-change="getUser"
@current-change="getUser"
v-model:current-page="pageNum"
v-model:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination>
<!--添加用户的对话框-->
<el-dialog title="添加用户" v-model="dialogVisibleForAdd" width="30%" style="padding-top: 30px">
<el-form ref="addForm" :model="addForm" label-width="100px">
<el-form-item label="工号">
<el-input :disabled="isEdit" v-model="addForm.id" placeholder="请输入工号..."></el-input>
</el-form-item>
<el-form-item label="姓名">
<el-input v-model="addForm.username" placeholder="请输入姓名..."></el-input>
</el-form-item>
<el-form-item label="班级">
<el-input v-model="addForm.className" placeholder="请输入班级..."></el-input>
</el-form-item>
<el-form-item label="学院">
<el-input v-model="addForm.department" placeholder="请输入班级..."></el-input>
</el-form-item>
<el-form-item label="密码">
<el-input v-model="addForm.password" placeholder="请输入密码..."></el-input>
</el-form-item>
<el-form-item label="身份">
<el-input v-model="addForm.role" placeholder="请输入密码..."></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="addUser"> </el-button>
<el-button @click="dialogVisibleForAdd = false"> </el-button>
</span>
</el-dialog>
</el-card>
</template>
<script>
import {ElMessage, ElMessageBox} from "element-plus";
let formData = new FormData;
export default {
name: "UserManage",
data() {
return {
loading:true,
isEdit:false,
pageSize:30,
pageNum:1,
total:0,
addForm:{
id:'',
username:'',
role:'teacher',
password:'123456',
className:'',
department:''
},
userList:[],
dialogVisibleForAdd: false,
}
},
mounted() {
this.getUser();
},
methods: {
getUser() {
this.loading = true;
this.userList = [];
this.$http({
method: 'post',
url: '/user/' + this.pageNum + '/' + this.pageSize,
data:{
id:'',
username:''
}
}).then(({data}) => {
this.userList = data.list;
this.total = data.total;
this.loading = false;
})
},
addUser() {
let method = '';
if(this.isEdit){
method = 'put';
}else {
method = 'post';
}
this.$http({
method: method,
url: '/user' ,
data: this.addForm
}).then(({data}) => {
if (data.code === 200) {
ElMessage({
message: '操作成功',
type: 'success'
})
this.isEdit = false;
this.dialogVisibleForAdd = false;
this.addForm.username = '';
this.addForm.id = '';
this.addForm.password = '123456';
this.getUser();
}else {
ElMessage({
message: data.msg,
type: 'error'
})
}
})
},
update(row){
this.addForm = JSON.parse(JSON.stringify(row));
this.isEdit = true;
this.dialogVisibleForAdd = true;
},
remove(id) {
ElMessageBox.confirm(
'该操作不可撤销',
'警告',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
)
.then(() => {
this.$http({
method: 'delete',
url: '/user/' + id,
}).then(res => {
this.getUser();
ElMessage({
type: 'success',
message: '删除成功',
})
})
})
.catch(() => {
ElMessage({
type: 'info',
message: '删除已取消',
})
})
},
fileChange(files, fileList) {
formData.append('file', files.raw)
files = null;
this.loading = true;
this.$http({
method: 'post',
url: '/addUserBatch',
data: formData,
headers: {
'Content-Type': 'multipart/form-data'
}
}).then(res => {
if (res.data.code === 200) {
this.$notify({
title: '上传成功',
message: res.data.msg,
type: 'success'
});
} else {
this.$notify({
title: '文件解析失败',
message: res.data.msg,
type: 'error'
});
}
formData = null;
formData = new FormData();
this.loading = false;
})
},
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,114 @@
<template>
<el-page-header content="实验项目申请" style="margin-bottom: 30px" @back="this.$router.push('/Home')"/>
<el-card>
<div>
<form @submit.prevent="onSubmit">
<el-form-item label="项目名称">
<el-input v-model="form.name" type="text" id="name" required/>
</el-form-item>
<el-form-item label="项目类型">
<el-checkbox-group v-model="form.type">
<el-checkbox label="类型一" name="type" />
<el-checkbox label="类型二" name="type" />
<el-checkbox label="类型三" name="type" />
<el-checkbox label="类型四" name="type" />
</el-checkbox-group>
</el-form-item>
<el-form-item label="备注">
<el-input v-model="form.desc" type="textarea" style="margin-left: 30px"/>
</el-form-item>
<el-form-item class="btn-Ex">
<el-button type="primary" @click="onSubmit">确认</el-button>
<el-button @click="cancelApply">取消</el-button>
</el-form-item>
</form>
</div>
</el-card>
<el-card style="margin-top: 10px">
<span style="width: 100%">待规划项目</span>
<el-radio-group v-model="size">
<el-radio label="large">Large</el-radio>
<el-radio>Default</el-radio>
<el-radio label="small">Small</el-radio>
</el-radio-group>
<el-table :data="itemList" style="width: 100%">
<el-table-column fixed prop="name" label="项目名称" width="150" />
<el-table-column prop="type" label="项目种类" width="120" />
<el-table-column prop="state" label="审批状态" width="120" />
<el-table-column prop="address" label="项目描述" width="600" />
<el-table-column fixed="right" label="操作" width="120">
<template #default>
<el-button link type="primary" size="small" @click="setting"
>Detail</el-button
>
<el-button link type="primary" size="small">设置</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
</template>
<script>
import axios from 'axios';
import { computed, ref } from 'vue'
const size = ref('')
computed(() => {
const marginMap = {
large: '32px',
default: '28px',
small: '24px',
}
return {
marginTop: marginMap[size.value] || marginMap.default,
}
});
export default {
name: 'ApplyExperiment',
data() {
return {
itemList: [],
form: {
name: '',
type: [],
desc: '',
},
size:'',
}
},
mounted() {
//
axios.get('http://localhost:8080/room').then(response => {
this.itemList = response.data;
});
},
methods: {
onSubmit() {
axios.post('http://localhost:8080/room', this.form)
.then(response => {
console.log(response.data);
//
})
.catch(error => {
console.error(error);
//
});
},
cancelApply(){
this.$router.push('/Home');
},
setting(){
this.$router.push('/BorrowRoom')
}
}
}
</script>
<style>
.btn-Ex {
display: inline-flex;
justify-content: center;
text-align: center;
width: 100%;
}
</style>

View File

@ -0,0 +1,234 @@
<template>
<el-page-header content="借用登记" style="margin-bottom: 30px" @back="this.$router.push('/Login')"/>
<el-card>
<Table :msg="borrowForm"/>
<el-divider/>
<div>
<el-form label-width="120px" :inline="true">
<el-form-item label="日期">
<el-date-picker
v-model="borrowForm.date"
type="date"
placeholder="请选择借用日期"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
@change="getRoom"
style="width: 200px"
/>
</el-form-item>
<el-form-item label="时间">
<el-select
v-model="borrowForm.time"
placeholder="请选择借用时间"
style="width: 200px"
multiple
collapse-tags
@change="getRoom"
placement="right"
>
<el-option
v-for="item in TimeOption"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-form>
</div>
<el-divider/>
<div style="margin-left:20px;margin-right:20px;padding-left: 70px;padding-top: 40px;padding-bottom: 30px">
<el-space :size="45" wrap>
<div v-for="(item,i) in Rooms">
<el-card :body-style="{ padding: '0px' }" style="width: 200px;">
<div style="padding: 14px">
<div style="margin-bottom: 10px">{{ item.name }}</div>
<div style="width: 100%;padding-left: 10px;padding-right: 10px">
<el-space wrap v-for="(subItem,j) in item.description">
<el-tag style="margin-top: 10px">{{ subItem }}</el-tag>
</el-space>
</div>
</div>
<el-button type="primary" @click="preBorrow(item.name)" v-if="item.status === 1" style="width: 100%">
借用该教室
</el-button>
<el-button type="info" disabled v-if="item.status === 2" style="width: 100%">维护中</el-button>
</el-card>
</div>
</el-space>
</div>
</el-card>
<el-dialog
v-model="confirmDialogVisible"
title="请确认您的借用信息"
width="30%"
>
<el-form label-width="120px">
<el-form-item label="教室">
{{ borrowForm.roomName }}
</el-form-item>
<el-form-item label="借用日期">
{{ borrowForm.date }}
</el-form-item>
<el-form-item label="借用时间">
<el-space v-for="(item,i) in borrowForm.time">
<el-tag>{{ item }}</el-tag>
</el-space>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="confirmDialogVisible = false">取消</el-button>
<el-button type="primary" @click="confirmBorrow">确认</el-button>
</span>
</template>
</el-dialog>
</template>
<script>
import {ElMessage, ElMessageBox} from "element-plus";
import Table from "../../components/Table"
export default {
name: "BorrowRoom",
data() {
return {
TimeOption: [],
userInfo: {
userId: '',
role: '',
username: '',
userDepart: '',
newPwd: '',
oldPwd: ''
},
Rooms: [],
isMedia: '',
borrowForm: {
applyUser: '',
time: [],
date: '',
roomName: '',
},
confirmDialogVisible: false,
}
},
created() {
//使TablesetTimes
this.borrowForm.date = this.setTimes(0);
},
mounted() {
this.userInfo.role = window.sessionStorage.getItem('role');
this.userInfo.username = window.sessionStorage.getItem("username");
this.userInfo.userId = window.sessionStorage.getItem("userId");
this.getAllTimeOptions();
},
methods: {
getRoom() {
if (this.borrowForm.date === '' || this.borrowForm.time.length === 0) {
return;
}
this.$http({
method: 'post',
url: '/borrowInfo/notBorrowedYet',
data: this.borrowForm
}).then(({data}) => {
if (data.code !== 200) {
ElMessage({
message: '教室信息获取失败,请联系管理员',
type: 'error',
})
} else {
this.Rooms = data.list;
for (let i = 0; i < this.Rooms.length; i++) {
this.Rooms[i].description = this.Rooms[i].description.split(';');
}
}
})
},
//
preBorrow(roomName) {
if (this.borrowForm.date === '' || this.borrowForm.time.length === 0) {
ElMessage({
message: '请完善借用信息',
type: 'warning',
})
return;
}
this.borrowForm.roomName = roomName;
//
this.borrowForm.applyUser = this.userInfo.username;
this.confirmDialogVisible = true;
},
confirmBorrow() {
this.confirmDialogVisible = false;
this.$http({
method: 'post',
url: '/borrowInfo',
data: this.borrowForm
}).then(res => {
if (res.data.code !== 200) {
ElMessage({
message: '登记失败,' + res.data.msg,
type: 'warning',
})
} else {
this.messageAlert();
this.getRoom();
}
})
},
messageAlert() {
ElMessageBox.confirm(
'登记成功',
{
confirmButtonText: '确认',
type: 'success',
}
)
},
getAllTimeOptions() {
this.$http({
url: '/timeOption',
method: 'get',
}).then(({data}) => {
for (let i = 0; i < data.list.length; i++) {
this.TimeOption.push({
label: data.list[i].name,
key: data.list[i].name,
value: data.list[i].name
})
}
})
},
//
setTimes(offset) {
//
let dataOp = new Date()
dataOp.setDate(dataOp.getDate() + offset)
return String(dataOp.getFullYear()) + '-' + String((dataOp.getMonth() + 1) < 10 ? '0' + (dataOp.getMonth() + 1) : (dataOp.getMonth() + 1)) + '-' + ((dataOp.getDate() + 1) <= 10 ? '0' + (dataOp.getDate()) : (dataOp.getDate()));
},
},
components: {
Table,
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,278 @@
<template>
<el-page-header content="借用记录" style="margin-bottom: 30px" @back="this.$router.push('/Login')"/>
<el-card>
<el-form label-width="120px">
<el-form-item label="日期">
<el-date-picker
v-model="subForm.date"
type="date"
placeholder="请选择借用日期"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
@change="getBorrowInfo"
style="width: 200px"
/>
</el-form-item>
<el-form-item label="时间">
<el-select
v-model="subForm.time"
placeholder="请选择借用时间"
style="width: 200px"
@change="getBorrowInfo"
>
<el-option
v-for="item in TimeOption"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="教室">
<el-select
v-model="subForm.roomName"
placeholder="请选择教室"
style="width: 200px"
@change="getBorrowInfo">
<el-option
v-for="item in Rooms"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="导入课表" v-show="userInfo.role === 'admin'">
<a href="/static/template.xls" download="课表模板.xlsx">
<el-button type="primary" style="margin-right: 10px;" :loading="loading">下载模板</el-button>
</a>
<el-breadcrumb :separator-icon="ArrowRight" style="margin-right: 20px;">
<el-date-picker v-model="this.startDate" type="date" format="YYYY-MM-DD" value-format="YYYY-MM-DD" placeholder="学期开始日期"/>
</el-breadcrumb>
<el-upload :on-change="fileChange" :show-file-list="false" :auto-upload="false">
<el-button type="warning" :loading="loading">上传</el-button>
</el-upload>
</el-form-item>
<el-form-item label="下载记录" v-show="userInfo.role === 'admin'">
<el-button type="primary" @click="exportExcel">导出记录</el-button>
</el-form-item>
</el-form>
<el-divider />
<div>
<el-table :data="borrowInfo" stripe border v-loading="loading" :highlight-current-row="true">
<el-table-column align="center" prop="date" label="日期" width="120" />
<el-table-column align="center" prop="time" label="时间" width="120" />
<el-table-column align="center" prop="roomName" label="教室名称" width="100" />
<el-table-column align="center" prop="reason" label="用途" width="180"/>
<el-table-column align="center" prop="name" label="借用人" />
<el-table-column align="center" prop="applyDate" label="申请时间" width="200"/>
<el-table-column align="center" width="100" label="状态">
<template #default="scope">
<el-tag v-show="scope.row.isAdmit === '0'" type="warning">待审核</el-tag>
<el-tag v-show="scope.row.isAdmit === '2'" type="danger">拒绝</el-tag>
<el-tag v-show="scope.row.isAdmit === '1'" type="success">通过</el-tag>
</template>
</el-table-column>
<el-table-column align="center" label="操作">
<template #default="scope">
<el-button type="danger" @click="cancel(scope.row.id)" v-show="userInfo.role === 'admin' || userInfo.username === scope.row.name">撤销</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
@size-change="getBorrowInfo"
@current-change="getBorrowInfo"
v-model:current-page="subForm.pageNum"
v-model:page-size="subForm.pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="subForm.total">
</el-pagination>
</div>
</el-card>
</template>
<script>
import {ElMessage} from "element-plus";
let formData = new FormData;
export default {
name: "BorrowInfoList",
data(){
return{
loading:true,
TimeOption:[],
Rooms:[],
userInfo:{
userId:'',
role:'',
username:'',
userDepart:'',
},
borrowInfo:[],
subForm:{
date:'',
time:'',
reason:'',
userId:'',
roomName:'',
pageNum:1,
pageSize: 30,
total:0
},
startDate:null,
}
},
mounted() {
this.userInfo.role = window.sessionStorage.getItem("role");
this.userInfo.userId = window.sessionStorage.getItem("userId");
this.userInfo.username = window.sessionStorage.getItem("username");
this.getBorrowInfo();
this.getAllRooms();
this.getAllTimeOptions();
console.log(this.userInfo)
},
methods:{
getAllTimeOptions(){
this.loading = true;
this.$http({
url:'/timeOption',
method:'get',
}).then(({data})=>{
for(let i=0;i<data.list.length;i++){
this.TimeOption.push({
label:data.list[i].name,
key:data.list[i].name,
value:data.list[i].name
})
}
this.loading = false;
})
},
getAllRooms(){
this.loading = true;
this.$http({
url:'/room',
method:'get',
}).then(({data})=>{
for(let i=0;i<data.list.length;i++){
this.Rooms.push({
label:data.list[i].name,
key:data.list[i].name,
value:data.list[i].name
})
}
this.loading = false;
})
},
getBorrowInfo(){
this.loading = true;
this.$http({
method:'post',
url: '/borrowInfo',
data:this.subForm
}).then(res =>{
console.log(res);
if (res.data.code !== 200){
ElMessage({
message: '教室信息获取失败',
type: 'error',
})
}else {
this.borrowInfo = res.data.RBI;
this.subForm.total = res.data.total;
}
this.loading = false;
})
},
cancel(id){
this.$http({
method:'delete',
url: '/borrowInfo/' + parseInt(id),
}).then(res =>{
ElMessage({
message: '撤销成功',
type: 'success',
})
this.getBorrowInfo();
})
},
fileChange(files, fileList) {
formData.append('file', files.raw)
files = null;
this.importTimeTable();
},
importTimeTable(){
this.loading = true;
if (this.startDate === null || this.subForm.roomName === ''){
alert("请选择日期和教室");
this.loading = false;
return;
}
formData.append("startDate",this.startDate);
formData.append("roomName",this.subForm.roomName);
this.$http({
method: 'post',
url: '/file/importTimeTable',
data: formData,
headers: {
'Content-Type': 'multipart/form-data'
}
}).then(res => {
if (res.data.code === 200) {
this.$notify({
title: '上传成功',
message: '',
type: 'success'
});
this.getBorrowInfo();
} else {
this.$notify({
title: '文件解析失败',
message: res.data.msg,
type: 'error'
});
}
formData = null;
formData = new FormData();
this.loading = false;
})
},
exportExcel() {
this.loading = true;
this.$http({
method: 'get',
url: '/borrowInfo/exportExcel',
responseType: "arraybuffer"
}).then((file) => {
//
let content = file.data;
// a
let elink = document.createElement("a");
//
elink.download ="借用记录.xls";
elink.style.display = "none";
let blob = new Blob([content], {type: "application/xls"})
elink.href = URL.createObjectURL(blob);
document.body.appendChild(elink);
elink.click();
document.body.removeChild(elink);
this.loading = false;
})
},
}
}
</script>
<style scoped>
</style>

58
web/src/views/Home.vue Normal file
View File

@ -0,0 +1,58 @@
<template>
<div class="background">
<el-container>
<el-aside width="200px">
<div style="height: 100%;background-color: #334154">
<el-menu
active-text-color="#1989FA"
background-color="#334154"
class="el-menu-vertical-demo"
default-active="2"
text-color="#fff"
:router="true">
<div style="height: 70px;width: 100%;padding-top: 20px">
<img src="../assets/img/logoW.png" style="height: 60%;width: 80%; margin-left: 20px;margin-top: 5px" alt="">
</div>
<el-menu-item index="/ApplyExperiment">实验项目申请</el-menu-item>
<el-menu-item index="/BorrowRoom">教室借用</el-menu-item>
<el-menu-item index="/RecordList">记录查询</el-menu-item>
<el-menu-item index="/Admit" >项目审核</el-menu-item>
<el-menu-item index="/UserManage" >用户管理</el-menu-item>
<el-menu-item index="/RoomTimeAndReasonManage" >系统管理</el-menu-item>
<el-menu-item index="/Personal">个人信息</el-menu-item>
</el-menu>
</div>
</el-aside>
<el-main>
<router-view></router-view>
</el-main>
</el-container>
</div>
</template>
<script>
export default {
name: "Home",
data(){
return{
userInfo:{
userId:'',
role:'',
username:'',
userDepart:'',
},
}
},
mounted() {
this.userInfo.role = window.sessionStorage.getItem("role");
this.userInfo.userId = window.sessionStorage.getItem("userId");
},
}
</script>
<style scoped>
.background{
width: 1440px;
height: 100%;
}
</style>

View File

@ -1,18 +0,0 @@
<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App"/>
</div>
</template>
<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'
export default {
name: 'HomeView',
components: {
HelloWorld
}
}
</script>

97
web/src/views/Login.vue Normal file
View File

@ -0,0 +1,97 @@
<template>
<div class="bac">
<div class="loginBox">
<p>账号密码登陆</p>
<el-space direction="vertical" :size="30">
<el-input v-model="subform.id" placeholder="学号" clearable style="width: 240px;height: 40px;"/>
<el-input v-model="subform.password" placeholder="密码" type="password" style="width: 240px;height: 40px;"/>
</el-space>
<el-button type="primary" @click="login()" @keyup.enter="login()" style="width: 240px;height: 40px;font-size: 14px">登陆</el-button>
</div>
</div>
</template>
<script>
import { Avatar, Key } from '@element-plus/icons-vue'
import {ElNotification} from "element-plus";
export default {
name: 'Login',
data(){
return{
subform:{
id:'',
password:'',
}
}
},
mounted() {
document.addEventListener('keydown', this.handleEnterKey);
},
methods:{
handleEnterKey(){
// keyCode13
if (event.keyCode === 13) {
//
this.login();
}
},
login(){
this.$http({
method:'post',
url:'/login',
data:this.subform
}).then(res =>{
if(res.data.code!==200){
ElNotification({
title: '登陆失败',
message: res.data.msg,
type: 'warning',
})
}else if (res.data.code === 200){
ElNotification({
title: '登陆成功',
message: '你好,' + res.data.username,
type: 'success',
})
//
window.sessionStorage.setItem('role',res.data.role);
window.sessionStorage.setItem('username',res.data.username);
window.sessionStorage.setItem('userId', res.data.id.toString());
window.sessionStorage.setItem('token', res.data.token);
this.$router.push('/Home');
}else {
ElNotification({
title: '服务器错误',
message: '工具人QQ3231977651',
type: 'error',
})
}
})
}
},
components:{
Avatar,
Key
}
}
</script>
<style scoped>
.bac{
width: 1440px;
height: 900px;
background: url("../assets/img/loginBac.png");
}
.loginBox{
width: 250px;
height: 320px;
position: absolute;
left: 62%;
top: 320px;
}
</style>

View File

@ -0,0 +1,92 @@
<template>
<!--个人信息-->
<el-page-header content="个人信息" style="margin-bottom: 30px" @back="this.$router.push('/Login')"/>
<el-card style="padding-left: 60px;padding-right: 60px">
<el-descriptions title="个人信息" border>
<el-descriptions-item label="用户ID">{{userInfo.userId}}</el-descriptions-item>
<el-descriptions-item label="用户名">{{userInfo.username}}</el-descriptions-item>
<el-descriptions-item label="身份">{{userInfo.role}}</el-descriptions-item>
<el-descriptions-item label="操作">
<el-button type="primary" @click="pwdVisable=true">修改密码</el-button>
<el-button type="danger" @click="logout">退出</el-button>
</el-descriptions-item>
</el-descriptions>
</el-card>
<el-dialog
v-model="pwdVisable"
title="修改密码"
width="30%"
>
<el-form label-width="120px">
<el-form-item label="旧密码">
<el-input v-model="userInfo.oldPwd"/>
</el-form-item>
<el-form-item label="新密码">
<el-input v-model="userInfo.newPwd"/>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="pwdVisable = false">取消</el-button>
<el-button type="primary" @click="changePwd">确认</el-button>
</span>
</template>
</el-dialog>
</template>
<script>
import {ElMessage} from "element-plus";
export default {
name: "Personal",
data(){
return{
userInfo:{
userId:'',
role:'',
username:'',
userDepart:'',
newPwd:'',
oldPwd:''
},
pwdVisable:false
}
},
mounted() {
this.userInfo.role = window.sessionStorage.getItem('role');
this.userInfo.name = window.sessionStorage.getItem("username");
this.userInfo.username = window.sessionStorage.getItem("username");
this.userInfo.userId = window.sessionStorage.getItem("userId");
},
methods:{
changePwd(){
this.$http({
url:'/changePwd',
method:'put',
data:this.userInfo
}).then(({data})=> {
if (data.code === 200){
ElMessage({
message: '修改成功',
type: 'success',
})
this.pwdVisable = false;
}else {
ElMessage({
message: '修改失败,' + data.msg,
type: 'warning',
})
}
})
},
logout(){
window.sessionStorage.clear();
this.$router.push('/Login')
}
}
}
</script>
<style scoped>
</style>

20
web/vue.config.js Normal file → Executable file
View File

@ -2,3 +2,23 @@ const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true
})
// module.exports = {
// chainWebpack: config => {
// config.module
// .rule('vue')
// .use('vue-loader')
// .tap(options => {
// options.compilerOptions.isCustomElement = tag => tag.startsWith('el-')
// return options
// })
//
// config.module
// .rule('ts')
// .use('ts-loader')
// .tap(options => {
// options.appendTsSuffixTo = [/\.vue$/]
// return options
// })
// }
// }