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