输入类

标签输入框

标签输入框能够将输入的内容生成标签。

代码演示

如何引入

<script setup lang="ts">
    import { TagInput } from '@fibrous/semi-ui-vue';
</script>

基本演示

敲击回车键后,输入内容将成为标签。标签内容如果为空串或者纯空格时,则会被过滤。

抖音

火山

西瓜视频

代码
<script setup lang="ts">
    import { TagInput } from '@fibrous/semi-ui-vue';
</script>

<template>
    <TagInput :defaultValue="['抖音', '火山', '西瓜视频']" placeholder="请输入..." @change="(v) => console.log(v)" />
</template>

批量添加

可以使用 separator 设置分隔符,来实现批量输入,它的默认值为英文逗号。支持多个分隔符以 string[] 格式传入。



代码
<script setup lang="ts">
    import { TagInput } from '@fibrous/semi-ui-vue';

    const separators = ['-', '/', '|', '++'];
</script>

<template>
    <TagInput placeholder="使用 - 进行批量输入" separator="-" @change="(v) => console.log(v)" />
    <br />
    <br />
    <TagInput placeholder="支持多个分隔符进行批量输入" :separator="separators" @change="(v) => console.log(v)" />
</template>

批量删除

可使用 showClear 设置是否支持一键删除所有标签和输入框内容。

抖音

火山

代码
<script setup lang="ts">
    import { TagInput } from '@fibrous/semi-ui-vue';

    const defaultValue = ['抖音', '火山'];
</script>

<template>
    <TagInput :defaultValue="defaultValue" placeholder="请输入..." showClear @change="(v) => console.log(v)" />
</template>

禁用

抖音

火山

西瓜视频

代码
<script setup lang="ts">
    import { TagInput } from '@fibrous/semi-ui-vue';
</script>

<template>
    <TagInput :defaultValue="['抖音', '火山', '西瓜视频']" disabled placeholder="请输入..." showClear />
</template>

尺寸大小

通过 size 控制标签输入框的大小尺寸,可选: small 、 default 、 large。





代码
<script setup lang="ts">
    import { TagInput } from '@fibrous/semi-ui-vue';
</script>

<template>
    <TagInput placeholder="small" size="small" />
    <br />
    <br />
    <TagInput placeholder="default" />
    <br />
    <br />
    <TagInput placeholder="large" size="large" />
</template>

不同校验状态样式

可以使用 validateStatus 设置不同校验状态的样式,它仅影响背景颜色等样式表现,可选值: default 、 warning 、 error。





代码
<script setup lang="ts">
    import { TagInput } from '@fibrous/semi-ui-vue';
</script>

<template>
    <TagInput placeholder="default" />
    <br />
    <br />
    <TagInput placeholder="warning" validateStatus="warning" />
    <br />
    <br />
    <TagInput placeholder="error" validateStatus="error" />
</template>

前缀 / 后缀

可以通过 #prefix 插槽传入输入框前缀,通过 #suffix 插槽传入输入框后缀,可以为文本或者图标等节点。
当插槽内容为纯文本或者 Icon 时,会自动带上左右间隔;若为自定义节点,则左右间隔为 0,如需可以在你传入的节点中自行设置。



Prefix




Suffix
代码
<script setup lang="ts">
    import { IconGift, IconVigoLogo } from '@fibrous/semi-icons-vue';
    import { TagInput } from '@fibrous/semi-ui-vue';
</script>

<template>
    <TagInput>
        <template #prefix>
            <IconVigoLogo />
        </template>
    </TagInput>
    <br />
    <br />
    <TagInput>
        <template #prefix>Prefix</template>
    </TagInput>
    <br />
    <br />
    <TagInput>
        <template #suffix>
            <IconGift />
        </template>
    </TagInput>
    <br />
    <br />
    <TagInput>
        <template #suffix>Suffix</template>
    </TagInput>
</template>

失焦后自动创建标签

可使用 addOnBlur,设置是否在 blur 事件触发时,将当前 input 的值自动创建成 tag。

抖音

火山

西瓜视频

代码
<script setup lang="ts">
    import { TagInput } from '@fibrous/semi-ui-vue';
</script>

