123 lines
2.4 KiB
Vue
123 lines
2.4 KiB
Vue
<script setup>
|
|
import { ref, computed } from 'vue'
|
|
|
|
const props = defineProps({
|
|
options: Array,
|
|
modelValue: [String, Number]
|
|
})
|
|
|
|
const emit = defineEmits(['update:modelValue'])
|
|
|
|
const isOpen = ref(false)
|
|
const selectedLabel = computed(() =>
|
|
props.options.find(opt => opt.value === props.modelValue)?.label || '请选择'
|
|
)
|
|
|
|
const toggleDropdown = () => {
|
|
isOpen.value = !isOpen.value
|
|
}
|
|
|
|
const selectOption = (option) => {
|
|
emit('update:modelValue', option.value)
|
|
isOpen.value = false
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="custom-select">
|
|
<div
|
|
class="select-header"
|
|
:class="{ 'select-active': isOpen }"
|
|
@click="toggleDropdown"
|
|
>
|
|
{{ selectedLabel }}
|
|
<span class="arrow"></span>
|
|
</div>
|
|
|
|
<transition name="slide">
|
|
<div v-show="isOpen" class="select-options">
|
|
<div
|
|
v-for="(option, index) in options"
|
|
:key="index"
|
|
class="select-item"
|
|
@click="selectOption(option)"
|
|
>
|
|
{{ option.label }}
|
|
</div>
|
|
</div>
|
|
</transition>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.custom-select {
|
|
position: relative;
|
|
width: 100%;
|
|
}
|
|
|
|
.select-header {
|
|
height: 56px;
|
|
padding: 0 15px;
|
|
border: 1px solid #B2B3B5;
|
|
border-radius: 10px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
cursor: pointer;
|
|
transition: border-color 0.3s;
|
|
}
|
|
|
|
.select-header:hover {
|
|
border-color: #79BBFF;
|
|
}
|
|
|
|
.select-active {
|
|
border-color: #79BBFF;
|
|
}
|
|
|
|
.arrow {
|
|
width: 24px;
|
|
height: 24px;
|
|
background: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%2366676C' d='M7 10l5 5 5-5z'/%3E%3C/svg%3E");
|
|
transition: transform 0.3s;
|
|
}
|
|
|
|
.select-active .arrow {
|
|
transform: rotate(180deg);
|
|
}
|
|
|
|
.select-options {
|
|
position: absolute;
|
|
top: 60px;
|
|
left: 0;
|
|
right: 0;
|
|
text-align: left;
|
|
background: #fff;
|
|
border: 1px solid #eee;
|
|
border-radius: 8px;
|
|
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
|
z-index: 100;
|
|
}
|
|
|
|
.select-item {
|
|
padding: 12px 16px;
|
|
cursor: pointer;
|
|
transition: background 0.2s;
|
|
}
|
|
|
|
.select-item:hover {
|
|
background: #f5f7fa;
|
|
}
|
|
|
|
.slide-enter-active,
|
|
.slide-leave-active {
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.slide-enter-from,
|
|
.slide-leave-to {
|
|
opacity: 0;
|
|
transform: translateY(-10px);
|
|
}
|
|
</style>
|