1.功能背景介绍
当许多系统的页面数量达到一定规模时,我们会引入菜单收藏功能。其目的在于让不同用户在使用系统时,能够将自己使用频率较高的页面进行收藏添加。这样,下次用户使用时,便可直接通过自己的收藏菜单进入相应页面进行操作。最终效果如下:
2.项目介绍
项目主要由菜单链接添加、收藏菜单添加及收藏菜单渲染及回显三个模块构成。
2.1 常用链接添加
首先需要完成的就是将整个菜单树进行组装,用于勾选相应的菜单进行添加。
这个模块中,最为重要的一点是需将后端传递过来的菜单树,借助 Element UI 提供的 el-tree
组件进行渲染。
以下是在 Vue 中渲染菜单树的前端代码如下:
<el-tree class="tree-border" :data="menuOptions" show-checkbox ref="menu" node-key="menuId"
:default-checked-keys="this.menuIdList" :check-strictly="true" empty-text="加载中,请稍后" :filter-node-method="filterNode":props="defaultProps">
</el-tree>
属性介绍
:data 整个菜单树的数据来源
ref 为数绑定的引用名称
show-checkbox 控制是否显示复选框
node-key 节点的关键字,我这里是使用的菜单id作为关键字
:default-checked-keys 默认勾选的数据集,可以用于菜单数据回显
:check-strictly="true" 父子节点关联,为true则关联,为false则不关联.在关联的情况下,选中子节点,父节点也会被选中.
empty-text 数据渲染前的提示
:filter-node-method 数据过滤绑定的方法,配合搜索进行检索
数据源menuOptions通过请求后端进行复制,代码如下:
/** 查询菜单下拉树结构 */
getTreeselect() {
listMenu().then(response => {
this.menuOptions = [];
const menu = { menuId: 0, label: '我的管理系统', disabled: true, children: [] };
const filteredData = response.data.filter(item => item.menuType !== 'F').map(item => {
const newItem = { ...item, label: item.menuName, disabled: item.menuType === 'M' && item.menuName !== '我的音乐' };
return newItem;});
menu.children = this.handleTree(filteredData, "menuId");
// console.log('wwdwdw', menu)
this.menuOptions.push(menu);
console.log('菜单数据树', this.menuOptions)
});
},
其中,menuOptions
需要在 data
中自行定义。通过 listMenu
请求后端,获取相应数据 response
。拿到数据后,由于后端数据结构的原因,需要对其进行过滤,筛选出二级菜单并排除增删改查按钮。具体情况可根据实际需求而定。
2.2 数据选中提交事件
菜单树已渲染完成,接下来需要对渲染出来的菜单树进行选择并添加到后端,同时要对已经存在的菜单进行数据回显。
菜单数据选中方法如下,其中需要对已经被勾选的数据进行过滤:
// 所有菜单节点数据
getMenuAllCheckedKeys() {
// 目前被选中的菜单节点
let checkedKeys = this.$refs.menu.getCheckedKeys(); //这里要注意的是refs后面的menu一定要跟前面菜单树的ref一致
// 半选中的菜单节点
// let halfCheckedKeys = this.$refs.menu.getHalfCheckedKeys(); //可选
checkedKeys.unshift.apply(checkedKeys);
// 对选中的菜单节点checkedKeys进行过滤已经选中的数据,this.menuIdList里面放的是回显的数据(后端查询的)
const uniqueNewCheckedKeys = checkedKeys.filter(menuId =>!this.menuIdList.includes(menuId));
eturn uniqueNewCheckedKeys;
}
接下来就是后端请求
//添加收藏菜单
Menuconfirm() {
this.addForm.menuList = this.getMenuAllCheckedKeys();
console.log("选中菜单数据为", this.addForm)
addMenuList(this.addForm).then(response => {
if (response.code == 200) {
this.$message.success(response.msg)
}
});
console.log('选中的菜单数据为', this.selectedMenuData);
this.addForm.menuList = this.selectedMenuData;
console.log('选中的菜单数据2为', this.addForm);
this.SelectList();
this.dialogVisible = false
}
这里的this.addForm.menuList就是我们获取的选中菜单数组,后面就是常规的axios请求及相关操作。
后端拿到前端传递过来的数据后,对其进行相应判断和添加(根据自己的情况而定,我的不一定适合你们)。
@PostMapping
public AjaxResult add(@RequestBody MyMenuListVo myMenuVO) {
int successCount = 0;
for (int i =0; i<myMenuVO.getMenuList().size(); i++){
Long id = new Long(String.valueOf(myMenuVO.getMenuList().get(i)));
SysMenu sysMenu = menuService.selectMenuById(id);
if (sysMenu.getParentId() !=0 || sysMenu.getMenuName().equals("我的桌面")){
MyMenuList myMenuList = new MyMenuList();
myMenuList.setUserId(myMenuVO.getUserId());
myMenuList.setMenuName(sysMenu.getMenuName());
myMenuList.setMenuId(sysMenu.getMenuId());
if (sysMenu.getMenuName().equals("我的桌面") ){
myMenuList.setMenuPath('/'+sysMenu.getPath());
} else {
String oldPath =menuService.selectMenuById(sysMenu.getParentId()).getPath();
myMenuList.setMenuPath('/'+oldPath+'/'+sysMenu.getPath());
}
if (myMenuListServer.insertMyMenuList(myMenuList)> 0) {
successCount++;
}
}
}
return toAjax(successCount);
}
2.3 已收藏菜单渲染及美化
后端就是一个普通的get查询,根据登录的用户id或者能够标识用户唯一性的属性进行查询。
@GetMapping("/list")
public AjaxResult select(String userId) {
return AjaxResult.success(myMenuListServer.selectMeunListByUser(userId));
}
前端对后端给定的数据进行相应渲染
<div style="display: flex; flex-flow: wrap; margin-top: 16px; ">
<button v-for="item in currentPageItems" :key="item.uid" @click="getUrlRouter(item.menuPath)" class="boxDiv">
<div>
<i class="el-icon-link" style=" font-size: 16px;"></i>{{ item.menuName }}
</div>
</button>
</div>
<div style=" text-align: center;"> <!-- 分页组件 -->
<el-pagination @current-change="handleCurrentChange" :current-page="currentPage" :page-size="pageSize"
layout="prev, next" :total="totalItems" />
</div>
相应的CSS样式代码:
<style scoped>
.boxDiv {
height: 40px;
width: 120px;
border: 1px solid #3271fd;
background-color: white;
margin-right: 15px;
border-radius: 5px;
text-align: center;
line-height: 40px;
margin-bottom: 12px;
}
.boxDiv:hover {
background-color: #3271fd;
color: white;
transform: translateY(-5px);
}
.addBtn:hover {
color: #3271fd;
}
.addBtn {
background-color: white;
border: 0px;
}
::v-deep .el-dialog {
border-radius: 10px;
}
::v-deep .el-dialog__body {
padding-bottom: 15px;
}
::v-deep .el-dialog__header {
margin-bottom: 0px;
background-color: #0176e3;
}
::v-deep .el-dialog__title,
::v-deep .el-dialog__headerbtn .el-dialog__close {
color: white;
}
::v-deep .el-pagination .btn-prev .el-icon,
::v-deep .el-pagination .btn-next .el-icon {
font-size: 16px;
margin-right: 50px;
}
</style>
最后还要对已经收藏的菜单数据进行一个回显操作。
//查询收藏菜单&菜单数据回显 myMenuList接收收藏菜单,menuIdList接收回显菜单Id集合
SelectList() {
selectMenuList(this.addForm.userId).then((response) => {
this.myMenuList = response.data
this.menuIdList = response.data.map(item => item.menuId);
console.log(this.menuIdList)
});
},
3.疑难点
-
需要在后端对菜单树的数据进行相应的组装,然后传递给前端。
-
在数据回显的过程中,务必注意在新增操作时,一定要将回显的数据进行过滤,否则会出现重复添加的情况。另外一种方法是在每次添加时,先将该用户在数据库中的收藏菜单全部删除,然后再次重新添加,这样也可以避免重复添加。
-
菜单树的子父级选中关联可根据自身情况决定。这里不希望选中父节点时所有子节点就被自动选中,所以进行了一些处理。