You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
330 lines
6.9 KiB
330 lines
6.9 KiB
<template>
|
|
<div class="dropdown" ref="dropdownRef">
|
|
<!-- 下拉框触发器 -->
|
|
<div class="dropdown-toggle" @click="toggleMenu" :class="{ 'active': isOpen }">
|
|
<span class="placeholder" :style="{ color: selectedItem ? '#333' : '#A8ABB2' }">
|
|
{{ selectedItem || placeholder }}
|
|
</span>
|
|
<span class="arrow">
|
|
<el-icon>
|
|
<ArrowDown />
|
|
</el-icon>
|
|
</span>
|
|
</div>
|
|
|
|
<!-- 下拉菜单 -->
|
|
<div class="dropdown-menu" v-if="isOpen">
|
|
<!-- 搜索框 -->
|
|
<div class="search">
|
|
<input type="text" v-model="searchData" class="search-input" placeholder="查询" @focus="handleSearchFocus"
|
|
@blur="handleSearchBlur">
|
|
<el-icon class="search-icon">
|
|
<Search />
|
|
</el-icon>
|
|
<el-icon class="clear-icon" v-if="searchData" @click="clearSearch">
|
|
<CircleClose />
|
|
</el-icon>
|
|
</div>
|
|
|
|
<!-- 选项区域:按钮样式 -->
|
|
<div class="menuContent">
|
|
<button class="dropdown-item" v-for="(item, index) in filteredItems" :key="index" @click="handleSelect(item)"
|
|
:class="{ 'selected': selectedItem === item }">
|
|
{{ item }}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, computed, watchEffect, onMounted } from 'vue';
|
|
|
|
const searchData = ref('')
|
|
const isOpen = ref(false)
|
|
const selectedItem = ref('')
|
|
const dropdownRef = ref(null)
|
|
|
|
const props = defineProps({
|
|
items: {
|
|
type: Array,
|
|
required: true,
|
|
default: () => []
|
|
},
|
|
placeholder: {
|
|
type: String,
|
|
default: '请选择支付方式'
|
|
},
|
|
modelValue: {
|
|
type: String,
|
|
default: ''
|
|
}
|
|
})
|
|
|
|
const emit = defineEmits(['update:modelValue', 'change'])
|
|
|
|
// 切换下拉菜单
|
|
const toggleMenu = () => {
|
|
isOpen.value = !isOpen.value
|
|
}
|
|
|
|
// 清除搜索
|
|
const clearSearch = () => {
|
|
searchData.value = ''
|
|
}
|
|
|
|
// 选择选项
|
|
const handleSelect = (item) => {
|
|
selectedItem.value = item
|
|
isOpen.value = false
|
|
emit('update:modelValue', item)
|
|
emit('change', item)
|
|
}
|
|
|
|
// 点击外部关闭菜单
|
|
const handleClickOutside = (event) => {
|
|
if (dropdownRef.value && !dropdownRef.value.contains(event.target)) {
|
|
isOpen.value = false
|
|
}
|
|
}
|
|
|
|
// 监听搜索框的焦点和失焦事件
|
|
const handleSearchFocus = () => {
|
|
// 可以在这里添加额外的逻辑
|
|
}
|
|
|
|
const handleSearchBlur = () => {
|
|
// 可以在这里添加额外的逻辑
|
|
}
|
|
|
|
// 搜索过滤
|
|
const filteredItems = computed(() => {
|
|
if (!searchData.value) return props.items
|
|
return props.items.filter(item =>
|
|
item.toLowerCase().includes(searchData.value.toLowerCase())
|
|
)
|
|
})
|
|
|
|
// 挂载/卸载事件
|
|
onMounted(() => {
|
|
document.addEventListener('click', handleClickOutside)
|
|
return () => {
|
|
document.removeEventListener('click', handleClickOutside)
|
|
}
|
|
})
|
|
|
|
// 监听外部值变化
|
|
watchEffect(() => {
|
|
selectedItem.value = props.modelValue
|
|
})
|
|
</script>
|
|
<style scoped lang="scss">
|
|
// 下拉容器
|
|
.dropdown {
|
|
position: relative;
|
|
width: 268px;
|
|
font-family: 'Arial', sans-serif;
|
|
}
|
|
|
|
// 触发器:控制展开/收起
|
|
.dropdown-toggle {
|
|
border: 1px solid #e5e7eb;
|
|
padding: 4px 12px;
|
|
/* 调整内边距以匹配按钮高度 */
|
|
height: 23px;
|
|
/* 调整高度以匹配按钮 */
|
|
cursor: pointer;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
background-color: #fff;
|
|
border-radius: 6px;
|
|
transition: all 0.3s ease;
|
|
|
|
.placeholder {
|
|
flex: 1;
|
|
font-size: 14px;
|
|
line-height: 18px;
|
|
color: #A8ABB2;
|
|
}
|
|
}
|
|
|
|
// 展开状态:边框+阴影高亮
|
|
.dropdown-toggle.active {
|
|
border-color: #678BFF;
|
|
box-shadow: 0 0 0 2px rgba(103, 139, 255, 0.1);
|
|
}
|
|
|
|
// 箭头图标:展开时旋转
|
|
.arrow {
|
|
margin-left: 8px;
|
|
color: #999;
|
|
transition: transform 0.3s ease;
|
|
}
|
|
|
|
.dropdown-toggle.active .arrow {
|
|
transform: rotate(180deg);
|
|
}
|
|
|
|
// 下拉菜单主体
|
|
.dropdown-menu {
|
|
position: absolute;
|
|
top: 100%; // 紧贴触发器下方
|
|
left: 0;
|
|
width: 100%;
|
|
border: 1px solid #678BFF;
|
|
max-height: 300px;
|
|
background-color: #fff;
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
|
border-radius: 0 0 8px 8px; // 仅底部两侧圆角
|
|
z-index: 1000;
|
|
margin-top: 15px; // 关键:不要有上外边距,尖角要紧贴触发器
|
|
overflow: visible;
|
|
|
|
|
|
&::before {
|
|
content: "";
|
|
position: absolute;
|
|
top: -8px;
|
|
left: 50%;
|
|
transform: translateX(-50%) scaleY(0.5);
|
|
width: 30px;
|
|
height: 16px;
|
|
background: #fff;
|
|
clip-path: polygon(0 100%, 100% 100%, 50% 0);
|
|
z-index: 1001;
|
|
border: none; /* 移除原来的边框 */
|
|
}
|
|
|
|
&::after {
|
|
content: "";
|
|
position: absolute;
|
|
top: -9px; /* 比 ::before 往下一点,制造边框效果 */
|
|
left: 50%;
|
|
transform: translateX(-50%) scaleY(0.5);
|
|
width: 30px;
|
|
height: 16px;
|
|
background: #678BFF;
|
|
clip-path: polygon(0 100%, 100% 100%, 50% 0);
|
|
z-index: 1000;
|
|
}
|
|
}
|
|
|
|
// 搜索框区域
|
|
.search {
|
|
position: sticky;
|
|
top: 0;
|
|
background-color: #FFFFFF;
|
|
z-index: 1002;
|
|
padding: 10px 14px 0px 10px;
|
|
}
|
|
|
|
// 搜索输入框:浅灰背景 + 图标定位
|
|
.search-input {
|
|
width: 100%;
|
|
height: 25px;
|
|
padding: 0 12px 0 5px;
|
|
/* 左侧留出图标空间 */
|
|
border: 1px solid #dcdfe6;
|
|
border-radius: 8px;
|
|
box-sizing: border-box;
|
|
background-color: #f8f9fa;
|
|
/* 浅灰背景匹配参考图 */
|
|
outline: none;
|
|
font-size: 12px;
|
|
transition: border-color 0.3s ease;
|
|
|
|
&::placeholder {
|
|
color: #909399;
|
|
}
|
|
|
|
&:hover {
|
|
border-color: #c0c4cc;
|
|
}
|
|
|
|
&:focus {
|
|
border-color: #678BFF;
|
|
}
|
|
}
|
|
|
|
// 搜索图标:左侧定位
|
|
.search-icon {
|
|
position: absolute;
|
|
top: 62%;
|
|
left: 50px;
|
|
transform: translateY(-50%);
|
|
color: #909399;
|
|
z-index: 1003;
|
|
}
|
|
|
|
// 清除图标:右侧定位 + hover效果
|
|
.clear-icon {
|
|
position: absolute;
|
|
top: 50%;
|
|
right: 20px;
|
|
transform: translateY(-50%);
|
|
color: #909399;
|
|
cursor: pointer;
|
|
z-index: 1003;
|
|
|
|
&:hover {
|
|
color: #606266;
|
|
}
|
|
}
|
|
|
|
// 选项容器:调整滚动区域高度
|
|
.menuContent {
|
|
max-height: 200px;
|
|
/* 减去搜索框高度和尖角高度 */
|
|
overflow-y: auto;
|
|
padding: 8px;
|
|
padding: 10px 14px 12px 10px;
|
|
}
|
|
|
|
// 选项按钮:无边框 + hover/选中效果
|
|
.dropdown-item {
|
|
width: 100%;
|
|
height: 25px;
|
|
padding: 5px 12px 5px 5px;
|
|
text-align: center;
|
|
cursor: pointer;
|
|
border: none;
|
|
border-radius: 8px;
|
|
background-color: #fff;
|
|
font-size: 12px;
|
|
margin: 5px 0;
|
|
color: #040A2D;
|
|
transition: all 0.2s ease;
|
|
|
|
&:hover {
|
|
background-color: #F3FAFE;
|
|
/* hover浅灰 */
|
|
}
|
|
|
|
&.selected {
|
|
background-color: #E5EBFE;
|
|
/* 选中浅蓝 */
|
|
color: #2741DE;
|
|
/* 选中文字蓝色 */
|
|
}
|
|
}
|
|
|
|
// 可选:滚动条美化(与原风格一致)
|
|
.menuContent::-webkit-scrollbar {
|
|
width: 6px;
|
|
}
|
|
|
|
.menuContent::-webkit-scrollbar-track {
|
|
background: #f1f1f1;
|
|
border-radius: 3px;
|
|
}
|
|
|
|
.menuContent::-webkit-scrollbar-thumb {
|
|
background: #c0c4cc;
|
|
border-radius: 3px;
|
|
|
|
&:hover {
|
|
background: #909399;
|
|
}
|
|
}
|
|
</style>
|