龙江网站建设/品牌宣传策划方案

龙江网站建设,品牌宣传策划方案,企查查官网入口网页版,互联网做视频网站需要许可证吗DeepSeek AI 聊天助手集成指南 先看完整效果: PixPin_2025-02-19_09-15-59 效果图: 目录 项目概述功能特点环境准备项目结构组件详解 ChatContainerChatInputMessageBubbleTypeWriter 核心代码示例使用指南常见问题 项目概述 基于 Vue 3 TypeScrip…

DeepSeek AI 聊天助手集成指南

先看完整效果:

PixPin_2025-02-19_09-15-59

效果图:
请添加图片描述

请添加图片描述

请添加图片描述

目录

  1. 项目概述
  2. 功能特点
  3. 环境准备
  4. 项目结构
  5. 组件详解
    • ChatContainer
    • ChatInput
    • MessageBubble
    • TypeWriter
  6. 核心代码示例
  7. 使用指南
  8. 常见问题

项目概述

基于 Vue 3 + TypeScript + Element Plus 开发的 AI 聊天助手,集成了 DeepSeek 的 API 服务。提供打字机模式和流式输出两种对话模式,支持多种 AI 模型切换。

功能特点

基础功能交互体验UI/UX其他特性
🚀 Vue 3 Composition API✨ 打字机效果🎨 Element Plus UI📱 响应式设计
💪 TypeScript 支持🌊 流式实时输出🖼️ 自定义用户头像🔄 模型切换
🔌 DeepSeek API 集成⌚ 消息时间显示🎭 思考状态动画🗑️ 对话清空

环境准备

1. 安装依赖

# 创建项目
npm create vue@latest# 安装依赖
npm install element-plus @element-plus/icons-vue axios

2. 环境配置

创建 .env.local 文件:

VITE_DEEPSEEK_API_KEY=your_api_key_here

项目结构

src/
├── components/chat/          # 聊天相关组件
│   ├── ChatContainer.vue    # 聊天容器组件
│   ├── ChatInput.vue        # 输入组件
│   ├── MessageBubble.vue    # 消息气泡组件
│   └── TypeWriter.vue       # 打字机效果组件
├── services/
│   └── aiService.ts         # API 服务封装
├── views/
│   ├── ChatView.vue        # 打字机模式页面
│   └── StreamView.vue      # 流式输出页面
└── App.vue                 # 根组件

组件详解

1. ChatContainer.vue

聊天界面的核心容器组件。

功能特性
  • 📝 消息列表管理
  • 🔄 自动滚动控制
  • ⌨️ 打字机效果管理
  • 🔀 模型切换
  • 🗑️ 清空对话
  • 💫 思考状态动画
  • 📱 响应式适配
组件 API
// Props
interface Props {title?: string                 // 聊天标题messages: Message[]            // 消息列表loading?: boolean              // 加载状态streamMode?: boolean          // 流式模式
}// Events
interface Events {send: (message: string) => voidclear: () => voidmodelChange: (model: ModelType) => void
}

完整代码如下:

