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.
dandelionPlatformToB-uni-v3/components/custom-tabs.vue

150 lines
4.4 KiB
Vue

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<!-- 横向滚动容器恢复scroll-into-view绑定动态变量targetScrollId移除ref -->
<scroll-view class="tabs-scroll-container" scroll-x="true" scroll-with-animation :show-scrollbar="false" :scroll-into-view="targetScrollId">
<view class="tabs-wrapper" :style="{ display: noScroll ? 'flex' : 'inline-block' }">
<!-- 遍历tab列表保留tab-${index}的id用于scroll-into-view定位 -->
<!-- 核心与scroll-into-view绑定的id -->
<view v-for="(item, index) in list" :key="index" :id="`tab-${index}`" hover-class="none" active-class="none" :style="{ flex: noScroll ? '1' : '' }" :class="['tab-item', `tab-item-${index}`, { active: activeIndex === index }]" @click="handleTabClick(index)">
<span class="tab-text" :style="getTabTextStyle(index)">{{ item.name }}</span>
<view class="tab-active-line" v-show="activeIndex === index" :style="getActiveLineStyle()"></view>
</view>
</view>
</scroll-view>
</template>
<script>
export default {
name: "ScrollableTabs",
props: {
list: { type: Array, default: () => [] },
noScroll: { type: Boolean, default: true },
current: { type: Number, default: 0 },
activeColor: { type: String, default: getApp().globalData.themeColor },
fontSize: { type: [String, Number], default: 32 },
activeFontWeight: { type: [String, Number], default: 600 },
normalColor: { type: String, default: "#333333" },
bgColor: { type: String, default: "#ffffff" },
},
data() {
return {
// activeIndex: this.current, // 改用computed计算属性
// targetScrollId: `tab-${this.current}`, // 核心变量控制scroll-into-view的目标id(改用computed计算属性)
};
},
computed: {
activeIndex() {
return this.current;
},
targetScrollId() {
return `tab-${this.current}`;
},
},
methods: {
// 点击tab切换核心通过变量赋值控制滚动
handleTabClick(index) {
if (this.activeIndex === index) return;
this.activeIndex = index;
this.$emit("change", index);
// 计算要滚动到的目标id让相邻tab显示到可视区
this.calcTargetScrollId(index);
},
/**
* 计算scroll-into-view的目标id完全基于索引无节点查询
* @param {Number} currentIndex - 点击的tab索引
*/
calcTargetScrollId(currentIndex) {
const listLen = this.list.length;
if (listLen <= 1) return;
let targetIndex = currentIndex;
// 情况1点击第一个tab → 滚动到第一个,确保第二个显示
if (currentIndex === 0) {
targetIndex = 0;
}
// 情况2点击最后一个tab → 滚动到倒数第二个,确保倒数第二个显示
else if (currentIndex === listLen - 1) {
targetIndex = listLen - 2;
}
// 情况3点击中间tab → 滚动到当前tab的前一个确保上一个和下一个都显示
else {
// 优先滚动到当前tab的前一个让当前+下一个都在可视区
targetIndex = currentIndex - 1;
// 兜底:确保目标索引不越界
targetIndex = Math.max(0, Math.min(targetIndex, listLen - 1));
}
// 变量赋值控制scroll-into-view滚动到目标tab
this.targetScrollId = `tab-${targetIndex}`;
},
// 动态生成tab文字样式
getTabTextStyle(index) {
const baseStyle = {
fontSize: `${this.fontSize}rpx`,
fontWeight: 400,
transition: "all 0.2s ease",
};
return this.activeIndex === index ? { ...baseStyle, color: this.activeColor, fontWeight: this.activeFontWeight } : { ...baseStyle, color: this.normalColor };
},
// 动态生成active底部横条样式
getActiveLineStyle() {
return {
backgroundColor: this.activeColor,
transition: "all 0.2s ease",
};
},
},
};
</script>
<style lang="scss" scoped>
/* 滚动容器:保留原有样式,仅移除无用注释 */
.tabs-scroll-container {
width: 100%;
white-space: nowrap;
height: 88rpx;
box-sizing: border-box;
background-color: v-bind(bgColor);
}
.tabs-wrapper {
width: 100%;
padding: 0;
}
.tab-item {
display: inline-block;
position: relative;
padding: 0 20rpx;
height: 88rpx;
line-height: 88rpx;
text-align: center;
box-sizing: border-box;
cursor: default;
-webkit-tap-highlight-color: transparent;
tap-highlight-color: transparent;
user-select: none;
}
.tab-text {
display: inline-block;
}
.tab-active-line {
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 40rpx;
height: 6rpx;
border-radius: 3rpx;
}
::-webkit-scrollbar {
display: none;
}
</style>