Skip to content

Sender 消息输入框

重大版本升级 v0.4.0

Sender 在 v0.4.0 进行了重大升级。

从 v0.3.0 升级?选择你的迁移方式:

方式一:快速迁移(推荐) 🚀

方式二:完全升级 📖

  • 直接升级到 v0.4.0,使用全新 API
  • 需要调整代码,但能获得更好的功能和性能
  • ⚠️ 部分 API 已被移除,详见下方 已移除的 API
  • 👉 查看 完整迁移方案

新项目: 直接使用下方 v0.4.0 的 API 和示例即可。

Sender 是一个高度可组合的聊天输入组件,支持文本输入、自动联想、提及功能、模板填充、语音输入和文件上传等多种功能。

  • 代码示例 - 输入模式、状态控制、字数限制
  • 输入增强 - 模板填充、提及功能、智能联想、语音输入、文件上传
  • 交互定制 - 取消操作、提交方式、快捷键、自定义插槽、方法调用
  • 样式配置 - 主题支持、组件尺寸

代码示例

输入模式

Sender 支持单行和多行两种输入模式,通过 mode 属性控制。

单行模式自动切换

在单行模式下,当输入内容超出宽度时,会自动切换为多行模式。

submitType="enter" 时,按 Ctrl+EnterShift+Enter 也会自动切换为多行模式并换行。

loading

状态控制

通过 loadingdisabled 属性控制组件状态。加载状态下可点击图标取消操作。

loading

内容管理

字数限制

通过 maxLengthshowWordLimit 属性实现字数限制和统计。

超出限制行为

超出字数限制时,不会自动截断内容,但会以红色标示真实字数,且无法提交。

loading

输入增强

Sender 采用可插拔的扩展架构,通过 extensions prop 灵活添加功能。所有扩展都支持响应式数据自动同步。

扩展使用

提供两种集成方式:

typescript
import { TrSender } from '@opentiny/tiny-robot'

// 便捷函数(推荐)
TrSender.mention(mentions, '@')
TrSender.suggestion(suggestions) // 不过滤
TrSender.suggestion(suggestions, { filterFn: customFilter }) // 自定义过滤
TrSender.template(templates)

// 标准配置(用于复杂场景)
TrSender.Mention.configure({ items: mentions, char: '@', allowSpaces: false })
TrSender.Suggestion.configure({ items: suggestions, filterFn: customFilter })

模板编辑

使用 Template 扩展实现模板填充功能,支持动态设置模板内容,光标自动聚焦到第一个可编辑字段。

响应式数据

通过 items 配置项传入响应式 ref,模板数据变化时会自动更新编辑器内容。

loading

配置详见扩展属性 - Template

提及功能

使用 Mention 扩展实现 @提及功能,输入触发字符(默认 @)触发提及选择,快速引用预设的助手或对象,支持键盘导航和搜索过滤。

自定义触发字符

支持自定义触发字符,例如使用 # 代替 @。配置 char: '#' 后,输入 # 即可触发提及列表,选中后显示为 #标签名 的格式。

删除提及

