菜单树的实现
菜单树类的实现,可以看这篇
新手小白,但超详细、超实用版!
有错误、有更好的思路、更好的实现方式请评论区留言更正,感谢!
1.构建数据库表
- 构建数据库表的语句:
CREATE TABLE `menu` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '菜单id',
`icon` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '菜单图标',
`menu_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '菜单名称',
`has_third` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '(可以不用)',
`url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '菜单路由',
`pid` bigint DEFAULT NULL COMMENT '父菜单id',
`order_value` int DEFAULT NULL COMMENT '菜单排序',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=177 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
- 数据:
这里要明白pid和order_value数据是怎么写的
pid就是该菜单的父菜单,没有就填0
order_value对每级菜单进行排序,对菜单的子菜单也进行排序
数据如下:
“1” “li-icon-xiangmuguanli” “基础管理” “0” “1”
“2” “icon-cat-skuQuery” “商品管理” “N” “goods/Goods” “1” “1”
“33” “li-icon-dingdanguanli” “订单管理” “0” “2”
“34” “icon-order-manage” “交易订单” “N” “pay/Order” “33” “1”
“71” “li-icon-xitongguanli” “系统管理” “0” “3”
“72” “icon-cus-manage” “用户管理” “N” “system/user” “71” “1”
“73” “icon-news-manage” “角色管理” “N” “system/Role” “71” “3”
“74” “icon-cs-manage” “公司管理” “N” “system/Dept” “71” “4”
“75” “icon-promotion-manage” “系统环境变量” “N” “system/Variable” “71” “5”
“76” “icon-cms-manage” “权限管理” “N” “system/Permission” “71” “6”
“128” “li-icon-shangchengxitongtubiaozitihuayuanwenjian91” “支付管理” “0” “5”
“129” “icon-provider-manage” “支付配置信息” “N” “machine/MachineConfig” “128” “1”
“150” “li-icon-shangchengxitongtubiaozitihuayuanwenjian91” “图表” “0” “4”
“159” “icon-provider-manage” “数据可视化” “N” “charts/statistics” “150” “1”
“174” “icon-cms-manage” “菜单管理” “N” “system/Module” “71” “2”
“175” “icon-provider-manage” “支付配置” “N” “pay/Config” “128” “2”
- 最终菜单呈现:
2.创建实体类对象
- Menu实体类对象
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import java.io.Serializable;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.annotations.ApiModel;
import lombok.Data;
@ApiModel(value = "Menu对象")
@Data
public class Menu implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
@JsonProperty("menuid")
private Long id;
private String icon;
@JsonProperty("menuname")
private String menuName;
private String hasThird;
private String url;
private Long pid;
private Integer orderValue;
}
- MenuVO对象(返回给前端的对象)
import cn.lhn.entity.Menu;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.List;
@Data
public class MenuVO extends Menu {
@JsonProperty("menus")
private List<MenuVO> children;
}
当前端需要的字段和实体类属性不一致时,使用该注解
@JsonProperty(“menuname”):该注解的值对应于前端需要返回的字段名称
这个注解会把menuName映射成menuname
反正就那意思
3.构建菜单工具类
import cn.lhn.vo.MenuVO;
import java.util.ArrayList;
import java.util.List;
public class MenuTreeUtil {
/**
* 将传来的数据整理成Tree形数据
* @param menuList 传来的数据(将要整理的数据)
* @return menuTree 整理完成的数据
*/
public static List<MenuVO> buildMenuTree(List<MenuVO> menuList){
//定义要返回的、要构建的最终的菜单树
List<MenuVO> menuTree = new ArrayList<>();
for (MenuVO menu : menuList) {
if (menu.getPid().longValue() == 0){ //找到根菜单
menuTree.add(findChildren(menu,menuList)); //再寻找并添加子节点
}
}
return menuTree;
}
/**
* 在menuList数据中继续给menu找子菜单
* @param menu 要追加子菜单的菜单
* @param menuList 要查找的目标数据
* @return 返回追加好的MenuVO
*/
public static MenuVO findChildren(MenuVO menu,List<MenuVO> menuList){
for (MenuVO item : menuList) { //遍历要查找的数据
if (menu.getId().longValue() == item.getPid().longValue()){ //找到了子菜单
if (menu.getChildren() == null){ //给要追加子菜单的菜单添加数组,准备添加子菜单
menu.setChildren(new ArrayList<MenuVO>());
}
menu.getChildren().add(findChildren(item,menuList)); //添加子菜单
}
}
return menu;
}
}
4.Controller层
import cn.lhn.result.Result;
import cn.lhn.service.IMenuService;
import cn.lhn.vo.MenuVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@Api(tags = "菜单模块")
@RestController
@RequestMapping
public class MenuController {
@Autowired
IMenuService menuService;
@ApiOperation("获取菜单列表")
@PostMapping("/Module/list")
public Result<List<MenuVO>> menuList(){
List<MenuVO> menuList = menuService.menu();
return Result.ok(menuList);
}
}
5.Service层
import cn.lhn.vo.MenuVO;
import java.util.List;
public interface IMenuService{
List<MenuVO> menu();
}
import cn.lhn.entity.Menu;
import cn.lhn.mapper.MenuMapper;
import cn.lhn.service.IMenuService;
import cn.lhn.utils.JsonUtil;
import cn.lhn.utils.MenuTreeUtil;
import cn.lhn.vo.MenuVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class MenuServiceImpl implements IMenuService {
@Autowired
MenuMapper menuMapper;
@Override
public List<MenuVO> menu() {
//数据库查询数据
List<MenuVO> menus = menuMapper.selectAll();
//构建菜单树
List<MenuVO> menuList = MenuTreeUtil.buildMenuTree(menus);
//对菜单树根菜单进行排序
menuList = menuList.stream().sorted(Comparator.comparing(Menu::getOrderValue))
.collect(Collectors.toList());
return menuList;
}
}
其实ServiceImpl和工具类就是实现菜单树的主要内容,还有实体类的构建
6.Mapper层
import cn.lhn.entity.Menu;
import cn.lhn.vo.MenuVO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface MenuMapper extends BaseMapper<Menu> {
@Select("select * from menu")
List<MenuVO> selectAll();
}
7.前端部分(仅供参考)
- 前端的部分代码(仅供参考,看懂就行,前端不归我管!)
<el-tree class="treeclass" ref="tree" :data="treeData"
default-expand-all="" :props="defaultProps"
@node-click="nodeclick" @check-change="handleClick"
check-strictly node-key="id" show-checkbox>
</el-tree>
:props=“defaultProps” :后端返回来的数据中的相应字段,要与defaultProps中的字段相对应
定义的实体类与前端需要的字段不一致时,可以使用前面说的@JsonProperty(“menus”)注解
export default {
data() {
return {
treeData: [],
defaultProps: {
children: 'menus',
label: 'menuname'
},
}
}
}
methods: {
// 获取数据
getdata() {
ModuleList()
.then(res => {
this.treeData = res.data
})
.catch(err => {
this.loading = false
this.$message.error('菜单管理列表失败,请稍后再试!')
})
}
}
export const ModuleList = () => { return req("post", "/api/Module/list") };