深入剖析基于数据库菜单列表实现Vue动态路由的高效策略

2024-04-18 21:18:00 浏览数 (1)

前言

大家好,我是腾讯云开发者社区的 Front_Yue,本篇文章将解决如何将数据库中菜单信息表转化为Vue路由信息列表。

在构建一个基于 Vue.js 的单页应用时,我们经常需要根据后端数据库中的系统菜单来动态生成前端路由。这样做的好处是,当后端菜单结构发生变化时,前端路由可以自动更新,无需手动修改代码。本文将介绍如何在 Vue 中实现查询数据库系统菜单并将其转化为 router 路由格式参数。

正文内容

一、服务端准备工作

1. 菜单信息表数据库设计

为了储存菜单信息,我们需要设计一个用于存储菜单信息的数据库表时,我们需要考虑菜单的基本属性以及可能的关联关系。以下是一个简单的菜单信息表数据库设计示例:

2. 获取菜单信息表接口

为了使客户端能够请求到数据库中的菜单信息,我们还需要设计查询菜单表接口,以下接口仅作参考。

代码语言:java复制
@Tag(name = "菜单权限表接口")
@RestController
@RequestMapping("/system/menu")
public class MenuController extends BaseController {

    @Autowired
    private IMenuService menuService;
    @Operation(summary ="查询菜单权限表")
    @GetMapping("/list")
    public AjaxResult list(Menu menu){
            List<Menu> list= menuService.list(menu);
            return success(list);
    }

    @Operation(summary ="分页查询菜单权限表")
    @GetMapping("/page")
    public AjaxResult page(Page<Menu> page,Menu menu){
            IPage<Menu> menuIPage= menuService.page(page, menu);
            return success(menuIPage);
    }
}

二.、客户端准备工作

1. 安装并引入 axios

为了与后端接口进行通信,我们可以使用 axios 这个流行的 HTTP 客户端库。首先,通过 npm 安装 axios:

代码语言:bash复制
npm install axios

然后,在 Vue 组件或 Vuex 中引入 axios:

代码语言:javascript复制
import axios from 'axios';

封装请求菜单信息列表api接口

代码语言:js复制
import request from '@/utils/request'

/* 查询菜单列表 */
export const listMenu = (params) => {
    return request({
        url: '/system/menu/list',
        method: 'get',
        params: params
    })
}

2. 请求服务端接口

在 Vue 组件的 createdmounted 生命周期钩子中,使用 axios 调用后端接口获取菜单数据:

代码语言:javascript复制
import { onMounted, reactive, ref } from "vue";
import {listMenu} from "@/api/system/menu";
const tableData = ref([{}]);
const queryParams = reactive({
    menuName: null,
    menuType: null,
    visible: null,
    status: null,
});
onMounted(() => {
    getMenuList();
});

const getMenuList = async () => {
    const res = await listMenu(queryParams);
    tableData.value = res.data;
};

经过上述方法,我们得到菜单数据如下:

代码语言:json复制
[
    {
        "createBy": null,
        "createTime": null,
        "updateBy": null,
        "updateTime": null,
        "remark": "",
        "menuId": 1,
        "menuName": "系统管理",
        "parentId": 0,
        "orderNum": 2,
        "path": "system",
        "component": "",
        "menuType": "M",
        "visible": "0",
        "status": "0",
        "perms": null,
        "icon": ""
    },
    {
        "createBy": null,
        "createTime": null,
        "updateBy": null,
        "updateTime": null,
        "remark": "",
        "menuId": 2,
        "menuName": "用户管理",
        "parentId": 1,
        "orderNum": 1,
        "path": "user",
        "component": "system/user/index",
        "menuType": "C",
        "visible": "0",
        "status": "0",
        "perms": null,
        "icon": ""
    },
    {
        "createBy": null,
        "createTime": null,
        "updateBy": null,
        "updateTime": null,
        "remark": "",
        "menuId": 3,
        "menuName": "菜单管理",
        "parentId": 1,
        "orderNum": 2,
        "path": "menu",
        "component": "system/menu/index",
        "menuType": "C",
        "visible": "0",
        "status": "0",
        "perms": null,
        "icon": ""
    }
]

三、封装菜单数据转换方法

为了优化代码逻辑,我们可以编写一个函数来将菜单数据转化为符合要求的路由参数,在编写函数之前我们先看看正确的路由格式。

代码语言:json复制
[
  {
    path: "/system",
    name:"System",
    component: () => import("@/layout/index.vue"),
    children: [
      {
        path: "menu",
        name: "Menu",
        component: () => import("@/pages/system/menu/index.vue"),
        meta: {
          title: "菜单",
          icon: "menu",
        },
      },
    ],
  }
]

有了上述目标数据作为参考,我们就可以编写转化函数了,我们需要定义一个menuUntil.js文件用于我们封装该方法。

  • 导入 listMenu 函数,该函数用于查询数据库中的系统菜单数据。
代码语言:javascript复制
import { listMenu } from "@/api/system/menu";
  • 定义 getRouters 函数,该函数是一个异步函数,因为它需要从数据库中获取菜单数据。
