Files
property-manager-side/wuye-manager-uniapp/components/tabs/tabs.vue
2026-06-09 10:10:43 +08:00

139 lines
2.9 KiB
Vue

<template>
<view class="tabs-wrapper">
<view
class="tabs_box"
:style="{
height: props.height + 'rpx',
lineHeight: props.height + 'rpx'
}"
>
<view
v-for="(item, index) in tabs"
:key="index"
@tap="handleTabs(item)"
class="tabs_items"
:class="{
active: item.value === currentActiveValue,
line: showBottomLine && item.value === currentActiveValue
}"
>
{{ item.label }}
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, computed, watch } from 'vue'
// 标签项类型
interface TabItem {
label: string
value: string | number
}
// Props 类型定义
interface Props {
tabslist?: any[]
height?: number
showBottomLine?: boolean
// 配置:传入字段名
options?: {
label: string
value: string
}
activeValue?: string | number
}
// 定义 Props
const props = withDefaults(defineProps<Props>(), {
tabslist: () => [],
height: 60,
showBottomLine: true,
options: () => ({ label: 'name', value: 'value' }),
activeValue: null
})
// ======================
// 格式化 tabs 数据(核心)
// ======================
const tabs = computed<TabItem[]>(() => {
const { tabslist, options } = props
if (!tabslist || tabslist.length === 0) return []
return tabslist.map(item => ({
label: item[options.label],
value: item[options.value]
}))
})
// ======================
// 当前激活值(响应式 + 可同步父组件更新)
// ======================
const currentActiveValue = ref<string | number>('')
// 初始化 + 父组件传值变化时自动同步(核心修复)
watch(
() => props.activeValue,
newVal => {
if (newVal !== null && newVal !== undefined) {
currentActiveValue.value = newVal
} else {
// 没有传默认值 → 选中第一个
currentActiveValue.value = tabs.value[0]?.value
}
},
{ immediate: true } // 一进页面就执行
)
// 事件
const emit = defineEmits<{
update: [value: string | number]
}>()
// 点击切换
const handleTabs = (item: TabItem) => {
currentActiveValue.value = item.value
emit('update', item.value)
}
</script>
<style lang="scss" scoped>
.tabs-wrapper {
flex-shrink: 0; /* 关键:不让被压缩 */
}
.tabs_box {
height: 80rpx;
line-height: 80rpx;
width: 100%;
display: flex;
align-items: center;
overflow-y: hidden;
overflow-x: auto;
.tabs_items {
flex-shrink: 0;
margin-right: 60rpx;
font-size: 28rpx;
color: #222222;
position: relative;
&.active {
color: #4d5dfa;
&.line {
&::after {
content: '';
display: block;
width: 100%;
height: 8rpx;
background: linear-gradient(180deg, #4d5dfa 0%, #bac0fa 100%);
border-radius: 4rpx;
position: absolute;
left: 0;
bottom: 0;
}
}
}
}
}
</style>