代码优化:清理冗余功能并修复关键问题
- 删除未使用的 axios 依赖 - 移除 Provider 接口中未使用的 model 字段 - 删除未使用的供应商连通性检查功能 - 修复 preload.ts 中缺失的 IPC 方法暴露 - 简化 UI:移除重复的单选按钮,仅保留启用按钮 - 修复 TypeScript 接口定义不完整的问题
This commit is contained in:
@@ -30,7 +30,6 @@
|
|||||||
"vite": "^5.0.0"
|
"vite": "^5.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.6.0",
|
|
||||||
"electron-store": "^8.1.0",
|
"electron-store": "^8.1.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0"
|
"react-dom": "^18.2.0"
|
||||||
|
|||||||
@@ -128,25 +128,6 @@ ipcMain.handle('selectConfigFile', async () => {
|
|||||||
return result.filePaths[0]
|
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) => {
|
ipcMain.handle('openExternal', async (_, url: string) => {
|
||||||
try {
|
try {
|
||||||
await shell.openExternal(url)
|
await shell.openExternal(url)
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
|||||||
addProvider: (provider: Provider) => ipcRenderer.invoke('addProvider', provider),
|
addProvider: (provider: Provider) => ipcRenderer.invoke('addProvider', provider),
|
||||||
deleteProvider: (id: string) => ipcRenderer.invoke('deleteProvider', id),
|
deleteProvider: (id: string) => ipcRenderer.invoke('deleteProvider', id),
|
||||||
updateProvider: (provider: Provider) => ipcRenderer.invoke('updateProvider', provider),
|
updateProvider: (provider: Provider) => ipcRenderer.invoke('updateProvider', provider),
|
||||||
checkStatus: (provider: Provider) => ipcRenderer.invoke('checkStatus', provider),
|
|
||||||
switchProvider: (providerId: string) => ipcRenderer.invoke('switchProvider', providerId),
|
switchProvider: (providerId: string) => ipcRenderer.invoke('switchProvider', providerId),
|
||||||
getClaudeCodeConfigPath: () => ipcRenderer.invoke('getClaudeCodeConfigPath'),
|
getClaudeCodeConfigPath: () => ipcRenderer.invoke('getClaudeCodeConfigPath'),
|
||||||
selectConfigFile: () => ipcRenderer.invoke('selectConfigFile'),
|
selectConfigFile: () => ipcRenderer.invoke('selectConfigFile'),
|
||||||
|
|||||||
@@ -1,144 +1,157 @@
|
|||||||
import { useState, useEffect, useRef } from 'react'
|
import { useState, useEffect, useRef } from "react";
|
||||||
import { Provider } from '../shared/types'
|
import { Provider } from "../shared/types";
|
||||||
import ProviderList from './components/ProviderList'
|
import ProviderList from "./components/ProviderList";
|
||||||
import AddProviderModal from './components/AddProviderModal'
|
import AddProviderModal from "./components/AddProviderModal";
|
||||||
import EditProviderModal from './components/EditProviderModal'
|
import EditProviderModal from "./components/EditProviderModal";
|
||||||
import { ConfirmDialog } from './components/ConfirmDialog'
|
import { ConfirmDialog } from "./components/ConfirmDialog";
|
||||||
import './App.css'
|
import "./App.css";
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [providers, setProviders] = useState<Record<string, Provider>>({})
|
const [providers, setProviders] = useState<Record<string, Provider>>({});
|
||||||
const [currentProviderId, setCurrentProviderId] = useState<string>('')
|
const [currentProviderId, setCurrentProviderId] = useState<string>("");
|
||||||
const [isAddModalOpen, setIsAddModalOpen] = useState(false)
|
const [isAddModalOpen, setIsAddModalOpen] = useState(false);
|
||||||
const [configPath, setConfigPath] = useState<string>('')
|
const [configPath, setConfigPath] = useState<string>("");
|
||||||
const [editingProviderId, setEditingProviderId] = useState<string | null>(null)
|
const [editingProviderId, setEditingProviderId] = useState<string | null>(
|
||||||
const [notification, setNotification] = useState<{ message: string; type: 'success' | 'error' } | null>(null)
|
null
|
||||||
const [isNotificationVisible, setIsNotificationVisible] = useState(false)
|
);
|
||||||
const [confirmDialog, setConfirmDialog] = useState<{ isOpen: boolean; title: string; message: string; onConfirm: () => void } | null>(null)
|
const [notification, setNotification] = useState<{
|
||||||
const timeoutRef = useRef<NodeJS.Timeout | null>(null)
|
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) {
|
if (timeoutRef.current) {
|
||||||
clearTimeout(timeoutRef.current)
|
clearTimeout(timeoutRef.current);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 立即显示通知
|
// 立即显示通知
|
||||||
setNotification({ message, type })
|
setNotification({ message, type });
|
||||||
setIsNotificationVisible(true)
|
setIsNotificationVisible(true);
|
||||||
|
|
||||||
// 设置淡出定时器
|
// 设置淡出定时器
|
||||||
timeoutRef.current = setTimeout(() => {
|
timeoutRef.current = setTimeout(() => {
|
||||||
setIsNotificationVisible(false)
|
setIsNotificationVisible(false);
|
||||||
// 等待淡出动画完成后清除通知
|
// 等待淡出动画完成后清除通知
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setNotification(null)
|
setNotification(null);
|
||||||
timeoutRef.current = null
|
timeoutRef.current = null;
|
||||||
}, 300) // 与CSS动画时间匹配
|
}, 300); // 与CSS动画时间匹配
|
||||||
}, duration)
|
}, duration);
|
||||||
}
|
};
|
||||||
|
|
||||||
// 加载供应商列表
|
// 加载供应商列表
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadProviders()
|
loadProviders();
|
||||||
loadConfigPath()
|
loadConfigPath();
|
||||||
}, [])
|
}, []);
|
||||||
|
|
||||||
// 清理定时器
|
// 清理定时器
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return () => {
|
return () => {
|
||||||
if (timeoutRef.current) {
|
if (timeoutRef.current) {
|
||||||
clearTimeout(timeoutRef.current)
|
clearTimeout(timeoutRef.current);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}, [])
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
const loadProviders = async () => {
|
const loadProviders = async () => {
|
||||||
const loadedProviders = await window.electronAPI.getProviders()
|
const loadedProviders = await window.electronAPI.getProviders();
|
||||||
const currentId = await window.electronAPI.getCurrentProvider()
|
const currentId = await window.electronAPI.getCurrentProvider();
|
||||||
setProviders(loadedProviders)
|
setProviders(loadedProviders);
|
||||||
setCurrentProviderId(currentId)
|
setCurrentProviderId(currentId);
|
||||||
}
|
};
|
||||||
|
|
||||||
const loadConfigPath = async () => {
|
const loadConfigPath = async () => {
|
||||||
const path = await window.electronAPI.getClaudeCodeConfigPath()
|
const path = await window.electronAPI.getClaudeCodeConfigPath();
|
||||||
setConfigPath(path)
|
setConfigPath(path);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
// 生成唯一ID
|
// 生成唯一ID
|
||||||
const generateId = () => {
|
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 = {
|
const newProvider: Provider = {
|
||||||
...provider,
|
...provider,
|
||||||
id: generateId()
|
id: generateId(),
|
||||||
}
|
};
|
||||||
await window.electronAPI.addProvider(newProvider)
|
await window.electronAPI.addProvider(newProvider);
|
||||||
await loadProviders()
|
await loadProviders();
|
||||||
setIsAddModalOpen(false)
|
setIsAddModalOpen(false);
|
||||||
}
|
};
|
||||||
|
|
||||||
const handleDeleteProvider = async (id: string) => {
|
const handleDeleteProvider = async (id: string) => {
|
||||||
const provider = providers[id]
|
const provider = providers[id];
|
||||||
setConfirmDialog({
|
setConfirmDialog({
|
||||||
isOpen: true,
|
isOpen: true,
|
||||||
title: '删除供应商',
|
title: "删除供应商",
|
||||||
message: `确定要删除供应商 "${provider?.name}" 吗?此操作无法撤销。`,
|
message: `确定要删除供应商 "${provider?.name}" 吗?此操作无法撤销。`,
|
||||||
onConfirm: async () => {
|
onConfirm: async () => {
|
||||||
await window.electronAPI.deleteProvider(id)
|
await window.electronAPI.deleteProvider(id);
|
||||||
await loadProviders()
|
await loadProviders();
|
||||||
setConfirmDialog(null)
|
setConfirmDialog(null);
|
||||||
showNotification('供应商删除成功', 'success')
|
showNotification("供应商删除成功", "success");
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
const handleSwitchProvider = async (id: string) => {
|
const handleSwitchProvider = async (id: string) => {
|
||||||
const success = await window.electronAPI.switchProvider(id)
|
const success = await window.electronAPI.switchProvider(id);
|
||||||
if (success) {
|
if (success) {
|
||||||
setCurrentProviderId(id)
|
setCurrentProviderId(id);
|
||||||
// 显示重启提示,时间更长
|
// 显示重启提示
|
||||||
showNotification('切换成功!请重启 Claude Code 终端以生效', 'success', 4000)
|
showNotification(
|
||||||
|
"切换成功!请重启 Claude Code 终端以生效",
|
||||||
|
"success",
|
||||||
|
2000
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
showNotification('切换失败,请检查配置', 'error')
|
showNotification("切换失败,请检查配置", "error");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleEditProvider = async (provider: Provider) => {
|
const handleEditProvider = async (provider: Provider) => {
|
||||||
try {
|
try {
|
||||||
await window.electronAPI.updateProvider(provider)
|
await window.electronAPI.updateProvider(provider);
|
||||||
await loadProviders()
|
await loadProviders();
|
||||||
setEditingProviderId(null)
|
setEditingProviderId(null);
|
||||||
// 显示编辑成功提示,时间较短
|
// 显示编辑成功提示
|
||||||
showNotification('供应商配置已保存', 'success', 2000)
|
showNotification("供应商配置已保存", "success", 2000);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('更新供应商失败:', error)
|
console.error("更新供应商失败:", error);
|
||||||
setEditingProviderId(null)
|
setEditingProviderId(null);
|
||||||
showNotification('保存失败,请重试', 'error')
|
showNotification("保存失败,请重试", "error");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleSelectConfigFile = async () => {
|
const handleSelectConfigFile = async () => {
|
||||||
const selectedPath = await window.electronAPI.selectConfigFile()
|
const selectedPath = await window.electronAPI.selectConfigFile();
|
||||||
if (selectedPath) {
|
if (selectedPath) {
|
||||||
setConfigPath(selectedPath)
|
setConfigPath(selectedPath);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="app">
|
<div className="app">
|
||||||
<header className="app-header">
|
<header className="app-header">
|
||||||
<h1>Claude Code 供应商切换器</h1>
|
<h1>Claude Code 供应商切换器</h1>
|
||||||
<div className="header-actions">
|
<div className="header-actions">
|
||||||
<button
|
<button className="add-btn" onClick={() => setIsAddModalOpen(true)}>
|
||||||
className="add-btn"
|
|
||||||
onClick={() => setIsAddModalOpen(true)}
|
|
||||||
>
|
|
||||||
添加供应商
|
添加供应商
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -148,7 +161,13 @@ function App() {
|
|||||||
<div className="provider-section">
|
<div className="provider-section">
|
||||||
{/* 浮动通知组件 */}
|
{/* 浮动通知组件 */}
|
||||||
{notification && (
|
{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}
|
{notification.message}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -201,7 +220,7 @@ function App() {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default App
|
export default App;
|
||||||
|
|||||||
@@ -44,12 +44,6 @@ const ProviderList: React.FC<ProviderListProps> = ({
|
|||||||
>
|
>
|
||||||
<div className="provider-info">
|
<div className="provider-info">
|
||||||
<div className="provider-name">
|
<div className="provider-name">
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="provider"
|
|
||||||
checked={isCurrent}
|
|
||||||
onChange={() => onSwitch(provider.id)}
|
|
||||||
/>
|
|
||||||
<span>{provider.name}</span>
|
<span>{provider.name}</span>
|
||||||
{isCurrent && <span className="current-badge">当前使用</span>}
|
{isCurrent && <span className="current-badge">当前使用</span>}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ export interface Provider {
|
|||||||
name: string
|
name: string
|
||||||
apiUrl: string
|
apiUrl: string
|
||||||
apiKey: string
|
apiKey: string
|
||||||
model?: string
|
|
||||||
websiteUrl?: string
|
websiteUrl?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,7 +22,6 @@ declare global {
|
|||||||
switchProvider: (providerId: string) => Promise<boolean>
|
switchProvider: (providerId: string) => Promise<boolean>
|
||||||
getClaudeCodeConfigPath: () => Promise<string>
|
getClaudeCodeConfigPath: () => Promise<string>
|
||||||
selectConfigFile: () => Promise<string | null>
|
selectConfigFile: () => Promise<string | null>
|
||||||
checkStatus: (provider: Provider) => Promise<boolean>
|
|
||||||
openExternal: (url: string) => Promise<boolean>
|
openExternal: (url: string) => Promise<boolean>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user