路由

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

vue-ts-vite-admin> yarn add vue-router@next

package.json

  "dependencies": {
    "vue": "^3.0.5",
    "vue-router": "^4.0.10"
  },

配置

模块说明

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

router\routes\index.ts import.meta.globEager('./modules/**/*.ts')

一个路由模块包含以下结构

import type { AppRouteModule } from '/@/router/types';

import { LAYOUT } from '/@/router/constant';
import { t } from '/@/hooks/web/useI18n';

const dashboard: AppRouteModule = {
  path: '/dashboard',
  name: 'Dashboard',
  component: LAYOUT,
  redirect: '/dashboard/analysis',
  meta: {
    icon: 'ion:grid-outline',
    title: t('routes.dashboard.dashboard'),
  },
  children: [
    {
      path: 'analysis',
      name: 'Analysis',
      component: () => import('/@/views/dashboard/analysis/index.vue'),
      meta: {
        affix: true,
        title: t('routes.dashboard.analysis'),
      },
    },
    {
      path: 'workbench',
      name: 'Workbench',
      component: () => import('/@/views/dashboard/workbench/index.vue'),
      meta: {
        title: t('routes.dashboard.workbench'),
      },
    },
  ],
};
export default dashboard;

多级路由

注意事项

  • 整个项目所有路由 name 不能重复
  • 所有的多级路由最终都会转成二级路由,所以不能内嵌子路由
  • 除了 layout 对应的 path 前面需要加 /,其余子路由都不要以/开头

示例

import type { AppRouteModule } from '/@/router/types';
import { getParentLayout, LAYOUT } from '/@/router/constant';
import { t } from '/@/hooks/web/useI18n';
const permission: AppRouteModule = {
  path: '/level',
  name: 'Level',
  component: LAYOUT,
  redirect: '/level/menu1/menu1-1/menu1-1-1',
  meta: {
    icon: 'ion:menu-outline',
    title: t('routes.demo.level.level'),
  },

  children: [
    {
      path: 'menu1',
      name: 'Menu1Demo',
      component: getParentLayout('Menu1Demo'),
      meta: {
        title: 'Menu1',
      },
      redirect: '/level/menu1/menu1-1/menu1-1-1',
      children: [
        {
          path: 'menu1-1',
          name: 'Menu11Demo',
          component: getParentLayout('Menu11Demo'),
          meta: {
            title: 'Menu1-1',
          },
          redirect: '/level/menu1/menu1-1/menu1-1-1',
          children: [
            {
              path: 'menu1-1-1',
              name: 'Menu111Demo',
              component: () => import('/@/views/demo/level/Menu111.vue'),
              meta: {
                title: 'Menu111',
              },
            },
          ],
        },
      ],
    },
  ],
};

export default permission;

Meta 配置说明

export interface RouteMeta {
  // 路由title  一般必填
  title: string;
  // 是否忽略权限,只在权限模式为Role的时候有效
  ignoreAuth?: boolean;
  // 可以访问的角色,只在权限模式为Role的时候有效
  roles?: RoleEnum[];
  // 是否忽略KeepAlive缓存
  ignoreKeepAlive?: boolean;
  // 是否固定标签
  affix?: boolean;
  // 图标,也是菜单图标
  icon?: string;
  // 内嵌iframe的地址
  frameSrc?: string;
  // 指定该路由切换的动画名
  transitionName?: string;
  // 隐藏该路由在面包屑上面的显示
  hideBreadcrumb?: boolean;
  // 如果该路由会携带参数,且需要在tab页上面显示。则需要设置为true
  carryParam?: boolean;
  // 隐藏所有子菜单
  hideChildrenInMenu?: boolean;
  // 当前激活的菜单。用于配置详情页时左侧激活的菜单路径
  currentActiveMenu?: string;
  // 当前路由不再标签页显示
  hideTab?: boolean;
  // 当前路由不再菜单显示
  hideMenu?: boolean;
  // 菜单排序,只对第一级有效
  orderNo?: number;
}

