21 changed files with 875 additions and 41 deletions
			
			
		- 
					1src/assets/styles/index.scss
- 
					89src/assets/styles/topbar.scss
- 
					81src/components/Breadcrumb/index.vue
- 
					44src/components/Hamburger/index.vue
- 
					119src/layout/components/Navbar.vue
- 
					24src/layout/components/Sidebar/FixiOSBug.js
- 
					0src/layout/components/Sidebar/Item.vue
- 
					37src/layout/components/Sidebar/Link.vue
- 
					82src/layout/components/Sidebar/Logo.vue
- 
					79src/layout/components/Sidebar/SidebarItem.vue
- 
					49src/layout/components/Sidebar/index.vue
- 
					31src/layout/components/TopMenus.vue
- 
					207src/layout/components/Topbar.vue
- 
					4src/layout/components/index.js
- 
					39src/layout/index.vue
- 
					1src/router/index.js
- 
					11src/router/routers.js
- 
					6src/settings.js
- 
					3src/store/getters.js
- 
					9src/store/modules/permission.js
- 
					0src/views/home.vue
| @ -0,0 +1,89 @@ | |||
| .top-nav { | |||
|   // margin-left: $sideBarWidth; | |||
|   width: 100%; | |||
|   background-color: #304156; | |||
|   position: fixed; | |||
|   top: 0; | |||
|   left: 0; | |||
|   z-index: 1001; | |||
|   overflow: hidden; | |||
| 
 | |||
|   .log { | |||
|     padding: 0 20px; | |||
|     line-height: 56px; | |||
|     font-size: 24px; | |||
|     font-weight: bold; | |||
|     color: rgb(191, 203, 217); | |||
|     float: left; | |||
|   } | |||
|   .el-menu { | |||
|     float: left; | |||
|     border: none!important; | |||
|     background-color: #304156; | |||
| 
 | |||
|     .nav-item { | |||
|       display: inline-block; | |||
|       .el-menu-item { | |||
|         color: rgb(191, 203, 217); | |||
|         &:hover { | |||
|           background-color: $subMenuHover !important; | |||
|         } | |||
|         &:focus { | |||
|           background-color: $subMenuHover !important; | |||
|           // color: $subMenuActiveText !important; | |||
|         } | |||
|       } | |||
|     } | |||
|   } | |||
| 
 | |||
|   .right-menu { | |||
|     float: right; | |||
|     height: 100%; | |||
| 
 | |||
|     &:focus { | |||
|       outline: none; | |||
|     } | |||
| 
 | |||
|     .right-menu-item { | |||
|       display: inline-block; | |||
|       padding: 0 8px; | |||
|       height: 100%; | |||
|       font-size: 18px; | |||
|       color: #5a5e66; | |||
|       vertical-align: text-bottom; | |||
| 
 | |||
|       &.hover-effect { | |||
|         cursor: pointer; | |||
|         transition: background .3s; | |||
| 
 | |||
|         &:hover { | |||
|           background: rgba(0, 0, 0, .025) | |||
|         } | |||
|       } | |||
|     } | |||
| 
 | |||
|     .avatar-container { | |||
|       margin-right: 30px; | |||
| 
 | |||
|       .avatar-wrapper { | |||
|         margin-top: 5px; | |||
|         position: relative; | |||
| 
 | |||
|         .user-avatar { | |||
|           cursor: pointer; | |||
|           width: 40px; | |||
|           height: 40px; | |||
|           border-radius: 10px; | |||
|         } | |||
| 
 | |||
|         .el-icon-caret-bottom { | |||
|           cursor: pointer; | |||
|           position: absolute; | |||
|           right: -20px; | |||
|           top: 25px; | |||
|           font-size: 12px; | |||
|         } | |||
|       } | |||
|     } | |||
|   } | |||
| } | |||
| @ -0,0 +1,81 @@ | |||
| <template> | |||
|   <el-breadcrumb class="app-breadcrumb" separator="/"> | |||
|     <transition-group name="breadcrumb"> | |||
|       <el-breadcrumb-item v-for="(item,index) in levelList" :key="item.path"> | |||
|         <span v-if="item.redirect==='noredirect'||index==levelList.length-1" class="no-redirect">{{ item.meta.title }}</span> | |||
|         <a v-else @click.prevent="handleLink(item)">{{ item.meta.title }}</a> | |||
|       </el-breadcrumb-item> | |||
|     </transition-group> | |||
|   </el-breadcrumb> | |||
| </template> | |||
| 
 | |||
