mirror of
https://github.com/Cp0204/quark-auto-save.git
synced 2026-01-12 07:10:44 +08:00
引入全局 SSE 单例,统一 onopen/onerror,任务列表与日历共用连接,切页不丢事件
- 进入日历页即时 tick 与本地补拉,消除 60s 等待 - 校正可见性回退判断为 appSSE;清理重复 SSE 回调设置 - 保留原轮询兜底与可见性恢复逻辑,确保 SSE 异常时行为一致
This commit is contained in:
parent
2d944600e6
commit
080c73fc07
@ -3237,6 +3237,15 @@
|
||||
// 任务列表自动检测更新相关
|
||||
tasklistAutoWatchTimer: null,
|
||||
tasklistLatestFilesSignature: '',
|
||||
// 全局 SSE 单例与监听标志
|
||||
appSSE: null,
|
||||
appSSEInitialized: false,
|
||||
calendarSSEListenerAdded: false,
|
||||
tasklistSSEListenerAdded: false,
|
||||
// 存放已绑定的事件处理器,便于避免重复绑定
|
||||
onCalendarChangedHandler: null,
|
||||
onTasklistChangedHandler: null,
|
||||
// 兼容旧字段(不再使用独立 SSE 实例)
|
||||
tasklistSSE: null,
|
||||
calendarSSE: null,
|
||||
// 创建任务相关数据
|
||||
@ -3576,6 +3585,8 @@
|
||||
activeTab(newValue, oldValue) {
|
||||
// 如果切换到任务列表页面,则刷新任务最新信息和元数据
|
||||
if (newValue === 'tasklist') {
|
||||
// 确保全局 SSE 已建立
|
||||
try { this.ensureGlobalSSE(); } catch (e) {}
|
||||
this.loadTaskLatestInfo();
|
||||
this.loadTasklistMetadata();
|
||||
// 启动任务列表的后台监听
|
||||
@ -3586,6 +3597,8 @@
|
||||
}
|
||||
// 切换到追剧日历:立刻检查一次并启动后台监听;离开则停止监听
|
||||
if (newValue === 'calendar') {
|
||||
// 确保全局 SSE 已建立
|
||||
try { this.ensureGlobalSSE(); } catch (e) {}
|
||||
// 立即检查一次(若已初始化过监听,直接调用tick引用)
|
||||
// 先本地读取一次,立刻应用“已转存”状态(不依赖轮询)
|
||||
try { this.loadCalendarEpisodesLocal && this.loadCalendarEpisodesLocal(); } catch (e) {}
|
||||
@ -3633,6 +3646,9 @@
|
||||
this.fetchUserInfo(); // 获取用户信息
|
||||
this.fetchAccountsDetail(); // 获取账号详细信息
|
||||
|
||||
// 应用级别:在挂载时确保全局 SSE 建立一次
|
||||
try { this.ensureGlobalSSE(); } catch (e) {}
|
||||
|
||||
// 迁移旧的localStorage数据到新格式(为每个账号单独存储目录)
|
||||
this.migrateFileManagerFolderData();
|
||||
|
||||
@ -3822,6 +3838,80 @@
|
||||
this.stopCalendarAutoWatch();
|
||||
},
|
||||
methods: {
|
||||
// 启动任务列表轮询兜底(仅在未运行时启动)
|
||||
startTasklistPollingFallback() {
|
||||
try {
|
||||
if (!this.tasklistAutoWatchTimer) {
|
||||
this.tasklistAutoWatchTimer = setInterval(async () => {
|
||||
try {
|
||||
const res = await axios.get('/task_latest_info');
|
||||
if (res.data && res.data.success) {
|
||||
const latestFiles = res.data.data.latest_files || {};
|
||||
const sig = this.calcLatestFilesSignature(latestFiles);
|
||||
if (sig && sig !== this.tasklistLatestFilesSignature) {
|
||||
// 更新签名,触发热更新
|
||||
this.tasklistLatestFilesSignature = sig;
|
||||
this.taskLatestFiles = latestFiles;
|
||||
// 重新加载任务元数据,确保海报和元数据能热更新
|
||||
await this.loadTasklistMetadata();
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('任务列表后台监听检查失败:', e);
|
||||
}
|
||||
}, 60000);
|
||||
}
|
||||
} catch (e) {}
|
||||
},
|
||||
// 确保全局 SSE 单例存在(仅建立一次)
|
||||
ensureGlobalSSE() {
|
||||
try {
|
||||
if (this.appSSEInitialized && this.appSSE) return;
|
||||
if (!this.appSSE) {
|
||||
this.appSSE = new EventSource('/api/calendar/stream');
|
||||
}
|
||||
this.appSSEInitialized = true;
|
||||
// 统一 onopen:SSE 成功后停止两侧轮询
|
||||
this.appSSE.onopen = () => {
|
||||
try {
|
||||
if (this.calendarAutoWatchTimer) {
|
||||
clearInterval(this.calendarAutoWatchTimer);
|
||||
this.calendarAutoWatchTimer = null;
|
||||
}
|
||||
} catch (e) {}
|
||||
try {
|
||||
if (this.tasklistAutoWatchTimer) {
|
||||
clearInterval(this.tasklistAutoWatchTimer);
|
||||
this.tasklistAutoWatchTimer = null;
|
||||
}
|
||||
} catch (e) {}
|
||||
};
|
||||
// 统一 onerror:关闭SSE并回退双侧轮询
|
||||
this.appSSE.onerror = () => {
|
||||
try { this.appSSE.close(); } catch (e) {}
|
||||
this.appSSE = null;
|
||||
this.appSSEInitialized = false;
|
||||
// 日历回退:若没有轮询定时器,则恢复轮询并立即执行一次
|
||||
try {
|
||||
if (!this.calendarAutoWatchTimer && this.calendarAutoWatchTickRef) {
|
||||
const baseIntervalMs = this.calendar && this.calendar.manageMode ? 5 * 1000 : 60 * 1000;
|
||||
this.calendarAutoWatchTimer = setInterval(this.calendarAutoWatchTickRef, baseIntervalMs);
|
||||
this.calendarAutoWatchTickRef();
|
||||
}
|
||||
} catch (e) {}
|
||||
// 任务列表回退:若没有轮询定时器,则恢复轮询
|
||||
try {
|
||||
if (!this.tasklistAutoWatchTimer) {
|
||||
this.startTasklistPollingFallback();
|
||||
}
|
||||
} catch (e) {}
|
||||
};
|
||||
// ping 心跳占位
|
||||
try { this.appSSE.addEventListener('ping', () => {}); } catch (e) {}
|
||||
} catch (e) {
|
||||
// 忽略失败,后续可回退轮询
|
||||
}
|
||||
},
|
||||
// 任务列表海报标题(悬停:#编号 任务名称 · 状态)
|
||||
getTasklistPosterTitle(task, index) {
|
||||
try {
|
||||
@ -5612,7 +5702,7 @@
|
||||
this.calendarAutoWatchTimer = null;
|
||||
}
|
||||
} else {
|
||||
if (!this.calendarSSE && !this.calendarAutoWatchTimer && this.calendarAutoWatchTickRef) {
|
||||
if (!this.appSSE && !this.calendarAutoWatchTimer && this.calendarAutoWatchTickRef) {
|
||||
const baseIntervalMs = this.calendar && this.calendar.manageMode ? 5 * 1000 : 60 * 1000;
|
||||
this.calendarAutoWatchTimer = setInterval(this.calendarAutoWatchTickRef, baseIntervalMs);
|
||||
this.calendarAutoWatchTickRef();
|
||||
@ -5624,19 +5714,10 @@
|
||||
window.addEventListener('focus', this.calendarAutoWatchFocusHandler);
|
||||
document.addEventListener('visibilitychange', this.calendarAutoWatchVisibilityHandler);
|
||||
|
||||
// 建立 SSE 连接,实时感知日历数据库变化(成功建立后停用轮询,失败时回退轮询)
|
||||
// 建立/复用全局 SSE 连接(成功建立后停用轮询,失败时回退轮询)
|
||||
try {
|
||||
if (!this.calendarSSE) {
|
||||
this.calendarSSE = new EventSource('/api/calendar/stream');
|
||||
// SSE 打开后,停止轮询
|
||||
this.calendarSSE.onopen = () => {
|
||||
try {
|
||||
if (this.calendarAutoWatchTimer) {
|
||||
clearInterval(this.calendarAutoWatchTimer);
|
||||
this.calendarAutoWatchTimer = null;
|
||||
}
|
||||
} catch (e) {}
|
||||
};
|
||||
this.ensureGlobalSSE();
|
||||
if (this.appSSE && !this.calendarSSEListenerAdded) {
|
||||
const onChanged = async (ev) => {
|
||||
try {
|
||||
// 解析变更原因(后端通过 SSE data 传递)
|
||||
@ -5717,21 +5798,10 @@
|
||||
try { await this.loadTodayUpdatesLocal(); } catch (e) {}
|
||||
} catch (e) {}
|
||||
};
|
||||
this.calendarSSE.addEventListener('calendar_changed', onChanged);
|
||||
// 初次连接会收到一次 ping,不做处理即可
|
||||
this.calendarSSE.addEventListener('ping', () => {});
|
||||
this.calendarSSE.onerror = () => {
|
||||
try { this.calendarSSE.close(); } catch (e) {}
|
||||
this.calendarSSE = null;
|
||||
// 回退:若没有轮询定时器,则恢复轮询
|
||||
try {
|
||||
if (!this.calendarAutoWatchTimer && this.calendarAutoWatchTickRef) {
|
||||
const baseIntervalMs = this.calendar && this.calendar.manageMode ? 5 * 1000 : 60 * 1000;
|
||||
this.calendarAutoWatchTimer = setInterval(this.calendarAutoWatchTickRef, baseIntervalMs);
|
||||
this.calendarAutoWatchTickRef();
|
||||
}
|
||||
} catch (e) {}
|
||||
};
|
||||
this.onCalendarChangedHandler = onChanged;
|
||||
this.appSSE.addEventListener('calendar_changed', onChanged);
|
||||
this.calendarSSEListenerAdded = true;
|
||||
// onopen/onerror 已集中在 ensureGlobalSSE,无需重复设置
|
||||
}
|
||||
} catch (e) {
|
||||
// 忽略 SSE 失败,继续使用轮询
|
||||
@ -5756,10 +5826,7 @@
|
||||
document.removeEventListener('visibilitychange', this.calendarAutoWatchVisibilityHandler);
|
||||
this.calendarAutoWatchVisibilityHandler = null;
|
||||
}
|
||||
if (this.calendarSSE) {
|
||||
try { this.calendarSSE.close(); } catch (e) {}
|
||||
this.calendarSSE = null;
|
||||
}
|
||||
// 不再关闭全局 SSE;仅移除本地监听(如有需要)
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
@ -9351,17 +9418,9 @@
|
||||
|
||||
// 建立 SSE 连接,实时感知任务列表变化(成功建立后停用轮询,失败时回退轮询)
|
||||
try {
|
||||
if (!this.tasklistSSE) {
|
||||
this.tasklistSSE = new EventSource('/api/calendar/stream');
|
||||
// SSE 打开后,停止轮询
|
||||
this.tasklistSSE.onopen = () => {
|
||||
try {
|
||||
if (this.tasklistAutoWatchTimer) {
|
||||
clearInterval(this.tasklistAutoWatchTimer);
|
||||
this.tasklistAutoWatchTimer = null;
|
||||
}
|
||||
} catch (e) {}
|
||||
};
|
||||
// 使用全局 SSE 单例
|
||||
this.ensureGlobalSSE();
|
||||
if (this.appSSE && !this.tasklistSSEListenerAdded) {
|
||||
const onTasklistChanged = async (ev) => {
|
||||
try {
|
||||
// 解析变更原因(后端通过 SSE data 传递)
|
||||
@ -9406,37 +9465,10 @@
|
||||
}
|
||||
} catch (e) {}
|
||||
};
|
||||
this.tasklistSSE.addEventListener('calendar_changed', onTasklistChanged);
|
||||
// 初次连接会收到一次 ping,不做处理即可
|
||||
this.tasklistSSE.addEventListener('ping', () => {});
|
||||
this.tasklistSSE.onerror = () => {
|
||||
try { this.tasklistSSE.close(); } catch (e) {}
|
||||
this.tasklistSSE = null;
|
||||
// 回退:若没有轮询定时器,则恢复轮询
|
||||
try {
|
||||
if (!this.tasklistAutoWatchTimer) {
|
||||
this.tasklistAutoWatchTimer = setInterval(async () => {
|
||||
try {
|
||||
const res = await axios.get('/task_latest_info');
|
||||
if (res.data && res.data.success) {
|
||||
const latestFiles = res.data.data.latest_files || {};
|
||||
const sig = this.calcLatestFilesSignature(latestFiles);
|
||||
if (sig && sig !== this.tasklistLatestFilesSignature) {
|
||||
// 更新签名,触发热更新
|
||||
this.tasklistLatestFilesSignature = sig;
|
||||
this.taskLatestFiles = latestFiles;
|
||||
|
||||
// 重新加载任务元数据,确保海报和元数据能热更新
|
||||
await this.loadTasklistMetadata();
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('任务列表后台监听检查失败:', e);
|
||||
}
|
||||
}, 60000);
|
||||
}
|
||||
} catch (e) {}
|
||||
};
|
||||
this.onTasklistChangedHandler = onTasklistChanged;
|
||||
this.appSSE.addEventListener('calendar_changed', onTasklistChanged);
|
||||
this.tasklistSSEListenerAdded = true;
|
||||
// onopen/onerror 已集中在 ensureGlobalSSE,无需重复设置
|
||||
}
|
||||
} catch (e) {
|
||||
// 忽略 SSE 失败,继续使用轮询
|
||||
|
||||
Loading…
Reference in New Issue
Block a user