Skip to content

PromptInput 系列 ​

可组合的富输入系统,包含 11 个子组件:

  • PromptInput — 外层容器,provide 输入上下文
  • PromptInputBody — 输入行容器(Textarea 占满整行;横向内边距在此层统一提供)
  • PromptInputTextarea — 弹性高度输入框:随内容自动增高,超过 --ai-chat-input-max-height(默认 200px)后内部滚动;键位模式由 PromptInput 的 sendKey 决定,粘贴文件进入附件管道
  • PromptInputSubmit — 发送/停止按钮(圆形主题色,置于 Footer 右侧动作区)
  • PromptInputFooter — 底部工具栏容器(左 tools / 右 hint 动作区)
  • PromptInputTools — 工具按钮组
  • PromptInputButton — 单个工具按钮。按钮宽度自适应:只放图标时保持 32px 方形;放文字(快捷指令 chip)时按内容自然撑开,不换行
  • PromptInputUploadButton — 图片/附件上传入口(触发文件选择管道)
  • PromptInputAttachments — 待发送附件预览列表(图片缩略图 + Lightbox 兜底预览)
  • PromptInputHeader — 顶部附件预览区域
  • PromptInputSuggestion — 输入内联建议浮层(/、@ 触发唤起,↑↓/Enter/Esc 键盘交互;受控设计,详见 PromptInputSuggestion)

代码演示 ​

空态 ​

对话入口的首屏形态:只有占位文本与发送按钮,聚焦即输入(默认键位 Alt+Enter 发送、Enter 换行):

AI Chat UI 可能会产生不准确的信息,请注意甄别内容的准确性

输入中(附件预览与发送) ​

完整输入区:键入多行文本观察弹性增高;附件有上传按钮 / 粘贴 / 拖拽三个入口(multiple,至多 3 个),发送走 beforeSend 上传钩子——上传期间附件项呈呼吸遮罩,完成后 send 事件携带上传产物:

AI Chat UI 可能会产生不准确的信息,请注意甄别内容的准确性

流式中(发送 / 停止切换) ​

发送后进入模拟流式回复,右下角按钮切换为停止方块,点击随时中断:

AI Chat UI 可能会产生不准确的信息,请注意甄别内容的准确性

真实接线中,按钮由 PromptInput 输入上下文的 status 驱动、在发送与停止间自动切换(停止点击发出 abort 事件交宿主中断流);本演示以默认插槽自绘两态图标模拟该切换。

基础用法 ​

vue
<PromptInput
  placeholder="输入消息..."
  @send="(payload) => chat.send(payload.text)"
>
  <PromptInputBody>
    <PromptInputTextarea />
  </PromptInputBody>
  <template #footer>
    <PromptInputFooter>
      <template #tools>
        <PromptInputTools>
          <PromptInputButton tooltip="上传">
            <!-- 图标 -->
          </PromptInputButton>
        </PromptInputTools>
      </template>
      <template #hint>
        <PromptInputSubmit />
      </template>
      <template #hint>
        <span>Enter 换行,Alt+Enter 发送</span>
      </template>
    </PromptInputFooter>
  </template>
</PromptInput>

发送键位 ​

sendKey 支持两种模式:

模式发送换行适用场景
'alt-enter'(默认)Alt+Enter(macOS 亦支持 ⌘+Enter)Enter长文输入为主,换行是高频操作(对齐 Claude/ChatGPT 桌面端)
'enter'EnterShift+Enter短问答为主,发送是高频操作
vue
<PromptInput send-key="enter" @send="onSend">
  <PromptInputBody>
    <PromptInputTextarea />
  </PromptInputBody>
  <template #footer>
    <PromptInputFooter>
      <template #hint>
        <PromptInputSubmit />
      </template>
    </PromptInputFooter>
  </template>
</PromptInput>

两种模式下 IME 组合输入期间的 Enter(选字/确认候选词)均不会触发发送,中文/日文输入无渗字风险。

附件上传 ​

附件有三个统一入口,全部走 PromptInput 的同一条校验管道(accept / maxFiles / maxSize):

  1. 按钮选择:PromptInputUploadButton(kind="image" 限定图片,kind="file" 跟随 accept)
  2. 粘贴:在输入框内直接 ⌘V / Ctrl+V 粘贴文件(如截图)
  3. 拖拽:拖动文件到输入框区域,边框高亮后松手

被管道拒绝的文件触发 error 事件并弹出内置 Toast 提示拒绝原因:

vue
<PromptInput :before-send="upload" @send="onSend" @error="onError">
  <PromptInputAttachments />
  <PromptInputBody>
    <PromptInputTextarea />
  </PromptInputBody>
  <template #footer>
    <PromptInputFooter>
      <template #tools>
        <PromptInputTools>
          <PromptInputUploadButton kind="image" />
          <PromptInputUploadButton kind="file" />
        </PromptInputTools>
      </template>
      <template #hint>
        <PromptInputSubmit />
      </template>
    </PromptInputFooter>
  </template>
</PromptInput>

beforeSend 上传钩子 ​

配置 beforeSend 后,发送时附件会先经过该钩子上传(期间附件项显示呼吸遮罩),resolve 返回的 Attachment[] 随 send 事件抛出;上传失败自动回置 error 态并 Toast:

ts
import type { Attachment } from '@toimc/core'

async function upload(files: File[]): Promise<Attachment[]> {
  const form = new FormData()
  files.forEach((f) => form.append('files', f))
  const res = await fetch('/api/upload', { method: 'POST', body: form })
  return res.json()
}

function onSend(payload: { text: string; attachments?: Attachment[] }) {
  chat.send(payload.text, payload.attachments)
}

未配置 beforeSend 时,send 事件携带原始 files: File[],上传逻辑完全交由宿主。

API ​

PromptInput ​

provide 输入上下文给子组件,并承载附件管道(选择 / 粘贴 / 拖拽上传)。

属性名类型默认值说明
disabledbooleanfalse禁用输入与发送
placeholderstringi18n promptInput.placeholder输入框占位文本
maxHeightnumber200输入框最大高度(px)
acceptstring''允许的文件类型(同 <input accept>),空为不限
multiplebooleanfalse是否允许多文件
maxFilesnumber5附件数量上限
maxSizenumber—单文件大小上限(字节)
sendKey'alt-enter' | 'enter''alt-enter'发送快捷键,Enter 发送时换行需 Shift+Enter
beforeSend(files: File[]) => Promise<Attachment[]>—发送前上传钩子;resolve 的 attachments 随 send 事件抛出
事件名参数说明
send(payload: { text: string; files?: File[]; attachments?: Attachment[] })提交消息;无 beforeSend 时携带原始 files,有则携带上传后的 attachments
abort()点击停止按钮;由内部 status 驱动,当前无公开驱动入口(见下方 PromptInputSubmit 注记)
error(payload: { files: File[]; reason: string })附件校验拒绝 / 上传失败

响应式:容器宽度 < 640px(容器查询)时自动紧凑——减小外边距并隐藏底部 disclaimer;输入框 max-height 跟随 --ai-chat-input-max-height(桌面 200px),窄屏可由 --ai-chat-input-max-height-mobile(默认 120px)覆盖收窄。

拖拽上传 ​

拖动文件到输入框区域(__wrapper)时高亮边框,松手即走与文件选择相同的校验管道(accept / maxFiles / maxSize),被拒文件触发 error 事件并弹出 Toast。非文件拖拽(如拖选文本)不响应。

插槽名说明
default输入主体(Textarea + Submit)
footer底部工具栏
disclaimer底部免责声明文案,缺省渲染 i18n promptInput.disclaimer(窄屏自动隐藏)

PromptInputTextarea ​

无自身 Props/Events。占位文本来自 PromptInput 的 placeholder prop;发送快捷键由 PromptInput 的 sendKey 决定,send 事件也在 PromptInput 上监听。粘贴图片/文件会自动进入附件预览列表。

PromptInputSubmit ​

无 Props。圆形主题色发送按钮(--ai-chat-color-accent 背景 + 白色上箭头),置于 PromptInputFooter 的 hint 右侧动作区(输入框右下方);颜色跟随主题预设变化。

插槽名说明
default自定义图标/内容(覆盖默认上箭头)

status 尚无公开驱动入口

按钮的发送/停止两态由 PromptInput 输入上下文的内部 status('ready' | 'streaming')驱动,但当前版本 PromptInput 未暴露 status / streaming prop,status 恒为 'ready'——停止方块与 abort 事件从公开 API 侧不可达。流式中需切换停止按钮时,按上方「流式中」演示的做法:默认插槽自绘两态图标 + @click.capture 拦截后自行中断。

PromptInputButton ​

属性名类型默认值说明
tooltipstring''悬停提示(写入按钮 title)
activebooleanfalse激活状态
disabledbooleanfalse禁用按钮
事件名参数说明
click()点击按钮

PromptInputUploadButton ​

上传入口按钮,点击触发隐藏的 <input type="file">,选中文件走 PromptInput 的附件管道(accept / maxFiles / maxSize 校验)。disabled 跟随输入上下文。

属性名类型默认值说明
kind'image' | 'file''file'image 时 accept 固定为 image/*,file 时跟随 PromptInput 的 accept
插槽名说明
default按钮图标/内容

PromptInputFooter ​

底部工具栏容器,左右分列。无 Props。

插槽名说明
tools左侧工具区(通常放 PromptInputTools + 上传/功能按钮)
hint右侧动作区(常放 PromptInputSubmit,也可放提示文案)

PromptInputAttachments ​

待发送附件(pendingFiles)预览列表,通常放在 Textarea 上方。图片渲染 40px 缩略图,非图片按类型渲染图标占位(📄 文档 / 🎵 音频 / 🎬 视频);uploading 态叠加呼吸遮罩,error 态红色边框且 title 显示错误信息;每项带文件名(单行省略)、大小与移除按钮。

图片点击预览走兜底链:宿主监听 preview 事件时只抛出事件、不弹内置预览;未监听则打开内置 ImageLightbox(Esc 关闭、左右方向键翻页)。

vue
<PromptInput v-model="..." @send="...">
  <PromptInputAttachments />
  <PromptInputBody>
    <PromptInputTextarea />
  </PromptInputBody>
</PromptInput>
事件名参数说明
preview(payload: { attachments: Attachment[]; index: number })宿主接管图片预览时触发;attachments 为当前全部图片(含 id/name/mediaType/url/size),index 指向被点击项