- 浏览: 70202 次
- 性别:
- 来自: 绍兴
-
最新评论
这个功能的思路是自己原创的,没看过其他例子,其实是在做之前网上搜了很多也没个可以参考的例子.主要技术是后台springsecurity做拦截判断,前台Extjs4做按钮动态渲染.如果你不了解springsecrity,那你可以先看下我的另一篇相关博客
创建数据库表(liqiubase方式创建看上一篇)
这张是权限表,也是菜单(按钮)表,每个菜单都有他自己需要访问链接的权限,当菜单类型为子菜单或按钮时还需要配置该按钮对应的js文件,对没错,每个按钮都有自己独立的js文件,在用户没有按钮权限的时候,前端是js都不需要加载的
添加菜单初始数据
添加了一个父级菜单用户管理,两个子集菜单账号管理和角色管理,同时在账号管理下添加两个按钮新增和修改,需要注意的是新增的授权链接是account!save.action而修改的授权链接是account!save.action,account!find.action,因为修改需要先查再能改,当用户被授予新增和修改两个权限的时候,访问account!save.action,account!find.action也就不会受阻了
创建角色表和角色权限关联表
角色和权限是多对多的关系,一个角色可以有多个权限,一个权限也可以属于多个角色,
配完初始数据以后前台展示大致是这样的(图片太大请在新窗口打开)
拥有角色管理的的用户可以添加删除角色,同时可以为没每个角色分配权限
创建用户角色关联表
这里给admin初始化了id为1的角色,也就是超级管理员,同样
拥有账号管理里的用户可以为账号添加角色
到这里数据的配置基本讲完了,再展示下初始数据的liqiubase文件,大致就是那几张表,
最后我再给游客2分配了几个权限,新增一个角色为游客2的账号
以上展示的只是数据配置的过程,通俗的讲就是想法,
现在想法已经出来了,权限也分配好了真正的权限控制核心代码就开始了,首先展示下我的hibernate entity
Account作为springsecrity的账户类必须实现UserDetails接口,实现的getAuthorities方法是告诉spring当前用户的角色,还有个自定义方法getSignPer用于获取当前用户的权限列表,Permission类有个方法getControlTypeByDg通过配置的jsUrl链接获取Extjs的组件类名,他们的用处后面就能看到,这里补充下我用的是Hibernate的注解方式,严格来讲项目中除了公共配置部分,其他包括Struts Spring等需要xml配置的我全用了注解,如果你对注解不是很了解用xml配置的形式也是可以的
用户通过springsecrity登录成功的时候,会把Account对象保存它的上下文,然后访问这个跳转到主页的action方法,action方法中从springsecrity上下文中获取到Account后通过getSignPer方法获取当前用户的权限(也就是菜单),下面就是用户cccq3y的菜单了
当用户点击新增确认按钮时,浏览器会向后台提交一个表单,url为account!save.action,首先会被springsecrity的MyFilterInvocationSecurityMetadataSource拦截到
返回的角色中应该有一个是游客2的id,然后再被MyAccessDecisionManager拦截到
注释写个很清楚了,把当前用户所拥有的角色和MyFilterInvocationSecurityMetadataSource返回的访问当前链接需要哪些角色,如果包含说明当前用户能访问该链接了,再没抛AccessDeniedException错误的情况下验证都通过
最后再说下前端代码,首先主页的jsp要根据权限加载js文件
然后来感受下extjs4的强大之处了,首先我封装一个公共的权限工具条
然后是grid基类
把当前组件的xtype作为参数,到后台获取对应的父级菜单,再根据用户拥有的按钮权限获取这个用户在当前组件的按钮权限,那么问题来了,我是如果通过xtype定位到那条权限数据的呢,真相是我在配置账号管理这个权限的时候配置的jsUrl为user/account/searchGrid然后通过上面Permission类自定义的getControlTypeByDg方法转成xtype,再来看看我的grid子类就清楚了
alias:widget.userAccountSearchGrid,这样就能和后台的权限做对应了,而且在点击菜单树的时候同样用到这个功能,我会根据当前被点击菜单的jsUrl属性转义成xtype,然后很方便的加载对应的组件,就这么简单.最后展示下我的js文件目录
写到这里差不多就结束了,写的有点乱,看不懂的可以加我qq探讨一下;
创建数据库表(liqiubase方式创建看上一篇)
<createTable tableName="permissions"> <column name="id" type="bigint"> <constraints nullable="false" primaryKey="true" /> </column> <!-- 权限名称 --> <column name="name" type="varchar(32)" /> <!-- 该权限需要访问的链接 --> <column name="actionurl" type="varchar(256)" /> <!-- 该权限需要用到的js --> <column name="jsurl" type="varchar(256)" /> <!-- 权限等级 1主菜单 2子菜单 3及以下按钮 --> <column name="level" type="int" /> <!-- 菜单父级id --> <column name="parentid" type="bigint"/> <!-- 排序编号 --> <column name="ordernum" type="int" defaultValue="0"/> </createTable>
这张是权限表,也是菜单(按钮)表,每个菜单都有他自己需要访问链接的权限,当菜单类型为子菜单或按钮时还需要配置该按钮对应的js文件,对没错,每个按钮都有自己独立的js文件,在用户没有按钮权限的时候,前端是js都不需要加载的
添加菜单初始数据
<insert tableName="permissions"> <column name="id" value="1"/> <column name="name" value="用户管理" /> <column name="level" value="1" /> <column name="parentid" value="-1"/> <column name="ordernum" value="1"/> </insert> <insert tableName="permissions"> <column name="id" value="101"/> <column name="name" value="账号管理" /> <column name="actionurl" value="account!query.action" /> <column name="jsurl" value="user/account/searchGrid" /> <column name="level" value="2" /> <column name="parentid" value="1"/> <column name="ordernum" value="1"/> </insert> <insert tableName="permissions"> <column name="id" value="102"/> <column name="name" value="角色管理" /> <column name="actionurl" value="" /> <column name="jsurl" value="user/role/mainPanel" /> <column name="level" value="2" /> <column name="parentid" value="1"/> <column name="ordernum" value="2"/> </insert> <insert tableName="permissions"> <column name="id" value="1001"/> <column name="name" value="新增" /> <column name="actionurl" value="account!save.action" /> <column name="jsurl" value="user/account/searchGridAddBtn" /> <column name="level" value="3" /> <column name="parentid" value="101"/> <column name="ordernum" value="1"/> </insert> <insert tableName="permissions"> <column name="id" value="1004"/> <column name="name" value="修改" /> <column name="actionurl" value="account!save.action,account!find.action" /> <column name="jsurl" value="user/account/searchGridUpdBtn" /> <column name="level" value="3" /> <column name="parentid" value="101"/> <column name="ordernum" value="1"/> </insert>
添加了一个父级菜单用户管理,两个子集菜单账号管理和角色管理,同时在账号管理下添加两个按钮新增和修改,需要注意的是新增的授权链接是account!save.action而修改的授权链接是account!save.action,account!find.action,因为修改需要先查再能改,当用户被授予新增和修改两个权限的时候,访问account!save.action,account!find.action也就不会受阻了
创建角色表和角色权限关联表
<createTable tableName="roles"> <column name="id" type="bigint"> <constraints nullable="false" primaryKey="true" /> </column> <column name="rolename" type="varchar(32)" /> </createTable> <insert tableName="roles"> <column name="id" value="1" /> <column name="rolename" value="超级管理员" /> </insert> <createTable tableName="role_permission"> <column name="id" type="bigint"> <constraints nullable="false" primaryKey="true" /> </column> <column name="perid" type="bigint" /> <column name="roleid" type="bigint" /> </createTable>
角色和权限是多对多的关系,一个角色可以有多个权限,一个权限也可以属于多个角色,
配完初始数据以后前台展示大致是这样的(图片太大请在新窗口打开)

