refactor: 改为 VSCode 风格的格式化 JSON 展示
改进: - 深色主题背景 (#1e1e1e) - VSCode 配色方案(键名蓝色、字符串橙色、数字绿色等) - 自动缩进 2 空格 - 完整展示所有数据,无折叠 - 类似代码编辑器的格式化效果 - 简洁清晰,易于阅读
This commit is contained in:
+48
-216
@@ -275,131 +275,47 @@
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
/* JSON 卡片式展示 */
|
||||
/* JSON 格式化展示 */
|
||||
.json-viewer {
|
||||
background: #f8f9fa;
|
||||
background: #1e1e1e;
|
||||
border-radius: 8px;
|
||||
padding: 0;
|
||||
padding: 20px;
|
||||
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
overflow: auto;
|
||||
max-height: 60vh;
|
||||
overflow-y: auto;
|
||||
color: #d4d4d4;
|
||||
}
|
||||
|
||||
.json-section {
|
||||
margin-bottom: 10px;
|
||||
.json-viewer pre {
|
||||
margin: 0;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.json-section-title {
|
||||
background: #667eea;
|
||||
color: white;
|
||||
padding: 12px 20px;
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
border-radius: 8px 8px 0 0;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
user-select: none;
|
||||
.json-key {
|
||||
color: #9cdcfe;
|
||||
}
|
||||
|
||||
.json-section-title:hover {
|
||||
background: #5568d3;
|
||||
.json-string {
|
||||
color: #ce9178;
|
||||
}
|
||||
|
||||
.json-section-toggle {
|
||||
font-size: 12px;
|
||||
.json-number {
|
||||
color: #b5cea8;
|
||||
}
|
||||
|
||||
.json-section-body {
|
||||
background: white;
|
||||
border-radius: 0 0 8px 8px;
|
||||
.json-boolean {
|
||||
color: #569cd6;
|
||||
}
|
||||
|
||||
.json-section-body.collapsed {
|
||||
display: none;
|
||||
.json-null {
|
||||
color: #569cd6;
|
||||
}
|
||||
|
||||
.json-field {
|
||||
display: flex;
|
||||
padding: 12px 20px;
|
||||
border-bottom: 1px solid #eee;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.json-field:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.json-field:hover {
|
||||
background: #f8f9fa;
|
||||
}
|
||||
|
||||
.json-field-label {
|
||||
min-width: 200px;
|
||||
font-weight: bold;
|
||||
color: #0066cc;
|
||||
padding-right: 20px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.json-field-value {
|
||||
flex: 1;
|
||||
word-break: break-all;
|
||||
color: #333;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.json-field-value.string {
|
||||
color: #22863a;
|
||||
}
|
||||
|
||||
.json-field-value.number {
|
||||
color: #005cc5;
|
||||
}
|
||||
|
||||
.json-field-value.boolean {
|
||||
color: #d73a49;
|
||||
}
|
||||
|
||||
.json-field-value.null {
|
||||
color: #6f42c1;
|
||||
}
|
||||
|
||||
.json-field-actions {
|
||||
margin-left: 10px;
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.json-copy-btn {
|
||||
padding: 4px 10px;
|
||||
font-size: 12px;
|
||||
background: #667eea;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.json-copy-btn:hover {
|
||||
background: #5568d3;
|
||||
}
|
||||
|
||||
.json-raw-btn {
|
||||
padding: 4px 10px;
|
||||
font-size: 12px;
|
||||
background: #6c757d;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.json-raw-btn:hover {
|
||||
background: #5a6268;
|
||||
.json-punctuation {
|
||||
color: #d4d4d4;
|
||||
}
|
||||
|
||||
/* Toast 通知 */
|
||||
@@ -619,116 +535,32 @@
|
||||
document.getElementById(modalId).classList.remove('active');
|
||||
}
|
||||
|
||||
// JSON 卡片式渲染
|
||||
function renderJSONCard(obj, parentPath = '') {
|
||||
let html = '';
|
||||
// JSON 格式化高亮
|
||||
function formatJSON(obj) {
|
||||
const json = JSON.stringify(obj, null, 2);
|
||||
|
||||
if (typeof obj !== 'object' || obj === null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
const path = parentPath ? `${parentPath}.${key}` : key;
|
||||
|
||||
if (value !== null && typeof value === 'object') {
|
||||
// 对象或数组 - 创建折叠区域
|
||||
const isArray = Array.isArray(value);
|
||||
const itemCount = isArray ? value.length : Object.keys(value).length;
|
||||
const sectionId = 'section-' + Math.random().toString(36).substr(2, 9);
|
||||
|
||||
html += `
|
||||
<div class="json-section">
|
||||
<div class="json-section-title" onclick="toggleSection('${sectionId}')">
|
||||
<span>${key} ${isArray ? `[${itemCount} items]` : `{${itemCount} keys}`}</span>
|
||||
<span class="json-section-toggle">▼</span>
|
||||
</div>
|
||||
<div class="json-section-body" id="${sectionId}">
|
||||
${renderJSONFields(value, path)}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
function renderJSONFields(obj, parentPath = '') {
|
||||
let html = '';
|
||||
|
||||
if (typeof obj !== 'object' || obj === null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
const path = parentPath ? `${parentPath}.${key}` : key;
|
||||
|
||||
if (value === null || typeof value !== 'object') {
|
||||
// 基本类型 - 渲染字段
|
||||
const typeClass = value === null ? 'null' : typeof value;
|
||||
let displayValue = '';
|
||||
let copyValue = '';
|
||||
|
||||
if (value === null) {
|
||||
displayValue = 'null';
|
||||
copyValue = 'null';
|
||||
} else if (typeof value === 'string') {
|
||||
displayValue = value;
|
||||
copyValue = value;
|
||||
} else if (typeof value === 'number' || typeof value === 'boolean') {
|
||||
displayValue = String(value);
|
||||
copyValue = String(value);
|
||||
// 语法高亮
|
||||
const highlighted = json
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.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)) {
|
||||
if (/:$/.test(match)) {
|
||||
cls = 'json-key';
|
||||
} else {
|
||||
cls = '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>';
|
||||
});
|
||||
|
||||
html += `
|
||||
<div class="json-field">
|
||||
<div class="json-field-label">${escapeHtml(key)}</div>
|
||||
<div class="json-field-value ${typeClass}">${escapeHtml(displayValue)}</div>
|
||||
<div class="json-field-actions">
|
||||
<button class="json-copy-btn" onclick="copyValue(\`${escapeHtml(copyValue).replace(/`/g, '\\`')}\`)">复制</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
} else {
|
||||
// 嵌套对象/数组 - 递归渲染
|
||||
html += renderJSONCard({[key]: value}, parentPath);
|
||||
}
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
// HTML 转义
|
||||
function escapeHtml(str) {
|
||||
if (typeof str !== 'string') {
|
||||
str = String(str);
|
||||
}
|
||||
const div = document.createElement('div');
|
||||
div.textContent = str;
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
// 切换区域折叠/展开
|
||||
function toggleSection(id) {
|
||||
const element = document.getElementById(id);
|
||||
const toggle = element.previousElementSibling.querySelector('.json-section-toggle');
|
||||
|
||||
if (element.classList.contains('collapsed')) {
|
||||
element.classList.remove('collapsed');
|
||||
toggle.textContent = '▼';
|
||||
} else {
|
||||
element.classList.add('collapsed');
|
||||
toggle.textContent = '▶';
|
||||
}
|
||||
}
|
||||
|
||||
// 复制单个值
|
||||
function copyValue(value) {
|
||||
navigator.clipboard.writeText(value).then(() => {
|
||||
showToast('已复制');
|
||||
}).catch(() => {
|
||||
showToast('复制失败', 'error');
|
||||
});
|
||||
return '<pre>' + highlighted + '</pre>';
|
||||
}
|
||||
|
||||
// 加载数据列表
|
||||
@@ -833,7 +665,7 @@
|
||||
currentViewData = data;
|
||||
|
||||
document.getElementById('view-modal-title').textContent = `查看数据 - ${tag}`;
|
||||
document.getElementById('json-content').innerHTML = renderJSONCard(data);
|
||||
document.getElementById('json-content').innerHTML = formatJSON(data);
|
||||
|
||||
openModal('view-modal');
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user