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 (
);
}
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]) => (
))}
);
}
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,
})),
);
}