<template>
    <TagInput addOnBlur :defaultValue="['抖音', '火山', '西瓜视频']" placeholder="请输入..." @change="(v) => console.log(v)" />
</template>

过滤重复标签

可使用 allowDuplicates,设置是否允许创建相同 tag,默认为 true。

抖音

火山

西瓜视频

代码
<script setup lang="ts">
    import { TagInput } from '@fibrous/semi-ui-vue';
</script>

<template>
    <TagInput :allowDuplicates="false" :defaultValue="['抖音', '火山', '西瓜视频']" placeholder="请输入..." @change="(v) => console.log(v)" />
</template>

输入限制

可使用 max 限制标签数量,添加后超出的部分不会成为标签,并且触发 exceed 事件。

可使用 maxLength 限制单个标签的最大长度,超出后将不允许再输入,并且触发 inputExceed 事件。

代码
<script setup lang="ts">
    import { TagInput, Toast } from '@fibrous/semi-ui-vue';

    const onExceed = (value: string[]) => {
        Toast.warning('超过 max');
        console.log(value);
    };
    const onInputExceed = (value: string) => {
        Toast.warning('超过 maxLength');
        console.log(value);
    };
</script>

<template>
    <TagInput :max="3" placeholder="最多输入3条标签.." @change="(v) => console.log(v)" @exceed="onExceed" />
    <TagInput :maxLength="5" placeholder="单个标签长度不超过5..." style="margin-top: 12px" @change="(v) => console.log(v)" @inputExceed="onInputExceed" />
</template>

限制标签展示数量

利用 maxTagCount 可以限制展示的标签数量,超出部分将以 +N 的方式展示。使用 showRestTagsPopover 可以设置在超出 maxTagCount 后,hover +N 是否显示 Popover,并且可以在 restTagsPopoverProps 属性中配置 Popover。

抖音

火山

+1
代码
<script setup lang="ts">
    import { TagInput } from '@fibrous/semi-ui-vue';

    const defaultValue = ['抖音', '火山', '西瓜视频'];
    const restTagsPopoverProps = { position: 'top' } as const;
</script>

<template>
    <TagInput :defaultValue="defaultValue" :maxTagCount="2" :restTagsPopoverProps="restTagsPopoverProps" showRestTagsPopover @change="(v) => console.log(v)" />
</template>

标签受控

可使用 v-model 绑定标签内容(modelValue 加 update:modelValue)实现标签内容受控。

抖音

代码
<script setup lang="ts">
    import { ref } from 'vue';
    import { TagInput } from '@fibrous/semi-ui-vue';

    const value = ref(['抖音']);
</script>

<template>
    <TagInput v-model="value" />
</template>

输入受控

可使用 v-model:inputValue 绑定输入框内容实现输入内容受控。

代码
<script setup lang="ts">
    import { ref } from 'vue';
    import { TagInput } from '@fibrous/semi-ui-vue';

    const value = ref('abc');
</script>

<template>
    <TagInput v-model:inputValue="value" />
</template>

回调

抖音

代码
<script setup lang="ts">
    import { TagInput } from '@fibrous/semi-ui-vue';
</script>

<template>
    <TagInput
        :defaultValue="['抖音']"
        placeholder="请输入..."
        showClear
        @add="(tag) => console.log(`onAdd,新增:${tag}`)"
        @blur="() => console.log('onBlur')"
        @change="(tag) => console.log(`onChange,当前标签数组:${tag}`)"
        @focus="() => console.log('onFocus')"
        @inputChange="(input) => console.log(`onInputChange,当前输入内容:${input}`)"
        @remove="(v, i) => console.log(`onRemove,移除:${v}, 序号:${i}`)"
    />
</template>

焦点管理

可以通过组件 ref 调用 blur() 和 focus() 方法对焦点进行管理。

抖音

火山

代码
<script setup lang="ts">
    import { useTemplateRef } from 'vue';
    import { Button, TagInput, type TagInputHandle } from '@fibrous/semi-ui-vue';

    const tagInput = useTemplateRef<TagInputHandle>('tagInput');
    const handleTagInputFocus = () => {
        tagInput.value?.focus();
    };
</script>

