前端接入DeepSeek

发布于:2025-07-30 ⋅ 阅读:(17) ⋅ 点赞:(0)

2025.07.29今天我学习了如何把DeepSeek接入到前端页面进行使用,效果如下:

一、去deepseek官网创建apiKey。

DeepSeek
 


 

(注意:需要充值才能使用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>


网站公告

今日签到

点亮在社区的每一天
去签到