// 主脚本文件 - Scene Step 编辑器 (修复版) function SceneStepEditor() { this.fileManager = new FileManager(); this.codeGenerator = new CodeGenerator(); this.currentSteps = []; this.stepFoldouts = []; this.actionFoldouts = []; this.initializeElements(); this.bindEvents(); this.loadInitialData(); } // 初始化DOM元素引用 SceneStepEditor.prototype.initializeElements = function() { this.fileSelect = document.getElementById('fileSelect'); this.newProcessBtn = document.getElementById('newProcessBtn'); this.stepsContainer = document.getElementById('stepsContainer'); this.saveBtn = document.getElementById('saveBtn'); this.generateCodeBtn = document.getElementById('generateCodeBtn'); // 目录选择相关元素 this.selectDirBtn = document.getElementById('selectDirBtn'); this.directoryInput = document.getElementById('directoryInput'); this.currentDirDisplay = document.getElementById('currentDirDisplay'); // 树状导航相关元素 this.sidebar = document.getElementById('sidebar'); this.sidebarToggle = document.getElementById('sidebarToggle'); this.treeContainer = document.getElementById('treeContainer'); this.scrollContainer = document.getElementById('scrollContainer'); // 模态框元素 this.newProcessModal = document.getElementById('newProcessModal'); this.newProcessName = document.getElementById('newProcessName'); this.confirmNewProcess = document.getElementById('confirmNewProcess'); this.cancelNewProcess = document.getElementById('cancelNewProcess'); this.confirmModal = document.getElementById('confirmModal'); this.confirmTitle = document.getElementById('confirmTitle'); this.confirmMessage = document.getElementById('confirmMessage'); this.confirmOk = document.getElementById('confirmOk'); this.confirmCancel = document.getElementById('confirmCancel'); this.codeModal = document.getElementById('codeModal'); this.generatedCode = document.getElementById('generatedCode'); this.copyCodeBtn = document.getElementById('copyCodeBtn'); this.closeCodeModal = document.getElementById('closeCodeModal'); }; // 绑定事件 SceneStepEditor.prototype.bindEvents = function() { var self = this; // 文件选择 this.fileSelect.addEventListener('change', function(e) { self.loadProcessFile(e.target.value); }); // 新增流程 this.newProcessBtn.addEventListener('click', function() { self.showNewProcessModal(); }); // 目录选择 - 智能判断使用哪种方式 this.selectDirBtn.addEventListener('click', function(e) { e.preventDefault(); self.handleDirectoryButtonClick(); }); this.directoryInput.addEventListener('change', function(e) { self.handleLegacyDirectorySelection(e); }); // 保存 this.saveBtn.addEventListener('click', function() { self.saveConfiguration(); }); // 生成代码 this.generateCodeBtn.addEventListener('click', function() { self.generateProcessEventsCode(); }); // 新增流程模态框 this.confirmNewProcess.addEventListener('click', function() { self.createNewProcess(); }); this.cancelNewProcess.addEventListener('click', function() { self.hideNewProcessModal(); }); // 确认模态框 this.confirmOk.addEventListener('click', function() { // 先保存回调函数,避免在hideConfirmModal中被清空 var callback = self.confirmCallback; self.hideConfirmModal(); if (callback) { callback(); } }); this.confirmCancel.addEventListener('click', function() { self.hideConfirmModal(); }); // 代码模态框 this.copyCodeBtn.addEventListener('click', function() { self.copyGeneratedCode(); }); this.closeCodeModal.addEventListener('click', function() { self.hideCodeModal(); }); // 点击模态框背景关闭 window.addEventListener('click', function(e) { if (e.target === self.newProcessModal) { self.hideNewProcessModal(); } if (e.target === self.confirmModal) { self.hideConfirmModal(); } if (e.target === self.codeModal) { self.hideCodeModal(); } }); // 键盘事件 document.addEventListener('keydown', function(e) { if (e.key === 'Escape') { self.hideNewProcessModal(); self.hideConfirmModal(); self.hideCodeModal(); } }); // 侧边栏切换 this.sidebarToggle.addEventListener('click', function() { self.toggleSidebar(); }); }; // 加载初始数据 SceneStepEditor.prototype.loadInitialData = function() { var self = this; // 更新界面提示信息 this.updateDirectoryHelp(); // 尝试恢复之前保存的目录句柄 if (this.fileManager.supportsFileSystemAccess) { this.fileManager.restoreDirectoryHandle().then(function(handle) { if (handle) { console.log("已恢复目录句柄,自动加载文件"); self.fileManager.loadFilesFromDirectoryHandle(handle).then(function(result) { if (result.success) { self.updateFileDropdown(); self.renderTreeNavigation(); self.showAlert("目录已恢复", "已自动恢复之前选择的目录 '" + result.directory + "' 并读取 " + result.loadedFiles + " 个文件"); } }); } else { // 加载默认文件 self.loadProcessFile(self.fileManager.getCurrentFileName()); } }); } else { // 加载默认文件 this.loadProcessFile(this.fileManager.getCurrentFileName()); } // 更新文件下拉列表 this.updateFileDropdown(); }; // 更新目录帮助信息 SceneStepEditor.prototype.updateDirectoryHelp = function() { var helpElement = document.getElementById('directoryHelp'); if (!helpElement) return; if (this.fileManager.supportsFileSystemAccess) { helpElement.innerHTML = '🎉 您的浏览器支持直接文件写入!选择项目文件夹后,新建和保存的文件将直接写入该文件夹'; helpElement.style.borderLeft = '2px solid #28a745'; helpElement.style.background = 'rgba(40, 167, 69, 0.1)'; } else { helpElement.innerHTML = '⚠️ 您的浏览器不支持直接文件写入,新建和保存的文件将下载到默认下载文件夹'; helpElement.style.borderLeft = '2px solid #ffc107'; helpElement.style.background = 'rgba(255, 193, 7, 0.1)'; } }; // 更新文件下拉列表 SceneStepEditor.prototype.updateFileDropdown = function() { var fileNames = this.fileManager.getFilesInCurrentDirectory(); var currentFile = this.fileManager.getCurrentFileName(); var currentDir = this.fileManager.getCurrentDirectory(); this.fileSelect.innerHTML = ''; for (var i = 0; i < fileNames.length; i++) { var fileName = fileNames[i]; var option = document.createElement('option'); option.value = fileName; option.textContent = fileName; if (fileName === currentFile) { option.selected = true; } this.fileSelect.appendChild(option); } // 更新当前目录显示 this.currentDirDisplay.textContent = '当前目录: ' + currentDir; // 更新保存按钮文本 this.saveBtn.textContent = '保存到 ' + currentFile; }; // 加载流程文件 SceneStepEditor.prototype.loadProcessFile = function(fileName) { if (!fileName) return; if (this.fileManager.setCurrentFile(fileName)) { this.currentSteps = this.fileManager.loadProcessFile(fileName); this.stepFoldouts = []; this.actionFoldouts = []; for (var i = 0; i < this.currentSteps.length; i++) { this.stepFoldouts.push(true); var actionFoldout = []; for (var j = 0; j < this.currentSteps[i].Actions.length; j++) { actionFoldout.push(true); } this.actionFoldouts.push(actionFoldout); } this.updateFileDropdown(); this.renderSteps(); } }; // 渲染步骤列表 SceneStepEditor.prototype.renderSteps = function() { this.stepsContainer.innerHTML = ''; if (this.currentSteps.length === 0) { this.renderEmptyState(); this.renderTreeNavigation(); // 同时更新树状导航 return; } for (var i = 0; i < this.currentSteps.length; i++) { this.renderStep(this.currentSteps[i], i); } // 渲染树状导航 this.renderTreeNavigation(); }; // 渲染空状态 SceneStepEditor.prototype.renderEmptyState = function() { var emptyDiv = document.createElement('div'); emptyDiv.className = 'empty-state'; emptyDiv.innerHTML = '

