291 lines
7.1 KiB
Vue
291 lines
7.1 KiB
Vue
<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>
|