---
title: "Select 选择器"
description: "从下拉列表中选择一个或多个值的控件"
category: Form
status: beta
since: 0.3.4
tags: [select, form, dropdown]
syncStatus: reviewed
verified: true
---

## 用法

### 基础
:::TuffDemoWrapper{demo="SelectSelectDemo" code-lang="vue"}
---
code: |
  <script setup lang="ts">
  import type { TxSelectValue } from '@talex-touch/tuffex'
  import { ref, useId } from 'vue'

  const value = ref<TxSelectValue>('engineering')
  const controlId = useId()
  </script>

  <template>
    <label :for="controlId">团队</label>
    <TuffSelect :id="controlId" v-model="value" placeholder="请选择" aria-label="团队">
      <TuffSelectItem value="design" label="设计团队" />
      <TuffSelectItem value="engineering" label="工程团队" />
      <TuffSelectItem value="support" label="支持团队" />
    </TuffSelect>
  </template>
---
:::

### 本地过滤
`searchable` 在面板内渲染搜索框，按已注册选项的 label 过滤。
:::TuffDemoWrapper{demo="SelectSelectSearchableDemo" code-lang="vue"}
---
code: |
  <template>
    <TuffSelect v-model="value" searchable search-placeholder="输入标签名称">
      <TuffSelectItem value="docs" label="Docs 文档" />
      <TuffSelectItem value="sdk" label="SDK 工具" />
      <TuffSelectItem value="release" label="Release 发布" />
    </TuffSelect>
  </template>
---
:::

### 远程搜索
`remote` 搭配 `editable`：触发器可编辑，面板打开期间输入即派发 `search`；用 `loading` 显示等待。
:::TuffDemoWrapper{demo="SelectSelectRemoteSearchableDemo" code-lang="vue"}
---
code: |
  <script setup lang="ts">
  import type { TxSelectOption, TxSelectValue } from '@talex-touch/tuffex'
  import { ref } from 'vue'

  const value = ref<TxSelectValue>('agent-builder')
  const remoteOptions = ref<TxSelectOption[]>([])

  async function onSearch(query: string) {
    remoteOptions.value = await searchApi(query)
  }
  </script>

  <template>
    <TuffSelect v-model="value" remote editable placeholder="搜索远程能力" @search="onSearch">
      <TuffSelectItem
        v-for="option in remoteOptions"
        :key="option.value"
        :value="option.value"
        :label="option.label"
      />
    </TuffSelect>
  </template>
---
:::

### 多选标签
`multiple` 时 `v-model` 为数组；点击选项切换选中，面板保持打开。
:::TuffDemoWrapper{demo="SelectSelectMultipleTagsDemo" code-lang="vue"}
---
code: |
  <template>
    <TuffSelect v-model="values" multiple :options="options" :max-tag-count="2" placeholder="选择标签" />
  </template>
---
:::

### 自助创建
`allowCreate` 下按 Enter 或点击底部按钮，以当前输入派发 `create` 并选中新值。
:::TuffDemoWrapper{demo="SelectSelectAllowCreateDemo" code-lang="vue"}
---
code: |
  <script setup lang="ts">
  import type { TxSelectOption, TxSelectValue } from '@talex-touch/tuffex'
  import { ref } from 'vue'

  const value = ref<TxSelectValue[]>(['agent'])
  const options = ref<TxSelectOption[]>([
    { value: 'agent', label: 'agent' },
    { value: 'workflow', label: 'workflow' },
  ])

  function onCreate(option: TxSelectOption) {
    options.value = [...options.value, option]
  }
  </script>

  <template>
    <TuffSelect
      v-model="value"
      multiple
      searchable
      allow-create
      create-text="添加标签"
      :options="options"
      @create="onCreate"
    />
  </template>
---
:::

### 分组与底部
`options` 接受 `{ label, options }` 分组；`footer` 插槽放添加按钮、说明或快捷操作。
:::TuffDemoWrapper{demo="SelectSelectGroupedFooterDemo" code-lang="vue"}
---
code: |
  <script setup lang="ts">
  const options = [
    { label: 'Manager', options: [{ value: 'jack', label: 'Jack' }, { value: 'lucy', label: 'Lucy' }] },
    { label: 'Engineer', options: [{ value: 'yiminghe', label: 'Yiminghe' }] },
  ]
  </script>

  <template>
    <TuffSelect v-model="value" :options="options" placeholder="选择成员">
      <template #footer>
        <button type="button">+ 添加成员</button>
      </template>
    </TuffSelect>
  </template>
---
:::

### 图标与描述
选项可带 `icon`（由宿主样式解析的 class）与 `description` 第二行。
:::TuffDemoWrapper{demo="SelectSelectRichOptionsDemo" code-lang="vue"}
---
code: |
  <script setup lang="ts">
  import type { TxSelectOptionLike } from '@talex-touch/tuffex'

  const options: TxSelectOptionLike[] = [
    {
      label: '最近使用',
      options: [
        { value: 'design-studio', label: '设计工作室', icon: 'i-carbon-paint-brush', description: '12 个项目' },
        { value: 'product-team', label: '产品团队', icon: 'i-carbon-layers', description: '8 个项目' },
      ],
    },
  ]
  </script>

  <template>
    <TuffSelect v-model="value" searchable :options="options" placeholder="选择工作区" />
  </template>