暂无步骤

' + '
' + '' + '
'; this.stepsContainer.appendChild(emptyDiv); }; // HTML转义函数 SceneStepEditor.prototype.escapeHtml = function(text) { var div = document.createElement('div'); div.textContent = text; return div.innerHTML; }; // 渲染单个步骤 SceneStepEditor.prototype.renderStep = function(step, stepIndex) { var stepDiv = document.createElement('div'); stepDiv.className = 'step-item'; // 确保折叠状态数组长度正确 while (this.stepFoldouts.length <= stepIndex) { this.stepFoldouts.push(true); } while (this.actionFoldouts.length <= stepIndex) { this.actionFoldouts.push([]); } var isExpanded = this.stepFoldouts[stepIndex]; stepDiv.innerHTML = '
' + '
' + '
' + (stepIndex === this.currentSteps.length - 1 ? '' : '' ) + '' + '
' + '' + '
' + '
' + '
' + '' + '' + '
' + '
' + '' + '
' + '
' + '
'; this.stepsContainer.appendChild(stepDiv); // 渲染动作列表 if (isExpanded) { this.renderActions(stepIndex); } }; // 渲染动作列表 SceneStepEditor.prototype.renderActions = function(stepIndex) { var actionsContainer = document.getElementById('actionsContainer_' + stepIndex); if (!actionsContainer) return; var step = this.currentSteps[stepIndex]; actionsContainer.innerHTML = ''; if (step.Actions.length === 0) { var emptyDiv = document.createElement('div'); emptyDiv.className = 'empty-state'; emptyDiv.innerHTML = '

暂无动作

