1050 lines
42 KiB
JavaScript
1050 lines
42 KiB
JavaScript
// 主脚本文件 - 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();
|
||
});
|