import AddOutlined from "@mui/icons-material/AddOutlined"; import DeleteOutlineOutlined from "@mui/icons-material/DeleteOutlineOutlined"; import InputAdornment from "@mui/material/InputAdornment"; import MenuItem from "@mui/material/MenuItem"; import TextField from "@mui/material/TextField"; import { useEffect, useMemo, useRef, useState } from "react"; import { adapterCatalogRows, adapterCommonFields, adapterConfigFieldValue, adapterDefinition, formatAdapterConfigJson, readAdapterConfig, updateAdapterCatalog, updateAdapterConfigField, } from "@/features/games/adapterConfigModel.js"; import { Button } from "@/shared/ui/Button.jsx"; import { JsonEditorField } from "@/shared/ui/JsonEditorField.jsx"; import styles from "@/features/games/components/AdapterConfigFields.module.css"; export function AdapterConfigFields({ adapterType, disabled = false, value, onChange }) { const definition = adapterDefinition(adapterType); const configValid = Boolean(readAdapterConfig(value)); const visualFieldsDisabled = disabled || !configValid; return (
{!configValid ?
请先在高级配置中修正 JSON 格式
: null} {(definition.fields || []).length > 0 ? ( ) : null} {definition.catalog ? ( ) : null} {(definition.advancedFields || []).length > 0 ? (
可选厂商参数
) : null}
高级配置 · 完整 JSON
); } function ConfigFieldGroup({ disabled, fields, title, value, onChange }) { return (
{title}
); } function ConfigFieldGrid({ disabled, fields, value, onChange }) { return (
{fields.map((field) => ( onChange(updateAdapterConfigField(value, field, inputValue))} /> ))}
); } function AdapterField({ disabled, field, value, onChange }) { const className = field.className === "wide" ? styles.wideField : undefined; const commonProps = { className, disabled, label: field.label, placeholder: field.placeholder, required: field.required, value, onChange: (event) => onChange(event.target.value), }; if (field.type === "select" || field.type === "selectNumber" || field.type === "selectBoolean") { return ( {(field.options || []).map(([optionValue, optionLabel]) => ( {optionLabel} ))} ); } return ( {field.unit} } : undefined, }} type={field.type === "integer" ? "number" : "text"} /> ); } function GameCatalogEditor({ adapterType, disabled, value, onChange }) { const definition = adapterDefinition(adapterType); const externalRows = useMemo(() => adapterCatalogRows(value), [value]); const externalSignature = useMemo(() => catalogSignature(externalRows), [externalRows]); const lastEmittedSignature = useRef(externalSignature); const adapterTypeRef = useRef(adapterType); const rowSequence = useRef(externalRows.length); const [rows, setRows] = useState(() => withRowKeys(externalRows, "initial")); useEffect(() => { const adapterChanged = adapterTypeRef.current !== adapterType; if (adapterChanged || externalSignature !== lastEmittedSignature.current) { rowSequence.current += externalRows.length; setRows(withRowKeys(externalRows, `sync-${rowSequence.current}`)); } adapterTypeRef.current = adapterType; lastEmittedSignature.current = externalSignature; }, [adapterType, externalRows, externalSignature]); const emitRows = (nextRows) => { setRows(nextRows); const nextValue = updateAdapterCatalog(value, nextRows); lastEmittedSignature.current = catalogSignature(adapterCatalogRows(nextValue)); onChange(nextValue); }; const addRow = () => { rowSequence.current += 1; setRows((current) => [ ...current, { rowKey: `draft-${rowSequence.current}`, providerGameId: "", gameName: "", gameUrl: "", iconUrl: "", coverUrl: "", gameLevelUid: "", }, ]); }; const changeRow = (rowKey, patch) => { emitRows(rows.map((row) => (row.rowKey === rowKey ? { ...row, ...patch } : row))); }; const removeRow = (rowKey) => { emitRows(rows.filter((row) => row.rowKey !== rowKey)); }; return (
游戏目录 · {rows.length}
{rows.length > 0 ? (
{rows.map((row, index) => { const duplicate = row.providerGameId.trim() !== "" && rows.some( (candidate, candidateIndex) => candidateIndex !== index && candidate.providerGameId.trim() === row.providerGameId.trim(), ); return (
changeRow(row.rowKey, { providerGameId: event.target.value }) } /> changeRow(row.rowKey, { gameName: event.target.value })} /> changeRow(row.rowKey, { gameUrl: event.target.value })} />
素材与游戏参数
changeRow(row.rowKey, { iconUrl: event.target.value })} /> changeRow(row.rowKey, { coverUrl: event.target.value }) } /> {adapterType === "yomi_v4" ? ( changeRow(row.rowKey, { gameLevelUid: event.target.value }) } /> ) : null}
); })}
) : (
当前无游戏目录
)}
); } function withRowKeys(rows, prefix) { return rows.map((row, index) => ({ ...row, rowKey: `${prefix}-${index}` })); } function catalogSignature(rows) { return JSON.stringify( (rows || []).map(({ providerGameId, gameName, gameUrl, iconUrl, coverUrl, gameLevelUid }) => ({ providerGameId, gameName, gameUrl, iconUrl, coverUrl, gameLevelUid, })), ); }