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