Custom tab component, options can be slotted html

Folder xxtabs

Four files index exposes render vue adds virtual nodes to slots (custom label structure) tabs tab overall abpaneq switching area

 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"
      >
        <div class="tab_item" v-if="typeof item.label === 'string'">
          {
   
   { item.text }}
        </div>

        <render v-else :params="item.label"></render>
      </div>
    </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,
    },
    tabWidth: {
      type: String,
      default: "",
    },
  },
  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;
    },
    // 设置tab点击栏
    setTabBar(tabsPaneInstance, slotElement) {
      // tab的描述信息可以是字符串也可以是render函数
      const label = tabsPaneInstance.label,
        type = typeof label;
      // 添加到数组项中,根据添加条件渲染
      this.tabNavList.push({
        text: type == "function" ? "" : label,
        renderFun: type == "function" ? label : "",
        name: tabsPaneInstance.name,
        label: slotElement.tab === undefined ? label : slotElement.tab,
      });
    },
    handleTabNavClick(item, index) {
      console.log('name',item,index)
      if (item.name == this.activeName) return;
      // 更新当前活跃项
      this.activeName = item.name;
      // 活跃项的索引
      this.currentIndex = index;
    },

    // 交换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 scoped>
.gnip-tab {
  height: 100%;
  /* width: var(tabWidth); */
  /* width: 200px; */
}
.gnip-tab-nav {
  display: flex;
  position: relative;
}
.tab-nav-item {
  background-color: #074889;

  padding-left: 5px;
  padding-right: 5px;

  line-height: 18px;
  text-align: center;

  height: 24px;

  box-sizing: border-box;

  background: rgb(7, 72, 137);
  border-left: 1px solid rgb(44, 100, 155);
  border-right: 1px solid rgb(44, 100, 155);
  border-bottom: 1px solid rgb(44, 100, 155);
  border-radius: 0px 0px 5px 5px;
}
.tab-nav-item.active {
  background-color: #0078ef;
}
.tab-nav-track {
  width: 100%;
  position: relative;
  height: 2px;
}
.tab-content-wrap{
  height: calc(100% - 24px);
}
.track-line {
  height: 2px;
  background-color: #2d8cf0;
  position: absolute;
  transition: left 0.35s;
}
.tab_item {
}
</style>

tabPane.vue

<template>

    <div class="gnip-tabs-pane" v-if="$parent.activeName === name">
    
      <!-- <transition :name="paneTransitionName"> -->

        <div class="tab-pane-content" >

          <slot name="default"></slot>
          <!-- <slot name="one"></slot> -->
        </div>
      <!-- </transition> -->
    </div>
  </template>
  <script>
  export default {
    props: {

      label: {
        type: [String, Function],
      },
      
      name: {
        type: String,
      },
      disabled: {
        type: Boolean,
        default: false,
      },
    },
    data() {
      return {
        paneTransitionName: "enter-right",
      };
    },
    created() {

      this.$parent.setTabBar(this,this.$slots);

    },
    mounted(){

    },
  };
  </script>
  <style scoped>
  .gnip-tabs-pane {
    height: 100%;
    overflow-x: hidden;
  }
.tab-pane-content{
  height: inherit;
}
  </style>
  

render.js

import { h } from 'vue'

export default {
  data() {
    return {
      msg: 'hello'
    }
  },
  props:{
    params: {
        type: Function,
        default() {
          return function(){};
        },
      },
  },
  render() {
//获取插槽内容创建成div
    return h('div', this.params())
  }
}
  

index.js

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

use

introduce

import { Tabs, TabPane } from "@/components/SreenTabs";

use

        <Tabs style="margin-left: 10px" :value="logTabName" show-track-bg>
          <TabPane label="日志" name="日志">
         日志文本
          </TabPane>

          <TabPane label="消息" name="消息">
            <template v-slot:tab>
              <n-badge :value="8" :offset="[5, -2]">
                <text style="color: white">消息</text></n-badge
              >
            </template>
           消息文本
          </TabPane>
          <TabPane label="异常" name="异常">
           异常文本
          </TabPane>
        </Tabs>

Guess you like

Origin blog.csdn.net/qq_51389137/article/details/131454443