项目介绍
所有功能使用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
微信扫一扫关注该公众号【测试开发者部落】
点击链接加入群聊【软件测试学习交流群】
https://jq.qq.com/?_wv=1027&k=5eVEhfN
软件测试学习交流QQ群号:511619105
网友评论