Pure Admin 保姆级文档(已更新至最新版v6.0.0)
  • 介绍
  • 快速开始
  • 目录结构
  • vscode文件夹详解
  • 平台配置
  • 布局
  • 路由和菜单
  • http请求
  • 打包和部署
  • 进阶

    • 图标
    • 主题和暗黑模式
    • 国际化
    • Tailwind CSS
    • RBAC权限
    • 类型声明
    • 单点登录
    • 自定义免登录
    • 打包优化
    • vite预构建
  • 生态

    • 对接平台的前后端项目
    • 函数工具库
    • 组件库
    • vite插件
  • 其他

    • 赞助
    • 常见问题
    • 非平台问题跟踪记录
    • git常用命令
    • 技术网站推荐
  • 赞助
    • 后端项目
    • 哔哩哔哩 (opens new window)
  • 日志

    • Github日志 (opens new window)
    • Gitee日志 (opens new window)
  • 预览

    • 完整版 (opens new window)
    • 精简版 (opens new window)
    • max版 (opens new window)
    • utils文档 (opens new window)
  • Github源码

    • 完整版 (opens new window)
    • 精简版 (opens new window)
    • Tauri版 (opens new window)
    • Electron版 (opens new window)
    • 配套后端 (opens new window)
    • 文档 (opens new window)
    • utils文档 (opens new window)
  • Gitee源码

    • 完整版 (opens new window)
    • 精简版 (opens new window)
    • Tauri版 (opens new window)
    • Electron版 (opens new window)
    • 配套后端 (opens new window)
    • 文档 (opens new window)
    • utils文档 (opens new window)
优质服务
JS版本
Max版本
JS版本、Max版本限时活动
  • 介绍
  • 快速开始
  • 目录结构
  • vscode文件夹详解
  • 平台配置
  • 布局
  • 路由和菜单
  • http请求
  • 打包和部署
  • 进阶

    • 图标
    • 主题和暗黑模式
    • 国际化
    • Tailwind CSS
    • RBAC权限
    • 类型声明
    • 单点登录
    • 自定义免登录
    • 打包优化
    • vite预构建
  • 生态

    • 对接平台的前后端项目
    • 函数工具库
    • 组件库
    • vite插件
  • 其他

    • 赞助
    • 常见问题
    • 非平台问题跟踪记录
    • git常用命令
    • 技术网站推荐
  • 赞助
    • 后端项目
    • 哔哩哔哩 (opens new window)
  • 日志

    • Github日志 (opens new window)
    • Gitee日志 (opens new window)
  • 预览

    • 完整版 (opens new window)
    • 精简版 (opens new window)
    • max版 (opens new window)
    • utils文档 (opens new window)
  • Github源码

    • 完整版 (opens new window)
    • 精简版 (opens new window)
    • Tauri版 (opens new window)
    • Electron版 (opens new window)
    • 配套后端 (opens new window)
    • 文档 (opens new window)
    • utils文档 (opens new window)
  • Gitee源码

    • 完整版 (opens new window)
    • 精简版 (opens new window)
    • Tauri版 (opens new window)
    • Electron版 (opens new window)
    • 配套后端 (opens new window)
    • 文档 (opens new window)
    • utils文档 (opens new window)
