Files
cc-switch/src/renderer/components/ProviderForm.tsx
farion1231 482298411a 代码重构:移除 any 类型并抽取共享表单组件
- 移除所有 any 类型声明,提升类型安全性
- 简化 ClaudeCodeSettings 接口为通用的 Record<string, any>,支持任意供应商配置
- 创建 ProviderForm 共享组件,减少重复代码约 300 行
- 优化 Store 类型定义,使用泛型约束确保类型安全

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-08 15:03:38 +08:00

223 lines
6.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useState, useEffect } from 'react'
import { Provider } from '../../shared/types'
import { updateCoAuthoredSetting, checkCoAuthoredSetting, extractWebsiteUrl } from '../utils/providerConfigUtils'
import { providerPresets } from '../config/providerPresets'
import './AddProviderModal.css'
interface ProviderFormProps {
title: string
submitText: string
initialData?: Provider
showPresets?: boolean
onSubmit: (data: Omit<Provider, 'id'>) => void
onClose: () => void
}
const ProviderForm: React.FC<ProviderFormProps> = ({
title,
submitText,
initialData,
showPresets = false,
onSubmit,
onClose
}) => {
const [formData, setFormData] = useState({
name: initialData?.name || '',
websiteUrl: initialData?.websiteUrl || '',
settingsConfig: initialData ? JSON.stringify(initialData.settingsConfig, null, 2) : ''
})
const [error, setError] = useState('')
const [disableCoAuthored, setDisableCoAuthored] = useState(false)
// 初始化时检查禁用签名状态
useEffect(() => {
if (initialData) {
const configString = JSON.stringify(initialData.settingsConfig, null, 2)
const hasCoAuthoredDisabled = checkCoAuthoredSetting(configString)
setDisableCoAuthored(hasCoAuthoredDisabled)
}
}, [initialData])
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault()
setError('')
if (!formData.name) {
setError('请填写供应商名称')
return
}
if (!formData.settingsConfig.trim()) {
setError('请填写配置内容')
return
}
let settingsConfig: Record<string, any>
try {
settingsConfig = JSON.parse(formData.settingsConfig)
} catch (err) {
setError('配置JSON格式错误请检查语法')
return
}
onSubmit({
name: formData.name,
websiteUrl: formData.websiteUrl,
settingsConfig
})
}
const handleChange = (
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
) => {
const { name, value } = e.target
if (name === 'settingsConfig') {
// 当用户修改配置时,尝试自动提取官网地址
const extractedWebsiteUrl = extractWebsiteUrl(value)
// 同时检查并同步选择框状态
const hasCoAuthoredDisabled = checkCoAuthoredSetting(value)
setDisableCoAuthored(hasCoAuthoredDisabled)
setFormData({
...formData,
[name]: value,
// 只有在官网地址为空时才自动填入
websiteUrl: formData.websiteUrl || extractedWebsiteUrl,
})
} else {
setFormData({
...formData,
[name]: value,
})
}
}
// 处理选择框变化
const handleCoAuthoredToggle = (checked: boolean) => {
setDisableCoAuthored(checked)
// 更新JSON配置
const updatedConfig = updateCoAuthoredSetting(formData.settingsConfig, checked)
setFormData({
...formData,
settingsConfig: updatedConfig,
})
}
const applyPreset = (preset: typeof providerPresets[0]) => {
const configString = JSON.stringify(preset.settingsConfig, null, 2)
setFormData({
name: preset.name,
websiteUrl: preset.websiteUrl,
settingsConfig: configString
})
// 同步选择框状态
const hasCoAuthoredDisabled = checkCoAuthoredSetting(configString)
setDisableCoAuthored(hasCoAuthoredDisabled)
}
return (
<div className="modal-overlay">
<div className="modal-content">
<h2>{title}</h2>
{error && <div className="error-message">{error}</div>}
{showPresets && (
<div className="presets">
<label></label>
<div className="preset-buttons">
{providerPresets.map((preset, index) => (
<button
key={index}
type="button"
className="preset-btn"
onClick={() => applyPreset(preset)}
>
{preset.name}
</button>
))}
</div>
</div>
)}
<form onSubmit={handleSubmit}>
<div className="form-group">
<label htmlFor="name"> *</label>
<input
type="text"
id="name"
name="name"
value={formData.name}
onChange={handleChange}
placeholder="例如Anthropic 官方"
required
autoComplete="off"
/>
</div>
<div className="form-group">
<label htmlFor="websiteUrl"></label>
<input
type="url"
id="websiteUrl"
name="websiteUrl"
value={formData.websiteUrl}
onChange={handleChange}
placeholder="https://example.com可选"
autoComplete="off"
/>
</div>
<div className="form-group">
<div className="label-with-checkbox">
<label htmlFor="settingsConfig">Claude Code (JSON) *</label>
<label className="checkbox-label">
<input
type="checkbox"
checked={disableCoAuthored}
onChange={(e) => handleCoAuthoredToggle(e.target.checked)}
/>
Claude Code
</label>
</div>
<textarea
id="settingsConfig"
name="settingsConfig"
value={formData.settingsConfig}
onChange={handleChange}
placeholder={`{
"env": {
"ANTHROPIC_BASE_URL": "https://api.anthropic.com",
"ANTHROPIC_AUTH_TOKEN": "sk-your-api-key-here"
}
}`}
rows={12}
style={{fontFamily: 'monospace', fontSize: '14px'}}
required
/>
<small className="field-hint">
Claude Code settings.json
</small>
</div>
<div className="form-actions">
<button type="button" className="cancel-btn" onClick={onClose}>
</button>
<button type="submit" className="submit-btn">
{submitText}
</button>
</div>
</form>
</div>
</div>
)
}
export default ProviderForm