1.第一步

申请deepseek API,详细教程参考下面视频:

DeepSeek API的申请方式_哔哩哔哩_bilibili

2.第二步

创建Vue3项目

【带小白做毕设】01. 前端Vue3 框架的快速搭建以及项目工程的讲解_哔哩哔哩_bilibili

3.第三步

创建deepseek.js封装deepseek请求接口

import axios from 'axios';

const DEEPSEEK_API_URL = 'https://api.deepseek.com'; // DeepSeek API端点
const API_KEY = '你的密钥'; //DeepSeek API密钥

const deepseekApi = axios.create({
    baseURL: DEEPSEEK_API_URL,
    headers: {
        'Authorization': `Bearer ${API_KEY}`,
        'Content-Type': 'application/json'
    }
});

export default {
    async chatCompletion(prompt, model = 'deepseek-chat', maxTokens = 2048) {
        try {
            const response = await deepseekApi.post('/chat/completions', {
                model,
                messages: [{ role: 'user', content: prompt }],
                max_tokens: maxTokens
            });
            return response.data;
        } catch (error) {
            console.error('调用 DeepSeek API 时出错', error);
            throw error;
        }
    }

};

4.第四步

编写前端,调用deepseek.js

<template>
  <div class="deepseek-container">
    <!-- 回答展示区域 -->
    <div class="response-area" v-if="response || isLoading">
      <div class="message-container">
        <!-- 头像区域 -->
        <div class="message-avatar">
          <img src="@/assets/imgs/chat.png" alt="DeepSeek" class="avatar-img" />
        </div>
        <!-- 消息内容区域 -->
        <div class="message-content">
          <!-- 加载状态显示动画 -->
          <div v-if="isLoading" class="loading-animation">
            <div class="loading-dot"></div>
            <div class="loading-dot"></div>
            <div class="loading-dot"></div>
            <span class="loading-text">加载时间较长,请耐心等待</span>
          </div>
          <!-- 正常状态下显示格式化后的响应内容 -->
          <div v-else class="markdown-content" v-html="formatResponse(response)"></div>
        </div>
      </div>
    </div>

    <!-- 输入区域 -->
    <div class="input-container">
      <div class="input-box">
        <!-- 文本输入框 -->
        <textarea
            v-model="userInput"
            placeholder="有什么我可以帮助你的吗?"
            @keydown.enter.exact.prevent="askDeepSeek"
            :disabled="isLoading"
        ></textarea>
        <!-- 发送按钮 -->
        <button
            @click="askDeepSeek"
            :disabled="isLoading || !userInput.trim()"
            class="send-button"
            :class="{ 'button-loading': isLoading }"
        >
        <span v-if="!isLoading">
            <!-- 发送图标 -->
            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
              <line x1="22" y1="2" x2="11" y2="13"></line>
              <polygon points="22 2 15 22 11 13 2 9 22 2"></polygon>
            </svg>
          </span>
          <!-- 加载时显示旋转动画 -->
          <span v-else class="loading-spinner"></span>
        </button>
      </div>
      <!-- 底部提示信息 -->
      <div class="footer-note">
        AI小助手 可能会产生不准确的信息,请谨慎验证
      </div>
    </div>
  </div>
</template>

<script>
import { ref } from 'vue';
import deepseekService from '@/api/deepseek'; // 引入DeepSeek API服务
import { marked } from 'marked'; // Markdown解析库

