一、头部组件实现
1.1 在layout目录下新建appHeader目录、并在目录下新建AppHeader组件。直接去element plus官网找 对应的静态源码即可
<template>
<el-header class="layout-header">
<!-- 图标-->
<el-icon>
<expand v-show="!isCollapse" @click="handleClose()"/>
<fold v-show="isCollapse" @click="handleOpen()"/>
</el-icon>
<!-- 面包屑-->
<el-breadcrumb separator="/">
<el-breadcrumb-item>homepage</el-breadcrumb-item>
<el-breadcrumb-item
><a href="/">promotion management</a></el-breadcrumb-item
>
<el-breadcrumb-item>promotion list</el-breadcrumb-item>
<el-breadcrumb-item>promotion detail</el-breadcrumb-item>
</el-breadcrumb>
<!-- 下拉菜单-->
<el-dropdown>
<span class="el-dropdown-link">
<el-avatar :size="32" :src="'https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png'"></el-avatar>
<el-icon class="el-icon--right">
<arrow-down></arrow-down>
</el-icon>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>用户姓名</el-dropdown-item>
<el-dropdown-item disabled>退出</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</el-header>
</template>
<script setup lang="ts">
import { reactive, computed } from 'vue'
import { ArrowDown, Expand, Fold } from '@element-plus/icons-vue'
import { mainStore } from '@/store'
const ms = mainStore()
const isCollapse = computed(() => {
return ms.getCollapse
})
const handleClose = ()=>{
console.log("handleClose"+isCollapse.value)
if(!isCollapse.value){
ms.setCollapse(!isCollapse.value)
}
}
const handleOpen = ()=>{
console.log("handleOpen"+isCollapse.value)
if(isCollapse.value){
ms.setCollapse(!isCollapse.value)
}
}
</script>
<style lang="scss" scoped>
.layout-header {
height: $base-tabbar-height;
background-color: palegoldenrod;
display: flex;
align-items: center;
.el-dropdown{
margin-left: auto;
.el-dropdown-link{
display: flex;
align-items: center;
justify-content: center;
}
}
.el-icon{
margin-right:17px;
}
}
</style>
1.2 布局页面导入AppHeader
引入 app-header组件 并对之前的右侧路由视图添加滚动组件
1.3 使用pinia来控制多个伸缩状态
安装pinia组件 在src下新建store目录,目录下新建index.ts
import { defineStore } from 'pinia'
export const mainStore = defineStore('mainStore', {
state() {
return {
// 定义一个是否水平折叠收起菜单 默认值是false。 true表示菜单折叠
isCollapse: false
}
},
getters: {
getCollapse: state => state.isCollapse
},
actions: {
setCollapse(isCollapse: boolean) {
this.isCollapse = isCollapse
}
}
})
1.4 main.ts使用pinia
1.5 导航菜单页面伸缩
<template>
<el-aside class="layout-menu" :width="!isCollapse ? '260px' : '64px'">
<!-- logo -->
<div class="layout-logo" v-show="!isCollapse">
<img src="@/assets/vue.svg" alt="logo图标" />
<p>v3后台管理系统</p>
</div>
<!-- 展示菜单 -->
<!-- 滚动组件 -->
<el-scrollbar class="layout-scrollbar">
<!-- 菜单组件-->
<el-menu
active-text-color="#ffd04b"
background-color="#545c64"
class="el-menu-vertical-demo"
default-active="2"
text-color="#fff"
router
:collapse="isCollapse"
>
<!-- 菜单列表组件即菜单树-->
<MenuTree :menu-list="menuItems"></MenuTree>
</el-menu>
</el-scrollbar>
</el-aside>
</template>
<script setup lang="ts">
import { reactive, computed } from 'vue'
import { mainStore } from '@/store/index.ts'
import MenuTree from '@/components/MenuTree/MenuTree.vue'
import Dict from '@/views/system/dict/Dict.vue'
import Acl from '@/views/acl/Acl.vue'
import Role from '@/views/acl/role/Role.vue'
import Permission from '@/views/acl/permission/Permission.vue'
import AclUser from '@/views/acl/user/AclUser.vue'
const ms = mainStore()
const isCollapse = computed(() => {
return ms.getCollapse
})
const menuItems = reactive([
{
name: 'index',
path: '/index',
meta: {
title: '首页',
hidden: false,
icon: 'HomeFilled'
}
},
{
name: 'Acl',
path: '/acl',
meta: {
title: '权限管理',
icon: 'Lock'
},
redirect: '/acl/user',
children: [
{
name: 'User',
path: '/acl/user',
component: AclUser,
meta: {
title: '用户管理',
icon: 'User'
}
},
{
path: '/acl/role',
component: Role,
name: 'Role',
meta: {
title: '角色管理',
icon: 'UserFilled'
}
},
{
path: '/acl/permission',
component: Permission,
name: 'Permission',
meta: {
title: '菜单管理',
icon: 'Monitor'
}
}
]
},
{
name: 'system',
path: '/system',
meta: {
title: '系统管理',
icon: 'Setting'
},
children: [
{
path: '/system/dict',
component: Dict,
name: 'Permission',
meta: {
title: '字典管理',
icon: 'Monitor',
hidden: false
}
}
]
}
])
</script>
<style lang="scss" scoped>
.layout-menu {
border-right: 1px solid #e4e7ed;
background-color: $base-menu-background;
.layout-logo {
width: 100%;
height: $base-menu-logo-height;
color: white;
display: flex;
align-items: center;
img {
width: 40px;
height: 40px;
}
p {
color: white;
}
}
.layout-scrollbar {
width: 100%;
height: calc(100vh - $base-menu-logo-height);
.el-menu {
border-right: none;
background-color: $base-menu-background;
}
.el-menu-vertical-demo:not(.el-menu-c--collapse) {
width: $base-menu-width;
}
}
}
</style>
1.6 优化roueter文件夹下的index.ts
页面组件才有懒加载方式,同时增加错误页面
import {
createRouter,
createWebHashHistory,
type RouteRecordRaw
} from ‘vue-router’
// 引入 login.ts
import LoginRouter from ‘@/router/modules/login.ts’
// RouteRecordRaw 内置的接口类型
const routes: RouteRecordRaw[] = [
…LoginRouter,
{
path: ‘/’,
redirect: ‘/layout’
},
{
path: ‘/layout’, // 布局页
name: ‘LayOut’,
component: ()=> import(‘@/views/layout/LayOut.vue’),
redirect: ‘/index’, // 进入主页的时候重定向到Index页面
children: [
{ path: ‘/:pathMatch(.)’,
name: ‘NotFound’,
component: ()=> import(‘@/views/error/Error.vue’)
},
{
path: ‘/index’,
name: ‘index’,
component: ()=> import(‘@/views/layout/index/Index.vue’)
},
{
path: ‘/acl’,
name: ‘Acl’,
component: ()=> import(‘@/views/error/Error.vue’),
children: [
{
path: ‘/acl/user’,
name: ‘User’,
component: ()=> import(‘@/views/acl/user/AclUser.vue’) ,
},
{
path: '/acl/role',
name: 'Role',
component: ()=> import('@/views/acl/role/Role.vue')
},
{
path: '/acl/permission',
name: 'Permission',
component: ()=> import('@/views/acl/permission/Permission.vue') ,
}
]
},
{
path: '/system',
name: 'system',
component: ()=> import('@/views/system/System.vue'),
children: [
{
path: '/system/dict',
name: 'dict',
component: ()=> import('@/views/system/dict/Dict.vue')
}
]
}
]
}
]
const router = createRouter({
history: createWebHashHistory(),
routes
})
// 导出路由
export default router