从未见如此厚颜无耻 發表於 2025-3-15 12:28:00

Vue3-DeepSeek-Chat流式AI对话|vite6+vant4+deepseek智能ai聊天助手

<p><span style="font-size: 18px; font-family: 宋体, &quot;Songti SC&quot;; color: rgba(131, 80, 242, 1)">原创新作<span style="background-color: rgba(204, 255, 255, 1)">vue3.5+deepseek+markdown+vant4</span>仿<strong>DeepSeek-R1</strong>流式输出ai聊天对话。</span></p>
<p><span style="font-size: 12px"><strong>deepseek-vue3-chat</strong> : 实战2025<span style="color: rgba(153, 51, 102, 1)">智能大模型ai会话</span>,基于<span style="background-color: rgba(255, 255, 153, 1); color: rgba(255, 0, 0, 1)">Vue3+Vite6+OpenAI集成接入DeepSeek聊天小助手</span>模板,支持<span style="background-color: rgba(204, 255, 204, 1)">流式打字输出效果、浅色/暗黑主题模式、代码高亮显示、针对移动端+PC端适配</span>处理。</span></p>
<p><img src="https://img2024.cnblogs.com/blog/1289798/202503/1289798-20250315110416747-532200101.png"></p>
<blockquote>
<p><span style="background-color: rgba(204, 255, 204, 1)"><span style="background-color: rgba(204, 255, 204, 1)">Vite8+DeepSeek网页版AI助手|vue3+arco本地web版ai流式打字问答系统</span></span></p>
<p><span style="background-color: rgba(255, 204, 255, 1)"><span style="background-color: rgba(255, 204, 255, 1)">uniapp+deepseek流式ai助理|uniapp+vue3对接deepseek三端Ai问答模板</span></span></p>
<p><span style="background-color: rgba(204, 255, 255, 1)"><span style="background-color: rgba(204, 255, 255, 1)">tauri2.10+deepseek+vite7客户端ai系统|Tauri2+Vue3.5桌面AI程序Exe</span></span></p>
<p><span style="background-color: rgba(255, 255, 153, 1)"><span style="background-color: rgba(255, 255, 153, 1)">flutter3-deepseek流式AI模板|Flutter3.27+Dio+DeepSeeek聊天ai助手</span></span></p>
</blockquote>
<h3><img src="https://img2024.cnblogs.com/blog/1289798/202506/1289798-20250625083110146-297151432.png"></h3>
<h3>🐬使用技术</h3>
<ul>
<li><span style="font-size: 12px">开发工具:VScode</span></li>
<li><span style="font-size: 12px">技术框架:Vite^6.2.0+Vue^3.5.13+vue-router^4.5.0</span></li>
<li><span style="font-size: 12px">大模型框架:DeepSeek-R1 + OpenAI</span></li>
<li><span style="font-size: 12px">UI组件库:Vant^4.9.17 (有赞vue3移动端组件库)</span></li>
<li><span style="font-size: 12px">状态管理:pinia^3.0.1</span></li>
<li><span style="font-size: 12px">高亮插件:highlight.js^11.11.1</span></li>
<li><span style="font-size: 12px">markdown解析:markdown-it</span></li>
<li><span style="font-size: 12px">本地缓存:pinia-plugin-persistedstate^4.2.0</span></li>
</ul>
<p><img src="https://img2024.cnblogs.com/blog/1289798/202503/1289798-20250315111405138-598170330.gif"></p>
<p><img src="https://img2024.cnblogs.com/blog/1289798/202503/1289798-20250315111448935-310631354.gif"></p>
<h3>✨项目特性</h3>
<ol>
<li><span style="font-size: 12px">Vue3+DeepSeek实现流式打字输出效果</span></li>
<li><span style="font-size: 12px">基于Vue3构建,集成DeepSeek,性能更优,对话丝滑流畅</span></li>
<li><span style="font-size: 12px">支持各种代码高亮,方便展示和分享代码片段</span></li>
<li><span style="font-size: 12px">使用vant4组件库,风格统一,时尚大气</span></li>
<li><span style="font-size: 12px">支持移动端+PC端750px像素适配,大屏也有良好体验</span></li>
</ol>
<p><img src="https://img2024.cnblogs.com/blog/1289798/202503/1289798-20250315114034641-1062318962.gif"></p>
<h3>🛠️项目结构目录</h3>
<p><span style="font-size: 12px">vue3-deepseek使用&nbsp;<span class="cnblogs_code">vite6.x</span>&nbsp;构建项目模板,采用&nbsp;<span class="cnblogs_code">vue3 setup</span>&nbsp;语法编码开发。</span></p>
<p><img src="https://img2024.cnblogs.com/blog/1289798/202503/1289798-20250315114341324-356704527.png"></p>
<blockquote>
<h4><span style="font-family: &quot;comic sans ms&quot;, sans-serif"><em>vue3-deepseek聊天ai对话项目已经同步到我的作品集,感谢大家的鼓励与支持!</em></span></h4>
<p><span style="font-size: 12px">vue3+vite6+deepseek+vant4流式聊天AI对话模板</span></p>
</blockquote>
<h3>环境变量配置.env</h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)"># title
VITE_APP_TITLE </span>= 'Vue3-DeepSeek-Chat'<span style="color: rgba(0, 0, 0, 1)">

