七 员工管理
1 数据库表 – 员工
CREATE TABLE `employee` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`username` varchar(255) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
`password` varchar(255) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
`admin` bit(1) DEFAULT NULL,
`dept_id` bigint(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
2 Employee 实体类
@Data
public class Employee {
private Long id;
private String username;
private String name;
private String password;
private String email;
private Integer age;
private boolean admin;
private Department dept;
}
3 EmployeeMapper接口
@Repository
public interface EmployeeMapper {
int deleteByPrimaryKey(Long id);
int insert(Employee record);
Employee selectByPrimaryKey(Long id);
List<Employee> selectAll();
int updateByPrimaryKey(Employee record);
List<Employee> selectForList(QueryObject qo);
void insertRelationBatch(@Param("employeeId") Long id, @Param("roleIds") Long[] roleIds);
void deleteRelation(Long employeeId);
Integer checkUsername(String username);
Employee getByUsernameAndPassword(@Param("username") String username, @Param("password") String password);
}
4 EmployeeMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="cn.tj.mapper.EmployeeMapper" >
<resultMap id="BaseResultMap" type="cn.tj.domain.Employee" >
<id column="id" property="id" />
<result column="username" property="username" />
<result column="name" property="name" />
<result column="password" property="password" />
<result column="email" property="email" />
<result column="age" property="age" />
<result column="admin" property="admin" />
<association columnPrefix="d_" property="dept" javaType="department">
<result column="id" property="id" />
<result column="name" property="name" />
<result column="sn" property="sn" />
</association>
</resultMap>
<delete id="deleteByPrimaryKey" >
delete from employee
where id = #{id}
</delete>
<delete id="deleteRelation">
delete from employee_role where employee_id = #{employeeId}
</delete>
<insert id="insert" useGeneratedKeys="true" keyProperty="id" >
insert into employee (username, name, password, email, age, admin, dept_id
)
values (#{username}, #{name}, #{password}, #{email}, #{age}, #{admin}, #{dept.id}
)
</insert>
<insert id="insertRelationBatch">
insert into employee_role(employee_id, role_id) values
<foreach collection="roleIds" separator="," item="roleId">
(#{employeeId},#{roleId})
</foreach>
</insert>
<update id="updateByPrimaryKey" >
update employee
set
name = #{name},
email = #{email},
age = #{age},
admin = #{admin},
dept_id = #{dept.id}
where id = #{id}
</update>
<select id="selectByPrimaryKey" resultMap="BaseResultMap" >
select e.id, e.username, e.name, e.password, e.email, e.age, e.admin,d.id d_id,d.name d_name,d.sn d_sn
from employee e left join department d on e.dept_id = d.id
where e.id = #{id}
</select>
<select id="selectAll" resultMap="BaseResultMap" >
select id, username, name, password, email, age, admin, dept_id
from employee
</select>
/*显示数据1查询2封装3存作用域4展示*/
<sql id="where_sql">
<where>
<if test="keyword != null and keyword != ''">
and (e.name like concat('%',#{keyword},'%') or e.email like concat('%',#{keyword},'%'))
</if>
<if test="deptId != null">
and e.dept_id = #{deptId}
</if>
</where>
</sql>
<select id="selectForList" resultMap="BaseResultMap">/*左外可以查出没有部门的员工信息*/
select e.id, e.username, e.name, e.password, e.email, e.age, e.admin,d.id d_id,d.name d_name,d.sn d_sn
from employee e left join department d on e.dept_id = d.id
<include refid="where_sql"/>
</select>
<select id="checkUsername" resultType="java.lang.Integer">
select count(*) from employee where username=#{username}
</select>
<select id="getByUsernameAndPassword" resultMap="BaseResultMap">
select e.id, e.username, e.name, e.password, e.email, e.age, e.admin,d.id d_id,d.name d_name,d.sn d_sn
from employee e left join department d on e.dept_id = d.id
where username=#{username} and password=#{password}
</select>
</mapper>
5 EmployeeController
@Controller
@RequestMapping("/employee")
public class EmployeeController {
@Autowired
private IEmployeeService employeeService;
@Autowired
private IDepartmentService departmentService;
@Autowired
private IRoleService roleService;
// 处理员工查询所有方法
@RequestMapping("/list")
@RequirePermission(name="员工列表",expression = "employee:list")
// 此处qo为自定义属性作为参数时会自动放到model中,前台可取qo,若不使用该注解,前台应使用employeeQueryObject获取qo,就是类型首字母小写,相当于 model.addAttribute(qo);,加注解后表示qo为该类型qo的key
public String list(Model model, @ModelAttribute("qo")EmployeeQueryObject qo, HttpServletRequest req){
PageInfo<Employee> pageInfo = employeeService.query(qo);
model.addAttribute("pageInfo",pageInfo);
List<Department> departments = departmentService.listAll();
model.addAttribute("departments",departments);
System.out.println(req.getRemoteAddr());
return "employee/list"; // WEB-INF/views/ employee/list .jsp
}
// 处理员工删除方法
@RequestMapping("/delete")
@RequirePermission(name="员工删除",expression = "employee:delete")
public String delete(Long id){
if (id != null) {
employeeService.delete(id);
}
return "redirect:/employee/list"; // 再次发起请求 到我们上面的查询所有的控制器方法。
}
// 进入员工新增/编辑页面方法
@RequestMapping("/input")
@RequirePermission(name="员工新增/编辑页面",expression = "employee:input")
public String input(Long id,Model model){
// 所有的部门信息
List<Department> departments = departmentService.listAll();
model.addAttribute("departments",departments);
// 所有角色信息
List<Role> roles = roleService.listAll();
model.addAttribute("roles",roles);
if (id != null) {
// 修改
Employee employee = employeeService.get(id);
model.addAttribute("employee",employee);
// 当前员工的角色信息(根据员工id进行查询)
List<Role> selfRoles = roleService.queryByEmployeeId(id);
model.addAttribute("selfRoles",selfRoles);
}
return "employee/input"; // WEB-INF/views/ employee/input .jsp
}
@RequestMapping("/saveOrUpdate")
@ResponseBody
@RequirePermission(name="员工新增/编辑",expression = "employee:saveOrUpdate")
public JsonResult saveOrUpdate(Employee employee,Long[] roleIds){
if(employee.getId() == null){
employeeService.save(employee,roleIds);
} else {
employeeService.update(employee,roleIds);
}
return new JsonResult(true,"保存成功");
}
// 检查用户名是否存在
@RequestMapping("/checkUsername")
@ResponseBody
public Map<String,Boolean> checkUsername(String username){
Map<String,Boolean> map = new HashMap<>();
Boolean flag = employeeService.checkUsername(username);
map.put("valid",flag);
return map;
}
}
6 IEmployeeService接口
public interface IEmployeeService {
void save(Employee employee, Long[] roleIds);
void delete(Long id);
void update(Employee employee, Long[] roleIds);
Employee get(Long id);
List<Employee> listAll();
// 查询分页方法
PageInfo<Employee> query(QueryObject qo);
/**
* 检查用户名是否存在
* @param username
* @return
*/
Boolean checkUsername(String username);
/**
* 根据用户名密码进行登录
* @param username
* @param password
* @return
*/
Employee getByUsernameAndPassword(String username, String password);
}
7 EmployeeServiceImpl
@Service
public class EmployeeServiceImpl implements IEmployeeService {
@Autowired
private EmployeeMapper employeeMapper;
public void setEmployeeMapper(EmployeeMapper employeeMapper) {
this.employeeMapper = employeeMapper;
}
@Override
@Transactional
public void save(Employee employee, Long[] roleIds) {
// 添加逻辑判断
/*if(employee.getAge() > 60 || employee.getAge() < 18
|| !this.checkUsername(employee.getUsername())
|| employee.getName() == null){
throw new RuntimeException("非法操作");
}*/
// 新增员工的数据
employeeMapper.insert(employee);
// 维护中间表关系(超级管理员不用维护中间表关系)
if(!employee.isAdmin() && roleIds != null && roleIds.length > 0){
/*是否是超级管理员*/
employeeMapper.insertRelationBatch(employee.getId(),roleIds);
}
}
@Override
@Transactional
public void delete(Long id) {
// 先删中间表
employeeMapper.deleteRelation(id);
// 删除自身数据
employeeMapper.deleteByPrimaryKey(id);
}
@Override
@Transactional
public void update(Employee employee, Long[] roleIds) {
// 修改员工数据
employeeMapper.updateByPrimaryKey(employee);
// 维护中间表关系(先删后加)
employeeMapper.deleteRelation(employee.getId());
if(!employee.isAdmin() && roleIds != null && roleIds.length > 0){
employeeMapper.insertRelationBatch(employee.getId(),roleIds);
}
}
@Override
public Employee get(Long id) {
return employeeMapper.selectByPrimaryKey(id);
}
@Override
public List<Employee> listAll() {
return employeeMapper.selectAll();
}
@Override
public PageInfo<Employee> query(QueryObject qo) {
PageHelper.startPage(qo.getCurrentPage(),qo.getPageSize());
return new PageInfo<Employee>(employeeMapper.selectForList(qo));
}
@Override
public Boolean checkUsername(String username) {
Integer count = employeeMapper.checkUsername(username);
return count==0;
}
@Override
public Employee getByUsernameAndPassword(String username, String password) {
// 基于性能优化填写的代码
if(!StringUtil.isNotEmpty(username) || !StringUtil.isNotEmpty(password)){
throw new RuntimeException("账号密码有误!!!");
}
Employee employee = employeeMapper.getByUsernameAndPassword(username,password);
if(employee == null){
throw new RuntimeException("账号密码有误!!!");
}
return employee;
}
}
8 EmployeeQueryObject
// 过滤查询条件,过滤之后也要分页,所以要继承QueryObject (避免冗余字段)
@Getter
@Setter
public class EmployeeQueryObject extends QueryObject {
private String keyword;
private Long deptId;
}
9 QueryObject
// 分页查询条件
@Getter
@Setter
public class QueryObject {
private int currentPage = 1;
private int pageSize = 5;
public int getStart(){
return (currentPage - 1) * pageSize;
}
}
10 前端页面 – 员工
① list.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>员工管理</title>
</head>
<body class="hold-transition skin-black sidebar-mini">
<div th:replace="common/fragment :: link"></div>
<div class="wrapper">
<div th:replace="common/fragment :: navbar"></div>
<div th:replace="common/fragment :: menu"></div>
<div class="content-wrapper">
<section class="content-header">
<h1>员工管理</h1>
</section>
<section class="content">
<div class="box">
<!--高级查询--->
<div style="margin: 10px;">
<form class="form-inline" id="searchForm" action="/employee/list" method="post">
<input type="hidden" name="currentPage" id="currentPage" value="1">
<div class="form-group">
<!--关键字回显,注意是input 标签需要回显-->
<label for="keyword" >关键字:</label>
<input type="text" th:value="${qo.keyword}" class="form-control" id="keyword" name="keyword" placeholder="请输入姓名/邮箱">
</div>
<div class="form-group">
<label for="dept"> 部门:</label>
<!--下拉框回显 th:field 相当于 th:selected="${department.id==qo.deptId}",去所有的option的value中找deptId等于传入值的项进行回显-->
<select class="form-control" id="dept" name="deptId" th:field="${qo.deptId}">
<!--下拉框显示所有部门-->
<option value="">全部</option>
<option th:each="department:${departments}"
th:text="${department.name}"
th:value="${department.id}"></option>
</select>
</div>
<button type="submit" class="btn btn-primary"><span class="glyphicon glyphicon-search"></span> 查询</button>
<a href="/employee/input" class="btn btn-success btn-input">
<span class="glyphicon glyphicon-plus"></span> 添加
</a>
</form>
</div>
<div class="box-body table-responsive ">
<table class="table table-hover table-bordered table-striped">
<thead>
<tr>
<th><input type="checkbox" id="allCb"></th>
<th>编号</th>
<th>用户名</th>
<th>真实姓名</th>
<th>邮箱</th>
<th>年龄</th>
<th>部门</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr th:each="employee,start:${pageInfo.list}">
<td><input type="checkbox" class="cb"></td>
<td th:text="${start.count}">1</td>
<td th:text="${employee.username}">zs</td>
<td th:text="${employee.name}">张三</td>
<td th:text="${employee.email}">zs@126.com</td>
<td th:text="${employee.age}">21</td>
<td th:text="${employee?.dept?.name}">开发部</td>
<td>
<a th:href="|/employee/input?id=${employee.id}|" class="btn btn-info btn-xs btn_redirect">
<span class="glyphicon glyphicon-pencil"></span> 编辑
</a>
<a class="btn btn-danger btn-xs btn-delete" th:data-url="|/employee/delete?id=${employee.id}|">
<span class="glyphicon glyphicon-trash"></span> 删除
</a>
</td>
</tr>
</tbody>
</table>
<div th:replace="common/fragment :: page"></div>
</div>
</div>
</section>
</div>
<div th:replace="common/fragment :: footer"></div>
</div>
</body>
</html>
② input.html
超级管理员默认拥有所有的角色,所有的权限,当点击超级管理员时,下方的分配角色权限区域应被隐藏
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>员工管理</title>
</head>
<body class="hold-transition skin-black sidebar-mini">
<div th:replace="common/fragment :: link"></div>
<div class="wrapper">
<div th:replace="common/fragment :: navbar"></div>
<div th:replace="common/fragment :: menu"></div>
<div class="content-wrapper">
<section class="content-header">
<h1>员工编辑</h1>
</section>
<section class="content">
<div class="box">
<form class="form-horizontal" action="/employee/saveOrUpdate" method="post" id="editForm" >
<input type="hidden" name="id" th:value="${employee?.id}">
<div class="form-group" style="margin-top: 10px;">
<label class="col-sm-2 control-label">用户名:</label>
<div class="col-sm-6">
<input type="text" class="form-control" th:disabled="${employee != null}" th:value="${employee?.username}" name="username" placeholder="请输入用户名">
</div>
</div>
<div class="form-group" style="margin-top: 10px;">
<label class="col-sm-2 control-label" >真实姓名:</label>
<div class="col-sm-6">
<input type="text" class="form-control" name="name" th:value="${employee?.name}" placeholder="请输入真实姓名">
</div>
</div>
<div class="form-group" th:if="${employee == null}">
<label for="password" class="col-sm-2 control-label">密码:</label>
<div class="col-sm-6">
<input type="password" class="form-control" id="password" name="password" placeholder="请输入密码">
</div>
</div>
<div class="form-group" th:if="${employee == null}">
<label for="repassword" class="col-sm-2 control-label">验证密码:</label>
<div class="col-sm-6">
<input type="password" class="form-control" id="repassword" name="repassword" placeholder="再输入一遍密码">
</div>
</div>
<div class="form-group">
<label for="email" class="col-sm-2 control-label">电子邮箱:</label>
<div class="col-sm-6">
<input type="text" th:value="${employee?.email}" class="form-control" id="email" name="email" placeholder="请输入邮箱">
</div>
</div>
<div class="form-group">
<label for="age" class="col-sm-2 control-label">年龄:</label>
<div class="col-sm-6">
<input type="text" class="form-control" th:value="${employee?.age}" id="age" name="age" placeholder="请输入年龄">
</div>
</div>
<div class="form-group">
<label for="dept" class="col-sm-2 control-label">部门:</label>
<div class="col-sm-6">
<select class="form-control" id="dept" name="dept.id">
<!--遍历所有部门-->
<option value="">请选择</option>
<option th:each="department:${departments}"
th:text="${department.name}"
th:value="${department.id}"
th:selected="${department.id == employee?.dept?.id}"></option>
</select>
</div>
</div>
<div class="form-group" id="adminDiv">
<label for="admin" class="col-sm-2 control-label">超级管理员:</label>
<div class="col-sm-6" style="margin-left: 15px;">
<input type="checkbox" id="admin" th:checked="${employee?.admin}" name="admin" class="checkbox" >
</div>
</div>
<div class="form-group" id="roleDiv">
<label for="role" class="col-sm-2 control-label">分配角色:</label><br/>
<div class="row" style="margin-top: 10px">
<div class="col-sm-2 col-sm-offset-2">
<select multiple class="form-control allRoles" id="role" style="height: 350px;" size="15">
<!--遍历所有角色-->
<option th:each="role:${roles}"
th:text="${role.name}"
th:value="${role.id}"></option>
</select>
</div>
<div class="col-sm-1" style="margin-top: 60px;" align="center">
<div>
<a type="button" class="btn btn-primary " style="margin-top: 10px" title="右移动"
onclick="moveSelected('allRoles', 'selfRoles')">
<span class="glyphicon glyphicon-menu-right"></span>
</a>
</div>
<div>
<a type="button" class="btn btn-primary " style="margin-top: 10px" title="左移动"
onclick="moveSelected('selfRoles', 'allRoles')">
<span class="glyphicon glyphicon-menu-left"></span>
</a>
</div>
<div>
<a type="button" class="btn btn-primary " style="margin-top: 10px" title="全右移动"
onclick="moveAll('allRoles', 'selfRoles')">
<span class="glyphicon glyphicon-forward"></span>
</a>
</div>
<div>
<a type="button" class="btn btn-primary " style="margin-top: 10px" title="全左移动"
onclick="moveAll('selfRoles', 'allRoles')">
<span class="glyphicon glyphicon-backward"></span>
</a>
</div>
</div>
<div class="col-sm-2">
<select multiple class="form-control selfRoles" style="height: 350px;" size="15" name="roleIds">
<option th:each="role:${selfRoles}"
th:text="${role.name}"
th:value="${role.id}"></option>
</select>
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-1 col-sm-6">
<button type="submit" class="btn btn-primary btn-submit">保存</button>
<a href="javascript:window.history.back()" class="btn btn-danger">取消</a>
</div>
</div>
</form>
</div>
</section>
</div>
<div th:replace="common/fragment :: footer"></div>
</div>
<script>
// 全部左右移动
function moveAll(src, target) {
$('.' + target).append($('.' + src + ' > option'));
}
// 选中左右移动
function moveSelected(src, target) {
$('.' + target).append($('.' + src + ' > option:selected'));
}
// 点击保存按钮
/* $('.btn-submit').click(function () {
$('.selfRoles > option').prop('selected',true);
$('#editForm').submit();
})*/
$(function () {
// 下拉列表去重
var arr = $('.selfRoles > option').map(function (index, domEle) {
return $(domEle).val();
})
$('.allRoles > option').each(function (i, e) {
// 判断每次遍历的元素是否在上面的数组中出现过?
if($.inArray($(e).val(),arr) >= 0){
// 若存在则返回索引,说明重复了。我们要将该元素删除
$(e).remove();
}
})
// 回显时超级管理员则需要隐藏下面的 div
if($('#admin').prop('checked')){
// 是选中状态,说明是超级管理员,需要隐藏下面角色部分(添加隐藏属性)
$('#roleDiv').addClass('hidden');
} else {
// 不是选中状态,说明不是超级管理员,需要显示下面角色部分(移除隐藏属性)
$('#roleDiv').removeClass('hidden')
}
})
// 点击超级管理员按钮隐藏角色部分
$('#admin').click(function () {
// 判断勾选状态
if($(this).prop('checked')){
// 是选中状态,说明是超级管理员,需要隐藏下面角色部分
$('#roleDiv').addClass('hidden');
} else {
// 不是选中状态,说明不是超级管理员,需要显示下面角色部分
$('#roleDiv').removeClass('hidden')
}
})
</script>
<script>
$("#editForm").bootstrapValidator({/*找到表单 调用插件*/
feedbackIcons: { // 图标
valid: 'glyphicon glyphicon-ok',/*对号*/
invalid: 'glyphicon glyphicon-remove',/*错号*/
validating: 'glyphicon glyphicon-refresh'/*转圈*/
},
fields:{ // 配置要验证的字段 开始验证
username:{
validators:{ // 验证的规则
notEmpty:{ // 不能为空
message:"用户名必填" // 错误时的提示信息 若这一层没有message则向上找validators层没有则再次向上找message
},
stringLength: { // 字符串的长度范围
min: 1,
max: 10,
message:"用户名长度为1~5位"
},
regexp: {//正则表达式
regexp: /^[a-zA-Z0-9_\.]+$/,
message: '禁止使用非法字符'
},
remote:{//远程校验 验证用户是否存在 可以添加延迟减少访问数据库次数
type:'post',
url:'/employee/checkUsername',
message:'用户名已存在'
}
}
},
name:{
validators:{ // 验证的规则
notEmpty:{ // 不能为空
message:"姓名必填" // 错误时的提示信息
},
stringLength: { // 字符串的长度范围
min: 1,
max: 5,
message:"昵称长度为1~5位"
}
}
},
password:{
validators:{
notEmpty:{ // 不能为空
message:"密码必填" // 错误时的提示信息
},
different: {
field: 'username',
message: '用户名与密码不能相同'
}
}
},
repassword:{
validators:{
notEmpty:{ // 不能为空
message:"密码必填" // 错误时的提示信息
},
identical: {// 两个字段的值必须相同
field: 'password',
message: '两次输入的密码必须相同'
},
}
},
email: {
validators: {
emailAddress: {}, // 邮箱格式
message:"请输入正确邮箱"
}
},
age:{
validators: {
between: { // 数字的范围
min: 18,
max: 60,
message:"非法年龄范围"
}
}
}
}
}).on('success.form.bv', function(e) {
// 阻止表单提交 原先button的selected操作挪到了这里 依然选中后再提交 监听表单 点击submit才会进入到这里
e.preventDefault();
$('.selfRoles > option').prop('selected', 'true');
// TODO 这里可以改成用异步的方式提交表单
$.post(
"/employee/saveOrUpdate",
$('#editForm').serialize(),
/*通过serialize()获取表单数据将其转换为一条String语句*/
function (data) {/*只有状态码为200才能走进function 当200缺插入失败才叫插入失败 这里是成功之后的回调函数*/
if(data.success){
// 插入成功 展示页面
location.href='/employee/list'
} else {/*插入失败跳模式框*/
Swal.fire({
text: data.msg,
icon:'error',
confirmButtonText: '确定!',
});
}
}
)
});
</script>
</body>
</html>
11 员工管理小结
- 员工列表展示
- 后台代码已经实现需要修改 Controller Service Mapper中的分页。直接在前台 /employe/list.html将数据展示即可。
- 高级查询部分需要回显。
- 员工新增
- 当进入新增页面时,需要回显所有的部门及角色信息。
- 若为超级管理员则不需处理中间表的关系。同时隐藏角色的前台(分配角色DIV)。(超级管理员不需任何角色,权限,默认拥有所有权限)
- 员工编辑
- 用户名不能修改。所以动态使用 th:disabled=“${employee != null}”。需要注意当是 disabled 时后台获取不到数据。那么我们需要修改 SQL 语句中的update 删除修改 用户名部分
- 密码和重复密码不需要显示。th:if=“${employee==null}” 需要注意。这样后台获取的密码将是 null 那么我们需要修改 SQL 语句中的update 删除修改 密码部分
- 在页面渲染完成后,需要注意若为超级管理员。则隐藏角色部分的DIV
- 员工删除
- 给定 th:data-url 即可。