Vue实现简单Tab标签页组件

Tab 标签页组件

基础用法

默认情况下启用第一个标签,可以通过v-model绑定当前激活的标签索引

<tabs v-model="active">
  <tab title="标签 1">内容 1</tab>
  <tab title="标签 2">内容 2</tab>
  <tab title="标签 3">内容 3</tab>
</van-tabs>
export default {
  data() {
    return {
      active: 2
    };
  }
}

点击事件

可以在tabs上绑定click事件和change事件,事件传参为标签对应的索引和标题

<tabs @click="handleClick"></tabs>
<tabs @change="handleChange"></tabs>

Tabs API

属性名 类型 默认值 说明
v-model String Number 0 当前标签的索引
color String red 标签和底部条颜色
duration Number 0.3 动画时间,单位秒
line-width Number tab.offsetWidth / 2 底部条宽度,单位 px
line-height Number 3 底部条高度,单位 px

Tab API

属性名 类型 默认值 说明
title String - 当前标签的索引

example

code

tab

<template>
  <div
    v-show="isSelected"
    class="tab__pane"
    >
    <slot />
  </div>
</template>

<script>
export default {
  name: 'tab',
  props: {
    title: String,
  },
  data() {
    return {
      parent: null,
    };
  },
  computed: {
    index() {
      return this.parent.tabs.indexOf(this);
    },
    isSelected() {
      return this.index === this.parent.curActive;
    },
  },
  watch: {
    title() {
      this.parent.setLine();
    }
  },
  methods: {
    findParent(name) {
      let parent = this.$parent;
      while (parent) {
        if (parent.$options.name === name) {
          this.parent = parent;
          break;
        }
        parent = parent.$parent; // 多层嵌套
      }
    },
  },
  created() {
    this.findParent('tabs');
  },
  mounted() {
    const { tabs } = this.parent;
    const index = this.parent.$slots.default.indexOf(this.$vnode);
    tabs.splice(index === -1 ? tabs.length : index, 0, this);
  },
  beforeDestroy() {
    this.parent.tabs.splice(this.index, 1);
  },
};
</script>

<style>
.tab__pane {
}
</style>

tabs

<template>
  <div class="tabs">
    <div class="tabs__nav">
      <div
       class="tabs__line"
       :style="lineStyle"
      ></div>
      <div
        v-for="(tab, index) in tabs"
        :key="index"
        ref="tabs"
        @click="onClick(index)"
        class="tab"
        :style="getTabStyle(tab, index)"
        :class="{'tab--active': index === curActive,}"
      >
        <span>{{tab.title}}</span>
      </div>
    </div>
    <slot/>
  </div>
</template>

<script>
export default {
  name: 'tabs',
  model: {
    prop: 'active',
  },
  props: {
    color: String,
    lineWidth: {
      type: Number,
      default: null,
    },
    lineHeight: {
      type: Number,
      default: 3,
    },
    duration: {
      type: Number,
      default: 0.3,
    },
    active: {
      type: [Number, String],
      default: 0,
    },
  },
  data() {
    return {
      tabs: [],
      curActive: null,
      lineStyle: {
        backgroundColor: this.color,
      },
    };
  },
  computed: {},
  watch: {
    active(val) {
      if (val !== this.curActive) {
        this.correctActive(val);
      }
    },
    color() {
      this.setLine();
    },
    tabs() {
      this.correctActive(this.curActive || this.active);
      this.setLine();
    },
    curActive() {
      this.setLine();
    },
  },
  mounted() {
    this.correctActive(this.active);
    this.isFirstLoaded = true;
    this.$nextTick(() => {
      this.isFirstLoaded = false;
    });
  },
  methods: {
    onClick(index) {
      const { title } = this.tabs[index];
      this.setCurActive(index);
      this.$emit('click', index, title);
    },
    setActive() {},
    correctActive(active) {
      active = +active;
      // console.log(active, 'active');
      const exist = this.tabs.some(tab => tab.index === active);
      const defaultActive = (this.tabs[0] || {}).index || 0;
      this.setCurActive(exist ? active : defaultActive);
    },
    setCurActive(active) {
      if (active !== this.curActive) {
        console.log(this.curActive);
        if (this.curActive !== null) {
          this.$emit('change', active, this.tabs[active].title);
        }
        this.curActive = active;
      }
    },
    setLine() {
      const animation = this.isFirstLoaded;
      this.$nextTick(() => {
        const { tabs } = this.$refs;
        if (!tabs || !tabs[this.curActive]) {
          return;
        }
        const tab = tabs[this.curActive];
        const { lineWidth, lineHeight } = this;
        /* eslint-disable no-unneeded-ternary */
        const width = lineWidth ? lineWidth : (tab.offsetWidth / 2);
        /* eslint-disable no-unneeded-ternary */
        const left = tab.offsetLeft + ((tab.offsetWidth - width) / 2);
        const lineStyle = {
          width: `${width}px`,
          backgroundColor: this.color,
          transform: `translateX(${left}px)`,
        };
        if (!animation) {
          lineStyle.transitionDuration = `${this.duration}s`;
        }
        let height = '';
        if (lineHeight) {
          height = `${lineHeight}px`;
        } else {
          height = '3px';
        }
        lineStyle.height = height;
        lineStyle.borderRadius = height;
        this.lineStyle = lineStyle;
      });
    },
    getTabStyle(tab, index) {
      if (index === this.curActive) {
        return {
          color: this.color,
          fontWeight: 'bold',
        };
      }
    },
  },
};
</script>
<style>
.tab {
  flex: 1;
  cursor: pointer;
  min-width: 0;
  padding: 0 5px;
  font-size: 14px;
  position: relative;
  color: #000;
  line-height: 50px;
  text-align: center;
  box-sizing: border-box;
  background-color: #fff;
  span {
    display: block;
  }
  &--active {
    font-weight: 500!important;
  }
}
.tabs {
  position: relative;
  &__nav {
    display: flex;
    user-select: none;
    position: relative;
    background-color: #fff;
    height: 100%;
    box-sizing: content-box;
  }
  &__line {
    z-index: 1;
    left: 0;
    bottom: 0px;
    height: 3px;
    position: absolute;
    border-radius: 3px;
    background-color: red;
  }
}
</style>

