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

news2024/12/28 18:23:47

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

该组件将具有以下功能:

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

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

  1. 创建 Vue 组件

    <template>
      <!-- 
    	tag-items-smooth  圆润
    .tag-items-card 卡片
    .tag-items-smart 灵动的
    -->
    	<div class="tacscontainerwidth tag-items-smart" :style="{ width: tabconf.tacscontainerwidth }">
    		<!-- 左箭头按钮 -->
    		<el-icon @click="scrollLeft">
    			<ArrowLeft />
    		</el-icon>
    		<!-- 标签滚动条容器 -->
    		<div class="tacspage" :style="{ width: tabconf.tacspagewidth }" ref="scrollbarRef">
    			<!-- 标签内容容器 -->
    			<div class="tag-items" :style="{ transform: `translateX(` + tabconf.translateX + `px)` }" ref="innerRef">
    				<!-- 标签项 -->
    				<div v-for="(item, index) in tabconf.tacsitems"
    					:class="index === tabconf.activeIndex ? 'basistag basis-tag-item active' : 'basistag basis-tag-item'"
    					style="z-index:99;width: max-content;" ref="tabsRefs" @click="tagitemchange(index)">
    					
    					<!-- 标签内容 -->
    					<div style="width: max-content;">
    						
    						<!-- 标签图标 -->
    						<component :is="item.icon" style="width: 15px;margin-right: 5px;" />
    						<!-- 标签标题 -->
    						<span style="width: max-content;" :data-index="index">{{ item.title }}</span>
    					</div>
    					<!-- 关闭图标 -->
    					<el-icon class="tacscontainerwidth-Close"  @click="closeTagItem(index)" style="">
    						<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;">
    			<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="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>
    
  2. 引入所需的图标和组件

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

    
<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. 定义组件的数据和方法

    <script setup lang="ts">
    import { onMounted, reactive, watchEffect, toRefs, ref, watch } from 'vue';
    
    // 定义组件的数据
    const tabconf = reactive({
      // 偏移量
      max: 0,
      // 偏移量
      translateX: 0,
      // 标签长度
      tacspagewidth: "100%",
      // 激活 tab 的 index
      activeIndex: 3,
      // 激活的 tab
      activeRoute: "",
      // tab 列表
      tacsitems: [
        { id: 1, title: "测试 1", icon: "Edit", path: "path" },
        { id: 2, title: "测试 2", icon: "Edit", path: "path" },
        { id: 3, title: "测试 3", icon: "Edit", path: "path" },
        { id: 4, title: "测试 4", icon: "Edit", path: "path" },
        { id: 5, title: "测试 5", icon: "Edit", path: "path" },
        { id: 6, title: "测试 6", icon: "Edit", path: "path" },
        { id: 7, title: "测试 7", icon: "Edit", path: "path" },
        { id: 8, title: "测试 8", icon: "Edit", path: "path" },
        { id: 9, title: "测试 9", icon: "Edit", path: "path" },
        { id: 10, title: "测试 10", icon: "Edit", path: "path" },
        { id: 11, title: "测试 11", icon: "Edit", path: "path" },
        { id: 12, title: "测试 12", icon: "Edit", path: "path" },
        { id: 13, title: "测试 13 测试测试", icon: "Edit", path: "path" },
        { id: 14, title: "测试 14", icon: "Edit", path: "path" },
        { id: 15, title: "测试 15", icon: "Edit", path: "path" },
        { id: 16, title: "测试 16", icon: "Edit", path: "path" },
        { id: 17, title: "测试 17", icon: "Edit", path: "path" },
        { id: 18, title: "测试 18", icon: "Edit", path: "path" },
        { id: 19, title: "测试 19", icon: "Edit", path: "path" },
        { id: 20, title: "测试 20", icon: "Edit", path: "path" },
      ],
      // 容器宽度
      tacscontainerwidth: "50%"
    });
    
    // 定义组件的方法
    // 向左滚动标签页
    function scrollLeft() {
      tabconf.translateX -= 100;
    }
    
    // 向右滚动标签页
    function scrollRight() {
      tabconf.translateX += 100;
    }
    
    // 关闭所有标签页
    function CloseTagItemAll() {
      tabconf.tacsitems.splice(1, tabconf.tacsitems.length);
      tabconf.activeIndex = 0;
      tabconf.activeRoute = tabconf.tacsitems[0].path;
    }
    
    // 关闭其他标签页
    function CloseTagItemOther() {
      CloseTagItemRight();
      CloseTagItemLeft();
    }
    
    // 关闭左侧标签页
    function CloseTagItemLeft() {
      if (tabconf.activeIndex === 0) {
        tabconf.activeIndex = 0;
        tabconf.activeRoute = tabconf.tacsitems[0].path;
      } else {
        tabconf.tacsitems.splice(1, tabconf.activeIndex - 1);
        tabconf.activeIndex = 1;
        tabconf.activeRoute = tabconf.tacsitems[1].path;
      }
    }
    
    // 关闭右侧标签页
    function CloseTagItemRight() {
      const length = tabconf.tacsitems.length - tabconf.activeIndex;
      tabconf.tacsitems.splice(tabconf.activeIndex + 1, length);
    }
    
    // 关闭指定索引的标签页
    function closeTagItem(index) {
      tabconf.tacsitems.splice(index, 1);
      if (tabconf.activeIndex >= index) {
        tabconf.activeIndex--;
      } else if (tabconf.activeIndex < 0) {
        tabconf.activeIndex = 0;
      }
    }
    
    // 切换标签页
    function tagitemchange(index) {
      tabconf.activeIndex = index;
      tabconf.activeRoute = tabconf.tacsitems[index].path;
    }
    
    // 初始化组件
    onMounted(() => {
      settagitemchange();
    });
    
    // 设置标签页切换的样式
    function settagitemchange() {
      const event = tabsRefs.value[tabconf.activeIndex];
      const element = event;
      const index = tabconf.activeIndex;
      const tacsitems = tabconf.tacsitems;
      tabconf.activeIndex = index;
      tabconf.activeRoute = tabconf.tacsitems[index].path;
    
      // 计算活动标签页的宽度
      const width = element.offsetWidth;
      // 获取元素的水平滚动位置
      const offsetLeft = element.offsetLeft;
      activeBoxStyle.width = width + 'px';
      activeBoxStyle.transform = `translateX(${offsetLeft}px)`;
    }
    
    // 用于更新缓存的函数,当前为空实现
    function UpdateCache() {
    
    }
    </script>
    
  2. 在模板中使用组件

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1704649.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

