# chat-app **Repository Path**: kindling-sss/chat-app ## Basic Information - **Project Name**: chat-app - **Description**: 基于 Vue3 + Vite + Element Plus 开发的智能聊天对话页面 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-02-11 - **Last Updated**: 2026-02-26 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # AI智能对话应用 基于 Vue3 + Vite + Element Plus 开发的智能聊天对话页面,支持响应式布局、打字机效果和流式响应处理。 ## 功能特性 ✅ **响应式布局** - PC端:左侧会话列表(280px)+ 右侧聊天窗口 - 平板端:自适应调整布局和间距 - 手机端:全屏聊天,侧边栏可呼出 ✅ **流式响应处理** - 支持 EventStream 格式响应 - 实时解析和显示 AI 回复 - 逐块处理响应数据,实现实时更新 ✅ **打字机效果** - 逐字蹦出展示AI回复 - 可配置打字速度和批量字数 - 支持新消息发送时中断当前打字机 ✅ **移动端交互** - 顶部菜单按钮唤醒对话列表 - 从屏幕左侧向右滑动唤醒对话列表 - 对话列表支持向左滑动删除 ✅ **状态反馈** - "AI正在输入..." 实时状态显示 - "AI已经回答完毕" 完成状态提示 - 错误提示和重试机制 ✅ **接口对接** - 完整的请求/响应封装 - 支持令牌鉴权 - 自动异常处理和重试机制 ✅ **交互体验** - 回车/点击按钮发送消息 - 发送中Loading状态 - 消息复制功能 - 时间戳显示 ## 技术栈 - **框架**: Vue 3 + Composition API - **构建工具**: Vite 5 - **UI组件库**: Element Plus - **HTTP客户端**: Fetch API (流式响应) + Axios (普通请求) - **样式**: Scoped CSS + CSS变量 ## 项目结构 ``` chat-app/ ├── public/ # 静态资源 │ └── eventStream响应数据示例.txt # EventStream响应格式示例 ├── src/ │ ├── api/ # API请求封装 │ │ └── chat.js # 聊天接口(支持流式响应) │ ├── components/ # 组件 │ │ ├── ChatPage.vue # 主页面(含侧边栏和聊天窗口) │ │ ├── MessageItem.vue # 消息项组件 │ │ └── InputArea.vue # 输入框组件 │ ├── config/ # 配置文件 │ │ └── api.js # API配置(地址、令牌) │ ├── App.vue # 根组件 │ └── main.js # 入口文件 ├── .coze # 项目配置 ├── README.md # 项目说明 ├── index.html # HTML模板 ├── package.json # 项目依赖 ├── pnpm-lock.yaml # 依赖锁文件 └── vite.config.js # Vite配置(代理) ``` ## 安装和运行 ### 1. 安装依赖 ```bash pnpm install ``` ### 2. 启动开发服务器 ```bash pnpm dev ``` 服务将在 `http://localhost:5000` 启动 ### 3. 构建生产版本 ```bash pnpm build ``` ### 4. 预览生产版本 ```bash pnpm preview ``` ## 配置说明 ### 1. API配置(`src/config/api.js`) ```javascript export const API_CONFIG = { // 代理路径 PROXY_PATH: '/api', // Authorization令牌 AUTH_TOKEN: 'Bearer your-token-here' } ``` **修改接口地址或令牌**: 1. 打开 `src/config/api.js` 2. 修改 `PROXY_PATH` 或 `AUTH_TOKEN` 3. 保存文件,开发服务器会自动热更新 ### 2. 打字机效果配置(`src/components/ChatPage.vue`) ```javascript const TYPEWRITER_CONFIG = { speed: 80, // 单字蹦出间隔(毫秒) chunkSize: 2 // 每次蹦出字数(1-3字) } ``` **调整打字机速度**: 1. 打开 `src/components/ChatPage.vue` 2. 修改 `TYPEWRITER_CONFIG.speed`(值越小速度越快) 3. 修改 `TYPEWRITER_CONFIG.chunkSize`(每次蹦出字数) 4. 保存文件,自动热更新 ### 3. Vite代理配置(`vite.config.js`) ```javascript export default defineConfig({ server: { port: 5000, host: '0.0.0.0', proxy: { '/api': { target: 'https://kzcyr72fwp.coze.site', changeOrigin: true, rewrite: (path) => path.replace(/^\/api/, '') } } } }) ``` **修改代理配置**: 1. 打开 `vite.config.js` 2. 修改 `proxy.target` 为你的接口地址 3. 重启开发服务器 ## 核心实现 ### 1. 流式响应处理(`src/api/chat.js`) 使用 Fetch API 处理 EventStream 响应: ```javascript const response = await fetch(`${API_CONFIG.PROXY_PATH}/stream_run`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': API_CONFIG.AUTH_TOKEN }, body: JSON.stringify({ content: { query: { prompt: [ { type: "text", content: { text: question } } ] } }, type: "query", project_id: 7599989802980704271 }) }) const reader = response.body.getReader() const decoder = new TextDecoder('utf-8') let fullText = '' let buffer = '' while (true) { const { done, value } = await reader.read() if (done) break buffer += decoder.decode(value, { stream: true }) // 处理完整的消息块(以空行分隔) const messages = buffer.split('\n\n') buffer = messages.pop() // 保留不完整的消息在缓冲区 for (const message of messages) { // 解析和处理消息... if (parsedData.content.answer) { fullText += parsedData.content.answer onChunk(fullText) // 实时回调更新UI } } } ``` ### 2. 响应式布局 使用媒体查询(`@media`)实现不同设备适配: ```css /* PC端(≥1200px) */ @media (min-width: 1200px) { .chat-sidebar { width: 280px; } .message-content-wrapper { max-width: 60%; } } /* 平板端(768px~1199px) */ @media (min-width: 768px) and (max-width: 1199px) { .chat-sidebar { width: 240px; } .message-content-wrapper { max-width: 80%; } } /* 手机端(≤767px) */ @media (max-width: 767px) { .chat-sidebar { position: fixed; } .message-content-wrapper { max-width: 85%; } } ``` ### 3. 打字机效果实现 核心逻辑(`src/components/ChatPage.vue`): ```javascript const startTypewriter = (fullText) => { return new Promise((resolve) => { if (typingTimer.value) { clearInterval(typingTimer.value) typingTimer.value = null } currentTypingText.value = '' isTyping.value = true let index = 0 const totalLength = fullText.length typingTimer.value = setInterval(() => { if (index >= totalLength) { clearInterval(typingTimer.value) typingTimer.value = null isTyping.value = false resolve() return } const chunk = fullText.substring(index, index + TYPEWRITER_CONFIG.chunkSize) currentTypingText.value += chunk index += TYPEWRITER_CONFIG.chunkSize if (messages.value.length > 0) { const lastMessage = messages.value[messages.value.length - 1] if (lastMessage.role === 'assistant') { lastMessage.content = currentTypingText.value } } scrollToBottom() }, TYPEWRITER_CONFIG.speed) }) } ``` ### 4. 移动端滑动手势 ```javascript // 从屏幕左侧向右滑动唤醒侧边栏 const handleTouchEnd = (e) => { if (!isMobile.value) return const touchEndX = e.changedTouches[0].clientX const touchEndY = e.changedTouches[0].clientY const deltaX = touchEndX - touchStartX const deltaY = Math.abs(touchEndY - touchStartY) // 从屏幕左侧向右滑动,且垂直滑动距离较小 if (deltaX > 50 && deltaY < 100 && touchStartX < 50) { toggleSidebar() } } // 对话列表滑动删除 const handleChatItemTouchEnd = (e, chatId) => { const touchX = e.changedTouches[0].clientX const deltaX = touchX - chatItemTouchStartX // 如果向左滑动超过40px,则显示删除按钮 if (deltaX < -40) { deletedChatId.value = chatId } else { // 否则恢复原位 deletedChatId.value = null } } ``` ### 5. 状态管理 使用 Vue 3 的响应式 API 管理状态: ```javascript // 核心修复:用 reactive 包装 aiMessage,确保属性更新有响应式 const aiMessage = reactive({ id: generateMessageId(), role: 'assistant', content: '', time: new Date(), isLoading: true }) // 流式回调更新 const onChunk = (chunkContent) => { // 实时更新响应式对象的属性,Vue能检测到变化 aiMessage.content = chunkContent aiMessage.isLoading = false // 移除加载状态 // 强制触发nextTick,确保DOM更新并滚动到底部 nextTick(() => { scrollToBottom() }) } ``` ## 常见问题排查 ### 1. 流式响应不显示 **原因**: - EventStream 格式解析错误 - 接口返回格式与预期不符 - 令牌权限不足 **解决方案**: 1. 检查浏览器控制台日志,查看是否有解析错误 2. 确认接口返回的是标准 EventStream 格式 3. 验证 `AUTH_TOKEN` 是否正确且有效 4. 查看 `public/eventStream响应数据示例.txt` 了解预期格式 ### 2. 移动端滑动手势不生效 **原因**: - 触摸事件监听器未正确绑定 - CSS `touch-action` 属性限制 - 浏览器兼容性问题 **解决方案**: 1. 检查控制台是否有相关错误 2. 确保 `chat-main` 元素存在且可点击 3. 尝试在不同移动设备或模拟器上测试 ### 3. 对话列表滑动删除不工作 **原因**: - CSS 样式问题 - 触摸事件处理逻辑错误 - 响应式状态更新问题 **解决方案**: 1. 检查 `chat-item-wrapper` 和相关样式 2. 确认触摸事件监听器已正确绑定 3. 查看控制台是否有相关错误 ### 4. 令牌失效问题 **原因**: - 令牌过期 - 令牌格式错误 - 令牌权限不足 **解决方案**: 1. 检查 `src/config/api.js` 中的 `AUTH_TOKEN` 是否正确 2. 确认令牌格式为 `Bearer your-token`(注意空格) 3. 查看浏览器Console日志,确认错误码为401/403 4. 联系接口提供方重新生成令牌 ### 5. 端口被占用 **错误信息**: ``` Port 5000 is in use, trying another one... ``` **解决方案**: ```bash # 查找占用5000端口的进程 ss -lptn 'sport = :5000' # 停止该进程 kill # 重新启动 pnpm dev ``` ### 6. 依赖安装失败 **解决方案**: ```bash # 清理缓存 pnpm store prune # 删除node_modules和lock文件 rm -rf node_modules pnpm-lock.yaml # 重新安装 pnpm install ``` ## 浏览器兼容性 - **PC端**: Chrome 90+, Firefox 88+, Edge 90+ - **移动端**: 微信浏览器, Safari (iOS 14+), Chrome (Android 10+) ## 性能优化建议 1. **打字机优化**:根据设备性能动态调整速度 2. **消息列表**:长列表使用虚拟滚动 3. **图片加载**:使用懒加载 4. **代码分割**:路由级别的代码分割 ## 开发规范 1. 使用 Composition API 和 `