Compare commits
2 Commits
fcad3681b4
...
e969ec2236
Author | SHA1 | Date |
---|---|---|
|
e969ec2236 | 2 days ago |
|
a9de1ec525 | 3 days ago |
46 changed files with 759 additions and 433 deletions
@ -0,0 +1,123 @@ |
|||||
|
type FireFN = (...argu: any[]) => void |
||||
|
|
||||
|
// 监听器类型定义,支持优先级
|
||||
|
interface Listener<F extends FireFN> { |
||||
|
fn: F |
||||
|
once: boolean |
||||
|
priority: number |
||||
|
} |
||||
|
|
||||
|
class FireEvent<T extends Record<string | symbol, FireFN>> { |
||||
|
// 使用 Map 存储事件监听器,支持 symbol 键
|
||||
|
private events = new Map<keyof T, Array<Listener<T[keyof T]>>>() |
||||
|
|
||||
|
// 获取事件监听器列表,如果不存在则创建
|
||||
|
private getListeners<S extends keyof T>(name: S): Array<Listener<T[S]>> { |
||||
|
if (!this.events.has(name)) { |
||||
|
this.events.set(name, []) |
||||
|
} |
||||
|
return this.events.get(name) as Array<Listener<T[S]>> |
||||
|
} |
||||
|
|
||||
|
// 按优先级排序监听器
|
||||
|
private sortListeners<S extends keyof T>(name: S) { |
||||
|
const listeners = this.getListeners(name) |
||||
|
listeners.sort((a, b) => b.priority - a.priority) |
||||
|
} |
||||
|
|
||||
|
// 打印事件和监听器信息
|
||||
|
print() { |
||||
|
console.log("Registered Events:") |
||||
|
this.events.forEach((listeners, name) => { |
||||
|
// 显式处理 symbol 类型
|
||||
|
const keyType = typeof name === "symbol" ? `Symbol(${name.description || ""})` : String(name) |
||||
|
console.log(` ${keyType}: ${listeners.length} listeners`) |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// 添加事件监听器,支持优先级
|
||||
|
on<S extends keyof T>(name: S, fn: T[S], priority = 0): this { |
||||
|
const listeners = this.getListeners(name) |
||||
|
listeners.push({ fn, once: false, priority }) |
||||
|
this.sortListeners(name) |
||||
|
return this // 支持链式调用
|
||||
|
} |
||||
|
|
||||
|
// 触发事件
|
||||
|
emit<S extends keyof T>(name: S, ...args: Parameters<T[S]>): this { |
||||
|
const listeners = this.getListeners(name).slice() // 创建副本以避免移除时的问题
|
||||
|
|
||||
|
for (const { fn } of listeners) { |
||||
|
try { |
||||
|
fn(...args) |
||||
|
} catch (error) { |
||||
|
console.error(`Error in event handler for ${String(name)}:`, error) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 移除一次性监听器
|
||||
|
if (listeners.some(l => l.once)) { |
||||
|
this.events.set( |
||||
|
name, |
||||
|
this.getListeners(name).filter(l => !l.once), |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
return this |
||||
|
} |
||||
|
|
||||
|
// 移除事件监听器
|
||||
|
off<S extends keyof T>(name: S, fn?: T[S]): this { |
||||
|
if (!this.events.has(name)) return this |
||||
|
|
||||
|
const listeners = this.getListeners(name) |
||||
|
|
||||
|
if (!fn) { |
||||
|
// 移除所有监听器
|
||||
|
this.events.delete(name) |
||||
|
} else { |
||||
|
// 移除特定监听器
|
||||
|
const filtered = listeners.filter(l => l.fn !== fn) |
||||
|
if (filtered.length === 0) { |
||||
|
this.events.delete(name) |
||||
|
} else { |
||||
|
this.events.set(name, filtered) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return this |
||||
|
} |
||||
|
|
||||
|
// 添加一次性事件监听器
|
||||
|
once<S extends keyof T>(name: S, fn: T[S], priority = 0): this { |
||||
|
const listeners = this.getListeners(name) |
||||
|
listeners.push({ fn, once: true, priority }) |
||||
|
this.sortListeners(name) |
||||
|
return this |
||||
|
} |
||||
|
|
||||
|
// 清除所有事件监听器
|
||||
|
clear(): this { |
||||
|
this.events.clear() |
||||
|
return this |
||||
|
} |
||||
|
|
||||
|
// 获取指定事件的监听器数量
|
||||
|
listenerCount<S extends keyof T>(name: S): number { |
||||
|
return this.events.get(name)?.length || 0 |
||||
|
} |
||||
|
|
||||
|
// 检查事件是否有监听器
|
||||
|
hasListeners<S extends keyof T>(name: S): boolean { |
||||
|
return this.listenerCount(name) > 0 |
||||
|
} |
||||
|
|
||||
|
// 获取所有事件名称
|
||||
|
eventNames(): Array<keyof T> { |
||||
|
return Array.from(this.events.keys()) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export function buildEmitter<T extends Record<string | symbol, FireFN>>() { |
||||
|
return new FireEvent<T>() |
||||
|
} |
@ -0,0 +1,51 @@ |
|||||
|
// type FireKey = string
|
||||
|
type FireFN = (...argu: any[]) => void |
||||
|
|
||||
|
class FireEvent<T extends Record<string | symbol, FireFN>> { |
||||
|
#events: Record<keyof T, FireFN[]> = {} as any |
||||
|
print() { |
||||
|
Object.keys(this.#events).forEach(key => { |
||||
|
console.log(`${key}: ${this.#events[key]}\n`) |
||||
|
}) |
||||
|
} |
||||
|
on<S extends keyof T>(name: S, fn: T[S]) { |
||||
|
if (!this.#events[name]) { |
||||
|
this.#events[name] = [] |
||||
|
} |
||||
|
this.#events[name].push(fn) |
||||
|
} |
||||
|
emit<S extends keyof T>(name: S, ...argu: Parameters<T[S]>) { |
||||
|
if (this.#events[name]) { |
||||
|
this.#events[name].forEach(fn => { |
||||
|
fn(...argu) |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
off<S extends keyof T>(name: S, fn?: T[S]) { |
||||
|
const len = this.#events[name].length |
||||
|
if (!len) { |
||||
|
return |
||||
|
} |
||||
|
if (!fn) { |
||||
|
this.#events[name] = [] |
||||
|
} else { |
||||
|
for (let i = len - 1; i >= 0; i--) { |
||||
|
const _fn = this.#events[name][i] |
||||
|
if (_fn === fn) { |
||||
|
this.#events[name].splice(i, 1) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
once<S extends keyof T>(name: S, fn: T[S]) { |
||||
|
const _fn: any = (...argu: any[]) => { |
||||
|
fn(...argu) |
||||
|
this.off<S>(name, _fn) |
||||
|
} |
||||
|
this.on(name, _fn) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export function buildEmitter<T extends Record<string | symbol, FireFN>>() { |
||||
|
return new FireEvent<T>() |
||||
|
} |
@ -0,0 +1,27 @@ |
|||||
|
// 抽象基类,使用泛型来正确推导子类类型
|
||||
|
abstract class BaseSingleton { |
||||
|
private static _instance: any |
||||
|
|
||||
|
public constructor() { |
||||
|
if (this.constructor === BaseSingleton) { |
||||
|
throw new Error("禁止直接实例化 BaseOne 抽象类") |
||||
|
} |
||||
|
|
||||
|
if ((this.constructor as any)._instance) { |
||||
|
throw new Error("构造函数私有化失败,禁止重复 new") |
||||
|
} |
||||
|
|
||||
|
// this.constructor 是子类,所以这里设为 instance
|
||||
|
;(this.constructor as any)._instance = this |
||||
|
} |
||||
|
|
||||
|
public static getInstance<T extends BaseSingleton>(this: new () => T): T { |
||||
|
const clazz = this as any as typeof BaseSingleton |
||||
|
if (!clazz._instance) { |
||||
|
clazz._instance = new this() |
||||
|
} |
||||
|
return clazz._instance as T |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export { BaseSingleton } |
@ -0,0 +1,13 @@ |
|||||
|
{ |
||||
|
"name": "base", |
||||
|
"version": "1.0.0", |
||||
|
"description": "", |
||||
|
"main": "index.js", |
||||
|
"scripts": { |
||||
|
"test": "echo \"Error: no test specified\" && exit 1" |
||||
|
}, |
||||
|
"keywords": [], |
||||
|
"author": "", |
||||
|
"license": "ISC", |
||||
|
"packageManager": "pnpm@10.4.1" |
||||
|
} |
@ -0,0 +1,13 @@ |
|||||
|
{ |
||||
|
"name": "helper", |
||||
|
"version": "1.0.0", |
||||
|
"description": "", |
||||
|
"main": "index.js", |
||||
|
"scripts": { |
||||
|
"test": "echo \"Error: no test specified\" && exit 1" |
||||
|
}, |
||||
|
"keywords": [], |
||||
|
"author": "", |
||||
|
"license": "ISC", |
||||
|
"packageManager": "pnpm@10.4.1" |
||||
|
} |
@ -0,0 +1,7 @@ |
|||||
|
export const enum EventEnum { |
||||
|
UPDATE_PROGRESS = "update-progress", |
||||
|
} |
||||
|
|
||||
|
export type EventMaps = { |
||||
|
[EventEnum.UPDATE_PROGRESS]: () => void |
||||
|
} |
@ -0,0 +1,8 @@ |
|||||
|
import { broadcast } from "main/utils" |
||||
|
import { EventEnum } from "../common" |
||||
|
|
||||
|
export { EventEnum } |
||||
|
|
||||
|
export function emit(key: EventEnum, ...args: any[]) { |
||||
|
broadcast(key, ...args) |
||||
|
} |
@ -0,0 +1,74 @@ |
|||||
|
type DownloadPercent = { |
||||
|
url: string |
||||
|
option?: object |
||||
|
onprocess?: (now: number, all: number) => void |
||||
|
onsuccess?: (data: any) => void |
||||
|
onerror?: (res: Response) => void |
||||
|
} |
||||
|
const RequestPercent = async ({ |
||||
|
url = "", |
||||
|
option = { |
||||
|
headers: { |
||||
|
responseType: "arraybuffer", |
||||
|
}, |
||||
|
}, |
||||
|
onsuccess, |
||||
|
onerror, |
||||
|
onprocess, |
||||
|
}: DownloadPercent) => { |
||||
|
const response = (await fetch(url, option)) as any |
||||
|
if (!response.ok) { |
||||
|
onerror?.(response) |
||||
|
throw new Error(`下载失败`) |
||||
|
} |
||||
|
const reader = response?.body.getReader() |
||||
|
|
||||
|
// 文件总长度
|
||||
|
const contentLength = +response.headers.get("content-length") |
||||
|
|
||||
|
let receivedLength = 0 |
||||
|
const chunks: any[] = [] |
||||
|
// eslint-disable-next-line no-constant-condition
|
||||
|
while (true) { |
||||
|
const { done, value } = await reader.read() |
||||
|
|
||||
|
if (done) { |
||||
|
break |
||||
|
} |
||||
|
|
||||
|
chunks.push(value) |
||||
|
receivedLength += value.length |
||||
|
onprocess?.(receivedLength, contentLength) |
||||
|
} |
||||
|
// 这里的chunksAll 已经是ArrayBuffer的数据类型了,可以直接返回,也可以转为blob处理
|
||||
|
const chunksAll = new Uint8Array(receivedLength) |
||||
|
let position = 0 |
||||
|
for (const chunk of chunks) { |
||||
|
chunksAll.set(chunk, position) |
||||
|
position += chunk.length |
||||
|
} |
||||
|
|
||||
|
onsuccess?.(chunksAll) |
||||
|
|
||||
|
return chunksAll |
||||
|
} |
||||
|
|
||||
|
export { RequestPercent } |
||||
|
export default RequestPercent |
||||
|
|
||||
|
// RequestPercent({
|
||||
|
// url: "http://117.21.250.136:9812/ZxqyGateway/biz/file/downApk/%E6%98%93%E4%BC%81%E6%95%B0%E8%BD%AC%E5%B9%B3%E5%8F%B0app-1.2.7.apk",
|
||||
|
// option: {
|
||||
|
// headers: {
|
||||
|
// responseType: "arraybuffer",
|
||||
|
// },
|
||||
|
// },
|
||||
|
// onerror: () => {},
|
||||
|
// onsuccess: data => {
|
||||
|
// fs.writeFileSync("./aaa.apk", Buffer.from(data))
|
||||
|
// console.log("success", data)
|
||||
|
// },
|
||||
|
// onprocess: (receivedLength, contentLength) => {
|
||||
|
// console.log(receivedLength, contentLength)
|
||||
|
// },
|
||||
|
// })
|
@ -0,0 +1,6 @@ |
|||||
|
import { buildEmitter } from "base/event/main" |
||||
|
import type { IOnFunc } from "setting/main" |
||||
|
|
||||
|
export const emitter = buildEmitter<{ |
||||
|
update: IOnFunc |
||||
|
}>() |
@ -1,5 +1,5 @@ |
|||||
import { Snippet } from "." |
import { Snippet } from "." |
||||
|
|
||||
export function useSnippet() { |
export function useSnippet() { |
||||
return Snippet.getInstance<Snippet>() |
return Snippet.getInstance() |
||||
} |
} |
||||
|
@ -0,0 +1,15 @@ |
|||||
|
import { EventEnum } from "helper/updater/common" |
||||
|
|
||||
|
const curProgress = ref(0) |
||||
|
|
||||
|
api.on(EventEnum.UPDATE_PROGRESS, ({ percent, now, all }) => { |
||||
|
curProgress.value = percent |
||||
|
}) |
||||
|
|
||||
|
function useUpdate() { |
||||
|
return { |
||||
|
curProgress, |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export { useUpdate } |
@ -0,0 +1,15 @@ |
|||||
|
import Updater from "helper/updater/main" |
||||
|
import _logger from "logger/main" |
||||
|
|
||||
|
const logger = _logger.createNamespace("UpdaterCommand") |
||||
|
|
||||
|
export default class UpdaterCommand { |
||||
|
static init() { |
||||
|
// 命令初始化
|
||||
|
logger.debug("UpdaterCommand init") |
||||
|
} |
||||
|
|
||||
|
async triggerHotUpdate() { |
||||
|
Updater.triggerHotUpdate() |
||||
|
} |
||||
|
} |
@ -1,5 +0,0 @@ |
|||||
const keys = ["hot-update-ready"] as const |
|
||||
|
|
||||
type AllKeys = (typeof keys)[number] |
|
||||
|
|
||||
export type { AllKeys } |
|
@ -1,14 +0,0 @@ |
|||||
// import type { AllKeys } from "../common"
|
|
||||
|
|
||||
const curProgress = ref(0) |
|
||||
// api.on<AllKeys>("progress", () => {
|
|
||||
// curProgress.value = 10
|
|
||||
// })
|
|
||||
|
|
||||
function useUpdate() { |
|
||||
return { |
|
||||
curProgress, |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
export { useUpdate } |
|
@ -1,10 +0,0 @@ |
|||||
import { inject } from "inversify" |
|
||||
import Updater from "main/modules/updater" |
|
||||
|
|
||||
export default class PlatFormCommand { |
|
||||
constructor(@inject(Updater) private _Updater: Updater) {} |
|
||||
|
|
||||
async triggerHotUpdate() { |
|
||||
await this._Updater.triggerHotUpdate() |
|
||||
} |
|
||||
} |
|
@ -1,8 +0,0 @@ |
|||||
import { broadcast } from "main/utils" |
|
||||
import { AllKeys } from "common/event/common" |
|
||||
|
|
||||
function emitHotUpdateReady(...argu) { |
|
||||
broadcast<AllKeys>("hot-update-ready", ...argu) |
|
||||
} |
|
||||
|
|
||||
export { emitHotUpdateReady } |
|
@ -1,12 +0,0 @@ |
|||||
export abstract class _Base { |
|
||||
static instance |
|
||||
|
|
||||
static getInstance<T>(): T { |
|
||||
if (!this.instance) { |
|
||||
// 如果实例不存在,则创建一个新的实例
|
|
||||
// @ts-ignore ...
|
|
||||
this.instance = new this() |
|
||||
} |
|
||||
return this.instance |
|
||||
} |
|
||||
} |
|
@ -0,0 +1,7 @@ |
|||||
|
## event |
||||
|
|
||||
|
通用事件处理模块 |
||||
|
|
||||
|
- main/**/* 处理主进程的模块 |
||||
|
- main/command.ts 会通过ioc收集,进入依赖管理中 |
||||
|
- 其他 处理渲染进程的模块 |
@ -0,0 +1,60 @@ |
|||||
|
import debug from "debug" |
||||
|
import { app } from "electron" |
||||
|
import path from "node:path" |
||||
|
import logger from "logger/main" |
||||
|
import * as rfs from "rotating-file-stream" |
||||
|
import fs from "fs" |
||||
|
|
||||
|
// 配置根目录
|
||||
|
const logsPath = app.getPath("logs") |
||||
|
logger.debug(`日志地址:${logsPath}`) |
||||
|
|
||||
|
const LOG_ROOT = path.join(logsPath) |
||||
|
|
||||
|
// 缓存当前应用启动的日志文件流
|
||||
|
let currentLogStream: rfs.RotatingFileStream | null = null |
||||
|
|
||||
|
// 生成当前启动时的日志文件名
|
||||
|
const getLogFileName = () => { |
||||
|
const now = new Date() |
||||
|
const timestamp = now.toISOString().replace(/[:.]/g, "-") |
||||
|
return `app-${timestamp}.log` |
||||
|
} |
||||
|
|
||||
|
// 覆盖 debug.log 方法
|
||||
|
const originalLog = debug.log |
||||
|
debug.log = function (...args) { |
||||
|
// 保留原始控制台输出
|
||||
|
originalLog.apply(this, args) |
||||
|
|
||||
|
// 确保日志目录存在
|
||||
|
if (!fs.existsSync(LOG_ROOT)) { |
||||
|
fs.mkdirSync(LOG_ROOT, { recursive: true }) |
||||
|
} |
||||
|
|
||||
|
// 延迟初始化日志流,直到第一次写入
|
||||
|
if (!currentLogStream) { |
||||
|
const logFileName = getLogFileName() |
||||
|
currentLogStream = rfs.createStream(logFileName, { |
||||
|
path: LOG_ROOT, |
||||
|
size: "10M", // 单个文件最大 10MB
|
||||
|
rotate: 10, // 保留最近 10 个文件
|
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// @ts-ignore 获取当前命名空间
|
||||
|
const namespace = this.namespace || "unknown" |
||||
|
|
||||
|
// 写入日志(添加时间戳和命名空间)
|
||||
|
const timestamp = new Date().toISOString() |
||||
|
const message = args.join(" ") |
||||
|
currentLogStream.write(`[${timestamp}] [${namespace}] ${message}\n`) |
||||
|
} |
||||
|
|
||||
|
app.on("before-quit", () => { |
||||
|
if (currentLogStream) { |
||||
|
currentLogStream.end() |
||||
|
currentLogStream.destroy() |
||||
|
currentLogStream = null |
||||
|
} |
||||
|
}) |
Loading…
Reference in new issue