(獲取本節完整代碼 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開發后臺管理模板—精確到按鈕的權限控制