项目源码
配置路由
首先创建3个页面:
view/dashboard.vue
<template>
<h1>dashboard view</h1>
</template>
view/user_list.vue
<template>
<h1>user list view</h1>
</template>
login.vue
<template>
<h1>login view</h1>
</template>
修改router/index.js文件:
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(''),
routes: [
{
path: '',
name: 'basic',
component: () => import('../layout/Layout.vue'),
children: [
{
path: '/dashboard',
name: 'dashboard',
component: () => import('../view/dashboard.vue')
},
{
path: '/user/list',
name: '/user/list',
component: () => import('../view/user_list.vue')
}
]
},
{
path: '/login',
name: 'login',
component: () => import('../view/login.vue')
}
]
})
export default router
然后再修改layout/Layout.vue文件中右侧页面主体:
<!-- 右侧页面主体开始 -->
<a-layout-content
:style="{ margin: '12px 10px', padding: '18px', background: '#fff', minHeight: '280px', borderRadius: '4px' }"
>
<router-view></router-view>
</a-layout-content>
<!-- 右侧页面主体结束 -->
修改完成之后路由就配置好了,分别访问router/index.ts文件中注册的三个地址:/dashboard、/user/list和/login:
可以看到路由已经注册成功了
*因为login页面没有注册在Layout之下,所以在新的页面中打开,这也正是我们想要的效果
commit-hash: 2051283
菜单配置
修改layout/Layout.vue文件:
<template>
<a-layout>
<!-- 左侧部分开始 -->
<a-layout-sider :style="{ overflow: 'auto', height: '100vh' }" v-model:collapsed="collapsed">
<!-- 左侧logo开始 -->
<div class="logo">
<img src="/vite.svg" class="logo_img" alt="Vite logo" />
<span class="logo_text" v-show="!collapsed">Ant Design</span>
</div>
<!-- 左侧logo结束 -->
<!-- 左侧菜单开始 -->
<a-menu v-model:selectedKeys="selectedKeys" :theme="state.theme" :items="menu" mode="inline" @click="menuClicked"/>
<!-- 左侧菜单结束 -->
</a-layout-sider>
<!-- 左侧部分结束 -->
<!-- 右侧部分开始 -->
<a-layout>
<!-- 右侧header开始 -->
<a-layout-header style="background: #fff; padding: 0; height: 50px;">
<MenuUnfoldOutlined
v-if="collapsed"
class="trigger"
@click="() => (collapsed = !collapsed)"
/>
<MenuFoldOutlined v-else class="trigger" @click="() => (collapsed = !collapsed)" />
</a-layout-header>
<!-- 右侧header结束 -->
<!-- 右侧页面主体开始 -->
<a-layout-content
:style="{ margin: '12px 10px', padding: '18px', background: '#fff', minHeight: '280px', borderRadius: '4px' }"
>
<router-view></router-view>
</a-layout-content>
<!-- 右侧页面主体结束 -->
</a-layout>
<!-- 右侧部分结束 -->
</a-layout>
</template>
<script setup lang="js">
import { ref, h } from 'vue';
import { useRouter } from 'vue-router'
const selectedKeys = ref(['1']);
const collapsed = ref(false);
const router = useRouter()
const state = ref({
theme: 'dark'
})
// 菜单
const menu = ref([
{
key: 'dashboard',
icon: () => h(DashboardOutlined),
label: '仪表盘',
title: '仪表盘',
path: '/dashboard'
},
{
key: 'sub1',
icon: () => h(SettingOutlined),
label: '系统设置',
title: '系统设置',
children: [
{
key: 'user_list',
label: '用户管理',
icon: () => h(AppstoreOutlined),
path: '/user/list'
},
],
},
]);
// 菜单点击事件
const menuClicked = ({item, key}) => {
console.log(item, key)
// 跳转到菜单配置的path地址取
router.push({ path: item.path })
}
import {
MenuUnfoldOutlined,
MenuFoldOutlined,
AppstoreOutlined,
SettingOutlined,
DashboardOutlined
} from '@ant-design/icons-vue'
</script>
<style>
.trigger {
display: block;
font-size: 18px;
width: 40px;
line-height: 50px;
padding: 2px 16px 0 16px;
cursor: pointer;
transition: color 0.3s;
}
.trigger:hover {
color: #1890ff;
}
.logo {
height: 32px;
margin: 9px;
overflow: hidden;
}
.logo_img {
margin-left: 12px;
display: block;
width: 32px;
height: 32px;
float: left;
}
.logo_text {
display: block;
float: left;
line-height: 32px;
text-align: center;
color: #fff;
font-size: 20px;
margin-left: 8px;
font-weight: 900;
}
.site-layout .site-layout-background {
background: #fff;
}
</style>
修改之后启动项目可以看到菜单已经配置好了:
commit-hash: 63bfad2
统一配置菜单和路由
上边我们实现了 路由 和 菜单 的配置,但是正常情况下菜单和路由是一起配置的,并且结构也相似,而且后期还会动态从后端获取菜单和路由数据,这里我们先实现 统一配置
我们这里说的动态配置路由和菜单是对
Layout下的页面的配置,login页面并不是嵌套在Layout之下的
我们先在一个地方存储菜单和路由的数据结构,首先创建/store/menu.js文件:
const menu = [
{
name: '仪表盘', // 菜单名称(router用)
label: '仪表盘', // 菜单名称(menu用)
key: '/dashboard', // 菜单路径,点击所跳转的地址,全局唯一
icon: 'DashboardOutlined', // 菜单图标
component: 'Dashboard', // 菜单对应的页面组件,这个在router里边回用到
},
{
name: '系统设置',
label: '系统设置',
key: '/system',
icon: 'SettingOutlined',
children: [ // 子菜单
{
name: '用户管理',
label: '用户管理',
key: '/user/list',
icon: 'AppstoreOutlined',
component: 'UserList',
}
]
}
]
export default menu
定义完菜单和路由的基本数据结构之后我们再创建router/view_component.js文件:
/**
* 注册页面组件
*/
interface RouteMap {
[key: string]: () => Promise<typeof import("*.vue")>;
}
const routerComponents: RouteMap = {
Dashboard: () => import('../view/dashboard.vue'),
UserList: () => import('../view/user_list.vue')
}
export default routerComponents
创建router/generator.js文件:
// 生成router可用的数据结构
import routerComponents from './view_component'
import router from './index'
export interface Menu {
name: string;
label: string;
key: string;
icon: string;
component?: string;
children: Menu[];
}
const genRouter = (menu: Menu[]) => {
const childrenNav: any[] = []
const makeRouter = (list: Menu[]) => {
for (let item of list) {
if (item.component) {
childrenNav.push({
path: item.key,
name: item.component,
component: routerComponents[item.component],
meta: {
keepAlive: true
}
})
}
if (item.hasOwnProperty('children') && item.children.length > 0) {
makeRouter(item.children)
}
}
}
makeRouter(menu)
return childrenNav;
}
const initRouter = (menu: Menu[]) => {
const routers = genRouter(menu)
routers.forEach(r => {
router.addRoute('basic', r)
})
}
export default initRouter
再修改router/index.js文件:
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(''),
routes: [
{
path: '',
name: 'basic',
component: () => import('../layout/Layout.vue'),
children: []
},
{
path: '/login',
name: 'login',
component: () => import('../view/login.vue')
}
]
})
export default router
然后再修改main.ts文件(这是临时修改,后边还会做调整),在main.js文件中注册路由:
import { createApp } from 'vue'
import router from './router'
import Antd from 'ant-design-vue';
import 'ant-design-vue/dist/reset.css';
import App from './App.vue'
// 暂时在这里注册路由,后期会做调整
import menu from './store/menu'
import initRouter from './router/route_generator';
initRouter(menu)
createApp(App).use(router).use(Antd).mount('#app')
这样路由就调整完成了
接下来再修改菜单
修改菜单比较简单,直接在Layout中引入menu并使用即可
修改layout/layout.vue文件:
<template>
<a-layout>
<!-- 左侧部分开始 -->
<a-layout-sider :style="{ overflow: 'auto', height: '100vh' }" v-model:collapsed="collapsed">
<!-- 左侧logo开始 -->
<div class="logo">
<img src="/vite.svg" class="logo_img" alt="Vite logo" />
<span class="logo_text" v-show="!collapsed">Ant Design</span>
</div>
<!-- 左侧logo结束 -->
<!-- 左侧菜单开始 -->
<a-menu v-model:selectedKeys="selectedKeys" :theme="state.theme" :items="state.menu" mode="inline" @click="menuClicked"/>
<!-- 左侧菜单结束 -->
</a-layout-sider>
<!-- 左侧部分结束 -->
<!-- 右侧部分开始 -->
<a-layout>
<!-- 右侧header开始 -->
<a-layout-header style="background: #fff; padding: 0; height: 50px;">
<MenuUnfoldOutlined
v-if="collapsed"
class="trigger"
@click="() => (collapsed = !collapsed)"
/>
<MenuFoldOutlined v-else class="trigger" @click="() => (collapsed = !collapsed)" />
</a-layout-header>
<!-- 右侧header结束 -->
<!-- 右侧页面主体开始 -->
<a-layout-content
:style="{ margin: '12px 10px', padding: '18px', background: '#fff', minHeight: '280px', borderRadius: '4px' }"
>
<router-view></router-view>
</a-layout-content>
<!-- 右侧页面主体结束 -->
</a-layout>
<!-- 右侧部分结束 -->
</a-layout>
</template>
<script setup lang="js">
import { ref, h, onMounted, reactive } from 'vue';
import { useRouter } from 'vue-router'
// 加载菜单和图标
import menu from '../store/menu'
import * as icons from '@ant-design/icons-vue'
const selectedKeys = ref([]);
const collapsed = ref(false);
const router = useRouter()
const state = reactive({
theme: 'dark',
menu: null, // menu设置为动态值,上边a-menu标签的items值也改为state.menu
})
onMounted(() => {
// 给state.menu赋值
state.menu = menu
// 我们在menu.js里边配置的icon为一个字符串,但是a-menu组件需要的icon为一个图标组件
// 所以这里需要把icon名称转换为icon组件
const genMenuIcon = (list) => {
for(let item of list) {
if (item.icon && typeof item.icon === 'string') {
item.icon = h(eval('icons.' + item.icon))
}
if (item.hasOwnProperty('children') && item.children.length > 0) {
genMenuIcon(item.children)
} else {
delete(item.children)
}
}
}
genMenuIcon(state.menu)
})
// 菜单点击事件
const menuClicked = ({item, key}) => {
console.log(item, key)
// 跳转到菜单配置的path地址取
router.push({ path: key })
}
import {
MenuUnfoldOutlined,
MenuFoldOutlined,
AppstoreOutlined,
SettingOutlined,
DashboardOutlined
} from '@ant-design/icons-vue'
</script>
<style>
.trigger {
display: block;
font-size: 18px;
width: 40px;
line-height: 50px;
padding: 2px 16px 0 16px;
cursor: pointer;
transition: color 0.3s;
}
.trigger:hover {
color: #1890ff;
}
.logo {
height: 32px;
margin: 9px;
overflow: hidden;
}
.logo_img {
margin-left: 12px;
display: block;
width: 32px;
height: 32px;
float: left;
}
.logo_text {
display: block;
float: left;
line-height: 32px;
text-align: center;
color: #fff;
font-size: 20px;
margin-left: 8px;
font-weight: 900;
}
.site-layout .site-layout-background {
background: #fff;
}
</style>
修改完成之后启动项目,可以看到菜单和路由都配置正常了,后边再新增页面可以直接在store/menu.ts中新增菜单结构,并在router/view_component.ts中注册对应的页面即可
总结
- 完成了菜单和路由的统一配置
commit-hash: dc33091