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.

475 lines
9.8 KiB
Vue

5 months ago
<template>
<view
class="ui-tab"
ref="tabRef"
:id="'tab-' + vm.uid"
:class="[
props.ui,
props.tpl,
props.bg,
props.align,
{ 'ui-tab-inline': props.inline },
{ 'ui-tab-scrolls': props.scroll },
]"
>
<block v-if="scroll">
<view class="ui-tab-scroll-warp">
<scroll-view
scroll-x="true"
class="ui-tab-scroll"
:scroll-left="state.curValue > 1 ? state.tabNodeList[state.curValue - 1].left : 0"
scroll-with-animation
:style="{ width: `${state.content.width}px` }"
>
<view class="ss-flex ss-col-center">
<su-tab-item
v-for="(item, index) in props.tab"
:data="item"
:index="index"
:key="index"
@up="upitem"
@tap.native="click(index, item)"
></su-tab-item>
<view
class="ui-tab-mark-warp"
:class="[{ over: state.over }]"
:style="[{ left: state.markLeft + 'px' }, { width: state.markWidth + 'px' }]"
>
<view
class="ui-tab-mark"
:class="[props.mark, { 'ui-btn': props.tpl == 'btn' || props.tpl == 'subtitle' }]"
:style="[
{
background:
props.tpl == 'btn' || props.tpl == 'subtitle' ? titleStyle.activeBg : 'none',
},
]"
></view>
</view>
</view>
</scroll-view>
</view>
</block>
<block v-else>
<su-tab-item
v-for="(item, index) in props.tab"
:data="item"
:index="index"
:key="index"
@up="upitem"
@tap.native="click(index, item)"
></su-tab-item>
<view
class="ui-tab-mark-warp"
:class="[{ over: state.over }]"
:style="[{ left: state.markLeft + 'px' }, { width: state.markWidth + 'px' }]"
>
<view
class="ui-tab-mark"
:class="[props.mark, { 'ui-btn': props.tpl == 'btn' || props.tpl == 'subtitle' }]"
></view>
</view>
</block>
</view>
</template>
<script>
export default {
name: 'SuTab',
};
</script>
<script setup>
/**
* 基础组件 - suTab
*/
import {
toRef,
ref,
reactive,
unref,
onMounted,
nextTick,
getCurrentInstance,
provide,
} from 'vue';
const vm = getCurrentInstance();
// 数据
const state = reactive({
curValue: 0,
tabNodeList: [],
scrollLeft: 0,
markLeft: 0,
markWidth: 0,
content: {
width: 100,
},
over: false,
});
const tabRef = ref(null);
// 参数
const props = defineProps({
modelValue: {
type: Number,
default: 0,
},
ui: {
type: String,
default: '',
},
bg: {
type: String,
default: '',
},
tab: {
type: Array,
default() {
return [];
},
},
// line dot long,subtitle,trapezoid
tpl: {
type: String,
default: 'line',
},
mark: {
type: String,
default: '',
},
align: {
type: String,
default: '',
},
curColor: {
type: String,
default: 'ui-TC',
},
defaultColor: {
type: String,
default: 'ui-TC',
},
scroll: {
type: Boolean,
default: false,
},
inline: {
type: Boolean,
default: false,
},
titleStyle: {
type: Object,
default: () => ({
activeBg: '#DA2B10',
activeColor: '#FEFEFE',
color: '#D70000',
}),
},
subtitleStyle: {
type: Object,
default: () => ({
activeColor: '#333',
color: '#C42222',
}),
},
});
const emits = defineEmits(['update:modelValue', 'change']);
onMounted(() => {
state.curValue = props.modelValue;
setCurValue(props.modelValue);
nextTick(() => {
computedQuery();
});
uni.onWindowResize((res) => {
computedQuery();
});
});
const computedQuery = () => {
uni.createSelectorQuery()
.in(vm)
.select('#tab-' + vm.uid)
.boundingClientRect((data) => {
if (data != null) {
if (data.left == 0 && data.right == 0) {
// setTimeout(() => {
computedQuery();
// }, 300);
} else {
state.content = data;
setTimeout(() => {
state.over = true;
}, 300);
}
} else {
console.log('tab-' + vm.uid + ' data error');
}
})
.exec();
};
const setCurValue = (value) => {
if (value == state.curValue) return;
state.curValue = value;
computedMark();
};
const click = (index, item) => {
setCurValue(index);
emits('update:modelValue', index);
emits('change', {
index: index,
data: item,
});
};
const upitem = (index, e) => {
state.tabNodeList[index] = e;
if (index == state.curValue) {
computedMark();
}
};
const computedMark = () => {
if (state.tabNodeList.length == 0) return;
let left = 0;
let list = unref(state.tabNodeList);
let cur = state.curValue;
state.markLeft = list[cur].left - state.content.left;
state.markWidth = list[cur].width;
};
const computedScroll = () => {
if (state.curValue == 0 || state.curValue == state.tabNodeList.length - 1) {
return false;
}
let i = 0;
let left = 0;
let list = state.tabNodeList;
for (i in list) {
if (i == state.curValue && i != 0) {
left = left - list[i - 1].width;
break;
}
left = left + list[i].width;
}
state.scrollLeft = left;
};
provide('suTabProvide', {
props,
curValue: toRef(state, 'curValue'),
});
</script>
<style lang="scss">
.ui-tab {
position: relative;
display: flex;
height: 4em;
align-items: center;
&.ui-tab-scrolls {
width: 100%;
/* #ifdef MP-WEIXIN */
padding-bottom: 10px;
/* #endif */
.ui-tab-scroll-warp {
overflow: hidden;
height: inherit;
width: 100%;
.ui-tab-scroll {
position: relative;
display: block;
white-space: nowrap;
overflow: auto;
min-height: 4em;
line-height: 4em;
width: 100% !important;
.ui-tab-mark-warp {
display: flex;
align-items: top;
justify-content: center;
.ui-tab-mark.ui-btn {
/* #ifndef MP-WEIXIN */
height: 2em;
width: calc(100% - 0.6em);
margin-top: 4px;
/* #endif */
/* #ifdef MP-WEIXIN */
height: 2em;
width: calc(100% - 0.6em);
margin-top: 4px;
/* #endif */
}
}
}
}
}
.ui-tab-mark-warp {
color: inherit;
position: absolute;
top: 0;
height: 100%;
z-index: 0;
&.over {
transition: 0.3s;
}
.ui-tab-mark {
color: var(--ui-BG-Main);
height: 100%;
}
}
&.line {
.ui-tab-mark {
border-bottom: 2px solid currentColor;
}
}
&.topline {
.ui-tab-mark {
border-top: 2px solid currentColor;
}
}
&.dot {
.ui-tab-mark::after {
content: '';
width: 0.5em;
height: 0.5em;
background-color: currentColor;
border-radius: 50%;
display: block;
position: absolute;
bottom: 0.3em;
left: 0;
right: 0;
margin: auto;
}
}
&.long {
.ui-tab-mark::after {
content: '';
width: 2em;
height: 0.35em;
background-color: currentColor;
border-radius: 5em;
display: block;
position: absolute;
bottom: 0.3em;
left: 0;
right: 0;
margin: auto;
}
}
&.trapezoid {
.ui-tab-mark::after {
content: '';
width: calc(100% - 2em);
height: 0.35em;
background-color: currentColor;
border-radius: 5em 5em 0 0;
display: block;
position: absolute;
bottom: 0;
left: 0;
right: 0;
margin: auto;
}
}
&.btn {
.ui-tab-mark-warp {
display: flex;
align-items: center;
justify-content: center;
.ui-tab-mark.ui-btn {
height: calc(100% - 1.6em);
width: calc(100% - 0.6em);
}
}
&.sm .ui-tab-mark.ui-btn {
height: calc(100% - 2px);
width: calc(100% - 2px);
border-radius: #{$radius - 2};
}
}
&.subtitle {
.ui-tab-mark-warp {
display: flex;
align-items: top;
justify-content: center;
padding-top: 0.6em;
.ui-tab-mark.ui-btn {
height: calc(100% - 2.8em);
width: calc(100% - 0.6em);
}
}
}
&.ui-tab-inline {
display: inline-flex;
height: 3.5em;
&.ui-tab-scrolls {
.ui-tab-scroll {
height: calc(3.5em + 17px);
line-height: 3.5em;
.ui-tab-mark-warp {
height: 3.5em;
}
}
}
&.btn {
.ui-tab-mark-warp {
.ui-tab-mark.ui-btn {
height: calc(100% - 10px);
width: calc(100% - 10px);
}
}
}
}
&.sm {
height: 70rpx !important;
&.ui-tab-inline {
height: 70rpx;
&.ui-tab-scrolls {
.ui-tab-scroll {
height: calc(70rpx + 17px);
line-height: 70rpx;
.ui-tab-mark-warp {
height: 70rpx;
}
}
}
&.btn .ui-tab-mark.ui-btn {
height: calc(100% - 2px);
width: calc(100% - 2px);
border-radius: #{$radius - 2};
}
}
}
}
</style>