软件工程-高校招生信息网开发过程问题小记
- 开发环境
- 开发过程
- 登录跳转模块
- shiro权限控制模块
- 后台信息显示模块
- 前台点击新闻标题显示内容模块
- 录取查询模块
- 留言模块
- 项目源码
一、开发环境
- 开发工具:IDEA、Hbuilder、Chrome
- 技术选型:SpringBoot、Layui、Qadmin后台模板、Thymeleaf、Jquery、Shiro、MySQL等
- 开发文档参考:http://www.qadmin.net/,https://www.layui.com/doc/modules/layer.html#layer.alert
二、开发过程
1.登录跳转模块
需求
- 用户输入账号、密码点击信息网站的登录按钮实现跳转后台。
- 前台信息网站使用的为layui的表单组件,后台使用的是Qadmin模板。
问题1:url隐藏表单参数
加上action发现没有反应,因为该项目整合了Thymeleaf,因此需要th:method="post"
<form class="layui-form layui-form-pane " th:method="post" th:action="@{/login}">
<div class="layui-form-item">
<label class="layui-form-label">账号</label>
<div class="layui-input-inline">
<input type="text" name="userNumber" lay-verify="required" placeholder="请输入"
autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">密码</label>
<div class="layui-input-inline">
<input type="password" name="password" placeholder="请输入密码"
autocomplete="off" class="layui-input">
</div>
<div class="layui-form-mid layui-word-aux">请务必填写用户名</div>
</div>
<div class="layui-form-item">
<button type="reset" class="layui-btn login-form-btn">重置</button>
<button class="layui-btn login-form-btn" type="submit"
>登录
</button>
<p th:text="${msg}">AAA</p>
</div>
</form>
问题2:前台夜幕按无样式、前台跳转到后台找不到页面
这里所出的问题基本都是 SpringMVC静态资源拦截、路径的问题
-
可能样式引入的路径错误,默认templates目录下放页面、static目录下放静态资源(js、css、图片)。如果直接放在static目录,则不需要配置什么,样式可以直接找到。可以直接写引入,也可以使用thymeleaf形式引入。
-
这些静态资源的配置可以应对特殊情况(对于标准文件位置),如静态资源文件放在static的其他文件夹,需要进行以下配置SpringMVC拓展。
// 2. 资源处理 @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/"); WebMvcConfigurer.super.addResourceHandlers(registry); }
问题3:后台页面只能显示左侧导航栏
因为使用的Qadmin后台模板是一个 基于 vue + layui 混合而成的合体,并无vue组件的实现,而是分左导航部分 + 右页面部分
右页面部分是在左导航部分点击按钮herf到另外一个页面,因此不是一个整体的html,问题所在就是:
- controller默认只能跳转一个html页面,也就是左侧导航部分
- 而右侧显示的页面发起的是一个xxxx.html的请求,需要在写一个对应的controller来处理请求,有多少页面就需要写多少个映射。
- 可以在这个controller中加上对应的service获取数据,通过model传到要显示的页面,因为每次打开页面都需要从新渲染数据。
问题4:重定向redirect跳转到static目录下的页面
-
默认转发是到templates目录下的文件
-
但是redirect重定向是到static目录下找html页面,需要带上
.html
后缀
其他问题
-
实现解决登录失败layui配合model进行弹窗,思路是jquery判断model的msg是否为空,不为空则弹窗
-
解决shiro整合Thymeleaf第一次重定向会出现
jessionid
登录失败的问题。需要配置server.servlet.session.tracking-modes=COOKIE
,参考:Thymeleaf自动在URL后加了;jsessionid=的问题导致首次登录失败! -
解决异步获取信息页面不跳转的问题,异步请求配合tabs的index切换异步发起请求。
-
页面onload发起异步请求,渲染第二部分新闻的信息
-
mybatis的配置多个扫描路径,resources/mapper下的子文件加需要配置保证能扫描到mapper-locations:
classpath*:/mapper/*.xml,classpath*:/mapper/back/*.xml
,注意一定要注意classpath*
-
Thymeleaf遍历可以获取索引
<tr th:each="admin ,stat: ${admins}"> <td th:text="${stat.index + 1}"></td> </tr>
-
实体类Admin继承User,表单传的添加用户的数据不完整,从父类继承的属性并未匹配上。继承关系需要取消,Admin中需要包含全部属性
关于请求
新闻模块
- 新闻tab一区,使用的是监听iddex发起异步请求,然后根据date的倒序返回json,显示若干条。然后直接jquer添加子元素完成添加。
- 新闻二区,采用的是onload加载发起异步请求,然后根据date的倒序返回json,显示若干条。然后直接jquer添加子元素完成添加。
用户模块
- 用户的显示(thymeleaf),在BackStageCobtroller中查询用户数据,返回model,渲染使用thymeleaf来遍历
- 用户的删除(thymeleaf),thymeleaf在href中定义请求url,通过
?id=xxx
传值,后端RequestParam接受。 - 用户的更新(thymeleaf),拿着id请求后端,然后查出信息回显到页面,通过
[[${xxx}]]
不行name报错undefined,thymeleaf配合隐藏的input才成功!! jquery根据id选择器渲染数据。 - 用户的新增(thymeleaf),表单action直接post提交
- 用户角色的显示(数据库对应的为admin,需要显示 管理员)
<td th:if="${admin.getRole() eq 'admin'}">管理员</td>
<td th:if="${admin.getRole() eq 'super_admin'}">超级管理员</td>
2.shiro后台权限控制
-
未登录状态对后台拦截
//设置未登录认证拦截器 filterMap.put("/back/**","authc");
-
只有超级管理员才有用户管理的权限,在userReleam里面已经添加角色,直接使用注解拦截,
@RequiresRoles("super_admin")
,或者使用过滤器链拦截,需要注意权限条件的顺序,如//下面这些权限只能实现单资源单用户(因为Map的Key唯一),对于单资源多用户需要设置角色) filterMap.put("/back/user_index.html","roles[super_admin]"); filterMap.put("/back/user_add.html","roles[super_admin]"); filterMap.put("/back/user_index","roles[super_admin]"); filterMap.put("/back/user_add","roles[super_admin]"); filterMap.put("/back/**","authc"); // 放在最后,前面的才能生效
-
侧边菜单显示,页面级别的控制。
// 命名空间 <html xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro"> // 在子菜单的遍历渲染中,控制到403页面 <div shiro:hasRoles="xxx"></div>
3.后台信息显示模块
-
表格渲染,content太长,开始准备使用折叠面板处理,但是折叠面板放在td中不太行,最后设置表格的style为内容溢出隐藏,这样又有一个问题,就是还需要能点击按钮显示全部文章内容。使用layui的button,然后监听弹出msg显示。不太好实现,最后直接使用
if (this.offsetWidth < this.scrollWidth) {}
实现弹出框。<td height="80px" > <div th:text="${news.getContent()}" // 设置长度过长隐藏 style="width: 500px ;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;"> </div> </td>
<script> layui.use(['element', 'layer'], function () { var element = layui.element; var layer = layui.layer; $(function() { $("td div").on("click", function() { //js主要利用offsetWidth和scrollWidth判断是否溢出。 //在这里scrollWidth是包含内容的完全高度offsetWidth是当前表格单元格的宽度。 if (this.offsetWidth < this.scrollWidth) { var that = this; var text = $(this).text(); //示范一个公告层 layer.open({ type: 1 ,title: '详细信息' //不显示标题栏 ,closeBtn: false ,area: '800px;' ,shade: 0.8 ,id: 'LAY_layuipro' //设定一个id,防止重复弹出 ,btn: ['关闭'] ,btnAlign: 'c' ,moveType: 0 //拖拽模式,0或者1 ,content: '<div style="padding: 20px ;"><pre>' + text + '</pre></div>' }); } }); }) }); window.onload = function () { layer.msg("未全部显示的信息可以点击查看~", {icon: 6,title:'tips',anim: 5,offset: '100px'}); } </script>
-
删除:点击按钮,通过
?=xxx
传递id直接删除 -
增加:id选择点击添加按钮,弹出来表单对话框(开始设置form的display属性为none),然后直接发起异步请求。
-
问题1:报错:
{"timestamp":"2021-06-01T15:48:03.020+00:00","status":415,"error":"Unsupported Media Type","message":"","path":"/news/save"}
,原因是后端加了@RequestBody
,这是jquery的方式,不是ajax。 -
问题2:最后需要刷新一些页面,从新获取文章信息,
window.location.href="/back/recruitment_dynamics_news.html";
注意不要加parent
-
问题3:日期的获取,使用jquery封装一个getFormatDate()的方法,返回当前时间提交到表单。
-
-
更新:点击修改按钮,class选择弹出来表单对话框(开始设置form的display属性为none),然后数据回显,修改完成发起异步请求。
-
问题1:数据回显实现,点击对应行的修改按钮,需要将本行的数据id,articleTitle,content回显至表单,主要是通过Jquery的选择器,首选class监听修改按钮点击事件,然后需要取出行数据
var title = $(this).parent("td").prevAll().children(".title").text() var id = $(this).parent("td").prevAll(".id").text() var content = $(this).parent("td").prevAll().children(".content").text() ... // 在成功打开弹出表单的回调事件中 ... // 数据回显 $('#title').val(title); $('#content').val(content); // 完整代码 <script> $('.update_btn').on('click', function(){ /* var that = this; var text = $(this).text(); var title = $(this).parents("tr").val().title var content = $(this).parents("tr").val().content */ var title = $(this).parent("td").prevAll().children(".title").text() var id = $(this).parent("td").prevAll(".id").text() var content = $(this).parent("td").prevAll().children(".content").text() //页面层,弹出表单 layer.open({ type: 1 //Page层类型 ,skin: 'layui-layer-molv' ,area: ['800px', '500px'] ,title: ['更新招生新闻','font-size:18px'] ,btn: ['确定', '取消'] ,shadeClose: true ,shade: 0 //遮罩透明度 ,maxmin: true //允许全屏最小化 ,content:$("#form_div") ,success: function(){ // 数据回显 $('#title').val(title); $('#content').val(content); } ,yes:function(){ if($('#title').val().length == 0 || $('#content').val() == 0){ layer.msg("标题、内容不能为空!") }else{ $.post({ url: "/news/update", dataType: 'json', data: { id:id, articleTitle: $('#title').val(), content: $('#content').val(), date: getFormatDate(), }, success: function (data) { layer.alert(data.message); window.location.href="/back/recruitment_dynamics_news.html"; }, error: function (error) { layer.msg(error); } }); } } }); }); </script>
-
4.前台点击标题显示文章内容
实现原理
-
首先放在li标题的长度需要做一个限制显示,测试的时候数据库字段给的长度太少了。也需要增加。
li{ white-space:nowrap; overflow: hidden; text-overflow:ellipsis; }
-
然后鼠标放上去需要显示全部标题tips
// 鼠标悬浮显示缩略全部信息 $(function() { $(document).on('mouseenter',"li.each_news",function(){ //layer.alert('hello'); if (this.offsetWidth < this.scrollWidth) { var that = this var title = $(this).children("a.each_title").eq(0).text() var date = $(this).children("span.each_date").eq(0).text() layer.tips(title + "日期:" + date, that, { tips: 1, time: 2000 }); } }) }),
-
点击标题显示文章内容
// 监听点击的标题,显示新闻内容 $(document).on('click',"li.each_news",function(){ // layer.alert('hello'); var content = $(this).children("span.each_content").eq(0).text() var title = $(this).children("a.each_title").eq(0).text() var date = $(this).children("span.each_date").eq(0).text() //示范一个公告层 layer.open({ type: 1 , title: '发布日期:' + date //标题栏 , closeBtn: false , area: '800px;' , shade: 0.8 , id: 'LAY_layuipro' //设定一个id,防止重复弹出 , btn: ['关闭'] , btnAlign: 'c' , moveType: 0 //拖拽模式,0或者1 , content: '<div style="padding: 20px ;"> <pre>' + content + '</pre></div>' }); });
5.录取模块
前台查询
- 点击按钮,Jquery根据id监听事件,layer弹出框中包含表单(设置表单的display为none)
- 填写完信息,点击确认查询按钮,直接发起post请求查询,根据res弹框反馈。
// 录取查询按钮监听事件
$(document).on('click',"#enroll_btn",function(){
//layer.alert('debug')
//页面层,弹出表单
layer.open({
type: 1 //Page层类型
,skin: 'layui-layer-molv'
,area: ['800px', '500px']
,title: ['录取查询','font-size:18px']
,btn: ['确定', '取消']
,shadeClose: true
,shade: 0 //遮罩透明度
,maxmin: true //允许全屏最小化
,content:$("#form_div")
,success: function(){
//layer.msg($('#userNumber').val())
//数据回显用不到
}
,yes:function(){
if($('#userNumber').val().length == 0 || $('#name').val() == 0){
layer.msg("准考证号、姓名不能为空!")
}else{
$.post({
url: "/enroll/find-one",
data: {
userNumber: $('#userNumber').val(),
name: $('#name').val(),
},
success: function (data) {
layer.alert(data.message);
},
error: function (error) {
layer.msg(error);
}
});
}
}
});
})
后台管理
- 新增:类似用户管理模块,点击新增按钮,弹出包含对话框的表单,填写数据后Jquery通过post的方式发起请求。
- 删除:类似用户管理模块,点击对应行的删除按钮,拿着id请求后端。
6.留言模块(前后台接口都在一块)
前台显示
-
tabs页面显示:留言最多为10条,按照时间顺序查得到,并且是已经回复的。
-
新增留言:点击留言按钮,跳转到填写信息的页面,点击页面的按钮,弹出表单,填写留言之后发起请求。
-
留言页面显示:显示全部的留言,使用layui的数据表格进行渲染。
// 前面html <table id="answered" lay-filter="answered_message"></table> // 自动渲染js <script> layui.use('table', function(){ var table = layui.table; //第一个实例,已经回复的留言 table.render({ elem: '#answered' ,height: 600 ,url: '/get-all-message' //数据接口 ,page: true //开启分页 ,cols: [ [ {field: 'id', title: 'ID', width:80, sort: true, fixed: 'left'} ,{field: 'problem', title: '留言问题', width:180} ,{field: 'answer', title: '管理员回答', width:180, sort: true} ,{field: 'examType', title: '考试类型', width:80} ,{field: 'examScore', title: '考试分数', width: 80} ,{field: 'studentLocation', title: '城市', width: 80, sort: true} ,{field: 'highSchool', title: '毕业高中', width: 80, sort: true} ,{field: 'questTime', title: '留言时间', width: 80} ] ] }); }); </script>
出现问题
-
数据表格渲染时报错:
org.thymeleaf.exceptions.TemplateProcessingException: Could not parse as expression: "
,原因是cols后的[[ ]]
变为被当成了thymeleaf的内联表达式,解决办法就是加上个换行。 -
行索引问题:数据库的id可能不连续,需要自定义模板显示
cols: [[ {field:'id', title: '文章标题', width: 200, templet: function(d) { return d.LAY_INDEX; }} //这里的templet值是模板元素的选择器 , ]] });
后台模块
-
显示:layui数据表格渲染,调用和前台一样的接口。
-
删除:layui数据表格的toolBar显示删除 按钮,点击可以拿到当前行对象,然后拿着id发起ajax请求。
-
回复:layui的toolbar点击相应按钮能获取到当前行对象,首先弹出表单进行数据回显,填写完回复后,发起请求更新回复。
// 2. 监听行操作 table.on('tool(messages)', function(obj){ //注:tool 是工具条事件名,test 是 table 原始容器的属性 lay-filter="对应的值" var data = obj.data; //获得当前行数据 var layEvent = obj.event; //获得 lay-event 对应的值(也可以是表头的 event 参数对应的值) var tr = obj.tr; //获得当前行 tr 的 DOM 对象(如果有的话) if(layEvent === 'detail'){ //查看 //do somehing } else if(layEvent === 'del'){ //删除 //console.log(obj) layer.confirm('确认要删除么', function(index){ obj.del(); //删除对应行(tr)的DOM结构,并更新缓存 layer.close(index); //向服务端发送删除指令 var id = obj.data.id $.ajax({ url: "/delete-message", data: {id: id}, type: "get", dataType: "json", success: function(data) { // data = jQuery.parseJSON(data); //dataType指明了返回数据为json类型,故不需要再反序列化 layer.alert(data.message) } }); }); } else if(layEvent === 'edit'){ //编辑 //do something //console.log(obj) //页面层,弹出表单 layer.open({ type: 1 //Page层类型 ,skin: 'layui-layer-molv' ,area: ['1000px', '600px'] ,title: ['回复留言','font-size:18px'] ,btn: ['确定', '取消'] ,shadeClose: true ,shade: 0 //遮罩透明度 ,maxmin: true //允许全屏最小化 ,content:$("#form_div") ,success: function(){ // 数据回显 $('#problem').val(obj.data.problem); // $('#content').val(""); $('#phone').val(obj.data.phone); $('#studentLocation').val(obj.data.studentLocation); $('#examType').val(obj.data.examType); $('#examScore').val(obj.data.examScore); $('#highSchool').val(obj.data.highSchool); } ,yes:function(){ //layer.msg("点击了确定!") if($('#answer').val() == 0){ layer.msg("回复不能为空!") } else{ $.post({ url: "/update-message", dataType: 'json', data: { id: obj.data.id, answer: $('#answer').val() }, success: function (data) { layer.open({ content: data.message, yes: function(index, layero){ //do something location.reload() layer.close(index); //如果设定了yes回调,需进行手工关闭 } }); }, error: function (error) { layer.msg(error); } }); } } }); //同步更新缓存对应的值 obj.update({ }); } });
三、项目源码
github地址:https://github.com/GitHubSi/enrollment-infomation_springboot-layui-qadmin-shiro