# port 默认http:</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">localhost:5173/</span>
VITE_PORT = 3001<span style="color: rgba(0, 0, 0, 1)">

# 运行时自动打开浏览器
VITE_OPEN </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">

# 开启https
VITE_HTTPS </span>= <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">

# 是否删除生产环境 console
VITE_DROP_CONSOLE </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">

# DeepSeek API配置
VITE_DEEPSEEK_API_KEY </span>=<span style="color: rgba(0, 0, 0, 1)"> 替换为你的 API Key
VITE_DEEPSEEK_BASE_URL </span>= https:<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">api.deepseek.com</span></pre>
</div>
<h3>入口文件配置main.js</h3>
<div class="cnblogs_code">
<pre>import { createApp } from 'vue'<span style="color: rgba(0, 0, 0, 1)">
import </span>'./style.scss'<span style="color: rgba(0, 0, 0, 1)">
import App from </span>'./App.vue'

<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 引入路由/状态管理</span>
import Router from './router'<span style="color: rgba(0, 0, 0, 1)">
import Pinia from </span>'./pinia'<span style="color: rgba(0, 0, 0, 1)">

import Plugins from </span>'./plugins'<span style="color: rgba(0, 0, 0, 1)">

const app </span>=<span style="color: rgba(0, 0, 0, 1)"> createApp(App)

app
.use(Router)
.use(Pinia)
.use(Plugins)
.mount(</span>'#app')</pre>
</div>
<p><img src="https://img2024.cnblogs.com/blog/1289798/202503/1289798-20250315115456309-133509995.png"></p>
<p><img src="https://img2024.cnblogs.com/blog/1289798/202503/1289798-20250315115530905-644854416.png"></p>
<h3>布局模板</h3>
<p><img src="https://img2024.cnblogs.com/blog/1289798/202503/1289798-20250315211624448-1209945226.png"></p>
<p><span style="font-size: 12px">如上图:页面整体分为<strong>顶部导航条+聊天对话区+底部编辑器</strong>三个部分。</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">template</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">div </span><span style="color: rgba(255, 0, 0, 1)">class</span><span style="color: rgba(0, 0, 255, 1)">="flexbox flex-col"</span><span style="color: rgba(255, 0, 0, 1)"> style</span><span style="color: rgba(0, 0, 255, 1)">="height:100%;"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">Toolbar </span><span style="color: rgba(255, 0, 0, 1)">:title</span><span style="color: rgba(0, 0, 255, 1)">="chatSession?.title"</span> <span style="color: rgba(0, 0, 255, 1)">/&gt;</span>

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

    <span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> 编辑器 </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">ChatEditor </span><span style="color: rgba(255, 0, 0, 1)">ref</span><span style="color: rgba(0, 0, 255, 1)">="editorRef"</span><span style="color: rgba(255, 0, 0, 1)"> :value</span><span style="color: rgba(0, 0, 255, 1)">="promptValue"</span><span style="color: rgba(255, 0, 0, 1)"> :reachBottom</span><span style="color: rgba(0, 0, 255, 1)">="reachBottom"</span><span style="color: rgba(255, 0, 0, 1)"> :scrollBottom</span><span style="color: rgba(0, 0, 255, 1)">="scrollToBottom"</span> <span style="color: rgba(0, 0, 255, 1)">/&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">div</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">template</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span></pre>