| <script> | |||
| import pathToRegexp from 'path-to-regexp' | |||
| 
 | |||
| export default { | |||
|   data() { | |||
|     return { | |||
|       levelList: null | |||
|     } | |||
|   }, | |||
|   watch: { | |||
|     $route(route) { | |||
|       // if you go to the redirect page, do not update the breadcrumbs | |||
|       if (route.path.startsWith('/redirect/')) { | |||
|         return | |||
|       } | |||
|       this.getBreadcrumb() | |||
|     } | |||
|   }, | |||
|   created() { | |||
|     this.getBreadcrumb() | |||
|   }, | |||
|   methods: { | |||
|     getBreadcrumb() { | |||
|       // only show routes with meta.title | |||
|       let matched = this.$route.matched.filter(item => item.meta && item.meta.title) | |||
|       const first = matched[0] | |||
| 
 | |||
|       if (!this.isDashboard(first)) { | |||
|         matched = [{ path: '/dashboard', meta: { title: '首页' }}].concat(matched) | |||
|       } | |||
| 
 | |||
|       this.levelList = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false) | |||
|     }, | |||
|     isDashboard(route) { | |||
|       const name = route && route.name | |||
|       if (!name) { | |||
|         return false | |||
|       } | |||
|       return name.trim().toLocaleLowerCase() === 'Dashboard'.toLocaleLowerCase() | |||
|     }, | |||
|     pathCompile(path) { | |||
|       // To solve this problem https://github.com/PanJiaChen/vue-element-admin/issues/561 | |||
|       const { params } = this.$route | |||
|       var toPath = pathToRegexp.compile(path) | |||
|       return toPath(params) | |||
|     }, | |||
|     handleLink(item) { | |||
|       const { redirect, path } = item | |||
|       if (redirect) { | |||
|         this.$router.push(redirect) | |||
|         return | |||
|       } | |||
|       this.$router.push(this.pathCompile(path)) | |||
|     } | |||
|   } | |||
| } | |||
| </script> | |||
| 
 | |||
| <style lang="scss" scoped> | |||
| .app-breadcrumb.el-breadcrumb { | |||
|   display: inline-block; | |||
|   font-size: 14px; | |||
|   line-height: 50px; | |||
|   margin-left: 8px; | |||
|   .no-redirect { | |||
|     color: #97a8be; | |||
|     cursor: text; | |||
|   } | |||
| } | |||
| </style> | |||
| @ -0,0 +1,44 @@ | |||
| <template> | |||
|   <div style="padding: 0 15px;" @click="toggleClick"> | |||
|     <svg | |||
|       :class="{'is-active':isActive}" | |||
|       class="hamburger" | |||
|       viewBox="0 0 1024 1024" | |||
|       xmlns="http://www.w3.org/2000/svg" | |||
|       width="64" | |||
|       height="64" | |||
|     > | |||
|       <path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z" /> | |||
|     </svg> | |||
|   </div> | |||
| </template> | |||
| 
 | |||
| <script> | |||
| export default { | |||
|   name: 'Hamburger', | |||
|   props: { | |||
|     isActive: { | |||
|       type: Boolean, | |||
|       default: false | |||
|     } | |||
|   }, | |||
|   methods: { | |||
|     toggleClick() { | |||
|       this.$emit('toggleClick') | |||
|     } | |||
|   } | |||
| } | |||
| </script> | |||
| 
 | |||
