Tz2/scene-step-editor-web/script.js

1050 lines
42 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 主脚本文件 - 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.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');
}
// 绑定事件
bindEvents() {
// 文件选择
this.fileSelect.addEventListener('change', (e) => {
this.loadProcessFile(e.target.value);
});
// 新增流程
this.newProcessBtn.addEventListener('click', () => {
this.showNewProcessModal();
});
// 保存
this.saveBtn.addEventListener('click', () => {
this.saveConfiguration();
});
// 生成代码
this.generateCodeBtn.addEventListener('click', () => {
this.generateProcessEventsCode();
});
// 新增流程模态框
this.confirmNewProcess.addEventListener('click', () => {
this.createNewProcess();
});
this.cancelNewProcess.addEventListener('click', () => {
this.hideNewProcessModal();
});
// 确认模态框
this.confirmOk.addEventListener('click', () => {
this.hideConfirmModal();
if (this.confirmCallback) {
this.confirmCallback();
}
});
this.confirmCancel.addEventListener('click', () => {
this.hideConfirmModal();
});
// 代码模态框
this.copyCodeBtn.addEventListener('click', () => {
this.copyGeneratedCode();
});
this.closeCodeModal.addEventListener('click', () => {
this.hideCodeModal();
});
// 点击模态框背景关闭
window.addEventListener('click', (e) => {
if (e.target === this.newProcessModal) {
this.hideNewProcessModal();
}
if (e.target === this.confirmModal) {
this.hideConfirmModal();
}
if (e.target === this.codeModal) {
this.hideCodeModal();
}
});
// 键盘事件
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
this.hideNewProcessModal();
this.hideConfirmModal();
this.hideCodeModal();
}
});
}
// 加载初始数据
loadInitialData() {
// 尝试从localStorage加载
this.fileManager.loadFromLocalStorage();
// 更新文件下拉列表
this.updateFileDropdown();
// 加载默认文件
this.loadProcessFile(this.fileManager.getCurrentFileName());
}
// 更新文件下拉列表
updateFileDropdown() {
const fileNames = this.fileManager.getFileNames();
const currentFile = this.fileManager.getCurrentFileName();
this.fileSelect.innerHTML = '';
fileNames.forEach(fileName => {
const option = document.createElement('option');
option.value = fileName;
option.textContent = fileName;
if (fileName === currentFile) {
option.selected = true;
}
this.fileSelect.appendChild(option);
});
// 更新保存按钮文本
this.saveBtn.textContent = `保存到 ${currentFile}`;
}
// 加载流程文件
loadProcessFile(fileName) {
if (!fileName) return;
if (this.fileManager.setCurrentFile(fileName)) {
this.currentSteps = this.fileManager.loadProcessFile(fileName);
this.stepFoldouts = new Array(this.currentSteps.length).fill(true);
this.actionFoldouts = this.currentSteps.map(step =>
new Array(step.Actions.length).fill(true)
);
this.updateFileDropdown();
this.renderSteps();
}
}
// 渲染步骤列表
renderSteps() {
this.stepsContainer.innerHTML = '';
if (this.currentSteps.length === 0) {
this.renderEmptyState();
return;
}
this.currentSteps.forEach((step, stepIndex) => {
this.renderStep(step, stepIndex);
});
}
// 渲染空状态
renderEmptyState() {
const emptyDiv = document.createElement('div');
emptyDiv.className = 'empty-state';
emptyDiv.innerHTML = `
<p>暂无步骤</p>
<div class="add-button-container">
<button class="btn-add-large" onclick="editor.addNewStep()">添加第一个步骤</button>
</div>
`;
this.stepsContainer.appendChild(emptyDiv);
}
// HTML转义函数
escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// 渲染单个步骤
renderStep(step, stepIndex) {
const stepDiv = document.createElement('div');
stepDiv.className = 'step-item';
// 确保折叠状态数组长度正确
this.ensureFoldoutLists(stepIndex);
const isExpanded = this.stepFoldouts[stepIndex];
stepDiv.innerHTML = `
<div class="separator-line gray"></div>
<div class="step-header">
<div class="step-controls">
${stepIndex === this.currentSteps.length - 1 ?
`<button class="btn btn-small btn-add" onclick="editor.addNewStep()" title="添加步骤">+</button>` :
''
}
<button class="btn btn-small btn-remove" onclick="editor.removeStep(${stepIndex})" title="删除步骤">-</button>
</div>
<button class="foldout ${isExpanded ? 'expanded' : ''}" onclick="editor.toggleStepFoldout(${stepIndex})">
${this.escapeHtml(step.StepDescription)} 编号: ${stepIndex}
</button>
</div>
<div class="step-content ${isExpanded ? 'expanded' : ''}">
<div class="form-group">
<label>描述</label>
<input type="text" class="form-control" value="${this.escapeHtml(step.StepDescription)}"
onchange="editor.updateStepDescription(${stepIndex}, this.value)">
</div>
<div class="actions-container" id="actionsContainer_${stepIndex}">
<!-- 动作将在这里渲染 -->
</div>
</div>
<div class="separator-line green"></div>
`;
this.stepsContainer.appendChild(stepDiv);
// 渲染动作列表
if (isExpanded) {
this.renderActions(stepIndex);
}
}
// 确保折叠状态数组长度正确
ensureFoldoutLists(stepIndex) {
while (this.stepFoldouts.length <= stepIndex) {
this.stepFoldouts.push(true);
}
while (this.actionFoldouts.length <= stepIndex) {
this.actionFoldouts.push([]);
}
}
// 确保动作折叠状态数组长度正确
ensureActionFoldoutList(stepIndex, actionIndex) {
this.ensureFoldoutLists(stepIndex);
while (this.actionFoldouts[stepIndex].length <= actionIndex) {
this.actionFoldouts[stepIndex].push(true);
}
}
// 切换步骤折叠状态
toggleStepFoldout(stepIndex) {
this.stepFoldouts[stepIndex] = !this.stepFoldouts[stepIndex];
this.renderSteps();
}
// 切换动作折叠状态
toggleActionFoldout(stepIndex, actionIndex) {
this.actionFoldouts[stepIndex][actionIndex] = !this.actionFoldouts[stepIndex][actionIndex];
this.renderActions(stepIndex);
}
// 更新步骤描述
updateStepDescription(stepIndex, value) {
if (this.currentSteps[stepIndex]) {
this.currentSteps[stepIndex].StepDescription = value;
}
}
// 添加新步骤
addNewStep() {
const newStep = DataUtils.createNewStep();
this.currentSteps.push(newStep);
this.stepFoldouts.push(true);
this.actionFoldouts.push([]);
this.renderSteps();
}
// 删除步骤
removeStep(stepIndex) {
this.showConfirmModal(
"确认删除",
`确定要删除步骤 "${this.currentSteps[stepIndex].StepDescription}" 吗?`,
() => {
this.currentSteps.splice(stepIndex, 1);
this.stepFoldouts.splice(stepIndex, 1);
this.actionFoldouts.splice(stepIndex, 1);
this.renderSteps();
}
);
}
// 渲染动作列表
renderActions(stepIndex) {
const actionsContainer = document.getElementById(`actionsContainer_${stepIndex}`);
if (!actionsContainer) return;
const step = this.currentSteps[stepIndex];
actionsContainer.innerHTML = '';
if (step.Actions.length === 0) {
const emptyDiv = document.createElement('div');
emptyDiv.className = 'empty-state';
emptyDiv.innerHTML = `
<p>暂无动作</p>
<div class="add-button-container">
<button class="btn-add-large" onclick="editor.addNewAction(${stepIndex})">添加第一个动作</button>
</div>
`;
actionsContainer.appendChild(emptyDiv);
return;
}
step.Actions.forEach((action, actionIndex) => {
this.renderAction(stepIndex, action, actionIndex, actionsContainer);
});
}
// 渲染单个动作
renderAction(stepIndex, action, actionIndex, container) {
this.ensureActionFoldoutList(stepIndex, actionIndex);
const isExpanded = this.actionFoldouts[stepIndex][actionIndex];
const actionDiv = document.createElement('div');
actionDiv.className = 'action-item';
actionDiv.innerHTML = `
<div class="action-header">
<div class="step-controls">
${actionIndex === this.currentSteps[stepIndex].Actions.length - 1 ?
`<button class="btn btn-small btn-add" onclick="editor.addNewAction(${stepIndex})" title="添加动作">+</button>` :
''
}
<button class="btn btn-small btn-remove" onclick="editor.removeAction(${stepIndex}, ${actionIndex})" title="删除动作">-</button>
${actionIndex > 0 ?
`<button class="btn btn-small btn-move" onclick="editor.moveActionUp(${stepIndex}, ${actionIndex})" title="上移">↑</button>` :
''
}
${actionIndex < this.currentSteps[stepIndex].Actions.length - 1 ?
`<button class="btn btn-small btn-move" onclick="editor.moveActionDown(${stepIndex}, ${actionIndex})" title="下移">↓</button>` :
''
}
</div>
<button class="foldout ${isExpanded ? 'expanded' : ''}" onclick="editor.toggleActionFoldout(${stepIndex}, ${actionIndex})">
${this.escapeHtml(action.Title)} 编号: ${stepIndex}-${actionIndex}
</button>
</div>
<div class="action-content ${isExpanded ? 'expanded' : ''}" id="actionContent_${stepIndex}_${actionIndex}">
<!-- 动作详情将在这里渲染 -->
</div>
`;
container.appendChild(actionDiv);
// 渲染动作详情
if (isExpanded) {
this.renderActionDetails(stepIndex, actionIndex);
}
}
// 渲染动作详情
renderActionDetails(stepIndex, actionIndex) {
const actionContent = document.getElementById(`actionContent_${stepIndex}_${actionIndex}`);
if (!actionContent) return;
const action = this.currentSteps[stepIndex].Actions[actionIndex];
actionContent.innerHTML = `
<div class="form-group">
<label>标题</label>
<input type="text" class="form-control" value="${this.escapeHtml(action.Title)}"
onchange="editor.updateActionProperty(${stepIndex}, ${actionIndex}, 'Title', this.value)">
</div>
<div class="form-group">
<label>描述</label>
<input type="text" class="form-control" value="${this.escapeHtml(action.Description)}"
onchange="editor.updateActionProperty(${stepIndex}, ${actionIndex}, 'Description', this.value)">
</div>
<div class="form-group">
<label>动作类型</label>
<select class="form-control" onchange="editor.updateActionType(${stepIndex}, ${actionIndex}, parseInt(this.value))">
<option value="${ProcessActionType.DEFAULT}" ${action.ActionType === ProcessActionType.DEFAULT ? 'selected' : ''}>默认</option>
<option value="${ProcessActionType.JUDGMENT}" ${action.ActionType === ProcessActionType.JUDGMENT ? 'selected' : ''}>判断题</option>
<option value="${ProcessActionType.MULTIPLE_CHOICE}" ${action.ActionType === ProcessActionType.MULTIPLE_CHOICE ? 'selected' : ''}>多选题</option>
</select>
</div>
<div id="actionTypeContent_${stepIndex}_${actionIndex}">
<!-- 根据动作类型显示不同内容 -->
</div>
`;
this.renderActionTypeContent(stepIndex, actionIndex);
}
// 根据动作类型渲染不同内容
renderActionTypeContent(stepIndex, actionIndex) {
const actionTypeContent = document.getElementById(`actionTypeContent_${stepIndex}_${actionIndex}`);
if (!actionTypeContent) return;
const action = this.currentSteps[stepIndex].Actions[actionIndex];
let content = '';
// 通用属性
content += `
<div class="form-group">
<label>分数</label>
<input type="number" class="form-control" value="${action.Score}" step="0.1"
onchange="editor.updateActionProperty(${stepIndex}, ${actionIndex}, 'Score', parseFloat(this.value) || 0)">
</div>
<div class="form-group">
<label>
<input type="checkbox" class="checkbox" ${action.RequireCorrectCompletion ? 'checked' : ''}
onchange="editor.updateActionProperty(${stepIndex}, ${actionIndex}, 'RequireCorrectCompletion', this.checked)">
需要正确完成
</label>
</div>
`;
if (action.ActionType === ProcessActionType.DEFAULT) {
content += `
<div class="form-group">
<label>
<input type="checkbox" class="checkbox" ${action.IsSequential ? 'checked' : ''}
onchange="editor.updateActionProperty(${stepIndex}, ${actionIndex}, 'IsSequential', this.checked)">
按顺序点击
</label>
</div>
<div id="targetObjectsContainer_${stepIndex}_${actionIndex}">
<!-- 目标对象列表 -->
</div>
`;
} else if (action.ActionType === ProcessActionType.JUDGMENT) {
content += `
<div id="judgmentQuestionsContainer_${stepIndex}_${actionIndex}">
<!-- 判断题列表 -->
</div>
`;
} else if (action.ActionType === ProcessActionType.MULTIPLE_CHOICE) {
content += `
<div id="multipleChoiceContainer_${stepIndex}_${actionIndex}">
<!-- 多选题列表 -->
</div>
`;
}
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);
}
}
// 更新动作属性
updateActionProperty(stepIndex, actionIndex, property, value) {
if (this.currentSteps[stepIndex] && this.currentSteps[stepIndex].Actions[actionIndex]) {
this.currentSteps[stepIndex].Actions[actionIndex][property] = value;
}
}
// 更新动作类型
updateActionType(stepIndex, actionIndex, newType) {
const 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);
}
}
// 添加新动作
addNewAction(stepIndex) {
const newAction = DataUtils.createNewAction();
this.currentSteps[stepIndex].Actions.push(newAction);
this.actionFoldouts[stepIndex].push(true);
this.renderActions(stepIndex);
}
// 删除动作
removeAction(stepIndex, actionIndex) {
const action = this.currentSteps[stepIndex].Actions[actionIndex];
this.showConfirmModal(
"确认删除",
`确定要删除动作 "${action.Title}" 吗?`,
() => {
this.currentSteps[stepIndex].Actions.splice(actionIndex, 1);
this.actionFoldouts[stepIndex].splice(actionIndex, 1);
this.renderActions(stepIndex);
}
);
}
// 上移动作
moveActionUp(stepIndex, actionIndex) {
if (actionIndex > 0) {
const actions = this.currentSteps[stepIndex].Actions;
const foldouts = this.actionFoldouts[stepIndex];
// 交换动作
[actions[actionIndex], actions[actionIndex - 1]] = [actions[actionIndex - 1], actions[actionIndex]];
// 交换折叠状态
[foldouts[actionIndex], foldouts[actionIndex - 1]] = [foldouts[actionIndex - 1], foldouts[actionIndex]];
this.renderActions(stepIndex);
}
}
// 下移动作
moveActionDown(stepIndex, actionIndex) {
const actions = this.currentSteps[stepIndex].Actions;
if (actionIndex < actions.length - 1) {
const foldouts = this.actionFoldouts[stepIndex];
// 交换动作
[actions[actionIndex], actions[actionIndex + 1]] = [actions[actionIndex + 1], actions[actionIndex]];
// 交换折叠状态
[foldouts[actionIndex], foldouts[actionIndex + 1]] = [foldouts[actionIndex + 1], foldouts[actionIndex]];
this.renderActions(stepIndex);
}
}
// 渲染目标对象列表
renderTargetObjects(stepIndex, actionIndex) {
const container = document.getElementById(`targetObjectsContainer_${stepIndex}_${actionIndex}`);
if (!container) return;
const action = this.currentSteps[stepIndex].Actions[actionIndex];
let content = '<h4>目标对象列表</h4>';
if (action.TargetObjects.length === 0) {
content += `
<div class="empty-state">
<p>暂无目标对象</p>
<div class="add-button-container">
<button class="btn-add-large" onclick="editor.addTargetObject(${stepIndex}, ${actionIndex})">添加目标对象</button>
</div>
</div>
`;
} else {
action.TargetObjects.forEach((target, targetIndex) => {
content += `
<div class="target-object-item">
<div class="target-object-header">
<strong>目标对象 ${targetIndex + 1}</strong>
${targetIndex > 0 ?
`<button class="btn btn-small btn-move" onclick="editor.moveTargetObjectUp(${stepIndex}, ${actionIndex}, ${targetIndex})" title="上移">↑</button>` :
''
}
${targetIndex < action.TargetObjects.length - 1 ?
`<button class="btn btn-small btn-move" onclick="editor.moveTargetObjectDown(${stepIndex}, ${actionIndex}, ${targetIndex})" title="下移">↓</button>` :
''
}
<button class="btn btn-small btn-remove" onclick="editor.removeTargetObject(${stepIndex}, ${actionIndex}, ${targetIndex})" title="删除">-</button>
</div>
<div class="form-group">
<label>对象名称</label>
<input type="text" class="form-control" value="${this.escapeHtml(target.ObjectName)}"
onchange="editor.updateTargetObjectProperty(${stepIndex}, ${actionIndex}, ${targetIndex}, 'ObjectName', this.value)">
</div>
<div class="form-group">
<label>对象类型</label>
<select class="form-control" onchange="editor.updateTargetObjectProperty(${stepIndex}, ${actionIndex}, ${targetIndex}, 'Type', parseInt(this.value))">
<option value="${ProcessTargetType.MODEL}" ${target.Type === ProcessTargetType.MODEL ? 'selected' : ''}>Model</option>
<option value="${ProcessTargetType.EVENT}" ${target.Type === ProcessTargetType.EVENT ? 'selected' : ''}>Event</option>
</select>
</div>
<div class="form-group">
<label>分数</label>
<input type="number" class="form-control" value="${target.Score}" step="0.1"
onchange="editor.updateTargetObjectProperty(${stepIndex}, ${actionIndex}, ${targetIndex}, 'Score', parseFloat(this.value) || 0)">
</div>
</div>
`;
});
content += `
<div class="add-button-container">
<button class="btn-add-large" onclick="editor.addTargetObject(${stepIndex}, ${actionIndex})">添加目标对象</button>
</div>
`;
}
container.innerHTML = content;
}
// 添加目标对象
addTargetObject(stepIndex, actionIndex) {
const newTarget = DataUtils.createNewTargetObject();
this.currentSteps[stepIndex].Actions[actionIndex].TargetObjects.push(newTarget);
this.renderTargetObjects(stepIndex, actionIndex);
}
// 删除目标对象
removeTargetObject(stepIndex, actionIndex, targetIndex) {
const target = this.currentSteps[stepIndex].Actions[actionIndex].TargetObjects[targetIndex];
this.showConfirmModal(
"确认删除",
`确定要删除目标对象 "${target.ObjectName}" 吗?`,
() => {
this.currentSteps[stepIndex].Actions[actionIndex].TargetObjects.splice(targetIndex, 1);
this.renderTargetObjects(stepIndex, actionIndex);
}
);
}
// 上移目标对象
moveTargetObjectUp(stepIndex, actionIndex, targetIndex) {
if (targetIndex > 0) {
const targets = this.currentSteps[stepIndex].Actions[actionIndex].TargetObjects;
[targets[targetIndex], targets[targetIndex - 1]] = [targets[targetIndex - 1], targets[targetIndex]];
this.renderTargetObjects(stepIndex, actionIndex);
}
}
// 下移目标对象
moveTargetObjectDown(stepIndex, actionIndex, targetIndex) {
const targets = this.currentSteps[stepIndex].Actions[actionIndex].TargetObjects;
if (targetIndex < targets.length - 1) {
[targets[targetIndex], targets[targetIndex + 1]] = [targets[targetIndex + 1], targets[targetIndex]];
this.renderTargetObjects(stepIndex, actionIndex);
}
}
// 更新目标对象属性
updateTargetObjectProperty(stepIndex, actionIndex, targetIndex, property, value) {
const target = this.currentSteps[stepIndex].Actions[actionIndex].TargetObjects[targetIndex];
if (target) {
target[property] = value;
}
}
// 渲染判断题列表
renderJudgmentQuestions(stepIndex, actionIndex) {
const container = document.getElementById(`judgmentQuestionsContainer_${stepIndex}_${actionIndex}`);
if (!container) return;
const action = this.currentSteps[stepIndex].Actions[actionIndex];
let content = '<h4>判断题配置</h4>';
if (action.JudgmentQuestions.length === 0) {
content += `
<div class="empty-state">
<p>暂无判断题</p>
<div class="add-button-container">
<button class="btn-add-large" onclick="editor.addJudgmentQuestion(${stepIndex}, ${actionIndex})">添加判断题</button>
</div>
</div>
`;
} else {
action.JudgmentQuestions.forEach((question, questionIndex) => {
content += `
<div class="question-item">
<div class="question-header">
<strong>题目 ${questionIndex + 1}</strong>
<button class="btn btn-small btn-remove" onclick="editor.removeJudgmentQuestion(${stepIndex}, ${actionIndex}, ${questionIndex})" title="删除">-</button>
</div>
<div class="form-group">
<label>题目内容</label>
<input type="text" class="form-control" value="${this.escapeHtml(question.Question)}"
onchange="editor.updateJudgmentQuestionProperty(${stepIndex}, ${actionIndex}, ${questionIndex}, 'Question', this.value)">
</div>
<div class="form-group">
<label>正确答案</label>
<input type="text" class="form-control" value="${this.escapeHtml(question.CorrectAnswer)}"
onchange="editor.updateJudgmentQuestionProperty(${stepIndex}, ${actionIndex}, ${questionIndex}, 'CorrectAnswer', this.value)">
</div>
<div class="form-group">
<label>分数</label>
<input type="number" class="form-control" value="${question.Score}" step="0.1"
onchange="editor.updateJudgmentQuestionProperty(${stepIndex}, ${actionIndex}, ${questionIndex}, 'Score', parseFloat(this.value) || 0)">
</div>
</div>
`;
});
content += `
<div class="add-button-container">
<button class="btn-add-large" onclick="editor.addJudgmentQuestion(${stepIndex}, ${actionIndex})">添加判断题</button>
</div>
`;
}
container.innerHTML = content;
}
// 添加判断题
addJudgmentQuestion(stepIndex, actionIndex) {
const newQuestion = DataUtils.createNewJudgmentQuestion();
this.currentSteps[stepIndex].Actions[actionIndex].JudgmentQuestions.push(newQuestion);
this.renderJudgmentQuestions(stepIndex, actionIndex);
}
// 删除判断题
removeJudgmentQuestion(stepIndex, actionIndex, questionIndex) {
this.showConfirmModal(
"确认删除",
"确定要删除这个判断题吗?",
() => {
this.currentSteps[stepIndex].Actions[actionIndex].JudgmentQuestions.splice(questionIndex, 1);
this.renderJudgmentQuestions(stepIndex, actionIndex);
}
);
}
// 更新判断题属性
updateJudgmentQuestionProperty(stepIndex, actionIndex, questionIndex, property, value) {
const question = this.currentSteps[stepIndex].Actions[actionIndex].JudgmentQuestions[questionIndex];
if (question) {
question[property] = value;
}
}
// 渲染多选题列表
renderMultipleChoiceQuestions(stepIndex, actionIndex) {
const container = document.getElementById(`multipleChoiceContainer_${stepIndex}_${actionIndex}`);
if (!container) return;
const action = this.currentSteps[stepIndex].Actions[actionIndex];
let content = '<h4>多选题配置</h4>';
if (action.MultipleChoiceQuestions.length === 0) {
content += `
<div class="empty-state">
<p>暂无多选题</p>
<div class="add-button-container">
<button class="btn-add-large" onclick="editor.addMultipleChoiceQuestion(${stepIndex}, ${actionIndex})">添加多选题</button>
</div>
</div>
`;
} else {
action.MultipleChoiceQuestions.forEach((question, questionIndex) => {
content += `
<div class="question-item">
<div class="question-header">
<strong>题目 ${questionIndex + 1}</strong>
<button class="btn btn-small btn-remove" onclick="editor.removeMultipleChoiceQuestion(${stepIndex}, ${actionIndex}, ${questionIndex})" title="删除">-</button>
</div>
<div class="form-group">
<label>题目内容</label>
<input type="text" class="form-control" value="${this.escapeHtml(question.Question)}"
onchange="editor.updateMultipleChoiceQuestionProperty(${stepIndex}, ${actionIndex}, ${questionIndex}, 'Question', this.value)">
</div>
<div class="form-group">
<label>分数</label>
<input type="number" class="form-control" value="${question.Score}" step="0.1"
onchange="editor.updateMultipleChoiceQuestionProperty(${stepIndex}, ${actionIndex}, ${questionIndex}, 'Score', parseFloat(this.value) || 0)">
</div>
<div class="form-group">
<label>选项列表</label>
<div id="optionsContainer_${stepIndex}_${actionIndex}_${questionIndex}">
<!-- 选项将在这里渲染 -->
</div>
</div>
</div>
`;
});
content += `
<div class="add-button-container">
<button class="btn-add-large" onclick="editor.addMultipleChoiceQuestion(${stepIndex}, ${actionIndex})">添加多选题</button>
</div>
`;
}
container.innerHTML = content;
// 渲染每个题目的选项
action.MultipleChoiceQuestions.forEach((question, questionIndex) => {
this.renderMultipleChoiceOptions(stepIndex, actionIndex, questionIndex);
});
}
// 渲染多选题选项
renderMultipleChoiceOptions(stepIndex, actionIndex, questionIndex) {
const container = document.getElementById(`optionsContainer_${stepIndex}_${actionIndex}_${questionIndex}`);
if (!container) return;
const question = this.currentSteps[stepIndex].Actions[actionIndex].MultipleChoiceQuestions[questionIndex];
let content = '';
question.Options.forEach((option, optionIndex) => {
const isCorrect = question.CorrectAnswers.includes(option);
content += `
<div class="option-item">
<input type="text" class="form-control" value="${this.escapeHtml(option)}"
onchange="editor.updateMultipleChoiceOption(${stepIndex}, ${actionIndex}, ${questionIndex}, ${optionIndex}, this.value)"
placeholder="选项 ${optionIndex + 1}">
<label>
<input type="checkbox" class="checkbox" ${isCorrect ? 'checked' : ''}
onchange="editor.toggleCorrectAnswer(${stepIndex}, ${actionIndex}, ${questionIndex}, ${optionIndex}, this.checked)">
是正确答案
</label>
<button class="btn btn-small btn-remove" onclick="editor.removeMultipleChoiceOption(${stepIndex}, ${actionIndex}, ${questionIndex}, ${optionIndex})" title="删除选项">-</button>
</div>
`;
});
content += `
<div class="add-button-container">
<button class="btn btn-small btn-add" onclick="editor.addMultipleChoiceOption(${stepIndex}, ${actionIndex}, ${questionIndex})">添加选项</button>
</div>
`;
container.innerHTML = content;
}
// 添加多选题
addMultipleChoiceQuestion(stepIndex, actionIndex) {
const newQuestion = DataUtils.createNewMultipleChoice();
this.currentSteps[stepIndex].Actions[actionIndex].MultipleChoiceQuestions.push(newQuestion);
this.renderMultipleChoiceQuestions(stepIndex, actionIndex);
}
// 删除多选题
removeMultipleChoiceQuestion(stepIndex, actionIndex, questionIndex) {
this.showConfirmModal(
"确认删除",
"确定要删除这个多选题吗?",
() => {
this.currentSteps[stepIndex].Actions[actionIndex].MultipleChoiceQuestions.splice(questionIndex, 1);
this.renderMultipleChoiceQuestions(stepIndex, actionIndex);
}
);
}
// 更新多选题属性
updateMultipleChoiceQuestionProperty(stepIndex, actionIndex, questionIndex, property, value) {
const question = this.currentSteps[stepIndex].Actions[actionIndex].MultipleChoiceQuestions[questionIndex];
if (question) {
question[property] = value;
}
}
// 添加多选题选项
addMultipleChoiceOption(stepIndex, actionIndex, questionIndex) {
const question = this.currentSteps[stepIndex].Actions[actionIndex].MultipleChoiceQuestions[questionIndex];
question.Options.push("");
this.renderMultipleChoiceOptions(stepIndex, actionIndex, questionIndex);
}
// 删除多选题选项
removeMultipleChoiceOption(stepIndex, actionIndex, questionIndex, optionIndex) {
const question = this.currentSteps[stepIndex].Actions[actionIndex].MultipleChoiceQuestions[questionIndex];
const option = question.Options[optionIndex];
// 从选项列表中删除
question.Options.splice(optionIndex, 1);
// 从正确答案列表中删除(如果存在)
const correctIndex = question.CorrectAnswers.indexOf(option);
if (correctIndex > -1) {
question.CorrectAnswers.splice(correctIndex, 1);
}
this.renderMultipleChoiceOptions(stepIndex, actionIndex, questionIndex);
}
// 更新多选题选项
updateMultipleChoiceOption(stepIndex, actionIndex, questionIndex, optionIndex, newValue) {
const question = this.currentSteps[stepIndex].Actions[actionIndex].MultipleChoiceQuestions[questionIndex];
const oldValue = question.Options[optionIndex];
// 更新选项值
question.Options[optionIndex] = newValue;
// 如果旧值在正确答案中,替换为新值
const correctIndex = question.CorrectAnswers.indexOf(oldValue);
if (correctIndex > -1) {
question.CorrectAnswers[correctIndex] = newValue;
}
}
// 切换正确答案状态
toggleCorrectAnswer(stepIndex, actionIndex, questionIndex, optionIndex, isCorrect) {
const question = this.currentSteps[stepIndex].Actions[actionIndex].MultipleChoiceQuestions[questionIndex];
const option = question.Options[optionIndex];
if (isCorrect) {
// 添加到正确答案列表
if (!question.CorrectAnswers.includes(option)) {
question.CorrectAnswers.push(option);
}
} else {
// 从正确答案列表中删除
const index = question.CorrectAnswers.indexOf(option);
if (index > -1) {
question.CorrectAnswers.splice(index, 1);
}
}
}
// 显示新增流程模态框
showNewProcessModal() {
this.newProcessName.value = "新流程";
this.newProcessModal.style.display = 'block';
this.newProcessName.focus();
this.newProcessName.select();
}
// 隐藏新增流程模态框
hideNewProcessModal() {
this.newProcessModal.style.display = 'none';
}
// 创建新流程
createNewProcess() {
const fileName = this.newProcessName.value.trim();
if (!fileName) {
alert("请输入流程名称");
return;
}
const result = this.fileManager.createNewProcessFile(fileName);
if (result.success) {
this.hideNewProcessModal();
this.updateFileDropdown();
this.loadProcessFile(result.fileName);
this.showAlert("新流程创建成功", `已创建新流程:${result.fileName}`);
} else {
alert(`创建失败:${result.error}`);
}
}
// 显示确认模态框
showConfirmModal(title, message, callback) {
this.confirmTitle.textContent = title;
this.confirmMessage.textContent = message;
this.confirmCallback = callback;
this.confirmModal.style.display = 'block';
}
// 隐藏确认模态框
hideConfirmModal() {
this.confirmModal.style.display = 'none';
this.confirmCallback = null;
}
// 显示代码模态框
showCodeModal(code) {
this.generatedCode.value = code;
this.codeModal.style.display = 'block';
}
// 隐藏代码模态框
hideCodeModal() {
this.codeModal.style.display = 'none';
}
// 复制生成的代码
copyGeneratedCode() {
this.generatedCode.select();
document.execCommand('copy');
this.showAlert("复制成功", "代码已复制到剪贴板");
}
// 显示提示信息
showAlert(title, message) {
// 简单的alert实现可以后续改为更美观的提示框
alert(`${title}\n${message}`);
}
// 保存配置
saveConfiguration() {
const currentFileName = this.fileManager.getCurrentFileName();
this.showConfirmModal(
"确认保存",
`是否要保存当前配置到 ${currentFileName}`,
() => {
const result = this.fileManager.saveProcessFile(currentFileName, this.currentSteps);
if (result.success) {
this.showAlert("保存成功", `配置已保存到 ${currentFileName}`);
} else {
alert(`保存失败:${result.error}`);
}
}
);
}
// 生成ProcessEvents代码
generateProcessEventsCode() {
try {
const currentFileName = this.fileManager.getCurrentFileName();
if (!this.currentSteps || this.currentSteps.length === 0) {
alert("当前流程文件为空,无法生成代码");
return;
}
const code = this.codeGenerator.generateProcessEventsCode(currentFileName, this.currentSteps);
this.showCodeModal(code);
} catch (error) {
alert(`生成代码失败:${error.message}`);
}
}
}
// 全局编辑器实例
let editor;
// 页面加载完成后初始化编辑器
document.addEventListener('DOMContentLoaded', function() {
editor = new SceneStepEditor();
});