文章的原文链接
https://www.qiwu.ga/archives/gulibug
这次的问题有这几点:
树型表格不能加载: 想用树型表格来加载权限列表但是死活不能展开也找不到按钮, 确认了api已经返回了正确的Json数据.
复选框全选与单选的对应效果不匹配: 用户角色分配功能区, 在为用户分配角色的时候发现勾选全选复选框之后剩下的复选框没有被选中的效果; 反之, 其余复选框全部勾选后,全选复选框没有选中效果.
用户登录系统选左侧菜单重复加载
用户使用非超级管理员身份登录不能显示左侧菜单: 确认是api返回数据中"data"属性为空.
ElementUI版本太低需要升级
npm uninstall element-ui -S
npm install element-ui -S
本项目来源于B站项目, 项目来源: 整合SpringSecurity
建议,可以把
data()
中绑定的isIndeterminate: false
初始化为这样,默认什么都不选中. 否则在没有任何复选框被选中情况下全选复选框默认是"-".
ps.你们的代码可能全是Cities什么城市有关的变量,我这里为了看着舒服我自行修改了变量, 变成Items相关的变量.
全选
indeterminate
: false 不会选中; true 显示"-"表示未全选.
v-model="checkAll"
: 双向绑定数据,判断全选复选框是否勾选.
@change="handleCheckAllChange"
: 事件监听, 一旦用户点击选框就会触发事件.
{
{item.roleName}}
v-model="checkedItems"
: 已经被选择的复选框列表.
@change="handleCheckedItemsChange"
: 勾选复选框的触发事件.
bug位置–1
修复后代码
handleCheckAllChange(val) {
//console.log('全选按钮勾选后' + val);
this.checkedItems = val ? this.getJsonToList(this.items,"id") : [];
//console.log(this.checkedItems);
this.isIndeterminate = false;
},
这里使用一个已提供的功能函数, 从角色Json数据中取出id,组合成字符数组.因为this.checkedItems
需要是一个id字符数组才能被识别那些复选框被选中, 大概是因为复选框的label值设置为了id.
这里修复了以后就可以保证,全选复选框勾选之后,其余的复选框都自动被勾选.
bug位置–2
添加代码,结果如下:
getById(userId){
userApi.getAssign(userId).then(response => {
var jsonObj = response.data.assignRoles
this.checkedItems = this.getJsonToList(jsonObj,"id")
this.items = response.data.allRolesList
this.isIndeterminate = this.checkedItems.length > 0 && this.checkedItems.length < this.items.length;
this.checkAll = this.checkedItems.length === this.items.length;
})
},
最后面添加两行代码, 设置isIndeterminate
有复选框选中的时候全选复选框会变成"-". 判断checkAll
是否为true,一旦全部子复选框被勾选,全选复选框就会自动变成√
.
这里修改完成以后就保证了自复选框全选后,全选复选框能显示勾选.
自此, 复选框有关的bug应该解决完毕, 如果其他功能部分的复选框有问题按此修改即可!
补充说明一个细节
getJsonToList(json,key){
//把JSON字符串转成对象
var list = JSON.parse(JSON.stringify(json));
//var list = JSON.parse(json)
var strText = []
//遍历这个集合对象,获取key的值
for(var i = 0; i < list.length; i++){
strText.push(list[i][key])
}
return strText;
},
这段代码的意思是把json数据转成string再转成array对象,这样就可以遍历这个返回的数据取出其中的每相属性进行操作,这个操作很值得学习!
这个很容易解决. 因为使用了动态路由,所以到 root 文件夹的 index.js 文件下把原来的 export const asyncRoutes = [...我们开始编写的静态路由]
修改成export const asyncRoutes = []
,即直接让它初始化为一个空数组. 因为路由规则要根据用户角色权限从后台api获取.
在浏览器的控制台中可以发现, 使用其他用户登录的时候, getMenu方法返回的data
的"permissionList"
属性值是空的. 很容易就把问题定位到了后台的 getMenu 接口(请求url:/admin/acl/index/menu
).
首先从控制台我们可以发现这个接口执行的一个sql语句
select
p.id,
p.pid,
p.name,
p.type,
p.permission_value,
path,
p.component,
p.icon,
p.status,
p.is_deleted,
p.gmt_create,
p.gmt_modified
from
acl_user_role ur
inner join
acl_role_permission rp
on rp.role_id = ur.role_id
inner join
acl_permission p
on p.id = rp.permission_id
where
ur.user_id = '2'
and ur.is_deleted = 0
and rp.is_deleted = 0
and p.is_deleted = 0
我们单独执行这条sql发现是有返回值的
那这就很好办了,排除了数据本身的问题. 这样问题基本就出在了数据的整合上, 因为我们这里要返回到前端的数据需要自行拼装成树型结构, 后台的很多方法都已经写好了逻辑也不是很复杂.
通过断点输出调试,发现存在bug的方法在于这个方法,位于PermissionServiceImpl.java
文件下:
通过分别输出selectPermissionList
, permissionList
, result
发现数据丢失从permissionList
开始.
接着我们把问题聚焦于
PermissionHelper.bulid(selectPermissionList);
的build的方法, 其中代码如下
这是一个递归构造子树的逻辑, 根节点是pid为0的权限, 查看数据库以下就发现是一条默认name为"全部数据"的数据项. 然而, 我们在开始的sql查询中没有把他查询出来. 但这一个数据项是一个通用项,是全部菜单权限的根节点,所以我们可以手动对其进行添加.
注意修改的代码在PermissionServiceImpl.java
文件下
@Override
public List<JSONObject> selectPermissionByUserId(String userId) {
List<Permission> selectPermissionList = null;
if(this.isSysAdmin(userId)) {
//如果是超级管理员,获取所有菜单
selectPermissionList = baseMapper.selectList(null);
} else {
selectPermissionList = baseMapper.selectPermissionByUserId(userId);
QueryWrapper<Permission> wrapper = new QueryWrapper<>();
wrapper.eq("id",1);
selectPermissionList.add(baseMapper.selectOne(wrapper));
}
List<Permission> permissionList = PermissionHelper.bulid(selectPermissionList);
List<JSONObject> result = MemuHelper.bulid(permissionList);
System.out.println(result);
return result;
}
添加的3行代码是:
QueryWrapper<Permission> wrapper = new QueryWrapper<>();
wrapper.eq("id",1);
selectPermissionList.add(baseMapper.selectOne(wrapper));
我们查询出这个通用根,把它加入到权限列表中即可, 随后使用子树构造的方法就可以构造出路由菜单来了.