| <style scoped> | |||
| .hamburger { | |||
|   display: inline-block; | |||
|   vertical-align: middle; | |||
|   width: 20px; | |||
|   height: 20px; | |||
| } | |||
| 
 | |||
| .hamburger.is-active { | |||
|   transform: rotate(180deg); | |||
| } | |||
| </style> | |||
| @ -0,0 +1,119 @@ | |||
| <template> | |||
|   <div class="navbar"> | |||
|     <hamburger id="hamburger-container" :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" /> | |||
|     <breadcrumb id="breadcrumb-container" class="breadcrumb-container" /> | |||
|   </div> | |||
| </template> | |||
| 
 | |||
| <script> | |||
| import { mapGetters } from 'vuex' | |||
| import Breadcrumb from '@/components/Breadcrumb' | |||
| import Hamburger from '@/components/Hamburger' | |||
| // import Avatar from '@/assets/images/avatar.png' | |||
| // import variables from '@/assets/styles/variables.scss' | |||
| // import Sidebar from './Sidebar/index' | |||
| 
 | |||
| export default { | |||
|   components: { | |||
|     Breadcrumb, | |||
|     Hamburger | |||
|   }, | |||
|   computed: { | |||
|     ...mapGetters(['sidebar']) | |||
|   }, | |||
|   methods: { | |||
|     toggleSideBar() { | |||
|       this.$store.dispatch('app/toggleSideBar') | |||
|     } | |||
| 
 | |||
|   } | |||
| } | |||
| </script> | |||
| 
 | |||
| <style lang="scss" scoped> | |||
| .navbar { | |||
|   height: 60px; | |||
|   overflow: hidden; | |||
|   position: relative; | |||
|   background: #fff; | |||
|   box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08); | |||
| 
 | |||
|   .navmenu { | |||
|     float: left; | |||
|   } | |||
| 
 | |||
|   .hamburger-container { | |||
|     line-height: 46px; | |||
|     height: 100%; | |||
|     float: left; | |||
|     cursor: pointer; | |||
|     transition: background 0.3s; | |||
|     -webkit-tap-highlight-color: transparent; | |||
| 
 | |||
|     &:hover { | |||
|       background: rgba(0, 0, 0, 0.025); | |||
|     } | |||
|   } | |||
| 
 | |||
|   .breadcrumb-container { | |||
|     float: left; | |||
|   } | |||
| 
 | |||
|   .errLog-container { | |||
|     display: inline-block; | |||
|     vertical-align: top; | |||
|   } | |||
| 
 | |||
|   .right-menu { | |||
|     float: right; | |||
|     height: 100%; | |||
|     line-height: 50px; | |||
| 
 | |||
|     &:focus { | |||
|       outline: none; | |||
|     } | |||
| 
 | |||
|     .right-menu-item { | |||
|       display: inline-block; | |||
|       padding: 0 8px; | |||
|       height: 100%; | |||
|       font-size: 18px; | |||
|       color: #5a5e66; | |||
|       vertical-align: text-bottom; | |||
| 
 | |||
|       &.hover-effect { | |||
|         cursor: pointer; | |||
|         transition: background 0.3s; | |||
| 
 | |||
|         &:hover { | |||
|           background: rgba(0, 0, 0, 0.025); | |||
|         } | |||
|       } | |||
|     } | |||
| 
 | |||
|     .avatar-container { | |||
|       margin-right: 30px; | |||
| 
 | |||
|       .avatar-wrapper { | |||
|         margin-top: 5px; | |||
|         position: relative; | |||
| 
 | |||
|         .user-avatar { | |||
|           cursor: pointer; | |||
|           width: 40px; | |||
|           height: 40px; | |||
|           border-radius: 10px; | |||
|         } | |||
| 
 | |||
|         .el-icon-caret-bottom { | |||
|           cursor: pointer; | |||
|           position: absolute; | |||
|           right: -20px; | |||
|           top: 25px; | |||
|           font-size: 12px; | |||
|         } | |||
|       } | |||
|     } | |||
|   } | |||
| } | |||
| </style> | |||
| @ -0,0 +1,24 @@ | |||
| export default { | |||
|   computed: { | |||
|     device() { | |||
|       return this.$store.state.app.device | |||
|     } | |||
|   }, | |||
|   mounted() { | |||
|     this.fixBugIniOS() | |||
|   }, | |||
|   methods: { | |||
|     fixBugIniOS() { | |||
|       const $subMenu = this.$refs.subMenu | |||
|       if ($subMenu) { | |||
|         const handleMouseleave = $subMenu.handleMouseleave | |||
|         $subMenu.handleMouseleave = (e) => { | |||
|           if (this.device === 'mobile') { | |||
|             return | |||
|           } | |||
|           handleMouseleave(e) | |||
|         } | |||
|       } | |||
|     } | |||
|   } | |||
| } | |||
| @ -0,0 +1,37 @@ | |||
| <template> | |||
|   <!-- eslint-disable vue/require-component-is --> | |||
|   <!-- 防止语法报错 --> | |||
|   <component v-bind="linkProps(to)"> | |||
|     <slot /> | |||
|   </component> | |||
| </template> | |||
| 
 | |||
