<template> <scroll-view scroll-x="true" scroll-with-animation :scroll-left="scrollLeft" ref="tabsscroll" class="colpu__tabs-header" :style="{ backgroundColor: backgroundColor, }" > <view class="colpu__tabs-header-scroll" :style="styles"> <template v-for="(item, idx) in dataList" :key="idx"> <view class="colpu__tabs-header-item" :class="{ active: selectIndex === idx }" @tap="selectHanddle(idx)" > {{ item[keyName] }} </view> </template> </view> <view v-if="showHover" class="colpu__tabs-header-hover" :style="{ left: hoverDetail.left + 'px', width: hoverDetail.width + 'px', borderRadius: borderRadius, bottom: bottom + 'px', }" ></view> </scroll-view> </template> <script setup> /** * tab最大支持10个,首scroll-view限制,会导致计算错误 */ import { getCurrentInstance, onMounted, ref, watch } from "vue"; const scrollLeft = ref(0); const hoverDetail = ref({ left: 0, width: 0 }); const tabListDetail = ref([]); const selectIndex = ref(0); const tabsDetail = ref({ left: 0, width: 0 }); const props = defineProps({ dataList: { type: Array, default: () => [], }, index: { type: Number, default: 0 }, bottom: { type: Number, default: 0, }, styles: { type: [Object, String], default: () => ({}), }, borderHeight: { type: Number, default: 2, }, borderRadius: { type: String, default: "3px", }, backgroundColor: { type: String, default: "none", }, keyName: { type: String, default: "name", }, showHover: { type: Boolean, default: true, }, }); const emits = defineEmits({ select: null, }); const getSelectQuery = () => { const query = uni.createSelectorQuery().in(getCurrentInstance()); const pr = []; pr.push( new Promise((res) => { query .selectAll(".colpu__tabs-header-item") .boundingClientRect((data) => { tabListDetail.value = data; res(data); }) .exec(); }) ); pr.push( new Promise((res) => { query .selectAll(".colpu__tabs-header") .boundingClientRect((value) => { tabsDetail.value = value[0]; res(value[0]); }) .exec(); }) ); return Promise.all(pr); }; onMounted(async () => { await getSelectQuery(); selectHanddle(props.index, false); }); const selectHanddle = (index, isEmit = true) => { const halfWidth = tabsDetail.value.width / 2; selectIndex.value = index; const data = tabListDetail.value[index] || {}; let { left = 0, width = 0 } = data; if (halfWidth > left) { scrollLeft.value = 0; } else { scrollLeft.value = left - halfWidth + width / 2; } hoverDetail.value.left = left; hoverDetail.value.width = width - 10; isEmit && emits("select", { index, data: props.dataList[index] }); }; defineExpose({ switchTab: (index) => { selectHanddle(index); }, }); watch( () => props.dataList, (value, ov) => { selectIndex.value = props.index; if (props.index !== 0) { selectHanddle(props.index, false); } } ); </script> <style lang="scss"> $color-primary: #001d4f; $color-tips: #999; .colpu__tabs-header { position: relative; flex: 1; $h: 48rpx; &-scroll { display: flex; align-items: center; flex-wrap: nowrap; align-content: stretch; justify-content: space-around; height: $h; .active { color: $color-primary; font-weight: 800; } } &-hover { $hoverH: 4rpx; position: absolute; width: 0; height: $hoverH; top: $h - $hoverH; border-radius: $hoverH; background-color: $color-primary; bottom: 0; transition: all 0.3s ease; } &-item { position: relative; margin: 0 10px; flex-shrink: 0; display: flex; justify-content: space-between; font-size: 28rpx; color: $color-tips; } } </style>