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

291 lines
7.1 KiB
Vue

1 year ago
<template>
<view class="tm-segTabs d-inline-block relative ">
<view class="tm-segTabs-wkbody fulled flex-start relative" :class="['pa-'+gutter]"
:style="{width:width>0?width+'rpx':'auto'}">
<view @click="clickItem(index, item)" :class="[
`px-${margin[0]} py-${margin[1]}`,
`text-size-${fontSize}`,
`round-${round}`,
active_index == index
? 'text-weight-b ' + (black_tmeme ? `text-grey-lighten-3 ` : `text-${color_tmeme}`)
: black_tmeme
? `text-grey bk`
: `text-${color}`
]" :id="'tm-segTabs-item-' + index" v-for="(item, index) in listData" :key="index"
class="tm-segTabs-item flex-shrink flex-center" :style="{
width:width_item+'px'
}">
<slot name="default"
:item="{color:color_tmeme, data: item, index: index, isActive: active_index == index }">
{{ returnKeyValue(item) }}
</slot>
</view>
</view>
<view :class="[black_tmeme ? 'grey-darken-5' : bgColor, `round-${round}`]"
class="tm-segTabs-bg absolute l-0 t-0 fulled " :style="{ height: body_height + 'px' }">
<view
:class="[`shadow-${activeColor}-${shadow}`,black_tmeme ? 'grey-darken-3' : activeColor, `round-${round}`,aniOn?'aniOn':'',`mt-${gutter} ml-${gutter/2}`]"
class="tm-segTabs-bg-bar relative"
:style="{ width: `${active_barWidth}px`, height: `${active_barHeight}px`,transform: `translateX(${left}px)` }">
</view>
</view>
</view>
</template>
<script>
/**
* 分段器选项卡
* @property {Number} value = [] 默认0,当前激活的选项.
* @property {Array} list = [] 默认数据,对象数组或者字符串数组
* @property {String} rang-key = [] 默认text,list对象数组时取文本的字段名称
* @property {String} color = [] 默认black,默认的文字颜色
* @property {String} bg-color = [] 默认grey-lighten-4,默认的背景色
* @property {String} active-font-color = [] 默认black,激活的文本色
* @property {String} active-color = [] 默认black,激活项的背景色
* @property {String} font-size = [] 默认 n,字号xxs,xs,s,n,g,lg,xl
* @property {Array} margin = [] 默认 [24,10],左右和上下的间距调整它可以控制宽度和高度
* @property {Number} round = [] 默认4 圆角
* @property {Number} shadow = [] 默认4 投影
* @property {Number} gutter = [] 默认4 四边的间隙
* @property {Number} width = [] 默认0 整体的宽度默认不自动宽度提供了后项目内的宽度为均分此宽度
* @property {Boolean} black = [] 默认false 是否暗黑模式
* @property {Boolean} fllow-theme = [] 默认true 是否跟随主题切换主色
*/
export default {
name: 'tm-segTabs',
props: {
value: {
type: Number,
defalut: 0
},
list: {
type: Array,
default: () => []
},
//整体的宽度,不设置使用默认计算的宽度。
width: {
type: Number,
default: 0
},
// 四周的间隙
gutter: {
type: Number,
default: 4
},
shadow: {
type: Number,
default: 4
},
margin: {
type: Array,
default: () => [24, 10]
},
rangKey: {
type: String,
default: 'text'
},
color: {
type: String,
default: 'black'
},
bgColor: {
type: String,
default: 'grey-lighten-4'
},
activeFontColor: {
type: String,
default: 'black'
},
activeColor: {
type: String,
default: 'white'
},
fontSize: {
type: String,
default: 'n'
},
round: {
type: String | Number,
default: 4
},
// 跟随主题色的改变而改变。
fllowTheme: {
type: Boolean | String,
default: true
},
black: {
type: Boolean | String,
default: null
}
},
data() {
return {
body_height: 0,
active_barHeight: 0,
active_barWidth: 0,
aniOn: false,
left: 0,
preventLeft: 0,
width_item_w: 0,
};
},
watch: {
value(newValue, oldValue) {
this.active_index = newValue;
},
list: {
deep: true,
async handler() {
this.width_item = this.width;
await this.setInits();
}
}
},
computed: {
width_item: {
get: function() {
return this.width_item_w;
},
set: function(val) {
if (val == 0) {
this.width_item_w = 'auto'
} else {
this.width_item_w = (uni.upx2px(val) / this.list.length)
}
}
},
black_tmeme: function() {
if (this.black !== null) return this.black;
return this.$tm.vx.state().tmVuetify.black;
},
color_tmeme: function() {
if (this.$tm.vx.state().tmVuetify.color !== null && this.$tm.vx.state().tmVuetify.color && this
.fllowTheme) {
return this.$tm.vx.state().tmVuetify.color;
}
return this.activeFontColor;
},
listData: function() {
return this.list;
},
active_index: {
get: function() {
return this.value;
},
set: function(val) {
this.active = val;
this.$emit('input', val);
this.$emit('update:value', val);
// this.$emit('change', val);
this.$nextTick(function() {
this.setDefaultPos();
});
}
}
},
created() {
this.active_index = this.value;
this.width_item = this.width;
},
mounted() {
let t = this;
t.setInits();
},
updated() {
this.setInits();
},
methods: {
setInits() {
let t = this;
this.width_item = this.width;
this.$nextTick(function() {
uni.createSelectorQuery().in(t).select('.tm-segTabs-wkbody')
.boundingClientRect().select('#tm-segTabs-item-' + this.active).boundingClientRect()
.exec(function(tx) {
let p = tx[0]
if (!p) return;
t.body_height = p.height;
t.preventLeft = p.left;
let p1 = tx[1]
if (!p1) return;
t.active_barHeight = p1.height;
let left = 0;
if (t.width == 0) {
t.active_barWidth = p1.width;
left = p1.left;
} else {
t.active_barWidth = t.width_item
left = t.preventLeft + t.width_item * t.active;
}
let lsl = Math.floor((t.gutter / 2))
t.left = left - t.preventLeft - uni.upx2px(lsl);
t.aniOn = true;
})
});
},
returnKeyValue(item) {
if (typeof item == 'string') {
return item;
}
if (typeof item == 'object') {
return item[this.rangKey];
}
},
setDefaultPos() {
let t = this;
uni.createSelectorQuery().in(t).select('#tm-segTabs-item-' + this.active)
.boundingClientRect().exec(
function(p1) {
if (!p1[0]) return;
t.active_barHeight = p1[0].height;
t.active_barWidth = p1[0].width;
let lsl = Math.floor((t.gutter / 2))
t.left = p1[0].left - t.preventLeft - uni.upx2px(lsl);
})
},
clickItem(index, item) {
this.active_index = index;
this.$emit('change', index);
this.$nextTick(function() {
this.setDefaultPos();
});
}
}
};
</script>
<style lang="scss">
.tm-segTabs {
.tm-segTabs-wkbody {
z-index: 2;
.tm-segTabs-item {
transition: all 0.2s linear;
}
}
.tm-segTabs-bg {
.tm-segTabs-bg-bar {
&.aniOn {
transition: all 0.2s ease-in-out;
}
}
box-shadow: 0 0 3px 2px rgba(0, 0, 0, 0.02) inset;
}
}
</style>