前言
最近的DeepSeek可谓是非常火,到处都在讨论DeepSeek,而且各种应用都开始接入DeepSeek,那我今天我也来凑凑热闹,查看DeepSeek官网可以看到有开放平台,调用方式也比较简单,那么话不多说,我们就基于DeepSeek开发一款属于自己的对话式AI应用。
对于技术栈的选择,由于是一个对话式AI应用,而且功能也是比较简单的,所以暂时不考虑接入后端,就一个前端页面能对话就可以满足需求了。所以采用Vite+Vue3来做这么一个小应用。
环境准备
首先安装node。这个就不多说了,官网下载node然后安装即可。安装后输入 node -v来查看是否安装成功。
//查看node版本。
node -v

出现版本号就表示安装成功了,接着通过脚手架初始化一个vue3的项目。
先设置一下镜像源
npm config set registry https://registry.npmmirror.com
然后安装 yarn这个包管理工具。也可以用npm或者pnpm,方式不唯一,能创建好一个vue3的项目即可。
npm install -g yarn
创建项目
接着开始创建一个vue3的项目,demo1是项目名称,后面按照自己的项目命名。
yarn create @vitejs/app demo1

上下键可以用来选择项目,我这里选择Vue,然后按下回车。

接着选择语言,我这里选择JavaScript,按下回车。

这样就说明项目新建好了, 按照提示,进入到项目目录下,然后安装依赖包,接着运行。
cd demo1
yarn
yarn dev

能出现这个页面就说明项目新建好了。
接下来安装一下axios和vue-router,axios用来发送请求,vue-router用来控制页面跳转。
yarn add axios
yarn add vue-router
接下来对一些重点文件做一下说明,对前端开发人员来说都是常规操作。
在src目录下新建router文件夹,然后新建index.js文件,代码如下。
// src/router/index.js
import { createRouter, createWebHistory } from 'vue-router';
import Home from '../views/Home.vue';
import DeepSeekIndex from '../views/DeepSeek.vue'; // 确保路径正确
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/deepseek',
name: 'DeepSeekIndex',
component: DeepSeekIndex // 确保组件正确
}
];
const router = createRouter({
history: createWebHistory(import.meta.env.VUE_APP_BASE_URL), // 修改这里
routes
});
export default router;
在src下新建views文件夹,views文件夹下新建Home.vue和DeepSeek.vue两个文件,Home.vue内容如下。
<template>
<div class="home-container">
<h1>欢迎来到 LMDeepSeek</h1>
<p>这是一个与DeepSeek对话的应用程序。点击“开始对话”按钮,开始您的对话之旅。</p>
<button @click="startConversation">开始对话</button> <!-- 添加开始对话按钮 -->
</div>
</template>
<script>
export default {
name: 'Home',
methods: {
startConversation() {
this.$router.push('/deepseek'); // 跳转到/deepseek页面
}
}
}
</script>
<style scoped>
.home-container {
max-width: 600px;
margin: 0 auto;
padding: 20px;
text-align: center;
}
h1 {
font-size: 2em;
margin-bottom: 20px;
}
p {
font-size: 1em;
color: #555;
}
button {
padding: 10px 20px;
border: none;
background-color: #007bff;
color: white;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background-color: #0056b3;
}
</style>
DeepSeek.vue内容如下。
<template>
<div class="deepseek-container">
<h1>DeepSeek 对话页面</h1>
<div class="chat-box">
<!-- 这里可以添加对话框组件 -->
<p>欢迎使用 LMDeepSeek!请开始您的对话。</p>
</div>
<div class="input-area">
<input v-model="userInput" @keyup.enter="sendMessage" placeholder="输入您的消息..." />
<button @click="sendMessage">发送</button>
</div>
</div>
</template>
<script>
export default {
name: 'DeepSeekIndex',
data() {
return {
userInput: ''
};
},
methods: {
sendMessage() {
// 这里可以添加发送消息到DeepSeek的逻辑
console.log('用户输入:', this.userInput);
this.userInput = ''; // 清空输入框
}
}
}
</script>
<style scoped>
.deepseek-container {
max-width: 100%;
margin: 0 auto;
padding: 20px;
border: 1px solid #ccc;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
height: 80vh; /* 铺满整个窗口 */
display: flex;
flex-direction: column;
}
.chat-box {
flex: 1; /* 铺满剩余空间 */
overflow-y: scroll;
border-bottom: 1px solid #ccc;
padding: 10px;
margin-bottom: 10px;
}
.input-area {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px;
}
.input-area input {
flex: 1;
padding: 10px;
margin-right: 10px;
border: 1px solid #ccc;
border-radius: 4px;
}
.input-area button {
padding: 10px 20px;
border: none;
background-color: #007bff;
color: white;
border-radius: 4px;
cursor: pointer;
}
.input-area button:hover {
background-color: #0056b3;
}
</style>
好了,我们的工作已经完成了一大半了,接着运行起来看一下。

首先看到的是首页内容,接着我们点击开始对话。

