You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

334 lines
7.0 KiB

<template>
<view class="niu-x-tabs component" :style="{opacity: isShow?1:0}" multiSlots>
<!-- <view class="scroll-view"> -->
<scroll-view :show-scrollbar="false" class="scroll-view" :scroll-x='!full' :scroll-with-animation='anim' :scroll-left="left">
<view class="niu-x-tabs__wrapper">
<view class="niu-x-tabs__inner" :class="{full,capsule}" :style="{backgroundColor: bg, height: height}">
<view class="niu-x-tabs__item" :class="[
value==index?'niu-x-tabs__active':'',capsule?'capsule':'',
value==index?activeClass:normalClass
]"
:style="[value==index?activeCustomStyle:normalCustomStyle, anim?{transition: '.3s linear'}:{}]" :key="index" v-for="(item,index) in list" @click="click(index,true)">
<!-- 循环中使用slot会报警告,暂时去除,未找到处理办法 -->
<!-- <slot :data="item" :pData="pData" :index="index"> -->
<text>{{item[akey]}}</text>
<view v-if="value==index && activeImage" class="niu-x-tabs__bg">
<image class="image" :src="activeImage" mode="widthFix"></image>
</view>
<!-- </slot> -->
</view>
<!-- {{ !!(value<list.length) }} -->
<slot name="bar"
:show="barIsShow"
:anim="anim"
:barLeft="barLeft"
:barWidth="barWidth"
:item="list[value]"
:props="{normalColor, activeColor}"
>
<view v-if="bar&&value<list.length" class="niu-x-tabs__bar" :class="{capsule}" :style="[{left:barLeft + 'px',width:barWidth + 'px',backgroundColor: barColor},, anim?{transition: 'left .3s ease, width .3s ease'}:{}]"></view>
</slot>
</view>
</view>
</scroll-view>
<!-- </view> -->
</view>
</template>
<script>
export default {
props: {
list: {
type: Array,
default: () => []
},
pData: {
type: Object|Array,
default: () => {}
},
akey: {
type: String,
default: 'text'
},
width: {
type: String,
default: ''
},
height: {
type: String,
default: '80rpx'
},
value: {
type: Number,
default: 0
},
full: {
type: Boolean,
default: false
},
capsule: {
type: Boolean,
default: false
},
normalColor: {
type: String,
default: '#B3B3B3'
},
activeColor: {
type: String,
default: '#39b54a'
},
capsuleActiveColor: {
type: String,
default: '#FFFFFF'
},
normalClass: {
type: String,
default: null
},
activeImage: {
type: String,
default: ''
},
activeClass: {
type: String,
default: null
},
normalStyle: {
type: Object,
default: null
},
activeStyle: {
type: Object,
default: null
},
bar: {
type: Boolean,
default: true
},
barPer: {
type: Number,
default: 1
},
barColor: {
type: String,
default: '#39b54a'
},
bg: {
type: String,
default: ''
}
},
data() {
return {
anim: false,
left: 0,
isRender: false,
isShow: false,
allLeft: [],
barLeft: 0,
barWidth: 0,
x_ml: 0 // 容器距离左侧屏幕的距离
};
},
computed: {
barIsShow(){
return !!(this.value<this.list.length)
},
activeCustomStyle() {
let customStyle = this.activeStyle ? this.activeStyle : {};
return {
color: this.capsule?this.capsuleActiveColor:this.activeColor,
width: this.width,
...customStyle
}
},
normalCustomStyle() {
let customStyle = this.normalStyle ? this.normalStyle : {};
return {
color: this.normalColor,
width: this.width,
...customStyle
}
}
},
mounted() {
this.init(this.value)
},
watch: {
list(newValue, oldValue) {
this.anim = false
this.init(this.value)
},
value(newValue, oldValue) {
if (this.isRender) return
if(newValue>=this.list.length) return
this.clickHere(newValue)
},
barPer(){
if (this.isRender) return
if(this.value>=this.list.length) return
this.clickHere(this.value)
},
},
methods: {
init(index = 0) {
this.isRender = true
this.getRect('.niu-x-tabs__inner').then(res => {
this.x_ml = res.left;
})
this.allLeft = []
let p = uni.upx2px(50)
this.getAllReact('.niu-x-tabs__inner .niu-x-tabs__item').then(res => {
this.allLeft = res.map(v => {
return [v.left - this.x_ml, v.width]
})
this.isRender = false
if(index < this.list.length){
this.clickHere(index)
this.left = index != 0 ? (this.allLeft[index][0] - this.allLeft[index][1] * 2) : 0;
}
this.isShow = true
setTimeout(()=>{
this.anim = true
},300)
})
},
click(index) {
this.$emit("input", index)
},
clickHere(index, isClick) {
if (isClick && this.value == index) return
if (this.allLeft.length) {
this.$emit("change", index)
this.barWidth = this.allLeft[index][1] * this.barPer
this.barLeft = this.allLeft[index][0] + (this.allLeft[index][1] - this.barWidth)/2
this.left = (this.allLeft[index][0] - this.allLeft[index][1] * 2)
}
},
getAllReact(selector) {
return new Promise(resolve => {
this.$nextTick(() => {
let query = uni.createSelectorQuery()
.in(this)
.selectAll(selector);
query.boundingClientRect(res => {
resolve(res)
}).exec()
})
})
},
getRect(selector) {
return new Promise(resolve => {
this.$nextTick(() => {
let query = uni.createSelectorQuery()
.in(this)
.select(selector);
query.boundingClientRect(res => {
resolve(res)
}).exec()
})
})
}
}
}
</script>
<style lang="scss" scoped>
$themeColor: #39b54a;
$capsuleColor: #ffffff;
$bg: #F0F0F0;
::-webkit-scrollbar {
width: 0;
height: 0;
color: transparent;
}
.niu-x-tabs.component {
position: relative;
background-color: transparent;
.niu-x-tabs__wrapper {
// width: 100%;
// overflow: auto;
}
.niu-x-tabs__inner {
display: inline-flex;
white-space: nowrap;
position: relative;
&.capsule {
overflow: hidden;
background-color: $bg;
}
&.full {
display: flex;
justify-content: space-around;
.niu-x-tabs__item {
flex: 1;
width: 0;
text-align: center;
}
}
// float: left;
.niu-x-tabs__item {
padding: 0 20rpx;
position: relative;
z-index: 1;
display: flex;
align-items: center;
justify-content: center;
&.niu-x-tabs__active {
color: $themeColor;
}
&.capsule {
margin: 0;
padding: 0 20rpx;
&.niu-x-tabs__active {
color: $capsuleColor;
}
}
.niu-x-tabs__bg {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
display: flex;
.image {
margin: auto;
width: 100%;
}
}
}
.niu-x-tabs__bar {
position: absolute;
bottom: 0;
left: 0;
width: 0;
height: 2px;
background-color: $themeColor;
&.capsule {
bottom: auto;
top: 50%;
transform: translateY(-50%);
z-index: 0;
height:65%;
border-radius: 50rpx;
}
}
}
}
</style>