首页 前端知识 三.三实现 Vue 标签页切换效果的组件开发

三.三实现 Vue 标签页切换效果的组件开发

2024-06-20 00:06:37 前端知识 前端哥 808 734 我要收藏

在本次开发中,我们将实现一个 Vue 组件,用于展示和切换标签页。
背景有移动动画效果
在这里插入图片描述
在这里插入图片描述

该组件将具有以下功能:

  • 标签页左右滚动
  • 点击标签页切换内容
  • 关闭指定标签页
  • 支持多种标签页风格

以下是实现该组件的具体步骤:

  1. 创建 Vue 组件
    <template>
	<!-- 
		tag-items-smooth  columns 圆润
	.comprehensive  comprehensive卡片
	.routine flexibility 灵动的
	-->
	<div class="tacscontainerwidth comprehensive"
		:class="configStore.config.labelStyleSwitch=='columns'? 'columns' : configStore.config.labelStyleSwitch=='comprehensive'? 'comprehensive':'flexibility'">

		<!-- 左箭头按钮 -->
		<el-icon @click="scrollLeft">
			<ArrowLeft />
		</el-icon>
		<!-- 标签滚动条容器 -->
		<div class="tacspage" ref="scrollbarRef" style="width: 100%;">
			<!-- 标签内容容器 -->
			<div class="tag-items" ref="innerRef">
				<!-- 标签项 -->
				<div v-for="(item, index) in tabconf.tabsView"
					:class="index === tabconf.activeIndex ? 'basistag basis-tag-item active' : 'basistag basis-tag-item'"
					ref="tabsRefs" @click="tagitemchange(index)">
					<!-- 标签内容 -->
					<div class=" basis-tag-item-content">
						<!-- 标签图标 -->
						<span v-if="typeof item.icon!== 'undefined' &&item.icon !=''&&item.icon!==null"
							style="width: 15px;  margin-right: 5px; display: flex; align-items: center;">
							<component :is="item.icon" class=" basis-tag-item-content-icon"
								v-if="configStore.config.labelIconSwitch" />
						</span>
						<!-- 标签标题 -->
						<span class=" basis-tag-item-content-title">{{ item.title }}</span>
					</div>
					<!-- 关闭图标 -->
					<el-icon v-if="index!=0" class="tacscontainerwidth-Close" @click="closeTagItem(index,$event)">
						<Close />
					</el-icon>

				</div>
				<!-- 选中标签页的指示框-背景图-->
				<div :style="activeBoxStyle" class="nav-tabs-active-box"></div>
			</div>
		</div>
		<!-- 右箭头按钮 -->
		<el-icon @click="scrollRight">
			<ArrowRight />
		</el-icon>
		<!-- 更多下拉 -->
		<el-dropdown style="margin-left: 15px;s">
			<span class="username-span"  >
				<!-- 更多图标 -->
				<el-icon>
					<Grid />
				</el-icon>
			</span>
			<template #dropdown>
				<el-dropdown-menu>
					<!-- <el-dropdown-item command="user">刷新</el-dropdown-item> -->
					<!-- <el-dropdown-item @click="addTagItem()">添加</el-dropdown-item> -->
					<el-dropdown-item @click="CloseTagItemLeft()">关闭左侧</el-dropdown-item>
					<el-dropdown-item @click="CloseTagItemRight()">关闭右侧</el-dropdown-item>
					<el-dropdown-item @click="CloseTagItemOther()">关闭其他</el-dropdown-item>
					<el-dropdown-item @click="CloseTagItemAll()">关闭全部</el-dropdown-item>
				</el-dropdown-menu>
			</template>
		</el-dropdown>
	</div>
</template> 
  1. 引入所需的图标和组件

  2. 定义组件的样式(还没优化好、请自己定义)

    
