一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

TinyRobot Sender打造强力AI聊天输入体验

时间:2026-06-13 12:10:23 编辑:袖梨 来源:一聚教程网

TinyRobot Sender:打造强大的 AI 聊天输入体验

 复制代码

为什么输入组件如此重要

在 AI 聊天应用中,输入框是用户与 AI 之间最重要的交互界面。一个优秀的输入组件,不仅是文本的"收容所",更是连接用户意图与 AI 能力的桥梁。无论模型能力多么强大,如果用户无法精准、高效地表达自己的想法,整个 AI 体验都会大打折扣。

TinyRobot Sender打造强大的AI聊天输入体验

传统的 <input><textarea> 已经无法满足现代 AI 应用的复杂需求。用户需要:

  • 结构化输入:通过模板快速搭建复杂 Prompt
  • 智能联想:输入时自动提示相关话题或产品
  • @提及:快速引用预设角色或上下文
  • 语音输入:解放双手,自然表达
  • 文件上传:将图片、文档等作为对话上下文

TinyRobot Sender 正是为此而生 —— 它是一个高度可组合、可扩展的 AI 聊天输入组件,基于 Tiptap 富文本编辑器构建,提供企业级的输入体验。


快速上手

1. 基础用法:模式切换

Sender 支持单行 (single) 和多行 (multiple) 两种输入模式。单行模式下内容超出宽度时会自动切换为多行,无需用户手动调整。

 复制代码<script setup lang="ts">
import { ref } from 'vue'
import { TrSender } from '@opentiny/tiny-robot'const content = ref('')
const mode = ref<'single' | 'multiple'>('single')const handleSubmit = (text: string) => {
  console.log('提交内容:', text)
  content.value = ''
}
</script><template>
  <div>
    <div style="display: flex; gap: 12px; margin-bottom: 16px;">
      <button
        :style="{ background: mode === 'single' ? '#1476ff' : '#f0f0f0', color: mode === 'single' ? '#fff' : '#333' }"
        @click="mode = 'single'"
      >
        单行模式
      </button>
      <button
        :style="{ background: mode === 'multiple' ? '#1476ff' : '#f0f0f0', color: mode === 'multiple' ? '#fff' : '#333' }"
        @click="mode = 'multiple'"
      >
        多行模式
      </button>
    </div>    <TrSender
      v-model="content"
      :mode="mode"
      placeholder="请输入消息..."
      :max-length="200"
      show-word-limit
      clearable
      @submit="handleSubmit"
    />
  </div>
</template>

核心 Props 说明

属性说明类型默认值
modelValue / v-model输入框内容绑定string''
mode输入模式'single' | 'multiple''single'
disabled是否禁用booleanfalse
loading加载状态(显示停止按钮)booleanfalse
maxLength最大输入长度numberInfinity
showWordLimit显示字数统计booleanfalse
size组件尺寸'normal' | 'small''normal'

扩展架构:可插拔的功能增强

Sender 的核心设计理念是可插拔的扩展架构。通过 extensions prop,你可以按需组合 Template、Mention、Suggestion 等功能扩展,每个扩展互相独立、自由组合。

 复制代码// 扩展就是普通的数组元素
const extensions = [
  TrSender.template(templateData),
  TrSender.mention(mentionItems),
  TrSender.suggestion(suggestionItems, { filterFn: customFilter })
]// 通过 props 传入
<TrSender :extensions="extensions" />

提供两种集成方式:

  • 便捷函数(推荐)TrSender.template()TrSender.mention()TrSender.suggestion(),用于简单场景
  • 标准配置TrSender.Mention.configure({...}),用于复杂配置(如自定义 onSelect 回调、filterFn 等)

2. Template 扩展:结构化 Prompt 模板

Template 扩展让用户通过预设模板快速填写结构化 Prompt。支持 text(固定文本)、block(可编辑字段)、select(下拉选择器)三种节点类型。

 复制代码<script setup lang="ts">
