Vue从后端取数据,实现动态路由

1、前端代码实现

1.App.vue

将获取菜单的方法放在全局中,以便每次刷新页面时,能够加载出。this.$store.state.userInfo是登入后存放在Vuex的用户信息

html 复制代码
<template>
  <div id="app">
    <router-view />
  </div>
</template>

<script>
import { generaMenu } from '@/assets/js/menu'
export default {
  created() {
    if (this.$store.state.userInfo != null) {
      generaMenu()
    }
  }
}
</script>

2.menu.js(@/assets/js路径下)

javascript 复制代码
import Layout from '@/layout/index.vue'
import router from '@/router'
import store from '@/store'
import axios from 'axios'
import Vue from 'vue'

export function generaMenu() {
  axios.get('/api/admin/user/menus').then(({ data }) => {
    if (data.flag) {
      let userMenus = data.data;
      userMenus.forEach((item) => {
        if (item.icon != null) {
          item.icon = 'iconfont ' + item.icon
        }
        if (item.component == 'Layout') {
          item.component = Layout
        }
        if (item.children && item.children.length > 0) {
          item.children.forEach((route) => {
            route.icon = 'iconfont ' + route.icon;
            route.component = loadView(route.component)
          })
        }
      })
      store.commit('saveUserMenus', userMenus);
      userMenus.forEach((item) => {
        router.addRoute(item)
      })
    } else {
      Vue.prototype.$message.error(data.message);
      router.push({ path: '/login' })
    }
  })
}

export const loadView = (view) => {
  return (resolve) => require([`@/views${view}`], resolve)
}

3.SideBar.vue(侧边栏)

html 复制代码
<template>
  <div>
    <el-menu
      class="side-nav-bar"
      router
      :collapse="this.$store.state.collapse"
      :default-active="this.$route.path"
      background-color="#304156"
      text-color="#BFCBD9"
      active-text-color="#409EFF">
      <template v-for="route of this.$store.state.userMenus">
        <template v-if="route.name && route.children && !route.hidden">
          <el-submenu :key="route.path" :index="route.path">
            <template slot="title">
              <i :class="route.icon" />
              <span>{{ route.name }}</span>
            </template>
            <template v-for="(item, index) of route.children">
              <el-menu-item v-if="!item.hidden" :key="index" :index="item.path">
                <i :class="item.icon" />
                <span slot="title">{{ item.name }}</span>
              </el-menu-item>
            </template>
          </el-submenu>
        </template>
        <template v-else-if="!route.hidden">
          <el-menu-item :index="route.path" :key="route.path">
            <i :class="route.children[0].icon" />
            <span slot="title">{{ route.children[0].name }}</span>
          </el-menu-item>
        </template>
      </template>
    </el-menu>
  </div>
</template>

<style scoped>
.side-nav-bar:not(.el-menu--collapse) {
  width: 210px;
}
.side-nav-bar {
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  overflow-x: hidden;
  overflow-y: auto;
}
.side-nav-bar i {
  margin-right: 1rem;
}
*::-webkit-scrollbar {
  width: 0.5rem;
  height: 1px;
}
*::-webkit-scrollbar-thumb {
  border-radius: 0.5rem;
  background-color: rgba(144, 147, 153, 0.3);
}
</style>

2、后端代码(SpringBoot)

1.Controller层

java 复制代码
package com.pzg.chat.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.api.ApiController;
import com.baomidou.mybatisplus.extension.api.R;
import com.pzg.chat.model.dto.MenuDTO;
import com.pzg.chat.model.dto.UserMenuDTO;
import com.pzg.chat.model.vo.ConditionVO;
import com.pzg.chat.model.vo.ResultVO;
import com.pzg.chat.service.MenuService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.io.Serializable;
import java.util.List;

/**
 * (SysMenu)表控制层
 *
 * @author makejava
 * @since 2024-01-17 14:38:59
 */
@Api(tags = "菜单模块")
@RestController
public class MenuController {

    @Resource
    private MenuService menuService;


    @ApiOperation(value = "查看当前用户菜单")
    @GetMapping("/admin/user/menus")
    public ResultVO<List<UserMenuDTO>> listUserMenus() {
        return ResultVO.ok(menuService.listUserMenus());
    }
}

2.Service层

java 复制代码
public interface MenuService extends IService<Menu> {

	List<UserMenuDTO> listUserMenus();
}

3.ServiceImpl层

java 复制代码
package com.pzg.chat.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pzg.chat.constant.CommonConstant;
import com.pzg.chat.entity.Menu;
import com.pzg.chat.mapper.MenuMapper;
import com.pzg.chat.model.dto.MenuDTO;
import com.pzg.chat.model.dto.UserMenuDTO;
import com.pzg.chat.model.vo.ConditionVO;
import com.pzg.chat.service.MenuService;
import com.pzg.chat.utils.BeanCopy;
import com.pzg.chat.utils.UserUtil;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

import static com.pzg.chat.constant.CommonConstant.COMPONENT;
import static com.pzg.chat.constant.CommonConstant.TRUE;


