From afadb0ced73f93bebc8530599484eddc1ea0623c Mon Sep 17 00:00:00 2001 From: Wonder Date: Mon, 6 Oct 2025 00:54:47 +0800 Subject: [PATCH] =?UTF-8?q?=20=F0=9F=90=9BFix:=20T4=20&=20T5=20&=20T6=20?= =?UTF-8?q?=E7=B4=AF=E7=A7=AF=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 48 ++++---- frontend/src/components/ChatPanel.vue | 151 ++---------------------- frontend/src/components/ConfigPanel.vue | 40 +++---- 3 files changed, 50 insertions(+), 189 deletions(-) diff --git a/README.md b/README.md index 8342d7a..19550bf 100644 --- a/README.md +++ b/README.md @@ -4,27 +4,27 @@ ## 任务清单 -| 任务编号 | 任务名称 | 优先级 | 说明 | 负责人 | 交付成果 | -|----------|----------------------------------|--------|----------------------------------------------------------------------|----------|--------------------------------| -| T1 | 明确需求并撰写 PRD 文档 | 高 | 完成产品需求文档,明确功能及非功能需求 | 产品 | 🟢 PRD 文档(含厂商列表、功能说明等) | -| T2 | 确定支持厂商并获取 API 信息 | 高 | 确认 5 个主流 AI API 厂商的端点、密钥类型和请求格式 | 产品 | 🟢 完整厂商支持列表(含 API 示例) | -| T3 | 搭建 Web 应用的基础架构(HTML/CSS/JS) | 中 | 创建 Web 首页和配置页、对话页,切换页面(可使用单页应用) | 开发 | 🔵 项目框架(HTML + CSS + 前端基础) | -| T4 | 实现厂商下拉选择功能 | 高 | 列出厂商选项,用户可选当前要配置的厂商 | 开发 | 🔵 下拉菜单 + 厂商动态展示 | -| T5 | 实现 API 配置输入界面 | 高 | 根据所选厂商显示对应输入项(如密钥 + 接入点) | 开发 | 🔵 动态表单(密钥/接入点输入) | -| T6 | 实现密钥输入验证 | 高 | 检查输入格式是否符合厂商要求(如 OpenAI 密钥以 `sk-` 开头) | 开发 | 🔵 格式校验 + 错误提示 | -| T7 | 实现配置保存与加密存储 | 高 | 加密存储用户输入的密钥和接入点,使用 localStorage 或 indexedDB | 开发 | 🔵 安全存储实现(AES 加密) | -| T8 | 创建厂商配置数据结构 | 中 | 在前端代码中定义厂商配置信息(如 ID、名称、端点、字段等) | 开发 | 🔵 `PLATFORMS` 配置数组(如 JS 对象结构) | -| T9 | 实现对话页界面 | 中 | 输入框 + 厂商选择器 + 发送按钮 + 响应展示区 | 开发 | 🔵 对话页面设计 + 基本交互 | -| T10 | 实现对话 API 调用逻辑 | 高 | 根据用户选择的厂商,使用对应密钥调用 AI API 并返回结果 | 开发 | 🔵 厂商 API 通信逻辑(POST 请求) | -| T11 | 实现响应展示与错误处理 | 高 | 显示 API 返回内容,处理 401、500 等错误信息 | 开发 | 🔵 响应展示模块 + 错误页/提示 | -| T12 | 添加「重试」机制(可选) | 中 | 对于调用失败的情况提供一键重试功能 | 开发 | 🔵 调用失败后支持重试按钮 | -| T13 | 构建配置页与对话页的切换逻辑 | 中 | 用户可切换配置页和对话页,保证数据在页面间同步(如密钥已保存则自动填充) | 开发 | 🔵 页面切换功能 + 自动填充已有配置 | -| T14 | 添加「首次未配置提醒」 | 中 | 如果没有配置任何厂商,引导用户进行配置 | 开发 | 🔵 首页友好提示信息 | -| T15 | 添加厂商配置删除功能 | 中 | 允许用户删除已有配置,避免冗余数据 | 开发 | 🔵 删除配置按钮 + localStorage 清理 | -| T16 | 添加厂商默认配置(可选) | 中 | 预设 OpenAI、通义等厂商的默认接入点和字段提示 | 开发 | 🔵 配置页面默认值设置 | -| T17 | 构建完整 UI 界面(风格统一) | 中 | 确保配置页和对话页操作一致、视觉统一 | UI / 前端 | 🔵 响应式设计 + 现代化界面 | -| T18 | 进行基本可用性测试 | 中 | 验证主要功能是否可用(配置保存、API 调用、错误提示等) | QA | 🔵 功能测试报告(手动/自动) | -| T19 | 添加部署方案(如 GitHub Pages) | 低 | 配置前端静态部署,便于快速访问 | 产品 | 🔵 部署文档 + 示例部署链接 | -| T20 | 撰写项目说明文档和使用教程 | 低 | 提供给用户快速上手的说明,包含配置方法和常见问题 | 产品 | 🔵 README 或使用指南文档 | -| T21 | 优化体验(如加载提示、输入缓存) | 低 | 提升用户体验,如加载动画、输入自动保存(可选) | 开发 | 🔵 更流畅、更友好的 UI 交互 | -| T22 | 收集用户反馈并迭代设计 | 低 | 收集可用性数据、建议,优化方案(可选) | 产品 | 🔵 用户反馈记录 + 迭代计划 | \ No newline at end of file +| 任务编号 | 任务名称 | 优先级 | 说明 | 负责人 | 交付成果 | +|----------|---------------------------------------|--------|----------------------------------------------------------------------|----------|--------------------------------| +| T1 | 明确需求并撰写 PRD 文档 | 高 | 完成产品需求文档,明确功能及非功能需求 | 产品 | 🟢 PRD 文档(含厂商列表、功能说明等) | +| T2 | 确定支持厂商并获取 API 信息 | 高 | 确认 5 个主流 AI API 厂商的端点、密钥类型和请求格式 | 产品 | 🟢 完整厂商支持列表(含 API 示例) | +| T3 | 搭建 Web 应用的基础架构(前端 VUE + 后端 SpringWEB) | 中 | 创建 Web 首页和配置页、对话页,切换页面(可使用单页应用) | 开发 | 🟢 项目框架 | +| T4 | 实现厂商下拉选择功能 | 高 | 列出厂商选项,用户可选当前要配置的厂商 | 开发 | 🟢 下拉菜单 | +| T5 | 实现 API 配置输入界面 | 高 | 根据所选厂商显示对应输入项(如密钥 + 接入点) | 开发 | 🟢 动态表单(密钥/接入点输入) | +| T6 | 实现密钥输入验证 | 高 | 检查输入格式是否符合厂商要求(如 OpenAI 密钥以 `sk-` 开头) | 开发 | 🟢 格式校验 + 错误提示 | +| T7 | 实现配置保存与加密存储 | 高 | 加密存储用户输入的密钥和接入点,使用 localStorage 或 indexedDB | 开发 | 🔵 安全存储实现(AES 加密) | +| T8 | 创建厂商配置数据结构 | 中 | 在前端代码中定义厂商配置信息(如 ID、名称、端点、字段等) | 开发 | 🔵 `PLATFORMS` 配置数组(如 JS 对象结构) | +| T9 | 实现对话页界面 | 中 | 输入框 + 厂商选择器 + 发送按钮 + 响应展示区 | 开发 | 🔵 对话页面设计 + 基本交互 | +| T10 | 实现对话 API 调用逻辑 | 高 | 根据用户选择的厂商,使用对应密钥调用 AI API 并返回结果 | 开发 | 🔵 厂商 API 通信逻辑(POST 请求) | +| T11 | 实现响应展示与错误处理 | 高 | 显示 API 返回内容,处理 401、500 等错误信息 | 开发 | 🔵 响应展示模块 + 错误页/提示 | +| T12 | 添加「重试」机制(可选) | 中 | 对于调用失败的情况提供一键重试功能 | 开发 | 🔵 调用失败后支持重试按钮 | +| T13 | 构建配置页与对话页的切换逻辑 | 中 | 用户可切换配置页和对话页,保证数据在页面间同步(如密钥已保存则自动填充) | 开发 | 🔵 页面切换功能 + 自动填充已有配置 | +| T14 | 添加「首次未配置提醒」 | 中 | 如果没有配置任何厂商,引导用户进行配置 | 开发 | 🔵 首页友好提示信息 | +| T15 | 添加厂商配置删除功能 | 中 | 允许用户删除已有配置,避免冗余数据 | 开发 | 🔵 删除配置按钮 + localStorage 清理 | +| T16 | 添加厂商默认配置(可选) | 中 | 预设 OpenAI、通义等厂商的默认接入点和字段提示 | 开发 | 🔵 配置页面默认值设置 | +| T17 | 构建完整 UI 界面(风格统一) | 中 | 确保配置页和对话页操作一致、视觉统一 | UI / 前端 | 🔵 响应式设计 + 现代化界面 | +| T18 | 进行基本可用性测试 | 中 | 验证主要功能是否可用(配置保存、API 调用、错误提示等) | QA | 🔵 功能测试报告(手动/自动) | +| T19 | 添加部署方案(如 GitHub Pages) | 低 | 配置前端静态部署,便于快速访问 | 产品 | 🔵 部署文档 + 示例部署链接 | +| T20 | 撰写项目说明文档和使用教程 | 低 | 提供给用户快速上手的说明,包含配置方法和常见问题 | 产品 | 🔵 README 或使用指南文档 | +| T21 | 优化体验(如加载提示、输入缓存) | 低 | 提升用户体验,如加载动画、输入自动保存(可选) | 开发 | 🔵 更流畅、更友好的 UI 交互 | +| T22 | 收集用户反馈并迭代设计 | 低 | 收集可用性数据、建议,优化方案(可选) | 产品 | 🔵 用户反馈记录 + 迭代计划 | \ No newline at end of file diff --git a/frontend/src/components/ChatPanel.vue b/frontend/src/components/ChatPanel.vue index e0ce462..ffb60c8 100644 --- a/frontend/src/components/ChatPanel.vue +++ b/frontend/src/components/ChatPanel.vue @@ -89,16 +89,17 @@ const loadConfiguredPlatforms = () => { const getPlatformName = (platformId) => { const platforms = [ { id: 'openai', name: 'OpenAI' }, - { id: 'anthropic', name: 'Anthropic' }, - { id: 'google', name: 'Google (Gemini)' }, - { id: 'qwen', name: '通义千问(阿里云)' }, - { id: 'wenxin', name: '文心一言(百度)' } + { id: 'zhipu', name: '智谱' }, + { id: 'deepseek', name: 'DeepSeek' }, + { id: 'qwen', name: '通义千问' }, + { id: 'siliconflow', name: '硅基流动' } ] const platform = platforms.find(p => p.id === platformId) return platform ? platform.name : platformId } // 发送消息 +// TODO 需要将用户配置(端点、密钥)和用户消息发送给后端统一处理 const sendMessage = async () => { if (!canSend.value) return @@ -125,6 +126,7 @@ const sendMessage = async () => { throw new Error('未找到厂商配置') } + // TODO 这里无需解密,应该交由后端解密 // 解密密钥 const apiKey = decrypt(config.apiKey) @@ -142,146 +144,9 @@ const sendMessage = async () => { } // 调用AI平台API +// TODO 此处由后端统一处理 const callAIPlatform = async (platformId, apiKey, endpoint, message) => { - // 根据不同平台调用不同的API - switch (platformId) { - case 'openai': - return await callOpenAI(apiKey, endpoint, message) - case 'anthropic': - return await callAnthropic(apiKey, endpoint, message) - case 'google': - return await callGoogle(apiKey, endpoint, message) - case 'qwen': - return await callQwen(apiKey, endpoint, message) - case 'wenxin': - return await callWenxin(apiKey, endpoint, message) - default: - throw new Error(`不支持的厂商: ${platformId}`) - } -} - -// 调用OpenAI API -const callOpenAI = async (apiKey, endpoint, message) => { - const response = await fetch(endpoint, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${apiKey}` - }, - body: JSON.stringify({ - model: 'gpt-3.5-turbo', - messages: [{ role: 'user', content: message }], - temperature: 0.7 - }) - }) - - if (!response.ok) { - const errorData = await response.json().catch(() => ({})) - throw new Error(`OpenAI API 调用失败 (${response.status}): ${errorData.error?.message || '未知错误'}`) - } - - const data = await response.json() - return data.choices[0]?.message?.content || '未获取到响应内容' -} - -// 调用Anthropic API -const callAnthropic = async (apiKey, endpoint, message) => { - const response = await fetch(endpoint, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'x-api-key': apiKey, - 'anthropic-version': '2023-06-01' - }, - body: JSON.stringify({ - model: 'claude-3-haiku-20240307', - messages: [{ role: 'user', content: message }], - max_tokens: 1024 - }) - }) - - if (!response.ok) { - const errorData = await response.json().catch(() => ({})) - throw new Error(`Anthropic API 调用失败 (${response.status}): ${errorData.error?.message || '未知错误'}`) - } - - const data = await response.json() - return data.content[0]?.text || '未获取到响应内容' -} - -// 调用Google Gemini API -const callGoogle = async (apiKey, endpoint, message) => { - const response = await fetch(`${endpoint}?key=${apiKey}`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - contents: [{ - parts: [ - { text: message } - ] - }] - }) - }) - - if (!response.ok) { - const errorData = await response.json().catch(() => ({})) - throw new Error(`Google API 调用失败 (${response.status}): ${errorData.error?.message || '未知错误'}`) - } - - const data = await response.json() - return data.candidates[0]?.content?.parts[0]?.text || '未获取到响应内容' -} - -// 调用通义千问API -const callQwen = async (apiKey, endpoint, message) => { - const response = await fetch(endpoint, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${apiKey}`, - 'X-DashScope-Scene': 'chat' - }, - body: JSON.stringify({ - model: 'qwen-plus', - input: { - messages: [{ role: 'user', content: message }] - }, - parameters: { - result_format: 'message' - } - }) - }) - - if (!response.ok) { - const errorData = await response.json().catch(() => ({})) - throw new Error(`通义千问 API 调用失败 (${response.status}): ${errorData.error?.message || '未知错误'}`) - } - - const data = await response.json() - return data.output?.choices[0]?.message?.content || '未获取到响应内容' -} - -// 调用文心一言API -const callWenxin = async (apiKey, endpoint, message) => { - const response = await fetch(`${endpoint}?access_token=${apiKey}`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - messages: [{ role: 'user', content: message }] - }) - }) - - if (!response.ok) { - const errorData = await response.json().catch(() => ({})) - throw new Error(`文心一言 API 调用失败 (${response.status}): ${errorData.error?.message || '未知错误'}`) - } - - const data = await response.json() - return data.result || '未获取到响应内容' + // TODO 待完善 } // 初始化 diff --git a/frontend/src/components/ConfigPanel.vue b/frontend/src/components/ConfigPanel.vue index 449d32a..173ae26 100644 --- a/frontend/src/components/ConfigPanel.vue +++ b/frontend/src/components/ConfigPanel.vue @@ -36,6 +36,7 @@ +