Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
Next.js SSR/hydration 与 SWR fetch-on-mount 存在根本冲突: hydration 卸载组件时 abort 的请求仍占用后端 DB 连接, retry 循环耗尽 PostgreSQL 连接池导致后端完全卡死。 admin-v2 使用 Vite + React + antd 纯 SPA 彻底消除此问题: - 12 页面全部完成(Login, Dashboard, Accounts, Providers, Models, API Keys, Usage, Relay, Config, Prompts, Logs, Agent Templates) - ProTable + ProForm + ProLayout 统一 UI 模式 - TanStack Query + Axios + Zustand 数据层 - JWT 自动刷新 + 401 重试机制 - 全部 18 网络请求 200 OK,零 ERR_ABORTED 同时更新 troubleshooting 第 13 节和 SaaS 平台文档。
111 lines
3.8 KiB
TypeScript
111 lines
3.8 KiB
TypeScript
// ============================================================
|
|
// 系统配置
|
|
// ============================================================
|
|
|
|
import { useState } from 'react'
|
|
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
|
import { Card, Tabs, message, Tag, Input, Button, Space, Typography } from 'antd'
|
|
import type { ProColumns } from '@ant-design/pro-components'
|
|
import { ProTable } from '@ant-design/pro-components'
|
|
import { configService } from '@/services/config'
|
|
import type { ConfigItem } from '@/types'
|
|
|
|
const { Title } = Typography
|
|
|
|
export default function Config() {
|
|
const queryClient = useQueryClient()
|
|
const [category, setCategory] = useState<string>('general')
|
|
const [editingId, setEditingId] = useState<string | null>(null)
|
|
const [editValue, setEditValue] = useState('')
|
|
|
|
const { data, isLoading } = useQuery({
|
|
queryKey: ['config', category],
|
|
queryFn: () => configService.list({ category }),
|
|
})
|
|
|
|
const updateMutation = useMutation({
|
|
mutationFn: ({ id, value }: { id: string; value: string }) =>
|
|
configService.update(id, { value }),
|
|
onSuccess: () => {
|
|
message.success('配置已更新')
|
|
queryClient.invalidateQueries({ queryKey: ['config', category] })
|
|
setEditingId(null)
|
|
},
|
|
onError: (err: Error) => message.error(err.message || '更新失败'),
|
|
})
|
|
|
|
const columns: ProColumns<ConfigItem>[] = [
|
|
{ title: '配置路径', dataIndex: 'key_path', width: 200, render: (_, r) => <code>{r.key_path}</code> },
|
|
{
|
|
title: '当前值',
|
|
dataIndex: 'current_value',
|
|
width: 250,
|
|
render: (_, record) => {
|
|
if (editingId === record.id) {
|
|
return (
|
|
<Space>
|
|
<Input
|
|
value={editValue}
|
|
onChange={(e) => setEditValue(e.target.value)}
|
|
style={{ width: 180 }}
|
|
onPressEnter={() => updateMutation.mutate({ id: record.id, value: editValue })}
|
|
/>
|
|
<Button size="small" type="primary" onClick={() => updateMutation.mutate({ id: record.id, value: editValue })}>
|
|
保存
|
|
</Button>
|
|
<Button size="small" onClick={() => setEditingId(null)}>取消</Button>
|
|
</Space>
|
|
)
|
|
}
|
|
return (
|
|
<span
|
|
onClick={() => { setEditingId(record.id); setEditValue(record.current_value || '') }}
|
|
style={{ cursor: 'pointer', color: '#1677ff' }}
|
|
>
|
|
{record.current_value || <Tag>未设置</Tag>}
|
|
</span>
|
|
)
|
|
},
|
|
},
|
|
{ title: '默认值', dataIndex: 'default_value', width: 200, render: (_, r) => r.default_value || '-' },
|
|
{ title: '类型', dataIndex: 'value_type', width: 80, render: (_, r) => <Tag>{r.value_type}</Tag> },
|
|
{ title: '描述', dataIndex: 'description', width: 200, ellipsis: true },
|
|
{
|
|
title: '需要重启',
|
|
dataIndex: 'requires_restart',
|
|
width: 90,
|
|
render: (_, r) => r.requires_restart ? <Tag color="orange">是</Tag> : <Tag>否</Tag>,
|
|
},
|
|
]
|
|
|
|
return (
|
|
<div>
|
|
<Title level={4} style={{ marginBottom: 24 }}>系统配置</Title>
|
|
|
|
<Tabs
|
|
activeKey={category}
|
|
onChange={(key) => { setCategory(key); setEditingId(null) }}
|
|
items={[
|
|
{ key: 'general', label: '通用' },
|
|
{ key: 'auth', label: '认证' },
|
|
{ key: 'relay', label: '中转' },
|
|
{ key: 'model', label: '模型' },
|
|
{ key: 'rate_limit', label: '限流' },
|
|
{ key: 'log', label: '日志' },
|
|
]}
|
|
/>
|
|
|
|
<ProTable<ConfigItem>
|
|
columns={columns}
|
|
dataSource={data ?? []}
|
|
loading={isLoading}
|
|
rowKey="id"
|
|
search={false}
|
|
toolBarRender={false}
|
|
pagination={false}
|
|
size="small"
|
|
/>
|
|
</div>
|
|
)
|
|
}
|