@Service
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements MenuService {

	@Override
	public List<UserMenuDTO> listUserMenus() {
		Integer id = UserUtil.getUserDetailsDTO().getId();
		List<Menu> menus = this.baseMapper.listMenusByUserInfoId(id);
		List<Menu> catalogs = listCatalogs(menus);
		Map<Integer, List<Menu>> childrenMap = getMenuMap(menus);
		List<UserMenuDTO> userMenuDTOS = convertUserMenuList(catalogs, childrenMap);
		return userMenuDTOS;
	}


	private List<Menu> listCatalogs(List<Menu> menus) {
		return menus.stream()
				.filter(item -> Objects.isNull(item.getParentId()))
				.sorted(Comparator.comparing(Menu::getOrderNum))
				.collect(Collectors.toList());
	}

	private List<UserMenuDTO> convertUserMenuList(List<Menu> catalogList, Map<Integer, List<Menu>> childrenMap) {
		return catalogList.stream().map(item -> {
			UserMenuDTO userMenuDTO = new UserMenuDTO();
			List<UserMenuDTO> list = new ArrayList<>();
			List<Menu> children = childrenMap.get(item.getId());
			if (CollectionUtils.isNotEmpty(children)) {
				userMenuDTO = BeanCopy.singleCopy(item, UserMenuDTO.class);
				list = children.stream()
						.sorted(Comparator.comparing(Menu::getOrderNum))
						.map(menu -> {
							UserMenuDTO dto = BeanCopy.singleCopy(menu, UserMenuDTO.class);
							dto.setHidden(menu.getIsHidden().equals(TRUE));
							return dto;
						})
						.collect(Collectors.toList());
			} else {
				userMenuDTO.setPath(item.getPath());
				userMenuDTO.setComponent(COMPONENT);
				list.add(UserMenuDTO.builder()
						.path("")
						.name(item.getName())
						.icon(item.getIcon())
						.component(item.getComponent())
						.build());
			}
			userMenuDTO.setHidden(item.getIsHidden().equals(TRUE));
			userMenuDTO.setChildren(list);
			return userMenuDTO;
		}).collect(Collectors.toList());
	}

}

4.Menu类

java 复制代码
package com.pzg.chat.entity;

import java.time.LocalDateTime;
import java.util.Date;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

/**
 * (SysMenu)表实体类
 *
 * @author makejava
 * @since 2024-01-17 14:38:59
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@TableName("sys_menu")
public class Menu {
    //主键
    private Integer id;
    //菜单名
    private String name;
    //菜单路径
    private String path;
    //组件
    private String component;
    //菜单icon
    private String icon;
    //创建时间
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    //更新时间
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
    //排序
    private Integer orderNum;
    //父id
    private Integer parentId;
    //是否隐藏  0否1是
    private Integer isHidden;


}

5.UserMenuDTO

java 复制代码
package com.pzg.chat.model.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class UserMenuDTO {

    private String name;

    private String path;

    private String component;

    private String icon;

    private Boolean hidden;

    private List<UserMenuDTO> children;

}

6.mybatis.xml

XML 复制代码
 <select id="listMenusByUserInfoId" resultType="com.pzg.chat.entity.Menu">
        SELECT DISTINCT m.id,
                        name,
                        path,
                        component,
                        icon,
                        is_hidden,
                        parent_id,
                        order_num
        FROM user_role ur
                 JOIN sys_role_menu rm ON ur.role_id = rm.role_id
                 JOIN sys_menu m ON rm.menu_id = m.id
        WHERE user_info_id = #{userInfoId}
    </select>

3、数据库表结构

1.表sys_menu

sql 复制代码
CREATE TABLE `sys_menu` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT '主键',
  `name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '菜单名',
  `path` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '菜单路径',
  `component` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '组件',
  `icon` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '菜单icon',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  `order_num` tinyint(1) NOT NULL COMMENT '排序',
  `parent_id` int DEFAULT NULL COMMENT '父id',
  `is_hidden` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否隐藏  0否1是',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=229 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC

2.表sys_role

sql 复制代码
CREATE TABLE `sys_role` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT '角色id',
  `role_name` varchar(120) NOT NULL COMMENT '角色名',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `updateTime` datetime DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

3.表sys_role_menu

sql 复制代码
CREATE TABLE `sys_role_menu` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT '主键',
  `role_id` int DEFAULT NULL COMMENT '角色id',
  `menu_id` int DEFAULT NULL COMMENT '菜单id',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=3060 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC

4. 表sys_user_info

sql 复制代码
CREATE TABLE `sys_user_info` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT '用户详情id',
  `nick_name` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '昵称',
  `username` varchar(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '账号',
  `gender` int DEFAULT NULL COMMENT '性别',
  `avatar` varchar(225) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '头像',
  `address` varchar(225) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '地址',
  `disable` int DEFAULT NULL COMMENT '是否禁用(0禁用,1不禁用)',
  `date_birth` datetime DEFAULT NULL COMMENT '出生日期',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `unique_username` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

4、效果

按照上面要求即可实现

相关推荐
四喜花露水21 分钟前
Vue 自定义icon组件封装SVG图标
前端·javascript·vue.js
前端Hardy31 分钟前
HTML&CSS: 实现可爱的冰墩墩
前端·javascript·css·html·css3
web Rookie1 小时前
JS类型检测大全:从零基础到高级应用
开发语言·前端·javascript
Au_ust1 小时前
css:基础
前端·css
帅帅哥的兜兜1 小时前
css基础:底部固定,导航栏浮动在顶部
前端·css·css3
工业甲酰苯胺1 小时前
C# 单例模式的多种实现
javascript·单例模式·c#
yi碗汤园1 小时前
【一文了解】C#基础-集合
开发语言·前端·unity·c#
就是个名称1 小时前
购物车-多元素组合动画css
前端·css
编程一生2 小时前
回调数据丢了?
运维·服务器·前端
丶21362 小时前
【鉴权】深入了解 Cookie:Web 开发中的客户端存储小数据
前端·安全·web