123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241 |
- <!DOCTYPE html>
- <html>
- <head>
- <title>JSON Editor</title>
- <style>
- body {
- font-family: Arial, sans-serif;
- background-color: #f5f5f5;
- margin: 0;
- padding: 20px;
- }
- .container {
- max-width: 1000px;
- margin: 0 auto;
- background-color: white;
- padding: 30px;
- border-radius: 8px;
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
- margin-bottom: 80px;
- }
- .form-group {
- margin-bottom: 20px;
- padding: 15px;
- border: 1px solid #eee;
- border-radius: 4px;
- background-color: #fafafa;
- }
- .form-group:hover {
- background-color: #f0f0f0;
- }
- .form-group label {
- display: block;
- margin-bottom: 8px;
- font-weight: bold;
- color: #333;
- word-break: break-word;
- }
- .index {
- display: inline-block;
- background-color: #007bff;
- color: white;
- padding: 2px 8px;
- border-radius: 3px;
- margin-right: 8px;
- font-size: 0.9em;
- }
- textarea {
- width: 100%;
- min-height: 50px;
- padding: 10px;
- border: 1px solid #ddd;
- border-radius: 4px;
- font-size: 14px;
- resize: vertical;
- box-sizing: border-box;
- }
- textarea:focus {
- outline: none;
- border-color: #007bff;
- box-shadow: 0 0 0 2px rgba(0,123,255,0.25);
- }
- button {
- background-color: #28a745;
- color: white;
- border: none;
- padding: 12px 24px;
- border-radius: 4px;
- cursor: pointer;
- font-size: 16px;
- transition: background-color 0.2s;
- }
- button:hover {
- background-color: #218838;
- }
- fieldset {
- border: 1px solid #ddd;
- border-radius: 4px;
- padding: 20px;
- margin-bottom: 20px;
- }
- legend {
- font-weight: bold;
- padding: 0 10px;
- color: #333;
- }
- .save-button-container {
- position: fixed;
- bottom: 0;
- left: 0;
- right: 0;
- background-color: rgba(255, 255, 255, 0.9);
- padding: 15px;
- text-align: center;
- box-shadow: 0 -2px 10px rgba(0,0,0,0.1);
- z-index: 1000;
- }
- .reference-text {
- margin-top: 8px;
- padding: 10px;
- background-color: #f8f9fa;
- border: 1px solid #e9ecef;
- border-radius: 4px;
- color: #666;
- font-size: 0.9em;
- }
- .reference-label {
- color: #666;
- font-size: 0.9em;
- margin-bottom: 5px;
- font-style: italic;
- }
- </style>
- </head>
- <body>
- <div class="container">
- <h1>JSON 编辑器</h1>
- <div id="editor"></div>
- </div>
- <div class="save-button-container">
- <button onclick="saveChanges()">保存更改</button>
- </div>
-
- <script>
- let jsonData = {};
- let referenceData = {};
- let indexCounter = 1;
-
- // 创建编辑表单
- function createForm(data, parentKey = '') {
- const container = document.createElement('div');
-
- for (const [key, value] of Object.entries(data)) {
- const currentKey = parentKey ? `${parentKey}.${key}` : key;
-
- if (typeof value === 'object' && value !== null) {
- const fieldset = document.createElement('fieldset');
- fieldset.innerHTML = `<legend>${key}</legend>`;
- fieldset.appendChild(createForm(value, currentKey));
- container.appendChild(fieldset);
- } else {
- const formGroup = document.createElement('div');
- formGroup.className = 'form-group';
-
- // 获取对应的参照文本
- const referenceValue = getNestedValue(referenceData, currentKey);
- const referenceHtml = referenceValue ? `
- <div class="reference-label">参照文本:</div>
- <div class="reference-text">${referenceValue}</div>
- ` : '';
-
- formGroup.innerHTML = `
- <label><span class="index">${indexCounter}</span>${key}:</label>
- <textarea data-key="${currentKey}">${value}</textarea>
- ${referenceHtml}
- `;
- container.appendChild(formGroup);
- indexCounter++;
- }
- }
-
- return container;
- }
-
- // 获取嵌套对象的值
- function getNestedValue(obj, path) {
- return path.split('.').reduce((current, key) =>
- current ? current[key] : undefined, obj);
- }
-
- // 获取数据并显示
- async function loadData() {
- try {
- // 并行加载两个文件
- const [dataResponse, referenceResponse] = await Promise.all([
- fetch('/api/data'),
- fetch('/api/reference')
- ]);
-
- jsonData = await dataResponse.json();
- referenceData = await referenceResponse.json();
-
- const editor = document.getElementById('editor');
- editor.innerHTML = '';
- indexCounter = 1;
- editor.appendChild(createForm(jsonData));
- } catch (error) {
- console.error('Error loading data:', error);
- alert('加载数据失败!');
- }
- }
-
- // 保存更改
- async function saveChanges() {
- const textareas = document.querySelectorAll('textarea');
- let updatedData = JSON.parse(JSON.stringify(jsonData));
-
- textareas.forEach(textarea => {
- const path = textarea.dataset.key.split('.');
- let current = updatedData;
-
- // 确保路径存在
- for (let i = 0; i < path.length - 1; i++) {
- if (current[path[i]] === undefined) {
- current[path[i]] = {};
- }
- current = current[path[i]];
- }
-
- // 安全地设置值
- if (current && path.length > 0) {
- const lastKey = path[path.length - 1];
- current[lastKey] = textarea.value;
- }
- });
-
- try {
- const response = await fetch('/api/save', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify(updatedData)
- });
-
- const result = await response.json();
- if (result.success) {
- alert('保存成功!');
- } else {
- alert('保存失败:' + (result.error || '未知错误'));
- }
- } catch (error) {
- console.error('Error saving data:', error);
- alert('保存失败:' + error.message);
- }
- }
-
- // 页面加载时获取数据
- loadData();
- </script>
- </body>
- </html>
|