class InterfaceConfig extends HTMLElement { constructor() { super(); this.attachShadow({ mode: 'open' }); this.idlContent = ''; this.filePath = ''; this.isEdited = false; } connectedCallback() { this.render(); this.addEventListeners(); // 加载IDL文件列表到下拉框 this.loadIdlFileList(); } addEventListeners() { // 新建文件按钮 this.shadowRoot.getElementById('new-file').addEventListener('click', () => this.handleNewFile()); // 文件选择下拉框 this.shadowRoot.getElementById('file-select-dropdown').addEventListener('change', (e) => { if (e.target.value) { this.handleFileChange(e.target.value); } }); // 保存按钮 this.shadowRoot.getElementById('save-button').addEventListener('click', () => this.saveFile()); // 另存为按钮 this.shadowRoot.getElementById('save-as-button').addEventListener('click', () => this.saveFileAs()); // 文本编辑器内容变化时更新可视化编辑器和编辑状态 this.shadowRoot.getElementById('idl-editor').addEventListener('input', () => { this.idlContent = this.shadowRoot.getElementById('idl-editor').value; this.updateVisualEditor(); this.setEditedState(true); }); } async selectFile() { try { // 获取IDL文件列表 const response = await fetch('/api/idl/list'); if (!response.ok) { const error = await response.json(); throw new Error(error.error || '获取文件列表失败'); } const data = await response.json(); // 显示文件选择对话框 if (!data.files || data.files.length === 0) { this.showConfirmDialog('文件列表', '没有可用的IDL文件', null); return; } // 创建文件选择对话框 const fileSelector = document.createElement('div'); fileSelector.className = 'method-dialog'; const fileListHtml = data.files.map(file => `
${file.name} 大小: ${this.formatFileSize(file.size)}, 修改时间: ${new Date(file.modified).toLocaleString()}
`).join(''); fileSelector.innerHTML = `

选择IDL文件

${fileListHtml}
`; this.shadowRoot.appendChild(fileSelector); // 添加文件项点击事件 fileSelector.querySelectorAll('.file-item').forEach(item => { item.addEventListener('click', async () => { const filename = item.dataset.filename; fileSelector.remove(); // 加载选定的文件 await this.loadFile(filename); }); }); // 对话框取消事件 this.shadowRoot.getElementById('dialog-cancel').addEventListener('click', () => { fileSelector.remove(); }); } catch (error) { // console.error('选择文件时出错:', error); // this.showConfirmDialog('选择文件错误', '选择文件时出错: ' + error.message, null); } } parseIdlContent() { // 针对XNAerodynamics.idl文件格式优化 const editor = this.shadowRoot.getElementById('idl-editor'); editor.value = this.idlContent; // 更新可视化编辑区域 this.updateVisualEditor(); } updateVisualEditor() { // 从文本解析IDL结构 const visualEditor = this.shadowRoot.getElementById('visual-editor'); // 清空可视化编辑器 if (visualEditor) { visualEditor.innerHTML = ''; } else { console.error('找不到visual-editor元素'); return; } if (!this.idlContent || this.idlContent.trim() === '') { visualEditor.innerHTML += '
无内容可显示
'; return; } try { // 预处理阶段使用更精确的正则表达式,避免破坏嵌套结构 let processedContent = this.idlContent .replace(/\r\n/g, '\n') // 统一换行符 .replace(/\r/g, '\n') // 兼容不同系统 .replace(/\/\/.*$/gm, '') // 移除注释 .replace(/\s+/g, ' ') // 压缩多余空白字符 .trim(); // 使用递归下降解析器解析IDL结构 const rootModules = this.parseModules(processedContent); // 创建模块树结构 const moduleTree = document.createElement('div'); moduleTree.className = 'module-tree'; // 递归构建DOM树 this.buildDOMTree(rootModules, moduleTree); // 添加到可视化编辑器 if (!visualEditor) { console.error('找不到visual-editor元素'); return; } // 检查是否有解析结果 if (rootModules.length === 0) { const emptyMsg = document.createElement('div'); emptyMsg.className = 'error-message'; emptyMsg.textContent = '未能解析出任何模块结构'; visualEditor.appendChild(emptyMsg); return; } visualEditor.appendChild(moduleTree); // 添加可视化编辑器的事件监听 this.addIDLVisualEditorListeners(); } catch (error) { console.error('解析IDL内容时出错:', error); const visualEditor = this.shadowRoot.getElementById('visual-editor'); if (visualEditor) { const errorMsg = document.createElement('div'); errorMsg.className = 'error-message'; errorMsg.textContent = `解析IDL内容时出错: ${error.message}`; visualEditor.appendChild(errorMsg); } } } addIDLVisualEditorListeners() { // 添加子模块按钮 this.shadowRoot.querySelectorAll('.add-submodule-btn').forEach(btn => { btn.addEventListener('click', (e) => { const moduleName = e.target.dataset.module; this.showModuleDialog(moduleName); }); }); // 添加结构体按钮 this.shadowRoot.querySelectorAll('.add-struct-btn').forEach(btn => { btn.addEventListener('click', (e) => { const moduleName = e.target.dataset.module; this.showStructDialog(moduleName); }); }); // 编辑模块按钮 this.shadowRoot.querySelectorAll('.edit-module-btn').forEach(btn => { btn.addEventListener('click', (e) => { const moduleName = e.target.dataset.module; const moduleNode = e.target.closest('.module-item'); this.showEditModuleDialog(moduleName, moduleNode); }); }); // 删除模块按钮 this.shadowRoot.querySelectorAll('.delete-module-btn').forEach(btn => { btn.addEventListener('click', (e) => { const moduleName = e.target.dataset.module; const moduleNode = e.target.closest('.module-item'); this.showConfirmDialog( `删除模块`, `确定要删除模块 "${moduleName}" 吗?这将同时删除其所有子模块和结构体!`, () => { moduleNode.remove(); this.updateIdlFromVisual(); } ); }); }); // 编辑结构体按钮 this.shadowRoot.querySelectorAll('.edit-struct-btn').forEach(btn => { btn.addEventListener('click', (e) => { const structName = e.target.dataset.struct; const structNode = e.target.closest('.struct-item'); this.showEditStructDialog(structName, structNode); }); }); // 删除结构体按钮 this.shadowRoot.querySelectorAll('.delete-struct-btn').forEach(btn => { btn.addEventListener('click', (e) => { const structName = e.target.dataset.struct; const structNode = e.target.closest('.struct-item'); this.showConfirmDialog( `删除结构体`, `确定要删除结构体 "${structName}" 吗?`, () => { structNode.remove(); this.updateIdlFromVisual(); } ); }); }); // 添加字段按钮 this.shadowRoot.querySelectorAll('.add-field-btn').forEach(btn => { btn.addEventListener('click', (e) => { const structName = e.target.dataset.struct; this.showFieldDialog(structName); }); }); // 编辑字段按钮 this.shadowRoot.querySelectorAll('.edit-field-btn').forEach(btn => { btn.addEventListener('click', (e) => { const fieldItem = e.target.closest('.field-item'); const annotation = fieldItem.querySelector('.field-annotation').textContent; const fieldType = fieldItem.querySelector('.field-type').textContent; const fieldName = fieldItem.querySelector('.field-name').textContent; const fieldArray = fieldItem.querySelector('.field-array').textContent; this.showFieldDialog(null, annotation, fieldType, fieldName, fieldArray, fieldItem); }); }); // 删除字段按钮 this.shadowRoot.querySelectorAll('.delete-field-btn').forEach(btn => { btn.addEventListener('click', (e) => { const fieldItem = e.target.closest('.field-item'); const fieldName = fieldItem.querySelector('.field-name').textContent; this.showConfirmDialog( `删除字段`, `确定要删除字段 "${fieldName}" 吗?`, () => { fieldItem.remove(); this.updateIdlFromVisual(); } ); }); }); } showStructDialog(moduleName) { // 创建结构体添加对话框 const dialog = document.createElement('div'); dialog.className = 'method-dialog'; dialog.innerHTML = `

添加新结构体

`; this.shadowRoot.appendChild(dialog); // 对话框事件 this.shadowRoot.getElementById('dialog-cancel').addEventListener('click', () => { dialog.remove(); }); this.shadowRoot.getElementById('dialog-save').addEventListener('click', () => { const structName = this.shadowRoot.getElementById('struct-name').value.trim(); if (structName) { // 找到对应的模块 const moduleElem = Array.from(this.shadowRoot.querySelectorAll('.module-item')).find( item => item.querySelector('.module-name').textContent.includes(moduleName) ); if (moduleElem) { // 创建新结构体节点 const structNode = document.createElement('div'); structNode.className = 'struct-item'; structNode.innerHTML = `
结构体: ${structName}
`; moduleElem.querySelector('.module-content').appendChild(structNode); // 为新添加的按钮绑定事件 structNode.querySelector('.add-field-btn').addEventListener('click', (e) => { this.showFieldDialog(structName); }); // 更新IDL文本 this.updateIdlFromVisual(); } } dialog.remove(); }); } showFieldDialog(structName, annotation = '', fieldType = 'double', fieldName = '', fieldArray = '', fieldItem = null) { // 创建字段编辑对话框 const dialog = document.createElement('div'); dialog.className = 'method-dialog'; // 如果是编辑现有字段,则从DOM元素获取准确的当前字段信息 let currentAnnotation = annotation; let currentFieldType = fieldType; let currentFieldName = fieldName; let currentFieldArray = fieldArray; if (fieldItem) { currentAnnotation = fieldItem.querySelector('.field-annotation').textContent.trim(); currentFieldType = fieldItem.querySelector('.field-type').textContent.trim(); currentFieldName = fieldItem.querySelector('.field-name').textContent.trim(); currentFieldArray = fieldItem.querySelector('.field-array').textContent.trim(); } // 确定数组维度和大小 let arrayDimension = 0; let arraySizes = ['', '']; if (currentFieldArray) { // 提取数组维度和大小 const matches = currentFieldArray.match(/\[([^\]]*)\]/g); if (matches) { arrayDimension = matches.length; for (let i = 0; i < arrayDimension && i < 2; i++) { arraySizes[i] = matches[i].replace('[', '').replace(']', ''); } } } dialog.innerHTML = `

${fieldItem ? '编辑字段' : '添加新字段'}

`; this.shadowRoot.appendChild(dialog); // 数组维度选择事件 this.shadowRoot.getElementById('array-dimension').addEventListener('change', (e) => { const dimension = parseInt(e.target.value); // 显示/隐藏数组大小输入框 for (let i = 1; i <= 2; i++) { const row = this.shadowRoot.getElementById(`array-size-row-${i}`); if (row) { if (i <= dimension) { row.classList.remove('hidden'); } else { row.classList.add('hidden'); } } } }); // 对话框取消事件 this.shadowRoot.getElementById('dialog-cancel').addEventListener('click', () => { dialog.remove(); }); // 对话框保存事件 this.shadowRoot.getElementById('dialog-save').addEventListener('click', () => { // 获取字段信息 const isOptional = this.shadowRoot.getElementById('field-optional').checked; const fieldAnnotation = isOptional ? '@optional' : ''; const fieldType = this.shadowRoot.getElementById('field-type').value; const fieldName = this.shadowRoot.getElementById('field-name').value.trim(); const arrayDimension = parseInt(this.shadowRoot.getElementById('array-dimension').value); // 构建数组表示 let fieldArray = ''; if (arrayDimension > 0) { for (let i = 1; i <= arrayDimension; i++) { const size = this.shadowRoot.getElementById(`array-size-${i}`).value.trim(); fieldArray += `[${size}]`; } } if (fieldName) { const fieldContent = `
${fieldAnnotation} ${fieldType} ${fieldName} ${fieldArray}
`; if (fieldItem) { // 更新现有字段 fieldItem.innerHTML = fieldContent; } else { // 添加新字段 const newFieldItem = document.createElement('div'); newFieldItem.className = 'field-item'; newFieldItem.innerHTML = fieldContent; // 找到对应的结构体添加字段 const structElem = Array.from(this.shadowRoot.querySelectorAll('.struct-item')).find( item => item.querySelector('.struct-name').textContent.includes(structName) ); if (structElem) { structElem.querySelector('.struct-fields').appendChild(newFieldItem); // 为新添加的按钮绑定事件 newFieldItem.querySelector('.edit-field-btn').addEventListener('click', (e) => { const fieldItem = e.target.closest('.field-item'); const annotation = fieldItem.querySelector('.field-annotation').textContent; const fieldType = fieldItem.querySelector('.field-type').textContent; const fieldName = fieldItem.querySelector('.field-name').textContent; const fieldArray = fieldItem.querySelector('.field-array').textContent; this.showFieldDialog(null, annotation, fieldType, fieldName, fieldArray, fieldItem); }); newFieldItem.querySelector('.delete-field-btn').addEventListener('click', (e) => { const fieldItem = e.target.closest('.field-item'); const fieldName = fieldItem.querySelector('.field-name').textContent; this.showConfirmDialog( `删除字段`, `确定要删除字段 "${fieldName}" 吗?`, () => { fieldItem.remove(); this.updateIdlFromVisual(); } ); }); } } // 更新IDL文本 this.updateIdlFromVisual(); } dialog.remove(); }); } updateIdlFromVisual() { // 从可视化编辑器更新IDL文本 let idlText = ''; const visualEditor = this.shadowRoot.getElementById('visual-editor'); const moduleTree = visualEditor.querySelector('.module-tree'); if (moduleTree) { const topModules = moduleTree.querySelectorAll(':scope > .module-item'); for (const moduleNode of topModules) { idlText += this.generateModuleText(moduleNode, 0); } } // 更新编辑器文本 this.idlContent = idlText; const editor = this.shadowRoot.getElementById('idl-editor'); if (editor) { editor.value = idlText; } // 设置为已编辑状态 this.setEditedState(true); } async saveFile() { if (!this.filePath) { this.showConfirmDialog('保存提示', '请先选择或创建一个文件', null); return; } try { // 从文本编辑器获取内容 this.idlContent = this.shadowRoot.getElementById('idl-editor').value; // 调用后端API保存文件 const response = await fetch('/api/idl/save', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ filename: this.filePath, content: this.idlContent }) }); if (!response.ok) { const error = await response.json(); throw new Error(error.error || '保存文件失败'); } // 显示临时成功消息,不需要确认 this.showTemporaryMessage('文件已成功保存'); // 更新编辑状态 this.setEditedState(false); } catch (error) { console.error('保存文件时出错:', error); this.showConfirmDialog('保存失败', '保存文件时出错: ' + error.message, null); } } async saveFileAs() { try { const newFilename = prompt('请输入新文件名:', this.filePath || 'new_interface'); if (!newFilename) { return; // 用户取消了操作 } // 从文本编辑器获取内容 this.idlContent = this.shadowRoot.getElementById('idl-editor').value; // 调用后端API保存文件 const response = await fetch('/api/idl/save', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ filename: newFilename, content: this.idlContent }) }); if (!response.ok) { const error = await response.json(); throw new Error(error.error || '另存为文件失败'); } const data = await response.json(); // 更新文件路径 this.filePath = data.filename; // 重新加载文件列表 await this.loadIdlFileList(); this.showTemporaryMessage('文件已成功保存为: ' + this.filePath); // 更新编辑状态 this.setEditedState(false); } catch (error) { //console.error('另存为文件时出错:', error); //this.showConfirmDialog('另存为错误', '另存为文件时出错: ' + error.message, null); } } showConfirmDialog(title, message, confirmCallback) { const dialog = document.createElement('div'); dialog.className = 'method-dialog'; dialog.innerHTML = `

${title}

${message}

`; this.shadowRoot.appendChild(dialog); // 对话框事件 this.shadowRoot.getElementById('dialog-cancel').addEventListener('click', () => { dialog.remove(); }); this.shadowRoot.getElementById('dialog-confirm').addEventListener('click', () => { if (confirmCallback) confirmCallback(); dialog.remove(); }); } // 添加一个新方法来显示临时消息 showTemporaryMessage(message, type = 'success') { const toast = document.createElement('div'); toast.className = `toast-message ${type}`; toast.textContent = message; this.shadowRoot.appendChild(toast); // 2秒后自动消失 setTimeout(() => { toast.classList.add('fade-out'); setTimeout(() => { toast.remove(); }, 500); }, 2000); } render() { this.shadowRoot.innerHTML = `
文本编辑
可视化编辑
`; } // 添加编辑状态设置方法 setEditedState(isEdited) { this.isEdited = isEdited; // 更新保存按钮的样式 const saveButton = this.shadowRoot.getElementById('save-button'); if (saveButton) { if (isEdited) { saveButton.classList.add('edited'); } else { saveButton.classList.remove('edited'); } } } // 添加处理文件切换的方法 async handleFileChange(newFilename) { if (this.isEdited) { // 显示保存确认对话框 this.showSaveConfirmDialog( '文件已修改', '当前文件已修改但未保存,是否保存更改?', async () => { // 保存当前文件 await this.saveFile(); // 加载新文件 await this.loadFile(newFilename); }, async () => { // 不保存,直接加载新文件 this.setEditedState(false); await this.loadFile(newFilename); } ); } else { // 直接加载新文件 await this.loadFile(newFilename); } } // 添加处理新建文件的方法 async handleNewFile() { if (this.isEdited) { // 显示保存确认对话框 this.showSaveConfirmDialog( '文件已修改', '当前文件已修改但未保存,是否保存更改?', async () => { // 保存当前文件 await this.saveFile(); // 创建新文件 await this.createNewFile(); }, async () => { // 不保存,直接创建新文件 this.setEditedState(false); await this.createNewFile(); } ); } else { // 直接创建新文件 await this.createNewFile(); } } // 添加保存确认对话框方法 showSaveConfirmDialog(title, message, saveCallback, discardCallback) { const dialog = document.createElement('div'); dialog.className = 'method-dialog'; dialog.innerHTML = `

${title}

${message}

`; this.shadowRoot.appendChild(dialog); // 对话框事件 this.shadowRoot.getElementById('dialog-cancel').addEventListener('click', () => { dialog.remove(); }); this.shadowRoot.getElementById('dialog-save').addEventListener('click', () => { dialog.remove(); if (saveCallback) saveCallback(); }); this.shadowRoot.getElementById('dialog-discard').addEventListener('click', () => { dialog.remove(); if (discardCallback) discardCallback(); }); } // 添加新建IDL文件方法 async createNewFile() { try { const filename = prompt('请输入新文件名:', 'new_interface'); if (!filename) { return; // 用户取消了操作 } // 调用后端API创建文件 const response = await fetch('/api/idl/create', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ filename }) }); if (!response.ok) { const error = await response.json(); throw new Error(error.error || '创建文件失败'); } const data = await response.json(); // 更新前端状态 this.fileHandle = null; // 清除本地文件句柄,改为使用后端API this.filePath = data.filename; this.idlContent = data.content; // 重新加载文件列表并选择当前文件 await this.loadIdlFileList(); // 设置下拉框选择 const dropdown = this.shadowRoot.getElementById('file-select-dropdown'); if (dropdown) { dropdown.value = this.filePath; } // 显示编辑区域和按钮 this.shadowRoot.getElementById('editor-container').style.display = 'flex'; this.shadowRoot.getElementById('save-button').style.display = 'inline-block'; this.shadowRoot.getElementById('save-as-button').style.display = 'inline-block'; // 更新编辑器内容 const editor = this.shadowRoot.getElementById('idl-editor'); if (editor) { editor.value = this.idlContent; } // 将IDL内容解析为结构化数据并渲染到编辑界面 this.parseIdlContent(); // 重置编辑状态 this.setEditedState(false); } catch (error) { //console.error('创建文件时出错:', error); //this.showConfirmDialog('创建文件错误', '创建文件时出错: ' + error.message, null); } } // HTML转义函数(防止XSS) escapeHtml(unsafe) { return unsafe .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } // 新增方法:递归解析模块 parseModules(content) { const modules = []; let pos = 0; while (pos < content.length) { // 跳过空白字符 while (pos < content.length && /\s/.test(content[pos])) pos++; if (pos >= content.length) break; // 检查是否是模块声明 if (content.substring(pos, pos+7) === 'module ') { // 提取模块名称 pos += 7; // 跳过 'module ' const nameStart = pos; // 找到模块名称的结束位置(空格或大括号前) while (pos < content.length && !/[\s{]/.test(content[pos])) pos++; const moduleName = content.substring(nameStart, pos).trim(); // 跳过空白字符,查找开始大括号 while (pos < content.length && content[pos] !== '{') pos++; if (pos >= content.length) break; // 找到开始大括号,现在查找匹配的结束大括号 pos++; // 跳过 '{' let braceCount = 1; const moduleContentStart = pos; while (pos < content.length && braceCount > 0) { if (content[pos] === '{') braceCount++; else if (content[pos] === '}') braceCount--; pos++; } if (braceCount === 0) { // 提取模块内容,不包括最后的大括号 const moduleContent = content.substring(moduleContentStart, pos-1).trim(); // 递归解析子模块和结构体 const module = { type: 'module', name: moduleName, subModules: this.parseModules(moduleContent), structs: this.parseStructs(moduleContent) }; modules.push(module); } } else { // 如果不是模块声明,跳过当前字符 pos++; } } return modules; } // 新增方法:解析结构体 parseStructs(content) { const structs = []; let pos = 0; while (pos < content.length) { // 跳过空白字符 while (pos < content.length && /\s/.test(content[pos])) pos++; if (pos >= content.length) break; // 检查是否是结构体声明 if (content.substring(pos, pos+7) === 'struct ') { // 提取结构体名称 pos += 7; // 跳过 'struct ' const nameStart = pos; // 找到结构体名称的结束位置(空格或大括号前) while (pos < content.length && !/[\s{]/.test(content[pos])) pos++; const structName = content.substring(nameStart, pos).trim(); // 跳过空白字符,查找开始大括号 while (pos < content.length && content[pos] !== '{') pos++; if (pos >= content.length) break; // 找到开始大括号,现在查找匹配的结束大括号 pos++; // 跳过 '{' let braceCount = 1; const structContentStart = pos; while (pos < content.length && braceCount > 0) { if (content[pos] === '{') braceCount++; else if (content[pos] === '}') braceCount--; pos++; } if (braceCount === 0) { // 提取结构体内容,不包括最后的大括号 const structContent = content.substring(structContentStart, pos-1).trim(); // 解析字段 const fields = this.parseFields(structContent); const struct = { type: 'struct', name: structName, fields: fields }; structs.push(struct); } } else if (content.substring(pos, pos+7) === 'module ') { // 跳过模块声明,因为它们已经在parseModules中处理了 // 找到匹配的大括号 while (pos < content.length && content[pos] !== '{') pos++; if (pos >= content.length) break; pos++; // 跳过 '{' let braceCount = 1; while (pos < content.length && braceCount > 0) { if (content[pos] === '{') braceCount++; else if (content[pos] === '}') braceCount--; pos++; } } else { // 如果不是结构体或模块声明,跳过当前字符 pos++; } } return structs; } // 新增方法:解析字段 parseFields(content) { const fields = []; const lines = content.split(';'); for (let line of lines) { line = line.trim(); if (!line) continue; let annotation = ''; let fieldType = ''; let fieldName = ''; let arrayInfo = ''; // 提取注解 if (line.startsWith('@')) { const annotationEnd = line.indexOf(' '); if (annotationEnd > 0) { annotation = line.substring(0, annotationEnd); line = line.substring(annotationEnd + 1).trim(); } } // 提取类型和名称 const parts = line.split(' ').filter(p => p.trim() !== ''); if (parts.length >= 2) { fieldType = parts[0]; const fullName = parts[1]; // 检查是否是数组 if (fullName.includes('[')) { fieldName = fullName.substring(0, fullName.indexOf('[')); arrayInfo = fullName.substring(fullName.indexOf('[')); } else { fieldName = fullName; } fields.push({ annotation: annotation, type: fieldType, name: fieldName, array: arrayInfo }); } } return fields; } // 新增方法:构建DOM树 buildDOMTree(modules, parentElement, isTopLevel = true) { for (const module of modules) { // 创建模块节点 const moduleNode = document.createElement('div'); moduleNode.className = 'module-item'; const moduleHeader = document.createElement('div'); moduleHeader.className = 'module-header'; moduleHeader.innerHTML = `
模块: ${module.name}
${!isTopLevel ? `` : ''} ${!isTopLevel ? `` : ''}
`; const moduleContent = document.createElement('div'); moduleContent.className = 'module-content'; moduleNode.appendChild(moduleHeader); moduleNode.appendChild(moduleContent); // 递归处理子模块 this.buildDOMTree(module.subModules, moduleContent, false); // 处理结构体 for (const struct of module.structs) { const structNode = this.createStructNode(struct); moduleContent.appendChild(structNode); } // 添加到父元素 parentElement.appendChild(moduleNode); } } // 新增方法:创建结构体节点 createStructNode(struct) { const structNode = document.createElement('div'); structNode.className = 'struct-item'; const structHeader = document.createElement('div'); structHeader.className = 'struct-header'; structHeader.innerHTML = `
结构体: ${struct.name}
`; const structFields = document.createElement('div'); structFields.className = 'struct-fields'; // 添加字段 for (const field of struct.fields) { const fieldItem = document.createElement('div'); fieldItem.className = 'field-item'; fieldItem.innerHTML = `
${field.annotation} ${field.type} ${field.name} ${field.array}
`; structFields.appendChild(fieldItem); } structNode.appendChild(structHeader); structNode.appendChild(structFields); return structNode; } // 新增方法:显示模块添加对话框 showModuleDialog(parentModuleName) { // 创建模块添加对话框 const dialog = document.createElement('div'); dialog.className = 'method-dialog'; dialog.innerHTML = `

添加子模块

`; this.shadowRoot.appendChild(dialog); // 对话框事件 this.shadowRoot.getElementById('dialog-cancel').addEventListener('click', () => { dialog.remove(); }); this.shadowRoot.getElementById('dialog-save').addEventListener('click', () => { const moduleName = this.shadowRoot.getElementById('module-name').value.trim(); if (moduleName) { // 找到父模块元素 const parentModuleElement = Array.from(this.shadowRoot.querySelectorAll('.module-item')).find( item => item.querySelector('.module-name').textContent.includes(parentModuleName) ); if (parentModuleElement) { // 创建新模块节点 const moduleNode = document.createElement('div'); moduleNode.className = 'module-item'; const moduleHeader = document.createElement('div'); moduleHeader.className = 'module-header'; moduleHeader.innerHTML = `
模块: ${moduleName}
`; const moduleContent = document.createElement('div'); moduleContent.className = 'module-content'; moduleNode.appendChild(moduleHeader); moduleNode.appendChild(moduleContent); // 添加到父模块 parentModuleElement.querySelector('.module-content').appendChild(moduleNode); // 为新添加的按钮绑定事件 moduleNode.querySelector('.add-submodule-btn').addEventListener('click', (e) => { this.showModuleDialog(moduleName); }); moduleNode.querySelector('.add-struct-btn').addEventListener('click', (e) => { this.showStructDialog(moduleName); }); moduleNode.querySelector('.edit-module-btn').addEventListener('click', (e) => { this.showEditModuleDialog(moduleName, moduleNode); }); moduleNode.querySelector('.delete-module-btn').addEventListener('click', (e) => { this.showConfirmDialog( `删除模块`, `确定要删除模块 "${moduleName}" 吗?这将同时删除其所有子模块和结构体!`, () => { moduleNode.remove(); this.updateIdlFromVisual(); } ); }); // 更新IDL文本 this.updateIdlFromVisual(); } } dialog.remove(); }); } // 助手方法:递归生成模块文本 generateModuleText(moduleNode, indentLevel) { const indent = ' '.repeat(indentLevel); const moduleName = moduleNode.querySelector('.module-name').textContent.replace('模块: ', ''); let moduleText = `${indent}module ${moduleName}\n${indent}{\n`; // 处理子内容:子模块和结构体 const moduleContent = moduleNode.querySelector('.module-content'); // 处理子模块 const subModules = moduleContent.querySelectorAll(':scope > .module-item'); for (const subModule of subModules) { moduleText += this.generateModuleText(subModule, indentLevel + 1); } // 处理结构体 const structs = moduleContent.querySelectorAll(':scope > .struct-item'); for (const struct of structs) { moduleText += this.generateStructText(struct, indentLevel + 1); } moduleText += `${indent}};\n\n`; return moduleText; } // 助手方法:生成结构体文本 generateStructText(structNode, indentLevel) { const indent = ' '.repeat(indentLevel); const structName = structNode.querySelector('.struct-name').textContent.replace('结构体: ', ''); let structText = `${indent}struct ${structName}\n${indent}{\n`; // 处理字段 const fields = structNode.querySelectorAll('.field-item'); for (const field of fields) { const annotation = field.querySelector('.field-annotation').textContent; const fieldType = field.querySelector('.field-type').textContent; const fieldName = field.querySelector('.field-name').textContent; const fieldArray = field.querySelector('.field-array').textContent; const annotationText = annotation ? `${annotation} ` : ''; structText += `${indent} ${annotationText}${fieldType} ${fieldName}${fieldArray};\n`; } structText += `${indent}};\n`; return structText; } // 新增方法:显示模块编辑对话框 showEditModuleDialog(moduleName, moduleNode) { // 创建模块编辑对话框 const dialog = document.createElement('div'); dialog.className = 'method-dialog'; // 从DOM元素中获取准确的当前模块名称 const currentModuleName = moduleNode.querySelector('.module-name').textContent.replace('模块: ', '').trim(); dialog.innerHTML = `

编辑模块名称

`; this.shadowRoot.appendChild(dialog); // 对话框事件 this.shadowRoot.getElementById('dialog-cancel').addEventListener('click', () => { dialog.remove(); }); this.shadowRoot.getElementById('dialog-save').addEventListener('click', () => { const newModuleName = this.shadowRoot.getElementById('module-name').value.trim(); if (newModuleName && newModuleName !== currentModuleName) { // 更新模块名称 const moduleNameElement = moduleNode.querySelector('.module-name'); moduleNameElement.textContent = `模块: ${newModuleName}`; // 更新按钮的dataset moduleNode.querySelectorAll('[data-module]').forEach(el => { el.dataset.module = newModuleName; }); // 更新IDL文本 this.updateIdlFromVisual(); } dialog.remove(); }); } // 新增方法:显示结构体编辑对话框 showEditStructDialog(structName, structNode) { // 创建结构体编辑对话框 const dialog = document.createElement('div'); dialog.className = 'method-dialog'; // 从DOM元素中获取准确的当前结构体名称 const currentStructName = structNode.querySelector('.struct-name').textContent.replace('结构体: ', '').trim(); dialog.innerHTML = `

编辑结构体名称

`; this.shadowRoot.appendChild(dialog); // 对话框事件 this.shadowRoot.getElementById('dialog-cancel').addEventListener('click', () => { dialog.remove(); }); this.shadowRoot.getElementById('dialog-save').addEventListener('click', () => { const newStructName = this.shadowRoot.getElementById('struct-name').value.trim(); if (newStructName && newStructName !== currentStructName) { // 更新结构体名称 const structNameElement = structNode.querySelector('.struct-name'); structNameElement.textContent = `结构体: ${newStructName}`; // 更新按钮的dataset structNode.querySelectorAll('[data-struct]').forEach(el => { el.dataset.struct = newStructName; }); // 更新IDL文本 this.updateIdlFromVisual(); } dialog.remove(); }); } // 添加格式化文件大小的辅助方法 formatFileSize(bytes) { if (bytes < 1024) return bytes + ' B'; if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(2) + ' KB'; return (bytes / (1024 * 1024)).toFixed(2) + ' MB'; } // 新增加载文件的方法,从后端API获取文件内容 async loadFile(filename) { try { const response = await fetch(`/api/idl/read?filename=${encodeURIComponent(filename)}`); if (!response.ok) { const error = await response.json(); throw new Error(error.error || '读取文件失败'); } const data = await response.json(); // 更新前端状态 this.fileHandle = null; // 清除本地文件句柄,改为使用后端API this.filePath = data.filename; this.idlContent = data.content; // 更新下拉框选择 const dropdown = this.shadowRoot.getElementById('file-select-dropdown'); if (dropdown) { dropdown.value = this.filePath; } // 显示编辑区域和保存按钮 this.shadowRoot.getElementById('editor-container').style.display = 'flex'; this.shadowRoot.getElementById('save-button').style.display = 'inline-block'; this.shadowRoot.getElementById('save-as-button').style.display = 'inline-block'; // 更新编辑器内容 const editor = this.shadowRoot.getElementById('idl-editor'); if (editor) { editor.value = this.idlContent; } // 将IDL内容解析为结构化数据并渲染到编辑界面 this.parseIdlContent(); // 重置编辑状态 this.setEditedState(false); } catch (error) { console.error('加载文件时出错:', error); this.showConfirmDialog('加载文件错误', '加载文件时出错: ' + error.message, null); } } // 添加加载文件列表的方法 async loadIdlFileList() { try { // 获取IDL文件列表 const response = await fetch('/api/idl/list'); if (!response.ok) { const error = await response.json(); throw new Error(error.error || '获取文件列表失败'); } const data = await response.json(); // 获取下拉框元素 const dropdown = this.shadowRoot.getElementById('file-select-dropdown'); // 清空现有选项 dropdown.innerHTML = ''; // 添加文件选项 if (data.files && data.files.length > 0) { data.files.forEach(file => { const option = document.createElement('option'); option.value = file.name; option.textContent = file.name; dropdown.appendChild(option); }); } } catch (error) { console.error('加载IDL文件列表失败:', error); } } } customElements.define('interface-config', InterfaceConfig);