vue3仿Deepseek/ChatGPT流式聊天AI界面,对接deepseek/OpenAI API

2025年实战Vue3仿ChatGPT/DeepSeek Stream流式打字输出AI聊天对话,正式完结啦。

vue3-deepseek-chat:2025新作vite6+vue3+deepseek+vant4+openai高颜值流式对话效果AI小助手。Vue3 集成 DeepSeek 的完整 API聊天大模型。支持dark+light主题模式、代码高亮、本地缓存,支持移动端+PC端完美显示。

技术栈

  • 编辑器:VScode
  • 技术框架:Vite^6.2.0+Vue^3.5.13+vue-router^4.5.0
  • AI大模型:DeepSeek-R1 / OpenAI
  • UI组件库:vant^4.9.17 (有赞vue3移动端组件库)
  • 状态管理:pinia^3.0.1
  • 高亮插件:highlight.js^11.11.1
  • markdown解析:markdown-it
  • 本地缓存:pinia-plugin-persistedstate^4.2.0

项目目录结构

配置.env

需自行去deepseek官网注册apikey,替换VITE_DEEPSEEK_API_KEY配置即可快速体验ai聊天功能。

bash 复制代码
# title
VITE_APP_TITLE = 'Vue3-DeepSeek-Chat'

# port 默认http://localhost:5173/
VITE_PORT = 3001

# 运行时自动打开浏览器
VITE_OPEN = true

# 开启https
VITE_HTTPS = false

# 是否删除生产环境 console
VITE_DROP_CONSOLE = true

# DeepSeek API配置
VITE_DEEPSEEK_API_KEY = 替换为你的 API Key
VITE_DEEPSEEK_BASE_URL = https://api.deepseek.com

main.js配置

ts 复制代码
import { createApp } from 'vue'
import './style.scss'
import App from './App.vue'

// 引入路由/状态管理
import Router from './router'
import Pinia from './pinia'

import Plugins from './plugins'

const app = createApp(App)

app
.use(Router)
.use(Pinia)
.use(Plugins)
.mount('#app')

ai聊天界面模板

如下图:整体分为顶部导航条+聊天功能区+底部操作栏三部分。

html 复制代码
<template>
  <div class="flexbox flex-col" style="height:100%;">
    <Toolbar :title="chatSession?.title" />

    <div class="v3ai__scrollview flex1">
      <!-- Chat对话 -->
      <div v-if="chatSession && !isEmpty(chatSession.data)" class="v3ai__chatbot" ref="scrollRef" @scroll="onScroll">
        <div class="v3ai__chatbot-sessions">
          ...
        </div>
        <!-- 滚动底部 -->
        <div class="v3ai__scrollbottom flex-c" :class="{'is-bottom': reachBottom}" @click="scrollToBottom"><i class="iconfont ai-arrD"></i></div>
      </div>
      <!-- 导语 -->
      <div v-else class="v3ai__chatbot-intro">
        <i class="logo iconfont ai-deepseek"></i>
        <h3 class="name"><span class="txt text-gradient">嗨~ Vue3-DeepSeek</span></h3>
        <p class="desc">你身边的智能小帮手,我可以帮你搜索、答疑、写作,请把你的任务交给我吧~</p>
        <div class="prompt">
          <p class="tip flex-c"><span class="flex1">你可以这样问</span><span class="flex-c" @click="refreshPrompt">换一换<i class="iconfont ai-shuaxin"></i></span></p>
          <ul class="list">
            <li v-for="(item,index) in promptList" :key="index">
              <div class="txt" @click="changePrompt(item.prompt)">{{item.emoji}} {{item.prompt}}</div>
            </li>
          </ul>
        </div>
      </div>
    </div>

    <!-- 编辑器 -->
    <ChatEditor ref="editorRef" :value="promptValue" :reachBottom="reachBottom" :scrollBottom="scrollToBottom" />
  </div>
</template>

vue3路由配置

ts 复制代码
/**
 * 路由配置
 * @author andy
 */

import { createRouter, createWebHashHistory } from 'vue-router'
import { appStore } from '@/pinia/modules/app'

// 批量导入modules路由
const modules = import.meta.glob('./modules/*.js', { eager: true })
const patchRoutes = Object.keys(modules).map(key => modules[key].default).flat()

const routes = [
  // 错误模块
  {
    path: '/:pathMatch(.*)*',
    component: () => import('@views/error/404.vue'),
    meta: {
      title: '404 error'
    }
  },
  ...patchRoutes
]

const router = createRouter({
  history: createWebHashHistory(),
  routes
})

// 全局钩子拦截
router.beforeEach((to, from, next) => {
  const store = appStore()
  if(to?.meta?.isAuth && !store.isLogged) {
    next('/login')
  }else {
    next()
  }
})

router.afterEach(() => {
  // ...
})

router.onError(error => {
  console.warn('Router Error>>', error.message);
})

export default router

vue3+pinia本地存储会话

