120 lines
2.5 KiB
Vue
120 lines
2.5 KiB
Vue
<template>
|
||
<div class="tab_wrap">
|
||
<div class="item_wrap" :style="{ gap: `${gap}px` }">
|
||
<div class="item" :ref="el => itemRefs[index] = el" v-for="(item, index) in props.list" :key="index"
|
||
:class="{ modelValue: modelValue == index }" @click="changeHandle(index)">
|
||
<span>{{ item.label }}</span>
|
||
</div>
|
||
<div class="active_wrap" :style="{ '--left': `${leftValue}px`, '--width': `${itemsWidth[modelValue]}px` }">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, onMounted, nextTick, watch } from 'vue'
|
||
|
||
const props = defineProps({
|
||
// {label: '设置',value: 1}
|
||
list: {
|
||
type: Array,
|
||
default: []
|
||
}
|
||
})
|
||
|
||
const emits = defineEmits(['update:modelValue', 'change'])
|
||
|
||
const modelValue = defineModel('modelValue', {
|
||
type: [String, Number],
|
||
required: true
|
||
})
|
||
|
||
// 更新active_wrap位置
|
||
function updateActivePosition(index) {
|
||
let left = 0
|
||
itemsWidth.value.forEach((val, i) => {
|
||
if (i < index) {
|
||
left += val
|
||
}
|
||
})
|
||
leftValue.value = left + gap.value * index
|
||
}
|
||
|
||
// 改变索引
|
||
function changeHandle(index) {
|
||
modelValue.value = index
|
||
updateActivePosition(index)
|
||
emits('change', index)
|
||
}
|
||
|
||
const itemRefs = ref([])
|
||
const itemsWidth = ref([])
|
||
const gap = ref(24)
|
||
const leftValue = ref(0)
|
||
|
||
onMounted(() => {
|
||
nextTick(() => {
|
||
itemRefs.value.forEach((el, index) => {
|
||
itemsWidth.value.push(el.offsetWidth)
|
||
})
|
||
console.log('itemRefs===', itemRefs.value);
|
||
console.log('itemsWidth===', itemsWidth.value);
|
||
|
||
updateActivePosition(modelValue.value)
|
||
})
|
||
})
|
||
|
||
// 监听modelValue变化,更新位置
|
||
watch(modelValue, (newVal) => {
|
||
updateActivePosition(newVal)
|
||
})
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.tab_wrap {
|
||
padding: 12px;
|
||
background-color: #F8F8F8;
|
||
border-radius: 2px;
|
||
|
||
.item_wrap {
|
||
position: relative;
|
||
display: flex;
|
||
align-items: center;
|
||
|
||
.item {
|
||
padding: 0 12px;
|
||
height: 36px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
position: relative;
|
||
z-index: 2;
|
||
cursor: pointer;
|
||
|
||
&.modelValue {
|
||
span {
|
||
color: #fff;
|
||
}
|
||
}
|
||
|
||
span {
|
||
font-size: 16px;
|
||
color: #999;
|
||
transition: all .1s ease-in-out .15s;
|
||
}
|
||
}
|
||
|
||
.active_wrap {
|
||
width: var(--width);
|
||
background-color: var(--el-color-primary);
|
||
height: 34px;
|
||
position: absolute;
|
||
top: 0;
|
||
left: var(--left);
|
||
z-index: 1;
|
||
border-radius: 2px;
|
||
transition: all .3s ease-in-out;
|
||
}
|
||
}
|
||
}
|
||
</style> |