<template>
    <TagInput ref="tagInput" :defaultValue="['抖音', '火山']" />
    <Button style="margin-top: 10px" @click="handleTagInputFocus">点击按钮聚焦</Button>
</template>

自定义标签渲染

可以使用 #tag 作用域插槽整体替换单个标签,作用域参数为 { value, index, onClose }。插槽内容为空时不渲染该标签。onClose 收到事件时会调用其 preventDefault,把它直接绑到按钮的 @click 即可。

avatar夏可漫@semi.com
代码
<script setup lang="ts">
    import { ref } from 'vue';
    import { IconClose } from '@fibrous/semi-icons-vue';
    import { Avatar, Button, TagInput } from '@fibrous/semi-ui-vue';

    const value = ref(['夏可漫']);
    const fallbackAvatar = '/assets/placeholder/avatar-a.svg';
    const avatars = new Map([
        ['夏可漫', '/assets/root-web-sites/avatarDemo.jpeg'],
        ['申悦', '/assets/root-web-sites/bf8647bffab13c38772c9ff94bf91a9d.jpg'],
        ['曲晨一', '/assets/root-web-sites/bf8647bffab13c38772c9ff94bf91a9d.jpg'],
        ['文嘉茂', '/assets/root-web-sites/avatarDemo.jpeg'],
    ]);
    const itemStyle = {
        backgroundColor: 'var(--semi-color-info-light-default)',
        padding: '4px 8px',
        borderRadius: '12px',
        display: 'flex',
        alignItems: 'center',
        fontSize: '14px',
        marginRight: '10px',
    };
</script>

<template>
    <TagInput v-model="value">
        <template #tag="{ value: tag, onClose }">
            <div :style="itemStyle">
                <Avatar alt="avatar" size="extra-small" :src="avatars.get(tag) ?? fallbackAvatar" />
                <span :style="{ marginLeft: '8px' }">{{ `${tag}@semi.com` }}</span>
                <Button :aria-label="`移除 ${tag}`" size="small" theme="borderless" type="tertiary" @click="onClose">
                    <template #icon>
                        <IconClose size="small" />
                    </template>
                </Button>
            </div>
        </template>
    </TagInput>
</template>

拖拽排序

将 draggable设为 true,开启拖拽排序功能。拖拽功能开启后,聚焦 TagInput(点击或 Tab)后 Tag 可拖拽,焦点离开 TagInput 后 Tag 不可拖拽。拖拽句柄位于标签旁边,可用键盘操作,见下文「键盘和焦点」。值相同的标签也能分别拖拽排序。

抖音

火山

西瓜视频

代码
<script setup lang="ts">
    import { TagInput } from '@fibrous/semi-ui-vue';
</script>

<template>
    <TagInput :allowDuplicates="false" :defaultValue="['抖音', '火山', '西瓜视频']" draggable placeholder="请输入..." @change="(v) => console.log(v)" />
</template>

API 参考

Props

