crm项目中遇到的错误与解决,踩坑记录

320 阅读4分钟

bug

thymeleaf 共享域对象

如果需要将对象的数据显示到前台页面我们可以通过使用thymeleaf实现这一点,Thymeleaf是一个动态渲染页面用的,他简单易懂,不像jsp技术那样需要将html页面改成jsp文件,thymeleaf可以直接在html页面上进行操作。通常我们使用到的是session作用域,说回我的项目crm,在查看市场活动的详情的阶段:

首先说明需求: 点击市场活动的名称时页面跳转到对应市场活动的详情页,在详情页就是该市场活动所对应的各个数据详细展示,如何实现,显然页面跳转到详情页就是同步请求而非异步请求,在请求的地址栏中带上对应市场活动的id,然后在后台控制层接收参数,并通过这个id去到数据库中查询对应的市场活动,得到市场活动的实体,并将该实体类保存在session作用域,然后返回页面的名,通过视图解析器解析到该页面,然后在页面上通过thymeleaf的形式获取会话域中的市场活动实体中的各个数据并显示。


    @RequestMapping("/testSession")
    public String testSessionByServlet(String id,HttpSession session) {
        Activity activity = activityService.selectActivityById(id); //从数据库中查询id对应的市场活动
        session.setAttribute("activity",activity);
        return "detail";//通过试图解析器解析到detail.html页面
    }

熟悉下面的这种thymeleaf的写法,否则每次写的时候多需要查,很费时,这次的crm的第一的开发就遇到的thymeleaf的语法写错而出现报错的情况,th:text="${activity.owner}" 这种写法它


<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
  <head>
      <meta charset="UTF-8">
      <title>Title</title>
  </head>
  <body>
      <p th:text="${session.activity.owner}">市场活动的所有者</p>
  </body>
</html>

其他的域对象相关的也需要了解下,下面是各个方式的代码简单熟悉下:

@Controller
public class ScopeController {
    /**
     * 使用servletAPI 向request域对象共享数据
     *
     * @return
     */
    @RequestMapping("/testRequestByServletAPI")
    public String testScope(HttpServletRequest request) {
        request.setAttribute("testRequestScope", "通过servletAPI实现request域对象的数据共享");
        return "nice";
    }
    /**
     * 1,向request域对象共享数据
     * 2,设置视图名称
     *
     * @return ModelAndView
     */
    @RequestMapping("/testModelAndView")//建议使用这种方式
    public ModelAndView testModelAndView() {
        ModelAndView modelAndView = new ModelAndView();
        //首先处理模型数据,即向请求域request共享数据
        modelAndView.addObject("testRequestScope", "ModelAndVeiw的使用");
        //设置试图名称
        modelAndView.setViewName("nice");
        System.out.println(modelAndView.getClass().getName());
        return modelAndView;
    }

    /*
        下面的三种方式利用的都是同一个对象实例化的  --> org.springframework.validation.support.BindingAwareModelMap
     */
    @RequestMapping("/testModel")
    public String testModel(Model model) {
        model.addAttribute("testRequestScope", "使用model共享request域中的数据");
        System.out.println(model.getClass().getName());//
        return "nice";
    }
    
    @RequestMapping("/testMap")
    public String testMap(Map<String, Object> map) {
        map.put("testRequestScope", "以map集合的方式");
        System.out.println(map.getClass().getName());
        return "nice";
    }
    
    @RequestMapping("/testModelMap")
    public String testModelMap(ModelMap modelMap) {
        modelMap.addAttribute("testRequestScope", "使用modelmap共享request请求域中的数据");
        System.out.println(modelMap.getClass().getName());
        return "nice";
    }

    //-----------------------------------------------------------------------------------------------------------------------------
    //向session域共享数据
    @RequestMapping("/testSession")
    public String testSessionByServlet(HttpSession session) {
        User user = new User(12,"king","23");
        session.setAttribute("user",user);
        session.setAttribute("sessionTest","在会话域的使用中建议使用原生的Servlet的方式(简单)");
        return "nice";
    }
    //application 域共享数据
    @RequestMapping("/testApplication")
    public String testApplication(HttpSession session){
        ServletContext application = session.getServletContext();
        application.setAttribute("testApplication","application域");
        return "nice";
    }
}

