Tz2/scene-step-editor-web/file-manager.js

468 lines
17 KiB
JavaScript
Raw Permalink 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.

// 文件管理器 - 处理流程文件的加载、保存和管理
function FileManager() {
this.currentFileName = "新流程.json";
this.currentDirectory = "默认目录"; // 当前工作目录
this.processFiles = new Map(); // 存储所有流程文件数据key为完整路径
this.directoryHandle = null; // File System Access API 目录句柄
this.supportsFileSystemAccess = false; // 是否支持 File System Access API
this.initializeDefaultFiles();
this.checkFileSystemAccessSupport();
}
// 初始化默认文件
FileManager.prototype.initializeDefaultFiles = function() {
// 创建一个默认的空流程文件
this.processFiles.set("新流程.json", []);
// 可以添加一些示例文件
this.addSampleFile();
};
// 添加示例文件
FileManager.prototype.addSampleFile = function() {
var sampleStep = DataUtils.createNewStep();
sampleStep.StepDescription = "示例步骤";
var sampleAction = DataUtils.createNewAction();
sampleAction.Title = "示例动作";
sampleAction.Description = "这是一个示例动作";
var sampleTarget = DataUtils.createNewTargetObject();
sampleTarget.ObjectName = "示例对象";
sampleTarget.Type = ProcessTargetType.EVENT;
sampleTarget.Score = 10;
sampleAction.TargetObjects.push(sampleTarget);
sampleStep.Actions.push(sampleAction);
this.processFiles.set("示例流程.json", [sampleStep]);
};
// 获取所有文件名
FileManager.prototype.getFileNames = function() {
return Array.from(this.processFiles.keys());
};
// 获取当前文件名
FileManager.prototype.getCurrentFileName = function() {
return this.currentFileName;
};
// 设置当前文件
FileManager.prototype.setCurrentFile = function(fileName) {
if (this.processFiles.has(fileName)) {
this.currentFileName = fileName;
return true;
}
return false;
};
// 加载流程文件
FileManager.prototype.loadProcessFile = function(fileName) {
if (this.processFiles.has(fileName)) {
var data = this.processFiles.get(fileName);
return DataUtils.deepClone(data);
}
return [];
};
// 保存流程文件
FileManager.prototype.saveProcessFile = function(fileName, steps) {
try {
// 验证数据
for (var i = 0; i < steps.length; i++) {
var validation = DataUtils.validateStep(steps[i]);
if (!validation.valid) {
throw new Error("步骤 " + (i + 1) + ": " + validation.message);
}
}
// 清理数据
var cleanedSteps = [];
for (var i = 0; i < steps.length; i++) {
var cleanedStep = DataUtils.deepClone(steps[i]);
var cleanedActions = [];
for (var j = 0; j < cleanedStep.Actions.length; j++) {
cleanedActions.push(DataUtils.cleanActionData(cleanedStep.Actions[j]));
}
cleanedStep.Actions = cleanedActions;
cleanedSteps.push(cleanedStep);
}
// 保存到内存
this.processFiles.set(fileName, cleanedSteps);
this.currentFileName = fileName;
// 如果支持本地存储也保存到localStorage
if (typeof Storage !== "undefined") {
var allFiles = {};
var self = this;
this.processFiles.forEach(function(value, key) {
allFiles[key] = value;
});
localStorage.setItem('sceneStepEditor_files', JSON.stringify(allFiles));
}
return { success: true };
} catch (error) {
return { success: false, error: error.message };
}
};
// 创建新流程文件
FileManager.prototype.createNewProcessFile = function(fileName) {
if (!fileName || fileName.trim() === "") {
return { success: false, error: "文件名不能为空" };
}
// 确保文件名以.json结尾
if (!fileName.endsWith('.json')) {
fileName += '.json';
}
// 检查文件是否已存在
if (this.processFiles.has(fileName)) {
return { success: false, error: "文件已存在" };
}
// 创建新文件
this.processFiles.set(fileName, []);
this.currentFileName = fileName;
return { success: true, fileName: fileName };
};
// 获取当前目录
FileManager.prototype.getCurrentDirectory = function() {
return this.currentDirectory;
};
// 设置当前目录
FileManager.prototype.setCurrentDirectory = function(directoryName) {
this.currentDirectory = directoryName || "默认目录";
};
// 从目录中加载文件
FileManager.prototype.loadFilesFromDirectory = function(files) {
var self = this;
var loadedCount = 0;
var totalFiles = 0;
// 计算json文件总数
for (var i = 0; i < files.length; i++) {
if (files[i].name.toLowerCase().endsWith('.json')) {
totalFiles++;
}
}
if (totalFiles === 0) {
return Promise.resolve({ success: false, error: "所选目录中没有找到JSON文件" });
}
return new Promise(function(resolve) {
// 清空当前文件(保留默认文件)
var keysToDelete = [];
self.processFiles.forEach(function(value, key) {
if (key !== "新流程.json" && key !== "示例流程.json") {
keysToDelete.push(key);
}
});
keysToDelete.forEach(function(key) {
self.processFiles.delete(key);
});
// 设置目录名(使用第一个文件的父目录名)
if (files.length > 0 && files[0].webkitRelativePath) {
var pathParts = files[0].webkitRelativePath.split('/');
if (pathParts.length > 1) {
self.currentDirectory = pathParts[0];
}
}
// 逐个读取JSON文件
for (var i = 0; i < files.length; i++) {
var file = files[i];
if (file.name.toLowerCase().endsWith('.json')) {
// 使用闭包捕获当前文件对象,避免异步回调时变量引用错误
(function(currentFile) {
self.readFileContent(currentFile, function(fileName, content, error) {
loadedCount++;
if (!error) {
try {
var data = JSON.parse(content);
// 验证数据格式
if (Array.isArray(data)) {
// 使用相对路径作为key
var relativePath = fileName;
if (currentFile.webkitRelativePath) {
// 移除目录前缀,只保留文件名
var parts = currentFile.webkitRelativePath.split('/');
relativePath = parts[parts.length - 1];
}
self.processFiles.set(relativePath, data);
console.log("成功加载文件: " + relativePath);
}
} catch (parseError) {
console.warn("无法解析JSON文件: " + fileName, parseError);
}
} else {
console.warn("读取文件失败: " + fileName, error);
}
// 所有文件处理完成
if (loadedCount === totalFiles) {
console.log("目录加载完成,总计加载文件: " + (self.processFiles.size - 2));
resolve({
success: true,
loadedFiles: self.processFiles.size - 2, // 减去默认文件数量
directory: self.currentDirectory
});
}
});
})(file);
}
}
});
};
// 读取文件内容
FileManager.prototype.readFileContent = function(file, callback) {
var reader = new FileReader();
reader.onload = function(e) {
callback(file.name, e.target.result, null);
};
reader.onerror = function(e) {
callback(file.name, null, e);
};
reader.readAsText(file, 'utf-8');
};
// 获取当前目录下的文件(用于下拉列表显示)
FileManager.prototype.getFilesInCurrentDirectory = function() {
var files = [];
var self = this;
this.processFiles.forEach(function(value, key) {
// 如果是默认文件或者不包含路径分隔符,直接添加
if (key === "新流程.json" || key === "示例流程.json" || key.indexOf('/') === -1) {
files.push(key);
}
});
return files;
};
// 创建新流程文件(保存到当前目录)
FileManager.prototype.createNewProcessFileInCurrentDir = function(fileName) {
if (!fileName || fileName.trim() === "") {
return { success: false, error: "文件名不能为空" };
}
// 确保文件名以.json结尾
if (!fileName.endsWith('.json')) {
fileName += '.json';
}
// 构建完整路径(当前目录下的文件)
var fullPath = fileName; // 简化路径,直接使用文件名
// 检查文件是否已存在
if (this.processFiles.has(fullPath)) {
return { success: false, error: "文件已存在" };
}
// 创建新文件
this.processFiles.set(fullPath, []);
this.currentFileName = fullPath;
return { success: true, fileName: fullPath, directory: this.currentDirectory };
};
// 检查 File System Access API 支持
FileManager.prototype.checkFileSystemAccessSupport = function() {
this.supportsFileSystemAccess = 'showDirectoryPicker' in window && 'showSaveFilePicker' in window;
console.log("File System Access API 支持状态:", this.supportsFileSystemAccess);
};
// 使用 File System Access API 选择目录
FileManager.prototype.selectDirectoryWithAPI = function() {
var self = this;
if (!this.supportsFileSystemAccess) {
return Promise.reject(new Error("浏览器不支持 File System Access API"));
}
return window.showDirectoryPicker().then(function(handle) {
self.directoryHandle = handle;
self.currentDirectory = handle.name;
console.log("已选择目录:", handle.name);
// 存储目录句柄到 IndexedDB 以便下次使用
self.storeDirectoryHandle(handle);
return self.loadFilesFromDirectoryHandle(handle);
});
};
// 存储目录句柄到 IndexedDB
FileManager.prototype.storeDirectoryHandle = function(handle) {
if (typeof indexedDB !== 'undefined') {
var request = indexedDB.open('SceneStepEditor', 1);
request.onupgradeneeded = function(e) {
var db = e.target.result;
if (!db.objectStoreNames.contains('directoryHandles')) {
db.createObjectStore('directoryHandles');
}
};
request.onsuccess = function(e) {
var db = e.target.result;
var transaction = db.transaction(['directoryHandles'], 'readwrite');
var store = transaction.objectStore('directoryHandles');
store.put(handle, 'currentDirectory');
console.log("目录句柄已保存到 IndexedDB");
};
}
};
// 从 IndexedDB 恢复目录句柄
FileManager.prototype.restoreDirectoryHandle = function() {
var self = this;
if (typeof indexedDB === 'undefined' || !this.supportsFileSystemAccess) {
return Promise.resolve(null);
}
return new Promise(function(resolve) {
var request = indexedDB.open('SceneStepEditor', 1);
request.onsuccess = function(e) {
var db = e.target.result;
if (!db.objectStoreNames.contains('directoryHandles')) {
resolve(null);
return;
}
var transaction = db.transaction(['directoryHandles'], 'readonly');
var store = transaction.objectStore('directoryHandles');
var getRequest = store.get('currentDirectory');
getRequest.onsuccess = function() {
var handle = getRequest.result;
if (handle) {
// 验证句柄是否仍然有效
handle.queryPermission().then(function(permission) {
if (permission === 'granted') {
self.directoryHandle = handle;
self.currentDirectory = handle.name;
console.log("已恢复目录句柄:", handle.name);
resolve(handle);
} else {
resolve(null);
}
}).catch(function() {
resolve(null);
});
} else {
resolve(null);
}
};
getRequest.onerror = function() {
resolve(null);
};
};
request.onerror = function() {
resolve(null);
};
});
};
// 从目录句柄加载文件
FileManager.prototype.loadFilesFromDirectoryHandle = function(directoryHandle) {
var self = this;
return new Promise(async function(resolve) {
try {
var loadedFiles = 0;
var filePromises = [];
// 清空当前文件(保留默认文件)
var keysToDelete = [];
self.processFiles.forEach(function(value, key) {
if (key !== "新流程.json" && key !== "示例流程.json") {
keysToDelete.push(key);
}
});
keysToDelete.forEach(function(key) {
self.processFiles.delete(key);
});
// 使用 for await...of 遍历目录中的文件
for await (const entry of directoryHandle.values()) {
if (entry.kind === 'file' && entry.name.toLowerCase().endsWith('.json')) {
var promise = entry.getFile().then(function(file) {
return file.text().then(function(content) {
try {
var data = JSON.parse(content);
if (Array.isArray(data)) {
self.processFiles.set(entry.name, data);
loadedFiles++;
console.log("成功加载文件:", entry.name);
}
} catch (parseError) {
console.warn("无法解析JSON文件:", entry.name, parseError);
}
});
}).catch(function(error) {
console.warn("读取文件失败:", entry.name, error);
});
filePromises.push(promise);
}
}
// 等待所有文件读取完成
await Promise.all(filePromises);
resolve({
success: true,
loadedFiles: loadedFiles,
directory: directoryHandle.name
});
} catch (error) {
console.error("遍历目录失败:", error);
resolve({ success: false, error: error.message });
}
});
};
// 直接写入文件到选择的目录
FileManager.prototype.writeFileToDirectory = function(fileName, data) {
var self = this;
if (!this.supportsFileSystemAccess || !this.directoryHandle) {
return Promise.reject(new Error("未选择目录或浏览器不支持直接写入"));
}
// 确保文件名以.json结尾
if (!fileName.endsWith('.json')) {
fileName += '.json';
}
return this.directoryHandle.getFileHandle(fileName, { create: true }).then(function(fileHandle) {
return fileHandle.createWritable().then(function(writable) {
var jsonString = JSON.stringify(data, null, 2);
return writable.write(jsonString).then(function() {
return writable.close().then(function() {
console.log("文件已写入目录:", fileName);
// 更新内存中的数据
self.processFiles.set(fileName, data);
self.currentFileName = fileName;
return { success: true, fileName: fileName, directory: self.currentDirectory };
});
});
});
});
};