SOURCE

console 命令行工具 X clear

                    
>
console
var Main = {
    data() {
        return {
            navList: [],
            activeKey: this.value,
            barWidth: 0,
            barOffset: 0,
            scrollable: false,
            navStyle: {
                transform: ''
            }
        }
    },
    computed: {
        barStyle() {
            return {
                width: `${this.barWidth}px`,
                transform: `translate3d(${this.barOffset}px,0px,0px)`
            }
        }
    },
    methods: {
        getTabs() {
            return this.$children.filter(item => item.$options.name === 'TabPane')
        },
        initTabs() {
            this.updateNav()
            this.updateStatus()
            this.updataBar()
        },
        updateNav() {
            this.navList = []
            this.getTabs().forEach((pane, index) => {
                this.navList.push({
                    label: pane.label,
                    name: pane.name || index
                })
                if (index === 0 && !this.activeKey) {
                    this.activeKey = pane.name
                }
            })
        },
        updataBar() {
            this.$nextTick(() => {
                const index = this.navList.findIndex(nav => nav.name === this.activeKey)
                const elemTabs = this.$refs.navWrap.querySelectorAll('.tabs-tab')
                const elemTab = elemTabs[index]
                this.barWidth = elemTab ? elemTab.offsetWidth : 0
                if (index > 0) {
                    let offset = 0
                    for (let i = 0; i < index; i++) {
                        offset += elemTabs[i].offsetWidth + 16
                    }
                    this.barOffset = offset
                } else {
                    this.barOffset = 0
                }
            })
        },
        updateStatus() {
            const tabs = this.getTabs()
            tabs.forEach(tab => (tab.show = tab.name === this.activeKey))
        },
        handleChange(index) {
            const nav = this.navList[index]
            this.activeKey = nav.name
        },
        handleResize() {
            const navWidth = this.$refs.nav.offsetWidth
            const scrollWidth = this.$refs.navScroll.offsetWidth
            if (scrollWidth < navWidth) {
                this.scrollable = true
            } else {
                this.scrollable = false
            }
            this.updateMove()
        },
        updateMove() {
            const navWidth = this.$refs.nav.offsetWidth
            const scrollWidth = this.$refs.navScroll.offsetWidth
            const currentOffset = this.getCurrentScrollOffset()
            if (scrollWidth < navWidth) {
                if (navWidth - currentOffset < scrollWidth) {
                    this.navStyle.transform = `translateX(-${navWidth - scrollWidth}px)`
                }
            } else {
                if (currentOffset > 0) {
                    this.navStyle.transform = `translateX(-${0}px)`
                }
            }
        },
        getCurrentScrollOffset() {
            const { navStyle } = this
            const reg = /translateX\(-(\d+(\.\d+)*)px\)/
            return navStyle.transform ? Number(navStyle.transform.match(reg)[1]) : 0
        },
        setOffset(value) {
            this.navStyle.transform = `translateX(-${value}px)`
        },
        scrollPrev() {
            const containerWidth = this.$refs.navScroll.offsetWidth
            const currentOffset = this.getCurrentScrollOffset()
            if (!currentOffset) return
            let newOffset = 0
            if (currentOffset > containerWidth) {
                newOffset = currentOffset - containerWidth
            }
            this.navStyle.transform = `translateX(-${newOffset}px)`
        },
        scrollNext() {
            const navWidth = this.$refs.nav.offsetWidth
            const containerWidth = this.$refs.navScroll.offsetWidth
            const currentOffset = this.getCurrentScrollOffset()
            if (navWidth - currentOffset <= containerWidth) return
            let newOffset = null
            if (navWidth - currentOffset > containerWidth * 2) {
                newOffset = currentOffset + containerWidth
            } else {
                newOffset = navWidth - containerWidth
            }
            this.navStyle.transform = `translateX(-${newOffset}px)`
        }
    },
    watch: {
        value(val) {
            this.activeKey = val
        },
        activeKey() {
            this.updateStatus()
            this.updataBar()
        }
    },
    mounted() {
        this.observer = elementResizeDetectorMaker()
        this.observer.listenTo(this.$refs.navWrap, this.handleResize)
    },
    beforeDestroy() {
        this.observer.removeListener(this.$refs.navWrap, this.handleResize)
    }
};
var Ctor = Vue.extend(Main)
new Ctor().$mount('#app')
<div id="app">
    <div class="tabs">
    <div ref="navWrap" class="tabs-nav-wrap" :class="[scrollable ? 'tabs-nav-scrollable' : '']">
      <span
        class="tabs-nav-prev"
        :class="[scrollable ?  '' : 'tabs-nav-scroll-disabled']"
        @click="scrollPrev"
      >&lt;</span>
      <span
        class="tabs-nav-next"
        :class="[scrollable ?  '' : 'tabs-nav-scroll-disabled']"
        @click="scrollNext"
      >&gt;</span>
      <div ref="navScroll" class="tabs-nav-scroll">
        <div ref="nav" class="tabs-nav" :style="navStyle">
          <div class="tabs-inv-bar" :style="barStyle"></div>
          <div
            class="tabs-tab"
            v-for="(item, index) in navList"
            :key="index"
            @click="handleChange(index)"
          >{{item.label}}</div>
        </div>
      </div>
    </div>
  </div>
</div>


    .tabs {
    .tabs-nav-wrap {
        position: relative;
        border-bottom: 1px solid #dcdee2;
        margin-bottom: 16px;
    }
    .tabs-tab {
        position: relative;
        display: inline-block;
        margin-right: 16px;
        padding: 8px 16px;
        cursor: pointer;
    }
    .tabs-inv-bar {
        position: absolute;
        left: 0;
        bottom: 0;
        background-color: #2d8cf0;
        height: 2px;
        transition: transform 300ms ease-in-out;
    }
    .tabs-nav-scroll {
        overflow: hidden;
        white-space: nowrap;
    }
    .tabs-nav {
        position: relative;
        float: left;
        transition: transform 0.5s ease-in-out;
    }
    .tabs-nav-prev,
    .tabs-nav-next {
        position: absolute;
        width: 32px;
        line-height: 32px;
        text-align: center;
        cursor: pointer;
    }
    .tabs-nav-prev {
        left: 0;
    }
    .tabs-nav-next {
        right: 0;
    }
    .tabs-nav-scrollable {
        padding: 0 32px;
    }
    .tabs-nav-scroll-disabled {
        display: none;
    }

本项目引用的自定义外部资源