Initial clean project import

This commit is contained in:
cat-shark
2026-06-19 18:41:41 +08:00
commit a13b804c7a
1306 changed files with 220568 additions and 0 deletions
+203
View File
@@ -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,
};
+22
View File
@@ -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,
};
+125
View File
@@ -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);
});