前言
上一篇写了弹出框出现位置定位,以及标签禁用的实现。本篇文章就开始写五个标签功能的实现,本篇文章针对重新加载标签,展开功能实现的过程。
弹出框定义点击事件
在弹出框中,定义了click事件,绑定了onContextmenuItem方法。
<li class="el-dropdown-menu__item" :class="item.disabled ? 'is-disabled' : ''" tabindex="-1"
@click="onContextmenuItem(item)">
<Icon size="12" :name="item.icon" />
<span>{{ item.label }}</span>
</li>
我们看看onContextmenuItem是如何定义的。
const emits = defineEmits<{
(e: 'contextmenuItemClick', item: ContextmenuItemClickEmitArg): void
}>()
// 点击标签,将contextItem emit给父组件
const onContextmenuItem = (item: ContextmenuItemClickEmitArg) => {
if (item.disabled) return
item.menu = toRaw(state.menu)
emits('contextmenuItemClick', item)
}
onContextmenuItem将item的menu赋值(标签禁用的不赋值,直接return返回,同时调用父组件的contextmenuItemClick方法。*
在vue中,defineProps是子组件接收父组件传递的值,defineEmits则子组件调用父组件事件,同时还可以传递参数,总的来说都是父子组件通信的。
这里的item指的就是之前讲的contextmenuItems中的功能标签。
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'},
]
弹出框组件调用了父组件tabs的contextmenuItemClick方法,并传递了item作为参数。
ContextmenuItemClickEmitArg
其中,形参中的ContextmenuItemClickEmitArg类型是自定义的接口。
export interface ContextMenuItem {
name: string
label: string
icon?: string
disabled?: boolean
}
export interface ContextmenuItemClickEmitArg extends ContextMenuItem {
menu?: RouteLocationNormalized
}
相当于在contextmenuItems类型上,添加了一个menu字段。menu字段在BuildAdmin中指的是菜单路由,这个munu是如何赋值的呢?
在上一篇讲到弹出框弹出时,右键tab标签,调用弹出框组件的onShowContextmenu方法显示弹出框时,onShowContextmenu就绑定了tab的路由,将menu赋值给了state.menu,在弹出框的标签点击事件onContextmenuItem就将state.menu赋值给了item.menu。
赋值在代码的61和82行。
tabs定义标签事件
在tabs中使用弹出框组件时,通过v-on来定义contextmenuItemClick方法,这样弹出框组件才能接收。
<Contextmenu ref="contextmenuRef" :items="state.contextmenuItems" @contextmenuItemClick="onContextmenuItem"/>
contextmenuItemClick又指向了onContextmenuItem方法,contextmenuItemClick就是实现标签功能具体的方法。
const onContextmenuItem = async (item: ContextmenuItemClickEmitArg) => {
const {name, menu} = item
if (!menu) return
switch (name) {
case 'refresh':
proxy.eventBus.emit('onTabViewRefresh', menu)
break
case 'close':
closeTab(menu)
break
case 'closeOther':
closeOtherTab(menu)
break
case 'closeAll':
closeAllTab(menu)
break
case 'fullScreen':
if (route.path !== menu?.path) {
router.push(menu?.path as string)
}
navTabs.setFullScreen(true)
break
}
}
在onContextmenuItem中,就是对ContextmenuItemClickEmitArg类型的item做了一个switch case的判断。
if (!menu) return是对禁用的标签不作处理,因为在上图onContextmenuItem方法的第81行,如果标签禁用,则menu不赋值。
思考
为什么非要在tabs中实现实现这些功能,还要父子组件各种值和方法传递,直接在弹出框组件实现不好吗?
-
灵活性:如果有多个组件使用弹出框组件,父组件通过传递不同的item,就能定制每个组件的弹出框标签列表。
-
tabs中已经拿到了一些变量,例如所有的tab(tabsViews),激活的tab等。就拿关闭其他标签来说,你得知道打开了哪些标签,才能关闭。这些在tabs实现的时候都定义了,所以在tabs中实现这些功能比较方便,直接可以复用。
结语
本篇主要根据vue3中父子组件方法调用通信,讲了BuildAdmin的弹出框标签功能架构的实现,主要是对emit的一个理解和使用.下一篇文章写重新加载标签功能的具体实现.