代码语言:javascript复制
export const getRouters = async () => {
  • 使用 import.meta.glob 函数获取所有的 Vue 组件,这些组件将作为路由的 component 属性。
代码语言:javascript复制
const modules = import.meta.glob("/src/**/index.vue");
  • 调用 listMenu 函数获取系统菜单数据。
代码语言:javascript复制
const res = await listMenu();
const data = res.data;
  • 创建一个名为 resultMap 的 Map 对象,用于存储菜单数据。
代码语言:javascript复制
const resultMap = new Map();
  • 遍历查询到的菜单数据,根据菜单类型(menuType)将数据转换为 Vue Router 可识别的路由格式参数,并将结果存储到 resultMap 中。
代码语言:javascript复制
data.forEach((item) => {
  if (item.menuType === "M") {
    resultMap.set(item.menuId, {
      name: item.path.charAt(0).toUpperCase()   item.path.slice(1),
      path: '/'  item.path,
      hidden: item.visible == "0" ? false : true,
      component: modules[`/src/layout/index.vue`],
      meta: {
        title: item.menuName,
        icon: item.icon || "user",
      },
      children: [],
    });
  } else if (item.menuType === "C") {
    const parent = resultMap.get(item.parentId);
    if (parent) {
      parent.children.push({
        name: item.path.charAt(0).toUpperCase()   item.path.slice(1),
        path: item.path,
        hidden: item.visible == "0" ? false : true,
        component:modules[`/src/pages/${item.component}.vue`],
        meta: {
          title: item.menuName,
          icon: item.icon || "user",
        },
      });
    }
  }
});
  • resultMap 中的值转换为数组并返回。
代码语言:javascript复制
return Array.from(resultMap.values());
};

完整代码如下:

代码语言:js复制
import { listMenu } from "@/api/system/menu";
export const getRouters = async () => {
  const modules = import.meta.glob("/src/**/index.vue");
  const res = await listMenu();
  const data = res.data;
  const resultMap = new Map();
  data.forEach((item) => {
    if (item.menuType === "M") {
      resultMap.set(item.menuId, {
        name: item.path.charAt(0).toUpperCase()   item.path.slice(1),
        path: '/'  item.path,
        hidden: item.visible == "0" ? false : true,
        component: modules[`/src/layout/index.vue`],
        meta: {
          title: item.menuName,
          icon: item.icon || "user",
        },
        children: [],
      });
    } else if (item.menuType === "C") {
      const parent = resultMap.get(item.parentId);
      if (parent) {
        parent.children.push({
          name: item.path.charAt(0).toUpperCase()   item.path.slice(1),
          path: item.path,
          hidden: item.visible == "0" ? false : true,
          component:modules[`/src/pages/${item.component}.vue`],
          meta: {
            title: item.menuName,
            icon: item.icon || "user",
          },
        });
      }
    }
  });
  return Array.from(resultMap.values());
};

四、在Router中使用该方法

对于Vue3,我们首先在 src/router目录下创建index.js文件用于初始化路由相关信息,创建路由,定义路由类型。

代码语言:js复制
import { createRouter, createWebHistory } from "vue-router";
import routes from "./routes"; // 专门用来处理路由列表
const router = createRouter({
    history: createWebHistory(),
    routes: routes
})
export default router

在该目录下我们在创建一个routes.js文件,用于存储路由信息,在次文件中,我们使用上述定义的方法,将其转化的路由与基本路由合并抛出,用于页面显示。

代码语言:js复制
// 引入封装的`menuUntil`方法
import { getRouters } from "@/utils/menuUntil";
const routes = await getRouters();
const baseRoute = [
  {
    path: "/",
    component: () => import("@/layout/index.vue"),
    redirect: "/index",
    children: [
      {
        path: "/index",
        name: "index",
        component: () => import("@/pages/index/index.vue"),
        meta: {
          title: "首页",
          icon: "label",
        },
      },
    ],
  },
  {
    path: "/login",
    name: "login",
    component: () => import("@/pages/login/index.vue"),
    meta: {
      title: "登录",
      icon: "label",
    },
  },
];
export default [...baseRoute,...routes];

上述执行getRouters方法后,得到的数据如下:

代码语言:json复制
[
    {
        "name": "System",
        "path": "/system",
        "hidden": false,
        "meta": {
            "title": "系统管理",
            "icon": "user"
        },
        "children": [
            {
                "name": "User",
                "path": "user",
                "hidden": false,
                "meta": {
                    "title": "用户管理",
                    "icon": "user"
                }
            },
            {
                "name": "Menu",
                "path": "menu",
                "hidden": false,
                "meta": {
                    "title": "菜单管理",
                    "icon": "user"
                }
            }
        ]
    }
]

经过以上过程,动态路由到此处理结束,在后续项目代码编写过程中,只需要路由信息添加到数据库表中,不再需要关注路由模块。

总结

本文介绍了如何在 Vue.js 和 Vue Router 中查询数据库系统菜单,通过定义转化数据的函数,并将其转化为 Vue Router 可识别的路由格式参数。通过这种方式,我们可以实现动态生成和渲染导航菜单以及对应的页面内容。

最后,感谢腾讯云开发者社区小伙伴的陪伴,如果你喜欢我的博客内容,认可我的观点和经验分享,请点赞、收藏和评论,这将是对我最大的鼓励和支持。同时,也欢迎大家提出宝贵的意见和建议,让我能够更好地改进和完善我的博客。谢谢!

我正在参与2024腾讯技术创作特训营最新征文,快来和我瓜分大奖!

0 人点赞