最近項目使用iview來開發,iview UI設計仍是蠻好的相對於element-ui,可是後臺模板佈局是塊硬傷,因此本身寫了一個通用頁面,以便之後能夠直接拿來用,下面貼上代碼,分享一下:
view admin的後臺管理系統模板,可是我的愚見感受有點過重了,因此本身結合iview的頁面佈局本身寫了一套通用的模板頁面,方便後續開發使用。vue
首先,咱們新建一個Layout.vue頁面,這個頁面就是整個佈局模板的頁面,咱們設置好sidebar、topbar、以及中間的content就好,而後content,咱們放上<router-view>就能夠了,路由就隨便你怎麼跳轉了。android
Layout.vueios
<style lang="less" scoped> @import '../../../assets/gls-theme/common.less'; .ivu-layout.ivu-layout-has-sider{ height: 100%; } .ivu-layout-sider{ background: #fff; } .ivu-layout-header{ height: 100px; line-height: 18px; } .ivu-menu{ height: 100%; } .admin-layout-container{ position: absolute; width: 100%; height: 100%; .layout{ background: #f5f7f9; position: relative; overflow: hidden; height: 100%; & .dropdown-wrap{ background: rgb(73, 80, 96); } & .logo{ background: #4c364f80; border-bottom: 1px solid #363e4f; width: auto; height: 60px; display: flex; text-align: center; align-items: center; justify-content: center; cursor: pointer; } } .layout-header-bar{ background: #fff; } .layout-logo-left{ width: 90%; height: 30px; background: #5b6270; border-radius: 3px; margin: 15px auto; } .menu-icon{ transition: all .3s; } .rotate-icon{ transform: rotate(-90deg); } .menu-item span{ display: inline-block; overflow: hidden; width: 69px; text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom; transition: width .2s ease .2s; } .menu-item i{ transform: translateX(0px); transition: font-size .2s ease, transform .2s ease; vertical-align: middle; font-size: 16px; } .collapsed-menu span{ width: 0px; transition: width .2s ease; } .collapsed-menu i{ transform: translateX(5px); transition: font-size .2s ease .2s, transform .2s ease .2s; vertical-align: middle; font-size: 22px; } } </style> <template> <section class="admin-layout-container"> <div class="layout"> <Layout> <Sider ref="side1" hide-trigger collapsible :collapsed-width="78" v-model="isCollapsed" style="background: rgb(73, 80, 96);"> <div class="logo" > <img :src="logo" width="100" v-if="!isCollapsed"/> <Avatar icon="person" size="large" v-else/> </div> <Menu ref="side_menu" :active-name="activeMenuName" :open-names="openMenuName" theme="dark" width="auto" :class="menuitemClasses" @on-select="choosedMenu" v-if="!isCollapsed"> <template v-for="(menu,menu_index) in menus"> <Submenu :name="menu.name" v-if="menu.children"> <template slot="title"> <Icon :size="20" :type="menu.icon"></Icon> {{menu.title}} </template> <MenuItem :name="child.name" v-for="(child ,child_index) in menu.children" :key="child_index"> <Icon :size="20" :type="child.icon"></Icon> {{child.title}} </MenuItem> </Submenu> <MenuItem :name="menu.name" v-if="!menu.children && menu.showInMenus"> <Icon :size="20" :type="menu.icon"></Icon> {{menu.title}} </MenuItem> </template> </Menu> <div class="dropdown-wrap"> <template v-for="(menu,menu_index) in menus" v-if="isCollapsed"> <Dropdown transfer placement="right-start" v-if="menu.children" @on-click="dropdownClick"> <Button style="width: 85px;margin-left: -5px;padding:10px 0;" type="text"> <Icon :size="25" color="#fff" :type="menu.icon"></Icon> </Button> <DropdownMenu style="width: 200px;" slot="list"> <template v-for="(child, i) in menu.children"> <DropdownItem :name="child.name"> <div style="display:flex;align-items:center;"> <Icon :size="16" :type="child.icon"></Icon> <span style="padding-left:10px;"> {{ child.title }} </span> </div> </DropdownItem> </template> </DropdownMenu> </Dropdown> <Dropdown transfer v-if="!menu.children && menu.showInMenus" placement="right-start" @on-click="dropdownClick"> <Button style="width: 85px;margin-left: -5px;padding:10px 0;" type="text"> <Icon :size="25" color="#fff" :type="menu.icon"></Icon> </Button> <DropdownMenu style="width: 200px;" slot="list"> <DropdownItem :name="menu.name"> <div style="display:flex;align-items:center;"> <Icon :size="16" :type="menu.icon"></Icon> <span style="padding-left:10px;"> {{ menu.title }} </span> </div> </DropdownItem> </DropdownMenu> </Dropdown> </template> </div> </Sider> <Layout> <Header :style="{position: 'fixed', width: isCollapsed?'calc(100% - 78px)':'calc(100% - 200px)', padding: 0, display:'flex', flexDirection:'column', zIndex:20 }" class="layout-header-bar"> <div style=" display:flex; align-tems:center; justify-content:space-between; position: relative; height:60px; line-height: 60px; z-index: 1; box-shadow: 0 2px 1px 1px rgba(100, 100, 100, 0.1);"> <div style="display:flex;align-items:center;"> <Icon @click.native="collapsedSider" :class="rotateIcon" :style="{margin: '0 20px 0'}" type="navicon-round" size="24"></Icon> <span style="font-size:18px;font-weight:bold">{{user.mechanism.name}}後臺管理系統</span> </div> <div style="margin-right:20px"> <!-- <Button type="text" icon="person" size="large">我的中心</Button> <Button type="text" icon="android-notifications" size="large" @click="clickNotice">消息通知</Button> --> <Button type="text" icon="android-exit" size="large" @click="quit">退出系統</Button> </div> </div> <div style="display: flex; position: relative; padding-left:10px; height: 40px; background: #f5f7f9; align-items: center; box-shadow: 0 2px 1px 1px rgba(100, 100, 100, 0.1);"> <template v-for="(tab,tab_index) in tags"> <Tag type="dot" :closable="tab.closable" :color="tab.choosed ? 'blue':'#e9eaec'" :name="tab.name" @click.native="clickTag(tab)" @on-close="closeTag" > {{tab.title}} </Tag> </template> </div> </Header> <Content :style="{ height: 'calc(100% - 100px)', position: 'absolute', top: '100px', overflow: 'auto', padding: '10px', width:isCollapsed?'calc(100% - 78px)':'calc(100% - 200px)' }"> <!--保存組件狀態到內存,避免從新渲染--> <keep-alive> <router-view/> </keep-alive> </Content> </Layout> </Layout> </div> </section> </template> <script> import {mapActions,mapState} from 'vuex' export default { data(){ return{ logo:`${this.$qiniuFileUrl}${process.env.LOGO}`, isCollapsed: false, // ------------------------------ 菜單操做開始 -------------------------------- title:'首頁', activeMenuName:'admin', openMenuName:[], menus:[ { title:'首頁', num:1, name:'admin', icon:'home', href:'/admin', closable:false, showInTags:true, showInMenus:true, choosed:true, }, { title:'課程管理', name:'course-manage', icon:'ios-bookmarks', href:'/admin/course', closable:true, showInTags:false, showInMenus:true, choosed:false, }, { title:'老師管理', name:'teacher-manage', icon:'person-stalker', href:'/admin/teacher', closable:true, showInTags:false, showInMenus:true, choosed:false, }, { title:'學生管理', name:'student-manage', icon:'university', href:'/admin/student', closable:true, showInTags:false, showInMenus:true, choosed:false, }, { title:'課堂', name:'class-manage-parent', icon:'easel', children:[ { title:'課堂管理', name:'classroom-manage', icon:'erlenmeyer-flask', href:'/admin/classroom', closable:true, showInTags:false, showInMenus:true, choosed:false, }, { title:'上課管理', name:'class-manage', icon:'android-time', href:'/admin/class', closable:true, showInTags:false, showInMenus:true, choosed:false, } ] }, { title:'APK管理', name:'apk-manage', icon:'social-android', href:'/admin/apk', closable:true, showInTags:false, showInMenus:true, choosed:false, }, { title:'設置', name:'setting', icon:'gear-a', href:'/admin/setting', closable:true, showInTags:false, showInMenus:true, choosed:false, }, { title:'消息通知', name:'notice', icon:'ios-navigate', href:'/notice', closable:true, showInTags:false, showInMenus:false, choosed:false, } ] // ------------------------------ 菜單操做結束 -------------------------------- } }, computed: { ...mapState( { user:state=>state.user } ), // 篩選menus中選中的menu tags(){ let tags = []; // 將menus中showInTags=true的標籤放到tags數組中 this.menus.forEach(menu=>{ if(menu.showInTags){ tags.push(menu); }else if(menu.children){ menu.children.forEach(child=>{ if(child.showInTags){ tags.push(child) } }) } }); console.log('tags=>',tags) //標籤數組排序,從小到到 tags.sort((a,b)=>{ return (a.num - b.num) }) return tags; }, rotateIcon () { return [ 'menu-icon', this.isCollapsed ? 'rotate-icon' : '' ]; }, menuitemClasses () { return [ 'menu-item', this.isCollapsed ? 'collapsed-menu' : '' ] } }, // ------------------------------ 菜單操做開始 -------------------------------- //刷新頁面以後保存並選中最後一次菜單和標籤 beforeRouteEnter (to, from, next) { next(vm => { // 經過 `vm` 訪問組件實例 let activeMenuName = localStorage.activeMenuName; vm.activeMenuName = activeMenuName; let tags_last_num = vm.tags[vm.tags.length - 1].num; if(activeMenuName && activeMenuName.length != 0){ vm.menus.forEach(_menu=>{ if(activeMenuName == _menu.name){ _menu.choosed = true; _menu.showInTags = true; _menu.num = tags_last_num + 1; } else if(_menu.children){ _menu.children.forEach(child=>{ if(activeMenuName == child.name){ child.choosed = true; child.showInTags = true; child.num = tags_last_num + 1; vm.openMenuName = [_menu.name]; } }) } else{ // 排除首頁 if(_menu.name != 'admin'){ _menu.choosed = false; _menu.showInTags = false; }else{ _menu.choosed = false; } } }) } vm.$nextTick(()=>{ vm.$refs.side_menu.updateOpened(); vm.$refs.side_menu.updateActiveName(); }); }) }, // ------------------------------ 菜單操做結束 -------------------------------- methods: { ...mapActions([ 'logout' ]), quit(){ this.logout(); localStorage.removeItem('token'); localStorage.removeItem('activeMenuName'); this.$router.push('/login') }, clickNotice(){ this.choosedMenu('notice'); }, collapsedSider() { this.$refs.side1.toggleCollapse(); }, // ------------------------------ 菜單操做開始 -------------------------------- closeTag(event, name){ // 判斷該標籤是不是選中狀態 // 若是是那麼就要設置標籤數組中最後一個標籤成選中狀態 // 若是否那麼就直接刪除就好 let is_choosed = false; this.menus.forEach((menu,_index)=>{ if(menu.name == name){ is_choosed = menu.choosed; menu.showInTags = false; }else if(menu.children){ menu.children.forEach(child=>{ if(child.name == name){ is_choosed = child.choosed; child.showInTags = false; } }) } }) // 關閉標籤並選中tags中最後一個標籤高亮 if(is_choosed){ let last_tag = this.tags[this.tags.length-1]; last_tag.choosed = true; this.$router.push(last_tag.href); this.activeMenuName = last_tag.name; localStorage.activeMenuName = this.activeMenuName; } }, clickTag(tag){ this.tags.forEach(_tag=>{ if(_tag.name == tag.name){ _tag.choosed=true; }else{ _tag.choosed= false; } }) // 設置菜單選中name this.activeMenuName = tag.name; localStorage.activeMenuName = this.activeMenuName; // 刷新菜單 this.$nextTick(()=>{ if(this.$refs.side_menu){ this.$refs.side_menu.updateActiveName() } }); //點擊tab跳轉 this.$router.push(`${tag.href}`); }, choosedMenu(name){ // 獲取標籤數組最後一個元素的num let tags_last_num = this.tags[this.tags.length - 1].num; // 設置選中菜單name this.activeMenuName = name; localStorage.activeMenuName = this.activeMenuName; let if_tab = false; //根據name查找對應的菜單對象 let menu = null; this.menus.forEach(_menu=>{ if(_menu.name == name){ // 只有不在tags數組中的元素才能設置num if(!_menu.showInTags){ _menu.num = tags_last_num + 1; } menu = _menu; _menu.showInTags = true; _menu.choosed = true; } else if(_menu.children){ _menu.children.forEach(child=>{ if(child.name == name){ // 只有不在tags數組中的元素才能設置num if(!_menu.showInTags){ child.num = tags_last_num + 1; } menu = child; child.showInTags = true; child.choosed = true; }else{ child.choosed = false; } }) } else { _menu.choosed = false; } }) this.$router.push(`${menu.href}`); }, dropdownClick(name){ this.choosedMenu(name); } // ------------------------------ 菜單操做結束 -------------------------------- } } </script>
在路由頁面裏面,我引入了幾個頁面,大家能夠根據個人路徑本身新建一下便可,而後把組件和路由結合起來就能夠成功運行並應用個人Layout.vue頁面進行後臺整個頁面的佈局了,是否是超方便的。git
Router.vuegithub
import Vue from 'vue' import Router from 'vue-router' import Home from '@/views/admin/Home.vue' import AdminLayout from '@/components/admin/Layout.vue' import Admin from '@/views/admin/Admin.vue' import UserManage from '@/views/admin/UserManage.vue' import CityManage from '@/views/admin/CityManage.vue' import ConditionManage from '@/views/admin/ConditionManage.vue' import ConditionTypeManage from '@/views/admin/ConditionTypeManage.vue' import IndustryManage from '@/views/admin/IndustryManage.vue' import Setting from '@/views/admin/Setting.vue' import Notice from '@/views/admin/Notice.vue' import Login from '@/views/login/Login.vue' Vue.use(Router) let router = new Router({ mode: 'history', routes: [ { path: '/', name: 'admin', component: AdminLayout, children:[ { path:'', name:'index', meta:{ title:'首頁', }, component:Admin }, { path:'usermanage', name:'user-manage', meta:{ title:'用戶管理', }, component:UserManage }, { path:'citymanage', name:'citymanage', meta:{ title:'城市管理' }, component:CityManage }, { path:'conditiontypemanage', name:'conditiontypemanage', meta:{ title:'條件類型管理' }, component:ConditionTypeManage }, { path:'conditionmanage', name:'conditionmanage', meta:{ title:'條件管理' }, component:ConditionManage }, { path:'industrymanage', name:'industrymanage', meta:{ title:'一級行業' }, component:IndustryManage }, { path:'setting', name:'setting', meta:{ title:'設置' }, component:Setting }, { path:'notice', name:'notice', meta:{ title:'通知' }, component:Notice }, { path:'test', name:'test', meta:{ title:'測試' }, component:Setting } ] }, { path:'/login', name:'login', meta:{ title:'登陸', }, component:Login }, ] }) router.beforeEach((to, from, next) => { let token = localStorage.token; if(token && to.name != 'login'){ next() }else if(token && to.name == 'login'){ next('/'); }else if(!token && to.name != 'login'){ next('/login') }else{ next() } }) export default router;
最後,總結一下:其實網上有好多相似的模板管理頁面,可是我的感受通常,因此本身寫了一個,相信在不久的未來這樣的模板佈局頁面會愈來愈多的。vue-router
一、iview官網vuex