hyapp-admin-platform/src/shared/ui/JsonEditorField.jsx
2026-05-23 12:06:47 +08:00

115 lines
3.9 KiB
JavaScript

import { useMemo, useState } from "react";
import TextField from "@mui/material/TextField";
import { Button } from "@/shared/ui/Button.jsx";
import styles from "@/shared/ui/JsonEditorField.module.css";
export function JsonEditorField({
className = "",
disabled = false,
label,
minRows = 6,
onChange,
required = false,
value,
}) {
const [validated, setValidated] = useState(false);
const validation = useMemo(() => validateJsonObject(value), [value]);
const showError = validated && !validation.valid;
const helperText = showError
? validation.message
: validated
? "JSON 格式正确"
: "支持格式化和校验,内容必须是 JSON 对象";
const lineCount = Math.max(1, String(value || "").split("\n").length);
const formatJson = () => {
const result = parseJsonObject(value);
setValidated(true);
if (!result.valid) {
return;
}
onChange(JSON.stringify(result.value, null, 2));
};
return (
<div className={[styles.root, className].filter(Boolean).join(" ")}>
<div className={styles.header}>
<div className={styles.title}>
<span>{label}</span>
{required ? <span className={styles.required}>*</span> : null}
</div>
<div className={styles.actions}>
<Button disabled={disabled} onClick={() => setValidated(true)}>
校验 JSON
</Button>
<Button disabled={disabled} onClick={formatJson}>
格式化 JSON
</Button>
</div>
</div>
<div className={[styles.editor, showError ? styles.editorInvalid : ""].filter(Boolean).join(" ")}>
<pre className={styles.gutter} aria-hidden="true">
{Array.from({ length: lineCount }, (_, index) => index + 1).join("\n")}
</pre>
<TextField
className={styles.editorField}
disabled={disabled}
minRows={minRows}
multiline
slotProps={{
input: { disableUnderline: true },
htmlInput: {
"aria-label": label,
autoCapitalize: "off",
autoCorrect: "off",
spellCheck: false,
},
}}
value={value}
variant="standard"
onBlur={() => setValidated(true)}
onChange={(event) => {
setValidated(false);
onChange(event.target.value);
}}
/>
</div>
<div
className={[styles.helper, showError ? styles.helperInvalid : validated ? styles.helperValid : ""].join(
" ",
)}
>
{helperText}
</div>
</div>
);
}
export function normalizeJsonObjectString(raw) {
const result = parseJsonObject(raw);
if (!result.valid) {
throw new Error(result.message);
}
return JSON.stringify(result.value);
}
function validateJsonObject(raw) {
return parseJsonObject(raw);
}
function parseJsonObject(raw) {
const value = String(raw || "").trim();
if (!value) {
return { valid: true, value: {} };
}
try {
const parsed = JSON.parse(value);
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
return { valid: false, message: '配置 JSON 必须是对象,例如 {"app_id": 123}' };
}
return { valid: true, value: parsed };
} catch (err) {
return { valid: false, message: `JSON 格式错误:${err.message}` };
}
}