<style scoped lang="scss">
  .fade-enter-active,
	.fade-leave-active {
		transition: opacity 0.3s;
	}

	.fade-enter,
	.fade-leave-to

	/* .fade-leave-active below version 2.1.8 */
		{
		opacity: 0;
	}

	.tacscontainerwidth {
		display: flex;
		align-items: center;
		position: absolute;

		.tacspage {

			overflow-y: hidden;
			overflow-x: scroll;
			scrollbar-width: none;
			-ms-overflow-style: none;
			/* 隐藏滚动条 */
		}
	}


	.tag-items {
		// white-space: nowrap;position: relative;
		//    transition: transform 0.3s ease; 
		display: flex;
		white-space: nowrap;
		position: relative;
		transition: transform var(--el-transition-duration);
		float: left;
	}

	.tag-items-card .basis-tag-item {
		margin-left: 10px;
		border: 1px solid #ccc;
		display: flex;
		display: flex;
		align-items: center;
		justify-items: center;
	}

	.tag-items-smooth .basis-tag-item {
		margin-left: 10px;
		border: 1px solid #ccc;
		border-radius: 5px;
		// width: 100px;
		display: flex;
		align-items: center;
		justify-items: center;
	}

	.tag-items-smart .basis-tag-item {
		margin-left: 10px;
		border: 0px;
		display: flex;
		align-items: center;
	}

	.basis-tag-item * {
		flex-shrink: 0;
		// display: flex;
		// align-items: center;
		// justify-items: center;

	}

	.nav-tabs-active-box {
		position: absolute;
		height: 5px;
		bottom: 0px;
		border-radius: var(--el-border-radius-base);
		background-color: #fcc;
		box-shadow: var(--el-box-shadow-light);
		transition: all 0.2s;
		-webkit-transition: all 0.2s;
		z-index: 2;
	}

	.basis-tag-item {
		padding: 5px;
	}


	.tag-items-smart .basis-tag-item {
		color: var(--el-color-info-light-3);
		// background-color: var(--el-menu-bg-color);
	}

	.active {
		color: var(--el-menu-text-color);

		// background-color: var(--el-menu-bg-color);
		border-bottom: 2px solid var(--el-menu-text-color);
		// background-color: #cfc;
	}

.tacscontainerwidth-Close{margin-left: 5px;width: 14px;    position: relative;
          font-size: 12px; 
          height: 14px;
          overflow: hidden;
          right: -2px;
          transform-origin: 100% 50%;}
	@keyframes moveBackground {
		0% {
			background-position: left top;
		}

		100% {
			background-position: right top;
		}
	}
    </style>
  1. 定义组件的数据和方法

4.1 下面的navTabsuseConfig 用了结合pinia和piniaPluginPersistedstate的使用案例

代码中用`navTabs`来存储标签基础信息,如:`activeIndex` 当前激活索引,`tabsView` 数组缓存;
代码中用`useConfig`来存储配置基础信息,如:	`labelSwitch` 标签开关、 	`labelIconSwitch` 标签图标开关 、`labelStyleSwitch` 标签样式

4.2下面的useRouter 用了Vue Router 使用教程

代码中获取`useRoute`r的路由并进行标签激活判断、以及进行跳转路由
    