| <script> | |||
| import { isExternal } from '@/utils/validate' | |||
| 
 | |||
| export default { | |||
|   props: { | |||
|     to: { | |||
|       type: String, | |||
|       required: true | |||
|     } | |||
|   }, | |||
|   methods: { | |||
|     linkProps(url) { | |||
|       if (isExternal(url)) { | |||
|         return { | |||
|           is: 'a', | |||
|           href: url, | |||
|           target: '_blank', | |||
|           rel: 'noopener' | |||
|         } | |||
|       } | |||
|       return { | |||
|         is: 'router-link', | |||
|         to: url | |||
|       } | |||
|     } | |||
|   } | |||
| } | |||
| 
 | |||
| </script> | |||
| @ -0,0 +1,82 @@ | |||
| <template> | |||
|   <div class="sidebar-logo-container" :class="{'collapse':collapse}"> | |||
|     <transition name="sidebarLogoFade"> | |||
|       <router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/"> | |||
|         <img v-if="logo" :src="logo" class="sidebar-logo"> | |||
|         <h1 v-else class="sidebar-title">{{ title }}</h1> | |||
|       </router-link> | |||
|       <router-link v-else key="expand" class="sidebar-logo-link" to="/"> | |||
|         <img v-if="logo" :src="logo" class="sidebar-logo"> | |||
|         <h1 class="sidebar-title">{{ title }} </h1> | |||
|       </router-link> | |||
|     </transition> | |||
|   </div> | |||
| </template> | |||
| 
 | |||
| <script> | |||
| import Logo from '@/assets/images/logo.png' | |||
| export default { | |||
|   name: 'SidebarLogo', | |||
|   props: { | |||
|     collapse: { | |||
|       type: Boolean, | |||
|       required: true | |||
|     } | |||
|   }, | |||
|   data() { | |||
|     return { | |||
|       title: '阅行资源后台管理系统', | |||
|       logo: Logo | |||
|     } | |||
|   } | |||
| } | |||
| </script> | |||
| 
 | |||
| <style lang="scss" scoped> | |||
| .sidebarLogoFade-enter-active { | |||
|   transition: opacity 1.5s; | |||
| } | |||
| 
 | |||
| .sidebarLogoFade-enter, | |||
| .sidebarLogoFade-leave-to { | |||
|   opacity: 0; | |||
| } | |||
| 
 | |||
