前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >深入剖析基于数据库菜单列表实现Vue动态路由的高效策略

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

原创
作者头像
Front_Yue
发布2024-04-18 21:18:00
3310
发布2024-04-18 21:18:00
举报
文章被收录于专栏:后端技术探险家码艺坊

前言

大家好,我是腾讯云开发者社区的 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腾讯技术创作特训营最新征文,快来和我瓜分大奖!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 正文内容
    • 一、服务端准备工作
      • 1. 菜单信息表数据库设计
      • 2. 获取菜单信息表接口
    • 二.、客户端准备工作
      • 1. 安装并引入 axios
      • 2. 请求服务端接口
    • 三、封装菜单数据转换方法
      • 四、在Router中使用该方法
      • 总结
      相关产品与服务
      云开发 CloudBase
      云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为200万+企业和开发者提供高可用、自动弹性扩缩的后端云服务,可用于云端一体化开发多种端应用(小程序、公众号、Web 应用等),避免了应用开发过程中繁琐的服务器搭建及运维,开发者可以专注于业务逻辑的实现,开发门槛更低,效率更高。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档