代码优化:清理冗余功能并修复关键问题

- 删除未使用的 axios 依赖
- 移除 Provider 接口中未使用的 model 字段
- 删除未使用的供应商连通性检查功能
- 修复 preload.ts 中缺失的 IPC 方法暴露
- 简化 UI:移除重复的单选按钮,仅保留启用按钮
- 修复 TypeScript 接口定义不完整的问题
This commit is contained in:
farion1231
2025-08-06 20:48:03 +08:00
parent dbafab57cf
commit 588883ffc4
6 changed files with 116 additions and 126 deletions

View File

@@ -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"

View File

@@ -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)

View File

@@ -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'),

View File

@@ -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,25 +161,31 @@ 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 && (
<div className="config-path">
<span>: {configPath}</span>
<button
className="browse-btn"
<button
className="browse-btn"
onClick={handleSelectConfigFile}
title="浏览选择配置文件"
>
@@ -201,7 +220,7 @@ function App() {
/>
)}
</div>
)
);
}
export default App
export default App;

View File

@@ -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>

View File

@@ -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>
}
}