赞
踩
在做后台管理系统时,最常见的系统布局就是:左右布局。左侧是菜单栏,右侧是内容区,内容区又分为头部和展示区。如下:
因此分析一下这个页面的结构:
html
部分<template>
<div class="app-wrapper">
<div class="layout-aside" :class="{collapse:isCollapse}">
<div class="layout-logo">
<router-link to="/">
<img 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>
通过上面的布局,就展示了效果图中的三大部分,为了能够实现左侧菜单栏的折叠与隐藏,通过一个isCollapse
来控制,这个变量主要控制的是左侧菜单栏的宽度及图标的显示与否。
js
部分<script>
import Header from "./components/Header";
import SideBar from "./components/SideBar";
import AppMain from "./components/AppMain";
import {mapState} from "vuex";
export default{
name:'layout',//此页面在router/index.js中对应的name
components:{Header,SideBar,AppMain},
computed:{...mapState(['isCollapse'])},
methods:{
...
}
}
</script>
css
部分<style lang="scss" scoped>
.app-wrapper{
position:relative;
}
.layout-aside{
position:fixed;
left:0;
top:0;
height:100wh;
width:210px;
transition:all 0.3s;
background-color:#304156;
.layout-logo{
height:60px;
background-color:#2b2f3a;
a{
display:flex;
width:100%;
height:60px;
justify-content:center;
align-items:center;
}
img{
width:32px;
height:32px;
}
span{
font-size:14px;
line-height:60px;
color:#fff;
margin-left:12px;
}
}
}
.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{
min-height:calc(100vh - 100px);
margin:70px 15px 10px 10px;
background-color:#fff;
}
</style>
sideBar
组件部分
这个结构在elementUi
中是有的,可以参考这个:
html
部分<template>
<el-scrollbar class="sidebar-scroll">
<el-menu class="el-menu-vertical-demo" :router="true" :unique-opened="false" :collapse="isCollapse" :default-active="currentRouter" background-color="#304156" text-color="#fff" active-text-color="#409eff" style="border:none">
<template v-for="(item,index) in menuData">
<el-menu-item :key="index" :index="onlyOneChild.path" v-if="hasOneShowingChild(item.children,item)&&(!onlyOneChild.children||onlyOneChild.onShowingChildren)">
<i :class="onlyOneChild.meta.icon"></i>
</el-menu-item>
<el-menu-item :keey="index" :index="item.path" v-else>
<template slot="title">
<i :class="item.meta.icon"></i>
<span>{{item.meta.title}}</span>
</template>
<template v-for="(subitem,j) in item.children">
<el-menu-item :index="subitem.path" :key="j" v-if="!subitem.hidden">
{{subitem.meta.title}}
</el-menu-item>
</template>
</el-menu-item>
<template>
</el-menu>
</el-scrollbar>
</template>
js
部分import {mapState,mapGetters} from "vuex";
export default{
name:'SideBar',
computed:{
...mapGetters('setting',['firstMenu','subMenu','menuData']),
isCollapse:function(){
return this.$store.state.isCollapse;
}
}
},
props:{
collapse:{
type:Boolean,
default:false
}
},
data(){
this.onlyOneChild = null;
return {
currentRouter:''
}
},
watch:{
$route(to,from){
this.currentRouter = to.path;
}
},
mouted(){
this.currentRouter = this.$route.path;
console.log(this.isCollapse);
},
methods:{
hasOneShowingChild(children=[],parent){
const showingChildren = children.filter(item=>{
if(item.hidden){
return false;
}else{
this.onlyOneChild = item;
return true;
}
});
if(showingChildren.length==1){
this.onlyOneChild = item;
return true;
}
if(showingChildren.length==0){
this.onlyOneChild = {...parent,onShowingChildren:true};
return true;
}
return false;
}
}
css
部分代码<style lang="scss" scoped>
.sidebar-scroll{
height:calc(100% - 60px);
}
.sidebar{
height:100%;
text-align:left;
border-right:none;
}
</style>
header
组件部分html
部分代码<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-unfoldd" v-show="isMenuOpen"></i>
</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://wpimg.wallstcn.com/f778738c-ef48-4870-b634-56703b4acafe.gif?imageView2/1/w/80/h/80" alt="avatar"/>
<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-menu icon="el-icon-plus>修改密码</el-dropdown-menu>
<el-dropdown-menu icon="el-icon-circle-plus" @click.native="handleLogout">退出登录</el-dropdown-menu>
</el-dropdown-menu>
</el-dropdown>
</div>
</div>
</template>
js
部分代码<script>
import {logout} from "@/api/user";
import {applicationCofiguration} from "@/api/abp/application";
import {mapMutations} from "vuex";
export default{
name:'Header',
data(){
isMenuOpen:false,
breadcrumbList:[],
currentName:''
},
watch:{
$route(to,from){
this.updateBreadcrumb(to.matched);
}
},
mounted(){
this.updateBreadcrumb(this.$route.matched);
this.handleChangeName();
},
methods:{
...mapMutations(['changeCollapse']),
updateBreadcrumb(list=[]){
this.breadcrumbList = list;
},
handleChangeName(){
applicationConfiguration().then(res=>{
this.currentName = res.currentUser.userName;
})
},
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>
css
部分代码<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;
}
}
.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>
appMain
组件部分html
部分<template>
<div class="app-main">
<transition name="fade-transfrom" mode="out-in">
<router-vieew />
</transition>
</div>
</template>
js
部分<script>
export default{
name:'AppMain'
}
</script>
css
部分<style lang="scss" scope>
.app-main{
width:100%;
height:100%;
}
</style>
store/setting
里面的menuData
部分export default{
namespaced:true,
state:{
menuData:[]
},
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||[]
}
},
mutations:{
setMenuData(state,menuData){
state.menuData = menuData;
}
}
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。