更新记录
1.0.0(2025-06-22)
下载此版本
uni-app 聊天组件
- 支持消息展示
- 支持实时滚动消息到最底下
- 支持复制消息
- 支持markdown格式渲染
- 支持重新编辑
- 支持打字机效果
- 可以对接到deepseek或其他api,实现流式滚动效果
平台兼容性
uni-app(4.03)
Vue2 |
Vue3 |
Chrome |
Safari |
app-vue |
app-nvue |
Android |
iOS |
鸿蒙 |
× |
√ |
√ |
√ |
√ |
× |
√ |
√ |
√ |
微信小程序 |
支付宝小程序 |
抖音小程序 |
百度小程序 |
快手小程序 |
京东小程序 |
鸿蒙元服务 |
QQ小程序 |
飞书小程序 |
快应用-华为 |
快应用-联盟 |
√ |
√ |
√ |
√ |
√ |
√ |
√ |
√ |
√ |
√ |
√ |
uni-app x(4.03)
Chrome |
Safari |
Android |
iOS |
鸿蒙 |
微信小程序 |
√ |
√ |
√ |
√ |
√ |
√ |
uz-chat 聊天消息组件
组件名:uz-chat
安装
在市场导入聊天消息组件uni_modules版本的即可,无需import
基本使用
<template>
<uz-chat @sendMessage="sendMessage" :isSending="isSending" :messages="messages" v-model:modelValue="inputMessage" :offset-height="topHeight + 'rpx'" @avatarClickDb="handleAvatarDoubleClick">
</uz-chat>
</template>
<script setup lang="ts">
import { ref, onMounted, nextTick, watch } from 'vue'
const isSending = ref(false)
interface Message {
role: 'user' | 'assistant'
content: string
timestamp: number
isPending?: boolean
}
const messages = ref<Message[]>([])
const inputMessage = ref('')
const baseUrl = import.meta.env.VITE_API_HOST || ''
watch(inputMessage, (newValue) => {
console.log('inputMessage 变化了:', newValue)
})
// 发送消息
const sendMessage = async (msg: string) => {
if (!msg.trim() || isSending.value) return
isSending.value = true
// 添加用户消息
messages.value.push({
role: 'user',
content: msg,
timestamp: Date.now()
})
// 清空输入框
inputMessage.value = ''
// 添加助手消息(初始为空)
const assistantMessage = {
role: 'assistant' as 'user' | 'assistant',
content: '',
isPending: true,
timestamp: Date.now()
}
messages.value.push(assistantMessage)
try {
const requestTask = await uni.request({
url: `${baseUrl}/ai/stream-chat`,
method: 'POST',
header: {
Accept: 'text/event-stream',
'Authorization': `Bearer ${token}`
},
responseType: 'arraybuffer',
data: {
messages: messages.value.map(({ role, content }) => ({ role, content }))
},
enableChunked: true,
success: (res) => {
console.log('请求成功:', res)
},
fail: (err) => {
console.error('请求失败:', err)
throw err
}
});
requestTask.onChunkReceived(async (res: any) => {
messages.value[messages.value.length - 1].isPending = false
const uint8Array = new Uint8Array(res.data);
const text = new TextDecoder('utf-8').decode(uint8Array);
const lines = text.split('\n')
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = line.slice(6)
if (data === '[DONE]') {
isSending.value = false
break
}
try {
const parsed = JSON.parse(data)
if (parsed.content) {
console.log("chant change")
const lastMessage = messages.value[messages.value.length - 1]
lastMessage.content += parsed.content
}
} catch (e) {
console.error('解析数据失败:', e, '原始数据:', data)
}
}
}
});
} catch (error) {
console.error('发送消息失败:', error)
const lastMessage = messages.value[messages.value.length - 1]
lastMessage.content = '抱歉,发生了错误,请稍后重试。'
isSending.value = false
}
}
</script>
服务端
关键设置为SSE,前端设置为SSE,流式返回,才有打字机效果
// 对接deepseek
async function createChatCompletion(messages: any[]) {
const openai = new OpenAI({
baseURL: 'https://api.deepseek.com',
apiKey: process.env.DEEPSEEK_API_KEY
})
try {
// 添加系统提示词
const systemPrompt = {
role: 'system',
content: `你是一个专业的编程助手`
}
// 将系统提示词添加到消息列表的开头
const messagesWithSystemPrompt = [systemPrompt, ...messages]
const completion = await openai.chat.completions.create({
messages: messagesWithSystemPrompt,
model: 'deepseek-chat',
stream: true
})
return completion
} catch (error) {
console.error('Error creating chat completion:', error)
throw error
}
}
// 响应前端
export const streamChat = async (req: Request, res: Response) => {
try {
const { messages } = req.body
// 设置 SSE 头部
res.setHeader('Content-Type', 'text/event-stream')
res.setHeader('Cache-Control', 'no-cache')
res.setHeader('Connection', 'keep-alive')
const stream = await createChatCompletion(messages)
for await (const chunk of stream) {
const content = chunk.choices[0]?.delta?.content
if (content) {
// 将内容包装成 JSON 格式
const data = JSON.stringify({
content,
role: 'assistant'
})
res.write(`data: ${data}\n\n`)
}
}
// 打印返回内容
res.write('data: [DONE]\n\n')
res.end()
} catch (error) {
console.error('Error in streamChat:', error)
res.status(500).json({ error: 'Internal server error' })
}
}
props
参数 |
说明 |
类型 |
默认值 |
可选值 |
messages |
聊天消息列表 |
Array |
[] |
- |
inputMessage |
输入框消息,支持v-model双向绑定 |
String |
|
- |
offsetHeight |
聊天消息组件要减去的高度,单位可以自己拼接 |
String |
0 |
- |
isSending |
是否正在发送消息,正在发送消息,则不能点击发送按钮 |
Boolean |
false |
- |
emits 事件
事件名 |
说明 |
参数 |
sendMessage |
发送消息的事件 |
|
slot 插槽
插槽名 |
说明 |
参数 |
content |
消息内容承载的插槽;方便扩展不同的消息类型,目前自带文本消息类型的组件uz-markdown |
{ message } |
如使用过程中有任何问题,或者您对uz-有一些好的建议,欢迎加入评论,请勿随意差评,毕竟创作不易。
插件规划
- [x] 支持消息
- [x] 支持丝滑滚动
- [x] 支持复制,编辑
- [x] 支持markdown渲染
- [ ] 支持上拉加载更多
- [ ] 支持自定义用户头像,icon等