import { ref } from 'vue'
import { Button } from '@opentiny/vue'
import { TrSender } from '@opentiny/tiny-robot'
import type { TemplateItem, StructuredData } from '@opentiny/tiny-robot'const content = ref('')
const templateData = ref<TemplateItem[]>([])// 便捷函数:传入响应式 ref,数据变化时自动同步编辑器
const extensions = [TrSender.template(templateData)]const setTemplate = () => {
  templateData.value = [
    { type: 'text', content: '请帮我写一份关于' },
    { type: 'block', content: '人工智能' },
    { type: 'text', content: '的' },
    { type: 'block', content: '技术报告' },
    { type: 'text', content: ',字数要求' },
    { type: 'block', content: '3000字' },
    { type: 'text', content: ',风格要求' },
    {
      type: 'select',
      placeholder: '选择风格',
      content: '',
      options: [
        { label: '正式学术', value: 'formal_academic' },
        { label: '通俗易懂', value: 'easy_reading' },
        { label: '技术深度', value: 'technical_deep' },
      ]
    },
    { type: 'text', content: '。' },
  ]
}const handleSubmit = (text: string, data?: StructuredData) => {
  console.log('纯文本:', text)
  // data 结构:
  // [
  //   { type: 'text', content: '请帮我写一份关于' },
  //   { type: 'block', content: '人工智能' },
  //   { type: 'text', content: '的技术报告...' }
  // ]
}
</script><template>
  <div style="display: flex; flex-direction: column; gap: 16px;">
    <Button size="small" @click="setTemplate">插入 Prompt 模板</Button>
    <TrSender
      v-model="content"
      :extensions="extensions"
      mode="multiple"
      placeholder="点击按钮插入模板..."
      :max-length="500"
      show-word-limit
      clearable
      @submit="handleSubmit"
    />
  </div>
</template>

Template 通过响应式 ref 机制,当 templateData.value 发生变化时,编辑器内容会自动更新,光标也会自动聚焦到第一个可编辑字段,无需手动调用任何方法。

3. Mention 扩展:@提及功能

Mention 扩展实现了类似社交平台的 @提及功能。输入触发字符(默认 @)会弹出选择列表,支持键盘导航(↑↓)和搜索过滤。

 复制代码<script setup lang="ts">
import { ref } from 'vue'
import { TrSender } from '@opentiny/tiny-robot'
import type { MentionItem, StructuredData } from '@opentiny/tiny-robot'const content = ref('')const assistants: MentionItem[] = [
  { label: '代码助手', value: 'you_are_a_coding_expert' },
  { label: '文案大师', value: 'you_are_a_copywriter' },
  { label: '数据分析师', value: 'you_are_a_data_analyst' },
  { label: '翻译专家', value: 'you_are_a_translator' },
]// 便捷函数:默认触发字符为 @
const extensions = [TrSender.mention(assistants, '@')]// 也可以自定义触发字符,如用 # 替代 @
// const extensions = [TrSender.mention(hashtags, '#')]const handleSubmit = (text: string, data?: StructuredData) => {
  // text: "帮我分析 @代码助手 返回的代码"
  // data: [
  //   { type: 'text', content: '帮我分析 ' },
  //   { type: 'mention', content: '代码助手', value: 'you_are_a_coding_expert' },
  //   { type: 'text', content: ' 返回的代码' }
  // ]  // 提取所有提及的助手
  const mentions = data?.filter(item => item.type === 'mention') || []
  const assistantIds = mentions.map(m => m.value)
  console.log('提及的助手:', assistantIds)
}
</script><template>
  <div style="display: flex; flex-direction: column; gap: 16px;">
    <p style="color: #666; font-size: 14px; margin: 0;">
       输入 <code>@</code> 触发提及选择,支持键盘导航和搜索过滤
    </p>
    <TrSender
      v-model="content"
      :extensions="extensions"
      placeholder="输入 @ 选择助手..."
      mode="multiple"
      :max-length="500"
      show-word-limit
      clearable
      @submit="handleSubmit"
    />
  </div>
</template>

Mention 扩展支持三个配置项:

配置项类型默认值说明
itemsMentionItem[] | Ref<MentionItem[]>[]提及项列表,支持响应式 ref
charstring'@'触发字符
allowSpacesbooleanfalse是否允许触发字符后输入空格

4. Suggestion 扩展:智能联想与自定义过滤