拥有角色管理的的用户可以添加删除角色,同时可以为没每个角色分配权限
创建用户角色关联表
<createTable tableName="account_role"> <column name="accountid" type="bigint" /> <column name="roleid" type="bigint" /> </createTable> <insert tableName="account_role"> <column name="accountid" value="1" /> <column name="roleid" value="1" /> </insert>
这里给admin初始化了id为1的角色,也就是超级管理员,同样
拥有账号管理里的用户可以为账号添加角色

到这里数据的配置基本讲完了,再展示下初始数据的liqiubase文件,大致就是那几张表,
最后我再给游客2分配了几个权限,新增一个角色为游客2的账号



以上展示的只是数据配置的过程,通俗的讲就是想法,
现在想法已经出来了,权限也分配好了真正的权限控制核心代码就开始了,首先展示下我的hibernate entity
package cn.sdh.entity; import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.SequenceGenerator; import javax.persistence.Table; import javax.persistence.Transient; import org.apache.struts2.json.annotations.JSON; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.GrantedAuthorityImpl; import org.springframework.security.core.userdetails.UserDetails; import cn.sdh.common.base.AuditEntity; @SuppressWarnings("serial") @Entity @Table(name="accounts") public class Account extends AuditEntity implements UserDetails { public Account(){ } public Account(Long id, String username, String password, String email,Date lastLoginTime, int userType){ this.id = id; this.username = username; this.password = password; this.email = email; this.lastLoginTime = lastLoginTime; this.userType = userType; } @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_ACCOUNT") @SequenceGenerator(name = "SEQ_ACCOUNT", sequenceName = "SEQ_ACCOUNT", initialValue = 2, allocationSize = 1) @Column(name="id") private Long id; @Column(name="username") private String username; @Column(name="password") private String password; @Column(name="email") private String email; //@Type(type="cn.sdh.common.userType.dateToInt") @Column(name="lastlogintime") private Date lastLoginTime; private int userType;//用户类型-1:超级管理员 0普通用户 1+级用户 //是否在线 @Transient private int isOnLine; //搜索时间 起止 @Transient private Date lastLoginTime1; @Transient private Date lastLoginTime2; @ManyToMany(fetch = FetchType.EAGER) @JoinTable(name="account_role",joinColumns={@JoinColumn(name="accountid")},inverseJoinColumns={@JoinColumn(name="roleid")}) private List<Role> roles = new ArrayList<Role>(); @Transient private File photo; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public List<Role> getRoles() { return roles; } public void setRoles(List<Role> roles) { this.roles = roles; } public Date getLastLoginTime() { return lastLoginTime; } public void setLastLoginTime(Date lastLoginTime) { this.lastLoginTime = lastLoginTime; } /** * 告诉spring当前用户的角色 */ @Override @JSON(serialize=false) public Collection<GrantedAuthority> getAuthorities() { Set<GrantedAuthority> authSet = new HashSet<GrantedAuthority>(); for (Role role : this.getRoles()) { authSet.add(new GrantedAuthorityImpl(role.getId().toString())); } return authSet; } @Override @JSON(serialize=false) public boolean isAccountNonExpired() { return true; } @Override @JSON(serialize=false) public boolean isAccountNonLocked() { return true; } @Override @JSON(serialize=false) public boolean isCredentialsNonExpired() { return true; } @Override @JSON(serialize=false) public boolean isEnabled() { return true; } public Date getLastLoginTime1() { return lastLoginTime1; } public void setLastLoginTime1(Date lastLoginTime1) { this.lastLoginTime1 = lastLoginTime1; } public Date getLastLoginTime2() { return lastLoginTime2; } public void setLastLoginTime2(Date lastLoginTime2) { this.lastLoginTime2 = lastLoginTime2; } public int getUserType() { return userType; } public void setUserType(int userType) { this.userType = userType; } public int getIsOnLine() { return isOnLine; } public void setIsOnLine(int isOnLine) { this.isOnLine = isOnLine; } @JSON(serialize=false) public List<Permission> getSignPer(){ List<Permission> perList = new ArrayList<Permission>(); for(Role role : this.getRoles()){ List<Permission> sublist = role.getPermissionList(); for(int h = 0; h<sublist.size(); h++){ boolean continueFlag = false; for(int j = 0;j<perList.size();j++){ if(role.getPermissionList().get(h).getId()==perList.get(j).getId()){ continueFlag = true; break; } } if(continueFlag){ continue; } if(role.getPermissionList().get(h).getLevel()==2){ role.getPermissionList().get(h).setLeaf(1); } role.getPermissionList().get(h).setChildren(null); //role.getPermissionList().get(h).setControlType(role.getPermissionList().get(h).getControlType()); perList.add(role.getPermissionList().get(h)); } } return perList; } public File getPhoto() { return photo; } public void setPhoto(File photo) { this.photo = photo; } }
package cn.sdh.entity; import java.util.List; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.OneToMany; import javax.persistence.Table; import javax.persistence.Transient; import org.apache.struts2.json.annotations.JSON; import cn.sdh.common.base.AuditEntity; /** * 权限类(菜单,按钮,功能) * @author 孙东辉 */ @Entity @Table(name="permissions") public class Permission extends AuditEntity{ /** * */ private static final long serialVersionUID = 7930766554705439940L; @Id @Column(name="id") private Long id; @Column(name="name") private String name; /** * 访问地址 */ @Column(name="actionurl") private String actionUrl; /** * js文件地址 */ @Column(name="jsurl") private String jsUrl; /** * 等级 1-N */ @Column(name="level") private int level; /** * 父级编号 */ @Column(name="parentid") private Long parentid; /** * 弹出的控件类 */ @Transient private String controlType; @Transient private int leaf; @Transient private boolean checked; @Transient private boolean expanded = true; @OneToMany(fetch=FetchType.EAGER) @JoinColumn(name = "parentid", referencedColumnName="id",insertable=false,updatable=false) private List<Permission> children; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getActionUrl() { return actionUrl; } public void setActionUrl(String actionUrl) { this.actionUrl = actionUrl; } public String getJsUrl() { return jsUrl; } public void setJsUrl(String jsUrl) { this.jsUrl = jsUrl; } public int getLevel() { return level; } public void setLevel(int level) { this.level = level; } public Long getParentid() { return parentid; } public void setParentid(Long parentid) { this.parentid = parentid; } @JSON(name="text") public String getName() { return name; } public void setName(String name) { this.name = name; } public String getControlType() { if(this.jsUrl!=null) return getControlTypeByDg(this.jsUrl); else return this.controlType; } public void setControlType(String controlType) { this.controlType = controlType; } public int getLeaf() { if(this.level==3){ return 1; } return this.leaf; } public void setLeaf(int leaf) { this.leaf = leaf; } public boolean isChecked() { return checked; } public void setChecked(boolean checked) { this.checked = checked; } public List<Permission> getChildren() { return children; } public void setChildren(List<Permission> children) { this.children = children; } public boolean isExpanded() { return expanded; } public void setExpanded(boolean expanded) { this.expanded = expanded; } /** * 通过路径获得组件名称 * @param path * @return */ public String getControlTypeByDg(String path){ if(path.indexOf("/")>=0){ path = path.replaceFirst(path.substring(path.indexOf("/"),path.indexOf("/")+2), path.substring(path.indexOf("/")+1,path.indexOf("/")+2).toUpperCase()); return getControlTypeByDg(path); }else{ return path; } } }
package cn.sdh.entity; import java.util.ArrayList; import java.util.List; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.OrderBy; import javax.persistence.SequenceGenerator; import javax.persistence.Table; import org.hibernate.annotations.Fetch; import org.hibernate.annotations.FetchMode; import cn.sdh.common.base.AuditEntity; /** * 角色类 * @author 孙东辉 */ @Entity @Table(name="roles") public class Role extends AuditEntity{ /** * */ private static final long serialVersionUID = 5459879676154477558L; @Id @Column(name="id") @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_ROLE") @SequenceGenerator(name = "SEQ_ROLE", sequenceName = "SEQ_ROLE", initialValue = 100, allocationSize = 1) private Long id; /** * 角色名称 */ @Column(name="rolename") private String roleName; /** * 拥有的权限集合 */ @ManyToMany(fetch = FetchType.EAGER) @Fetch(FetchMode.SUBSELECT) @OrderBy("level") @JoinTable(name="role_permission",joinColumns={@JoinColumn(name="roleid")},inverseJoinColumns={@JoinColumn(name="perid")}) private List<Permission> permissionList = new ArrayList<Permission>(); public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getRoleName() { return roleName; } public void setRoleName(String roleName) { this.roleName = roleName; } public List<Permission> getPermissionList() { return permissionList; } public void setPermissionList(List<Permission> permissionList) { this.permissionList = permissionList; } }
Account作为springsecrity的账户类必须实现UserDetails接口,实现的getAuthorities方法是告诉spring当前用户的角色,还有个自定义方法getSignPer用于获取当前用户的权限列表,Permission类有个方法getControlTypeByDg通过配置的jsUrl链接获取Extjs的组件类名,他们的用处后面就能看到,这里补充下我用的是Hibernate的注解方式,严格来讲项目中除了公共配置部分,其他包括Struts Spring等需要xml配置的我全用了注解,如果你对注解不是很了解用xml配置的形式也是可以的
用户通过springsecrity登录成功的时候,会把Account对象保存它的上下文,然后访问这个跳转到主页的action方法,action方法中从springsecrity上下文中获取到Account后通过getSignPer方法获取当前用户的权限(也就是菜单),下面就是用户cccq3y的菜单了
/** * 跳转到主页 * * @throws JSONException */ public String index() throws JSONException { entity = SpringSecurityUtils.getCurrentUser(); List<Permission> perList = entity.getSignPer(); HttpServletRequest request = ServletActionContext.getRequest(); request.setAttribute("perList", perList); request.setAttribute("perListJson", JSONUtil.serialize(perList)); return "index"; }

当用户点击新增确认按钮时,浏览器会向后台提交一个表单,url为account!save.action,首先会被springsecrity的MyFilterInvocationSecurityMetadataSource拦截到
package cn.sdh.audit; import java.util.Collection; import java.util.HashSet; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.ConfigAttribute; import org.springframework.security.access.SecurityConfig; import org.springframework.security.web.FilterInvocation; import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource; import org.springframework.stereotype.Service; import cn.sdh.common.exception.ServiceException; import cn.sdh.entity.Permission; import cn.sdh.entity.Role; import cn.sdh.service.RoleService; import cn.sdh.utils.MyCacheUtil; /** * 告诉spring当前访问链接 需要什么权限 在这边定义 * @author 孙东辉 * */ @Service public class MyFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource { @Autowired private RoleService roleService; public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException { //这个是当前访问的链接 String requestUrl = ((FilterInvocation) object).getRequestUrl(); //设置哪些页面不需要权限就可访问 if(requestUrl.indexOf("account!index.action") >= 0 || requestUrl.indexOf("index.jsp") >= 0 || requestUrl.indexOf("permission!queryBtnListByControlType.action") >= 0 || requestUrl.indexOf("account!searchOnlineList.action")>=0 || requestUrl.indexOf("queryDicDataByKey.action")>=0){ return null; } // 对于带参数的请求,截取?前面的uri int urlIndex = requestUrl.indexOf("?"); if (urlIndex != -1) { requestUrl = requestUrl.substring(0, urlIndex); } //如果第一位是/ 去掉 if(requestUrl.startsWith("/")){ requestUrl = requestUrl.substring(1); } //从缓存中取当前系统的所有角色 List<Role> roles = (List<Role>) MyCacheUtil.getRolesList(); if(roles == null || roles.size()==0){//如果缓存中没有 try { roles = roleService.query(); //把角色放入缓存 MyCacheUtil.setRolesList(roles); } catch (ServiceException e) { throw new IllegalArgumentException(e.getMsg()); } } Collection<ConfigAttribute> c = new HashSet<ConfigAttribute>(); //遍历所有角色 List<Permission> permissionList = null; if(roles.size()>0){ for(int i = 0;i<roles.size();i++){ permissionList = roles.get(i).getPermissionList(); for(int j = 0;j<permissionList.size();j++){ //如果权限中的actionUrl包含当前访问的链接,当说明现在这个角色是可以访问该链接的 if(permissionList.get(j)!=null&&permissionList.get(j).getActionUrl()!=null&&permissionList.get(j).getActionUrl().indexOf(requestUrl)>=0){ ConfigAttribute configAttribute = new SecurityConfig(roles.get(i).getId()+""); c.add(configAttribute); break; } } } }else{ return null; } //最后返回的是一个角色的集合,并且这些角色都是可以访问该链接的 return c; } public Collection<ConfigAttribute> getAllConfigAttributes() { return null; } public boolean supports(Class<?> clazz) { return true; } }
返回的角色中应该有一个是游客2的id,然后再被MyAccessDecisionManager拦截到
package cn.sdh.audit; import java.util.Collection; import java.util.Iterator; import org.springframework.security.access.AccessDecisionManager; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.access.ConfigAttribute; import org.springframework.security.access.SecurityConfig; import org.springframework.security.authentication.InsufficientAuthenticationException; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; /** * 自定义访问决定器 控制用户能否访问该链接 * @author sun * */ public class MyAccessDecisionManager implements AccessDecisionManager { public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException { //如果访问的页面没有权限限制 那么不做拦截继续访问 如果未空说明该页面不需要任何角色 // 注意这里的configAttributes 其实就是MyFilterInvocationSecurityMetadataSource返回的 if (configAttributes == null) { return; } //如果有权限限制 那么需要迭代权限 验证用户所拥有的权限是否包含 当前页面的访问权限 Iterator<ConfigAttribute> ite = configAttributes.iterator(); while (ite.hasNext()) { //获取访问页面需要的权限 ConfigAttribute ca = ite.next(); //获取访问页面需要的权限的权限名称 String needRole = ((SecurityConfig) ca).getAttribute(); //迭代用户所拥有的权限 for (GrantedAuthority ga : authentication.getAuthorities()) { //如果用户的其中一个权限满足 访问链接需要的其中一个权限,那么访问将继续 if (needRole.equals(ga.getAuthority())) { return; } } } //否则 访问将被终止 throw new AccessDeniedException("没有权限访问"); } public boolean supports(ConfigAttribute attribute) { // TODO Auto-generated method stub return true; } public boolean supports(Class<?> clazz) { // TODO Auto-generated method stub return true; } }
注释写个很清楚了,把当前用户所拥有的角色和MyFilterInvocationSecurityMetadataSource返回的访问当前链接需要哪些角色,如果包含说明当前用户能访问该链接了,再没抛AccessDeniedException错误的情况下验证都通过
最后再说下前端代码,首先主页的jsp要根据权限加载js文件
<!-- 根据权限加载js文件 --> <s:iterator value="#request.perList" var="bean"> <s:if test="#bean.jsUrl!=null"> <script type="text/javascript" src="common/jscode/<s:property value="#bean.jsUrl"/>.js"></script> </s:if> </s:iterator>
然后来感受下extjs4的强大之处了,首先我封装一个公共的权限工具条
/** * 权限按钮工具条 */ Ext.define("app.base.auditbar", { extend : "Ext.toolbar.Toolbar", alias : 'widget.auditbar', initComponent : function() { var me = this; var call = function(){ var sme = me; // 根据组件类型 获取它所有的按钮 Ext.Ajax.request({ url : 'permission!queryBtnListByControlType.action', params : { 'entity.controlType' : me.compont }, success : new app.ajaxHand({ success : function(result,scope) { var btns = result.searchList; for ( var i = 0; i < btns.length; i++) { if(scope){ sme = scope; } sme.insert(i, [ { xtype : btns[i].controlType, text : btns[i].text } ]); } } },sme) }); }; if(me.remote){ call(); } if(me.searchItems){ if(!me.items){ me.items = []; } me.items =me.items.concat(['->', { xtype : "searchItems", items : me.searchItems } ]); } me.callParent(me); } });
然后是grid基类
Ext.define("app.base.grid", { extend : "Ext.grid.Panel", //日期 dateRender : function(){return Ext.util.Format.dateRenderer(app.constant.date_render);}, //日期加时间 datetimeRender : function(){return Ext.util.Format.dateRenderer(app.constant.datetime_render);}, //单元格编辑插件 editingPlugin : function(){ return Ext.create('Ext.grid.plugin.CellEditing',{});}, //行编辑插件 rowEditingPlugin : function(){return Ext.create('Ext.grid.plugin.RowEditing', { clicksToMoveEditor: 1, errorSummary : false, autoCancel: true });}, //行复选框 selModelPlugin : function(){return Ext.create("Ext.selection.CheckboxModel");}, initComponent : function(){ var me = this; if(me.paging){ me.bbar = Ext.create('app.base.paging',{ store:me.store }); } me.tbar = Ext.create('app.base.auditbar',{ compont : me.xtype, searchItems : me.searchItems, remote:me.auditbarEnable, items : me.btns }); this.callParent(); }, //添加行 addRow : function(){ this.store.insert(0,new (Ext.ModelManager.getModel(this.model))); this.editing.cancelEdit(); if(this.editing){ this.editing.startEdit(0,0); } }, //删除行 deleteRow : function(){ var recode = this.getSelectionModel().getSelection(); this.store.remove(recode); }, //验证选中行 checkSelectRow : function(count,tip){ if(!count){ count = 1; } tip = tip || "请"+(count!=1?"至少":"")+"选择一行要删除的记录"; var recode = this.getSelectionModel().getSelection(); if(recode.length==0||(count==1&&recode.length>1)){ app.alert(tip); return false; } var usedata = []; var ids = ""; for(var i = 0;i<recode.length;i++){ usedata.push(recode[i].data); ids += recode[i].data.id; if(i!=recode.length-1){ ids += ","; } } usedata.unshift(ids); return usedata; } });
把当前组件的xtype作为参数,到后台获取对应的父级菜单,再根据用户拥有的按钮权限获取这个用户在当前组件的按钮权限,那么问题来了,我是如果通过xtype定位到那条权限数据的呢,真相是我在配置账号管理这个权限的时候配置的jsUrl为user/account/searchGrid然后通过上面Permission类自定义的getControlTypeByDg方法转成xtype,再来看看我的grid子类就清楚了
Ext.define("app.user.account.searchGrid", { extend : "app.user.account.grid", alias: 'widget.userAccountSearchGrid', initComponent : function(){ var me = this; me.editing = me.rowEditingPlugin(); Ext.apply(this,{ plugins : [me.editing], paging : true, selModel : me.selModelPlugin(), searchItems :[{ fieldLabel : '用户名', xtype : 'textfield', labelAlign : 'right', name : 'entity.username', labelWidth : 45 },{ fieldLabel : '邮箱', xtype : 'textfield', labelAlign : 'right', name : 'entity.email', labelWidth : 45 },{ fieldLabel : '上次登录起', xtype : 'datefield', labelAlign : 'right', name : 'entity.lastLoginTime1', labelWidth : 75 },{ fieldLabel : '上次登录止', xtype : 'datefield', labelAlign : 'right', name : 'entity.lastLoginTime2', labelWidth : 75 }] }); me.callParent(); } });
alias:widget.userAccountSearchGrid,这样就能和后台的权限做对应了,而且在点击菜单树的时候同样用到这个功能,我会根据当前被点击菜单的jsUrl属性转义成xtype,然后很方便的加载对应的组件,就这么简单.最后展示下我的js文件目录

写到这里差不多就结束了,写的有点乱,看不懂的可以加我qq探讨一下;
发表评论
-
Web开发学习13聊聊java反射
2016-07-12 18:41 780很喜欢一本叫《走出软件作坊》的书,其中有一句话让我较为深刻,“ ... -
Web开发学习12 浅谈设计模式
2016-07-01 13:33 1336在我刚接触 ... -
Web开发学习11 全局缓存控制
2015-10-12 14:27 756缓存用的好可以减少数据库的压力,在大并发的情况下极大的提升服务 ... -
Web开发学习10 hadoop实战
2015-10-09 14:02 780前一篇说到项目记录了很多埋点日志,当有一天项目需求需要对这些日 ... -
Web开发学习(9)全局埋点
2015-07-27 11:10 2049埋点的作用是把客户端每次访问服务端的操作记录下来,包括请求连接 ... -
Web开发学习8Struts2基类封装
2015-07-24 12:03 721一个好的基类可以帮助开发者减少很多开发工作,像我这种爱偷懒的人 ... -
Web开发学习6添加liqiubase
2015-07-22 14:29 1504开发过程中经常碰到数据库更改的情况,在日常环境下如果每位开发人 ... -
浅列JavaEnum
2014-01-05 16:42 804enum Province{ ZHEJIANG,SHANGHA ... -
maven常用命令
2013-07-08 10:20 8041、创建普通Java项目: ... -
canvas绘制科赫雪花
2012-12-20 17:24 2177<html> <head> ... -
html5 audio与video方法属性事件概括
2012-12-20 14:46 1138play() 继续播放 pause() ... -
spring 整合mina
2012-12-20 11:48 1136首先定义自定义过滤器 <bean id=" ... -
Web开发学习(5)添加springsecurity应用
2011-12-14 15:00 999我学习公司这个架构多半就是为了把springsecurity搞 ... -
Web开发学习(4)添加spring应用
2011-12-14 14:45 812印象中似乎没做过不用spring的项目,因为它在web开发中的 ... -
Web开发学习(3)配置struts-tiles插件(附带json插件)
2011-12-05 15:22 2591struts的tiles插件是非常实用的,从某些方面可以很好的 ... -
使用线程删除导出临时文件
2011-12-01 16:33 2295项目支持大数据量导出e ... -
jQuery实现表格行点击选中复选框
2011-11-23 12:36 6025这个需求是在项目完成后客户提出的要求,看似简单但是还需要一点小 ... -
Web开发学习(2)配置convention插件
2011-11-04 10:28 1415注解的方式某些程度上能减少xml的配置量,个人感觉使程序更加清 ... -
Web开发学习(1)使用eclipse搭建maven项目
2011-10-20 10:04 5765首先用eclipse创建工程 ne ... -
数据库连接泛型基类创建
2011-06-30 11:49 1011记录 以便以后参考---- 基类代码 public cl ...
相关推荐
重点:所有项目均附赠详尽的SQL文件,这一细节的处理,让我们的项目相比其他博主的作品,严谨性提升了不止一个量级!更重要的是,所有项目源码均经过我亲自的严格测试与验证,确保能够无障碍地正常运行。 1.项目适用场景:本项目特别适用于计算机领域的毕业设计课题、课程作业等场合。对于计算机科学与技术等相关专业的学生而言,这些项目无疑是一个绝佳的选择,既能满足学术要求,又能锻炼实际操作能力。 2.超值福利:所有定价为9.9元的项目,均包含完整的SQL文件。如需远程部署可随时联系我,我将竭诚为您提供满意的服务。在此,也想对一直以来支持我的朋友们表示由衷的感谢,你们的支持是我不断前行的动力! 3.求关注:如果觉得我的项目对你有帮助,请别忘了点个关注哦!你的支持对我意义重大,也是我持续分享优质资源的动力源泉。再次感谢大家的支持与厚爱! 4.资源详情:https://blog.csdn.net/2301_78888169/article/details/144929660 更多关于项目的详细信息与精彩内容,请访问我的CSDN博客!
2024年AI代码平台及产品发展简报-V11
蓝桥杯算法学习冲刺(主要以题目为主)
QPSK调制解调技术研究与FPGA实现:详细实验文档的探索与实践,基于FPGA实现的QPSK调制解调技术:实验文档详细解读与验证,QPSK调制解调 FPGA设计,有详细实验文档 ,QPSK调制解调; FPGA设计; 详细实验文档,基于QPSK调制的FPGA设计与实验文档
PID、ADRC和MPC轨迹跟踪控制器在Matlab 2018与Carsim 8中的Simulink仿真研究,PID、ADRC与MPC轨迹跟踪控制器在Matlab 2018与Carsim 8中的仿真研究,PID, ADRC和MPC轨迹跟踪控制器Simulink仿真模型。 MPC用于跟踪轨迹 ADRC用于跟踪理想横摆角 PID用于跟踪轨迹 轨迹工况有双移线,避障轨迹,正弦轨迹多种 matlab版本为2018,carsim版本为8 ,PID; ADRC; MPC; 轨迹跟踪控制器; Simulink仿真模型; 双移线; 避障轨迹; 正弦轨迹; MATLAB 2018; CarSim 8,基于Simulink的PID、ADRC与MPC轨迹跟踪控制器仿真模型研究
重点:所有项目均附赠详尽的SQL文件,这一细节的处理,让我们的项目相比其他博主的作品,严谨性提升了不止一个量级!更重要的是,所有项目源码均经过我亲自的严格测试与验证,确保能够无障碍地正常运行。 1.项目适用场景:本项目特别适用于计算机领域的毕业设计课题、课程作业等场合。对于计算机科学与技术等相关专业的学生而言,这些项目无疑是一个绝佳的选择,既能满足学术要求,又能锻炼实际操作能力。 2.超值福利:所有定价为9.9元的项目,均包含完整的SQL文件。如需远程部署可随时联系我,我将竭诚为您提供满意的服务。在此,也想对一直以来支持我的朋友们表示由衷的感谢,你们的支持是我不断前行的动力! 3.求关注:如果觉得我的项目对你有帮助,请别忘了点个关注哦!你的支持对我意义重大,也是我持续分享优质资源的动力源泉。再次感谢大家的支持与厚爱! 4.资源详情:https://blog.csdn.net/2301_78888169/article/details/144486173 更多关于项目的详细信息与精彩内容,请访问我的CSDN博客!
内容概要:本文档详细介绍了一个利用Matlab实现Transformer-Adaboost结合的时间序列预测项目实例。项目涵盖Transformer架构的时间序列特征提取与建模,Adaboost集成方法用于增强预测性能,以及详细的模型设计思路、训练、评估过程和最终的GUI可视化。整个项目强调数据预处理、窗口化操作、模型训练及其优化(包括正则化、早停等手段)、模型融合策略和技术部署,如GPU加速等,并展示了通过多个评估指标衡量预测效果。此外,还提出了未来的改进建议和发展方向,涵盖了多层次集成学习、智能决策支持、自动化超参数调整等多个方面。最后部分阐述了在金融预测、销售数据预测等领域中的广泛应用可能性。 适合人群:具有一定编程经验的研发人员,尤其对时间序列预测感兴趣的研究者和技术从业者。 使用场景及目标:该项目适用于需要进行高质量时间序列预测的企业或机构,比如金融机构、能源供应商和服务商、电子商务公司。目标包括但不限于金融市场的波动性预测、电力负荷预估和库存管理。该系统可以部署到各类平台,如Linux服务器集群或云计算环境,为用户提供实时准确的预测服务,并支持扩展以满足更高频率的数据吞吐量需求。 其他说明:此文档不仅包含了丰富的理论分析,还有大量实用的操作指南,从项目构思到具体的代码片段都有详细记录,使用户能够轻松复制并改进这一时间序列预测方案。文中提供的完整代码和详细的注释有助于加速学习进程,并激发更多创新想法。
液滴穿越障碍:从文献到案例的复现研究,液滴破裂与障碍物穿越:文献复现案例研究,液滴生成并通过障碍物破裂。 该案例是文献复现,文献与案例一起。 ,液滴生成; 障碍物破裂; 文献复现; 案例研究,液滴破裂:障碍挑战的文献复现案例
蓝桥杯算法学习冲刺(主要以题目为主)
蓝桥杯算法学习冲刺(主要以题目为主)
基于最小递归二乘法的MPC自适应轨迹跟踪控制优化 针对轮胎刚度时变特性提升模型精度与鲁棒性,仿真验证满足车辆低速高精度跟踪与高速稳定性提升。,基于变预测时域MPC自适应轨迹跟踪控制与轮胎侧偏刚度优化提升模型精度和鲁棒性,基于变预测时域的MPC自适应轨迹跟踪控制,针对轮胎刚度时变的特点造成控制模型精度降低,基于最小递归二乘法(RLS)估算的轮胎侧偏刚度,提升了模型的控制精度和鲁棒性,通过carsim与simulink联合仿真结果发现,改进后的轨迹跟踪控制器既满足了车辆低速行驶下的轨 迹跟踪精度,也一定程度上克服了高速下车辆容易失去稳定性的问题。 有详细的lunwen分析说明和资料,以及本人的,仿真包运行。 ,基于变预测时域的MPC; 自适应轨迹跟踪控制; 轮胎刚度时变; 控制模型精度降低; 最小递归二乘法(RLS)估算; 模型控制精度和鲁棒性提升; carsim与simulink联合仿真; 轨迹跟踪控制器; 车辆稳定性。,基于变预测时域MPC的轮胎刚度自适应轨迹跟踪控制策略研究
GMSK调制解调技术研究:基于FPGA设计与实验详解,GMSK调制解调技术详解:基于FPGA设计的实验文档与实践应用,GMSK调制解调 FPGA设计,有详细实验文档 ,GMSK调制解调; FPGA设计; 详细实验文档; 实验结果分析,GMSK调制解调技术:FPGA设计与实验详解
# 基于Arduino和Python的Cansat卫星系统 ## 项目简介 本项目是一个Cansat卫星系统,旨在设计和实现一个小型卫星模型,通过火箭发射至1公里高空,并使用地面站接收其传输的数据。项目涉及Arduino编程、Python数据处理和可视化。 ## 主要特性和功能 1. 硬件组件 使用Arduino Nano作为Cansat的微控制器。 搭载BMP 280温度和压力传感器、ATGM336H GPS模块、LoRa通信模块等。 地面站使用Arduino Uno和LoRa通信模块接收数据。 2. 数据处理 使用Python进行数据处理和可视化,包括数据清洗、计算风速、绘制温度、压力、风速和海拔随时间变化的图表等。 3. 通信与控制 通过LoRa模块实现Cansat与地面站之间的数据传输。 提供实时监视和记录数据的脚本。 ## 安装和使用步骤 ### 1. 硬件准备
U9300C 龙尚4G模块安装后模块才能正常使用,win7 win10驱动程序,支持USB转接板。
# 基于Arduino平台的物联网温湿度监控系统 ## 项目简介 这是一个基于Arduino平台的物联网温湿度监控项目,旨在通过简单的硬件设备实现环境数据的实时监测与远程管理。该项目适用于智能家居、农业种植等领域。 ## 项目的主要特性和功能 1. 温湿度数据采集通过Arduino板连接温湿度传感器,实时采集环境数据。 2. 数据传输将采集到的数据通过无线网络模块发送到服务器或远程终端。 3. 数据可视化可在电脑或移动设备端展示实时的温湿度数据。 4. 报警功能当温湿度数据超过预设阈值时,自动触发报警通知。 ## 安装使用步骤 前提假设用户已经下载了本项目的源码文件。以下是简单明了的安装使用步骤 1. 环境准备安装Arduino开发环境,配置必要的硬件接口。 2. 硬件连接将Arduino板与温湿度传感器、无线网络模块连接。 3. 代码上传将本项目提供的Arduino代码上传至Arduino板。
基于需求响应与清洁能源接入的配电网重构优化:综合成本与混合整数凸规划模型分析(matlab实现),基于需求响应与清洁能源接入的配电网重构算法研究:网损与成本优化的仿真分析,高比例清洁能源接入下计及需求响应的配电网重构(matlab代码) 该程序复现《高比例清洁能源接入下计及需求响应的配电网重构》,以考虑网损成本、弃风弃光成本和开关操作惩罚成本的综合成本最小为目标,针对配电网重构模型的非凸性,引入中间变量并对其进行二阶锥松弛,构建混合整数凸规划模型,采用改进的 IEEE33 节点配电网进行算例仿真,分析了需求响应措施和清洁能源渗透率对配电网重构结果的影响。 该程序复现效果和出图较好(详见程序结果部分),注释清楚,方便学习 ,高比例清洁能源; 需求响应; 配电网重构; 二阶锥松弛; 综合成本最小化; MATLAB代码; IEEE33节点配电网; 复现效果; 出图; 注释清楚。,Matlab代码复现:高比例清洁能源接入下的配电网重构模型与需求响应分析
# 基于C++的RapidJSON库测试项目 ## 项目简介 本项目是一个基于C++的RapidJSON库测试项目,主要用于测试RapidJSON库的功能正确性、性能以及稳定性。RapidJSON是一个高效的C++ JSON解析生成库,广泛应用于各种场景。本项目通过编写一系列的单元测试,覆盖了RapidJSON库的主要功能点,包括JSON解析、生成、内存管理、编码转换等,以确保RapidJSON库在各种情况下都能正确、稳定地工作。 ## 项目的主要特性和功能 1. 单元测试框架使用Google Test测试框架进行单元测试,确保测试的可靠性和可扩展性。 2. 全面测试覆盖覆盖了RapidJSON库的主要功能点,包括JSON解析、生成、内存管理、编码转换等,以及针对各种输入数据的测试。 3. 性能测试通过性能基准测试,评估RapidJSON库在处理不同规模和类型的JSON数据时的性能表现。
蓝桥杯算法学习冲刺(主要以题目为主)
内容概要:本文详细介绍如何安装和初步使用 VMware 虚拟机,从下载安装 VMware 到创建和配置新的虚拟机。主要内容包括:软件选择和安装步骤、虚拟机的新建配置、操作系统安装及初始化设置、安装 VMware Tools 提升性能以及一些常用的 VMWare 功能,如虚拟网络的不同连接方式及其应用场景。同时介绍了 VMware 软件在网络连接管理和服务配置方面的一些要点,确保虚拟机正常运行。 适用人群:计算机操作较为熟练、有意搭建不同操作系统测试环境的技术人员,以及想要了解虚拟机基本概念及应用的学生。 使用场景及目标:适合于个人用户进行系统兼容性和安全性的验证;适用于企业或开发者做软件测试、模拟复杂环境下作业,确保不影响宿主机正常工作的前提下完成多种任务;适用于教学培训环境中部署实验平台。此外,还可以用来隔离特定业务流程(比如银行工具)、探索不同类型操作系统的特点。 其他说明:需要注意的是,为了避免安装过程中出现问题,建议暂时关闭杀毒软件和防火墙。安装 VMware 需要接受许可协议,同时可以选择安装路径和安装类型(典型/自定义)。最后,对于网络设置,默认提供的三种模式——桥接模式、仅主机模式和 NAT 模式,可以帮助用户根据不同需求灵活调整网络连接方式。
java毕业设计源码