119 lines
2.6 KiB
Vue
119 lines
2.6 KiB
Vue
<template>
|
|
<view class="check-seg">
|
|
<view
|
|
class="check-seg__item"
|
|
:class="{ 'check-seg__item--active': isPass, 'is-active-anim': isPass && animKey === 'pass' }"
|
|
@click="select(passKey)"
|
|
>
|
|
<text class="check-seg__icon">{{ '\u2713' }}</text>
|
|
<text class="check-seg__text">{{ passText }}</text>
|
|
</view>
|
|
<view
|
|
class="check-seg__item"
|
|
:class="{ 'check-seg__item--active': isFail, 'is-active-anim': isFail && animKey === 'fail' }"
|
|
@click="select(failKey)"
|
|
>
|
|
<text class="check-seg__icon">{{ '\u2717' }}</text>
|
|
<text class="check-seg__text">{{ failText }}</text>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { computed, ref, watch, nextTick } from 'vue'
|
|
|
|
const props = defineProps({
|
|
modelValue: { type: [Boolean, Object], default: null },
|
|
passKey: { type: String, default: 'pass' },
|
|
failKey: { type: String, default: 'fail' },
|
|
passText: { type: String, default: '通过' },
|
|
failText: { type: String, default: '不通过' }
|
|
})
|
|
|
|
const emit = defineEmits(['update:modelValue'])
|
|
|
|
const isPass = computed(() => props.modelValue === props.passKey)
|
|
const isFail = computed(() => props.modelValue === props.failKey)
|
|
|
|
const animKey = ref('')
|
|
|
|
watch(() => props.modelValue, async () => {
|
|
animKey.value = ''
|
|
await nextTick()
|
|
animKey.value = isPass.value ? 'pass' : 'fail'
|
|
})
|
|
|
|
const select = (key) => {
|
|
emit('update:modelValue', key)
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.check-seg {
|
|
display: inline-flex;
|
|
border-radius: 12rpx;
|
|
overflow: hidden;
|
|
border: 1rpx solid #e5e7eb;
|
|
|
|
&__item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6rpx;
|
|
padding: 12rpx 24rpx;
|
|
transition: all 0.25s ease;
|
|
background: #f9fafb;
|
|
|
|
&:first-child {
|
|
border-right: 1rpx solid #e5e7eb;
|
|
}
|
|
|
|
&:active {
|
|
transform: scale(0.96);
|
|
}
|
|
|
|
&--active {
|
|
background: #ebf9f0;
|
|
|
|
.check-seg__icon { color: #22c55e; }
|
|
.check-seg__text { color: #16a34a; font-weight: 600; }
|
|
|
|
&:first-child {
|
|
border-right-color: #b7ebc6;
|
|
}
|
|
}
|
|
|
|
&--active:not(:first-child) {
|
|
background: #fef2f2;
|
|
|
|
.check-seg__icon { color: #ef4444; }
|
|
.check-seg__text { color: #dc2626; font-weight: 600; }
|
|
}
|
|
}
|
|
|
|
&__icon {
|
|
font-size: 24rpx;
|
|
color: #9ca3af;
|
|
font-weight: 700;
|
|
transition: color 0.25s ease;
|
|
}
|
|
|
|
&__text {
|
|
font-size: 24rpx;
|
|
color: #6b7280;
|
|
font-weight: 500;
|
|
transition: color 0.25s ease;
|
|
}
|
|
}
|
|
|
|
// ===== 激活动画(双选分段) =====
|
|
@keyframes popIn {
|
|
0% { transform: scale(0.5); opacity: 0; }
|
|
60% { transform: scale(1.15); }
|
|
100% { transform: scale(1); opacity: 1; }
|
|
}
|
|
|
|
.check-seg__item.is-active-anim .check-seg__icon {
|
|
animation: popIn 0.3s ease-out;
|
|
}
|
|
</style>
|