vue+elementUi——实现后台管理系统的布局(sideBar+header+appMain)

在做后台管理系统时,最常见的系统布局就是:左右布局。左侧是菜单栏,右侧是内容区,内容区又分为头部和展示区。如下:

在这里插入图片描述
因此分析一下这个页面的结构:

1.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来控制,这个变量主要控制的是左侧菜单栏的宽度及图标的显示与否。

2.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>

3.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>

4.sideBar组件部分

在这里插入图片描述
这个结构在elementUi中是有的,可以参考这个:
在这里插入图片描述

1.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>
2.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;
	}
}
3.css部分代码
<style lang="scss" scoped>
	.sidebar-scroll{
		height:calc(100% - 60px);
	}
	.sidebar{
		height:100%;
		text-align:left;
		border-right:none;
	}
</style>

5.header组件部分

在这里插入图片描述

1.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>
2.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>
3.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>

6.appMain组件部分

1.html部分
<template>
	<div class="app-main">
		<transition name="fade-transfrom" mode="out-in">
			<router-vieew />
		</transition>
	</div>
</template>
2.js部分
<script>
export default{
	name:'AppMain'
}
</script>
3.css部分
<style lang="scss" scope>
	.app-main{
		width:100%;
		height:100%;
	}
</style>

7.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;
		}
	}
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

叶浩成520

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值