Appearance
预设管理系统
概述
预设管理系统(Preset Management System)是 Eagle2Ae AE 扩展 v2.4.0 引入的核心功能模块,为每个面板实例提供独立的预设文件管理。该系统支持预设文件的创建、保存、加载、导出、导入和备份恢复等功能,确保多面板实例间配置的独立性和一致性。
核心特性
面板特定预设文件
- 每个面板实例拥有独立的预设文件(Eagle2Ae1.Presets, Eagle2Ae2.Presets, Eagle2Ae3.Presets)
- 预设文件包含导入设置、用户偏好、UI设置等完整配置
- 支持预设文件的版本管理和兼容性处理
智能文件管理
- 自动创建预设目录和文件
- 支持预设文件的导出和导入
- 提供预设文件的备份和恢复功能
实时同步机制
- 配置变更时自动保存到预设文件
- 支持预设文件的实时加载和应用
- 提供预设文件变更监听和通知
版本兼容性
- 支持预设文件格式的向前和向后兼容
- 提供预设文件迁移机制
- 自动处理旧版本预设文件的升级
技术实现
核心类结构
javascript
/**
* 预设管理系统
* 负责管理每个面板实例的预设文件,支持导出导入和备份恢复
*/
class PresetManager {
/**
* 构造函数
* @param {Object} aeExtension - AE扩展实例
*/
constructor(aeExtension) {
this.aeExtension = aeExtension;
this.csInterface = aeExtension.csInterface;
this.settingsManager = aeExtension.settingsManager;
this.logManager = aeExtension.logManager;
// 获取面板ID
this.panelId = this.getPanelId();
// 初始化状态
this.presetsDirectory = null;
this.presetsFileName = this.getPresetFileName();
this.isSaving = false;
this.saveQueue = [];
// 绑定方法上下文
this.loadPresetsFromDisk = this.loadPresetsFromDisk.bind(this);
this.savePresetsSilently = this.savePresetsSilently.bind(this);
this.exportPresetsToFile = this.exportPresetsToFile.bind(this);
this.importPresetsFromFile = this.importPresetsFromFile.bind(this);
this.backupPresets = this.backupPresets.bind(this);
this.restorePresets = this.restorePresets.bind(this);
this.log(`💾 预设管理系统已为面板 ${this.panelId} 初始化`, 'debug');
}
}面板ID识别实现
javascript
/**
* 获取当前面板 ID
* @returns {string} 'panel1', 'panel2', 或 'panel3'
*/
getPanelId() {
try {
if (this.csInterface && typeof this.csInterface.getExtensionID === 'function') {
const extensionId = this.csInterface.getExtensionID();
// 从 Extension ID 中提取面板编号
if (extensionId.includes('panel1')) {
return 'panel1';
} else if (extensionId.includes('panel2')) {
return 'panel2';
} else if (extensionId.includes('panel3')) {
return 'panel3';
}
}
// Demo 模式:从 URL 参数获取
if (window.location && window.location.search) {
const urlParams = new URLSearchParams(window.location.search);
const panelParam = urlParams.get('panel');
if (panelParam && ['panel1', 'panel2', 'panel3'].includes(panelParam)) {
return panelParam;
}
}
// 默认返回 panel1
return 'panel1';
} catch (error) {
return 'panel1';
}
}预设文件名生成实现
javascript
/**
* 获取当前面板的预设文件名
* @returns {string} 'Eagle2Ae1.Presets', 'Eagle2Ae2.Presets', 或 'Eagle2Ae3.Presets'
*/
getPresetFileName() {
const panelNumber = this.panelId.replace('panel', '');
return `Eagle2Ae${panelNumber}.Presets`;
}
/**
* 获取预设文件完整路径
* @returns {string} 预设文件完整路径
*/
getPresetsFilePath() {
const baseFolder = this.getPresetsBaseFolderPath();
const fileName = this.getPresetFileName();
if (baseFolder) {
// 用户自定义目录
return `${baseFolder}\\${fileName}`;
} else {
// 默认目录
if (window.require) {
const path = window.require('path');
const os = window.require('os');
const documentsPath = path.join(os.homedir(), 'Documents');
return path.join(documentsPath, 'Eagle2Ae-Ae', 'presets', fileName);
} else {
// 降级方案
return `我的文档\\Eagle2Ae-Ae\\presets\\${fileName}`;
}
}
}预设目录管理实现
javascript
/**
* 获取预设目录基础路径
* @returns {string|null} 预设目录路径或null
*/
getPresetsBaseFolderPath() {
try {
if (this.settingsManager && typeof this.settingsManager.getPreference === 'function') {
const p = this.settingsManager.getPreference('presetsDirectory');
if (p && typeof p === 'string' && p.trim() !== '') return p;
}
} catch (e) {
this.log(`⚠️ 获取预设目录失败:${e.message}`, 'warning');
}
return null;
}
/**
* 确保预设目录已准备就绪
* @returns {Promise<void>}
*/
async ensurePresetsFolderReady() {
// Demo 模式:虚拟文件系统不需要创建目录
if (window.__DEMO_MODE_ACTIVE__) {
this.log('📁 Demo 模式:使用虚拟文件系统', 'info');
return;
}
try {
const params = {};
const base = this.getPresetsBaseFolderPath();
if (base) {
params.baseFolderFsPath = base;
} else {
params.targetSubFolder = 'Eagle2Ae-Ae\\presets';
}
const result = await this.executeExtendScript('ensurePresetsFolder', params);
if (result && result.success) {
this.log(`📁 预设目录就绪:${result.folderPath}`, 'info');
} else {
const msg = result && result.error ? result.error : '未知错误';
this.log(`⚠️ 创建预设目录失败:${msg}`, 'warning');
}
} catch (e) {
this.log(`⚠️ ${(window.i18n?.getText('logs.ensurePresetDirErrorPrefix') || 'Ensure preset directory error:')} ${e.message}`, 'warning');
}
}预设加载实现
javascript
/**
* 从磁盘加载预设
* @returns {Promise<void>}
*/
async loadPresetsFromDisk() {
try {
const fileName = this.getPresetFileName();
this.log(`🔍 尝试加载预设文件: ${fileName}`, 'info');
this.log(window.i18n?.getText('logs.tryingToLoadLocalPresets') || 'Trying to load local presets...', 'info');
let parsed = null;
// Demo 模式:从虚拟文件系统加载
if (window.__DEMO_MODE_ACTIVE__) {
try {
let content = null;
// 尝试从虚拟文件系统读取
if (window.demoFileSystem) {
// 使用面板特定的文件名
const demoFileName = `Eagle2Ae-Ae/presets/${this.getPresetFileName()}`;
const result = window.demoFileSystem.readFile(demoFileName);
if (result.success) {
content = result.content;
this.log(`✅ 从虚拟文件系统加载预设 (${result.size} bytes)`, 'info');
}
}
// 降级:从 localStorage 读取
if (!content) {
content = localStorage.getItem('eagle2ae_preset_json');
if (content) {
this.log('✅ 从浏览器存储加载预设 (Demo 模式)', 'info');
}
}
if (content) {
parsed = JSON.parse(content);
} else {
this.log('ℹ️ Demo 模式:未找到保存的预设', 'info');
// 如果预设文件不存在,创建默认预设文件
const fileNameToCreate = this.getPresetFileName();
this.log(`📝 预设文件不存在,正在创建: ${fileNameToCreate}`, 'info');
const saveResult = await this.savePresetsSilently();
if (saveResult) {
this.log(`✅ 默认预设文件已创建: ${this.getPresetFileName()}`, 'success');
// 应用默认配置到界面
try {
const quickSettingsContainer = document.querySelector('.quick-settings-container');
if (quickSettingsContainer) {
this.updateSettingsUI();
this.loadQuickSettings();
} else {
this.log('⚠️ DOM 未就绪,跳过 UI 更新', 'warning');
}
} catch (uiError) {
this.log(`⚠️ UI 更新失败: ${uiError.message}`, 'warning');
}
} else {
this.log(`⚠️ 创建默认预设文件失败`, 'warning');
}
return;
}
} catch (e) {
this.log(`⚠️ Demo 模式加载预设失败: ${e.message}`, 'warning');
return;
}
} else {
// CEP 模式:从文件系统加载
// 使用面板特定的文件名
const params = { fileName: this.getPresetFileName() };
const baseFolder = this.getPresetsBaseFolderPath();
if (baseFolder) {
params.baseFolderFsPath = baseFolder;
} else {
params.targetSubFolder = 'Eagle2Ae-Ae\\presets';
}
const result = await this.executeExtendScript('readImportSettingsFromJSON', params);
if (!result || !result.success) {
const msg = result && result.error ? result.error : '未找到预设文件';
this.log(`ℹ️ 本地预设不可用:${msg}`, 'info');
// 如果预设文件不存在,创建默认预设文件
const fileNameToCreate = this.getPresetFileName();
this.log(`📝 预设文件不存在,正在创建: ${fileNameToCreate}`, 'info');
const saveResult = await this.savePresetsSilently();
if (saveResult) {
this.log(`✅ 默认预设文件已创建: ${this.getPresetFileName()}`, 'success');
// 应用默认配置到界面
try {
const quickSettingsContainer = document.querySelector('.quick-settings-container');
if (quickSettingsContainer) {
this.updateSettingsUI();
this.loadQuickSettings();
} else {
this.log('⚠️ DOM 未就绪,跳过 UI 更新', 'warning');
}
} catch (uiError) {
this.log(`⚠️ UI 更新失败: ${uiError.message}`, 'warning');
}
} else {
this.log(`⚠️ 创建默认预设文件失败`, 'warning');
}
return;
}
// 解析 JSON
parsed = typeof result.jsonData === 'string' ? JSON.parse(result.jsonData) : result.jsonData;
}
// 应用配置到设置管理器
if (parsed && parsed.importSettings) {
this.settingsManager.saveSettings(parsed.importSettings);
}
// 应用用户偏好
if (parsed && parsed.userPreferences) {
this.settingsManager.savePreferences(parsed.userPreferences);
}
// 应用 UI 面板组设置
if (parsed && parsed.uiSettings) {
try {
this.setPanelLocalStorage('uiSettings', JSON.stringify(parsed.uiSettings));
this.log('✅ 已恢复 UI 面板组设置', 'info');
} catch (e) {
console.warn('无法保存 uiSettings:', e);
}
}
// 应用语言设置
if (parsed && parsed.language) {
try {
localStorage.setItem('language', parsed.language);
localStorage.setItem('lang', parsed.language);
// 如果 i18n 系统已加载,切换语言
if (window.i18n && typeof window.i18n.setLanguage === 'function') {
window.i18n.setLanguage(parsed.language);
}
this.log(`✅ 已恢复语言设置: ${parsed.language}`, 'info');
} catch (e) {
console.warn('无法应用语言设置:', e);
}
}
// 应用主题设置
if (parsed && parsed.userPreferences && parsed.userPreferences.theme) {
try {
this.setPanelLocalStorage('aeTheme', parsed.userPreferences.theme);
// 如果主题切换函数存在,应用主题
if (typeof this.applyTheme === 'function') {
this.applyTheme(parsed.userPreferences.theme);
}
this.log(`✅ 已恢复主题设置: ${parsed.userPreferences.theme}`, 'info');
} catch (e) {
console.warn('无法应用主题设置:', e);
}
}
// 应用项目旁设置
if (parsed && parsed.projectAdjacentSettings) {
try {
localStorage.setItem('ae_extension_project_adjacent_settings', JSON.stringify(parsed.projectAdjacentSettings));
this.log('✅ 已恢复项目旁复制设置', 'info');
} catch (e) {
console.warn('无法保存项目旁设置:', e);
}
}
// 应用自定义文件夹设置
if (parsed && parsed.customFolderSettings) {
try {
localStorage.setItem('ae_extension_custom_folder_settings', JSON.stringify(parsed.customFolderSettings));
this.log('✅ 已恢复自定义文件夹设置', 'info');
} catch (e) {
console.warn('无法保存自定义文件夹设置:', e);
}
}
// 应用到UI
try {
// 检查关键 DOM 元素是否存在
const quickSettingsContainer = document.querySelector('.quick-settings-container');
if (quickSettingsContainer) {
this.updateSettingsUI();
this.loadQuickSettings();
this.log('✅ 已加载并应用本地预设(包含 UI 设置、语言、主题等)', 'success');
} else {
this.log('⚠️ DOM 未就绪,跳过 UI 更新(配置已加载到内存)', 'warning');
}
} catch (uiError) {
this.log(`⚠️ UI 更新失败: ${uiError.message}`, 'warning');
}
} catch (error) {
this.log(`${window.i18n?.getText('logs.loadLocalPresetsFailedPrefix') || 'Failed to load local presets:'} ${error.message}`, 'warning');
}
}预设保存实现
javascript
/**
* 静默保存预设到JSON(无弹窗与打开文件夹)
* @returns {Promise<boolean>} 是否保存成功
*/
async savePresetsSilently() {
try {
const fileName = this.getPresetFileName();
this.log(`💾 准备保存预设到文件: ${fileName}`, 'info');
const settings = this.settingsManager.getSettings();
const preferences = this.settingsManager.getPreferences();
// 收集所有配置
const exportPayload = {
importSettings: settings,
userPreferences: preferences,
uiSettings: this.getUISettingsFromLocalStorage(),
exportedAt: new Date().toISOString()
};
// Demo 模式:保存到虚拟文件系统
if (window.__DEMO_MODE_ACTIVE__) {
try {
const jsonContent = JSON.stringify(exportPayload, null, 2);
// 保存到虚拟文件系统
if (window.demoFileSystem) {
// 使用面板特定的文件名
const demoFileName = `Eagle2Ae-Ae/presets/${this.getPresetFileName()}`;
const result = window.demoFileSystem.writeFile(
demoFileName,
jsonContent
);
if (result.success) {
this.log(`💾 预设已保存到虚拟文件系统 (${result.size} bytes)`, 'info');
return true;
} else {
throw new Error(result.error);
}
} else {
// 降级:保存到 localStorage
localStorage.setItem('eagle2ae_preset_json', jsonContent);
this.log('💾 预设已保存到浏览器存储 (Demo 模式)', 'info');
return true;
}
} catch (e) {
this.log(`⚠️ Demo 模式保存预设失败: ${e.message}`, 'warning');
return false;
}
}
// CEP 模式:保存到文件系统
// 使用面板特定的文件名
const params = {
fileName: this.getPresetFileName(),
overwrite: true,
jsonData: JSON.stringify(exportPayload, null, 2)
};
const baseFolder = this.getPresetsBaseFolderPath();
if (baseFolder) {
params.baseFolderFsPath = baseFolder;
} else {
params.targetSubFolder = 'Eagle2Ae-Ae\\presets';
}
const result = await this.executeExtendScript('exportImportSettingsToJSON', params);
if (result && result.success) {
this.log('💾 预设已自动保存到文档目录', 'info');
return true;
} else {
const msg = result && result.error ? result.error : '未知错误';
this.log(`⚠️ 自动保存预设失败: ${msg}`, 'warning');
return false;
}
} catch (error) {
this.log(`⚠️ 自动保存预设异常: ${error.message}`, 'warning');
return false;
}
}预设导出实现
javascript
/**
* 导出预设到文件
* @param {string} fileName - 文件名
* @returns {Promise<boolean>} 是否导出成功
*/
async exportPresetsToFile(fileName = null) {
try {
const exportFileName = fileName || this.getPresetFileName();
this.log(`📤 准备导出预设到文件: ${exportFileName}`, 'info');
// 获取当前设置
const settings = this.settingsManager.getSettings();
const preferences = this.settingsManager.getPreferences();
// 构造导出数据
const exportData = {
version: '2.4.0',
exportedAt: new Date().toISOString(),
panelId: this.panelId,
importSettings: settings,
userPreferences: preferences,
uiSettings: this.getUISettingsFromLocalStorage(),
language: localStorage.getItem('language') || 'zh-CN'
};
// Demo 模式:使用虚拟文件系统导出
if (window.__DEMO_MODE_ACTIVE__) {
try {
const jsonContent = JSON.stringify(exportData, null, 2);
if (window.demoFileSystem) {
// 使用虚拟下载功能
const demoFileName = `Eagle2Ae-Ae/presets/${exportFileName}`;
const result = window.demoFileSystem.downloadFile(
demoFileName,
exportFileName,
jsonContent
);
if (result.success) {
this.log(`📤 预设已导出到虚拟文件系统: ${exportFileName}`, 'success');
return true;
} else {
throw new Error(result.error);
}
} else {
// 降级:使用浏览器下载
const blob = new Blob([jsonContent], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = exportFileName;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
this.log(`📤 预设已通过浏览器下载: ${exportFileName}`, 'success');
return true;
}
} catch (e) {
this.log(`⚠️ Demo 模式导出预设失败: ${e.message}`, 'warning');
return false;
}
}
// CEP 模式:使用ExtendScript导出
const params = {
fileName: exportFileName,
overwrite: true,
jsonData: JSON.stringify(exportData, null, 2)
};
const baseFolder = this.getPresetsBaseFolderPath();
if (baseFolder) {
params.baseFolderFsPath = baseFolder;
} else {
params.targetSubFolder = 'Eagle2Ae-Ae\\presets';
}
const result = await this.executeExtendScript('exportImportSettingsToJSON', params);
if (result && result.success) {
this.log(`📤 预设已导出到文件: ${result.savedPath}`, 'success');
// 显示成功提示
this.showDropMessage(`✅ 预设已导出: ${exportFileName}`, 'success');
return true;
} else {
const errorMsg = result && result.error ? result.error : '未知错误';
this.log(`⚠️ 导出预设失败: ${errorMsg}`, 'error');
// 显示错误提示
this.showDropMessage(`❌ 预设导出失败: ${errorMsg}`, 'error');
return false;
}
} catch (error) {
this.log(`❌ 导出预设异常: ${error.message}`, 'error');
this.showDropMessage(`❌ 导出预设异常: ${error.message}`, 'error');
return false;
}
}预设导入实现
javascript
/**
* 从文件导入预设
* @param {string} filePath - 文件路径
* @returns {Promise<boolean>} 是否导入成功
*/
async importPresetsFromFile(filePath = null) {
try {
this.log('📥 准备从文件导入预设', 'info');
let importFilePath = filePath;
// 如果没有提供文件路径,让用户选择文件
if (!importFilePath) {
if (window.__DEMO_MODE_ACTIVE__) {
// Demo 模式:使用虚拟文件选择
if (window.demoFileSystem) {
const result = window.demoFileSystem.showOpenDialog({
title: '选择预设文件',
filters: [{ name: 'JSON Files', extensions: ['json'] }],
properties: ['openFile']
});
if (result.success && result.filePaths && result.filePaths.length > 0) {
importFilePath = result.filePaths[0];
} else {
this.log('用户取消了文件选择', 'info');
return false;
}
} else {
// 降级:使用简单的输入框
importFilePath = prompt('请输入预设文件路径:');
if (!importFilePath) {
this.log('用户取消了文件输入', 'info');
return false;
}
}
} else {
// CEP 模式:使用文件选择对话框
const result = await this.executeExtendScript('showOpenPresetsFileDialog', {});
if (result && result.success && result.filePath) {
importFilePath = result.filePath;
} else {
const errorMsg = result && result.error ? result.error : '用户取消了文件选择';
this.log(`❌ 文件选择失败: ${errorMsg}`, 'warning');
return false;
}
}
}
if (!importFilePath) {
this.log('❌ 未提供有效的预设文件路径', 'error');
return false;
}
this.log(`📥 正在导入预设文件: ${importFilePath}`, 'info');
let fileContent = null;
// 读取文件内容
if (window.__DEMO_MODE_ACTIVE__) {
// Demo 模式:从虚拟文件系统读取
if (window.demoFileSystem) {
const result = window.demoFileSystem.readFile(importFilePath);
if (result.success) {
fileContent = result.content;
this.log(`📥 从虚拟文件系统读取预设 (${result.size} bytes)`, 'info');
} else {
throw new Error(result.error);
}
} else {
// 降级:从 localStorage 读取
fileContent = localStorage.getItem('eagle2ae_import_preset_json');
if (fileContent) {
this.log('📥 从浏览器存储读取预设 (Demo 模式)', 'info');
} else {
throw new Error('未找到预设文件内容');
}
}
} else {
// CEP 模式:从文件系统读取
const params = { filePath: importFilePath };
const result = await this.executeExtendScript('readImportSettingsFromFile', params);
if (result && result.success && result.fileContent) {
fileContent = result.fileContent;
this.log(`📥 从文件系统读取预设 (${result.fileSize} bytes)`, 'info');
} else {
const errorMsg = result && result.error ? result.error : '读取文件失败';
throw new Error(errorMsg);
}
}
if (!fileContent) {
throw new Error('无法读取预设文件内容');
}
// 解析JSON内容
let parsedData;
try {
parsedData = JSON.parse(fileContent);
} catch (parseError) {
throw new Error(`JSON解析失败: ${parseError.message}`);
}
// 验证预设数据
if (!this.validatePresetData(parsedData)) {
throw new Error('预设文件格式无效');
}
// 应用导入的预设
const applyResult = await this.applyImportedPresets(parsedData);
if (applyResult.success) {
this.log('✅ 预设导入成功', 'success');
this.showDropMessage('✅ 预设导入成功', 'success');
// 刷新UI
this.updateSettingsUI();
this.loadQuickSettings();
return true;
} else {
throw new Error(applyResult.error || '应用预设失败');
}
} catch (error) {
this.log(`❌ 导入预设失败: ${error.message}`, 'error');
this.showDropMessage(`❌ 导入预设失败: ${error.message}`, 'error');
return false;
}
}
/**
* 验证预设数据
* @param {Object} data - 预设数据
* @returns {boolean} 是否有效
*/
validatePresetData(data) {
if (!data || typeof data !== 'object') {
return false;
}
// 检查必需字段
const requiredFields = ['importSettings', 'userPreferences'];
for (const field of requiredFields) {
if (!data.hasOwnProperty(field)) {
this.log(`❌ 预设数据缺少必需字段: ${field}`, 'error');
return false;
}
}
// 验证导入设置
if (data.importSettings && typeof data.importSettings !== 'object') {
this.log('❌ 导入设置格式无效', 'error');
return false;
}
// 验证用户偏好
if (data.userPreferences && typeof data.userPreferences !== 'object') {
this.log('❌ 用户偏好格式无效', 'error');
return false;
}
return true;
}
/**
* 应用导入的预设
* @param {Object} presetData - 预设数据
* @returns {Object} 应用结果
*/
async applyImportedPresets(presetData) {
try {
const { importSettings, userPreferences, uiSettings, language } = presetData;
// 应用导入设置
if (importSettings) {
const settingsResult = this.settingsManager.saveSettings(importSettings);
if (!settingsResult.success) {
throw new Error(`应用导入设置失败: ${settingsResult.error}`);
}
this.log('✅ 导入设置已应用', 'success');
}
// 应用用户偏好
if (userPreferences) {
const prefsResult = this.settingsManager.savePreferences(userPreferences);
if (!prefsResult.success) {
throw new Error(`应用用户偏好失败: ${prefsResult.error}`);
}
this.log('✅ 用户偏好已应用', 'success');
}
// 应用UI设置
if (uiSettings) {
try {
this.setPanelLocalStorage('uiSettings', JSON.stringify(uiSettings));
this.log('✅ UI设置已应用', 'success');
} catch (e) {
this.log(`⚠️ 应用UI设置失败: ${e.message}`, 'warning');
}
}
// 应用语言设置
if (language) {
try {
localStorage.setItem('language', language);
localStorage.setItem('lang', language);
// 如果i18n系统可用,更新语言
if (window.i18n && typeof window.i18n.setLanguage === 'function') {
window.i18n.setLanguage(language);
}
this.log(`✅ 语言设置已应用: ${language}`, 'success');
} catch (e) {
this.log(`⚠️ 应用语言设置失败: ${e.message}`, 'warning');
}
}
return { success: true };
} catch (error) {
return { success: false, error: error.message };
}
}预设备份实现
javascript
/**
* 备份预设文件
* @param {string} backupName - 备份名称
* @returns {Promise<boolean>} 是否备份成功
*/
async backupPresets(backupName = null) {
try {
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').replace('T', '_');
const backupFileName = backupName || `Eagle2Ae-${this.panelId}-backup-${timestamp}.json`;
this.log(`🔒 准备备份预设到文件: ${backupFileName}`, 'info');
// 获取当前设置
const settings = this.settingsManager.getSettings();
const preferences = this.settingsManager.getPreferences();
// 构造备份数据
const backupData = {
version: '2.4.0',
backedUpAt: new Date().toISOString(),
panelId: this.panelId,
importSettings: settings,
userPreferences: preferences,
uiSettings: this.getUISettingsFromLocalStorage(),
language: localStorage.getItem('language') || 'zh-CN',
backupType: 'manual'
};
// Demo 模式:保存到虚拟文件系统
if (window.__DEMO_MODE_ACTIVE__) {
try {
const jsonContent = JSON.stringify(backupData, null, 2);
if (window.demoFileSystem) {
// 保存到备份目录
const backupDir = 'Eagle2Ae-Ae/backups';
const backupPath = `${backupDir}/${backupFileName}`;
const result = window.demoFileSystem.writeFile(backupPath, jsonContent);
if (result.success) {
this.log(`🔒 预设已备份到虚拟文件系统: ${backupFileName}`, 'success');
return true;
} else {
throw new Error(result.error);
}
} else {
// 降级:保存到 localStorage
const backupKey = `eagle2ae_backup_${timestamp}`;
localStorage.setItem(backupKey, jsonContent);
this.log(`🔒 预设已备份到浏览器存储: ${backupKey}`, 'success');
return true;
}
} catch (e) {
this.log(`⚠️ Demo 模式备份预设失败: ${e.message}`, 'warning');
return false;
}
}
// CEP 模式:保存到备份目录
const params = {
fileName: backupFileName,
overwrite: true,
jsonData: JSON.stringify(backupData, null, 2)
};
// 使用备份目录
params.targetSubFolder = 'Eagle2Ae-Ae\\backups';
const result = await this.executeExtendScript('exportImportSettingsToJSON', params);
if (result && result.success) {
this.log(`🔒 预设已备份到文件: ${result.savedPath}`, 'success');
this.showDropMessage(`✅ 预设已备份: ${backupFileName}`, 'success');
return true;
} else {
const errorMsg = result && result.error ? result.error : '未知错误';
this.log(`⚠️ 备份预设失败: ${errorMsg}`, 'error');
this.showDropMessage(`❌ 预设备份失败: ${errorMsg}`, 'error');
return false;
}
} catch (error) {
this.log(`❌ 备份预设异常: ${error.message}`, 'error');
this.showDropMessage(`❌ 备份预设异常: ${error.message}`, 'error');
return false;
}
}预设恢复实现
javascript
/**
* 恢复预设文件
* @param {string} backupFilePath - 备份文件路径
* @returns {Promise<boolean>} 是否恢复成功
*/
async restorePresets(backupFilePath = null) {
try {
this.log('🔓 准备从备份恢复预设', 'info');
let restoreFilePath = backupFilePath;
// 如果没有提供备份文件路径,让用户选择
if (!restoreFilePath) {
if (window.__DEMO_MODE_ACTIVE__) {
// Demo 模式:使用虚拟文件选择
if (window.demoFileSystem) {
const result = window.demoFileSystem.showOpenDialog({
title: '选择备份文件',
filters: [{ name: 'JSON Files', extensions: ['json'] }],
properties: ['openFile']
});
if (result.success && result.filePaths && result.filePaths.length > 0) {
restoreFilePath = result.filePaths[0];
} else {
this.log('用户取消了备份文件选择', 'info');
return false;
}
} else {
// 降级:使用简单的输入框
restoreFilePath = prompt('请输入备份文件路径:');
if (!restoreFilePath) {
this.log('用户取消了备份文件输入', 'info');
return false;
}
}
} else {
// CEP 模式:使用文件选择对话框
const result = await this.executeExtendScript('showOpenBackupFileDialog', {});
if (result && result.success && result.filePath) {
restoreFilePath = result.filePath;
} else {
const errorMsg = result && result.error ? result.error : '用户取消了备份文件选择';
this.log(`❌ 备份文件选择失败: ${errorMsg}`, 'warning');
return false;
}
}
}
if (!restoreFilePath) {
this.log('❌ 未提供有效的备份文件路径', 'error');
return false;
}
this.log(`🔓 正在从备份文件恢复预设: ${restoreFilePath}`, 'info');
let fileContent = null;
// 读取备份文件内容
if (window.__DEMO_MODE_ACTIVE__) {
// Demo 模式:从虚拟文件系统读取
if (window.demoFileSystem) {
const result = window.demoFileSystem.readFile(restoreFilePath);
if (result.success) {
fileContent = result.content;
this.log(`🔓 从虚拟文件系统读取备份 (${result.size} bytes)`, 'info');
} else {
throw new Error(result.error);
}
} else {
// 降级:从 localStorage 读取
const backupKeys = Object.keys(localStorage).filter(key => key.startsWith('eagle2ae_backup_'));
if (backupKeys.length > 0) {
fileContent = localStorage.getItem(backupKeys[0]);
if (fileContent) {
this.log('🔓 从浏览器存储读取备份 (Demo 模式)', 'info');
} else {
throw new Error('未找到备份文件内容');
}
} else {
throw new Error('未找到任何备份');
}
}
} else {
// CEP 模式:从文件系统读取
const params = { filePath: restoreFilePath };
const result = await this.executeExtendScript('readImportSettingsFromFile', params);
if (result && result.success && result.fileContent) {
fileContent = result.fileContent;
this.log(`🔓 从文件系统读取备份 (${result.fileSize} bytes)`, 'info');
} else {
const errorMsg = result && result.error ? result.error : '读取备份文件失败';
throw new Error(errorMsg);
}
}
if (!fileContent) {
throw new Error('无法读取备份文件内容');
}
// 解析JSON内容
let parsedData;
try {
parsedData = JSON.parse(fileContent);
} catch (parseError) {
throw new Error(`JSON解析失败: ${parseError.message}`);
}
// 验证备份数据
if (!this.validateBackupData(parsedData)) {
throw new Error('备份文件格式无效');
}
// 确认恢复操作
const confirmRestore = confirm(`确定要从备份 "${restoreFilePath}" 恢复预设吗?\n这将覆盖当前的所有设置。`);
if (!confirmRestore) {
this.log('用户取消了预设恢复操作', 'info');
return false;
}
// 应用恢复的预设
const applyResult = await this.applyRestoredPresets(parsedData);
if (applyResult.success) {
this.log('✅ 预设恢复成功', 'success');
this.showDropMessage('✅ 预设恢复成功', 'success');
// 刷新UI
this.updateSettingsUI();
this.loadQuickSettings();
// 重新加载预设以确保一致性
await this.loadPresetsFromDisk();
return true;
} else {
throw new Error(applyResult.error || '应用恢复的预设失败');
}
} catch (error) {
this.log(`❌ 恢复预设失败: ${error.message}`, 'error');
this.showDropMessage(`❌ 恢复预设失败: ${error.message}`, 'error');
return false;
}
}
/**
* 验证备份数据
* @param {Object} data - 备份数据
* @returns {boolean} 是否有效
*/
validateBackupData(data) {
if (!data || typeof data !== 'object') {
return false;
}
// 检查必需字段
const requiredFields = ['importSettings', 'userPreferences', 'backedUpAt'];
for (const field of requiredFields) {
if (!data.hasOwnProperty(field)) {
this.log(`❌ 备份数据缺少必需字段: ${field}`, 'error');
return false;
}
}
// 验证备份时间戳
if (data.backedUpAt && isNaN(Date.parse(data.backedUpAt))) {
this.log('❌ 备份时间戳格式无效', 'error');
return false;
}
// 验证导入设置
if (data.importSettings && typeof data.importSettings !== 'object') {
this.log('❌ 导入设置格式无效', 'error');
return false;
}
// 验证用户偏好
if (data.userPreferences && typeof data.userPreferences !== 'object') {
this.log('❌ 用户偏好格式无效', 'error');
return false;
}
return true;
}
/**
* 应用恢复的预设
* @param {Object} backupData - 备份数据
* @returns {Object} 应用结果
*/
async applyRestoredPresets(backupData) {
try {
const { importSettings, userPreferences, uiSettings, language } = backupData;
// 应用导入设置
if (importSettings) {
const settingsResult = this.settingsManager.saveSettings(importSettings);
if (!settingsResult.success) {
throw new Error(`应用导入设置失败: ${settingsResult.error}`);
}
this.log('✅ 导入设置已恢复', 'success');
}
// 应用用户偏好
if (userPreferences) {
const prefsResult = this.settingsManager.savePreferences(userPreferences);
if (!prefsResult.success) {
throw new Error(`应用用户偏好失败: ${prefsResult.error}`);
}
this.log('✅ 用户偏好已恢复', 'success');
}
// 应用UI设置
if (uiSettings) {
try {
this.setPanelLocalStorage('uiSettings', JSON.stringify(uiSettings));
this.log('✅ UI设置已恢复', 'success');
} catch (e) {
this.log(`⚠️ 恢复UI设置失败: ${e.message}`, 'warning');
}
}
// 应用语言设置
if (language) {
try {
localStorage.setItem('language', language);
localStorage.setItem('lang', language);
// 如果i18n系统可用,更新语言
if (window.i18n && typeof window.i18n.setLanguage === 'function') {
window.i18n.setLanguage(language);
}
this.log(`✅ 语言设置已恢复: ${language}`, 'success');
} catch (e) {
this.log(`⚠️ 恢复语言设置失败: ${e.message}`, 'warning');
}
}
return { success: true };
} catch (error) {
return { success: false, error: error.message };
}
}API参考
核心方法
PresetManager
预设管理器主类
javascript
/**
* 预设管理器
* 负责管理每个面板实例的预设文件,支持导出导入和备份恢复
*/
class PresetManager构造函数
javascript
/**
* 构造函数
* @param {Object} aeExtension - AE扩展实例
*/
constructor(aeExtension)getPanelId()
获取当前面板ID
javascript
/**
* 获取当前面板 ID
* @returns {string} 'panel1', 'panel2', 或 'panel3'
*/
getPanelId()getPresetFileName()
获取当前面板的预设文件名
javascript
/**
* 获取当前面板的预设文件名
* @returns {string} 'Eagle2Ae1.Presets', 'Eagle2Ae2.Presets', 或 'Eagle2Ae3.Presets'
*/
getPresetFileName()getPresetsFilePath()
获取预设文件完整路径
javascript
/**
* 获取预设文件完整路径
* @returns {string} 预设文件完整路径
*/
getPresetsFilePath()getPresetsBaseFolderPath()
获取预设目录基础路径
javascript
/**
* 获取预设目录基础路径
* @returns {string|null} 预设目录路径或null
*/
getPresetsBaseFolderPath()ensurePresetsFolderReady()
确保预设目录已准备就绪
javascript
/**
* 确保预设目录已准备就绪
* @returns {Promise<void>}
*/
async ensurePresetsFolderReady()loadPresetsFromDisk()
从磁盘加载预设
javascript
/**
* 从磁盘加载预设
* @returns {Promise<void>}
*/
async loadPresetsFromDisk()savePresetsSilently()
静默保存预设到JSON
javascript
/**
* 静默保存预设到JSON(无弹窗与打开文件夹)
* @returns {Promise<boolean>} 是否保存成功
*/
async savePresetsSilently()exportPresetsToFile()
导出预设到文件
javascript
/**
* 导出预设到文件
* @param {string} fileName - 文件名
* @returns {Promise<boolean>} 是否导出成功
*/
async exportPresetsToFile(fileName = null)importPresetsFromFile()
从文件导入预设
javascript
/**
* 从文件导入预设
* @param {string} filePath - 文件路径
* @returns {Promise<boolean>} 是否导入成功
*/
async importPresetsFromFile(filePath = null)backupPresets()
备份预设文件
javascript
/**
* 备份预设文件
* @param {string} backupName - 备份名称
* @returns {Promise<boolean>} 是否备份成功
*/
async backupPresets(backupName = null)restorePresets()
恢复预设文件
javascript
/**
* 恢复预设文件
* @param {string} backupFilePath - 备份文件路径
* @returns {Promise<boolean>} 是否恢复成功
*/
async restorePresets(backupFilePath = null)辅助方法
validatePresetData()
验证预设数据
javascript
/**
* 验证预设数据
* @param {Object} data - 预设数据
* @returns {boolean} 是否有效
*/
validatePresetData(data)applyImportedPresets()
应用导入的预设
javascript
/**
* 应用导入的预设
* @param {Object} presetData - 预设数据
* @returns {Object} 应用结果
*/
async applyImportedPresets(presetData)validateBackupData()
验证备份数据
javascript
/**
* 验证备份数据
* @param {Object} data - 备份数据
* @returns {boolean} 是否有效
*/
validateBackupData(data)applyRestoredPresets()
应用恢复的预设
javascript
/**
* 应用恢复的预设
* @param {Object} backupData - 备份数据
* @returns {Object} 应用结果
*/
async applyRestoredPresets(backupData)使用示例
基本使用
javascript
// 创建预设管理器实例
const presetManager = new PresetManager(aeExtension);
// 加载预设
await presetManager.loadPresetsFromDisk();
// 保存预设
const saveResult = await presetManager.savePresetsSilently();
if (saveResult) {
console.log('预设保存成功');
}
// 导出预设
await presetManager.exportPresetsToFile('my-preset.json');
// 导入预设
await presetManager.importPresetsFromFile('imported-preset.json');
// 备份预设
await presetManager.backupPresets('backup-20250930.json');
// 恢复预设
await presetManager.restorePresets('backup-20250930.json');高级使用
javascript
// 自定义预设目录
presetManager.settingsManager.updatePreference('presetsDirectory', '/custom/presets/path');
// 批量操作预设
async function batchPresetOperations() {
try {
// 1. 备份当前预设
await presetManager.backupPresets();
// 2. 修改设置
presetManager.settingsManager.updateField('mode', 'project_adjacent');
presetManager.settingsManager.updateField('addToComposition', true);
// 3. 保存修改后的预设
await presetManager.savePresetsSilently();
// 4. 导出预设文件
await presetManager.exportPresetsToFile('modified-preset.json');
console.log('批量预设操作完成');
} catch (error) {
console.error('批量预设操作失败:', error);
}
}
// 监听预设变更
presetManager.settingsManager.addListener((type, data) => {
if (type === 'settings' || type === 'preferences') {
// 设置变更时自动保存预设
presetManager.savePresetsSilently();
}
});最佳实践
预设管理建议
定期备份
javascript
// 定期自动备份预设
setInterval(async () => {
try {
const timestamp = new Date().toISOString().split('T')[0];
await presetManager.backupPresets(`auto-backup-${timestamp}.json`);
console.log(`自动备份完成: ${timestamp}`);
} catch (error) {
console.error('自动备份失败:', error);
}
}, 86400000); // 每24小时备份一次版本控制
javascript
// 为不同项目创建专门的预设文件
function createProjectPreset(projectName) {
const presetName = `${projectName}-preset.json`;
return presetManager.exportPresetsToFile(presetName);
}
// 恢复项目预设
function restoreProjectPreset(projectName) {
const presetName = `${projectName}-preset.json`;
return presetManager.importPresetsFromFile(presetName);
}团队协作
javascript
// 导出团队标准预设
async function exportTeamPreset() {
const teamSettings = {
mode: 'project_adjacent',
addToComposition: true,
timelineOptions: {
placement: 'current_time'
}
};
// 应用团队设置
presetManager.settingsManager.saveSettings(teamSettings);
// 导出团队预设文件
await presetManager.exportPresetsToFile('team-standard-preset.json');
console.log('团队标准预设已导出');
}
// 导入团队标准预设
async function importTeamPreset() {
await presetManager.importPresetsFromFile('team-standard-preset.json');
console.log('团队标准预设已导入');
// 刷新UI以应用新设置
presetManager.updateSettingsUI();
presetManager.loadQuickSettings();
}性能优化
防抖保存
javascript
// 使用防抖避免频繁保存预设
const debounce = (func, wait) => {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
};
const debouncedSavePresets = debounce(() => {
presetManager.savePresetsSilently();
}, 1000); // 1秒防抖延迟
// 在设置变更时调用防抖保存
presetManager.settingsManager.addListener(() => {
debouncedSavePresets();
});缓存机制
javascript
// 实现预设缓存机制
class CachedPresetManager extends PresetManager {
constructor(aeExtension) {
super(aeExtension);
this.presetCache = new Map();
this.cacheTimeout = 30000; // 30秒缓存超时
}
async loadPresetsFromDisk() {
const cacheKey = `preset_${this.panelId}`;
const cached = this.presetCache.get(cacheKey);
if (cached && (Date.now() - cached.timestamp) < this.cacheTimeout) {
// 使用缓存的预设
this.log('使用缓存的预设', 'debug');
return cached.data;
}
// 加载新预设并缓存
const presets = await super.loadPresetsFromDisk();
this.presetCache.set(cacheKey, {
data: presets,
timestamp: Date.now()
});
return presets;
}
clearCache() {
this.presetCache.clear();
this.log('预设缓存已清除', 'debug');
}
}批量操作
javascript
// 批量处理多个面板的预设
async function batchProcessPanelPresets(panelIds, operation) {
const results = [];
for (const panelId of panelIds) {
try {
const panelPresetManager = new PresetManager(aeExtension);
panelPresetManager.panelId = panelId;
const result = await operation(panelPresetManager);
results.push({ panelId, success: true, result });
} catch (error) {
results.push({ panelId, success: false, error: error.message });
}
}
return results;
}
// 使用示例:批量备份所有面板预设
const panelIds = ['panel1', 'panel2', 'panel3'];
const backupResults = await batchProcessPanelPresets(panelIds, async (presetManager) => {
return await presetManager.backupPresets();
});
console.log('批量备份结果:', backupResults);故障排除
常见问题
预设文件丢失
- 症状:面板配置重置为默认值
- 解决:
- 检查预设文件存储路径
- 验证文件权限
- 恢复备份预设文件
预设导入失败
- 症状:导入预设文件时报错或无反应
- 解决:
- 检查预设文件格式是否正确
- 验证JSON语法是否有效
- 确认预设文件版本兼容性
预设保存失败
- 症状:修改设置后预设未保存或保存报错
- 解决:
- 检查localStorage权限
- 验证文件系统写入权限
- 检查磁盘空间是否充足
备份恢复失败
- 症状:从备份文件恢复预设时报错或配置未应用
- 解决:
- 验证备份文件完整性
- 检查备份文件版本兼容性
- 手动应用关键设置
调试技巧
启用详细日志
javascript
// 在控制台中启用详细日志
localStorage.setItem('debugLogLevel', '0');
// 监控预设操作
presetManager.addEventListener('presetOperation', (event) => {
console.log('预设操作:', event.detail);
});检查预设文件
javascript
// 检查预设文件是否存在
function checkPresetFileExists() {
const presetFileName = presetManager.getPresetFileName();
const presetFilePath = presetManager.getPresetsFilePath();
console.log('预设文件名:', presetFileName);
console.log('预设文件路径:', presetFilePath);
// 在CEP环境中检查文件是否存在
if (window.cep && window.cep.fs) {
const fileResult = window.cep.fs.readFile(presetFilePath);
if (fileResult.err === 0) {
console.log('预设文件存在,大小:', fileResult.data.length, '字节');
} else {
console.log('预设文件不存在');
}
}
}性能监控
javascript
// 监控预设操作性能
async function monitorPresetPerformance(operation, ...args) {
const startTime = performance.now();
try {
const result = await operation(...args);
const endTime = performance.now();
console.log(`${operation.name} 操作耗时: ${endTime - startTime}ms`);
return result;
} catch (error) {
const endTime = performance.now();
console.log(`${operation.name} 操作失败,耗时: ${endTime - startTime}ms`);
throw error;
}
}
// 使用示例
const saveResult = await monitorPresetPerformance(
presetManager.savePresetsSilently.bind(presetManager)
);扩展性
自定义扩展
扩展预设管理器
javascript
// 创建自定义预设管理器
class CustomPresetManager extends PresetManager {
constructor(aeExtension) {
super(aeExtension);
this.customFeatures = new Map();
}
/**
* 注册自定义功能
* @param {string} name - 功能名称
* @param {Function} feature - 功能实现
*/
registerCustomFeature(name, feature) {
this.customFeatures.set(name, feature);
}
/**
* 执行自定义功能
* @param {string} name - 功能名称
* @param {...any} args - 参数
* @returns {any} 功能执行结果
*/
executeCustomFeature(name, ...args) {
const feature = this.customFeatures.get(name);
if (feature && typeof feature === 'function') {
return feature(...args);
}
throw new Error(`未知的自定义功能: ${name}`);
}
}
// 注册自定义功能
const customPresetManager = new CustomPresetManager(aeExtension);
customPresetManager.registerCustomFeature('cloudSync', async (presets) => {
// 实现云端同步功能
console.log('同步预设到云端:', presets);
return { success: true };
});插件化架构
javascript
// 创建预设插件
class PresetPlugin {
constructor(presetManager) {
this.presetManager = presetManager;
this.init();
}
init() {
// 添加插件特定的方法
this.presetManager.cloudSync = this.cloudSync.bind(this);
this.presetManager.versionControl = this.versionControl.bind(this);
// 监听预设变更事件
this.presetManager.settingsManager.addListener((type, data) => {
if (type === 'autoSave') {
// 自动同步到云端
this.handleAutoSync(data);
}
});
}
/**
* 云端同步
* @param {Object} presets - 预设数据
*/
async cloudSync(presets) {
try {
// 实现云端同步逻辑
const response = await fetch('/api/presets/sync', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(presets)
});
if (response.ok) {
const result = await response.json();
this.presetManager.log('✅ 预设已同步到云端', 'success');
return result;
} else {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
} catch (error) {
this.presetManager.log(`❌ 云端同步失败: ${error.message}`, 'error');
throw error;
}
}
/**
* 版本控制
* @param {Object} presets - 预设数据
*/
async versionControl(presets) {
try {
// 实现版本控制逻辑
const version = presets.version || '1.0.0';
const timestamp = new Date().toISOString();
const versionedPresets = {
...presets,
version: version,
timestamp: timestamp,
history: presets.history || []
};
// 添加到历史记录
versionedPresets.history.push({
version: version,
timestamp: timestamp,
changes: this.detectChanges(presets)
});
this.presetManager.log(`📦 预设版本已更新: ${version}`, 'info');
return versionedPresets;
} catch (error) {
this.presetManager.log(`❌ 版本控制失败: ${error.message}`, 'error');
throw error;
}
}
/**
* 检测变更
* @param {Object} presets - 预设数据
* @returns {Array} 变更列表
*/
detectChanges(presets) {
// 实现变更检测逻辑
return ['设置已更新'];
}
/**
* 处理自动同步
* @param {Object} data - 自动保存数据
*/
async handleAutoSync(data) {
try {
// 检查是否启用云端同步
const cloudSyncEnabled = this.presetManager.settingsManager.getPreference('cloudSyncEnabled');
if (!cloudSyncEnabled) return;
// 执行云端同步
await this.cloudSync(data);
} catch (error) {
this.presetManager.log(`自动同步失败: ${error.message}`, 'warning');
}
}
}
// 应用插件
const plugin = new PresetPlugin(presetManager);