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本地存储会话
使用pinia
和pinia-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仿携程