顶部菜单响应式

master
toesbieya 6 years ago
parent 39fdbafab8
commit 7374c2b97b
  1. 2
      vue/src/layout/component/Navbar/component/Breadcrumb.vue
  2. 219
      vue/src/layout/component/Navbar/component/RootMenu.vue
  3. 2
      vue/src/layout/component/Navbar/index.vue
  4. 6
      vue/src/layout/component/Sidebar/index.vue
  5. 2
      vue/src/layout/mixin/actionOnSelectMenu.js

@ -36,7 +36,7 @@ export default {
@import "~@/asset/style/variables.scss"; @import "~@/asset/style/variables.scss";
.app-breadcrumb { .app-breadcrumb {
margin-left: 8px; margin: 0 8px;
line-height: $nav-height; line-height: $nav-height;
.no-redirect { .no-redirect {

@ -1,10 +1,16 @@
<script type="text/jsx"> <script type="text/jsx">
import jumpOnSelectMenuMixin from "@/layout/mixin/jumpOnSelectMenu" /**
* 简易顶部菜单参考了ant design的响应式设计
*/
import ClickOutside from 'element-ui/lib/utils/clickoutside'
import actionOnSelectMenuMixin from "@/layout/mixin/actionOnSelectMenu"
export default { export default {
name: "RootMenu", name: "RootMenu",
mixins: [jumpOnSelectMenuMixin], mixins: [actionOnSelectMenuMixin],
directives: {ClickOutside},
props: { props: {
// //
@ -15,31 +21,165 @@ export default {
alwaysShow: Boolean alwaysShow: Boolean
}, },
data() {
return {
//
//undefined
//-1
lastVisibleIndex: undefined,
//menu-popover
showPopover: false
}
},
methods: { methods: {
onSelect(menu) { onSelect(menu, e) {
e.stopPropagation()
const path = menu.fullPath const path = menu.fullPath
if (path === this.activeMenu) return if (path === this.activeMenu) return
this.jumpOnSelectMenu(path, false) this.showPopover = false
this.actionOnSelectMenu(path, false)
},
//mounted
setChildrenWidth() {
const ul = this.$el
if (!ul) return
const menuItemNodes = ul.children
if (!menuItemNodes || menuItemNodes.length === 0) return
//''
this.overflowedIndicatorWidth = 57
this.menuItemSizes = Array.from(menuItemNodes).map(i => i.getBoundingClientRect().width)
this.originalTotalWidth = this.menuItemSizes.reduce((acc, cur) => acc + cur, 0)
},
resize() {
const width = this.$el.getBoundingClientRect().width
let lastVisibleIndex = undefined
//
if (this.originalTotalWidth > width) {
lastVisibleIndex = -1
//
let currentSumWidth = 0
for (const liWidth of this.menuItemSizes) {
currentSumWidth += liWidth
if (currentSumWidth + this.overflowedIndicatorWidth > width) {
break
}
lastVisibleIndex += 1
} }
}
this.lastVisibleIndex = lastVisibleIndex
}, },
render() { renderItemContent(h, menu) {
//selectpayload const {title, icon} = menu.meta
const {activeMenu, menu, alwaysShow} = this const arr = []
if (!Array.isArray(menu) || menu.length <= 1 && !alwaysShow) return icon && arr.push(<v-icon icon={icon}/>)
arr.push(title)
return arr
},
renderOverflowedIndicator(h, hideAll) {
return ( return (
<ul class="root-menu"> <div class="root-menu-item-title">
{menu.map(i => { {hideAll ? <i class="el-icon-menu"/> : '...'}
</div>
)
},
renderVisibleMenus(h, visibleMenus) {
if (visibleMenus.length <= 0) return
const activeMenu = this.activeMenu
return visibleMenus.map(i => {
const key = i.fullPath + i.meta.title const key = i.fullPath + i.meta.title
const className = {'root-menu-item': true, 'active': i.fullPath === activeMenu} const className = {'root-menu-item': true, 'active': i.fullPath === activeMenu}
const icon = i.meta.icon
return ( return (
<li key={key} class={className} on-click={() => this.onSelect(i)}> <li key={key} class={className} on-click={e => this.onSelect(i, e)}>
{icon && <v-icon icon={icon}/>} <div class="root-menu-item-title">
{i.meta.title} {this.renderItemContent(h, i)}
</div>
</li>
)
})
},
renderHideMenus(h, hideMenus) {
if (hideMenus.length <= 0) return
const activeMenu = this.activeMenu
let hasActiveChild = false
const children = hideMenus.map(i => {
const className = {'root-sub-menu-item': true, 'active': i.fullPath === activeMenu}
if (className.active) hasActiveChild = true
return (
<li class={className} on-click={e => this.onSelect(i, e)}>
{this.renderItemContent(h, i)}
</li>
)
})
const className = {
'root-menu-item': true,
'root-sub-menu': true,
'active': this.showPopover || hasActiveChild
}
return (
<li
v-click-outside={() => this.showPopover = false}
key="overflowed-indicator"
class={className}
on-click={() => this.showPopover = true}
>
{this.renderOverflowedIndicator(h, hideMenus.length === this.menu.length)}
<transition name="el-zoom-in-top">
<ul v-show={this.showPopover} class="menu-popover">
{children}
</ul>
</transition>
</li> </li>
) )
})} }
},
mounted() {
this.setChildrenWidth()
this.resizeObserver = new ResizeObserver(() => this.resize())
this.resizeObserver.observe(this.$el)
this.$once('hook:beforeDestroy', () => {
this.resizeObserver && this.resizeObserver.disconnect()
})
},
render(h) {
const {lastVisibleIndex, menu, alwaysShow} = this
if (!Array.isArray(menu) || menu.length <= 1 && !alwaysShow) {
return
}
let visibleMenus = menu, hideMenus = []
if (lastVisibleIndex !== undefined) {
//
if (lastVisibleIndex === -1) {
visibleMenus = []
hideMenus = menu
}
//
else if (lastVisibleIndex !== menu.length - 1) {
visibleMenus = menu.slice(0, lastVisibleIndex + 1)
hideMenus = menu.slice(lastVisibleIndex + 1)
}
}
return (
<ul class="root-menu">
{this.renderVisibleMenus(h, visibleMenus)}
{this.renderHideMenus(h, hideMenus)}
</ul> </ul>
) )
} }
@ -50,6 +190,8 @@ export default {
@import "~@/asset/style/variables.scss"; @import "~@/asset/style/variables.scss";
.root-menu { .root-menu {
width: 100%;
flex: 1;
height: $nav-height; height: $nav-height;
line-height: $nav-height; line-height: $nav-height;
list-style: none; list-style: none;
@ -66,14 +208,51 @@ export default {
cursor: pointer; cursor: pointer;
transition: all .2s ease-in-out; transition: all .2s ease-in-out;
> .icon { &:hover, &.active {
margin-right: 6px; border-bottom: 2px solid $--color-primary;
vertical-align: -0.125em;
> .root-menu-item-title {
color: $--color-primary;
}
}
} }
&:hover, &.active { .root-sub-menu {
position: relative;
> .menu-popover {
position: absolute;
left: -20px;
background-color: #fff;
margin: 5px 0;
padding: 5px 0;
min-width: 100px;
text-align: left;
border-radius: 4px;
box-shadow: 0 1px 6px rgba(0, 0, 0, .2);
z-index: 10;
}
&-item {
margin: 0;
line-height: normal;
padding: 7px 16px;
color: #515a6e;
white-space: nowrap;
list-style: none;
cursor: pointer;
&.active, &:hover {
color: $--color-primary; color: $--color-primary;
border-bottom: 2px solid $--color-primary; }
}
}
.root-menu-item-title, .root-sub-menu-item {
> .icon {
font-size: 14px;
margin-right: 6px;
vertical-align: -0.125em;
} }
} }
} }

@ -1,6 +1,6 @@
<template> <template>
<nav class="navbar"> <nav class="navbar">
<div> <div style="width: 30%">
<hamburger class="navbar-item" :active="!sidebarCollapse" @click="sidebarCtrl"/> <hamburger class="navbar-item" :active="!sidebarCollapse" @click="sidebarCtrl"/>
<breadcrumb v-if="showBreadcrumb"/> <breadcrumb v-if="showBreadcrumb"/>

@ -1,13 +1,13 @@
<script type="text/jsx"> <script type="text/jsx">
import {mapState} from 'vuex' import {mapState} from 'vuex'
import jumpOnSelectMenuMixin from "@/layout/mixin/jumpOnSelectMenu" import actionOnSelectMenuMixin from "@/layout/mixin/actionOnSelectMenu"
import Logo from './component/Logo' import Logo from './component/Logo'
import SidebarItem from './component/SidebarItem' import SidebarItem from './component/SidebarItem'
export default { export default {
name: 'sidebar', name: 'sidebar',
mixins: [jumpOnSelectMenuMixin], mixins: [actionOnSelectMenuMixin],
components: {SidebarItem, Logo}, components: {SidebarItem, Logo},
@ -122,7 +122,7 @@ export default {
//mobile //mobile
this.device === 'mobile' && this.$store.commit('setting/sidebarCollapse', true) this.device === 'mobile' && this.$store.commit('setting/sidebarCollapse', true)
jump && this.jumpOnSelectMenu(index) jump && this.actionOnSelectMenu(index)
} }
}, },

@ -2,7 +2,7 @@ import {isExternal} from "@/util/validate"
export default { export default {
methods: { methods: {
jumpOnSelectMenu(fullPath, refreshWhenSame = true) { actionOnSelectMenu(fullPath, refreshWhenSame = true) {
//外部链接时打开新窗口 //外部链接时打开新窗口
if (isExternal(fullPath)) { if (isExternal(fullPath)) {
return window.open(fullPath) return window.open(fullPath)
Loading…
Cancel
Save