SpringBootspring boot

使用Ajax技术开发简易版人事管理系统(了解)

2019-05-29  本文已影响212人  测试老杨

项目介绍

所有功能使用Ajax技术,浏览器发送Ajax请求给后台,后台返回json格式的数据,JS回调函数对返回的JSON数据进行处理。针对浏览器禁用cookies的情况,通过URL地址重写技术将sessionId放到URL地址里面。用户的登录状态以及权限使用session缓存。

后台开发使用的框架:Spring + Spring MVC + MyBatis
前端开发使用的框架:BootStrap + EasyUI
服务器软件:tomcat
数据库软件:mysql
开发工具:eclipse + maven

主要功能:
权限管理(已完成)部门管理(已完成),员工管理(未完成),考勤管理(未完成),薪资管理(未完成),我的考勤(未完成),我的薪资(未完成)

主要角色:
管理员,人事专员,考勤员,薪资福利专员,普通员工


image.png
image.png

部门管理功能实现的思路

1、从github上下载某开源项目的代码
2、执行数据初始化的脚本(建库和建表)
3、创建部门表
4、界面配置部门管理功能的URL地址
5、给管理员角色分配部门管理功能的权限
6、设计部门模板类Dept(可以通过工具自动生成)
7、设计部门分页查询工具类DeptExample(可以通过工具自动生成)
8、设计部门表单类DeptForm
9、设计表单实体转换类DeptConvert
10、开发DeptService接口
11、开发DeptService接口的实现类
12、开发数据库访问接口DeptMapper(能够对部门表进行增删改查)
13、设计部门表映射文件DeptMapper.xml(可以通过工具自动生成)
14、开发控制器DeptController(处理浏览器发送过来的请求)
15、控制器DeptController里面会调用DeptService
16、DeptService里面会调用数据库接口DeptMapper
17、设计部门列表页面的JSP代码
18、设计部门表单页面的JSP代码
19、设计部门管理功能的JS代码(发送Ajax请求和回调函数)

从github下载某开源项目的源代码

image.png

创建部门表

