自学前端11:如何实现tab耶弹出框的弹出和隐藏,以及标签禁用的小技巧

社区Vue前端框架

前言

在上篇文章中写了如何实现弹出框,最后也留下了一个问题:在tab栏中点击哪里,弹出框就出现在哪里,这个是怎么实现的?

在此之前我们先思考:在浏览器中右键,通常会出现什么?

prevent和$event

在这里我们的需求是,在右键tab导航栏时,弹出选项框。但实际上在右键点击时,会弹出浏览器菜单。

picture.image

我们之前在实现tab的关闭时,讲了 @click.stop 阻止点击事件冒泡。这里为了右键时不弹出浏览器的菜单,使用 @contextmenu.prevent

click指的是左键点击事件,contextmenu指的是右键点击事件。prevent的作用就是阻止原生事件,这里指的就是右键不再弹出浏览器菜单,而是触发绑定的新事件。我们看看新事件的方法:

@contextmenu.prevent="onContextmenu(item, $event)"

在tab的父标签中绑定了右键事件,阻止右键菜单并且调用onContextmenu方法。

picture.image

onContextmenu传入了两个参数,item指得就是路由,这样就可以将tab与弹出框的标签绑定;vue中通过v-on绑定事件处理函数, $event 参数可以访问原生事件对象,其中包含了事件发生时的所有信息和参数,在这里指的是右键点击事件,我们看一下它的属性。

picture.image

其中clientXclientY是鼠标事件触发时的鼠标相对于浏览器窗口的位置,通过这两个属性就可以解决开头提到的在tab栏中点击哪里,弹出框就出现在哪里这个问题。

我们看看onContextmenu实现逻辑。

onContextmenu

在tabs.vue中,一共为弹出框一共定义了五个功能标签。

contextmenuItems: [
    {name: 'refresh', label: '重新加载', icon: 'fa fa-refresh'},
    {name: 'close', label: '关闭标签', icon: 'fa fa-times'},
    {name: 'fullScreen', label: '当前标签全屏', icon: 'el-icon-FullScreen'},
    {name: 'closeOther', label: '关闭其他标签', icon: 'fa fa-minus'},
    {name: 'closeAll', label: '关闭全部标签', icon: 'fa fa-stop'},
],

onContextmenu没有实现上面的功能,只是作为一个入口,将tab对应的router和鼠标坐标传递给弹出框组件的contextmenuRef函数。

const onContextmenu = (menu: RouteLocationNormalized, event: MouseEvent) => {
    // 禁用刷新,只有打开的tab才能刷新
    state.contextmenuItems[0].disabled = route.path !== menu.path
    // 当只有一个tab时,禁用关闭其他和关闭全部
    state.contextmenuItems[4].disabled = state.contextmenuItems[3].disabled = navTabs.state.tabsView.length == 1 ? true : false
    const {clientX, clientY} = event
    contextmenuRef.value.onShowContextmenu(menu, {
        x: clientX,
        y: clientY
    })
}

contextmenuRef是弹出框组件的引用,使用ref实现的。

 <Contextmenu ref="contextmenuRef" :items="state.contextmenuItems" @contextmenuItemClick="onContextmenuItem"/>

1. 实现弹出框坐标位置

onShowContextmenu是在弹出框组件中定义的,接收tabs中onContextmenu传入的路由、坐标参数。onShowContextmenu只有三行代码。

const onShowContextmenu = (menu: RouteLocationNormalized, axis: Axis) => {
    state.menu = menu
    state.axis = axis
    state.show = true
}

将路由、坐标赋值给state变量,设置show为ture。

picture.image

Axis是自定义的接口,里面只有x、y两个属性。弹出框使用v-show绑定了show变量决定是否弹出,所以在onShowContextmenu被调用时,将show设置为true,这样就弹出框就能展示。

而代表坐标位置的Axis变量,被弹出框的style属性(即css)绑定。

<div
  class="el-popper is-pure is-light el-dropdown__popper ba-contextmenu"
  :style="`top:${state.axis.y + 5}px; left:${state.axis.x - 14}px; width:${props.width}px`"
  :key="Math.random()"
  v-show="state.show"
  aria-hidden="false"
  data-popper-placement="bottom"
>

利用top和left修改弹出框的位置,就能实现在tab栏中点击哪里,弹出框就出现在哪里。

2. 弹出框关闭

在弹出框组件中,除了定义onShowContextmenu在tabs中调用,用来触发显示弹出框,还定义了onHideContextmenu用来关闭弹出框。

const onHideContextmenu = () => {
    state.show = false
}

就一行简单的代码,将show设置为false即可。那么,想一下弹出框在什么时候会隐藏呢?是不是鼠标左键点击弹出框以外的位置就会隐藏。

import {useEventListener} from '@vueuse/core'

onMounted(() => {
    useEventListener(document, 'click', onHideContextmenu)
})

使用的是 @vueuse/coreuseEventListener实现的,用来监听document的click事件,监听到则触发onHideContextmenu来关闭弹出框。

3. 标签禁用

disabled是在tabs.vue中定义contextmenuItems时设定的属性,在渲染弹出框的时候,就会使用此属性,来判断在某些情况下哪些标签会被禁用。

标签禁用的情况有两种:

  1. 只有当前打开的tab才能刷新,此刻如果右键点击其他tab,显示弹出框的时候要禁用
  2. 当只有一个tab时,关闭其他页面、关闭所有页面功能要禁用

所以在onContextMenu中添加下面两行代码,每次弹出框弹出之前都会先完成5个标签disabled属性的初始化:

// 禁用刷新,只有打开的tab才能刷新
state.contextmenuItems[0].disabled = route.path !== menu.path && navTabs.state.tabsView.length > 1
// 当只有一个tab时,禁用关闭其他和关闭全部
state.contextmenuItems[4].disabled = state.contextmenuItems[3].disabled = navTabs.state.tabsView.length == 1 ? true : false

tabsView是一个存放路由的列表,tabs的渲染就是遍历tabsView。这里加一个大于1的判断原因是:在首次访问时,是通过getFirstRoute获取路由渲染的第一个tab(控制台),这里没有触发route.push跳转,route.path与控制台的path就不相等,重新加载就会被禁用,如果这里要加length判断,避免禁用。下面是没有加length判断的情况:

picture.image

BuildAdmin在实现重新加载禁用时,就没做length的判断。在第七篇写tab及滑动块实现时,因为一些技术问题,就用了和BuildAdmin不一样的方法进行实现的。所以后面涉及tab的部分需要做一些适当的修改。

在弹出框组件渲染标签时,将class与disabled绑定。

<li class="el-dropdown-menu__item" :class="item.disabled ? 'is-disabled' : ''" >

这里使用了三目运算符,当class为is-disabled时,ElementPlus会自动渲染css。

picture.image

ElementPlus自动将cursor设置为not-allowedcolor设置为内部定义的禁用颜色变量 --el-text-color-disabled( #c0c4cc) ,这样就实现了标签禁用。

结语

本篇文章主要讲了弹出框的两个知识点:弹出位置和标签禁用,都是对ElementPlus和vue简单的使用。

0
0
0
0
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论