</div>
<p><img src="https://img2024.cnblogs.com/blog/1289798/202503/1289798-20250315121153737-1103815794.gif"></p>
<h3>路由配置</h3>
<p><img src="https://img2024.cnblogs.com/blog/1289798/202503/1289798-20250315115801428-1154894481.png"></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
* 路由配置
* @author andy
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">

import { createRouter, createWebHashHistory } from </span>'vue-router'<span style="color: rgba(0, 0, 0, 1)">
import { appStore } from </span>'@/pinia/modules/app'

<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 批量导入modules路由</span>
const modules = import.meta.glob('./modules/*.js', { eager: <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)"> })
const patchRoutes </span>= Object.keys(modules).map(key =&gt; modules.<span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)">).flat()

const routes </span>=<span style="color: rgba(0, 0, 0, 1)"> [
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 错误模块</span>
<span style="color: rgba(0, 0, 0, 1)">{
    path: </span>'/:pathMatch(.*)*'<span style="color: rgba(0, 0, 0, 1)">,
    component: () </span>=&gt; import('@views/error/404.vue'<span style="color: rgba(0, 0, 0, 1)">),
    meta: {
      title: </span>'404 error'<span style="color: rgba(0, 0, 0, 1)">
    }
},
...patchRoutes
]

const router </span>=<span style="color: rgba(0, 0, 0, 1)"> createRouter({
history: createWebHashHistory(),
routes
})

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 全局钩子拦截</span>
router.beforeEach((to, from, next) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
const store </span>=<span style="color: rgba(0, 0, 0, 1)"> appStore()
</span><span style="color: rgba(0, 0, 255, 1)">if</span>(to?.meta?.isAuth &amp;&amp; !<span style="color: rgba(0, 0, 0, 1)">store.isLogged) {
    next(</span>'/login'<span style="color: rgba(0, 0, 0, 1)">)
}</span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
    next()
}
})

router.afterEach(() </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> ...</span>
<span style="color: rgba(0, 0, 0, 1)">})

router.onError(error </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> {
console.warn(</span>'Router Error&gt;&gt;'<span style="color: rgba(0, 0, 0, 1)">, error.message);
})