<template><!-- 聊天容器主组件 --><div class="chat-container"><!-- 聊天头部:标题和清空按钮 --><div class="chat-header"><div class="header-left"><h3>{{ title }}</h3><el-select v-model="currentModel" size="small" class="model-select":disabled="loading"><el-optionv-for="(label, model) in modelOptions":key="model":label="label":value="model"/></el-select></div><el-button type="danger" plainclass="clear-button"@click="showClearConfirm"><template #icon><el-icon><Delete /></el-icon></template>清空全部对话</el-button></div><!-- 消息列表区域:包含所有对话内容 --><div class="chat-messages" ref="messagesContainer"><!-- 循环渲染消息气泡 --><MessageBubblev-for="(message, index) in messages":key="index":content="message.content":is-user="message.role === 'user'":use-typewriter="!initialLoad && !streamMode && message.role === 'assistant'"@complete="handleMessageComplete(index)"/><!-- AI思考中状态显示 --><div v-if="loading && (!messages.length || messages[messages.length - 1].role === 'user')" class="message message-ai thinking-message"><div class="message-content"><div class="avatar-wrapper"><el-avatar :size="40" class="ai-avatar"><el-icon><Service /></el-icon></el-avatar></div><div class="bubble-wrapper"><div class="bubble thinking-bubble"><div class="dots-container"><span class="dot"></span><span class="dot"></span><span class="dot"></span></div></div></div></div></div></div><!-- 输入区域组件 --><ChatInput :disabled="loading || isTyping"@send="$emit('send', $event)"/><!-- 确认弹窗 --><el-dialogv-model="showConfirmDialog"title="确认清空"width="400px":show-close="false"class="clear-dialog"><div class="dialog-content"><el-icon class="warning-icon" color="#E6A23C"><Warning /></el-icon><p>确定要清空所有对话记录吗?</p><p class="warning-text">此操作不可恢复</p></div><template #footer><div class="dialog-footer"><el-button @click="showConfirmDialog = false">取消</el-button><el-button type="danger" @click="handleClear">确认清空</el-button></div></template></el-dialog></div>
</template><script setup lang="ts">
import { ref, onMounted, nextTick, watch } from 'vue'
import { Service, Loading, Delete, Warning } from '@element-plus/icons-vue'
import { ModelType } from '@/services/aiService'// 消息类型定义
interface Message {role: 'user' | 'assistant' | 'system'content: string
}// 组件属性定义
const props = defineProps<{title?: string          // 聊天标题messages: Message[]     // 消息列表loading?: boolean       // 加载状态streamMode?: boolean     // 新增流式模式属性
}>()// 定义组件事件
const emit = defineEmits<{send: [message: string]clear: []modelChange: [model: ModelType]
}>()// 组件状态
const messagesContainer = ref<HTMLElement | null>(null)
const isTyping = ref(false)
const initialLoad = ref(true) // 添加初始加载标记// 确认弹窗状态
const showConfirmDialog = ref(false)// 模型选项
const modelOptions = {[ModelType.Chat]: 'DeepSeek-V3 (通用对话)',[ModelType.Reasoner]: 'DeepSeek-R1 (推理增强)'
}// 当前选择的模型
const currentModel = ref<ModelType>(ModelType.Chat)// 处理消息打字完成事件
const handleMessageComplete = (index: number) => {if (index === props.messages.length - 1) {isTyping.value = false}
}// 监听新消息,控制打字机效果
watch(() => props.messages, (newMessages, oldMessages) => {// 跳过初始加载的消息if (initialLoad.value) {initialLoad.value = falsereturn}if (newMessages.length > oldMessages?.length) {const lastMessage = newMessages[newMessages.length - 1]// 只在非流式模式下启用打字机效果if (lastMessage.role === 'assistant' && !props.streamMode) {isTyping.value = true}}
}, { deep: true })// 监听模型变化
watch(currentModel, (newModel) => {emit('modelChange', newModel)
})// 滚动到底部方法
const scrollToBottom = async () => {await nextTick()if (messagesContainer.value) {messagesContainer.value.scrollTop = messagesContainer.value.scrollHeight}
}// 监听消息变化,自动滚动
watch(() => props.messages, scrollToBottom, { deep: true })// 组件挂载时滚动到底部
onMounted(() => {scrollToBottom()// 初始加载完成后重置标记nextTick(() => {initialLoad.value = false})
})// 显示确认弹窗
const showClearConfirm = () => {showConfirmDialog.value = true
}// 处理清空操作
const handleClear = () => {showConfirmDialog.value = falseemit('clear')
}
</script><style scoped>
/* 容器基础样式 */
.chat-container {display: flex;flex-direction: column;height: 100%;width: 100%;max-width: 1200px;margin: 0 auto;background: #fff;border-radius: 16px;box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);overflow: hidden;
}/* 头部样式优化 */
.chat-header {display: flex;justify-content: space-between;align-items: center;box-sizing: border-box;padding: 16px 24px;  /* 减小内边距 */background: #fff;border-bottom: 1px solid #eee;box-shadow: 0 1px 6px rgba(0, 0, 0, 0.02);  /* 减小阴影 */z-index: 1;
}.chat-header h3 {font-size: 18px;  /* 减小标题字号 */font-weight: 600;color: #303133;margin: 0;
}/* 在线状态指示器调整 */
.chat-header h3::before {width: 6px;  /* 减小指示点大小 */height: 6px;margin-right: 8px;
}/* 消息列表区域样式 */
.chat-messages {flex: 1;overflow-y: auto;padding: 30px;display: flex;flex-direction: column;gap: 24px;background: #f9fafb;background-image: radial-gradient(circle at 25px 25px, rgba(0, 0, 0, 0.02) 2%, transparent 0%),radial-gradient(circle at 75px 75px, rgba(0, 0, 0, 0.02) 2%, transparent 0%);background-size: 100px 100px;scroll-behavior: smooth;scrollbar-width: thin;scrollbar-color: transparent transparent;
}/* 加载动画 */
.loading {animation: rotating 2s linear infinite;margin-right: 8px;
}@keyframes rotating {from { transform: rotate(0deg); }to { transform: rotate(360deg); }
}/* 自定义滚动条样式 */
.chat-messages::-webkit-scrollbar {width: 4px;
}.chat-messages::-webkit-scrollbar-track {background: transparent;
}.chat-messages::-webkit-scrollbar-thumb {background-color: rgba(0, 0, 0, 0.1);border-radius: 4px;transition: all 0.3s ease;
}/* 只在悬停时显示滚动条 */
.chat-messages:hover::-webkit-scrollbar-thumb {background-color: rgba(0, 0, 0, 0.2);
}/* 响应式布局 */
@media (max-width: 1400px) {.chat-container {max-width: 1000px;}
}@media (max-width: 1200px) {.chat-container {height: 100%;margin: 0;border-radius: 0;}.chat-view {padding: 0;}
}/* 思考中状态样式 */
.thinking-message {opacity: 0.8;animation: fadeInUp 0.3s ease-out;
}/* 思考中的气泡样式 */
.thinking-bubble {min-width: 60px;padding: 12px 16px !important;background: rgba(255, 255, 255, 0.9) !important;backdrop-filter: blur(8px);
}/* 跳动点动画 */
.dots-container {display: flex;align-items: center;gap: 6px;height: 20px;padding: 0 4px;
}.dot {width: 8px;height: 8px;background: #67c23a;border-radius: 50%;display: inline-block;opacity: 0.8;animation: bounce 1.4s infinite ease-in-out both;
}.dot:nth-child(1) {animation-delay: -0.32s;
}.dot:nth-child(2) {animation-delay: -0.16s;
}@keyframes bounce {0%, 80%, 100% { transform: scale(0);} 40% { transform: scale(1);}
}@keyframes fadeInUp {from {opacity: 0;transform: translateY(10px);}to {opacity: 0.8;transform: translateY(0);}
}/* 消息位置调整 */
.message-ai.thinking-message {margin: 0;padding-top: 12px;
}.message-ai.thinking-message .message-content {align-items: center;
}.ai-avatar {background: #67c23a;box-shadow: 0 2px 8px rgba(103, 194, 58, 0.2);
}.avatar-wrapper {width: 40px;height: 40px;flex-shrink: 0;
}.bubble-wrapper {display: flex;flex-direction: column;gap: 4px;
}.message {transition: transform 0.3s ease-out;
}.thinking-message {position: sticky;bottom: 30px;margin-top: 20px;z-index: 1;
}/* 清空按钮样式调整 */
.clear-button {padding: 8px 16px;  /* 减小按钮内边距 */font-size: 13px;    /* 减小字号 */height: 32px;       /* 固定高度 */
}.clear-button :deep(.el-icon) {font-size: 14px;    /* 减小图标大小 */margin-right: 4px;vertical-align: -1px;
}/* 头部布局间距调整 */
.header-left {gap: 12px;  /* 减小间距 */
}/* 优化动画效果 */
@keyframes pulse {0% {transform: scale(0.95);box-shadow: 0 0 0 0 rgba(103, 194, 58, 0.3);}70% {transform: scale(1);box-shadow: 0 0 0 4px rgba(103, 194, 58, 0);}100% {transform: scale(0.95);box-shadow: 0 0 0 0 rgba(103, 194, 58, 0);}
}/* 弹窗样式 */
.clear-dialog :deep(.el-dialog__header) {padding: 20px 24px;margin: 0;border-bottom: 1px solid #eee;
}.clear-dialog :deep(.el-dialog__title) {font-size: 18px;font-weight: 600;
}.clear-dialog :deep(.el-dialog__body) {padding: 30px 24px;
}.clear-dialog :deep(.el-dialog__footer) {padding: 16px 24px;border-top: 1px solid #eee;
}.dialog-content {display: flex;flex-direction: column;align-items: center;text-align: center;
}.warning-icon {font-size: 48px;margin-bottom: 16px;
}.dialog-content p {margin: 0;font-size: 16px;color: #303133;
}.warning-text {margin-top: 8px !important;font-size: 14px !important;color: #909399 !important;
}.dialog-footer {display: flex;justify-content: flex-end;gap: 12px;
}/* 弹窗动画 */
.clear-dialog :deep(.el-overlay) {backdrop-filter: blur(4px);
}.clear-dialog :deep(.el-dialog) {border-radius: 12px;overflow: hidden;box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
}/* 按钮样式优化 */
.dialog-footer :deep(.el-button) {padding: 10px 20px;border-radius: 6px;font-weight: 500;
}.dialog-footer :deep(.el-button--danger) {box-shadow: 0 2px 8px rgba(245, 108, 108, 0.2);
}.dialog-footer :deep(.el-button--danger:hover) {transform: translateY(-1px);box-shadow: 0 4px 12px rgba(245, 108, 108, 0.3);
}/* 模型选择器样式优化 */
.model-select {width: 200px;
}:deep(.el-select .el-input__wrapper) {padding: 0 12px;  /* 减小内边距 */height: 32px;     /* 减小高度 */
}:deep(.el-select .el-input__inner) {font-size: 13px;  /* 减小字号 */
}
</style> 

