更新记录

1.0.0(2026-08-11)

  • chat-list:高性能消息列表,支持自动滚动探底、触顶加载历史、用户 / 助手气泡差异化渲染、流式打字机指示。
  • stream-markdown:流式 Markdown 与 LaTeX 公式渲染器,内置半包语法自动修复(未闭合的 ``` 代码块、$ 公式自动补全),支持代码高亮与一键复制。
  • chat-input:交互输入框,支持自动换行、发送 / 停止状态切换、快捷 Prompt 推荐词,已适配底部安全区。
  • streamDecoder 核心库:手写 UTF-8 增量解码器(解决中文半包乱码)、标准 SSE 解析器、打字机缓冲队列,以及跨端统一的 streamRequest(H5 用 fetch、App / 微信小程序用 onChunkReceived)。

平台兼容性

uni-app(3.8.2)

Vue2 Vue3 Chrome Safari app-vue app-nvue Android iOS 鸿蒙
√ √ √ √ √ √ √ √ √
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 小红书小程序 快应用-华为 快应用-联盟
√ √ √ √ √ √ √ √ √ √ √ √

流式对话与 Markdown 渲染组件(stream-chat-kit)

一套面向 uni-app(Vue 3 + TypeScript)的高扩展性、跨端兼容对话 UI 组件集,适用于构建在线客服、文档问答、知识助手等需要「流式输出 + Markdown / 公式渲染」的场景。

支持端:H5 / iOS App / Android App / 微信小程序。

特性

  • chat-list:高性能消息列表,支持自动滚动探底、触顶加载历史、用户 / 助手气泡差异化渲染、流式打字机指示。
  • stream-markdown:流式 Markdown 与 LaTeX 公式渲染器,内置半包语法自动修复(未闭合的 ``` 代码块、$ 公式自动补全),支持代码高亮与一键复制。
  • chat-input:交互输入框,支持自动换行、发送 / 停止状态切换、快捷 Prompt 推荐词,已适配底部安全区。
  • streamDecoder 核心库:手写 UTF-8 增量解码器(解决中文半包乱码)、标准 SSE 解析器、打字机缓冲队列,以及跨端统一的 streamRequest(H5 用 fetch、App / 微信小程序用 onChunkReceived)。

目录结构

uni_modules/stream-chat-kit/
├── components/
│   ├── chat-list/chat-list.vue
│   ├── stream-markdown/stream-markdown.vue
│   └── chat-input/chat-input.vue
├── utils/
│   ├── streamDecoder.ts   # 流解码与 SSE 兼容核心库
│   └── types.ts           # 公共类型
├── utssdk/index.uts       # 原生辅助模块(满足 uni_modules 发布规范)
└── package.json

依赖安装

组件通过动态引入使用 marked(Markdown 解析)与 katex(公式渲染),两个库均为可选依赖:未安装时组件自动降级为纯文本 / 原始公式展示,项目仍可正常运行。如需完整效果,请在项目根目录执行:

npm install marked katex

说明:KaTeX 的完整样式在 H5 / App 端自动注入;微信小程序端公式可正常渲染,但视觉样式受 rich-text 限制,建议在页面全局引入 katex/dist/katex.min.css 以获得最佳效果。

快速上手

uni_modules 组件会被自动全局注册,无需手动 import。在页面中直接使用即可:

<template>
  <view class="container">
    <chat-list :messages="messages" :loading="loading" :theme="theme" @load-history="onHistory" />
    <chat-input v-model="input" :loading="loading" :quick-prompts="prompts" @send="onSend" @stop="onStop" />
  </view>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { StreamBuffer } from '../../../uni_modules/stream-chat-kit/utils/streamDecoder'
import type { ChatMessage } from '../../../uni_modules/stream-chat-kit/utils/types'

const messages = ref<ChatMessage[]>([])
const input = ref('')
const loading = ref(false)
const theme = ref<'light' | 'dark'>('light')
const prompts = ['介绍一下 Vue 3', '写一段快速排序']

