后台管理的模板很多,vue本身就提供了完整的vue-template-admin,vue-admin-beautiful等后台管理系统化框架,但是这些框架正是因为成体系而显得繁重。假如你想搭建一个静态的后台管理模板页面和几个单独的菜单页面,直接就上框架是否就有点大材小用了呢 百分之九十以上的后台管理布局基本都是头部导航,侧边栏,主内容三部分组成。所以,将其单独摘出来为一个单独的轻量后台页面就很有必要了。
1. 效果展示
最常见的系统布局就是:左右布局。左侧是菜单栏,右侧是内容区,内容区又分为头部和展示区。。所以我们的效果显示如下:
目录结构呢,也很简单,因为不涉及请求,所以就是vue+element+vuex即可
这种就很适合做vue的静态页面演示开发,因为就只有几个页面,不涉及复杂的路由和权限啥的。
本博文是借鉴了这篇博客写的:vue+elementUi——实现后台管理系统的布局(sideBar+header+appMain)_element ui页面布局模板-CSDN博客
但是由于它的细节问题太多,在调试过程中也遇到了一些问题,所以重新写一篇调试完后的代码,主要的就是方便你我他。
2.关键代码
如果迁入自由的项目,可以复制对应的关键代码,store代码,配置对应的路由,自己调试也可。如果想直接拿来即用的,下面也会附上vue代码git地址。
index.vue 主页面,负责引入头部,侧边栏,主内容组件
<template>
<div class="app-wrapper">
<div class="layout-aside" :class="isCollapse?'collapse':''">
<div class="layout-logo">
<router-link to="/">
<img v-show="!isCollapse" style="width:50px;height:auto" src="@/assets/logo.png" alt="logo"/>
<img v-show="isCollapse" style="width:44px;height:auto" src="@/assets/logo.png" alt="logo"/>
<!-- <span v-show="!isCollapse">工业品超市管理后台</span> -->
</router-link>
</div>
<SideBar :collapse="isCollapse" />
</div>
<div class="layout-container" :class="{collapse:isCollapse}">
<div class="layout-header" :class="{collapse:isCollapse}">
<Header />
</div>
<div class="layout-main">
<AppMain />
</div>
</div>
</div>
</template>
<script>
import Header from "@/components/Header";
import SideBar from "@/components/SideBar";
import AppMain from "@/components/AppMain";
export default{
name:'layout',//此页面在router/index.js中对应的name
components:{Header,SideBar,AppMain},
computed:{
isCollapse:function(){
return this.$store.state.isCollapse;
}
},
methods:{
}
}
</script>
<style lang="scss" scoped>
.app-wrapper{
position:relative;
}
.layout-aside{
position:fixed;
left:0;
top:0;
height:100vh;
width:210px;
transition:all 0.3s;
background-color:#fff;
.layout-logo{
height:60px;
background-color:#ffffff;
a{
display:flex;
width:100%;
height:60px;
justify-content:center;
align-items:center;
}
img{
width:100px;
height:auto;
}
}
}
.layout-aside.collapse{
width:64px;
}
.layout-container{
margin-left:210px;
height:100%;
overflow:hidden;
}
.layout-container.collapse{
margin-left:64px;
transition:all 0.1s;
}
.layout-header{
position:fixed;
z-index:1;
top:0;
right:0;
width:calc(100% - 210px);
height:60px;
box-shadow:0 1px 3px rgba(0,21,41,0.08);
background-color:#fff;
}
.layout-header.collapse{
width:calc(100% - 64px);
transition:all 0.1s;
}
.layout-main{
padding: 20px;
min-height:calc(100vh - 150px);
margin:70px 15px 10px 10px;
background-color:#fff;
}
</style>
Header头部部分:
<template>
<div class="header-wrapper">
<div class="header-left">
<div class="open-icon" @click="handleCollapse">
<i class="el-icon-s-fold" v-show="!isMenuOpen"></i>
<i class="el-icon-s-unfold" v-show="isMenuOpen"></i>
<span style="font-size:16px;margin-left:5px">梦缘系统</span>
</div>
<el-breadcrumb separator="/">
<template v-for="(item,index) in breadcrumbList">
<el-breadcrumb-item :key="index" v-if="item.meta.title" :to="{path:item.path}">
</el-breadcrumb-item>
</template>
</el-breadcrumb>
</div>
<div class="header-right">
<span class="header-user">{{currentName}},欢迎回来</span>
<el-dropdown trigger="click">
<span class="el-dropdown-line">
<img src="https://liuqingwushui.top/usr/uploads/2024/10/09/1728443808722546.jpg" style="border-radius:50%;width:32px" alt="avatar"/>
<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item icon="el-icon-setting">修改密码</el-dropdown-item >
<el-dropdown-item icon="el-icon-guide" @click.native="handleLogout">退出登录</el-dropdown-item >
</el-dropdown-menu>
</el-dropdown>
</div>
</div>
</template>
<script>
// import {logout} from "@/api/user";
export default{
name:'Header',
data(){
return {
isMenuOpen:false,
breadcrumbList:[],
currentName:'admin'
}
},
watch:{
$route(to,from){
this.updateBreadcrumb(to.matched);
}
},
mounted(){
this.updateBreadcrumb(this.$route.matched);
},
methods:{
updateBreadcrumb(list=[]){
this.breadcrumbList = list;
},
handleCollapse(){
this.isMenuOpen= !this.isMenuOpen;
this.$store.commit('changeCollapse',this.isMenuOpen);
},
handleLogout(){
this.$confirm('确认退出?','提示',{
confirmButtonTextt:'确定',
cancelButtonText:'取消',
type:'warning'
}).then(()=>{
//logout();
this.$router.push('/login');
}).catch(()=>{})
}
}
}
</script>
<style lang="scss" scope>
.header-wrapper{
display:flex;
justify-content:space-between;
align-content:center;
padding:0 15px;
height:60px;
.header-left{
display:flex;
align-items:center;
.open-icon{
font-size:20px;
margin-right:15px;
cursor:pointer;
display: flex;
align-items: center;
}
}
.header-right{
display:flex;
align-items:center;
.header-user{
margin-right:15px;
}
}
}
.el-dropdown-link{
cursor:pointer;
color:#409eff;
img{
width:40px;
height:40px;
border-radius:5px;
}
}
.el-icon-arrow-down{
font-size:12px;
}
.demostration{
display:block;
color:#8492a6;
font-size:14px;
margin-bottom:20px;
}
</style>
Sidebar侧边栏部分:
<template>
<el-scrollbar class="sidebar-scroll">
<el-menu class="el-menu-vertical-demo" :default-active="this.$route.path" :collapse="isCollapse" router>
<template v-for="(item,index) in menuData">
<el-submenu v-if="item.children && item.children.length > 0" :key="index" :index="item.path">
<template slot="title">
<i :class="item.meta.icon"></i>
<span>{{ item.meta.title }}</span>
</template>
<el-menu-item
v-for="(child,childIndex) in item.children"
:key="`${index}-${childIndex}`"
:index="child.path"
>
<i :class="child.meta.icon"></i>
<span>{{ child.meta.title }}</span>
</el-menu-item>
</el-submenu>
<el-menu-item v-else :key="index" :index="item.path">
<i :class="item.meta.icon"></i>
<span>{{ item.meta.title }}</span>
</el-menu-item>
</template>
</el-menu>
</el-scrollbar>
</template>
<script>
import {mapState,mapGetters} from "vuex";
export default{
name:'SideBar',
computed:{
...mapGetters(['firstMenu','subMenu','menuData']),
isCollapse:function(){
return this.$store.state.isCollapse;
}
},
props:{
collapse:{
type:Boolean,
default:false
}
},
data(){
return {
currentRouter:''
}
},
watch:{
$route(to,from){
this.currentRouter = to.path;
}
},
mouted(){
this.currentRouter = this.$route.path;
},
methods:{
}
}
</script>
<style lang="scss" scoped>
.sidebar-scroll{
height:calc(100% - 60px);
}
.sidebar{
height:100%;
text-align:left;
border-right:none;
}
.el-menu-vertical-demo:not(.el-menu--collapse) {
width: 210px;
min-height: 400px;
}
</style>
Appmain主内容部分:
<template>
<div class="app-main">
<transition name="fade-transfrom" mode="out-in">
<router-view />
</transition>
</div>
</template>
<script>
export default{
name:'AppMain'
}
</script>
<style lang="scss" scope>
.app-main{
width:100%;
height:100%;
}
</style>
Store状态管理js:
// src/store/index.js
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
state:{
isCollapse:false,
menuData:[
{
path:'/dream/home',
meta:{
icon:'el-icon-data-line',
title:'首页'
}
},
{
path:'/dream/2',
meta:{
icon:'el-icon-office-building',
title:'梦缘'
}
},
{
path:'/dream/3',
meta:{
icon:'el-icon-place',
title:'流情'
}
},
{
path:'/dream/4',
meta:{
icon:'el-icon-postcard',
title:'日志'
}
},
{
path:'/dream/5',
meta:{
icon:'el-icon-pie-chart',
title:'数据'
},
children:[
{
path:'/dream/6',
meta:{
icon:'el-icon-postcard',
title:'数据1'
}
},
]
}
]
},
mutations:{
changeCollapse: (state,isCollapse) => {
state.isCollapse = isCollapse
},
setMenuData(state,menuData){
state.menuData = menuData;
}
},
actions: {
// 异步 actions
},
getters:{
menuData(state,rootState){
if(state.filterMenu){
const {permissions,roles} = rootState.accout;
return filterMenu(JSON.parse(JSON.stringfy(state.menuData)),permissions,roles)
}
return state.menuData;
},
firstMenu(state){
const {menuData} = state;
if(menuData.length>0&&!menuData[0].fullPath){
formatFullPath(menuData);
}
return menuData.map(item=>{
const menuItem = {...item};
delete menuItem.children;
return menuItem
})
},
subMenu(state){
const {menuData,activateFirst} = state;
if(menuData.length>0&&!menuData[0].fullPath){
formatFullPath(menuData);
}
const current = menuData.find(menu=>menu.fullPath== activatedFirst);
return current && current.chilren||[]
}
},
modules: {
// 模块
}
});
3.示例源码下载
git地址:vue-admin-static: vue+element后台管理极简版:头部和侧边导航栏,固定路由。适合写vue简单的静态演示,不适合做复杂系统开发