Suggestion 扩展提供智能输入联想功能。用户输入时自动弹出建议列表,支持键盘导航和 Tab 自动补全。

 复制代码<script setup lang="ts">
import { ref, computed } from 'vue'
import { TrSender } from '@opentiny/tiny-robot'
import type { SenderSuggestionItem } from '@opentiny/tiny-robot'const input = ref('')const suggestions: SenderSuggestionItem[] = [
  { content: 'ECS-云服务器卡顿排查' },
  { content: 'ECS-备份弹性云服务器' },
  { content: 'ECS-实例无法启动处理' },
  { content: 'CDN-权限管理配置指南' },
  { content: 'CDN-缓存刷新问题排查' },
  { content: 'OSS-存储桶访问控制' },
]// 标准配置:使用自定义 filterFn 实现按分类前缀匹配
const extensions = computed(() => [
  TrSender.Suggestion.configure({
    items: suggestions,
    // 自定义过滤逻辑
    filterFn: (items: SenderSuggestionItem[], query: string) => {
      if (!query) return []
      const lowerQuery = query.toLowerCase()
      // 按 - 前面的分类标签匹配
      return items.filter((item) => {
        const category = item.content.split('-')[0].toLowerCase()
        return category.startsWith(lowerQuery)
      })
    },
    showAutoComplete: true,
    popupWidth: 500,
    onSelect: (item) => {
      console.log('选中建议:', item.content)
    },
  }),
])
</script><template>
  <div style="display: flex; flex-direction: column; gap: 16px;">
    <p style="color: #666; font-size: 14px; margin: 0;">
       输入 ECS、CDN 或 OSS 查看分类联想,Tab 键快速补全
    </p>
    <TrSender
      v-model="input"
      :extensions="extensions"
      placeholder="输入 ECS 或 CDN 查看建议..."
      @submit="(text) => console.log('提交:', text)"
    />
  </div>
</template>

Suggestion 配置项详解:

配置项说明类型默认值
items建议项列表SenderSuggestionItem[][]
filterFn自定义过滤函数,不传则不过滤(items, query) => itemsundefined
showAutoComplete显示灰色补全提示booleantrue
activeSuggestionKeys激活建议的按键string[]['Enter']
popupWidth弹窗宽度(支持数字/百分比)number | string400
onSelect选中回调,返回 false 阻止默认回填(item) => void | false-

5. VoiceButton:语音输入

VoiceButton 是一个独立的语音输入按钮组件,通过 footerfooter-right 插槽集成到 Sender 中。它同时支持浏览器内置的 Web Speech API 和自定义第三方语音识别服务。

 复制代码<script setup lang="ts">
import { ref } from 'vue'
import { TrSender, VoiceButton } from '@opentiny/tiny-robot'const content = ref('')
const voiceMode = ref<'mixed' | 'continuous'>('mixed')const handleSubmit = (text: string) => {
  console.log('提交内容:', text)
  content.value = ''
}
</script><template>
  <div style="display: flex; flex-direction: column; gap: 16px;">
    <div style="display: flex; gap: 12px; align-items: center;">
      <span>语音模式:</span>
      <label><input type="radio" v-model="voiceMode" value="mixed" /> 混合输入</label>
      <label><input type="radio" v-model="voiceMode" value="continuous" /> 连续识别</label>
    </div>
    <p style="color: #666; font-size: 13px; margin: 0;">
      {{ voiceMode === 'mixed'
        ? '识别结果追加到输入框,可继续编辑修改'
        : '持续识别并自动替换内容,适合长段语音输入' }}
    </p>    <!-- VoiceButton 通过 key 在模式切换时重新实例化 -->
    <TrSender
      :key="voiceMode"
      v-model="content"
      mode="multiple"
      @submit="handleSubmit"
    >
      <template #footer-right>
        <VoiceButton
          :speech-config="voiceMode === 'mixed'
            ? { autoReplace: false, interimResults: true }
            : { autoReplace: true, continuous: true }"
          tooltip="语音输入"
          @speech-final="(transcript) => { content += transcript }"
        />
      </template>
    </TrSender>
  </div>
</template>

VoiceButton 核心配置:

