Files
Sprout-Farm/Server/JsonEdit/templates/json_editor.html

627 lines
21 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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', '[&quot;新&quot;, &quot;热门&quot;]')">
数组示例
</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>