上一篇主要讲了如何动态添加导航栏的tab,那么本篇将会写如何关闭tab、实现滑动块已经一些细节上的操作。
这是实现新增、关闭等功能后的tabs.vue,里面绑定的方法逻辑都是我基于BuildAdmin重构实现的,后面在讲滑动块的时候,可以回来看看图中html的代码。
在第一次访问BuildAdmin或者刷新页面时,导航栏只有一个tab,这里选择是将 控制台 设置成了第一个tab。
所以我在router.ts中,实现了getFirstRoute() 来获取第一个route。
export const getFirstRoute = (routes: RouteRecordRaw[]): null | RouteLocationNormalized => {
let route: any = null
routes.forEach((item) => {
if (item.meta?.menu_type == 'tab') {
route = item
}t
})
return route
}
看过前面路由动态加载文章的知道,从后台获取的路由信息,处理后是放在了navTabs.state.tabsViewRoutes中,然后渲染menu。所以getFirst是对tabsViewRoutes进行遍历,来获取第一个route。
获取了第一个router怎么渲染到导航栏呢。看过上篇导航栏tabs的知道,最终是将navTabs.state.tabsView中的路由渲染成导航栏的tab,所以只要将firstRoute放到tabsView就可以了,那么什么时候放呢?
这里利用vue component的声明周期函数onMounted,这个函数之后再tabs.vue初始化完成之后执行,而且只会执行一次。然后将并将firstRoute渲染,并将activeRoute设置为firstRoute。
为了实现tab的关闭功能,tab右侧都会有一个叉号的icon。但是当只有一个tab的时候,是没有关闭按钮的,所以需要v-show来判断当前tabsView的长度是否大于1,如果是则显示。
<Icon v-show="navTabs.state.tabsView.length > 1" />
这里就是复习一下上一篇的addTabs() 实现新增tab的流程:当点击菜单路由时,在路由守卫中会调用setActiveRoute将即将跳转的路由,即to设置为activeRoute,这时候调用addTabs(),先判断tabsView中是否有这个路由,如果没有,则放入,并将activeIndex设置为activeRoute在tabsView中的下标。
关键:activeRoute和activeIndex。
我们点击菜单或者tab的时候,会发现有个滑动块会滑动到tab下面。
其实这个滑动块就是一个div,只不过它的宽度是和位置是动态可变的。滑动块的html在div.nav-tabs中是这么定义的:
<div :style="activeBoxStyle" class="nav-tabs-active-box"></div>
可以看到滑动块的style样式属性绑定了一个变量activeBoxStyle,接着来看看如何实现在js中,如何利用activeBoxStyle定义此div的位置和宽度。
首先就是利用reactive来定义响应式的activeBoxStyle变量,定义两个属性,一个是width表示宽度,另一个trasnform是元素转换,滑动块实在水平轴上进行来回变换,所以就用translateX。
const activeBoxStyle = reactive({
width: '0',
transform: 'translateX(0px)'
})
默认值都为0。
思考一下,滑动块的宽度是不是选中tab(即activeRoute)的div的宽度,在水平轴的位置是不是tab的div的起始位置,这么一说,我们岂不是获取到选中的这个tab的div,然后通过一些属性取得width和startOffset不就行了?
css中,有一个clientWidth属性,表示的就是元素的宽度,offsetLeft是子元素(tab的div)左侧离父元素(navTab导航栏)的距离。
所以定义一个方法,来计算宽度和水平轴距离,赋值给activeBoxStyle。
const selectNavTab = (dom: HTMLDivElement) => {
activeBoxStyle.width = dom.clientWidth + 'px'
activeBoxStyle.transform = `translateX(${dom.offsetLeft}px)`
}
形参dom代表的是tab的div元素。这样滑动块的宽度和在水平轴的位置就计算出来了。那么,什么时候要调用这个方法呢,或者换种说法,什么时候回触发滑动块的移动?
因为我们只实现了新增和跳转tab,这里就先以此为例来讲滑动块的原理。
还记得我们是如何实现tab的新增吗?
这里就是复习一下上一篇的addTabs() 实现新增tab的流程:当点击菜单路由时,在路由守卫中会调用setActiveRoute将即将跳转的路由,即to设置为activeRoute,并触发watch调用回调执行addTabs()。
然后判断tabsView中是否有这个路由,如果没有,则放入,并将activeIndex设置为activeRoute在tabsView中的下标。
那么我们也要在watch的回调函数中,实现滑动块的滑动,即调用selectNavTab方法。但是要新建/跳转的tab的div需要怎么获取。这时候就用到了 @vueuse/core 库中的useTemplateRefsList方法。
useTemplateRefsList的作用就是通过ref绑定在元素上,就能将元素的dom放到list中。
<div class="ba-nav-tab"
v-for="(item, idx) in navTabs.state.tabsView"
:ref="tabsRefs.set"
>
</div>
<script setup lang="ts">
import {useTemplateRefsList} from '@vueuse/core'
const tabsRefs = useTemplateRefsList<HTMLDivElement>()
</script>
ba-nav-tab就是一个个tab,使用ref将HTMLDivElement元素绑定在了useTemplateRefsList中。
那我们如何知道当前新建/跳转的tab是useTemplateRefsList中的哪个div。这时候,上一篇名不见经传的activeIndex就出来发挥作用了。
activeIndex是activeRoute在tabsView的位置,而tabsViews路由和RefsList中div元素是顺序对应的,所以通过activeIndex就能获取到目标div。
这样,就在动态新增/跳转tab时实现了滑动块。
同样,在关闭tab时也会触发滑动块滑动,这个就放在下一篇tab的关闭中一起讲,期待下一次再见。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。