安装
pnpm add vue-router@4
一、创建路由配置
1.1、创建 src/router/routes/index.ts 文件,并进行路由配置
// src/router/routes/index.ts
import type { RouteRecordRaw } from 'vue-router'
import { DashboardOutlined, HomeOutlined } from '@ant-design/icons-vue'
import { LOGIN_NAME, PAGE_NOT_FOUND_NAME } from '../constant'
export const routesList: Array<RouteRecordRaw> = [
{
path: '/',
name: 'Layout',
redirect: '/dashboard/welcome',
component: () => import('@/layout/layout.vue'), // 需要创建 src/layout/layout.vue 文件否则会报错
meta: {
title: '首页',
fullPath: '/',
},
children: [
{
path: '/dashboard',
name: 'Dashboard',
redirect: '/dashboard/welcome',
meta: {
title: '概览页',
fullPath: '/dashboard',
icon: DashboardOutlined,
},
children: [
{
path: 'welcome',
name: 'DashboardWelcome',
meta: {
title: '工作台',
fullPath: '/dashboard/welcome',
icon: HomeOutlined,
},
component: () => import('@/views/dashboard/welcome/welcome.vue'), // 需要创建 src/views/dashboard/welcome/welcome.vue 文件否则会报错
},
],
},
{
path: '/account',
redirect: '/account/settings',
name: 'Account',
meta: {
title: '个人中心',
fullPath: '/account',
hideInMenu: true,
},
children: [
{
path: 'settings',
name: 'AccountSettings',
component: () => import('@/views/account/settings/settings.vue'),
meta: {
title: '个人设置',
fullPath: '/account/settings',
},
},
],
},
],
},
{
path: '/login',
name: LOGIN_NAME,
component: () => import('@/views/login/login.vue'),
meta: {
title: '登录',
},
},
{
path: '/:pathMatch(.*)*', // 捕获所有路由或 404 Not found 路由
name: PAGE_NOT_FOUND_NAME,
meta: {
title: '404 Not found',
},
component: () => import('@/views/error/404.vue'), // 需要创建 src/views/error/404.vue 文件否则会报错
}
];
1.2、创建 types/vue-router.d.ts 自定义路由配置项 meta: RouteMeta 的类型声明
// types/vue-router.d.ts
import type { RouteMeta as VRouteMeta } from 'vue-router'
declare module 'vue-router' {
interface RouteMeta extends VRouteMeta {
/** 标题 */
title: string;
/** 当前路由所在的完整路径, 用来实现登录后的重定向功能 */
fullPath: string
/**
* 菜单图标; antd 图标名称, 这是临时写法
* TODO: 后期还要根据 antd icon 中提供的方法,进行自定义一些图标,但是需要注意的是,自定义图标后需要验证侧边栏菜单中的图标是否还能正常展示
* 例如: antd 图标为 <StepBackwardOutlined />,那么 icon 属性的值则是:StepBackwardOutlined
*/
icon?: any;
/** 是否需要缓存 */
keepAlive?: boolean
/** 是否外链 */
isExt?: boolean
/**
* 外链打开方式
* 1: 新窗口打开
* 2: 内嵌 iframe
*/
extOpenMode?: 1 | 2
/** 是否隐藏页面加载进度条 */
hideProgressBar?: boolean;
/**
* 不在菜单中显示
* 此属性只会控制侧边栏中的一级菜单是否展示,不会对子级菜单的是否展示进行控制
* 如果要对子级菜单进行控制,请使用 hideChildrenInMenu 属性
*/
hideInMenu?: boolean
/**
* 在菜单中隐藏子节点, 一般在 children 中有隐藏的子节点时,可以设置 hideChildrenInMenu: true 来只显示父级菜单名称
* 例如: 父级菜单: 用户列表页 (/user/list) 路由配置中可以设置 hideChildrenInMenu: true,
* 例如: 子级菜单: 用户详情页 (/user/list/1, /user/list/2, /user/list/3) 不会出现在菜单中
*/
hideChildrenInMenu?: boolean
/** 不在页面 header 中的面包屑导航中显示 */
hideInBreadcrumb?: boolean
/** 不在tab标签页中显示 */
hideInTabs?: boolean
}
}
1.3、创建 src/layout/layout.vue 文件
// src/layout/layout.vue
<script setup>
</script>
<template>
<div class="mt-12px">layout/layout.vue</div>
<router-view />
</template>
<style scoped>
</style>
1.4、创建 src/views/dashboard/welcome/welcome.vue 文件
// src/views/dashboard/welcome/welcome.vue
<script setup>
</script>
<template>
<div class="mt-12px">
工作台
</div>
</template>
<style scoped>
</style>
1.5、创建 src/views/login/login.vue 文件
// src/views/login/login.vue
<script setup lang="ts">
import { message } from 'ant-design-vue'
import { ref } from 'vue'
const loading = ref(false)
const loginFormModel = ref({
username: 'admin',
password: 'a123456',
})
const handleSubmit = async () => {
const { username, password } = loginFormModel.value
if (username.trim() === '' || password.trim() === '') {
return message.warning('用户名或密码不能为空!')
}
message.loading('登录中...', 0)
loading.value = true
}
</script>
<template>
<div class="login-box">
<div class="login-logo">
<img src="~@/assets/logo.svg" width="45">
<h1 class="mb-0 ml-2 text-3xl font-bold">
后台管理系统
</h1>
</div>
<a-form layout="horizontal" :model="loginFormModel" @submit.prevent="handleSubmit">
<a-form-item>
<a-input v-model:value="loginFormModel.username" size="large" placeholder="admin">
<template #prefix>
<Icon icon="ant-design:user-outlined" />
</template>
</a-input>
</a-form-item>
<a-form-item>
<a-input
v-model:value="loginFormModel.password"
size="large"
type="password"
placeholder="a123456"
autocomplete="new-password"
>
<template #prefix>
<Icon icon="ant-design:lock-outlined" />
</template>
</a-input>
</a-form-item>
<a-form-item>
<a-button type="primary" html-type="submit" size="large" :loading="loading" block>
登录
</a-button>
</a-form-item>
</a-form>
</div>
</template>
<style lang="less" scoped>
.login-box {
display: flex;
flex-direction: column;
align-items: center;
width: 100vw;
height: 100vh;
padding-top: 240px;
background: #fff;
background-size: 100%;
.login-logo {
display: flex;
align-items: center;
margin-bottom: 30px;
}
:deep(.ant-form) {
width: 400px;
.ant-col {
width: 100%;
}
.ant-form-item-label {
padding-right: 6px;
}
}
}
</style>
1.6、创建 src/views/error/404.vue 文件
// src/views/error/404.vue
<script setup>
</script>
<template>
<div class="mt-12px">src/views/error/404.vue</div>
</template>
<style scoped>
</style>
二、创建路由实例
2.1、创建 src/router/index.ts 文件,并进行实例化路由器
// src/router/index.ts
import type { App } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'
import { routesList } from './routes'
export const router = createRouter({
// import.meta.env.BASE_URL
history: createWebHashHistory(import.meta.env.BASE_URL),
routes: routesList, // 将 1.1 中创建好的路由配置引入到这里
});
// 通过 setupRouter 函数将 router 实例传递给 app 应用
export async function setupRouter(app: App) {
app.use(router)
// 路由准备就绪后,路由插件帮我们做了如下操作:
// 1、全局注册 RouterView 和 RouterLink 组件。
// 2、添加全局 $router 和 $route 属性(可以在组件模板中使用)
// 3、启用 useRouter() 和 useRoute() 组合式函数。
// 4、触发路由器解析初始路由。
await router.isReady()
}
export default router;
三、注册路由器插件(在 src/main.ts 中)
// src/main.ts
import { createApp } from 'vue'
const app = createApp(App)
// 挂载路由
async function setupAppRouter() {
await setupRouter(app)
// 路由准备就绪后才能挂载APP实例
app.mount('#app')
}
setupAppRouter()
四、添加路由出口(在 src/App.vue 中)
// src/App.vue
<template>
<!-- 采用 RouterView 插槽的方式设置路由出口,便于扩展其它功能 -->
<router-view v-slot="{ Component }">
<component :is="Component" />
</router-view>
</template>