标签输入
概述
用于输入、展示和删除多个标签值的组合式多值输入组件。支持受控/非受控的 string[] 值、通过 Enter/Tab/失焦/分隔符输入/粘贴创建标签、完整的键盘选中与删除(含 RTL 反转)、max/duplicate 约束以及隐藏表单输入序列化。适用于关键词列表、邮件收件人等需要快速键盘操作而非纯文本输入的小型标记集合场景。
用法
特性
- 🏷 标签模型 — 经
useControllableState支持受控/非受控string[];清空事件 + 逐动作的addTag/removeTag事件 - ⌨️ 完整键盘支持 —
ArrowLeft/ArrowRight选中相邻标签(逻辑方向,RTL 反转),Backspace/Delete删除选中标签,Home/End跳转首/末标签,输入即取消选中 - ✂️ 分隔符与粘贴 — 输入分隔符(字符串或
RegExp)时提交已输入内容,或对粘贴文本按分隔符拆分(addOnPaste) - ⏎ 提交触发 —
Enter始终提交;Tab与失焦仅在开启addOnTab/addOnBlur时提交 - 🚦 约束 —
max限制总数,duplicate允许重复值,displayValue改写标签文案;被拒绝的值触发invalid事件 - 🧩 可组合部件 —
Root/Control/Item/ItemText/ItemDelete/Clear,compact 支持*Props透传与可完全替换的item插槽 - 📋 表单集成 —
name/required/disabled经视觉隐藏输入序列化;aria-controls将输入与标签列表容器关联 - 🔤 本地化 UI — 默认
aria-label来自 locale 文案,且每个部件均可覆盖 - 🚫 禁用 / 只读 —
disabled阻止全部交互;readonly阻止输入但保留标签删除与清空
组件家族
STagsInput— 样式包装层,将 props 转发给 Aria compact 并注入tagsInputVariants类(6 个 slot:root/item/itemText/itemDelete/control/clear)TagsInputCompact(Aria)—TagsInputRoot+ 每个值一个TagsInputItem(默认 item 文本/删除)+Control+Clear(由clearable门控)的数据驱动组合;无样式用法从@vean/aria/tags-input导入TagsInputRoot(Aria)— 状态所有者:useControllableState管理值数组、集合注册、键盘/删除/清空逻辑与视觉隐藏表单输入TagsInputControl(Aria)— 提交型输入:分隔符输入、粘贴拆分、Enter/Tab/失焦提交、输入法组合保护TagsInputItem/TagsInputItemText/TagsInputItemDelete(Aria)— 注册到集合的标签项、其文本 span 与可键盘操作的删除按钮TagsInputClear(Aria)— 清空按钮,默认样式在根组件有值时显示,渲染由clearable门控
演示
基础
API
Aria 层
8TagsInput
属性
| 属性名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
class | ClassValue | — | 应用于根元素的额外类名。 |
size | ThemeSize | — | 组件的视觉尺寸。 |
ui | Partial<TagsInputUi> | — | 该组件的按槽类重写。 |
clearable | boolean | — | 是否渲染“清除”触发器。 |
controlProps | TagsInputControlProps | — | 转发给输入元素的属性。 |
clearProps | TagsInputClearProps | — | 转发给 clear 元素的 props。 |
itemProps | TagsInputItemProps | — | 转发给 item 元素的 props。 |
itemTextProps | TagsInputItemTextProps | — | 已将属性转发至 item 文本元素。 |
itemDeleteProps | TagsInputItemDeleteProps | — | 传递给 item delete 元素的 props。 |
dir | Direction | — | 标签输入的阅读方向。 |
modelValue | string[] | — | tags 输入的受控值。 |
defaultValue | string[] | — | tags 输入的未受控默认值。 |
addOnPaste | boolean | — | 粘贴时是否添加标签。 |
addOnTab | boolean | — | 是否在制表符处添加标签。 |
addOnBlur | boolean | — | 是否在焦点移开时添加标签。 |
duplicate | boolean | — | 是否允许重复的标签。 |
delimiter | string | RegExp | — | 用于添加标签的分隔符。 |
max | number | — | 标签最大数量。设为 0 表示无限制。 |
displayValue | ((value: string) => string) | "(value: string) => value" | 显示标签的值。当需要对值进行修改(如添加后缀)时,此功能非常有用。 |
id | string | — | 输入元素的 ID |
autofocus | boolean | — | 当值为 `true` 时,系统会自动聚焦于输入内容。 |
autocomplete | 'search' | 'name' | 'email' | 'tel' | 'url' | 'on' | 'off' | 'username' | 'current-password' | 'new-password' | 'one-time-code' | 'given-name' | 'family-name' | 'organization' | 'street-address' | 'address-level2' | 'postal-code' | 'country' | 'cc-name' | 'cc-number' | 'cc-exp' | 'cc-csc' | 'transaction-amount' | — | 输入字段的 autocomplete 属性。
除 `on` / `off` 开关外,还支持 HTML 自动填充标记(包括密码管理器的值,例如
`current-password` / `new-password`)。 |
disabled | boolean | — | 当值为 `true` 时,禁止用户与输入内容进行交互。 |
maxlength | number | — | 输入内容允许的最大字符数 |
minlength | number | — | 输入内容允许的最小字符数 |
pattern | string | — | 输入的 pattern 属性 |
placeholder | string | — | 输入的占位符 |
readonly | boolean | — | 当值为 `true` 时,输入内容为只读。 |
type | InputTypeHTMLAttribute | — | 输入元素的类型。 |
name | string | — | 字段的名称。作为名称/值对的一部分,与其所属表单一同提交。 |
required | boolean | — | 当值为 `true` 时,表示用户必须先设置该值,所属表单才能提交。 |
事件
| 事件名 | 参数 | 描述 |
|---|---|---|
update:modelValue | string[] | 当模型值发生变化时触发。 |
invalid | string | 当发生无效情况时触发。 |
addTag | string | 在出现 add 标签时生成。 |
removeTag | string | 当出现 remove 标签时生成。 |
插槽
| 插槽名 | 参数 | 描述 |
|---|---|---|
item | TagsInputCompactSlotProps | 自定义项目内容或完全替换项目。 |
TagsInputClear
属性
| 属性名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
type | ButtonType | 'button' | 按钮元素的类型。可以是 'button'、'submit' 或 'reset' 之一。 |
disabled | boolean | — | 该组件是否已禁用。 |
asChild | boolean | — | 将默认渲染元素替换为作为子元素传递的元素,并合并两者的属性及行为。 |
as | AsTag | Component | 'div' | 该组件应渲染为的元素或组件。可通过 `asChild` 进行覆盖。 |
TagsInputCompact
属性
| 属性名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
clearable | boolean | — | 是否渲染“清除”触发器。 |
controlProps | TagsInputControlProps | — | 转发给输入元素的属性。 |
clearProps | TagsInputClearProps | — | 转发给 clear 元素的 props。 |
itemProps | TagsInputItemProps | — | 转发给 item 元素的 props。 |
itemTextProps | TagsInputItemTextProps | — | 已将属性转发至 item 文本元素。 |
itemDeleteProps | TagsInputItemDeleteProps | — | 传递给 item delete 元素的 props。 |
dir | Direction | — | 标签输入的阅读方向。 |
modelValue | string[] | — | tags 输入的受控值。 |
defaultValue | string[] | — | tags 输入的未受控默认值。 |
addOnPaste | boolean | — | 粘贴时是否添加标签。 |
addOnTab | boolean | — | 是否在制表符处添加标签。 |
addOnBlur | boolean | — | 是否在焦点移开时添加标签。 |
duplicate | boolean | — | 是否允许重复的标签。 |
delimiter | string | RegExp | — | 用于添加标签的分隔符。 |
max | number | — | 标签最大数量。设为 0 表示无限制。 |
displayValue | ((value: string) => string) | "(value: string) => value" | 显示标签的值。当需要对值进行修改(如添加后缀)时,此功能非常有用。 |
id | string | — | 输入元素的 ID |
autofocus | boolean | — | 当值为 `true` 时,系统会自动聚焦于输入内容。 |
autocomplete | 'search' | 'name' | 'email' | 'tel' | 'url' | 'on' | 'off' | 'username' | 'current-password' | 'new-password' | 'one-time-code' | 'given-name' | 'family-name' | 'organization' | 'street-address' | 'address-level2' | 'postal-code' | 'country' | 'cc-name' | 'cc-number' | 'cc-exp' | 'cc-csc' | 'transaction-amount' | — | 输入字段的 autocomplete 属性。
除 `on` / `off` 开关外,还支持 HTML 自动填充标记(包括密码管理器的值,例如
`current-password` / `new-password`)。 |
disabled | boolean | — | 当值为 `true` 时,禁止用户与输入内容进行交互。 |
maxlength | number | — | 输入内容允许的最大字符数 |
minlength | number | — | 输入内容允许的最小字符数 |
pattern | string | — | 输入的 pattern 属性 |
placeholder | string | — | 输入的占位符 |
readonly | boolean | — | 当值为 `true` 时,输入内容为只读。 |
type | InputTypeHTMLAttribute | — | 输入元素的类型。 |
name | string | — | 字段的名称。作为名称/值对的一部分,与其所属表单一同提交。 |
required | boolean | — | 当值为 `true` 时,表示用户必须先设置该值,所属表单才能提交。 |
事件
| 事件名 | 参数 | 描述 |
|---|---|---|
update:modelValue | string[] | 当模型值发生变化时触发。 |
invalid | string | 当发生无效情况时触发。 |
addTag | string | 在出现 add 标签时生成。 |
removeTag | string | 当出现 remove 标签时生成。 |
插槽
| 插槽名 | 参数 | 描述 |
|---|---|---|
item | TagsInputCompactSlotProps | 自定义项目内容或完全替换项目。 |
TagsInputControl
TagsInputItem
属性
| 属性名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
value | string | — | 与该标签关联的值。 |
disabled | boolean | — | 是否禁用该标签项。 |
asChild | boolean | — | 将默认渲染元素替换为作为子元素传递的元素,并合并两者的属性及行为。 |
as | AsTag | Component | 'div' | 该组件应渲染为的元素或组件。可通过 `asChild` 进行覆盖。 |
TagsInputItemDelete
属性
| 属性名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
type | ButtonType | 'button' | 按钮元素的类型。可以是 'button'、'submit' 或 'reset' 之一。 |
disabled | boolean | — | 该组件是否已禁用。 |
asChild | boolean | — | 将默认渲染元素替换为作为子元素传递的元素,并合并两者的属性及行为。 |
as | AsTag | Component | 'div' | 该组件应渲染为的元素或组件。可通过 `asChild` 进行覆盖。 |
TagsInputItemText
属性
| 属性名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
asChild | boolean | — | 将默认渲染元素替换为作为子元素传递的元素,并合并两者的属性及行为。 |
as | AsTag | Component | 'div' | 该组件应渲染为的元素或组件。可通过 `asChild` 进行覆盖。 |
TagsInputRoot
属性
| 属性名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
dir | Direction | — | 标签输入的阅读方向。 |
modelValue | string[] | — | tags 输入的受控值。 |
defaultValue | string[] | — | tags 输入的未受控默认值。 |
addOnPaste | boolean | — | 粘贴时是否添加标签。 |
addOnTab | boolean | — | 是否在制表符处添加标签。 |
addOnBlur | boolean | — | 是否在焦点移开时添加标签。 |
duplicate | boolean | — | 是否允许重复的标签。 |
delimiter | string | RegExp | — | 用于添加标签的分隔符。 |
max | number | — | 标签最大数量。设为 0 表示无限制。 |
displayValue | ((value: string) => string) | "(value: string) => value" | 显示标签的值。当需要对值进行修改(如添加后缀)时,此功能非常有用。 |
id | string | — | 输入元素的 ID |
autofocus | boolean | — | 当值为 `true` 时,系统会自动聚焦于输入内容。 |
autocomplete | 'search' | 'name' | 'email' | 'tel' | 'url' | 'on' | 'off' | 'username' | 'current-password' | 'new-password' | 'one-time-code' | 'given-name' | 'family-name' | 'organization' | 'street-address' | 'address-level2' | 'postal-code' | 'country' | 'cc-name' | 'cc-number' | 'cc-exp' | 'cc-csc' | 'transaction-amount' | — | 输入字段的 autocomplete 属性。
除 `on` / `off` 开关外,还支持 HTML 自动填充标记(包括密码管理器的值,例如
`current-password` / `new-password`)。 |
disabled | boolean | — | 当值为 `true` 时,禁止用户与输入内容进行交互。 |
maxlength | number | — | 输入内容允许的最大字符数 |
minlength | number | — | 输入内容允许的最小字符数 |
pattern | string | — | 输入的 pattern 属性 |
placeholder | string | — | 输入的占位符 |
readonly | boolean | — | 当值为 `true` 时,输入内容为只读。 |
type | InputTypeHTMLAttribute | — | 输入元素的类型。 |
name | string | — | 字段的名称。作为名称/值对的一部分,与其所属表单一同提交。 |
required | boolean | — | 当值为 `true` 时,表示用户必须先设置该值,所属表单才能提交。 |
事件
| 事件名 | 参数 | 描述 |
|---|---|---|
update:modelValue | string[] | 当模型值发生变化时触发。 |
invalid | string | 当发生无效情况时触发。 |
addTag | string | 在出现 add 标签时生成。 |
removeTag | string | 当出现 remove 标签时生成。 |
注意事项
架构与对标差异
TagsInputRoot 经 useControllableState 持有值数组与集合注册表(每个 Item 通过 useCollectionItem 注册)。TagsInputControl 只负责提交:监听分隔符/粘贴/Enter/Tab/失焦,经根组件的 onAddValue 校验并归一化,被拒绝时通过 invalid 事件上报。键盘选中状态(selectedElement)位于根组件,控制层的 keydown 处理器据此用方向键移动选中(逻辑方向,RTL 感知)并用 Backspace/Delete/Home/End 删除标签。compact 以 index-value 键迭代值数组,使重复值保持稳定且避免原地值修补。多数对标库把标签作为某个 Select 的一种模式提供;Aria/styled 分离、逐部件 *Props 透传、插槽驱动的标签渲染以及 invalid/addTag/removeTag 事件面是本组件的差异点。
| 能力 | VeanUI | Ant Design | Element Plus | Mantine | Naive UI | shadcn |
|---|---|---|---|---|---|---|
| Aria/styled 分离 | ✅ | — | — | — | — | — |
| 受控 / 非受控 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Enter / 分隔符 / 粘贴添加 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| 键盘选中 + 删除 | ✅ | — | — | ✅ | — | — |
| RTL 方向反转 | ✅ | — | — | — | — | — |
max / duplicate 约束 | ✅ | ✅ | ✅ | ✅ | ✅ | — |
RegExp 分隔符 | ✅ | — | — | — | — | — |
逐部件 *Props 透传 | ✅ | — | — | — | — | — |
| 自定义标签插槽 | ✅ | ✅ | ✅ | ✅ | — | ✅ |
| 表单隐藏输入序列化 | ✅ | — | — | — | — | — |
本地化 aria-label | ✅ | ✅ | ✅ | ✅ | ✅ | — |
注意事项
- 值始终是
string[];除非设置duplicate,否则重复值会被拒绝。displayValue只改标签文案——存储值保持原始字符串。 max={0}表示不限制;任何正数都会限制数量,被拒绝的添加以尝试值触发invalid事件。addOnBlur会在失焦时提交待输入文本,但落在标签列表内删除/清空按钮上的失焦会被跳过(经aria-controls守卫)。addOnTab会阻止默认的 Tab 移出并改为提交;若需离开字段请关闭该选项或改为失焦提交。- compact 在
clearable不为false时渲染清空触发器;默认样式在根组件有值前将其隐藏。 - 受控模式(绑定
modelValue)下,父级必须更新绑定的数组——组件只触发update:modelValue。 readonly保持输入只读但允许标签删除与清空;需要完全禁止交互请使用disabled。- 输入的
aria-controls指向派生的标签列表容器 id({id}-tags-list);传入id可启用失焦守卫与表单 label 关联。
常见问题
如何只在 Enter 时添加标签?
默认即如此——输入值后按 Enter。Tab/失焦只有在开启 addOnTab/addOnBlur 时才提交。
如何让用户一次粘贴多个标签?
开启 addOnPaste 并保持默认 delimiter=","——粘贴 "a,b,c" 会创建三个标签。需要灵活拆分时使用 RegExp 分隔符,例如 :delimiter="/[,,]/"。
如何禁止重复标签?
重复值默认被拒绝并触发 invalid 事件(携带被拒绝的值)。传入 duplicate 可允许重复(compact 按位置为每项设键,重复项保持稳定)。
值如何进入表单提交?
设置 name 后,值序列化为视觉隐藏输入(每标签一个)。配合 required,空标签列表会将控件标记为无效。
为什么受控值没有更新?
受控模式下组件只触发 update:modelValue;必须重新绑定(如 v-model)使 prop 变化,否则 UI 停留在之前的数组上。