<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>JSON格式化工具 - EasyUI版</title>
<link rel="stylesheet" type="text/css" href="https://www.jeasyui.com/easyui/themes/default/easyui.css">
<link rel="stylesheet" type="text/css" href="https://www.jeasyui.com/easyui/themes/icon.css">
<script type="text/javascript" src="https://www.jeasyui.com/easyui/jquery.min.js"></script>
<script type="text/javascript" src="https://www.jeasyui.com/easyui/jquery.easyui.min.js"></script>
<style>
body {
margin: 0;
padding: 0;
font-family: "Microsoft YaHei";
background: #f2f2f2;
}
.container {
padding: 10px;
}
.toolbar {
padding: 10px;
background: #fafafa;
border-bottom: 1px solid #ddd;
}
.status-bar {
padding: 5px 10px;
background: #f5f5f5;
color: #666;
border-top: 1px solid #ddd;
}
.code-block {
font-family: Consolas, Monaco, monospace;
font-size: 16px;
line-height: 1.5;
border: 1px solid #ddd;
background: #fff;
padding: 10px;
white-space: pre; /* 确保格式保留 */
}
.json-key { color: #881391; }
.json-string { color: #c41a16; }
.json-number { color: #1c00cf; }
.json-boolean { color: #0d22aa; }
.json-null { color: #777; }
/* 树形视图样式优化 */
.easyui-tree {
font-family: Consolas, Monaco, monospace;
font-size: 13px;
line-height: 1.5;
}
.tree-node {
padding: 2px 0;
}
</style>
</head>
<body class="easyui-layout">
<div data-options="region:'north', split:false" style="height:60px;background:#2d3d50;color:#fff;padding:10px 15px;">
<h2 style="margin:0;"><i class="icon-script"></i> JSON格式化工具</h2>
</div>
<div data-options="region:'center'" style="padding:10px;">
<div class="easyui-panel" title="JSON处理" style="width:100%;height:100%;padding:5px;">
<div class="toolbar">
<a href="javascript:void(0)" class="easyui-linkbutton" iconCls="icon-ok" onclick="formatJson()">格式化</a>
<a href="javascript:void(0)" class="easyui-linkbutton" iconCls="icon-reload" onclick="compressJson()">压缩</a>
<a href="javascript:void(0)" class="easyui-linkbutton" iconCls="icon-search" onclick="validateJson()">验证</a>
<a href="javascript:void(0)" class="easyui-linkbutton" iconCls="icon-clear" onclick="clearAll()">清空</a>
<a href="javascript:void(0)" class="easyui-linkbutton" iconCls="icon-cut" onclick="copyToClipboard()">复制</a>
<a href="javascript:void(0)" class="easyui-linkbutton" iconCls="icon-tip" onclick="toggleTreeView()">树视图</a>
</div>
<div class="easyui-layout" style="width:100%;height:90%;">
<div data-options="region:'west', split:true" style="width:50%;padding:5px;">
<div class="easyui-panel" title="输入" style="width:100%;height:100%;">
<textarea id="jsonInput" style="width:100%;height:95%;border:0;padding:10px;resize:none;"></textarea>
</div>
</div>
<div data-options="region:'center'" style="padding:5px;">
<div class="easyui-tabs" style="width:100%;height:100%;">
<div title="格式化输出" style="padding:5px;">
<pre id="jsonOutput" class="code-block"></pre>
</div>
<div title="树形视图" style="padding:5px;">
<ul id="jsonTree" class="easyui-tree"></ul>
</div>
</div>
</div>
</div>
<div class="status-bar">
<span id="status">就绪</span>
</div>
</div>
</div>
<script>
// 存储原始格式化字符串(用于复制功能)
let rawFormattedJson = '';
// 格式化JSON并添加颜色
function formatJson() {
try {
const input = $('#jsonInput').val();
if (!input.trim()) {
$.messager.alert('提示', '请输入JSON内容', 'info');
return;
}
const obj = JSON.parse(input);
const formatted = JSON.stringify(obj, null, 4);
rawFormattedJson = formatted; // 保存原始格式化字符串
const highlighted = syntaxHighlight(formatted);
$('#jsonOutput').html(highlighted);
$('#status').text('格式化成功');
// 构建树形视图
buildTreeView(obj);
// 切换到格式化输出标签页
$('.easyui-tabs').tabs('select', 0);
} catch (e) {
$.messager.alert('错误', '无效的JSON格式: ' + e.message, 'error');
$('#status').text('格式化失败: ' + e.message);
rawFormattedJson = '';
}
}
// JSON语法高亮
function syntaxHighlight(json) {
json = json.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) {
let cls = 'json-number';
if (/^"/.test(match)) {
cls = /:$/.test(match) ? 'json-key' : 'json-string';
} else if (/true|false/.test(match)) {
cls = 'json-boolean';
} else if (/null/.test(match)) {
cls = 'json-null';
}
return '<span class="' + cls + '">' + match + '</span>';
});
}
// 压缩JSON
function compressJson() {
try {
const input = $('#jsonInput').val();
if (!input.trim()) {
$.messager.alert('提示', '请输入JSON内容', 'info');
return;
}
const obj = JSON.parse(input);
const compressed = JSON.stringify(obj);
rawFormattedJson = compressed;
const highlighted = syntaxHighlight(compressed);
$('#jsonOutput').html(highlighted);
$('#status').text('压缩成功');
// 切换到格式化输出标签页
$('.easyui-tabs').tabs('select', 0);
} catch (e) {
$.messager.alert('错误', '无效的JSON格式: ' + e.message, 'error');
$('#status').text('压缩失败: ' + e.message);
rawFormattedJson = '';
}
}
// 切换树视图
function toggleTreeView() {
const inputVal = $('#jsonInput').val().trim();
if (!inputVal) {
$.messager.alert('提示', '请先输入JSON内容', 'info');
return;
}
try {
const obj = JSON.parse(inputVal);
// 强制重新构建树形视图,确保数据最新
buildTreeView(obj);
$('.easyui-tabs').tabs('select', 1);
// 切换后尝试展开根节点
setTimeout(() => {
const roots = $('#jsonTree').tree('getRoots');
if (roots.length > 0) {
$('#jsonTree').tree('expand', roots[0].target);
}
}, 100); // 短暂延迟,确保DOM已更新
} catch (e) {
$.messager.alert('提示', '请输入有效的JSON格式', 'info');
}
}
// 清空内容
function clearAll() {
$('#jsonInput').val('');
$('#jsonOutput').html('');
$('#jsonTree').tree('loadData', []);
$('#status').text('已清空');
rawFormattedJson = '';
}
// 验证JSON
function validateJson() {
try {
const input = $('#jsonInput').val();
if (!input.trim()) {
$.messager.alert('提示', '请输入JSON内容', 'info');
return;
}
JSON.parse(input);
$.messager.alert('成功', 'JSON格式有效', 'info');
$('#status').text('验证通过');
} catch (e) {
$.messager.alert('错误', '无效的JSON格式: ' + e.message, 'error');
$('#status').text('验证失败: ' + e.message);
}
}
// 复制到剪贴板(优化版)
function copyToClipboard() {
if (rawFormattedJson.trim()) {
copyTextToClipboard(rawFormattedJson);
return;
}
const output = $('#jsonOutput').text();
if (!output.trim()) {
$.messager.alert('提示', '没有可复制的内容', 'info');
return;
}
copyTextToClipboard(output);
}
// 通用复制方法
function copyTextToClipboard(text) {
const textarea = document.createElement('textarea');
textarea.style.position = 'fixed';
textarea.style.top = '-999px';
textarea.style.left = '-999px';
textarea.value = text;
document.body.appendChild(textarea);
textarea.select();
textarea.setSelectionRange(0, text.length); // 兼容移动设备
try {
const successful = document.execCommand('copy');
if (successful) {
$.messager.alert('成功', '已复制到剪贴板', 'info');
$('#status').text('内容已复制');
} else {
throw new Error('复制失败');
}
} catch (e) {
$.messager.alert('错误', '复制失败,请手动复制', 'error');
$('#status').text('复制失败');
} finally {
document.body.removeChild(textarea);
}
}
// 构建树形视图
function buildTreeView(obj) {
try {
const treeNode = convertToTreeData(obj); // 单个根节点对象
// console.log('树形视图数据:', treeNode); // 确认数据结构
$('#jsonTree').tree({
data: [treeNode], // 关键:将单个节点包装成数组
animate: true,
lines: true,
// 确保节点文本中的HTML标签正确渲染(不被转义)
formatter: function(node) {
return node.text;
},
// 简化点击逻辑,直接切换展开/折叠状态
onClick: function(node) {
$(this).tree('toggle', node.target);
}
});
// 自动展开根节点(确保初始化后可见)
setTimeout(() => {
const roots = $('#jsonTree').tree('getRoots');
if (roots.length > 0) {
$('#jsonTree').tree('expand', roots[0].target);
}
}, 100); // 延迟确保DOM已更新
} catch (e) {
console.error('构建树形视图失败:', e);
$.messager.alert('错误', '树形视图加载失败: ' + e.message, 'error');
}
}
// 转换为树形数据(优化版:按值类型显示不同颜色)
function convertToTreeData(obj, name = 'root') {
// 处理基本类型值
if (obj === null || typeof obj !== 'object') {
let valueClass = 'json-number';
let displayValue = obj;
if (typeof obj === 'string') {
valueClass = 'json-string';
displayValue = `"${obj.replace(/"/g, '\\"')}"`; // 转义双引号,避免HTML解析问题
} else if (typeof obj === 'boolean') {
valueClass = 'json-boolean';
} else if (obj === null) {
valueClass = 'json-null';
displayValue = 'null';
}
return {
text: `<span class="json-key">${name}:</span> <span class="${valueClass}">${displayValue}</span>`,
iconCls: 'icon-ok',
state: 'open' // 基本类型无子节点,始终展开
};
}
// 处理数组
if (Array.isArray(obj)) {
const children = obj.map((item, index) => convertToTreeData(item, `[${index}]`));
return {
text: `<span class="json-key">${name}</span> <span class="json-number">(${obj.length}项)</span>`,
iconCls: 'icon-list',
children: children,
state: children.length > 0 ? 'closed' : 'open' // 有子节点默认折叠
};
}
// 处理对象
const keys = Object.keys(obj);
const children = keys.map(key => convertToTreeData(obj[key], key));
return {
text: `<span class="json-key">${name}</span> <span class="json-number">(${keys.length}个属性)</span>`,
iconCls: 'icon-folder',
children: children,
state: children.length > 0 ? 'closed' : 'open' // 有子节点默认折叠
};
}
</script>
</body>
</html>