export default {
  setup() {
    // 定义响应式变量
    const userInput = ref(''); // 用户输入
    const response = ref(''); // API响应
    const isLoading = ref(false); // 加载状态

    /**
     * 格式化响应文本
     * @param {string} text - 原始文本
     * @returns {string} 格式化后的HTML
     */
    const formatResponse = (text) => {
      if (!text) return '';

      // 1. 先处理Markdown的特殊结构(标题、列表等)
      // 2. 去除多余空行,但保留列表项和段落间的必要间距
      const cleanedText = text
          .replace(/^\s+|\s+$/g, '') // 去除首尾空白
          .replace(/\n{1,}/g, '\n') // 3个以上换行替换为2个
          .replace(/(\n\s*){2,}(\*|\-|\d+\.)/g, '\n$1') // 列表项前的多余空行
          .replace(/(#+)\s+\n/g, '$0 ') // 标题后的空行
          .replace(/\n{1,}/g, '\n'); // 最终确保最多连续2个换行

      return marked.parse(cleanedText);
    };

    /**
     * 向DeepSeek API提问
     */
    const askDeepSeek = async () => {
      // 空输入或正在加载时不做处理
      if (!userInput.value.trim() || isLoading.value) return;

      isLoading.value = true; // 开始加载
      response.value = ''; // 清空之前的响应

      try {
        // 调用API获取响应
        const result = await deepseekService.chatCompletion(userInput.value);
        response.value = result.choices[0].message.content; // 获取响应内容
      } catch (error) {
        // 错误处理
        response.value = '**出错啦**\n\n抱歉,获取回答时出现问题,请稍后再试。';
        console.error('API调用错误:', error);
      } finally {
        isLoading.value = false; // 结束加载
      }
    };

    // 暴露给模板使用的变量和方法
    return {
      userInput,
      response,
      isLoading,
      askDeepSeek,
      formatResponse,
    };
  },
};
</script>

<style scoped>
/* 主容器样式 */
.deepseek-container {
  max-width: 800px;
  margin: 0 auto;
  padding: 20px;
  font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
  color: #333;
}

/* 响应区域样式 */
.response-area {
  margin-bottom: 20px;
  border-radius: 8px;
  background-color: #f9f9f9; /* 浅背景以提高可读性 */
  padding: 15px; /* 增加内边距 */
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); /* 加深阴影 */
}

/* 消息容器布局 */
.message-container {
  display: flex;
  padding: 0; /* 移除默认填充 */
  gap: 12px; /* 元素间距 */
}

/* 头像样式 */
.message-avatar {
  flex-shrink: 0; /* 防止头像被压缩 */
}

.avatar-img {
  width: 40px; /* 增大头像 */
  height: 40px;
  border-radius: 50%;
  object-fit: cover; /* 确保适配 */
}

/* 消息内容样式 */
.message-content {
  flex-grow: 1; /* 占据剩余空间 */
  line-height: 1.5; /* 提高可读性 */
  text-align: left;
}

/* Markdown内容样式 */
.markdown-content {
  white-space: normal; /* 不保留换行 */
  word-break: break-word; /* 长单词换行 */
  text-align: left;
  font-size: 14px; /* 调整字体大小 */
}

/* 段落样式 */
.markdown-content :deep(p) {
  margin: 0.2em 0;
  line-height: 1.5;
}

/* 列表样式 */
.markdown-content :deep(ul),
.markdown-content :deep(ol) {
  padding-left: 1.5em;
  margin: 0.5em 0;
}

/* 行内代码样式 */
.markdown-content :deep(code) {
  background-color: #f0f0f0; /* 代码背景 */
  padding: 2px 4px;
  border-radius: 4px;
  font-family: monospace; /* 等宽字体 */
}

/* 代码块样式 */
.markdown-content :deep(pre) {
  background-color: #f8f8f8; /* 代码块背景 */
  padding: 12px;
  border-radius: 6px;
  overflow-x: auto; /* 水平滚动 */
  margin: 0.75em 0; /* 代码块周围的边距 */
}

/* 引用块样式 */
.markdown-content :deep(blockquote) {
  border-left: 3px solid #ddd; /* 引用块 */
  padding-left: 12px;
  margin: 0.75em 0;
  color: #666; /* 引用文本颜色 */
}

/* 输入容器样式 */
.input-container {
  position: relative;
}

/* 输入框样式 */
.input-box {
  position: relative;
  border: 1px solid #ddd;
  border-radius: 8px;
  padding: 8px;
  background-color: white;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
}

/* 文本输入框样式 */
textarea {
  width: 100%;
  min-height: 80px;
  border: none;
  resize: none; /* 禁止调整大小 */
  outline: none; /* 去除轮廓 */
  padding: 8px 40px 8px -40px; /* 右侧留出按钮空间 */
  font-size: 16px;
  line-height: 1.5;
}

/* 发送按钮样式 */
.send-button {
  position: absolute;
  right: 12px;
  bottom: 12px;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background-color: #1a73e8;
  color: white;
  border: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.2s; /* 过渡效果 */
}

/* 禁用状态按钮样式 */
.send-button:disabled {
  background-color: #ccc;
  cursor: not-allowed;
}

/* 悬停状态按钮样式 */
.send-button:not(:disabled):hover {
  background-color: #0d5bba;
}

/* 加载状态按钮样式 */
.button-loading {
  background-color: #0d5bba;
}

/* 加载旋转动画 */
.loading-spinner {
  width: 20px;
  height: 20px;
  border: 2px solid rgba(255, 255, 255, 0.3);
  border-radius: 50%;
  border-top-color: white;
  animation: spin 1s ease-in-out infinite; /* 旋转动画 */
}

/* 加载点动画容器 */
.loading-animation {
  display: flex;
  gap: 6px; /* 加载点之间的间隔 */
  padding: 8px 0;
}

/* 单个加载点样式 */
.loading-dot {
  width: 8px;
  height: 8px;
  background-color: #aaa;
  border-radius: 50%;
  animation: bounce 1.4s infinite ease-in-out; /* 弹跳动画 */
}

/* 第二个加载点延迟 */
.loading-dot:nth-child(2) {
  animation-delay: 0.2s;
}
.loading-text {
  margin-left: 10px; /* 与加载动画保持间隔 */
  font-size: 14px; /* 文本大小 */
  color: #aaaaaa; /* 文本颜色,可根据需要调整 */
  align-self: center; /* 垂直居中 */
}
/* 第三个加载点延迟 */
.loading-dot:nth-child(3) {
  animation-delay: 0.4s;
}

/* 底部提示样式 */
.footer-note {
  margin-top: 8px;
  font-size: 12px;
  color: #b4d5ff;
  text-align: center;
}
.footer-note:hover {
  color:#0742b1;
}
/* 旋转动画定义 */
@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

/* 弹跳动画定义 */
@keyframes bounce {
  0%, 80%, 100% {
    transform: translateY(0);
  }
  40% {
    transform: translateY(-8px);
  }
}
</style>

效果展示

 

Logo

一站式 AI 云服务平台

更多推荐