在前台页面点击链接实现跳转时关于地址栏拼写时的问题

因为需要点击的链接是通过字符串拼接的形式添加到前端HTML页面中如果接触到该项目就会很容易明白这点!下面是简单的代码:

$.each(data.activitys, function (index, obj) {
   //如何将obj 的属性id 添加到value上??
   // alert(obj.id);
   htmlStr += "<tr className="active">";
   //熟练使用Thymeleaf表达式真的很重要,一些jQuery相关的表达式也要学会,这里的id值得设置就很,没有思路!!!
   //将obj中的id如何赋值给input 表单中的value属性,😂
   htmlStr += "<td><input type="checkbox" value="" + obj.id + ""/></td>";/*给单选框绑定id,以便后续的修改删除操作*/
   //下面的onclick="" 中拼上请求地址和请求地址需要带上的参数,这里是id,(拼上变量) onclick="window.location.href='http://localhost:8080/crm01/workbench/activity/detail.do?id="+obj.id+"'" 这种拼法我还是没有理解,看了教程似懂非懂,但是可以确定的是你拼接是否正确可以在前台查看,调试模式
   htmlStr += "<td><a style="text-decoration: none; cursor: pointer;" id='detailD' onclick="window.location.href='http://localhost:8080/crm01/workbench/activity/detail.do?id="+obj.id+"'">" + obj.name + "</a></td>";/*活动名*/
   htmlStr += "<td>" + obj.owner + "</td>";
   htmlStr += "<td>" + obj.startDate + "</td>";
   htmlStr += "<td>" + obj.endDate + "</td>";
   htmlStr += "</tr>";
});

上面的写法就是将activitys里的记录遍历拼接到一个字符串htmlstr上,(这是一种解决方式或者说是技巧熟悉这种处理方式)接下来的操作就是将得到的字符串加到指定的位置:

$("#fuck").html(htmlStr);

就是将得到的字符串添加到id为fuck的元素中,(fuck😂这不是重点哈哈!)元素.html(htmlStr);htmlStr为需要添加的html文件的字符串。添加的方式还有几种 innerHtml innerText append ... 这些也需要了解下!

上面的说法有点问题,但是为了纠错的效果更佳所以还是留下! 其实$("#id").html(); 是jQuery中的一个方法,他可以设值也可以取值当他有参数时就设值没有参数时就从指定的元素取值,更具体的可以参考:菜鸟教程里的jQuery设值相关

所以$("#fuck").html(htmlStr);就是往指定元素(id属性为fuck的元素)设值,默认会覆盖掉,如果原来的内容要用上可以使用回调函数。

<tbody id="fuck">
   <tr class="active">
      <td><input type="checkbox" /></td>
      <td><a style="text-decoration: none; cursor: pointer;" onclick="window.location.href='detail.html';">发传单</a></td>
                       <td>zhangsan</td>
      <td>2020-10-10</td>
      <td>2020-10-20</td>
   </tr>
   <tr class="active">
        <td><input type="checkbox" /></td>
        <td><a style="text-decoration: none; cursor: pointer;" onclick="window.location.href='detail.html';">发传单</a></td>
        <td>zhangsan</td>
        <td>2020-10-10</td>
        <td>2020-10-20</td>
   </tr>
</tbody>

下面是菜鸟教程中的案例,说的就是使用回调函数在原来的基础上添加的情况!

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js">
</script>
<script>
$(document).ready(function(){
  $("#btn1").click(function(){
    $("#test1").text(function(i,origText){
      return "旧文本: " + origText + " 新文本: Hello world! (index: " + i + ")"; 
    });
  });

  $("#btn2").click(function(){
    $("#test2").html(function(i,origText){
      return "旧 html: " + origText + " 新 html: Hello <b>world!</b> (index: " + i + ")"; 
    });
  });

});
</script>
</head>

<body>
<p id="test1">这是一个有 <b>粗体</b> 字的段落。</p>
<p id="test2">这是另外一个有 <b>粗体</b> 字的段落。</p>
<button id="btn1">显示 新/旧 文本</button>
<button id="btn2">显示 新/旧 HTML</button>
</body>