属性说明类型默认值
modelValue当前标签,配合 v-model 使用;undefined 时按非受控处理,null 表示受控且没有标签;从数组变回 undefined 时清空标签。string[] | nullundefined
defaultValue非受控时的初始标签。string[]semiGlobalDefault("TagInput", "defaultValue")
inputValue当前输入框内容,配合 v-model:inputValue 使用。stringsemiGlobalDefault("TagInput", "inputValue")
inputValueModifiersv-model:inputValue 的修饰符 trim 与 number,由 Vue 在使用 v-model:inputValue.trim 时传入。ModelModifiersundefined
disabled是否禁用。booleansemiGlobalDefault("TagInput", "disabled")
maxLength单个标签的最大长度。numbersemiGlobalDefault("TagInput", "maxLength")
max允许的标签最大数量。numbersemiGlobalDefault("TagInput", "max")
maxTagCount标签的最大展示数量,超出后以 +N 形式展示。numbersemiGlobalDefault("TagInput", "maxTagCount")
showRestTagsPopover超过 maxTagCount 后,hover 到 +N 时是否通过 Popover 显示剩余标签。booleansemiDefault("TagInput", "showRestTagsPopover", true)
restTagsPopoverProps+N 浮层的 Popover 属性,不含 content。PopoverPropssemiGlobalDefault("TagInput", "restTagsPopoverProps")
showContentTooltip标签文字被截断时,hover 标签是否通过浮层显示全部内容;传对象时可指定浮层类型与透传给浮层的属性。boolean | ShowTooltipsemiDefault("TagInput", "showContentTooltip", true)
allowDuplicates是否允许添加相同的标签。booleansemiDefault("TagInput", "allowDuplicates", true)
addOnBlur焦点离开组件时,是否将输入框内容自动创建为标签。booleansemiDefault("TagInput", "addOnBlur", false)
draggable是否开启拖拽排序。booleansemiDefault("TagInput", "draggable", false)
expandRestTagsOnClick不可拖拽时,焦点进入组件(点击、Tab 或 focus 方法)后是否展开被折叠的标签。booleansemiDefault("TagInput", "expandRestTagsOnClick", true)
placeholder没有标签时输入框的占位文字。stringsemiGlobalDefault("TagInput", "placeholder")
insetLabelId内嵌标签容器的 id,供 aria-labelledby 引用。stringundefined
separator批量输入时的分隔符,可传多个;传 null 不拆分。string | string[] | nullsemiDefault("TagInput", "separator", ",")
showClear是否显示一键清除所有标签和输入内容的按钮。booleansemiDefault("TagInput", "showClear", false)
size尺寸,可选 small、default、large。SizesemiDefault("TagInput", "size", "default")
split自定义分隔符处理函数,返回拆分后的字符串数组。Type((originString: string, separators: string | string[] | null) => string[])undefined
validateStatus校验状态,仅影响样式;error 时输入框带 aria-invalid。TagInputValidateStatussemiDefault("TagInput", "validateStatus", "default")
autofocus挂载时是否自动聚焦输入框并激活组件,禁用时不生效。booleansemiDefault("TagInput", "autofocus", false)
preventScroll组件内 focus 方法是否阻止浏览器滚动到新聚焦的元素。booleansemiGlobalDefault("TagInput", "preventScroll")

Events

事件说明回调参数
update:modelValue标签变化时触发,供 v-model 使用。[value: string[]]
change标签变化时触发。[value: string[]]
update:inputValue输入框内容变化时触发,供 v-model:inputValue 使用。[value: string]
inputChange输入框内容变化时触发;事件对象可能是 input、keydown、focusout、click 或 composition 事件,读取专有字段前先按 event.type 收窄。[value: string, event: Event]
add添加标签时触发,参数为新增的标签。[addedValue: string[]]
remove移除标签时触发,参数为被移除的标签及其序号。[removedValue: string, index: number]
exceed添加后超过 max 时触发,参数为截断前的完整列表。[value: string[]]
inputExceed输入内容超过 maxLength 时触发,参数为被拒绝的输入内容。[value: string]
focus焦点从组件外进入时触发一次;焦点在输入框、标签、清除按钮之间移动不触发。[event: FocusEvent]
blur焦点离开整个组件时触发一次;焦点在组件内部移动不触发。[event: FocusEvent]
keydown输入框按键时在组件处理之后触发;其中调用 preventDefault 不会阻止 Enter 添加标签。[event: KeyboardEvent]

Slots

插槽说明作用域参数
prefix输入框前缀,文字或图标会自动带上左右间隔。any
suffix输入框后缀,文字或图标会自动带上左右间隔。any
insetLabel内嵌标签,显示在输入框前;同时提供 prefix 插槽时只显示 prefix。显示时,没有 aria-label、aria-labelledby 的输入框以它命名。any
clearIcon替换清除按钮中的图标;内容为空时使用默认图标。any
tag整体替换单个标签,作用域参数为 value、index 和关闭函数 onClose;内容为空时不渲染该标签。TagInputTagSlotProps

Exposes

名称说明类型
focus使输入框获得焦点并激活组件。Type() => void
blur使组件内任意持有焦点的元素(输入框、标签、清除按钮)失去焦点。Type() => void

