当前位置: 首页 > news >正文

打造智能聊天体验:前端集成 DeepSeek AI 助你快速上手

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>

相关文章:

打造智能聊天体验:前端集成 DeepSeek AI 助你快速上手

DeepSeek AI 聊天助手集成指南 先看完整效果&#xff1a; PixPin_2025-02-19_09-15-59 效果图&#xff1a; 目录 项目概述功能特点环境准备项目结构组件详解 ChatContainerChatInputMessageBubbleTypeWriter 核心代码示例使用指南常见问题 项目概述 基于 Vue 3 TypeScrip…...

github生成badges的方法

在Github页面上生成类似下面这样的badge的方法 你可以通过以下步骤在GitHub个人主页的README中创建类似的技术栈徽章&#xff1a; 一、使用 Shields.io 生成徽章 Shields.io 是一个开源徽章生成工具&#xff0c;支持自定义文本、颜色、图标等参数。 1. 基础模板 https://…...

vulnhub靶场渗透之SickOs1.2渗透教程,计划任务提权、chkrootkit提权

vulnhub靶场渗透之SickOs1.2渗透教程&#xff0c;计划任务提权、chkrootkit提权 一、信息收集 1、首先拿到靶场先扫一下ip 2025.3.7 AM 8&#xff1a;36 arp-scan -l 扫描同网段 nmap -sP 192.168.66.24/02、指纹扫描 nmap -sS -sV 192.168.66.130 指纹扫描PORT STATE S…...

Ubuntu系统部署.NET 8网站项目

一、使用XShell连接 Ubuntu系统初次连接时默认的用户名为&#xff1a;ubuntu&#xff0c;使用此用户名与系统登录密码进行连接。 登录成功效果如下图&#xff1a; 二、root用户登录 linux下有超级用户&#xff08;root&#xff09;和普通用户&#xff0c;普通用户不能直接操…...

CI/CD—Jenkins配置一次完整的jar自动化发布流程

背景&#xff1a; 实现设想&#xff1a; 要创建自动化发布&#xff0c;需要准备一台测试服务器提前安装好java运行所需的环境&#xff0c;JDK版本最好和Windows开发机器上的版本一致&#xff0c;在Jenkins上配置将构建好的jar上传到测试服务器上&#xff0c;测试服务器自动启动…...

【Academy】Web 缓存欺骗 ------ Web cache deception

Web 缓存欺骗 ------ Web cache deception 1. 概述2. Web 缓存2.1 缓存键2.2 缓存规则 3. 构建 Web 缓存欺骗攻击3.1 使用缓存破坏器3.2 检测缓存的响应 4. 利用静态扩展缓存规则4.1 路径映射差异4.2 利用路径映射差异4.3 分隔符差异4.4 利用分隔符差异4.5 分隔符解码差异4.6 利…...

MATLAB表格Table与时间序列Timetable的高效操作方法

MATLAB中的表格&#xff08;Table&#xff09; 和 时间序列&#xff08;Timetable&#xff09; 是处理结构化数据和时间相关数据的核心工具。以下从基础操作到高级技巧&#xff0c;分步骤详解其使用方法。 一、创建与基础操作 1. 表格&#xff08;Table&#xff09;的创建与访…...

【leetcode hot 100 21】合并两个有序链表

解法一&#xff1a;新建一个链表存放有序的合并链表。当list1和list2至少有一个非空时&#xff0c;返回非空的&#xff1b;否则找出两个链表的最小值作为新链表的头&#xff0c;然后依次比较两链表&#xff0c;每次都先插入小的值。 /*** Definition for singly-linked list.*…...

本地部署 OpenManus 保姆级教程(Windows 版)

一、环境搭建 我的电脑是Windows 10版本&#xff0c;其他的没尝试&#xff0c;如果大家系统和我的不一致&#xff0c;请自行判断&#xff0c;基本上没什么大的出入啊。 openManus的Git地址&#xff1a;https://github.com/mannaandpoem/OpenManus 根据官网的两种安装推荐方式如…...

20250310:OpenCV mat对象与base64互转

代码: https://github.com/ReneNyffenegger/cpp-base64 指南:https://renenyffenegger.ch/notes/development/Base64/Encoding-and-decoding-base-64-with-cpp/ 实操:...

