diff --git a/assets/core/game/GameManager.ts b/assets/core/game/GameManager.ts index d8cfabc..3f6a188 100644 --- a/assets/core/game/GameManager.ts +++ b/assets/core/game/GameManager.ts @@ -9,6 +9,7 @@ import { director, isValid } from 'cc'; import { GameComponent } from '../../module/common/GameComponent'; import { resLoader } from '../common/loader/ResLoader'; import { ViewUtil } from '../utils/ViewUtil'; +import { View } from '../../types/Types'; /** 游戏元素打开参数 */ export interface ElementParams { @@ -37,7 +38,7 @@ export class GameManager { * @param params 可选参数据 * @returns Promise 成功返回节点,失败返回 null */ - async open(parent: Node | GameComponent, prefabPath: string, params?: ElementParams): Promise { + async open(parent: View, prefabPath: string, params?: ElementParams): Promise { try { // 简化 bundleName 获取逻辑 const bundleName = params?.bundle || resLoader.defaultBundleName; @@ -61,7 +62,7 @@ export class GameManager { return null; } node.parent = parent; - + // 记录手动管理的节点,便于后续释放 this._manualNodes.add(node); } @@ -144,7 +145,7 @@ export class GameManager { destroy(): void { // 释放所有手动管理的节点 this.releaseAllManualNodes(); - + // 清理引用 this.root = null!; } diff --git a/assets/core/gui/Gui.ts b/assets/core/gui/Gui.ts index b126489..03ede4b 100644 --- a/assets/core/gui/Gui.ts +++ b/assets/core/gui/Gui.ts @@ -1,3 +1,4 @@ +import type { GameComponentCtor, UICtor } from '../../types/Types'; import type { UIConfigMap } from './layer/LayerEnum'; import type { UIConfig } from './layer/UIConfig'; @@ -5,10 +6,9 @@ const configs: UIConfigMap = {}; export namespace gui { /** 注册界面组件 */ - export function register(key: string, config: UIConfig) { - return function (ctor: any) { - //@ts-ignore - ctor[gui.internal.GUI_KEY] = key; + export function register(key: string, config: UIConfig): (ctor: GameComponentCtor) => void { + return function (ctor: GameComponentCtor): void { + (ctor as any)[gui.internal.GUI_KEY] = key; internal.setConfig(key, config); }; } @@ -19,28 +19,26 @@ export namespace gui { export const GUI_KEY = 'OOPS_GUI_KEY'; /** 获取界面唯一关键字 */ - export function getKey(ctor: any) { - return ctor[GUI_KEY]; + export function getKey(ctor: UICtor): string { + return (ctor as any)[GUI_KEY]; } /** 获取界面组件配置 */ - export function getConfig(key: string) { + export function getConfig(key: string): UIConfig { return configs[key]; } - /** 获取界面组件配置 */ - export function setConfig(key: string, config: UIConfig) { - const c = getConfig(key); - if (c == null) { - configs[key] = config; - } - else { + /** 设置界面组件配置 */ + export function setConfig(key: string, config: UIConfig): void { + if (configs[key] != null) { console.error(`界面${key}重复注册`); + return; } + configs[key] = config; } /** 初始化界面组件配置 */ - export function initConfigs(uicm: UIConfigMap) { + export function initConfigs(uicm: UIConfigMap): void { for (const key in uicm) { configs[key] = uicm[key]; } diff --git a/assets/libs/ecs/ECSEntity.ts b/assets/libs/ecs/ECSEntity.ts index 99f1798..3e6171a 100644 --- a/assets/libs/ecs/ECSEntity.ts +++ b/assets/libs/ecs/ECSEntity.ts @@ -269,7 +269,7 @@ export class ECSEntity { remove(ctor: CompType, isRecycle = true): void { const componentTypeId = typeof ctor === 'number' ? ctor : ctor.tid; const compName = typeof ctor === 'number' ? '' : ctor.compName; - + if (!this.mask.has(componentTypeId)) { return; } @@ -280,7 +280,7 @@ export class ECSEntity { } comp.ent = null!; - + if (isRecycle) { comp.reset(); @@ -296,7 +296,8 @@ export class ECSEntity { // 限制缓存组件数量,防止内存泄漏 if (this.compTid2Obj.size < ECSEntity.MAX_CACHE_COMP) { this.compTid2Obj.set(componentTypeId, comp); // 用于缓存显示对象组件 - } else { + } + else { // 超过限制,强制回收 console.warn(`实体 ${this.name} 缓存组件数量超过限制,强制回收组件 ${compName}`); comp.reset(); @@ -338,10 +339,10 @@ export class ECSEntity { // 移除实体上所有组件 this.compTid2Ctor.forEach(this._remove, this); destroyEntity(this); - + // 清理缓存的组件对象,防止内存泄漏 this.compTid2Obj.clear(); - + // 回收 mask 到对象池 this.mask.destroy(); } diff --git a/assets/module/common/CCBusiness.ts b/assets/module/common/CCBusiness.ts index a514bf1..b864f91 100644 --- a/assets/module/common/CCBusiness.ts +++ b/assets/module/common/CCBusiness.ts @@ -21,7 +21,7 @@ export class CCBusiness { destroy() { // 释放消息对象 if (this._event) { - this._event.destroy(); + this._event.clear(); this._event = null; } diff --git a/assets/module/common/CCEntity.ts b/assets/module/common/CCEntity.ts index f3f9bd0..5f25d3a 100644 --- a/assets/module/common/CCEntity.ts +++ b/assets/module/common/CCEntity.ts @@ -1,27 +1,20 @@ -import type { __private, Node } from 'cc'; -import { resLoader } from '../../core/common/loader/ResLoader'; +import type { Node } from 'cc'; +import { oops } from '../../core/Oops'; import { gui } from '../../core/gui/Gui'; import type { UIParam } from '../../core/gui/layer/LayerUIElement'; import { LayerUIElement } from '../../core/gui/layer/LayerUIElement'; -import { oops } from '../../core/Oops'; import { ViewUtil } from '../../core/utils/ViewUtil'; import { ecs } from '../../libs/ecs/ECS'; import type { ECSEntity } from '../../libs/ecs/ECSEntity'; import type { CompType } from '../../libs/ecs/ECSModel'; +import type { BusinessCtor, EntityCtor, UICtor, View, ViewCtor } from '../../types/Types'; import type { CCBusiness } from './CCBusiness'; -import type { CCView } from './CCView'; import { GameComponent } from './GameComponent'; -type ECSCtor = - | __private.__types_globals__Constructor - | __private.__types_globals__AbstractedConstructor; -type ECSView = CCView; -type EntityCtor = new (...args: any[]) => T; -type BusinessCtor = CCBusiness> = new (...args: any[]) => T; - /** ECS 游戏模块实体 */ export abstract class CCEntity extends ecs.Entity { //#region 子模块管理 + /** 单例子实体集合(key: 实体类构造函数,value: 实体实例) */ private singletons: Map = null!; @@ -84,50 +77,66 @@ export abstract class CCEntity extends ecs.Entity { //#region 游戏视图层管理 /** * 通过资源内存中获取预制上的组件添加到ECS实体中 - * @param ctor 界面逻辑组件 + * @param ctor 界面逻辑组件(支持 ECSView 或使用 gui.register 注册的 GameComponent) * @param parent 显示对象父级 - * @param path 显示资源地址 - * @param bundleName 资源包名称 + * @param path 显示资源地址(可选,不传时使用 @game.prefab 装饰器注册的路径) + * @param bundleName 资源包名称(可选,不传时使用 @game.prefab 装饰器注册的包名) */ - async addPrefab( - ctor: ECSCtor, - parent: Node | GameComponent, - path: string, - bundleName: string = resLoader.defaultBundleName + async addPrefab( + ctor: ViewCtor, + parent: View, + path?: string, + bundleName?: string ): Promise { + // 未传入路径时,从装饰器注册的数据中获取 + if (path == null) { + path = (ctor as any).GAME_PREFAB_PATH; + bundleName = (ctor as any).GAME_PREFAB_BUNDLE; + if (path == null) { + throw new Error(`组件 ${(ctor as any).name} 未使用 @game.prefab 装饰器注册,请添加 @game.prefab('path/to/prefab') 装饰器或手动传入路径参数`); + } + } + let node: Node; // 跟随父节点释放自动释放当前资源 if (parent instanceof GameComponent) { node = await parent.createPrefabNode(path, bundleName); const comp = node.getComponent(ctor); - if (!comp) { - throw new Error(`组件 ${ctor.name} 不存在于预制 ${path} 中`); - } - this.add(comp); + if (comp) this.add(comp as unknown as ecs.Comp); node.parent = parent.node; } // 手动内存管理 else { node = await ViewUtil.createPrefabNodeAsync(path, bundleName); const comp = node.getComponent(ctor); - if (!comp) { - throw new Error(`组件 ${ctor.name} 不存在于预制 ${path} 中`); - } - this.add(comp); + if (comp) this.add(comp as unknown as ecs.Comp); node.parent = parent; } return node; } + /** + * 移除预制体组件及其节点 + * @param node 要销毁的节点或组件(Node 或 GameComponent) + */ + removePrefab(node: View): void { + if (node instanceof GameComponent) { + node.remove(); + } + else { + node.destroy(); + } + } + /** * 添加视图层组件 - * @param ctor 界面逻辑组件 + * @param ctor 界面逻辑组件(支持 CCView 或使用 gui.register 注册的 GameComponent 子类) * @param params 界面参数 * @returns 界面节点 */ - async addUi(ctor: ECSCtor, params?: UIParam): Promise { + async addUi(ctor: UICtor, params?: UIParam): Promise { const key = gui.internal.getKey(ctor); if (!key) { throw new Error(`${ctor.name} 界面组件未使用 gui.register 注册`); @@ -146,21 +155,18 @@ export abstract class CCEntity extends ecs.Entity { } const node = await oops.gui.open(key, params); - const comp = node.getComponent(ctor) as ecs.Comp; - if (!comp) { - throw new Error(`界面节点上未找到组件 ${ctor.name}`); - } + const comp = node.getComponent(ctor) as unknown as ecs.Comp; + if (comp) this.add(comp); - this.add(comp); oops.gui.show(key); return node; } /** * 移除视图层组件 - * @param ctor 界面逻辑组件 + * @param ctor 界面逻辑组件(支持 CCView 或使用 gui.register 注册的 GameComponent 子类) */ - removeUi(ctor: CompType) { + removeUi(ctor: UICtor) { const key = gui.internal.getKey(ctor); if (key) { @@ -170,12 +176,13 @@ export abstract class CCEntity extends ecs.Entity { return; } - const comp = node.getComponent(LayerUIElement); - if (comp) { + const layer = node.getComponent(LayerUIElement); + if (layer) { // 处理界面关闭动画播放完成后,移除ECS组件,避免使用到组件实体数据还在动画播放时在使用导致的空对象问题 - comp.onClose = () => { + layer.onClose = () => { try { - this.remove(ctor); + const view = node.getComponent(ctor) as unknown as ecs.Comp; + if (view) this.remove(ctor as unknown as CompType); } catch (error) { console.error(`移除界面组件失败: ${key}`, error); @@ -185,11 +192,12 @@ export abstract class CCEntity extends ecs.Entity { } else { // 没有 LayerUIElement,直接移除 - this.remove(ctor); + this.remove(ctor as unknown as CompType); } } else { - this.remove(ctor); + // 组件未使用 gui.register 注册,尝试直接移除 + this.remove(ctor as unknown as CompType); } } //#endregion @@ -224,6 +232,10 @@ export abstract class CCEntity extends ecs.Entity { //@ts-ignore business.init(); this.businesss.set(cls, business); + + // 将业务逻辑组件直接附加到实体对象身上,方便直接获取 + Reflect.set(this, cls.name, business); + return business as T; } @@ -247,6 +259,9 @@ export abstract class CCEntity extends ecs.Entity { if (business) { business.destroy(); this.businesss.delete(cls); + + // 清理实体上的业务逻辑组件引用 + Reflect.set(this, cls.name, null); } } } diff --git a/assets/module/common/CCView.ts b/assets/module/common/CCView.ts index 19d1703..06aa6db 100644 --- a/assets/module/common/CCView.ts +++ b/assets/module/common/CCView.ts @@ -11,6 +11,7 @@ import { ECSModel } from '../../libs/ecs/ECSModel'; import { VM } from '../../libs/model-view/ViewModel'; import { VMBase } from '../../libs/model-view/VMBase'; import type { CCEntity } from './CCEntity'; +import type { UICtor } from '../../types/Types'; import { GameComponent } from './GameComponent'; /** @@ -43,7 +44,7 @@ export class RoleViewComp extends CCView { @ecs.register('LoadingView', false) export class LoadingViewComp extends CCView { protected mvvm = true; // 启用 MVVM 功能 - + data: LoadingData = { finished: 0, total: 0, @@ -70,20 +71,19 @@ export abstract class CCView extends GameComponent implement //#region MVVM 功能相关(仅在 mvvm = true 时使用) /** 是否启用 MVVM 功能(子类可覆盖为 true) */ - protected mvvm: boolean = false; - - /** + protected mvvm = false; + + /** * MVVM 绑定的标签,延迟初始化以节省内存 * 仅在启用 MVVM 时创建 */ protected tag?: string; - - /** + + /** * 需要绑定的私有数据 * 注意:子类应该显式初始化此属性 */ protected data?: any; - //#endregion /** * 组件加载时调用 @@ -109,11 +109,11 @@ export abstract class CCView extends GameComponent implement // 优化:只在必要时替换点号,使用更快的 replaceAll(如果支持) this.tag = `_temp<${uuid.replace('.', '')}>`; VM.add(this.data!, this.tag); - + // 搜寻所有节点:找到 watch path const comps = this.getVMComponents(); const len = comps.length; - + // 优化:避免属性查找,缓存 tag const tag = this.tag; for (let i = 0; i < len; i++) { @@ -140,10 +140,10 @@ export abstract class CCView extends GameComponent implement * @private */ private replaceVMPath(comp: Component, tag: string) { - // @ts-ignore - 优化:使用 any 类型避免多次类型转换 + // 优化:使用 any 类型避免多次类型转换 const vmComp: any = comp; const path: string = vmComp.watchPath; - + // 优化:使用严格相等避免类型转换 if (vmComp.templateMode === true) { const pathArr: string[] = vmComp.watchPathArr; @@ -171,20 +171,20 @@ export abstract class CCView extends GameComponent implement */ private getVMComponents(): Component[] { const comps = this.node.getComponentsInChildren(VMBase); - + // 优化:提前返回,避免不必要的计算 if (comps.length === 0) { return comps; } - + // 优化:只在有嵌套 CCView 时才获取 parents const parents = this.node.getComponentsInChildren(CCView); - + // 优化:使用数组长度判断,避免创建新数组 let hasNested = false; const len = parents.length; const myUuid = this.uuid; - + for (let i = 0; i < len; i++) { const p = parents[i]; if (p.uuid !== myUuid && p.mvvm) { @@ -219,9 +219,10 @@ export abstract class CCView extends GameComponent implement result.push(comps[i]); } } - + return result; } + //#endregion /** 从父节点移除自己 */ remove() { @@ -229,20 +230,20 @@ export abstract class CCView extends GameComponent implement console.error(`组件 ${this.name} 移除失败,实体不存在`); return; } - + if (this.tid < 0) { console.error(`组件 ${this.name} 移除失败,组件未注册 (tid=${this.tid})`); return; } - + const cct = ECSModel.compCtors[this.tid]; if (!cct) { console.error(`组件 ${this.name} 移除失败,组件构造函数不存在 (tid=${this.tid})`); return; } - - this.ent.removeUi(cct); - this.ent = null!; // 清空引用,避免内存泄漏 + + this.ent.removeUi(cct as unknown as UICtor); + this.ent = null!; // 清空引用,避免内存泄漏 } /** @@ -257,10 +258,9 @@ export abstract class CCView extends GameComponent implement // 解除全部引用 if (this.tag) { VM.remove(this.tag); - // @ts-ignore - 优化:显式清空引用,帮助 GC this.tag = undefined; } - + // @ts-ignore - 优化:显式清空引用,帮助 GC this.data = undefined; } @@ -269,4 +269,4 @@ export abstract class CCView extends GameComponent implement } abstract reset(): void; -} \ No newline at end of file +} diff --git a/assets/module/decorator.meta b/assets/module/decorator.meta new file mode 100644 index 0000000..dd9bb0a --- /dev/null +++ b/assets/module/decorator.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.2.0", + "importer": "directory", + "imported": true, + "uuid": "72863800-5c5d-44f0-b86c-a5b0d0643d8f", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/module/decorator/GamePrefabDecorator.ts b/assets/module/decorator/GamePrefabDecorator.ts new file mode 100644 index 0000000..4a00495 --- /dev/null +++ b/assets/module/decorator/GamePrefabDecorator.ts @@ -0,0 +1,32 @@ +import { resLoader } from '../../core/common/loader/ResLoader'; +import { GameComponentCtor } from '../../types/Types'; + +/** + * 游戏装饰器命名空间 + */ +export namespace prefab { + /** + * Prefab 装饰器 - 用于标记组件的预制体路径 + * @param path 预制体路径 + * @param bundleName 资源包名称(可选,默认使用 resLoader.defaultBundleName) + * @example + * ```typescript + * @ccclass('V_Backpack_Prop') + * @ecs.register('V_Backpack_Prop', false) + * @prefab.register('gui/backpack/prefab/V_Backpack_Prop') + * export class V_Backpack_Prop extends CCView { + * // ... + * } + * + * // 使用时 + * await entity.addPrefab(V_Backpack_Prop, parentNode); + * ``` + */ + export function register(path: string, bundleName?: string): (ctor: GameComponentCtor) => void { + return function (ctor: GameComponentCtor): void { + const bundle = bundleName || resLoader.defaultBundleName; + (ctor as any).GAME_PREFAB_PATH = path; + (ctor as any).GAME_PREFAB_BUNDLE = bundle; + }; + } +} diff --git a/assets/module/decorator/GamePrefabDecorator.ts.meta b/assets/module/decorator/GamePrefabDecorator.ts.meta new file mode 100644 index 0000000..598f2eb --- /dev/null +++ b/assets/module/decorator/GamePrefabDecorator.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "7c1ef3cb-50c6-49d4-b17c-11b8d37535bf", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/types.meta b/assets/types.meta new file mode 100644 index 0000000..7295582 --- /dev/null +++ b/assets/types.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.2.0", + "importer": "directory", + "imported": true, + "uuid": "172d5432-5689-483f-8ede-94d9fdf5c747", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/types/Types.ts b/assets/types/Types.ts new file mode 100644 index 0000000..590b202 --- /dev/null +++ b/assets/types/Types.ts @@ -0,0 +1,38 @@ +import type { Node } from 'cc'; +import type { GameComponent } from 'db://oops-framework/module/common/GameComponent'; +import type { ecs } from 'db://oops-framework/libs/ecs/ECS'; +import type { CCView } from 'db://oops-framework/module/common/CCView'; +import type { CCEntity } from 'db://oops-framework/module/common/CCEntity'; +import type { CCBusiness } from 'db://oops-framework/module/common/CCBusiness'; + +// ==================== 通用构造函数类型 ==================== + +/** 通用构造函数类型 */ +type Ctor = new (...args: any[]) => T; + +// ==================== 视图类型 ==================== + +/** GameComponent 及其子类的构造函数类型,用于类型安全的组件实例化 */ +export type GameComponentCtor = Ctor; + +/** UI 组件构造函数类型(用于继承自 GameComponent 并使用 gui.register 注册的组件) */ +export type UICtor = Ctor; + +/** 通用的视图组件构造函数类型(支持 ECSView 或 GameComponent) */ +export type ViewCtor = Ctor; + +/** ECS 游戏视图组件类型(继承自 CCView,用于完整的 ECS 组件) */ +export type ECSView = CCView; + +/** 视图节点类型(Node 或 GameComponent) */ +export type View = Node | GameComponent; + +// ==================== 实体类型 ==================== + +/** ECS 实体构造函数类型 */ +export type EntityCtor = Ctor; + +// ==================== 业务逻辑类型 ==================== + +/** ECS 业务逻辑组件构造函数类型 */ +export type BusinessCtor = CCBusiness> = Ctor; diff --git a/assets/types/Types.ts.meta b/assets/types/Types.ts.meta new file mode 100644 index 0000000..4cf1bd6 --- /dev/null +++ b/assets/types/Types.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "4d52b2a4-af0e-4ae4-b575-920ec36b6bcf", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/dist/asset-directory.js b/dist/asset-directory.js index 24d6088..05bba40 100644 --- a/dist/asset-directory.js +++ b/dist/asset-directory.js @@ -1,9 +1,6 @@ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); -exports.template = exports.$ = void 0; -exports.update = update; -exports.ready = ready; -exports.close = close; +exports.close = exports.ready = exports.update = exports.template = exports.$ = void 0; const fs_1 = require("fs"); const path_1 = require("path"); exports.$ = { @@ -41,5 +38,8 @@ function update(assetList, metaList) { this.$.section.hidden = false; } } +exports.update = update; function ready() { } +exports.ready = ready; function close() { } +exports.close = close; diff --git a/dist/assets-menu.js b/dist/assets-menu.js index cc7d2dc..709fdb2 100644 --- a/dist/assets-menu.js +++ b/dist/assets-menu.js @@ -1,26 +1,9 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.onAssetMenu = onAssetMenu; -const tinypng_1 = require("./tinypng"); +exports.onAssetMenu = void 0; /** 资源栏右键菜单 */ function onAssetMenu(assetInfo) { - return [ - { - label: 'i18n:oops-framework.name', - submenu: [ - { - label: `i18n:oops-framework.tools_asset_menu`, - submenu: [ - { - label: `i18n:oops-framework.tools_compress`, - click() { - (0, tinypng_1.compress)(assetInfo.file); - }, - } - ] - } - ], - }, - ]; + return []; } +exports.onAssetMenu = onAssetMenu; ; diff --git a/dist/common/version.js b/dist/common/version.js index 1b1b2a7..53ca430 100644 --- a/dist/common/version.js +++ b/dist/common/version.js @@ -15,27 +15,15 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? ( }) : function(o, v) { o["default"] = v; }); -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; Object.defineProperty(exports, "__esModule", { value: true }); -exports.checkUpdate = checkUpdate; -exports.statistics = statistics; -exports.reload = reload; +exports.reload = exports.statistics = exports.checkUpdate = void 0; const package_util_1 = require("./package-util"); const https = __importStar(require("https")); /** @@ -80,6 +68,7 @@ function checkUpdate() { console.error("【Oops Framework】请检查你的网络是否正常,框架版本验证失败"); }); } +exports.checkUpdate = checkUpdate; async function statistics() { // 获取本地 IP 地址 const os = require('os'); @@ -123,12 +112,14 @@ async function statistics() { req.write(postData); req.end(); } +exports.statistics = statistics; async function reload() { const path = await Editor.Package.getPath(package_util_1.PackageUtil.name); await Editor.Package.unregister(path); await Editor.Package.register(path); await Editor.Package.enable(path); } +exports.reload = reload; /** * 获取本地版本号 * @returns {string} diff --git a/dist/main.js b/dist/main.js index 2e9c340..4e4a575 100644 --- a/dist/main.js +++ b/dist/main.js @@ -1,8 +1,6 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.methods = exports.config = void 0; -exports.load = load; -exports.unload = unload; +exports.methods = exports.config = exports.unload = exports.load = void 0; const electron_1 = require("electron"); const version_1 = require("./common/version"); /** @@ -13,11 +11,13 @@ function load() { (0, version_1.checkUpdate)(); (0, version_1.statistics)(); } +exports.load = load; /** * @en Hooks triggered after extension uninstallation is complete * @zh 扩展卸载完成后触发的钩子 */ function unload() { } +exports.unload = unload; /** * @en * @zh 为扩展的主进程的注册方法 diff --git a/dist/tinypng.js b/dist/tinypng.js deleted file mode 100644 index f86e916..0000000 --- a/dist/tinypng.js +++ /dev/null @@ -1,159 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.compress = compress; -const fs_1 = __importDefault(require("fs")); -const https_1 = __importDefault(require("https")); -const path_1 = __importDefault(require("path")); -const url_1 = __importDefault(require("url")); -const exts = ['.png', '.jpg', '.jpeg']; -const max = 5200000; -const options = { - method: 'POST', - hostname: 'tinypng.com', - path: '/backend/opt/shrink', - headers: { - rejectUnauthorized: 'false', - 'Postman-Token': Date.now(), - 'Cache-Control': 'no-cache', - 'Content-Type': 'application/x-www-form-urlencoded', - 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36' - } -}; -function compress(filePath) { - if (!fs_1.default.existsSync(filePath)) { - console.log(`路径不存在:${filePath}`); - return; - } - const fileName = path_1.default.basename(filePath); - if (!fs_1.default.statSync(filePath).isDirectory()) { - if (exts.includes(path_1.default.extname(filePath))) { - console.log(`[${fileName}] 压缩中...`); - fileTinyUpload(filePath) - .then(data => { - console.log(`[1/1] [${fileName}] 压缩成功,原始: ${toSize(data.input.size)},压缩: ${toSize(data.output.size)},压缩比: ${toPercent(data.output.ratio)}`); - }) - .catch(err => { - console.log(`[1/1] [${fileName}] 压缩失败!报错:${err}`); - }); - } - else { - console.log(`[${fileName}] 压缩失败!报错:只支持 png、jpg 与 jpeg 格式`); - } - } - else { - let totalCount = 0; - let processedCount = 0; - fileEach(filePath, (filePathInDir) => { - totalCount++; - const relativePath = path_1.default.relative(filePath, filePathInDir); - fileTinyUpload(filePathInDir) - .then(data => { - console.log(`[${++processedCount}/${totalCount}] [${relativePath}] 压缩成功,原始: ${toSize(data.input.size)},压缩: ${toSize(data.output.size)},压缩比: ${toPercent(data.output.ratio)}`); - }) - .catch(err => { - console.log(`[${++processedCount}/${totalCount}] [${relativePath}] 压缩失败!报错:${err}`); - }); - }); - } -} -function getRandomIP() { - return Array.from(Array(4)).map(() => Math.floor(255 * Math.random())).join('.'); -} -function fileEach(dir, callback) { - fs_1.default.readdir(dir, (err, files) => { - if (err) { - console.error(err); - return; - } - files.forEach((file) => { - const filePath = path_1.default.join(dir, file); - fs_1.default.stat(filePath, (statErr, stats) => { - if (statErr) { - console.error(statErr); - return; - } - if (stats.isDirectory()) { - fileEach(filePath, callback); - } - else { - if (stats.size <= max && stats.isFile() && exts.includes(path_1.default.extname(file))) { - callback(filePath); - } - } - }); - }); - }); -} -function fileUpload(filePath) { - return new Promise((resolve, reject) => { - options.headers['X-Forwarded-For'] = getRandomIP(); - const req = https_1.default.request(options, (res) => { - let data = ''; - res.on('data', (chunk) => { - data += chunk; - }); - res.on('end', () => { - try { - const result = JSON.parse(data); - if (result.error) { - reject(result.message); - } - else { - resolve(result); - } - } - catch (parseErr) { - reject(parseErr); - } - }); - }); - req.write(fs_1.default.readFileSync(filePath), 'binary'); - req.on('error', err => { - reject(err); - }); - req.end(); - }); -} -function fileUpdate(filePath, data) { - return new Promise((resolve, reject) => { - const urlObj = new url_1.default.URL(data.output.url); - const req = https_1.default.request(urlObj, (res) => { - let body = ''; - res.setEncoding('binary'); - res.on('data', (chunk) => { - body += chunk; - }); - res.on('end', () => { - fs_1.default.writeFile(filePath, body, 'binary', (err) => { - if (err) { - reject(err); - } - else { - resolve(data); - } - }); - }); - }); - req.on('error', (err) => { - reject(err); - }); - req.end(); - }); -} -function fileTinyUpload(filePath) { - return fileUpload(filePath).then(data => fileUpdate(filePath, data)); -} -function toSize(size) { - if (size < 1024) - return size + 'B'; - else if (size < 1048576) - return (size / 1024).toFixed(2) + 'KB'; - else - return (size / 1024 / 1024).toFixed(2) + 'MB'; -} -function toPercent(ratio) { - return (100 * ratio).toFixed(2) + '%'; -} diff --git a/i18n/en.js b/i18n/en.js index afe506e..f6b2d6d 100644 --- a/i18n/en.js +++ b/i18n/en.js @@ -19,8 +19,6 @@ module.exports = { createView: "Create ECS view layer script", createViewMvvm: "Create ECS view layer script - MVVM", tools: "Framework Tools", - tools_asset_menu: "Tools", - tools_compress: "Image Compression", tools_animator_editor: "Animation State Machine Editor", panel_create_file: "Create Framework Template", -}; \ No newline at end of file +}; diff --git a/i18n/zh.js b/i18n/zh.js index 79f9f54..a98631a 100644 --- a/i18n/zh.js +++ b/i18n/zh.js @@ -19,8 +19,6 @@ module.exports = { createView: "创建 ECS 视图层脚本", createViewMvvm: "创建 ECS 视图层脚本 - MVVM", tools: "框架工具", - tools_asset_menu: "工具", - tools_compress: "图片压缩", tools_animator_editor: "动画状态机编辑器", panel_create_file: "创建框架模板" -}; \ No newline at end of file +}; diff --git a/package.json b/package.json index 68c20c5..1b7e98b 100644 --- a/package.json +++ b/package.json @@ -28,12 +28,7 @@ "readonly": false } }, - "assets": { - "menu": { - "methods": "./dist/assets-menu.js", - "assetMenu": "onAssetMenu" - } - }, + "menu": [ { "path": "i18n:oops-framework.name/i18n:oops-framework.tools", diff --git a/src/assets-menu.ts b/src/assets-menu.ts index efc99b8..1e953d6 100644 --- a/src/assets-menu.ts +++ b/src/assets-menu.ts @@ -1,24 +1,6 @@ import { AssetInfo } from "../@types/packages/asset-db/@types/public"; -import { compress } from "./tinypng"; /** 资源栏右键菜单 */ export function onAssetMenu(assetInfo: AssetInfo) { - return [ - { - label: 'i18n:oops-framework.name', - submenu: [ - { - label: `i18n:oops-framework.tools_asset_menu`, - submenu: [ - { - label: `i18n:oops-framework.tools_compress`, - click() { - compress(assetInfo.file); - }, - } - ] - } - ], - }, - ]; -}; \ No newline at end of file + return []; +}; diff --git a/src/tinypng.ts b/src/tinypng.ts deleted file mode 100644 index d48be3c..0000000 --- a/src/tinypng.ts +++ /dev/null @@ -1,159 +0,0 @@ -import fs from 'fs'; -import https from 'https'; -import path from 'path'; -import url from 'url'; - -const exts = ['.png', '.jpg', '.jpeg']; -const max = 5200000; -const options: any = { - method: 'POST', - hostname: 'tinypng.com', - path: '/backend/opt/shrink', - headers: { - rejectUnauthorized: 'false', - 'Postman-Token': Date.now(), - 'Cache-Control': 'no-cache', - 'Content-Type': 'application/x-www-form-urlencoded', - 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36' - } -}; - -export function compress(filePath: string): void { - if (!fs.existsSync(filePath)) { - console.log(`路径不存在:${filePath}`); - return; - } - const fileName = path.basename(filePath); - if (!fs.statSync(filePath).isDirectory()) { - if (exts.includes(path.extname(filePath))) { - console.log(`[${fileName}] 压缩中...`); - fileTinyUpload(filePath) - .then(data => { - console.log(`[1/1] [${fileName}] 压缩成功,原始: ${toSize(data.input.size)},压缩: ${toSize(data.output.size)},压缩比: ${toPercent(data.output.ratio)}`); - }) - .catch(err => { - console.log(`[1/1] [${fileName}] 压缩失败!报错:${err}`); - }); - } - else { - console.log(`[${fileName}] 压缩失败!报错:只支持 png、jpg 与 jpeg 格式`); - } - } - else { - let totalCount = 0; - let processedCount = 0; - fileEach(filePath, (filePathInDir) => { - totalCount++; - const relativePath = path.relative(filePath, filePathInDir); - fileTinyUpload(filePathInDir) - .then(data => { - console.log(`[${++processedCount}/${totalCount}] [${relativePath}] 压缩成功,原始: ${toSize(data.input.size)},压缩: ${toSize(data.output.size)},压缩比: ${toPercent(data.output.ratio)}`); - }) - .catch(err => { - console.log(`[${++processedCount}/${totalCount}] [${relativePath}] 压缩失败!报错:${err}`); - }); - }); - } -} - -function getRandomIP(): string { - return Array.from(Array(4)).map(() => Math.floor(255 * Math.random())).join('.'); -} - -function fileEach(dir: string, callback: (filePath: string) => void): void { - fs.readdir(dir, (err: any, files: any[]) => { - if (err) { - console.error(err); - return; - } - files.forEach((file: any) => { - const filePath = path.join(dir, file); - fs.stat(filePath, (statErr: any, stats: { isDirectory: () => any; size: number; isFile: () => any; }) => { - if (statErr) { - console.error(statErr); - return; - } - if (stats.isDirectory()) { - fileEach(filePath, callback); - } - else { - if (stats.size <= max && stats.isFile() && exts.includes(path.extname(file))) { - callback(filePath); - } - } - }); - }); - }); -} - -function fileUpload(filePath: string): Promise { - return new Promise((resolve, reject) => { - options.headers['X-Forwarded-For'] = getRandomIP(); - const req = https.request(options, (res: any) => { - let data = ''; - res.on('data', (chunk: string) => { - data += chunk; - }); - res.on('end', () => { - try { - const result = JSON.parse(data); - if (result.error) { - reject(result.message); - } else { - resolve(result); - } - } catch (parseErr) { - reject(parseErr); - } - }); - }); - req.write(fs.readFileSync(filePath), 'binary'); - req.on('error', err => { - reject(err); - }); - req.end(); - }); -} - -function fileUpdate(filePath: string, data: any): Promise { - return new Promise((resolve, reject) => { - const urlObj = new url.URL(data.output.url); - const req = https.request(urlObj, (res: any) => { - let body = ''; - res.setEncoding('binary'); - res.on('data', (chunk: string) => { - body += chunk; - }); - res.on('end', () => { - fs.writeFile(filePath, body, 'binary', (err: any) => { - if (err) { - reject(err); - } else { - resolve(data); - } - }); - }); - }); - req.on('error', (err: any) => { - reject(err); - }); - req.end(); - }); -} - -function fileTinyUpload(filePath: string): Promise { - return fileUpload(filePath).then(data => fileUpdate(filePath, data)); -} - -function toSize(size: number): string { - if (size < 1024) - return size + 'B'; - else if (size < 1048576) - return (size / 1024).toFixed(2) + 'KB'; - else - return (size / 1024 / 1024).toFixed(2) + 'MB'; -} - -function toPercent(ratio: number): string { - return (100 * ratio).toFixed(2) + '%'; -} \ No newline at end of file diff --git a/static/style/default/index.css b/static/style/default/index.css deleted file mode 100644 index a856db3..0000000 --- a/static/style/default/index.css +++ /dev/null @@ -1,37 +0,0 @@ -ui-input { - width: 100%; - min-width: 100px; - min-height: 30px; - line-height: 30px; -} - -ui-button { - height: 30px; -} - -ui-label { - margin-top: 5px; - margin-bottom: 5px; -} - -.f { - flex: 1; - overflow: auto; - padding: 10px; -} - -.c { - display: inline-flex; - width: 100%; - flex-wrap: nowrap; -} - -.c ui-button { - width: 40%; -} - -.b { - text-align: center; - padding: 10px 0; - margin-top: 5px; -} \ No newline at end of file