esp8266的rtos和nonos区别

https://bbs.espressif.com/viewtopic.php?t75242#p100294 https://blog.csdn.net/ydogg/article/details/72598752

Function Calling学习

Function Calling第一篇 Agent&#xff1a;AI 主动提要求Function Calling&#xff1a;AI 要求执行某个函数场景举例&#xff1a;明天上班是否要带伞&#xff1f;AI反过来问你&#xff0c;明天天气怎么样&#xff1f; Function Calling 的基本流程 Function Calling 完整的官…

【传知代码】私人订制词云图-论文复现

文章目录 概述原理介绍核心逻辑1、选取需要解析的txt文档2、选取背景图明确形状3、配置停用词4、创建分词词典&#xff0c;主要解决新的网络热词、专有名词等不识别问题 技巧1、中文乱码问题&#xff0c;使用的时候指定使用的文字字体2、更换背景图3、词库下载以及格式转换方式…

数组单调栈-901. 股票价格跨度、leetcode

单调栈作为一种数据结构在求解类递增、递减方面的题目中有较为广泛的应用&#xff0c;在以往的leetcode中所见到的相关单调栈的题目均为单一元素&#xff0c;今天刷到901题目时&#xff0c;想到了将数组元素作为单调栈中元素的方法进行求解。 题目链接及描述 901. 股票价格跨…

C++ | Leetcode C++题解之第108题将有序数组转换为二叉搜索树

题目&#xff1a; 题解&#xff1a; class Solution { public:TreeNode* sortedArrayToBST(vector<int>& nums) {return helper(nums, 0, nums.size() - 1);}TreeNode* helper(vector<int>& nums, int left, int right) {if (left > right) {return nu…

Python 全栈体系【四阶】(五十四)

第五章 深度学习 十二、光学字符识别&#xff08;OCR&#xff09; 3. 文字识别技术 3.1 CRNNCTC(2015) CRNN&#xff08;Convolutional Recurrent Neural Network&#xff09;即卷积递归神经网络&#xff0c;是DCNN和RNN的组合&#xff0c;专门用于识别图像中的序列式对象。…

计算机组成原理易混淆知识点总结(持续更新)

目录 1.机器字长&#xff0c;存储字长与指令字长 2.指令周期,机器周期,时钟周期 3.CPI,IPS,MIPS 4.翻译程序和汇编程序 5.计算机体系结构和计算机组成的区别和联系 6.基准程序执行得越快说明机器的性能越好吗? 1.机器字长&#xff0c;存储字长与指令字长 不同的机器三者…

