24 Commits
Author SHA1 Message Date
MrKBear de67ab0d68 Merge pull request 'Optimize Storage Modular' (#16) from dev-mrkbear into master
Reviewed-on: http://git.mrkbear.com/MrKBear/mini-dlpu-v3/pulls/16
2022-01-03 13:36:55 +08:00
MrKBear 121b620312 Optimize Modular 2022-01-03 13:32:52 +08:00
MrKBear 49ed8e588a Remove test case 2022-01-03 08:44:39 +08:00
MrKBear 22af68428f Storage multi instance processing logic 2022-01-02 18:29:21 +08:00
MrKBear 4f49012bb6 Update ReadMe file 2021-12-31 17:20:54 +08:00
MrKBear 73563de980 Merge pull request 'Add API wait function, Update ReadMe file' (#15) from dev-mrkbear into master
Reviewed-on: http://git.mrkbear.com/MrKBear/mini-dlpu-v3/pulls/15
2021-12-31 17:18:17 +08:00
MrKBear eb77678686 Update ReadMe file 2021-12-31 17:17:04 +08:00
MrKBear fc6fee74b0 Add api wait function 2021-12-31 16:26:57 +08:00
MrKBear 9543fa8409 Motd paprams name 2021-12-31 15:48:46 +08:00
MrKBear fadbc4dc72 Update ReadMe file 2021-12-31 14:29:34 +08:00
MrKBear ad4570dae0 Merge branch 'dev-mrkbear' of http://git.mrkbear.com/MrKBear/mini-dlpu-v3 into dev-mrkbear 2021-12-31 14:18:21 +08:00
MrKBear 8d248466eb Update ReadMe file 2021-12-31 14:17:52 +08:00
MrKBear bfa5ed0e3a Merge pull request 'Rename Emitter Merge Logger into one file.' (#14) from dev-mrkbear into master
Reviewed-on: http://git.mrkbear.com/MrKBear/mini-dlpu-v3/pulls/14
2021-12-31 14:04:31 +08:00
MrKBear 582c8446e2 Merge branch 'master' into dev-mrkbear 2021-12-31 14:03:26 +08:00
MrKBear 0fc0fa932a Rename event emitter to emitter 2021-12-31 13:53:26 +08:00
MrKBear 98f4236c35 Merge Logger Module into one File! 2021-12-31 13:43:23 +08:00
MrKBear b37977418a Merge pull request 'Add API Module!' (#13) from dev-mrkbear into master
Reviewed-on: http://git.mrkbear.com/MrKBear/mini-dlpu-v3/pulls/13
2021-12-31 11:24:27 +08:00
MrKBear 7a78cf0594 Add API Module! 2021-12-31 11:20:18 +08:00
ben.qin af68984caa Add API Moudle! 2021-12-30 17:31:16 +08:00
MrKBear 623307ef2b Merge pull request 'Add API Module' (#12) from dev-mrkbear into master
Reviewed-on: http://git.mrkbear.com/MrKBear/mini-dlpu-v3/pulls/12
2021-12-30 16:18:24 +08:00
ben.qin 14b03af584 Modify EventEmitter to class style 2021-12-30 16:14:21 +08:00
MrKBear c3773a3c71 Add API Module! 2021-12-29 21:03:10 +08:00
ben.qin 0140136a0b Add API Moudle! 2021-12-29 17:33:27 +08:00
MrKBear 41519b78e4 Add API Lib 2021-12-21 07:48:03 +08:00
15 changed files with 1590 additions and 1052 deletions
+46 -3
View File
@@ -2,6 +2,22 @@
掌上教务处作为工业大学的社区开源项目,自从2017年开始已有近5年岁月,在无数同学的贡献之下,为大家打造便捷的校园服务。
__*!!!警告!!!*__
请在主仓库提交代码,而非镜像仓库!在镜像仓库提交的代码将会在同步时被覆盖!
主仓库: http://git.mrkbear.com/MrKBear/mini-dlpu-v3
镜像仓库: https://github.com/Mr-k-bear/mini-dlpu-v3
## 目录
- [社区介绍](#小程序社区)
- [项目设计](#第三代小程序)
- [贡献规范](#社区贡献规范)
- [入门文档(等待撰写)](#第三代掌上教务处小程序)
- [设计架构(等待撰写)](#第三代掌上教务处小程序)
## 小程序社区
掌上教务处小程序诞生于2017年中旬,此时微信小程序刚刚公测。
@@ -48,12 +64,39 @@
3. 更好的拓展性,加入更多大家喜欢的功能
新功能:
1. 正在讨论设计,等待你的建议...
## 社区贡献规范
请仔细阅读!
### 项目贡献流程
请先邮件联系 ```mrkbear@mrkbear.com``` 获得 Gitea 平台账号
在仓库中创建自己的分支,分支命名规范为 ```dev-你的昵称```
在仓库中创建自己的分支,分支命名规范为 ```dev-你的昵称```,例如 ```dev-mrkbear```
克隆此储库,在本地 ```checkout``` 到自己的分支
克隆此储库,在本地 ```git checkout dev-你的昵称``` 到自己的分支,进行改动。
开发完成后 ```push``` 到自己的远程分支,并发起合并请求,等待 review
开发完成后 ```git push``` 到自己的远程分支,并发起合并请求到 ```master``` 分支
发起合并请求时,需要指派给 ```MrKBear``` 进行代码审核,审核通过后,代码将完成合并。
### 注意事项
1. ```master``` 分支处于保护状态,仅通过合并请求进行修改
2. 代码提交时,请使用清晰明确的 ```message```
正例: ```Add timetable page``` 反例: ```阿巴阿巴阿巴阿巴```
3. 请勿将任何个人隐私信息以任何方式,放入代码中
4. 为保证 CI/CD,提交代码前必须保证编译可以通过
5. 一个文件不要超过 1000 行代码,尽量保证代码可读性
## 贡献者
@MrKBear (熊鲜森)
+15 -16
View File
@@ -1,30 +1,29 @@
import { Logger } from "./core/Logger";
import { LevelLogLabel, LifeCycleLogLabel } from "./core/PresetLogLabel";
import { Storage } from "./core/Storage";
import { IAppAPIParam } from "./core/Api";
import { IAppStorageParam, Storage, IStorageData } from "./core/Storage";
import { Logger, LevelLogLabel, LifeCycleLogLabel } from "./core/Logger";
App({
App<IAppAPIParam & IAppStorageParam>({
/**
* API 模块需要的全局数据
* 参见 "/core/Api"
*/
api: {
nextId: 1,
pool: new Set()
},
/**
* 存储缓存键值
*/
storageCache: new Set<string>(),
storage: new Map<string, Storage<IStorageData>>(),
/**
* 小程序加载时
*/
onLaunch() {
Logger.log("小程序启动...",
LevelLogLabel.TraceLabel, LifeCycleLogLabel.OnLaunchLabel);
let s = new Storage("test", {
a: new Date(),
be: 2
});
setTimeout(() => {
s.set("be", 12);
}, 1000)
LevelLogLabel.InfoLabel, LifeCycleLogLabel.OnLaunchLabel);
}
})
+556
View File
@@ -0,0 +1,556 @@
import { Emitter } from "./Emitter";
import { Logger, LogLabel, LevelLogLabel, colorRadio, StatusLabel } from "./Logger";
interface IAppAPIParam {
api: {
/**
* API 编号
*/
nextId: number;
/**
* 请求池
* 保存正在等待的 API 请求
*/
pool: Set<API<IAnyData, IAnyData>>;
}
}
interface IAnyData {
[x:string]: any
}
type IWxRequestOption<O> = WechatMiniprogram.RequestOption<O>;
type DeepReadonly<T> = {
readonly [P in keyof T]: DeepReadonly<T[P]>;
};
/**
* 请求参数设置
*/
type IParamSetting<T extends IAnyData> = {
[P in keyof T]: {
/**
* 默认值
*/
defaultValue?: T[P],
/**
* ### 数据测试
* 1、支持正则表达式测试 \
* 2、支持使用 string === string 测试 \
* 3、支持使用 number === number 测试 \
* 4、支持使用自定义函数测试
*/
tester?: RegExp | ((data:T[P]) => boolean) | string | number,
/**
* ### 预解析函数
* 1、此函数用来处理该键值 \
* 2、当返回 undefined 时此键值被遗弃 \
* 3、返回值时,此键值被覆盖
*
* @param data 当前给定数据
* @param key 当前给定数据键值
* @param all 全部输入数据
* @returns 解析结果
*/
parse?: ((data:T[P], key:string, all:DeepReadonly<Partial<T>>) => T[P] | undefined),
/**
* 是否为请求头数据
*/
isHeader?: boolean,
/**
* 是否为模板
*/
isTemplate?: boolean
/**
* 是否可选
*/
Optional?: boolean
}
}
type SuccessCallbackResult<O extends IAnyData> = WechatMiniprogram.RequestSuccessCallbackResult<O>;
type GeneralCallbackResult = WechatMiniprogram.GeneralCallbackResult;
/**
* API 事件
*/
type IAPIEvent<I extends IAnyData, O extends IAnyData> = {
/**
* 当数据初始化事件
*/
initData: Partial<I>;
/**
* 请求数据解析完成后
*/
parseRequestData: Partial<I>;
/**
* 请求发送前
*/
request: IWxRequestOption<O>;
/**
* 成功回调
*/
success: SuccessCallbackResult<O>;
/**
* 失败回调
*/
fail: GeneralCallbackResult;
/**
* 完成回调
*/
complete: GeneralCallbackResult;
}
/**
* 接口调用
*/
class API<I extends IAnyData, O extends IAnyData> extends Emitter<IAPIEvent<I, O>> {
/**
* 基础 URL
* TODO: 这里可能涉及负载均衡
*/
public static get baseUrl():string {
return "https://blog.mrkbear.com";
}
public static defaultLogLabel:LogLabel = new LogLabel(
`API:API`, colorRadio(200, 120, 222)
);
/**
* Logger 使用的标签
*/
private LogLabel:LogLabel = API.defaultLogLabel;
/**
* Api 唯一 ID
*/
public key:string = "API";
/**
* API url
*/
public url:string = "/";
/**
* API 需要的参数列表
*/
public params:IParamSetting<I> = {} as any;
/**
* API 需要的数据
*/
public data?:Partial<I>;
/**
* 请求数据
*/
public requestData?:IWxRequestOption<O>;
//#region wx.request
public timeout?:number;
public method:HTTPMethod = HTTPMethod.GET;
public enableHttp2:boolean = false;
public enableQuic:boolean = false;
public enableCache:boolean = false;
/**
* 是否自动解析返回的 json
* 对应 wx.request 的 dataType
*/
public jsonParse:boolean = true;
//#endregion wx.request
/**
* 初始化标签
*/
public initDebugLabel(key: string) {
this.key = key;
this.LogLabel = new LogLabel(
`API:${ this.key }`, colorRadio(200, 120, 222)
);
// 添加 API 生命周期调试事件
this.on("request", (data) => {
Logger.logMultiple(
[LevelLogLabel.InfoLabel, this.LogLabel, StatusLabel.Pending],
`请求发送中: `, data
);
})
this.on("success", (data) => {
Logger.logMultiple(
[LevelLogLabel.InfoLabel, this.LogLabel, StatusLabel.Success],
`请求成功: `, data
);
})
this.on("fail", (data) => {
Logger.logMultiple(
[LevelLogLabel.ErrorLabel, this.LogLabel, StatusLabel.Failed],
`请求失败: `, data.errMsg
);
})
}
/**
* 初始化数据
* 注意:data 是不安全的,请传入数据副本
* @param data API需要的全部数据
*/
public param(data?: Partial<I>):this {
this.data = data;
if (this.data === void 0) {
Logger.log(`数据初始化异常: 没有输入 [data] 数据!`,
LevelLogLabel.ErrorLabel, this.LogLabel);
return this;
}
for (let key in this.params) {
let data = this.data[key];
let { defaultValue, Optional, tester } = this.params[key];
// 默认值赋予
if (data === void 0 && defaultValue !== void 0) {
this.data[key] = defaultValue;
}
// 数据存在测试
if (data === void 0 && !Optional) {
Logger.log(`数据校验异常: 数据 [${key}] 是必须的,但是并没有接收到!`,
LevelLogLabel.ErrorLabel, this.LogLabel);
}
// 用户自定义测试
if (data !== void 0 && tester !== void 0) {
let testRes:boolean = false;
if (tester instanceof RegExp) {
testRes = tester.test(data!);
} else if (typeof tester === "string" || typeof tester === "number") {
testRes = tester === data;
} else if (tester instanceof Function) {
testRes = tester(data!);
} else {
Logger.logMultiple(
[LevelLogLabel.ErrorLabel, this.LogLabel],
`数据校验异常: [${ key }] 参数存在未知类型的 tester:`, tester
);
}
if (!testRes) {
Logger.logMultiple(
[LevelLogLabel.ErrorLabel, this.LogLabel],
`数据校验异常: [${ key }] 参数数据未通过自定义的 tester:`, data
);
}
}
}
// 触发数据初始化事件
this.emit("initData", this.data);
// 重置请求数据
const requestData:IWxRequestOption<O> = this.requestData = {
url: API.baseUrl + this.url,
data: {}, header: {},
timeout: this.timeout,
method: this.method,
dataType: this.jsonParse ? "json" : undefined,
enableHttp2: this.enableHttp2,
enableQuic: this.enableQuic,
enableCache: this.enableCache
};
// 数据解析
for (let key in this.params) {
let data = this.data[key];
let { parse } = this.params[key];
// 数据预解析
if (parse !== void 0) {
this.data[key] = parse(data!, key, this.data as DeepReadonly<Partial<I>>);
}
}
// 触发数据解析
this.emit("parseRequestData", this.data);
// 数据收集
for (let key in this.params) {
let data = this.data[key];
let { isHeader, isTemplate } = this.params[key];
// 加载数据
if (!isTemplate) {
if (isHeader) {
requestData.header![key] = data;
} else {
(requestData.data as IAnyData)[key] = data;
}
}
}
return this;
}
/**
* 请求的唯一标识符
*/
protected id:number = 0;
/**
* 将此请求加入到请求池
*/
protected addPool():this {
let app = getApp<IAppAPIParam>();
if (app.api.pool.has(this as any)) {
return this;
}
// 获取标识符
if (!this.id) {
this.id = app.api.nextId;
app.api.nextId ++;
}
app.api.pool.add(this as any);
return this;
}
/**
* 从 pool 中移除
*/
protected removePool():this {
let app = getApp<IAppAPIParam>();
app.api.pool.delete(this as any);
return this;
}
/**
* 寻找相似的请求
*/
protected findSameAPI():API<IAnyData, IAnyData> | undefined {
if (this.requestData === void 0) {
Logger.log(`搜索相似请求异常: 没有收集 [requestData] 数据!`,
LevelLogLabel.ErrorLabel, this.LogLabel);
return;
}
let app = getApp<IAppAPIParam>();
let sameAPI:API<IAnyData, IAnyData> | undefined;
// 判断 API 是否相似
app.api.pool.forEach((api) => {
if (api === this) return;
if (!api.requestData) return;
if (api.requestData!.url !== this.requestData!.url) return;
if (api.requestData!.method !== this.requestData!.method) return;
sameAPI = api;
});
return sameAPI;
}
/**
* 标记此实例是否已被使用
*/
private isUsed = false;
/**
* 请求策略
*/
public policy:RequestPolicy = RequestPolicy.RequestAnyway;
/**
* 运行 API
*/
public request():this {
if (this.requestData === void 0) {
Logger.log(`请求发送异常: 没有收集 [requestData] 数据!`,
LevelLogLabel.ErrorLabel, this.LogLabel);
return this;
}
if (this.isUsed) {
Logger.log(`请求发送异常: 此实例已经使用过了,请使用新实例操作`,
LevelLogLabel.ErrorLabel, this.LogLabel);
return this;
} else {
this.isUsed = true;
}
// 加入请求池
this.addPool();
// 发起请求
let request = () => {
// 触发请求发送事件
this.emit("request", this.requestData!)
wx.request<O>({
...this.requestData!,
success: (e) => {
this.emit("success", e);
},
fail: (e) => {
this.emit("fail", e);
},
complete: (e) => {
this.emit("complete", e);
}
});
}
if (this.policy !== RequestPolicy.RequestAnyway) {
let lastAPI = this.findSameAPI();
if (lastAPI) {
// 被上次请求阻止
if (this.policy === RequestPolicy.BlockByLastRequest) {
return this;
}
// 使用上次请求结果
if (this.policy === RequestPolicy.useLastRequest) {
lastAPI.on("success", (e) => {
this.emit("success", e as SuccessCallbackResult<O>);
this.emit("complete", {errMsg: e.errMsg});
});
lastAPI.on("fail", (e) => {
this.emit("fail", e);
this.emit("complete", {errMsg: e.errMsg});
});
}
// 等待上次请求
if (this.policy === RequestPolicy.waitLastRequest) {
lastAPI.on("success", () => request());
lastAPI.on("fail", () => request());
}
}
} else {
request();
}
// 监听请求完成后,从 pool 中移除,释放内存
this.on("complete", () => {
this.removePool();
})
return this;
}
/**
* 等待结果
*/
public wait(): Promise<IRespondData<O>>;
public wait(callBack?: ICallBack<O>): this;
public wait(callBack?: ICallBack<O>): Promise<IRespondData<O>> | this {
// 存在 callback 使用传统回调
if (callBack) {
callBack.success && this.on("success", callBack.success);
callBack.fail && this.on("fail", callBack.fail);
callBack.complete && this.on("complete", callBack.complete);
return this;
}
// 不存在 callback 使用 Promise 对象
else {
return new Promise((r) => {
this.on("success", r);
this.on("fail", r);
});
}
}
}
/**
* 自动响应数据
*/
type IRespondData<O extends IAnyData> = Partial<IAPIEvent<IAnyData, O>["success"]> & IAPIEvent<IAnyData, O>["fail"];
/**
* 回调对象
*/
interface ICallBack<O extends IAnyData> {
success?: (data: IAPIEvent<{}, O>["success"]) => any;
fail?: (data: IAPIEvent<{}, O>["fail"]) => any;
complete?: (data: IAPIEvent<{}, O>["complete"]) => any;
}
/**
* Request 请求策略
* 此策略用于节流
*/
enum RequestPolicy {
/**
* 什么都不管,发就完了
*/
RequestAnyway = 1,
/**
* 如果存在等待中的相似请求
* 等待相似请求结束后在发送
*/
waitLastRequest = 2,
/**
* 如果存在等待中的相似请求
* 阻止本次请求发送
* 将相似请求的结果作为本次请求结果
*/
useLastRequest = 3,
/**
* 如果存在等待中的相似请求
* 阻止本次请求发送
*/
BlockByLastRequest = 4
}
/**
* HTTP 请求类型
*/
enum HTTPMethod {
OPTIONS = "OPTIONS",
GET = "GET",
HEAD = "HEAD",
POST = "POST",
PUT = "PUT",
DELETE = "DELETE",
TRACE = "TRACE",
CONNECT = "CONNECT"
}
export default API;
export { API, IParamSetting, IAppAPIParam, ICallBack, HTTPMethod, RequestPolicy }
+87
View File
@@ -0,0 +1,87 @@
export type EventType = string | symbol;
// An event handler can take an optional event argument
// and should not return a value
export type Handler<T = any> = (event: T) => void;
// An array of all currently registered event handlers for a type
export type EventHandlerList<T = any> = Array<Handler<T>>;
// A map of event types and their corresponding event handlers.
export type EventHandlerMap<Events extends Record<EventType, any>> = Map<
keyof Events,
EventHandlerList<Events[keyof Events]>
>;
export class Emitter<Events extends Record<EventType, any>> {
/**
* A Map of event names to registered handler functions.
*/
public all: EventHandlerMap<Events>;
public constructor() {
this.all = new Map();
}
public resetAll() {
this.all = new Map();
}
public reset<Key extends keyof Events>(type: Key) {
this.all!.set(type, [] as EventHandlerList<Events[keyof Events]>);
}
/**
* Register an event handler for the given type.
* @param {string|symbol} type Type of event to listen for
* @param {Function} handler Function to call in response to given event
* @memberOf mitt
*/
public on<Key extends keyof Events>(type: Key, handler: Handler<Events[Key]>) {
const handlers: Array<Handler<Events[Key]>> | undefined = this.all!.get(type);
if (handlers) {
handlers.push(handler);
}
else {
this.all!.set(type, [handler] as EventHandlerList<Events[keyof Events]>);
}
}
/**
* Remove an event handler for the given type.
* If `handler` is omitted, all handlers of the given type are removed.
* @param {string|symbol} type Type of event to unregister `handler` from
* @param {Function} [handler] Handler function to remove
* @memberOf mitt
*/
public off<Key extends keyof Events>(type: Key, handler?: Handler<Events[Key]>) {
const handlers: Array<Handler<Events[Key]>> | undefined = this.all!.get(type);
if (handlers) {
if (handler) {
handlers.splice(handlers.indexOf(handler) >>> 0, 1);
}
else {
this.all!.set(type, []);
}
}
}
/**
* Invoke all handlers for the given type.
*
* @param {string|symbol} type The event type to invoke
* @param {Any} [evt] Any value (object is recommended and powerful), passed to each handler
* @memberOf mitt
*/
emit<Key extends keyof Events>(type: Key, evt: Events[Key]) {
let handlers = this.all!.get(type);
if (handlers) {
(handlers as EventHandlerList<Events[keyof Events]>)
.slice()
.map((handler) => {
handler(evt!);
});
}
}
}
-119
View File
@@ -1,119 +0,0 @@
export type EventType = string | symbol;
// An event handler can take an optional event argument
// and should not return a value
export type Handler<T = unknown> = (event: T) => void;
export type WildcardHandler<T = Record<string, unknown>> = (
type: keyof T,
event: T[keyof T]
) => void;
// An array of all currently registered event handlers for a type
export type EventHandlerList<T = unknown> = Array<Handler<T>>;
export type WildCardEventHandlerList<T = Record<string, unknown>> = Array<WildcardHandler<T>>;
// A map of event types and their corresponding event handlers.
export type EventHandlerMap<Events extends Record<EventType, unknown>> = Map<
keyof Events | '*',
EventHandlerList<Events[keyof Events]> | WildCardEventHandlerList<Events>
>;
export interface Emitter<Events extends Record<EventType, unknown>> {
all: EventHandlerMap<Events>;
on<Key extends keyof Events>(type: Key, handler: Handler<Events[Key]>): void;
on(type: '*', handler: WildcardHandler<Events>): void;
off<Key extends keyof Events>(type: Key, handler?: Handler<Events[Key]>): void;
off(type: '*', handler: WildcardHandler<Events>): void;
emit<Key extends keyof Events>(type: Key, event: Events[Key]): void;
emit<Key extends keyof Events>(type: undefined extends Events[Key] ? Key : never): void;
}
/**
* Mitt: Tiny (~200b) functional event emitter / pubsub.
* @name mitt
* @returns {Mitt}
*/
export default function mitt<Events extends Record<EventType, unknown>>(
all?: EventHandlerMap<Events>
): Emitter<Events> {
type GenericEventHandler =
| Handler<Events[keyof Events]>
| WildcardHandler<Events>;
all = all || new Map();
return {
/**
* A Map of event names to registered handler functions.
*/
all,
/**
* Register an event handler for the given type.
* @param {string|symbol} type Type of event to listen for, or `'*'` for all events
* @param {Function} handler Function to call in response to given event
* @memberOf mitt
*/
on<Key extends keyof Events>(type: Key, handler: GenericEventHandler) {
const handlers: Array<GenericEventHandler> | undefined = all!.get(type);
if (handlers) {
handlers.push(handler);
}
else {
all!.set(type, [handler] as EventHandlerList<Events[keyof Events]>);
}
},
/**
* Remove an event handler for the given type.
* If `handler` is omitted, all handlers of the given type are removed.
* @param {string|symbol} type Type of event to unregister `handler` from, or `'*'`
* @param {Function} [handler] Handler function to remove
* @memberOf mitt
*/
off<Key extends keyof Events>(type: Key, handler?: GenericEventHandler) {
const handlers: Array<GenericEventHandler> | undefined = all!.get(type);
if (handlers) {
if (handler) {
handlers.splice(handlers.indexOf(handler) >>> 0, 1);
}
else {
all!.set(type, []);
}
}
},
/**
* Invoke all handlers for the given type.
* If present, `'*'` handlers are invoked after type-matched handlers.
*
* Note: Manually firing '*' handlers is not supported.
*
* @param {string|symbol} type The event type to invoke
* @param {Any} [evt] Any value (object is recommended and powerful), passed to each handler
* @memberOf mitt
*/
emit<Key extends keyof Events>(type: Key, evt?: Events[Key]) {
let handlers = all!.get(type);
if (handlers) {
(handlers as EventHandlerList<Events[keyof Events]>)
.slice()
.map((handler) => {
handler(evt!);
});
}
handlers = all!.get('*');
if (handlers) {
(handlers as WildCardEventHandlerList<Events>)
.slice()
.map((handler) => {
handler(type, evt!);
});
}
}
};
}
-227
View File
@@ -1,227 +0,0 @@
/**
* 调试输出样式
*/
class LogStyle {
/**
* 日志文字颜色
*/
private color:string | undefined;
/**
* 日志背景颜色
*/
private backgroundColor:string | undefined;
/**
* 日志文字粗细
*/
private weight:string | undefined;
/**
* 日志文字大小
*/
private size:string | undefined;
/**
* 日志文字字体
*/
private family:string | undefined;
/**
* 日志文字圆角
*/
private borderRadius:string | undefined;
/**
* 日志文字边框
*/
private border:string | undefined;
/**
* 日志文字外边距
*/
private margin:string | undefined;
/**
* 日志文字内边距
*/
private padding:string | undefined;
/**
* 设置颜色
* @param color 日志文字颜色
* @param backgroundColor 日志背景颜色
*/
public setColor(color?:string, backgroundColor?:string):LogStyle {
this.color = color ?? this.color;
this.backgroundColor = backgroundColor ?? this.backgroundColor;
return this;
}
/**
* 设置边框
* @param borderRadius 日志文字圆角
* @param border 日志文字边框
*/
public setBorder(borderRadius?:string, border?:string):LogStyle {
this.borderRadius = borderRadius ?? this.borderRadius;
this.border = border ?? this.border;
return this;
}
/**
* 设置文字
* @param weight 日志文字粗细
* @param family 日志文字字体
*/
public setFont(weight?:string, family?:string):LogStyle {
this.weight = weight ?? this.weight;
this.family = family ?? this.family;
return this;
}
/**
* 设置文字大小
* @param size 日志文字大小
*/
public setSize(size?:string):LogStyle {
this.size = size ?? this.size;
return this;
}
/**
* 设置内边距外边距
* @param padding 内边距
* @param margin 外边距
*/
public setBlank(padding?:string, margin?:string):LogStyle {
this.padding = padding ?? this.padding;
this.margin = margin ?? this.margin;
return this;
}
/**
* 字符化转义样式
*/
public stringify():string {
let stringArr:string[] = [];
this.color && stringArr.push(`color:${ this.color }`);
this.backgroundColor && stringArr.push(`background-color:${ this.backgroundColor }`);
this.weight && stringArr.push(`font-weight:${ this.weight }`);
this.family && stringArr.push(`font-family:${ this.family }`);
this.borderRadius && stringArr.push(`border-radius:${ this.borderRadius }`);
this.border && stringArr.push(`border:${ this.border }`);
this.size && stringArr.push(`font-size:${ this.size }`);
this.padding && stringArr.push(`padding:${ this.padding }`);
this.margin && stringArr.push(`margin:${ this.margin }`);
return stringArr.join(";");
}
/**
* 克隆一个新的 LogStyle
*/
public clone():LogStyle {
return new LogStyle()
.setColor(this.color, this.backgroundColor)
.setBorder(this.borderRadius, this.border)
.setFont(this.weight, this.family)
.setBlank(this.padding, this.margin)
.setSize(this.size)
}
}
/**
* 日志标签
*/
class LogLabel {
/**
* 关键字
* 用于标识这个类别
*/
public key:string;
/**
* 文字样式
*/
public style:LogStyle;
/**
* 是否受到过滤器影响
*/
public checked:boolean;
/**
* 是否输出
*/
public display:boolean;
/**
* 是否为附件标签
* 例如回车、时间、代码位置
*/
public attach:boolean;
/**
* @param key 关键字
* @param style 文字样式
*/
constructor(key:string, style:LogStyle,
checked?:boolean, display?:boolean, attach?:boolean) {
this.key = key;
this.style = style;
this.checked = checked ?? true;
this.display = display ?? true;
this.attach = attach ?? false;
}
/**
* 获得 Logger 输出使用的内容
*/
public getLoggerOutput():string {
if(!this.display) return "";
return `%c${ this.key }`;
}
/**
* 获得 Text 输出内容
*/
public getTextOutput():string {
if(!this.display) return "";
return `[${ this.key }]`;
}
/**
* 获得 style 格式化
*/
public getStyleOutput():string {
if(!this.display) return "";
return this.style.stringify();
}
/**
* 校验
*/
public checking(src:RegExp | string):boolean {
let pass = false;
// 关闭校验
if(!this.checked) return pass;
if(src instanceof RegExp) {
pass = (src as RegExp).test(this.key)
} else {
pass = (src as string) === this.key;
}
return pass;
}
}
export default LogLabel;
export {LogLabel, LogStyle}
+563 -3
View File
@@ -1,7 +1,228 @@
import { LOGGER_FILTER, LOGGER_CONSOLE, LOGGER_STYLE } from "./Config";
import { StackLogLabel } from "./PresetLogLabel";
import { LogLabel } from "./LogLabel";
/**
* 调试输出样式
*/
class LogStyle {
/**
* 日志文字颜色
*/
private color:string | undefined;
/**
* 日志背景颜色
*/
private backgroundColor:string | undefined;
/**
* 日志文字粗细
*/
private weight:string | undefined;
/**
* 日志文字大小
*/
private size:string | undefined;
/**
* 日志文字字体
*/
private family:string | undefined;
/**
* 日志文字圆角
*/
private borderRadius:string | undefined;
/**
* 日志文字边框
*/
private border:string | undefined;
/**
* 日志文字外边距
*/
private margin:string | undefined;
/**
* 日志文字内边距
*/
private padding:string | undefined;
/**
* 设置颜色
* @param color 日志文字颜色
* @param backgroundColor 日志背景颜色
*/
public setColor(color?:string, backgroundColor?:string):LogStyle {
this.color = color ?? this.color;
this.backgroundColor = backgroundColor ?? this.backgroundColor;
return this;
}
/**
* 设置边框
* @param borderRadius 日志文字圆角
* @param border 日志文字边框
*/
public setBorder(borderRadius?:string, border?:string):LogStyle {
this.borderRadius = borderRadius ?? this.borderRadius;
this.border = border ?? this.border;
return this;
}
/**
* 设置文字
* @param weight 日志文字粗细
* @param family 日志文字字体
*/
public setFont(weight?:string, family?:string):LogStyle {
this.weight = weight ?? this.weight;
this.family = family ?? this.family;
return this;
}
/**
* 设置文字大小
* @param size 日志文字大小
*/
public setSize(size?:string):LogStyle {
this.size = size ?? this.size;
return this;
}
/**
* 设置内边距外边距
* @param padding 内边距
* @param margin 外边距
*/
public setBlank(padding?:string, margin?:string):LogStyle {
this.padding = padding ?? this.padding;
this.margin = margin ?? this.margin;
return this;
}
/**
* 字符化转义样式
*/
public stringify():string {
let stringArr:string[] = [];
this.color && stringArr.push(`color:${ this.color }`);
this.backgroundColor && stringArr.push(`background-color:${ this.backgroundColor }`);
this.weight && stringArr.push(`font-weight:${ this.weight }`);
this.family && stringArr.push(`font-family:${ this.family }`);
this.borderRadius && stringArr.push(`border-radius:${ this.borderRadius }`);
this.border && stringArr.push(`border:${ this.border }`);
this.size && stringArr.push(`font-size:${ this.size }`);
this.padding && stringArr.push(`padding:${ this.padding }`);
this.margin && stringArr.push(`margin:${ this.margin }`);
return stringArr.join(";");
}
/**
* 克隆一个新的 LogStyle
*/
public clone():LogStyle {
return new LogStyle()
.setColor(this.color, this.backgroundColor)
.setBorder(this.borderRadius, this.border)
.setFont(this.weight, this.family)
.setBlank(this.padding, this.margin)
.setSize(this.size)
}
}
/**
* 日志标签
*/
class LogLabel {
/**
* 关键字
* 用于标识这个类别
*/
public key:string;
/**
* 文字样式
*/
public style:LogStyle;
/**
* 是否受到过滤器影响
*/
public checked:boolean;
/**
* 是否输出
*/
public display:boolean;
/**
* 是否为附件标签
* 例如回车、时间、代码位置
*/
public attach:boolean;
/**
* @param key 关键字
* @param style 文字样式
*/
constructor(key:string, style:LogStyle,
checked?:boolean, display?:boolean, attach?:boolean) {
this.key = key;
this.style = style;
this.checked = checked ?? true;
this.display = display ?? true;
this.attach = attach ?? false;
}
/**
* 获得 Logger 输出使用的内容
*/
public getLoggerOutput():string {
if(!this.display) return "";
return `%c${ this.key }`;
}
/**
* 获得 Text 输出内容
*/
public getTextOutput():string {
if(!this.display) return "";
return `[${ this.key }]`;
}
/**
* 获得 style 格式化
*/
public getStyleOutput():string {
if(!this.display) return "";
return this.style.stringify();
}
/**
* 校验
*/
public checking(src:RegExp | string):boolean {
let pass = false;
// 关闭校验
if(!this.checked) return pass;
if(src instanceof RegExp) {
pass = (src as RegExp).test(this.key)
} else {
pass = (src as string) === this.key;
}
return pass;
}
}
/**
* 多重内容捆绑
@@ -231,5 +452,344 @@ class Logger {
}
/**
* 栈信息
*/
class StackInfo {
/**
* 函数名
*/
public functionName:string | undefined;
/**
* 文件名
*/
public fileName:string | undefined;
/**
* 文件路径
*/
public url:string | undefined;
/**
* 文件名和行号
*/
public fileNameLine: string | undefined;
/**
* 设置信息
* @param functionName 函数名
* @param fileName 文件名
* @param url 文件路径
*/
public setInfo(functionName:string, fileNameLine:string, url:string):StackInfo {
this.functionName = functionName;
this.fileNameLine = fileNameLine;
this.url = url;
return this;
}
/**
* 计算文件名
*/
public calcFileName():string | undefined {
let replaceToTs = this.fileNameLine?.replace(".js", ".ts");
let matched = replaceToTs?.match(/^(.+\.(js|ts)):\d+:\d+$/);
return matched ? matched[1] : undefined;
}
/**
* 计算路径名
*/
public calcPathName():string | undefined {
let replaceToTs = this.url?.replace(".js", ".ts");
let matched = replaceToTs?.match(/^https?:\/\/(\d+\.){3}\d+:\d+\/(.+):\d+:\d+$/);
return matched ? matched[2] : undefined;
}
/**
* 获取函数调用栈列表
*/
public static getCallStack():StackInfo[] {
// 获取堆栈信息
let stack:string | undefined = new Error().stack;
if (stack === void 0) return [];
// 去除 Error
stack = stack.replace(/^(Error)\s/, "");
// 获取堆栈信息
let stackList:string[] = stack.split(/\n/);
let callStack:StackInfo[] = [];
for(let i = 0; i < stackList.length; i++) {
let matcher = stackList[i].match(/^\s+at\s+(.+)\s(\(.+\))/);
if (matcher === null || matcher.length < 3) continue;
let fileName = matcher[2].match(/.+\/(.+\..+:\d+:\d+)\)/);
if (fileName === null || matcher.length < 2) continue;
callStack.push(new StackInfo().setInfo(
matcher[1], fileName[1], matcher[2]?.replace(/(\(|\))/g, "")
))
}
// console.log(callStack);
return callStack;
}
/**
* 排除的
*/
public static readonly excludeFile:RegExp = /^.*(\\|\/)core(\\|\/)(.*Log.*).js:\d+:\d+/;
/**
* 获取第一个调用栈
*/
public static getFirstStack():StackInfo | undefined {
let callStack = this.getCallStack();
for(let i = 0; i < callStack.length; i++) {
if(!callStack[i].url) continue;
if(!StackInfo.excludeFile.test(callStack[i].url ?? "")) {
return callStack[i];
}
}
return;
}
}
/**
* 内部预定义的 LogLabel
*/
class StackLogLabel {
/**
* 堆栈路径样式
*/
public static readonly normalStyle:LogStyle = new LogStyle()
.setColor("#979797").setBorder("4px", "1px solid #979797").setBlank("0 5px");
/**
* 一个回车
*/
public static readonly blankLabel = new LogLabel("\n\r",
new LogStyle(), false, true, true);
/**
* 包含文件名和行号的 label
*/
public static get fileNameLabel():LogLabel {
// 获得调用堆栈
let stack = StackInfo.getFirstStack();
return new LogLabel(stack?.calcFileName() ?? "Unknown file name",
StackLogLabel.normalStyle, false, true, true);
}
/**
* 包含 URL 链接的 label
*/
public static get urlLabel():LogLabel {
// 获得调用堆栈
let stack = StackInfo.getFirstStack();
return new LogLabel(stack?.calcPathName() ?? "Unknown url",
StackLogLabel.normalStyle, false, true, true);
}
/**
* 仅仅用来 filter 的 URL 链接的 label
*/
public static get filterUrlLabel():LogLabel {
// 获得调用堆栈
let stack = StackInfo.getFirstStack();
return new LogLabel(stack?.calcPathName() ?? "Unknown url",
StackLogLabel.normalStyle, true, false, true);
}
}
/**
* 生成圆角颜色标签样式
*/
const normalLevelStyleGen = (color:string):LogStyle => {
return new LogStyle().setBorder("4px", `1px solid ${color}`)
.setColor(color).setBlank("0 5px");
}
/**
* 调试等级标签
*/
class LevelLogLabel {
/**
* 致命
*/
static readonly FatalLabel = new LogLabel(
"FATAL", normalLevelStyleGen("#FF00CC")
);
/**
* 错误
*/
static readonly ErrorLabel = new LogLabel(
"ERROR", normalLevelStyleGen("#FF0000")
);
/**
* 警告
*/
static readonly WarnLabel = new LogLabel(
"WARN", normalLevelStyleGen("#FF9900")
);
/**
* 消息
*/
static readonly InfoLabel = new LogLabel(
"INFO", normalLevelStyleGen("#99FF00")
);
/**
* 调试
*/
static readonly DebugLabel = new LogLabel(
"DEBUG", normalLevelStyleGen("#00FF99")
);
/**
* 追踪
*/
static readonly TraceLabel = new LogLabel(
"TRACE", normalLevelStyleGen("#00CCFF")
);
}
/**
* 生成圆角颜色标签样式
*/
const normalLifeStyleGen = (r:number, g:number, b:number):LogStyle => {
return new LogStyle().setBorder("4px", `1px solid rgb(${ r }, ${ g }, ${ b })`)
.setColor(`rgb(${ r }, ${ g }, ${ b })`, `rgba(${ r }, ${ g }, ${ b }, .1)`)
.setBlank("0 5px");
}
/**
* 生命周期标签
*/
class LifeCycleLogLabel {
/**
* 小程序加载时
*/
static readonly OnLaunchLabel = new LogLabel(
"onLaunch", normalLifeStyleGen(160, 32, 240)
);
/**
* 生命周期函数--监听页面加载
*/
static readonly OnLoadLabel = new LogLabel(
"onLoad", normalLifeStyleGen(255, 140, 105)
);
/**
* 生命周期函数--监听页面初次渲染完成
*/
static readonly OnReadyLabel = new LogLabel(
"onReady", normalLifeStyleGen(255, 127, 36)
);
/**
* 生命周期函数--监听页面显示
*/
static readonly OnShowLabel = new LogLabel(
"onShow", normalLifeStyleGen(255, 215, 0)
)
/**
* 生命周期函数--监听页面隐藏
*/
static readonly OnHideLabel = new LogLabel(
"onHide", normalLifeStyleGen(173, 255, 47)
);
/**
* 生命周期函数--监听页面卸载
*/
static readonly OnUnloadLabel = new LogLabel(
"onUnload", normalLifeStyleGen(127, 255, 212)
);
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
static readonly OnPullDownRefreshLabel = new LogLabel(
"onPullDownRefresh", normalLifeStyleGen(0, 191, 255)
);
/**
* 页面上拉触底事件的处理函数
*/
static readonly OnReachBottomLabel = new LogLabel(
"onReachBottom", normalLifeStyleGen(84, 255, 159)
);
/**
* 用户点击右上角分享
*/
static readonly OnShareAppMessageLabel = new LogLabel(
"onShareAppMessage", normalLifeStyleGen(147, 112, 219)
);
}
class StatusLabel {
/**
* 等待
*/
static readonly Pending = new LogLabel(
"◉", new LogStyle().setBlank("0 2px").setBorder("1000px", "1px solid lightblue").setColor("lightblue")
);
/**
* 成功
*/
static readonly Success = new LogLabel(
"√", new LogStyle().setBlank("0 4px").setBorder("1000px", "1px solid lightgreen").setColor("lightgreen")
);
/**
* 失败
*/
static readonly Failed = new LogLabel(
"x", new LogStyle().setBlank("0 4px").setBorder("1000px", "1px solid red").setColor("red")
);
}
const NormalStyle = StackLogLabel.normalStyle;
export default Logger;
export { Logger };
export {
Logger, LogLabel, LevelLogLabel, StackLogLabel, LifeCycleLogLabel, LogStyle,
StackInfo, StatusLabel, NormalStyle, normalLifeStyleGen as colorRadio
};
+173 -139
View File
@@ -1,7 +1,5 @@
import mitt, { Emitter, EventHandlerMap, EventType, Handler, WildcardHandler } from "./EventEmitter";
import { LogLabel, LogStyle } from "./LogLabel";
import { Logger } from "./Logger";
import { LevelLogLabel } from "./PresetLogLabel";
import { Emitter } from "./Emitter";
import { Logger, LogLabel, colorRadio, LevelLogLabel } from "./Logger";
/**
* 自定义对象类型
@@ -52,6 +50,149 @@ type Depends<M extends Manager<AnyWXContext>> = {
[x:string]: Modular<M, Depends<M>>;
};
/**
* 微信继承的函数
*/
class WXInstanceMethods<
E extends IAnyTypeObject = IAnyTypeObject,
W extends AnyWXContext = AnyWXContext
>
extends Emitter<E>
implements InstanceMethods<W["data"]> {
public superContext: W;
public constructor(context: W) {
super();
this.superContext = context;
}
public setData(data: Partial<W["data"]> & WechatMiniprogram.IAnyObject, callback?: () => void): void {
return this.superContext.setData(data, callback);
}
public hasBehavior(behavior: string): void {
return this.superContext.hasBehavior(behavior);
}
public triggerEvent<DetailType = any>(name: string, detail?: DetailType, options?: WechatMiniprogram.Component.TriggerEventOption): void {
return this.superContext.triggerEvent(name, detail, options)
}
public createSelectorQuery(): WechatMiniprogram.SelectorQuery {
return this.superContext.createSelectorQuery();
}
public createIntersectionObserver(options: WechatMiniprogram.CreateIntersectionObserverOption): WechatMiniprogram.IntersectionObserver {
return this.superContext.createIntersectionObserver(options);
}
public selectComponent(selector: string): WechatMiniprogram.Component.TrivialInstance {
return this.superContext.selectComponent(selector);
}
public selectAllComponents(selector: string): WechatMiniprogram.Component.TrivialInstance[] {
return this.superContext.selectAllComponents(selector);
}
public selectOwnerComponent(): WechatMiniprogram.Component.TrivialInstance {
return this.superContext.selectOwnerComponent();
}
public getRelationNodes(relationKey: string): WechatMiniprogram.Component.TrivialInstance[] {
return this.superContext.getRelationNodes(relationKey);
}
public groupSetData(callback?: () => void): void {
return this.superContext.groupSetData(callback);
}
public getTabBar(): WechatMiniprogram.Component.TrivialInstance {
return this.superContext.getTabBar();
}
public getPageId(): string {
return this.superContext.getPageId();
}
public animate(selector: string, keyFrames: WechatMiniprogram.Component.KeyFrame[], duration: number, callback?: () => void): void;
public animate(selector: string, keyFrames: WechatMiniprogram.Component.ScrollTimelineKeyframe[], duration: number,
scrollTimeline: WechatMiniprogram.Component.ScrollTimelineOption): void;
public animate(selector: any, keyFrames: any, duration: any, scrollTimeline?: any): void {
return this.superContext.animate(selector, keyFrames, duration, scrollTimeline);
}
public clearAnimation(selector: string, callback: () => void): void;
public clearAnimation(selector: string, options?: WechatMiniprogram.Component.ClearAnimationOptions, callback?: () => void): void;
public clearAnimation(selector: any, options?: any, callback?: any): void {
return this.superContext.clearAnimation(selector, options, callback);
}
public getOpenerEventChannel(): WechatMiniprogram.EventChannel {
return this.superContext.getOpenerEventChannel();
}
}
/**
* 微信继承的属性
*/
class WXInstanceProperties<
E extends IAnyTypeObject = IAnyTypeObject,
W extends AnyWXContext = AnyWXContext
>
extends WXInstanceMethods<E, W>
implements InstanceProperties {
public override superContext: W;
constructor(context: W) {
super(context);
this.superContext = context;
}
public get is(): string { return this.superContext.is };
public get route(): string { return this.superContext.route };
public get options(): Record<string, string | undefined> { return this.superContext.options };
}
class WXILifetime<
E extends IAnyTypeObject = IAnyTypeObject,
W extends AnyWXContext = AnyWXContext
>
extends WXInstanceProperties<E, W>
implements ILifetime {
public onLoad(query: Record<string, string | undefined>): void | Promise<void> {};
public onShow(): void | Promise<void> {};
public onReady(): void | Promise<void> {};
public onHide(): void | Promise<void> {};
public onUnload(): void | Promise<void> {};
public onPullDownRefresh(): void | Promise<void> {};
public onReachBottom(): void | Promise<void> {};
public onShareAppMessage(options: WechatMiniprogram.Page.IShareAppMessageOption): void | WechatMiniprogram.Page.ICustomShareContent {};
public onShareTimeline(): void | WechatMiniprogram.Page.ICustomTimelineContent {};
public onPageScroll(options: WechatMiniprogram.Page.IPageScrollOption): void | Promise<void> {};
public onTabItemTap(options: WechatMiniprogram.Page.ITabItemTapOption): void | Promise<void> {};
public onResize(options: WechatMiniprogram.Page.IResizeOption): void | Promise<void> {};
public onAddToFavorites(options: WechatMiniprogram.Page.IAddToFavoritesOption): WechatMiniprogram.Page.IAddToFavoritesContent {
return {};
};
}
/**
* 页面模组
* @template M 所属 Manager
@@ -62,9 +203,11 @@ type Depends<M extends Manager<AnyWXContext>> = {
class Modular<
M extends Manager<AnyWXContext> = Manager<AnyWXContext>,
DEP extends Depends<M> = Depends<M>,
E extends Record<EventType, unknown> = Record<EventType, unknown>,
TD extends IAnyTypeObject = IAnyTypeObject>
implements WXContext<TD, IAnyTypeObject>, Emitter<E> {
E extends IAnyTypeObject = IAnyTypeObject,
TD extends IAnyTypeObject = IAnyTypeObject
>
extends WXILifetime<E, M["context"]>
implements WXContext<TD, IAnyTypeObject> {
// [x:string]: any;
@@ -96,7 +239,7 @@ implements WXContext<TD, IAnyTypeObject>, Emitter<E> {
public functionList:Set<string>;
/**
* 函数使用的参数列表
* 模组使用的参数列表
*/
public paramList:Set<string>;
@@ -105,13 +248,6 @@ implements WXContext<TD, IAnyTypeObject>, Emitter<E> {
*/
public nameSpace:string;
// 映射主上下文属性
public get is():string { return this.context.is };
public get route():string { return this.context.route };
public get options():Record<string, string | undefined> {
return this.context.options;
};
/**
* 一旦被类被构造,整个页面的全部申明周期将
* 由该类实例控制,此后请勿修改任何生命周期函数
@@ -121,6 +257,8 @@ implements WXContext<TD, IAnyTypeObject>, Emitter<E> {
*/
public constructor(manager:M, nameSpace:string, depend?: DEP) {
super(manager.context);
// 保存微信上下文
this.manager = manager;
@@ -131,37 +269,9 @@ implements WXContext<TD, IAnyTypeObject>, Emitter<E> {
this.functionList = new Set<string>();
this.paramList = new Set<string>();
this.nameSpace = nameSpace;
this.emitter = mitt<E>();
}
/**
* 内部事件控制器
*/
private emitter:Emitter<E>;
public get all():EventHandlerMap<E> { return this.emitter.all };
on<Key extends keyof E>(type: Key, handler: Handler<E[Key]>): void;
on(type: "*", handler: WildcardHandler<E>): void;
on(type: any, handler: any): void {
return this.emitter.on(type, handler);
}
off<Key extends keyof E>(type: Key, handler?: Handler<E[Key]>): void;
off(type: "*", handler: WildcardHandler<E>): void;
off(type: any, handler?: any): void {
return this.emitter.off(type, handler);
}
emit<Key extends keyof E>(type: Key, event: E[Key]): void;
emit<Key extends keyof E>(type: undefined extends E[Key] ? Key : never): void;
emit(type: any, event?: any): void {
return this.emitter.emit(type, event);
}
public setData(data:Partial<TD>, callback?: () => void):void {
public override setData(data:Partial<TD>, callback?: () => void):void {
if(this.data === void 0) {
this.data = {} as TD;
@@ -189,76 +299,6 @@ implements WXContext<TD, IAnyTypeObject>, Emitter<E> {
(this.context as IAnyTypeObject)
[`${ this.nameSpace }$${ name }`] = fn.bind(this);
}
//#region 映射微信的继承函数
public hasBehavior(behavior: string): void {
return this.context.hasBehavior(behavior);
}
public triggerEvent<DetailType>(name: string, detail?: DetailType,
options?: WechatMiniprogram.Component.TriggerEventOption): void {
return this.context.triggerEvent<DetailType>(name, detail, options);
}
public createSelectorQuery(): WechatMiniprogram.SelectorQuery {
return this.context.createSelectorQuery();
}
public createIntersectionObserver(options: WechatMiniprogram.CreateIntersectionObserverOption):
WechatMiniprogram.IntersectionObserver {
return this.context.createIntersectionObserver(options);
}
public selectComponent(selector: string): WechatMiniprogram.Component.TrivialInstance {
return this.context.selectComponent(selector);
}
public selectAllComponents(selector: string): WechatMiniprogram.Component.TrivialInstance[] {
return this.context.selectAllComponents(selector);
}
public selectOwnerComponent(): WechatMiniprogram.Component.TrivialInstance {
return this.context.selectOwnerComponent();
}
public getRelationNodes(relationKey: string): WechatMiniprogram.Component.TrivialInstance[] {
return this.context.getRelationNodes(relationKey);
}
public groupSetData(callback?: () => void): void {
return this.context.groupSetData(callback);
}
public getTabBar(): WechatMiniprogram.Component.TrivialInstance {
return this.context.getTabBar();
}
public getPageId(): string {
return this.context.getPageId();
}
public animate(selector: string, keyFrames: WechatMiniprogram.Component.KeyFrame[],
duration: number, callback?: () => void): void;
public animate(selector: string, keyFrames: WechatMiniprogram.Component.ScrollTimelineKeyframe[],
duration: number, scrollTimeline: WechatMiniprogram.Component.ScrollTimelineOption): void;
public animate(selector: any, keyFrames: any, duration: any, scrollTimeline?: any): void {
return this.context.animate(selector, keyFrames, duration, scrollTimeline);
}
public clearAnimation(selector: string, callback: () => void): void;
public clearAnimation(selector: string, options?: WechatMiniprogram.Component.ClearAnimationOptions,
callback?: () => void): void;
public clearAnimation(selector: any, options?: any, callback?: any): void {
return this.context.clearAnimation(selector, options, callback);
}
public getOpenerEventChannel(): WechatMiniprogram.EventChannel {
return this.context.getOpenerEventChannel();
}
//#endregion
}
/**
@@ -271,19 +311,9 @@ class Manager<WXC extends AnyWXContext = AnyWXContext> {
* 微信生命周期
*/
static readonly WxLifeCycle:(keyof ILifetime)[] = [
"onShow",
"onReady",
"onHide",
"onUnload",
"onPullDownRefresh",
"onReachBottom",
"onShareAppMessage",
"onShareTimeline",
"onAddToFavorites",
"onPageScroll",
"onResize",
"onTabItemTap"
]
"onShow", "onReady", "onHide", "onUnload", "onPullDownRefresh", "onReachBottom",
"onShareAppMessage", "onShareTimeline","onAddToFavorites","onPageScroll", "onResize", "onTabItemTap"
];
/**
* 保存页面上下文
@@ -315,9 +345,10 @@ class Manager<WXC extends AnyWXContext = AnyWXContext> {
* @param depend 模块依赖
* @returns 模块实例
*/
public addModule<DEP extends Depends<this>, M extends Modular<this, DEP>>
(mode: new (manager:Manager<WXC>, nameSpace:string, depend?:DEP) => M,
nameSpace:string, depend?:DEP):M {
public addModule<DEP extends Depends<this>, M extends Modular<this, DEP>> (
mode: new (manager:this, nameSpace:string, depend?:DEP) => M,
nameSpace:string, depend?:DEP
):M {
let mod = new mode(this, nameSpace, depend);
this.modules.push(mod);
return mod;
@@ -327,14 +358,14 @@ class Manager<WXC extends AnyWXContext = AnyWXContext> {
* 创建指定生命周期的钩子
* @param key 生命周期键值
*/
public creatHooks(key:keyof ILifetime):(...arg: any[]) => Promise<any> {
return async (...arg: any[]) => {
public creatHooks<Key extends keyof ILifetime>(key: Key): ILifetime[Key] {
let hook = (async (...arg: any[]) => {
let hooks:Promise<any>[] = [];
for(let i = 0; i < this.modules.length; i++) {
let fn:Function = (this.modules[i] as IAnyTypeObject)[key];
let fn:Function = this.modules[i][key];
if(fn === void 0) continue;
let res: Promise<any> | any = fn.apply(this.modules[i], arg);
@@ -367,7 +398,10 @@ class Manager<WXC extends AnyWXContext = AnyWXContext> {
}
return Promise.all(hooks);
}
});
// TODO: 此处为,关键位置,容易出错,请再次检查
return (hook as any);
}
/**
@@ -410,7 +444,7 @@ class Manager<WXC extends AnyWXContext = AnyWXContext> {
this.context.setData(this.context.data);
// 调用全部模块的 onLoad 周期
let res = this.creatHooks("onLoad")(query as any);
let res = this.creatHooks("onLoad")(query);
// 打印每个模块的键值对使用情况
for(let i = 0; i < this.modules.length; i++) {
@@ -429,7 +463,7 @@ class Manager<WXC extends AnyWXContext = AnyWXContext> {
if(data.length > 0) log += `Using Props: ${ data.join(", ") }\n`;
if(func.length > 0) log += `Using Function: ${ func.join(", ") }\n`;
Logger.log(log, LevelLogLabel.TraceLabel, Manager.AddModuleLabel);
Logger.log(log, LevelLogLabel.InfoLabel, Manager.AddModuleLabel);
}
return res;
@@ -438,9 +472,9 @@ class Manager<WXC extends AnyWXContext = AnyWXContext> {
/**
* 模块被添加时的标签
*/
public static readonly AddModuleLabel = new LogLabel("addModule",
new LogStyle().setBorder("4px", `1px solid #8600FF`)
.setColor("#FF00FF", "rgba(54, 0, 255, .2)").setBlank("0 5px")
public static readonly AddModuleLabel = new LogLabel(
"addModule",
colorRadio(54, 0, 255)
)
/**
-322
View File
@@ -1,322 +0,0 @@
import { LogLabel, LogStyle } from "./LogLabel";
/**
* 栈信息
*/
class StackInfo {
/**
* 函数名
*/
public functionName:string | undefined;
/**
* 文件名
*/
public fileName:string | undefined;
/**
* 文件路径
*/
public url:string | undefined;
/**
* 文件名和行号
*/
public fileNameLine: string | undefined;
/**
* 设置信息
* @param functionName 函数名
* @param fileName 文件名
* @param url 文件路径
*/
public setInfo(functionName:string, fileNameLine:string, url:string):StackInfo {
this.functionName = functionName;
this.fileNameLine = fileNameLine;
this.url = url;
return this;
}
/**
* 计算文件名
*/
public calcFileName():string | undefined {
let replaceToTs = this.fileNameLine?.replace(".js", ".ts");
let matched = replaceToTs?.match(/^(.+\.(js|ts)):\d+:\d+$/);
return matched ? matched[1] : undefined;
}
/**
* 计算路径名
*/
public calcPathName():string | undefined {
let replaceToTs = this.url?.replace(".js", ".ts");
let matched = replaceToTs?.match(/^https?:\/\/(\d+\.){3}\d+:\d+\/(.+):\d+:\d+$/);
return matched ? matched[2] : undefined;
}
/**
* 获取函数调用栈列表
*/
public static getCallStack():StackInfo[] {
// 获取堆栈信息
let stack:string | undefined = new Error().stack;
if (stack === void 0) return [];
// 去除 Error
stack = stack.replace(/^(Error)\s/, "");
// 获取堆栈信息
let stackList:string[] = stack.split(/\n/);
let callStack:StackInfo[] = [];
for(let i = 0; i < stackList.length; i++) {
let matcher = stackList[i].match(/^\s+at\s+(.+)\s(\(.+\))/);
if (matcher === null || matcher.length < 3) continue;
let fileName = matcher[2].match(/.+\/(.+\..+:\d+:\d+)\)/);
if (fileName === null || matcher.length < 2) continue;
callStack.push(new StackInfo().setInfo(
matcher[1], fileName[1], matcher[2]?.replace(/(\(|\))/g, "")
))
}
// console.log(callStack);
return callStack;
}
/**
* 排除的
*/
public static readonly excludeFile:RegExp = /^.*(\\|\/)core(\\|\/)(.*Log.*).js:\d+:\d+/;
/**
* 获取第一个调用栈
*/
public static getFirstStack():StackInfo | undefined {
let callStack = this.getCallStack();
for(let i = 0; i < callStack.length; i++) {
if(!callStack[i].url) continue;
if(!StackInfo.excludeFile.test(callStack[i].url ?? "")) {
return callStack[i];
}
}
return;
}
}
/**
* 内部预定义的 LogLabel
*/
class StackLogLabel {
/**
* 堆栈路径样式
*/
public static readonly normalStyle:LogStyle = new LogStyle()
.setColor("#979797").setBorder("4px", "1px solid #979797").setBlank("0 5px");
/**
* 一个回车
*/
public static readonly blankLabel = new LogLabel("\n\r",
new LogStyle(), false, true, true);
/**
* 包含文件名和行号的 label
*/
public static get fileNameLabel():LogLabel {
// 获得调用堆栈
let stack = StackInfo.getFirstStack();
return new LogLabel(stack?.calcFileName() ?? "Unknown file name",
StackLogLabel.normalStyle, false, true, true);
}
/**
* 包含 URL 链接的 label
*/
public static get urlLabel():LogLabel {
// 获得调用堆栈
let stack = StackInfo.getFirstStack();
return new LogLabel(stack?.calcPathName() ?? "Unknown url",
StackLogLabel.normalStyle, false, true, true);
}
/**
* 仅仅用来 filter 的 URL 链接的 label
*/
public static get filterUrlLabel():LogLabel {
// 获得调用堆栈
let stack = StackInfo.getFirstStack();
return new LogLabel(stack?.calcPathName() ?? "Unknown url",
StackLogLabel.normalStyle, true, false, true);
}
}
/**
* 生成圆角颜色标签样式
*/
const normalLevelStyleGen = (color:string):LogStyle => {
return new LogStyle().setBorder("4px", `1px solid ${color}`)
.setColor(color).setBlank("0 5px");
}
/**
* 调试等级标签
*/
class LevelLogLabel {
/**
* 致命
*/
static readonly FatalLabel = new LogLabel(
"FATAL", normalLevelStyleGen("#FF00CC")
);
/**
* 错误
*/
static readonly ErrorLabel = new LogLabel(
"ERROR", normalLevelStyleGen("#FF0000")
);
/**
* 警告
*/
static readonly WarnLabel = new LogLabel(
"WARN", normalLevelStyleGen("#FF9900")
);
/**
* 消息
*/
static readonly InfoLabel = new LogLabel(
"INFO", normalLevelStyleGen("#99FF00")
);
/**
* 调试
*/
static readonly DebugLabel = new LogLabel(
"DEBUG", normalLevelStyleGen("#00FF99")
);
/**
* 追踪
*/
static readonly TraceLabel = new LogLabel(
"TRACE", normalLevelStyleGen("#00CCFF")
);
}
/**
* 生成圆角颜色标签样式
*/
const normalLifeStyleGen = (r:number, g:number, b:number):LogStyle => {
return new LogStyle().setBorder("4px", `1px solid rgb(${ r }, ${ g }, ${ b })`)
.setColor(`rgb(${ r }, ${ g }, ${ b })`, `rgba(${ r }, ${ g }, ${ b }, .1)`)
.setBlank("0 5px");
}
/**
* 生命周期标签
*/
class LifeCycleLogLabel {
/**
* 小程序加载时
*/
static readonly OnLaunchLabel = new LogLabel(
"onLaunch", normalLifeStyleGen(160, 32, 240)
);
/**
* 生命周期函数--监听页面加载
*/
static readonly OnLoadLabel = new LogLabel(
"onLoad", normalLifeStyleGen(255, 140, 105)
);
/**
* 生命周期函数--监听页面初次渲染完成
*/
static readonly OnReadyLabel = new LogLabel(
"onReady", normalLifeStyleGen(255, 127, 36)
);
/**
* 生命周期函数--监听页面显示
*/
static readonly OnShowLabel = new LogLabel(
"onShow", normalLifeStyleGen(255, 215, 0)
)
/**
* 生命周期函数--监听页面隐藏
*/
static readonly OnHideLabel = new LogLabel(
"onHide", normalLifeStyleGen(173, 255, 47)
);
/**
* 生命周期函数--监听页面卸载
*/
static readonly OnUnloadLabel = new LogLabel(
"onUnload", normalLifeStyleGen(127, 255, 212)
);
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
static readonly OnPullDownRefreshLabel = new LogLabel(
"onPullDownRefresh", normalLifeStyleGen(0, 191, 255)
);
/**
* 页面上拉触底事件的处理函数
*/
static readonly OnReachBottomLabel = new LogLabel(
"onReachBottom", normalLifeStyleGen(84, 255, 159)
);
/**
* 用户点击右上角分享
*/
static readonly OnShareAppMessageLabel = new LogLabel(
"onShareAppMessage", normalLifeStyleGen(147, 112, 219)
);
}
const NormalStyle = StackLogLabel.normalStyle;
export {
NormalStyle,
StackInfo,
StackLogLabel,
LevelLogLabel,
LifeCycleLogLabel,
normalLifeStyleGen as colorRadio,
};
+69 -9
View File
@@ -1,6 +1,12 @@
import { LogLabel } from "./LogLabel";
import { Logger } from "./Logger";
import { LevelLogLabel, colorRadio } from "./PresetLogLabel";
import { Logger, LogLabel, LevelLogLabel, colorRadio } from "./Logger";
interface IAppStorageParam {
/**
* storage 缓存
*/
storage: Map<string, Storage<IStorageData>>
}
/**
* 状态
@@ -83,7 +89,8 @@ type IStorageData = {
* 1. 该类封装了 wxStorage 操作
* 2. 全异步获取无阻塞
* 3. 使用数据缓缓冲区,优化高频存取
* 4.
* 4. 如果全局范围内已存在莫键值 storage 的实例
* 则此实例将链接到已存在实例
*/
class Storage<T extends IStorageData> {
@@ -105,18 +112,41 @@ class Storage<T extends IStorageData> {
/**
* 缓存数据
*/
private cache:T;
private _cache: T = {} as T;
private set cache(data: T) {
if (this.cacheStorage) {
for (const key in data) {
this.cacheStorage.cache[key] = data[key];
}
} else {
for (const key in data) {
this._cache[key] = data[key];
}
}
}
private get cache():T {
if (this.cacheStorage) return this.cacheStorage.cache;
else return this._cache;
}
/**
* cache 到 storage 等待列表
*/
private saveWaiter:Waiter = new Waiter();
/**
* 缓存对象
*/
private cacheStorage:Storage<T> | undefined;
/**
* 将数据从 cache 同步到 storage
*/
public async save():Promise<void> {
// 如果存在链接的实例
if (this.cacheStorage) return this.cacheStorage.save();
// 如果没有开始存储
// 发起一次异步读取
if(this.saveWaiter.state === StorageState.DONE)
@@ -131,7 +161,7 @@ class Storage<T extends IStorageData> {
success: (data) => {
Logger.log(`数据保存成功! errMsg: ${ data.errMsg }`,
LevelLogLabel.TraceLabel, this.StorageLogLabel);
LevelLogLabel.InfoLabel, this.StorageLogLabel);
},
fail: (data) => {
@@ -152,6 +182,24 @@ class Storage<T extends IStorageData> {
});
}
/**
* 在缓存中搜索此键值的实例
*/
private findStorageCache(): boolean {
let { storage: storageMap } = getApp<IAppStorageParam>();
// 查找缓存
let storage = storageMap.get(this.key);
if (storage) {
this.cacheStorage = storage as Storage<T>;
return true;
};
// 缓存此实例
storageMap.set(this.key, this);
return false;
}
/**
* 重置全部数据
*/
@@ -166,13 +214,25 @@ class Storage<T extends IStorageData> {
/**
* @param defaultData 键值默认数据
*/
public constructor(key:string, defaultData:T) {
public constructor(key:string, defaultData?:T) {
this.key = key;
this.defaultData = defaultData;
this.defaultData = defaultData ?? {} as T;
this.StorageLogLabel = new LogLabel(
`Storage:${ this.key }`, colorRadio(34, 230, 258)
);
// 如果已找到其他实力,将此实例链接到目标实例
if (this.findStorageCache()) {
// 设置默认值
for (const key in this.defaultData) {
if (this.cache[key] === void 0) {
this.set(key, this.defaultData[key]);
}
}
return;
};
// 读取数据到缓存
this.cache = wx.getStorageSync<T>(this.key);
@@ -207,4 +267,4 @@ class Storage<T extends IStorageData> {
}
export default Storage;
export { Storage, StorageState, Waiter };
export { Storage, StorageState, Waiter, IAppStorageParam, IStorageData };
-205
View File
@@ -1,205 +0,0 @@
// import { Logger } from "../logger/Logger";
import { LogStyle, LogLabel } from "./LogLabel";
/**
* 测试结果
*/
class TestResult {
/**
* 用例名称
*/
public caseName:string;
/**
* 测试结果
*/
public result:boolean;
/**
* 消息
*/
public message:string;
/**
* 附加消息
*/
public attach:string;
/**
* 初始化
* @param caseName 用例名称
*/
constructor(caseName:string) {
this.caseName = caseName;
this.result = false;
this.message = "";
this.attach = "";
}
/**
* 设置结果
*/
public setResult(result:boolean, message?:string, attach?:string) {
this.result = result;
this.message = message ?? (result ? "success!" : "failed!");
this.attach = attach ?? this.attach;
return this;
}
}
/**
* 测试函数结构
*/
type TestFunction = () => TestResult | Promise<TestResult>;
/**
* 收集测试函数结构
*/
class CaseCollect {
/**
* 用例键名
*/
public key:string;
/**
* 用例测试函数
*/
public caseFunction:TestFunction;
/**
* 测试结果
*/
result: Promise<TestResult> | undefined;
/**
* @param key 测试用例键名
* @param caseFunction 测试函数
*/
public constructor(key:string, caseFunction:TestFunction) {
this.key = key;
this.caseFunction = caseFunction;
}
/**
* 运行测试用例
*/
public async runTestCase():Promise<CaseCollect> {
let result = this.caseFunction();
if(result instanceof Promise) {
this.result = result;
} else {
this.result = Promise.resolve(result);
}
return this;
}
public static readonly baseStyle = new LogStyle().setBlank();
public static readonly successLabel:LogLabel = new LogLabel("√",
new LogStyle().setBlank("0 4px").setBorder("1000px", "1px solid green")
);
/**
* 打印结果
* @param current 当前进度
* @param total 总进度
*/
public printResult(current?:number, total?:number) {
// 如果测试没有运行,先运行它
if(this.result === void 0) this.runTestCase();
this.result?.then((res) => {
if(res.result) {
console.log(
`%c√%c %c1/1%c %c${ this.key }%c ` + res.message,
"padding:0 4px; border-radius:1000px; border:1px solid green; color:green",
"", "padding:0 4px; border-radius:4px; border:1px solid green; color:green",
"", "padding:0 4px; border-radius:4px; border:1px solid #979797; color:#979797",
""
)
} else {
console.log(
`%c√%c %c1/1%c %c${ this.key }%c ` + res.message,
"padding:0 4px; border-radius:1000px; border:1px solid red; color:red",
"", "padding:0 4px; border-radius:4px; border:1px solid red; color:red",
"", "padding:0 4px; border-radius:4px; border:1px solid #979797; color:#979797",
""
)
}
console.log(res)
})
}
/**
* 收集测试用例
* @param testCaseClass 测试用例表
*/
public static collectCase(testCaseClass:ITestCase):CaseCollect[] {
// 获取静态方法 key
let key = Object.getOwnPropertyNames(testCaseClass);
// 过滤掉通用的方法和属性
key = key.filter((key) => !/(length|name|prototype)/.test(key) );
// 生成 CaseCollect
let caseCollect = [];
for (let i = 0; i < key.length; i++) {
caseCollect.push(new CaseCollect(key[i], testCaseClass[key[i]]))
}
return caseCollect;
}
/**
* 运行测试样例
*/
public static async runCollectCase(cases:CaseCollect[]):Promise<CaseCollect[]> {
let running:Promise<CaseCollect>[] = [];
for(let i = 0; i < cases.length; i++) {
running.push(cases[i].runTestCase());
}
return Promise.all(running);
}
/**
* 启动单元测试
*/
public static runUnitTest(testCaseClass:ITestCase) {
let caseCollect = this.collectCase(testCaseClass);
CaseCollect.runCollectCase(caseCollect).then((caseCollect:CaseCollect[]) => {
for(let i = 0; i < caseCollect.length; i++) {
caseCollect[i].printResult()
}
})
}
}
/**
* 测试用例接口
*/
interface ITestCase {
/**
* 测试用例函数
*/
[key:string]:TestFunction;
}
export default ITestCase;
export { ITestCase, TestResult, TestFunction, CaseCollect };
+6 -8
View File
@@ -1,12 +1,10 @@
import { Modular, Manager, ILifetime } from "../../core/Module";
import { LevelLogLabel, LifeCycleLogLabel, NormalStyle } from "../../core/PresetLogLabel";
import { LogLabel } from "../../core/LogLabel";
import { Logger } from "../../core/Logger";
import { Logger, LogLabel, LevelLogLabel, LifeCycleLogLabel, NormalStyle } from "../../core/Logger";
/**
* 在 UI 中显示的数据
*/
type DisplayData = {
interface IDisplayData {
/**
* 显示内容
@@ -22,8 +20,8 @@ type DisplayData = {
/**
* 模组事件
*/
type StatusBarEvent = {
m: DisplayData
interface StatusBarEvent {
m: IDisplayData
};
/**
@@ -101,14 +99,14 @@ implements Partial<ILifetime> {
Logger.log(`计算并设置 StatusBar 的高度为: ${ barHeight + 4 }px, ` +
`状态栏高度: ${ statusBarHeight }px, 胶囊占位: ${ capsule }px`,
LevelLogLabel.DebugLabel, StatusBar.StatusBarLabel);
LevelLogLabel.InfoLabel, StatusBar.StatusBarLabel);
}
public onLoad() {
Logger.log("StatusBar 模块加载...",
LevelLogLabel.TraceLabel, LifeCycleLogLabel.OnLoadLabel, StatusBar.StatusBarLabel);
LevelLogLabel.InfoLabel, LifeCycleLogLabel.OnLoadLabel, StatusBar.StatusBarLabel);
this.setHeight();
}
+72
View File
@@ -0,0 +1,72 @@
import { Modular, Manager, ILifetime } from "../../core/Module";
import { API, IParamSetting } from "../../core/Api";
import { Storage } from "../../core/Storage";
/**
* 顶部状态栏
*/
class TestCore<M extends Manager> extends Modular<M>
implements Partial<ILifetime> {
public override onLoad() {
let s = new Storage("test", {
a: new Date(),
be: 2
});
let s2 = new Storage("test", {
be: 1,
aa: "abc"
});
s2.set("be", 4);
console.log(s, s2);
setTimeout(() => {
s.set("be", 12);
}, 1000)
interface ITestApiInput {
name: string,
id: number,
info: {
data: string
}
}
class TestApi extends API<ITestApiInput, {}> {
public override params: IParamSetting<ITestApiInput> = {
name: {
tester: "123",
isHeader: true
},
id: {
parse: (i) => ++i,
},
info: {}
}
public constructor() {
super();
this.initDebugLabel("TestApi");
}
}
let api = new TestApi();
api.param({
name: "123",
id: 456,
info: {
data: "abc"
}
}).request().wait({
success: (d) => console.log(d)
})
}
}
export default TestCore;
export { TestCore };
+2
View File
@@ -1,5 +1,6 @@
import { Manager} from "../../core/Module";
import { StatusBar } from "./StatusBar";
import { TestCore } from "./TestCore";
/**
* 此页面使用 Manager 进行模块化管理
@@ -7,4 +8,5 @@ import { StatusBar } from "./StatusBar";
*/
Manager.Page((manager)=>{
manager.addModule(StatusBar, "statusBar");
manager.addModule(TestCore, "testCore");
})
+1 -1
View File
@@ -35,7 +35,7 @@
"outputPath": ""
},
"enableEngineNative": false,
"useIsolateContext": true,
"useIsolateContext": false,
"userConfirmedBundleSwitch": false,
"packNpmManually": false,
"packNpmRelationList": [],