2025.07.29今天我学习了如何把DeepSeek接入到前端页面进行使用,效果如下:
一、去deepseek官网创建apiKey。
(注意:需要充值才能使用deepseek)
二、deepseekAPI.js
在api目录下创建deepseekAPI.js组件
import axios from 'axios';
// 移除 interface 声明
// 简单的 JSDoc 注释替代类型注释
/**
* @param {Array} messages - 消息数组
* @returns {Promise<Object>} 返回消息对象
*/
const apiKey = 'xxxxxxxxxxxxxxxx';//换成你自己的apiKey
const headers = {
'Authorization': `Bearer ${apiKey}`,
"Content-Type": "application/json",
"Accept": "application/json",
}
// 调用方法
export const getDeepSeekReply = async (messages) => {
const params = {
messages: messages,
model: "deepseek-chat",
max_tokens: 1500,
temperature: 0.7,
top_p: 1,
frequency_penalty: 0,
presence_penalty: 0,
};
try {
const response = await axios.post('https://api.deepseek.com/chat/completions', params, {
headers: headers
});
return response.data.choices[0].message;
} catch (error) {
console.error('Axios error:', error);
return { content: '获取失败.', role: 'assistant' };
}
}
// 获取当前余额
export const getRemainingNum = async () => {
try {
const response = await axios.get('https://api.deepseek.com/user/balance', {
headers: headers
});
return response.data.balance_infos[0].total_balance;
} catch (error) {
console.error('Axios error:', error);
return { content: '获取失败.', role: 'assistant' };
}
}
三、新页面
包含打印机效果,以及复制文本内容功能。
<template>
<div class="app-container">
<div class="chat-container">
<div class="messages-container" ref="messagesContainer">
<div v-for="(message, index) in messages" :key="index" :class="['message', message.type]">
<div class="message-content">
<div class="avatar">
<el-avatar icon="el-icon-user-solid"/>
</div>
<div class="text-content">
<div class="sender">{{ message.sender }}</div>
<!-- 修改文本显示方式 -->
<div class="text" v-if="message.type === 'received' && message.isTyping">
<div>
<el-tooltip content="复制">
<el-button icon="el-icon-document-copy" type="text" @click="copy_content(message)" />
</el-tooltip>
</div>
<span v-for="(char, charIndex) in message.displayedText" :key="charIndex">{{ char }}</span>
<span class="typing-cursor">|</span>
</div>
<div class="text" v-else>
<div v-if="message.type === 'received'">
<el-tooltip content="复制">
<el-button icon="el-icon-document-copy" type="text" @click="copy_content(message)" />
</el-tooltip>
</div>
{{ message.text }}</div>
<div class="time">{{ message.time }}</div>
</div>
</div>
</div>
</div>
<div v-if="isLoading" class="loading">正在加载...</div>
<div class="input-container">
<el-input type="textarea" v-model="newMessage" placeholder="输入消息..." @keydown.enter.native.prevent="sendMessage" ref="messageInput"/>
<button @click="sendMessage" :disabled="!newMessage.trim() || isTyping" class="send-button">发送</button>
</div>
</div>
</div>
</template>
<script>
import { getDeepSeekReply } from '@/api/deepseekApi';
import moment from 'moment';
import axios from 'axios'
export default {
name: 'ChatInterface',
data() {
return {
newMessage: '',
messages: [],
isLoading: false,
isTyping: false, // 标记AI是否正在打字
copy_text:'',//复制文本
is_copy:false,//是否复制成功
}
},
methods: {
async sendMessage() {
if (!this.newMessage.trim() || this.isTyping) return;
this.is_copy = false;
// 添加新消息
this.messages.push({
type: 'sent',
sender: 'admin',
text: this.newMessage,
time: moment().format('HH:mm'),
});
// 保存输入内容并清空输入框
let input_message = this.newMessage;
this.newMessage = '';
this.isLoading = true;
try {
let demo_text = await getDeepSeekReply([{content:input_message,role:'user'}]);
let message = demo_text.content;
// 开始打字机效果
this.startTypingEffect(message);
} catch (error) {
console.error('请求失败:', error);
this.messages.push({
type: 'received',
sender: 'AI',
text: '抱歉,获取回复失败,请稍后重试。',
time: moment().format('HH:mm'),
});
this.isLoading = false;
}
},
startTypingEffect(fullText) {
this.isLoading = false;
this.isTyping = true;
// 添加一个空的AI消息占位
const messageIndex = this.messages.length;
this.messages.push({
type: 'received',
sender: 'AI',
text: fullText, // 保存完整文本
displayedText: '', // 当前显示的文本
isTyping: true, // 标记正在打字
time: moment().format('HH:mm'),
});
let currentIndex = 0;
const typingSpeed = 10; // 打字速度(毫秒)
const typeNextChar = () => {
if (currentIndex < fullText.length) {
// 逐字添加字符
this.messages[messageIndex].displayedText += fullText.charAt(currentIndex);
currentIndex++;
// 滚动到底部
// this.$nextTick(() => {
// this.scrollToBottom();
// });
// 继续打字
setTimeout(typeNextChar, typingSpeed);
} else {
// 打字完成
this.messages[messageIndex].isTyping = false;
this.isTyping = false;
// 滚动到底部
this.$nextTick(() => {
this.scrollToBottom();
});
}
};
// 开始打字
typeNextChar();
},
// 滚动到底部
scrollToBottom() {
const container = this.$refs.messagesContainer;
if (container) {
container.scrollTop = container.scrollHeight;
}
},
// 复制文本内容
copy_content(row){
this.copy_text = row.text;//复制文本内容
document.execCommand('copy');
// 只有在https local受信任的网址才能使用
// navigator.clipboard.writeText(row.text).then(()=>{
// this.$message.success('复制成功');
// })
},
copyListener(event){
const clipboardData = event.clipboardData || window.clipboardData;
clipboardData.setData('text', this.copy_text);
event.preventDefault();
if(!this.is_copy){
this.is_copy = true;
this.$message.success('复制成功');
}
}
},
mounted() {
// 组件挂载后滚动到底部
this.scrollToBottom();
// 聚焦输入框
this.$refs.messageInput.focus();
window.addEventListener('copy', this.copyListener);//复制文本
}
}
</script>
<style scoped>
.chat-container {
display: flex;
flex-direction: column;
width: 100%;
height: 80vh;
margin: 0 auto;
border: 1px solid #e0e0e0;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.messages-container {
flex: 1;
overflow-y: auto;
padding: 20px;
background-color: #f9f9f9;
}
.message {
margin-bottom: 15px;
}
.message-content {
display: flex;
align-items: flex-start;
}
.avatar img {
width: 40px;
height: 40px;
border-radius: 50%;
margin-right: 10px;
}
.text-content {
max-width: 70%;
}
.sender {
font-size: 12px;
color: #666;
margin-bottom: 4px;
}
.text {
background-color: white;
padding: 10px 15px;
border-radius: 18px;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
word-wrap: break-word;
white-space: pre-wrap;
}
.time {
font-size: 11px;
color: #999;
margin-top: 4px;
text-align: right;
}
/* 发送的消息样式 */
.message.sent .message-content {
flex-direction: row-reverse;
}
.message.sent .avatar {
margin-right: 0;
margin-left: 10px;
}
.message.sent .text-content {
text-align: right;
}
.message.sent .text {
background-color: #4A90E2;
color: white;
border-radius: 18px 4px 18px 18px;
}
.message.sent .time {
text-align: right;
}
/* 接收的消息样式 */
.message.received .text {
background-color: #99de68;
border-radius: 4px 18px 18px 18px;
}
.input-container {
display: flex;
padding: 15px;
background-color: white;
border-top: 1px solid #e0e0e0;
}
textarea {
flex: 1;
padding: 10px;
border: 1px solid #e0e0e0;
border-radius: 20px;
resize: none;
height: 60px;
font-family: inherit;
font-size: 14px;
outline: none;
}
textarea:focus {
border-color: #4A90E2;
}
.send-button {
margin-left: 10px;
padding: 0 25px;
background-color: #4A90E2;
color: white;
border: none;
border-radius: 20px;
cursor: pointer;
font-size: 14px;
transition: background-color 0.3s;
}
.send-button:hover:not(:disabled) {
background-color: #357ABD;
}
.send-button:disabled {
background-color: #cccccc;
cursor: not-allowed;
}
/* 滚动条样式 */
.messages-container::-webkit-scrollbar {
width: 6px;
}
.messages-container::-webkit-scrollbar-track {
background: #f1f1f1;
}
.messages-container::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 3px;
}
.messages-container::-webkit-scrollbar-thumb:hover {
background: #a8a8a8;
}
.loading {
text-align: center;
color: #666;
margin-top: 10px;
}
/* 添加打字机光标样式 */
.typing-cursor {
display: inline-block;
width: 2px;
height: 1em;
background-color: #333;
margin-left: 2px;
animation: blink 1s infinite;
}
@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0; }
}
</style>