/** * Admin 后台 - 配置项行组件(报刊风) * * 展示态:键名 + 来源徽标(数据库覆盖 / .env 默认 / 未配置) + 脱敏值 + 操作按钮 * 编辑态:输入框 + 保存/取消 * 由数据源页与 LLM 配置页共用。 */ import { useEffect, useState } from 'react' import type { DataSourceSetting } from './types' import { Badge, Spinner } from './components' export const ORIGIN_BADGE: Record = { db: { text: '数据库覆盖', status: 'success' }, env: { text: '.env 默认', status: 'info' }, none: { text: '未配置', status: 'error' }, } export default function SettingRow({ setting, editing, busy, onEdit, onCancel, onSave, onClear, detectModels, }: { setting: DataSourceSetting editing: boolean busy: boolean onEdit: () => void onCancel: () => void onSave: (value: string) => void onClear: () => void /** 可选:编辑态提供「检测可用模型」能力(如 LLM_MODEL 行) */ detectModels?: () => Promise }) { const [value, setValue] = useState('') const origin = ORIGIN_BADGE[setting.origin] // 行内模型检测 const [detecting, setDetecting] = useState(false) const [detected, setDetected] = useState(null) const [detectError, setDetectError] = useState('') // 进入编辑态时清空上次的检测结果 useEffect(() => { if (editing) { setDetected(null) setDetectError('') } }, [editing]) async function handleDetect() { if (!detectModels || detecting) return setDetecting(true) setDetectError('') try { setDetected(await detectModels()) } catch (err) { setDetected(null) setDetectError(err instanceof Error ? err.message : '检测失败') } finally { setDetecting(false) } } if (editing) { return (
{setting.key} {setting.sensitive && 敏感}
setValue(e.target.value)} placeholder={`输入新的 ${setting.label}`} autoFocus autoComplete="off" className="field flex-1" />
{detectModels && (
{detectError && (

{detectError}

)} {detected && detected.length > 0 && (
{detected.map(id => ( ))}
)} {detected && detected.length === 0 && !detectError && (

服务未返回可用模型

)}
)}
) } return (
{setting.key} {origin.text}
{setting.configured ? setting.masked : '—'}
{setting.origin === 'db' && ( )}
) }