当前位置:首页 » 《休闲阅读》 » 正文

vue-element-admin动态菜单改造,含数据库以及前端目录结构参考

6 人参与  2024年04月22日 16:03  分类 : 《休闲阅读》  评论

点击全文阅读


*小tip:每次重新启动前端项目加载困难可以crlt+shift+delete清除浏览器缓存后再重新启动

1.前端改造

首先我们拿到项目,得了解一下目录结构

他是通过src->router->index.js,根据路由来进行页面的跳转

constantRoutes:通用页面路由

asyncRoutes:动态路由

1.1参数讲解

各个参数的意思

有子目录的目录,以最常见的table举例,table在这里,ctrl+左键点进去就行

参数与目录的对照

1.2动态菜单思路

我们要做到的是,根据后端返回的json对象,动态的显示目录而vue-element-admin,是写死了的菜单,所以我们调用后端接口,实现目录的拼接,最终达到实现动态菜单的目的那么我们就要仿造router目录下index.js文件,动态的生成相似的json对象

1.3前端代码

修改router目录下的index.js中的asyncRoutes方法,使其为空,要我们动态的加入菜单

export const asyncRoutes = []

首先在src->api>user.js,加入一个接口方法

//这里加一个,根据data(token)的不同,后台会返回不同的字符串结果,动态菜单完成export function authMenu(data) {  const jsonData = JSON.stringify(data); // 手动转换为 JSON 字符串  console.log("data数据是", jsonData); // 在这里添加打印语句  return request({    url: '/user/selectMenu',    method: 'post',    data: jsonData, // 使用转换后的 JSON 字符串    headers: {      'Content-Type': 'application/json'    }  });}

修改store/modules/permission.js文件,在 generateRoutes方法里面调用上面的authMenu接口

import { asyncRoutes, constantRoutes } from '@/router'import { authMenu } from '@/api/user'// 【新加入】引入请求,后面有文件,先不慌import Layout from '@/layout'// 【新加入】引入layout//这里自己写方法,作用就是向 asyncRoutes 插入路由,达到动态路由的效果/** * 【新加入】后台查询的菜单数据拼装成路由格式的数据 * @param routes */export function generaMenu(routes, data) {  //data挨个遍历data.forEach(item => {    //path不为空的话,就新建一个对象,装数据  if (item.path !== '') {      //这个就仿照目录的机构,搭建    const menu = {      path: item.path,      component: Layout, //这个不用写data里面的内容,引用就行了      redirect: item.redirect,      children: [],      meta: {   // 使用 title 和 icon 创建 meta 对象        title: item.title,        icon: item.icon      }    }    //遍历子标签,并加入到主目录的children中去    // 判断是否有子标签    if (item.children && Array.isArray(item.children) && item.children.length > 0) {      // 遍历子标签,并加入到主目录的 children 中去      item.children.forEach(childItem => {        const menu2 = {          path: childItem.path,          component: (resolve) => require([`@/views${childItem.component}`], resolve),          name: childItem.name,          meta: {   // 使用 title 和 icon 创建 meta 对象            title: childItem.title,            icon: childItem.icon          }        }        // 加入到主目录的 children 中去        menu.children.push(menu2)      })    }    //追加    routes.push(menu)  }})//把404加到最后,因为作者说  // 404 page must be placed at the end !!!const menu3 = {  path: '*',  redirect: '/404',  hidden: true}//追加routes.push(menu3)}const state = {  routes: [],  addRoutes: []}const mutations = {  SET_ROUTES: (state, routes) => {    state.addRoutes = routes    state.routes = constantRoutes.concat(routes)  }}const actions = { generateRoutes({ commit,rootState },) {    return new Promise(resolve => {      const loadMenuData = [] // 保留加载动态路由的代码      // authMenu 调用可能也需要根据你的需要来决定是否删除      authMenu(rootState.user.token).then(response => {        let data = response        if (response.code !== 20000) {          // 错误处理逻辑        } else {          data = response.data          Object.assign(loadMenuData, data)          const tempAsyncRoutes = Object.assign([], asyncRoutes)          generaMenu(tempAsyncRoutes, loadMenuData)          let accessedRoutes          accessedRoutes = tempAsyncRoutes || []          commit('SET_ROUTES', accessedRoutes)          resolve(accessedRoutes)        }      })    }).catch(error => {      console.log(error)    })  }}export default {  namespaced: true,  state,  mutations,  actions}

最后,修改views/login下的index.vue,dispatch 一下(在登录成功的前提下)

向handleLogin中添加dispatch( "permission/generateRoutes",userRoles)