会打开一个对话页面,不过目前还没有接入Deepseek。
接入DeepSeek
首先进入到DeepSeek的官网,注册一下,然后创建一个API Key,请注意,这个key只有在第一次创建的时候可以看到,之后就看不到了,注意保存。
然后需要实名认证和充值,没有充值的话,调用接口会提示402 (Payment Required)错误,这就是接口没有充钱,如何只是测试用,稍微充个几块钱就行。
然后参考官网的接口文档。

安装openai
yarn add openai
接下来就是DeepSeek.vue的代码。
<template>
<div class="deepseek-container">
<h1>DeepSeek 对话页面</h1>
<div class="chat-box">
<!-- 这里可以添加对话框组件 -->
<div v-if="messages.length === 0" class="welcome-message">
欢迎使用 LMDeepSeek!请开始您的对话。
</div>
<div
v-for="(message, index) in messages"
:key="index"
:class="['message', message.role]"
>
<img
:src="getAvatar(message.role)"
:alt="`${message.role} avatar`"
class="avatar"
/>
<div v-if="message.role === 'system' && message.content ==''" class="loading"></div>
<div v-else class="content">{{ message.content }}</div>
</div>
</div>
<div class="input-area">
<input
v-model="userInput"
@keyup.enter="sendMessage"
placeholder="输入您的消息..."
/>
<button @click="sendMessage">发送</button>
</div>
</div>
</template>
<script>
import OpenAI from "openai";
export default {
name: "DeepSeekIndex",
data() {
return {
userInput: "",
messages: [
// {
// role: "system",
// content: "",
// },
// {
// role: "user",
// content: "Hello! How can I assist you today?",
// },
],
};
},
methods: {
async sendMessage() {
this.messages.push(
{
role: "user",
content: this.userInput,
},
{
role: "system",
content: "",
}
);
const userInput = this.userInput;
this.userInput = "";
const openai = new OpenAI({
baseURL: "https://api.deepseek.com",
apiKey: "your key",
dangerouslyAllowBrowser: true,
});
const completion = await openai.chat.completions.create({
messages: [{ role: "user", content: userInput }],
model: "deepseek-chat",
stream: true,
});
let last = this.messages.length - 1;
let content = "";
for await (const chunk of completion) {
content += chunk.choices[0].delta?.content || "";
this.messages[last].content = content;
}
},
getAvatar(role) {
return role === "user"
? "/src/assets/user-avatar.png"
: "/src/assets/deepseek-avatar.png";
},
},
};
</script>
<style scoped>
.deepseek-container {
max-width: 100%;
margin: 0 auto;
padding: 20px;
border: 1px solid #ccc;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
height: 80vh; /* 铺满整个窗口 */
display: flex;
flex-direction: column;
}
.chat-box {
flex: 1; /* 铺满剩余空间 */
overflow-y: scroll;
border-bottom: 1px solid #ccc;
padding: 10px;
margin-bottom: 10px;
display: flex;
flex-direction: column;
}
.message {
display: flex;
align-items: flex-start;
margin-bottom: 10px;
}
.message.user {
justify-content: flex-end;
flex-direction: row-reverse; /* 用户消息反向排列 */
}
.message.system {
justify-content: flex-start;
}
.avatar {
width: 40px;
height: 40px;
border-radius: 50%;
margin-right: 10px;
}
.message.user .avatar {
margin-left: 10px;
margin-right: 0;
}
.content {
max-width: 70%;
padding: 10px;
border-radius: 8px;
background-color: #007bff;
color:#ffffff
}
.message.user .content {
background-color: #007bff;
color: white;
margin-right: 10px; /* 用户消息内容与边界的距离 */
}
.message.system .content {
margin-left: 10px; /* AI助手消息内容与边界的距离 */
text-align: left;
}
/* 确保用户消息整体靠右对齐 */
.message.user {
align-self: flex-end;
}
/* 确保系统消息整体靠左对齐 */
.message.system {
align-self: flex-start;
}
.input-area {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px;
}
.input-area input {
flex: 1;
padding: 10px;
margin-right: 10px;
border: 1px solid #ccc;
border-radius: 4px;
}
.input-area button {
padding: 10px 20px;
border: none;
background-color: #007bff;
color: white;
border-radius: 4px;
cursor: pointer;
}
.input-area button:hover {
background-color: #0056b3;
}
.welcome-message {
text-align: center;
color: #aaa;
}
.loading {
display: inline-block;
font-size: 20px; /* 根据需要调整大小 */
position: relative;
}
.loading::after {
content: '...';
position: absolute;
right: -1em;
top: 0;
animation: loadingDots 1.5s steps(5, end) infinite;
}
@keyframes loadingDots {
0%, 20% {
content: '...';
}
40% {
content: '..';
}
60% {
content: '.';
}
80%, 100% {
content: '';
}
}
</style>
官网默认的示例没有启用流式输出,所以等待时间会比较久,采用流式输出,可以减少等待时间,并且消息是一点点加载出来的,接下来看看效果。

想要获取全部源码的,请关注下方的微信公众号,然后回复ds,就可以获取全部源码啦。
Study hard and make progress every day.
1403

被折叠的 条评论
为什么被折叠?



