fix: 调度中心ws订阅的相关方法在初始化时暴露

This commit is contained in:
DLmaster361
2025-09-24 21:43:54 +08:00
parent bd58a512c9
commit 34df37c040
5 changed files with 257 additions and 24 deletions

View File

@@ -0,0 +1,136 @@
// schedulerHandlers.ts
// 提供在调度中心 UI 未加载前也能工作的消息处理实现。
// 核心策略:将重要事件保存到 localStorage或内存队列并暴露注册点供 UI 在挂载时接收并回放。
// no types needed here to avoid circular/unused imports
const PENDING_TABS_KEY = 'scheduler-pending-tabs'
const PENDING_COUNTDOWN_KEY = 'scheduler-pending-countdown'
const POWER_ACTION_KEY = 'scheduler-power-action'
type UIHooks = {
onNewTab?: (tab: any) => void
onCountdown?: (data: any) => void
}
let uiHooks: UIHooks = {}
export function registerSchedulerUI(hooks: UIHooks) {
uiHooks = { ...uiHooks, ...hooks }
}
// helper: push pending tab id to localStorage
function pushPendingTab(taskId: string) {
try {
const raw = localStorage.getItem(PENDING_TABS_KEY)
const arr = raw ? JSON.parse(raw) : []
if (!arr.includes(taskId)) {
arr.push(taskId)
localStorage.setItem(PENDING_TABS_KEY, JSON.stringify(arr))
}
} catch (e) {
// ignore
}
}
function popPendingTabs(): string[] {
try {
const raw = localStorage.getItem(PENDING_TABS_KEY)
if (!raw) return []
localStorage.removeItem(PENDING_TABS_KEY)
return JSON.parse(raw)
} catch (e) {
return []
}
}
function storePendingCountdown(data: any) {
try {
localStorage.setItem(PENDING_COUNTDOWN_KEY, JSON.stringify(data))
} catch (e) {
// ignore
}
}
function consumePendingCountdownAndClear(): any | null {
try {
const raw = localStorage.getItem(PENDING_COUNTDOWN_KEY)
if (!raw) return null
localStorage.removeItem(PENDING_COUNTDOWN_KEY)
return JSON.parse(raw)
} catch (e) {
return null
}
}
function savePowerAction(value: string) {
try {
localStorage.setItem(POWER_ACTION_KEY, value)
} catch (e) {
// ignore
}
}
// 导出:供 useWebSocket 在模块加载时就能使用的处理函数
export function handleTaskManagerMessage(wsMessage: any) {
if (!wsMessage || typeof wsMessage !== 'object') return
const { type, data } = wsMessage
try {
if (type === 'Signal' && data && data.newTask) {
const taskId = String(data.newTask)
// 将任务 ID 写入 pending 队列UI 在挂载时会回放
pushPendingTab(taskId)
// 如果 UI 已注册回调,则立即通知
if (uiHooks.onNewTab) {
try {
uiHooks.onNewTab({ title: `调度台自动-${taskId}`, websocketId: taskId })
} catch (e) {
console.warn('[SchedulerHandlers] onNewTab handler error:', e)
}
}
}
} catch (e) {
console.warn('[SchedulerHandlers] handleTaskManagerMessage error:', e)
}
}
export function handleMainMessage(wsMessage: any) {
if (!wsMessage || typeof wsMessage !== 'object') return
const { type, data } = wsMessage
try {
if (type === 'Message' && data && data.type === 'Countdown') {
// 存储倒计时消息,供 UI 回放
storePendingCountdown(data)
if (uiHooks.onCountdown) {
try {
uiHooks.onCountdown(data)
} catch (e) {
console.warn('[SchedulerHandlers] onCountdown handler error:', e)
}
}
} else if (type === 'Update' && data && data.PowerSign !== undefined) {
// 保存电源操作显示值(供 UI 加载时读取)
savePowerAction(String(data.PowerSign))
}
} catch (e) {
console.warn('[SchedulerHandlers] handleMainMessage error:', e)
}
}
// UI 在挂载时调用,消费并回放 pending 数据
export function consumePendingTabIds(): string[] {
return popPendingTabs()
}
export function consumePendingCountdown(): any | null {
return consumePendingCountdownAndClear()
}
export default {
handleTaskManagerMessage,
handleMainMessage,
registerSchedulerUI,
consumePendingTabIds,
consumePendingCountdown,
}

