627 lines
21 KiB
HTML
627 lines
21 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>简易JSON编辑器 - 批量添加键值</title>
|
||
<style>
|
||
* {
|
||
margin: 0;
|
||
padding: 0;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
body {
|
||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||
background: #f5f5f5;
|
||
padding: 20px;
|
||
}
|
||
|
||
.container {
|
||
max-width: 1200px;
|
||
margin: 0 auto;
|
||
background: white;
|
||
border-radius: 10px;
|
||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.header {
|
||
background: #4a90e2;
|
||
color: white;
|
||
padding: 20px;
|
||
text-align: center;
|
||
}
|
||
|
||
.header h1 {
|
||
margin-bottom: 5px;
|
||
}
|
||
|
||
.main-content {
|
||
display: grid;
|
||
grid-template-columns: 300px 1fr;
|
||
gap: 0;
|
||
min-height: 600px;
|
||
}
|
||
|
||
.sidebar {
|
||
background: #f8f9fa;
|
||
border-right: 1px solid #dee2e6;
|
||
padding: 20px;
|
||
}
|
||
|
||
.editor-area {
|
||
padding: 20px;
|
||
}
|
||
|
||
.section {
|
||
background: white;
|
||
border: 1px solid #dee2e6;
|
||
border-radius: 5px;
|
||
padding: 15px;
|
||
margin-bottom: 15px;
|
||
}
|
||
|
||
.section h3 {
|
||
margin-bottom: 15px;
|
||
color: #333;
|
||
}
|
||
|
||
.btn {
|
||
background: #4a90e2;
|
||
color: white;
|
||
border: none;
|
||
padding: 10px 20px;
|
||
border-radius: 5px;
|
||
cursor: pointer;
|
||
font-size: 14px;
|
||
margin: 5px 5px 5px 0;
|
||
width: 100%;
|
||
}
|
||
|
||
.btn:hover {
|
||
background: #357abd;
|
||
}
|
||
|
||
.btn-success {
|
||
background: #28a745;
|
||
}
|
||
|
||
.btn-success:hover {
|
||
background: #218838;
|
||
}
|
||
|
||
.input-group {
|
||
margin-bottom: 15px;
|
||
}
|
||
|
||
.input-group label {
|
||
display: block;
|
||
margin-bottom: 5px;
|
||
font-weight: 500;
|
||
color: #333;
|
||
}
|
||
|
||
.input-group input {
|
||
width: 100%;
|
||
padding: 8px 12px;
|
||
border: 1px solid #dee2e6;
|
||
border-radius: 5px;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.input-group input:focus {
|
||
outline: none;
|
||
border-color: #4a90e2;
|
||
}
|
||
|
||
#jsonEditor {
|
||
width: 100%;
|
||
height: 500px;
|
||
border: 1px solid #dee2e6;
|
||
border-radius: 5px;
|
||
padding: 15px;
|
||
font-family: 'Courier New', monospace;
|
||
font-size: 14px;
|
||
line-height: 1.5;
|
||
resize: vertical;
|
||
}
|
||
|
||
#jsonEditor:focus {
|
||
outline: none;
|
||
border-color: #4a90e2;
|
||
}
|
||
|
||
.file-upload {
|
||
position: relative;
|
||
display: inline-block;
|
||
cursor: pointer;
|
||
overflow: hidden;
|
||
width: 100%;
|
||
}
|
||
|
||
.file-upload input[type=file] {
|
||
position: absolute;
|
||
left: -9999px;
|
||
}
|
||
|
||
.alert {
|
||
padding: 10px 15px;
|
||
margin-bottom: 15px;
|
||
border-radius: 5px;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.alert-success {
|
||
background: #d4edda;
|
||
color: #155724;
|
||
border: 1px solid #c3e6cb;
|
||
}
|
||
|
||
.alert-error {
|
||
background: #f8d7da;
|
||
color: #721c24;
|
||
border: 1px solid #f5c6cb;
|
||
}
|
||
|
||
.alert-info {
|
||
background: #d1ecf1;
|
||
color: #0c5460;
|
||
border: 1px solid #bee5eb;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.main-content {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
<div class="header">
|
||
<h1>🔧 JSON批量编辑器</h1>
|
||
<p>批量添加键值对到JSON文件</p>
|
||
</div>
|
||
|
||
<div class="main-content">
|
||
<!-- 侧边栏 -->
|
||
<div class="sidebar">
|
||
<!-- 文件操作 -->
|
||
<div class="section">
|
||
<h3>📁 文件操作</h3>
|
||
<div class="file-upload btn">
|
||
<input type="file" id="fileInput" accept=".json" />
|
||
上传JSON文件
|
||
</div>
|
||
<button class="btn btn-success" onclick="downloadJSON()">
|
||
下载修改后的JSON
|
||
</button>
|
||
</div>
|
||
|
||
<!-- 批量添加键值 -->
|
||
<div class="section">
|
||
<h3>⚡ 批量添加键值</h3>
|
||
<div class="input-group">
|
||
<label for="keyName">键名:</label>
|
||
<input type="text" id="keyName" placeholder="例: 能否购买" />
|
||
</div>
|
||
<div class="input-group">
|
||
<label for="keyValue">键值:</label>
|
||
<input type="text" id="keyValue" placeholder="支持多种类型,见下方说明" />
|
||
</div>
|
||
<button class="btn btn-success" onclick="batchAddProperty()">
|
||
批量添加到所有对象
|
||
</button>
|
||
|
||
<div style="margin-top: 15px; padding: 10px; background: #f8f9fa; border-radius: 5px; font-size: 12px;">
|
||
<strong>支持的数据类型:</strong><br>
|
||
• 字符串: hello world<br>
|
||
• 数字: 123 或 3.14<br>
|
||
• 布尔值: true 或 false<br>
|
||
• 空值: null<br>
|
||
• 对象: {"key": "value"}<br>
|
||
• 数组: [1, 2, 3]<br>
|
||
<small style="color: #666;">系统会自动识别并转换数据类型</small>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 快速示例 -->
|
||
<div class="section">
|
||
<h3>📝 快速示例</h3>
|
||
<button class="btn" onclick="loadSampleJSON()">
|
||
加载示例数据
|
||
</button>
|
||
|
||
<div style="margin-top: 15px;">
|
||
<strong style="font-size: 12px;">快速填入示例键值:</strong><br>
|
||
<button class="btn" style="font-size: 11px; padding: 5px 10px; margin: 2px;" onclick="fillExample('能否购买', 'true')">
|
||
布尔值示例
|
||
</button>
|
||
<button class="btn" style="font-size: 11px; padding: 5px 10px; margin: 2px;" onclick="fillExample('价格', '150')">
|
||
数字示例
|
||
</button>
|
||
<button class="btn" style="font-size: 11px; padding: 5px 10px; margin: 2px;" onclick="fillExample('备注', '新增属性')">
|
||
字符串示例
|
||
</button>
|
||
<button class="btn" style="font-size: 11px; padding: 5px 10px; margin: 2px;" onclick="fillExample('tags', '["新", "热门"]')">
|
||
数组示例
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- JSON格式化操作 -->
|
||
<div class="section">
|
||
<h3>🔧 格式化操作</h3>
|
||
<button class="btn" onclick="formatJSONStandard()">
|
||
标准格式化
|
||
</button>
|
||
<button class="btn btn-success" onclick="minifyJSON()">
|
||
最小化(压缩)
|
||
</button>
|
||
<button class="btn" style="background: #17a2b8;" onclick="oneLinePerObject()">
|
||
一行化(一个对象一行)
|
||
</button>
|
||
<button class="btn" style="background: #6f42c1; color: white;" onclick="validateJSON()">
|
||
验证JSON格式
|
||
</button>
|
||
|
||
<div style="margin-top: 15px; padding: 10px; background: #f8f9fa; border-radius: 5px; font-size: 12px;">
|
||
<strong>格式化说明:</strong><br>
|
||
• <strong>标准格式化</strong>: 2空格缩进,易于阅读<br>
|
||
• <strong>最小化</strong>: 去除空格,节省空间<br>
|
||
• <strong>一行化</strong>: 每个对象占一行,便于比较<br>
|
||
• <strong>验证格式</strong>: 检查JSON语法是否正确<br>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 编辑区域 -->
|
||
<div class="editor-area">
|
||
<!-- 消息区域 -->
|
||
<div id="messageArea"></div>
|
||
|
||
<!-- JSON编辑器 -->
|
||
<textarea id="jsonEditor" placeholder="在此输入或上传JSON数据..."></textarea>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
// 初始化
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
document.getElementById('fileInput').addEventListener('change', handleFileUpload);
|
||
});
|
||
|
||
// 显示消息
|
||
function showMessage(message, type = 'success') {
|
||
const messageArea = document.getElementById('messageArea');
|
||
let alertClass = 'alert-success';
|
||
|
||
if (type === 'error') {
|
||
alertClass = 'alert-error';
|
||
} else if (type === 'info') {
|
||
alertClass = 'alert-info';
|
||
}
|
||
|
||
messageArea.innerHTML = `
|
||
<div class="alert ${alertClass}">
|
||
${message}
|
||
</div>
|
||
`;
|
||
|
||
setTimeout(() => {
|
||
messageArea.innerHTML = '';
|
||
}, 3000);
|
||
}
|
||
|
||
// 加载示例JSON
|
||
function loadSampleJSON() {
|
||
const sampleJSON = {
|
||
"测试作物": {
|
||
"花费": 1,
|
||
"生长时间": 3,
|
||
"收益": 9999,
|
||
"品质": "普通"
|
||
},
|
||
"小麦": {
|
||
"花费": 120,
|
||
"生长时间": 120,
|
||
"收益": 100,
|
||
"品质": "普通"
|
||
},
|
||
"稻谷": {
|
||
"花费": 100,
|
||
"生长时间": 240,
|
||
"收益": 120,
|
||
"品质": "普通"
|
||
}
|
||
};
|
||
|
||
document.getElementById('jsonEditor').value = JSON.stringify(sampleJSON, null, 2);
|
||
showMessage('示例数据已加载');
|
||
}
|
||
|
||
// 快速填入示例键值对
|
||
function fillExample(keyName, keyValue) {
|
||
document.getElementById('keyName').value = keyName;
|
||
document.getElementById('keyValue').value = keyValue;
|
||
showMessage(`已填入示例: ${keyName} = ${keyValue}`, 'info');
|
||
}
|
||
|
||
// 文件上传处理
|
||
function handleFileUpload(event) {
|
||
const file = event.target.files[0];
|
||
if (!file) return;
|
||
|
||
if (!file.name.toLowerCase().endsWith('.json')) {
|
||
showMessage('请选择JSON文件', 'error');
|
||
return;
|
||
}
|
||
|
||
const reader = new FileReader();
|
||
reader.onload = function(e) {
|
||
try {
|
||
const content = e.target.result;
|
||
JSON.parse(content); // 验证JSON格式
|
||
document.getElementById('jsonEditor').value = content;
|
||
showMessage('文件上传成功');
|
||
} catch (error) {
|
||
showMessage('JSON格式错误: ' + error.message, 'error');
|
||
}
|
||
};
|
||
reader.readAsText(file);
|
||
|
||
// 清空文件输入
|
||
event.target.value = '';
|
||
}
|
||
|
||
// 批量添加属性
|
||
function batchAddProperty() {
|
||
const keyName = document.getElementById('keyName').value.trim();
|
||
const keyValue = document.getElementById('keyValue').value.trim();
|
||
const jsonContent = document.getElementById('jsonEditor').value.trim();
|
||
|
||
if (!keyName) {
|
||
showMessage('请输入键名', 'error');
|
||
return;
|
||
}
|
||
|
||
if (!jsonContent) {
|
||
showMessage('请输入或上传JSON数据', 'error');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
let jsonData = JSON.parse(jsonContent);
|
||
|
||
// 智能类型转换
|
||
let processedValue = parseValue(keyValue);
|
||
|
||
// 批量添加属性
|
||
const result = addPropertyToAllObjects(jsonData, keyName, processedValue);
|
||
|
||
if (result.count > 0) {
|
||
document.getElementById('jsonEditor').value = JSON.stringify(jsonData, null, 2);
|
||
showMessage(`成功为 ${result.count} 个对象添加了属性 "${keyName}": ${JSON.stringify(processedValue)}`);
|
||
} else {
|
||
showMessage('未找到可添加属性的对象', 'info');
|
||
}
|
||
|
||
} catch (error) {
|
||
showMessage('JSON格式错误: ' + error.message, 'error');
|
||
}
|
||
}
|
||
|
||
// 智能解析值的类型
|
||
function parseValue(value) {
|
||
// 如果输入为空,返回空字符串
|
||
if (value === '') {
|
||
return '';
|
||
}
|
||
|
||
// 尝试解析为null
|
||
if (value.toLowerCase() === 'null') {
|
||
return null;
|
||
}
|
||
|
||
// 尝试解析为undefined(虽然JSON不支持,但转为null)
|
||
if (value.toLowerCase() === 'undefined') {
|
||
return null;
|
||
}
|
||
|
||
// 尝试解析为布尔值
|
||
if (value.toLowerCase() === 'true') {
|
||
return true;
|
||
}
|
||
if (value.toLowerCase() === 'false') {
|
||
return false;
|
||
}
|
||
|
||
// 尝试解析为数字
|
||
if (!isNaN(value) && !isNaN(parseFloat(value))) {
|
||
// 检查是否为整数
|
||
if (Number.isInteger(parseFloat(value))) {
|
||
return parseInt(value, 10);
|
||
} else {
|
||
return parseFloat(value);
|
||
}
|
||
}
|
||
|
||
// 尝试解析为JSON对象或数组
|
||
if ((value.startsWith('{') && value.endsWith('}')) ||
|
||
(value.startsWith('[') && value.endsWith(']'))) {
|
||
try {
|
||
return JSON.parse(value);
|
||
} catch (e) {
|
||
// 如果解析失败,当作字符串处理
|
||
return value;
|
||
}
|
||
}
|
||
|
||
// 默认当作字符串处理
|
||
return value;
|
||
}
|
||
|
||
// 递归为所有对象添加属性
|
||
function addPropertyToAllObjects(obj, key, value) {
|
||
let count = 0;
|
||
|
||
function traverse(current) {
|
||
if (typeof current === 'object' && current !== null) {
|
||
if (Array.isArray(current)) {
|
||
// 处理数组
|
||
current.forEach(item => traverse(item));
|
||
} else {
|
||
// 处理对象
|
||
current[key] = value;
|
||
count++;
|
||
|
||
// 继续递归处理嵌套对象
|
||
Object.values(current).forEach(val => {
|
||
if (typeof val === 'object' && val !== null && val !== current) {
|
||
traverse(val);
|
||
}
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
traverse(obj);
|
||
return { count };
|
||
}
|
||
|
||
// 下载JSON
|
||
function downloadJSON() {
|
||
const content = document.getElementById('jsonEditor').value.trim();
|
||
|
||
if (!content) {
|
||
showMessage('没有可下载的内容', 'error');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
// 验证JSON格式
|
||
JSON.parse(content);
|
||
|
||
const blob = new Blob([content], { type: 'application/json' });
|
||
const url = URL.createObjectURL(blob);
|
||
const link = document.createElement('a');
|
||
link.href = url;
|
||
link.download = `edited_json_${new Date().getTime()}.json`;
|
||
document.body.appendChild(link);
|
||
link.click();
|
||
document.body.removeChild(link);
|
||
URL.revokeObjectURL(url);
|
||
|
||
showMessage('JSON文件下载成功');
|
||
} catch (error) {
|
||
showMessage('JSON格式错误,无法下载: ' + error.message, 'error');
|
||
}
|
||
}
|
||
|
||
// 标准格式化JSON
|
||
function formatJSONStandard() {
|
||
const content = document.getElementById('jsonEditor').value.trim();
|
||
|
||
if (!content) {
|
||
showMessage('请输入JSON数据', 'error');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const jsonData = JSON.parse(content);
|
||
const formatted = JSON.stringify(jsonData, null, 2);
|
||
document.getElementById('jsonEditor').value = formatted;
|
||
showMessage('JSON标准格式化完成');
|
||
} catch (error) {
|
||
showMessage('JSON格式错误: ' + error.message, 'error');
|
||
}
|
||
}
|
||
|
||
// 最小化JSON(压缩)
|
||
function minifyJSON() {
|
||
const content = document.getElementById('jsonEditor').value.trim();
|
||
|
||
if (!content) {
|
||
showMessage('请输入JSON数据', 'error');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const jsonData = JSON.parse(content);
|
||
const minified = JSON.stringify(jsonData);
|
||
document.getElementById('jsonEditor').value = minified;
|
||
showMessage('JSON最小化完成');
|
||
} catch (error) {
|
||
showMessage('JSON格式错误: ' + error.message, 'error');
|
||
}
|
||
}
|
||
|
||
// 一行化格式(一个对象一行)
|
||
function oneLinePerObject() {
|
||
const content = document.getElementById('jsonEditor').value.trim();
|
||
|
||
if (!content) {
|
||
showMessage('请输入JSON数据', 'error');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const jsonData = JSON.parse(content);
|
||
let formatted = '';
|
||
|
||
if (Array.isArray(jsonData)) {
|
||
// 如果是数组,每个元素占一行
|
||
formatted = '[\n';
|
||
jsonData.forEach((item, index) => {
|
||
formatted += ' ' + JSON.stringify(item);
|
||
if (index < jsonData.length - 1) {
|
||
formatted += ',';
|
||
}
|
||
formatted += '\n';
|
||
});
|
||
formatted += ']';
|
||
} else if (typeof jsonData === 'object' && jsonData !== null) {
|
||
// 如果是对象,每个键值对占一行
|
||
formatted = '{\n';
|
||
const keys = Object.keys(jsonData);
|
||
keys.forEach((key, index) => {
|
||
formatted += ' ' + JSON.stringify(key) + ': ' + JSON.stringify(jsonData[key]);
|
||
if (index < keys.length - 1) {
|
||
formatted += ',';
|
||
}
|
||
formatted += '\n';
|
||
});
|
||
formatted += '}';
|
||
} else {
|
||
// 基本类型直接输出
|
||
formatted = JSON.stringify(jsonData);
|
||
}
|
||
|
||
document.getElementById('jsonEditor').value = formatted;
|
||
showMessage('JSON一行化格式完成');
|
||
} catch (error) {
|
||
showMessage('JSON格式错误: ' + error.message, 'error');
|
||
}
|
||
}
|
||
|
||
// 验证JSON格式
|
||
function validateJSON() {
|
||
const content = document.getElementById('jsonEditor').value.trim();
|
||
|
||
if (!content) {
|
||
showMessage('请输入JSON数据', 'error');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
JSON.parse(content);
|
||
showMessage('JSON格式验证通过');
|
||
} catch (error) {
|
||
showMessage('JSON格式错误: ' + error.message, 'error');
|
||
}
|
||
}
|
||
</script>
|
||
</body>
|
||
</html> |