el-menu在作为导航时非常好用,有非常多的使用场景,但是这个组件本身有一些问题,在使用时需要额外注意:
一、el-menu多次修改default-active时,视图不更新Bug:
解决办法很简单:
// 不高亮任何地方 如果写成固定值,则第二次执行该语句时el-menu不会更新视图
curNavActive.value = Math.random().toString()
// 高亮指定index的el-menu-item 如果不这样写,则第二次执行该语句时el-menu不会更新视图
curNavActive.value = ''
nextTick(() => {
curNavActive.value = '知识库'
})
二、el-menu-item点击事件会冒泡到el-sub-menu的Bug:
el-menu同时给`el-sub-menu`和下面的`el-menu-item`绑定事件时,点击el-menu-item,事件会冒泡到el-sub-menu中,而且el-menu-item的click事件在组件内部做了处理,不会返回event事件的参数,所以无法使用事件修饰符直接阻止冒泡事件,只能在el-sub-menu的事件中判断:
const subMenu_clickEvent = ($event: MouseEvent) => {
// el-menu-item事件会冒泡到这里 如果点击的文件夹下面的item 不要执行任何操作
let subMenuTitle = document.querySelector('.subMenuTitle')
if ($event.target !== subMenuTitle) {
return
} else {
// 真实el-sub-menu应该执行的事件 ...
}
}
三、el-menu阻止el-sub-menu的展开和折叠事件:
不要把事件挂载在 el-sub-menu本身上,而是要挂载在其 title插槽中,并且阻止其事件冒泡即可!
<el-sub-menu
index="知识库"
>
<template #title>
<!-- 在 title插槽中绑定一个事件 并阻止其冒泡,这样在点击这个区域时,
el-sub-menu就不会展开/折叠了 -->
<el-icon><Collection /></el-icon>
<div class="subMenuTitle" @click.stop="switchToLibHome">
中医知识库
</div>
</template>
<el-menu-item
v-for="item in $knStore.curSourceList"
:key="item.id"
:index="item.id + '知识源'"
@click="switchViewFile(item)"
>
<span>{{ item.name }}</span>
</el-menu-item>
</el-sub-menu>
在 title插槽中,使用一个足够大的div来包裹里面的内容(注意不要包裹icon图标),这样用户只能通过点击这个 div 之外的部分来展开和折叠 sub-menu。
就相当于阻止了sub-menu自带的展开/折叠事件。
设置 width: 100% 和 flex-grow:1 都行,但是建议设置为width: 100% ,而且不要懒省劲用style设置行内样式,因为这个地方比较特殊,它的样式优先级比较低才好。
不把事件挂载到el-sub-menu本身还有一个好处,现在子菜单下面的子节点的事件不会再冒泡到el-sub-menu上了。也就无需判断 event.target了。
但是这样一来,el-menu就不能进行收起折叠了,因为这个div有宽度相关的样式,所以就会出现el-sub-menu没有正常折叠的bug:
如果el-menu不需要收起,那么到这里就算结束了,展开情况下菜单使用很正常,但是如果还想对收起状态进行处理,那就需要在el-menu菜单收起时,控制其标签样式了:
1、首先,将div更换为span标签(这很重要!),并设置css样式为 width: 100% 。
<el-sub-menu
index="知识库"
expand-close-icon="CaretBottom"
expand-open-icon="CaretTop"
>
<template #title>
<!-- icon图标一定一定要写在span外面,否则el-menu收起时会没有图标! -->
<el-icon><Collection /></el-icon>
<!-- div标签也一定要改为span标签! -->
<span class="subMenuTitle" @click.stop="switchToLibHome">
中医知识库
</span>
<!--
<div class="subMenuTitle" @click.stop="switchToLibHome">
中医知识库
</div>
-->
</template>
......
</el-sub-menu>
2、然后在el-menu整体收起时,用js获取这个标签,并将这个标签的width设置为0即可。
但是在研究后发现,其实elementUI内部在el-menu收起时已经将内部所有span标签的宽度设置为0了,只不过优先级没有我们设置的优先级高,所以没有生效。
也就是说,我们只需要降低span标签的 width:100% css样式的优先级即可,我是通过移动css代码的位置来降低其优先级的,反正只要优先级比elementUI的低就可以:
这样就不需要js去控制css样式了,更简单方便。
最后给这个span标签加上超长省略样式:
// el-menu子菜单样式(不要移动它的位置,要保持其css的低优先级,否则el-menu收起时会出现占位问题)
.subMenuTitle {
width: 100%;
// 超长文本截断
overflow: hidden;
text-overflow: ellipsis;
}
现在好像一切都可以了,但是我们还需要做最后一步:
给 sub-menu的小图标也注册个点击事件,防止在el-menu收起时,点击图标无效的情况发生。
<template #title>
<!-- 给小图标也注册点击事件,不然收起时点不到下面的span标签 -->
<el-icon @click.stop="switchToLibHome"><Collection /></el-icon>
<span class="subMenuTitle" @click.stop="switchToLibHome">
{{ $knStore.curLib?.name }}
</span>
</template>
最后的效果很完美:
四、el-sub-menu子项过多,折叠时无法完全显示Bug:
可以看到,在收起状态下,el-sub-menu的弹窗即使上下两端都顶到了头,也没显示完全,下面还有1个元素。
而这个el-sub-menu的弹窗默认是在body中插入的,我在Vue子页面中不能只通过css去操控body中的元素。
所以就需要使用js来操控其样式了。
1、先给popper弹窗加上一个自定义样式:
<el-sub-menu
index="..."
popper-class="subMenuPopper_knowledgeLib"
>
...
</el-sub-menu>
2、再在折叠el-menu之后使用js去操控submenu的弹窗样式
// 收起el-menu
const foldNav = () => {
isCollapse.value = true
navWidth.value = 5.5
// 折叠后 给知识源列表弹窗添加样式 避免知识源文件过多时弹窗无法完全显示所有文件列表
setTimeout(() => {
let subMenuPopper: HTMLElement | null = document.body.querySelector(
'.subMenuPopper_knowledgeLib'
)
if (subMenuPopper) {
subMenuPopper.style.maxHeight = '80vh'
subMenuPopper.style.overflow = 'auto'
}
}, 2000)
}
这里不知道为什么使用 nextTick 始终获取不到这个popper元素,可能是这个元素的渲染比较特殊吧,只能是使用定时器来获取,1秒还是2秒都可以。
更改样式之后的效果: