首页IT科技vue tab页面(vue选项卡Tabs组件实现示例详解)

vue tab页面(vue选项卡Tabs组件实现示例详解)

时间2025-06-20 15:42:10分类IT科技浏览5379
导读:概述 前端项目中,多数页面涉及到选项卡切换,包括路由切换,指令v-if等,本质上其实和选项卡切换思想差不多,如果是个简单的选项卡,还是很简单的,我们也不需要什么组件库的组件,自己也能几行代码写出来,但是涉及到动画,尺寸计算,拖拽的功能的时候,多数情况下,自己写还是要花点时间的,组件库就提供了现成的,拿来改改样式就...

概述

前端项目中            ,多数页面涉及到选项卡切换                     ,包括路由切换        ,指令v-if等         ,本质上其实和选项卡切换思想差不多                    ,如果是个简单的选项卡            ,还是很简单的      ,我们也不需要什么组件库的组件                   ,自己也能几行代码写出来                ,但是涉及到动画   ,尺寸计算                  ,拖拽的功能的时候                   ,多数情况下,自己写还是要花点时间的               ,组件库就提供了现成的                      ,拿来改改样式就行    ,为了对这个组件更加深入的理解            ,这里自己实现一个带拖拽                     ,过渡的tabs组件               。

效果图

实现过程

组件分析

组件包含两部分:Tabs组件和TabPane组件        ,参考绝大多数组件库的习惯 组件主要分为需要点击的tab栏和下面对应的内容块 我们需要对内容区和选项卡点击区分别加上过渡动画         ,提升用户体验 最后需要加上拖拽调整选项卡顺序的功能

所需的前置知识

熟悉vue内置transition组件 深入掌握vue父子组件通信                    ,除开emit和props            ,还需要掌握inject      ,emit和props                   ,还需要掌握inject                ,emit和props   ,还需要掌握inject                  ,parent,vnode                   ,渲染函数等等,这些业务开发中用的不多               ,但是组件库里面比较常见                    。 了解dom中位置计算和尺寸的基本计算 熟悉html5新增拖拽相关事件

项目组件文件夹

Tabs.vue

<template> <div class="gnip-tab"> <div class="gnip-tab-nav"> <div v-for="(item, index) in tabNavList" @click.stop="handleTabNavClick(item, index)" :class="[tab-nav-item, item.name == activeName ? active : ]" ref="tabNavItemRefs" @drop="handleDrop(item, $event, index)" @dragstart="handelDragstart(item, $event, index)" @dragover="handleDragOver(item, $event, index)" draggable="true" > <span v-if="item.text">{{ item.text }}</span> <render v-if="item.renderFun" :renderFn="item.renderFun"></render> </div> </div> <!-- 滚动滑块 --> <div class="tab-nav-track" :style="{ background: showTrackBg ? #e5e7eb : , }" > <span class="track-line" :style="{ width: trackLineWidht + px, left: left + px }" ></span> </div> <div class="tab-content-wrap"> <slot></slot> </div> </div> </template> <script> // render组件                      ,label为render函数的时候进行渲染 import Render from "./render"; export default { props: { // v-model的那项 value: { type: String, }, // 是否显示滑块背景 showTrackBg: { type: Boolean, default: false, }, }, components: { Render, }, data() { return { // tab数组 tabNavList: [], // 当前活跃项 activeName: "", // 滑块的宽度 trackLineWidht: 0, // 当前活跃索引 currentIndex: 0, // 滑块偏移量 left: 0, // 拖拽开始的哪项 dragOriginItemIndex: null, // 拖拽活跃项的索引 dragStartIndex: null, }; }, mounted() { this.init(); }, methods: { // 初始化 init() { // 默认当前活跃项为外部v-model的值 this.activeName = this.value; // 页面渲染任务之后计算滑块偏移量和宽度 this.$nextTick(() => { this.currentIndex = this.$children.findIndex( (component) => component.name == this.value ); this.computedTrackWidth(); }); }, // 设置tab点击栏 setTabBar(tabsPaneInstance) { // tab的描述信息可以是字符串也可以是render函数 const label = tabsPaneInstance.label, type = typeof label; // 添加到数组项中    ,根据添加条件渲染 this.tabNavList.push({ text: type == "function" ? "" : label, renderFun: type == "function" ? label : "", name: tabsPaneInstance.name, }); }, handleTabNavClick(item, index) { if (item.name == this.activeName) return; // 更新当前活跃项 this.activeName = item.name; // 活跃项的索引 this.currentIndex = index; // 计算滑块的偏移量和宽度 this.computedTrackWidth(); }, // 计算滑块的偏移量和宽度 computedTrackWidth() { // 插槽子组件的索引集合 const tabNavItemRefsList = this.$refs.tabNavItemRefs; // 导航tab项的宽度 const scrollWidth = tabNavItemRefsList[this.currentIndex].scrollWidth; // 滑块的宽度为scrollWidth this.trackLineWidht = scrollWidth; // 定位的偏移量为offsetLeft this.left = tabNavItemRefsList[this.currentIndex].offsetLeft; }, /* 关于拖拽请参考MDN文档: https://developer.mozilla.org/zh-CN/docs/Web/API/DragEvent            ,实现拖拽需要清楚关于拖拽相关的几个事件 */ // 开始拖拽 handelDragstart(item, event, index) { // 说明是拖拽的当前活跃的哪一项                     ,记录这一项的索引位置 if (item.name == this.activeName) { this.dragStartIndex = index; } this.dragOriginItemIndex = index; }, // 推拽进入目标区域 handleDragOver(item, event) { // 阻止默认事件 event.preventDefault(); }, //拖拽进入有效item handleDrop(item, event, index) { event.preventDefault(); // 说明拖动的位置是变了的 if (this.dragOriginItemIndex != index) { // 交换数据        ,重新渲染生成tab栏 this.swap(this.dragOriginItemIndex, index); // 重新计算滑块的偏移量 if (this.dragStartIndex !== null) { this.currentIndex = index; // 记住         ,数据更新为异步操作                    ,因此我们这里需要用到nextTick            ,将计算任务放到渲染任务完成之后执行      ,避免计算不准确 this.$nextTick(() => { this.computedTrackWidth(); this.dragStartIndex = null; }); } else { // 不是点击拖拽当前活跃项                   ,也要重新计算滑块跨度和位置                ,因为每个tab项的宽度不一致   ,因此                  ,每次拖拽都需要重新计算 this.$nextTick(() => { this.computedTrackWidth(); }); } // 这里还可以根据需要                   ,发布一个拖拽完成事件 } }, // 交换tab数据项 swap(start, end) { let startItem = this.tabNavList[start]; let endItem = this.tabNavList[end]; // 由于直接通过索引修改数组,无法触发响应式               ,因此需要$set this.$set(this.tabNavList, start, endItem); this.$set(this.tabNavList, end, startItem); }, }, }; </script> <style lang="less"> .gnip-tab { .gnip-tab-nav { display: flex; position: relative; .tab-nav-item { padding: 0 20px; cursor: pointer; line-height: 2; } } .tab-nav-item.active { color: #2d8cf0; } .tab-nav-track { width: 100%; position: relative; height: 2px; .track-line { height: 2px; background-color: #2d8cf0; position: absolute; transition: left 0.35s; } } } </style>

TabPane.vue

<template> <div class="gnip-tabs-pane"> <transition :name="paneTransitionName"> <div class="tab-pane-content" v-show="$parent.activeName == name"> <slot name="default"></slot> </div> </transition> </div> </template> <script> export default { props: { // tab项的文本或者render函数 label: { type: [String, Function], }, // 每项标识 name: { type: String, }, // 是否禁用当前项 disabled: { type: Boolean, default: false, }, }, data() { return { paneTransitionName: "enter-right", }; }, created() { // 统一tab的数据给父组件进行处理和渲染 this.$parent.setTabBar(this); }, }; </script> <style lang="less"> .gnip-tabs-pane { overflow-x: hidden; .enter-right-enter-active { transition: transform 0.35s; } .enter-right-enter { transform: translateX(100%); } .enter-right-to { transform: translateX(0); } } </style>

render.js

主要用于将函数通过转化为render函数形式的组件(前提未提供模板)

export default { name: "RenderCell", props: { renderFn: Function, }, render(h) { return this.renderFn(h); }, };

index.js

按需导出组件

import TabPane from "./TabPane.vue"; export { Tabs, TabPane };

使用

App.vue

<template> <div class="app"> <div class="aline"> <Tabs v-model="tabName" show-track-bg> <TabPane label="首页" name="name1">首页</TabPane> <TabPane label="图书详情页" name="name2" disabled>图书详情页</TabPane> <TabPane label="个人主页" name="name3">个人主页</TabPane> <TabPane :label="labelRender" name="name4">购物车</TabPane> </Tabs> </div> </div> </div> </template> <script> import { Tabs, TabPane } from "@/components/Tabs"; export default { components: { Tabs, TabPane }, data() { return { tabName: "name1", labelRender(h) { return h("div", "购物车"); }, }; }, }; </script> <style lang="less"> * { margin: 0; padding: 0; } .app { padding: 20px; button { padding: 10px; background-color: #008c8c; color: #fff; margin: 20px 0; } .container { .operate { text-align: center; } .aline { width: 50%; } h2 { font-weight: bold; font-size: 20px; } .aline { &:nth-child(1) { margin-right: 20px; } } display: flex; justify-content: space-between; } } .aline { display: flex; justify-content: center; } .item { margin: 40px; img { width: 250px; height: 200px; } ul { margin: 0 auto; li { border: 1px solid red; height: 200px; width: 250px; } } } </style>

总结

通过上述组件的实现                      ,对于HTML5拖拽事件的应用更加熟悉    ,关于拖拽请参考MDN文档: developer.mozilla.org/zh-CN/docs/…

以上就是vue选项卡Tabs组件实现示例详解的详细内容            ,更多关于vue选项卡Tabs组件的资料请关注本站其它相关文章!

声明:本站所有文章                     ,如无特殊说明或标注        ,均为本站原创发布      。任何个人或组织         ,在未征得本站同意时                    ,禁止复制            、盗用                     、采集        、发布本站内容到任何网站         、书籍等各类媒体平台           。如若本站内容侵犯了原著者的合法权益            ,可联系我们进行处理                     。

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
python中def的作用(Python中deque的操作整理)