Skip to content

Sender 消息输入框 ​

重大版本升级 v0.4

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

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

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

方式二:完全升级 📖

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

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

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

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

代码示例 ​

输入模式 ​

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

单行模式自动切换

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

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

loading

状态控制 ​

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

loading

内容管理 ​

字数限制 ​

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

超出限制行为

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

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
结构化数据:submit 事件 - 结构化数据说明

智能联想 ​

使用 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

附件、图片等内容通常由独立组件维护,不会写入 Sender 的编辑器文本。当用户只上传附件或图片、不输入额外文本时,Sender 无法感知这部分外部内容,会因输入框为空而不显示默认发送按钮。此时可以通过 hasExternalContent 告诉 Sender 当前存在外部可提交内容,例如 :has-external-content="files.length > 0"。设置后,即使输入框为空,默认发送按钮也会显示,快捷键提交和 submit() 方法也会按有内容处理。默认清空按钮仍只根据编辑器文本显示;外部内容数据由业务侧维护,建议由附件或图片列表提供自己的删除入口。

配置详见:UploadButton 属性、VoiceButton 属性

交互定制 ​

取消操作 ​

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

loading

提交方式 ​

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

loading

快捷键参考 ​

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

换行与提交行为说明

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

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

自定义选中按键

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

自定义插槽 ​

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

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

插槽作用域

footer 和 footer-right 插槽提供了作用域数据,包括 editor、hasContent、disabled、loading 等状态,以及 focus、insert、append、replace 等操作方法,可用于实现自定义功能按钮。

loading

方法调用 ​

loading

样式配置 ​

主题支持 ​

主题继承

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

组件尺寸 ​

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

loading

Props ​

属性名说明类型默认值
modelValue绑定值(v-model)string''
defaultValue默认值(非响应式)string''
placeholder输入框占位文本string'请输入内容...'
mode输入模式'single' | 'multiple''single'
size @0.4组件尺寸'normal' | 'small''normal'
disabled是否禁用booleanfalse
loading是否加载中booleanfalse
autofocus自动获取焦点booleanfalse
enterkeyhint @0.4移动端虚拟键盘回车键提示EnterKeyHint'send'
autoSize自动调整高度boolean | { minRows: number, maxRows: number }{ minRows: 1, maxRows: 5 }
clearable是否可清空booleanfalse
hasExternalContent @0.5是否存在外部可提交内容,适用于附件、图片等不写入编辑器文本的内容booleanfalse
maxLength最大输入长度numberInfinity
showWordLimit是否显示字数统计booleanfalse
submitType提交方式'enter' | 'ctrlEnter' | 'shiftEnter''enter'
stopText停止按钮文字string'停止响应'
defaultActions @0.4默认操作按钮配置DefaultActionsundefined
extensions @0.4扩展列表 (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,
})
配置项类型默认值说明
itemsSenderSuggestionItem[] | Ref<SenderSuggestionItem[]>[]建议项列表(可选)
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 包含完整的 SenderSuggestionItem 信息(content、data、highlights),可用于业务逻辑处理。

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内容插槽,用于完全自定义编辑器内容{ editor }
actions-inline @0.4单行模式下的操作按钮区域-
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在 loading 状态下点击停止按钮时触发,用于取消正在进行的操作(如 AI 响应)()

submit 事件参数说明

  • text:纯文本内容,适用于简单场景(如直接发送给 AI)
  • data:结构化数据数组,仅在使用 Template 或 Mention 扩展时返回,包含文本和特殊节点的完整信息

根据业务需求选择使用:

  • 简单场景:只使用 text 参数
  • 复杂场景:使用 data 参数提取特殊节点信息或自定义拼接格式

详见:结构化数据

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设置编辑器内容(content: string)void
getContent @0.4获取编辑器内容-string
cancel @0.4手动触发取消-void

UploadButton Methods ​

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

VoiceButton Methods ​

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

结构化数据 ​

当使用 Template 或 Mention 扩展时,submit 事件的第二个参数 data 返回结构化数据数组。

使用建议:

  • 简单场景:使用 text 参数(纯文本)
  • 复杂场景:使用 data 参数提取特殊节点或自定义格式
Mention 扩展 ​
typescript
function handleSubmit(text: string, data?: StructuredData) {
  // text: "帮我分析 @张三 的周报"
  // data: [
  //   { type: 'text', content: '帮我分析 ' },
  //   { type: 'mention', content: '张三', value: '用户ID' },
  //   { type: 'text', content: ' 的周报' }
  // ]
  
  // 提取提及项
  const mentions = data?.filter(item => item.type === 'mention') || []
  
  // 自定义格式(如 Slack 风格)
  const customText = data?.map(item => 
    item.type === 'mention' ? `<@${item.value}>` : item.content
  ).join('')
}

Template 扩展 ​

typescript
function handleSubmit(text: string, data?: StructuredData) {
  // text: "帮我分析 张三 的周报"
  // data: [
  //   { type: 'text', content: '帮我分析 ' },
  //   { type: 'block', content: '张三' },
  //   { type: 'text', content: ' 的周报' }
  // ]
  
  // 提取模板块
  const blocks = data?.filter(item => item.type === 'block') || []
  
  // 自定义格式(如 Mustache 风格)
  const customText = data?.map(item => 
    item.type === 'block' ? `{{${item.content}}}` : item.content
  ).join('')
}

类型定义:详见 Types - StructuredData

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 SenderSuggestionItem {
  content: string // 建议项内容(必填)
  highlights?: string[] | HighlightFunction // 高亮方式(可选)
  data?: Record<string, unknown> // 自定义数据(可选)
}

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

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

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

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

// 移动端虚拟键盘回车键提示
type EnterKeyHint = 'enter' | 'done' | 'go' | 'next' | 'previous' | 'search' | 'send'

// 扩展类型
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提交按钮尺寸
--tr-sender-tooltip-gap按钮 Tooltip 与触发按钮的间距

尺寸变体

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

Tooltip 间距

Sender 按钮的 Tooltip 间距可通过 --tr-sender-tooltip-gap 全局自定义,默认值为 8px。由于 Tooltip 弹层通常挂载在全局层级,建议在 :root 或全局主题作用域下设置该变量。

使用示例

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;
}

/* 全局调整 Sender 按钮 Tooltip 间距 */
:root {
  --tr-sender-tooltip-gap: 10px;
}

已移除的 API ​

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

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使用 UploadButton 的 select 事件
speech-start使用 VoiceButton 的 speech-start 事件
speech-end使用 VoiceButton 的 speech-end 事件
speech-interim使用 VoiceButton 的 speech-interim 事件
speech-error使用 VoiceButton 的 speech-error 事件
suggestion-select使用 Suggestion 扩展的 onSelect 回调

Methods ​

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