外部页面嵌套

只需要将 frameSrc 设置为需要跳转的地址即可

const IFrame = () => import('/@/views/sys/iframe/FrameBlank.vue');
{
  path: 'doc',
  name: 'Doc',
  component: IFrame,
  meta: {
    frameSrc: 'https://vvbin.cn/doc-next/',
    title: t('routes.demo.iframe.doc'),
  },
},

外链

只需要将 path 设置为需要跳转的HTTP 地址即可

{
  path: 'https://vvbin.cn/doc-next/',
  name: 'DocExternal',
  component: IFrame,
  meta: {
    title: t('routes.demo.iframe.docExternal'),
  },
}

图标

这里的 icon 配置,会同步到 菜单

icon 的值可以查看 Icon

新增路由

如何新增一个路由模块

  1. src/router/routes/modules内新增一个模块文件

示例,在src/router/routes/modules下新增 test.ts 文件

import type { AppRouteModule } from '/@/router/types';
import { LAYOUT } from '/@/router/constant';
import { t } from '/@/hooks/web/useI18n';

const dashboard: AppRouteModule = {
  path: '/about',
  name: 'About',
  component: LAYOUT,
  redirect: '/about/index',
  meta: {
    icon: 'simple-icons:about-dot-me',
    title: t('routes.dashboard.about'),
  },
  children: [
    {
      path: 'index',
      name: 'AboutPage',
      component: () => import('/@/views/sys/about/index.vue'),
      meta: {
        title: t('routes.dashboard.about'),
        icon: 'simple-icons:about-dot-me',
      },
    },
  ],
};

export default dashboard;

到这里你路由已经添加完成,不需要手动引入,放在src/router/routes/modules内的文件会自动被加载

验证

您可以尝试访问 ip:端口/about/index出现对应组件内容即代表成功

路由刷新

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

实现

import { useRedo } from '/@/hooks/web/usePage';
import { defineComponent } from 'vue';
export default defineComponent({
  setup() {
    const redo = useRedo();
    // 执行刷新
    redo();
    return {};
  },
});

Redirect

src/views/sys/redirect/index.vue

import { defineComponent, unref } from 'vue';
import { useRouter } from 'vue-router';
export default defineComponent({
  name: 'Redirect',
  setup() {
    const { currentRoute, replace } = useRouter();
    const { params, query } = unref(currentRoute);
    const { path } = params;
    const _path = Array.isArray(path) ? path.join('/') : path;
    replace({
      path: '/' + _path,
      query,
    });
    return {};
  },
});

页面跳转

页面跳转建议采用项目提供的 useGo

方式

import { useGo } from '/@/hooks/web/usePage';
import { defineComponent } from 'vue';
export default defineComponent({
  setup() {
    const go = useGo();

    // 执行刷新
    go();
    go(PageEnum.Home);
    return {};
  },
});

多标签页

标签页使用的是 keep-aliverouter-view 实现,实现切换 tab 后还能保存切换之前的状态。

如何开启页面缓存

开启缓存有 3 个条件

  1. src/settings/projectSetting.ts内将openKeepAlive设置为true
  2. 路由设置 name,且不能重复
  3. 路由对应的组件加上 name,与路由设置的 name 保持一致
 {
   ...,
    // name
    name: 'Login',
    // 对应组件组件的name
    component: () => import('/@/views/sys/login/index.vue'),
    ...
  },

  // /@/views/sys/login/index.vue
  export default defineComponent({
    // 需要和路由的name一致
    name:"Login"
  });

注意

keep-alive 生效的前提是

include - 字符串或正则表达式,只有名称匹配的组件会被缓存

所以需要将路由的 name 属性及对应的页面的 name 设置成一样

如何让某个页面不缓存

可在 router.meta 下配置

可以将 ignoreKeepAlive配置成true即可关闭缓存。

export interface RouteMeta {
  // 是否忽略KeepAlive缓存
  ignoreKeepAlive?: boolean;
}