使用piniapinia-plugin-persistedstate插件存储本地聊天会话。

ts 复制代码
export const chatStore = defineStore('chat', {
  state: () => ({
    // 聊天会话记录
    sessionId: '',
    session: []
  }),
  getters: {},
  actions: {
    // 创建新对话
    createSession(ssid) {
      this.sessionId = ssid
      this.session.unshift({
        sessionId: ssid,
        title: '',
        data: []
      })
    },

    // 新增会话
    addSession(message) {
      // 判断当前会话uuid是否存在,不存在创建新对话
      if(!this.sessionId) {
        const ssid = guid()
        this.createSession(ssid)
      }
      this.session.map(item => {
        if(item.sessionId == this.sessionId) {
          if(!item.title) {
            item.title = message.content
          }
          item.data.push(message)
        }
      })
    },

    // 更新某一条会话
    updateSession(key, content) {
      this.session.map(item => {
        if(item.sessionId == this.sessionId) {
          if(item.data && !isEmpty(item.data)) {
            item.data.map((it, index) => {
              if(it.key == key) {
                it.content = content
              }
            })
          }
        }
      })
    },

    // 获取会话
    getSession() {
      return this.session.find(item => item.sessionId == this.sessionId)
    },

    // 移除会话
    removeSession(ssid) {
      const index = this.session.findIndex(item => item?.sessionId === ssid)
      if(index > -1) {
        this.session.splice(index, 1)
      }
      this.sessionId = ''
    },
    // 删除某一条会话
    deleteSession(key) {
      this.session.map(item => {
        if(item.sessionId == this.sessionId) {
          if(item.data && !isEmpty(item.data)) {
            item.data.map((it, index) => {
              if(it.key == key) {
                item.data.splice(index, 1)
              }
            })
          }
        }
      })
    },

    // 清空会话
    clearSession() {
      this.session = []
      this.sessionId = ''
    }
  },
  // 本地持久化存储(默认存储localStorage)
  persist: true
  /* persist: {
    // key: 'chatStore', // 不设置则是默认app
    storage: localStorage,
    paths: ['ssid', 'sessions'] // 设置缓存键
  } */
})

vue3对接deepseek api

ts 复制代码
const completion = await openai.chat.completions.create({
  messages: [
    {role: 'user', content: editorValue}
  ],
  model: 'deepseek-chat', // deepseek-chat对话模型 deepseek-reasoner推理模型
  stream: false, // 流式输出
  max_tokens: 8192, // 限制一次请求中模型生成 completion 的最大 token 数(默认使用 4096)
  temperature: 0.4, // 严谨采样 越低越严谨(默认1)
})

一次性输出返回结果。

ts 复制代码
console.log(completion.choices[0].message.content)

流式返回结果需for循环特殊处理。

ts 复制代码
// 处理流式输出
let content = ''
for await (const chunk of completion) {
  content += chunk.choices[0].delta.content;
  chatState.updateSession(uniKey, content)
  if(chunk.choices[0].finish_reason == 'stop') {
    loading.value = false
  }
  if(props.reachBottom) {
    props.scrollBottom()
  }
}

希望以上分享对大家有些帮助。 开发不易,感谢小伙伴们的阅读与支持!

基于uniapp+vue3+uv-ui跨三端酒店预订app模板系统

自研tauri2.0+vite6.x+vue3+arco-design桌面版os管理系统Tauri2-OS

2025版Flutter3.27实战抖音App商城|flutter3+getX短视频+直播+聊天实例

electron31+vite5.x+elementPlus桌面端后台管理系统

Vite5-Electron-Wechat聊天实例|electron31+vue3客户端聊天EXE

Flutter3-Hotel通用酒店预订app实例|flutter3.27仿携程

相关推荐
搬砖-无恙几秒前
vue uniapp里照片多张照片展示
前端·vue.js·uni-app
菜又爱编程2 分钟前
【uni-app运行错误】SassError: expected selector @import “@/uni.scss“;
前端·uni-app·scss
草明8 分钟前
使用 Chrome Flags 设置(适用于 HTTP 站点开发)
前端·chrome·http
余俊晖28 分钟前
DeepSeek-R1思路训练多模态大模型-Vision-R1开源及实现方法思路
llm·多模态·deepseek
武昌库里写JAVA1 小时前
微服务架构: SpringCloud实战案例
vue.js·spring boot·毕业设计·源码·课程设计
Tz一号1 小时前
前端 git规范-不同软件(GitHub、Sourcetree、WebStorm)、命令行合并方式下增加 --no-ff的方法
前端·git·github
Loadings1 小时前
MCP从理解到实现
前端·cursor·ai 编程
冬冬小圆帽1 小时前
防止手机验证码被刷:React + TypeScript 与 Node.js + Express 的全面防御策略
前端·后端·react.js·typescript
Cmoigo2 小时前
React Native自定义View(Android侧)
前端·react native
LanceJiang2 小时前
文本溢出移入Tooltip提示,我的LeText组件
前端·vue.js