【Vue3】el-menu的踩坑和使用

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秒都可以。

更改样式之后的效果:

  • 16
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值