300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > vue 循环tabs 标签页 组件_Vue render函数实战--实现tabs选项卡组件

vue 循环tabs 标签页 组件_Vue render函数实战--实现tabs选项卡组件

时间:2023-03-29 17:47:43

相关推荐

vue 循环tabs 标签页 组件_Vue render函数实战--实现tabs选项卡组件

用过Element ui库的童鞋肯定知道组件,简单、好用、可以自定义标签页,不知道广大童鞋们在刚开始使用组件的时候有没有想过它是如何实现的?我咋刚开始使用组件的时候就有去想过,也想去实现一个超级简单的tabs选项卡组件,无奈当时功力不够,未能实现。最近的一个简单项目中正好要用到选项卡组件,由于项目简单也就没有使用任何第三方库,于是就自己动手写了个选项卡组件。

1、实现tabs选项卡组件的思考

用户管理

配置管理

角色管理

定时任务补偿

问题:

如何根据来生成标签页?

如何过滤组件中的子元素,使得在使用的时候只显示,而不会显示其他组件或div之类的元素?

2、实现思路

想根据来生成标签页就需要使用到,使用用的形式肯定是不行的,因为无法获取到的数量;使用的形式行不通,那就只有使用render函数了

过滤组件中的子元素也需要使用render函数

3、代码实现

index.js

import PTabs from './PTabs';

import PTabPane from './PTabPane';

export default function tabsInstall(Vue) {

if(tabsInstall.installed){

return;

}

ponent('PTabs', PTabs);

ponent('PTabPane', PTabPane);

}

PTabs.vue

import PTabNav from './PTabNav';

export default {

name: "PTabs",

props: {

value: {

type: [String, Number],

default: ''

},

beforeClick: {

type: Function,

default(){

return function () {};

}

}

},

components: {

PTabNav

},

data(){

return {

pTabPanes: [],

currentName: this.value || 0

}

},

methods: {

addPane(pane){

this.pTabPanes.push(pane);

if(!this.currentName){

this.setCurrentName(this.pTabPanes[0].name);

}

},

removePane(pane){

let index = this.pTabPanes.indexOf(pane);

if(index > -1){

this.pTabPanes.splice(index, 1);

}

},

setCurrentName(name){

if(this.currentName !== name){

this.currentName = name;

this.$emit('input', name);

}

},

// 标签页点击事件

handTabNavClick(name, pane, e){

if(this.currentName === name || pane.disabled){

return;

}

let before = this.beforeClick();

if(before && before.then){

before.then(() => {

this.setCurrentName(name);

this.$emit('tabClick', pane, e);

})

}else{

this.setCurrentName(name);

this.$emit('tabClick', pane, e);

}

}

},

watch: {

value(newVal){

this.setCurrentName(newVal);

},

currentName(){

this.$nextTick(() => {

this.$refs.p_tab_nav.scrollToActiveTab();

});

}

},

render(h) {

let {$scopedSlots} = this;

let $default = $scopedSlots.default();

let qTabPanes = $default.map(item => {

/* 过滤xxx中传递的xxx内容。这里只接收组件,因为我们需要根据组件的数量来生成

* 组件,如果参差了其它节点则会导致不能正确生成组件 */

if(ponentOptions && ponentOptions.tag === 'PTabPane'){

return item;

}

});

let qTab = h('PTabNav', {

props: {

// 将tab-pane传递给 组件,组件就知道要有多少个tab-item了

tabPanes: this.pTabPanes,

handTabNavClick: this.handTabNavClick

},

ref: 'p_tab_nav'

});

let qTabBody = h('div', {

staticClass: 'p-tabs_content'

}, qTabPanes);

console.log($default)

return h('div', {

staticClass: 'p-tabs'

}, [qTab, qTabBody]);

},

mounted() {

//console.log(this)

this.$nextTick(() => {

this.$refs.p_tab_nav.scrollToActiveTab();

});

}

}

.p-tabs{

.p-tabs_header{

position: relative;

margin-bottom: 15px;

&.is-scrollable{

padding-left: 20px;

padding-right: 20px;

}

}

.p-tabs_nav-prev,

.p-tabs_nav-next{

position: absolute;

top: 0;

width: 20px;

height: 100%;

display: none;

&::before{

position: absolute;

content: ' ';

font-size: 0;

line-height: 0;

width: 10px;

height: 10px;

top: 50%;

left: 50%;

border-top: 1px solid #eee;

border-left: 1px solid #eee;

margin: -5px 0 0 -5px;

}

cursor: pointer;

&.disabled{

cursor: default;

border-color: #aaa;

}

}

.p-tabs_nav-prev{

left: 0;

&:before{

transform: rotate(-45deg);

}

}

.p-tabs_nav-next{

right: 0;

&:before{

transform: rotate(135deg);

}

}

.p-tabs_header{

&.is-scrollable{

.p-tabs_nav-prev,

.p-tabs_nav-next{

display: block;

}

}

}

.p-tabs_nav-scroll{

overflow: hidden;

}

.p-tabs_nav-list{

position: relative;

float: left;

white-space: nowrap;

transition: transform .3s;

}

.p-tabs_nav-item{

display: inline-block;

height: 40px;

line-height: 40px;

padding: 0 20px;

color: #fff;

cursor: pointer;

&.active,

&:hover{

color: #ffb845;

}

&.disabled{

cursor: not-allowed;

color: #aaa;

&:hover{

color: #aaa;

}

}

}

.p-tabs_content{

position: relative;

overflow: hidden;

}

.p-tabs-pane{

color: #fff;

}

}

PTabPane.vue

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);

}

}

PTabNav.vue

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结构

全部技术教学新手教学

*/

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();

}

}

4、使用

main.js

// 引入tabs组件

import tabs from './components/p-tabs';

// 全局注册p-tabs组件

Vue.use(tabs);

页面中使用

用户管理

配置管理

角色管理

定时任务补偿

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。