View File

@@ -3,7 +3,8 @@ import { message, Modal, notification } from 'ant-design-vue'
import { Service } from '@/api/services/Service'
import { TaskCreateIn } from '@/api/models/TaskCreateIn'
import { PowerIn } from '@/api/models/PowerIn'
import { useWebSocket, setTaskManagerHandler, setMainMessageHandler } from '@/composables/useWebSocket'
import { useWebSocket, ExternalWSHandlers } from '@/composables/useWebSocket'
import schedulerHandlers from './schedulerHandlers'
import type { ComboBoxItem } from '@/api/models/ComboBoxItem'
import type { QueueItem, Script } from './schedulerConstants'
import {
@@ -184,7 +185,7 @@ export function useSchedulerLogic() {
title: options?.title || `调度台${tabCounter}`,
closable: true,
status: validStatus,
selectedTaskId: null,
selectedTaskId: options?.websocketId || null,
selectedMode: TaskCreateIn.mode.AutoMode,
websocketId: options?.websocketId || null,
taskQueue: [],
@@ -767,10 +768,61 @@ export function useSchedulerLogic() {
// 初始化函数
const initialize = () => {
// 设置全局WebSocket的消息处理函数
setTaskManagerHandler(handleTaskManagerMessage)
setMainMessageHandler(handleMainMessage)
// 通过 import 的 ExternalWSHandlers 直接注册处理函数,保证导入方能够永久引用并调用
ExternalWSHandlers.taskManagerMessage = handleTaskManagerMessage
ExternalWSHandlers.mainMessage = handleMainMessage
console.log('[Scheduler] 已设置全局WebSocket消息处理函数')
// 注册 UI hooks 到 schedulerHandlers使其能在 schedulerHandlers 检测到 pending 时回放到当前 UI
try {
schedulerHandlers.registerSchedulerUI({
onNewTab: (tab) => {
try {
// 创建并订阅新调度台
const newTab = addSchedulerTab({ title: tab.title, status: '运行', websocketId: tab.websocketId })
subscribeToTask(newTab)
saveTabsToStorage(schedulerTabs.value)
} catch (e) {
console.warn('[Scheduler] registerSchedulerUI onNewTab error:', e)
}
},
onCountdown: (data) => {
try {
// 直接启动前端倒计时
startPowerCountdown(data)
} catch (e) {
console.warn('[Scheduler] registerSchedulerUI onCountdown error:', e)
}
}
})
// 回放 pending tabs如果有的话
const pending = schedulerHandlers.consumePendingTabIds()
if (pending && pending.length > 0) {
pending.forEach((taskId: string) => {
try {
const newTab = addSchedulerTab({ title: `调度台${taskId}`, status: '运行', websocketId: taskId })
subscribeToTask(newTab)
} catch (e) {
console.warn('[Scheduler] replay pending tab error:', e)
}
})
saveTabsToStorage(schedulerTabs.value)
}
// 回放 pending countdown如果有的话
const pendingCountdown = schedulerHandlers.consumePendingCountdown()
if (pendingCountdown) {
try {
startPowerCountdown(pendingCountdown)
} catch (e) {
console.warn('[Scheduler] replay pending countdown error:', e)
}
}
} catch (e) {
console.warn('[Scheduler] schedulerHandlers registration failed:', e)
}
// 新增:为已有的“运行中”标签恢复 WebSocket 订阅,防止路由切换返回后不再更新
try {
schedulerTabs.value.forEach(tab => {
@@ -811,8 +863,7 @@ export function useSchedulerLogic() {
}
// 清理全局WebSocket的消息处理函数
setTaskManagerHandler(() => {})
setMainMessageHandler(() => {})
// 不再清理或重置导出的处理函数,保持使用者注册的处理逻辑永久有效
schedulerTabs.value.forEach(tab => {
if (tab.websocketId) {