export </span><span style="color: rgba(0, 0, 255, 1)">default</span> router</pre>
</div>
<p><img src="https://img2024.cnblogs.com/blog/1289798/202503/1289798-20250315115915352-1364055923.png"></p>
<p><img src="https://img2024.cnblogs.com/blog/1289798/202503/1289798-20250315115924392-723515158.png"></p>
<p><img src="https://img2024.cnblogs.com/blog/1289798/202503/1289798-20250315115936813-1523547459.png"></p>
<p><img src="https://img2024.cnblogs.com/blog/1289798/202503/1289798-20250315115945745-1410380700.png"></p>
<p><img src="https://img2024.cnblogs.com/blog/1289798/202503/1289798-20250315115956088-6433701.png"></p>
<p><img src="https://img2024.cnblogs.com/blog/1289798/202503/1289798-20250315120010977-540777232.png"></p>
<p><img src="https://img2024.cnblogs.com/blog/1289798/202503/1289798-20250315120025003-1004813714.png"></p>
<p><img src="https://img2024.cnblogs.com/blog/1289798/202503/1289798-20250315120038162-683983035.png"></p>
<p><img src="https://img2024.cnblogs.com/blog/1289798/202503/1289798-20250315120050208-2086967406.png"></p>
<p><img src="https://img2024.cnblogs.com/blog/1289798/202503/1289798-20250315120102910-778436751.png"></p>
<p><img src="https://img2024.cnblogs.com/blog/1289798/202503/1289798-20250315120119360-1439249219.png"></p>
<p><img src="https://img2024.cnblogs.com/blog/1289798/202503/1289798-20250315120141933-1262958377.png"></p>
<p><img src="https://img2024.cnblogs.com/blog/1289798/202503/1289798-20250315120202678-402516366.png"></p>
<p><img src="https://img2024.cnblogs.com/blog/1289798/202503/1289798-20250315120214409-1302593388.png"></p>
<p><img src="https://img2024.cnblogs.com/blog/1289798/202503/1289798-20250315120236466-1252787301.png"></p>
<p><img src="https://img2024.cnblogs.com/blog/1289798/202503/1289798-20250315120250195-1328024632.png"></p>
<p><img src="https://img2024.cnblogs.com/blog/1289798/202503/1289798-20250315120308015-150011619.png"></p>
<p><img src="https://img2024.cnblogs.com/blog/1289798/202503/1289798-20250315120327969-1439491164.png"></p>
<p><img src="https://img2024.cnblogs.com/blog/1289798/202503/1289798-20250315120346924-67567818.png"></p>
<h3>vue3+pinia状态管理</h3>
<p><img src="https://img2024.cnblogs.com/blog/1289798/202503/1289798-20250315120447072-1620738521.png"></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
* 状态管理 Pinia
* @author andy
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">

import { createPinia } from </span>'pinia'
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 引入pinia本地持久化存储</span>
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'<span style="color: rgba(0, 0, 0, 1)">

const pinia </span>=<span style="color: rgba(0, 0, 0, 1)"> createPinia()
pinia.use(piniaPluginPersistedstate)

