(获取本节完整代码 GitHub/chizijijiadami/vue-elementui-2)
0、写在前面
关于布局:我们常见的后台管理页面结构一般有下图中显示的三种,如果把Tabs(导航的多标签显示)、Crumbs(面包屑)、Content(页面内容)和Footer(底部)当做中间区域视为一块整体,可以看到主要的区别就是Menu(菜单栏)位置的区别,待会我们就根据这些来设立vuex(vuex官网)的布局状态值,主要考虑以下两点:
(1)菜单栏的位置;
(2)设置是否需要该模块,包括Tabs、Crumbs、Foooter。
关于功能:在后台模板中我们会用到以下几种功能:
● Menu菜单自动化
● Tabs多标签
● Crumbs面包屑导航
● vuex 的 modules
● svg 图标雪碧图 svg-sprite-loader
● 批量导入资源
● 自定义全局组件—批量全局注册
● 自定义全局方法
● 获取接口数据、模拟数据——axios+Mockjs 【关联:axios源码学习到使用】
● 精确到按钮级别的权限控制
行动前准备:接下来我们用 GitHub/chizijijiadami/hand-to-hand 的代码来做准备些工作。可以参见下文章 vue实践1.1 企业官网——prerender-spa-plugin预渲染 ,第2段 css预处理器stylus( stylus官网)、第5段vuex状态管理( vuex官网 )。
a、运行后自动打开浏览器src>package.json
- "serve": "vue-cli-service serve --mode development",
+ "serve": "vue-cli-service serve --mode development --open",
b、安装
yarn add vuex element-ui
yarn add stylus stylus-loader -D
c、src>App.vue
<template>
<div id="app">
- <header>
- <nav><router-link to="/index">index</router-link><router-link to="/list">list</router-link></nav>
- </header>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App',
}
</script>
d、src>main.js
+ import Element from "element-ui";
+ import 'element-ui/lib/theme-chalk/index.css';
+ // 使用Element UI
+ Vue.use(Element, {
+ size: "small"
+ });
- import './assets/styles/reset.css'
- import './assets/styles/style.css'
e、src>pages>Index>index.vue
<template>
<div>
<p class="index-p">Index-index</p>
- <img src="~@/assets/images/jerry.png" alt="" srcset="">
</div>
</template>
<script>
export default {
name:"IndexIndex",
- created(){
- console.log(process.env.VUE_APP_BASE_API,'输出VUE_APP_BASE_API');
- }
}
</script>
- <style>
- @import '~@/assets/styles/component.css'
- </style>
f、删除
src>assets>images>jerry.png
src>assets>styles>component.css
src>assets>styles>reset.css
src>assets>styles>style.css
准备工作到此结束。
1、新建布局文件
src>pages>Layout>components>Header.vue
<template>
<div class="app-header">header</div>
</template>
src>pages>Layout>components>Mune.vue
<template>
<div class="app-menu">
<el-menu router default-active="/index/index" class="el-menu-vertical-demo">
<el-menu-item index="/index/index">
<i class="el-icon-location"></i>
<span>首页</span>
</el-menu-item>
<el-submenu index="list">
<template slot="title">
<i class="el-icon-s-grid"></i>列表
</template>
<el-menu-item index="/list/detail">
<i class="el-icon-goods"></i>详情
</el-menu-item>
<el-menu-item index="/list/feature">
<i class="el-icon-document"></i>特性
</el-menu-item>
</el-submenu>
</el-menu>
</div>
</template>
src>pages>Layout>components>Tabs.vue
<template>
<div clss="app-tabs">
<el-tabs type="card">
<el-tab-pane
label="首页"
></el-tab-pane>
</el-tabs>
</div>
</template>
src>pages>Layout>components>Crumbs.vue
<template>
<div clss="app-crumbs">
<el-breadcrumb separator-class="el-icon-arrow-right">
<el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
</el-breadcrumb>
</div>
</template>
src>pages>Layout>components>Content.vue
<template>
<div class="app-content"><router-view></router-view></div>
</template>
src>pages>Layout>components>Footer.vue
<template>
<div class="app-footer">大米工厂</div>
</template>
src>pages>Layout>index.vue
<template>
<div class="app-page">
<Header />
<Menu />
<div class="app-container">
<Tabs />
<Crumbs />
<Content />
<Footer />
</div>
</div>
</template>
<script>
import Header from "./components/Header";
import Tabs from "./components/Tabs";
import Menu from "./components/Menu";
import Crumbs from "./components/Crumbs";
import Content from "./components/Content";
import Footer from "./components/Footer";
export default {
components: {
Header,
Menu,
Tabs,
Crumbs,
Content,
Footer
}
};
</script>
2、修改src>router>index.js
export default new Router({
+ scrollBehavior() { //页面内容多过一屏时,需设置滚动位置
+ return { x: 0, y: 0 }
+ },
routes: [
{
path: '',
- redirect: '/index',
+ redirect: '/index/index'
},
{
path: '/index',
- component: _import('Index/index')
+ component: _import('Layout/index'),
+ redirect: '/index/index',
+ children:[
+ {
+ path: 'index',
+ component: _import('Index/index'),
+ }
+ ]
},
{
path: '/list',
- component: _import('List/index'),
+ component: _import('Layout/index'),
children: [
{
path: 'detail',
component: _import('List/Detail/index')
},
{
path: 'feature',
component: _import('List/Feature/index')
}
]
},
{
path: '/404',
component: _import('ErrorPages/404')
},
{
path: '*',
redirect: '/404'
}
]
})
访问检查下个模块都加载出来了,接下来我们先按照布局1来写样式.
3、布局1样式
(1)样式文件中
src>assets>styles>reset.styl
// 主要是一些原生样式的覆盖,直接看源代码
src>assets>styles>base.styl
$body-bgcolor=#f5f6f7
html
background-color $body-bgcolor
src>assets>styles>layout.styl
$bg-color = #eee
$header_height = 60px
$menu_width = 200px
.app-header
background-color white
position absolute
top 0
left 0
height $header_height
width 100%
.app-menu
background-color white
position absolute
top $header_height
left 0
width $menu_width
height calc(100% - 60px) // $header_height
.app-container
height 100vh
padding $header_height 0 0 $menu_width
.app-tabs
background-color white
height 40px
.app-crumbs
height 30px
line-height 30px
.app-content
background-color white
padding 0 10px
min-height calc(100% - 40px - 30px - 40px) // app-tabs_height、app-crumbs_height、app-footer_height
.app-footer
background-color white
height 40px
line-height 40px
src>assets>styles>index.styl ,统一导入样式
@require './reset.styl'
@require './base.styl'
@require './layout.styl'
src>main.js中引用
//样式
import "@/assets/styles/index.styl";
浏览器中查看一下,有了。
尽管stylus中有了变量,但是无法跟 calc 友好结合,如果该变量涉及 calc 计算多的话在改版的时候容易漏掉,所以我们把这些搬运到布局文件中进行。
(2)在布局文件中计算
src>pages>Layout>index.vue
<template>
<div class="app-page">
+ <Header :style="{height:header.height}" />
+ <Menu :style="{top:header.height,width:menu.width,height:menuHeight()}" />
+ <div class="app-container" :style="{padding:containerPadding()}">
+ <Tabs :style="{height:tabs.height}" />
+ <Crumbs :style="{height:crumbs.height,padding:'0 '+content.margin}" :crumbs="crumbs" :content="content"/>
+ <Content :style="{'min-height':contentMinHeight(),margin:contentMargin()}"/>
+ <Footer :style="{height:footer.height,'line-height':footer.height}" />
</div>
</div>
</template>
<script>
import Header from "./components/Header";
import Tabs from "./components/Tabs";
import Menu from "./components/Menu";
import Crumbs from "./components/Crumbs";
import Content from "./components/Content";
import Footer from "./components/Footer";
export default {
components: {
Header,
Menu,
Tabs,
Crumbs,
Content,
Footer
},
+ data() {
+ return {
+ header: {
+ height: "60px"
+ },
+ menu: {
+ width: "200px"
+ },
+ content: {
+ margin: "10px"
+ },
+ tabs: {
+ height: "40px"
+ },
+ crumbs: {
+ height: "30px"
+ },
+ footer: {
+ height: "40px"
+ }
+ };
+ },
+ methods: {
+ menuHeight() {
+ return "calc(100% - " + this.header.height + ")";
+ },
+ containerPadding() {
+ return (
+ this.header.height +
+ " 0 0 " +
+ this.menu.width
+ );
+ },
+ contentMinHeight() {
+ return (
+ "calc(100% - " +
+ this.tabs.height +
+ " - " +
+ this.crumbs.height +
+ " - " +
+ this.footer.height +
+ ")"
+ );
+ }
+ }
};
</script>
src>assets>style>layout.styl
$bg-color = #eee
- $header_height = 60px
- $menu_width = 200px
.app-header
background-color white
position absolute
top 0
left 0
- height $header_height
width 100%
.app-menu
background-color white
position absolute
- top $header_height
left 0
- width $menu_width
- height calc(100% - 60px) // $header_height
.app-container
height 100vh
+ .app-tabs,.app-crumbs,.app-content,.app-footer
+ background-color white //这后面的都删掉
- padding $header_height 0 0 $menu_width
- .app-tabs
- background-color white
- height 40px
- .app-crumbs
- height 30px
- line-height 30px
-.app-content
- background-color white
- padding 0 10px
- min-height calc(100% - 40px - 30px - 40px) // app-tabs_height、app-crumbs_height、app-footer_height
-.app-footer
- background-color white
- height 40px
- line-height 40px
调整结束,看一下浏览器是没有变化的。
另外左侧菜单栏,往往是可以收缩的,下面我们从这里入手结合vuex进行布局调整。
4、结合vuex实现多种布局样式
(1)菜单栏收缩
新建 src>data>store>index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store=new Vuex.Store({
state: {
menu: {
isCollapse: false //默认菜单展开
}
},
mutations: {
SET_MENU_ISCOLLAPSE: state => {
state.menu.isCollapse = !state.menu.isCollapse
}
},
actions: {
setMenuIsCollapse({ commit }) {
commit('SET_MENU_ISCOLLAPSE')
}
}
})
export default store
引入 src>main.js
+ import store from './data/store'
new Vue({
router,
+ store,
render: h => h(App),
}).$mount('#app')
菜单页面中取值 src>pages>Layout>components>Menu.vue
- <el-menu router default-active="/index/index" class="el-menu-vertical-demo">
+ <el-menu :collapse="isCollapse" :collapse-transition="false" router
default-active="/index/index" class="el-menu-vertical-demo">
+ <script>
+ export default {
+ computed: {
+ isCollapse() {
+ return this.$store.state.menu.isCollapse;
+ }
+ }
+ };
+ </script>
添加控制事件 src>pages>Layout>components>Hearder.vue
<template>
- <div class="app-header"></div>
+ <div class="app-header">
+ <el-button type="primary" plain :icon="isCollapse?'el-icon-s-fold':'el-icon-s-unfold'"></el-button>
+ </div>
</template>
+ <script>
+ export default {
+ computed: {
+ isCollapse() {
+ return this.$store.state.menu.isCollapse;
+ }
+ },
+ methods:{
+ setMenuIsCollapse(){
+ this.$store.dispatch('setMenuIsCollapse');
+ }
+ }
+ };
+ </script>
点击看看效果,可以收缩,但是外边 div 没有同时进行收缩,布局文件中再改点东西。
src>pages>Layout>components>Hearder.vue
- <Menu :style="{top:header.height,widthmenu.width,height:menuHeight()}" />
+ <Menu :style="{top:header.height,width:isCollapse?menu.widthCollapse:menu.width,height:menuHeight()}" />
+ computed: { //script中添加
+ isCollapse() {
+ return this.$store.state.menu.isCollapse;
+ }
+ },
这么加只是收缩了菜单栏外层,还有 app-container 的 padding 也要调整
containerPadding() {
return (
this.header.height +
" 0 0 " +
- this.menu.width
+ (this.isCollapse ? this.menu.widthCollapse : this.menu.width)
);
},
现在点点看,就正常了。
(2)菜单栏位置变化
根据我们前面的布局分类给菜单栏位置设定状态值。
src>data>store>index.js
menu: {
isCollapse: false,
+ location:"VH" //V、VH、H三个值,V表示在左侧,VH表示横跨头部,H表示在头部,默认 V
}
根据这个设定,我们目前的布局是 V,现在我们做 VH 。
src>pages>Layout>index.vue——在布局页获取
computed: {
isCollapse() {
return this.$store.state.menu.isCollapse;
},
+ menuLocation(){
+ return this.$store.state.menu.location;
+ }
},
观察布局图,从V到VH只是菜单栏跟头部的决定定位值不同。那么我们要做的,修改Menu的 top、height 属性加,Header的 left、width属性。
src>pages>Layout>index.vue
- <Header :style="{height:header.height}" />
- <Menu :style="{top:header.height,width:isCollapse?menu.widthCollapse:menu.width,height:menuHeight()}" />
+ <Header :style="{height:header.height,left:headerLeft(),width:headerWidth()}" />
+ <Menu :style="{top:menuTop(),width:this.menuWidth(),height:menuHeight()}" />
methods:{
+ headerLeft(){ //涉及三目运算多时,最好加上括号方便读码——这里别忘记菜单栏的收缩
+ return this.menuLocation === "V" ? "0" : ( this.isCollapse ? this.menu.widthCollapse : this.menu.width );
+ },
+ headerWidth(){
+ return this.menuLocation==="V" ? "100%" : "calc(100% - "+this.menuWidth()+")"
+ },
+ menuTop() {
+ return this.menuLocation === "V" ? this.header.height : "0";
+ },
menuHeight() {
- return "calc(100% - " + this.header.height + ")";
+ return this.menuLocation==="V" ? "calc(100% - " + this.header.height + ")" : "100vh";
},
+ menuWidth(){ //这个值用到的比较多我们单独写成一个方式使用
+ return this.isCollapse?this.menu.widthCollapse:this.menu.width;
+ },
containerPadding() {
return (
this.header.height +
" 0 0 " +
- (this.isCollapse ? this.menu.widthCollapse : this.menu.width)
+ this.menuWidth()
);
},
......
}
src>assets>style>layout.styl
.app-header
background-color white
position absolute
top 0
- left 0
- width 100%
.app-menu
background-color white
position absolute
- left 0
.app-container
height 100vh
.app-tabs,.app-content,.app-footer
background-color white
修改完成,浏览器里点点看。
接下来,我们写 H 布局。
src>data>store>index.js
menu: {
isCollapse: false,
- location:"VH" //V、VH、H三个值,V表示在左侧,VH表示横跨头部,H表示在头部
+ location:"H" //V、VH、H三个值,V表示在左侧,VH表示横跨头部,H表示在头部
}
观察一下,相对其他两个布局,布局文件Menu这块是直接没了的,还要修改app-container的 padding,Herder的 left、width 属性。
src>pages>Layout>index.vue
- <Menu :style="{top:menuTop(),width:this.menuWidth(),height:menuHeight()}" />
+ <Menu v-if="menuLocation!=='H'" :style="{top:menuTop(),width:this.menuWidth(),height:menuHeight()}" />
//methods中
headerLeft(){
- return this.menuLocation==="V" ? "0": this.menuWidth();
+ return this.menuLocation==="V" || this.menuLocation==="H" ? "0": this.menuWidth();
},
headerWidth(){
- return this.menuLocation==="V" ? "100%" : "calc(100% - "+this.menuWidth()+")"
+ return this.menuLocation==="V" || this.menuLocation==="H" ? "100%" : "calc(100% - "+this.menuWidth()+")"
},
menuWidth() {
- return this.isCollapse ? this.menu.widthCollapse : this.menu.width;
+ return this.menuLocation === "H"?"0px":this.isCollapse ? this.menu.widthCollapse : this.menu.width;
},
到这里还剩下菜单栏没有在Header中显示,我们接着改Header。
src>pages>Layout>componets>Header.vue
<template>
<div class="app-header">
- <el-button type="primary" plain @click='setMenuIsCollapse' :icon="isCollapse?'el-icon-s-fold':'el-icon-s-unfold'"></el-button>
+ <Menu v-if="menuLocation==='H'"/>
+ <el-button v-if="menuLocation!=='H'" type="primary" plain @click='setMenuIsCollapse' :icon="isCollapse?'el-icon-s-fold':'el-icon-s-unfold'"></el-button>
</div>
</template>
<script>
+ import Menu from "./Menu";
+ export default {
+ components:{
+ Menu
+ },
computed: {
isCollapse() {
return this.$store.state.menu.isCollapse;
},
+ menuLocation() {
+ return this.$store.state.menu.location;
+ }
},
methods:{
setMenuIsCollapse(){
this.$store.dispatch('setMenuIsCollapse');
}
}
};
</script>
src>pages>Layout>componets>Menu.vue
<el-menu
:collapse="isCollapse"
:collapse-transition="false"
router
default-active="/index/index"
class="el-menu-vertical-demo"
+ :mode="menuLocation==='H'?'horizontal':'vertical'"
>
<script>
export default {
computed: {
isCollapse() {
return this.$store.state.menu.isCollapse;
},
+ menuLocation() {
+ return this.$store.state.menu.location;
+ }
}
};
</script>
这就全部改好了,随意切换下布局状态值,完美 :)
(3)Tabs、Crumbs、Footer状态
src>data>store>index.js
state: {
menu: {
isCollapse: false,
location:"VH" //V、VH、H三个值,V表示在左侧,VH表示横跨头部,H表示在头部
},
+ tabs:{
+ isShow:true
+ },
+ crumbs:{
+ isShow:true
+ },
+ footer:{
+ isShow:true
+ }
},
src>pages>Layout>index.vue
<div class="app-container" :style="{padding:containerPadding()}">
- <Tabs :style="{height:tabs.height}" />
- <Crumbs v-if="isShowCrumbs" :style="{height:crumbs.height,padding:'0 '+content.margin}" :crumbs="crumbs" :content="content" />
+ <Tabs v-if="isShowTabs" :style="{height:tabs.height}" />
+ <Crumbs v-if="isShowCrumbs" :style="{height:crumbs.height,padding:'0 '+content.margin}" :crumbs="crumbs" :content="content" />
<Content :style="{'min-height':contentMinHeight(),margin:contentMargin()}" />
- <Footer :style="{height:footer.height,'line-height':footer.height}" />
+ <Footer v-if="isShowFooter" :style="{height:footer.height,'line-height':footer.height}" />
</div>
computed: {
isCollapse() {
return this.$store.state.menu.isCollapse;
},
menuLocation() {
return this.$store.state.menu.location;
},
+ isShowTabs(){
+ return this.$store.state.tabs.isShow;
+ },
+ isShowCrumbs(){
+ return this.$store.state.crumbs.isShow;
+ },
+ isShowFooter(){
+ return this.$store.state.footer.isShow;
+ }
},
现在可以修改相应的状态值,看下页面这些组件的显示情况,内容不够一屏时需要页面撑开,就需要调整Content的 min-height 属性,先穷举组合情况:
● Content
● Tabs + Content
● Crumbs + Content
● Tabs + Crumbs + Content
● Tabs + Crumbs + Content + Footer
● Crumbs + Content + Footer
● Tabs + Content + Footer
● Content + Footer
src>pages>Layout>index.vue
contentMargin() {
- return "0 " + this.content.margin;
+ let marginTop = this.isShowCrumbs ? "0" : this.content.margin;
+ let marginBottom = this.isShowFooter ? "0" : this.content.margin;
+ return marginTop + " " + this.content.margin +" "+ marginBottom;
},
contentMinHeight() {
- return (
- "calc(100% - " +
- this.tabs.height +
- " - " +
- this.crumbs.height +
- " - " +
- this.footer.height +
")"
- );
+ let tabsHeight = this.isShowTabs ? this.tabs.height: "0px" ;
+ let crumbsHeight = this.isShowCrumbs ? this.crumbs.height : this.content.margin; //只有content时留有底部空隙
+ let footerHeight = this.isShowFooter ? this.footer.height : this.content.margin;
+ return ("calc(100% - " +tabsHeight + " - " +crumbsHeight +" - " +footerHeight +")");
}
现在任意调整状态值看看。
(4)Tabs位置调整
随意在一个页面添加超过一屏的内容,滚动看看,发现忘记将 Tabs 浮动置顶了,现在修改添加一下。
src>assets>style>layout.styl
.app-menu
background-color white
position fixed
+ .app-tabs
+ position fixed
src>pages>Layout>index.vue
- <Tabs v-if="isShowTabs" :style="{height:tabs.height}" />
+ <Tabs v-if="isShowTabs" :style="{height:tabs.height,top:header.height,left:tabsLeft(),width:tabsWidth()}" />
containerPadding() {
+ let tabsHeight=this.isShowTabs ? this.tabs.height : "0px"
+ let paddingTop=parseInt(this.header.height)+parseInt(tabsHeight)+"px"
return (
- this.header.height +
+ paddingTop +
" 0 0 " +
this.menuWidth()
);
},
+ tabsLeft(){
+ return this.menuWidth();
+ },
+ tabsWidth(){
+ return "calc(100% - "+this.menuWidth()+" )"
+ },
contentMinHeight() {
- let tabsHeight = this.isShowTabs ? this.tabs.height: "0px" ; //tabs的高已经在containerPadding中有计算,此处去掉
let crumbsHeight = this.isShowCrumbs ? this.crumbs.height : this.content.margin;
let footerHeight = this.isShowFooter ? this.footer.height : this.content.margin;
- return ("calc(100% - " +tabsHeight + " - " +crumbsHeight +" - " +footerHeight +")");
+ return ("calc(100% - " +crumbsHeight +" - " +footerHeight +")");
}
到此我们后台模板最基本的布局就写成了,接下来我们需要进行模板块功能的实现:Menu菜单自动化(现在Menu中的导航都是我们手写的,应当根据路由文件router自动生成相应数据为佳)、Tabs多标签、Crumbs路径等将在接下来的文章中实现。
这里拿到的代码中一些文件的引入路径跟文章里的是有些区别的,小伙伴么可以想到是为什么么,答案下章说哦。
感谢阅读,喜欢的话点个赞吧:)
更多内容请关注后续文章。。。
一、vue入门基础开发—手把手教你用vue开发
三、vue+ElementUI开发后台管理模板—功能、资源、全局组件
四、vue+ElementUI开发后台管理模板—方法指令、接口数据
五、vue+ElementUI开发后台管理模板—精确到按钮的权限控制
网友评论