vue2下拉菜单
示例一:基础原生下拉菜单(美化样式)
<template><div class="basic-select"><label for="fruit">选择水果:</label><select id="fruit" v-model="selectedFruit"><option disabled value="">请选择</option><option value="apple">苹果</option><option value="banana">香蕉</option><option value="orange">橙子</option></select><p>你选择了:<strong>{{ selectedFruit }}</strong></p></div>
</template><script>
export default {data() {return {selectedFruit: ''}}
}
</script><style scoped>
.basic-select {font-family: sans-serif;margin: 20px;
}
select {padding: 8px 12px;border-radius: 4px;border: 1px solid #ccc;font-size: 14px;
}
</style>
示例二:自定义下拉菜单(点击展开 + 激活状态)
<template><div class="dropdown" ref="dropdown"><div class="dropdown-toggle" @click="toggleDropdown">{{ selected || '请选择水果' }}<span class="arrow" :class="{ open: isOpen }">▼</span></div><ul v-if="isOpen" class="dropdown-menu"><liv-for="item in items":key="item":class="{ active: item === selected }"@click="selectItem(item)">{{ item }}</li></ul></div>
</template><script>
export default {data() {return {isOpen: false,selected: '',items: ['苹果', '香蕉', '橙子']}},mounted() {document.addEventListener('click', this.handleClickOutside)},beforeDestroy() {document.removeEventListener('click', this.handleClickOutside)},methods: {toggleDropdown() {this.isOpen = !this.isOpen},selectItem(item) {this.selected = itemthis.isOpen = false},handleClickOutside(event) {if (!this.$refs.dropdown.contains(event.target)) {this.isOpen = false}}}
}
</script><style scoped>
.dropdown {position: relative;width: 200px;font-family: sans-serif;
}
.dropdown-toggle {padding: 10px;background: #f9f9f9;border: 1px solid #ccc;cursor: pointer;user-select: none;display: flex;justify-content: space-between;align-items: center;
}
.arrow {transition: transform 0.3s ease;
}
.arrow.open {transform: rotate(180deg);
}
.dropdown-menu {position: absolute;top: 100%;left: 0;width: 100%;background: white;border: 1px solid #ccc;box-shadow: 0 2px 6px rgba(0,0,0,0.1);z-index: 10;
}
.dropdown-menu li {padding: 10px;cursor: pointer;
}
.dropdown-menu li:hover {background: #f0f0f0;
}
.dropdown-menu li.active {background: #e6f7ff;font-weight: bold;
}
</style>
示例三:标签式下拉菜单(多选 + 激活状态)
<template><div class="tag-dropdown"><div class="tag" v-for="item in items" :key="item" :class="{ active: selectedTags.includes(item) }"@click="toggleTag(item)">{{ item }}</div><p>已选择:<span v-if="selectedTags.length">{{ selectedTags.join(', ') }}</span><span v-else>无</span></p></div>
</template><script>
export default {data() {return {items: ['苹果', '香蕉', '橙子', '西瓜', '葡萄'],selectedTags: []}},methods: {toggleTag(item) {const index = this.selectedTags.indexOf(item)if (index === -1) {this.selectedTags.push(item)} else {this.selectedTags.splice(index, 1)}}}
}
</script><style scoped>
.tag-dropdown {font-family: sans-serif;margin: 20px;
}
.tag {display: inline-block;padding: 6px 12px;margin: 5px;border-radius: 20px;background: #eee;cursor: pointer;transition: all 0.2s ease;
}
.tag:hover {background: #ddd;
}
.tag.active {background: #1890ff;color: white;font-weight: bold;
}
</style>
示例 四:联动下拉菜单(省市区三级联动)
<template><div class="linkage-select"><select v-model="selectedProvince" @change="loadCities"><option disabled value="">选择省份</option><option v-for="p in provinces" :key="p" :value="p">{{ p }}</option></select><select v-model="selectedCity" @change="loadDistricts" v-if="cities.length"><option disabled value="">选择城市</option><option v-for="c in cities" :key="c" :value="c">{{ c }}</option></select><select v-model="selectedDistrict" v-if="districts.length"><option disabled value="">选择区县</option><option v-for="d in districts" :key="d" :value="d">{{ d }}</option></select></div>
</template><script>
export default {data() {return {selectedProvince: '',selectedCity: '',selectedDistrict: '',provinces: ['广东', '浙江'],cities: [],districts: []}},methods: {loadCities() {this.selectedCity = ''this.selectedDistrict = ''this.cities = this.selectedProvince === '广东'? ['广州', '深圳']: ['杭州', '宁波']this.districts = []},loadDistricts() {this.selectedDistrict = ''if (this.selectedCity === '广州') this.districts = ['天河', '越秀']else if (this.selectedCity === '深圳') this.districts = ['南山', '福田']else if (this.selectedCity === '杭州') this.districts = ['西湖', '滨江']else if (this.selectedCity === '宁波') this.districts = ['海曙', '江北']}}
}
</script><style scoped>
.linkage-select select {margin: 10px;padding: 8px;border-radius: 4px;border: 1px solid #ccc;
}
</style>
示例 五:搜索过滤下拉菜单
适合:选项很多时,用户可快速定位目标。
<template><div class="search-dropdown"><input type="text" v-model="search" placeholder="搜索水果..." /><ul class="dropdown-menu"><liv-for="item in filteredItems":key="item"@click="select(item)":class="{ active: item === selected }">{{ item }}</li></ul><p>你选择了:{{ selected }}</p></div>
</template><script>
export default {data() {return {search: '',selected: '',items: ['苹果', '香蕉', '橙子', '西瓜', '葡萄', '芒果']}},computed: {filteredItems() {return this.items.filter(item =>item.toLowerCase().includes(this.search.toLowerCase()))}},methods: {select(item) {this.selected = item}}
}
</script><style scoped>
.search-dropdown {width: 220px;font-family: sans-serif;
}
input {width: 100%;padding: 8px;margin-bottom: 5px;box-sizing: border-box;
}
.dropdown-menu {border: 1px solid #ccc;max-height: 150px;overflow-y: auto;background: white;padding: 0;margin: 0;list-style: none;
}
.dropdown-menu li {padding: 8px;cursor: pointer;
}
.dropdown-menu li.active {background: #1890ff;color: white;
}
</style>
示例 六:分组下拉菜单
适合:分类展示选项,如国家/地区、部门/职位等。
<template><div class="grouped-dropdown"><select v-model="selected"><option disabled value="">请选择</option><optgroupv-for="group in groupedOptions":key="group.label":label="group.label"><option v-for="item in group.items" :key="item" :value="item">{{ item }}</option></optgroup></select><p>你选择了:{{ selected }}</p></div>
</template><script>
export default {data() {return {selected: '',groupedOptions: [{ label: '热带水果', items: ['香蕉', '芒果', '菠萝'] },{ label: '温带水果', items: ['苹果', '梨', '葡萄'] }]}}
}
</script><style scoped>
.grouped-dropdown {font-family: sans-serif;margin: 20px;
}
select {padding: 8px;border-radius: 4px;
}
</style>
示例七:自定义图标下拉菜单(点击展开 + 激活状态+浏览器失焦关闭)
<template><div class="icon-dropdown" ref="dropdown"><!-- 图标按钮 --><iclass="iconfont icon-diaoseban trigger-icon"@click.stop="handleIconClick":class="{ active: isOpen }"></i><!-- 下拉菜单带动画 --><transition name="fade-slide"><ul v-if="isOpen" class="dropdown-menu"><liv-for="item in iconList":key="item.icon"@click="selectIcon(item)":class="{ selected: item.icon === selectedIcon }"><i :class="item.icon"></i><span>{{ item.name }}</span></li></ul></transition></div>
</template><script>
export default {data() {return {isOpen: false,selectedIcon: "",iconList: [{ name: "雕色板", icon: "iconfont icon-diaoseban" },{ name: "设置", icon: "iconfont icon-shezhi" },{ name: "用户", icon: "iconfont icon-yonghu" },],};},mounted() {window.addEventListener("blur", this.handleWindowBlur);},beforeDestroy() {window.removeEventListener("blur", this.handleWindowBlur);},methods: {handleWindowBlur() {this.closeDropdown();},closeDropdown() {this.isOpen = false;document.removeEventListener("click", this.handleClickOutside, true);},handleIconClick() {this.isOpen = true;// 延迟绑定外部点击监听,避免刚点击图标就关闭setTimeout(() => {document.addEventListener("click", this.handleClickOutside, true);}, 0);},selectIcon(item) {this.selectedIcon = item.icon;this.closeDropdown();},handleClickOutside(event) {if (!this.$refs.dropdown.contains(event.target)) {this.closeDropdown();}},},
};
</script><style scoped>
.trigger-icon {font-size: 24px;cursor: pointer;color: #666;transition: color 0.3s ease, transform 0.3s ease;
}
.trigger-icon:hover {color: #1890ff;
}
.trigger-icon.active {color: #1890ff;transform: rotate(90deg);
}.dropdown-menu {position: absolute;margin-top: 8px;background: white;border: 1px solid #ccc;border-radius: 4px;list-style: none;padding: 6px 0;box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);z-index: 100;
}
.dropdown-menu li {display: flex;align-items: center;padding: 6px 12px;cursor: pointer;transition: background-color 0.2s ease;
}
.dropdown-menu li:hover {background-color: #f5f5f5;
}
.dropdown-menu li.selected {background-color: #e6f7ff;
}
.dropdown-menu li i {margin-right: 8px;font-size: 18px;
}/* 动画过渡效果 */
.fade-slide-enter-active,
.fade-slide-leave-active {transition: all 0.3s ease;
}
.fade-slide-enter,
.fade-slide-leave-to {opacity: 0;transform: translateY(-10px);
}
</style>
⚠️ 注意事项
window.blur
会在浏览器失去焦点时触发,比如切换到其他应用、点击任务栏等但它不会触发在浏览器内部的点击,所以仍需保留
document.click
的监听来处理常规关闭