属性说明类型默认值
speechConfig.autoReplace是否自动替换内容(false 则追加)booleantrue
speechConfig.continuous是否持续识别booleanfalse
speechConfig.interimResults是否返回中间结果booleanfalse
speechConfig.lang识别语言string浏览器默认语言
autoInsert是否自动插入识别结果到编辑器booleantrue

如需集成阿里云、百度、Azure 等第三方语音服务,通过 speechConfig.customHandler 传入自定义处理器即可。

6. UploadButton:文件上传

UploadButton 允许用户上传文件(图片、文档等)作为对话的上下文输入,支持文件类型过滤、大小限制和数量限制。

 复制代码<script setup lang="ts">
import { ref } from 'vue'
import { TrSender, UploadButton } from '@opentiny/tiny-robot'const content = ref('')
const uploadedFiles = ref<File[]>([])const handleSubmit = (text: string) => {
  console.log('提交文本:', text)
  console.log('附带文件:', uploadedFiles.value)
  content.value = ''
  uploadedFiles.value = []
}const handleFilesSelected = (files: File[]) => {
  uploadedFiles.value = files
  // 可以在这里做上传逻辑或直接作为附件信息
}
</script><template>
  <div style="display: flex; flex-direction: column; gap: 16px;">
    <TrSender
      v-model="content"
      mode="multiple"
      placeholder="输入问题,或上传相关文件..."
      clearable
      @submit="handleSubmit"
    >
      <template #footer-right>
        <UploadButton
          accept="image/*,.pdf,.doc,.docx"
          :multiple="true"
          :max-size="10"
          :max-count="5"
          tooltip="上传文件(最多5个,每个≤10MB)"
          tooltip-placement="top"
          @select="handleFilesSelected"
        />
      </template>
    </TrSender>    <div v-if="uploadedFiles.length" style="font-size: 13px; color: #666;">
      已选择:{{ uploadedFiles.map(f => f.name).join(', ') }}
    </div>
  </div>
</template>

UploadButton 核心配置:

属性说明类型默认值
accept接受的 MIME 类型string'*'
multiple是否支持多选booleanfalse
maxSize文件大小限制(MB)number-
maxCount最大文件数量number-
tooltip悬停提示文本string-

7. 自定义插槽:灵活布局

Sender 提供多个插槽位置以支持灵活的布局扩展:

插槽位置作用域典型用途
header输入框上方-标题、提示信息
prefix输入框内部左侧-AI 图标、模型选择
content编辑器内容区域{ editor }完全自定义编辑器内容
actions-inline单行模式操作区-单行模式下的操作按钮
footer底部左侧{ editor, hasContent, disabled, loading }增强功能按钮
footer-right底部右侧-上传、语音按钮
 复制代码<script setup lang="ts">
import { ref } from 'vue'
import { TrSender, UploadButton, VoiceButton } from '@opentiny/tiny-robot'const content = ref('')
const deepThinking = ref(false)const handleSubmit = (text: string) => {
  console.log('提交:', text, deepThinking.value ? '(深度思考模式)' : '')
  content.value = ''
}
</script><template>
  <TrSender
    v-model="content"
    placeholder="输入你的问题..."
    mode="multiple"
    clearable
    @submit="handleSubmit"
  >
    <!-- 顶部标题 -->
    <template #header>
      <div style="text-align: center; font-weight: 700; padding: 8px 0;">
         AI 智能助手
      </div>
    </template>    <!-- 输入框前缀图标 -->
    <template #prefix>
      <span style="font-size: 22px; margin-right: 8px;"></span>
    </template>    <!-- 底部左侧功能按钮 -->
    <template #footer="{ editor, hasContent }">
      <button
        :style="{
          padding: '4px 12px', borderRadius: '16px', border: '1px solid #e0e0e0',
          background: deepThinking ? '#1476ff' : 'transparent',
          color: deepThinking ? '#fff' : '#666', cursor: 'pointer', fontSize: '13px'
        }"
        @click="deepThinking = !deepThinking"
      >
        {{ deepThinking ? ' 深度思考' : ' 深度思考' }}
      </button>
      <span v-if="hasContent" style="font-size: 12px; color: #999; margin-left: 8px;">
        已输入 {{ content.length }} 字
      </span>
    </template>    <!-- 底部右侧功能按钮 -->
    <template #footer-right>
      <UploadButton
        accept="image/*"
        tooltip="上传图片"
        tooltip-placement="top"
        @select="(files) => console.log('选择文件:', files)"
      />
      <VoiceButton
        :speech-config="{ autoReplace: false, interimResults: true }"
        tooltip="语音输入"
        tooltip-placement="top"
      />
    </template>
  </TrSender>
