import { useEffect, useState } from "react"; import { App, Button, Drawer, Form, Input, InputNumber, Popconfirm, Segmented, Select, Space, Switch, Table, Tag } from "antd"; import type { ColumnsType } from "antd/es/table"; import { ArrowLeft, Plus, RefreshCw, Search, Trash2 } from "lucide-react"; import { ListToolbar, TableSurface } from "@/components/layout/workspace-page"; import { createAdminChannelModel, deleteAdminChannelModel, fetchAdminChannelModels, listAdminChannelModels, updateAdminChannelModel, type ChannelModel } from "@/services/api/wallet"; import type { ModelChannel } from "@/stores/use-config-store"; type FormValues = { modelKey: string; displayName?: string; capability: ChannelModel["capability"]; billingMode: ChannelModel["billingMode"]; unitPrice: number; enabled: boolean; }; export function ChannelModelManager({ channel, onClose, onChanged }: { channel: ModelChannel; onClose: () => void; onChanged: () => void | Promise }) { const { message } = App.useApp(); const [items, setItems] = useState([]); const [editing, setEditing] = useState(null); const [loading, setLoading] = useState(false); const [fetching, setFetching] = useState(false); const [saving, setSaving] = useState(false); const [editorOpen, setEditorOpen] = useState(false); const [keyword, setKeyword] = useState(""); const [capability, setCapability] = useState("all"); const [status, setStatus] = useState<"all" | "enabled" | "disabled">("all"); const [page, setPage] = useState(1); const [pageSize, setPageSize] = useState(20); const [form] = Form.useForm(); const billingMode = Form.useWatch("billingMode", form) || "fixed_request"; const modelCapability = Form.useWatch("capability", form); const reload = async () => { if (!channel) return; setLoading(true); try { setItems((await listAdminChannelModels(channel.id)).models); } catch (error) { message.error(error instanceof Error ? error.message : "读取渠道模型失败"); } finally { setLoading(false); } }; useEffect(() => { void reload(); setEditing(null); setEditorOpen(false); setKeyword(""); setCapability("all"); setStatus("all"); setPage(1); }, [channel.id]); const fetchModels = async () => { setFetching(true); try { // 拉取只导入缺失项;新模型仍需管理员定价并手动启用。 const result = await fetchAdminChannelModels(channel.id); await reload(); await onChanged(); if (result.models.length === 0) message.warning("上游没有返回可用模型"); else if (result.added > 0) message.success(`已拉取 ${result.models.length} 个模型,新增 ${result.added} 个待配置模型`); else message.info(`已拉取 ${result.models.length} 个模型,没有需要新增的模型`); } catch (error) { message.error(error instanceof Error ? error.message : "拉取模型失败"); } finally { setFetching(false); } }; const startCreate = () => { setEditing(null); form.setFieldsValue({ modelKey: "", displayName: "", capability: capabilityFromInterface(channel?.interfaceType), billingMode: "fixed_request", unitPrice: 0, enabled: true }); setEditorOpen(true); }; const startEdit = (item: ChannelModel) => { setEditing(item); form.setFieldsValue({ modelKey: item.modelKey, displayName: item.displayName, capability: item.capability, billingMode: item.billingMode, unitPrice: item.unitPriceMicrocredits / 1_000_000, enabled: item.enabled }); setEditorOpen(true); }; const save = async () => { const values = await form.validateFields(); setSaving(true); try { const payload = { modelKey: values.modelKey.trim(), displayName: values.displayName?.trim() || values.modelKey.trim(), capability: values.capability, billingMode: values.billingMode, unitPriceMicrocredits: Math.round(values.unitPrice * 1_000_000), priceConfigured: true, enabled: values.enabled !== false, }; if (editing) await updateAdminChannelModel(channel.id, editing.id, payload); else await createAdminChannelModel(channel.id, payload); await reload(); await onChanged(); setEditorOpen(false); setEditing(null); message.success(editing ? "模型配置已更新" : "模型已添加"); } catch (error) { message.error(error instanceof Error ? error.message : "保存模型失败"); } finally { setSaving(false); } }; const remove = async (item: ChannelModel) => { try { await deleteAdminChannelModel(channel.id, item.id); await reload(); await onChanged(); message.success("模型已删除"); } catch (error) { message.error(error instanceof Error ? error.message : "删除模型失败"); } }; const columns: ColumnsType = [ { title: "模型", render: (_, item) => (
{item.displayName || item.modelKey}
{item.modelKey}
), }, { title: "能力", dataIndex: "capability", width: 90, render: capabilityLabel }, { title: "计费", width: 165, render: (_, item) => (item.priceConfigured ? `${formatCredits(item.unitPriceMicrocredits)} 积分 / ${item.billingMode === "per_second" ? "秒" : "次"}` : 未配置价格) }, { title: "版本", dataIndex: "priceVersion", width: 75, render: (value) => `v${value}` }, { title: "状态", dataIndex: "enabled", width: 85, render: (enabled) => (enabled ? 启用 : 停用) }, { title: "操作", width: 120, render: (_, item) => ( void remove(item)}> { setKeyword(""); setCapability("all"); setStatus("all"); setPage(1); }}> } value={keyword} placeholder="搜索模型标识或显示名称" onChange={(event) => { setKeyword(event.target.value); setPage(1); }} /> { setStatus(value); setPage(1); }} options={[{ label: "全部状态", value: "all" }, { label: "已启用", value: "enabled" }, { label: "已停用", value: "disabled" }]} /> `${range[0]}-${range[1]} / 共 ${total} 个模型`, onChange: (nextPage, nextPageSize) => { setPage(nextPageSize !== pageSize ? 1 : nextPage); setPageSize(nextPageSize); } }} scroll={{ x: 760 }} /> setEditorOpen(false)} styles={{ body: { paddingBottom: 88 } }} extra={editing ? : null}>