'); } '); } CRM项目SSM框架构建(三) | Journey to paradise

CRM项目SSM框架构建(三)


完成业务:

  • 市场活动主页的显示
  • 创建市场活动
  • 查询市场活动
  • 修改市场活动
  • 删除市场活动
  • 批量导出市场活动
  • 批量导入市场活动

市场活动主页的显示

Mapper层

UserMapper.java

List<User> selectAllUsers();

UserMapper.xml

在Mybatis逆向工程的基础上加方法(后面的mapper方法都是在Mybatis逆向工程的基础上加):

<select id="selectAllUsers" resultMap="BaseResultMap">
  select <include refid="Base_Column_List"/>
  from tbl_user
  where lock_state='1'
</select>

Service层

UserService.java

    List<User> queryAllUsers();

UserServiceImpl.java

@Override
public List<User> queryAllUsers() &#123;
    return userMapper.selectAllUsers();
&#125;

Controller层

ActivityController.java

@Autowired
private UserService userService;

@Autowired
private ActivityService activityService;

@Autowired
private ActivityRemarkService activityRemarkService;

@RequestMapping("/workbench/activity/index.do")
public String index(HttpServletRequest request)&#123;
    //查询所有用户
    List<User> users = userService.queryAllUsers();
    //把数据保存在request中
    request.setAttribute("userList",users);
    //请求转发到市场活动主页面
    return "workbench/activity/index";
&#125;

前台页面

workbench/index.jsp


