---
title: "Picker 滚轮选择"
description: "按列滚动选值的滚轮选择器"
category: Form
status: beta
since: 0.3.4
tags: [picker, form, wheel, selection]
syncStatus: reviewed
verified: true
---

## 用法

### 弹层、内联与紧凑行高
`v-model:visible` 控制弹层，`popup="false"` 内联渲染，`itemHeight` / `visibleItemCount` 调整行高与行数。
:::TuffDemoWrapper{demo="PickerPickerDemo" code-lang="vue"}
---
code: |
  <script setup lang="ts">
  import type { PickerColumn, PickerValue } from '@talex-touch/tuffex'
  import { ref } from 'vue'

  const visible = ref(false)
  const value = ref<PickerValue>(['pro', 'monthly'])

  const columns: PickerColumn[] = [
    {
      key: 'plan',
      options: [
        { value: 'free', label: '免费版' },
        { value: 'pro', label: '专业版' },
        { value: 'team', label: '团队版', disabled: true },
      ],
    },
    {
      key: 'cycle',
      options: [
        { value: 'monthly', label: '月付' },
        { value: 'annual', label: '年付' },
      ],
    },
  ]
  </script>

  <template>
    <TxButton @click="visible = true">打开选择器</TxButton>
    <TxPicker v-model="value" v-model:visible="visible" title="订阅计划" :columns="columns" />

    <TxPicker v-model="value" :popup="false" title="订阅计划" :columns="columns" />

    <TxPicker
      v-model="value"
      :popup="false"
      :show-toolbar="false"
      :columns="columns"
      :item-height="28"
      :visible-item-count="4"
    />
  </template>
---
:::

### 最佳实践

- `modelValue` 保持为与 `columns` 顺序一致的完整数组；稀疏数组会被归一化，但显式值更便于审计表单状态。
- 列会重排或条件渲染时提供 `column.key`，避免 Vue 把旧列复用给不同数据。
- 选项 `value` 使用稳定的原始值，展示文案放在 `label`。
- 设置页常驻展示用内联（`popup=false`）；移动端或短任务用弹层。
- 不要提交禁用项的值：组件只在归一化或滚动恢复时跳过禁用项。

## API 参考

### 属性

| 属性名 | 类型 | 默认值 | 说明 |
|------|------|---------|------|
| `modelValue` | `PickerValue` | `[]` | 每列的选中值；缺失或非法时归一化到首个可用项。 |
| `columns` | `PickerColumn[]` | `[]` | 从左到右的列定义。 |
| `visible` | `boolean` | `false` | 弹层显示状态，配合 `v-model:visible`。 |
| `popup` | `boolean` | `true` | `true` 时 Teleport 为底部弹层，`false` 时内联渲染。 |
| `title` | `string` | `''` | 工具栏标题。 |
| `showToolbar` | `boolean` | `true` | 显示取消、标题与确认工具栏。 |
| `confirmText` | `string` | `'Confirm'` | 确认按钮文案。 |
| `cancelText` | `string` | `'Cancel'` | 取消按钮文案。 |
| `disabled` | `boolean` | `false` | 禁用工具栏、选项与拖拽。 |
| `itemHeight` | `number` | `36` | 行高（px），最小 24。 |
| `visibleItemCount` | `number` | `5` | 可见行数；偶数向上取奇数，最小 3。 |
| `closeOnClickMask` | `boolean` | `true` | 点击遮罩关闭弹层。 |
| `lazyMount` | `boolean` | `true` | 首次打开后再挂载弹层内容。 |

### 事件

| 事件名 | 参数 | 说明 |
|------|------|------|
| `update:modelValue` | `(value: PickerValue)` | 转动改变列值时触发。 |
| `change` | `(value: PickerValue)` | 与 `update:modelValue` 同时触发。 |
| `update:visible` | `(visible: boolean)` | 打开或关闭时触发。 |
| `confirm` | `(value: PickerValue)` | 点击确认时以当前值触发，随后关闭。 |
| `cancel` | - | 点击取消时触发，随后关闭。 |
| `open` | - | 打开时触发。 |
| `close` | - | 关闭时触发。 |

### 暴露方法

| 名称 | 类型 | 说明 |
|------|------|------|
| `open` | `() => void` | 写入 `visible=true` 打开弹层。 |
| `close` | `() => void` | 关闭弹层。 |
| `toggle` | `() => void` | 切换弹层。 |

### 类型

```ts
type PickerValue = Array<string | number>
```

`PickerColumn`，`columns` 中的一项：

| 字段 | 类型 | 说明 |
|------|------|------|
| `key` | `string` | 可选的稳定 key。 |
| `options` | `PickerOption[]` | 该列的选项。 |

`PickerOption`，`options` 中的一项：

| 字段 | 类型 | 说明 |
|------|------|------|
| `value` | `string \| number` | 该列派发的原始值。 |
| `label` | `string` | 显示文本。 |
| `disabled` | `boolean` | 禁止选择，归一化回退时跳过。 |

## 概述

- 每一列是组件自己转动的滚筒，不是原生滚动容器；指针、滚轮、点击与键盘都由列处理。
- 拖拽跟手，松手后按速度惯性滑行，以三次缓出停在最近的可用行；滚轮停止 120ms 后吸附。
- 减少动态效果时拖拽仍然跟手，但没有惯性滑行，吸附不做缓动。
- 受控父级回传的值若正是某列正转向的值，该列会转完这一圈，不会被重新放置。
- 只绘制滚筒正面的行（默认 11 行，与列长无关）；每行带 `aria-setsize` 与 `aria-posinset`，读屏不受影响。

## 技术实现

- 行按与偏移量的距离 `rotateX` 排布在滚筒面上（默认 `r = 116px`、步进 17.6°、透视 348px）；行不参与命中测试，点击位置由投影几何闭式反解到行。
- 源码：`packages/tuffex/packages/components/src/picker/`。

<TuffDocSourceLink />
