'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, faEraser } 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); }; // 移除JSON中的转义斜杠 const removeSlashes = () => { if (!jsonInput) return; try { // 直接在原始输入字符串上移除转义斜杠 const processed = jsonInput.replace(/\\\//g, '/'); // 检查是否有变化 if (processed === jsonInput) { console.log('没有检测到需要替换的内容,JSON未改变'); return; } console.log('移除斜杠前:', jsonInput); console.log('移除斜杠后:', processed); // 更新输入框而不是直接格式化 setJsonInput(processed); // 手动触发格式化 setTimeout(() => formatJson(processed, isCompressed), 100); } catch (error) { console.error('移除斜杠处理失败:', error); } }; // 字符串转义 const escapeString = () => { if (!jsonInput) return; try { // 将常见字符转义为JSON字符串中的格式 const processed = jsonInput .replace(/\\/g, '\\\\') // 先转义反斜杠 .replace(/"/g, '\\"') // 转义双引号 .replace(/\n/g, '\\n') // 转义换行符 .replace(/\r/g, '\\r') // 转义回车符 .replace(/\t/g, '\\t') // 转义制表符 .replace(/\f/g, '\\f') // 转义换页符 .replace(/\b/g, '\\b'); // 转义退格符 // 检查是否有变化 if (processed === jsonInput) { console.log('没有检测到需要转义的内容'); return; } // 更新输入框 setJsonInput(processed); // 不需要立即格式化,因为用户可能还需要进一步编辑 } catch (error) { console.error('字符串转义处理失败:', error); } }; // 字符串反转义 const unescapeString = () => { if (!jsonInput) return; try { // 将JSON字符串中的转义字符还原为原始字符 const processed = jsonInput .replace(/\\"/g, '"') // 反转义双引号 .replace(/\\n/g, '\n') // 反转义换行符 .replace(/\\r/g, '\r') // 反转义回车符 .replace(/\\t/g, '\t') // 反转义制表符 .replace(/\\f/g, '\f') // 反转义换页符 .replace(/\\b/g, '\b') // 反转义退格符 .replace(/\\\\/g, '\\'); // 最后反转义反斜杠 // 检查是否有变化 if (processed === jsonInput) { console.log('没有检测到需要反转义的内容'); return; } // 更新输入框 setJsonInput(processed); // 不需要立即格式化,因为用户可能还需要进一步编辑 } catch (error) { console.error('字符串反转义处理失败:', error); } }; // 复制结果到剪贴板 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')}