未声明的属性中,id、name、role 与 aria-* 里描述输入框的部分(aria-label、aria-labelledby、aria-describedby、aria-invalid、aria-required、aria-controls、aria-expanded、aria-haspopup、aria-autocomplete、aria-activedescendant、aria-errormessage)落在内部输入框上;class、style、data-*、title 等其余属性落在根元素上,根元素上的 @click 与组件内部的激活处理都会执行。focus、blur 事件的对象是根元素上的 focusin、focusout,声明为组件事件后,@change、@focus、@blur、@keydown 只收到组件事件,不再收到冒泡上来的原生事件。

Accessibility

ARIA

  • TagInput 支持传入 aria-label 来表示该 TagInput 作用,它落在输入框上,根节点不带;
  • 根节点的角色是 group,把输入框、标签和清除按钮作为一个整体;禁用时根节点带 aria-disabled="true",未禁用时不带;
  • 禁用时输入框是原生 disabled,标签是静态文本,不带 aria-label;validateStatus 为 error 时输入框带 aria-invalid,显式传入的 aria-invalid 优先;
  • showClear 开启、有标签或输入内容且未禁用时,渲染清除按钮。它是原生 button,名称为语言包中的「清空标签输入」,在 hover 或焦点位于 TagInput 内时显示。从输入框按 Tab 移到清除按钮,Enter 或 Space 清空,效果与鼠标点击相同,清空后焦点回到输入框;也可在空输入框中按 Backspace 删除最后一个标签。

键盘和焦点

  • 输入框中按 Enter 把输入内容添加为标签,空输入时不做任何事。Enter 一律阻止默认行为,TagInput 位于表单内时不会提交表单。
  • 输入框为空时按 Backspace 删除最后一个标签。
  • 可关闭的标签是 role="button" 的 Tab 停靠点,位于输入框之前;聚焦标签后按 Backspace 或 Delete 关闭它,标签被移除后焦点回到输入框。禁用时标签是静态文本,不可聚焦。
  • 开启 showClear 且有内容时,清除按钮在输入框之后:Tab 到达,Enter 或 Space 清空,焦点回到输入框。
  • 激活状态跟随焦点:焦点从组件外进入(点击、Tab 或 focus())即激活,焦点离开整个组件即退出。焦点在输入框、标签与清除按钮之间移动不算离开组件,不触发 focus、blur,也不会触发 addOnBlur。激活时,被 maxTagCount 折叠的标签会展开(expandRestTagsOnClick 为 false 时不展开)。
  • 开启 draggable 并激活后,每个标签前出现拖拽句柄,句柄是独立的 Tab 停靠点,位于它所属的标签之前。聚焦句柄后按 Space 拿起,方向键移动,Space 放下,读屏会播报拿起、移动与放下。

设计变量

