You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

是否存在与下图所示效果一致的Flutter图片类组件?

分组多选下拉组件实现方案

以下是与你提供的参考图效果完全匹配的组件实现,可直接使用或快速迁移到任意前端框架:

  • 顶部带搜索输入框,可实时过滤所有选项
  • 选项按分组展示,支持分组标题折叠/展开
  • 支持多选,选中项以标签形式展示在输入框内,支持单独删除
  • 下拉面板滚动加载,适配大量选项场景
  • 点击空白区域自动收起下拉面板

基础实现代码(原生HTML/CSS/JS,可直接运行)

<div class="multi-select-group">
  <div class="select-input-wrap">
    <div class="selected-tags">
      <!-- 选中的标签动态插入这里 -->
    </div>
    <input type="text" class="search-input" placeholder="搜索选项">
    <span class="arrow-icon">▼</span>
  </div>
  <div class="dropdown-panel hide">
    <div class="option-group">
      <div class="group-title">分组1 <span class="fold-icon">−</span></div>
      <div class="group-options">
        <label class="option-item">
          <input type="checkbox" value="option1-1">
          <span>选项1-1</span>
        </label>
        <label class="option-item">
          <input type="checkbox" value="option1-2">
          <span>选项1-2</span>
        </label>
      </div>
    </div>
    <div class="option-group">
      <div class="group-title">分组2 <span class="fold-icon">−</span></div>
      <div class="group-options">
        <label class="option-item">
          <input type="checkbox" value="option2-1">
          <span>选项2-1</span>
        </label>
        <label class="option-item">
          <input type="checkbox" value="option2-2">
          <span>选项2-2</span>
        </label>
      </div>
    </div>
  </div>
</div>
.multi-select-group {
  width: 350px;
  position: relative;
  font-family: system-ui, sans-serif;
}
.select-input-wrap {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  padding: 6px 12px;
  border: 1px solid #d9d9d9;
  border-radius: 4px;
  gap: 6px;
  cursor: text;
}
.selected-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}
.tag-item {
  display: flex;
  align-items: center;
  padding: 2px 6px;
  background: #f0f0f0;
  border-radius: 2px;
  font-size: 14px;
  gap: 4px;
}
.tag-close {
  cursor: pointer;
  font-size: 12px;
  color: #666;
}
.search-input {
  flex: 1;
  border: none;
  outline: none;
  padding: 4px 0;
  min-width: 120px;
}
.arrow-icon {
  font-size: 12px;
  color: #999;
}
.dropdown-panel {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  max-height: 300px;
  overflow-y: auto;
  border: 1px solid #d9d9d9;
  border-radius: 4px;
  background: #fff;
  z-index: 999;
}
.dropdown-panel.hide {
  display: none;
}
.group-title {
  padding: 8px 12px;
  background: #fafafa;
  font-weight: 500;
  font-size: 14px;
  cursor: pointer;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.group-options {
  padding: 4px 0;
}
.option-item {
  display: flex;
  align-items: center;
  padding: 6px 12px;
  gap: 8px;
  cursor: pointer;
  font-size: 14px;
}
.option-item:hover {
  background: #f5f5f5;
}
// 核心交互逻辑
const selectWrap = document.querySelector('.multi-select-group')
const input = selectWrap.querySelector('.search-input')
const dropdown = selectWrap.querySelector('.dropdown-panel')
const selectedTagsWrap = selectWrap.querySelector('.selected-tags')
const groupTitles = selectWrap.querySelectorAll('.group-title')

// 展开/收起下拉
selectWrap.addEventListener('click', (e) => {
  if (e.target.closest('.select-input-wrap')) {
    dropdown.classList.toggle('hide')
    input.focus()
  }
})
document.addEventListener('click', (e) => {
  if (!e.target.closest('.multi-select-group')) {
    dropdown.classList.add('hide')
  }
})

// 分组折叠/展开
groupTitles.forEach(title => {
  title.addEventListener('click', () => {
    const options = title.nextElementSibling
    const icon = title.querySelector('.fold-icon')
    options.classList.toggle('hide')
    icon.textContent = options.classList.contains('hide') ? '+' : '−'
  })
})

// 选项选中逻辑
const checkboxes = selectWrap.querySelectorAll('input[type="checkbox"]')
checkboxes.forEach(checkbox => {
  checkbox.addEventListener('change', () => {
    const value = checkbox.value
    const label = checkbox.nextElementSibling.textContent
    if (checkbox.checked) {
      // 新增标签
      const tag = document.createElement('div')
      tag.className = 'tag-item'
      tag.dataset.value = value
      tag.innerHTML = `${label}<span class="tag-close">×</span>`
      selectedTagsWrap.appendChild(tag)
      // 标签删除逻辑
      tag.querySelector('.tag-close').addEventListener('click', () => {
        checkbox.checked = false
        tag.remove()
      })
    } else {
      // 删除对应标签
      const tag = selectedTagsWrap.querySelector(`[data-value="${value}"]`)
      tag && tag.remove()
    }
  })
})

// 搜索过滤逻辑
input.addEventListener('input', () => {
  const keyword = input.value.trim().toLowerCase()
  const allOptions = selectWrap.querySelectorAll('.option-item')
  allOptions.forEach(option => {
    const text = option.textContent.toLowerCase()
    option.style.display = text.includes(keyword) ? 'flex' : 'none'
  })
})

框架适配说明

  • Vue项目:可直接将逻辑改写成v-model绑定选中数组、v-for渲染选项和分组的形式,无需操作DOM
  • React项目:可使用useState管理选中值、展开状态、搜索关键词,配合useEffect处理过滤逻辑
  • 可自定义样式,调整配色、圆角、尺寸即可匹配你的项目UI规范

内容的提问来源于stack exchange,提问作者cahbs

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 21:54:02