// 主脚本文件 - 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 =
'' +
'' +
'' +
'
' +
'' +
'' +
'
' +
'
' +
'' +
'
' +
'
' +
'';
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 =
'' +
'' +
'' +
'
';
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 +=
'' +
'' +
'
' +
'' +
'' +
'
' +
'
' +
'' +
'' +
'
' +
'
' +
'' +
'' +
'
' +
'
';
}
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 +=
'';
}
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 +=
'' +
'' +
'
' +
'' +
'' +
'
' +
'
' +
'' +
'' +
'
' +
'
' +
'
';
}
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;
});