WPS的付费功能,这款软件可完美平替

因为作者有工作上的需求加上WPS使用批量提取图片需要会员&#xff0c;所以自己使用cursor制作了一个从excel中提取图片的工具。 支持提取Excel中的浮动图片和根据图片链接来下载图片。 Excel Image Extractor Excel图片提取工具 软件的功能非常强大&#xff0c;支持提取Excel中…...

L1-088 静静的推荐

L1-088 静静的推荐 - 团体程序设计天梯赛-练习集 (pintia.cn) 题解 这里代码很简单&#xff0c;但是主要是循环里面的内容很难理解&#xff0c;下面是关于循环里面的内容理解&#xff1a; 这里 n 10 表示有 10 个学生&#xff0c;k 2 表示企业接受 2 批次的推荐名单&#…...

NS3学习——运行自定义拥塞控制算法步骤

目录 一、添加优化后的代码文件 二、更改CMakeLists文件中内容 三、重新配置和编译ns-3 四、常见问题 目的&#xff1a;想在tcp拥塞控制算法的基础上进行优化改进&#xff0c;之后在ns3中运行优化后的算法&#xff0c;即自定义拥塞控制算法&#xff1b; 以tcpVegas算法为…...

前端开发中的常见设计模式:全面解析与实践

1. 引言 1.1 设计模式的重要性 设计模式是软件开发中经过验证的解决方案&#xff0c;能够帮助开发者解决常见的设计问题。在前端开发中&#xff0c;合理使用设计模式可以提高代码的可维护性、可扩展性和复用性。 1.2 本文的目标 本文旨在全面解析前端开发中常见的设计模式&…...

VSCode 2025最新 前端开发必备插件推荐汇总(提效指南)

&#x1f31f;前言: 如果你是一名前端开发工程师&#xff0c;合适的开发工具能大大提高工作效率。Visual Studio Code (VSCode) 凭借其轻量级、高扩展性的特点&#xff0c;已成为众多前端开发者在win系电脑的首选IDE。 名人说&#xff1a;博观而约取&#xff0c;厚积而薄发。—…...

【redis】redis的单线程模型为什么效率高?

文章目录 单线程模型单线程的 Redis 为什么效率高&#xff1f;速度快&#xff1f;IO多路复用epoll 单线程模型 Redis 只使用一个线程&#xff0c;处理所有的命令请求。不是说一个 Redis 服务器进程内部只有一个线程&#xff0c;其实也有多个线程&#xff0c;多个线程是在处理网…...

scala的集合

scala的集合系统的区分了可变&#xff08; mutable &#xff09;和不可变&#xff08;immutable &#xff09;集合。 mkString(seq:String)&#xff1a;方法是将原字符串使用特定的字符串seq分割。 mkString(statrt:String,seq:String,end:String)&#xff1a;方法是将原字符…...

Flink状态管理深度探索:从Keyed State到分布式快照

Flink状态管理深度探索:从Keyed State到分布式快照 在大数据实时计算领域,Apache Flink凭借其精准的状态管理能力成为行业标杆。本文将从状态管理的核心机制出发,结合金融行业PB级数据处理实践,深入解析状态后端、容错机制与大规模优化策略。 一、Flink状态管理核心架构 …...

Android 11 DAC和MAC

