281 lines
9.0 KiB
JavaScript
Raw Normal View History

2025-04-28 12:25:20 +08:00
/**
* 文件操作模块
* @type {module}
*/
import { formatXml, markEdited, resetEditState } from './utils.js';
// 获取Scenario目录路径前缀
let scenarioDirPrefix = null;
/**
* 获取Scenario目录路径
* @returns {string} Scenario目录的绝对路径
*/
async function getScenarioDir() {
if (scenarioDirPrefix) return scenarioDirPrefix;
try {
// 从文件列表API获取Scenario目录路径
const response = await fetch('/api/scenario-files');
if (response.ok) {
const files = await response.json();
if (files && files.length > 0) {
// 提取第一个文件的目录作为Scenario目录
const filePath = files[0].path;
scenarioDirPrefix = filePath.substring(0, filePath.lastIndexOf('/'));
return scenarioDirPrefix;
}
}
return ''; // 如果无法获取,返回空字符串
} catch (error) {
console.error('获取Scenario目录失败:', error);
return '';
}
}
/**
* 加载场景文件列表
* @param {HTMLElement} component - 组件实例
* @returns {Promise<boolean>} 是否成功加载
*/
export async function loadScenarioFiles(component) {
// 设置按钮为刷新中状态
const refreshButton = component.shadowRoot.getElementById('refreshButton');
if (refreshButton) {
refreshButton.classList.add('refreshing');
}
let retryCount = 0;
const maxRetries = 3; // 最多重试3次
const tryLoadFiles = async () => {
try {
const response = await fetch('/api/scenario-files');
if (!response.ok) {
throw new Error(`服务器返回错误: ${response.status} ${response.statusText}`);
}
const files = await response.json();
component.scenarioFiles = files;
// 更新文件选择器
updateFileSelector(component);
return true;
} catch (error) {
if (retryCount < maxRetries) {
retryCount++;
// 指数级退避重试
const retryDelay = Math.pow(2, retryCount) * 500;
await new Promise(resolve => setTimeout(resolve, retryDelay));
return tryLoadFiles();
}
alert(`加载文件列表失败: ${error.message}`);
return false;
} finally {
// 无论成功失败,移除刷新中状态
if (refreshButton) {
refreshButton.classList.remove('refreshing');
}
}
};
const success = await tryLoadFiles();
// 检查是否是用户通过刷新按钮手动触发的刷新
const isManualRefresh = document.activeElement === refreshButton;
if (success && isManualRefresh) {
// 只有在用户明确点击刷新按钮时才清空当前文件
component.currentFile = null;
component.xmlContent = '';
component.updateFileContent();
component.isEdited = resetEditState(component.shadowRoot);
// 更新文件选择器,确保没有文件被选中
const fileSelector = component.shadowRoot.getElementById('scenarioFile');
if (fileSelector) {
fileSelector.value = '';
}
}
return success;
}
/**
* 更新文件选择器
* @param {HTMLElement} component - 组件实例
*/
export function updateFileSelector(component) {
const fileSelector = component.shadowRoot.getElementById('scenarioFile');
if (fileSelector) {
// 清除现有选项,保留第一个
while (fileSelector.options.length > 1) {
fileSelector.remove(1);
}
// 添加文件选项
component.scenarioFiles.forEach(file => {
const option = document.createElement('option');
option.value = file.path;
option.textContent = file.name;
fileSelector.appendChild(option);
});
// 验证文件选择器选项
const options = Array.from(fileSelector.options);
options.forEach((opt, index) => {
if (index === 0) return; // 跳过第一个空选项
});
} else {
console.log('未找到文件选择器元素');
}
}
/**
* 加载文件内容
* @param {HTMLElement} component - 组件实例
* @param {string} filePath - 文件路径
* @returns {Promise<void>}
*/
export async function loadFileContent(component, filePath) {
try {
const response = await fetch(`/api/file-content?path=${encodeURIComponent(filePath)}`);
if (!response.ok) {
throw new Error(`加载文件内容失败: ${response.status} ${response.statusText}`);
}
component.xmlContent = await response.text();
component.currentFile = filePath;
// 检查内容是否为空
if (!component.xmlContent.trim()) {
// 如果是.sce文件且内容为空则报文件内容为空的错误
if (filePath.toLowerCase().endsWith('.sce')) {
throw new Error('文件内容为空');
}
}
component.updateFileContent();
// 重置编辑状态,因为刚刚加载了新文件
component.isEdited = resetEditState(component.shadowRoot);
} catch (error) {
console.error('加载文件内容失败:', error);
component.xmlContent = '<!-- 加载文件内容失败 -->';
component.updateFileContent();
}
}
/**
* 保存文件内容
* @param {HTMLElement} component - 组件实例
* @param {string} filePath - 文件路径
* @param {string} content - 文件内容
* @returns {Promise<boolean>} 是否成功保存
*/
export async function saveFileContent(component, filePath, content) {
try {
// 更新XML内容
if (component.autoSaveToXml) {
component.updateXmlFromVisualEditor();
}
// 格式化XML内容
const formattedContent = formatXml(component.xmlContent);
// 确保使用绝对路径
let absolutePath = filePath;
if (!filePath.startsWith('/')) {
const scenarioDir = await getScenarioDir();
// 如果filePath是相对路径但带有./,则移除它
const cleanPath = filePath.startsWith('./') ? filePath.substring(2) : filePath;
absolutePath = `${scenarioDir}/${cleanPath}`;
}
const response = await fetch('/api/save-file', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
path: absolutePath,
content: formattedContent
})
});
if (!response.ok) {
throw new Error(`保存文件失败: ${response.status} ${response.statusText}`);
}
// 重置编辑状态
component.isEdited = resetEditState(component.shadowRoot);
// 更新当前文件
component.currentFile = absolutePath;
// 刷新文件列表
await loadScenarioFiles(component);
// 选中当前文件
const fileSelector = component.shadowRoot.getElementById('scenarioFile');
if (fileSelector && component.currentFile) {
fileSelector.value = component.currentFile;
}
alert('文件保存成功');
return true;
} catch (error) {
console.error('保存文件失败:', error);
alert(`保存文件失败: ${error.message}`);
return false;
}
}
/**
* 创建新配置
* @param {HTMLElement} component - 组件实例
* @param {string} fileName - 文件名
* @returns {Promise<boolean>} 是否成功创建
*/
export async function createNewConfig(component, fileName) {
try {
// 使用服务器API创建新文件
const response = await fetch('/api/create-config-file', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
fileName: fileName
})
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(`创建文件失败: ${errorData.error || response.statusText}`);
}
const data = await response.json();
// 更新文件列表
await loadScenarioFiles(component);
// 加载新创建的文件内容
await loadFileContent(component, data.path);
// 选中新创建的文件
const fileSelector = component.shadowRoot.getElementById('scenarioFile');
if (fileSelector) {
fileSelector.value = data.path;
}
return true;
} catch (error) {
console.error('创建新配置文件失败:', error);
alert(`创建新配置文件失败: ${error.message}`);
return false;
}
}