' + '
' + '' + '
'; actionsContainer.appendChild(emptyDiv); return; } for (var i = 0; i < step.Actions.length; i++) { this.renderAction(stepIndex, step.Actions[i], i, actionsContainer); } }; // 渲染单个动作 SceneStepEditor.prototype.renderAction = function(stepIndex, action, actionIndex, container) { // 确保动作折叠状态数组长度正确 while (this.actionFoldouts[stepIndex].length <= actionIndex) { this.actionFoldouts[stepIndex].push(true); } var isExpanded = this.actionFoldouts[stepIndex][actionIndex]; var actionDiv = document.createElement('div'); actionDiv.className = 'action-item'; actionDiv.innerHTML = '
' + '
' + (actionIndex === this.currentSteps[stepIndex].Actions.length - 1 ? '' : '' ) + '' + '
' + '' + '
' + '
' + '' + '
'; container.appendChild(actionDiv); // 渲染动作详情 if (isExpanded) { this.renderActionDetails(stepIndex, actionIndex); } }; // 基本的方法实现 SceneStepEditor.prototype.toggleStepFoldout = function(stepIndex) { this.stepFoldouts[stepIndex] = !this.stepFoldouts[stepIndex]; this.renderSteps(); }; SceneStepEditor.prototype.toggleActionFoldout = function(stepIndex, actionIndex) { this.actionFoldouts[stepIndex][actionIndex] = !this.actionFoldouts[stepIndex][actionIndex]; this.renderActions(stepIndex); }; SceneStepEditor.prototype.updateStepDescription = function(stepIndex, value) { if (this.currentSteps[stepIndex]) { this.currentSteps[stepIndex].StepDescription = value; // 同步更新树状导航显示 this.renderTreeNavigation(); } }; SceneStepEditor.prototype.addNewStep = function() { var newStep = DataUtils.createNewStep(); this.currentSteps.push(newStep); this.stepFoldouts.push(true); this.actionFoldouts.push([]); this.renderSteps(); }; SceneStepEditor.prototype.removeStep = function(stepIndex) { var self = this; this.showConfirmModal( "确认删除", "确定要删除步骤 \"" + this.currentSteps[stepIndex].StepDescription + "\" 吗?", function() { self.currentSteps.splice(stepIndex, 1); self.stepFoldouts.splice(stepIndex, 1); self.actionFoldouts.splice(stepIndex, 1); self.renderSteps(); } ); }; SceneStepEditor.prototype.addNewAction = function(stepIndex) { var newAction = DataUtils.createNewAction(); this.currentSteps[stepIndex].Actions.push(newAction); this.actionFoldouts[stepIndex].push(true); this.renderActions(stepIndex); // 同步更新树状导航 this.renderTreeNavigation(); }; SceneStepEditor.prototype.removeAction = function(stepIndex, actionIndex) { var self = this; var action = this.currentSteps[stepIndex].Actions[actionIndex]; this.showConfirmModal( "确认删除", "确定要删除动作 \"" + action.Title + "\" 吗?", function() { self.currentSteps[stepIndex].Actions.splice(actionIndex, 1); self.actionFoldouts[stepIndex].splice(actionIndex, 1); self.renderActions(stepIndex); // 同步更新树状导航 self.renderTreeNavigation(); } ); }; SceneStepEditor.prototype.renderActionDetails = function(stepIndex, actionIndex) { var actionContent = document.getElementById('actionContent_' + stepIndex + '_' + actionIndex); if (!actionContent) return; var action = this.currentSteps[stepIndex].Actions[actionIndex]; actionContent.innerHTML = '
' + '' + '' + '
' + '
' + '' + '' + '
' + '
' + '' + '' + '
' + '
' + '' + '
'; this.renderActionTypeContent(stepIndex, actionIndex); }; SceneStepEditor.prototype.updateActionProperty = function(stepIndex, actionIndex, property, value) { if (this.currentSteps[stepIndex] && this.currentSteps[stepIndex].Actions[actionIndex]) { this.currentSteps[stepIndex].Actions[actionIndex][property] = value; // 如果修改的是Title属性,需要更新树状导航显示 if (property === 'Title') { this.renderTreeNavigation(); } } }; SceneStepEditor.prototype.updateActionType = function(stepIndex, actionIndex, newType) { var action = this.currentSteps[stepIndex].Actions[actionIndex]; if (action.ActionType !== newType) { action.ActionType = newType; // 清理不相关的数据 if (newType === ProcessActionType.DEFAULT) { action.JudgmentQuestions = []; action.MultipleChoiceQuestions = []; if (!action.TargetObjects) { action.TargetObjects = []; } } else if (newType === ProcessActionType.JUDGMENT) { action.TargetObjects = []; action.MultipleChoiceQuestions = []; action.IsSequential = false; if (!action.JudgmentQuestions) { action.JudgmentQuestions = []; } } else if (newType === ProcessActionType.MULTIPLE_CHOICE) { action.TargetObjects = []; action.JudgmentQuestions = []; action.IsSequential = false; if (!action.MultipleChoiceQuestions) { action.MultipleChoiceQuestions = []; } } this.renderActionTypeContent(stepIndex, actionIndex); } }; SceneStepEditor.prototype.renderActionTypeContent = function(stepIndex, actionIndex) { var actionTypeContent = document.getElementById('actionTypeContent_' + stepIndex + '_' + actionIndex); if (!actionTypeContent) return; var action = this.currentSteps[stepIndex].Actions[actionIndex]; var content = '
' + '' + '' + '
' + '
' + '' + '
'; if (action.ActionType === ProcessActionType.DEFAULT) { content += '
' + '' + '
' + '
' + '' + '
'; } else if (action.ActionType === ProcessActionType.JUDGMENT) { content += '
' + '' + '
'; } else if (action.ActionType === ProcessActionType.MULTIPLE_CHOICE) { content += '
' + '' + '
'; } actionTypeContent.innerHTML = content; // 渲染具体内容 if (action.ActionType === ProcessActionType.DEFAULT) { this.renderTargetObjects(stepIndex, actionIndex); } else if (action.ActionType === ProcessActionType.JUDGMENT) { this.renderJudgmentQuestions(stepIndex, actionIndex); } else if (action.ActionType === ProcessActionType.MULTIPLE_CHOICE) { this.renderMultipleChoiceQuestions(stepIndex, actionIndex); } }; // 渲染目标对象列表 SceneStepEditor.prototype.renderTargetObjects = function(stepIndex, actionIndex) { var container = document.getElementById('targetObjectsContainer_' + stepIndex + '_' + actionIndex); if (!container) return; var action = this.currentSteps[stepIndex].Actions[actionIndex]; var content = '

目标对象列表

'; if (action.TargetObjects.length === 0) { content += '
' + '

暂无目标对象

' + '
' + '' + '
' + '
'; } else { for (var i = 0; i < action.TargetObjects.length; i++) { var target = action.TargetObjects[i]; content += '
' + '
' + '目标对象 ' + (i + 1) + '' + '' + '
' + '
' + '' + '' + '
' + '
' + '' + '' + '
' + '
' + '' + '' + '
' + '
'; } content += '
' + '' + '
'; } container.innerHTML = content; }; // 添加目标对象 SceneStepEditor.prototype.addTargetObject = function(stepIndex, actionIndex) { var newTarget = DataUtils.createNewTargetObject(); this.currentSteps[stepIndex].Actions[actionIndex].TargetObjects.push(newTarget); this.renderTargetObjects(stepIndex, actionIndex); }; // 删除目标对象 SceneStepEditor.prototype.removeTargetObject = function(stepIndex, actionIndex, targetIndex) { var self = this; var target = this.currentSteps[stepIndex].Actions[actionIndex].TargetObjects[targetIndex]; this.showConfirmModal( "确认删除", "确定要删除目标对象 \"" + target.ObjectName + "\" 吗?", function() { self.currentSteps[stepIndex].Actions[actionIndex].TargetObjects.splice(targetIndex, 1); self.renderTargetObjects(stepIndex, actionIndex); } ); }; // 更新目标对象属性 SceneStepEditor.prototype.updateTargetObjectProperty = function(stepIndex, actionIndex, targetIndex, property, value) { var target = this.currentSteps[stepIndex].Actions[actionIndex].TargetObjects[targetIndex]; if (target) { target[property] = value; } }; // 模态框方法 SceneStepEditor.prototype.showNewProcessModal = function() { this.newProcessName.value = "新流程"; this.newProcessModal.style.display = 'block'; this.newProcessName.focus(); this.newProcessName.select(); }; SceneStepEditor.prototype.hideNewProcessModal = function() { this.newProcessModal.style.display = 'none'; }; SceneStepEditor.prototype.createNewProcess = function() { var fileName = this.newProcessName.value.trim(); if (!fileName) { alert("请输入流程名称"); return; } var self = this; // 尝试直接写入到选择的目录 if (this.fileManager.supportsFileSystemAccess && this.fileManager.directoryHandle) { this.fileManager.writeFileToDirectory(fileName, []).then(function(result) { self.hideNewProcessModal(); self.updateFileDropdown(); self.loadProcessFile(result.fileName); self.showAlert("新流程创建成功", "已在目录 '" + result.directory + "' 中直接创建新流程:" + result.fileName + "\n\n🎉 文件已直接保存到您选择的项目文件夹中!"); }).catch(function(error) { console.error("直接写入失败,降级到下载模式:", error); self.createNewProcessFallback(fileName); }); return; } // 降级到传统模式 this.createNewProcessFallback(fileName); }; // 降级的新建流程方法 SceneStepEditor.prototype.createNewProcessFallback = function(fileName) { var result = this.fileManager.createNewProcessFileInCurrentDir(fileName); if (result.success) { this.hideNewProcessModal(); this.updateFileDropdown(); this.loadProcessFile(result.fileName); // 触发下载保存到本地 this.downloadProcessFile(result.fileName); var message = "已创建新流程:" + result.fileName; if (this.fileManager.directoryHandle) { message += "\n\n💡 文件已自动下载,请将其保存到您选择的项目文件夹 '" + result.directory + "' 中,然后重新选择该文件夹以刷新文件列表。"; } else { message += "\n\n💡 文件已自动下载到默认下载文件夹。建议先选择项目文件夹以启用直接保存功能。"; } this.showAlert("新流程创建成功", message); } else { alert("创建失败:" + result.error); } }; // 处理目录按钮点击 SceneStepEditor.prototype.handleDirectoryButtonClick = function() { var self = this; // 优先使用 File System Access API if (this.fileManager.supportsFileSystemAccess) { this.currentDirDisplay.textContent = "正在选择目录..."; this.selectDirBtn.disabled = true; this.selectDirBtn.textContent = "📂 正在选择目录..."; this.fileManager.selectDirectoryWithAPI().then(function(result) { self.selectDirBtn.disabled = false; self.selectDirBtn.textContent = "📂 选择本地项目文件夹"; if (result.success) { // 更新界面 self.updateFileDropdown(); // 如果有文件,加载第一个 var fileNames = self.fileManager.getFilesInCurrentDirectory(); if (fileNames.length > 0) { var firstFile = fileNames[0]; self.fileManager.setCurrentFile(firstFile); self.loadProcessFile(firstFile); } self.showAlert("目录选择成功", "已选择目录 '" + result.directory + "' 并读取 " + result.loadedFiles + " 个JSON流程文件\n\n🎉 现在新建和保存的文件将直接写入此目录!"); // 重新渲染树状导航 self.renderTreeNavigation(); } else { self.currentDirDisplay.textContent = "当前目录: " + self.fileManager.getCurrentDirectory(); alert("选择目录失败:" + result.error); } }).catch(function(error) { self.selectDirBtn.disabled = false; self.selectDirBtn.textContent = "📂 选择本地项目文件夹"; self.currentDirDisplay.textContent = "当前目录: " + self.fileManager.getCurrentDirectory(); // 如果用户取消选择,不显示错误 if (error.name === 'AbortError') { console.log("用户取消了目录选择"); return; } alert("选择目录时发生错误:" + error.message); }); return; } // 降级到传统文件选择方式 console.log("降级到传统文件选择方式"); this.directoryInput.click(); }; // 处理传统的目录选择(通过 file input) SceneStepEditor.prototype.handleLegacyDirectorySelection = function(event) { var self = this; var files = event.target.files; if (!files || files.length === 0) { return; } // 显示加载提示 this.currentDirDisplay.textContent = "正在读取本地文件夹..."; this.selectDirBtn.disabled = true; this.selectDirBtn.textContent = "📂 正在读取文件夹..."; this.fileManager.loadFilesFromDirectory(files).then(function(result) { self.selectDirBtn.disabled = false; self.selectDirBtn.textContent = "📂 选择本地项目文件夹"; if (result.success) { // 更新界面 self.updateFileDropdown(); // 如果有文件,加载第一个 var fileNames = self.fileManager.getFilesInCurrentDirectory(); if (fileNames.length > 0) { var firstFile = fileNames[0]; self.fileManager.setCurrentFile(firstFile); self.loadProcessFile(firstFile); } self.showAlert("本地文件夹读取成功", "已从本地 " + result.directory + " 文件夹读取 " + result.loadedFiles + " 个JSON流程文件\n\n⚠️ 由于浏览器限制,新建和保存的文件将下载到默认下载文件夹,请手动移动到项目文件夹中"); // 重新渲染树状导航 self.renderTreeNavigation(); } else { self.currentDirDisplay.textContent = "当前目录: " + self.fileManager.getCurrentDirectory(); alert("读取失败:" + result.error); } }).catch(function(error) { self.selectDirBtn.disabled = false; self.selectDirBtn.textContent = "📂 选择本地项目文件夹"; self.currentDirDisplay.textContent = "当前目录: " + self.fileManager.getCurrentDirectory(); alert("读取本地文件夹时发生错误:" + error.message); }); }; SceneStepEditor.prototype.showConfirmModal = function(title, message, callback) { this.confirmTitle.textContent = title; this.confirmMessage.textContent = message; this.confirmCallback = callback; this.confirmModal.style.display = 'block'; }; SceneStepEditor.prototype.hideConfirmModal = function() { this.confirmModal.style.display = 'none'; this.confirmCallback = null; }; SceneStepEditor.prototype.showCodeModal = function(code) { this.generatedCode.value = code; this.codeModal.style.display = 'block'; }; SceneStepEditor.prototype.hideCodeModal = function() { this.codeModal.style.display = 'none'; }; SceneStepEditor.prototype.copyGeneratedCode = function() { this.generatedCode.select(); document.execCommand('copy'); this.showAlert("复制成功", "代码已复制到剪贴板"); }; SceneStepEditor.prototype.showAlert = function(title, message) { alert(title + "\n" + message); }; // 下载流程文件到本地 SceneStepEditor.prototype.downloadProcessFile = function(fileName) { try { var steps = this.fileManager.loadProcessFile(fileName); var jsonString = JSON.stringify(steps, null, 2); // 创建Blob对象 var blob = new Blob([jsonString], { type: 'application/json' }); // 创建下载链接 var url = window.URL.createObjectURL(blob); var a = document.createElement('a'); a.href = url; a.download = fileName; // 隐藏链接并触发点击 a.style.display = 'none'; document.body.appendChild(a); a.click(); // 清理 document.body.removeChild(a); window.URL.revokeObjectURL(url); console.log("文件下载触发: " + fileName); } catch (error) { console.error("下载文件失败: ", error); alert("下载文件失败:" + error.message); } }; SceneStepEditor.prototype.saveConfiguration = function() { var self = this; var currentFileName = this.fileManager.getCurrentFileName(); var currentDir = this.fileManager.getCurrentDirectory(); var confirmMessage = "是否要保存当前配置到 " + currentFileName + "?"; if (this.fileManager.supportsFileSystemAccess && this.fileManager.directoryHandle) { confirmMessage += "\n\n🎉 文件将直接保存到您选择的项目文件夹 '" + currentDir + "' 中。"; } else { confirmMessage += "\n\n💡 文件将自动下载到您的下载文件夹,请手动移动到项目文件夹中。"; } this.showConfirmModal("确认保存", confirmMessage, function() { // 尝试直接写入到选择的目录 if (self.fileManager.supportsFileSystemAccess && self.fileManager.directoryHandle) { self.fileManager.writeFileToDirectory(currentFileName, self.currentSteps).then(function(result) { self.showAlert("保存成功", "配置已直接保存到目录 '" + result.directory + "' 中的文件:" + result.fileName + "\n\n🎉 文件已直接更新,无需手动移动文件!"); }).catch(function(error) { console.error("直接写入失败,降级到下载模式:", error); self.saveConfigurationFallback(currentFileName, currentDir); }); return; } // 降级到传统保存方式 self.saveConfigurationFallback(currentFileName, currentDir); }); }; // 降级的保存配置方法 SceneStepEditor.prototype.saveConfigurationFallback = function(currentFileName, currentDir) { var result = this.fileManager.saveProcessFile(currentFileName, this.currentSteps); if (result.success) { // 触发下载 this.downloadProcessFile(currentFileName); var message = "配置已保存并下载:" + currentFileName; if (this.fileManager.directoryHandle) { message += "\n\n💡 请将下载的文件放入您的项目文件夹 '" + currentDir + "' 中,然后重新选择该文件夹以刷新文件列表。"; } else { message += "\n\n💡 文件已下载到默认下载文件夹。建议先选择项目文件夹以启用直接保存功能。"; } this.showAlert("保存成功", message); } else { alert("保存失败:" + result.error); } }; SceneStepEditor.prototype.generateProcessEventsCode = function() { try { var currentFileName = this.fileManager.getCurrentFileName(); if (!this.currentSteps || this.currentSteps.length === 0) { alert("当前流程文件为空,无法生成代码"); return; } var code = this.codeGenerator.generateProcessEventsCode(currentFileName, this.currentSteps); this.showCodeModal(code); } catch (error) { alert("生成代码失败:" + error.message); } }; // 全局编辑器实例 var editor; // 渲染判断题列表 SceneStepEditor.prototype.renderJudgmentQuestions = function(stepIndex, actionIndex) { var container = document.getElementById('judgmentQuestionsContainer_' + stepIndex + '_' + actionIndex); if (!container) return; var action = this.currentSteps[stepIndex].Actions[actionIndex]; var content = '

判断题配置

'; if (action.JudgmentQuestions.length === 0) { content += '
' + '

暂无判断题

' + '
' + '' + '
' + '
'; } else { for (var i = 0; i < action.JudgmentQuestions.length; i++) { var question = action.JudgmentQuestions[i]; content += '
' + '
' + '题目 ' + (i + 1) + '' + '' + '
' + '
' + '' + '' + '
' + '
' + '' + '' + '
' + '
' + '' + '' + '
' + '
'; } content += '
' + '' + '
'; } container.innerHTML = content; }; // 添加判断题 SceneStepEditor.prototype.addJudgmentQuestion = function(stepIndex, actionIndex) { var newQuestion = DataUtils.createNewJudgmentQuestion(); this.currentSteps[stepIndex].Actions[actionIndex].JudgmentQuestions.push(newQuestion); this.renderJudgmentQuestions(stepIndex, actionIndex); }; // 删除判断题 SceneStepEditor.prototype.removeJudgmentQuestion = function(stepIndex, actionIndex, questionIndex) { var self = this; this.showConfirmModal( "确认删除", "确定要删除这个判断题吗?", function() { self.currentSteps[stepIndex].Actions[actionIndex].JudgmentQuestions.splice(questionIndex, 1); self.renderJudgmentQuestions(stepIndex, actionIndex); } ); }; // 更新判断题属性 SceneStepEditor.prototype.updateJudgmentQuestionProperty = function(stepIndex, actionIndex, questionIndex, property, value) { var question = this.currentSteps[stepIndex].Actions[actionIndex].JudgmentQuestions[questionIndex]; if (question) { question[property] = value; } }; // 渲染多选题列表 SceneStepEditor.prototype.renderMultipleChoiceQuestions = function(stepIndex, actionIndex) { var container = document.getElementById('multipleChoiceContainer_' + stepIndex + '_' + actionIndex); if (!container) return; var action = this.currentSteps[stepIndex].Actions[actionIndex]; var content = '

多选题配置

'; if (action.MultipleChoiceQuestions.length === 0) { content += '
' + '

暂无多选题

' + '
' + '' + '
' + '
'; } else { for (var i = 0; i < action.MultipleChoiceQuestions.length; i++) { var question = action.MultipleChoiceQuestions[i]; content += '
' + '
' + '题目 ' + (i + 1) + '' + '' + '
' + '
' + '' + '' + '
' + '
' + '' + '' + '
' + '
' + '' + '
' + '' + '
' + '
' + '
'; } content += '
' + '' + '
'; } container.innerHTML = content; // 渲染每个题目的选项 for (var i = 0; i < action.MultipleChoiceQuestions.length; i++) { this.renderMultipleChoiceOptions(stepIndex, actionIndex, i); } }; // 渲染多选题选项 SceneStepEditor.prototype.renderMultipleChoiceOptions = function(stepIndex, actionIndex, questionIndex) { var container = document.getElementById('optionsContainer_' + stepIndex + '_' + actionIndex + '_' + questionIndex); if (!container) return; var question = this.currentSteps[stepIndex].Actions[actionIndex].MultipleChoiceQuestions[questionIndex]; var content = ''; for (var i = 0; i < question.Options.length; i++) { var option = question.Options[i]; var isCorrect = question.CorrectAnswers.indexOf(option) !== -1; content += '
' + '' + '' + '' + '
'; } content += '
' + '' + '
'; container.innerHTML = content; }; // 添加多选题 SceneStepEditor.prototype.addMultipleChoiceQuestion = function(stepIndex, actionIndex) { var newQuestion = DataUtils.createNewMultipleChoice(); this.currentSteps[stepIndex].Actions[actionIndex].MultipleChoiceQuestions.push(newQuestion); this.renderMultipleChoiceQuestions(stepIndex, actionIndex); }; // 删除多选题 SceneStepEditor.prototype.removeMultipleChoiceQuestion = function(stepIndex, actionIndex, questionIndex) { var self = this; this.showConfirmModal( "确认删除", "确定要删除这个多选题吗?", function() { self.currentSteps[stepIndex].Actions[actionIndex].MultipleChoiceQuestions.splice(questionIndex, 1); self.renderMultipleChoiceQuestions(stepIndex, actionIndex); } ); }; // 更新多选题属性 SceneStepEditor.prototype.updateMultipleChoiceQuestionProperty = function(stepIndex, actionIndex, questionIndex, property, value) { var question = this.currentSteps[stepIndex].Actions[actionIndex].MultipleChoiceQuestions[questionIndex]; if (question) { question[property] = value; } }; // 添加多选题选项 SceneStepEditor.prototype.addMultipleChoiceOption = function(stepIndex, actionIndex, questionIndex) { var question = this.currentSteps[stepIndex].Actions[actionIndex].MultipleChoiceQuestions[questionIndex]; question.Options.push(""); this.renderMultipleChoiceOptions(stepIndex, actionIndex, questionIndex); }; // 删除多选题选项 SceneStepEditor.prototype.removeMultipleChoiceOption = function(stepIndex, actionIndex, questionIndex, optionIndex) { var question = this.currentSteps[stepIndex].Actions[actionIndex].MultipleChoiceQuestions[questionIndex]; var option = question.Options[optionIndex]; // 从选项列表中删除 question.Options.splice(optionIndex, 1); // 从正确答案列表中删除(如果存在) var correctIndex = question.CorrectAnswers.indexOf(option); if (correctIndex > -1) { question.CorrectAnswers.splice(correctIndex, 1); } this.renderMultipleChoiceOptions(stepIndex, actionIndex, questionIndex); }; // 更新多选题选项 SceneStepEditor.prototype.updateMultipleChoiceOption = function(stepIndex, actionIndex, questionIndex, optionIndex, newValue) { var question = this.currentSteps[stepIndex].Actions[actionIndex].MultipleChoiceQuestions[questionIndex]; var oldValue = question.Options[optionIndex]; // 更新选项值 question.Options[optionIndex] = newValue; // 如果旧值在正确答案中,替换为新值 var correctIndex = question.CorrectAnswers.indexOf(oldValue); if (correctIndex > -1) { question.CorrectAnswers[correctIndex] = newValue; } }; // 切换正确答案状态 SceneStepEditor.prototype.toggleCorrectAnswer = function(stepIndex, actionIndex, questionIndex, optionIndex, isCorrect) { var question = this.currentSteps[stepIndex].Actions[actionIndex].MultipleChoiceQuestions[questionIndex]; var option = question.Options[optionIndex]; if (isCorrect) { // 添加到正确答案列表 if (question.CorrectAnswers.indexOf(option) === -1) { question.CorrectAnswers.push(option); } } else { // 从正确答案列表中删除 var index = question.CorrectAnswers.indexOf(option); if (index > -1) { question.CorrectAnswers.splice(index, 1); } } }; // 树状导航相关方法 // 切换侧边栏显示/隐藏 SceneStepEditor.prototype.toggleSidebar = function() { this.sidebar.classList.toggle('collapsed'); }; // 渲染树状导航 SceneStepEditor.prototype.renderTreeNavigation = function() { this.treeContainer.innerHTML = ''; if (this.currentSteps.length === 0) { var emptyDiv = document.createElement('div'); emptyDiv.className = 'tree-empty-state'; emptyDiv.innerHTML = '

暂无步骤

'; this.treeContainer.appendChild(emptyDiv); return; } for (var i = 0; i < this.currentSteps.length; i++) { this.renderTreeNode(this.currentSteps[i], i); } }; // 渲染单个树节点(步骤) SceneStepEditor.prototype.renderTreeNode = function(step, stepIndex) { var self = this; var isExpanded = this.stepFoldouts[stepIndex]; // 创建步骤节点 var stepNode = document.createElement('div'); stepNode.className = 'tree-node tree-node-step'; stepNode.id = 'tree-step-' + stepIndex; // 步骤头部 var stepHeader = document.createElement('div'); stepHeader.className = 'tree-node-header'; stepHeader.innerHTML = '
' + (isExpanded ? '📂' : '📁') + '
' + '
' + this.escapeHtml(step.StepDescription) + ' (' + stepIndex + ')' + '
' + '
' + '' + '
'; stepNode.appendChild(stepHeader); // 添加动作子节点容器 if (step.Actions && step.Actions.length > 0) { var childrenContainer = document.createElement('div'); childrenContainer.className = 'tree-children' + (isExpanded ? '' : ' collapsed'); childrenContainer.id = 'tree-children-' + stepIndex; for (var j = 0; j < step.Actions.length; j++) { this.renderTreeActionNode(childrenContainer, step.Actions[j], stepIndex, j); } stepNode.appendChild(childrenContainer); } // 绑定事件 - 整个标题区域可点击折叠/展开 stepHeader.addEventListener('click', function(e) { // 如果点击的是跳转按钮,则不触发折叠 if (e.target.closest('.btn-tree-jump')) { return; } self.toggleStepFromTree(stepIndex); }); var jumpBtn = stepHeader.querySelector('.btn-tree-jump'); if (jumpBtn) { jumpBtn.addEventListener('click', function(e) { e.stopPropagation(); self.jumpToStep(stepIndex); }); } this.treeContainer.appendChild(stepNode); }; // 渲染动作子节点 SceneStepEditor.prototype.renderTreeActionNode = function(container, action, stepIndex, actionIndex) { var self = this; var actionNode = document.createElement('div'); actionNode.className = 'tree-node tree-node-action'; actionNode.id = 'tree-action-' + stepIndex + '-' + actionIndex; var actionHeader = document.createElement('div'); actionHeader.className = 'tree-node-header'; actionHeader.innerHTML = '
' + '
' + this.escapeHtml(action.Title) + ' (' + stepIndex + '-' + actionIndex + ')' + '
'; actionNode.appendChild(actionHeader); // 绑定跳转事件 - 整个动作头部都可以点击跳转 actionHeader.addEventListener('click', function(e) { e.stopPropagation(); self.jumpToAction(stepIndex, actionIndex); }); // 添加鼠标悬停效果,让用户知道可以点击 actionHeader.addEventListener('mouseenter', function() { actionHeader.style.cursor = 'pointer'; actionHeader.style.backgroundColor = 'rgba(74, 144, 226, 0.1)'; }); actionHeader.addEventListener('mouseleave', function() { actionHeader.style.cursor = ''; actionHeader.style.backgroundColor = ''; }); container.appendChild(actionNode); }; // 从树状导航切换步骤折叠状态 SceneStepEditor.prototype.toggleStepFromTree = function(stepIndex) { // 切换主界面的折叠状态 this.stepFoldouts[stepIndex] = !this.stepFoldouts[stepIndex]; // 重新渲染主界面和树状导航 this.renderSteps(); }; // 跳转到指定步骤 SceneStepEditor.prototype.jumpToStep = function(stepIndex) { var stepElement = this.stepsContainer.children[stepIndex]; if (stepElement) { // 找到步骤的标题元素 var stepHeader = stepElement.querySelector('.step-header'); if (stepHeader) { stepHeader.scrollIntoView({ behavior: 'smooth', block: 'start', inline: 'nearest' }); // 高亮整个步骤 this.highlightElement(stepElement); } } }; // 跳转到指定动作 SceneStepEditor.prototype.jumpToAction = function(stepIndex, actionIndex) { // 首先确保步骤是展开的 if (!this.stepFoldouts[stepIndex]) { this.stepFoldouts[stepIndex] = true; this.renderSteps(); } // 等待DOM更新后再跳转 var self = this; setTimeout(function() { var actionsContainer = document.getElementById('actionsContainer_' + stepIndex); if (actionsContainer && actionsContainer.children[actionIndex]) { var actionElement = actionsContainer.children[actionIndex]; // 找到动作的标题元素 var actionHeader = actionElement.querySelector('.action-header'); if (actionHeader) { actionHeader.scrollIntoView({ behavior: 'smooth', block: 'start', inline: 'nearest' }); // 高亮整个动作 self.highlightElement(actionElement); } else { // 如果没有找到标题,则滚动到动作元素顶部 actionElement.scrollIntoView({ behavior: 'smooth', block: 'start', inline: 'nearest' }); self.highlightElement(actionElement); } } }, 100); }; // 高亮元素 SceneStepEditor.prototype.highlightElement = function(element) { element.style.transition = 'all 0.3s ease'; element.style.backgroundColor = 'rgba(74, 144, 226, 0.2)'; element.style.transform = 'scale(1.01)'; setTimeout(function() { element.style.backgroundColor = ''; element.style.transform = ''; }, 1500); }; // 页面加载完成后初始化编辑器 document.addEventListener('DOMContentLoaded', function() { // 初始化全局编辑器实例 window.editor = new SceneStepEditor(); editor = window.editor; });