|
|
@ -1,50 +1,53 @@ |
|
|
// import MemoryStorage from './storage/MemoryStorage.js';
|
|
|
// import MemoryStorage from './storage/MemoryStorage.js';
|
|
|
import LocalStorage from './storage/LocalStorage.js'; |
|
|
|
|
|
|
|
|
import LocalStorage from './storage/LocalStorage.js' |
|
|
// import SessionStorage from './storage/SessionStorage.js';
|
|
|
// import SessionStorage from './storage/SessionStorage.js';
|
|
|
import IndexedDBStorage from './storage/IndexedDBStorage.js'; |
|
|
|
|
|
import ServerStorage from './storage/ServerStorage.js'; |
|
|
|
|
|
|
|
|
import IndexedDBStorage from './storage/IndexedDBStorage.js' |
|
|
|
|
|
import ServerStorage from './storage/ServerStorage.js' |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 自动保存管理器 |
|
|
* 自动保存管理器 |
|
|
* 协调各种存储方式,处理冲突合并 |
|
|
* 协调各种存储方式,处理冲突合并 |
|
|
*/ |
|
|
*/ |
|
|
class AutoSaveManager { |
|
|
class AutoSaveManager { |
|
|
constructor(key, serverOptions = { |
|
|
|
|
|
endpoint:process.env.VUE_APP_BASE_API, |
|
|
|
|
|
syncEndpoint:process.env.VUE_APP_BASE_API, |
|
|
|
|
|
historyEndpoint:process.env.VUE_APP_BASE_API, |
|
|
|
|
|
}) { |
|
|
|
|
|
this.key = key; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
constructor( |
|
|
|
|
|
key, |
|
|
|
|
|
serverOptions = { |
|
|
|
|
|
endpoint: process.env.VUE_APP_BASE_API, |
|
|
|
|
|
syncEndpoint: process.env.VUE_APP_BASE_API, |
|
|
|
|
|
historyEndpoint: process.env.VUE_APP_BASE_API |
|
|
|
|
|
} |
|
|
|
|
|
) { |
|
|
|
|
|
this.key = key |
|
|
|
|
|
|
|
|
try { |
|
|
try { |
|
|
this.storages = { |
|
|
this.storages = { |
|
|
// memory: new MemoryStorage(key),
|
|
|
// memory: new MemoryStorage(key),
|
|
|
// localStorage: new LocalStorage(key),
|
|
|
|
|
|
|
|
|
localStorage: new LocalStorage(key), |
|
|
// sessionStorage: new SessionStorage(key),
|
|
|
// sessionStorage: new SessionStorage(key),
|
|
|
indexedDB: new IndexedDBStorage(key), |
|
|
|
|
|
|
|
|
// indexedDB: new IndexedDBStorage(key),
|
|
|
server: new ServerStorage(key, serverOptions) |
|
|
server: new ServerStorage(key, serverOptions) |
|
|
}; |
|
|
|
|
|
|
|
|
} |
|
|
} catch (error) { |
|
|
} catch (error) { |
|
|
console.error('初始化存储失败:', error); |
|
|
|
|
|
|
|
|
console.error('初始化存储失败:', error) |
|
|
// 如果某个存储初始化失败,只使用可用的存储
|
|
|
// 如果某个存储初始化失败,只使用可用的存储
|
|
|
this.storages = { |
|
|
this.storages = { |
|
|
// memory: new MemoryStorage(key),
|
|
|
// memory: new MemoryStorage(key),
|
|
|
localStorage: new LocalStorage(key), |
|
|
|
|
|
|
|
|
localStorage: new LocalStorage(key) |
|
|
// sessionStorage: new SessionStorage(key)
|
|
|
// sessionStorage: new SessionStorage(key)
|
|
|
}; |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
this.states = {}; |
|
|
|
|
|
this.initStates(); |
|
|
|
|
|
|
|
|
|
|
|
this.isSaving = false; |
|
|
|
|
|
this.saveTimer = null; |
|
|
|
|
|
this.debounceDelay = 1000;// 防抖延迟
|
|
|
|
|
|
this.currentContent = ''; |
|
|
|
|
|
this.lastSavedContent = ''; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.states = {} |
|
|
|
|
|
this.initStates() |
|
|
|
|
|
|
|
|
|
|
|
this.isSaving = false |
|
|
|
|
|
this.saveTimer = null |
|
|
|
|
|
this.debounceDelay = 1000 // 防抖延迟
|
|
|
|
|
|
this.currentContent = '' |
|
|
|
|
|
this.lastSavedContent = '' |
|
|
|
|
|
|
|
|
// 异步初始化
|
|
|
// 异步初始化
|
|
|
this.initPromise = this.init(); |
|
|
|
|
|
|
|
|
this.initPromise = this.init() |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
@ -52,12 +55,12 @@ class AutoSaveManager { |
|
|
*/ |
|
|
*/ |
|
|
initStates() { |
|
|
initStates() { |
|
|
for (const type in this.storages) { |
|
|
for (const type in this.storages) { |
|
|
this.states[type] = { |
|
|
|
|
|
status: 'idle', |
|
|
|
|
|
lastSaved: null, |
|
|
|
|
|
|
|
|
this.states[type] = { |
|
|
|
|
|
status: 'idle', |
|
|
|
|
|
lastSaved: null, |
|
|
error: null, |
|
|
error: null, |
|
|
initialized: false |
|
|
initialized: false |
|
|
}; |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -65,7 +68,7 @@ class AutoSaveManager { |
|
|
* 等待初始化完成 |
|
|
* 等待初始化完成 |
|
|
*/ |
|
|
*/ |
|
|
async waitForInit() { |
|
|
async waitForInit() { |
|
|
await this.initPromise; |
|
|
|
|
|
|
|
|
await this.initPromise |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
@ -74,17 +77,17 @@ class AutoSaveManager { |
|
|
async init() { |
|
|
async init() { |
|
|
try { |
|
|
try { |
|
|
// 加载所有存储的数据
|
|
|
// 加载所有存储的数据
|
|
|
await this.loadFromAllStorages(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
await this.loadFromAllStorages() |
|
|
|
|
|
|
|
|
// 设置页面关闭前保存
|
|
|
// 设置页面关闭前保存
|
|
|
this.setupBeforeUnload(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.setupBeforeUnload() |
|
|
|
|
|
|
|
|
// 设置页面崩溃检测
|
|
|
// 设置页面崩溃检测
|
|
|
this.setupCrashDetection(); |
|
|
|
|
|
|
|
|
|
|
|
console.log(`AutoSaveManager 初始化完成,key: ${this.key}`); |
|
|
|
|
|
|
|
|
this.setupCrashDetection() |
|
|
|
|
|
|
|
|
|
|
|
console.log(`AutoSaveManager 初始化完成,key: ${this.key}`) |
|
|
} catch (error) { |
|
|
} catch (error) { |
|
|
console.error('AutoSaveManager 初始化失败:', error); |
|
|
|
|
|
|
|
|
console.error('AutoSaveManager 初始化失败:', error) |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -92,41 +95,43 @@ class AutoSaveManager { |
|
|
* 从所有存储加载数据并合并 |
|
|
* 从所有存储加载数据并合并 |
|
|
*/ |
|
|
*/ |
|
|
async loadFromAllStorages() { |
|
|
async loadFromAllStorages() { |
|
|
const results = {}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const results = {} |
|
|
|
|
|
|
|
|
// 并行加载所有存储
|
|
|
// 并行加载所有存储
|
|
|
const promises = Object.entries(this.storages).map(async ([type, storage]) => { |
|
|
|
|
|
try { |
|
|
|
|
|
// 确保存储已初始化
|
|
|
|
|
|
if (storage.waitForInit) { |
|
|
|
|
|
await storage.waitForInit(); |
|
|
|
|
|
|
|
|
const promises = Object.entries(this.storages).map( |
|
|
|
|
|
async ([type, storage]) => { |
|
|
|
|
|
try { |
|
|
|
|
|
// 确保存储已初始化
|
|
|
|
|
|
if (storage.waitForInit) { |
|
|
|
|
|
await storage.waitForInit() |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const result = await storage.load() |
|
|
|
|
|
results[type] = result |
|
|
|
|
|
this.updateState(type, 'loaded', { |
|
|
|
|
|
...result, |
|
|
|
|
|
initialized: true |
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
return result |
|
|
|
|
|
} catch (error) { |
|
|
|
|
|
console.error(`加载${type}存储失败:`, error) |
|
|
|
|
|
this.updateState(type, 'error', { |
|
|
|
|
|
error: error.message, |
|
|
|
|
|
initialized: false |
|
|
|
|
|
}) |
|
|
|
|
|
return null |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const result = await storage.load(); |
|
|
|
|
|
results[type] = result; |
|
|
|
|
|
this.updateState(type, 'loaded', { |
|
|
|
|
|
...result, |
|
|
|
|
|
initialized: true |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
return result; |
|
|
|
|
|
} catch (error) { |
|
|
|
|
|
console.error(`加载${type}存储失败:`, error); |
|
|
|
|
|
this.updateState(type, 'error', { |
|
|
|
|
|
error: error.message, |
|
|
|
|
|
initialized: false |
|
|
|
|
|
}); |
|
|
|
|
|
return null; |
|
|
|
|
|
} |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
await Promise.allSettled(promises); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
await Promise.allSettled(promises) |
|
|
|
|
|
|
|
|
// 选择最新版本的内容
|
|
|
// 选择最新版本的内容
|
|
|
// this.currentContent = this.resolveConflicts(results);
|
|
|
// this.currentContent = this.resolveConflicts(results);
|
|
|
this.lastSavedContent = this.currentContent; |
|
|
|
|
|
|
|
|
|
|
|
return this.currentContent; |
|
|
|
|
|
|
|
|
this.lastSavedContent = this.currentContent |
|
|
|
|
|
|
|
|
|
|
|
return this.currentContent |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
@ -135,31 +140,31 @@ class AutoSaveManager { |
|
|
* @returns {string} 合并后的内容 |
|
|
* @returns {string} 合并后的内容 |
|
|
*/ |
|
|
*/ |
|
|
resolveConflicts(results) { |
|
|
resolveConflicts(results) { |
|
|
let latestContent = ''; |
|
|
|
|
|
let latestTimestamp = 0; |
|
|
|
|
|
|
|
|
let latestContent = '' |
|
|
|
|
|
let latestTimestamp = 0 |
|
|
|
|
|
|
|
|
Object.entries(results).forEach(([type, result]) => { |
|
|
Object.entries(results).forEach(([type, result]) => { |
|
|
if (result && result.success && result.data) { |
|
|
if (result && result.success && result.data) { |
|
|
let data = result.data; |
|
|
|
|
|
let timestamp = 0; |
|
|
|
|
|
let content = ''; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let data = result.data |
|
|
|
|
|
let timestamp = 0 |
|
|
|
|
|
let content = '' |
|
|
|
|
|
|
|
|
if (typeof data === 'object' && data !== null) { |
|
|
if (typeof data === 'object' && data !== null) { |
|
|
timestamp = data.timestamp || data.lastModified || 0; |
|
|
|
|
|
content = data.content || ''; |
|
|
|
|
|
|
|
|
timestamp = data.timestamp || data.lastModified || 0 |
|
|
|
|
|
content = data.content || '' |
|
|
} else if (typeof data === 'string') { |
|
|
} else if (typeof data === 'string') { |
|
|
content = data; |
|
|
|
|
|
timestamp = Date.now(); // 字符串数据给一个默认时间戳
|
|
|
|
|
|
|
|
|
content = data |
|
|
|
|
|
timestamp = Date.now() // 字符串数据给一个默认时间戳
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (timestamp > latestTimestamp) { |
|
|
if (timestamp > latestTimestamp) { |
|
|
latestTimestamp = timestamp; |
|
|
|
|
|
latestContent = content; |
|
|
|
|
|
|
|
|
latestTimestamp = timestamp |
|
|
|
|
|
latestContent = content |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
return latestContent || ''; |
|
|
|
|
|
|
|
|
return latestContent || '' |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
@ -167,22 +172,22 @@ class AutoSaveManager { |
|
|
* @param {string} content - 要保存的内容 |
|
|
* @param {string} content - 要保存的内容 |
|
|
*/ |
|
|
*/ |
|
|
save(content) { |
|
|
save(content) { |
|
|
this.currentContent = content; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.currentContent = content |
|
|
|
|
|
|
|
|
// 如果内容没有变化,不保存
|
|
|
// 如果内容没有变化,不保存
|
|
|
if (content === this.lastSavedContent) { |
|
|
if (content === this.lastSavedContent) { |
|
|
return; |
|
|
|
|
|
|
|
|
return |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 清除之前的定时器
|
|
|
// 清除之前的定时器
|
|
|
if (this.saveTimer) { |
|
|
if (this.saveTimer) { |
|
|
clearTimeout(this.saveTimer); |
|
|
|
|
|
|
|
|
clearTimeout(this.saveTimer) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 设置新的定时器
|
|
|
// 设置新的定时器
|
|
|
this.saveTimer = setTimeout(() => { |
|
|
this.saveTimer = setTimeout(() => { |
|
|
this.doSave(content); |
|
|
|
|
|
}, this.debounceDelay); |
|
|
|
|
|
|
|
|
this.doSave(content) |
|
|
|
|
|
}, this.debounceDelay) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
@ -191,51 +196,53 @@ class AutoSaveManager { |
|
|
*/ |
|
|
*/ |
|
|
async doSave(content) { |
|
|
async doSave(content) { |
|
|
if (this.isSaving) { |
|
|
if (this.isSaving) { |
|
|
return; |
|
|
|
|
|
|
|
|
return |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
this.isSaving = true; |
|
|
|
|
|
this.lastSavedContent = content; |
|
|
|
|
|
|
|
|
this.isSaving = true |
|
|
|
|
|
this.lastSavedContent = content |
|
|
|
|
|
|
|
|
try { |
|
|
try { |
|
|
// 并行保存到所有存储
|
|
|
// 并行保存到所有存储
|
|
|
const promises = Object.entries(this.storages).map(async ([type, storage]) => { |
|
|
|
|
|
try { |
|
|
|
|
|
// 检查存储是否可用
|
|
|
|
|
|
if (!storage || this.states[type]?.error) { |
|
|
|
|
|
this.updateState(type, 'error', { |
|
|
|
|
|
error: '存储不可用', |
|
|
|
|
|
retry: true |
|
|
|
|
|
}); |
|
|
|
|
|
return null; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
this.updateState(type, 'saving'); |
|
|
|
|
|
const result = await storage.save(content); |
|
|
|
|
|
|
|
|
|
|
|
if (result && result.success) { |
|
|
|
|
|
this.updateState(type, 'saved', result); |
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
|
const promises = Object.entries(this.storages).map( |
|
|
|
|
|
async ([type, storage]) => { |
|
|
|
|
|
try { |
|
|
|
|
|
// 检查存储是否可用
|
|
|
|
|
|
if (!storage || this.states[type]?.error) { |
|
|
|
|
|
this.updateState(type, 'error', { |
|
|
|
|
|
error: '存储不可用', |
|
|
|
|
|
retry: true |
|
|
|
|
|
}) |
|
|
|
|
|
return null |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
this.updateState(type, 'saving') |
|
|
|
|
|
const result = await storage.save(content) |
|
|
|
|
|
|
|
|
|
|
|
if (result && result.success) { |
|
|
|
|
|
this.updateState(type, 'saved', result) |
|
|
|
|
|
} else { |
|
|
|
|
|
this.updateState(type, 'error', { |
|
|
|
|
|
error: result?.error || '保存失败', |
|
|
|
|
|
retry: true |
|
|
|
|
|
}) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return result |
|
|
|
|
|
} catch (error) { |
|
|
|
|
|
console.error(`保存到${type}失败:`, error) |
|
|
this.updateState(type, 'error', { |
|
|
this.updateState(type, 'error', { |
|
|
error: result?.error || '保存失败', |
|
|
|
|
|
|
|
|
error: error.message, |
|
|
retry: true |
|
|
retry: true |
|
|
}); |
|
|
|
|
|
|
|
|
}) |
|
|
|
|
|
return null |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
return result; |
|
|
|
|
|
} catch (error) { |
|
|
|
|
|
console.error(`保存到${type}失败:`, error); |
|
|
|
|
|
this.updateState(type, 'error', { |
|
|
|
|
|
error: error.message, |
|
|
|
|
|
retry: true |
|
|
|
|
|
}); |
|
|
|
|
|
return null; |
|
|
|
|
|
} |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
await Promise.allSettled(promises); |
|
|
|
|
|
|
|
|
await Promise.allSettled(promises) |
|
|
} finally { |
|
|
} finally { |
|
|
this.isSaving = false; |
|
|
|
|
|
|
|
|
this.isSaving = false |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -251,10 +258,10 @@ class AutoSaveManager { |
|
|
status, |
|
|
status, |
|
|
...data, |
|
|
...data, |
|
|
lastUpdated: Date.now() |
|
|
lastUpdated: Date.now() |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
if (status === 'saved' && data.timestamp) { |
|
|
if (status === 'saved' && data.timestamp) { |
|
|
this.states[type].lastSaved = data.timestamp; |
|
|
|
|
|
|
|
|
this.states[type].lastSaved = data.timestamp |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -263,7 +270,7 @@ class AutoSaveManager { |
|
|
* @returns {object} 状态对象 |
|
|
* @returns {object} 状态对象 |
|
|
*/ |
|
|
*/ |
|
|
getAllStates() { |
|
|
getAllStates() { |
|
|
return { ...this.states }; |
|
|
|
|
|
|
|
|
return { ...this.states } |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
@ -273,9 +280,9 @@ class AutoSaveManager { |
|
|
*/ |
|
|
*/ |
|
|
async getHistory(storageType) { |
|
|
async getHistory(storageType) { |
|
|
if (this.storages[storageType]) { |
|
|
if (this.storages[storageType]) { |
|
|
return await this.storages[storageType].getHistory(); |
|
|
|
|
|
|
|
|
return await this.storages[storageType].getHistory() |
|
|
} |
|
|
} |
|
|
return { success: false, error: '存储类型不存在' }; |
|
|
|
|
|
|
|
|
return { success: false, error: '存储类型不存在' } |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
@ -285,12 +292,12 @@ class AutoSaveManager { |
|
|
*/ |
|
|
*/ |
|
|
async restoreFromHistory(storageType, versionData) { |
|
|
async restoreFromHistory(storageType, versionData) { |
|
|
if (this.storages[storageType]) { |
|
|
if (this.storages[storageType]) { |
|
|
const content = versionData.content || versionData; |
|
|
|
|
|
await this.doSave(content); |
|
|
|
|
|
this.currentContent = content; |
|
|
|
|
|
return { success: true, content }; |
|
|
|
|
|
|
|
|
const content = versionData.content || versionData |
|
|
|
|
|
await this.doSave(content) |
|
|
|
|
|
this.currentContent = content |
|
|
|
|
|
return { success: true, content } |
|
|
} |
|
|
} |
|
|
return { success: false, error: '存储类型不存在' }; |
|
|
|
|
|
|
|
|
return { success: false, error: '存储类型不存在' } |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
@ -300,9 +307,9 @@ class AutoSaveManager { |
|
|
window.addEventListener('beforeunload', (event) => { |
|
|
window.addEventListener('beforeunload', (event) => { |
|
|
if (this.currentContent !== this.lastSavedContent) { |
|
|
if (this.currentContent !== this.lastSavedContent) { |
|
|
// 同步保存(不防抖)
|
|
|
// 同步保存(不防抖)
|
|
|
this.doSaveSync(this.currentContent); |
|
|
|
|
|
|
|
|
this.doSaveSync(this.currentContent) |
|
|
} |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
}) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
@ -311,15 +318,15 @@ class AutoSaveManager { |
|
|
*/ |
|
|
*/ |
|
|
async doSaveSync(content) { |
|
|
async doSaveSync(content) { |
|
|
// 只保存到本地存储,确保数据不丢失
|
|
|
// 只保存到本地存储,确保数据不丢失
|
|
|
const localStorages = ['memory', 'localStorage', 'sessionStorage']; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const localStorages = ['memory', 'localStorage', 'sessionStorage'] |
|
|
|
|
|
|
|
|
localStorages.forEach(async (type) => { |
|
|
localStorages.forEach(async (type) => { |
|
|
try { |
|
|
try { |
|
|
await this.storages[type].save(content); |
|
|
|
|
|
|
|
|
await this.storages[type].save(content) |
|
|
} catch (error) { |
|
|
} catch (error) { |
|
|
console.error(`页面关闭前保存到${type}失败:`, error); |
|
|
|
|
|
|
|
|
console.error(`页面关闭前保存到${type}失败:`, error) |
|
|
} |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
}) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
@ -330,23 +337,23 @@ class AutoSaveManager { |
|
|
document.addEventListener('visibilitychange', () => { |
|
|
document.addEventListener('visibilitychange', () => { |
|
|
if (document.visibilityState === 'visible') { |
|
|
if (document.visibilityState === 'visible') { |
|
|
// 页面恢复显示,检查是否需要同步
|
|
|
// 页面恢复显示,检查是否需要同步
|
|
|
this.checkAndSync(); |
|
|
|
|
|
|
|
|
this.checkAndSync() |
|
|
} |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
// 监听页面冻结和恢复
|
|
|
// 监听页面冻结和恢复
|
|
|
document.addEventListener('freeze', () => { |
|
|
document.addEventListener('freeze', () => { |
|
|
localStorage.setItem(`pageFrozen_${this.key}`, Date.now()); |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
localStorage.setItem(`pageFrozen_${this.key}`, Date.now()) |
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
document.addEventListener('resume', () => { |
|
|
document.addEventListener('resume', () => { |
|
|
const frozenTime = localStorage.getItem(`pageFrozen_${this.key}`); |
|
|
|
|
|
|
|
|
const frozenTime = localStorage.getItem(`pageFrozen_${this.key}`) |
|
|
if (frozenTime && Date.now() - frozenTime > 5000) { |
|
|
if (frozenTime && Date.now() - frozenTime > 5000) { |
|
|
// 页面被冻结超过5秒,可能是崩溃恢复
|
|
|
// 页面被冻结超过5秒,可能是崩溃恢复
|
|
|
this.showCrashNotification(); |
|
|
|
|
|
|
|
|
this.showCrashNotification() |
|
|
} |
|
|
} |
|
|
localStorage.removeItem(`pageFrozen_${this.key}`); |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
localStorage.removeItem(`pageFrozen_${this.key}`) |
|
|
|
|
|
}) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
@ -354,11 +361,11 @@ class AutoSaveManager { |
|
|
*/ |
|
|
*/ |
|
|
async checkAndSync() { |
|
|
async checkAndSync() { |
|
|
// 重新加载所有数据并合并
|
|
|
// 重新加载所有数据并合并
|
|
|
await this.loadFromAllStorages(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
await this.loadFromAllStorages() |
|
|
|
|
|
|
|
|
// 如果有服务器存储,尝试同步离线队列
|
|
|
// 如果有服务器存储,尝试同步离线队列
|
|
|
if (this.storages.server) { |
|
|
if (this.storages.server) { |
|
|
this.storages.server.processQueue(); |
|
|
|
|
|
|
|
|
this.storages.server.processQueue() |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -366,7 +373,7 @@ class AutoSaveManager { |
|
|
* 显示崩溃通知 |
|
|
* 显示崩溃通知 |
|
|
*/ |
|
|
*/ |
|
|
showCrashNotification() { |
|
|
showCrashNotification() { |
|
|
const notification = document.createElement('div'); |
|
|
|
|
|
|
|
|
const notification = document.createElement('div') |
|
|
notification.style.cssText = `
|
|
|
notification.style.cssText = `
|
|
|
position: fixed; |
|
|
position: fixed; |
|
|
top: 20px; |
|
|
top: 20px; |
|
|
@ -377,16 +384,16 @@ class AutoSaveManager { |
|
|
border-radius: 5px; |
|
|
border-radius: 5px; |
|
|
z-index: 10000; |
|
|
z-index: 10000; |
|
|
box-shadow: 0 2px 10px rgba(0,0,0,0.2); |
|
|
box-shadow: 0 2px 10px rgba(0,0,0,0.2); |
|
|
`;
|
|
|
|
|
|
|
|
|
`
|
|
|
notification.innerHTML = `
|
|
|
notification.innerHTML = `
|
|
|
<strong>页面恢复</strong><br> |
|
|
<strong>页面恢复</strong><br> |
|
|
检测到页面可能发生了异常,已自动恢复您的数据。 |
|
|
检测到页面可能发生了异常,已自动恢复您的数据。 |
|
|
`;
|
|
|
|
|
|
document.body.appendChild(notification); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
`
|
|
|
|
|
|
document.body.appendChild(notification) |
|
|
|
|
|
|
|
|
setTimeout(() => { |
|
|
setTimeout(() => { |
|
|
notification.remove(); |
|
|
|
|
|
}, 5000); |
|
|
|
|
|
|
|
|
notification.remove() |
|
|
|
|
|
}, 5000) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
@ -394,20 +401,22 @@ class AutoSaveManager { |
|
|
* @returns {Promise<Array>} 备份文件列表 |
|
|
* @returns {Promise<Array>} 备份文件列表 |
|
|
*/ |
|
|
*/ |
|
|
async getBackupFiles() { |
|
|
async getBackupFiles() { |
|
|
const backupPromises = Object.entries(this.storages).map(async ([type, storage]) => { |
|
|
|
|
|
if (typeof storage.getBackupFiles === 'function') { |
|
|
|
|
|
try { |
|
|
|
|
|
const result = await storage.getBackupFiles(); |
|
|
|
|
|
return { type, ...result }; |
|
|
|
|
|
} catch (error) { |
|
|
|
|
|
return { type, success: false, error: error.message }; |
|
|
|
|
|
|
|
|
const backupPromises = Object.entries(this.storages).map( |
|
|
|
|
|
async ([type, storage]) => { |
|
|
|
|
|
if (typeof storage.getBackupFiles === 'function') { |
|
|
|
|
|
try { |
|
|
|
|
|
const result = await storage.getBackupFiles() |
|
|
|
|
|
return { type, ...result } |
|
|
|
|
|
} catch (error) { |
|
|
|
|
|
return { type, success: false, error: error.message } |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
return { type, success: false, error: '不支持备份管理' } |
|
|
} |
|
|
} |
|
|
return { type, success: false, error: '不支持备份管理' }; |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
const results = await Promise.all(backupPromises); |
|
|
|
|
|
return results; |
|
|
|
|
|
|
|
|
const results = await Promise.all(backupPromises) |
|
|
|
|
|
return results |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
@ -415,10 +424,10 @@ class AutoSaveManager { |
|
|
*/ |
|
|
*/ |
|
|
async saveImmediately(currentContent) { |
|
|
async saveImmediately(currentContent) { |
|
|
if (this.saveTimer) { |
|
|
if (this.saveTimer) { |
|
|
clearTimeout(this.saveTimer); |
|
|
|
|
|
|
|
|
clearTimeout(this.saveTimer) |
|
|
} |
|
|
} |
|
|
await this.doSave(currentContent); |
|
|
|
|
|
|
|
|
await this.doSave(currentContent) |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
export default AutoSaveManager; |
|
|
|
|
|
|
|
|
export default AutoSaveManager |