---
:::

### 状态边框
`status` 只提供边框，校验文案放在表单项中。
:::TuffDemoWrapper{demo="SelectSelectStatusDemo" code-lang="vue"}
---
code: |
  <template>
    <TuffSelect v-model="errorValue" status="error" :options="options" placeholder="错误状态" />
    <TuffSelect v-model="warningValue" status="warning" :options="options" placeholder="警告状态" />
  </template>
---
:::

### 禁用
值只读时禁用整个选择器；个别选项不可用时禁用对应的 `TuffSelectItem`，它仍留在列表中。
:::TuffDemoWrapper{demo="SelectSelectDisabledDemo" code-lang="vue"}
---
code: |
  <template>
    <TuffSelect v-model="value" placeholder="禁用状态" disabled eager>
      <TuffSelectItem value="locked" label="已锁定配置" />
    </TuffSelect>
  </template>
---
:::

:::TuffDemoWrapper{demo="SelectSelectDisabledOptionDemo" code-lang="vue"}
---
code: |
  <template>
    <TuffSelect v-model="value" placeholder="请选择">
      <TuffSelectItem value="standard" label="标准策略" />
      <TuffSelectItem value="enterprise" label="企业策略" disabled />
      <TuffSelectItem value="manual" label="手动策略" />
    </TuffSelect>
  </template>
---
:::

### 滚动面板
`dropdownMaxHeight` 限制面板高度；打开时选中项滚入视区。
:::TuffDemoWrapper{demo="SelectSelectScrollableDropdownDemo" code-lang="vue"}
---
code: |
  <template>
    <TuffSelect v-model="value" placeholder="选择队列" :dropdown-max-height="180">
      <TuffSelectItem v-for="queue in queues" :key="queue.value" :value="queue.value" :label="queue.label" />
    </TuffSelect>
  </template>
---
:::

### 宽度
触发器默认 240px，面板始终与触发器等宽；给组件设置宽度即可同时改变两者。
:::TuffDemoWrapper{demo="SelectSelectWidthDemo" code-lang="vue"}
---
code: |
  <template>
    <TuffSelect v-model="compact" :options="options" />
    <TuffSelect v-model="fluid" style="width: 100%" :options="options" />
  </template>
---
:::

### 最佳实践

- 数据驱动的列表用 `options`；少量需要内联标记的静态项用 `TuffSelectItem`，两者不要混用。
- 单选用相邻的 `<label for>` 与 `id`，或 `aria-label` 命名；多选用 `aria-label` / `aria-labelledby`。不要用 label 包裹选择器。
- 远程请求放在宿主层，用 `searchDebounce` 限制频率。
- 新建项需要再次出现时，在 `create` 回调中把它写入自己的 `options`。
- `modelValue` 与 `multiple` 对齐：单选用标量，多选用数组。

## API 参考

### TuffSelect

#### 属性

| 属性名 | 类型 | 默认值 | 说明 |
|------|------|---------|------|
| `modelValue` / `v-model` | `string \| number \| Array<string \| number>` | `''` | 选中值；`multiple` 时为数组。 |
| `placeholder` | `string` | `'Please select'` | 未选中时触发器的占位文本。 |
| `disabled` | `boolean` | `false` | 禁用触发器、关闭面板并阻止选择。 |
| `multiple` | `boolean` | `false` | 多选，选中项以标签显示。 |
| `status` | `'default' \| 'error' \| 'warning'` | `'default'` | 校验状态边框。 |
| `eager` | `boolean` | `true` | 预先挂载面板，使插槽选项稳定注册。 |
| `options` | `TxSelectOptionLike[]` | `[]` | 普通选项或分组。 |
| `maxTagCount` | `number` | - | 最多显示的标签数，其余显示为 `+ N ...`。 |
| `maxTagTextLength` | `number` | - | 标签文本的最大长度，超出截断。 |
| `searchable` | `boolean` | `false` | 非编辑模式下在面板内显示本地搜索框。 |
| `searchPlaceholder` | `string` | `'Search'` | 面板搜索框的占位文本。 |
| `editable` | `boolean` | `false` | 触发器文本可编辑并作为搜索词，聚焦即展开。 |
| `remote` | `boolean` | `false` | 启用可编辑远程模式，面板打开期间派发 `search`。 |
| `allowCreate` | `boolean` | `false` | 允许用当前输入创建新选项。 |
| `createText` | `string` | `'Add item'` | 默认创建按钮的文案。 |
| `loading` | `boolean` | `false` | 显示加载状态。 |
| `loadingText` | `string` | `'Loading...'` | 默认加载文案。 |
| `emptyText` | `string` | `'No results'` | 默认空状态文案。 |
| `searchDebounce` | `number` | `0` | 远程 `search` 的防抖时长（ms）。 |
| `dropdownMaxHeight` | `number` | `280` | 面板列表的最大高度（px）。 |
| `dropdownOffset` | `number` | `6` | 面板与触发器的距离。 |
| `contentPadding` | `number` | `8` | 面板内容的间距。 |
| `optionPadding` | `number` | `0` | 选项水平缩进补偿。 |
| `animation` | `BaseAnchorAnimationOptions` | - | 透传给 Popover / BaseAnchor 的动画配置。 |
| `panelVariant` | `'solid' \| 'dashed' \| 'plain'` | `'solid'` | 面板表面样式。 |
| `panelBackground` | `'pure' \| 'mask' \| 'blur' \| 'glass' \| 'refraction'` | `'refraction'` | 面板背景。 |
| `panelShadow` | `'none' \| 'soft' \| 'medium'` | `'soft'` | 面板阴影强度。 |
| `panelRadius` | `number` | `18` | 面板圆角。 |
| `panelPadding` | `number` | `0` | 面板内边距。 |
| `panelCard` | `BaseAnchorPanelCardProps` | - | 透传给面板的 card 覆盖配置。 |