......

  • 市场活动
  • .....

    创建市场活动并保存

    需求分析

    1. 用户在市场活动主页面,点击”创建”按钮,弹出创建市场活动的模态窗口;

    2. 用户在创建市场活动的模态窗口填写表单,点击”保存”按钮,完成创建市场活动的功能.

      • 所有者是动态的(//在现实市场活动主页面时,就从数据库中查询出所有用户并且显示在创建的模态窗口中)

      • 所有者和名称不能为空

        • 如果开始日期和结束日期都不为空,则结束日期不能比开始日期小
      • 成本只能为非负整数

      • 创建成功之后,关闭模态窗口,刷新市场活动列,显示第一页数据,保持每页显示条数不变

        • 创建失败,提示信息创建失败,模态窗口不关闭,市场活动列表也不刷新
    最终效果

    时序&流程

    前台页面

    workbench/activity/index.jsp

    1. 给“创建”按钮一个id选择器createActivityBtn

      
      
    2. 在入口函数给“createActivityBtn”按钮添加点击事件

      //给"创建"按钮添加单击事件
      $("#createActivityBtn").click(function () &#123;
          //初始化工作
          //重置表单
          $("#createActivityForm").get(0).reset();
      
          //弹出创建市场活动的模态窗口
          $("#createActivityModal").modal("show");
      &#125;);
      

    效果:

    Mapper层

    UserMapper.java

    创建查询用户方法

    /**
    * 查询所有用户
    * @return
    */
    List<User> selectAllUser();
    

    UserMapper.xml

    编写查询语句,查询tbl_user表中用户状态正常的所有用户的值

    <select id="selectAllUser" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List" />
        from tbl_user
        where lock_state = '1'
    </select>
    

    ActivityMapper.java

    修改插入方法

    /**
    * 保存创建的市场活动
    */
    int insertActivity(Activity activity);
    

    ActivityMapper.xml

    修改插入sql语句

    <insert id="insertActivity" parameterType="com.yyp.crm.workbench.domain.Activity" >
        insert into tbl_activity (id, owner, name, start_date,
        end_date, cost, description,
        create_time, create_by)
        values (#&#123;id,jdbcType=CHAR&#125;, #&#123;owner,jdbcType=CHAR&#125;, #&#123;name,jdbcType=VARCHAR&#125;, #&#123;startDate,jdbcType=CHAR&#125;,
        #&#123;endDate,jdbcType=CHAR&#125;, #&#123;cost,jdbcType=VARCHAR&#125;, #&#123;description,jdbcType=VARCHAR&#125;,
        #&#123;createTime,jdbcType=CHAR&#125;, #&#123;createBy,jdbcType=VARCHAR&#125;)
    </insert>
    

    Service层

    UserService.java

    创建用户数据查询服务

    List<User> queryAllUsers();
    

    UserServiceImpl.java

    调用userMapper中的selectAllUser方法实现UserService接口的queryAllUsers服务

    @Override
    public List<User> queryAllUsers() &#123;
        return userMapper.selectAllUser();
    &#125;
    

    ActivityService.java

    创建插入数据服务

    int saveCreateActivity(Activity activity);
    

    ActivityServiceImpl.java

    调用activityMapper中的insertActivity方法实现ActivityService接口的saveCreateActivity服务

    @Autowired
    ActivityMapper activityMapper;
    
    @Override
    public int saveCreateActivity(Activity activity) &#123;
        return activityMapper.insertActivity(activity);
    &#125;
    

    Controller层

    ActivityController.java

    @Autowired
    private UserService userService;
    
    @RequestMapping("/workbench/activity/index.do")
    public String index(HttpServletRequest request)&#123;
        //调用service层方法,查询所有的用户
        List<User> userList = userService.queryAllUsers();
        //把数据保存到request中
        request.setAttribute("userList",userList);
        //请求转发到市场活动的主页面
        return "workbench/activity/index";
    &#125;
    
    @RequestMapping("/workbench/activity/saveCreateActivity.do")
    public @ResponseBody Object saveCreateActivity(Activity activity, HttpSession session)&#123;
        User user = (User) session.getAttribute(Contants.SESSION_USER);
        //封装参数
        activity.setId(UUIDUtils.getUUID());
        activity.setCreateTime(DateUtils.formateDateTime(new Date()));
        activity.setCreateBy(user.getId());
    
        ReturnObject returnObject=new ReturnObject();
        try &#123;
            //调用service层方法,保存创建的市场活动
            int ret = activityService.saveCreateActivity(activity);
    
            //根据查询结果设置返回值
            if(ret>0)&#123;
                returnObject.setCode(Contants.RETURN_OBJECT_CODE_SUCCESS);
            &#125;else&#123;
                returnObject.setCode(Contants.RETURN_OBJECT_CODE_FAIL);
                returnObject.setMessage("系统忙,请稍后重试....");
            &#125;
        &#125;catch (Exception e)&#123;
            e.printStackTrace();
    
            returnObject.setCode(Contants.RETURN_OBJECT_CODE_FAIL);
            returnObject.setMessage("系统忙,请稍后重试....");
        &#125;
        //返回查询结果
        return returnObject;
    &#125;
    

    前台页面

    workbench/activity/index.jsp

    将创建市场活动的模态窗口的所有者列表改为动态

    
        
    
    

    给“保存”按钮一个id选择器saveCreateActivityBtn

    
    

    在入口函数给“saveCreateActivityBtn”按钮添加点击事件;提交表单前验证表单数据是否满足需求,根据需求对不同状态做出响应

    //给"保存"按钮添加单击事件
    $("#saveCreateActivityBtn").click(function () &#123;
        //收集参数
        var owner=$("#create-marketActivityOwner").val();
        var name=$.trim($("#create-marketActivityName").val());
        var startDate=$("#create-startDate").val();
        var endDate=$("#create-endDate").val();
        var cost=$.trim($("#create-cost").val());
        var description=$.trim($("#create-description").val());
        //表单验证
        //所有者和名称不能为空
        if(owner=="")&#123;
            alert("所有者不能为空");
            return;
        &#125;
        if(name=="")&#123;
            alert("名称不能为空");
            return;
        &#125;
        //如果开始日期和结束日期都不为空,则结束日期不能比开始日期小
        if(startDate!=""&&endDate!="")&#123;
            //使用字符串的大小代替日期的大小
            if(endDate<startDate)&#123;
                alert("结束日期不能比开始日期小");
                return;
            &#125;
        &#125;
        //成本只能为非负整数
        var regExp=/^(([1-9]\d*)|0)$/;
        if(!regExp.test(cost))&#123;
            alert("成本只能为非负整数");
            return;
        &#125;
        //发送请求
        $.ajax(&#123;
            url:'workbench/activity/saveCreateActivity.do',
            data:&#123;
                owner:owner,
                name:name,
                startDate:startDate,
                endDate:endDate,
                cost:cost,
                description:description
            &#125;,
            type:'post',
            dataType:'json',
            //创建成功之后,关闭模态窗口,刷新市场活动列,显示第一页数据,保持每页显示条数不变
            success:function (data) &#123;
                if(data.code=="1")&#123;
                    //关闭模态窗口
                    $("#createActivityModal").modal("hide");
                    //刷新市场活动列,显示第一页数据,保持每页显示条数不变(保留)
                    queryActivityByConditionForPage(1,$("#demo_pag1").bs_pagination('getOption', 'rowsPerPage'));
                &#125;else&#123;
                    //创建失败,提示信息创建失败,模态窗口不关闭,市场活动列表也不刷新
                    //提示信息
                    alert(data.message);
                    //模态窗口不关闭
                    $("#createActivityModal").modal("show");//可以不写。
                &#125;
            &#125;
        &#125;);
    &#125;);
    
    给日期添加日历插件datetimepicker

    利用前端日历插件bootstrap-datetimepicker将日期输入改为下拉选择

    官方文档:https://www.bootcss.com/p/bootstrap-datetimepicker/

    前端引入日历插件

    1. 引入jquery、bootscrap框架开发包:.js,.css

    2. 下载BOOTSTRAP_DATETIMEPICKER插件,拷贝到项目webapp目录下

      <!--JQUERY-->
          <script type="text/javascript" src="jquery/jquery-1.11.1-min.js"></script>
          <!--BOOTSTRAP框架-->
          <link rel="stylesheet" type="text/css" href="jquery/bootstrap_3.3.0/css/bootstrap.min.css">
          <script type="text/javascript" src="jquery/bootstrap_3.3.0/js/bootstrap.min.js"></script>
          <!--BOOTSTRAP_DATETIMEPICKER插件-->
          <link rel="stylesheet" type="text/css" href="jquery/bootstrap-datetimepicker-master/css/bootstrap-datetimepicker.min.css">
          <script type="text/javascript" src="jquery/bootstrap-datetimepicker-master/js/bootstrap-datetimepicker.min.js"></script>
          <script type="text/javascript" src="jquery/bootstrap-datetimepicker-master/locale/bootstrap-datetimepicker.zh-CN.js"></script>
      
    3. 创建容器

    1. 入口函数对容器调用工具函数:

      $(".mydate").datetimepicker(&#123;
          language:	'zh-CN',//语言
          format:		'yyyy-mm-dd',//日期的格式
          minView: 	'month',//可以选择的最小视图
          initialDate:new Date(),//初始化显示的日期
          autoclose:	true,//设置选择完日期或者时间之后,日否自动关闭日历
          todayBtn:	true,//设置是否显示”今天"按钮,默认是false
          clearBtn:	true//设置是否显示"清空"按钮,默认是false
      &#125;);
      

    查询市场活动

    需求分析

    1. 当市场活动主页面加载完成之后,显示所有数据的第一页;

    2. 用户在市场活动主页面填写查询条件,点击”查询”按钮,显示所有符合条件的数据的第一页,保持每页显示条数不变

    3. 实现翻页功能.

      1. 在市场活动主页面,显示市场活动列表和记录的总条数
      2. 默认每页显示条数:10
    最终效果

    时序&流程

    Mapper层

    ActivityMapper.java

    数据库中需要查询到某页需要展示的数据和符合该条件市场活动的总条数

    /**
         * 根据条件分页查询市场活动的列表
         * @param map
         * @return
         */
    List<Activity> selectActivityByConditionForPage(Map<String,Object> map);
    
    /**
         * 根据条件查询市场活动的总条数
         * @param map
         * @return
         */
    int selectCountOfActivityByCondition(Map<String,Object> map);
    

    ActivityMapper.xml

    <select id="selectActivityByConditionForPage" parameterType="map" resultMap="BaseResultMap">
        select a.id,u1.name as owner,a.name,a.start_date,a.end_date,a.cost,a.description,a.create_time,
        u2.name as create_by,a.edit_time,u3.name as edit_by
        from tbl_activity a
        join tbl_user u1 on a.owner=u1.id
        join tbl_user u2 on a.create_by=u2.id
        left join tbl_user u3 on a.edit_by=u3.id
        <where>
            <if test="name!=null and name!=''">
                and a.name like '%' #&#123;name&#125; '%'
            </if>
            <if test="owner!=null and owner!=''">
                and u1.name like '%' #&#123;owner&#125; '%'
            </if>
            <if test="startDate!=null and startDate!=''">
                and a.start_date&gt;=#&#123;startDate&#125;
            </if>
            <if test="endDate!=null and endDate!=''">
                and a.end_date&lt;=#&#123;endDate&#125;
            </if>
        </where>
        order by a.create_time desc
        limit #&#123;beginNo&#125;,#&#123;pageSize&#125;
    </select>
    
    <select id="selectCountOfActivityByCondition" parameterType="map" resultType="int">
        select count(*)
        from tbl_activity a
        join tbl_user u1 on a.owner=u1.id
        join tbl_user u2 on a.create_by=u2.id
        left join tbl_user u3 on a.edit_by=u3.id
        <where>
            <if test="name!=null and name!=''">
                and a.name like '%' #&#123;name&#125; '%'
            </if>
            <if test="owner!=null and owner!=''">
                and u1.name like '%' #&#123;owner&#125; '%'
            </if>
            <if test="startDate!=null and startDate!=''">
                and a.start_date&gt;=#&#123;startDate&#125;
            </if>
            <if test="endDate!=null and endDate!=''">
                and a.end_date&lt;=#&#123;endDate&#125;
            </if>
        </where>
    </select>
    

    Service层

    ActivityService.java

    创建两个业务,分别是查询活动数据和总数量

    List<Activity> queryActivityByConditionForPage(Map<String,Object> map);
    int queryCountOfActivityByCondition(Map<String,Object> map);
    

    ActivityServiceImpl.java

    ​ 调用activityMapper中的selectActivityByConditionForPage和selectCountOfActivityByCondition方法实现ActivityService中的queryActivityByConditionForPage和queryCountOfActivityByCondition业务

    @Override
    public List<Activity> queryActivityByConditionForPage(Map<String, Object> map) &#123;
        return activityMapper.selectActivityByConditionForPage(map);
    &#125;
    
    @Override
    public int queryCountOfActivityByCondition(Map<String, Object> map) &#123;
        return activityMapper.selectCountOfActivityByCondition(map);
    &#125;
    

    Controller层

    ActivityController.java

    @RequestMapping("/workbench/activity/queryActivityByConditionForPage.do")
    public @ResponseBody Object queryActivityByConditionForPage(String name,String owner,String startDate,String endDate,int pageNo,int pageSize)&#123;
        //封装参数
        Map<String,Object> map=new HashMap<>();
        map.put("name",name);
        map.put("owner",owner);
        map.put("startDate",startDate);
        map.put("endDate",endDate);
        map.put("beginNo",(pageNo-1)*pageSize);
        map.put("pageSize",pageSize);
    
        //调用service层方法,查询数据
        List<Activity> activityList=activityService.queryActivityByConditionForPage(map);
        int totalRows=activityService.queryCountOfActivityByCondition(map);
    
        //根据查询结果结果,生成响应信息
        Map<String,Object> retMap=new HashMap<>();
        retMap.put("activityList",activityList);
        retMap.put("totalRows",totalRows);
        return retMap;
    &#125;
    

    前台页面

    workbench/activity/index.jsp

    给“查询”按钮加上id选择器

    
    

    入口函数中调用queryActivityByConditionForPage函数

    //当市场活动主页面加载完成,查询所有数据的第一页以及所有数据的总条数,默认每页显示10条
    queryActivityByConditionForPage(1,10);
    
    //给"查询"按钮添加单击事件
    $("#queryActivityBtn").click(function () &#123;
        //查询所有符合条件数据的第一页以及所有符合条件数据的总条数;
        queryActivityByConditionForPage(1,$("#demo_pag1").bs_pagination('getOption', 'rowsPerPage'));
    &#125;);
    
    添加分页插件bs_pagination

    插件描述页面:https://www.jq22.com/jquery-info1025

    在入口写分页显示函数,传入参数翻到第几页pageNo,一页显示多少条内容pageSize,调用分页插件工具函数,显示翻页信息。

    function queryActivityByConditionForPage(pageNo,pageSize)&#123;
        //收集参数
        var name=$("#query-name").val();
        var owner=$("#query-owner").val();
        var startDate=$("#query-startDate").val();
        var endDate=$("#query-endDate").val();
        // var pageNo=1;
        // var pageSize=10;
        //发送请求
        $.ajax(&#123;
            url:'workbench/activity/queryActivityByConditionForPage.do',
            data:&#123;
                name:name,
                owner:owner,
                startDate:startDate,
                endDate:endDate,
                pageNo:pageNo,
                pageSize:pageSize
            &#125;,
            type:'post',
            dataType:'json',
            success:function (data) &#123;
                //显示总条数
                //$("#totalRowsB").text(data.totalRows);
                //显示市场活动的列表
                //遍历activityList,拼接所有行数据
                var htmlStr="";
                $.each(data.activityList,function (index,obj) &#123;
                    htmlStr+="<tr class=\"active\">";
                    htmlStr+="<td><input type=\"checkbox\" value=\""+obj.id+"\"/></td>";
                    htmlStr+="<td><a style=\"text-decoration: none; cursor: pointer;\" οnclick=\"window.location.href='detail.html';\">"+obj.name+"</a></td>";
                    htmlStr+="<td>"+obj.owner+"</td>";
                    htmlStr+="<td>"+obj.startDate+"</td>";
                    htmlStr+="<td>"+obj.endDate+"</td>";
                    htmlStr+="</tr>";
                &#125;);
                $("#tBody").html(htmlStr);
    
                //计算总页数
                var totalPages=1;
                if(data.totalRows%pageSize==0)&#123;
                    totalPages=data.totalRows/pageSize;
                &#125;else&#123;
                    totalPages=parseInt(data.totalRows/pageSize)+1;
                &#125;
    
                //对容器调用bs_pagination工具函数,显示翻页信息
                $("#demo_pag1").bs_pagination(&#123;
                    currentPage:pageNo,//当前页号,相当于pageNo
    
                    rowsPerPage: pageSize,//每页显示条数,相当于pageSize
                    totalRows:data.totalRows,//总条数
                    totalPages: totalPages,  //总页数,必填参数.
    
                    visiblePageLinks:5,//最多可以显示的卡片数
    
                    showGoToPage:true,//是否显示"跳转到"部分,默认true--显示
                    showRowsPerPage:true,//是否显示"每页显示条数"部分。默认true--显示
                    showRowsInfo:true,//是否显示记录的信息,默认true--显示
    
                    //用户每次切换页号,都自动触发本函数;
                    //每次返回切换页号之后的pageNo和pageSize
                    onChangePage: function(event,pageObj) &#123; // returns page_num and rows_per_page after a link has clicked
                        //js代码
                        //alert(pageObj.currentPage);
                        //alert(pageObj.rowsPerPage);
                        queryActivityByConditionForPage(pageObj.currentPage,pageObj.rowsPerPage);
                    &#125;
                &#125;);
            &#125;
        &#125;);
    &#125;
    

    删除市场活动

    需求分析

    1. 用户在市场活动主页面,选择要删除的市场活动,点击”删除”按钮,弹出确认窗口;

    2. 用户点击”确定”按钮,完成删除市场活动的功能

    • 每次至少删除一条市场活动

    • 可以批量删除市场活动

    • 删除成功之后,刷新市场活动列表,显示第一页数据,保持每页显示条数不变

    • 删除失败,提示信息,列表不刷新

    最终效果:

    时序&流程

    Mapper层

    ActivityMapper.java

    根据市场活动id删除对应数据

    /**
         * 根据ids批量删除市场活动
         * @param ids
         * @return
         */
    int deleteActivityByIds(String[] ids);
    

    ActivityMapper.xml

    删除可能不止一条数据,遍历传入的id列表,删除所有对应的数据

    <delete id="deleteActivityByIds" parameterType="string">
        delete from tbl_activity where id in
        <foreach collection="array" item="id" separator="," open="(" close=")">
            #&#123;id&#125;
        </foreach>
    </delete>
    

    Service层

    ActivityService.java

    创建根据id删除市场活动数据的业务

    int deleteActivityByIds(String[] ids);
    

    ActivityServiceImpl.java

    调用activityMapper中的deleteActivityByIds方法实现deleteActivityByIds中的deleteActivityByIds业务

    @Override
    public int deleteActivityByIds(String[] ids) &#123;
        return activityMapper.deleteActivityByIds(ids);
    &#125;
    

    Controller层

    @RequestMapping("/workbench/activity/deleteActivityIds.do")
    public @ResponseBody Object deleteActivityIds(String[] id)&#123;
        ReturnObject returnObject=new ReturnObject();
        try &#123;
            //调用service层方法,删除市场活动
            int ret = activityService.deleteActivityByIds(id);
            if(ret>0)&#123;
                returnObject.setCode(Contants.RETURN_OBJECT_CODE_SUCCESS);
            &#125;else&#123;
                returnObject.setCode(Contants.RETURN_OBJECT_CODE_FAIL);
                returnObject.setMessage("系统忙,请稍后重试....");
            &#125;
        &#125;catch (Exception e)&#123;
            e.printStackTrace();
            returnObject.setCode(Contants.RETURN_OBJECT_CODE_FAIL);
            returnObject.setMessage("系统忙,请稍后重试....");
        &#125;
        return returnObject;
    &#125;
    

    前台页面

    workbench/activity/index.jsp

    1. 给全选框加上id选择器

      
      名称
      所有者
      开始日期
      结束日期
      
    2. 给”全选”按钮添加单击事件

      //给"全选"按钮添加单击事件
      $("#checkAll").click(function () &#123;
          //如果"全选"按钮是选中状态,则列表中所有checkbox都选中
          $("#tBody input[type='checkbox']").prop("checked",this.checked);
      &#125;);
      
      //取消全选
      $("#tBody").on("click","input[type='checkbox']",function () &#123;
          // alert("debug");
          //如果列表中的所有checkbox都选中,则"全选"按钮也选中
          if($("#tBody input[type='checkbox']").size()==$("#tBody input[type='checkbox']:checked").size())&#123;
              $("#checkAll").prop("checked",true);
          &#125;else&#123;//如果列表中的所有checkbox至少有一个没选中,则"全选"按钮也取消
              $("#checkAll").prop("checked",false);
          &#125;
      &#125;);
      
    3. 给”删除”按钮添加单击事件,完成:获取所有选择活动的id;确保id数量>1;向后台发起删除请求;根据删除情况做出响应

      //给"删除"按钮添加单击事件
      $("#deleteActivityBtn").click(function () &#123;
          //收集参数
          //获取列表中所有被选中的checkbox
          var chekkedIds=$("#tBody input[type='checkbox']:checked");
          if(chekkedIds.size()==0)&#123;
              alert("请选择要删除的市场活动");
              return;
          &#125;
      
          if(window.confirm("确定删除吗?"))&#123;
              var ids="";
              $.each(chekkedIds,function () &#123;//id=xxxx&id=xxx&.....&id=xxx&
                  ids+="id="+this.value+"&";
              &#125;);
              ids=ids.substr(0,ids.length-1);//id=xxxx&id=xxx&.....&id=xxx
      
              //发送请求
              $.ajax(&#123;
                  url:'workbench/activity/deleteActivityIds.do',
                  data:ids,
                  type:'post',
                  dataType:'json',
                  success:function (data) &#123;
                      if(data.code=="1")&#123;
                          //刷新市场活动列表,显示第一页数据,保持每页显示条数不变
                          queryActivityByConditionForPage(1,$("#demo_pag1").bs_pagination('getOption', 'rowsPerPage'));
                      &#125;else&#123;
                          //提示信息
                          alert(data.message);
                      &#125;
                  &#125;
              &#125;);
          &#125;
      &#125;);
      

    修改市场活动

    需求分析

    1. ​ 用户在市场活动主页面,选择要修改的市场活动,点击”修改”按钮,弹出修改市场活动的模态窗口;
    2. ​ 用户在修改市场活动的模态窗口填写表单,点击”更新”按钮,完成修改市场活动的功能.
      • 每次能且只能修改一条市场活动
      • 所有者 动态的
      • 表单验证(同创建)
      • 修改成功之后,关闭模态窗口,刷新市场活动列表,保持页号和每页显示条数都不变
      • 修改失败,提示信息,模态窗口不关闭,列表也不刷新
    最终效果:

    时序&流程

    Mapper层

    ActivityMapper.java

    Activity selectActivityById(String id);
    

    ActivityMapper.xml

    <select id="selectActivityById" parameterType="string" resultMap="BaseResultMap">
        select <include refid="Base_Column_List"></include>
        from tbl_activity
        where id=#&#123;id&#125;
    </select>
    

    Service层

    ActivityService.java

    Activity queryActivityById(String id);
    

    ActivityServiceImpl.java

    @Override
    public Activity queryActivityById(String id) &#123;
        return activityMapper.selectActivityById(id);
    

    Controller层

    ActivityController.java

    @RequestMapping("/workbench/activity/queryActivityById.do")
    @ResponseBody
    public Object queryActivityById(String id)&#123;
        Activity activity = activityService.queryActivityById(id);
        return activity;
    &#125;
    

    前台页面

    workbench/activity/index.jsp

    //给修改按钮添加单击事件
    $("#editActivityBtn").click(function (){
       //收集参数,获取列表中被选中的checkbox
       var checkedIds=$("#tBody input[type='checkbox']:checked");
       if (checkedIds.size()==0){
          alert("请选中要修改的市场活动");
          return;
       }
       if (checkedIds.size()>1){
          alert("每次只能修改一条市场活动");
          return;
       }
       //var id = checkedIds.val();
       //var id = checkedIds.get(0).value;
       var id = checkedIds[0].value;
    
       //发送请求
       $.ajax({
          url:'workbench/activity/queryActivityById.do',
          data:{
             id:id
          },
          type:'post',
          dataType:'json',
          success:function (data){
             //把市场活动信息显示在修改的模态窗口上
             $("#edit-id").val(data.id);
             $("#edit-marketActivityOwner").val(data.owner);
             $("#edit-marketActivityName").val(data.name);
             $("#edit-startTime").val(data.startDate);
             $("#edit-endTime").val(data.endDate);
             $("#edit-cost").val(data.cost);
             $("#edit-description").val(data.description);
    
             //弹出模态窗口
             $("#editActivityModal").modal("show");
          }
    
       });
    });
    

    导入市场活动

    需求分析

    1. 用户在市场活动主页面,点击”导入”按钮,弹出导入市场活动的模态窗口;

      1. 用户在导入市场活动的模态窗口选择要上传的文件,点击”导入”按钮,完成导入市场活动的功能.
      • 只支持.xls
      • 文件大小不超过5MB
      • 导入成功之后,提示成功导入记录条数,关闭模态窗口,刷新市场活动列表,显示第一页数据,保持每页显示条数不变
      • 导入失败,提示信息,模态窗口不关闭,列表也不刷新

    时序&流程

    准备工作

    配置springMVC的文件上传解析器

    applicationContext-mvc.xml

    <!-- 配置文件上传解析器 id:必须是multipartResolver-->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="maxUploadSize" value="#&#123;1024*1024*5&#125;"/>
        <property name="defaultEncoding" value="utf-8"/>
    </bean>
    

    关于excel文件操作的工具类

    HSSFUtils.java

    public static String getCellValueForStr(HSSFCell cell)&#123;
        String ret="";
        if(cell.getCellType()==HSSFCell.CELL_TYPE_STRING)&#123;
            ret=cell.getStringCellValue();
        &#125;else if(cell.getCellType()==HSSFCell.CELL_TYPE_NUMERIC)&#123;
            ret=cell.getNumericCellValue()+"";
        &#125;else if(cell.getCellType()==HSSFCell.CELL_TYPE_BOOLEAN)&#123;
            ret=cell.getBooleanCellValue()+"";
        &#125;else if(cell.getCellType()==HSSFCell.CELL_TYPE_FORMULA)&#123;
            ret=cell.getCellFormula();
        &#125;else&#123;
            ret="";
        &#125;
        return ret;
    &#125;
    

    Mapper层

    ActivityMapper.java

    插入所有市场活动数据

    int insertActivityByList(List<Activity> activityList);
    

    ActivityMapper.xml

    <insert id="insertActivityByList" parameterType="com.yyp.crm.workbench.domain.Activity">
        insert into tbl_activity(id, owner, name, start_date, end_date, cost, description, create_time, create_by)
        values
        <foreach collection="list" item="obj" separator=",">
            (#&#123;obj.id&#125;,#&#123;obj.owner&#125;,#&#123;obj.name&#125;,#&#123;obj.startDate&#125;,#&#123;obj.endDate&#125;,#&#123;obj.cost&#125;,#&#123;obj.description&#125;,#&#123;obj.createTime&#125;,#&#123;obj.createBy&#125;)
        </foreach>
    </insert>
    

    Service层

    ActivityService.java

    保存插入的市场活动列表

    int saveCreateActivityByList(List<Activity> activityList);
    

    ActivityServiceImpl.java

    @Override
    public int saveCreateActivityByList(List<Activity> activityList) &#123;
        return activityMapper.insertActivityByList(activityList);
    &#125;
    

    Controller层

    @RequestMapping("/workbench/activity/importActivity.do")
    public @ResponseBody Object importActivity(MultipartFile activityFile, String userName, HttpSession session)&#123;
        System.out.println("userName="+userName);
        User user=(User) session.getAttribute(Contants.SESSION_USER);
        ReturnObject returnObject=new ReturnObject();
        try &#123;
            InputStream is=activityFile.getInputStream();
            HSSFWorkbook wb=new HSSFWorkbook(is);
            //根据wb获取HSSFSheet对象,封装了一页的所有信息
            HSSFSheet sheet=wb.getSheetAt(0);//页的下标,下标从0开始,依次增加
            //根据sheet获取HSSFRow对象,封装了一行的所有信息
            HSSFRow row=null;
            HSSFCell cell=null;
            Activity activity=null;
            List<Activity> activityList=new ArrayList<>();
            for(int i=1;i<=sheet.getLastRowNum();i++) &#123;//sheet.getLastRowNum():最后一行的下标
                row=sheet.getRow(i);//行的下标,下标从0开始,依次增加
                activity=new Activity();
                activity.setId(UUIDUtils.getUUID());
                activity.setOwner(user.getId());
                activity.setCreateTime(DateUtils.formateDateTime(new Date()));
                activity.setCreateBy(user.getId());
    
                for(int j=0;j<row.getLastCellNum();j++) &#123;//row.getLastCellNum():最后一列的下标+1
                    //根据row获取HSSFCell对象,封装了一列的所有信息
                    cell=row.getCell(j);//列的下标,下标从0开始,依次增加
    
                    //获取列中的数据
                    String cellValue= HSSFUtils.getCellValueForStr(cell);
                    if(j==0)&#123;
                        activity.setName(cellValue);
                    &#125;else if(j==1)&#123;
                        activity.setStartDate(cellValue);
                    &#125;else if(j==2)&#123;
                        activity.setEndDate(cellValue);
                    &#125;else if(j==3)&#123;
                        activity.setCost(cellValue);
                    &#125;else if(j==4)&#123;
                        activity.setDescription(cellValue);
                    &#125;
                &#125;
    
                //每一行中所有列都封装完成之后,把activity保存到list中
                activityList.add(activity);
            &#125;
    
            //调用service层方法,保存市场活动
            int ret=activityService.saveCreateActivityByList(activityList);
    
            returnObject.setCode(Contants.RETURN_OBJECT_CODE_SUCCESS);
            returnObject.setRetData(ret);
        &#125;catch (Exception e)&#123;
            e.printStackTrace();
            returnObject.setCode(Contants.RETURN_OBJECT_CODE_FAIL);
            returnObject.setMessage("系统忙,请稍后重试....");
        &#125;
        return returnObject;
    

    前台页面

    workbench/activity/index.jsp

    给”导入”按钮添加单击事件

    //给"导入"按钮添加单击事件
    $("#importActivityBtn").click(function () &#123;
        //收集参数
        var activityFileName=$("#activityFile").val();
        var suffix=activityFileName.substr(activityFileName.lastIndexOf(".")+1).toLocaleLowerCase();//xls,XLS,Xls,xLs,....
        //表单验证
        if(suffix!="xls")&#123;
            alert("只支持xls文件");
            return;
        &#125;
        var activityFile=$("#activityFile")[0].files[0];
        if(activityFile.size>5*1024*1024)&#123;
            alert("文件大小不超过5MB");
            return;
        &#125;
    
        //FormData是ajax提供的接口,可以模拟键值对向后台提交参数;
        //FormData最大的优势是不但能提交文本数据,还能提交二进制数据
        var formData=new FormData();
        formData.append("activityFile",activityFile);
        formData.append("userName","元昱鹏");
    
        //发送请求
        $.ajax(&#123;
            url:'workbench/activity/importActivity.do',
            data:formData,
            processData:false,//设置ajax向后台提交参数之前,是否把参数统一转换成字符串:true--是,false--不是,默认是true
            contentType:false,//设置ajax向后台提交参数之前,是否把所有的参数统一按urlencoded编码:true--是,false--不是,默认是true
            type:'post',
            dataType:'json',
            success:function (data) &#123;
                if(data.code=="1")&#123;
                    //提示成功导入记录条数
                    alert("成功导入"+data.retData+"条记录");
                    //关闭模态窗口
                    $("#importActivityModal").modal("hide");
                    //刷新市场活动列表,显示第一页数据,保持每页显示条数不变
                    queryActivityByConditionForPage(1,$("#demo_pag1").bs_pagination('getOption', 'rowsPerPage'));
                &#125;else&#123;
                    //提示信息
                    alert(data.message);
                    //模态窗口不关闭
                    $("#importActivityModal").modal("show");
                &#125;
            &#125;
        &#125;);
    &#125;);
    

    导出市场活动

    需求分析

    1. 用户在市场活动主页面,选择要导出的市场活动,点击”选择导出”按钮,把所有选择的数据生成一个excel文件,弹出文件下载的对话框;

    2. 用户选择要保存的目录,完成选择导出市场活动的功能.

    • 每次至少选择导出一条记录

    • 导出成功之后,页面不刷新

    时序&流程

    Mapper层

    ActivityMapper.java

    /**
         * 根据ids查询市场活动
         * @param ids
         * @return
         */
    List<Activity> selectActivityByIds(String[] ids);
    

    ActivityMapper.xml

    <select id="selectActivityByIds" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from tbl_activity
        where id in
        <foreach collection="array" item="id" separator="," open="(" close=")">
            #&#123;id&#125;
        </foreach>
    </select>
    

    Service层

    ActivityService.java

    List<Activity> queryActivityByIds(String[] ids);
    

    ActivityServiceImpl.java

    @Override
    public List<Activity> queryActivityByIds(String[] ids) &#123;
        return activityMapper.selectActivityByIds(ids);
    &#125;
    

    Controller层

    @RequestMapping("/workbench/activity/exportActivitysByIds.do")
    public void exportActivitysByIds(HttpServletResponse response, HttpServletRequest request) throws Exception &#123;
    
        String checkId=request.getParameter("checkId");
        System.out.println("接收的checkid:"+checkId);
        String[] ids=checkId.split(",");//切割字符串返回数组
    
        //调用service层方法,根据id查询市场活动
        List<Activity> activityList = activityService.queryActivityByIds(ids);
        //创建exel文件,并且把activityList写入到excel文件中
        HSSFWorkbook wb=new HSSFWorkbook();
        HSSFSheet sheet=wb.createSheet("市场活动列表");
        HSSFRow row=sheet.createRow(0);
        HSSFCell cell;
        //第一行表头
        String[] titles = &#123;"ID","所有者","名称","开始日期","结束日期","成本","描述","创建时间","创建者","修改时间","修改者"&#125;;
        for (int i = 0 ; i < titles.length ; i ++)&#123;
            cell=row.createCell(i);
            cell.setCellValue(titles[i]);
        &#125;
    
        //遍历activityList,创建HSSFRow对象,生成所有的数据行
        if (activityList!=null && activityList.size()>0)&#123;
            Activity activity=null;
            for(int i=0;i<activityList.size();i++) &#123;
                activity=activityList.get(i);
                //每遍历出一个activity,生成一行
                row=sheet.createRow(i+1);
                //每一行创建11列,每一列的数据从activity中获取
                String[] contents = &#123;activity.getId(), activity.getOwner(), activity.getName(), activity.getStartDate(), activity.getEndDate(), activity.getCost(),
                                     activity.getDescription(), activity.getCreateTime(), activity.getCreateBy(), activity.getEditTime(), activity.getEditBy()&#125;;
                for (int j = 0 ; j < contents.length ; j ++)&#123;
                    cell=row.createCell(j);
                    cell.setCellValue(contents[j]);
    
                &#125;
            &#125;
        &#125;
    
        //把生成的excel文件下载到客户端
        response.setContentType("application/octet-stream;charset=UTF-8");
        response.addHeader("Content-Disposition","attachment;filename=activityList.xls");
        OutputStream out=response.getOutputStream();
    
        wb.write(out);
        //关闭资源
        wb.close();
        out.flush();
    &#125;
    

    前台页面

    workbench/activity/index.jsp

    //给"选择导出"按钮添加单击事件
    $("#exportActivityXzBtn").click(function () &#123;
        //收集参数
        //获取列表中所有被选中的checkbox
        var chekkedIds=$("#tBody input[type='checkbox']:checked");
        if(chekkedIds.size()==0)&#123;
            alert("请选择要导出的市场活动");
            return;
        &#125;
    
        //拼接字符串
        var checkId=[];
        $("input[type='checkbox']:checked").each(function (i)&#123;
            checkId[i]=$(this).val();//选中额框的值给定义的数组
        &#125;)
    
        //发送同步请求
        window.location.href="workbench/activity/exportActivitysByIds.do?checkId="+checkId;
    &#125;);
    

    复习知识点

    1)页面切割技术

    • <frameset><frame>

      • :用来切割页面
        <frameset cols="20%,60%,20%" rows="10%,80%,10%">
        
      • :显示页面
        <frame src="url">
                <frameset cols="20%,60%,20%">
                    <frame src="url1" name="f1">
                    <frame src="url2" name="f2">
                    <frame src="url3" name="f3">
                </frameset>
        
        <a href="url" target="f3">test</a>
        
        • 每一个标签就是一个独立的浏览器窗口

    • <div><iframe>

      • <div>:切割页面

        <div style="height:10%;width=20%">	
        
      • <iframe>:显示页面

        <div style="height:10%;width=20%">
                <iframe href="url">
        </div>
        
    • 模态窗口

      • 模拟的窗口,本质上是<div>,通过设置z-index大小来实现的

        • 初始时,z-index初始参数是<0,所以不显示
        • 需要显示时,z-index值设置成>0即可
        • bootstrap来控制z-index的大小
      • 控制模态窗口的显示与隐藏:

        • 方式一:通过标签的属性data-toggle=”modal” data-target=”模态窗口的id”

        • 方式二:通过js函数控制

           选择器(选中div).modal("show");//显示选中的模态窗口
           选择器(选中div).modal("hide");//关闭选中的模态窗口
          
        • 方式三:通过标签的属性data-dismiss=””

          • 点击添加了data-dismiss=””属性的标签,自动关闭该标签所在的模态窗口
      • 模态窗口的意义

        • 模态窗口本质上就是原来页面中的一个<div>,只有一个页面;所有的操作都是在同一个页面中完成()

    2)正则表达式

    • 语言,语法:定义字符串的匹配模式,可以用来判断指定的具体字符串是否符合匹配模式。

    • 语法通则:

      • //:在js中定义一个正则表达式. var regExp=/…/;

      • ^:匹配字符串的开头位置

      • $: 匹配字符串的结尾

      • []:匹配指定字符集中的一位字符.

        • var regExp=/^[abc]$/;
        • var regExp=/^[a-z0-9]$/;
      • {}:匹配次数

        • var regExp=/^[abc]{5}$/;
        • {m}:匹配m此
        • {m,n}:匹配m次到n次
        • {m,}:匹配m次或者更多次
      • 特殊符号:

        • \d:匹配一位数字,相当于[0-9]
        • \D:匹配一位非数字
        • \w:匹配所有字符,包括字母、数字、下划线。
        • \W:匹配非字符,除了字母、数字、下划线之外的字符。
      • *:匹配0次或者多次,相当于{0,}

        • +:匹配1次或者多次,相当于{1,}
      • ?:匹配0次或者1次,相当于{0,1}

    3)前端插件使用步骤

    1)引入开发包:.js,.css

    1. 引入jquery、boot scrap包到jsp文件中:
    
    
    
    
    
    
    1. 下载插件开发包并引入jsp

      
      
      
      

    2)创建容器:

    3)当容器加载完成之后,对容器调用工具函数.

    4)分页查询过程

    1. queryActivityByConditionForPage(1,10)
      |->把pageNo,pageSize和查询条件一起发送到后台,查询数据
      |->data
      |->activityList:遍历list,显示列表
      |->totalRows:调用工具函数,显示翻页信息
    2. 当用户切换页号或者每页显示条数时:pageNo,pageSize
      |->翻页信息会自动变化
      |->手动刷新列表:
      |->把pageNo,pageSize和查询条件一起发送到后台,查询数据
      |->data
      |->activityList:遍历list,显示列表
      |->totalRows:调用工具函数,显示翻页信息

    data提交参数

    ajax向后台发送请求时,可以通过data提交参数,data的数据格式有三种格式:

    1. data:{
          k1:v1,
          k2:v2,
          ....
       }
      
      • 劣势:只能向后台提交一个参数名对应一个参数值的数据,不能向后台提交一个参数名对应多个参数值的数据
      • 优势: 操作简单
    2. data:k1=v1&k2:v2&....
      
      • 劣势:操作麻烦

      • 优势:不但能够向后台提交一个参数名对应一个参数值的数据,还能向后台提交一个参数名对应多个参数值的数据。

    3. data:FormData对象
      
      • 优势:不但能提交字符串数据, 还能提交二进制数据
      • 劣势:操作更复杂
    • 函数:如果一段用来完成特定功能的代码到处出现,可以封装成函数。
    • 函数的参数:在编写函数的过程中,如果有一个或者多个数据无法确定,可以把这些数据定义成函数的参数(形参),将来由函数的调用者来传递参数的具体的值(实参)。

    5)在指定标签显示jsp页面片段&js截取字符串

    在指定标签显示jsp页面片段:

    • 选择器.html(jsp页面片段的字符串);
    • 在选择的标签中作为html代码显示,覆盖显示
    • 选择器.append(jsp页面片段的字符串);
    • 在选择的标签中作为html代码显示,追加显示
    • 选择器.after(jsp页面片段的字符串);
    • 在选择器前面显示
    • 选择器.before(jsp页面片段的字符串);
    • 在选择器后面显示
    • 选择器.text(jsp页面片段的字符串);
    • 作为文本显示

    js截取字符串:

    • str.substr(startIndex,length);
      • 从下标为startIndex的字符开始截取,截取length个字符
    • str.substring(startIndex,endIndex)
      • 从下标为startIndex的字符开始截取,截取到下标是endIndex的字符

    6)页面中给元素添加事件

    固有元素与动态元素:

    • 固有元素:当调用事件函数给元素添加事件时,如果元素已经生成,则这些元素叫做固有元素
    • 动态生成的元素:当调用事件函数给元素添加事件时,如果元素还没有生成,后来生成的元素叫做动态生成的元素

    在页面中给元素添加事件语法:

    • 使用元素的事件属性:onxxxx=“f()”

      • 只能给固有元素添加事件
    • 使用jquery对象:

      选择器.xxxx(function(){
          //js代码
          //this
      });
      
      • 只能给固有元素添加事件
    • 使用jquery的on函数:

      父选择器.on("事件类型",子选择器,function(){
          //js代码
          //this
      });
      
      • 不但能给固有元素添加事件,还能够给动态生成的元素添加事件
      • 父元素:必须是固有元素,可以直接父元素,也可以是间接父元素.原则固有父元素范围越小越好
      • 子选择器:目标元素,跟父选择器构成一个父子选择器
      • 事件类型:跟事件属性和事件函数一一对应

    7)文件上传

    文件上传的表单三个条件:

    1. 表单组件标签必须用: