From 9fed446a6f8cf338732e3f90ba388b64a781eec3 Mon Sep 17 00:00:00 2001 From: jinchao <383321154@qq.com> Date: Fri, 9 May 2025 16:29:50 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B0=86=E7=99=BB=E9=99=86=E9=A1=B5=E9=9D=A2?= =?UTF-8?q?=E5=92=8C=E4=B8=BB=E9=A1=B5=E9=9D=A2=E5=90=88=E4=BA=8C=E4=B8=BA?= =?UTF-8?q?=E4=B8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- XNSimHtml/bak/interface-config.js | 2120 ------------------------ XNSimHtml/components/auth-component.js | 439 +++++ XNSimHtml/components/user-info.js | 21 +- XNSimHtml/index.html | 111 -- XNSimHtml/main.html | 685 +++++--- XNSimHtml/script.js | 204 --- XNSimHtml/server.js | 12 +- XNSimHtml/style.css | 776 --------- 8 files changed, 875 insertions(+), 3493 deletions(-) delete mode 100644 XNSimHtml/bak/interface-config.js create mode 100644 XNSimHtml/components/auth-component.js delete mode 100644 XNSimHtml/index.html delete mode 100644 XNSimHtml/script.js delete mode 100644 XNSimHtml/style.css diff --git a/XNSimHtml/bak/interface-config.js b/XNSimHtml/bak/interface-config.js deleted file mode 100644 index cd5710a..0000000 --- a/XNSimHtml/bak/interface-config.js +++ /dev/null @@ -1,2120 +0,0 @@ -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); \ No newline at end of file diff --git a/XNSimHtml/components/auth-component.js b/XNSimHtml/components/auth-component.js new file mode 100644 index 0000000..1083e14 --- /dev/null +++ b/XNSimHtml/components/auth-component.js @@ -0,0 +1,439 @@ +class AuthComponent extends HTMLElement { + constructor() { + super(); + this.attachShadow({ mode: 'open' }); + } + + connectedCallback() { + this.render(); + this.setupEventListeners(); + } + + render() { + this.shadowRoot.innerHTML = ` + +
+
+
+

XNSim

+
+

欢迎使用XNSim仿真平台

+

高效、专业的仿真解决方案

+
+
+
+
+
+ + +
+ +
+
+
+ + +
+
+ +
+ + +
+
+
+ + +
+ +
+
+ +
+
+
+
+ + +
+
+ + +
+
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+ +
+
+
+
+ `; + } + + setupEventListeners() { + const loginToggle = this.shadowRoot.getElementById('loginToggle'); + const registerToggle = this.shadowRoot.getElementById('registerToggle'); + const loginForm = this.shadowRoot.getElementById('loginForm'); + const registerForm = this.shadowRoot.getElementById('registerForm'); + const loginFormElement = this.shadowRoot.getElementById('loginFormElement'); + const registerFormElement = this.shadowRoot.getElementById('registerFormElement'); + const togglePasswordButtons = this.shadowRoot.querySelectorAll('.toggle-password'); + + // 创建Toast提示函数 + const showToast = (message) => { + const toast = document.createElement('div'); + toast.className = 'toast'; + toast.textContent = message; + this.shadowRoot.appendChild(toast); + + setTimeout(() => toast.classList.add('show'), 10); + + setTimeout(() => { + toast.classList.remove('show'); + setTimeout(() => toast.remove(), 300); + }, 2000); + }; + + // 切换表单显示 + loginToggle.addEventListener('click', () => { + loginToggle.classList.add('active'); + registerToggle.classList.remove('active'); + loginForm.classList.add('active'); + registerForm.classList.remove('active'); + }); + + registerToggle.addEventListener('click', () => { + registerToggle.classList.add('active'); + loginToggle.classList.remove('active'); + registerForm.classList.add('active'); + loginForm.classList.remove('active'); + }); + + // 密码显示/隐藏 + togglePasswordButtons.forEach(button => { + button.addEventListener('click', () => { + const input = button.parentElement.querySelector('input'); + const icon = button.querySelector('img'); + if (input.type === 'password') { + input.type = 'text'; + icon.src = 'assets/icons/png/visiable_b.png'; + } else { + input.type = 'password'; + icon.src = 'assets/icons/png/invisiable_b.png'; + } + }); + }); + + // 表单提交 + loginFormElement.addEventListener('submit', (e) => { + e.preventDefault(); + const username = this.shadowRoot.getElementById('loginUsername').value; + const password = this.shadowRoot.getElementById('loginPassword').value; + const remember = this.shadowRoot.getElementById('remember').checked; + + if (!username || !password) { + showToast('请输入用户名和密码'); + return; + } + + this.dispatchEvent(new CustomEvent('login', { + detail: { username, password, remember }, + bubbles: true, + composed: true + })); + }); + + registerFormElement.addEventListener('submit', (e) => { + e.preventDefault(); + const username = this.shadowRoot.getElementById('regUsername').value; + const password = this.shadowRoot.getElementById('regPassword').value; + const confirmPassword = this.shadowRoot.getElementById('regConfirmPassword').value; + + if (!username || !password) { + showToast('用户名和密码为必填项'); + return; + } + + if (password !== confirmPassword) { + showToast('两次输入的密码不一致'); + return; + } + + const userInfo = { + full_name: this.shadowRoot.getElementById('fullName').value.trim() || '', + phone: this.shadowRoot.getElementById('phone').value.trim() || '', + email: this.shadowRoot.getElementById('email').value.trim() || '', + department: this.shadowRoot.getElementById('department').value.trim() || '', + position: this.shadowRoot.getElementById('position').value.trim() || '', + access_level: 1 + }; + + this.dispatchEvent(new CustomEvent('register', { + detail: { username, password, userInfo }, + bubbles: true, + composed: true + })); + }); + } +} + +customElements.define('auth-component', AuthComponent); \ No newline at end of file diff --git a/XNSimHtml/components/user-info.js b/XNSimHtml/components/user-info.js index 850c633..e9b1073 100644 --- a/XNSimHtml/components/user-info.js +++ b/XNSimHtml/components/user-info.js @@ -82,6 +82,10 @@ class UserInfo extends HTMLElement { box-shadow: 0 2px 8px rgba(0,0,0,0.1); display: none; z-index: 1000; + min-width: 150px; + max-width: 250px; + white-space: nowrap; + overflow: hidden; } .user-dropdown.active .user-dropdown-menu { @@ -95,6 +99,9 @@ class UserInfo extends HTMLElement { cursor: pointer; transition: background-color 0.3s; gap: 8px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } .dropdown-item:hover { @@ -159,6 +166,11 @@ class UserInfo extends HTMLElement { border-style: solid; border-color: transparent transparent rgba(0, 0, 0, 0.8) transparent; } + + .dropdown-item span { + overflow: hidden; + text-overflow: ellipsis; + }
@@ -242,13 +254,15 @@ class UserInfo extends HTMLElement { try { const userInfoStr = localStorage.getItem('userInfo'); if (!userInfoStr) { - window.location.href = 'index.html'; + document.getElementById('authContainer').style.display = 'block'; + document.getElementById('mainContainer').style.display = 'none'; return; } userInfo = JSON.parse(userInfoStr); } catch (error) { console.error('解析用户信息失败:', error); - window.location.href = 'index.html'; + document.getElementById('authContainer').style.display = 'block'; + document.getElementById('mainContainer').style.display = 'none'; return; } @@ -314,7 +328,8 @@ class UserInfo extends HTMLElement { logout() { localStorage.removeItem('userInfo'); - window.location.href = 'index.html'; + document.getElementById('authContainer').style.display = 'block'; + document.getElementById('mainContainer').style.display = 'none'; } } diff --git a/XNSimHtml/index.html b/XNSimHtml/index.html deleted file mode 100644 index 3017774..0000000 --- a/XNSimHtml/index.html +++ /dev/null @@ -1,111 +0,0 @@ - - - - - - XNSim - 登录/注册 - - - - - - -
-
-
-

XNSim

-
-

欢迎使用XNSim仿真平台

-

高效、专业的仿真解决方案

-
-
-
-
-
- - -
- -
-
-
- - -
-
- -
- - -
-
-
- - -
- -
-
- -
-
-
-
- - -
-
- - -
-
-
-
- -
- - -
-
-
- -
- - -
-
-
-
-
- - -
-
- - -
-
-
-
- - -
-
- - -
-
- -
-
-
-
- - - \ No newline at end of file diff --git a/XNSimHtml/main.html b/XNSimHtml/main.html index 8fc2654..2509b56 100644 --- a/XNSimHtml/main.html +++ b/XNSimHtml/main.html @@ -7,6 +7,7 @@ + @@ -183,10 +184,39 @@ flex: 1; overflow: hidden; } + + /* Toast 提示样式 */ + .toast { + position: fixed; + top: 20px; + right: 20px; + background: rgba(0, 0, 0, 0.8); + color: white; + padding: 12px 24px; + border-radius: 8px; + font-size: 14px; + z-index: 10000; + opacity: 0; + transition: opacity 0.3s ease; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + } + .toast.show { + opacity: 1; + } + + #authContainer, #mainContainer { + display: none; + } + #authContainer.visible, #mainContainer.visible { + display: block; + } -
+
+ +
+
@@ -210,284 +240,391 @@
\ No newline at end of file diff --git a/XNSimHtml/script.js b/XNSimHtml/script.js deleted file mode 100644 index 7913d0b..0000000 --- a/XNSimHtml/script.js +++ /dev/null @@ -1,204 +0,0 @@ -document.addEventListener('DOMContentLoaded', function() { - // 获取DOM元素 - const loginForm = document.getElementById('loginForm'); - const registerForm = document.getElementById('registerForm'); - const loginToggle = document.getElementById('loginToggle'); - const registerToggle = document.getElementById('registerToggle'); - - // 处理密码可见性切换 - document.querySelectorAll('.toggle-password').forEach(button => { - button.addEventListener('click', function() { - const input = this.previousElementSibling; - const icon = this.querySelector('.visibility-icon'); - - // 切换密码可见性 - if (input.type === 'password') { - input.type = 'text'; - icon.src = 'assets/icons/png/visiable_b.png'; - } else { - input.type = 'password'; - icon.src = 'assets/icons/png/invisiable_b.png'; - } - }); - }); - - // 创建Toast提示函数 - function showToast(message) { - const toast = document.createElement('div'); - toast.className = 'toast'; - toast.textContent = message; - document.body.appendChild(toast); - - setTimeout(() => toast.classList.add('show'), 10); - - setTimeout(() => { - toast.classList.remove('show'); - setTimeout(() => toast.remove(), 300); - }, 2000); - } - - // 添加Toast样式 - const style = document.createElement('style'); - style.textContent = ` - .toast { - position: fixed; - top: 20px; - right: 20px; - background: rgba(0, 0, 0, 0.8); - color: white; - padding: 12px 24px; - border-radius: 8px; - font-size: 14px; - z-index: 10000; - opacity: 0; - transition: opacity 0.3s ease; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); - } - .toast.show { - opacity: 1; - } - `; - document.head.appendChild(style); - - // 处理表单切换 - if (loginToggle && registerToggle) { - loginToggle.addEventListener('click', () => { - loginToggle.classList.add('active'); - registerToggle.classList.remove('active'); - loginForm.classList.add('active'); - registerForm.classList.remove('active'); - }); - - registerToggle.addEventListener('click', () => { - registerToggle.classList.add('active'); - loginToggle.classList.remove('active'); - registerForm.classList.add('active'); - loginForm.classList.remove('active'); - }); - } - - // 处理登录表单提交 - if (loginForm) { - loginForm.querySelector('form').addEventListener('submit', async (e) => { - e.preventDefault(); - - const username = document.getElementById('loginUsername').value; - const password = document.getElementById('loginPassword').value; - const remember = document.getElementById('remember').checked; - - if (!username || !password) { - showToast('请输入用户名和密码'); - return; - } - - try { - const response = await fetch('/api/login', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - username, - password, - remember - }) - }); - - const data = await response.json(); - - if (data.success) { - localStorage.setItem('userInfo', JSON.stringify(data.user)); - showToast(`欢迎回来,${data.user.username}!`); - setTimeout(() => { - window.location.href = 'main.html'; - }, 1000); - } else { - showToast(data.message || '登录失败,请检查用户名和密码'); - } - } catch (error) { - console.error('登录请求错误:', error); - showToast('登录请求失败,请稍后再试'); - } - }); - } - - // 处理注册表单提交 - if (registerForm) { - registerForm.querySelector('form').addEventListener('submit', async (e) => { - e.preventDefault(); - - const username = document.getElementById('regUsername').value; - const password = document.getElementById('regPassword').value; - const confirmPassword = document.getElementById('regConfirmPassword').value; - - if (!username || !password) { - showToast('用户名和密码为必填项'); - return; - } - - if (password !== confirmPassword) { - showToast('两次输入的密码不一致'); - return; - } - - const userInfo = { - full_name: document.getElementById('fullName').value.trim() || '', - phone: document.getElementById('phone').value.trim() || '', - email: document.getElementById('email').value.trim() || '', - department: document.getElementById('department').value.trim() || '', - position: document.getElementById('position').value.trim() || '', - access_level: 1 - }; - - try { - const response = await fetch('/api/register', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - username, - password, - userInfo - }) - }); - - const data = await response.json(); - - if (data.success) { - showToast('注册成功!请登录'); - loginToggle.click(); - e.target.reset(); - } else { - let errorMessage; - switch (data.userId) { - case -1: - errorMessage = '注册失败:系统错误'; - break; - case -2: - errorMessage = '注册失败:用户名已存在'; - break; - case -3: - errorMessage = '注册失败:无效的用户信息格式'; - break; - case -4: - errorMessage = '注册失败:用户名不能为空'; - break; - case -5: - errorMessage = '注册失败:密码不能为空'; - break; - case -6: - errorMessage = '注册失败:无效的权限级别'; - break; - default: - errorMessage = data.message || '注册失败,请稍后重试'; - } - showToast(errorMessage); - } - } catch (error) { - console.error('注册请求失败:', error); - showToast('注册请求失败,请稍后重试'); - } - }); - } -}); \ No newline at end of file diff --git a/XNSimHtml/server.js b/XNSimHtml/server.js index bf83ee5..de8a1b3 100644 --- a/XNSimHtml/server.js +++ b/XNSimHtml/server.js @@ -37,6 +37,13 @@ if (!xnCorePath) { // 中间件 app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: true })); + +// 根路径直接返回main.html +app.get('/', (req, res) => { + res.sendFile(path.join(__dirname, 'main.html')); +}); + +// 静态文件服务 - 放在根路径处理之后 app.use(express.static(__dirname)); // 监听进程退出事件 @@ -80,11 +87,6 @@ app.use('/api/qa', qaRoutes); app.use('/api/todos', todoRoutes); app.use('/api', userRoutes); -// 主页路由 -app.get('/', (req, res) => { - res.sendFile(path.join(__dirname, 'main.html')); -}); - // 接口配置页面路由 app.get('/interface-config', (req, res) => { res.sendFile(path.join(__dirname, 'interface-config.html')); diff --git a/XNSimHtml/style.css b/XNSimHtml/style.css deleted file mode 100644 index fe6460c..0000000 --- a/XNSimHtml/style.css +++ /dev/null @@ -1,776 +0,0 @@ -* { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -body { - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; - background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); - min-height: 100vh; - display: flex; - align-items: center; - justify-content: center; -} - -.container { - width: 90%; - max-width: 1200px; - min-height: 600px; - background: white; - border-radius: 20px; - box-shadow: 0 15px 35px rgba(0, 0, 0, 0.1); - display: flex; - overflow: hidden; -} - -.content { - flex: 1; - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - padding: 60px; - display: flex; - align-items: center; - justify-content: center; - color: white; -} - -.welcome-section { - text-align: center; -} - -.welcome-section h1 { - font-size: 3.5rem; - margin-bottom: 2rem; - font-weight: 700; - text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2); -} - -.welcome-text { - font-size: 1.2rem; - line-height: 1.8; - opacity: 0.9; -} - -.auth-container { - width: 500px; - background: white; - padding: 40px; - display: flex; - flex-direction: column; -} - -.form-toggle { - display: flex; - margin-bottom: 30px; - border-radius: 8px; - overflow: hidden; - box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); -} - -.toggle-btn { - flex: 1; - padding: 15px; - border: none; - background: #f0f0f0; - cursor: pointer; - font-size: 1rem; - font-weight: 500; - transition: all 0.3s ease; -} - -.toggle-btn.active { - background: #667eea; - color: white; -} - -.form-panel { - display: none; - animation: fadeIn 0.3s ease; - margin-top: 10px; -} - -@keyframes fadeIn { - from { opacity: 0; transform: translateY(10px); } - to { opacity: 1; transform: translateY(0); } -} - -.form-panel.active { - display: block; -} - -.form-panel h2 { - font-size: 1.8rem; - color: #333; - margin-bottom: 25px; - text-align: center; -} - -.form-row { - display: flex; - gap: 20px; - margin-bottom: 20px; -} - -.form-row .form-group { - flex: 1; - margin-bottom: 0; -} - -.form-group { - margin-bottom: 20px; -} - -.form-group label { - display: block; - margin-bottom: 8px; - color: #555; - font-weight: 500; -} - -.form-group input { - width: 100%; - padding: 12px 15px; - border: 2px solid #eee; - border-radius: 8px; - font-size: 1rem; - transition: all 0.3s ease; -} - -.form-group input:focus { - border-color: #667eea; - outline: none; - box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1); -} - -.remember-me { - display: flex; - align-items: center; - gap: 8px; - margin-bottom: 20px; -} - -.remember-me input { - width: auto; -} - -.submit-btn { - width: 100%; - padding: 15px; - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - border: none; - border-radius: 8px; - color: white; - font-size: 1rem; - font-weight: 500; - cursor: pointer; - transition: all 0.3s ease; -} - -.submit-btn:hover { - transform: translateY(-2px); - box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4); -} - -@media (max-width: 1024px) { - .container { - flex-direction: column; - min-height: auto; - margin: 20px; - } - - .content { - padding: 40px; - } - - .auth-container { - width: 100%; - } -} - -@media (max-width: 480px) { - .container { - margin: 0; - border-radius: 0; - } - - .auth-container { - padding: 20px; - } - - .welcome-section h1 { - font-size: 2.5rem; - } -} - -.main-container { - width: 100%; - min-height: 100vh; - display: flex; - flex-direction: column; -} - -.main-nav { - display: none; -} - -.logo { - display: none; -} - -.user-info { - display: none; -} - -main { - flex: 1; - padding: 2rem; - background-color: #f5f5f5; -} - -.content-section { - background-color: white; - border-radius: 8px; - padding: 2rem; - box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); -} - -.content-section h2 { - color: #333; - margin-bottom: 1.5rem; - padding-bottom: 0.5rem; - border-bottom: 2px solid #f0f0f0; -} - -.admin-features, -.user-features { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); - gap: 1rem; - margin-top: 1rem; -} - -.feature-btn { - padding: 1rem; - background: linear-gradient(135deg, #6e8efb, #a777e3); - border: none; - border-radius: 8px; - color: white; - font-size: 1rem; - cursor: pointer; - transition: all 0.3s ease; - text-align: center; -} - -.feature-btn:hover { - transform: translateY(-2px); - box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); -} - -@media (max-width: 768px) { - .main-nav { - padding: 1rem; - flex-direction: column; - gap: 1rem; - text-align: center; - } - - .admin-features, - .user-features { - grid-template-columns: 1fr; - } - - main { - padding: 1rem; - } -} - -/* 内容包装器 */ -.content-wrapper { - display: flex; - height: 100vh; /* 修改为全屏高度 */ -} - -/* 主工具栏样式 */ -.main-toolbar { - width: 80px; - background-color: #2c3e50; - display: flex; - flex-direction: column; - align-items: center; - padding: 20px 0; - transition: width 0.3s ease; -} - -.main-toolbar.collapsed { - width: 50px; -} - -.main-toolbar.collapsed .tool-item span { - display: none; -} - -.main-toolbar.collapsed .logo-image { - width: 30px; - height: 30px; -} - -.tools-container { - flex: 1; - width: 100%; - display: flex; - flex-direction: column; - align-items: center; -} - -.toolbar-toggle { - position: absolute; - right: -12px; - top: 50%; - transform: translateY(-50%); - width: 24px; - height: 24px; - background-color: #34495e; - border-radius: 50%; - display: flex; - justify-content: center; - align-items: center; - cursor: pointer; - color: #fff; - box-shadow: 0 2px 4px rgba(0,0,0,0.2); - z-index: 100; -} - -.toolbar-toggle i { - transition: transform 0.3s ease; -} - -.sub-toolbar.collapsed .toolbar-toggle i { - transform: rotate(180deg); -} - -.toolbar-toggle:hover { - background-color: #2c3e50; -} - -/* 调整子工具栏的过渡效果 */ -.sub-toolbar { - width: 200px; - background-color: #34495e; - position: relative; - transition: all 0.3s ease; - display: flex; -} - -.sub-toolbar.collapsed { - width: 0; -} - -.sub-toolbar-content { - min-width: 200px; - padding: 20px 0; - opacity: 1; - transition: opacity 0.2s ease; -} - -.sub-toolbar.collapsed .sub-toolbar-content { - opacity: 0; -} - -.tool-item { - width: 100%; - display: flex; - flex-direction: column; - align-items: center; - padding: 15px 0; - color: #ecf0f1; - cursor: pointer; - transition: all 0.3s ease; -} - -.tool-item i { - font-size: 24px; - margin-bottom: 5px; -} - -.tool-item span { - font-size: 12px; - text-align: center; -} - -.tool-item:hover { - background-color: #34495e; -} - -.tool-item.active { - background-color: #3498db; -} - -/* 子工具栏样式 */ -.sub-menu { - display: none; - white-space: nowrap; -} - -.sub-menu.active { - display: block; -} - -.sub-item { - padding: 12px 20px; - color: #ecf0f1; - cursor: pointer; - transition: all 0.3s ease; -} - -.sub-item:hover { - background-color: #2c3e50; -} - -.sub-item.active { - background-color: #3498db; -} - -/* 主要内容区域 */ -.main-content { - flex: 1; - padding: 20px; - background-color: #f5f5f5; - overflow-y: auto; -} - -#contentArea { - background-color: white; - border-radius: 8px; - padding: 20px; - box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); -} - -/* 响应式设计 */ -@media (max-width: 768px) { - .content-wrapper { - flex-direction: column; - } - - .main-toolbar { - width: 100%; - height: 60px; - flex-direction: row; - justify-content: space-around; - padding: 0; - } - - .tool-item { - padding: 10px; - } - - .sub-toolbar { - width: 100%; - height: auto; - max-height: 200px; - } - - .form-row { - flex-direction: column; - gap: 15px; - } - - .form-row .form-group { - margin-bottom: 0; - } - - .auth-container { - padding: 20px; - } -} - -.logo-container { - width: 100%; - padding: 8px; - margin-bottom: 10px; - display: flex; - justify-content: center; - align-items: center; -} - -.logo-image { - width: 50px; - height: 50px; - object-fit: contain; -} - -.content-header { - display: flex; - justify-content: space-between; - align-items: center; - padding: 12px 20px; - background-color: white; - border-bottom: 1px solid #e0e0e0; -} - -.breadcrumb { - display: flex; - align-items: center; - gap: 8px; - color: #666; -} - -.breadcrumb i { - font-size: 14px; - color: #999; -} - -/* 用户下拉菜单 */ -.user-dropdown { - position: relative; -} - -.user-dropdown-toggle { - display: flex; - align-items: center; - gap: 8px; - cursor: pointer; - padding: 6px 12px; - border-radius: 4px; - transition: background-color 0.2s ease; -} - -.user-dropdown-toggle:hover { - background-color: #f5f5f5; -} - -.user-dropdown-toggle i { - font-size: 12px; - color: #666; - transition: transform 0.2s ease; -} - -.user-dropdown.active .user-dropdown-toggle i { - transform: rotate(180deg); -} - -.user-dropdown-menu { - position: absolute; - top: 100%; - right: 0; - margin-top: 8px; - background-color: white; - border-radius: 4px; - box-shadow: 0 2px 10px rgba(0,0,0,0.1); - min-width: 160px; - display: none; - z-index: 1000; -} - -.user-dropdown.active .user-dropdown-menu { - display: block; -} - -.dropdown-item { - padding: 10px 16px; - display: flex; - align-items: center; - gap: 8px; - color: #333; - cursor: pointer; - transition: background-color 0.2s ease; -} - -.dropdown-item i { - font-size: 14px; - color: #666; - width: 16px; - text-align: center; -} - -.dropdown-item:hover { - background-color: #f5f5f5; -} - -.dropdown-divider { - height: 1px; - background-color: #e0e0e0; - margin: 4px 0; -} - -.admin-only { - display: none; -} - -/* 主内容区域调整 */ -#contentArea { - padding: 20px; -} - -/* 标签页容器 */ -.tabs-container { - background-color: white; - border-bottom: 1px solid #e0e0e0; -} - -.tabs-header { - display: flex; - padding: 0 20px; - overflow-x: auto; - scrollbar-width: thin; - scrollbar-color: #ccc transparent; - height: 40px; -} - -.tabs-header::-webkit-scrollbar { - height: 4px; -} - -.tabs-header::-webkit-scrollbar-track { - background: transparent; -} - -.tabs-header::-webkit-scrollbar-thumb { - background-color: #ccc; - border-radius: 2px; -} - -.tab { - display: flex; - align-items: center; - gap: 8px; - padding: 8px 16px; - background-color: #f5f5f5; - border: 1px solid #e0e0e0; - border-bottom: none; - border-radius: 4px 4px 0 0; - margin-right: 4px; - margin-top: 4px; - cursor: pointer; - position: relative; - user-select: none; - color: #666; - min-width: 120px; - max-width: 200px; - height: 36px; - transition: all 0.2s ease; -} - -.tab i { - font-size: 14px; -} - -.tab span { - flex: 1; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.tab .close-tab { - visibility: hidden; - width: 16px; - height: 16px; - display: flex; - align-items: center; - justify-content: center; - border-radius: 50%; - font-size: 12px; - color: #999; - margin-left: 4px; -} - -.tab:not([data-tab="overview"]):hover .close-tab { - visibility: visible; -} - -.tab .close-tab:hover { - background-color: rgba(0, 0, 0, 0.1); - color: #666; -} - -.tab.active { - background-color: white; - border: 1px solid #e0e0e0; - border-bottom: 1px solid white; - margin-bottom: -1px; - color: #2c3e50; - font-weight: 500; - box-shadow: 0 -2px 4px rgba(0,0,0,0.05); -} - -.tab.active::before { - content: ''; - position: absolute; - top: -1px; - left: -1px; - right: -1px; - height: 2px; - background: #3498db; - border-radius: 2px 2px 0 0; -} - -.tab.active i { - color: #3498db; -} - -.tab:hover:not(.active) { - background-color: #f0f0f0; -} - -/* 标签页内容区域 */ -.tabs-content { - flex: 1; - overflow: auto; - background-color: white; - position: relative; -} - -.tab-pane { - display: none; - padding: 20px; -} - -.tab-pane.active { - display: block; -} - -/* 调整主内容区域的布局 */ -.main-content { - display: flex; - flex-direction: column; - height: 100%; -} - -.password-input { - position: relative; - display: flex; - align-items: center; -} - -.password-input input { - padding-right: 40px; -} - -.toggle-password { - position: absolute; - right: 10px; - top: 50%; - transform: translateY(-50%); - background: none; - border: none; - padding: 5px; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - transition: opacity 0.3s ease; -} - -.toggle-password:hover { - opacity: 0.7; -} - -.toggle-password:focus { - outline: none; -} - -.visibility-icon { - width: 20px; - height: 20px; - object-fit: contain; -} \ No newline at end of file