</template>

8. submitType:灵活控制提交流程

不同的应用场景需要不同的提交方式。Sender 通过 submitType 属性提供三种方式:

 复制代码<script setup lang="ts">
import { ref } from 'vue'
import { TrSender } from '@opentiny/tiny-robot'const content = ref('')
const submitType = ref<'enter' | 'ctrlEnter' | 'shiftEnter'>('ctrlEnter')const handleSubmit = (text: string) => {
  console.log('提交:', text)
  content.value = ''
}
</script><template>
  <div style="display: flex; flex-direction: column; gap: 16px;">
    <div style="display: flex; gap: 16px; align-items: center;">
      <span>提交方式:</span>
      <label><input type="radio" v-model="submitType" value="enter" /> Enter 提交</label>
      <label><input type="radio" v-model="submitType" value="ctrlEnter" /> Ctrl+Enter 提交</label>
      <label><input type="radio" v-model="submitType" value="shiftEnter" /> Shift+Enter 提交</label>
    </div>    <TrSender
      v-model="content"
      :submit-type="submitType"
      placeholder="输入内容后使用对应快捷键提交..."
      @submit="handleSubmit"
    />
  </div>
</template>

快捷键行为对照表

submitType提交快捷键换行快捷键
enterEnterCtrl+Enter / Shift+Enter
ctrlEnterCtrl+EnterEnter
shiftEnterShift+EnterEnter

结构化数据:读懂用户的意图

Sender 的 submit 事件不仅返回纯文本,当使用了 Template 或 Mention 扩展时,还会返回结构化的 data 参数,让你能够精确提取用户的意图。

 复制代码// submit 事件签名
@submit="(text: string, data?: StructuredData) => { ... }"

Mention 结构化数据

 复制代码const handleSubmit = (text: string, data?: StructuredData) => {
  // 用户输入: "帮我分析 @代码助手 的代码质量"
  // text: "帮我分析 @代码助手 的代码质量"
  // data: [
  //   { type: 'text', content: '帮我分析 ' },
  //   { type: 'mention', content: '代码助手', value: 'you_are_a_coding_expert' },
  //   { type: 'text', content: ' 的代码质量' }
  // ]  // 提取提及的助手 ID 列表
  const mentions = data?.filter(item => item.type === 'mention') || []
  const assistantIds = mentions.map(item => item.value)  // 自定义 Slack 风格格式
  const customText = data?.map(item =>
    item.type === 'mention' ? `<@${item.value}>` : item.content
  ).join('')
}

Template 结构化数据

 复制代码const handleSubmit = (text: string, data?: StructuredData) => {
  // 用户通过模板输入后提交
  // text: "请帮我写一份关于人工智能的技术报告,字数要求3000字"
  // data: [
  //   { type: 'text', content: '请帮我写一份关于' },
  //   { type: 'block', content: '人工智能' },
  //   { type: 'text', content: '的技术报告...' }
  // ]  // 提取所有可编辑块的值
  const blocks = data?.filter(item => item.type === 'block') || []
  const blockValues = blocks.map(b => b.content)
}

这种结构化设计让你可以轻松实现:

  • 将 @提及转换为自定义协议格式
  • 提取模板中的关键填充字段
  • 构建更丰富的 AI 请求上下文

v0.4 升级与 SenderCompat 迁移

Sender 在 v0.4 版本进行了重大重构,底层从传统 textarea 升级为基于 Tiptap 的富文本编辑器,带来了更强大的扩展能力和更灵活的插槽系统。

如果你的项目正在使用 v0.3.x

TinyRobot 提供了 SenderCompat 兼容组件作为过渡方案,只需修改一行导入:

 复制代码// 旧代码 (v0.3.x)
