Files
qiming/qiming-mobile/components/segmented-control/segmented-control.uvue

132 lines
3.0 KiB
Plaintext

<template>
<view class="segmented-control">
<view
v-for="(item, index) in options"
:key="index"
class="segment-item"
:class="{ 'active': selectedIndex === index }"
@click="handleSelect(index)"
>
<text class="segment-text" :class="{ 'active-text': selectedIndex === index }">
{{ displayLabel(item.label) }}
</text>
</view>
</view>
</template>
<script setup lang="uts">
import { translateText } from "@/utils/i18n";
interface SegmentOption {
label: string
value: string | number
}
interface Props {
options: SegmentOption[]
modelValue?: string | number
defaultIndex?: number
}
const props = withDefaults(defineProps<Props>(), {
defaultIndex: 0
})
const emit = defineEmits<{
'update:modelValue': [value: string | number]
'change': [value: string | number, index: number]
}>()
const selectedIndex = ref<number>(props.defaultIndex)
// 监听外部传入的 modelValue 变化
watch(() => props.modelValue, (newValue) => {
if (newValue !== undefined) {
const index = props.options.findIndex(option => option.value === newValue)
if (index !== -1) {
selectedIndex.value = index
}
}
}, { immediate: true })
const handleSelect = (index: number) => {
selectedIndex.value = index
const selectedOption = props.options[index]
emit('update:modelValue', selectedOption.value)
emit('change', selectedOption.value, index)
}
/**
* 统一兼容 key 和旧的字面量文案,减少调用方迁移成本。
*/
const displayLabel = (label: string): string => {
return translateText(label)
}
// 初始化时发送默认值
onMounted(() => {
if (props.modelValue === undefined && props.options.length > 0) {
const defaultOption = props.options[props.defaultIndex]
emit('update:modelValue', defaultOption.value)
emit('change', defaultOption.value, props.defaultIndex)
}
})
</script>
<style scoped lang="scss">
.segmented-control {
display: flex;
flex-direction: row;
background-color: #f5f5f5;
border-radius: 12rpx;
padding: 6rpx;
position: relative;
z-index: 1;
}
.segment-item {
flex: 1;
padding: 16rpx 24rpx;
text-align: center;
border-radius: 8rpx;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
z-index: 2;
cursor: pointer;
margin-right: 8rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
&:last-child {
margin-right: 0;
}
&:hover {
background-color: rgba(255, 255, 255, 0.5);
}
&.active {
background-color: #ffffff;
z-index: 3;
}
}
.segment-text {
text-align: center;
font-size: 28rpx;
color: #666666;
font-weight: 400;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
line-height: 44rpx;
z-index: 4;
&.active-text {
color: #5147FF;
font-weight: 600;
text-shadow: 0 1rpx 2rpx rgba(81, 71, 255, 0.1);
}
}
</style>