2. ChatInput.vue

输入组件特点:

  • 自适应文本框高度
  • 字数限制和显示
  • Enter 快捷发送
  • 优雅的加载状态
  • 内联发送按钮
  • 防重复提交
template 部分
  • .chat-input: 组件容器
  • .input-wrapper: 输入框和按钮的包装容器
  • el-input: 文本输入框,支持 textarea 自动伸缩
  • el-button: 发送按钮,带 Position 图标
script setup 部分
组件属性 props
  • disabled:是否禁用输入框(可选)
组件事件 emit
  • send(message: string):发送消息事件
组件状态 ref
  • message:输入的消息内容
关键方法
  • handleSend():发送消息,去除前后空格,防止空消息发送
组件使用
<ChatInput :disabled="isLoading" @send="handleSendMessage" />

完整代码如下:

<template><!-- 聊天输入组件 --><div class="chat-input"><div class="input-wrapper"><el-inputv-model="message"type="textarea":maxlength="2000":autosize="{ minRows: 1, maxRows: 4 }"show-word-limitresize="none"placeholder="输入您的问题..."@keyup.enter.exact="handleSend":disabled="disabled"class="custom-input"/><el-button type="primary" :loading="disabled"@click="handleSend":disabled="!message.trim()"class="send-button"><template #icon><el-icon><Position /></el-icon></template>发送</el-button></div></div>
</template><script setup lang="ts">
import { ref } from 'vue'
import { Position } from '@element-plus/icons-vue'// 组件属性定义
const props = defineProps<{disabled?: boolean    // 禁用状态
}>()// 定义事件
const emit = defineEmits<{send: [message: string]  // 发送消息事件
}>()// 输入内容
const message = ref('')// 发送消息处理
const handleSend = () => {const trimmedMessage = message.value.trim()if (!trimmedMessage || props.disabled) returnemit('send', trimmedMessage)message.value = ''
}
</script><style scoped>
/* 输入区域容器样式 */
.chat-input {padding: 16px 24px;border-top: 1px solid #eee;background: #fff;border-radius: 0 0 16px 16px;position: relative;
}/* 输入框包装器 */
.input-wrapper {position: relative;display: flex;gap: 12px;align-items: flex-start;
}/* 输入框样式优化 */
.custom-input {flex: 1;transition: all 0.3s ease;
}.custom-input :deep(.el-textarea__inner) {padding: 12px 16px;padding-right: 120px; /* 为字数限制留出空间 */font-size: 14px;border-radius: 12px;border: 1px solid #e4e7ed;background: #f9fafb;box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.02);transition: all 0.3s ease;line-height: 1.6;min-height: 48px;resize: none;
}.custom-input :deep(.el-textarea__inner:hover) {background: #fff;border-color: #c0c4cc;
}.custom-input :deep(.el-textarea__inner:focus) {background: #fff;border-color: #409eff;box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.1);
}/* 字数限制样式 */
.custom-input :deep(.el-input__count) {position: absolute;right: 12px;bottom: 8px;background: transparent;font-size: 12px;color: #909399;padding: 0;height: auto;line-height: 1;margin: 0;
}/* 发送按钮样式 */
.send-button {padding: 0 24px;font-size: 14px;border-radius: 10px;height: 48px;transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);font-weight: 500;white-space: nowrap;flex-shrink: 0;
}.send-button:not(:disabled) {background: linear-gradient(135deg, #409eff, #3a8ee6);border: none;
}.send-button:not(:disabled):hover {transform: translateY(-1px);box-shadow: 0 4px 12px rgba(64, 158, 255, 0.3);
}.send-button:not(:disabled):active {transform: translateY(0);
}.send-button :deep(.el-icon) {font-size: 16px;margin-right: 4px;vertical-align: -2px;
}/* 禁用状态样式 */
.custom-input :deep(.el-textarea__inner:disabled) {background: #f5f7fa;border-color: #e4e7ed;cursor: not-allowed;opacity: 0.7;
}/* 响应式调整 */
@media (max-width: 768px) {.chat-input {padding: 12px 16px;}.input-wrapper {gap: 8px;}.send-button {padding: 0 16px;height: 48px;}.custom-input :deep(.el-textarea__inner) {padding-right: 90px;}
}/* 超小屏幕隐藏发送按钮文字 */
@media (max-width: 480px) {.send-button {padding: 0;width: 48px;}.send-button :deep(.el-icon) {margin: 0;}.send-button span:not(.el-icon) {display: none;}
}
</style> 

3. MessageBubble.vue

消息气泡组件特点:

  • 显示消息气泡
  • 区分用户消息和 AI 消息
  • 支持头像展示
  • 支持打字机效果
  • 自动格式化时间
template 部分
  • .message: 主要消息容器,包含用户或 AI 消息

  • .avatar-wrapper: 头像区域

  • .bubble-wrapper
    消息内容区域
    • .bubble: 消息文本,支持打字机效果
    • .time: 消息时间
script setup 部分
组件属性 props
  • content:消息内容
  • isUser:是否为用户消息
  • useTypewriter(可选):是否启用打字机效果
组件事件 emit
  • complete:打字机动画完成事件
关键方法
  • formatTime():格式化消息时间(HH:mm 格式)
组件使用
<MessageBubble :content="'你好!这是 AI 回复的消息。'" :isUser="false" :useTypewriter="true" @complete="handleTypingComplete"
/>

完整代码如下:

<template><!-- 消息气泡组件 --><div :class="['message', isUser ? 'message-user' : 'message-ai']"><div class="message-content"><!-- 头像区域 --><div class="avatar-wrapper"><el-avatar :size="40" :class="isUser ? 'user-avatar' : 'ai-avatar'":src="isUser ? userAvatar : undefined"><el-icon v-if="isUser && !userAvatar"><User /></el-icon><el-icon v-if="!isUser"><Service /></el-icon></el-avatar></div><!-- 消息内容区域 --><div class="bubble-wrapper"><div class="bubble"><TypeWriterv-if="!isUser && useTypewriter":text="content":speed="30"@complete="$emit('complete')"/><span v-else>{{ content }}</span></div><!-- 消息时间 --><div class="time">{{ formatTime() }}</div></div></div></div>
</template><script setup lang="ts">
import { User, Service } from '@element-plus/icons-vue'
import userAvatar from '@/assets/user.jpg'// 组件属性定义
defineProps<{content: string      // 消息内容isUser: boolean      // 是否为用户消息useTypewriter?: boolean  // 是否使用打字机效果
}>()// 定义事件
defineEmits<{complete: []      // 打字完成事件
}>()// 格式化时间方法
const formatTime = () => {const now = new Date()return `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}`
}
</script><style scoped>
/* 消息容器基础样式 */
.message {display: flex;margin-bottom: 20px;animation: fadeIn 0.3s ease-in-out;
}/* 头像样式 */
.message-content {display: flex;align-items: flex-start;gap: 12px;max-width: 70%;
}/* 用户头像样式 */
.avatar-wrapper {width: 40px;height: 40px;flex-shrink: 0;
}.user-avatar {box-shadow: 0 2px 8px rgba(64, 158, 255, 0.2);
}.user-avatar :deep(img) {object-fit: cover;width: 100%;height: 100%;
}.ai-avatar {background: #67c23a;box-shadow: 0 2px 8px rgba(103, 194, 58, 0.2);
}/* 气泡容器样式 */
.bubble-wrapper {display: flex;flex-direction: column;gap: 4px;
}.message-user {justify-content: flex-end;
}.message-user .message-content {flex-direction: row-reverse;
}/* 消息气泡样式 */
.bubble {padding: 16px 20px;border-radius: 12px;background: #fff;line-height: 1.6;font-size: 15px;box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);position: relative;transition: all 0.3s ease;
}.bubble:hover {box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
}.message-user .bubble {background: #409eff;color: white;
}/* 时间显示样式 */
.time {font-size: 12px;color: #909399;margin: 0 8px;opacity: 0.8;
}.message-user .time {text-align: right;
}/* 动画效果 */
@keyframes fadeIn {from {opacity: 0;transform: translateY(10px);}to {opacity: 1;transform: translateY(0);}
}
</style> 

4. TypeWriter.vue

组件描述

TypeWriter 组件用于实现文本逐字出现的打字机效果,适用于 AI 回复等场景。支持自定义打字速度、延迟时间,并提供事件监听打字过程。

打字机效果组件特点:
  • 逐字显示文本
  • 自定义打字速度
  • 支持延迟启动
  • 文本变化时重新播放
  • 提供文本更新和完成事件

组件结构

template 部分
  • <slot>: 组件支持插槽,默认显示 displayText
script setup 部分
组件属性 props
  • text:要显示的文本
  • speed(可选):打字速度(默认 30ms
  • delay(可选):延迟启动时间(默认 0ms
组件事件 emit
  • complete:打字完成事件
  • textUpdate:每次文本更新时触发
组件状态
  • displayText:当前已显示的文本
  • currentIndex:当前打字位置
  • timer:定时器引用
关键方法
  • startTyping():递归执行逐字显示文本
  • 监听 props.text 变化,重新播放打字动画
  • onUnmounted() 清理定时器
组件使用
<TypeWriter :text="'你好,这是打字机效果演示。'" :speed="50" :delay="500"@complete="handleComplete"@textUpdate="handleTextUpdate"
><template #default="{ text }"><span class="custom-style">{{ text }}</span></template>
</TypeWriter>

完整代码如下:

<template><!-- 打字机效果组件 --><div><slot :text="displayText">{{ displayText }}</slot></div>
</template><script setup lang="ts">
import { ref, watch, onUnmounted } from 'vue'// 组件属性定义
const props = defineProps<{text: string      // 要显示的文本speed?: number    // 打字速度delay?: number    // 开始延迟
}>()// 定义事件
const emit = defineEmits<{complete: []                  // 打字完成事件textUpdate: [text: string]    // 文本更新事件
}>()// 组件状态
const displayText = ref('')
let currentIndex = 0
let timer: number | null = null// 打字效果实现
const startTyping = () => {if (currentIndex < props.text.length) {displayText.value = props.text.slice(0, currentIndex + 1)emit('textUpdate', displayText.value)currentIndex++timer = window.setTimeout(startTyping, props.speed || 30)} else {emit('complete')}
}// 监听文本变化
watch(() => props.text, () => {if (timer) {clearTimeout(timer)}currentIndex = 0displayText.value = ''timer = window.setTimeout(startTyping, props.delay || 0)
}, { immediate: true })// 组件卸载时清理定时器
onUnmounted(() => {if (timer) {clearTimeout(timer)}
})
</script><style scoped>
/* 打字机容器样式 */
.typewriter {display: inline-block;
}
</style> 

API 集成

aiService.ts

封装 DeepSeek API 的 AI 聊天服务,支持普通聊天、推理模式、流式响应等功能。提供灵活的模型切换和参数配置:

class AIChatService {// 普通对话请求async chat(messages: ChatCompletionRequestMessage[]) {// ... API 调用实现}// 流式对话请求async streamChat(messages: ChatCompletionRequestMessage[],onChunk: (chunk: string) => void) {// ... 流式 API 调用实现}// 模型配置更新updateConfig(newConfig: Partial<ChatRequestConfig>) {// ... 配置更新逻辑}
}
功能特点
  1. 支持普通聊天(同步请求)
  2. 支持推理模型(Reasoner)
  3. 支持流式响应
  4. 支持动态更新 API 配置
  5. 提供错误处理
枚举 ModelType
枚举项说明
Chat普通聊天模型(deepseek-chat
Reasoner推理模型(deepseek-reasoner
类方法
方法说明
chat(messages: ChatCompletionRequestMessage[])发送聊天请求(同步)
reason(prompt: string)使用 Reasoner 模型推理
updateConfig(newConfig: Partial<ChatRequestConfig>)更新配置
streamChat(messages: ChatCompletionRequestMessage[], onChunk: (chunk: string) => void)流式聊天
streamReason(prompt: string, onChunk: (chunk: string) => void)流式推理
使用示例
普通聊天
const response = await aiService.chat([{ role: 'user', content: '你好,AI!' }])
console.log(response)  // 输出 AI 回复
流式聊天
await aiService.streamChat([{ role: 'user', content: '请介绍一下 Vue 3' }],(chunk) => {console.log('AI 回复片段:', chunk)}
)
推理模式
const result = await aiService.reason('如何优化前端性能?')
console.log(result)
更新配置
aiService.updateConfig({ temperature: 0.9, max_tokens: 1500 })

使用指南

打字机模式

<ChatContainertitle="DeepSeek 打字机模式":messages="messages":loading="loading"@send="handleSend"
/>

流式输出模式

<ChatContainertitle="DeepSeek 流式输出":messages="messages":loading="loading":stream-mode="true"@send="handleStreamSend"
/>

常见问题

API 相关

  • ✅ 检查 API Key 配置
  • 🌐 确认网络连接
  • 🔍 查看控制台错误
  • ⚙️ 验证请求参数

界面显示

  • 📱 检查响应式布局
  • 📏 确认容器高度
  • 📜 验证滚动配置
  • 🎨 检查样式冲突

性能优化

  • 🔍 合理使用 v-show/v-if
  • 🔄 避免深度监听
  • 📊 优化滚动事件
  • 💾 虚拟滚动处理

核心代码示例

1. 打字机效果 (TypeWriter.vue)

<template><span ref="textContainer"></span>
</template><script setup lang="ts">
import { ref, onMounted, onUnmounted } from "vue";const props = defineProps<{text: string;speed?: number;
}>();const emit = defineEmits<{complete: [];
}>();const textContainer = ref<HTMLElement | null>(null);
let currentIndex = 0;
let timer: number | null = null;const startTyping = () => {if (currentIndex < props.text.length) {if (textContainer.value) {textContainer.value.textContent = props.text.slice(0, currentIndex + 1);}currentIndex++;timer = window.setTimeout(startTyping, props.speed || 30);} else {emit("complete");}
};onMounted(() => {startTyping();
});onUnmounted(() => {if (timer) clearTimeout(timer);
});
</script>

2. 流式输出 (StreamView.vue)

<script setup lang="ts">
const handleSend = async (message: string) => {messages.value.push({role: "user",content: message,});loading.value = true;try {const assistantMessage = {role: "assistant" as const,content: "",};messages.value.push(assistantMessage);// 流式回调处理const streamCallback = (chunk: string) => {assistantMessage.content += chunk;};await aiService.streamChat(messages.value.slice(0, -1), streamCallback);} catch (error) {ElMessage.error("发送消息失败,请重试");messages.value.pop();} finally {loading.value = false;}
};
</script>

3. API 服务 (aiService.ts)

class AIChatService {// 配置定义private config: ChatRequestConfig = {model: "deepseek-chat",temperature: 0.7,max_tokens: 2000,stream: false,system_message: "你是一个友好的中文助手。",};// 普通对话请求async chat(messages: ChatCompletionRequestMessage[]) {try {const response = await axios.post(`${API_CONFIG.baseURL}/v1/chat/completions`,{model: this.config.model,messages: [{ role: "system", content: this.config.system_message },...messages,],temperature: this.config.temperature,max_tokens: this.config.max_tokens,},{headers: {Authorization: `Bearer ${API_CONFIG.apiKey}`,},});return response.data.choices[0].message.content;} catch (error) {throw new Error("聊天服务出错了");}}// 流式对话请求async streamChat(messages: ChatCompletionRequestMessage[],onChunk: (chunk: string) => void) {try {const response = await fetch(`${API_CONFIG.baseURL}/v1/chat/completions`,{method: "POST",headers: {"Content-Type": "application/json",Authorization: `Bearer ${API_CONFIG.apiKey}`,},body: JSON.stringify({model: this.config.model,messages: [{ role: "system", content: this.config.system_message },...messages,],stream: true,}),});const reader = response.body?.getReader();const decoder = new TextDecoder();while (reader) {const { done, value } = await reader.read();if (done) break;const chunk = decoder.decode(value);const lines = chunk.split("\n").filter((line) => line.trim());for (const line of lines) {if (line.startsWith("data: ")) {const data = JSON.parse(line.slice(6));const content = data.choices[0].delta.content;if (content) onChunk(content);}}}} catch (error) {throw new Error("流式聊天服务出错了");}}
}

4. 思考动画 (ChatContainer.vue)

<template><div v-if="loading" class="thinking-message"><div class="message-content"><el-avatar class="ai-avatar"><el-icon><Service /></el-icon></el-avatar><div class="bubble thinking-bubble"><div class="dots-container"><span class="dot"></span><span class="dot"></span><span class="dot"></span></div></div></div></div>
</template><style scoped>
.thinking-message {opacity: 0.8;animation: fadeInUp 0.3s ease-out;
}.thinking-bubble {min-width: 60px;padding: 12px 16px;background: rgba(255, 255, 255, 0.9);backdrop-filter: blur(8px);
}.dots-container {display: flex;align-items: center;gap: 6px;height: 20px;
}.dot {width: 8px;height: 8px;background: #67c23a;border-radius: 50%;opacity: 0.8;animation: bounce 1.4s infinite ease-in-out both;
}@keyframes bounce {0%,80%,100% {transform: scale(0);}40% {transform: scale(1);}
}@keyframes fadeInUp {from {opacity: 0;transform: translateY(10px);}to {opacity: 0.8;transform: translateY(0);}
}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/71598.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Mac 清理缓存,提高内存空间

步骤 1.打开【访达】 2.菜单栏第五个功能【前往】&#xff0c;点击【个人】 3.【command shift J】显示所有文件&#xff0c;打开【资源库】 4.删除【Containers】和【Caches】文件 Containers 文件夹&#xff1a;用于存储每个应用程序的沙盒数据&#xff0c;确保应用程序…

CentOS的ssh复制文件

1.前提 首先要已经连接上了对方的ssh 2.命令 scp [文件] 目标IP:目标路径 例如&#xff1a; $PWD是一个环境变量&#xff0c;可以获取当前绝对目录&#xff0c;ssh上传的时候一定要确保对方有这个目录才行&#xff0c;不然会报错 3.递归上传 scp -r 目录 目标IP:路径 可以…

《Python实战进阶》专栏 No.3:Django 项目结构解析与入门DEMO

《Python实战进阶》专栏 第3集&#xff1a;Django 项目结构解析与入门DEMO 在本集中&#xff0c;我们将深入探讨 Django 的项目结构&#xff0c;并实际配置并运行一个入门DEMO博客网站&#xff0c;帮助你在 Web 开发中更高效地使用 Django。Django 是一个功能强大的 Python Web…

前端(vue)学习笔记(CLASS 1):vue框架入门

1、vue上手 概念&#xff1a;vue是一个用于构建用户界面的渐进式框架 vue的两种使用方式&#xff1a; 1、vue的核心包开发 场景&#xff1a;局部模块改造 2、vue核心包&vue插件工程化开发 场景&#xff1a;整站开发 1、创建实例 核心步骤 1、准备容器&#xff08;…

1.14作业

1 if($x[scheme]http||$x[scheme]https){ $ip gethostbyname($x[host]); echo </br>.$ip.</br>; if(!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {die(ip!); }echo file_get_contents($_POST[url]);可以DNS重…

DeepSeek模型快速部署教程-搭建自己的DeepSeek

前言&#xff1a;在人工智能技术飞速发展的今天&#xff0c;深度学习模型已成为推动各行各业智能化转型的核心驱动力。DeepSeek 作为一款领先的 AI 模型&#xff0c;凭借其高效的性能和灵活的部署方式&#xff0c;受到了广泛关注。无论是自然语言处理、图像识别&#xff0c;还是…

学习aigc

DALLE2 论文 Hierarchical Text-Conditional Image Generation with CLIP Latents [2204.06125] Hierarchical Text-Conditional Image Generation with CLIP LatentsAbstract page for arXiv paper 2204.06125: Hierarchical Text-Conditional Image Generation with CLIP L…

POI pptx转图片

前言 ppt页面预览一直是个问题&#xff0c;office本身虽然有预览功能但是收费&#xff0c;一些开源的项目的预览又不太好用&#xff0c;例如开源的&#xff1a;kkfileview pptx转图片 1. 引入pom依赖 我这个项目比较老&#xff0c;使用版本较旧 <dependency><gro…

零基础学python--------第三节:Python的流程控制语法

Python&#xff0c;浮点数 11.345(单&#xff1a;4个字节&#xff0c; 双&#xff1a;8个字节) 。 十进制的数字25 ---> 11001 讲一个小数转化为二进制&#xff1a; 不断的乘以2 。取整数部分。 十进制的0.625 ----> 二进制&#xff1a; 0&#xff0c; 101 。 0.3 ---…

骶骨神经

骶骨肿瘤手术后遗症是什么_39健康网_癌症 [健康之路]匠心仁术&#xff08;七&#xff09; 勇闯禁区 骶骨肿瘤切除术

DeepSeek智能测试知识库助手PRO版:多格式支持+性能优化

前言 测试工程师在管理测试资产时,需要面对多种文档格式、大量文件分类及知识库的构建任务。为了解决这些问题,我们升级了 DeepSeek智能测试知识库助手,不仅支持更多文档格式,还加入了 多线程并发处理 和 可扩展格式支持,大幅提升处理性能和灵活性。 主要功能亮点: 多格…

基于Python+Vue开发的反诈视频宣传管理系统源代码

项目简介 该项目是基于PythonVue开发的反诈视频宣传管理系统&#xff08;前后端分离&#xff09;&#xff0c;这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Python编程技能&#xff0c;同时锻炼他们的项目设计与开发能力。通过学习基于Python的反…

StarRocks FE leader节点CPU使用率周期性的忽高忽低问题分析

背景 本文基于 StarRocks 3.3.5 最近在做一些 StarRocks 相关的指标监控的时候&#xff0c;看到了FE master的CPU使用率相对其他FE节点是比较高的&#xff0c;且 呈现周期性的变化&#xff08;周期为8分钟&#xff09;&#xff0c; 于此同时FE master节点的GC频率相对于其他节…

第37章 合作之路与占坑成功

在春寒料峭的时节&#xff0c;那丝丝寒意宛如一缕缕若有若无的轻烟&#xff0c;在空气中悄然弥漫。锐创所的会议室&#xff0c;宛如一个被岁月尘封的神秘空间&#xff0c;暖黄色的灯光晕染开来&#xff0c;像是为整个房间披上了一层朦胧的薄纱&#xff0c;陈旧却又带着几分温馨…

Rpc导读

手写Rpc框架 - 导读 git仓库-all-rpc GTIEE&#xff1a;https://gitee.com/quercus-sp204/all-rpc 【参考源码 yrpc】 1. Rpc概念 RPC 即远程过程调用&#xff08;Remote Procedure Call&#xff09; &#xff0c;就是通过网络从远程计算机程序上请求服务。 本地调用抽象&…

网络安全:防范NetBIOS漏洞的攻击

稍微懂点电脑知识的朋友都知道&#xff0c;NetBIOS 是计算机局域网领域流行的一种传输方式&#xff0c;但你是否还知道&#xff0c;对于连接互联网的机器来讲&#xff0c;NetBIOS是一大隐患。 漏洞描述 NetBIOS(Network Basic Input Output System&#xff0c;网络基本输入输…

使用代码与 AnythingLLM 交互的基本方法和示例

AnythingLLM 是一个基于大语言模型&#xff08;LLM&#xff09;的工具&#xff0c;主要用于构建和管理个人或企业知识库。虽然它主要提供图形化界面&#xff08;GUI&#xff09;进行操作&#xff0c;但也可以通过代码进行一些高级配置和集成。以下是使用代码与 AnythingLLM 交互…

用DeepSeek零基础预测《哪吒之魔童闹海》票房——从数据爬取到模型实战

系列文章目录 1.元件基础 2.电路设计 3.PCB设计 4.元件焊接 5.板子调试 6.程序设计 7.算法学习 8.编写exe 9.检测标准 10.项目举例 11.职业规划 文章目录 **一、为什么要预测票房&#xff1f;****二、准备工作****三、实战步骤详解****Step 1&#xff1a;数据爬取与清洗&am…

如何将MySQL数据库迁移至阿里云

将 MySQL 数据库迁移至阿里云可以通过几种不同的方法&#xff0c;具体选择哪种方式取决于你的数据库大小、数据复杂性以及对迁移速度的需求。阿里云提供了多种迁移工具和服务&#xff0c;本文将为你介绍几种常见的方法。 方法一&#xff1a;使用 阿里云数据库迁移服务 (DTS) 阿…

Ubuntu22.04 - gflags的安装和使用

目录 gflags 介绍gflags 安装gflags 使用 gflags 介绍 gflags 是Google 开发的一个开源库&#xff0c;用于 C应用程序中命令行参数的声明、定义和解析。gflags 库提供了一种简单的方式来添加、解析和文档化命令行标志(flags),使得程序可以根据不同的运行时配置进行调整。 它具…