Vue 2.x + Element后台模板开发教程(三)后台首页模板设计
最近在学习vue.js开发,想做个简单的管理系统,研究了很长时间,看了很多慕课网的视频教程,教程地址:https://www.imooc.com/new/course/list?c=vuejs,看完教程开始试着做项目,做到后台首页被几个问题困扰了很久。
1、后台模板,模板功能怎么实现,之前都是嵌套iframe,现在怎么处理呢?研究了两天,发现这里的控制使用子路由实现,vue在路由跳转时,跳转到子路由会保留父路由的页面内容。
2、从网上下载了很多代码,都没跑起来,左侧菜单都是动态的,都得配置接口程序。
3、vue后台模板路由也需要根据接口返回权限动态实现,如果访问地址路由里面没有跳转到404页面。
本文实现的后台模板只是静态页面,动态路由、动态菜单会随着学习逐步完善。
先看看效果图:
实现思路:先创建一个主模板文件,home.vue,然后顶部是一个组件header.vue,底部是一个组件footer.vue,左侧菜单栏是一个组件Sidebar.vue,还有个tags.vue组件用于管理打开页面,中间内容部分是一个<router-view></router-view>用于展示具体跳转的页面,新增完目录如下:
home页面代码:
<template> <div class="wrapper"> <v-head></v-head> <v-sidebar></v-sidebar> <div class="content-box" :class="{'content-collapse':collapse}"> <div class="content_wrapper"> <v-tags></v-tags> <div class="content" style="flex:1;"> <div class="content_inner"> <transition name="move" mode="out-in"> <keep-alive :include="tagsList"> <router-view></router-view> </keep-alive> </transition> </div> </div> <v-footer></v-footer> </div> </div> </div> </template> <style lang="scss" scoped> .content_wrapper { display: flex; flex-direction: column; height: 100%; .content_inner { // background: #fff; height: 100%; } } </style> <script> import vHead from './Header.vue'; import vSidebar from './Sidebar.vue'; import vTags from './Tags.vue'; import vFooter from './Footer.vue'; import bus from './bus'; export default { data() { return { tagsList: [], collapse: false } }, components: { vHead, vSidebar, vTags, vFooter }, created() { bus.$on('collapse', msg => { this.collapse = msg; }) // 只有在标签页列表里的页面才使用keep-alive,即关闭标签之后就不保存到内存中了。 bus.$on('tags', msg => { let arr = []; for (let i = 0, len = msg.length; i < len; i++) { msg[i].name && arr.push(msg[i].name); } this.tagsList = arr; }) } } </script>
import vHead from './Header.vue'; import vSidebar from './Sidebar.vue'; import vTags from './Tags.vue'; import vFooter from './Footer.vue'; import bus from './bus'; components: { vHead, vSidebar, vTags, vFooter },
以上代码引用各个子组件。
Header.vue代码如下:
<template> <div class="header"> <!-- 折叠按钮 --> <div class="collapse-btn" @click="collapseChage"> <i class="el-icon-menu"></i> </div> <div class="logo">孔子人才网后台管理系统</div> <div class="header-right"> <div class="header-user-con"> <!-- <marquee onMouseOver="this.start()" style="font-size:18px;padding-bottom:4px;width:100px;" scrollamount="1">{{getlev}}</marquee> --> <div style="font-size:18px;padding-bottom:4px;width:60px;">admin</div> <!-- 全屏显示 --> <div class="btn-fullscreen" @click="handleFullScreen"> <el-tooltip effect="dark" :content="fullscreen?`取消全屏`:`全屏`" placement="bottom"> <i class="el-icon-rank"></i> </el-tooltip> </div> <!-- 消息中心 --> <div class="btn-bell"> <el-tooltip effect="dark" :content="message?`有${message}条未读消息`:`消息中心`" placement="bottom"> <router-link to="/tabs"> <i class="el-icon-bell"></i> </router-link> </el-tooltip> <span class="btn-bell-badge" v-if="message"></span> </div> <!-- 用户头像 --> <div class="user-avator"><img src="../../assets/Images/img.jpg"></div> <!-- 用户名下拉菜单 --> <el-dropdown class="user-name" trigger="click" @command="handleCommand"> <span class="el-dropdown-link"> 用户名<i class="el-icon-caret-bottom"></i> </span> <el-dropdown-menu slot="dropdown"> <a href="https://github.com/merciqiao" target="_blank"> <el-dropdown-item>关于作者</el-dropdown-item> </a> <a href="https://github.com/merciqiao/merciqiao-vue" target="_blank"> <el-dropdown-item>项目仓库</el-dropdown-item> </a> <a href="/zanzhu" target="_blank"> <el-dropdown-item style="color:orange;">赞助作者</el-dropdown-item> </a> <el-dropdown-item divided command="changeZh">切换中文</el-dropdown-item> <el-dropdown-item command="changeEn">切换英文</el-dropdown-item> <el-dropdown-item divided command="loginout">退出登录</el-dropdown-item> </el-dropdown-menu> </el-dropdown> </div> </div> </div> </template> <script> import bus from './bus'; export default { data() { return { collapse: false, fullscreen: false, name: 'merciqiao', lev:'青铜级', message: 2 } }, computed:{ username(){ let username = this.$common.getSessionStorage('username'); return username ? username : this.name; }, getlev(){ let levList = this.$common.getSessionStorage('lev',true); let lev=''; if(levList){ for(var i=0;i<levList.length;i++){ lev+=levList[i].roleName; } } return lev ? lev : this.lev; } }, methods:{ // 用户名下拉菜单选择事件 handleCommand(command) { if(command == 'loginout'){ this.$common.removeSessionStorage('token'); this.$router.push('/login'); } else if(command == 'changeZh'){ this.$i18n.locale = 'zh_CN'; } else if(command == 'changeEn'){ this.$i18n.locale = 'en_US'; } }, // 侧边栏折叠 collapseChage(){ this.collapse = !this.collapse; bus.$emit('collapse', this.collapse); }, // 全屏事件 handleFullScreen(){ let element = document.documentElement; if (this.fullscreen) { if (document.exitFullscreen) { document.exitFullscreen(); } else if (document.webkitCancelFullScreen) { document.webkitCancelFullScreen(); } else if (document.mozCancelFullScreen) { document.mozCancelFullScreen(); } else if (document.msExitFullscreen) { document.msExitFullscreen(); } } else { if (element.requestFullscreen) { element.requestFullscreen(); } else if (element.webkitRequestFullScreen) { element.webkitRequestFullScreen(); } else if (element.mozRequestFullScreen) { element.mozRequestFullScreen(); } else if (element.msRequestFullscreen) { // IE11 element.msRequestFullscreen(); } } this.fullscreen = !this.fullscreen; } }, mounted(){ if(document.body.clientWidth < 1366){ this.collapseChage(); } } } </script> <style scoped> .header { position: relative; box-sizing: border-box; width: 100%; height: 70px; font-size: 22px; color: #fff; background-color: #07c4a8; } .collapse-btn{ float: left; padding: 0 21px; cursor: pointer; line-height: 70px; } .header .logo{ float: left; width:250px; line-height: 70px; } .header-right{ float: right; padding-right: 50px; } .header-user-con{ display: flex; height: 70px; align-items: center; } .btn-fullscreen{ transform: rotate(45deg); margin-right: 5px; font-size: 24px; } .btn-bell, .btn-fullscreen{ position: relative; width: 30px; height: 30px; text-align: center; border-radius: 15px; cursor: pointer; } .btn-bell-badge{ position: absolute; right: 0; top: -2px; width: 8px; height: 8px; border-radius: 4px; background: #f56c6c; color: #fff; } .btn-bell .el-icon-bell{ color: #fff; } .user-name{ margin-left: 10px; } .user-avator{ margin-left: 20px; } .user-avator img{ display: block; width:40px; height:40px; border-radius: 50%; } .el-dropdown-link{ color: #fff; cursor: pointer; } .el-dropdown-menu__item{ text-align: center; } </style>
footer.vue组件代码如下:
<template> <footer> <div class="footer_content"> <!-- 京ICP备18050367号-1 qq群:73110051(无广告) {{$t("footer.title")}}--> </div> </footer> </template> <style lang="scss"> footer{ border-top:1px solid #ddd; flex: 0 0 auto; .footer_content{ width: 340px; margin: 0 auto; text-align: center; line-height: 60px; height: 60px; color:#606266; .qq{ font-size: 9px; } } } </style>
Siderbar.vue组件代码如下:
<template> <div class="sidebar"> <el-menu class="sidebar-el-menu" :default-active="onRoutes" :collapse="collapse" background-color="#324157" text-color="#bfcbd9" active-text-color="#20a0ff" unique-opened router> <el-submenu index="1"> <template slot="title"> <i class="el-icon-location"></i> <span>导航一</span> </template> <el-menu-item-group> <template slot="title">分组一</template> <el-menu-item index="1-1">选项1</el-menu-item> <el-menu-item index="1-2">选项2</el-menu-item> </el-menu-item-group> <el-menu-item-group title="分组2"> <el-menu-item index="1-3">选项3</el-menu-item> </el-menu-item-group> <el-submenu index="1-4"> <template slot="title">选项4</template> <el-menu-item index="1-4-1">选项1</el-menu-item> </el-submenu> </el-submenu> <el-menu-item index="2"> <i class="el-icon-menu"></i> <span slot="title">导航二</span> </el-menu-item> <el-menu-item index="3" disabled> <i class="el-icon-document"></i> <span slot="title">导航三</span> </el-menu-item> <el-menu-item index="4"> <i class="el-icon-setting"></i> <span slot="title">导航四</span> </el-menu-item> </el-menu> <!--<el-menu class="sidebar-el-menu" :default-active="onRoutes" :collapse="collapse" background-color="#324157" text-color="#bfcbd9" active-text-color="#20a0ff" unique-opened router> <template v-for="(item,i) in menuList"> <template v-if="item.subs&&item.subs.length"> <el-submenu :index="item.index" :key="i"> <template slot="title"> <i :class="item.icon"></i><span slot="title">{{ item.title }}</span> </template> <el-menu-item v-for="(subItem,i) in item.subs" :key="i" :index="subItem.index"> {{ subItem.title }} </el-menu-item> </el-submenu> </template> </template> </el-menu>--> </div> </template> <script> import bus from './bus'; import { mapGetters } from 'vuex' export default { data() { return { collapse: false, items: [{ icon: 'el-icon-setting', index: 'index', title: '系统首页', }, { icon: 'el-icon-tickets', index: 'table', title: '基础模块', subs: [{ index: 'searchinput', title: '查询输入页' }, { index: 'tabpage', title: '标签选项卡' }, { index: 'tablepage', title: '综合表格页' }, { index: 'formpage', title: '表单页' }, { index: 'treepage', title: '树组件页' } ] }, { icon: 'el-icon-setting', index: '3', title: '系统管理', subs: [{ index: 'organizationTree', title: '机构管理' }, { index: 'sysUser', title: '用户管理' }, { index: 'sysRole', title: '角色管理' }, { index: 'sysAcl', title: '角色资源授权' }, { index: 'sysResource', title: '资源管理' }, { index: 'sysMenu', title: '菜单管理' } ] }, // { // icon: 'el-icon-message', // index: 'tabs', // title: 'tab选项卡' // }, // { // icon: 'el-icon-date', // index: '3', // title: '表单相关', // subs: [ // { // index: 'form', // title: '基本表单' // }, // { // index: 'editor', // title: '富文本编辑器' // }, // { // index: 'markdown', // title: 'markdown编辑器' // }, // { // index: 'upload', // title: '文件上传' // } // ] // }, // { // icon: 'el-icon-star-on', // index: 'charts', // title: 'schart图表' // }, // { // icon: 'el-icon-rank', // index: 'drag', // title: '拖拽列表' // }, // { // icon: 'el-icon-warning', // index: 'permission', // title: '权限测试' // }, { icon: 'el-icon-error', index: '404', title: '404页面' } ] } }, computed: mapGetters({ menuList: 'getMenuList', onRoutes() { return this.$route.path.replace('/', ''); } }), created() { // 通过 Event Bus 进行组件间通信,来折叠侧边栏 bus.$on('collapse', msg => { this.collapse = msg; }) } } </script> <style scoped> .sidebar { display: block; position: absolute; left: 0; top: 70px; bottom: 0; overflow-y: scroll; } .sidebar::-webkit-scrollbar { width: 0; } .sidebar-el-menu:not(.el-menu--collapse) { width: 250px; } .sidebar>ul { height: 100%; } </style>
Tags.vue组件代码如下:
<template> <div class="tags" v-if="showTags"> <ul> <li class="tags-li" v-for="(item,index) in tagsList" :class="{'active': isActive(item.path)}" :key="index"> <router-link :to="item.path" class="tags-li-title"> {{item.title}} </router-link> <span v-show="item.title!='系统首页'" class="tags-li-icon" @click="closeTags(index)"><i class="el-icon-close"></i></span> </li> </ul> <div class="tags-close-box"> <el-dropdown @command="handleTags"> <el-button size="small" type="primary"> 标签选项<i class="el-icon-arrow-down el-icon--right"></i> </el-button> <el-dropdown-menu size="medium" slot="dropdown"> <el-dropdown-item command="other">关闭其他</el-dropdown-item> <el-dropdown-item command="all">关闭所有</el-dropdown-item> </el-dropdown-menu> </el-dropdown> </div> </div> </template> <script> import bus from './bus'; export default { data() { return { tagsList: [] } }, methods: { isActive(path) { return path === this.$route.fullPath; }, // 关闭单个标签 closeTags(index) { const delItem = this.tagsList.splice(index, 1)[0]; const item = this.tagsList[index] ? this.tagsList[index] : this.tagsList[index - 1]; if (item) { delItem.path === this.$route.fullPath && this.$router.push(item.path); }else{ this.$router.push('/'); } }, // 关闭全部标签 closeAll(){ this.tagsList = [{ name: "index", path: "/index", title: "系统首页" }]; this.$router.push('/'); }, // 关闭其他标签 closeOther(){ const curItem = this.tagsList.filter(item => { return item.path === this.$route.fullPath; }) this.tagsList = curItem; }, // 设置标签 setTags(route){ const isExist = this.tagsList.some(item => { return item.path === route.fullPath; }) !isExist && this.tagsList.push({ title: route.meta.title, path: route.fullPath, // name: route.matched[1].components.default.name name: route.name }) bus.$emit('tags', this.tagsList); }, handleTags(command){ command === 'other' ? this.closeOther() : this.closeAll(); } }, computed: { showTags() { return this.tagsList.length > 0; } }, watch:{ $route(newValue, oldValue){ this.setTags(newValue); } }, created(){ this.setTags(this.$route); } } </script> <style lang="scss"> $tag_height:34px;//tab高度变量 .tags { position: relative; height: $tag_height; overflow: hidden; background: #fff; padding-right: 120px; flex: 0 0 auto; } .tags ul { box-sizing: border-box; width: 100%; height: 100%; } .tags-li { float: left; margin: 3px 5px 2px 3px; border-radius: 3px; font-size: 12px; overflow: hidden; cursor: pointer; height: $tag_height - 8px; line-height: $tag_height - 8px; border: 1px solid #e9eaec; background: #fff; padding: 0 5px 0 12px; vertical-align: middle; color: #666; -webkit-transition: all .3s ease-in; -moz-transition: all .3s ease-in; transition: all .3s ease-in; } .tags-li:not(.active):hover { background: #f8f8f8; } .tags-li.active { color: #fff; } .tags-li-title { float: left; max-width: 80px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; margin-right: 5px; color: #666; } .tags-li.active .tags-li-title { color: #fff; } .tags-close-box { position: absolute; right: 0; top: 0; box-sizing: border-box; padding-top: 1px; text-align: center; width: 110px; height: $tag_height; background: #fff; box-shadow: -3px 0 15px 3px rgba(0, 0, 0, .1); z-index: 10; } </style>
最后修改路由配置文件,代码如下:
const routes = [{ path: '/', name: 'Login', component: Login }, { name: 'Home', path: '/Home', component: Home, meta: { title: '系统首页' } } ]
项目创建是用的vue cli3脚手架创建的。
以下是博主微信欢迎沟通交流。
本文附件(20201209171134recruitplatformbk.rar)价格:¥5元
立刻购买资源
注:支付完成后请等待支付页面返回下载页面或者刷新此页面进行下载。
猜您可能还喜欢
- CERT_HAS_EXPIRED错误如何解决(2240)
- Js异步async、await关键字详细介绍(lambda表达式中使用async和await关键字)(2187)
- Vue 2.x + Element后台模板开发教程(三)后台首页模板设计(1213)
- vuex中 this.$store.dispatch() 与 this.$store.commit()方法的区别(1189)
- vuejs中如何使用axios调用后台接口(1025)
- Vue 2.x + Element后台模板开发教程(一)(1020)
- Error: Cannot find module ‘chokidar‘ 解决方法(1009)
- Vue后台开发实例教程(二)Title修改及左侧菜单修改(898)
- vue.js初探(一)vue.js全家桶介绍(823)
- vue.js初探(三)vue页面结构(820)
评论列表
发表评论
文章分类
文章归档
- 2025年3月 (1)
- 2024年6月 (2)
- 2024年5月 (2)
- 2024年4月 (4)
- 2024年3月 (30)
- 2024年1月 (4)
- 2023年12月 (2)
- 2023年11月 (4)
- 2023年10月 (4)
- 2023年9月 (6)
- 2023年3月 (2)
- 2023年2月 (1)
- 2023年1月 (1)
- 2022年12月 (1)
- 2022年9月 (21)
- 2022年8月 (10)
- 2022年7月 (3)
- 2022年4月 (1)
- 2022年3月 (13)
- 2021年8月 (1)
- 2021年3月 (1)
- 2020年12月 (42)
- 2020年11月 (7)
- 2020年10月 (5)
- 2020年8月 (1)
- 2020年6月 (1)
- 2020年3月 (2)
- 2019年12月 (8)
- 2019年11月 (3)
- 2019年9月 (1)
- 2019年4月 (1)
- 2019年3月 (6)
- 2019年2月 (1)
- 2018年7月 (7)
阅读排行
- 1.asp.net mvc内微信pc端、H5、JsApi支付方式总结(5702)
- 2.各大搜索网站网站收录提交入口地址(3201)
- 3.Windows 10休眠文件更改存储位置(3163)
- 4.ECharts仪表盘实例及参数使用详解(3095)
- 5.windows 10安装myeclipse 10破解补丁cracker.jar、run.bat闪退解决办法(2991)
- 6.HTML5 WebSocket与C#建立Socket连接实现代码(2865)
- 7.华为鸿蒙系统清除微信浏览器缓存方法(2777)
- 8.CERT_HAS_EXPIRED错误如何解决(2240)
- 9.Js异步async、await关键字详细介绍(lambda表达式中使用async和await关键字)(2187)
- 10.HBuilder编辑器格式化代码(2118)