| .sidebar-logo-container { | |||
|   position: relative; | |||
|   width: 100%; | |||
|   height: 50px; | |||
|   line-height: 50px; | |||
|   text-align: center; | |||
|   overflow: hidden; | |||
| 
 | |||
|   & .sidebar-logo-link { | |||
|     height: 100%; | |||
|     width: 100%; | |||
| 
 | |||
|     & .sidebar-logo { | |||
|       width: 32px; | |||
|       height: 32px; | |||
|       vertical-align: middle; | |||
|       margin-right: 6px; | |||
|     } | |||
| 
 | |||
|     & .sidebar-title { | |||
|       display: inline-block; | |||
|       margin: 0; | |||
|       color: #fff; | |||
|       font-weight: 600; | |||
|       line-height: 50px; | |||
|       font-size: 14px; | |||
|       font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif; | |||
|       vertical-align: middle; | |||
|     } | |||
|   } | |||
| 
 | |||
|   &.collapse { | |||
|     .sidebar-logo { | |||
|       margin-right: 0px; | |||
|     } | |||
|   } | |||
| } | |||
| </style> | |||
| @ -0,0 +1,79 @@ | |||
| <template> | |||
|   <div v-if="!item.hidden" class="menu-wrapper"> | |||
|     <template v-if="hasOneShowingChild(item.children, item) && (!onlyOneChild.children || onlyOneChild.noShowingChildren) && !item.alwaysShow"> | |||
|       <app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path)"> | |||
|         <el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{ 'submenu-title-noDropdown': !isNest }"> | |||
|           <item :icon="onlyOneChild.meta.icon || (item.meta && item.meta.icon)" :title="onlyOneChild.meta.title" /> | |||
|         </el-menu-item> | |||
|       </app-link> | |||
|     </template> | |||
| 
 | |||
|     <el-submenu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body> | |||
|       <template slot="title"> | |||
|         <item v-if="item.meta" :icon="item.meta && item.meta.icon" :title="item.meta.title" /> | |||
|       </template> | |||
|       <sidebar-item v-for="child in item.children" :key="child.path" :is-nest="true" :item="child" :base-path="resolvePath(child.path)" class="nest-menu" /> | |||
|     </el-submenu> | |||
|   </div> | |||
| </template> | |||
| 
 | |||
| <script> | |||
| import path from 'path' | |||
| import { isExternal } from '@/utils/validate' | |||
| 
 | |||
| export default { | |||
|   name: 'SidebarItem', | |||
| 
 | |||
|   props: { | |||
|     // route object | |||
|     item: { | |||
|       type: Object, | |||
|       required: true | |||
|     }, | |||
|     isNest: { | |||
|       type: Boolean, | |||
|       default: false | |||
|     }, | |||
|     basePath: { | |||
|       type: String, | |||
|       default: '' | |||
|     } | |||
|   }, | |||
|   data() { | |||
|     this.onlyOneChild = null | |||
|     return {} | |||
|   }, | |||
|   methods: { | |||
|     hasOneShowingChild(children = [], parent) { | |||
|       const showingChildren = children.filter((item) => { | |||
|         if (item.hidden) { | |||
|           return false | |||
|         } else { | |||
|           this.onlyOneChild = item | |||
|           return true | |||
|         } | |||
|       }) | |||
| 
 | |||
|       if (showingChildren.length === 1) { | |||
|         return true | |||
|       } | |||
| 
 | |||
|       if (showingChildren.length === 0) { | |||
|         this.onlyOneChild = { ...parent, path: '', noShowingChildren: true } | |||
|         return true | |||
|       } | |||
| 
 | |||
|       return false | |||
|     }, | |||
|     resolvePath(routePath) { | |||
|       if (isExternal(routePath)) { | |||
|         return routePath | |||
|       } | |||
|       if (isExternal(this.basePath)) { | |||
|         return this.basePath | |||
|       } | |||
|       return path.resolve(this.basePath, routePath) | |||
|     } | |||
|   } | |||
| } | |||
| </script> | |||
| @ -0,0 +1,49 @@ | |||
| <template> | |||
|   <div :class="{ 'has-logo': showLogo }"> | |||
|     <logo v-if="showLogo" :collapse="isCollapse" /> | |||
|     <el-scrollbar wrap-class="scrollbar-wrapper"> | |||
|       <el-menu :default-active="activeMenu" :collapse="isCollapse" :background-color="variables.menuBg" :text-color="variables.menuText" :active-text-color="variables.menuActiveText" :collapse-transition="false" mode="vertical"> | |||
|         <sidebar-item v-for="route in routes" :key="route.path" :item="route" :base-path="route.path" /> | |||
|       </el-menu> | |||
|     </el-scrollbar> | |||
|   </div> | |||
| </template> | |||
| 
 | |||