优质服务
JS版本
Max版本
  • 指南

    • 介绍
    • 快速开始
    • 目录结构
    • .vscode文件夹详解
    • 平台配置
    • 布局
    • 路由和菜单
      • 路由和菜单配置
      • 静态路由
        • 如何生成一级菜单
        • 如何生成二级菜单(两种模式)
        • 第一种(该模式针对父级菜单下只有一个子菜单的情况,在子菜单的 meta 属性中加上 showParent: true 即可)
        • 第二种(该模式针对父级菜单下有大于或等于两个子菜单的情况)
        • 如何生成三级菜单(两种模式)
        • 第一种(该模式针对父级菜单下只有一个子菜单的情况,在子菜单的 meta 属性中加上 showParent: true 即可)
        • 第二种(该模式针对父级菜单下有大于或等于两个子菜单的情况)
        • 如何生成四级菜单(两种模式)
        • 第一种(该模式针对父级菜单下只有一个子菜单的情况,在子菜单的 meta 属性中加上 showParent: true 即可)
        • 第二种(该模式针对父级菜单下有大于或等于两个子菜单的情况)
        • 注意点
        • 国际化
        • 重定向
        • 类型提示
        • 添加无 layout 的页面
        • 禁止修改或删除的路由
        • 如何只要静态路由
      • 动态路由
        • 如何生成一级菜单
        • 只传 path 模式
        • 传 path 和 component 模式
        • 如何生成二级菜单(两种模式)
        • 第一种(该模式针对父级菜单下只有一个子菜单的情况,在子菜单的 meta 属性中加上 showParent: true 即可)
        • 第二种(该模式针对父级菜单下有大于或等于两个子菜单的情况)
        • 如何生成三级菜单(两种模式)
        • 第一种(该模式针对父级菜单下只有一个子菜单的情况,在子菜单的 meta 属性中加上 showParent: true 即可)
        • 第二种(该模式针对父级菜单下有大于或等于两个子菜单的情况)
        • 如何生成四级菜单(两种模式)
        • 第一种(该模式针对父级菜单下只有一个子菜单的情况,在子菜单的 meta 属性中加上 showParent: true 即可)
        • 第二种(该模式针对父级菜单下有大于或等于两个子菜单的情况)
        • 注意点
        • 动态路由缓存本地
        • 国际化
        • 重定向
        • 重置路由
        • 常用场景
        • 基础用法
        • 具体实现代码
      • 静态路由、动态路由相同的配置
        • 内嵌 iframe 和外链
        • 菜单排序 rank
        • 菜单图标 icon
        • 路由页面缓存 keepAlive
        • 页面进场、离场动画定制化 transition
        • 页面和按钮级别权限设置
      • 常见疑问
        • 为什么路由的 name 必写,而且必须唯一
        • 为什么路由的 name 需和页面的 name 保持一致?
      • 更多路由跳转示例代码
    • http请求
    • 打包和部署
  • 进阶

    • 图标(有新的更新)
    • 主题和暗黑模式
    • 国际化
    • Tailwind CSS(有新的更新)
    • RBAC权限
    • 类型声明
    • 单点登录
    • 自定义免登录
    • 打包优化
    • vite预构建
  • 生态

    • 对接平台的前后端项目
    • 函数工具库
    • 组件库
    • vite插件
  • 其他

    • 赞助
    • 常见问题
    • 非平台问题跟踪记录
    • git常用命令
    • 技术网站推荐
目录

路由和菜单

路由和菜单是极为重要的,配置正确很关键,平台尽最大可能详细描述

# 路由和菜单配置

点击查看完整路由和菜单配置
interface RouteConfigsTable {
  // 路由路径
  path: string;
  // 路由名称(必须保持唯一)
  name: string;
  // 路由重定向(默认跳转地址)
  redirect: string;
  // 路由元信息,通俗来说就是路由上的额外信息 https://router.vuejs.org/zh/guide/advanced/meta.html#%E8%B7%AF%E7%94%B1%E5%85%83%E4%BF%A1%E6%81%AF
  meta: {
    // 菜单名称(兼容国际化、非国际化,如果用国际化的写法就必须在根目录的locales文件夹下对应添加)
    title: string;
    // 菜单图标
    icon: string | FunctionalComponent | IconifyIcon;
    // 是否在菜单中显示
    showLink: boolean;
    // 菜单排序,值越高排的越后(只针对顶级路由)
    rank: number;
  };
  // 子路由配置项
  children: [
    {
      // 路由路径
      path: string;
      // 路由名称(必须唯一并且和当前路由component字段对应的页面里用defineOptions包起来的name保持一致)
      name: string;
      // 路由重定向
      redirect: string;
      // 按需加载需要展示的页面
      component: RouteComponent;
      // 路由元信息
      meta: {
        // 菜单名称(兼容国际化、非国际化,如果用国际化的写法就必须在根目录的locales文件夹下对应添加)
        title: string;
        // 菜单图标
        icon: string | FunctionalComponent | IconifyIcon;
        // 菜单名称右侧的额外图标
        extraIcon: string | FunctionalComponent | IconifyIcon;
        // 是否显示该菜单
        showLink: boolean;
        // 是否显示父级菜单
        showParent: boolean;
        // 页面级别权限设置
        roles: Array<string>;
        // 按钮级别权限设置
        auths: Array<string>;
        // 是否缓存该路由页面(开启后,会保存该页面的整体状态,刷新后会清空状态)
        keepAlive: boolean;
        // 需要内嵌的iframe链接地址
        frameSrc: string;
        // 内嵌的iframe页面是否开启首次加载动画
        frameLoading: boolean;
        // 页面加载动画(两种模式,第二种权重更高,第一种直接采用vue内置的transitions动画,第二种是使用animate.css编写进、离场动画,平台更推荐使用第二种模式,已经内置了animate.css,直接写对应的动画名即可)
        transition: {
          // 当前页面动画,这里是第一种模式,比如 name: "fade" 更具体看后面链接 https://cn.vuejs.org/api/built-in-components.html#transition
          name: string;
          // 当前页面进场动画,这里是第二种模式,比如 enterTransition: "animate__fadeInLeft"
          enterTransition: string;
          // 当前页面离场动画,这里是第二种模式,比如 leaveTransition: "animate__fadeOutRight"
          leaveTransition: string;
        };
        // 当前菜单名称或自定义信息禁止添加到标签页
        hiddenTag: boolean;
        // 显示在标签页的最大数量,需满足后面的条件:不显示在菜单中的路由并且是通过query或params传参模式打开的页面。在完整版全局搜dynamicLevel即可查看代码演示
        dynamicLevel: number;
        // 将某个菜单激活(主要用于通过query或params传参的路由,当它们通过配置showLink: false后不在菜单中显示,就不会有任何菜单高亮,而通过设置activePath指定激活菜单即可获得高亮,activePath为指定激活菜单的path)
        activePath: string;
      };
    },
  ],
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

# 静态路由

在前端代码里写上固定的路由即静态路由。平台支持任意层级菜单格式,下面列举最常用的菜单层级模式,更深层级按照下面格式即可

阅读前须知

一般来说一级菜单代表无任何子菜单的菜单,二级菜单代表一级菜单下面有子菜单,三级菜单代表二级菜单下面有子菜单 以此类推

# 如何生成一级菜单

点击查看

比如我们要生成一个名为 加油 的菜单