CREATE TABLE `dept` (
  `id` bigint(19) NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `name` varchar(64) NOT NULL COMMENT '部门名',
  `address` varchar(100) DEFAULT NULL COMMENT '地址',
  `code` varchar(64) NOT NULL COMMENT '编号',
  `icon` varchar(32) DEFAULT NULL COMMENT '图标',
  `pid` bigint(19) DEFAULT NULL COMMENT '父级主键',
  `is_leaf` tinyint(1) DEFAULT '0' COMMENT '叶子节点',
  `seq` tinyint(2) NOT NULL DEFAULT '0' COMMENT '排序',
  `del_flag` tinyint(1) DEFAULT '0' COMMENT '删除标记',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=36 DEFAULT CHARSET=utf8 COMMENT='部门表';

配置部门管理功能的URL地址

image.png

给系统管理员角色分配部门管理的权限

image.png

开发控制器DeptController(处理浏览器发送过来的请求)

代码如下:

package com.testin.hr.web;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.testin.hr.service.DeptService;
import com.testin.hr.web.form.DeptForm;

/**
 * 
 * @author yangzc
 *
 */
@Controller
public class DeptController {

    @Autowired
    DeptService deptService;

    @RequestMapping(value = "/hr/depts/ui/{ui}", method = RequestMethod.GET)
    public String ui(@PathVariable("ui") String ui) {
        return "hr/dept/dept-" + ui;
    }

    @RequestMapping(value = "/hr/depts", method = RequestMethod.POST)
    @ResponseBody
    public Object save(DeptForm form) {
        return deptService.saveOrUpdate(form);
    }

    @RequestMapping(value = "/hr/depts/{ids}", method = RequestMethod.DELETE)
    @ResponseBody
    public Object delete(@PathVariable("ids") String ids) {
        return deptService.deleteByIds(ids);
    }

    @RequestMapping(value = "/hr/depts", method = RequestMethod.GET)
    @ResponseBody
    public Object list(DeptForm form) {
        return deptService.listPage(form);
    }

    @RequestMapping(value = "/hr/depts/tree", method = RequestMethod.GET)
    @ResponseBody
    public Object tree(DeptForm form) {
        return deptService.tree(form);
    }

    @RequestMapping(value = "/hr/depts/{id}", method = RequestMethod.GET)
    @ResponseBody
    public Object get(@PathVariable("id") Long id) {
        return deptService.get(id);
    }
}

DeptService接口实现类的设计

代码如下:

package com.testin.hr.service.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.testin.common.base.BaseResult;
import com.testin.common.base.EUTreeGridResult;
import com.testin.common.base.Tree;
import com.testin.hr.convert.DeptConvert;
import com.testin.hr.dao.DeptMapper;
import com.testin.hr.domain.Dept;
import com.testin.hr.domain.DeptExample;
import com.testin.hr.service.DeptService;
import com.testin.hr.web.form.DeptForm;
import com.testin.sys.convert.SysOrgConvert;
import com.testin.sys.domain.SysOrg;

@Service
@Transactional
public class DeptServiceImpl implements DeptService {

    @Autowired
    DeptMapper deptMapper;

    public BaseResult delete(Long id) {
        deptMapper.deleteByPrimaryKey(id);
        return BaseResult.ok("删除成功");
    }

    @Override
    public BaseResult deleteByIds(String ids) {
        DeptExample example = new DeptExample();
        example.createCriteria().andIdIn(idsToList(ids));
        List<Dept> deptList = deptMapper.selectByExample(example);
        for (Dept org : deptList) {
            org.setDelFlag(1);
            deptMapper.updateByPrimaryKeySelective(org);
        }
        return BaseResult.ok("删除成功");
    }


    @Override
    public BaseResult get(Long id) {
        Dept dept = deptMapper.selectByPrimaryKey(id);
        return BaseResult.ok("查询成功", DeptConvert.entityToForm(dept));
    }

    @Override
    public BaseResult list(DeptForm form) {
        DeptExample example = new DeptExample();
        example.createCriteria().andDelFlagEqualTo(0);
        List<Dept> deptList = deptMapper.selectByExample(example);
        return BaseResult.ok("查询成功", DeptConvert.entityListToFormList(deptList));
    }

    @Override
    public EUTreeGridResult listPage(DeptForm form) {
        DeptExample example = new DeptExample();
        //设置分页
        example.setStart((form.getPage() - 1) * form.getRows());
        example.setSize(form.getRows());

        //排序
        example.setOrderByClause("seq ASC");

        //查询条件
        if (form != null) {
            DeptExample.Criteria criteria = example.createCriteria();
            //条件-关键字
            if (form.getKeyword() != null && !form.getKeyword().equals("")) {
                criteria.andNameLike("%" + form.getKeyword() + "%");
                example.or().andAddressLike("%" + form.getKeyword() + "%");
            }
            //其它条件
            criteria.andDelFlagEqualTo(0);
        }

        //查询总记录
        long count = deptMapper.countByExample(example);
        //查询分页列表
        List<Dept> deptList = deptMapper.selectPageByExample(example);

        //返回结果
        EUTreeGridResult result = new EUTreeGridResult(count, DeptConvert.entityListToFormList(deptList));
        return result;
    }

    @Override
    public List<Tree> tree(DeptForm form) {
        DeptExample example = new DeptExample();
        example.setOrderByClause("seq ASC");
        example.createCriteria().andDelFlagEqualTo(0);
        List<Dept> deptList = deptMapper.selectByExample(example);
        return prepareTree(deptList);
    }

    private List<Tree> prepareTree(List<Dept> deptList) {
        List<Tree> allTreeList = deptListToTreeList(deptList);
        List<Tree> topList = new ArrayList<>();
        for (Tree tree : allTreeList) {
            // 遍历所有节点,将父菜单id与传过来的id比较
            if (tree.getPid() == null) {
                tree.setChildren(prepareTreeChiled(tree.getId(), allTreeList));
                topList.add(tree);
            }
        }
        return topList;
    }

    private List<Tree> prepareTreeChiled(Long id, List<Tree> allTreeList) {
        // 子菜单
        List<Tree> childList = new ArrayList<>();
        for (Tree tree : allTreeList) {
            // 遍历所有节点,将父菜单id与传过来的id比较
            if (tree.getPid() != null && tree.getPid().equals(id)) {
                childList.add(tree);
            }
        }
        // 把子菜单的子菜单再循环一遍
        for (Tree tree : childList) {
            if (tree.getIsLeaf() == 1) {
                tree.setChildren(prepareTreeChiled(tree.getId(), allTreeList));
            }
        }
        // 递归退出条件
        if (childList.size() == 0) {
            return null;
        }
        return childList;
    }

    private List<Tree> deptListToTreeList(List<Dept> deptList) {
        List<Tree> treeList = new ArrayList<>();
        if (deptList != null && deptList.size() > 0) {
            for (Dept dept : deptList) {
                treeList.add(deptToTree(dept));
            }
        }
        return treeList;
    }

    private Tree deptToTree(Dept dept) {
        Tree tree = new Tree();
        tree.setId(dept.getId());
        tree.setText(dept.getName());
        tree.setIconCls(dept.getIcon());
        tree.setIsLeaf(dept.getIsLeaf());
        tree.setPid(dept.getPid());
        return tree;
    }

    @Override
    public BaseResult saveOrUpdate(DeptForm form) {
        Dept entity = DeptConvert.formToEntity(form);

        if (entity.getId() != null) {
            entity.setUpdateTime(new Date());
            deptMapper.updateByPrimaryKeySelective(entity);
        } else {
            entity.setIsLeaf(0);
            entity.setDelFlag(0);
            entity.setUpdateTime(new Date());
            entity.setCreateTime(new Date());
            deptMapper.insert(entity);
        }

        //更新父部门状态
        if (entity.getPid() != null) {
            Dept dept = deptMapper.selectByPrimaryKey(entity.getPid());
            dept.setIsLeaf(1);
            deptMapper.updateByPrimaryKey(dept);
        }
        return BaseResult.ok("保存成功");
    }

    @Override
    public BaseResult update(DeptForm form) {
        DeptExample example = new DeptExample();
        deptMapper.updateByExample(DeptConvert.formToEntity(form), example);
        return BaseResult.ok("更新成功");
    }

    private List<Long> idsToList(String ids) {
        String[] id = ids.split(",");
        List<Long> idList = new ArrayList<>();
        for (int i = 0; i < id.length; i++) {
            idList.add(Long.parseLong(id[i]));
        }
        return idList;
    }
}

部门列表页面的JSP脚本设计

代码如下:

<%--
Created by IntelliJ IDEA.
Date: 2016-12-30
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!-- 权限控制标签库 -->
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<!-- 工具栏 -->
<div id="DeptToolbar" style="padding:5px;height:auto">
    <div>
        <a href="#" class="easyui-linkbutton" data-options="iconCls:'icon-add'" plain="true" onclick="javascript:Dept.list.add()">增加</a>
        <a href="#" class="easyui-linkbutton" data-options="iconCls:'icon-cancel'" plain="true" onclick="javascript:Dept.list.delete()">删除</a>
        <a href="#" class="easyui-linkbutton" data-options="iconCls:'icon-edit'" plain="true" onclick="javascript:Dept.list.edit()">编辑</a>
        <a href="#" class="easyui-linkbutton" data-options="iconCls:'icon-reload'" plain="true" onclick="javascript:Dept.list.reload()">刷新</a>
        <a href="#" class="easyui-linkbutton" data-options="iconCls:'icon-standard-plugin-delete'" plain="true" onclick="javascript:Dept.list.collapseAll()">折叠</a>
        <a href="#" class="easyui-linkbutton" data-options="iconCls:'icon-standard-plugin-add'" plain="true" onclick="javascript:Dept.list.expandAll()">展开</a>
        <!-- 权限控制标签 对应com.testin.common.shiro.ShiroDbRealm.doGetAuthorizationInfo方法 -->
        <shiro:hasPermission name="user:create1">
            <a href="javascript:void(0)" class="easyui-linkbutton" iconCls="icon-remove" plain="true" data-options="disabled:false" onclick="del()">删除</a>
            <span class="toolbar-item dialog-tool-separator"></span>
        </shiro:hasPermission>
    <span style="float: right;margin-right: 10px;padding: 1px">
        <span>关键字:</span>
        <input lang="searchDept" name="keyword" style="line-height:19px;border:1px solid #ccc">
        <a href="#" class="easyui-linkbutton" data-options="iconCls:'icon-clear'" plain="true" onclick="javascript:Dept.list.clear()">清除</a>
        <a href="#" class="easyui-linkbutton" data-options="iconCls:'icon-search'" plain="true" onclick="javascript:Dept.list.search()">搜索</a>
    </span>        
    </div>
</div>
<!-- 列表 -->
<table id="DeptList" data-options="border:false"  style="width: 100%;" title="部门列表"></table>
<!-- 弹窗  --> <!-- inline:true 不然多次打开tab会重复提交表单 -->
<div id="DeptEdit" title="编辑部门" style="width:500px;height:400px;top: 100px;padding: 10px;display: none" data-options="iconCls: 'icon-save',closed: true,modal: true,inline:true,buttons:[{text:'保存',iconCls:'icon-save',handler:function(){Dept.input.submitForm()}},{text:'取消',iconCls:'icon-cancel',handler:function(){Dept.input.close()}}]"  ></div>
<script src="<%=request.getContextPath()%>/jsp/hr/dept/dept.js"></script>
<script>
    Dept.list.init('<%=request.getContextPath()%>');
</script>

部门编辑界面的JSP脚本设计

代码如下:

<%--
Created by IntelliJ IDEA.
Date: 2016-12-30
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<form id="DeptForm" method="post">
     <table class="com_table"  align="center">
        <input type="hidden" name="id">
        <input type="hidden" name="isLeaf">
        <tr>
            <td></td>
            <td><label>部门名:</label></td>
            <td><input class="easyui-textbox com_input" name="name" data-options="required:true"/></td>
            <td></td>
        </tr>
        <tr>
            <td></td>
            <td><label>地址:</label></td>
            <td><input class="easyui-textbox com_input" name="address" data-options="required:false"/></td>
            <td></td>
        </tr>
        <tr>
            <td></td>
            <td><label>编号:</label></td>
            <td><input class="easyui-textbox com_input" name="code" data-options="required:false"/></td>
            <td></td>
        </tr>
        <tr>
            <td></td>
            <td><label>图标:</label></td>
            <td><input class="easyui-textbox com_input" name="icon" data-options="required:false"/></td>
            <td></td>
        </tr>
        <tr>
            <td></td>
            <td><label>上级部门:</label></td>
            <td><select class="easyui-combobox com_input"  id="parentDept"  name="pid" data-options="textField:'text',valueField:'id'"></select></td>
            <td></td>
        </tr>
        <tr>
            <td></td>
            <td><label>排序:</label></td>
            <td><input class="easyui-numberspinner" name="seq" data-options="min:0,max:10000,editable:true,required:false,value:0"/></td>
            <td></td>
        </tr>
    </table>
</form>

<script src="<%=request.getContextPath()%>/jsp/hr/dept/dept.js"></script>
<script>
    Dept.input.init('<%=request.getContextPath()%>');
</script>

部门管理功能的JS脚本设计

代码如下:

var ctx = "";//项目部署的工程名
var DeptList;
var DeptEdit;
var DeptForm;

//其它组件
var parentDept;

var Dept = {
    URL: {
        inputUI: function () {
            return ctx + "/hr/depts/ui/input";
        },
        listUI: function () {
            return ctx + "/hr/depts/ui/list";
        },
        list: function () {
            return ctx + "/hr/depts/";
        },
        tree: function () {
            return ctx + "/hr/depts/tree";
        },
        save: function () {
            return ctx + "/hr/depts/";
        },
        delete: function (ids) {
            return ctx + "/hr/depts/" + ids;
        },
        get: function (id) {
            return ctx + "/hr/depts/" + id;
        }
    },
    input: {
        init: function (ct) {
            ctx = ct;
            Dept.input.initComponent();
            Dept.input.initForm();
        },
        initComponent: function () {
            DeptForm = $("#DeptForm");
            parentDept = $("#parentDept");
        },
        initForm: function () {
            DeptForm.form({
                url: Dept.URL.save(),
                onSubmit: function () {
                    // do some check
                    // return false to prevent submit;
                },
                success: function (data) {
                    var data = eval('(' + data + ')');
                    if (data.code == 200) {
                        Dept.input.close();
                        Dept.list.reload();
                    }
                }
            });
        },
        submitForm: function () {
            // submit the form
            DeptForm.submit();
        },
        close: function () {
            DeptEdit.dialog('close');
        },
    },
    list: {
        init: function (ct) {
            ctx = ct;
            Dept.list.initComponent();
            Dept.list.initList();
        },
        initComponent: function () {
            DeptList = $("#DeptList");
            DeptEdit = $('#DeptEdit');
        },
        initList: function () {
            DeptList.treegrid({
                url: Dept.URL.list(),
                method: 'get',
                pagination: true,
                pageSize: 30,
                toolbar: '#DeptToolbar',//SysOrg.list.toolbar,
                singleSelect: false,
                collapsible: false,
                idField: 'id',
                treeField: 'name',
                parentField: 'pid',
                columns: [[
                    {field: 'ck', checkbox: true},
                    {field: 'id', title: '主键id', hidden: true},
                    {field: 'name', title: '部门名', width: '13.571%', hidden: false},
                    {field: 'address', title: '地址', width: '13.571%', hidden: false},
                    {field: 'code', title: '编号', width: '13.571%', hidden: false},
                    {field: 'icon', title: '图标', width: '13.571%', hidden: false},
                    {field: 'pid', title: '父级主键', width: '13.571%', hidden: true},
                    {field: 'seq', title: '排序', width: '13.571%', hidden: false},
                    {field: 'createTime', title: '创建时间', width: '13.571%', hidden: false},
                ]],
                //设置选中事件,清除之前的行选择
                onClickRow: function (row) {
                    DeptList.treegrid("unselectAll");
                    DeptList.treegrid("selectRow",row.id);
                },
                loadFilter: function (data, parentId) {
                    var opt = $(this).data().treegrid.options;
                    var parentField;
                    if (opt.parentField) {
                        parentField = opt.parentField;
                        var jsonStr = JSON.stringify(data); //可以将json对象转换成json对符串
                        jsonStr = jsonStr.replace(new RegExp(parentField, "gm"), "_parentId");
                        return JSON.parse(jsonStr); //可以将json字符串转换成json对象
                    }
                }
            });
        },
        getSelectionsIds: function () {
            var sels = DeptList.datagrid("getSelections");
            var ids = [];
            for (var i in sels) {
                ids.push(sels[i].id);
            }
            ids = ids.join(",");
            return ids;
        },
        //增
        add: function () {
            DeptEdit.dialog({
                    href: Dept.URL.inputUI(),
                    onLoad: function () {
                        parentDept.combotree({
                            url: Dept.URL.tree(),
                            method: 'get',
                            panelHeight: 'auto'
                        });
                    }
                })
                .dialog("open");
        },
        //改
        edit: function () {
            var sels = DeptList.treegrid("getSelections");
            if (sels.length < 1) {
                $.messager.alert("对话框", "至少选择一行");
                return;
            }

            if (sels.length > 1) {
                $.messager.alert("对话框", "只能选择一行");
                return;
            }

            DeptEdit.dialog({
                    href: Dept.URL.inputUI(),
                    onLoad: function () {
                        //方案一:使用Form的load去load数据
                        //SysOrgForm.form("load", SysOrg.URL.get(sels[0].id));
                        //方案二:直接使用列表的row数据
                        //SysOrgForm.form("load",sels[0]);
                        //方案三:使用Ajax请求数据
                        $.ajax({
                            type: "GET",
                            url: Dept.URL.get(sels[0].id),
                            success: function (data) {
                                if (data.code == 200) {
                                    DeptForm.form("load", data.data);
                                    parentDept.combotree({
                                        url: Dept.URL.tree(),
                                        method: 'get',
                                        panelHeight: 'auto',
                                        onLoadSuccess: function () {
                                            parentDept.combotree('setValue', data.data.pid);
                                        }
                                    });
                                }
                            }
                        });
                    }
                })
                .dialog("open");
        },
        //删
        delete: function () {
            var ids = Dept.list.getSelectionsIds();
            if (ids.length == 0) {
                $.messager.alert("对话框", "至少选择一行");
                return;
            }

            $.messager.confirm({
                title: '确认提示框',
                msg: '你确定要删除吗?',
                fn: function (r) {
                    if (r) {
                        $.ajax({
                            type: "DELETE",
                            url: Dept.URL.delete(ids),
                            success: function () {
                                Dept.list.reload();
                                Dept.list.clearSelectionsAndChecked();
                            }
                        });
                    }
                }
            });
        },
        //刷新
        reload: function () {
            DeptList.treegrid("reload");
        },
        collapseAll: function () {
            var roots = DeptList.treegrid("getRoots");
            for (var i in roots) {
                DeptList.treegrid("collapseAll", roots[i].id);
            }
        },
        expandAll: function () {
            var roots = DeptList.treegrid("getRoots");
            for (var i in roots) {
                DeptList.treegrid("expandAll", roots[i].id);
            }
        },
        clearSelectionsAndChecked: function () {
            DeptList.treegrid("clearChecked");
            DeptList.treegrid("clearSelections");
        },
        //搜索
        search: function () {
            var searchName = [];
            var searchValue = [];
            $("input[lang='searchDept']").each(function (i) {
                searchName[i] = $(this).attr("name");
                searchValue[i] = $(this).val();
            });

            var queryParamsArr = [];
            for (var i = 0; i < searchName.length; i++) {
                queryParamsArr.push(searchName[i] + ":'" + searchValue[i] + "'")
            }
            var queryParams = "{" + queryParamsArr.join(",") + "}";

            DeptList.treegrid({
                queryParams: eval('(' + queryParams + ')'),
                onLoadSuccess: function (data) {
                    //回显搜索内容
                    $("input[lang='searchDept']").each(function (i) {
                        $(this).val(searchValue[i]);
                    });
                }
            });
        },
        //清空
        clear: function () {
            $("input[lang='searchDept']").each(function (i) {
                $(this).val("");
            });
        }
    }
}

参考资料

[01] jquery+easyui实现页面布局和增删改查操作(SSH2框架支持)
https://blessht.iteye.com/blog/1069749/
[02] EasyUi – 布局Layout + 登录界面
https://www.cnblogs.com/tangge/p/3224595.html
[03] 前端框架 EasyUI 页面布局 Layout
https://www.bbsmax.com/A/LRnJWwwJqY/
[04] 基于SSM-EasyUI的权限管理系统
https://blog.csdn.net/uq_jin/article/details/56283719
https://github.com/cskun/rds-sys
[05]cookie禁用后session怎么使用url重写详细讲解
https://blog.csdn.net/weixin_40648117/article/details/78844100
[06] 设置shiro超时时间
https://blog.csdn.net/wahaha13168/article/details/82389982
[07] springmvc easyui全局处理session过期时跳转的登陆页面内嵌至tab页中
https://www.oschina.net/question/1017341_236885
[08] 解决easyui在session过期后iframe跳转到登录页面的问题
https://blog.csdn.net/ace_place/article/details/53053282
[09] 基于SSM框架和easyUI框架的简易人事管理系统
https://blog.csdn.net/qq_34247759/article/details/82993706
[10] SpringMvc+Mybatis实现一个简单人事管理系统
https://blog.csdn.net/qq_35709874/article/details/77477955

微信扫一扫关注该公众号【测试开发者部落】

image.png
点击链接加入群聊【软件测试学习交流群】
https://jq.qq.com/?_wv=1027&k=5eVEhfN
软件测试学习交流QQ群号:511619105
上一篇下一篇

猜你喜欢

热点阅读