<script setup lang="ts">
	import { onMounted, reactive, ref, defineProps, watch, nextTick } from 'vue';
	import { useConfig } from '../../../../stores/config' // 使用配置 store 
	import { navTabs } from '../../../../stores/navTabs' // 使用配置 store 
	
	import { useRouter } from 'vue-router' // 使用配置 useRouter 
	
	const router = useRouter(); 
	//组件接受参数
	const props = defineProps({
		tacscontainerwidth: {
			type: String,
			default: '90%',
		},
		tabconf: {
			type: Object,
			default: () => ({})
		},
	});
	// 获取缓存
	const configStore = useConfig();
	const navTabsstores = navTabs();
	// console.log(navTabsstores)
	const tabconf = navTabsstores
	const activeBoxStyle = reactive({
		width: "0px",
		transform: 'translateX(0px)',
	}) 
	// 滚动条容器的ref
	const scrollbarRef = ref();
	//所有标签项的ref
	const tabsRefs = ref();
	// 切换标签是的动画样式初始化
	onMounted(() => {
		let routerView=router.currentRoute.value.path;
		tabconf.tabsView.forEach(function(item, index) {
				if(routerView==item.path){
					tabconf.activeIndex=index;
				}	 
					 
		    
		  });
		settagitemchange(tabconf.activeIndex)
	})
	watch(router.currentRoute, (newValue, oldValue) => {
 
			nextTick(function () { 
				// 在这里进行 v-for 重新加载完毕后的操作
				let routerView=router.currentRoute.value.path;
				tabconf.tabsView.forEach(function(item, index) {
						if(routerView==item.path){
							tabconf.activeIndex=index;
						}	  
				  });
					
				settagitemchange(tabconf.activeIndex)
			})
		 

	});
	/* 向左滚动标签页 */
	function scrollLeft() {
		scrollbarRefscroll(-100);
	}
	/* 向右滚动标签页 */
	function scrollRight() {
		scrollbarRefscroll(100);
	}
	/* 关闭所有标签页 */
	function CloseTagItemAll() {
		tabconf.activeIndex=0
		tabsViewSplice(1, tabconf.tabsView.length) 
		// 重新设置选中背景
	}
	/*
	* 关闭其他标签页
	*/
	function CloseTagItemOther() {
		 tabconf.tabsView.splice(tabconf.activeIndex + 1, tabconf.tabsView.length - tabconf.activeIndex);
		 tabconf.tabsView.splice(1, tabconf.activeIndex - 1);  
		settagitemchange(1);
	}
	/*
	* 关闭左侧标签页
	*/
	function CloseTagItemLeft() {  
		tabsViewSplice(1, tabconf.activeIndex - 1,1) 
	}
	/*
	* 关闭右侧标签页
	*/
	function CloseTagItemRight() {
		 
		tabsViewSplice(tabconf.activeIndex + 1, tabconf.tabsView.length - tabconf.activeIndex,tabconf.activeIndex)
	} 
	/*
	* 关闭指定索引的标签页
	* @param {number} index - 要关闭的标签页索引
	*/
	function closeTagItem(index,event) {
		// 阻止事件冒泡,防止子事件调用父级事件
		event.stopPropagation(); 
		// 关闭指定索引的标签页,并确保 activeIndex 的有效性 
		let activeIndex=0;
		if(tabconf.activeIndex>=index&&index>0){
			 activeIndex=tabconf.activeIndex-1
		}else if(index>0){
			activeIndex=tabconf.activeIndex-1
		}
		
		tabsViewSplice(index, 1,activeIndex) 
	}
	/*
	* 切换标签页
	* @param {Event} event - 鼠标点击事件对象
	*/
	function tagitemchange(index) {
		tabconf.activeIndex = index;  
		router.push(tabconf.tabsView[index].path) 
		settagitemchange(index)
	}
	
	
	//滚动条移动
	function scrollbarRefscroll(scrollNum) {
		scrollbarRef.value.scrollLeft += scrollNum 
	} 
	// 删除
	function tabsViewSplice(start, length,activeIndex=0) {   
			tabconf.activeIndex = activeIndex;  
		if (start >= 1) {
			tabconf.tabsView.splice(start, length);
		} 
		router.push(tabconf.tabsView[activeIndex].path) 
		// 重新设置选中背景 
		settagitemchange(tabconf.activeIndex) 
	}
	
	/*
	* 切换标签动画
	* @param {Event} event - 鼠标点击事件对象
	*/
	function settagitemchange(activeIndex) {  
		// console.log(tabconf.tabsView,activeIndex,"index");
		tabconf.activeIndex=activeIndex;
		  // 动画
		const event = tabsRefs.value[activeIndex];
		const element = event;
		const width = element.offsetWidth;
		// 获取元素的水平滚动位置
		const offsetLeft = element.offsetLeft;
		// 修改动画参数
		activeBoxStyle.width = width + 'px'
		activeBoxStyle.transform = `translateX(${offsetLeft}px)`
		// console.log('element', element);
		// console.log('索引:', tabconf.activeIndex);
		// console.log('path:', tabconf.activeRoute);
		// console.log('宽度:', width);
		// console.log('scrollLeft:', scrollLeft);

	}
</script> 
  1. 在模板中使用组件

通过以上步骤,我们实现了一个具有标签页切换功能的 Vue 组件。在实际应用中,可以根据需要进一步扩展和定制组件的功能。

希望这篇开发博客对你有所帮助!如果你有任何问题或建议,请随时留言。

转载请注明出处或者链接地址:https://www.qianduange.cn//article/12921.html
标签
评论
发布的文章

爱心

2024-06-27 17:06:24

读魏书生的心得体会

2024-07-03 14:07:10

jQuery 选择器

2024-05-12 00:05:34

Vue中public/assets目录区别

2024-07-02 23:07:29

大家推荐的文章
会员中心 联系我 留言建议 回顶部
复制成功!