Vue2下项目集成DeepSeek API
本文介绍了如何创建并配置一个基于Vue2的DeepSeek问答应用。首先通过vue create命令创建项目,安装axios、markdown等必要依赖。核心功能包括:1)使用App.vue构建聊天界面,实现用户与AI的对话交互;2)通过deepseek.js配置API接口,支持流式响应和思考模式;3)实现消息渲染、Markdown解析、对话历史管理等功能。应用特点包含:实时对话流、消息分角色显示
·
1.使用命令下载vue2项目
vue create deepseek
2.进入到deepseek项目里 下载依赖 启动
1.进入到项目里
cd deepseek
2.下载依赖
npm install
3.下载deepseek所需要的依赖
npm install axios
npm install markdown
npm install marked
npm install html-loader
npm install dompurify
3.启动
npm run serve
3.接下来就是核心代码了 搭建最基础问答页面
3-1 App.vue 替换成以下内容
<template>
<div id="app">
<div class="chat-container">
<div class="chat-content">
<div class="chatBox">
<div>
<img
src="https://cdn.deepseek.com/platform/favicon.png"
style="color: blue; margin: 10px 10px 5px 10px"
class="avatar"
alt="机器人头像"
/>
<h1 style="color: limegreen; margin: 0px 10px 0px 10px">嗨~Vue3-DeepSeek</h1>
<div style="margin: 0px 10px 0px 10px">
你身边的智能小帮手,我可以帮你搜索、答疑、写作,请把你的任务交给我吧~
</div>
</div>
<div
v-if="errorMessage"
class="error-message"
>
{{ errorMessage }}
</div>
<div class="chat-messages">
<div
v-for="(message, index) in currentMessages"
:key="index"
class="message"
>
<div
v-if="message.sender === 'user'"
class="user-message-container"
>
<article class="message-content user-message">{{ message.text }}</article>
<img
src="../src/assets/ermimi.jpg"
class="avatar user-avatar"
alt="用户头像"
/>
</div>
<div
v-else
class="bot-message-container"
>
<img
src="https://cdn.deepseek.com/platform/favicon.png"
class="avatar bot-avatar"
alt="机器人头像"
/>
<article
class="message-content bot-message"
v-html="renderMarkdown(message.text)"
:data-completed="message.completed || !isStreaming"
></article>
</div>
</div>
</div>
<div class="chat-input">
<textarea
v-model="inputMessage"
placeholder="请输入您的问题"
@keyup.enter.exact="handleSend"
rows="6"
:disabled="isStreaming"
/>
<button
@click="handleButtonClick"
:disabled="!inputMessage.trim() && !isStreaming"
>
{{ isStreaming ? "停止生成" : "发送" }}
</button>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import marked from "marked";
import DOMPurify from "dompurify";
import { postStream } from "./deepSeek.js";
export default {
name: "App",
data() {
return {
currentMessages: [],
inputMessage: "",
errorMessage: "",
currentBotMessage: "",
isStreaming: false,
abortController: null,
conversationId: null,
// 头像URL可以存储在data中以便动态更改
userAvatar: "",
botAvatar: "",
};
},
created() {
this.generateConversationId();
},
methods: {
generateConversationId() {
this.conversationId = "conv-" + Date.now();
},
renderMarkdown(content) {
content = content || "";
content = content.replace(
/<think>/g,
'<div class="thinking-container"><img src="https://cdn-icons-png.flaticon.com/512/1055/1055687.png" class="thinking-icon"><span class="deepThink">'
);
if (!content.includes("</think>")) {
content = content.concat("</span></div>");
}
if (content.includes("</think>")) {
content = content.replace(/<\/span><\/div>/g, "");
content = content.replace(/<\/think>/g, "</span></div>");
}
const html = marked(content);
const sanitizedHtml = DOMPurify.sanitize(html);
const tempDiv = document.createElement("div");
tempDiv.innerHTML = sanitizedHtml.toString();
const deepThinkElements = tempDiv.querySelectorAll(".deepThink");
deepThinkElements.forEach((element) => {
if (element.textContent.trim() === "") {
element.textContent = "暂无推理过程";
}
});
return tempDiv.innerHTML;
},
async handleSend() {
if (!this.inputMessage.trim() || this.isStreaming) return;
this.abortController = new AbortController();
const userMessage = {
sender: "user",
text: this.inputMessage,
conversationId: this.conversationId,
};
this.currentMessages.push(userMessage);
const botMessageIndex = this.currentMessages.length;
this.currentMessages.push({
sender: "system",
text: "",
completed: false,
conversationId: this.conversationId,
});
const conversationHistory = this.buildConversationHistory();
this.inputMessage = "";
this.isStreaming = true;
this.currentBotMessage = "";
try {
const response = await postStream(
{
messages: conversationHistory,
model: "deepseek-chat",
stream: true,
conversation_id: this.conversationId,
},
{
signal: this.abortController.signal,
}
);
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (this.isStreaming) {
const { done, value } = await reader.read();
if (done) break;
const text = decoder.decode(value);
const lines = text.split("\n\n").filter((line) => line.trim());
for (const line of lines) {
if (line.trim() === "data: [DONE]") {
this.finishGeneration(botMessageIndex);
return;
}
if (line.startsWith("data: ")) {
try {
const jsonStr = line.substring(6);
const data = JSON.parse(jsonStr);
console.log(data, "data==data");
if (data.choices?.[0]?.delta?.content) {
this.currentBotMessage += data.choices[0].delta.content;
this.$set(this.currentMessages, botMessageIndex, {
sender: "system",
text: this.currentBotMessage,
completed: false,
conversationId: this.conversationId,
});
this.$nextTick(() => {
const container = this.$el.querySelector(".chat-messages");
container.scrollTop = container.scrollHeight;
});
}
} catch (e) {
console.error("解析JSON出错:", e);
}
}
}
}
} catch (error) {
if (error.name !== "AbortError") {
console.error("流式请求出错:", error);
this.errorMessage = "请求出错: " + error.message;
this.$set(this.currentMessages, botMessageIndex, {
sender: "system",
text: "请求出错: " + error.message,
completed: true,
conversationId: this.conversationId,
});
}
} finally {
if (this.isStreaming) {
this.finishGeneration(botMessageIndex);
}
}
},
buildConversationHistory() {
const currentConversationMessages = this.currentMessages.filter(
(msg) => msg.conversationId === this.conversationId
);
const recentMessages = currentConversationMessages.slice(-10);
return recentMessages.map((message) => {
return {
role: message.sender === "user" ? "user" : "assistant",
content: message.text,
};
});
},
finishGeneration(index) {
this.isStreaming = false;
if (this.abortController) {
this.abortController.abort();
this.abortController = null;
}
this.$set(this.currentMessages, index, {
...this.currentMessages[index],
completed: true,
});
},
handleButtonClick() {
if (this.isStreaming) {
this.finishGeneration(this.currentMessages.length - 1);
} else {
this.handleSend();
}
},
startNewConversation() {
this.generateConversationId();
},
},
};
</script>
<style scoped>
#app {
display: flex;
height: 100vh;
margin: 0;
padding: 0;
background-color: #f9f9f9;
}
.chat-container {
display: flex;
width: 100%;
height: 100%;
border: 1px solid #e0e0e0;
border-radius: 10px;
overflow: hidden;
background-color: white;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.chat-content {
width: 100%;
height: 100%;
padding: 15px;
box-sizing: border-box;
}
.chatBox {
display: flex;
flex-direction: column;
height: 92%;
width: 100%;
border: 1px solid #e0e0e0;
border-radius: 10px;
overflow: hidden;
background-color: white;
}
.error-message {
background-color: #f8d7da;
color: #721c24;
padding: 12px;
margin-bottom: 12px;
border-radius: 8px;
text-align: center;
}
.chat-messages {
flex: 1;
padding: 15px;
overflow-y: auto;
}
.message {
margin-bottom: 15px;
display: flex;
align-items: flex-start;
}
.user-message-container {
display: flex;
justify-content: flex-end;
width: 100%;
align-items: flex-start;
gap: 10px;
}
.bot-message-container {
display: flex;
justify-content: flex-start;
width: 100%;
align-items: flex-start;
gap: 10px;
}
.avatar {
width: 40px;
height: 40px;
border-radius: 50%;
object-fit: cover;
flex-shrink: 0;
}
.user-avatar {
border: 2px solid #2196f3;
}
.bot-avatar {
border: 2px solid #4caf50;
}
.message-content {
max-width: calc(80% - 50px);
padding: 10px 15px;
border-radius: 18px;
line-height: 1.4;
word-wrap: break-word;
}
.user-message {
background-color: #e3f2fd;
margin-left: 20%;
}
.bot-message {
background-color: #f5f5f5;
margin-right: 20%;
}
.chat-input {
display: flex;
padding: 15px;
border-top: 1px solid #e0e0e0;
background-color: #fff;
}
.chat-input textarea {
flex: 1;
padding: 12px;
border: 1px solid #e0e0e0;
border-radius: 8px;
margin-right: 12px;
resize: none;
min-height: 60px;
max-height: 150px;
font-family: inherit;
font-size: 14px;
}
.chat-input textarea:focus {
outline: none;
border-color: #2196f3;
}
.chat-input button {
padding: 0 20px;
height: 48px;
background-color: #2196f3;
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
transition: background-color 0.2s;
}
.chat-input button:hover {
background-color: #1976d2;
}
.chat-input button:disabled {
background-color: #b0bec5;
cursor: not-allowed;
}
.bot-message:not([data-completed="true"]):after {
content: "|";
animation: blink 1s infinite;
color: #666;
}
@keyframes blink {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0;
}
}
/* 深度思考样式 */
.deepThink {
color: #666;
font-style: italic;
background-color: #f0f0f0;
padding: 8px 12px;
border-radius: 8px;
display: inline-block;
margin-top: 5px;
}
.thinking-container {
display: flex;
align-items: center;
gap: 8px;
margin: 5px 0;
}
.thinking-icon {
width: 16px;
height: 16px;
opacity: 0.6;
}
</style>
3-2 和App.vue 同级下 建个deepseek.js(接口及集合DeepSeek一些配置)
const DEEPSEEK_API_KEY = ""; // 替换为你的 DeepSeek API Key
// /**
// * 调用 DeepSeek API 示例
// * @param {Object} data 请求参数
// * @returns {Promise} API 响应
// */
// export function postStream(data) {
// return fetch("https://api.deepseek.com/chat/completions", {
// method: "post",
// body: JSON.stringify(data),
// headers: {
// Authorization: `Bearer ${DEEPSEEK_API_KEY}`,
// "Content-Type": "application/json",
// },
// });
// }
/**
* 调用 DeepSeek API 示例
* @param {Object} data 请求参数
* @returns {Promise} API 响应
*/
export function postStream(data) {
// 确保传入的data对象包含model和参数配置
const requestData = {
// 指定模型版本
model: "deepseek-reasoner",
// 模型版本配置
model_version: "DeepSeek-V3.2-Exp",
// 添加思考模式配置
params: {
// 是否启用思考模式
thinking_mode: true,
// 其他可选参数
temperature: 0.7,
max_tokens: 4096,
// 更多参数...
},
// 合并用户传入的其他数据
...data,
};
return fetch("https://api.deepseek.com/chat/completions", {
method: "post",
body: JSON.stringify(requestData),
headers: {
Authorization: `Bearer ${DEEPSEEK_API_KEY}`,
"Content-Type": "application/json",
},
});
}
火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐
所有评论(0)