更新记录

1.0.0(2025-06-22) 下载此版本

uni-app 聊天组件

  1. 支持消息展示
  2. 支持实时滚动消息到最底下
  3. 支持复制消息
  4. 支持markdown格式渲染
  5. 支持重新编辑
  6. 支持打字机效果
  7. 可以对接到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等

隐私、权限声明

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

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

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

许可协议

MIT协议

暂无用户评论。

使用中有什么不明白的地方,就向插件作者提问吧~ 我要提问