import { useEffect, useState, useCallback } from "react"; import { Table, Button, Modal, Form, Input, Select, Switch, Space, message, Popconfirm, Tag, } from "antd"; import { PlusOutlined, EditOutlined, DeleteOutlined, ExperimentOutlined } from "@ant-design/icons"; import type { AIModel } from "../types"; import * as modelApi from "../api/models"; const PROVIDERS = [ { label: "OpenAI", value: "openai" }, { label: "Azure", value: "azure" }, { label: "自定义", value: "custom" }, ]; export default function ModelList() { const [models, setModels] = useState([]); const [loading, setLoading] = useState(false); const [modalOpen, setModalOpen] = useState(false); const [editingModel, setEditingModel] = useState(null); const [form] = Form.useForm(); const fetchModels = useCallback(async () => { setLoading(true); try { const data = await modelApi.listModels(); setModels(data); } catch { message.error("获取模型列表失败"); } finally { setLoading(false); } }, []); useEffect(() => { fetchModels(); }, [fetchModels]); const handleCreate = () => { setEditingModel(null); setModalOpen(true); }; const handleEdit = (model: AIModel) => { setEditingModel(model); setModalOpen(true); }; useEffect(() => { if (!modalOpen) return; // 延迟确保 Form DOM 已渲染 const timer = setTimeout(() => { if (editingModel) { const extra = editingModel.extra_params || {}; form.setFieldsValue({ name: editingModel.name, provider: editingModel.provider, model_name: extra.model || "", endpoint: editingModel.endpoint, api_key: "", extra_params: JSON.stringify(extra, null, 2), is_enabled: editingModel.is_enabled, remark: editingModel.remark, }); } else { form.resetFields(); form.setFieldsValue({ provider: "openai", is_enabled: true, extra_params: "{}" }); } }, 0); return () => clearTimeout(timer); }, [modalOpen, editingModel]); const handleSubmit = async () => { try { const values = await form.validateFields(); let extraParams = {}; try { extraParams = JSON.parse(values.extra_params || "{}"); } catch { message.error("扩展参数 JSON 格式错误"); return; } const payload = { ...values, extra_params: { ...extraParams, ...(values.model_name ? { model: values.model_name } : {}) }, }; delete payload.model_name; if (editingModel) { if (!payload.api_key) delete payload.api_key; await modelApi.updateModel(editingModel.id, payload); message.success("更新成功"); } else { await modelApi.createModel(payload); message.success("创建成功"); } setModalOpen(false); fetchModels(); } catch (err: unknown) { if (err instanceof Error) message.error(err.message); } }; const handleDelete = async (id: string) => { await modelApi.deleteModel(id); message.success("删除成功"); fetchModels(); }; const handleToggle = async (id: string, enabled: boolean) => { await modelApi.toggleModel(id, enabled); fetchModels(); }; const handleTest = async (id: string, name: string) => { message.loading({ content: `正在测试 ${name}...`, key: "test" }); try { const res = await modelApi.testModel(id); if (res.success) { message.success({ content: `${name} 测试通过`, key: "test" }); Modal.info({ title: `测试结果 - ${name}`, content: res.result, width: 600 }); } else { message.error({ content: `测试失败: ${res.error}`, key: "test", duration: 5 }); } } catch (err: unknown) { message.error({ content: err instanceof Error ? err.message : "测试失败", key: "test", duration: 5 }); } }; const columns = [ { title: "名称", dataIndex: "name", key: "name", width: 150 }, { title: "供应商", dataIndex: "provider", key: "provider", width: 80, render: (v: string) => { const label = PROVIDERS.find((p) => p.value === v)?.label || v; return {label}; }, }, { title: "模型名", key: "model_name", width: 140, render: (_: unknown, r: AIModel) => {(r.extra_params as Record)?.model as string || "-"}, }, { title: "接口地址", dataIndex: "endpoint", key: "endpoint", ellipsis: true }, { title: "启用", dataIndex: "is_enabled", key: "is_enabled", width: 80, render: (v: boolean, record: AIModel) => ( handleToggle(record.id, checked)} /> ), }, { title: "备注", dataIndex: "remark", key: "remark", width: 150, ellipsis: true, render: (v: string | null) => v || "-", }, { title: "操作", key: "actions", width: 160, render: (_: unknown, record: AIModel) => ( { setModalOpen(false); form.resetFields(); }} onOk={handleSubmit} width={600} >
); }