| <script> | |||
| import { mapGetters } from 'vuex' | |||
| import Logo from './Logo' | |||
| import SidebarItem from './SidebarItem' | |||
| import variables from '@/assets/styles/variables.scss' | |||
| 
 | |||
| export default { | |||
|   components: { SidebarItem, Logo }, | |||
|   props: { | |||
| 
 | |||
|   }, | |||
|   computed: { | |||
|     ...mapGetters(['sidebar']), | |||
|     routes() { | |||
|       return this.$store.state.permission.currentRoutes.children | |||
|     }, | |||
|     activeMenu() { | |||
|       const route = this.$route | |||
|       const { meta, path } = route | |||
| 
 | |||
|       if (meta.activeMenu) { | |||
|         return meta.activeMenu | |||
|       } | |||
|       return path | |||
|     }, | |||
|     showLogo() { | |||
|       return this.$store.state.settings.sidebarLogo | |||
|     }, | |||
|     variables() { | |||
|       return variables | |||
|     }, | |||
|     isCollapse() { | |||
|       return !this.sidebar.opened | |||
|     } | |||
|   } | |||
| 
 | |||
| } | |||
| </script> | |||
| @ -1,31 +0,0 @@ | |||
| <template> | |||
|   <div> | |||
|     <el-menu :default-active="activeMenu" :background-color="variables.menuBg" :text-color="variables.menuText" :active-text-color="variables.menuActiveText" :collapse-transition="false" mode="horizontal"> | |||
|       <el-menu-item v-for="route in sidebarRouters" :key="route.path" :item="route" :base-path="route.path" /> | |||
|     </el-menu> | |||
|   </div> | |||
| </template> | |||
| 
 | |||
| <script> | |||
| import { mapGetters } from 'vuex' | |||
| import variables from '@/assets/styles/variables.scss' | |||
| 
 | |||
| export default { | |||
|   name: 'TopMenus', | |||
|   computed: { | |||
|     ...mapGetters(['sidebarRouters', 'sidebar']), | |||
|     activeMenu() { | |||
|       const route = this.$route | |||
|       const { meta, path } = route | |||
| 
 | |||
|       if (meta.activeMenu) { | |||
|         return meta.activeMenu | |||
|       } | |||
|       return path | |||
|     }, | |||
|     variables() { | |||
|       return variables | |||
|     } | |||
|   } | |||
| } | |||
| </script> | |||
| @ -0,0 +1,207 @@ | |||
| <template> | |||
|   <div class="top-nav"> | |||
|     <el-menu :active-text-color="variables.menuActiveText" :default-active="activeMenu" mode="horizontal" @select="handleSelect"> | |||
|       <div v-for="item in routes" :key="item.path" class="nav-item"> | |||
|         <app-link :to="resolvePath(item)"> | |||
|           <el-menu-item v-if="!item.hidden" :index="item.path"> | |||
|             {{ item.meta ? item.meta.title : item.children[0].meta.title }} | |||
|           </el-menu-item> | |||
|         </app-link> | |||
|       </div> | |||
|     </el-menu> | |||
| 
 | |||