在 Android 11 中,DAC(Discretionary Access Control,自主访问控制) 和 MAC(Mandatory Access Control,强制访问控制) 是两种不同的访问控制机制,主要用于 权限管理、安全性 以及 进程间访问控制。 1. DAC(自主访问控制) DAC(Discretionary Access Control,自主访…...

平衡二叉树(AVL树)

平衡二叉树是啥我就不多说了&#xff0c;本篇博客只讲原理与方法。 首先引入平衡因子的概念。平衡因子&#xff08;Balance Factor&#xff09;&#xff0c;以下简称bf。 bf 右子树深度 - 左子树深度。平衡结点的平衡因子可为&#xff1a;-1&#xff0c;0&#xff0c;1。除此…...

SSM架构 +java后台 实现rtsp流转hls流,在前端html上实现视频播放

序言&#xff1a;书接上文&#xff0c;我们继续 SSM架构 NginxFFmpeg实现rtsp流转hls流&#xff0c;在前端html上实现视频播放 步骤一&#xff1a;把rtsp流转化为hls流&#xff0c;用Java代码进行转换 package com.tools;import java.io.BufferedReader; import java.io.IOExc…...

贪心算法--

1.柠檬水找零 link:860. 柠檬水找零 - 力扣&#xff08;LeetCode&#xff09; code class Solution { public:bool lemonadeChange(vector<int>& bills) {// 贪心算法&#xff0c; 优先花出大面额bill&#xff0c; 尽可能保护小面额billint five 0, ten 0;// 不…...

【从0到1搞懂大模型】神经网络的实现:数据策略、模型调优与评估体系(3)

一、数据集的划分 &#xff08;1&#xff09;按一定比例划分为训练集和测试集 我们通常取8-2、7-3、6-4、5-5比例切分&#xff0c;直接将数据随机划分为训练集和测试集&#xff0c;然后使用训练集来生成模型&#xff0c;再用测试集来测试模型的正确率和误差&#xff0c;以验证…...

CTF工具集合-持续更新

工具地址https://github.com/huan-cdm/ctf_tools工具介绍&#xff1a; 1.ARCHPR&#xff1a;压缩包密码破解工具 2.StegSolve-1.4.jar&#xff1a;隐写图片查看工具 3.ctf_decrypt_tool.rar&#xff1a;随波逐流CTF编码工具 4.010_Editor_All_Versions_For_Windows_CracKed.…...

小方摄像头接入本地服务器的方法

最早众筹时买了几个小方摄像头&#xff0c;后来嫌弃分辨率&#xff0c;就淘汰吃灰好几年&#xff0c;最近想折腾个摄像头识别的小项目&#xff0c;秉着不投入先凑合跑起来的原则&#xff0c;想到了尘封已久的小方&#xff0c;想看看能不能通过网络拉取数据流。 搜索了下&#x…...

取反符号~

取反符号 ~ 用于对整数进行按位取反操作。它会将二进制表示中的每一位取反&#xff0c;即 0 变 1&#xff0c;1 变 0。 示例 a 5 # 二进制表示为 0000 0101 b ~a # 按位取反&#xff0c;结果为 1111 1010&#xff08;补码表示&#xff09; print(b) # 输出 -6解释 5 的二…...

Jenkins实现自动化构建与部署:上手攻略

一、持续集成与Jenkins核心价值 1.1 为什么需要自动化构建&#xff1f; 在现代化软件开发中&#xff0c;团队每日面临以下挑战&#xff1a; 高频代码提交&#xff1a;平均每个开发者每天提交5-10次代码。多环境部署&#xff1a;开发、测试、预发布、生产环境需频繁同步。复杂…...

爱普生温补晶振 TG5032CFN高精度稳定时钟的典范

在科技日新月异的当下&#xff0c;众多领域对时钟信号的稳定性与精准度提出了极为严苛的要求。爱普生温补晶振TG5032CFN是一款高稳定性温度补偿晶体振荡器&#xff08;TCXO&#xff09;。该器件通过内置温度补偿电路&#xff0c;有效抑制环境温度变化对频率稳定性的影响&#x…...

【Java 面试 八股文】计算机网络篇

操作系统篇 1. 什么是HTTP? HTTP 和 HTTPS 的区别?2. 为什么说HTTPS比HTTP安全? HTTPS是如何保证安全的&#xff1f;3. 如何理解UDP 和 TCP? 区别? 应用场景?3.1 TCP 和 UDP 的特点3.2 适用场景 4. 如何理解TCP/IP协议?5. DNS协议 是什么&#xff1f;说说DNS 完整的查询…...

OpenHarmony5.0分布式系统源码实现分析—软总线

一、引言 OpenHarmony 作为一款面向万物互联的操作系统&#xff0c;其分布式软总线&#xff08;Distributed SoftBus&#xff09;是实现设备间高效通信和协同的核心技术之一。分布式软总线通过构建一个虚拟的总线网络&#xff0c;使得不同设备能够无缝连接、通信和协同工作。本…...

Spring Boot/Spring Cloud 整合 ELK(Elasticsearch、Logstash、Kibana)详细避坑指南

我们在开发中经常会写日志&#xff0c;所以需要有个日志可视化界面管理&#xff0c;使用ELK可以实现高效集中化的日志管理与分析&#xff0c;提升性能稳定性&#xff0c;满足安全合规要求&#xff0c;支持开发运维工作。 下述是我在搭建ELK时遇到的许许多多的坑&#xff0c;希望…...

云原生周刊:Istio 1.25.0 正式发布

开源项目推荐 Dstack Dstack 是一个开源的 AI 计算管理平台&#xff0c;旨在简化 AI 任务的部署和管理。它支持本地和云端运行 AI 工作负载&#xff0c;并提供自动化的 GPU 资源调度&#xff0c;使开发者能够更高效地利用计算资源。Dstack 兼容 K8s&#xff0c;可以无缝集成到…...

微前端如何拯救大型项目

前言 在前端开发的世界中&#xff0c;我们经常会遇到这样的问题&#xff1a;一个大型项目往往由多个团队共同开发&#xff0c;每个团队负责一部分功能。然而&#xff0c;随着项目的不断扩大和复杂化&#xff0c;前端代码库变得越来越庞大和难以维护。这时&#xff0c;微前端&a…...

RabbitMQ 高级特性:从 TTL 到消息分发的全面解析 (下)

RabbitMQ高级特性 RabbitMQ 高级特性解析&#xff1a;RabbitMQ 消息可靠性保障 &#xff08;上&#xff09;-CSDN博客 RabbitMQ 高级特性&#xff1a;从 TTL 到消息分发的全面解析 &#xff08;下&#xff09;-CSDN博客 引言 RabbitMQ 作为一款强大的消息队列中间件&#xff…...

OpenManus-通过源码方式本地运行OpenManus,含踩坑及处理方案

前言&#xff1a;最近 Manus 火得一塌糊涂啊&#xff0c;OpenManus 也一夜之间爆火&#xff0c;那么作为程序员应该来尝尝鲜 1、前期准备 FastGithub&#xff1a;如果有科学上网且能正常访问 github 则不需要下载此软件&#xff0c;此软件是提供国内直接访问 githubGit&#…...

Ubuntu22.04修改root用户并安装cuda

由于本人工作原因&#xff0c;经常会遇到需要给ubuntu打显卡驱动的问题&#xff0c;虽然说不难吧&#xff0c;但是耐不住机器多&#xff0c;重复多次也就烦了&#xff0c;于是抽出了一点时间&#xff0c;并且在deepseek的帮助之下&#xff0c;写了一个自动安装驱动的脚本&#…...

Java LeetCode 热题 100 回顾38

干货分享&#xff0c;感谢您的阅读&#xff01;LeetCode 热题 100 回顾_力code热题100-CSDN博客 一、哈希部分 1.两数之和 &#xff08;简单&#xff09; 题目描述 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两…...

MySQL复习笔记

文章目录 1.MySQL1.1什么是数据库1.2 数据库分类1.3 MySQL简介1.4连接数据库 2. 操作数据库2.1 操作数据库2.2 数据库的列类型2.3 数据库的字段属性&#xff08;重点&#xff09;2.4 创建数据库表&#xff08;重点&#xff09;2.5 数据表的类型2.6 修改数据表 3. MySQL 数据管理…...

解释 TypeScript 中的类型系统,如何定义和使用类型?

1. 类型系统的核心作用 TypeScript类型系统本质上是JavaScript的静态类型增强方案&#xff0c;提供三个核心价值&#xff1a; 开发阶段类型检查&#xff08;类似编译时eslint&#xff09;更清晰的API文档&#xff08;类型即文档&#xff09;更好的IDE自动补全支持 代码示例&…...

安裝do時出現log file support is not available

“log file support is not available (press RETURN)” 这个提示信息表明日志文件支持不可用&#xff0c;让你按回车键继续。出现这种情况可能是因为 Odoo 的日志相关配置存在问题或者一些必要的依赖没有正确安装配置。以下是一些可以尝试的解决办法&#xff1a; 1. 检查 Odo…...

[HTTP协议]应用层协议HTTP从入门到深刻理解并落地部署自己的云服务(1)知识基础

[HTTP协议]应用层协议HTTP从入门到深刻理解并落地部署自己的云服务(1)知识基础 水墨不写bug 文章目录 (一)概念梳理1.什么是协议?2.什么是应用层?3. 为什么要进行分层&#xff1f; &#xff08;二&#xff09;HTTP协议2.1 初识HTTP协议2.2HTTP协议的URL2.2.1域名2.2.2端口号2…...

机票改签请求

示例代码&#xff1a; tool def update_ticket_to_new_flight(ticket_no: str, new_flight_id: int) -> str:"""Update the users ticket to a new valid flight.Args:ticket_no (str): The ticket number to be updated.new_flight_id (int): The ID of th…...

linux下文件读写操作

Linux下&#xff0c;文件I/O是操作系统与文件系统之间进行数据传输的关键部分。文件I/O操作允许程序读取和写入文件&#xff0c;管理文件的打开、关闭、创建和删除等操作。 1. 文件描述符 在Linux中&#xff0c;每个打开的文件都由一个文件描述符来表示。文件描述符是一个非负…...

命名管道的创建和通信实现

目录 命名管道的创建 使用函数创建命名管道的通信 预备创建 makefile设计 server.hpp设计 clent.hpp设计 comm.hpp设计 server.cc设计 clent.cc设计 测试运行 今天我们来学习命名管道 由于匿名管道&#xff08;pipe()&#xff09;无法在两个毫不相干的进程之间进行通…...

C++和OpenGL实现3D游戏编程【连载24】——父物体和子物体之间的坐标转换

欢迎来到zhooyu的C++和OpenGL游戏专栏,专栏连载的所有精彩内容目录详见下边链接: 🔥C++和OpenGL实现3D游戏编程【总览】 父子物体的坐标转换 1、本节要实现的内容 前面章节我们了解了父物体与子物体的结构,它不仅能够表示物体之间的层次关系,更重要的一个作用就是展示物…...

21.HarmonyOS Next CustomSlider组件步长控制教程(三)

温馨提示&#xff1a;本篇博客的详细代码已发布到 git : https://gitcode.com/nutpi/HarmonyosNext 可以下载运行哦&#xff01; 文章目录 1. 步长控制概述2. 步长基本概念2.1 什么是步长&#xff1f;2.2 步长的作用 3. 设置步长3.1 基本参数3.2 代码示例 4. 步长与范围的关系4…...

小白学习:rag向量数据库

学习视频&#xff1a; https://www.bilibili.com/video/BV11zf6YyEnT/?spm_id_from333.337.search-card.all.click 例子&#xff1a; 用户提出问题 客服机器人基于rag回答用户问题 过程拆解&#xff1a; 客户问题 – 转化为向量表示 – 在向量数据库中进行相似性搜索 – 系…...

STM32 CAN模块原理与应用详解

目录 概述 一、CAN模块核心原理 1. CAN协议基础 2. STM32 CAN控制器结构 3. 波特率配置 二、CAN模块配置步骤&#xff08;基于HAL库&#xff09; 1. 初始化CAN外设 2. 配置过滤器 3. 启动CAN通信 三、数据收发实现 1. 发送数据帧 2. 接收数据帧&#xff08;中断方式…...

NO.29十六届蓝桥杯备战|string九道练习|reverse|翻转|回文(C++)

P5015 [NOIP 2018 普及组] 标题统计 - 洛谷 #include <bits/stdc.h> using namespace std;int main() {ios::sync_with_stdio(false);cin.tie(nullptr);string s;getline(cin, s);int sz s.size();int cnt 0;for (int i 0; i < sz; i){if (isspace(s[i]))continue…...

最新版本TOMCAT+IntelliJ IDEA+MAVEN项目创建(JAVAWEB)

前期所需&#xff1a; 1.apache-tomcat-10.1.18-windows-x64&#xff08;tomcat 10.1.8版本或者差不多新的版本都可以&#xff09; 2.IntelliJ idea 24年版本 或更高版本 3.已经配置好MAVEN了&#xff08;一定先配置MAVEN再搞TOMCAT会事半功倍很多&#xff09; 如果有没配置…...