代码优化:清理冗余功能并修复关键问题
- 删除未使用的 axios 依赖 - 移除 Provider 接口中未使用的 model 字段 - 删除未使用的供应商连通性检查功能 - 修复 preload.ts 中缺失的 IPC 方法暴露 - 简化 UI:移除重复的单选按钮,仅保留启用按钮 - 修复 TypeScript 接口定义不完整的问题
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -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<Record<string, Provider>>({})
|
||||
const [currentProviderId, setCurrentProviderId] = useState<string>('')
|
||||
const [isAddModalOpen, setIsAddModalOpen] = useState(false)
|
||||
const [configPath, setConfigPath] = useState<string>('')
|
||||
const [editingProviderId, setEditingProviderId] = useState<string | null>(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<NodeJS.Timeout | null>(null)
|
||||
const [providers, setProviders] = useState<Record<string, Provider>>({});
|
||||
const [currentProviderId, setCurrentProviderId] = useState<string>("");
|
||||
const [isAddModalOpen, setIsAddModalOpen] = useState(false);
|
||||
const [configPath, setConfigPath] = useState<string>("");
|
||||
const [editingProviderId, setEditingProviderId] = useState<string | null>(
|
||||
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<NodeJS.Timeout | null>(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<Provider, 'id'>) => {
|
||||
const handleAddProvider = async (provider: Omit<Provider, "id">) => {
|
||||
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 (
|
||||
<div className="app">
|
||||
<header className="app-header">
|
||||
<h1>Claude Code 供应商切换器</h1>
|
||||
<div className="header-actions">
|
||||
<button
|
||||
className="add-btn"
|
||||
onClick={() => setIsAddModalOpen(true)}
|
||||
>
|
||||
<button className="add-btn" onClick={() => setIsAddModalOpen(true)}>
|
||||
添加供应商
|
||||
</button>
|
||||
</div>
|
||||
@@ -148,18 +161,24 @@ function App() {
|
||||
<div className="provider-section">
|
||||
{/* 浮动通知组件 */}
|
||||
{notification && (
|
||||
<div className={`notification-floating ${notification.type === 'error' ? 'notification-error' : 'notification-success'} ${isNotificationVisible ? 'fade-in' : 'fade-out'}`}>
|
||||
<div
|
||||
className={`notification-floating ${
|
||||
notification.type === "error"
|
||||
? "notification-error"
|
||||
: "notification-success"
|
||||
} ${isNotificationVisible ? "fade-in" : "fade-out"}`}
|
||||
>
|
||||
{notification.message}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<ProviderList
|
||||
providers={providers}
|
||||
currentProviderId={currentProviderId}
|
||||
onSwitch={handleSwitchProvider}
|
||||
onDelete={handleDeleteProvider}
|
||||
onEdit={setEditingProviderId}
|
||||
/>
|
||||
providers={providers}
|
||||
currentProviderId={currentProviderId}
|
||||
onSwitch={handleSwitchProvider}
|
||||
onDelete={handleDeleteProvider}
|
||||
onEdit={setEditingProviderId}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{configPath && (
|
||||
@@ -201,7 +220,7 @@ function App() {
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export default App
|
||||
export default App;
|
||||
|
||||
@@ -44,12 +44,6 @@ const ProviderList: React.FC<ProviderListProps> = ({
|
||||
>
|
||||
<div className="provider-info">
|
||||
<div className="provider-name">
|
||||
<input
|
||||
type="radio"
|
||||
name="provider"
|
||||
checked={isCurrent}
|
||||
onChange={() => onSwitch(provider.id)}
|
||||
/>
|
||||
<span>{provider.name}</span>
|
||||
{isCurrent && <span className="current-badge">当前使用</span>}
|
||||
</div>
|
||||
|
||||
@@ -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<boolean>
|
||||
getClaudeCodeConfigPath: () => Promise<string>
|
||||
selectConfigFile: () => Promise<string | null>
|
||||
checkStatus: (provider: Provider) => Promise<boolean>
|
||||
openExternal: (url: string) => Promise<boolean>
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user