路由

项目路由配置存放于 src/router 下面, src/router/modules 用于存放路由模块,在该目录下的文件会自动注册。


# 模块说明

在 src/router/modules 内的 ts 文件会被视为一个路由模块。



import { RouteRecordRaw } from 'vue-router';
import { Layout } from '@/router/constant';
import { DashboardOutlined } from '@vicons/antd';
import { renderIcon } from '@/utils/index';

const routeName = 'dashboard';

const routes: Array<RouteRecordRaw> = [
  {
    path: '/dashboard',
    name: routeName,
    redirect: '/dashboard/console',
    component: Layout,
    meta: {
      title: 'Dashboard',
      icon: renderIcon(DashboardOutlined),
      permissions: ['dashboard_console', 'dashboard_console', 'dashboard_workplace'],
      sort: 0,
    },
    children: [
      {
        path: 'console',
        name: `${routeName}_console`,
        meta: {
          title: '主控台',
          permissions: ['dashboard_console'],
        },
        component: () => import('@/views/dashboard/console/console.vue'),
      },
      {
        path: 'workplace',
        name: `${routeName}_workplace`,
        meta: {
          title: '工作台',
          keepAlive: true,
          permissions: ['dashboard_workplace'],
        },
        component: () => import('@/views/dashboard/workplace/workplace.vue'),
      },
    ],
  },
];
export default routes;


# 多级路由

注意事项 整个项目所有路由 name 不能重复

除了 layout 对应的 path 前面需要加 / ,其余子路由都不要以 / 开头

多级路由,当没有配置时, redirect 默认为第一个子路由,配置则优先按配置


import { RouteRecordRaw } from 'vue-router';
import { Layout, ParentLayout } from '@/router/constant';
import { WalletOutlined } from '@vicons/antd';
import { renderIcon } from '@/utils/index';

const routeName = 'comp';

const routes: Array<RouteRecordRaw> = [
  {
    path: '/comp',
    name: routeName,
    component: Layout,
    redirect: '/comp/table',
    meta: {
      title: '组件示例',
      icon: renderIcon(WalletOutlined),
      sort: 8,
    },
    children: [
      {
        path: 'table',
        name: `${routeName}_table`,
        redirect: '/comp/table/basic',
        component: ParentLayout,
        meta: {
          title: '表格',
        },
        children: [
          {
            path: 'basic',
            name: `${routeName}_table_basic`,
            meta: {
              title: '基础表格',
            },
            component: () => import('@/views/comp/table/basic.vue'),
          },
          {
            path: 'editCell',
            name: `${routeName}_table_editCell`,
            meta: {
              title: '单元格编辑',
            },
            component: () => import('@/views/comp/table/editCell.vue'),
          },
          {
            path: 'editRow',
            name: `${routeName}_table_editRow`,
            meta: {
              title: '整行编辑',
            },
            component: () => import('@/views/comp/table/editRow.vue'),
          },
        ],
      },
      {
        path: 'upload',
        name: `${routeName}_upload`,
        meta: {
          title: '上传',
        },
        component: () => import('@/views/comp/upload/index.vue'),
      },
    ],
  },
];
export default routes;


# Meta 配置说明

export interface RouteMeta {
  //菜单名称 必填
  title: string;
  //禁用菜单
  disabled:boolean;
  //隐藏菜单 路由依然可访问
  hidden: boolean;
  //菜单图标  
  icon: VNode;
  //缓存该路由
  keepAlive: boolean;
  //排序越小越排前
  sort: number;
  //取消自动计算根路由模式 开启之后,当菜单子菜单只有1个的时候,会直接显示子菜单
  alwaysShow: boolean
  // 当路由设置了该属性,则会高亮相对应的侧边栏。
  // 这在某些场景非常有用,比如:一个列表页路由为:/list/basic-list
  // 点击进入详情页,这时候路由为/list/basic-info/1,但你想在侧边栏高亮列表的路由,就可以进行如下设置
  // 注意是配置高亮路由 `name`,不是path
  activeMenu: string;
  //是否跟路由 顶部混合菜单,必须传 true,否则左侧会显示异常(场景就是,分割菜单之后,当一级菜单没有子菜单)
  isRoot: boolean;
  //内联外部地址
  frameSrc: string;
  //菜单包含权限集合,满足其中一个就会显示
  permissions: string[];
  //是否固定标签 比如 常见的主控台 或者特定页面
  affix: boolean;
}


# 图标

为了简化使用,只需用renderIcon(WalletOutlined)方法,传入 xicons 中图标即可


# 新增路由

在 src/router/modules 内新增一个模块文件。

