Initial clean project import
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
import {AppRuntime} from "../env";
|
||||
import {ipcMain, WebContents} from "electron";
|
||||
import {StrUtil} from "../../lib/util";
|
||||
// 导入 taskEventSystem 以注册 IPC handlers
|
||||
import './taskEventSystem';
|
||||
|
||||
const init = async () => {
|
||||
};
|
||||
|
||||
type NameType = "main" | string | WebContents;
|
||||
type EventType = "APP_READY" | "CALL_PAGE" | "CHANNEL" | "BROADCAST";
|
||||
type BroadcastType =
|
||||
| "ConfigChange"
|
||||
| "ConfigEnvChange"
|
||||
| "UserChange"
|
||||
| "DarkModeChange"
|
||||
| "HotkeyWatch"
|
||||
| "Notice"
|
||||
| "MonitorEvent"
|
||||
| "TaskStatusChange"
|
||||
| "TaskProgressChange"
|
||||
| "TaskComplete"
|
||||
| "TaskFailed";
|
||||
|
||||
const broadcast = (
|
||||
type: BroadcastType,
|
||||
data: any,
|
||||
option?: {
|
||||
limit?: boolean;
|
||||
scopes?: string[];
|
||||
pages?: string[];
|
||||
}
|
||||
) => {
|
||||
data = data || {};
|
||||
option = Object.assign(
|
||||
{
|
||||
limit: false,
|
||||
scopes: [],
|
||||
pages: [],
|
||||
},
|
||||
option
|
||||
);
|
||||
if (option.pages.length > 0) {
|
||||
for (const p of option.pages) {
|
||||
send(p, "BROADCAST", {type, data});
|
||||
}
|
||||
} else {
|
||||
if (!option.limit || option.scopes.includes("main")) {
|
||||
send("main", "BROADCAST", {type, data});
|
||||
}
|
||||
if (!option.limit || option.scopes.includes("pages")) {
|
||||
for (let name in AppRuntime.windows) {
|
||||
send(name, "BROADCAST", {type, data});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const sendRaw = (webContents: any, type: EventType, data: any = {}, id?: string): boolean => {
|
||||
id = id || StrUtil.randomString(32);
|
||||
const payload = {id, type, data};
|
||||
webContents.send("MAIN_PROCESS_MESSAGE", payload);
|
||||
return true;
|
||||
};
|
||||
|
||||
const send = (name: NameType, type: EventType, data: any = {}, id?: string): boolean => {
|
||||
id = id || StrUtil.randomString(32);
|
||||
const payload = {id, type, data};
|
||||
if (typeof name !== 'string') {
|
||||
(name as WebContents).send("MAIN_PROCESS_MESSAGE", payload);
|
||||
return true;
|
||||
}
|
||||
if (name === "main") {
|
||||
if (!AppRuntime.mainWindow) {
|
||||
return false;
|
||||
}
|
||||
// console.log('send', payload)
|
||||
AppRuntime.mainWindow?.webContents.send("MAIN_PROCESS_MESSAGE", payload);
|
||||
} else {
|
||||
if (!AppRuntime.windows[name]) {
|
||||
return false;
|
||||
}
|
||||
AppRuntime.windows[name]?.webContents.send("MAIN_PROCESS_MESSAGE", payload);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
ipcMain.handle("event:send", async (_, name: NameType, type: EventType, data: any) => {
|
||||
send(name, type, data);
|
||||
});
|
||||
|
||||
const callPage = async (
|
||||
name: NameType,
|
||||
type: string,
|
||||
data: any,
|
||||
option?: {
|
||||
waitReadyTimeout?: number;
|
||||
timeout?: number;
|
||||
}
|
||||
): Promise<{
|
||||
code: number;
|
||||
msg: string;
|
||||
data?: any;
|
||||
}> => {
|
||||
option = Object.assign(
|
||||
{
|
||||
waitReadyTimeout: 10 * 1000,
|
||||
timeout: 60 * 1000,
|
||||
},
|
||||
option
|
||||
);
|
||||
return new Promise((resolve, reject) => {
|
||||
const id = StrUtil.randomString(32);
|
||||
const timer = setTimeout(() => {
|
||||
ipcMain.removeListener(listenerKey, listener);
|
||||
resolve({code: -1, msg: "timeout"});
|
||||
}, option.timeout);
|
||||
const listener = (_, result) => {
|
||||
clearTimeout(timer);
|
||||
resolve(result);
|
||||
return true;
|
||||
};
|
||||
const listenerKey = "event:callPage:" + id;
|
||||
ipcMain.once(listenerKey, listener);
|
||||
const payload = {
|
||||
type,
|
||||
data,
|
||||
option: {
|
||||
waitReadyTimeout: option.waitReadyTimeout,
|
||||
},
|
||||
};
|
||||
if (!send(name, "CALL_PAGE", payload, id)) {
|
||||
clearTimeout(timer);
|
||||
ipcMain.removeListener(listenerKey, listener);
|
||||
resolve({code: -1, msg: "send failed"});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
ipcMain.handle(
|
||||
"event:callPage",
|
||||
async (
|
||||
_,
|
||||
name: string,
|
||||
type: string,
|
||||
data: any,
|
||||
option?: {
|
||||
timeout?: number;
|
||||
}
|
||||
) => {
|
||||
return callPage(name, type, data, option);
|
||||
}
|
||||
);
|
||||
|
||||
let onChannelIsListen = false;
|
||||
let channelOnCallback = {};
|
||||
|
||||
const sendChannel = (channel: string, data: any) => {
|
||||
send("main", "CHANNEL", {channel, data});
|
||||
};
|
||||
|
||||
const onChannel = (channel: string, callback: (data: any) => void) => {
|
||||
if (!channelOnCallback[channel]) {
|
||||
channelOnCallback[channel] = [];
|
||||
}
|
||||
channelOnCallback[channel].push(callback);
|
||||
if (!onChannelIsListen) {
|
||||
onChannelIsListen = true;
|
||||
ipcMain.handle("event:channelSend", (event, channel_, data) => {
|
||||
if (channelOnCallback[channel_]) {
|
||||
channelOnCallback[channel_].forEach((callback: (data: any) => void) => {
|
||||
callback(data);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const offChannel = (channel: string, callback: (data: any) => void) => {
|
||||
if (channelOnCallback[channel]) {
|
||||
channelOnCallback[channel] = channelOnCallback[channel].filter((item: (data: any) => void) => {
|
||||
return item !== callback;
|
||||
});
|
||||
}
|
||||
if (channelOnCallback[channel].length === 0) {
|
||||
delete channelOnCallback[channel];
|
||||
}
|
||||
};
|
||||
|
||||
export default {
|
||||
init,
|
||||
send,
|
||||
};
|
||||
|
||||
export const Events = {
|
||||
broadcast,
|
||||
send,
|
||||
sendRaw,
|
||||
sendChannel,
|
||||
callPage,
|
||||
onChannel,
|
||||
offChannel,
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
import { ipcRenderer } from "electron";
|
||||
|
||||
const init = () => {};
|
||||
|
||||
const send = (name: string, type: string, data: any = {}) => {
|
||||
return ipcRenderer.invoke("event:send", name, type, data).then();
|
||||
};
|
||||
|
||||
const callPage = async (name: string, type: string, data: any, option: any) => {
|
||||
return ipcRenderer.invoke("event:callPage", name, type, data, option);
|
||||
};
|
||||
|
||||
const channelSend = async (channel: string, data: any) => {
|
||||
return ipcRenderer.invoke("event:channelSend", channel, data);
|
||||
};
|
||||
|
||||
export default {
|
||||
init,
|
||||
send,
|
||||
callPage,
|
||||
channelSend,
|
||||
};
|
||||
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
* 任务事件系统
|
||||
* 用于在任务状态改变时通知前端,避免轮询
|
||||
*/
|
||||
|
||||
import { EventEmitter } from 'events';
|
||||
import { Events } from './main';
|
||||
|
||||
class TaskEventSystem extends EventEmitter {
|
||||
private static instance: TaskEventSystem;
|
||||
|
||||
private constructor() {
|
||||
super();
|
||||
this.setMaxListeners(100); // 防止内存泄漏警告
|
||||
}
|
||||
|
||||
static getInstance(): TaskEventSystem {
|
||||
if (!TaskEventSystem.instance) {
|
||||
TaskEventSystem.instance = new TaskEventSystem();
|
||||
}
|
||||
return TaskEventSystem.instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务状态改变时触发
|
||||
* @param taskId 任务 ID
|
||||
* @param oldStatus 旧状态
|
||||
* @param newStatus 新状态
|
||||
* @param data 任务完整数据
|
||||
*/
|
||||
emitTaskStatusChange(
|
||||
taskId: number | string,
|
||||
oldStatus: string | undefined,
|
||||
newStatus: string,
|
||||
data?: any
|
||||
) {
|
||||
console.log(`[TaskEventSystem] 任务 ${taskId} 状态变化: ${oldStatus} → ${newStatus}`);
|
||||
|
||||
// 1. 本地事件(供后端使用)
|
||||
this.emit('task:statusChange', {
|
||||
taskId,
|
||||
oldStatus,
|
||||
newStatus,
|
||||
timestamp: Date.now(),
|
||||
data
|
||||
});
|
||||
|
||||
// 2. 广播到前端(供前端 UI 使用)
|
||||
Events.broadcast('TaskStatusChange', {
|
||||
taskId,
|
||||
oldStatus,
|
||||
newStatus,
|
||||
timestamp: Date.now(),
|
||||
...data
|
||||
});
|
||||
|
||||
console.log(`[TaskEventSystem] 已广播任务状态变化事件`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务进度改变时触发
|
||||
*/
|
||||
emitTaskProgressChange(
|
||||
taskId: number | string,
|
||||
progress: number,
|
||||
message?: string
|
||||
) {
|
||||
// 广播到前端
|
||||
Events.broadcast('TaskProgressChange', {
|
||||
taskId,
|
||||
progress,
|
||||
message,
|
||||
timestamp: Date.now()
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务完成时触发
|
||||
*/
|
||||
emitTaskComplete(
|
||||
taskId: number | string,
|
||||
result?: any
|
||||
) {
|
||||
Events.broadcast('TaskComplete', {
|
||||
taskId,
|
||||
result,
|
||||
timestamp: Date.now()
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务失败时触发
|
||||
*/
|
||||
emitTaskFailed(
|
||||
taskId: number | string,
|
||||
error: string
|
||||
) {
|
||||
Events.broadcast('TaskFailed', {
|
||||
taskId,
|
||||
error,
|
||||
timestamp: Date.now()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const taskEventSystem = TaskEventSystem.getInstance();
|
||||
|
||||
// IPC handlers 供前端调用
|
||||
import { ipcMain } from 'electron';
|
||||
|
||||
ipcMain.handle('task:emitStatusChange', (event, taskId, oldStatus, newStatus, data) => {
|
||||
taskEventSystem.emitTaskStatusChange(taskId, oldStatus, newStatus, data);
|
||||
});
|
||||
|
||||
ipcMain.handle('task:emitProgressChange', (event, taskId, progress, message) => {
|
||||
taskEventSystem.emitTaskProgressChange(taskId, progress, message);
|
||||
});
|
||||
|
||||
ipcMain.handle('task:emitComplete', (event, taskId, result) => {
|
||||
taskEventSystem.emitTaskComplete(taskId, result);
|
||||
});
|
||||
|
||||
ipcMain.handle('task:emitFailed', (event, taskId, error) => {
|
||||
taskEventSystem.emitTaskFailed(taskId, error);
|
||||
});
|
||||
Reference in New Issue
Block a user