软件工程期末大作业_xxx高校招生信息网(SpringBoot + Layui + shiro + Qadmin )

软件工程-高校招生信息网开发过程问题小记
  • 开发环境
  • 开发过程
    • 登录跳转模块
    • 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

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

scl、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值