更新记录

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 选择选项时触发,返回选中的选项对象

组件方法

方法 说明
closePanel 关闭下拉面板

组件插槽

插槽名 说明 作用域参数
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>

隐私、权限声明

1. 本插件需要申请的系统权限列表:

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

许可协议

MIT协议

暂无用户评论。