|     <div class="right-menu"> | |||
|       <!-- <template v-if="device !== 'mobile'"> | |||
|         <search id="header-search" class="right-menu-item" /> | |||
|         <el-tooltip content="项目文档" effect="dark" placement="bottom"> | |||
|           <Doc class="right-menu-item hover-effect" /> | |||
|         </el-tooltip> | |||
|         <el-tooltip content="全屏缩放" effect="dark" placement="bottom"> | |||
|           <screenfull id="screenfull" class="right-menu-item hover-effect" /> | |||
|         </el-tooltip> | |||
|         <el-tooltip content="布局设置" effect="dark" placement="bottom"> | |||
|           <size-select id="size-select" class="right-menu-item hover-effect" /> | |||
|         </el-tooltip> | |||
|       </template> --> | |||
| 
 | |||
|       <el-dropdown class="avatar-container right-menu-item hover-effect" trigger="click"> | |||
|         <div class="avatar-wrapper"> | |||
|           <img :src="user.avatarName ? baseApi + '/avatar/' + user.avatarName : Avatar" class="user-avatar"> | |||
|           <i class="el-icon-caret-bottom" /> | |||
|         </div> | |||
|         <el-dropdown-menu slot="dropdown"> | |||
|           <span style="display:block;" @click="show = true"> | |||
|             <el-dropdown-item> | |||
|               布局设置 | |||
|             </el-dropdown-item> | |||
|           </span> | |||
|           <router-link to="/user/center"> | |||
|             <el-dropdown-item> | |||
|               个人中心 | |||
|             </el-dropdown-item> | |||
|           </router-link> | |||
|           <span style="display:block;" @click="open"> | |||
|             <el-dropdown-item divided> | |||
|               退出登录 | |||
|             </el-dropdown-item> | |||
|           </span> | |||
|         </el-dropdown-menu> | |||
|       </el-dropdown> | |||
|     </div> | |||
|   </div> | |||
| </template> | |||
| 
 | |||
| <script> | |||
| import { mapGetters } from 'vuex' | |||
| import AppLink from './Sidebar/Link' | |||
| import variables from '@/assets/styles/variables.scss' | |||
| import { isExternal } from '@/utils/validate' | |||
| import Avatar from '@/assets/images/avatar.png' | |||
| 
 | |||