export </span><span style="color: rgba(0, 0, 255, 1)">default</span> pinia</pre>
</div>
<p><span style="font-size: 12px">vue3和deepseek聊天会话都保存在本地存储中。</span></p>
<div class="cnblogs_code">
<pre>export const chatStore = defineStore('chat'<span style="color: rgba(0, 0, 0, 1)">, {
state: () </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> ({
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 聊天会话记录</span>
    sessionId: ''<span style="color: rgba(0, 0, 0, 1)">,
    session: []
}),
getters: {},
actions: {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 创建新对话</span>
<span style="color: rgba(0, 0, 0, 1)">    createSession(ssid) {
      </span><span style="color: rgba(0, 0, 255, 1)">this</span>.sessionId =<span style="color: rgba(0, 0, 0, 1)"> ssid
      </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.session.unshift({
      sessionId: ssid,
      title: </span>''<span style="color: rgba(0, 0, 0, 1)">,
      data: []
      })
    },

    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 新增会话</span>
<span style="color: rgba(0, 0, 0, 1)">    addSession(message) {
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 判断当前会话uuid是否存在,不存在创建新对话</span>
      <span style="color: rgba(0, 0, 255, 1)">if</span>(!<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.sessionId) {
      const ssid </span>=<span style="color: rgba(0, 0, 0, 1)"> guid()
      </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.createSession(ssid)
      }
      </span><span style="color: rgba(0, 0, 255, 1)">this</span>.session.map(item =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
      </span><span style="color: rgba(0, 0, 255, 1)">if</span>(item.sessionId == <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.sessionId) {
          </span><span style="color: rgba(0, 0, 255, 1)">if</span>(!<span style="color: rgba(0, 0, 0, 1)">item.title) {
            item.title </span>=<span style="color: rgba(0, 0, 0, 1)"> message.content
          }
          item.data.push(message)
      }
      })
    },

    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 更新某一条会话</span>
<span style="color: rgba(0, 0, 0, 1)">    updateSession(key, content) {
      </span><span style="color: rgba(0, 0, 255, 1)">this</span>.session.map(item =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
      </span><span style="color: rgba(0, 0, 255, 1)">if</span>(item.sessionId == <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.sessionId) {
          </span><span style="color: rgba(0, 0, 255, 1)">if</span>(item.data &amp;&amp; !<span style="color: rgba(0, 0, 0, 1)">isEmpty(item.data)) {
            item.data.map((it, index) </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> {
            </span><span style="color: rgba(0, 0, 255, 1)">if</span>(it.key ==<span style="color: rgba(0, 0, 0, 1)"> key) {
                it.content </span>=<span style="color: rgba(0, 0, 0, 1)"> content
            }
            })
          }
      }
      })
    },

    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 获取会话</span>
<span style="color: rgba(0, 0, 0, 1)">    getSession() {
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">this</span>.session.find(item =&gt; item.sessionId == <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.sessionId)
    },

    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 移除会话</span>
<span style="color: rgba(0, 0, 0, 1)">    removeSession(ssid) {
      const index </span>= <span style="color: rgba(0, 0, 255, 1)">this</span>.session.findIndex(item =&gt; item?.sessionId ===<span style="color: rgba(0, 0, 0, 1)"> ssid)
      </span><span style="color: rgba(0, 0, 255, 1)">if</span>(index &gt; -1<span style="color: rgba(0, 0, 0, 1)">) {
      </span><span style="color: rgba(0, 0, 255, 1)">this</span>.session.splice(index, 1<span style="color: rgba(0, 0, 0, 1)">)
      }
      </span><span style="color: rgba(0, 0, 255, 1)">this</span>.sessionId = ''<span style="color: rgba(0, 0, 0, 1)">
    },
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 删除某一条会话</span>
<span style="color: rgba(0, 0, 0, 1)">    deleteSession(key) {
      </span><span style="color: rgba(0, 0, 255, 1)">this</span>.session.map(item =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
      </span><span style="color: rgba(0, 0, 255, 1)">if</span>(item.sessionId == <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.sessionId) {
          </span><span style="color: rgba(0, 0, 255, 1)">if</span>(item.data &amp;&amp; !<span style="color: rgba(0, 0, 0, 1)">isEmpty(item.data)) {
            item.data.map((it, index) </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> {
            </span><span style="color: rgba(0, 0, 255, 1)">if</span>(it.key ==<span style="color: rgba(0, 0, 0, 1)"> key) {
                item.data.splice(index, </span>1<span style="color: rgba(0, 0, 0, 1)">)
            }
            })
          }
      }
      })
    },

    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 清空会话</span>
