利用DeepSeek V3模型、siliconflow大模型一站式云服务平台以及vue3.0实现一个在线人工智能客服对话系统。
因为deepseek官网的api密钥使用起来比较缓慢,所以可以使用第三方的,具体操作请自行查阅资料。
siliconflow官网
完整运行代码
运行之前请先修改请求头(headers)中的密钥(token)以及对话模型(model)
请注意:这里的使用的是siliconflow中的token
<template>
<div class="chatbot-container">
<div
class="avatar"
@click="toggleChat"
:style="{
transform: isChatOpen
? 'scale(0.9) rotate(-8deg)'
: 'scale(1) rotate(0)',
}"
></div>
<div
class="chat-dialog"
:class="{ active: isChatOpen }"
v-click-outside="closeChat"
>
<div class="dialog-header">
<h3>在线客服</h3>
<div class="close-btn" @click="toggleChat">×</div>
</div>
<div class="dialog-body">
<div class="message-container" ref="scrollbarRef">
<div v-for="(message, index) in messagesWithTimestamps" :key="index">
<!-- 时间戳 -->
<div class="timestamp" v-if="message.showTime">
{{ formatSendTime(message.timestamp) }}
</div>
<!-- 消息列表 -->
<div
:class="[
'message-bubble',
message.type === 'user' ? 'user-message' : 'bot-message',
]"
>
{{ message.content }}
</div>
</div>
</div>
<div class="input-area">
<input
v-model="inputMessage"
type="text"
class="message-input"
placeholder="输入消息..."
@keypress.enter="sendMessage"
/>
<button class="send-button" @click="sendMessage">发送</button>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { nextTick, ref, computed } from "vue";
const isChatOpen = ref(false);
const inputMessage = ref("");
const messages = ref([]);
//创建新的对话数组,加上属性showTime
const messagesWithTimestamps = computed(() => {
return messages.value.map((item, index) => ({
...item,
showTime: index == 0 || shouldShowTime(index),
}));
});
//计算两次会话时间是否超过3分钟方法
const shouldShowTime = (index) => {
const current = new Date(messages.value[index - 1].timestamp);
const next = new Date(messages.value[index].timestamp);
const diff = next ? next - current : 0;
return diff > 3 * 60 * 1000; // 如果间隔超过3分钟返回true
};
// 切换聊天窗口
const toggleChat = () => {
isChatOpen.value = !isChatOpen.value;
if (isChatOpen.value && messages.value.length === 0) {
messages.value.push({
type: "bot",
content: "您好!我是智能客服小助手,很高兴为您服务。请问有什么可以帮您?",
timestamp: new Date(), //时间戳
});
}
};
// 关闭聊天窗口
const closeChat = () => {
isChatOpen.value = false;
};
// 发送消息
const sendMessage = () => {
const message = inputMessage.value.trim();
if (message) {
messages.value.push({
type: "user",
content: message,
timestamp: new Date(), //时间戳
});
inputMessage.value = "";
const messageContainer = document.querySelector(".message-container");
try {
setTimeout(async () => {
messages.value.push({
type: "bot",
content: "正在思考中...",
timestamp: new Date(), //时间戳
});
nextTick(() => {
// 滚动到底部
messageContainer.scrollTop = messageContainer.scrollHeight;
});
// 发送请求
const response = await fetch(
"https://api.siliconflow.cn/v1/chat/completions",
{
method: "POST",
headers: {
Authorization:
"Bearer 你的token",
"Content-Type": "application/json",
},
body: JSON.stringify({
frequency_penalty: 0,
max_tokens: 1024,
model: "你的ai模型",
temperature: 0,
top_k: 49,
top_p: 0.7,
messages: [
{
role: "user",
content: message,
},
],
}),
}
);
const data = await response.json();
// 过滤 "正在思考中..." 消息
messages.value = messages.value.filter((item) => {
return item.content.indexOf("正在思考中") === -1;
});
if (response.choices) {
// 处理响应数据
messages.value.push({
type: "bot",
content: data.choices[0].message.content,
timestamp: new Date(), //时间戳
});
} else {
// 处理错误情况
messages.value.push({
type: "bot",
content: "抱歉,我现在无法回答您的问题,请稍后再试。",
timestamp: new Date(), //时间戳
});
}
nextTick(() => {
// 滚动到底部
messageContainer.scrollTop = messageContainer.scrollHeight;
});
}, 1000);
// 滚动到底部
} catch (error) {
console.error("Error:", error);
}
}
};
const formatTime = (date) => {
// 格式化时间为“时:分”
const hours = date.getHours().toString().padStart(2, "0");
const minutes = date.getMinutes().toString().padStart(2, "0");
return hours + ":" + minutes;
};
const getWeekday = (date) => {
// 获取星期几的中文表示
const weekdays = [
"星期天",
"星期一",
"星期二",
"星期三",
"星期四",
"星期五",
"星期六",
];
return weekdays[date.getDay()];
};
const formatSendTime = (sendTime) => {
const now = new Date();
const sendDate = new Date(sendTime);
// 计算时间差(以毫秒为单位)
const timeDiff = now - sendDate;
const startOfToday = new Date(
now.getFullYear(),
now.getMonth(),
now.getDate()
);
const startOfTargetDate = new Date(
sendDate.getFullYear(),
sendDate.getMonth(),
sendDate.getDate()
);
// 一天内的毫秒数
const oneDay = 24 * 60 * 60 * 1000;
// 如果发送时间在当前时间之前
if (timeDiff < 0) {
return "Invalid time"; // 或者其他错误处理
}
// 发生在同一天
if (startOfToday.getTime() === startOfTargetDate.getTime()) {
return formatTime(sendDate);
}
// 如果发送时间在一天内
if (timeDiff < oneDay) {
return "昨天 " + formatTime(sendDate);
}
// 如果发送时间在二天至七天内
if (timeDiff < 7 * oneDay) {
const weekday = getWeekday(sendDate);
return weekday + " " + formatTime(sendDate);
}
// 如果发送时间超过七天
return (
sendDate.toLocaleDateString("zh-CN", {
year: "numeric",
month: "2-digit",
day: "2-digit",
}) +
" " +
formatTime(sendDate)
);
};
</script>
<style>
/* 原有样式保持不变 */
:root {
--primary-color: #4a90e2;
--hover-color: #357abd;
}
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background: #f0f2f5;
margin: 0;
font-family: "Microsoft YaHei", sans-serif;
}
.chatbot-container {
position: fixed;
bottom: 30px;
right: 30px;
z-index: 1000;
}
/* 新增头像样式 */
.avatar {
width: 60px;
height: 60px;
border-radius: 50%;
cursor: pointer;
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.2);
box-shadow: 0 8px 20px rgba(74, 144, 226, 0.3);
transition: all 0.3s ease;
position: relative;
overflow: hidden;
background-color: #357abd;
}
.avatar:hover {
transform: scale(1.1) rotate(8deg);
box-shadow: 0 12px 25px rgba(74, 144, 226, 0.4);
}
.avatar::before {
content: "";
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 30px;
height: 30px;
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white"><path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-3 12H7v-2h10v2zm0-3H7v-2h10v2zm0-3H7V6h10v2z"/></svg>')
no-repeat center;
}
/* 聊天对话框 */
.chat-dialog {
position: absolute;
bottom: 80px;
right: 0;
width: 320px;
background: white;
border-radius: 12px;
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.1);
transform: scale(0);
transform-origin: bottom right;
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.chat-dialog.active {
transform: scale(1);
}
.dialog-header {
padding: 16px;
background: var(--primary-color);
border-radius: 12px 12px 0 0;
color: white;
display: flex;
align-items: center;
}
.dialog-header h3 {
margin: 0;
font-size: 16px;
font-weight: 500;
}
.close-btn {
margin-left: auto;
cursor: pointer;
padding: 0px 4px;
border-radius: 50%;
background: rgba(0, 0, 0, 0.1);
}
.dialog-body {
padding: 16px;
height: 300px;
overflow-y: auto;
display: flex;
flex-direction: column;
}
.timestamp {
display: flex;
align-items: center;
justify-content: center;
font-size: 10px;
margin-bottom: 3px;
}
.message-container {
flex: 1;
overflow-y: auto;
margin-bottom: 16px;
display: flex;
flex-direction: column;
}
.input-area {
display: flex;
gap: 8px;
padding-top: 12px;
border-top: 1px solid #eee;
}
.message-input {
flex: 1;
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 20px;
outline: none;
transition: border-color 0.3s;
}
.message-input:focus {
border-color: var(--primary-color);
}
.send-button {
padding: 8px 16px;
background: var(--primary-color);
color: white;
border: none;
border-radius: 20px;
cursor: pointer;
font-size: 14px;
}
.send-button:hover {
background: var(--hover-color);
}
.message-bubble {
padding: 8px 12px;
margin-bottom: 8px;
border-radius: 4px;
animation: fadeIn 0.3s ease;
max-width: 80%;
display: inline-block;
word-break: break-all;
box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.12);
border: 1px solid #e4e7ed;
}
.user-message {
background: var(--primary-color);
color: #ffffff;
float: right;
}
.bot-message {
background: #ffffff;
color: #303133;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* 响应式设计 */
@media (max-width: 480px) {
.chatbot-container {
bottom: 20px;
right: 20px;
}
.chat-dialog {
width: 90vw;
bottom: 70px;
}
.avatar {
width: 50px;
height: 50px;
}
}
</style>
效果在线预览
ps:文本对话请求可能会有连接超时的问题!