从零开始搭建一个JavaSSM作业管理系统(四)
从零开始搭建一个JavaSSM作业管理系统系列
从零开始搭建一个JavaSSM作业管理系统(一)
从零开始搭建一个JavaSSM作业管理系统(二)
从零开始搭建一个JavaSSM作业管理系统(三)
从零开始搭建一个JavaSSM作业管理系统(四)
项目下载地址
说明!!!
1.本系列的文章仅展示搭建SSM作业管理系统的大致流程,文章中不会提供该项目的完整代码,如果需要完整代码可以在提供的链接中自行下载
2.本系列只展示作业管理系统一小部分功能实现(登录功能、管理员界面的学生信息管理功能),大部分功能都是信息的增删改查基本操作,重复度较多,大体步骤也都相同,故不做过多展示,请大家见谅
3.博主本人也算是spring框架的初学者,写此系列的目的旨在分享个人在学习SSM过程中的一些经验,如果大家在博客中发现代码或一些解释有误,还望多多指正
从零开始搭建一个JavaSSM作业管理系统(四)
- 从零开始搭建一个JavaSSM作业管理系统系列
- 前言
- 摘要
- 说明
- 登录功能
- 1.html
- 2.js
- 3.Controller层
- 4.演示
- 管理员界面-学生信息管理功能
- 增加学生信息
- 1.html
- 2.js
- 3.Controller层
- 4.演示
- 查看学生详细列表
- 1.html
- 2.js
- 3.Controller层
- 4.Layui工具类
- 5.演示
- 删除、修改学生信息
- 1.html
- 2.js
- 3.Controller层
- 4.演示
- 结语
前言
在完成底层工作后,下面我们来逐一实现作业管理系统的功能
摘要
本文主要介绍作业管理系统相关功能的实现
- 登录功能
- 管理员界面的学生信息管理功能
本文也是该系列的最后一篇
说明
前端我采用了Layui框架
相关代码的使用可以参考Layui官方文档
Layui
登录功能
学生身份
教师身份
管理员身份
登录界面
作业管理系统-学生界面
作业管理系统-教师界面
作业管理系统-管理员界面
1.html
<form class="layui-form zyl_pad_01" action="">
<div class="layui-col-sm12 layui-col-md12">
<div class="layui-form-item">
<input type="text" name="userId" id="userId" lay-verify="required|userId" autocomplete="off" placeholder="账号" class="layui-input">
<i class="layui-icon layui-icon-username zyl_lofo_icon"></i>
</div>
</div>
<div class="layui-col-sm12 layui-col-md12">
<div class="layui-form-item">
<input type="password" name="password" id="password" lay-verify="required|password" autocomplete="off" placeholder="密码" class="layui-input">
<i class="layui-icon layui-icon-password zyl_lofo_icon"></i>
</div>
</div>
<div class="layui-col-sm12 layui-col-md12">
<div class="layui-form-item">
<select name="type" id="type" lay-filter="">
<option value=""></option>
<option value="0" selected="">学生</option>
<option value="1">教师</option>
<option value="2">管理员</option>
</select>
<i class="layui-icon layui-icon-friends zyl_lofo_icon"></i>
</div>
</div>
<div class="layui-col-sm12 layui-col-md12">
<div class="layui-row">
<div class="layui-col-xs4 layui-col-sm4 layui-col-md4">
<div class="layui-form-item">
<input type="text" name="vercode" id="vercode" lay-verify="required|vercodes" autocomplete="off" placeholder="验证码" class="layui-input" maxlength="4">
<i class="layui-icon layui-icon-vercode zyl_lofo_icon"></i>
</div>
</div>
<div class="layui-col-xs4 layui-col-sm4 layui-col-md4">
<div class="zyl_lofo_vercode zylVerCode" onclick="zylVerCode()"></div>
</div>
</div>
</div>
<div class="layui-col-sm12 layui-col-md12">
<button class="layui-btn layui-btn-fluid" lay-submit="" lay-filter="login">立即登录</button>
</div>
</form>
2.js
layui.use(['form'], function(){
var form = layui.form;
//自定义验证规则
form.verify({
userId: function(value){
if(hasLetter(value) == true || isChina(value) == true){
return '账号不能含有英文字母或中文';
}
}
,password: [/^[\S]{5,12}$/,'密码必须5到12位,且不能出现空格']
,vercodes: function(value){
//获取验证码
var zylVerCode = $(".zylVerCode").html();
if(value!=zylVerCode){
return '验证码错误(区分大小写)';
}
}
,content: function(value){
layedit.sync(editIndex);
}
});
//监听提交
form.on('submit(login)', function(data){
//用ajax时需要注意你的url接口、采用哪一种方式type获取,它的使用的哪种数据类型datatype
$.ajax({
url:'/home/login',
type:'get',
dataType:'json',
data:data.field,
beforeSend:function(){
//弹出的lodinng层
layer.load(2,{
shade:[0.5,"#02fff0"]
});
},
success:function(res){
if (res.redirect) {
window.location.href = res.url;
} else {
layer.msg("无法进入系统,输入的账号或密码错误");
}
},
error:function(){
//用户输入与接口内容不对应,显示文字
layer.msg("访问失败")
},
complete:function(){
//关掉loading
layer.closeAll("loading")
}
})
return false;//不会跳转到网址栏,只会跳转到你要的界面 一定要写。
});
});
(1)layui.use() 主要用来导入所需要用到的Layui模块,这里我导入了form,即Layui的表单模块,同时所有js代码也需要写在use()的function里面
如果在js代码里使用form需要定义var form = layui.form
具体详细用法可以参考Layui的官方文档
(2)form.verify() 主要定义表单内元素的验证规则,如果表单内某个标签需要自定义验证规则,需要在该标签内添加lay-verify=“名字”,如
<input type="text" lay-verify="required|name" class="layui-input">
required|name - 添加’required’则设置该标签为必需填写的标签,’|’后的name则相当于验证ID
验证时只需在js代码中编写:
form.verify({
name:验证的规则
})
(3)form.on(‘submit(login)’, function(data){}) 为监听表单提交的函数
对应button如下:
<button class="layui-btn layui-btn-fluid" lay-submit="" lay-filter="login">立即登录</button>
在button标签里加入lay-filter即可监听提交
3.Controller层
@Controller
@RequestMapping("/home")
public class HomeManagementController {
@Autowired
private StudentService studentService;
@Autowired
private TeacherService teacherService;
@Autowired
private AdminService adminService;
//登录信息处理
@RequestMapping(value = "/login",method = RequestMethod.GET)
@ResponseBody
private Map<String, Object> login(HttpServletRequest request){
Map<String, Object> modelMap = new HashMap<>();
int type = HttpServletRequestUtil.getInt(request,"type");
long userId = HttpServletRequestUtil.getLong(request,"userId");
String inputPassword = HttpServletRequestUtil.getString(request,"password");
String realPassword;
if (type == 0){
Student student = studentService.getStudentById(userId);
realPassword = student.getPassword();
if (realPassword.equals(inputPassword)){
request.getSession().setAttribute("studentId",userId);
modelMap.put("redirect",true);
modelMap.put("url","/home/studentIndex?studentId="+student.getStudentId());
}else {
modelMap.put("redirect",false);
}
}else if (type == 1){
Teacher teacher = teacherService.getTeacherById(userId);
realPassword = teacher.getPassword();
if (realPassword.equals(inputPassword)){
request.getSession().setAttribute("teacherId",userId);
modelMap.put("redirect",true);
modelMap.put("url","/home/teacherIndex?teacherId="+teacher.getTeacherId());
}else {
modelMap.put("redirect",false);
}
}else if (type == 2){
Admin admin = adminService.getAdminById(userId);
realPassword = admin.getPassword();
if (realPassword.equals(inputPassword)){
request.getSession().setAttribute("adminId",userId);
modelMap.put("redirect",true);
modelMap.put("url","/home/adminIndex?adminName="+admin.getAdminName());
}else {
modelMap.put("redirect",false);
}
}else {
modelMap.put("redirect",false);
}
return modelMap;
}
}
Controller层主要接受前端传过来的数据:
type-用户类型
userId-账号
password-密码
前后端处理数据流程如下:
首先根据type判断用户的类型,再根据账号和密码判断该用户的账号和密码是否正确,如果正确,返回相应的url和success(状态为true),前端再根据返回的url跳转的相应界面;如果不正确只返回success(状态为false)
4.演示
此处只演示学生界面的登录
管理员界面-学生信息管理功能
学生信息管理
增加学生信息
删除学生信息
修改学生信息
查看学生详细列表
增加学生信息
1.html
<form class="layui-form" action="" lay-filter="add-studentInfo">
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label">学生头像</label>
<input type="file" name="profileImg">
</div>
</div>
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label">学生号</label>
<div class="layui-input-inline">
<input type="text" name="studentId" lay-verify="required|studentId" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-inline">
<label class="layui-form-label">学生姓名</label>
<div class="layui-input-inline">
<input type="text" name="studentName" lay-verify="required|studentName" autocomplete="off" class="layui-input">
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">性别</label>
<div class="layui-input-block">
<input type="radio" name="gender" value="男" title="男">
<input type="radio" name="gender" value="女" title="女">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">邮箱</label>
<div class="layui-input-block">
<input type="text" name="email" lay-verify="email" placeholder="" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">简介</label>
<div class="layui-input-block">
<textarea name="studentDesc" placeholder="请输入内容" class="layui-textarea"></textarea>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">所属班级</label>
<div class="layui-input-block">
<div class="layui-col-md6">
<select id="clazz">
</select>
</div>
</div>
</div>
<div class="layui-form-item layui-layout-admin">
<div class="layui-input-block">
<div class="layui-footer" style="left: 0;">
<button class="layui-btn" lay-submit="" lay-filter="add-student">立即提交</button>
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
</div>
</div>
</div>
</form>
2.js
layui.use(['index', 'form'], function(){
var $ = layui.$
,layer = layui.layer
,form = layui.form;
form.render(null, 'add-studentInfo');
/* 自定义验证规则 */
form.verify({
studentId: function(value){
if(hasLetter(value) === true || isChina(value) === true){
return '学生号不能含有中文';
}
}
,studentName: function(value){
if(containsNumber(value) === true){
return '学生姓名不能含有数字';
}
}
});
getClazzList();
// 获取班级列表
function getClazzList(){
$.get('/admin/listClazzMap', function(res) {
if (res.success){
var tempHtml = '';
//根据班级列表动态拼接html
res.clazzList.map(function (item, index) {
tempHtml += '<option data-id="' + item.clazzId + '">'
+ item.clazzDesc + '</option>';
});
$('#clazz').html(tempHtml);
form.render('select');
}
});
}
var student = { };
/* 监听提交 */
form.on('submit(add-student)', function(data){
// 获取缩略图文件流
var studentImg = $('input[name="profileImg"]')[0].files[0];
student.studentId = $('input[name="studentId"]').val();
student.studentName = $('input[name="studentName"]').val();
student.gender = $('input[name="gender"]:checked').val();
student.email = $('input[name="email"]').val();
student.studentDesc = $('textarea[name="studentDesc"]').val();
student.clazz = {
clazzId : $('#clazz').find('option').not(function () {
return !this.selected;
}).data('id')
};
var formData = new FormData();
formData.append('studentImg', studentImg);
formData.append('studentStr',JSON.stringify(student));
//用ajax时需要注意你的url接口、采用哪一种方式type获取,它的使用的哪种数据类型datatype
$.ajax({
url:'/admin/addStudent',
type:'post',
data:formData,
contentType : false,
processData : false,
success:function(res){
if (res.success){
layer.msg("添加学生信息成功");
}else {
layer.msg("添加学生信息失败" + res.errMsg);
}
}
})
return false;
});
});
form.render(null, ‘add-studentInfo’) 主要对表单所有元素更新渲染,相当于初始化表单
form.render(‘select’) 主要对表单中select标签更新渲染,由于学生表单中select标签为动态插入,Layui中form模块的自动化渲染是会对其失效的,所以需要在动态插入select标签里的元素后再进行一次渲染
有关表单渲染的详细介绍可以参考Layui的官方文档
3.Controller层
@Controller
@RequestMapping("/admin")
public class StudentManagementController {
@Autowired
private StudentService studentService;
//添加学生信息
//获取前端ajax传递的字符串,解析字符串为相应的student实体,根据解析好的数据添加学生信息
@RequestMapping(value = "/addStudent",method = RequestMethod.POST)
@ResponseBody
private Map<String,Object> addStudent(HttpServletRequest request){
Map<String,Object> modelMap = new HashMap<>();
//1.接受并转化相应的参数
//获取前端传过来的学生信息,并将它转换成实体类;
//同时获取前端传递过来的文件流,将它接受到studentImg里面去
String studentStr = HttpServletRequestUtil.getString(request,"studentStr");
if (studentStr == null){
modelMap.put("success",false);
modelMap.put("errMsg","请输入学生信息");
return modelMap;
}
ObjectMapper mapper = new ObjectMapper();
Student student;
try {
student = mapper.readValue(studentStr,Student.class);
} catch (JsonProcessingException e) {
e.printStackTrace();
modelMap.put("success",false);
modelMap.put("errMsg",e.getMessage());
return modelMap;
}
CommonsMultipartFile studentImg;
CommonsMultipartResolver commonsMultipartResolver =
new CommonsMultipartResolver(
request.getSession().getServletContext()
);
MultipartHttpServletRequest multipartHttpServletRequest = (MultipartHttpServletRequest) request;
studentImg = (CommonsMultipartFile) multipartHttpServletRequest.getFile("studentImg");
//2.添加学生信息
StudentExecution studentExecution;
try {
ImageHolder imageHolder = null;
if (studentImg != null){
imageHolder = new ImageHolder(studentImg.getOriginalFilename(),studentImg.getInputStream());
}
studentExecution = studentService.addStudent(student,imageHolder);
if (studentExecution.getState() == StudentStateEnum.SUCCESS.getState()){
modelMap.put("success",true);
}else{
modelMap.put("success",false);
modelMap.put("errMsg",studentExecution.getStateInfo());
}
} catch (IOException e) {
modelMap.put("success",false);
modelMap.put("errMsg",e.getMessage());
}
return modelMap;
//3.返回结果
}
}
4.演示
查看学生详细列表
1.html
<table class="layui-hide" id="student-list" lay-filter="student-list"></table>
2.js
layui.use(['table'], function(){
var table = layui.table
table.render({
elem: '#student-list'
,id: 'studentTable'
,url: '/admin/listStudent'
,toolbar: '#operation'
,title: '学生信息列表'
,cols: [[
{ field:'studentId', title:'学生ID号', width:120, fixed: 'left', unresize: true, sort: true},
{ field:'password', title:'密码', width:150},
{ field:'studentName', title:'学生姓名', width:120, sort: true},
{ field:'gender', title:'性别', width:80},
{ field:'email', title:'邮箱', width:150, templet: function(res){
return '<em>'+ res.email +'</em>'
}},
{ field:'studentDesc', title:'简介', width:250},
{ title:'班级ID号', width:120, sort: true, templet: function(data){
return data.clazz.clazzId;
}},
{ title:'班级名', width:150, sort: true, templet: function(data){
return data.clazz.clazzDesc;
}},
{ title:'创建时间', width:120, templet: function(res){
return new Date(res.createTime).Format("yyyy-MM-dd");
}},
{ title:'最近一次更新时间', width:160, templet: function(res){
if (res.lastEditTime != null){
return new Date(res.lastEditTime).Format("yyyy-MM-dd");
}else {
return "暂未更新";
}
}},
{ fixed: 'right', title:'操作', toolbar: '#operation', width:150}
]]
,page: true
});
});
(1)elem对应于html中id为student-list的table标签
(2)url是将要访问的接口
(3)cols,与controller层返回的集合相关联,里面的field值对应着集合里面相同名字的字段,如果需要更改controller层返回的某一个字段的格式,需要用到templet
示例如下:
{ field:'email', title:'邮箱', width:150, templet: function(res){
return '<em>'+ res.email +'</em>'
}}
此处代码是将后端传递过来的字段email通过templet读取,并用html的标签将字段值包裹起来
最终显示如下,这是其中一行的数据:
可以看到,表格中邮箱那一列下面的值是斜体的,可见templet生效了
(4)toolbar控件-工具条
示例如下:
{ fixed: 'right', title:'操作', toolbar: '#operation', width:150}
<script type="text/html" id="operation">
<a class="layui-btn layui-btn-xs" lay-event="edit">编辑</a>
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
</script>
通过定义的id为operation的组件,Layui的table便能显示出工具条
3.Controller层
@Controller
@RequestMapping("/admin")
public class StudentManagementController {
@Autowired
private StudentService studentService;
//列出所有学生列表-返回为Layui类型
@RequestMapping(value = "/listStudent",method = RequestMethod.GET)
@ResponseBody
public Layui listStudent(){
//查询显示列表数据
List<Student> studentList = studentService.getStudentList();
return Layui.data(studentList.size(),studentList);
}
}
4.Layui工具类
Layui的table有规定的数据格式,所以需要定义一个Layui工具类封装查询到的列表数据
public class Layui extends HashMap<String, Object> {
public static Layui data(Integer count, List<?> data){
Layui r = new Layui();
r.put("code", 0);
r.put("msg", "");
r.put("count", count);
r.put("data", data);
return r;
}
}
返回数据格式如下:
5.演示
删除、修改学生信息
1.html
<table class="layui-hide" id="student-list" lay-filter="student-list"></table>
<script type="text/html" id="operation"> <a class="layui-btn layui-btn-xs" lay-event="edit">编辑</a> <a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a> </script>
2.js
(1)表单监听,通过工具条控件中lay-event的值来进行情况判断
//监听行工具事件
table.on('tool(student-list)', function(obj){
var data = obj.data;
if(obj.event === 'del'){
layer.confirm('确定要删除吗', function(index){
delStudentById(data.studentId);
layer.close(index);
});
} else if(obj.event === 'edit'){
layer.open({
//layer提供了5种层类型。可传入的值有:0(信息框,默认)1(页面层)2(iframe层)3(加载层)4(tips层)
type: 1,
title: "修改学生信息",
area: ['600px', '400px'],
content: $("#popUpdateStudent")//引用的弹出层的页面层的方式加载修改界面表单
});
//动态向表传递赋值可以参看文章进行修改界面的更新前数据的显示,当然也是异步请求的要数据的修改数据的获取
setFormValue(obj,data);
}
});
(2)修改界面表单
<!--这里是弹出层的表单信息-->
<!--style是在本页隐藏,只有点击编辑才会弹出-->
<div class="layui-row" id="popUpdateStudent" style="display:none;">
<div class="layui-col-md10">
<form class="layui-form layui-from-pane" action="" style="margin-top:20px" lay-filter="modify-student">
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label">学生头像</label>
<input type="file" name="profileImg">
</div>
</div>
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label">学生号</label>
<div class="layui-input-inline">
<input type="text" name="studentId" class="layui-input" readonly>
</div>
</div>
<div class="layui-inline">
<label class="layui-form-label">学生姓名</label>
<div class="layui-input-inline">
<input type="text" name="studentName" lay-verify="required|studentName" class="layui-input">
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">性别</label>
<div class="layui-input-block">
<input type="radio" name="gender" value="男" title="男">
<input type="radio" name="gender" value="女" title="女">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">邮箱</label>
<div class="layui-input-block">
<input type="text" name="email" lay-verify="email" class="layui-input">
</div>
</div>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">简介</label>
<div class="layui-input-block">
<textarea name="studentDesc" placeholder="请输入内容" class="layui-textarea"></textarea>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">所属班级</label>
<div class="layui-input-block">
<div class="layui-col-md6">
<select id="clazz">
</select>
</div>
</div>
</div>
<div class="layui-form-item layui-layout-admin">
<div class="layui-input-block">
<div class="layui-footer" style="left: 0;">
<button class="layui-btn" lay-submit="" lay-filter="edit-student">立即提交</button>
</div>
</div>
</div>
</form>
</div>
</div>
3.Controller层
@Controller
@RequestMapping("/admin")
public class StudentManagementController {
@Autowired
private StudentService studentService;
//修改学生信息
//获取前端ajax传递的字符串,解析字符串为相应的student实体,根据解析好的数据修改学生信息
@RequestMapping(value = "/modifyStudent",method = RequestMethod.POST)
@ResponseBody
public Map<String,Object> modifyTeacher(HttpServletRequest request) throws IOException {
Map<String, Object> modelMap = new HashMap<>();
// 接收前端参数的变量的初始化
ObjectMapper mapper = new ObjectMapper();
Student student;
// 若请求中存在文件流,则取出相关的文件
CommonsMultipartFile studentImg = null;
CommonsMultipartResolver commonsMultipartResolver =
new CommonsMultipartResolver(
request.getSession().getServletContext()
);
if (commonsMultipartResolver.isMultipart(request)){
MultipartHttpServletRequest multipartHttpServletRequest = (MultipartHttpServletRequest) request;
studentImg = (CommonsMultipartFile) multipartHttpServletRequest.getFile("studentImg");
}
try {
String studentStr = HttpServletRequestUtil.getString(request, "studentStr");
// 尝试获取前端传过来的表单string流并将其转换成Student实体类
student = mapper.readValue(studentStr, Student.class);
} catch (Exception e) {
modelMap.put("success", false);
modelMap.put("errMsg", e.toString());
return modelMap;
}
try {
ImageHolder imageHolder = null;
// 开始进行学生信息变更操作
if (studentImg != null){
imageHolder = new ImageHolder(studentImg.getOriginalFilename(),studentImg.getInputStream());
}
StudentExecution studentExecution = studentService.modifyStudent(student, imageHolder);
if (studentExecution.getState() == StudentStateEnum.SUCCESS.getState()) {
modelMap.put("success", true);
} else {
modelMap.put("success", false);
modelMap.put("errMsg", studentExecution.getStateInfo());
}
} catch (RuntimeException e) {
modelMap.put("success", false);
modelMap.put("errMsg", e.toString());
return modelMap;
}
return modelMap;
}
//删除学生信息
//根据前端路由路径中传递的studentId值删除指定学生信息
@RequestMapping(value = "/deleteStudent",method = RequestMethod.GET)
@ResponseBody
public Map<String,Object> deleteStudent(@RequestParam("studentId") Long studentId){
Map<String,Object> modelMap = new HashMap<>();
studentService.deleteStudent(studentId);
modelMap.put("success",true);
return modelMap;
}
}
4.演示
结语
本系列虽然结束了,但是作业管理系统要实现的功能还有很多,其实系统的各种功能都涉及信息的增删改查功能,大体实现方法均可以仿照我实现学生信息管理的流程来设计
我个人而言,对于每个功能的设计流程如下:
dao层
service层
controller层
前端界面
每设计完一个层级的功能后,便对该层进行测试
- 对dao层和service层进行UT测试即可(UT测试的流程我已经在前面展示过了)
- 对controller层的测试一般与前端相关联,比较简单的方法是将数据以map形式返回到前端,在浏览器的网址直接输入对应的路由
关于作业管理系统,还有很多复杂的功能,例如文件上传、文件下载、图像处理等等,这些我并没有在该系列里展示出来。就我个人理解,这些功能都是在基本功能上的扩展,如果理解了SSM框架增删改查四种基本操作实现方法的话,相信其他功能的实现方法自然很好理解
最后也再次说明一下,我也是spring框架的初学者,写这个系列的目的也是希望像我一样初学SSM框架的人有所帮助吧,通过这个系列向大家展示搭建SSM框架的整个流程
关于这个系列的所有代码和解释如果大家发现哪里有错误的话,欢迎指正
还没有评论,来说两句吧...