Backspace 删除提及项时会保留触发字符(如 @#),可继续选择其他项。

loading

配置详见扩展属性 - Mention

智能联想

使用 Suggestion 扩展实现智能联想功能,支持键盘导航(↑↓ 选择,Enter 确认)和自动补全提示。

自动补全提示

选中建议项时,输入框会以灰色文本显示剩余部分,并显示 "TAB" 提示,按 Tab 键快速应用补全。

基础用法

不传 filterFn 时,直接显示所有建议项,不做任何过滤。

loading

自定义过滤

通过 filterFn 自定义过滤逻辑,实现模糊匹配、前缀匹配等。

loading

高亮模式

支持三种高亮模式,满足不同的使用场景:

  1. 自动匹配:不设置 highlights,自动高亮与输入内容匹配的部分
  2. 精确指定:通过 highlights 数组精确指定需要高亮的文本片段
  3. 自定义函数:通过 highlights 函数完全控制高亮逻辑,实现复杂的高亮规则
loading

配置详见扩展属性 - Suggestion

语音输入

通过 VoiceButton 组件实现语音输入功能,支持浏览器内置语音识别和第三方语音识别服务。

组件化设计

语音输入功能通过独立的 VoiceButton 组件实现,可按需添加到 footer 插槽中,无需额外配置。

基础语音识别

使用浏览器内置的语音识别功能,支持混合输入和连续识别两种模式。

loading

自定义语音服务

支持集成第三方语音识别服务(如阿里云、百度、Azure 等)。

loading

参考实现

speechHandlers.ts 提供了阿里云一句话识别和实时识别的完整示例,包括录音处理、API 调用、流式识别等。

自定义录音 UI

支持完全自定义语音录制界面,适用于移动端按住说话等场景。

loading

配置详见VoiceButton 属性

按钮配置

默认按钮配置

通过 defaultActions 属性统一配置默认按钮(Clear、Submit)的状态和提示。

loading

增强按钮

通过插槽添加增强按钮(Upload、Voice 等),每个按钮都有独立的配置。

loading

配置详见UploadButton 属性VoiceButton 属性

交互定制

取消操作

在 loading 状态下,点击停止按钮会触发 cancel 事件,用于取消正在进行的操作(如 AI 响应)。

loading

提交方式

通过 submitType 属性控制提交快捷键,支持 enterctrlEntershiftEnter 三种方式。

loading

快捷键参考

快捷键功能适用条件
Enter提交内容 / 换行submitType="enter"
Ctrl+Enter提交内容 / 换行submitType="ctrlEnter" / submitType="enter"
Shift+Enter提交内容 / 换行submitType="shiftEnter" / submitType="enter"
Tab选中联想项联想开启时
Esc关闭联想联想开启时
↑ / ↓导航联想项联想开启时

换行与提交行为说明

  • submitType="enter" 时:按 Enter 提交,按 Ctrl+EnterShift+Enter 换行
  • submitType="ctrlEnter" 时:按 Ctrl+Enter 提交,按 Enter 换行
  • submitType="shiftEnter" 时:按 Shift+Enter 提交,按 Enter 换行

在单行模式下使用换行快捷键时,会自动切换为多行模式。

自定义选中按键

通过 activeSuggestionKeys 可自定义选中联想项的按键。默认支持 EnterTab

自定义插槽

Sender 提供了多个插槽位置,方便扩展功能:

  • header - 顶部区域,可添加标题、提示信息等
  • prefix - 输入框前缀区域,可添加图标、标签等(位于输入框内部)
  • footer - 底部左侧区域,可添加功能按钮
  • footer-right - 底部右侧区域,可添加操作按钮

插槽作用域

footerfooter-right 插槽提供了作用域数据,包括 editorhasContentdisabledloading 等状态,以及 focusinsertappendreplace 等操作方法,可用于实现自定义功能按钮。

loading

方法调用

loading

样式配置

主题支持

主题继承

主题会根据父级 ThemeProvider 的配置自动继承,无需重复设置。

组件尺寸

通过 size 属性控制组件尺寸,支持 normal(默认)和 small(紧凑)两种尺寸。

loading

Props

属性名说明类型默认值
modelValue绑定值(v-model)string''
defaultValue默认值(非响应式)string''
placeholder输入框占位文本string'请输入内容...'
mode输入模式'single' | 'multiple''single'
size 0.4.0组件尺寸'normal' | 'small''normal'
disabled是否禁用booleanfalse
loading是否加载中booleanfalse
autofocus自动获取焦点booleanfalse
autoSize自动调整高度boolean | { minRows: number, maxRows: number }{ minRows: 1, maxRows: 5 }
clearable是否可清空booleanfalse
maxLength最大输入长度numberInfinity
showWordLimit是否显示字数统计booleanfalse
submitType提交方式'enter' | 'ctrlEnter' | 'shiftEnter''enter'
stopText停止按钮文字string'停止响应'
defaultActions 0.4.0默认操作按钮配置DefaultActionsundefined
extensions 0.4.0扩展列表 (Template, Mention, Suggestion 等)Extension[][]

扩展系统

使用 extensions 属性配置功能扩展,提供灵活的配置和完整的类型支持。

Template

模板填充功能扩展,支持动态设置模板内容。

typescript
// 便捷函数
TrSender.template(templates)

// 标准配置
TrSender.Template.configure({ items: templates })
配置项类型说明
itemsTemplateItem[] | Ref<TemplateItem[]>模板数据列表

Mention

@提及功能扩展,支持快速引用预设的助手或对象,支持自定义触发字符。

typescript
// 便捷函数(使用默认 '@' 触发)
TrSender.mention(mentions)

// 便捷函数(自定义触发字符)
TrSender.mention(mentions, '#') // 使用 '#' 触发

// 标准配置
TrSender.Mention.configure({ items: mentions, char: '@', allowSpaces: false })
配置项类型默认值说明
itemsMentionItem[] | Ref<MentionItem[]>[]提及项列表,支持响应式 ref
charstring'@'触发字符,支持任意字符(如 '@''#''!' 等)
allowSpacesbooleanfalse是否允许在触发字符后输入空格
onSelectFunction-选中提及项时的回调函数

Suggestion

智能联想功能扩展,支持自动过滤、自定义过滤和多种高亮方式。

typescript
// 便捷函数
TrSender.suggestion(suggestions) // 不过滤,显示所有项
TrSender.suggestion(suggestions, { filterFn: customFilter }) // 自定义过滤

// 标准配置
TrSender.Suggestion.configure({
  items: suggestions,
  filterFn: (items, query) => items.filter((item) => item.content.includes(query)),
  showAutoComplete: true,
})
配置项类型默认值说明
itemsSuggestionItem[] | Ref<SuggestionItem[]>[]建议项列表
filterFnFunctionundefined过滤函数(不传则不过滤)
showAutoCompletebooleantrue自动补全
activeSuggestionKeysstring[]['Enter']激活按键
popupWidthnumber | string400弹窗宽度
onSelect(item) => void | false-选中回调,返回 false 阻止默认回填

popupWidth 格式

支持数字(如 500)、百分比(如 '100%')、CSS 单位(如 '20rem'

高亮方式

typescript
{ content: 'ECS-云服务器' }  // 自动匹配
{ content: 'RDS-数据库', highlights: ['RDS', '数据库'] }  // 精确指定
{ content: 'OSS-存储', highlights: (text, query) => [...] }  // 自定义函数

onSelect 回调

选中建议项时触发,返回 false 可阻止默认回填行为:

typescript
// 默认行为:自动回填
onSelect: (item) => {
  console.log('Selected:', item)
  // 不返回 false,内容会自动回填到编辑器
}

// 阻止默认回填并自定义
onSelect: (item) => {
  editor.commands.setContent(`前缀-${item.content}-后缀`)
  return false // 阻止默认回填
}

// 条件性阻止
onSelect: (item) => {
  if (item.data?.needsValidation) {
    validateAndFill(item)
    return false
  }
  // 否则使用默认回填
}

回调参数

item 包含完整的 SuggestionItem 信息(contentlabeldatahighlights),可用于业务逻辑处理。

UploadButton

文件上传按钮组件,支持文件类型过滤、大小限制和数量限制。

属性名说明类型默认值
disabled是否禁用booleanfalse
accept接受的文件类型string'*'
multiple是否支持多选booleanfalse
reset选择后是否重置 inputbooleantrue
maxSize文件大小限制(MB)number-
maxCount最大文件数量number-
tooltipTooltipTooltipContent-
tooltipPlacementTooltip 位置TooltipPlacement'top'
icon自定义图标VNode | ComponentIconUpload
size按钮尺寸number | string32

VoiceButton

语音输入按钮组件,支持浏览器内置语音识别和第三方语音识别服务。

属性名说明类型默认值
icon自定义图标VNode | ComponentIconVoice
disabled是否禁用booleanfalse
size按钮尺寸'small' | 'normal''normal'
tooltipTooltipTooltipContent-
tooltipPlacementTooltip 位置TooltipPlacement'top'
speechConfig语音配置SpeechConfig-
autoInsert是否自动插入识别结果到编辑器booleantrue
onButtonClick按钮点击拦截器Function-

Slots

插槽名称描述作用域参数
header头部插槽,位于输入框上方-
prefix前缀插槽,位于输入框左侧-
content 0.4.0内容插槽,用于完全自定义编辑器内容{ editor }
actions-inline 0.4.0单行模式下的操作按钮区域-
footer底部自定义区域{ editor, hasContent, disabled, loading }
footer-right底部右侧区域-

Events

事件名说明回调参数
update:modelValue内容更新(value: string)
submit提交内容(text: string, data?: StructuredData)
clear清空内容()
focus获得焦点(event: FocusEvent)
blur失去焦点(event: FocusEvent)
input输入变化(value: string)
cancel 0.4.0在 loading 状态下点击停止按钮时触发,用于取消正在进行的操作(如 AI 响应)()

UploadButton Events

事件名说明回调参数
select文件选择成功(files: File[])
error文件验证失败(error: Error, file?: File)

VoiceButton Events

事件名说明回调参数
speech-start开始录音()
speech-interim中间结果(transcript: string)
speech-final最终结果(transcript: string)
speech-end结束录音(transcript?: string)
speech-error识别错误(error: Error)

Methods

方法名说明参数返回值
focus使输入框获取焦点-void
blur使输入框失去焦点-void
clear清空输入内容-void
submit手动触发提交-void
setContent 0.4.0设置编辑器内容(content: string)void
getContent 0.4.0获取编辑器内容-string
cancel 0.4.0手动触发取消-void

UploadButton Methods

方法名说明参数返回值
open打开文件选择器-void

VoiceButton Methods

方法名说明参数返回值
start开始录音-void
stop停止录音-void

Types

typescript
// DefaultActions 默认按钮配置
interface DefaultActions {
  submit?: {
    disabled?: boolean // 是否禁用提交按钮
    tooltip?: string // 提交按钮提示文本
    tooltipPlacement?: TooltipPlacement // Tooltip 位置
  }
  clear?: {
    disabled?: boolean // 是否禁用清空按钮
    tooltip?: string // 清空按钮提示文本
    tooltipPlacement?: TooltipPlacement // Tooltip 位置
  }
}

// ToolTip 内容
type TooltipContent = string | (() => string | VNode)

// Tooltip 位置
type TooltipPlacement =
  | 'top'
  | 'top-start'
  | 'top-end'
  | 'bottom'
  | 'bottom-start'
  | 'bottom-end'
  | 'left'
  | 'left-start'
  | 'left-end'
  | 'right'
  | 'right-start'
  | 'right-end'

// SpeechConfig 语音配置
interface SpeechConfig {
  customHandler?: SpeechHandler // 自定义语音处理器
  lang?: string // 识别语言,默认浏览器语言
  continuous?: boolean // 是否持续识别
  interimResults?: boolean // 是否返回中间结果
  autoReplace?: boolean // 是否自动替换内容
  onVoiceButtonClick?: (isRecording, preventDefault) => void // 按钮点击拦截器
}

// 模板项(联合类型)
type TemplateItem =
  | {
      id?: string // 模板 ID(可选,组件会自动生成)
      type: 'text' // 类型:普通文本
      content: string // 内容
    }
  | {
      id?: string // 模板 ID(可选,组件会自动生成)
      type: 'block' // 类型:模板块(可编辑)
      content: string // 内容
    }
  | {
      id?: string // 模板 ID(可选,组件会自动生成)
      type: 'select' // 类型:选择器
      content: string // 内容(选中的值)
      placeholder?: string // 占位文字(仅用于输入配置)
      options?: SelectOption[] // 选项列表(仅用于输入配置)
      value?: string // 当前选中的值(仅用于输入配置)
    }

// 选择器选项
interface SelectOption {
  label: string // 显示文本
  value: string // 选择后的值
}

// 提及项(输入配置)
interface MentionItem {
  label: string // 显示名称,如 "小小画家"
  value: string // 关联值
}

// 提及项(输出结构)
type MentionStructuredItem = 
  | { type: 'text', content: string }
  | { type: 'mention', content: string, value: string }

// 建议项
interface SuggestionItem {
  content: string // 建议项内容(必填)
  highlights?: string[] | HighlightFunction // 高亮方式(可选)
}

// 高亮函数类型
type HighlightFunction = (suggestionText: string, inputText: string) => SuggestionTextPart[]

// 高亮文本片段
interface SuggestionTextPart {
  text: string // 文本片段
  isMatch: boolean // 是否高亮
}

// 结构化数据(submit 事件返回)
type StructuredData = TemplateItem[] | MentionStructuredItem[]

// 输入模式
type InputMode = 'single' | 'multiple'

// 扩展类型
import type { Extension } from '@tiptap/core'

CSS 变量

Sender 组件提供了丰富的 CSS 变量用于自定义样式。

基础颜色

变量名说明
--tr-sender-bg-color背景颜色
--tr-sender-text-color文本颜色
--tr-sender-placeholder-color占位符颜色
--tr-sender-button-hover-bg按钮悬停背景

尺寸和间距

变量名说明
--tr-sender-font-size字体大小
--tr-sender-line-height行高
--tr-sender-border-radius圆角大小
--tr-sender-padding内边距
--tr-sender-gap元素间距
--tr-sender-footer-gap底部元素间距

Header 区域

变量名说明
--tr-sender-header-padding头部内边距
--tr-sender-header-divider-inset头部分割线缩进
--tr-sender-multi-main-padding多行模式主区域内边距

Footer 区域

变量名说明
--tr-sender-footer-padding底部内边距

前缀和操作区

变量名说明
--tr-sender-prefix-padding-right前缀区域右内边距
--tr-sender-actions-padding-right操作区域右内边距

按钮

变量名说明
--tr-sender-button-size按钮尺寸
--tr-sender-button-size-submit提交按钮尺寸

尺寸变体

所有变量都支持通过 size 属性自动切换。当 size="small" 时,组件会使用对应的 -small 变体(如 --tr-sender-font-size-small)。

使用示例

css
/* 自定义背景色 */
.my-sender {
  --tr-sender-bg-color: #f5f5f5;
  --tr-sender-text-color: #333;
}

/* 自定义按钮尺寸 */
.my-sender {
  --tr-sender-button-size: 40px;
  --tr-sender-button-size-submit: 44px;
}

已移除的 API

以下 API 在 v0.4.0 中已被移除,请参考替代方案进行迁移。

Props

属性名原说明替代方案
allowSpeech是否开启语音输入使用 VoiceButton 组件
speech语音识别配置使用 VoiceButton.speechConfig
allowFiles是否允许文件上传使用 UploadButton 组件
buttonGroup按钮组配置使用 defaultActions 和插槽
theme主题样式使用 ThemeProvider 包裹
suggestions输入建议列表使用 Suggestion 扩展
suggestionPopupWidth建议弹窗宽度使用 Suggestion 扩展配置
activeSuggestionKeys激活建议项的按键使用 Suggestion 扩展配置
templateData模板数据使用 Template 扩展

Slots

插槽名称替代方案
actions改用 actions-inline
footer-left改用 footer
decorativeContent改用 disabled + content

Events

事件名替代方案
change使用 blur 事件
files-selected使用 UploadButtonselect 事件
speech-start使用 VoiceButtonspeech-start 事件
speech-end使用 VoiceButtonspeech-end 事件
speech-interim使用 VoiceButtonspeech-interim 事件
speech-error使用 VoiceButtonspeech-error 事件
suggestion-select使用 Suggestion 扩展的 onSelect 回调

Methods

方法名替代方案
startSpeech使用 VoiceButton.start()
stopSpeech使用 VoiceButton.stop()
activateTemplateFirstField自动处理,无需调用