| export default { | |||
|   name: 'Topbar', | |||
|   components: { | |||
|     AppLink | |||
|   }, | |||
|   data() { | |||
|     return { | |||
|       Avatar: Avatar, | |||
|       dialogVisible: false, | |||
|       routes: this.$store.getters.navMenus | |||
|     } | |||
|   }, | |||
|   computed: { | |||
|     ...mapGetters(['device', 'user', 'baseApi']), | |||
|     activeMenu() { | |||
|       const route = this.$route | |||
|       const { meta, path } = route | |||
|       console.log(this.routes) | |||
|       // if set path, the sidebar will highlight the path you set | |||
|       if (meta.activeMenu) { | |||
|         return meta.activeMenu | |||
|       } | |||
|       // 如果是首页,首页高亮 | |||
|       if (path === '/dashboard') { | |||
|         return '/' | |||
|       } | |||
|       // 如果不是首页,高亮一级菜单 | |||
|       const activeMenu = '/' + path.split('/')[1] | |||
|       return activeMenu | |||
|     }, | |||
|     variables() { | |||
|       return variables | |||
|     }, | |||
|     sidebar() { | |||
|       return this.$store.state.app.sidebar | |||
|     }, | |||
|     show: { | |||
|       get() { | |||
|         return this.$store.state.settings.showSettings | |||
|       }, | |||
|       set(val) { | |||
|         this.$store.dispatch('settings/changeSetting', { | |||
|           key: 'showSettings', | |||
|           value: val | |||
|         }) | |||
|       } | |||
|     } | |||
| 
 | |||
|   }, | |||
|   methods: { | |||
|     // 通过当前路径找到二级菜单对应项,存到store,用来渲染左侧菜单 | |||
|     initCurrentRoutes() { | |||
|       const { path } = this.$route | |||
|       console.log(this.routes) | |||
|       let route = this.routes.find( | |||
|         item => item.path === '/' + path.split('/')[1] | |||
|       ) | |||
|       // 如果找不到这个路由,说明是首页 | |||
|       if (!route) { | |||
|         route = this.routes.find(item => item.path === '/') | |||
|       } | |||
|       this.$store.commit('permission/SET_CURRENT_ROUTES', route) | |||
|       this.setSidebarHide(route) | |||
|     }, | |||
|     // 判断该路由是否只有一个子项或者没有子项,如果是,则在一级菜单添加跳转路由 | |||
|     isOnlyOneChild(item) { | |||
|       if (item.children && item.children.length === 1) { | |||
|         return true | |||
|       } | |||
|       return false | |||
|     }, | |||
|     resolvePath(item) { | |||
|       // 如果是个完成的url直接返回 | |||
|       if (isExternal(item.path)) { | |||
|         return item.path | |||
|       } | |||
|       // 如果是首页,就返回重定向路由 | |||
|       if (item.path === '/') { | |||
|         const path = item.redirect | |||
|         return path | |||
|       } | |||
| 
 | |||
|       // 如果有子项,默认跳转第一个子项路由 | |||
|       let path = '' | |||
|       /** | |||
|        * item 路由子项 | |||
|        * parent 路由父项 | |||
|        */ | |||
|       const getDefaultPath = (item, parent) => { | |||
|         // 如果path是个外部链接(不建议),直接返回链接,存在个问题:如果是外部链接点击跳转后当前页内容还是上一个路由内容 | |||
|         if (isExternal(item.path)) { | |||
|           path = item.path | |||
|           return | |||
|         } | |||
|         // 第一次需要父项路由拼接,所以只是第一个传parent | |||
|         if (parent) { | |||
|           path += (parent.path + '/' + item.path) | |||
|         } else { | |||
|           path += ('/' + item.path) | |||
|         } | |||
|         // 如果还有子项,继续递归 | |||
|         if (item.children) { | |||
|           getDefaultPath(item.children[0]) | |||
|         } | |||
|       } | |||
| 
 | |||
|       if (item.children) { | |||
|         getDefaultPath(item.children[0], item) | |||
|         return path | |||
|       } | |||
| 
 | |||
|       return item.path | |||
|     }, | |||
|     handleSelect(key, keyPath) { | |||
|       // 把选中路由的子路由保存store | |||
|       const route = this.routes.find(item => item.path === key) | |||
|       this.$store.commit('permission/SET_CURRENT_ROUTES', route) | |||
|       this.setSidebarHide(route) | |||
|     }, | |||
|     // 设置侧边栏的显示和隐藏 | |||
|     setSidebarHide(route) { | |||
|       if (!route.children || route.children.length === 1) { | |||
|         this.$store.dispatch('app/toggleSideBarHide', true) | |||
|       } else { | |||
|         this.$store.dispatch('app/toggleSideBarHide', false) | |||
|       } | |||
|     }, | |||
|     toggleSideBar() { | |||
|       this.$store.dispatch('app/toggleSideBar') | |||
|     }, | |||
|     open() { | |||
|       this.$confirm('确定注销并退出系统吗?', '提示', { | |||
|         confirmButtonText: '确定', | |||
|         cancelButtonText: '取消', | |||
|         type: 'warning' | |||
|       }).then(() => { | |||
|         this.logout() | |||
|       }) | |||
|     }, | |||
|     logout() { | |||
|       this.$store.dispatch('LogOut').then(() => { | |||
|         location.reload() | |||
|       }) | |||
|     } | |||
|   } | |||
| } | |||
| </script> | |||
| @ -1,2 +1,4 @@ | |||
| export { default as AppMain } from './AppMain' | |||
| export { default as TopMenus } from './TopMenus' | |||
| export { default as Navbar } from './Navbar' | |||
| export { default as Sidebar } from './Sidebar' | |||
| export { default as Topbar } from './Topbar' | |||
						Write
						Preview
					
					
					Loading…
					
					Cancel
						Save
					
		Reference in new issue