#### 事件

| 事件名 | 参数 | 说明 |
|------|------|------|
| `update:modelValue` | `(value: string \| number \| Array<string \| number>)` | 选择或清空后触发；多选时为数组。 |
| `change` | `(value: string \| number \| Array<string \| number>)` | 与 `update:modelValue` 同时触发。 |
| `search` | `(query: string)` | 远程模式下，面板打开且输入文本变化时触发。 |
| `create` | `(option: TxSelectOption)` | `allowCreate` 用当前输入创建选项时触发。 |

#### 插槽

| 插槽名 | Props | 说明 |
|------|------|------|
| `default` | - | 以 `TuffSelectItem` 声明选项（旧式写法）。 |
| `option` | `{ option, selected }` | `options` 模式下的单个选项内容。 |
| `tag` | `{ option, remove }` | 多选标签内容。 |
| `group` | `{ group }` | 分组标题。 |
| `loading` | - | 加载状态。 |
| `empty` | - | 空状态。 |
| `footer` | `{ query, canCreate, create }` | 面板底部内容。 |

#### 暴露方法

| 方法 | 说明 |
|------|------|
| `open()` | 打开面板。 |
| `close()` | 关闭面板。 |
| `toggle()` | 切换面板开合。 |
| `focus()` | 聚焦触发器输入框。 |
| `blur()` | 使触发器输入框失焦。 |
| `clear()` | 清空值与选中 label；单选派发 `''`，多选派发 `[]`。 |

### TuffSelectItem

#### 属性

| 属性名 | 类型 | 默认值 | 说明 |
|------|------|---------|------|
| `value` | `string \| number` | - | 选中时写入 Select 的值。 |
| `label` | `string` | - | 显示文本与选中后的回显；未传时用插槽文本。 |
| `disabled` | `boolean` | `false` | 不可选中，但仍显示在列表中。 |
| `icon` | `string` | - | 行首图标的 class，由宿主样式解析。 |
| `description` | `string` | - | label 下方的说明文字。 |

#### 插槽

| 插槽名 | Props | 说明 |
|------|------|------|
| `default` | - | 选项行的标题内容；为空时依次回退到 `label`、值。 |

### 类型

:::TuffCodeBlock{lang="ts"}
---
code: |
  type TxSelectValue = string | number

  interface TxSelectOption {
    value: TxSelectValue
    label: string
    disabled?: boolean
    icon?: string
    description?: string
  }

  interface TxSelectOptionGroup {
    label: string
    disabled?: boolean
    options: TxSelectOption[]
  }

  type TxSelectOptionLike = TxSelectOption | TxSelectOptionGroup
---
:::

## 概述

- 传了 `options` 时由它渲染，`TuffSelectItem` 只在没有 `options` 时注册；未传 `label` 时依次用插槽文本、原始值。
- 非编辑模式点击触发器开合面板；`editable` 或 `remote` 时聚焦即展开。
- 单选选中启用项后关闭面板；多选保持打开。
- `searchable` 只在非编辑模式下本地过滤；`remote` 关闭本地过滤，只在面板打开时派发 `search`。
- 值先于选项传入时，选项注册后再解析显示文本；有限的数字字符串与数字按数值相等匹配。
- 触发器是 `combobox`（`aria-haspopup="listbox"`、`aria-expanded`），列表是 `listbox`（多选时加 `aria-multiselectable`），选项是带 `aria-selected` 的 `option`。`id` 与可访问名落在实际 combobox 上，`class`、`style` 留在外层。

## 技术实现

- 面板由 Popover 渲染；`TuffSelectItem` 经注入向父级注册 value 与 label。
- 源码：`packages/tuffex/packages/components/src/select/`；导出 `TuffSelect`、`TuffSelectItem`（别名 `TxSelect`、`TxSelectItem`）及公共类型。

<TuffDocSourceLink />