END

原文地址:https://www.cnblogs.com/xiongxiaolong/p/10274325.html

时间: 2024-10-11 06:58:24

Vue实现简单Tab标签页组件的相关文章

Tab标签页接口(1)

Tab标签接口的用简单的应用是把不同的标签页的接口组件集中放在同一个接口布局文件,而且程序代码也会集中放在主类的程序文件中.这种方法的好处就是项目中的文件数目不会增加,但是如果Tab标签的接口组件个数比较多,或者是程序代码比较复杂,把它们集中放在同一个文件会造成日后程序维护上的困难.我们已经学习过Intent对象的使用方法,这里我们就用Intent对象来建立Tab标签页接口.这个新的方法其实是基于一个很简单的概念,就是每一个Tab标签页都对应到一个独立的Activity类,因此不同标签页的接口布

如何一行jquery代码写出tab标签页(链式操作)

啦啦!今天又学了一招,js写几十行的tab标签页jquery写一行就行啦,用到了链式操作!以下是代码: <!DOCTYPE html> <html lang="en"> <head> <style> *{ padding: 0; margin: 0; } ul{ list-style-type: none; } #ul{ height: 30px; margin-bottom: 10px; } #ul li { height: 30px;

tab标签页四种写法

html: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, minimal-ui" /> <meta name="apple-mobile-we

jquery插件之tab标签页或滑动门

该插件乃本博客作者所写,目的在于提升作者的js能力,也给一些js菜鸟在使用插件时提供一些便利,老鸟就悠然地飞过吧. 此插件旨在实现目前较为流行的tab标签页或滑动门特效,在此插件中默认使用的是鼠标滑过事件,您也可以根据自己的实际需求将滑过事件hover改为点击事件click.整体代码如下: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1

Bootstrap插件——(Tab)标签页

项目中用到了Bootstrap的(Tab)标签页插件,记录如下: 代码如下: 1 <div class="tabbable"> 2 <ul class="nav nav-tabs"> 3 <li class="active"><a href="#第一个id" data-toggle="tab">选项一</a></li> //第一个选项,

简单说说tab标签页和轮播图

准确的来说,一个页面能够独立的做出轮播图才算是刚步入前端世界.说轮播图之前先来说说标签页,.因为它比较简单,我的效果使用jquery来实现的,. 具体代码只有一点点,. var timeout; $(document).ready(function(){ $("#tabfirst li").each(function(index){ var liNode=$(this); liNode.click(function(){ timeout=setTimeout(function(){ $

整理用js实现tab标签页

首先是css样式,比如这样的: 1 <style> 2 *{ 3 margin:0; 4 padding: 0; 5 list-style: none; 6 font-size: 12px; 7 } 8 .notice{ 9 width: 298px; 10 height: 98px; 11 margin: 10px; 12 border:1px solid #7c7c7c; 13 overflow: hidden; 14 } 15 .notice-tit{ 16 height: 27px;

简单实现tab标签页切换

<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title> <script src="http://lib.sinaapp.com/js/jquery/1.9.1/jquery-1.9.1.min.js"></script> <style>

vue内置的标签(组件)

component:用于动态组件,查看博文vue学习之组件. <component :is="componentId"></component> transition:过渡和动画,查看官网文档进入/离开&列表过渡. <!-- 简单元素 --> <transition> <div v-if="ok">toggled content</div> </transition> tra