From 7ffd03e039177f05514f50e797120072583c2c29 Mon Sep 17 00:00:00 2001 From: farion1231 Date: Tue, 5 Aug 2025 09:51:41 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=BE=9B=E5=BA=94=E5=95=86?= =?UTF-8?q?=E7=BC=96=E8=BE=91=E5=8A=9F=E8=83=BD=E5=92=8C=E5=AF=86=E7=A0=81?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E5=88=87=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 为供应商列表添加启用和编辑按钮 - 创建EditProviderModal组件支持编辑供应商信息 - 实现updateProvider API接口 - 为API Key输入框添加密码显示/隐藏功能,使用SVG图标 - 更新预设供应商配置为YesCode和PackyCode - 移除model字段,简化供应商配置 --- src/main/preload.ts | 1 + src/renderer/App.tsx | 23 +++ src/renderer/components/AddProviderModal.css | 48 +++++++ src/renderer/components/AddProviderModal.tsx | 68 +++++---- src/renderer/components/EditProviderModal.tsx | 135 ++++++++++++++++++ src/renderer/components/ProviderList.css | 37 +++++ src/renderer/components/ProviderList.tsx | 17 ++- src/shared/types.ts | 1 + 8 files changed, 298 insertions(+), 32 deletions(-) create mode 100644 src/renderer/components/EditProviderModal.tsx diff --git a/src/main/preload.ts b/src/main/preload.ts index 8bbd260..6854a8e 100644 --- a/src/main/preload.ts +++ b/src/main/preload.ts @@ -6,6 +6,7 @@ contextBridge.exposeInMainWorld('electronAPI', { getCurrentProvider: () => ipcRenderer.invoke('getCurrentProvider'), 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') diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index 473dd0b..212847f 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -2,6 +2,7 @@ import React, { useState, useEffect } from 'react' import { Provider, ProviderStatus } from '../shared/types' import ProviderList from './components/ProviderList' import AddProviderModal from './components/AddProviderModal' +import EditProviderModal from './components/EditProviderModal' import './App.css' function App() { @@ -11,6 +12,7 @@ function App() { const [isAddModalOpen, setIsAddModalOpen] = useState(false) const [isRefreshing, setIsRefreshing] = useState(false) const [configPath, setConfigPath] = useState('') + const [editingProviderId, setEditingProviderId] = useState(null) // 加载供应商列表 useEffect(() => { @@ -81,6 +83,18 @@ function App() { } } + const handleEditProvider = async (provider: Provider) => { + try { + await window.electronAPI.updateProvider(provider) + await loadProviders() + setEditingProviderId(null) + alert('保存成功!') + } catch (error) { + console.error('更新供应商失败:', error) + alert('保存失败,请重试') + } + } + return (
@@ -109,6 +123,7 @@ function App() { statuses={statuses} onSwitch={handleSwitchProvider} onDelete={handleDeleteProvider} + onEdit={setEditingProviderId} /> {configPath && ( @@ -124,6 +139,14 @@ function App() { onClose={() => setIsAddModalOpen(false)} /> )} + + {editingProviderId && providers[editingProviderId] && ( + setEditingProviderId(null)} + /> + )}
) } diff --git a/src/renderer/components/AddProviderModal.css b/src/renderer/components/AddProviderModal.css index 1bc5966..f1bb6c4 100644 --- a/src/renderer/components/AddProviderModal.css +++ b/src/renderer/components/AddProviderModal.css @@ -20,6 +20,8 @@ max-height: 90vh; overflow-y: auto; box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2); + position: relative; + z-index: 1001; } .modal-content h2 { @@ -79,6 +81,9 @@ border-radius: 4px; font-size: 0.95rem; transition: border-color 0.2s; + position: relative; + z-index: 1; + background: white; } .form-group input:focus { @@ -119,4 +124,47 @@ .submit-btn:hover { background: #229954; +} + +.password-input-wrapper { + position: relative; + display: flex; + align-items: center; +} + +.password-input-wrapper input { + padding-right: 2.5rem; +} + +.password-toggle { + position: absolute; + right: 0.5rem; + top: 50%; + transform: translateY(-50%); + padding: 0.375rem; + background: none; + border: none; + cursor: pointer; + color: #7f8c8d; + transition: color 0.2s; + display: flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + z-index: 2; +} + +.password-toggle svg { + width: 20px; + height: 20px; + stroke-width: 2; +} + +.password-toggle:hover { + color: #3498db; +} + +.password-toggle:focus { + outline: none; } \ No newline at end of file diff --git a/src/renderer/components/AddProviderModal.tsx b/src/renderer/components/AddProviderModal.tsx index 2102a5d..6d79af3 100644 --- a/src/renderer/components/AddProviderModal.tsx +++ b/src/renderer/components/AddProviderModal.tsx @@ -11,9 +11,9 @@ const AddProviderModal: React.FC = ({ onAdd, onClose }) = const [formData, setFormData] = useState({ name: '', apiUrl: '', - apiKey: '', - model: 'claude-3-opus-20240229' + apiKey: '' }) + const [showPassword, setShowPassword] = useState(false) const handleSubmit = (e: React.FormEvent) => { e.preventDefault() @@ -36,14 +36,12 @@ const AddProviderModal: React.FC = ({ onAdd, onClose }) = // 预设的供应商配置 const presets = [ { - name: '官方 Anthropic', - apiUrl: 'https://api.anthropic.com', - model: 'claude-3-opus-20240229' + name: 'YesCode', + apiUrl: 'https://co.yes.vg' }, { - name: 'OpenRouter', - apiUrl: 'https://openrouter.ai/api/v1', - model: 'anthropic/claude-3-opus' + name: 'PackyCode', + apiUrl: 'https://api.packycode.com' } ] @@ -51,8 +49,7 @@ const AddProviderModal: React.FC = ({ onAdd, onClose }) = setFormData({ ...formData, name: preset.name, - apiUrl: preset.apiUrl, - model: preset.model + apiUrl: preset.apiUrl }) } @@ -106,27 +103,36 @@ const AddProviderModal: React.FC = ({ onAdd, onClose }) =
- -
- -
- - +
+ + +
diff --git a/src/renderer/components/EditProviderModal.tsx b/src/renderer/components/EditProviderModal.tsx new file mode 100644 index 0000000..75033e7 --- /dev/null +++ b/src/renderer/components/EditProviderModal.tsx @@ -0,0 +1,135 @@ +import React, { useState, useEffect } from 'react' +import { Provider } from '../../shared/types' +import './AddProviderModal.css' + +interface EditProviderModalProps { + provider: Provider + onSave: (provider: Provider) => void + onClose: () => void +} + +const EditProviderModal: React.FC = ({ provider, onSave, onClose }) => { + const [formData, setFormData] = useState({ + name: provider.name, + apiUrl: provider.apiUrl, + apiKey: provider.apiKey + }) + const [showPassword, setShowPassword] = useState(false) + + useEffect(() => { + setFormData({ + name: provider.name, + apiUrl: provider.apiUrl, + apiKey: provider.apiKey + }) + }, [provider]) + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault() + console.log('提交表单,当前数据:', formData) + + if (!formData.name || !formData.apiUrl || !formData.apiKey) { + alert('请填写所有必填字段') + return + } + + console.log('调用 onSave,provider:', provider, 'formData:', formData) + onSave({ + ...provider, + ...formData + }) + } + + const handleChange = (e: React.ChangeEvent) => { + const { name, value } = e.target + console.log('输入变化:', name, value) + setFormData(prev => ({ + ...prev, + [name]: value + })) + } + + return ( +
+
e.stopPropagation()}> +

编辑供应商

+ +
+
+ + +
+ +
+ + +
+ +
+ +
+ + +
+
+ +
+ + +
+
+
+
+ ) +} + +export default EditProviderModal \ No newline at end of file diff --git a/src/renderer/components/ProviderList.css b/src/renderer/components/ProviderList.css index 1ebb102..b1507cb 100644 --- a/src/renderer/components/ProviderList.css +++ b/src/renderer/components/ProviderList.css @@ -105,6 +105,43 @@ gap: 0.5rem; } +.enable-btn { + padding: 0.375rem 0.75rem; + border: 1px solid #27ae60; + background: white; + color: #27ae60; + border-radius: 4px; + cursor: pointer; + font-size: 0.85rem; + transition: all 0.2s; +} + +.enable-btn:hover:not(:disabled) { + background: #27ae60; + color: white; +} + +.enable-btn:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.edit-btn { + padding: 0.375rem 0.75rem; + border: 1px solid #3498db; + background: white; + color: #3498db; + border-radius: 4px; + cursor: pointer; + font-size: 0.85rem; + transition: all 0.2s; +} + +.edit-btn:hover { + background: #3498db; + color: white; +} + .delete-btn { padding: 0.375rem 0.75rem; border: 1px solid #e74c3c; diff --git a/src/renderer/components/ProviderList.tsx b/src/renderer/components/ProviderList.tsx index 4b8ac21..6cb0ea3 100644 --- a/src/renderer/components/ProviderList.tsx +++ b/src/renderer/components/ProviderList.tsx @@ -8,6 +8,7 @@ interface ProviderListProps { statuses: Record onSwitch: (id: string) => void onDelete: (id: string) => void + onEdit: (id: string) => void } const ProviderList: React.FC = ({ @@ -15,7 +16,8 @@ const ProviderList: React.FC = ({ currentProviderId, statuses, onSwitch, - onDelete + onDelete, + onEdit }) => { const formatResponseTime = (time: number) => { if (time < 0) return '-' @@ -77,6 +79,19 @@ const ProviderList: React.FC = ({
+ +