handleLogin() {      this.$refs.loginForm.validate((valid) => {        if (valid) {          this.loading = true;          // 假设有一种方法用于获取用户角色,例如 getUserRoles()          const userRoles = ["admin"];          this.$store            .dispatch("user/login", this.loginForm)            .then(() => {              // 将预定义的角色 "admin" 传递给 generateRoutes action              return this.$store.dispatch(                "permission/generateRoutes",                userRoles              );            })            .then((accessedRoutes) => {              // 将动态路由添加到路由器              this.$router.addRoutes(accessedRoutes);              // 导航到指定路径或默认路径 ('/')              this.$router.push({                path: this.redirect || "/",                query: this.otherQuery,              });              // 重置加载状态              this.loading = false;            })            .catch(() => {              // 在登录失败的情况下重置加载状态              this.loading = false;            });        } else {          console.log("提交错误!!");          return false;        }      });    },

2.后端改造

@Controller层

 @Resource    private MenuService menuService;   /**     * 动态菜单获取     */    @PostMapping("/selectMenu")    public MenuResponse selectMenu(@RequestBody VoToken voToken) {        MenuResponse res = new MenuResponse();        try {            // 验证token的合法和有效性            String tokenValue = JwtUtil.verity(voToken.getToken());            if (tokenValue != null && tokenValue.startsWith(JwtUtil.TOKEN_SUCCESS)) {                // 从令牌中提取实际的用户名                String username = tokenValue.replaceFirst(JwtUtil.TOKEN_SUCCESS, "");                // 记录调用日志                log.info("从令牌中提取的用户名: {}", username);                // 调用 MenuService 获取菜单数据                List<VoMenu> menus = menuService.getAllMenus();                // 记录菜单数量的日志                log.info("获取的菜单数量: {}", menus.size());                // 构建响应对象                res.setCode(Constants.STATUS_OK);                res.setMsg(Constants.MESSAGE_OK);                res.setData(menus);            } else {                // 记录token验证失败的日志                log.warn("Token验证失败");                // 验证失败                res.setCode(Constants.STATUS_FAIL);                res.setMsg(Constants.MESSAGE_FAIL);            }        } catch (Exception e) {            // 记录处理请求时发生的异常            log.error("处理请求时发生异常", e);            // 处理异常            res.setCode(60204);            res.setMsg("返回失败");        }        return res;    }

@Servicer

package com.mv.servicer;import com.mv.entity.VO.VoMenu;import java.awt.*;import java.util.List;public interface MenuService {    List<VoMenu> getAllMenus();}

@ServiceImpl

package com.mv.servicer.Impl;import com.mv.entity.VO.VoMenu;import com.mv.mapper.MenuMapper;import com.mv.servicer.MenuService;import org.springframework.stereotype.Service;import javax.annotation.Resource;import java.awt.*;import java.util.List;@Servicepublic class MenuServiceImpl implements MenuService {    @Resource    private MenuMapper menuMapper;    @Override    public List<VoMenu> getAllMenus() {        List<VoMenu> allMenus = menuMapper.getAllMenus();        return VoMenu.buildMenuTree(allMenus);    }    // 可以根据需要添加其他方法}

@Mapper

package com.mv.mapper;import com.mv.entity.VO.VoMenu;import org.apache.ibatis.annotations.Mapper;import java.util.List;@Mapperpublic interface MenuMapper {    List<VoMenu> getAllMenus();}

@Mapper.xml

<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" ><mapper namespace="com.mv.mapper.MenuMapper">    <select id="getAllMenus" resultType="com.mv.entity.VO.VoMenu">        SELECT * FROM menus;    </select></mapper>

对应数据库的实体类@VoMenus

package com.mv.entity.VO;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;@Data@NoArgsConstructor@AllArgsConstructorpublic class VoMenu {    private Integer id;        // 使用 Integer 类型,与数据库表中的 id 类型匹配    private String path;    private String component;    private String redirect;    private String name;    private String title;       // 新增字段 title    private String icon;        // 新增字段 icon    private Integer parent_id;   // 与数据库表中的 parent_id 类型匹配    private List<VoMenu> children;        // Getters and setters        // Constructors    public Integer getId() {        return id;    }    public Integer getParentId() {        return parent_id;    }    public static List<VoMenu> buildMenuTree(List<VoMenu> menuList) {        Map<Integer, VoMenu> menuMap = new HashMap<>();        for (VoMenu menu : menuList) {            menuMap.put(menu.getId(), menu);        }        List<VoMenu> tree = new ArrayList<>();        for (VoMenu menu : menuList) {            if (menu.getParentId() != null) {                VoMenu parent = menuMap.get(menu.getParentId());                if (parent != null) {                    if (parent.getChildren() == null) {                        parent.setChildren(new ArrayList<>());                    }                    parent.getChildren().add(menu);                }            } else {                // 如果没有父菜单,说明是顶级菜单                tree.add(menu);            }        }        return tree;    }}

后端单方面测试 我使用的是apifox

返回数据成功。

3.数据库menus以及前端目录结构参考

3.1数据库里的父子级菜单

** 我的动态菜单获取并没有设置权限,因为我的后台只有一个管理员admin

3.2.前端目录结构

位于原项目@/views下的permission文件夹

3.3.前后端互联展示

3.3.1后端返回数据:

{    "code": 20000,    "msg": "成功,",    "data": [        {            "id": 1,            "path": "/permission",            "component": "Layout",            "redirect": "/permission/page",            "name": "Permission",            "title": "Permission",            "icon": "lock",            "parent_id": null,            "children": [                {                    "id": 2,                    "path": "/permission/page",                    "component": "/permission/page",                    "redirect": null,                    "name": "PagePermission",                    "title": "Page Permission",                    "icon": null,                    "parent_id": 1,                    "children": null,                    "parentId": 1                },                {                    "id": 3,                    "path": "/permission/directive",                    "component": "/permission/directive",                    "redirect": null,                    "name": "DirectivePermission",                    "title": "Directive Permission",                    "icon": null,                    "parent_id": 1,                    "children": null,                    "parentId": 1                },                {                    "id": 4,                    "path": "/permission/role",                    "component": "/permission/role",                    "redirect": null,                    "name": "RolePermission",                    "title": "Role Permission",                    "icon": null,                    "parent_id": 1,                    "children": null,                    "parentId": 1                }            ],            "parentId": null        }    ]}

3.3.2前端渲染效果:

前后端互联完成。


点击全文阅读


本文链接:http://zhangshiyu.com/post/98734.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1