靠!!! 说了这么多回到重点字符串拼接!! 也就是下面这段代码的onclick属性中字符串和变量的拼接!

htmlStr += "<td><a style="text-decoration: none; cursor: pointer;" id='detailD' onclick="window.location.href='http://localhost:8080/crm01/workbench/activity/detail.do?id="+obj.id+"'">" + obj.name + "</a></td>";/*活动名*/

pjbl.jpg

但是问题是:如何在字符串中拼接上变量?我想过通过给元素设置id 然后通过jQuery操作实现单击跳转并拼上变量,但是这里的变量是在遍历过程中的(可以看上面出现的代码,不难发现这点)所以只能在遍历过程中就将属性设定好! onclick=

onclick=\"window.location.href='http://localhost:8080/crm01/workbench/activity/detail.do?id="+obj.id+"'\"

这里重点是转义符的使用! 如何使用我还要去学!

  • ==补上==
  • ==补上==

因为onclick属性填写时因为拼接的错误导致链接点击没有反应,调了好久以为是别的部分的问题,比如jQuery虽然现在想想,好傻!还有一直不确定onclick属性是否写对,(可以直接在前台页面中调试得到!)

<td><a style="text-decoration: none; cursor: pointer;" id='detailD' onclick="window.location.href='http://localhost:8080/crm01/workbench/activity/detail.do?id="+obj.id+"'">" + obj.name + "</a></td>

pj2.jpg 结果为: onclick="window.location.href='http://localhost:8080/crm01/workbench/activity/detail.do?id=a87bda71633648899732637b2056a0aa'"

还有一种情况是这样的(错误的情况):

onclick=window.location.href='http://localhost:8080/crm01/workbench/activity/detail.do?id=a87bda71633648899732637b2056a0aa' 他会将window.location.href当成变量处理,这是不合理里的,他是js语法!这个会导致点击链接无响应!

如果一些写好的js页面的动态的效果出不来可能是因为页面中的某个部分出错了可能是语法的错误,导致往下执行不了

添加备注成功后刷新列表的功能的实现

因为刷新列表只是局部的刷新,并不用刷新整个页面,所以我们使用ajax技术,添加成功后刷新列表的功能写在success方法中。 将新添加的备注添加到备注列表下面,因为我们已经熟悉了jQuery的添加的几种方法 就是先将需要显示的部分拼成一个字符串,然后追加 append(),after(), before(),他们的区别就不在赘述了! 本次项目中使用before()最合适,拼接字符串往字符串中填写数据时出现了问题,发现th:text="${}"等语法失效!!!!

$("#savaRemark").click(function(){
   let remark = $("#remark").val();
   let activityId = $("#activityId").val();
   if(remark==''||remark==null){
      alert("备注内容为空!");
      return;
   }
   $.ajax({
      url:"http://localhost:8080/crm01/workbench/activity/saveRemark.do",
      data:{
         noteContent:remark,
         activityId:activityId
      },
      type:'post',
      dataType:'json',
      success:function (data){
         if(data.code=="1"){
            $("#remark").val("");//清空输入框
            //刷新备注列表
            // window.location.href="http://localhost:8080/crm01/workbench/activity/detail.do?id="+$("#activityId").val();
            htmlStr="\t<div class="remarkDiv" style="height: 60px;">\n" +
                  "\t\t\t<img title=""+data.retData.createBy+"" src="/image/user-thumbnail.png" style="width: 30px; height:30px;">\n" +
                  "\t\t\t<div style="position: relative; top: -40px; left: 40px;" >\n" +
                  "\t\t\t\t<h5>"+data.retData.noteContent+"</h5>\n" +
                  "\t\t\t\t<!--thymeleaf的行内写法-->\n" +
                  "\t\t\t\t<font color="gray">市场活动</font> <font color="gray">-</font> <b>[[${session.activity.name}]]</b><small style="color: gray;">[[${session.activity.createTime}]] 由 [[${session.activity.createBy}]]</small>\n" +
                  "\t\t\t\t<div style="height: 33px;width: 100%;"></div>\n" +
                  "\t\t\t\t<div style="position: relative; left: 500px; top: -30px; height: 30px; width: 100px; display: none;">\n" +
                  "\t\t\t\t\t<!--给标签绑定数据的方式,这里因为不是表单组件,所以不建议使用value属性绑定数据,我们这里使用了自定义的属性的方式,th:attr="remarkId=${remark.id}" thymeleaf的这个写法nice!-->\n" +
                  "\t\t\t\t\t<a class="myHref" href="javascript:void(0);" th:attr="remarkId=#{data.retData.id}"><span class="glyphicon glyphicon-edit" style="font-size: 20px; color: #E6E6E6;"></span></a>\n" +
                  "\t\t\t\t\t&nbsp;&nbsp;&nbsp;&nbsp;\n" +
                  "\t\t\t\t\t<a class="myHref" href="javascript:void(0);" th:attr="remarkId=#{data.retData.id}"><span class="glyphicon glyphicon-remove" style="font-size: 20px; color: #E6E6E6;"></span></a>\n" +
                  "\t\t\t\t</div>\n" +
                  "\t\t\t</div>\n" +
                  "\t\t</div>";
            $("#remarkDiv").before(htmlStr);
            console.log(htmlStr);

         }else {
         alert(data.message);
         }
      }
   });
});