<span style="color: rgba(0, 0, 0, 1)">    clearSession() {
      </span><span style="color: rgba(0, 0, 255, 1)">this</span>.session =<span style="color: rgba(0, 0, 0, 1)"> []
      </span><span style="color: rgba(0, 0, 255, 1)">this</span>.sessionId = ''<span style="color: rgba(0, 0, 0, 1)">
    }
},
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 本地持久化存储(默认存储localStorage)</span>
persist: <span style="color: rgba(0, 0, 255, 1)">true</span>
<span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)"> persist: {
    // key: 'chatStore', // 不设置则是默认app
    storage: localStorage,
    paths: ['ssid', 'sessions'] // 设置缓存键
} </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
})</span></pre>
</div>
<h3>vue3调用deepseek接口</h3>
<ul>
<li><strong>非流式输出</strong></li>
</ul>
<div class="cnblogs_code">
<pre>const completion =<span style="color: rgba(0, 0, 0, 1)"> await openai.chat.completions.create({
messages: [
    {role: </span>'user'<span style="color: rgba(0, 0, 0, 1)">, content: editorValue}
],
model: </span>'deepseek-chat', <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> deepseek-chat对话模型 deepseek-reasoner推理模型</span>
stream: <span style="color: rgba(0, 0, 255, 1)">false</span>, <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 流式输出</span>
max_tokens: 8192, <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 限制一次请求中模型生成 completion 的最大 token 数(默认使用 4096)</span>
temperature: 0.4, <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 严谨采样 越低越严谨(默认1)</span>
<span style="color: rgba(0, 0, 0, 1)">})<br>
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 处理返回数据</span>
console.log(completion.choices.message.content)</pre>
</div>
<ul>
<li><strong>流式输出</strong></li>
</ul>
<p><span style="font-size: 12px">通过for循环处理流式输出数据。</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 处理流式输出</span>
let content = ''
<span style="color: rgba(0, 0, 255, 1)">for</span><span style="color: rgba(0, 0, 0, 1)"> await (const chunk of completion) {
content </span>+= chunk.choices.delta.content;
chatState.updateSession(uniKey, content)
</span><span style="color: rgba(0, 0, 255, 1)">if</span>(chunk.choices.finish_reason == 'stop'<span style="color: rgba(0, 0, 0, 1)">) {
    loading.value </span>= <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">
}
</span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)">(props.reachBottom) {
    props.scrollBottom()
}
}</span></pre>
</div>
<p><img src="https://img2024.cnblogs.com/blog/1289798/202503/1289798-20250315121910715-1798718007.gif"></p>
<p>综上就是vue3+deepseek实战ai对话小助手的一些知识分享,希望对大家有所帮助!感谢大家的阅读与支持~</p>
<h3>electron39-vue3ai电脑端AI模板|electron39+deepseek+vite7聊天ai应用</h3>
<h3>Vite8+DeepSeek网页版AI助手|vue3+arco本地web版ai流式打字问答系统</h3>
<h3>uniapp+deepseek流式ai助理|uniapp+vue3对接deepseek三端Ai问答模板</h3>
<p><strong>附上几个最新实战项目案例</strong></p>
<blockquote>
<p><span style="font-size: 12px">Electron38-Wechat电脑端聊天|vite7+electron38仿微信桌面端聊天系统</span></p>
<p><span style="font-size: 12px">最新版uni-app+vue3+uv-ui跨三端仿微信app聊天应用【h5+小程序+app端】</span></p>
<p><span style="font-size: 12px">Electron38-Vue3OS客户端OS系统|vite7+electron38+arco桌面os后台管理</span></p>
<p><span style="font-size: 12px">electron38-admin桌面端后台|Electron38+Vue3+ElementPlus管理系统</span></p>
<p><span style="font-size: 12px">flutter3-winseek客户端AI实例|Flutter3.32+DeepSeek流式ai对话模板Exe</span></p>
<p><span style="font-size: 12px">flutter3-trip仿携程酒店预订|Flutter3.27+Getx预约旅游酒店App程序</span></p>
<p><span style="font-size: 12px">最新版Flutter3.38+Dart3.10仿写抖音APP直播+短视频+聊天应用程序</span></p>
<p><span style="font-size: 12px">Tauri2.9+Vue3桌面版OS系统|vite7+tauri2+arcoDesign电脑端os后台模板</span></p>
<p><span style="font-size: 12px">Flutter3-MacOS桌面OS系统|flutter3.32+window_manager客户端OS模板</span></p>
<p><span style="font-size: 12px">uniapp+vue3酒店预订|vite5+uniapp预约订房系统模板(h5+小程序+App端)</span></p>
</blockquote>
<p><img src="https://img2024.cnblogs.com/blog/1289798/202503/1289798-20250315122354468-1538948487.gif"></p>
<p>&nbsp;</p>

</div>
<div id="MySignature" role="contentinfo">
    本文为博主原创文章,未经博主允许不得转载,欢迎大家一起交流 QQ(282310962) wx(xy190310)<br><br>
来源:https://www.cnblogs.com/xiaoyan2017/p/18773480
頁: [1]
查看完整版本: Vue3-DeepSeek-Chat流式AI对话|vite6+vant4+deepseek智能ai聊天助手