import { TrSender } from '@opentiny/tiny-robot'// 使用 SenderCompat 快速迁移
import { TrSenderCompat as TrSender } from '@opentiny/tiny-robot'

推荐的迁移路径

 复制代码v0.3.x Sender → SenderCompat(快速迁移,改导入即可)→ v0.4 Sender(按需完全升级)

v0.4 主要变更一览

变更类型v0.3.xv0.4说明
语音输入allow-speech propVoiceButton 独立组件组件化设计,通过插槽集成
文件上传allow-files + button-groupUploadButton 独立组件扁平化配置,更灵活
智能联想suggestions propSuggestion 扩展通过 extensions 接入
@提及-Mention 扩展v0.4 新增功能
模板填充v-model:templateDataTemplate 扩展响应式 ref 驱动
主题theme propThemeProvider 包裹全局继承,所有子组件自动生效
模板类型名type: 'template'type: 'block'类型名称变更,需手动适配

完全升级到 v0.4

如果你准备完全升级到 v0.4,下面是一个典型的迁移对照:

 复制代码<!-- v0.3.x 写法 -->
<template>
  <TrSender
    v-model="content"
    :allow-speech="true"
    :speech="{ lang: 'zh-CN' }"
    :allow-files="true"
    :button-group="{ file: { accept: 'image/*' } }"
    :suggestions="filteredSuggestions"
    @speech-end="onSpeechEnd"
    @files-selected="onFilesSelected"
  />
</template><!-- v0.4 写法 -->
<template>
  <TrSender
    v-model="content"
    :extensions="extensions"
  >
    <template #footer-right>
      <UploadButton accept="image/*" @select="onFilesSelected" />
      <VoiceButton :speech-config="{ lang: 'zh-CN' }" @speech-final="onSpeechEnd" />
    </template>
  </TrSender>
</template><script setup lang="ts">
import { ref } from 'vue'
import { TrSender, UploadButton, VoiceButton } from '@opentiny/tiny-robot'const content = ref('')const extensions = [
  TrSender.Suggestion.configure({ items: [], filterFn: myFilter }),
]const onFilesSelected = (files: File[]) => { /* ... */ }
const onSpeechEnd = (transcript: string) => { /* ... */ }
</script>

事件与方法速查

事件

事件名说明回调参数
update:modelValue内容更新(value: string)
submit提交内容(text: string, data?: StructuredData)
cancel取消操作(loading 状态下点击停止)()
clear清空内容()
focus获得焦点(event: FocusEvent)
blur失去焦点(event: FocusEvent)
input输入变化(value: string)

方法(通过 ref 调用)

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

总结

TinyRobot Sender 通过以下设计原则,为 AI 聊天应用提供了企业级的输入体验:

  1. 可组合:各功能通过独立的扩展或组件实现,按需引入
  2. 响应式:扩展配置支持 Vue ref,数据变化自动同步 UI
  3. 结构化:submit 事件返回文本 + 结构化数据,精准传达用户意图
  4. 可插拔:Template、Mention、Suggestion 三大扩展独立运作,互不干扰
  5. 渐进迁移:通过 SenderCompat 兼容组件,平滑从 v0.3.x 升级

无论是构建一个简单的 AI 问答输入框,还是打造复杂的企业级 Copilot 交互界面,Sender 都能胜任。


OpenTiny NEXT 是一套企业智能前端开发解决方案,以生成式 UI 和 WebMCP 两大核心技术为基础,对现有传统的 TinyVue 组件库、TinyEngine 低代码引擎等产品进行智能化升级,构建出面向 Agent 应用的前端 NEXT-SDKs、AI Extension、TinyRobot智能助手、GenUI等新产品,加速企业应用的智能化改造,实现AI理解用户意图自主完成任务。

欢迎加入 OpenTiny 开源社区。添加微信小助手:opentiny-official 一起参与交流前端技术~

OpenTiny 官网:opentiny.design/ TinyRobot 代码仓库:github.com/opentiny/ti… (欢迎star ⭐) TinyRobot skill源码:github.com/opentiny/ag… (欢迎 Star ⭐)

热门栏目