'use client'; import React, { useState, useRef, useEffect } from 'react'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faCode, faCopy, faCheck, faCompress, faExpand, faSearch, faTrash, faSync, faFolderOpen, faFolder, faSpinner, faSave, faHistory, faTimes, faEdit, faStar, faTrashAlt } from '@fortawesome/free-solid-svg-icons'; import dynamic from 'next/dynamic'; import BackToTop from '@/components/BackToTop'; import ToolHeader from '@/components/ToolHeader'; import { useLanguage } from '@/context/LanguageContext'; // 动态导入@uiw/react-json-view组件,避免SSR问题 const ReactJson = dynamic(() => import('@uiw/react-json-view'), { ssr: false }); // 定义历史记录条目类型 interface JsonHistoryItem { id: string; title: string; json: string; timestamp: number; isFavorite?: boolean; } export default function JsonFormatter() { const { t } = useLanguage(); const [jsonInput, setJsonInput] = useState(''); const jsonInputRef = useRef(null); const jsonPathInputRef = useRef(null); // 状态管理 const [jsonOutput, setJsonOutput] = useState(''); const [jsonPath, setJsonPath] = useState(''); const [pathResult, setPathResult] = useState(''); const [errorMessage, setErrorMessage] = useState(''); const [isCompressed, setIsCompressed] = useState(false); const [isFoldable, setIsFoldable] = useState(true); const [copied, setCopied] = useState(false); const [isLoading, setIsLoading] = useState(false); const [isLargeJson, setIsLargeJson] = useState(false); const [validationResult, setValidationResult] = useState<{ isValid: boolean; message: string; }>({ isValid: false, message: '' }); // 历史记录相关状态 const [historyItems, setHistoryItems] = useState([]); const [isHistoryOpen, setIsHistoryOpen] = useState(false); const [savingTitle, setSavingTitle] = useState(''); const [isSaveModalOpen, setIsSaveModalOpen] = useState(false); const [editingItem, setEditingItem] = useState(null); // 参考值,确保能在格式化过程中保持加载状态 const processingRef = useRef(false); // 从本地存储加载历史记录 useEffect(() => { const savedHistory = localStorage.getItem('json_formatter_history'); if (savedHistory) { try { const parsedHistory = JSON.parse(savedHistory) as JsonHistoryItem[]; setHistoryItems(parsedHistory); } catch (e) { console.error(t('tools.json_formatter.load_history_error'), e); } } }, [t]); // 保存历史记录到本地存储 const saveHistoryToLocalStorage = (items: JsonHistoryItem[]) => { localStorage.setItem('json_formatter_history', JSON.stringify(items)); }; // 格式化JSON const formatJson = (json: string, compress = false) => { if (!json.trim()) { setJsonOutput(''); setValidationResult({ isValid: false, message: '' }); return; } // 检查JSON大小 const isLarge = json.length > 100000; setIsLargeJson(isLarge); // 设置加载状态和处理参考值 setIsLoading(true); processingRef.current = true; // 使用setTimeout确保UI先更新,但不添加不必要的延迟 setTimeout(() => { try { // 处理可能的JS对象文本 (将单引号转为双引号) const processedJson = json .replace(/(['"])?([a-zA-Z0-9_]+)(['"])?:/g, '"$2":') // 键名标准化 .replace(/'/g, '"'); // 单引号转双引号 try { let parsed; try { parsed = JSON.parse(processedJson); } catch (e) { // 尝试使用eval处理JS对象(不安全,但为了更好的兼容性) try { // eslint-disable-next-line no-eval parsed = eval('(' + json + ')'); } catch (/* eslint-disable-next-line @typescript-eslint/no-unused-vars */ _) { throw e; // 如果eval也失败了,抛出原始错误 } } // 根据模式输出不同格式 let formattedJson; if (compress) { formattedJson = JSON.stringify(parsed); } else { formattedJson = JSON.stringify(parsed, null, 2); } // 设置输出 setJsonOutput(formattedJson); // 计算大小 const sizeKB = (formattedJson.length / 1024).toFixed(1); const largeJsonMessage = t('tools.json_formatter.large_json_processed').replace('{size}', sizeKB); setValidationResult({ isValid: true, message: isLarge ? largeJsonMessage : t('tools.json_formatter.json_valid') }); setErrorMessage(''); // 如果有JSONPath查询,执行查询 if (jsonPath) { queryJsonPath(parsed, jsonPath); } // 完成后取消加载状态和处理参考值 setIsLoading(false); processingRef.current = false; } catch (error) { if (error instanceof Error) { setErrorMessage(error.message); setValidationResult({ isValid: false, message: t('tools.json_formatter.json_invalid') }); setIsLoading(false); processingRef.current = false; } } } catch (error) { if (error instanceof Error) { setErrorMessage(error.message); setValidationResult({ isValid: false, message: t('tools.json_formatter.json_invalid') }); setIsLoading(false); processingRef.current = false; } } }, 0); }; // 取消正在进行的格式化操作 const cancelFormatting = () => { processingRef.current = false; setIsLoading(false); }; // 清除组件卸载时可能的处理操作 useEffect(() => { return () => { processingRef.current = false; }; }, []); // 压缩/美化切换 const toggleCompression = () => { setIsCompressed(!isCompressed); formatJson(jsonInput, !isCompressed); }; // 切换折叠功能 const toggleFoldable = () => { setIsFoldable(!isFoldable); }; // 复制结果到剪贴板 const copyToClipboard = () => { if (jsonOutput) { navigator.clipboard.writeText(jsonOutput) .then(() => { setCopied(true); setTimeout(() => setCopied(false), 2000); }) .catch(err => console.error(t('tools.json_formatter.copy_failed'), err)); } }; // 清空输入 const clearInput = () => { // 如果正在处理,先取消 if (isLoading) { cancelFormatting(); } setJsonInput(''); setJsonOutput(''); setErrorMessage(''); setValidationResult({ isValid: false, message: '' }); setPathResult(''); if (jsonInputRef.current) { jsonInputRef.current.focus(); } }; // 处理输入变化 const handleInputChange = (e: React.ChangeEvent) => { const value = e.target.value; setJsonInput(value); }; // 处理粘贴事件 const handlePaste = (e: React.ClipboardEvent) => { // 获取粘贴的内容 const pastedText = e.clipboardData.getData('text'); if (pastedText && pastedText.trim().length > 0) { // 如果正在处理,先取消 if (isLoading) { cancelFormatting(); } // 更新输入内容 setJsonInput(pastedText); // 立即设置加载状态但延迟执行格式化,确保UI更新 setIsLoading(true); processingRef.current = true; setTimeout(() => formatJson(pastedText, isCompressed), 100); } }; // 路径查询变化 const handlePathChange = (e: React.ChangeEvent) => { const value = e.target.value; setJsonPath(value); // 如果有有效的JSON和路径,执行查询 if (value && jsonOutput) { try { const parsed = JSON.parse(jsonOutput); queryJsonPath(parsed, value); } catch (/* eslint-disable-next-line @typescript-eslint/no-unused-vars */ _error) { // JSON解析错误,忽略 } } else { setPathResult(''); } }; // 执行JSONPath查询 const queryJsonPath = (json: Record, path: string) => { if (!path) { setPathResult(''); return; } try { // 简单的路径解析,支持点符号和方括号 const segments = path .replace(/\[(\w+)\]/g, '.$1') // 将[abc]转换为.abc .replace(/^\./, '') // 移除开头的点 .split('.'); let result: unknown = json; for (const segment of segments) { if (typeof result === 'object' && result !== null && segment in result) { result = (result as Record)[segment]; } else { throw new Error(`路径 '${path}' 不存在`); } } // 格式化结果 if (typeof result === 'object' && result !== null) { setPathResult(JSON.stringify(result, null, 2)); } else { setPathResult(String(result)); } } catch (error) { if (error instanceof Error) { setPathResult(`查询错误: ${error.message}`); } } }; // 加载示例JSON const loadExample = () => { // 如果正在处理,先取消 if (isLoading) { cancelFormatting(); } const example = { name: "极速箱", version: "1.0.0", description: "高效开发工具集成平台", author: { name: "JiSuXiang开发团队", email: "support@jisuxiang.com" }, features: [ "JSON格式化与验证", "时间戳转换", "编码转换工具", "正则表达式测试" ], statistics: { tools: 2400, users: 10000000, rating: 4.9 }, isOpenSource: true, lastUpdate: "2063-12-01T08:00:00Z" }; const exampleJson = JSON.stringify(example); setJsonInput(exampleJson); formatJson(exampleJson, isCompressed); }; // 重新格式化 const reformat = () => { // 如果正在处理,先取消 if (isLoading) { cancelFormatting(); } formatJson(jsonInput, isCompressed); }; // 渲染可折叠的JSON const renderFoldableJson = (jsonStr: string) => { if (!jsonStr) return null; try { const jsonObj = JSON.parse(jsonStr); return (
); } catch (/* eslint-disable-next-line @typescript-eslint/no-unused-vars */ _error) { // 如果解析失败,回退到普通模式 return
{jsonStr}
; } }; // 处理输入/输出区域的样式 const getTextareaClasses = (hasError: boolean) => { return `w-full p-4 font-mono text-sm rounded-md border ${ hasError ? 'border-[rgb(var(--color-error))]' : 'border-[rgba(var(--color-primary),0.2)]' } focus:outline-none focus:border-[rgb(var(--color-primary))] focus:ring-1 focus:ring-[rgb(var(--color-primary))] min-h-[350px] transition-all bg-[rgb(var(--color-bg-secondary))] text-[rgb(var(--color-text-primary))]`; }; // 右侧输出区域样式 const outputAreaClasses = `${getTextareaClasses(false)} overflow-auto relative flex-grow json-output-area`; // 工具栏按钮样式 const toolbarButtonClass = "px-3 py-1.5 rounded text-sm flex items-center gap-1 transition-all border border-transparent hover:border-[rgba(var(--color-primary),0.3)]"; // 历史记录项目样式 const historyItemClass = "p-3 rounded-md border border-[rgba(var(--color-primary),0.15)] hover:border-[rgba(var(--color-primary),0.4)] transition-all cursor-pointer bg-[rgb(var(--color-bg-secondary))] flex justify-between items-center mb-2"; // 自动格式化(仅在首次输入更改后) useEffect(() => { if (jsonInput) { formatJson(jsonInput, isCompressed); } }, []); // 保存当前JSON到历史记录 const saveToHistory = () => { if (!jsonOutput || !jsonOutput.trim()) return; if (editingItem) { // 更新现有项目 const updatedItem = { ...editingItem, title: savingTitle || `未命名 ${new Date().toLocaleString()}`, json: jsonOutput, timestamp: Date.now() }; const updatedHistory = historyItems.map(item => item.id === editingItem.id ? updatedItem : item ); setHistoryItems(updatedHistory); saveHistoryToLocalStorage(updatedHistory); } else { // 创建新项目 const newItem: JsonHistoryItem = { id: Date.now().toString(), title: savingTitle || `未命名 ${new Date().toLocaleString()}`, json: jsonOutput, timestamp: Date.now() }; const updatedHistory = [newItem, ...historyItems]; setHistoryItems(updatedHistory); saveHistoryToLocalStorage(updatedHistory); } // 重置状态 setSavingTitle(''); setIsSaveModalOpen(false); setEditingItem(null); }; // 加载历史记录中的JSON const loadFromHistory = (item: JsonHistoryItem) => { setJsonInput(item.json); formatJson(item.json, isCompressed); setIsHistoryOpen(false); }; // 删除历史记录项目 const deleteHistoryItem = (id: string, e: React.MouseEvent) => { e.stopPropagation(); // 防止触发父元素的点击事件 const updatedHistory = historyItems.filter(item => item.id !== id); setHistoryItems(updatedHistory); saveHistoryToLocalStorage(updatedHistory); }; // 编辑历史记录项目标题 const startEditingTitle = (item: JsonHistoryItem, e: React.MouseEvent) => { e.stopPropagation(); // 防止触发父元素的点击事件 setEditingItem(item); setSavingTitle(item.title); setIsSaveModalOpen(true); }; // 切换收藏状态 const toggleFavorite = (id: string, e: React.MouseEvent) => { e.stopPropagation(); // 防止触发父元素的点击事件 const updatedHistory = historyItems.map(item => { if (item.id === id) { return { ...item, isFavorite: !item.isFavorite }; } return item; }); setHistoryItems(updatedHistory); saveHistoryToLocalStorage(updatedHistory); }; return (
{/* 使用自定义样式 */} {/* 使用 ToolHeader 组件 */} {/* 验证状态显示 */}
{validationResult.message && !isLoading && ( {validationResult.message} )} {isLoading && ( {isLargeJson ? t('tools.json_formatter.processing_large_json') : t('tools.json_formatter.parsing_json')} )}
{/* 工具栏 */}
{/* 新增的保存和历史记录按钮 */} {isLoading && ( )}
{/* 主内容区 */}
{/* 输入区域 */}
{t('tools.json_formatter.paste_json_here')}