zoukankan      html  css  js  c++  java
  • 使用vue开发自定义tabs标签页组件

    共涉及基础组件文件 tabs.vue pane.vue 和样式文件 tabs.scss 和案例页面文件 index.vue 四个文件

    完整代码可到gitee下载

    话不多说直接上代码:

    tabs.vue 内容如下:

      1 <template>
      2   <div class="tabs" :class="{'tabs-border': showBorder}">
      3     <div :class="'tabs-' + getTabsType() + '-bar'">
      4       <div v-for="(item,index) in navList" :class="tabCls(item)" @click="handleChange(index)" :style="getTabFontStyle()">
      5         {{ item.label }}
      6         <span v-if="ifShowClose(item)" class="close icon" @click.stop="closeTab(index)" />
      7       </div>
      8     </div>
      9     <div class="tabs-content">
     10       <slot />
     11     </div>
     12   </div>
     13 </template>
     14 
     15 <script>
     16 export default {
     17   name: 'Tabs',
     18   props: {
     19     value: {
     20       type: [String, Number]
     21     },
     22     border: {
     23       type: Boolean,
     24       default: false
     25     },
     26     tabsType: {
     27       type: String,
     28       default: 'tab'
     29     },
     30     fontSize: {
     31       type: [String, Number],
     32       default: 16
     33     },
     34     fontColor: {
     35       type: String,
     36       default: '#000000'
     37     }
     38   },
     39   data() {
     40     return {
     41       defType: ['tab', 'card', 'card2', 'brief'],
     42       currentValue: this.value,
     43       showBorder: this.border,
     44       navList: []
     45     }
     46   },
     47   watch: {
     48     value: function(val) {
     49       this.currentValue = val
     50     },
     51     currentValue: function() {
     52       this.updateStatus()
     53     }
     54   },
     55   methods: {
     56     tabCls: function(item) {
     57       var active = ''
     58       var tabsType = this.getTabsType()
     59       var tabsBaseClass = 'tabs-' + tabsType
     60 
     61       if (item.name === this.currentValue) {
     62         active = 'tabs-' + tabsType + '-active'
     63       }
     64 
     65       return [
     66         tabsBaseClass,
     67         active
     68       ]
     69     },
     70     getTabs() {
     71       // 通过遍历子组件,得到所有的pane组件
     72       return this.$children.filter(function(item) {
     73         return item.$options.name === 'Pane' || item.$options.name === 'pane'
     74       })
     75     },
     76     updateNav() {
     77       this.navList = []
     78       var _this = this
     79       this.getTabs().forEach(function(pane, index) {
     80         _this.navList.push({
     81           label: pane.label,
     82           name: pane.name || index,
     83           closable: pane.closable
     84         })
     85         if (!pane.name) {
     86           pane.name = index
     87         }
     88         if (index == 0) {
     89           if (!_this.currentValue) {
     90             _this.currentValue = pane.name || index
     91           }
     92         }
     93       })
     94       this.updateStatus()
     95     },
     96     updateStatus() {
     97       var _this = this
     98       // 显示当前选中的tab对应的pane组件
     99       _this.getTabs().forEach(function(tab) {
    100         tab.show = tab.name === _this.currentValue
    101         // return
    102       })
    103     },
    104     handleChange: function(index) {
    105       var nav = this.navList[index]
    106       var name = nav.name
    107       // 更新当前选择的tab
    108       this.currentValue = name
    109       // 更新value
    110       this.$emit('input', name)
    111     },
    112     ifShowClose(item) {
    113       // 是否显示关闭标签按钮
    114       return item.closable
    115     },
    116     // 点击关闭按钮触发的事件
    117     closeTab(index) {
    118       // console.log(this.navList[index].name, this.currentValue);
    119       // 如果关闭的是当前选择的tab,则将currentValue转到前一个tab
    120       if (this.navList[index].name == this.currentValue) {
    121         let toIndex = index - 1
    122         toIndex = toIndex >= 0 ? toIndex : this.navList.length + toIndex
    123         console.log(toIndex)
    124         this.currentValue = this.navList[toIndex].name
    125       }
    126       // 关闭当前标签页
    127       this.navList.splice(index, 1)
    128     },
    129     // 根据组件属性tabsType 设置tabs头部样式
    130     getTabsType() {
    131       if (this.$data.defType.indexOf(this.tabsType) === -1) {
    132         return 'tab'
    133       }
    134       return this.tabsType
    135     },
    136     // 根据组件属性fontSize 设置tab 字体大小和颜色
    137     getTabFontStyle() {
    138       if (!/^#[0-9a-fA-F]{6}$/.test(this.fontColor)) {
    139         this.fontColor = '#000000'
    140       }
    141       if (!/^[1-3][0-9]$/.test(this.fontSize) || this.fontSize < 12) {
    142         this.fontSize = 16
    143       }
    144       return 'font-size: ' + this.fontSize + 'px; color: ' + this.fontColor
    145     }
    146   }
    147 }
    148 </script>
    149 <style scoped>
    150 </style>

    pane.vue 内容如下:

     1 <template>
     2   <div :class="getCls()">
     3     <slot />
     4   </div>
     5 </template>
     6 
     7 <script>
     8 export default {
     9   name: 'Pane',
    10   props: {
    11     name: {
    12       type: String
    13     },
    14     label: {
    15       type: String,
    16       default: ''
    17     },
    18     closable: {
    19       type: Boolean,
    20       default: false
    21     }
    22   },
    23   data: function() {
    24     return {
    25       show: true
    26     }
    27   },
    28   watch: {
    29     label() {
    30       this.updateNav()
    31     }
    32   },
    33   mounted() {
    34     this.updateNav()
    35   },
    36   methods: {
    37     updateNav() {
    38       this.$parent.updateNav()
    39     },
    40     // 决定pane是否显示
    41     getCls() {
    42       return [
    43         'pane',
    44         {
    45           'pane-active': this.show
    46         }
    47       ]
    48     }
    49   }
    50 }
    51 </script>
    52 
    53 <style scoped>
    54 </style>

    tabs.scss 内容如下:

      1 [v-cloak] {
      2     display: none;
      3 }
      4 .tabs {
      5     margin: 3px;
      6     font-size: 14px;
      7     color: #657180;
      8 }
      9 .tabs-border {
     10     border-width: 1px;
     11     border-radius: 2px;
     12     border-style: solid;
     13     border-color: #e6e6e6;
     14     box-shadow: 0 2px 5px 0 rgb(0 0 0 / 10%)
     15 }
     16 .tabs-tab-bar:after {
     17     content: '';
     18     width: 100%;
     19     height: 1px;
     20     display: block;
     21     margin-top: -1px;
     22     background: #d7dde4;
     23 }
     24 .tabs-tab {
     25     cursor: pointer;
     26     padding: 6px 16px;
     27     margin-right: 6px;
     28     background: #fff;
     29     position: relative;
     30     display: inline-block;
     31     border: 1px solid #d7dde4;
     32 }
     33 .tabs-tab-active {
     34     color: #3399ff;
     35     border-top: 1px solid #3399ff;
     36     border-bottom: 1px solid #fff;
     37 }
     38 .tabs-tab-active:before {
     39     content: '';
     40     top: 0;
     41     left: 0;
     42     right: 0;
     43     height: 1px;
     44     display: block;
     45     position: absolute;
     46     background: #3399ff;
     47 }
     48 /*brief 风格的tabs 样式 start*/
     49 .tabs-brief-bar:after {
     50     content: '';
     51     width: 100%;
     52     height: 1px;
     53     display: block;
     54     margin-top: -1px;
     55     background: #d7dde4;
     56 }
     57 .tabs-brief {
     58     cursor: pointer;
     59     padding: 8px 16px;
     60     background: #fff;
     61     position: relative;
     62     display: inline-block;
     63 }
     64 .tabs-brief-active {
     65     border-top: none;
     66     font-weight: 800;
     67     color: #3399ff !important;
     68     border-bottom: 1px solid #d7dde4;
     69 }
     70 .tabs-brief-active:after {
     71     content: '';
     72     left: 0;
     73     right: 0;
     74     height: 2px;
     75     bottom: -3px;
     76     display: block;
     77     position: absolute;
     78     background: #3399ff;
     79 }
     80 /*brief 风格的tabs 样式 end*/
     81 
     82 /*card 风格的tabs 样式 start*/
     83 .tabs-card-bar {
     84     background: #F1F1F1;
     85 }
     86 .tabs-card {
     87     cursor: pointer;
     88     padding: 8px 16px;
     89     position: relative;
     90     display: inline-block;
     91 }
     92 .tabs-card-active {
     93     font-weight: 800;
     94     color: #3399ff !important;
     95 }
     96 /*card 风格的tabs 样式 end*/
     97 
     98 /*card2 风格的tabs 样式 start*/
     99 .tabs-card2-bar {
    100     background: #F1F1F1;
    101 }
    102 .tabs-card2 {
    103     cursor: pointer;
    104     padding: 8px 16px;
    105     position: relative;
    106     display: inline-block;
    107 }
    108 .tabs-card2-active {
    109     font-weight: 800;
    110     background: #ffffff;
    111 }
    112 /*card 风格的tabs 样式 end*/
    113 
    114 .tabs-content {
    115     min-height: 100px;
    116     padding: 6px;
    117 }
    118 .pane {
    119     height: 0;
    120     width: 100%;
    121     visibility: hidden;
    122     transition: all .5s ease-in;
    123     transform: translateX(-100%);
    124 }
    125 .pane-active {
    126     visibility: visible;
    127     transform: translateX(0);
    128 }
    129 
    130 /* 关闭按钮样式 start*/
    131 .close.icon {
    132     width: 11px;
    133     height: 11px;
    134     color: #000;
    135     margin-left: 5px;
    136     border-radius: 50%;
    137     position: relative;
    138     display: inline-block;
    139 }
    140 .close.icon:hover {
    141     background: #eee;
    142 }
    143 .close.icon:before {
    144     content: '';
    145     top: 5px;
    146     width: 11px;
    147     height: 1px;
    148     position: absolute;
    149     background-color: currentColor;
    150     -webkit-transform: rotate(-45deg);
    151     transform: rotate(-45deg);
    152 }
    153 .close.icon:after {
    154     content: '';
    155     top: 5px;
    156     width: 11px;
    157     height: 1px;
    158     position: absolute;
    159     background-color: currentColor;
    160     -webkit-transform: rotate(45deg);
    161     transform: rotate(45deg);
    162 }  
    163 /* 关闭按钮样式 end*/

    index.vue 内容如下:

    <template>
      <div class="app-main">
        <tabs v-model="activePane" 
          tabs-type="card" // 标签页类型 非必填 可选 tab/brief/card/card2 默认tab
          :font-size="16" // 标签头部字体大小 数值型 非必填 默认16
          font-color="#888888" // 标签头部字体颜色 非必填 默认#000000
          :border="true" // 是否显示外边框 非必填 默认false
      >
          <pane label="标签一" // 标签页名称 必填
           name="1" //标签页定位符 非必填 为空时取标签页的索引值进行定位
            :closable="false" // 标签页是否可以关闭 非必填  默认false
       >
            <tabs v-model="activePane1" tabs-type="card" :font-size="16" :border="true">
              <pane label="内标签一" name="11">
                标签一 内部标签页内容一
              </pane>
              <pane label="内标签二" name="12">
                标签一 内部标签页内容二
              </pane>
              <pane label="内标签三" name="13">
                标签一 内部标签页内容三
              </pane>
            </tabs>
          </pane>
    
          <pane label="标签二" name="2" :closable="false">
            <tabs v-model="activePane2" tabs-type="card2" :font-size="16" :border="true">
              <pane label="内标签一" name="21">
                标签二 内部标签页内容一
              </pane>
              <pane label="内标签二" name="22">
                标签二 内部标签页内容二
              </pane>
              <pane label="内标签三" name="23">
                标签二 内部标签页内容三
              </pane>
            </tabs>
          </pane>
    
          <pane label="标签三" name="3">
            <tabs v-model="activePane3">
              <pane label="内标签一" name="31">
                标签三 内部标签页内容一
              </pane>
    
              <pane label="内标签二" name="32">
    
                <tabs v-model="activePane32" tabs-type="brief">
                  <pane label="内标签一" name="32">
                    标签三 内部标签二的内部标签页内容一
                  </pane>
                  <pane label="内标签二" name="322">
                    标签三 内部标签二的内部标签页内容二
                  </pane>
                  <pane label="内标签三" name="323">
                    标签三 内部标签二的内部标签页内容三
                  </pane>
                </tabs>
              </pane>
    
              <pane label="内标签三" name="33">
    
                <tabs v-model="activePane33" tabs-type="brief" :border="true">
                  <pane label="内标签一" name="331">
                    标签三 内部标签三的内部标签页内容一
                  </pane>
                  <pane label="内标签二" name="332">
                    标签三 内部标签三的内部标签页内容二
                  </pane>
                  <pane label="内标签三" name="333">
                    标签三 内部标签三的内部标签页内容三
                  </pane>
                </tabs>
              </pane>
            </tabs>
          </pane>
        </tabs>
      </div>
    </template>
    
    <script>
    // 引入相应组件
    import Tabs from '@/components/tabs/tabs.vue'
    import Pane from '@/components/tabs/pane.vue'
    export default {
      name: 'Test2',
      components: {
        Tabs,
        Pane
      },
      data() {
        return {
          activePane: '3',
          activePane1: '11',
          activePane2: '22',
          activePane3: '33',
          activePane32: '322',
          activePane33: '333'
        }
      }
    }
    </script>
    // 引入标签页样式文件
    <style>
    @import '../../styles/tabs.scss';// 根据实际应用填写此路径
    </style>

    tabs-type 不写 或 tabs-type 为空 或 tabs-type="tab"时:

    tabs-type="brief"时:

    tabs-type="brief" 且 :border="true" 时:

    tabs-type="card" 且 :border="true" 时:

    tabs-type="card2"且 :border="true"时:

     完整代码文件已上传gitee,传送门

    转发自 https://juejin.cn/post/6844903881965568007 以上为在原作基础上略作改动
    第一次编写发布博客 如有不足之处还望多多指教

  • 相关阅读:
    [thinkphp] 是如何输出一个页面的
    [thinkphp] 获取根目录绝对路径
    onethink 插件模板定位
    win7 安全模式开启声音
    百度贴吧楼层评论地址
    第一天问题
    [php] 解析JSON字符串
    NDK编译时两 .so之间调用问题
    CDN问题积累
    C++模板特化
  • 原文地址:https://www.cnblogs.com/jindao3691/p/14538041.html
Copyright © 2011-2022 走看看