|
|
|
|
@ -5,6 +5,7 @@ import {getters as asideGetters, mutations as asideMutations} from "@/layout/sto |
|
|
|
|
import Logo from './Logo' |
|
|
|
|
import NavMenuItem from '@/component/menu/NavMenu/item' |
|
|
|
|
import {getSidebarMenus, getActiveMenuByRoute} from "@/layout/util" |
|
|
|
|
import {getOffsetTop} from "@/util/browser" |
|
|
|
|
|
|
|
|
|
export default { |
|
|
|
|
name: 'Aside', |
|
|
|
|
@ -21,25 +22,57 @@ export default { |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
computed: { |
|
|
|
|
device: () => appGetters.device, |
|
|
|
|
isMobile: () => appGetters.isMobile, |
|
|
|
|
activeRootMenu: () => appGetters.activeRootMenu, |
|
|
|
|
|
|
|
|
|
//侧边栏的折叠状态,true折叠false展开,仅在pc端可折叠 |
|
|
|
|
//侧边栏菜单 |
|
|
|
|
menus: () => getSidebarMenus(), |
|
|
|
|
|
|
|
|
|
//当时移动端或设置了侧边栏自动隐藏时将侧边栏用抽屉包裹 |
|
|
|
|
renderInDrawer() { |
|
|
|
|
return this.isMobile || asideGetters.autoHide |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
//侧边栏的显隐状态,true显示、false隐藏 |
|
|
|
|
show() { |
|
|
|
|
//store中要显示那就显示 |
|
|
|
|
if (asideGetters.show) return true |
|
|
|
|
|
|
|
|
|
//移动端,false |
|
|
|
|
if (this.isMobile) return false |
|
|
|
|
|
|
|
|
|
//未设置侧边栏自动隐藏,false |
|
|
|
|
if (!asideGetters.autoHide) return false |
|
|
|
|
|
|
|
|
|
//鼠标在侧边栏内,true |
|
|
|
|
if (!this.mouseOutside) return true |
|
|
|
|
|
|
|
|
|
//侧边栏处于折叠状态 且 存在弹出的子菜单,true |
|
|
|
|
return this.collapse && this.openedMenus.length > 0 |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
//侧边栏的折叠状态,true折叠、false展开,仅在pc端可折叠 |
|
|
|
|
collapse() { |
|
|
|
|
return asideGetters.collapse && this.device === 'pc' |
|
|
|
|
return asideGetters.collapse && !this.isMobile |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
//是否隐藏侧边栏 |
|
|
|
|
//①设置了侧边栏自动隐藏且鼠标不在侧边栏内且是pc端 |
|
|
|
|
//②侧边栏处于折叠状态且是移动端 |
|
|
|
|
hide() { |
|
|
|
|
return asideGetters.autoHide && this.mouseOutside && this.device === 'pc' |
|
|
|
|
|| asideGetters.collapse && this.device === 'mobile' |
|
|
|
|
//判断是否需要绑定鼠标移动的事件 |
|
|
|
|
shouldBindMouseMoveEvent() { |
|
|
|
|
//如果是移动端,false |
|
|
|
|
if (this.isMobile) return false |
|
|
|
|
|
|
|
|
|
//如果未启用侧边栏自动隐藏,false |
|
|
|
|
if (!asideGetters.autoHide) return false |
|
|
|
|
|
|
|
|
|
//侧边栏为打开状态,false |
|
|
|
|
if (this.show) return false |
|
|
|
|
|
|
|
|
|
//鼠标在侧边栏外部,true |
|
|
|
|
return this.mouseOutside |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
watch: { |
|
|
|
|
//由于elMenu的initOpenedMenu()不会触发select事件,所以手动实现菜单收起 |
|
|
|
|
'$route.path': { |
|
|
|
|
immediate: true, |
|
|
|
|
handler(v) { |
|
|
|
|
@ -55,33 +88,45 @@ export default { |
|
|
|
|
//如果侧边栏中没有对应的激活菜单,则收起全部 |
|
|
|
|
if (!item) return menu.openedMenus = [] |
|
|
|
|
|
|
|
|
|
//由于elMenu的initOpenedMenu()不会触发select事件,所以选择手动触发 |
|
|
|
|
this.onSelect(item.index, item.indexPath, item, false) |
|
|
|
|
|
|
|
|
|
//仅当非抽屉模式下滚动至激活的菜单 |
|
|
|
|
!this.renderInDrawer && this.$nextTick(this.moveToCurrentMenu) |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
//切换至移动端时收起侧边栏 |
|
|
|
|
device: { |
|
|
|
|
isMobile: { |
|
|
|
|
immediate: true, |
|
|
|
|
handler(v) { |
|
|
|
|
v === 'mobile' && asideMutations.close() |
|
|
|
|
v && asideMutations.close() |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
//顶部菜单改变时重设高亮项 |
|
|
|
|
//顶部菜单改变时重设高亮项(ele考虑不周,只要菜单被点击就会激活) |
|
|
|
|
activeRootMenu(v) { |
|
|
|
|
v && this.resetActiveMenu() |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
//设置了侧边栏自动隐藏后,根据状态添加或移除鼠标移动事件 |
|
|
|
|
hide(v) { |
|
|
|
|
if (!asideGetters.autoHide) return |
|
|
|
|
const method = `${v ? 'add' : 'remove'}EventListener` |
|
|
|
|
document[method]('mousemove', this.moveEvent) |
|
|
|
|
//抽屉模式下侧边栏显示时滚动至激活菜单 |
|
|
|
|
show(v) { |
|
|
|
|
v && this.$nextTick(this.moveToCurrentMenu) |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
//添加或移除鼠标移动事件 |
|
|
|
|
shouldBindMouseMoveEvent(v) { |
|
|
|
|
//尝试移除之前可能添加的事件 |
|
|
|
|
window.removeEventListener('mousemove', this.moveEvent) |
|
|
|
|
|
|
|
|
|
v && window.addEventListener('mousemove', this.moveEvent) |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
methods: { |
|
|
|
|
//开启侧边栏自动隐藏后的鼠标移动事件 |
|
|
|
|
moveEvent(e) { |
|
|
|
|
//鼠标移动至屏幕左侧边缘时,标识鼠标在侧边栏内部 |
|
|
|
|
if (e.clientX <= 1) this.mouseOutside = false |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
@ -95,27 +140,75 @@ export default { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
jump && this.actionOnSelectMenu(index) |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
mounted() { |
|
|
|
|
if (asideGetters.autoHide) { |
|
|
|
|
document.addEventListener('mousemove', this.moveEvent) |
|
|
|
|
//抽屉模式下需要关闭抽屉 |
|
|
|
|
if (this.renderInDrawer && this.show) { |
|
|
|
|
asideMutations.close() |
|
|
|
|
this.mouseOutside = true |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
//滚动至当前激活的菜单 |
|
|
|
|
moveToCurrentMenu() { |
|
|
|
|
const menu = this.$refs.menu, cur = this.activeMenu |
|
|
|
|
if (!menu || !cur) return |
|
|
|
|
|
|
|
|
|
const curInstance = menu.items[cur] |
|
|
|
|
if (!curInstance) return |
|
|
|
|
|
|
|
|
|
let el = curInstance.$el |
|
|
|
|
|
|
|
|
|
//当侧边栏折叠时,需要滚动至可视区域的元素是激活菜单的最顶层父节点 |
|
|
|
|
if (this.collapse) { |
|
|
|
|
let rootParent = curInstance |
|
|
|
|
while (rootParent.$parent.$options.componentName !== 'ElMenu') { |
|
|
|
|
rootParent = rootParent.$parent |
|
|
|
|
} |
|
|
|
|
el = rootParent.$el |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* |
|
|
|
|
* 这里考虑了菜单展开时的200ms动画时间 |
|
|
|
|
* 为什么不分情况讨论?比如当subMenu已经是展开状态时,无需延时滚动 |
|
|
|
|
* 但这种情况无法判断,因为这时menu.openedMenus已经包含了subMenu,无论subMenu之前是否展开 |
|
|
|
|
* 所以统一延时300ms |
|
|
|
|
* */ |
|
|
|
|
window.setTimeout(() => this.scrollMenuIntoView(el, menu.$el), 300) |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
//将指定菜单滚动到可视区域内 |
|
|
|
|
scrollMenuIntoView(el, container) { |
|
|
|
|
const {scrollTop, scrollHeight, offsetHeight: menuHeight} = container |
|
|
|
|
|
|
|
|
|
//当菜单高度不足以滚动时跳过 |
|
|
|
|
if (scrollHeight <= menuHeight) return |
|
|
|
|
|
|
|
|
|
const elHeight = el.offsetHeight, between = getOffsetTop(el, container) |
|
|
|
|
|
|
|
|
|
//计算需要滚动的距离,undefined说明不需要滚动 |
|
|
|
|
let distance |
|
|
|
|
|
|
|
|
|
if (between < 0) distance = between |
|
|
|
|
else if (between + elHeight > menuHeight) { |
|
|
|
|
distance = between + elHeight - menuHeight |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (distance !== undefined) { |
|
|
|
|
container.scrollTo({top: scrollTop + distance, behavior: 'smooth'}) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
this.$once('hook:beforeDestroy', () => { |
|
|
|
|
document.removeEventListener('mousemove', this.moveEvent) |
|
|
|
|
}) |
|
|
|
|
beforeDestroy() { |
|
|
|
|
window.removeEventListener('mousemove', this.moveEvent) |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
render() { |
|
|
|
|
const menus = getSidebarMenus() |
|
|
|
|
if (menus.length <= 0) return |
|
|
|
|
if (this.menus.length <= 0) return |
|
|
|
|
|
|
|
|
|
const aside = ( |
|
|
|
|
<aside |
|
|
|
|
class={{'aside': true, 'collapse': this.collapse}} |
|
|
|
|
on-mouseenter={() => this.mouseOutside = false} |
|
|
|
|
on-mouseleave={() => this.mouseOutside = true} |
|
|
|
|
> |
|
|
|
|
{asideGetters.showLogo && <logo collapse={this.collapse}/>} |
|
|
|
|
@ -126,9 +219,9 @@ export default { |
|
|
|
|
collapse-transition={false} |
|
|
|
|
default-active={this.activeMenu} |
|
|
|
|
unique-opened={asideGetters.uniqueOpen} |
|
|
|
|
on-select={this.onSelect} |
|
|
|
|
on={{'select': this.onSelect, 'hook:mounted': this.watchOpenedMenus}} |
|
|
|
|
> |
|
|
|
|
{menus.map(m => ( |
|
|
|
|
{this.menus.map(m => ( |
|
|
|
|
<nav-menu-item |
|
|
|
|
menu={m} |
|
|
|
|
show-parent={asideGetters.showParentOnCollapse} |
|
|
|
|
@ -140,13 +233,10 @@ export default { |
|
|
|
|
</aside> |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
//当是移动端 或 设置了侧边栏自动隐藏时,渲染成抽屉 |
|
|
|
|
const renderInDrawer = this.device === 'mobile' || asideGetters.autoHide |
|
|
|
|
|
|
|
|
|
if (renderInDrawer) { |
|
|
|
|
if (this.renderInDrawer) { |
|
|
|
|
return ( |
|
|
|
|
<el-drawer |
|
|
|
|
visible={asideGetters.show} |
|
|
|
|
visible={this.show} |
|
|
|
|
with-header={false} |
|
|
|
|
direction="ltr" |
|
|
|
|
size="auto" |
|
|
|
|
|