<template> <div v-show="show"> <slot></slot> </div> </template> <script> export default { name: "PTabPane", props: { label: { type: String, default: '' }, name: { type: [String, Number], default: '' }, disabled: { type: Boolean, default: false } }, data(){ return { loaded: false } }, computed: { show(){ if(this.$parent.currentName === this.name){ if(!this.loaded){ this.loaded = true; } return true; } return false; } }, watch: { label(){ // label更新的时候强制更新父组件,以触发PTabNav才能更新 this.$parent.$forceUpdate(); } }, mounted() { // 当当前组件创建的时候将当前组件添加到父组件的pTabPanes中,以触发PTabNav才能更新 this.$parent.addPane(this); }, destroyed() { if(this.$el && this.$el.parentNode){ this.$el.parentNode.removeChild(this.$el); } // 当当前组件销毁时需从父组件中的pTabPanes中移除当前组件,以触发PTabNav才能更新 this.$parent.removePane(this); } } </script>
PTabNav.vue
<script> function noop() {}; export default { name: "PTabNav", props: { tabPanes: { type: Array, default(){ return []; } }, handTabNavClick: { type: Function, default(){ return function () {}; } } }, data(){ return { navPrevDisabled: true, navNextDisabled: true, // 控制左右箭头显示 scrollable: false, listOffset: 0 } }, methods: { navPrevClickEvent(){ if(!this.navPrevDisabled){ let navScrollW = this.$refs.nav_scroll.offsetWidth; let navListW = this.$refs.nav_list.offsetWidth; let maxTransformX = 0; let transformX = this.listOffset - navScrollW; if(transformX < maxTransformX){ transformX = maxTransformX; } if(transformX === this.listOffset){ return; } console.log('上一页按钮点击了', transformX); this.listOffset = transformX; if(transformX === 0){ this.navPrevDisabled = true; this.navNextDisabled = false; }else if(transformX === (navListW - navScrollW)){ this.navPrevDisabled = false; this.navNextDisabled = true; }else{ this.navPrevDisabled = false; this.navNextDisabled = false; } } }, navNextClickEvent(){ if(!this.navNextDisabled){ let navScrollW = this.$refs.nav_scroll.offsetWidth; let navListW = this.$refs.nav_list.offsetWidth; let maxTransformX = navListW - navScrollW; let transformX = this.listOffset + navScrollW; if(transformX > maxTransformX){ transformX = maxTransformX; } if(transformX === this.listOffset){ return; } console.log('下一页按钮点击了', transformX); this.listOffset = transformX; if(transformX === 0){ this.navPrevDisabled = true; this.navNextDisabled = false; }else if(transformX === (navListW - navScrollW)){ this.navPrevDisabled = false; this.navNextDisabled = true; }else{ this.navPrevDisabled = false; this.navNextDisabled = false; } } }, // 计算 .p-tabs_nav-list 是否溢出 calculateListSpilled(){ let navScrollW = this.$refs.nav_scroll.offsetWidth; let navListW = this.$refs.nav_list.offsetWidth; if(navScrollW < navListW){ this.scrollable = true; }else{ if(this.listOffset > 0){ this.listOffset = 0; } this.scrollable = false; } }, // 滚动条滚动到激活的tab scrollToActiveTab(){ if(this.scrollable){ this.$nextTick(() => { let navScrollW = this.$refs.nav_scroll.offsetWidth; let navList = this.$refs.nav_list; let activeTab = navList.querySelector('.active'); let activeTabOffsetLeft = 0; if(activeTab){ activeTabOffsetLeft = activeTab.offsetLeft; } let transformX = activeTabOffsetLeft + activeTab.offsetWidth - navScrollW; transformX = transformX < 0 ? 0 : transformX; this.listOffset = transformX; if(transformX === 0){ this.navPrevDisabled = true; this.navNextDisabled = false; }else if(transformX === (navList.offsetWidth - navScrollW)){ this.navPrevDisabled = false; this.navNextDisabled = true; }else{ this.navPrevDisabled = false; this.navNextDisabled = false; } }); } } }, computed: { listOffsetTran(){ console.log('dddd',`translateX(-${this.listOffset}px);`) return { transform: `translateX(-${this.listOffset}px)` } } }, render(h) { /*dom结构 <div> <span></span> <span></span> <div> <div> <div>全部</div> <div>技术教学</div> <div>新手教学</div> </div> </div> </div> */ let navPrev = h('span', { staticClass: 'p-tabs_nav-prev', 'class': { disabled: this.navPrevDisabled }, on: { click: this.navPrevClickEvent } }); let navNext = h('span', { staticClass: 'p-tabs_nav-next', 'class': { disabled: this.navNextDisabled }, on: { click: this.navNextClickEvent } }); // 生成标签页 let navItems = this.tabPanes.map(item => { let $labelSlot = item.$scopedSlots.label ? item.$scopedSlots.label() : null; let labelContent = $labelSlot ? $labelSlot : item.label; return h('div', { staticClass: 'p-tabs_nav-item', 'class': { active: this.$parent.currentName === item.name, disabled: item.disabled, }, on: { click: (e) => { this.handTabNavClick(item.name, item, e); } } }, [labelContent]); }); let navScroll = h('div', { staticClass: 'p-tabs_nav-scroll', ref: 'nav_scroll' }, [ h('div', { staticClass: 'p-tabs_nav-list', ref: 'nav_list', style: this.listOffsetTran }, [navItems]) ]); return h('div', { staticClass: 'p-tabs_header', 'class': { 'is-scrollable': this.scrollable }, }, [navPrev, navNext, navScroll]); }, updated(){ this.calculateListSpilled(); }, mounted() { this.calculateListSpilled(); } } </script>
4、使用
main.js
// 引入tabs组件 import tabs from './components/p-tabs'; // 全局注册p-tabs组件 Vue.use(tabs);
页面中使用