Files
video-create/web/client/src/components/config/ConfigForm.tsx

190 lines
7.2 KiB
TypeScript
Raw Normal View History

2026-05-07 02:29:31 +08:00
import { useState, useEffect } from 'react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
2026-05-07 02:29:31 +08:00
import { api } from '@/lib/api';
export function ConfigForm() {
const [form, setForm] = useState({
model: '',
baseUrl: '',
authToken: '',
defaultImageModel: '',
defaultVideoModel: '',
defaultFormat: '',
ossEndpoint: '',
ossBucket: '',
});
2026-05-07 02:29:31 +08:00
const [saving, setSaving] = useState(false);
const [saved, setSaved] = useState(false);
2026-05-07 02:29:31 +08:00
useEffect(() => {
api.getConfigs().then((list) => {
const next = { ...form };
for (const item of list) {
try {
const v = item.value as Record<string, string>;
if (item.key === 'api_keys') {
if (v.ANTHROPIC_MODEL) next.model = v.ANTHROPIC_MODEL;
if (v.ANTHROPIC_BASE_URL) next.baseUrl = v.ANTHROPIC_BASE_URL;
if (v.ANTHROPIC_AUTH_TOKEN) next.authToken = v.ANTHROPIC_AUTH_TOKEN;
}
if (item.key === 'defaults') {
if (v.imageModel) next.defaultImageModel = v.imageModel;
if (v.videoModel) next.defaultVideoModel = v.videoModel;
if (v.format) next.defaultFormat = v.format;
}
if (item.key === 'endpoints') {
if (v.ossEndpoint) next.ossEndpoint = v.ossEndpoint;
if (v.ossBucket) next.ossBucket = v.ossBucket;
}
} catch {}
}
setForm(next);
2026-05-07 02:29:31 +08:00
});
}, []);
const handleChange = (key: string, value: string) => {
setForm((f) => ({ ...f, [key]: value }));
};
const handleSave = async () => {
2026-05-07 02:29:31 +08:00
setSaving(true);
try {
await api.saveConfig('api_keys', {
ANTHROPIC_MODEL: form.model,
ANTHROPIC_BASE_URL: form.baseUrl,
ANTHROPIC_AUTH_TOKEN: form.authToken,
});
await api.saveConfig('defaults', {
imageModel: form.defaultImageModel,
videoModel: form.defaultVideoModel,
format: form.defaultFormat,
});
await api.saveConfig('endpoints', {
ossEndpoint: form.ossEndpoint,
ossBucket: form.ossBucket,
});
setSaved(true);
setTimeout(() => setSaved(false), 2000);
} catch {}
2026-05-07 02:29:31 +08:00
setSaving(false);
};
return (
<div className="max-w-xl mx-auto p-6 space-y-8 overflow-auto">
<h2 className="text-lg font-semibold text-zinc-800"></h2>
<section className="space-y-4">
<h3 className="text-sm font-medium text-zinc-500 uppercase tracking-wide">API </h3>
<div className="space-y-3 bg-white rounded-lg border border-zinc-200 p-4">
<div>
<label className="text-xs font-medium text-zinc-500"></label>
<Input
value={form.model}
onChange={(e) => handleChange('model', e.target.value)}
placeholder="deepseek-v4-pro[1m]"
className="mt-1 bg-zinc-50 border-zinc-200"
/>
</div>
<div>
<label className="text-xs font-medium text-zinc-500">Base URL</label>
<Input
value={form.baseUrl}
onChange={(e) => handleChange('baseUrl', e.target.value)}
placeholder="https://api.deepseek.com/anthropic"
className="mt-1 bg-zinc-50 border-zinc-200 font-mono text-xs"
/>
</div>
<div>
<label className="text-xs font-medium text-zinc-500">Auth Token</label>
<Input
value={form.authToken}
onChange={(e) => handleChange('authToken', e.target.value)}
type="password"
placeholder="sk-..."
className="mt-1 bg-zinc-50 border-zinc-200 font-mono text-xs"
/>
</div>
2026-05-07 02:29:31 +08:00
</div>
</section>
<section className="space-y-4">
<h3 className="text-sm font-medium text-zinc-500 uppercase tracking-wide"></h3>
<div className="space-y-3 bg-white rounded-lg border border-zinc-200 p-4">
<div className="grid grid-cols-3 gap-3">
<div>
<label className="text-xs font-medium text-zinc-500"></label>
<select
value={form.defaultImageModel}
onChange={(e) => handleChange('defaultImageModel', e.target.value)}
className="mt-1 w-full h-10 rounded-md border border-zinc-200 bg-zinc-50 px-3 text-sm"
>
<option value=""></option>
<option value="gemini">Gemini</option>
<option value="mj">Midjourney</option>
<option value="gpt">GPT Image</option>
<option value="kling">Kling</option>
</select>
</div>
<div>
<label className="text-xs font-medium text-zinc-500"></label>
<select
value={form.defaultVideoModel}
onChange={(e) => handleChange('defaultVideoModel', e.target.value)}
className="mt-1 w-full h-10 rounded-md border border-zinc-200 bg-zinc-50 px-3 text-sm"
>
<option value=""></option>
<option value="veo3-fast">Veo3 Fast</option>
<option value="veo3-fast-frames">Veo3 Fast Frames</option>
<option value="kling">Kling</option>
<option value="grok">Grok</option>
</select>
</div>
<div>
<label className="text-xs font-medium text-zinc-500"></label>
<select
value={form.defaultFormat}
onChange={(e) => handleChange('defaultFormat', e.target.value)}
className="mt-1 w-full h-10 rounded-md border border-zinc-200 bg-zinc-50 px-3 text-sm"
>
<option value=""></option>
<option value="9:16">9:16 </option>
<option value="16:9">16:9 </option>
<option value="1:1">1:1 </option>
</select>
</div>
</div>
</div>
</section>
<section className="space-y-4">
<h3 className="text-sm font-medium text-zinc-500 uppercase tracking-wide">OSS </h3>
<div className="space-y-3 bg-white rounded-lg border border-zinc-200 p-4">
<div>
<label className="text-xs font-medium text-zinc-500">Endpoint</label>
<Input
value={form.ossEndpoint}
onChange={(e) => handleChange('ossEndpoint', e.target.value)}
placeholder="https://oss-cn-hangzhou.aliyuncs.com"
className="mt-1 bg-zinc-50 border-zinc-200 font-mono text-xs"
/>
</div>
<div>
<label className="text-xs font-medium text-zinc-500">Bucket</label>
<Input
value={form.ossBucket}
onChange={(e) => handleChange('ossBucket', e.target.value)}
placeholder="my-bucket"
className="mt-1 bg-zinc-50 border-zinc-200"
/>
</div>
</div>
</section>
<Button onClick={handleSave} disabled={saving} className="w-full">
{saved ? '已保存' : saving ? '保存中...' : '保存设置'}
</Button>
2026-05-07 02:29:31 +08:00
</div>
);
}