组织机构使用ztree插件,加载数据时使用数据权限过滤(只能访问登录用户的单位及其下属单位), 点击部门加载相应用户。
<!-- 数据范围过滤 --> BaseService.dataScopeFilter(user, "a", ""),
${sqlMap.dsf} 比如使用jn_jsb登录,生成的sqlMap.dsf 是 AND (a.id = '7' OR a.parent_ids LIKE '0,1,7,%' OR a.id IS NULL) ,
这个静态常量 DEL_FLAG_NORMAL 是在baseEntity里面, sqlMap 也是其中一个父类的属性。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<%@ page contentType="text/html;charset=UTF-8" %> <%@ include file="/WEB-INF/views/include/taglib.jsp"%> <html> <head> <title>用户管理</title> <meta name="decorator" content="default"/> <%@include file="/WEB-INF/views/include/treeview.jsp" %> <style type="text/css"> .ztree {overflow:auto;margin:0;_margin-top:10px;padding:10px 0 0 10px;} </style> </head> <body> <sys:message content="${message}"/> <div id="content" class="row-fluid"> <div id="left" class="accordion-group"> <div class="accordion-heading"> <a class="accordion-toggle">组织机构<i class="icon-refresh pull-right" onclick="refreshTree();"></i></a> </div> <div id="ztree" class="ztree"></div> </div> <div id="openClose" class="close"> </div> <div id="right"> <iframe id="officeContent" src="${ctx}/sys/user/list" width="100%" height="91%" frameborder="0"></iframe> </div> </div> <script type="text/javascript"> var setting = {data:{simpleData:{enable:true,idKey:"id",pIdKey:"pId",rootPId:'0'}}, //回调函数,树节点的点击事件 callback:{onClick:function(event, treeId, treeNode){ var id = treeNode.id == '0' ? '' :treeNode.id; $('#officeContent').attr("src","${ctx}/sys/user/list?office.id="+id+"&office.name="+treeNode.name); } } }; function refreshTree(){ $.getJSON("${ctx}/sys/office/treeData",function(data){ $.fn.zTree.init($("#ztree"), setting, data).expandAll(true); }); } refreshTree(); var leftWidth = 180; // 左侧窗口大小 var htmlObj = $("html"), mainObj = $("#main"); var frameObj = $("#left, #openClose, #right, #right iframe"); function wSize(){ var strs = getWindowSize().toString().split(","); htmlObj.css({"overflow-x":"hidden", "overflow-y":"hidden"}); mainObj.css("width","auto"); frameObj.height(strs[0] - 5); var leftWidth = ($("#left").width() < 0 ? 0 : $("#left").width()); $("#right").width($("#content").width()- leftWidth - $("#openClose").width() -5); $(".ztree").width(leftWidth - 10).height(frameObj.height() - 46); } </script> <script src="${ctxStatic}/common/wsize.min.js" type="text/javascript"></script> </body> </html>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/** * 获取机构JSON数据。 * @param extId 排除的ID * @param type 类型(1:公司;2:部门/小组/其它:3:用户) * @param grade 显示级别 * @param response * @return */ @RequiresPermissions("user") @ResponseBody @RequestMapping(value = "treeData") public List<Map<String, Object>> treeData(@RequestParam(required=false) String extId, @RequestParam(required=false) String type, @RequestParam(required=false) Long grade, @RequestParam(required=false) Boolean isAll, HttpServletResponse response) { List<Map<String, Object>> mapList = Lists.newArrayList(); List<Office> list = officeService.findList(isAll); for (int i=0; i<list.size(); i++){ Office e = list.get(i); if ((StringUtils.isBlank(extId) || (extId!=null && !extId.equals(e.getId()) && e.getParentIds().indexOf(","+extId+",")==-1)) && (type == null || (type != null && (type.equals("1") ? type.equals(e.getType()) : true))) && (grade == null || (grade != null && Integer.parseInt(e.getGrade()) <= grade.intValue())) && Global.YES.equals(e.getUseable())){ Map<String, Object> map = Maps.newHashMap(); map.put("id", e.getId()); map.put("pId", e.getParentId()); map.put("pIds", e.getParentIds()); map.put("name", e.getName()); if (type != null && "3".equals(type)){ map.put("isParent", true); } mapList.add(map); } } return mapList; } } ============================================= public List<Office> findList(Boolean isAll){ if (isAll != null && isAll){ return UserUtils.getOfficeAllList(); }else{ return UserUtils.getOfficeList(); } } =============================== /** * 获取当前用户有权限访问的部门 * @return */ public static List<Office> getOfficeList(){ @SuppressWarnings("unchecked") List<Office> officeList = (List<Office>)getCache(CACHE_OFFICE_LIST); if (officeList == null){ User user = getUser(); if (user.isAdmin()){ officeList = officeDao.findAllList(new Office()); }else{ Office office = new Office(); // AND (a.id = '7' OR a.parent_ids LIKE '0,1,7,%' OR a.id IS NULL) office.getSqlMap().put("dsf", BaseService.dataScopeFilter(user, "a", "")); officeList = officeDao.findList(office); } putCache(CACHE_OFFICE_LIST, officeList); } return officeList; } /** * 获取当前用户有权限访问的部门 * @return */ public static List<Office> getOfficeAllList(){ @SuppressWarnings("unchecked") List<Office> officeList = (List<Office>)getCache(CACHE_OFFICE_ALL_LIST); if (officeList == null){ officeList = officeDao.findAllList(new Office()); } return officeList; } ============================================ <select id="findList" resultType="Office"> SELECT <include refid="officeColumns"/> FROM sys_office a <include refid="officeJoins"/> WHERE a.del_flag = #{DEL_FLAG_NORMAL} <!-- 数据范围过滤 --> ${sqlMap.dsf} OR a.id = #{currentUser.office.id} ORDER BY a.code </select>
排序功能, 箭头上下切换,在userList.jsp 中,使用了一个tag标签实现此功能
<sys:tableSort id="orderBy" name="orderBy" value="${page.orderBy}" callback="page();"/>, orderBy作为参数把值(比如:login_name DESC)传入sql
<choose>
<when test="page !=null and page.orderBy != null and page.orderBy != ''">
ORDER BY ${page.orderBy}
</when>
<otherwise>
ORDER BY c.code, o.code, a.name
</otherwise>
</choose>
这个class 按tag规则使用<th class="sort-column login_name">登录名</th><th class="sort-column name">姓名</th>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<%@ tag language="java" pageEncoding="UTF-8"%> <%@ include file="/WEB-INF/views/include/taglib.jsp"%> <%@ attribute name="id" type="java.lang.String" required="true"%> <%@ attribute name="name" type="java.lang.String" required="true"%> <%@ attribute name="value" type="java.lang.String" required="true"%> <%@ attribute name="callback" type="java.lang.String" required="true"%> <input id="${id}" name="${name}" type="hidden" value="${value}"/> <%-- 使用方法: 1.将本tag写在查询的from里;2.在需要排序th列class上添加:sort-column + 排序字段名;3.后台sql添加排序引用page.orderBy;实例文件:userList.jsp、UserDao.xml --%> <script type="text/javascript"> $(document).ready(function() { var orderBy = $("#${id}").val().split(" "); $(".sort-column").each(function(){ if ($(this).hasClass(orderBy[0])){ orderBy[1] = orderBy[1]&&orderBy[1].toUpperCase()=="DESC"?"down":"up"; $(this).html($(this).html()+" <i class="icon icon-arrow-"+orderBy[1]+""></i>"); } }); $(".sort-column").click(function(){ var order = $(this).attr("class").split(" "); var sort = $("#${id}").val().split(" "); for(var i=0; i<order.length; i++){ if (order[i] == "sort-column"){order = order[i+1]; break;} } if (order == sort[0]){ sort = (sort[1]&&sort[1].toUpperCase()=="DESC"?"ASC":"DESC"); $("#${id}").val(order+" DESC"!=order+" "+sort?"":order+" "+sort); }else{ $("#${id}").val(order+" ASC"); } ${callback} }); }); </script>
Apache POI 3.9的简单封装,使用Annotation定义导出导入字段 ExcelExcel导入导出:http://thinkgem.iteye.com/blog/1833431
注意:导出字段名(默认调用当前字段的“get”方法,如指定导出字段为对象,请填写“对象名.对象属性”,例:“area.name”、“office.name”)
如果是字典类型,请设置字典的type值 ,如: dictType="sys_user_type" ,其他注解属性看代码内的注释。
表单字段校验采用的是 Hibernate Validation :http://www.cnblogs.com/afeng7882999/p/4300032.html
在bean中注解后,在页面对应是 (登录名: class="required userName"), htmlEscape="false" 表示不进行字符转义
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<%@ page contentType="text/html;charset=UTF-8" %> <%@ include file="/WEB-INF/views/include/taglib.jsp"%> <html> <head> <title>用户管理</title> <meta name="decorator" content="default"/> <script type="text/javascript"> $(document).ready(function() { $("#no").focus(); $("#inputForm").validate({ rules: { loginName: {remote: "${ctx}/sys/user/checkLoginName?oldLoginName=" + encodeURIComponent('${user.loginName}')} }, messages: { loginName: {remote: "用户登录名已存在"}, confirmNewPassword: {equalTo: "输入与上面相同的密码"} }, submitHandler: function(form){ loading('正在提交,请稍等...'); form.submit(); }, errorContainer: "#messageBox", errorPlacement: function(error, element) { $("#messageBox").text("输入有误,请先更正。"); if (element.is(":checkbox")||element.is(":radio")||element.parent().is(".input-append")){ error.appendTo(element.parent().parent()); } else { error.insertAfter(element); } } }); }); </script> </head> <body> <ul class="nav nav-tabs"> <li><a href="${ctx}/sys/user/list">用户列表</a></li> <li class="active"><a href="${ctx}/sys/user/form?id=${user.id}">用户<shiro:hasPermission name="sys:user:edit">${not empty user.id?'修改':'添加'}</shiro:hasPermission><shiro:lacksPermission name="sys:user:edit">查看</shiro:lacksPermission></a></li> </ul><br/> <form:form id="inputForm" modelAttribute="user" action="${ctx}/sys/user/save" method="post" class="form-horizontal"> <form:hidden path="id"/> <sys:message content="${message}"/> <div class="control-group"> <label class="control-label">头像:</label> <div class="controls"> <form:hidden id="nameImage" path="photo" htmlEscape="false" maxlength="255" class="input-xlarge"/> <sys:ckfinder input="nameImage" type="images" uploadPath="/photo" selectMultiple="false" maxWidth="100" maxHeight="100"/> </div> </div> <div class="control-group"> <label class="control-label">归属公司:</label> <div class="controls"> <sys:treeselect id="company" name="company.id" value="${user.company.id}" labelName="company.name" labelValue="${user.company.name}" title="公司" url="/sys/office/treeData?type=1" cssClass="required"/> </div> </div> <div class="control-group"> <label class="control-label">归属部门:</label> <div class="controls"> <sys:treeselect id="office" name="office.id" value="${user.office.id}" labelName="office.name" labelValue="${user.office.name}" title="部门" url="/sys/office/treeData?type=2" cssClass="required" notAllowSelectParent="true"/> </div> </div> <div class="control-group"> <label class="control-label">工号:</label> <div class="controls"> <form:input path="no" htmlEscape="false" maxlength="50" class="required"/> <span class="help-inline"><font color="red">*</font> </span> </div> </div> <div class="control-group"> <label class="control-label">姓名:</label> <div class="controls"> <form:input path="name" htmlEscape="false" maxlength="50" class="required"/> <span class="help-inline"><font color="red">*</font> </span> </div> </div> <div class="control-group"> <label class="control-label">登录名:</label> <div class="controls"> <input id="oldLoginName" name="oldLoginName" type="hidden" value="${user.loginName}"> <form:input path="loginName" htmlEscape="false" maxlength="50" class="required userName"/> <span class="help-inline"><font color="red">*</font> </span> </div> </div> <div class="control-group"> <label class="control-label">密码:</label> <div class="controls"> <input id="newPassword" name="newPassword" type="password" value="" maxlength="50" minlength="3" class="${empty user.id?'required':''}"/> <c:if test="${empty user.id}"><span class="help-inline"><font color="red">*</font> </span></c:if> <c:if test="${not empty user.id}"><span class="help-inline">若不修改密码,请留空。</span></c:if> </div> </div> <div class="control-group"> <label class="control-label">确认密码:</label> <div class="controls"> <input id="confirmNewPassword" name="confirmNewPassword" type="password" value="" maxlength="50" minlength="3" equalTo="#newPassword"/> <c:if test="${empty user.id}"><span class="help-inline"><font color="red">*</font> </span></c:if> </div> </div> <div class="control-group"> <label class="control-label">邮箱:</label> <div class="controls"> <form:input path="email" htmlEscape="false" maxlength="100" class="email"/> </div> </div> <div class="control-group"> <label class="control-label">电话:</label> <div class="controls"> <form:input path="phone" htmlEscape="false" maxlength="100"/> </div> </div> <div class="control-group"> <label class="control-label">手机:</label> <div class="controls"> <form:input path="mobile" htmlEscape="false" maxlength="100"/> </div> </div> <div class="control-group"> <label class="control-label">是否允许登录:</label> <div class="controls"> <form:select path="loginFlag"> <form:options items="${fns:getDictList('yes_no')}" itemLabel="label" itemValue="value" htmlEscape="false"/> </form:select> <span class="help-inline"><font color="red">*</font> “是”代表此账号允许登录,“否”则表示此账号不允许登录</span> </div> </div> <div class="control-group"> <label class="control-label">用户类型:</label> <div class="controls"> <form:select path="userType" class="input-xlarge"> <form:option value="" label="请选择"/> <form:options items="${fns:getDictList('sys_user_type')}" itemLabel="label" itemValue="value" htmlEscape="false"/> </form:select> </div> </div> <div class="control-group"> <label class="control-label">用户角色:</label> <div class="controls"> <form:checkboxes path="roleIdList" items="${allRoles}" itemLabel="name" itemValue="id" htmlEscape="false" class="required"/> <span class="help-inline"><font color="red">*</font> </span> </div> </div> <div class="control-group"> <label class="control-label">备注:</label> <div class="controls"> <form:textarea path="remarks" htmlEscape="false" rows="3" maxlength="200" class="input-xlarge"/> </div> </div> <c:if test="${not empty user.id}"> <div class="control-group"> <label class="control-label">创建时间:</label> <div class="controls"> <label class="lbl"><fmt:formatDate value="${user.createDate}" type="both" dateStyle="full"/></label> </div> </div> <div class="control-group"> <label class="control-label">最后登陆:</label> <div class="controls"> <label class="lbl">IP: ${user.loginIp} 时间:<fmt:formatDate value="${user.loginDate}" type="both" dateStyle="full"/></label> </div> </div> </c:if> <div class="form-actions"> <shiro:hasPermission name="sys:user:edit"><input id="btnSubmit" class="btn btn-primary" type="submit" value="保 存"/> </shiro:hasPermission> <input id="btnCancel" class="btn" type="button" value="返 回" onclick="history.go(-1)"/> </div> </form:form> </body> </html>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/** * Copyright © 2012-2016 <a href="https://github.com/thinkgem/jeesite">JeeSite</a> All rights reserved. */ package com.thinkgem.jeesite.modules.sys.entity; import java.util.Date; import java.util.List; import javax.validation.constraints.NotNull; import org.hibernate.validator.constraints.Email; import org.hibernate.validator.constraints.Length; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonIgnore; import com.google.common.collect.Lists; import com.thinkgem.jeesite.common.config.Global; import com.thinkgem.jeesite.common.persistence.DataEntity; import com.thinkgem.jeesite.common.supcan.annotation.treelist.cols.SupCol; import com.thinkgem.jeesite.common.utils.Collections3; import com.thinkgem.jeesite.common.utils.excel.annotation.ExcelField; import com.thinkgem.jeesite.common.utils.excel.fieldtype.RoleListType; /** * 用户Entity * @author ThinkGem * @version 2013-12-05 */ public class User extends DataEntity<User> { private static final long serialVersionUID = 1L; private Office company; // 归属公司 private Office office; // 归属部门 private String loginName;// 登录名 private String password;// 密码 private String no; // 工号 private String name; // 姓名 private String email; // 邮箱 private String phone; // 电话 private String mobile; // 手机 private String userType;// 用户类型 private String loginIp; // 最后登陆IP private Date loginDate; // 最后登陆日期 private String loginFlag; // 是否允许登陆 private String photo; // 头像 private String oldLoginName;// 原登录名 private String newPassword; // 新密码 private String oldLoginIp; // 上次登陆IP private Date oldLoginDate; // 上次登陆日期 private Role role; // 根据角色查询用户条件 private List<Role> roleList = Lists.newArrayList(); // 拥有角色列表 public User() { super(); this.loginFlag = Global.YES; } public User(String id){ super(id); } public User(String id, String loginName){ super(id); this.loginName = loginName; } public User(Role role){ super(); this.role = role; } public String getPhoto() { return photo; } public void setPhoto(String photo) { this.photo = photo; } public String getLoginFlag() { return loginFlag; } public void setLoginFlag(String loginFlag) { this.loginFlag = loginFlag; } @SupCol(isUnique="true", isHide="true") @ExcelField(title="ID", type=1, align=2, sort=1) public String getId() { return id; } @JsonIgnore @NotNull(message="归属公司不能为空") @ExcelField(title="归属公司", align=2, sort=20) public Office getCompany() { return company; } public void setCompany(Office company) { this.company = company; } @JsonIgnore @NotNull(message="归属部门不能为空") @ExcelField(title="归属部门", align=2, sort=25) public Office getOffice() { return office; } public void setOffice(Office office) { this.office = office; } @Length(min=1, max=100, message="登录名长度必须介于 1 和 100 之间") @ExcelField(title="登录名", align=2, sort=30) public String getLoginName() { return loginName; } public void setLoginName(String loginName) { this.loginName = loginName; } @JsonIgnore @Length(min=1, max=100, message="密码长度必须介于 1 和 100 之间") public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Length(min=1, max=100, message="姓名长度必须介于 1 和 100 之间") @ExcelField(title="姓名", align=2, sort=40) public String getName() { return name; } @Length(min=1, max=100, message="工号长度必须介于 1 和 100 之间") @ExcelField(title="工号", align=2, sort=45) public String getNo() { return no; } public void setNo(String no) { this.no = no; } public void setName(String name) { this.name = name; } @Email(message="邮箱格式不正确") @Length(min=0, max=200, message="邮箱长度必须介于 1 和 200 之间") @ExcelField(title="邮箱", align=1, sort=50) public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } @Length(min=0, max=200, message="电话长度必须介于 1 和 200 之间") @ExcelField(title="电话", align=2, sort=60) public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } @Length(min=0, max=200, message="手机长度必须介于 1 和 200 之间") @ExcelField(title="手机", align=2, sort=70) public String getMobile() { return mobile; } public void setMobile(String mobile) { this.mobile = mobile; } @ExcelField(title="备注", align=1, sort=900) public String getRemarks() { return remarks; } @Length(min=0, max=100, message="用户类型长度必须介于 1 和 100 之间") @ExcelField(title="用户类型", align=2, sort=80, dictType="sys_user_type") public String getUserType() { return userType; } public void setUserType(String userType) { this.userType = userType; } @ExcelField(title="创建时间", type=0, align=1, sort=90) public Date getCreateDate() { return createDate; } @ExcelField(title="最后登录IP", type=1, align=1, sort=100) public String getLoginIp() { return loginIp; } public void setLoginIp(String loginIp) { this.loginIp = loginIp; } @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @ExcelField(title="最后登录日期", type=1, align=1, sort=110) public Date getLoginDate() { return loginDate; } public void setLoginDate(Date loginDate) { this.loginDate = loginDate; } public String getOldLoginName() { return oldLoginName; } public void setOldLoginName(String oldLoginName) { this.oldLoginName = oldLoginName; } public String getNewPassword() { return newPassword; } public void setNewPassword(String newPassword) { this.newPassword = newPassword; } public String getOldLoginIp() { if (oldLoginIp == null){ return loginIp; } return oldLoginIp; } public void setOldLoginIp(String oldLoginIp) { this.oldLoginIp = oldLoginIp; } @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") public Date getOldLoginDate() { if (oldLoginDate == null){ return loginDate; } return oldLoginDate; } public void setOldLoginDate(Date oldLoginDate) { this.oldLoginDate = oldLoginDate; } public Role getRole() { return role; } public void setRole(Role role) { this.role = role; } @JsonIgnore @ExcelField(title="拥有角色", align=1, sort=800, fieldType=RoleListType.class) public List<Role> getRoleList() { return roleList; } public void setRoleList(List<Role> roleList) { this.roleList = roleList; } @JsonIgnore public List<String> getRoleIdList() { List<String> roleIdList = Lists.newArrayList(); for (Role role : roleList) { roleIdList.add(role.getId()); } return roleIdList; } public void setRoleIdList(List<String> roleIdList) { roleList = Lists.newArrayList(); for (String roleId : roleIdList) { Role role = new Role(); role.setId(roleId); roleList.add(role); } } /** * 用户拥有的角色名称字符串, 多个角色名称用','分隔. */ public String getRoleNames() { return Collections3.extractToString(roleList, "name", ","); } public boolean isAdmin(){ return isAdmin(this.id); } public static boolean isAdmin(String id){ return id != null && "1".equals(id); } @Override public String toString() { return id; } }
数据导入导出封装的比较好,贴个导入的方法,以前自己写的太差了,校验都是自己写,重复造轮子。
return "redirect:" + adminPath + "/sys/user/list?repage"; //传递 repage 参数,来记住页码,参考Page.java
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/** * 导入用户数据 * @param file * @param redirectAttributes * @return */ @RequiresPermissions("sys:user:edit") @RequestMapping(value = "import", method=RequestMethod.POST) public String importFile(MultipartFile file, RedirectAttributes redirectAttributes) { if(Global.isDemoMode()){ addMessage(redirectAttributes, "演示模式,不允许操作!"); return "redirect:" + adminPath + "/sys/user/list?repage"; } try { int successNum = 0; int failureNum = 0; StringBuilder failureMsg = new StringBuilder(); ImportExcel ei = new ImportExcel(file, 1, 0); List<User> list = ei.getDataList(User.class); for (User user : list){ try{ if ("true".equals(checkLoginName("", user.getLoginName()))){//检验用户名是否重复 user.setPassword(SystemService.entryptPassword("123456")); BeanValidators.validateWithException(validator, user); //JSR303 Validator(Hibernate Validator)工具类校验字段,不符合注解抛出异常 systemService.saveUser(user); successNum++; }else{ failureMsg.append("<br/>登录名 "+user.getLoginName()+" 已存在; "); failureNum++; } }catch(ConstraintViolationException ex){ failureMsg.append("<br/>登录名 "+user.getLoginName()+" 导入失败:"); List<String> messageList = BeanValidators.extractPropertyAndMessageAsList(ex, ": "); for (String message : messageList){ failureMsg.append(message+"; "); failureNum++; } }catch (Exception ex) { failureMsg.append("<br/>登录名 "+user.getLoginName()+" 导入失败:"+ex.getMessage()); } } if (failureNum>0){ failureMsg.insert(0, ",失败 "+failureNum+" 条用户,导入信息如下:"); } addMessage(redirectAttributes, "已成功导入 "+successNum+" 条用户"+failureMsg); } catch (Exception e) { addMessage(redirectAttributes, "导入用户失败!失败信息:"+e.getMessage()); } return "redirect:" + adminPath + "/sys/user/list?repage"; //传递 repage 参数,来记住页码,参考Page.java }