function onSend(text: string) {
  const t = text.trim()
  if (!t || loading.value) return
  messages.value.push({ id: Date.now() + '', role: 'user', content: t, status: 'done' })
  const aiId = Date.now() + '-ai'
  messages.value.push({ id: aiId, role: 'assistant', content: '', status: 'streaming' })
  loading.value = true

  // 用打字机缓冲队列把高频字符节流后写入消息
  const buffer = new StreamBuffer(20, (chunk) => {
    const msg = messages.value.find((m) => m.id === aiId)
    if (msg) msg.content += chunk
  })
  // 这里以本地字符串模拟流式,真实场景替换为 streamRequest 的 onMessage
  let i = 0
  const full = '这是**流式**回复示例,支持 `代码` 与公式 $a^2+b^2=c^2$。'
  const timer = setInterval(() => {
    if (i >= full.length) {
      clearInterval(timer)
      buffer.flush()
      const m = messages.value.find((x) => x.id === aiId)
      if (m) m.status = 'done'
      loading.value = false
      return
    }
    buffer.push(full.slice(i, i + 3))
    i += 3
  }, 30)
}

function onStop() { loading.value = false }
function onHistory() { /* 在此向前追加历史消息 */ }
</script>

对接真实 SSE 后端

import { streamRequest } from '../../../uni_modules/stream-chat-kit/utils/streamDecoder'

const handle = streamRequest({
  url: 'https://your-api.com/v1/chat/stream',
  method: 'POST',
  header: { Authorization: 'Bearer <token>' },
  data: { message: text },
  onMessage: (data) => {
    const json = JSON.parse(data)
    const delta = json.choices?.[0]?.delta?.content || ''
    const msg = messages.value.find((m) => m.id === aiId)
    if (msg) msg.content += delta // stream-markdown 会自动修复半包语法
  },
  onDone: () => { /* 标记完成 */ },
  onError: (e) => { /* 错误处理 */ }
})

// 中断:handle.abort()

组件 API

chat-list

属性 类型 默认值 说明
messages ChatMessage[] [] 消息列表
loading boolean false 全局加载态,最后一条助手消息等待首字时显示
theme 'light' \| 'dark' light 主题
userAvatar string — 用户头像 URL
botAvatar string — 助手头像 URL
unread number 0 “回到底部”按钮角标数

事件:

事件 参数 说明
load-history — 列表滚动到顶部时触发,用于加载更早的消息

stream-markdown

属性 类型 默认值 说明
content string '' 流式内容(可未闭合)
theme 'light' \| 'dark' light 主题(影响代码块配色)

特性:自动修复未闭合代码块 / 公式、代码高亮 + 一键复制、KaTeX 公式渲染(降级为原始公式文本)。

chat-input

属性 类型 默认值 说明
modelValue string '' v-model 输入内容
loading boolean false 请求中(按钮变为“停止”)
theme 'light' \| 'dark' light 主题
placeholder string 输入消息… 占位文案
quickPrompts (string \| QuickPrompt)[] [] 快捷推荐词
showQuick boolean true 是否展示快捷词
autoSend boolean true 点击快捷词后是否自动发送

事件:

事件 参数 说明
update:modelValue string 输入内容变化
send string 点击发送或回车
stop — 请求中点“停止”
quick string 点击快捷词

公共类型

type ChatRole = 'user' | 'assistant' | 'system'
type ChatMessageStatus = 'pending' | 'streaming' | 'done' | 'error'

interface ChatMessage {
  id: string
  role: ChatRole
  content: string
  status?: ChatMessageStatus
  createdAt?: number
  extra?: Record<string, any>
}

平台兼容说明

能力 H5 App 微信小程序
流式请求 fetch Reader uni.request chunked uni.request onChunkReceived
中文半包解码 ✅ ✅ ✅
Markdown / 代码高亮 ✅ ✅ ✅
KaTeX 公式(带样式) ✅ ✅ △(需全局引 CSS)

本地运行 Demo

  1. 用 HBuilder X 打开本仓库根目录(含 manifest.json 的项目根)。
  2. 在项目根目录执行 npm install marked katex 安装可选依赖。
  3. 点 HBuilder X 顶部「运行」→ 选择 H5 / 微信小程序 / App 基座即可预览。
  4. Demo 页位于 src/pages/index/index.vue,使用本地模拟流式输出,无需后端即可体验。

许可

MIT

隐私、权限声明

1. 本插件需要申请的系统权限列表:

无

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集任何数据

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

无

暂无用户评论。