export interface RouteMeta {
  //菜单名称 必填
  title: string;
  //禁用菜单
  disabled:boolean;
  //隐藏菜单 路由依然可访问
  hidden: boolean;
  //菜单图标  
  icon: VNode;
  //缓存该路由
  keepAlive: boolean;
  //排序越小越排前
  sort: number;
  //取消自动计算根路由模式 开启之后,当菜单子菜单只有1个的时候,会直接显示子菜单
  alwaysShow: boolean
  // 当路由设置了该属性,则会高亮相对应的侧边栏。
  // 这在某些场景非常有用,比如:一个列表页路由为:/list/basic-list
  // 点击进入详情页,这时候路由为/list/basic-info/1,但你想在侧边栏高亮列表的路由,就可以进行如下设置
  // 注意是配置高亮路由 `name`,不是path
  activeMenu: string;
  //是否跟路由 顶部混合菜单,必须传 true,否则左侧会显示异常(场景就是,分割菜单之后,当一级菜单没有子菜单)
  isRoot: boolean;
  //内联外部地址
  frameSrc: string;
  //菜单包含权限集合,满足其中一个就会显示
  permissions: string[];
  //是否固定标签 比如 常见的主控台 或者特定页面
  affix: boolean;
}


此时路由已添加完成,不需要手动引入,放在 src/router/modules 内的文件会自动加载。


# 验证

访问 ip:端口/#/result/success 出现对应组件内容即代表成功


# 路由刷新

项目中采用的是重定向方式

//使用
import { useRoute } from 'vue-router';
const route = useRoute();
router.push({
    path: '/redirect' + unref(route).fullPath,
});

//实现
import { defineComponent, onBeforeMount } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { NEmpty } from 'naive-ui';

export default defineComponent({
  name: 'Redirect',
  setup() {
    const route = useRoute();
    const router = useRouter();
    onBeforeMount(() => {
      const { params, query } = route;
      const { path } = params;
      router.replace({
        path: '/' + (Array.isArray(path) ? path.join('/') : path),
        query,
      });
    });
    return () => <NEmpty />;
  },
});


# 外联

在侧边栏中配置一个外链,只要你在 name 中填写了合法的 url 路径,当你点击侧边栏的时候就会帮你新开这个页面。

{
    path: '/external',
    name: 'https://jekip.github.io/docs/',
    meta: {
        title: '文档地址',
        icon: renderIcon(CheckCircleOutlined),
        sort: 4,
    }
}


# 内联

在侧边栏中配置一个内联外部地址,只要你在 meta.frameSrc填写了合法的 url路径,当你点击侧边栏的时候就会帮你内联显示这个页面。

import { RouteRecordRaw } from 'vue-router';
import { Layout } from '@/router/constant';
import { DesktopOutline } from '@vicons/ionicons5';
import { renderIcon } from '@/utils/index';

const IFrame = () => import('@/views/iframe/index.vue');
const routes: Array<RouteRecordRaw> = [
  {
    path: '/frame',
    name: 'Frame',
    redirect: '/frame/docs',
    component: Layout,
    meta: {
      title: '外部页面',
      sort: 9,
      icon: renderIcon(DesktopOutline),
    },
    children: [
      {
        path: 'docs',
        name: 'frame-docs',
        meta: {
          title: '项目文档(内嵌)',
          frameSrc: 'https://naive-ui-admin-docs.vercel.app',
        },
        component: IFrame,
      },
      {
        path: 'naive',
        name: 'frame-naive',
        meta: {
          title: 'NaiveUi(内嵌)',
          frameSrc: 'https://www.naiveui.com',
        },
        component: IFrame,
      },
    ],
  },
];
export default routes;


# 根路由

系统已经帮你做了判断,当一个路由下面的children声明的路由大于 >1 个时,自动会变成嵌套的模式

如果子路由正好等于一个就会默认将子路由作为根路由显示在侧边栏中,若不想这样,可以通过设置在根路由meta中设置 alwaysShow: true来取消这一特性

{
  path: '/external',
  name: 'external',
  component: Layout,
  meta: {
    sort: 4, //排序依然还是在这里定义
  },
  children: [
    {
      path: 'console',
      name: `console`,
      meta: {
        title: '主控台',
        permission: ['dashboard_console'],
      },
      component: () => import('@/views/dashboard/console/console.vue'),
    }
  ]
}


# 页面缓存

开启缓存有3个条件

1、在router中meta内将keepAlive设置为truealwaysShow: true来取消这一特性

2、路由设置name且不能重复 alwaysShow: true来取消这一特性

3、路由对应的组件加上name与路由设置的name保持一致

注、页面缓存,只支持到二级路由,三级或以上路由无法缓存

 //路由配置
 {
    name: 'Login',// 对应组件组件的name
    component: () => import('@/views/login/index.vue'),
  };
  //组件配置
  export default defineComponent({
    name:"Login" // 需要和路由的name一致
  });


# history 模式

项目默认是history模式,当您需要配置VITE_PUBLIC_PATH 为目录的时候,初始化路由的时候,也需要设置目录

比如,VITE_PUBLIC_PATH设置成:test-admin 那么 src\router\index.ts 中 history: createWebHistory('test-admin'),推荐是通过wrapperEnv函数获取VITE_PUBLIC_PATH配置