VMware ESXi 兼容性查询

官网兼容性查询地址&#xff1a;https://www.vmware.com/resources/compatibility/search.php

Android Studio自带Profiler工具进行CPU资源及线程问题分析步骤

1、运行需要检测CPU资源问题与线程问题的程序 这里以“com.example.opengltest”程序为例。 2、点击Profiler按钮 3、点击SESIONS ""号按钮选择设备&#xff0c;选择对应设备下的应用或进程 4、双击CPU区块 5、选择Trace config选项&#xff0c;选择“Java/Kotli…

Reader类的使用方法和技巧,你掌握了吗?

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。运营社区&#xff1a;C站/掘金/腾讯云&#xff1b;欢迎大家常来逛逛 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互相学习&#xff0c;一…

raft 协议和etcd实战

文章目录 leader选举日志复制安全性preVote 阶段preVote 投票阶段preVote阶段的作用 集群成员变更etcd raft实践etcd raft写流程时序图问题如何保障消息的幂等性&#xff0c;不能重复提交如何保障消息的原子性&#xff0c;不能提交一半raft wal日志的作用是MVCC方式的作用etcd …

瑞吉外卖项目学习笔记(一)

项目展示&#xff1a; 一、软件开发整体介绍 1.1 软件开发流程 作为软件开发人员&#xff0c;我们的主要工作是在 编码阶段 1.2 角色分工 1.3 软件环境 二、瑞吉外面项目介绍 2.1 项目介绍 系统管理后台页面&#xff1a; 移动端页面&#xff1a; 2.2 产品原型展示 产品原型是…

2024 年科技裁员综合清单

推荐阅读&#xff1a; 独立国家的共同财富 美国千禧一代的收入低于父辈 创造大量就业机会却毁掉了财富 这四件事是创造国家财富的关键 全球财富报告证实联盟自始至终无能 美国人已陷入无休止债务循环中&#xff0c;这正在耗尽他们的财务生命 2024 年&#xff0c;科技行业…

二分例题(D.负重越野,I.路径规划)

这两天的训练赛都有一道二分的题&#xff0c;但是都没往二分上面想&#xff0c;同样不知道怎么二分。 D. Fast and Fat 思路 二分的关键也就是check函数怎么写了&#xff0c;求队伍最大速度&#xff0c;可以分为速度>mid和<mid两部分&#xff0c;再判断&#xff0c;能不…

课时138:变量进阶_变量实践_综合案例

2.1.3 综合案例 学习目标 这一节&#xff0c;我们从 免密认证、脚本实践、小结 三个方面来学习 免密认证 案例需求 A 以主机免密码认证 连接到 远程主机B我们要做主机间免密码认证需要做三个动作1、本机生成密钥对2、对端机器使用公钥文件认证3、验证手工演示 本地主机生成…

dolphinscheduler standalone安装

官方文档&#xff1a;https://dolphinscheduler.apache.org/en-us/docs/3.1.3/guide/installation/standalone 1.安装&#xff08;以放在/home为例&#xff09; 下载见&#xff1a;https://download.csdn.net/download/taotao_guiwang/89311365 tar -xvzf apache-dolphinsche…

《中国改革报》的发行范围有哪些?

《中国改革报》是国家发展和改革委员会主管的全国性综合类报纸&#xff0c;其发行范围广泛&#xff0c;涵盖了全国各地。 该报在全国范围内公开发行&#xff0c;读者群体包括政府部门、研究机构、企业界、学术界以及关注中国改革与发展的社会各界人士。 它通过订阅、零售等多…

力扣hot100学习记录(十)

21. 合并两个有序链表 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 题意 将两个有序列表合并 思路 用两个指针分别指向两个链表最开始&#xff0c;每次把较小的拿出来&#xff0c;相应指针后移&#xff0c;直到一个链表…

Java 多线程(01)

运行一个 Java 程序就是跑一个 Java 进程&#xff0c;该进程至少有一个线程即主线程&#xff0c;而 main 方法就是主线程的入口&#xff1b; 一、常见多线程名词解释 并发&#xff1a;一个 CPU 核心上&#xff0c;通过快速轮转调度的方式&#xff0c;执行多个进程&#xff0c…

linux文件编程api: creat

1.基本信息 功能 创建新文件 头文件 #include<fcntl.h> 函数形式 int creat(const char *pathname, mode_t mode); 返回值 如果成功&#xff0c;则返回文件描述符号 如果失败&#xff0c;则返回-1 参数 pathname: 创建的文件名 mode: 新建文件时&#xff0c;文件权限…