更新记录
v0.1.0(2026-04-03)
下载此版本
首次上传
平台兼容性
uni-app(3.7.3)
| Vue2 |
Vue3 |
Chrome |
Safari |
app-vue |
app-nvue |
Android |
iOS |
鸿蒙 |
| - |
√ |
√ |
√ |
√ |
- |
√ |
√ |
- |
| 微信小程序 |
支付宝小程序 |
抖音小程序 |
百度小程序 |
快手小程序 |
京东小程序 |
鸿蒙元服务 |
QQ小程序 |
飞书小程序 |
小红书小程序 |
快应用-华为 |
快应用-联盟 |
| - |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
气泡菜单组件 (popover-menu)
组件介绍
popover-menu 是一个气泡菜单组件,支持多种展开方向、自定义样式、选项禁用等功能。适用于筛选、选择等场景。
组件属性
| 属性 |
类型 |
默认值 |
说明 |
| options |
Array |
[{ value: "", label: "全部" }] |
选项列表,每项包含 value、label 和可选的 disabled |
| panelWidth |
Number |
300 |
面板宽度(rpx) |
| panelHeight |
Number |
300 |
面板高度(rpx),设置后选项高度超过面板区域时会滚动 |
| offsetGap |
Number |
16 |
面板与按钮之间的偏移间距(rpx) |
| offsetDirection |
String |
'bottom' |
面板偏移方向:'bottom-start'、'bottom-end'、'top-start'、'top-end'、'top'、'bottom' |
| customClass |
String |
'' |
自定义按钮类名 |
| optionClass |
String |
'' |
自定义选项类名 |
| showArrow |
Boolean |
true |
是否显示面板箭头 |
| disabled |
Boolean |
false |
是否禁用组件 |
组件事件
| 事件 |
参数 |
说明 |
| select |
option |
选择选项时触发,返回选中的选项对象 |
组件方法
组件插槽
| 插槽名 |
说明 |
作用域参数 |
| prefix |
按钮前缀内容,默认显示筛选图标 |
- |
| label |
按钮标签内容,默认显示选中项文本 |
option: 当前选项 |
| suffix |
按钮后缀内容,默认显示箭头图标 |
- |
| option-label |
选项标签内容,默认显示选项文本 |
option: 菜单选项 |
| option-check |
选中选项的确认图标 |
option: 菜单选项 |
使用示例
<template>
<popover-menu :options="menuOptions" />
</template>
<script setup>
import { popoverMenu } from '@/components/popover-menu/popover-menu'
const menuOptions = [
{ value: 'all', label: '全部' },
{ value: 'option1', label: '选项一' },
{ value: 'option2', label: '选项二' }
]
</script>
完整实例
<script setup lang="ts">
import PopoverMenu from '@/components/popover-menu.vue'
const timeOptions = [
{ value: 'all', label: '全部' },
{ value: 'today', label: '今天' },
{ value: 'week', label: '本周' },
{ value: 'month', label: '本月' },
{ value: 'year', label: '今年' },
]
const placements = [
{ name: 'top', label: '顶部居中' },
{ name: 'top-start', label: '顶部左对齐' },
{ name: 'top-end', label: '顶部右对齐' },
{ name: 'bottom', label: '底部居中' },
{ name: 'bottom-start', label: '底部左对齐' },
{ name: 'bottom-end', label: '底部右对齐' },
] as const
function handleSelect(option: { value: string | number; label: string }) {
console.log('选中:', option)
}
function handleVisibleChange(visible: boolean, placement: string) {
console.log(`placement-${placement} 可见性变化:`, visible)
}
</script>
<template>
<view class="section">
<text class="section-title">6个位置测试 (placement)</text>
<view class="placement-grid">
<view
v-for="p in placements"
:key="p.name"
class="placement-item"
>
<text class="placement-label">{{ p.label }}</text>
<PopoverMenu
:options="timeOptions"
:offsetDirection="p.name"
trigger="click"
@select="handleSelect"
@visible-change="(v) => handleVisibleChange(v, p.name)"
>
<view class="custom-trigger">
<text class="trigger-text">{{ p.name }}</text>
</view>
</PopoverMenu>
</view>
</view>
</view>
<!-- 测试 disabled 状态 -->
<view class="section">
<text class="section-title">禁用状态测试</text>
<view class="button-row">
<view class="test-item">
<text class="label">正常状态</text>
<PopoverMenu
:options="timeOptions"
trigger="click"
/>
</view>
<view class="test-item">
<text class="label">禁用状态</text>
<PopoverMenu
:options="timeOptions"
trigger="click"
:disabled="true"
/>
</view>
</view>
</view>
<!-- 测试 disabled 选项 -->
<view class="section">
<text class="section-title">禁用选项测试</text>
<PopoverMenu
:options="[
{ value: '1', label: '选项1' },
{ value: '2', label: '选项2(禁用)', disabled: true },
{ value: '3', label: '选项3' },
]"
trigger="click"
/>
</view>
<!-- 测试自定义选项 label 和 check 图标 -->
<view class="section">
<text class="section-title">自定义选项 Label 和 Check 图标</text>
<view class="button-row">
<!-- 示例1: 自定义选项标签样式 -->
<view class="test-item">
<text class="label">自定义选项标签</text>
<PopoverMenu
:options="[
{ value: 'hot', label: '热门' },
{ value: 'new', label: '最新' },
{ value: 'best', label: '精华' },
]"
offset-direction="bottom"
>
<template #option-label="{ option, selected }">
<view class="custom-option-label">
<text class="option-icon">{{ option.value === 'hot' ? '🔥' : option.value === 'new' ? '🆕' : '⭐' }}</text>
<text :class="['option-label-text', { 'option-selected': selected }]">
{{ option.label }}
</text>
</view>
</template>
</PopoverMenu>
</view>
<!-- 示例2: 自定义选中图标 -->
<view class="test-item">
<text class="label">自定义选中图标</text>
<PopoverMenu
:options="timeOptions"
offset-direction="bottom"
>
<template #option-check="{ option }">
<image
class="custom-check-icon"
src="data:image/svg+xml,%3Csvg width='18' height='18' viewBox='0 0 24 24' fill='%23811813' xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpath d='M8 12L11 15L16 9' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' fill='none'/%3E%3C/svg%3E"
mode="aspectFit"
/>
</template>
</PopoverMenu>
</view>
<!-- 示例3: 同时自定义 label 和 check -->
<view class="test-item">
<text class="label">完全自定义选项</text>
<PopoverMenu
:options="[
{ value: '***', label: '微信支付' },
{ value: 'alipay', label: '支付宝' },
{ value: 'card', label: '银行卡' },
]"
offset-direction="bottom"
>
<template #option-label="{ option, selected }">
<view class="payment-option">
<text class="payment-icon">{{ option.value === '***' ? '💚' : option.value === 'alipay' ? '💙' : '💳' }}</text>
<text :class="['payment-text', { 'payment-selected': selected }]">{{ option.label }}</text>
</view>
</template>
<template #option-check="{ option }">
<view class="payment-check">
<text class="check-emoji">✅</text>
</view>
</template>
</PopoverMenu>
</view>
</view>
</view>
</template>
<style lang="scss">
page {
background-color: #f5f5f5;
}
.page-title {
display: block;
margin-bottom: 40rpx;
font-size: 40rpx;
font-weight: bold;
color: #333;
text-align: center;
}
.section {
margin-bottom: 50rpx;
padding: 30rpx;
background-color: #fff;
border-radius: 16rpx;
}
.section-title {
display: block;
margin-bottom: 30rpx;
font-size: 32rpx;
font-weight: 600;
color: #333;
border-left: 8rpx solid #811813;
padding-left: 20rpx;
}
.button-row {
display: flex;
flex-wrap: wrap;
gap: 40rpx;
}
.test-item {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 16rpx;
}
.label {
font-size: 26rpx;
color: #666;
}
// placement 网格布局
.placement-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 40rpx;
}
.placement-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 20rpx;
padding: 30rpx;
background-color: #f8f8f8;
border-radius: 12rpx;
}
.placement-label {
font-size: 26rpx;
color: #666;
}
// 自定义触发器样式
.custom-trigger {
padding: 16rpx 32rpx;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 50rpx;
}
.trigger-text {
font-size: 28rpx;
color: #fff;
font-weight: 500;
}
.custom-trigger-btn {
padding: 20rpx 40rpx;
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
border-radius: 50rpx;
}
.custom-trigger-text {
font-size: 28rpx;
color: #fff;
font-weight: 500;
}
// 自定义下拉内容
.custom-dropdown {
min-width: 200rpx;
}
.dropdown-header {
padding: 20rpx 30rpx;
background-color: #f0f0f0;
border-bottom: 1rpx solid #e0e0e0;
}
.header-title {
font-size: 28rpx;
font-weight: 600;
color: #333;
}
.dropdown-item {
padding: 24rpx 30rpx;
font-size: 28rpx;
color: #333;
transition: background-color 0.2s;
&:active {
background-color: #f5f5f5;
}
}
// 自定义选项标签样式
.custom-option-label {
display: flex;
align-items: center;
gap: 12rpx;
}
.option-icon {
font-size: 32rpx;
}
.option-label-text {
font-size: 28rpx;
color: #666;
&.option-selected {
color: #811813;
font-weight: 600;
}
}
// 自定义选中图标
.custom-check-icon {
display: block;
width: 36rpx;
height: 36rpx;
}
// 支付选项样式
.payment-option {
display: flex;
align-items: center;
gap: 16rpx;
}
.payment-icon {
font-size: 36rpx;
}
.payment-text {
font-size: 28rpx;
color: #333;
&.payment-selected {
color: #07c160;
font-weight: 600;
}
}
.payment-check {
display: flex;
align-items: center;
justify-content: center;
}
.check-emoji {
font-size: 32rpx;
}
</style>