import React, { useState, useEffect, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { Button, Form, Typography, Banner, Tabs, TabPane, Card, Input, InputNumber, Switch, TextArea, Row, Col, Divider, } from '@douyinfe/semi-ui'; import { IconCode, IconPlus, IconDelete, IconRefresh, } from '@douyinfe/semi-icons'; const { Text } = Typography; const JSONEditor = ({ value = '', onChange, field, label, placeholder, extraText, extraFooter, showClear = true, template, templateLabel, editorType = 'keyValue', rules = [], formApi = null, ...props }) => { const { t } = useTranslation(); // 初始化JSON数据 const [jsonData, setJsonData] = useState(() => { // 初始化时解析JSON数据 if (typeof value === 'string' && value.trim()) { try { const parsed = JSON.parse(value); return parsed; } catch (error) { return {}; } } if (typeof value === 'object' && value !== null) { return value; } return {}; }); // 根据键数量决定默认编辑模式 const [editMode, setEditMode] = useState(() => { // 如果初始JSON数据的键数量大于10个,则默认使用手动模式 if (typeof value === 'string' && value.trim()) { try { const parsed = JSON.parse(value); const keyCount = Object.keys(parsed).length; return keyCount > 10 ? 'manual' : 'visual'; } catch (error) { // JSON无效时默认显示手动编辑模式 return 'manual'; } } return 'visual'; }); const [jsonError, setJsonError] = useState(''); // 数据同步 - 当value变化时总是更新jsonData(如果JSON有效) useEffect(() => { try { let parsed = {}; if (typeof value === 'string' && value.trim()) { parsed = JSON.parse(value); } else if (typeof value === 'object' && value !== null) { parsed = value; } setJsonData(parsed); setJsonError(''); } catch (error) { console.log('JSON解析失败:', error.message); setJsonError(error.message); // JSON格式错误时不更新jsonData } }, [value]); // 处理可视化编辑的数据变化 const handleVisualChange = useCallback((newData) => { setJsonData(newData); setJsonError(''); const jsonString = Object.keys(newData).length === 0 ? '' : JSON.stringify(newData, null, 2); // 通过formApi设置值(如果提供的话) if (formApi && field) { formApi.setValue(field, jsonString); } onChange?.(jsonString); }, [onChange, formApi, field]); // 处理手动编辑的数据变化 const handleManualChange = useCallback((newValue) => { onChange?.(newValue); // 验证JSON格式 if (newValue && newValue.trim()) { try { const parsed = JSON.parse(newValue); setJsonError(''); // 预先准备可视化数据,但不立即应用 // 这样切换到可视化模式时数据已经准备好了 } catch (error) { setJsonError(error.message); } } else { setJsonError(''); } }, [onChange]); // 切换编辑模式 const toggleEditMode = useCallback(() => { if (editMode === 'visual') { // 从可视化模式切换到手动模式 setEditMode('manual'); } else { // 从手动模式切换到可视化模式,需要验证JSON try { let parsed = {}; if (typeof value === 'string' && value.trim()) { parsed = JSON.parse(value); } else if (typeof value === 'object' && value !== null) { parsed = value; } setJsonData(parsed); setJsonError(''); setEditMode('visual'); } catch (error) { setJsonError(error.message); // JSON格式错误时不切换模式 return; } } }, [editMode, value]); // 添加键值对 const addKeyValue = useCallback(() => { const newData = { ...jsonData }; const keys = Object.keys(newData); let counter = 1; let newKey = `field_${counter}`; while (newData.hasOwnProperty(newKey)) { counter += 1; newKey = `field_${counter}`; } newData[newKey] = ''; handleVisualChange(newData); }, [jsonData, handleVisualChange]); // 删除键值对 const removeKeyValue = useCallback((keyToRemove) => { const newData = { ...jsonData }; delete newData[keyToRemove]; handleVisualChange(newData); }, [jsonData, handleVisualChange]); // 更新键名 const updateKey = useCallback((oldKey, newKey) => { if (oldKey === newKey || !newKey) return; const newData = {}; Object.entries(jsonData).forEach(([k, v]) => { if (k === oldKey) { newData[newKey] = v; } else { newData[k] = v; } }); handleVisualChange(newData); }, [jsonData, handleVisualChange]); // 更新值 const updateValue = useCallback((key, newValue) => { const newData = { ...jsonData }; newData[key] = newValue; handleVisualChange(newData); }, [jsonData, handleVisualChange]); // 填入模板 const fillTemplate = useCallback(() => { if (template) { const templateString = JSON.stringify(template, null, 2); // 通过formApi设置值(如果提供的话) if (formApi && field) { formApi.setValue(field, templateString); } // 无论哪种模式都要更新值 onChange?.(templateString); // 如果是可视化模式,同时更新jsonData if (editMode === 'visual') { setJsonData(template); } // 清除错误状态 setJsonError(''); } }, [template, onChange, editMode, formApi, field]); // 渲染键值对编辑器 const renderKeyValueEditor = () => { if (typeof jsonData !== 'object' || jsonData === null) { return (
{t('无效的JSON数据,请检查格式')}
); } const entries = Object.entries(jsonData); return (
{entries.length === 0 && (
{t('暂无数据,点击下方按钮添加键值对')}
)} {entries.map(([key, value], index) => ( updateKey(key, newKey)} /> {renderValueInput(key, value)}
); }; // 添加嵌套对象 const flattenObject = useCallback((parentKey) => { const newData = { ...jsonData }; let primitive = ''; const obj = newData[parentKey]; if (obj && typeof obj === 'object') { const firstKey = Object.keys(obj)[0]; if (firstKey !== undefined) { const firstVal = obj[firstKey]; if (typeof firstVal !== 'object') primitive = firstVal; } } newData[parentKey] = primitive; handleVisualChange(newData); }, [jsonData, handleVisualChange]); const addNestedObject = useCallback((parentKey) => { const newData = { ...jsonData }; if (typeof newData[parentKey] !== 'object' || newData[parentKey] === null) { newData[parentKey] = {}; } const existingKeys = Object.keys(newData[parentKey]); let counter = 1; let newKey = `field_${counter}`; while (newData[parentKey].hasOwnProperty(newKey)) { counter += 1; newKey = `field_${counter}`; } newData[parentKey][newKey] = ''; handleVisualChange(newData); }, [jsonData, handleVisualChange]); // 渲染参数值输入控件(支持嵌套) const renderValueInput = (key, value) => { const valueType = typeof value; if (valueType === 'boolean') { return (
updateValue(key, newValue)} /> {value ? t('true') : t('false')}
); } if (valueType === 'number') { return ( updateValue(key, newValue)} style={{ width: '100%' }} step={key === 'temperature' ? 0.1 : 1} precision={key === 'temperature' ? 2 : 0} placeholder={t('输入数字')} /> ); } if (valueType === 'object' && value !== null) { // 渲染嵌套对象 const entries = Object.entries(value); return ( {entries.length === 0 && ( {t('空对象,点击下方加号添加字段')} )} {entries.map(([nestedKey, nestedValue], index) => ( { const newData = { ...jsonData }; const oldValue = newData[key][nestedKey]; delete newData[key][nestedKey]; newData[key][newKey] = oldValue; handleVisualChange(newData); }} /> {typeof nestedValue === 'object' && nestedValue !== null ? (