下表是组件内部的 Sass 变量,不能覆写。定制样式请覆写 --semi-* CSS 变量(见 Design Token 页),或写不分层的 CSS 规则。
Sass 变量值用法
$transition_duration-tagInput-bgvar(--semi-transition_duration-none)标签输入框-背景色-动画持续时间
$transition_function-tagInput-bgvar(--semi-transition_function-easeIn)标签输入框-背景色-过渡曲线
$transition_delay-tagInput-bgvar(--semi-transition_delay-none)标签输入框-背景色-延迟时间
$transition_duration-tagInput-bordervar(--semi-transition_duration-none)标签输入框-边框-动画持续时间
$transition_function-tagInput-bordervar(--semi-transition_function-easeIn)标签输入框-边框-过渡曲线
$transition_delay-tagInput-bordervar(--semi-transition_delay-none)标签输入框-边框-延迟时间
$color-tagInput-icon-defaultvar(--semi-color-text-2)标签输入框图标颜色 - 默认
$color-tagInput-icon-hovervar(--semi-color-primary-hover)标签输入框图标颜色 - 悬浮
$color-tagInput_disabled-text-defaultvar(--semi-color-disabled-text)标签输入框禁用文字颜色
$color-tagInput_disabled-bgvar(--semi-color-disabled-fill)标签输入框禁用背景色
$color-tagInput_default-bg-focusvar(--semi-color-fill-0)标签输入框背景颜色 - 选中态
$color-tagInput-border-defaulttransparent标签输入框描边颜色 - 默认
$color-tagInput-border-hovertransparent标签输入框描边颜色 - 悬浮
$color-tagInput-border-focusvar(--semi-color-focus-border)标签输入框描边颜色 - 选中态
$color-tagInput_prefix-defaultvar(--semi-color-text-2)标签输入框 prefix 颜色
$color-tagInput_suffix-defaultvar(--semi-color-text-2)标签输入框 suffix 颜色
$color-tagInput_default-bg-defaultvar(--semi-color-fill-0)标签输入框背景颜色 - 默认
$color-tagInput_default-bg-hovervar(--semi-color-fill-1)标签输入框背景颜色 - 悬浮
$color-tagInput_warning-bg-defaultvar(--semi-color-warning-light-default)警告标签输入框背景颜色 - 默认
$color-tagInput_warning-border-defaultvar(--semi-color-warning-light-default)警告标签输入框描边颜色 - 默认
$color-tagInput_warning-bg-hovervar(--semi-color-warning-light-hover)警告标签输入框背景颜色 - 悬浮
$color-tagInput_warning-border-hovervar(--semi-color-warning-light-hover)警告标签输入框描边颜色 - 悬浮
$color-tagInput_warning-bg-focusvar(--semi-color-warning-light-default)警告标签输入框背景颜色 - 选中
$color-tagInput_warning-border-focusvar(--semi-color-warning)警告标签输入框描边颜色 - 选中
$color-tagInput_danger-bg-defaultvar(--semi-color-danger-light-default)危险标签输入框背景颜色 - 默认
$color-tagInput_danger-border-defaultvar(--semi-color-danger-light-default)危险标签输入框描边颜色 - 默认
$color-tagInput_danger-bg-hovervar(--semi-color-danger-light-hover)危险标签输入框背景颜色 - 悬浮
$color-tagInput_danger-border-hovervar(--semi-color-danger-light-hover)危险标签输入框描边颜色 - 悬浮
$color-tagInput_danger-bg-focusvar(--semi-color-danger-light-default)危险标签输入框背景颜色 - 选中
$color-tagInput_danger-border-focusvar(--semi-color-danger)危险标签输入框描边颜色 - 选中
$color-tagInput_sortable_item_over-bgvar(--semi-color-primary)拖拽经过的元素前竖线背景色
$color-tagInput_handler-icon-defaultvar(--semi-color-text-2)可拖拽的标签拖拽按钮颜色
$spacing-tagInput_small-Y1px小尺寸标签输入框标签顶部外边距
$spacing-tagInput_default-Y$spacing-super-tight默认尺寸标签输入框标签顶部外边距
$spacing-tagInput_large-Y3px大尺寸标签输入框标签顶部外边距
$spacing-tagInput_wrapper_n_paddingX$spacing-tight标签输入框标签容器水平内边距
$spacing-tagInput_drag_handler-marginRight4px拖拽handler icon的右外边距
$spacing-tagInput_tag_icon_paddingLeft4pxtag中有handler icon时tag的左内边距
$spacing-tagInput_prefix_suffix-marginX$spacing-base-tight标签输入框 prefix/suffix 水平外边距
$height-tagInput-large$height-control-large大尺寸标签输入框高度
$height-tagInput-default$height-control-default默认尺寸标签输入框高度
$height-tagInput-small$height-control-small小尺寸标签输入框高度
$height-tagInput_input_small20px小尺寸标签输入框Input框高度
$height-tagInput_input_default24px默认尺寸标签输入框Input框高度
$height-tagInput_input_large24px大尺寸标签输入框Input框高度
$width-tagInput-clear-medium$width-icon-medium * 2标签输入框清空按钮宽度
$width-tagInput-border-default$border-thickness-control标签输入框描边描边宽度 - 默认
$width-tagInput-border-hover$width-tagInput-border-default标签输入框描边描边宽度 - 悬浮
$width-tagInput-border-focus$border-thickness-control-focus标签输入框描边宽度 - 选中态
$width-tagInput_sortable_item_over2px拖拽经过的元素前竖线宽度
$radius-tagInputvar(--semi-border-radius-small)标签输入框圆角
$font-tagInput_prefix_suffix-fontWeight$font-weight-bold标签输入框 prefix/suffix 文字字重