From 588883ffc405976e6a9bceb9773fe8736a2bdece Mon Sep 17 00:00:00 2001 From: farion1231 Date: Wed, 6 Aug 2025 20:48:03 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96=EF=BC=9A?= =?UTF-8?q?=E6=B8=85=E7=90=86=E5=86=97=E4=BD=99=E5=8A=9F=E8=83=BD=E5=B9=B6?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=85=B3=E9=94=AE=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 删除未使用的 axios 依赖 - 移除 Provider 接口中未使用的 model 字段 - 删除未使用的供应商连通性检查功能 - 修复 preload.ts 中缺失的 IPC 方法暴露 - 简化 UI:移除重复的单选按钮,仅保留启用按钮 - 修复 TypeScript 接口定义不完整的问题 --- package.json | 1 - src/main/index.ts | 19 -- src/main/preload.ts | 1 - src/renderer/App.tsx | 213 ++++++++++++----------- src/renderer/components/ProviderList.tsx | 6 - src/shared/types.ts | 2 - 6 files changed, 116 insertions(+), 126 deletions(-) diff --git a/package.json b/package.json index 53df9eb..e53ec56 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,6 @@ "vite": "^5.0.0" }, "dependencies": { - "axios": "^1.6.0", "electron-store": "^8.1.0", "react": "^18.2.0", "react-dom": "^18.2.0" diff --git a/src/main/index.ts b/src/main/index.ts index 9e0739d..b83e801 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -128,25 +128,6 @@ ipcMain.handle('selectConfigFile', async () => { return result.filePaths[0] }) -ipcMain.handle('checkStatus', async (_, provider: Provider) => { - // 简单的连通性检查 - 向API地址发送HEAD请求 - try { - const controller = new AbortController() - const timeoutId = setTimeout(() => controller.abort(), 5000) - - const response = await fetch(provider.apiUrl, { - method: 'HEAD', - signal: controller.signal - }) - - clearTimeout(timeoutId) - return response.ok - } catch (error) { - console.error('检查供应商状态失败:', error) - return false - } -}) - ipcMain.handle('openExternal', async (_, url: string) => { try { await shell.openExternal(url) diff --git a/src/main/preload.ts b/src/main/preload.ts index ec3ab67..7523054 100644 --- a/src/main/preload.ts +++ b/src/main/preload.ts @@ -7,7 +7,6 @@ contextBridge.exposeInMainWorld('electronAPI', { addProvider: (provider: Provider) => ipcRenderer.invoke('addProvider', provider), deleteProvider: (id: string) => ipcRenderer.invoke('deleteProvider', id), updateProvider: (provider: Provider) => ipcRenderer.invoke('updateProvider', provider), - checkStatus: (provider: Provider) => ipcRenderer.invoke('checkStatus', provider), switchProvider: (providerId: string) => ipcRenderer.invoke('switchProvider', providerId), getClaudeCodeConfigPath: () => ipcRenderer.invoke('getClaudeCodeConfigPath'), selectConfigFile: () => ipcRenderer.invoke('selectConfigFile'), diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index c67f3f3..dfd1fca 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -1,144 +1,157 @@ -import { useState, useEffect, useRef } from 'react' -import { Provider } from '../shared/types' -import ProviderList from './components/ProviderList' -import AddProviderModal from './components/AddProviderModal' -import EditProviderModal from './components/EditProviderModal' -import { ConfirmDialog } from './components/ConfirmDialog' -import './App.css' +import { useState, useEffect, useRef } from "react"; +import { Provider } from "../shared/types"; +import ProviderList from "./components/ProviderList"; +import AddProviderModal from "./components/AddProviderModal"; +import EditProviderModal from "./components/EditProviderModal"; +import { ConfirmDialog } from "./components/ConfirmDialog"; +import "./App.css"; function App() { - const [providers, setProviders] = useState>({}) - const [currentProviderId, setCurrentProviderId] = useState('') - const [isAddModalOpen, setIsAddModalOpen] = useState(false) - const [configPath, setConfigPath] = useState('') - const [editingProviderId, setEditingProviderId] = useState(null) - const [notification, setNotification] = useState<{ message: string; type: 'success' | 'error' } | null>(null) - const [isNotificationVisible, setIsNotificationVisible] = useState(false) - const [confirmDialog, setConfirmDialog] = useState<{ isOpen: boolean; title: string; message: string; onConfirm: () => void } | null>(null) - const timeoutRef = useRef(null) + const [providers, setProviders] = useState>({}); + const [currentProviderId, setCurrentProviderId] = useState(""); + const [isAddModalOpen, setIsAddModalOpen] = useState(false); + const [configPath, setConfigPath] = useState(""); + const [editingProviderId, setEditingProviderId] = useState( + null + ); + const [notification, setNotification] = useState<{ + message: string; + type: "success" | "error"; + } | null>(null); + const [isNotificationVisible, setIsNotificationVisible] = useState(false); + const [confirmDialog, setConfirmDialog] = useState<{ + isOpen: boolean; + title: string; + message: string; + onConfirm: () => void; + } | null>(null); + const timeoutRef = useRef(null); // 设置通知的辅助函数 - const showNotification = (message: string, type: 'success' | 'error', duration = 3000) => { + const showNotification = ( + message: string, + type: "success" | "error", + duration = 3000 + ) => { // 清除之前的定时器 if (timeoutRef.current) { - clearTimeout(timeoutRef.current) + clearTimeout(timeoutRef.current); } - + // 立即显示通知 - setNotification({ message, type }) - setIsNotificationVisible(true) - + setNotification({ message, type }); + setIsNotificationVisible(true); + // 设置淡出定时器 timeoutRef.current = setTimeout(() => { - setIsNotificationVisible(false) + setIsNotificationVisible(false); // 等待淡出动画完成后清除通知 setTimeout(() => { - setNotification(null) - timeoutRef.current = null - }, 300) // 与CSS动画时间匹配 - }, duration) - } + setNotification(null); + timeoutRef.current = null; + }, 300); // 与CSS动画时间匹配 + }, duration); + }; // 加载供应商列表 useEffect(() => { - loadProviders() - loadConfigPath() - }, []) + loadProviders(); + loadConfigPath(); + }, []); // 清理定时器 useEffect(() => { return () => { if (timeoutRef.current) { - clearTimeout(timeoutRef.current) + clearTimeout(timeoutRef.current); } - } - }, []) - + }; + }, []); const loadProviders = async () => { - const loadedProviders = await window.electronAPI.getProviders() - const currentId = await window.electronAPI.getCurrentProvider() - setProviders(loadedProviders) - setCurrentProviderId(currentId) - } + const loadedProviders = await window.electronAPI.getProviders(); + const currentId = await window.electronAPI.getCurrentProvider(); + setProviders(loadedProviders); + setCurrentProviderId(currentId); + }; const loadConfigPath = async () => { - const path = await window.electronAPI.getClaudeCodeConfigPath() - setConfigPath(path) - } - + const path = await window.electronAPI.getClaudeCodeConfigPath(); + setConfigPath(path); + }; // 生成唯一ID const generateId = () => { - return Date.now().toString(36) + Math.random().toString(36).substr(2, 9) - } + return Date.now().toString(36) + Math.random().toString(36).substr(2, 9); + }; - const handleAddProvider = async (provider: Omit) => { + const handleAddProvider = async (provider: Omit) => { const newProvider: Provider = { ...provider, - id: generateId() - } - await window.electronAPI.addProvider(newProvider) - await loadProviders() - setIsAddModalOpen(false) - } + id: generateId(), + }; + await window.electronAPI.addProvider(newProvider); + await loadProviders(); + setIsAddModalOpen(false); + }; const handleDeleteProvider = async (id: string) => { - const provider = providers[id] + const provider = providers[id]; setConfirmDialog({ isOpen: true, - title: '删除供应商', + title: "删除供应商", message: `确定要删除供应商 "${provider?.name}" 吗?此操作无法撤销。`, onConfirm: async () => { - await window.electronAPI.deleteProvider(id) - await loadProviders() - setConfirmDialog(null) - showNotification('供应商删除成功', 'success') - } - }) - } + await window.electronAPI.deleteProvider(id); + await loadProviders(); + setConfirmDialog(null); + showNotification("供应商删除成功", "success"); + }, + }); + }; const handleSwitchProvider = async (id: string) => { - const success = await window.electronAPI.switchProvider(id) + const success = await window.electronAPI.switchProvider(id); if (success) { - setCurrentProviderId(id) - // 显示重启提示,时间更长 - showNotification('切换成功!请重启 Claude Code 终端以生效', 'success', 4000) + setCurrentProviderId(id); + // 显示重启提示 + showNotification( + "切换成功!请重启 Claude Code 终端以生效", + "success", + 2000 + ); } else { - showNotification('切换失败,请检查配置', 'error') + showNotification("切换失败,请检查配置", "error"); } - } + }; const handleEditProvider = async (provider: Provider) => { try { - await window.electronAPI.updateProvider(provider) - await loadProviders() - setEditingProviderId(null) - // 显示编辑成功提示,时间较短 - showNotification('供应商配置已保存', 'success', 2000) + await window.electronAPI.updateProvider(provider); + await loadProviders(); + setEditingProviderId(null); + // 显示编辑成功提示 + showNotification("供应商配置已保存", "success", 2000); } catch (error) { - console.error('更新供应商失败:', error) - setEditingProviderId(null) - showNotification('保存失败,请重试', 'error') + console.error("更新供应商失败:", error); + setEditingProviderId(null); + showNotification("保存失败,请重试", "error"); } - } + }; const handleSelectConfigFile = async () => { - const selectedPath = await window.electronAPI.selectConfigFile() + const selectedPath = await window.electronAPI.selectConfigFile(); if (selectedPath) { - setConfigPath(selectedPath) + setConfigPath(selectedPath); } - } + }; return (

Claude Code 供应商切换器

-
@@ -148,25 +161,31 @@ function App() {
{/* 浮动通知组件 */} {notification && ( -
+
{notification.message}
)} - + + providers={providers} + currentProviderId={currentProviderId} + onSwitch={handleSwitchProvider} + onDelete={handleDeleteProvider} + onEdit={setEditingProviderId} + />
- + {configPath && (
配置文件位置: {configPath} -
- ) + ); } -export default App \ No newline at end of file +export default App; diff --git a/src/renderer/components/ProviderList.tsx b/src/renderer/components/ProviderList.tsx index 373caa3..b102978 100644 --- a/src/renderer/components/ProviderList.tsx +++ b/src/renderer/components/ProviderList.tsx @@ -44,12 +44,6 @@ const ProviderList: React.FC = ({ >
- onSwitch(provider.id)} - /> {provider.name} {isCurrent && 当前使用}
diff --git a/src/shared/types.ts b/src/shared/types.ts index 7f896b6..dfb5124 100644 --- a/src/shared/types.ts +++ b/src/shared/types.ts @@ -3,7 +3,6 @@ export interface Provider { name: string apiUrl: string apiKey: string - model?: string websiteUrl?: string } @@ -23,7 +22,6 @@ declare global { switchProvider: (providerId: string) => Promise getClaudeCodeConfigPath: () => Promise selectConfigFile: () => Promise - checkStatus: (provider: Provider) => Promise openExternal: (url: string) => Promise } }