值得思考的是在上面可以使用thymeleaf的行内写法可以正常显示,但是一些需要用到th:text="" th:attr="" 等语法失效??? 如何解决? 难道只能用jsp的方式?😅 我们已经过渡到了thymeleaf这里就用它解决吧!!

当动态添加到页面中时,原来的事件失效的问题

下图为正常显示,也就是还未添加时!

dt1.jpg

当我们将新添的备注动态添加到页面中时,刚新添的部分的事件失效!!

dt2.jpg

但是之前添加过的部分效果可以正常显示!

dt3.jpg

也就是动态添加的部分失效了,如何改进?

事件绑定

事件绑定的有两种方式:

  • 拿到要绑定事件的jQuery对象,调事件函数。
  • 通过该元素已经存在的父标签,然后通过on。

第一种方式:

$(".remarkDiv").mouseover(function(){
   $(this).children("div").children("div").show();
});

$(".remarkDiv").mouseout(function(){
   $(this).children("div").children("div").hide();
});

$(".myHref").mouseover(function(){
   $(this).children("span").css("color","red");
});

$(".myHref").mouseout(function(){
   $(this).children("span").css("color","#E6E6E6");
});

通过固有的父元素调on

选取元素(同children()),给元素设值:

在修改备注的功能的实现时:点击修改按钮

image.png 弹出模态框 image.png 修改image.png 修改成功后(在数据库中成功修改)关闭模态窗口,将相关的数据同步到前端页面

image.png

上面红色部分是为了测试时更好滴判断是否选中元素成功? 为什么在选取元素是否成功时还需要这么麻烦,因为我对元素选取时一些知识点的模糊,比如选取指定元素的子元素时的children()的用法

$("#div_"+id).children("h5").text(noteContent);

将修改的内容更新到页面中时通过id选取到指定的元素,然后通过jQuery的方法text(),将文本部分设值到页面中。 我使用了 $("#div_"+id).children("h5").innerIext(noteContent);结果他是不能被解析的,因为jQuery中没有这个方法,他是原生js的函数。 反正注意吧!

下拉拦的操作

将从数据库中查询到的数据显示到模态框中,以便修改。所有者的部分首先要将数据库中查询出来的所有者在文本框显示,如下图,可以选中下拉栏以选中。还要获取下拉栏选中的id,传到后台将做的修改更新

image.png 可以选择下拉栏以修改所有者,下拉栏的选项为当前系统的用户名。

image.png

<select class="form-control" id="edit-marketActivityOwner">
   <option th:each="user:${users}" th:text="${user.name}" th:value="${user.name}" th:attr="id1=${user.id}" ></option>
</select>

总之学会熟练使用thymeleaf以及前端各个元素的操作,才能更高效地开发,不用把时间浪费在这些样式的问题和数据填充的问题。

下拉栏的操作待解决!!!!