vue+ElementUI—实现基础后台管理布局(sideBar+header+appMain)(附源码)

发布于:2024-10-16 ⋅ 阅读:(14) ⋅ 点赞:(0)

后台管理的模板很多,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简单的静态演示,不适合做复杂系统开发