  1. 新建 src/router/modules/fighting.ts 文件,加入如下代码

fighting.ts

// 最简代码,也就是这些字段必须有
export default {
  path: "/fighting",
  meta: {
    title: "加油"
  },
  children: [
    {
      path: "/fighting/index",
      name: "Fighting",
      component: () => import("@/views/fighting/index.vue"),
      meta: {
        title: "加油"
      }
    }
  ]
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  1. 新建 src/views/fighting/index.vue 文件,加入如下代码

index.vue

<script setup lang="ts">
defineOptions({
  // name 作为一种规范最好必须写上并且和路由的name保持一致
  name: "Fighting"
});
</script>

<template>
  <h1>加油</h1>
</template>
1
2
3
4
5
6
7
8
9
10

上面所写代码如下图效果

img

# 如何生成二级菜单(两种模式)

# 第一种(该模式针对父级菜单下只有一个子菜单的情况,在子菜单的 meta 属性中加上 showParent: true 即可)

点击查看
// 最简代码,也就是这些字段必须有
export default {
  path: "/fighting",
  meta: {
    title: "励志"
  },
  children: [
    {
      path: "/fighting/index",
      name: "Fighting",
      component: () => import("@/views/fighting/index.vue"),
      meta: {
        title: "加油",
        // 通过设置showParent为true,显示父级
        showParent: true
      }
    }
  ]
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

如下图效果

img

# 第二种(该模式针对父级菜单下有大于或等于两个子菜单的情况)

点击查看
// 最简代码,也就是这些字段必须有
export default {
  path: "/fighting",
  meta: {
    title: "励志"
  },
  children: [
    {
      path: "/fighting/index",
      name: "Fighting",
      component: () => import("@/views/fighting/index.vue"),
      meta: {
        title: "加油"
      }
    },
    {
      path: "/fighting/effort",
      name: "Effort",
      component: () => import("@/views/fighting/effort.vue"),
      meta: {
        title: "努力"
      }
    }
  ]
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

如下图效果

img

# 如何生成三级菜单(两种模式)

# 第一种(该模式针对父级菜单下只有一个子菜单的情况,在子菜单的 meta 属性中加上 showParent: true 即可)

点击查看
// 最简代码,也就是这些字段必须有
export default {
  path: "/nested",
  meta: {
    title: "多级菜单"
  },
  children: [
    {
      path: "/nested/menu1",
      meta: {
        title: "菜单1"
      },
      children: [
        {
          path: "/nested/menu1/menu1-1",
          component: () => import("@/views/nested/menu1/menu1-1/index.vue"),
          name: "Menu1-1",
          meta: {
            title: "菜单1-1",
            // 通过设置showParent为true,显示父级
            showParent: true
          }
        }
      ]
    }
  ]
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

如下图效果

img

# 第二种(该模式针对父级菜单下有大于或等于两个子菜单的情况)

点击查看
// 最简代码,也就是这些字段必须有
export default {
  path: "/nested",
  meta: {
    title: "多级菜单"
  },
  children: [
    {
      path: "/nested/menu1",
      meta: {
        title: "菜单1"
      },
      children: [
        {
          path: "/nested/menu1/menu1-1",
          component: () => import("@/views/nested/menu1/menu1-1/index.vue"),
          name: "Menu1-1",
          meta: {
            title: "菜单1-1"
          }
        },
        {
          path: "/nested/menu1/menu1-2",
          component: () => import("@/views/nested/menu1/menu1-2/index.vue"),
          name: "Menu1-2",
          meta: {
            title: "菜单1-2"
          }
        }
      ]
    }
  ]
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

如下图效果

img

# 如何生成四级菜单(两种模式)

# 第一种(该模式针对父级菜单下只有一个子菜单的情况,在子菜单的 meta 属性中加上 showParent: true 即可)

点击查看
// 最简代码,也就是这些字段必须有
export default {
  path: "/nested",
  meta: {
    title: "多级菜单"
  },
  children: [
    {
      path: "/nested/menu1",
      meta: {
        title: "菜单1"
      },
      children: [
        {
          path: "/nested/menu1/menu1-1",
          meta: {
            title: "菜单1-1"
          },
          children: [
            {
              path: "/nested/menu1/menu1-1/menu1-1-1",
              component: () =>
                import("@/views/nested/menu1/menu1-1/menu1-1-1/index.vue"),
              name: "Menu1-1-1",
              meta: {
                title: "菜单1-1-1",
                // 通过设置showParent为true,显示父级
                showParent: true
              }
            }
          ]
        }
      ]
    }
  ]
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

如下图效果

img

# 第二种(该模式针对父级菜单下有大于或等于两个子菜单的情况)

点击查看
// 最简代码,也就是这些字段必须有
export default {
  path: "/nested",
  meta: {
    title: "多级菜单"
  },
  children: [
    {
      path: "/nested/menu1",
      meta: {
        title: "菜单1"
      },
      children: [
        {
          path: "/nested/menu1/menu1-1",
          meta: {
            title: "菜单1-1"
          },
          children: [
            {
              path: "/nested/menu1/menu1-1/menu1-1-1",
              component: () =>
                import("@/views/nested/menu1/menu1-1/menu1-1-1/index.vue"),
              name: "Menu1-1-1",
              meta: {
                title: "菜单1-1-1"
              }
            },
            {
              path: "/nested/menu1/menu1-1/menu1-1-2",
              component: () =>
                import("@/views/nested/menu1/menu1-1/menu1-1-2/index.vue"),
              name: "Menu1-1-2",
              meta: {
                title: "菜单1-1-2"
              }
            }
          ]
        }
      ]
    }
  ]
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

如下图效果

img

# 注意点

# 国际化

只需要在路由 meta 的 title 字段中使用国际化写法即可,比如 title: "menus.hsabnormal"
这个 menus.hsabnormal 必须在 locales 文件夹下进行配置哦,如下图

img

# 重定向

可以看到上面生成菜单的路由配置项我并没有加上 redirect 属性,当使用 fighting 路径跳转时,就会跑到 404 页面

所以我们最好在写静态路由配置时将父级加上 redirect 属性,redirect 可以填写您想要重定向的 path 即可,具体可以参考 src/router/modules/nested.ts (opens new window)

# 类型提示

分两种

  1. export default 后跟 {},这也是最常用的一种,在路由的配置项最后加上 as RouteConfigsTable 即可获得类型提示,如下图

img

  1. export default 后跟 [],在路由的配置项最后加上 as Array<RouteConfigsTable> 即可获得类型提示,如下图

img

您肯定想问 RouteConfigsTable 是哪里来的呢?

我在这呢,快快点我查看 😊 (opens new window)

# 添加无 layout 的页面

使用场景:需要外嵌平台某个页面,不需要展示菜单导航以及额外模块,只需要展示业务内容模块
无 layout 页面也就相当于一个全屏空白页面,没有菜单导航以及额外模块

如何添加?

  1. 在 src/router/modules/remaining.ts 文件添加对应路由 具体参考 (opens new window)

  2. 添加对应业务代码页面 具体参考 (opens new window)

温馨提示

必须在 src/router/modules/remaining.ts 文件添加哦

# 禁止修改或删除的路由

src/router/modules 文件夹里的 home.ts、remaining.ts、error.ts 这三个文件不可修改或删除,因为平台的某些核心逻辑是根据这三个文件作处理

img

如果不想在页面显示,在顶级路由的 meta 属性加上 showLink: false 即可,如下图

img

# 如何只要静态路由

平台在登录时默认会走 getAsyncRoutes (opens new window) 接口请求后端返回的动态路由,默认用 mock (opens new window) 模拟返回,如果只要静态路由,按照下面两个步骤修改即可

  1. 在 src/views/login/index.vue 文件作如下改动
<script setup lang="ts">
import { setToken } from "@/utils/auth";
import { addPathMatch, getTopMenu } from "@/router/utils";
import { usePermissionStoreHook } from "@/store/modules/permission";

const onLogin = async (formEl: FormInstance | undefined) => {
  if (!formEl) return;
  await formEl.validate(valid => {
    if (valid) {
      loading.value = true;
      setToken({
        username: "admin",
        roles: ["admin"],
        accessToken: "eyJhbGciOiJIUzUxMiJ9.admin"
      } as any);
      // 全部采取静态路由模式
      usePermissionStoreHook().handleWholeMenus([]);
      addPathMatch();
      router.push(getTopMenu(true).path);
      message("登录成功", { type: "success" });
      loading.value = false;
    }
  });
};
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

img

  1. 在 src/router/index.ts 文件作如下改动
import { addPathMatch } from "./utils";

// 使用下面方法替换initRouter
usePermissionStoreHook().handleWholeMenus([]);
addPathMatch();
if (!useMultiTagsStoreHook().getMultiTagsCache) {
  const { path } = to;
  const route = findRouteByPath(path, router.options.routes[0].children);
  getTopMenu(true);
  // query、params模式路由传参数的标签页不在此处处理
  if (route && route.meta?.title) {
    if (isAllEmpty(route.parentId) && route.meta?.backstage) {
      // 此处为动态顶级路由(目录)
      const { path, name, meta } = route.children[0];
      useMultiTagsStoreHook().handleTags("push", {
        path,
        name,
        meta,
      });
    } else {
      const { path, name, meta } = route;
      useMultiTagsStoreHook().handleTags("push", {
        path,
        name,
        meta,
      });
    }
  }
}
// 确保动态路由完全加入路由列表并且不影响静态路由(注意:动态路由刷新时router.beforeEach可能会触发两次,第一次触发动态路由还未完全添加,第二次动态路由才完全添加到路由列表,如果需要在router.beforeEach做一些判断可以在to.name存在的条件下去判断,这样就只会触发一次)
if (isAllEmpty(to.name)) router.push(to.fullPath);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

img img

# 动态路由

后端返回的路由即动态路由。平台支持任意层级菜单格式,下面列举最常用的菜单层级模式,更深层级按照下面格式即可

平台用 mock 模拟后端,下面举例也是用 mock 举例哦,当然和后端返回的路由格式是一模一样的

阅读前须知

后端传 path 或传 component,平台对这两种模式都做了兼容 具体实现代码 1 (opens new window) 具体实现代码 2 (opens new window)

第一种:下面所有生成菜单例子中的 path 都有个规则:如果这个 path 对应的是实际业务 .vue 或 .tsx 代码,就必须跟实际业务 .vue 或 .tsx 代码路径保持一致

第二种:菜单的 component 对应实际业务 .vue 或 .tsx 代码路径,这时 path 可以随便写啦,但是前面必须有个 / ,比如 path: '/anything' (这种情况我只在 如何生成一级菜单 里举例,多级菜单写法类似)

点击查看具体实现代码逻辑解析



 









 













 
 
 
 








// 具体实现代码1高亮处解析:
// 将/src/views/文件里所有的.vue和.tsx文件塞进modulesRoutes变量
// 当然这里只匹配了`.vue`和`.tsx`,如果您想匹配别的在下面 {} 加上即可,记得用 , 分开
const modulesRoutes = import.meta.glob("/src/views/**/*.{vue,tsx}");

// 具体实现代码2高亮处解析:
// addAsyncRoutes函数作用为过滤后端传来的动态路由,重新生成规范路由,
// 先读取后端返回的所有路由,从中过滤出component和path字段,然后在modulesRoutesKeys中对应匹配,
// 如果对应路由同时匹配到component和path,优先以component为主,这样您的path路径就可以随便写啦,
// 如果匹配到path未匹配到component,path必须按上面提到的跟实际业务`.vue`或`.tsx`代码路径保持一致,
// 如果匹配到component未匹配到path或者两个都未匹配到,我想问问您咱能好好玩不😭
function addAsyncRoutes(arrRoutes: Array<RouteRecordRaw>) {
  if (!arrRoutes || !arrRoutes.length) return;
  const modulesRoutesKeys = Object.keys(modulesRoutes);
  arrRoutes.forEach((v: RouteRecordRaw) => {
    // 将backstage属性加入meta,标识此路由为后端返回路由
    v.meta.backstage = true;
    // 父级的redirect属性取值:如果子级存在且父级的redirect属性不存在,默认取第一个子级的path;如果子级存在且父级的redirect属性存在,取存在的redirect属性,会覆盖默认值
    if (v?.children && v.children.length && !v.redirect)
      v.redirect = v.children[0].path;
    // 父级的name属性取值:如果子级存在且父级的name属性不存在,默认取第一个子级的name;如果子级存在且父级的name属性存在,取存在的name属性,会覆盖默认值(注意:测试中发现父级的name不能和子级name重复,如果重复会造成重定向无效(跳转404),所以这里给父级的name起名的时候后面会自动加上`Parent`,避免重复)
    if (v?.children && v.children.length && !v.name)
      v.name = (v.children[0].name as string) + "Parent";
    if (v.meta?.frameSrc) {
      v.component = IFrame;
    } else {
      // 对后端传component组件路径和不传做兼容(如果后端传component组件路径,那么path可以随便写,如果不传,component组件路径会跟path保持一致)
      const index = v?.component
        ? modulesRoutesKeys.findIndex(ev => ev.includes(v.component as any))
        : modulesRoutesKeys.findIndex(ev => ev.includes(v.path));
      v.component = modulesRoutes[modulesRoutesKeys[index]];
    }
    if (v?.children && v.children.length) {
      addAsyncRoutes(v.children);
    }
  });
  return arrRoutes;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

# 如何生成一级菜单

# 只传 path 模式

点击查看

比如我们要生成一个名为 加油 的菜单

  1. 来到 mock/asyncRoutes.ts 文件,加入如下代码

asyncRoutes.ts

// 最简代码,也就是这些字段必须有
const fightingRouter = {
  path: "/fighting",
  meta: {
    title: "加油"
  },
  children: [
    {
      path: "/fighting/index",
      name: "Fighting",
      meta: {
        title: "加油"
      }
    }
  ]
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  1. 新建 src/views/fighting/index.vue 文件,加入如下代码

index.vue

<script setup lang="ts">
defineOptions({
  // name 作为一种规范最好必须写上并且和路由的name保持一致
  name: "Fighting"
});
</script>

<template>
  <h1>加油</h1>
</template>
1
2
3
4
5
6
7
8
9
10

上面所写代码如下图效果

img

# 传 path 和 component 模式

点击查看

比如我们要生成一个名为 加油 的菜单

  1. 来到 mock/asyncRoutes.ts 文件,加入如下代码

asyncRoutes.ts

// 最简代码,也就是这些字段必须有
const fightingRouter = {
  path: "/fighting",
  meta: {
    title: "加油"
  },
  children: [
    {
      // path随便写,但前面必须有个 `/`
      path: "/anything",
      // component对应的值前不需要加 / 值对应的是实际业务 `.vue` 或 `.tsx` 代码路径
      component: "fighting/index",
      name: "Fighting",
      meta: {
        title: "加油"
      }
    }
  ]
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  1. 新建 src/views/fighting/index.vue 文件,加入如下代码

index.vue

<script setup lang="ts">
defineOptions({
  // name 作为一种规范最好必须写上并且和路由的name保持一致
  name: "Fighting"
});
</script>

<template>
  <h1>加油</h1>
</template>
1
2
3
4
5
6
7
8
9
10

上面所写代码如下图效果

img

猜您可能有这种想法 🤔️

img

# 如何生成二级菜单(两种模式)

# 第一种(该模式针对父级菜单下只有一个子菜单的情况,在子菜单的 meta 属性中加上 showParent: true 即可)

点击查看
// 最简代码,也就是这些字段必须有
const fightingRouter = {
  path: "/fighting",
  meta: {
    title: "励志"
  },
  children: [
    {
      path: "/fighting/index",
      name: "Fighting",
      meta: {
        title: "加油",
        // 通过设置showParent为true,显示父级
        showParent: true
      }
    }
  ]
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

如下图效果

img

# 第二种(该模式针对父级菜单下有大于或等于两个子菜单的情况)

点击查看
// 最简代码,也就是这些字段必须有
const fightingRouter = {
  path: "/fighting",
  meta: {
    title: "励志"
  },
  children: [
    {
      path: "/fighting/index",
      name: "Fighting",
      meta: {
        title: "加油"
      }
    },
    {
      path: "/fighting/effort",
      name: "Effort",
      meta: {
        title: "努力"
      }
    }
  ]
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

如下图效果

img

# 如何生成三级菜单(两种模式)

# 第一种(该模式针对父级菜单下只有一个子菜单的情况,在子菜单的 meta 属性中加上 showParent: true 即可)

点击查看
// 最简代码,也就是这些字段必须有
const nestedRouter = {
  path: "/nested",
  meta: {
    title: "多级菜单"
  },
  children: [
    {
      path: "/nested/menu1",
      meta: {
        title: "菜单1"
      },
      children: [
        {
          path: "/nested/menu1/menu1-1/index",
          name: "Menu1-1",
          meta: {
            title: "菜单1-1",
            // 通过设置showParent为true,显示父级
            showParent: true
          }
        }
      ]
    }
  ]
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

如下图效果

img

# 第二种(该模式针对父级菜单下有大于或等于两个子菜单的情况)

点击查看
// 最简代码,也就是这些字段必须有
const nestedRouter = {
  path: "/nested",
  meta: {
    title: "多级菜单"
  },
  children: [
    {
      path: "/nested/menu1",
      meta: {
        title: "菜单1"
      },
      children: [
        {
          path: "/nested/menu1/menu1-1/index",
          name: "Menu1-1",
          meta: {
            title: "菜单1-1"
          }
        },
        {
          path: "/nested/menu1/menu1-2/index",
          name: "Menu1-2",
          meta: {
            title: "菜单1-2"
          }
        }
      ]
    }
  ]
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

如下图效果

img

# 如何生成四级菜单(两种模式)

# 第一种(该模式针对父级菜单下只有一个子菜单的情况,在子菜单的 meta 属性中加上 showParent: true 即可)

点击查看
// 最简代码,也就是这些字段必须有
const nestedRouter = {
  path: "/nested",
  meta: {
    title: "多级菜单"
  },
  children: [
    {
      path: "/nested/menu1",
      meta: {
        title: "菜单1"
      },
      children: [
        {
          path: "/nested/menu1/menu1-1",
          meta: {
            title: "菜单1-1"
          },
          children: [
            {
              path: "/nested/menu1/menu1-1/menu1-1-1/index",
              name: "Menu1-1-1",
              meta: {
                title: "菜单1-1-1",
                // 通过设置showParent为true,显示父级
                showParent: true
              }
            }
          ]
        }
      ]
    }
  ]
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

如下图效果

img

# 第二种(该模式针对父级菜单下有大于或等于两个子菜单的情况)

点击查看
// 最简代码,也就是这些字段必须有
const nestedRouter = {
  path: "/nested",
  meta: {
    title: "多级菜单"
  },
  children: [
    {
      path: "/nested/menu1",
      meta: {
        title: "菜单1"
      },
      children: [
        {
          path: "/nested/menu1/menu1-1",
          meta: {
            title: "菜单1-1"
          },
          children: [
            {
              path: "/nested/menu1/menu1-1/menu1-1-1/index",
              name: "Menu1-1-1",
              meta: {
                title: "菜单1-1-1"
              }
            },
            {
              path: "/nested/menu1/menu1-1/menu1-1-2/index",
              name: "Menu1-1-2",
              meta: {
                title: "菜单1-1-2"
              }
            }
          ]
        }
      ]
    }
  ]
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

如下图效果

img

# 注意点

# 动态路由缓存本地

平台在 v3.9.7 版本 默认关闭 CachingAsyncRoutes 动态路由缓存本地 (opens new window) 使其在开发环境下调试更方便,不用每次修改动态路由都要先清空本地缓存的动态路由,更推荐在生产环境开启

开启动态路由缓存本地后会优先读取本地缓存的路由 点击查看具体实现代码 (opens new window) 如果您在开发环境下开启,每次修改动态路由都需要先清空 async-routes 这个键名的 sessionStorage 点击查看参考代码 (opens new window)

img

# 国际化

只需要在路由 meta 的 title 字段中使用国际化写法即可,比如 title: "menus.hssysManagement"
这个 menus.hssysManagement 必须在 locales 文件夹下进行配置哦,如下图

img

# 重定向

动态路由无需填写 redirect 重定向属性 平台自动处理,逻辑为:如果子级存在且父级的 redirect 属性不存在,默认取第一个子级的 path 作为父级的 redirect

当然您也可以指定父级的 redirect 重定向属性,平台监测到存在指定父级的 redirect 后,会取指定的属性

点击查看具体实现代码 (opens new window)

# 重置路由

平台的重置路由功能只针对动态路由

# 常用场景

用户切换角色或退出登录时需调用重置路由功能,将路由和菜单恢复到初始化状态

# 基础用法
import { resetRouter } from "@/router";

resetRouter();
1
2
3
# 具体实现代码

点击查看具体实现代码 (opens new window)

# 静态路由、动态路由相同的配置

下面以动态路由返回举例,静态路由也是相同的配置,区别是静态路由需写在 src/router/modules 里

# 内嵌 iframe 和外链

点击查看

读重点:内嵌 iframe 将地址写进 frameSrc 属性里;外链将地址写进 name 属性里
具体代码实现:内嵌 iframe (opens new window) 外链 (opens new window)

// 最简代码,也就是这些字段必须有
const frameRouter = {
  path: "/iframe",
  meta: {
    title: "内嵌 iframe 和外链"
  },
  children: [
    // 内嵌 iframe
    {
      path: "/iframe/pure",
      // name必须写,写法随意
      name: "anything",
      meta: {
        title: "内嵌 iframe",
        // 需要内嵌页面的地址
        frameSrc: "https://pure-admin.cn",
        // 内嵌的iframe页面是否开启首次加载动画,默认true,下面可以不写,如果内嵌的iframe页面已经存在首加载动画,可以将frameLoading设为false
        // frameLoading: true
      }
    },
    // 外链
    {
      // path必须写,必须以 / 开头,推荐格式 / 后跟随意单词,不同的外链path不要重复哦
      path: "/anything",
      // 外链地址写在name属性里
      name: "https://pure-admin.cn",
      meta: {
        title: " 外链"
      }
    }
  ]
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

如下图效果

img

# 菜单排序 rank

在路由的 meta 里加入 rank 字段即可对菜单进行排序,rank 可不写、可为 null、可为 undefined、可为空值,但是平台推荐 rank 最好加上,对菜单进行有序排列使其看起来整齐,而且还能通过排序把您想要第一眼看到的菜单放到最前面

点击查看具体实现代码 (opens new window)

温馨提示

菜单排序 rank 只对顶级菜单生效 点击查看为何如此设计 (opens new window)
img

# 菜单图标 icon

点击查看如何配置菜单图标

# 路由页面缓存 keepAlive

在路由的 meta 里加入 keepAlive: true 即可对当前路由页面进行缓存,被缓存的路由页面不会被销毁

生效前提:对应页面 name 必须与路由的 name 保持一致

img

虽然 vue-router 官方只支持到二级 keep-alive 缓存,但平台对路由进行了 处理 (opens new window),只要对当前路由设置了 keepAlive: true,并且对应的页面 name 与路由的 name 保持一致,不管层级多深都会支持 keep-alive 缓存

# 页面进场、离场动画定制化 transition

在路由的 meta 里加入下面代码即可定制化单个页面进场、离场动画,平台内置了 Animate.css ,所以您直接在 Animate.css (opens new window) 最右侧复制所需动画即可

transition: {
  // 页面进场动画
  enterTransition: "animate__fadeInRight",
  // 页面离场动画
  leaveTransition: "animate__fadeOutDown"
}
1
2
3
4
5
6

点击查看具体实现代码 (opens new window)

# 页面和按钮级别权限设置

平台内置 RBAC 权限模式,无论静态还是动态路由,配置都会生效,点击查看具体 RBAC 权限讲解

# 常见疑问

# 为什么路由的 name 必写,而且必须唯一

  1. 没有硬编码的 URL ;params 的自动编码/解码;防止在 url 中出现打字错误;绕过路径排序(如显示一个) 点击了解具体 (opens new window)
  2. 虽然跳转路由有很多方式,但平台只推荐使用 name 跳转,如下面写法。优点:一是写法简单,尤其当 path 稍长时优点更突出,因为 name 只有一个单词;二是规范统一,都用 name 跳转也方便维护。尤其当项目过大时对整体维护起关键作用
<script setup lang="ts">
import { useRouter } from "vue-router";

const router = useRouter();
router.push({ name: 'Fighting' })
</script>
1
2
3
4
5
6
  1. 平台的某些关键模块(路由模块、标签模块等)都依赖 name,所以独一无二的 name 对平台的稳定性起关键作用

# 为什么路由的 name 需和页面的 name 保持一致?

正常情况下这并不是硬性要求,而是一种规范,方便维护查找,通过路由 name 全局搜索即可知道对应页面代码位置,通过页面对应的 name 也可知道其对应哪个路由

当然,如果当前路由页面需要 路由页面缓存 keepAlive 就必须保持一致

# 更多路由跳转示例代码

点击查看示例代码 (opens new window)

效果如下

img

上次更新: 2025/05/09, 02:23:01
布局
http请求

← 布局 http请求→

最近更新
更多文章>
starsstars18k18k
forksforks3.4k3.4k
Theme by Vdoing | Copyright © 2020-2025 pure-admin | MIT License