Compare commits
	
		
			30 Commits
		
	
	
		
			cd8ca7dcc4
			...
			df4229bf2f
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| df4229bf2f | |||
| 43d87b40f7 | |||
| 660f6921de | |||
| 86101e01d1 | |||
| 1f2a2ad6bd | |||
| b91a9271a7 | |||
| ec635b9ae6 | |||
| 6f96a69900 | |||
| 58d8d74158 | |||
| 96b3414d22 | |||
| 529011b0dd | |||
| 4da257c2e2 | |||
| f11eb63ee1 | |||
| 49c4f7f0d4 | |||
| 4158887d2e | |||
| a8e16f5972 | |||
| d9825d6d68 | |||
| fab73f00ab | |||
| edb26b3d8b | |||
| 443f82ea75 | |||
| 75adb97abb | |||
| b36996e352 | |||
| 6bef08f12e | |||
| 1f24a59bd9 | |||
| 14d0c9c123 | |||
| b99a3412e2 | |||
| 63300f68f8 | |||
| acf4f94798 | |||
| 87f4d220e5 | |||
| 7a5d43281b | 
							
								
								
									
										95
									
								
								miniprogram/api/EduBase.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								miniprogram/api/EduBase.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,95 @@ | ||||
| import { API, IAnyData, GeneralCallbackResult } from "../core/Api"; | ||||
| import { EventType } from "../core/Emitter"; | ||||
| 
 | ||||
| interface ILoginEvent { | ||||
| 
 | ||||
|     /** | ||||
|      * session 过期 | ||||
|      */ | ||||
|     expire: GeneralCallbackResult; | ||||
| 
 | ||||
|     /** | ||||
|      * 登录失败 | ||||
|      */ | ||||
|     unauthorized: GeneralCallbackResult; | ||||
| 
 | ||||
|     /** | ||||
|      * 未知的问题 | ||||
|      */ | ||||
|     error: GeneralCallbackResult; | ||||
| 
 | ||||
|     /** | ||||
|      * 数据损坏或丢失 | ||||
|      */ | ||||
|     badData: GeneralCallbackResult; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * 教务处基础 API | ||||
|  * @template I API 输入数据 | ||||
|  * @template O API 输出数据 | ||||
|  * @template E API 中的事件 | ||||
|  * @template U 用户自定义的输出数据 | ||||
|  */ | ||||
| abstract class EduBase< | ||||
| 	I extends IAnyData = IAnyData,  | ||||
| 	O extends IAnyData = IAnyData, | ||||
| 	E extends Record<EventType, any> = {} | ||||
| > extends API<I, O, { | ||||
| 	[P in (keyof ILoginEvent | keyof E)]: P extends keyof ILoginEvent ? ILoginEvent[P] : E[P] | ||||
| }> { | ||||
| 
 | ||||
| 	protected useEduCallback( | ||||
| 		parseFunction: (data: any) => O | ||||
| 	): void { | ||||
| 
 | ||||
| 		this.addFailedCallBack(); | ||||
| 
 | ||||
|         this.on("success", (data) => { | ||||
| 
 | ||||
|             let isSuccess = true; | ||||
|             let errMsg = ""; | ||||
|             let res: O | undefined; | ||||
|             const info: any = data.data; | ||||
| 
 | ||||
|             // 数据缺失检测
 | ||||
|             if(!info) { | ||||
|                 isSuccess = false; | ||||
|                 errMsg = "Bad Data"; | ||||
|                 this.emit("badData", { errMsg }); | ||||
|             } | ||||
| 
 | ||||
|             if (isSuccess) switch (info.code) { | ||||
|                 case (1): | ||||
|                     res = parseFunction(info.data); | ||||
|                     errMsg = info.err_msg ?? "Success"; | ||||
|                     this.emit("ok", res!); | ||||
|                     break; | ||||
| 
 | ||||
|                 case (2): | ||||
|                     isSuccess = false; | ||||
|                     errMsg = info.err_msg ?? "Session Expire"; | ||||
|                     this.emit("expire", { errMsg }); | ||||
|                     break; | ||||
| 
 | ||||
|                 case (3): | ||||
|                     isSuccess = false; | ||||
|                     errMsg = info.err_msg ?? "Unauthorized"; | ||||
|                     this.emit("unauthorized", { errMsg }); | ||||
|                     break; | ||||
| 
 | ||||
|                 case (4): | ||||
|                     isSuccess = false; | ||||
|                     errMsg = info.err_msg ?? "Error"; | ||||
|                     this.emit("error", { errMsg }); | ||||
|                     break; | ||||
|             } | ||||
| 
 | ||||
|             if (!isSuccess) this.emit("no", { errMsg }); | ||||
|             this.emit("done", { errMsg, data: res }); | ||||
|         }); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| export { EduBase }; | ||||
| export default EduBase; | ||||
| @ -1,4 +1,5 @@ | ||||
| import { API, HTTPMethod, IParamSetting, GeneralCallbackResult } from "../core/Api"; | ||||
| import { HTTPMethod, IParamSetting } from "../core/Api"; | ||||
| import { EduBase } from "./EduBase"; | ||||
| 
 | ||||
| interface ILoginInput { | ||||
| 
 | ||||
| @ -42,35 +43,12 @@ interface ILoginOutput { | ||||
|     eduSession: string; | ||||
| } | ||||
| 
 | ||||
| interface ILoginEvent { | ||||
| 
 | ||||
|     /** | ||||
|      * session 过期 | ||||
|      */ | ||||
|     expire: GeneralCallbackResult; | ||||
| 
 | ||||
|     /** | ||||
|      * 登录失败 | ||||
|      */ | ||||
|     unauthorized: GeneralCallbackResult; | ||||
| 
 | ||||
|     /** | ||||
|      * 未知的问题 | ||||
|      */ | ||||
|     error: GeneralCallbackResult; | ||||
| 
 | ||||
|     /** | ||||
|      * 数据损坏或丢失 | ||||
|      */ | ||||
|     badData: GeneralCallbackResult; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Login API | ||||
|  * 此 API 用来向教务处发起登录请求 | ||||
|  * 请求成功后将获得教务处返回的 session | ||||
|  */ | ||||
| class Login extends API<ILoginInput, ILoginOutput, ILoginEvent> { | ||||
| class Login extends EduBase<ILoginInput, ILoginOutput> { | ||||
| 
 | ||||
|     public override url: string = "/login"; | ||||
| 
 | ||||
| @ -92,57 +70,13 @@ class Login extends API<ILoginInput, ILoginOutput, ILoginEvent> { | ||||
|         super(); | ||||
|         this.initDebugLabel("Login"); | ||||
| 
 | ||||
|         this.addFailedCallBack(); | ||||
| 
 | ||||
|         this.on("success", (data) => { | ||||
| 
 | ||||
|             let isSuccess = true; | ||||
|             let errMsg = ""; | ||||
|             let res: ILoginOutput | undefined; | ||||
|             const info: any = data.data; | ||||
| 
 | ||||
|             // 数据缺失检测
 | ||||
|             if(!info) { | ||||
|                 isSuccess = false; | ||||
|                 errMsg = "Bad Data"; | ||||
|                 this.emit("badData", { errMsg }); | ||||
|             } | ||||
| 
 | ||||
|             if (isSuccess) switch (info.code) { | ||||
|                 case (1): | ||||
|                     res = { | ||||
|                         idCardLast6: info.data.idCard, | ||||
|                         eduService: info.data.ip, | ||||
|                         actualName: info.data.name, | ||||
|                         isSubscribeWxAccount: info.data.official, | ||||
|                         eduSession: info.data.session | ||||
|                     }; | ||||
|                     errMsg = info.err_msg ?? "Success"; | ||||
|                     this.emit("ok", res); | ||||
|                     break; | ||||
| 
 | ||||
|                 case (2): | ||||
|                     isSuccess = false; | ||||
|                     errMsg = info.err_msg ?? "Session Expire"; | ||||
|                     this.emit("expire", { errMsg }); | ||||
|                     break; | ||||
| 
 | ||||
|                 case (3): | ||||
|                     isSuccess = false; | ||||
|                     errMsg = info.err_msg ?? "Unauthorized"; | ||||
|                     this.emit("unauthorized", { errMsg }); | ||||
|                     break; | ||||
| 
 | ||||
|                 case (4): | ||||
|                     isSuccess = false; | ||||
|                     errMsg = info.err_msg ?? "Error"; | ||||
|                     this.emit("error", { errMsg }); | ||||
|                     break; | ||||
|             } | ||||
| 
 | ||||
|             if (!isSuccess) this.emit("no", { errMsg }); | ||||
|             this.emit("done", { errMsg, data: res }); | ||||
|         }); | ||||
|         this.useEduCallback((data) => ({ | ||||
|             idCardLast6: data.idCard, | ||||
|             eduService: data.ip, | ||||
|             actualName: data.name, | ||||
|             isSubscribeWxAccount: data.official, | ||||
|             eduSession: data.session | ||||
|         })); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										110
									
								
								miniprogram/api/Schedule.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								miniprogram/api/Schedule.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,110 @@ | ||||
| import { HTTPMethod, IParamSetting } from "../core/Api"; | ||||
| import { EduBase } from "./EduBase"; | ||||
| 
 | ||||
| interface IScheduleInput { | ||||
| 
 | ||||
|     /** | ||||
|      * session | ||||
|      */ | ||||
|     cookie: string; | ||||
| 
 | ||||
|     /** | ||||
|      * 学期 | ||||
|      */ | ||||
|     semester: string; | ||||
| } | ||||
| 
 | ||||
| interface IClassData { | ||||
| 
 | ||||
|     /** | ||||
|      * 课程名字 | ||||
|      */ | ||||
|     name: string; | ||||
| 
 | ||||
|     /** | ||||
|      * 上课地点 | ||||
|      */ | ||||
|     room?: string; | ||||
| 
 | ||||
|     /** | ||||
|      * 课程老师 | ||||
|      */ | ||||
|     teacher?: string; | ||||
| 
 | ||||
|     /** | ||||
|      * 周数 | ||||
|      */ | ||||
|     week: string; | ||||
| } | ||||
| 
 | ||||
| type IScheduleOutput = { | ||||
| 
 | ||||
|     /** | ||||
|      * 课程列表 | ||||
|      */ | ||||
|     classList: IClassData[]; | ||||
| 
 | ||||
|     /** | ||||
|      * 稀疏矩阵编号 | ||||
|      */ | ||||
|     index: number; | ||||
| }[]; | ||||
| 
 | ||||
| /** | ||||
|  * Schedule API | ||||
|  * 需要session与semester | ||||
|  * 此 API 用来向教务处发起获取课程表的请求 | ||||
|  * 请求成功后将获得教务处返回的课程表JSON文件 | ||||
|  */ | ||||
| class Schedlue extends EduBase<IScheduleInput, IScheduleOutput> { | ||||
|   | ||||
|     public override url = "/course_timetable"; | ||||
| 
 | ||||
|     public override method: HTTPMethod = HTTPMethod.GET; | ||||
| 
 | ||||
|     public override params: IParamSetting<IScheduleInput> = { | ||||
| 
 | ||||
|         cookie: { | ||||
|             mapKey: "cookie", | ||||
|             isHeader: true | ||||
|         }, | ||||
| 
 | ||||
|         semester: { | ||||
|             mapKey: "semester", | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     public constructor() { | ||||
|         super(); | ||||
|         this.initDebugLabel("Schedule"); | ||||
|          | ||||
|         this.useEduCallback((data) => { | ||||
|             const res: IScheduleOutput = []; | ||||
| 
 | ||||
|             for( let i = 0; i < data.length; i++ ) { | ||||
|                 const classList: IClassData[] = []; | ||||
|                 const CTTDetails = data[i].CTTDetails ?? []; | ||||
| 
 | ||||
|                 for( let j = 0; j < CTTDetails.length; j++ ) { | ||||
| 
 | ||||
|                     classList.push({ | ||||
|                         name: CTTDetails[j].Name, | ||||
|                         room: CTTDetails[j].Room, | ||||
|                         teacher: CTTDetails[j].Teacher, | ||||
|                         week: CTTDetails[j].Week, | ||||
|                     }) | ||||
|                 } | ||||
| 
 | ||||
|                 res.push({ | ||||
|                     classList, | ||||
|                     index: data[i].Id | ||||
|                 }) | ||||
|             } | ||||
|             return res; | ||||
| 
 | ||||
|         }); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export { Schedlue }; | ||||
| export default Schedlue; | ||||
| @ -1,9 +1,9 @@ | ||||
| import { IAppAPIParam } from "./core/Api"; | ||||
| import { IAppAPIParam, IAnyData } from "./core/Api"; | ||||
| import { IAppStorageParam, Storage, IStorageData } from "./core/Storage"; | ||||
| import { Logger, LevelLogLabel, LifeCycleLogLabel } from "./core/Logger"; | ||||
| 
 | ||||
| 
 | ||||
| App<IAppAPIParam & IAppStorageParam>({ | ||||
| App<IAppAPIParam & IAppStorageParam & IAnyData>({ | ||||
| 
 | ||||
|     /** | ||||
|      * API 模块需要的全局数据 | ||||
|  | ||||
| @ -168,16 +168,17 @@ class API< | ||||
| > { | ||||
| 
 | ||||
|     /** | ||||
|      * 基础 URL | ||||
|      * TODO: 这里可能涉及负载均衡 | ||||
|      * 默认调试标签 | ||||
|      */ | ||||
|     public static get baseUrl():string { | ||||
|         return "https://jwc.nogg.cn"; | ||||
|     } | ||||
| 
 | ||||
|     public static defaultLogLabel:LogLabel = new LogLabel( | ||||
|         `API:API`, colorRadio(200, 120, 222) | ||||
|     ); | ||||
|      | ||||
|     /** | ||||
|      * 基础 URL | ||||
|      * TODO: 这里可能涉及负载均衡 | ||||
|      */ | ||||
|     public baseUrl: string = "https://jwc.nogg.cn"; | ||||
| 
 | ||||
|     /** | ||||
|      * Logger 使用的标签 | ||||
| @ -319,7 +320,7 @@ class API< | ||||
| 
 | ||||
|         // 重置请求数据
 | ||||
|         const requestData:IWxRequestOption<O> = this.requestData = { | ||||
|             url: API.baseUrl + this.url, | ||||
|             url: this.baseUrl + this.url, | ||||
|             data: {}, header: {}, | ||||
|             timeout: this.timeout, | ||||
|             method: this.method, | ||||
| @ -697,4 +698,4 @@ enum HTTPMethod { | ||||
| } | ||||
| 
 | ||||
| export default API; | ||||
| export { API, IParamSetting, IAppAPIParam, ICallBack, HTTPMethod, RequestPolicy, GeneralCallbackResult } | ||||
| export { API, IParamSetting, IAppAPIParam, IAnyData, ICallBack, HTTPMethod, RequestPolicy, GeneralCallbackResult } | ||||
							
								
								
									
										250
									
								
								miniprogram/core/Data.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										250
									
								
								miniprogram/core/Data.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,250 @@ | ||||
| 
 | ||||
| /** | ||||
|  * 数据层键值设置 | ||||
|  */ | ||||
| interface IDataParamSettingItem { | ||||
| 
 | ||||
|     /** | ||||
|      * 类型 | ||||
|      */ | ||||
|     type: any; | ||||
| 
 | ||||
|     /** | ||||
|      * 键值是否可以获取 | ||||
|      */ | ||||
|     isGet: boolean; | ||||
| 
 | ||||
|     /** | ||||
|      * 键值是否可以设置 | ||||
|      */ | ||||
|     isSet: boolean; | ||||
| 
 | ||||
|     /** | ||||
|      * 是否仅为异步获取 | ||||
|      */ | ||||
|     isGetAsync?: boolean; | ||||
| 
 | ||||
|     /** | ||||
|      * 是否仅为异步设置 | ||||
|      */ | ||||
|     isSetAsync?: boolean; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * 数据层参数类型 | ||||
|  */ | ||||
| interface IDataParamSetting { | ||||
|     [x: string]: IDataParamSettingItem | ||||
| } | ||||
| 
 | ||||
| type IGet<T> = () => T; | ||||
| type IGetAsync<T> = () => Promise<T>; | ||||
| type ISet<T> = (data: T) => INone; | ||||
| type ISetAsync<T> = (data: T) => Promise<INone>; | ||||
| 
 | ||||
| type INone = undefined | void | unknown; | ||||
| 
 | ||||
| /** | ||||
|  * 注册表结构 | ||||
|  */ | ||||
| type IRegistryItem<S extends IDataParamSettingItem> = { | ||||
| 
 | ||||
|     /** | ||||
|      * 获取方法 | ||||
|      */ | ||||
|     get: S["isGetAsync"] extends true ? INone : S["isGet"] extends true ? IGet<S["type"]> : INone; | ||||
| 
 | ||||
|     /** | ||||
|      * 异步获取方法 | ||||
|      */ | ||||
|     getAsync: S["isGet"] extends true ? IGetAsync<S["type"]> : INone; | ||||
| 
 | ||||
|     /** | ||||
|      * 设置方法 | ||||
|      */ | ||||
|     set: S["isSetAsync"] extends true ? INone : S["isSet"] extends true ? ISet<S["type"]> : INone; | ||||
| 
 | ||||
|     /** | ||||
|      * 异步设置方法 | ||||
|      */ | ||||
|     setAsync: S["isSet"] extends true ? ISetAsync<S["type"]> : INone; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * 注册表参数类型 | ||||
|  */ | ||||
| type IRegistry<D extends IDataParamSetting> = { | ||||
|     [P in keyof D]: IRegistryItem<D[P]>; | ||||
| } | ||||
| 
 | ||||
| type IRegistryPartial<D extends IDataParamSetting> = { | ||||
|     [P in keyof D]?: Partial<IRegistryItem<D[P]>>; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| type IAutoSelect<IF extends boolean, A, B> = IF extends true ? A : B; | ||||
| 
 | ||||
| /** | ||||
|  * Core 数据层架构 | ||||
|  *  | ||||
|  * 使用示例: | ||||
|  * ```typescript
 | ||||
|  * class TestData extends Data<{ | ||||
|  *     test: { | ||||
|  *         type: number, | ||||
|  *         isGet: true, | ||||
|  *         isSet: true, | ||||
|  *         isGetAsync: true | ||||
|  *     } | ||||
|  * }> { | ||||
|  *     public onLoad() { | ||||
|  *         let dataObject = {key: 1} | ||||
|  *         this.getter("test", () => 1); | ||||
|  *         this.registerKeyFromObject("test", "key", dataObject); | ||||
|  *     } | ||||
|  * } | ||||
|  * ``` | ||||
|  */ | ||||
| class Data<D extends IDataParamSetting> { | ||||
| 
 | ||||
|     /** | ||||
|      * getter setter 注册表 | ||||
|      */ | ||||
|     private registryList: IRegistryPartial<D> = {}; | ||||
| 
 | ||||
|     /** | ||||
|      * 加载函数 | ||||
|      */ | ||||
|     public onLoad(): any {}; | ||||
| 
 | ||||
|     /** | ||||
|      * 注册一个 Setter 到键值上 | ||||
|      * @param key 注册的键值 | ||||
|      * @param setter 注册的 Setter | ||||
|      * @param isAsync 是否为异步函数 | ||||
|      */ | ||||
|     protected setter<KEY extends keyof D> (key: KEY, setter: IRegistryItem<D[KEY]>["set"]): this | ||||
|     protected setter<KEY extends keyof D> (key: KEY, setter: IRegistryItem<D[KEY]>["setAsync"], isAsync: true): this | ||||
|     protected setter< | ||||
|         KEY extends keyof D,  | ||||
|         ASYNC extends boolean | ||||
|     > ( | ||||
|         key: KEY,  | ||||
|         setter: IAutoSelect<ASYNC, IRegistryItem<D[KEY]>["setAsync"], IRegistryItem<D[KEY]>["set"]>,  | ||||
|         isAsync?: ASYNC | ||||
|     ): this { | ||||
| 
 | ||||
|         // 如果此键值不存在,新建
 | ||||
|         if (!this.registryList[key]) this.registryList[key] = {}; | ||||
| 
 | ||||
|         // 设置异步 setter
 | ||||
|         if (isAsync) this.registryList[key]!.setAsync = setter as IRegistryItem<D[KEY]>["setAsync"]; | ||||
| 
 | ||||
|         // 设置同步 setter
 | ||||
|         else this.registryList[key]!.set = setter as IRegistryItem<D[KEY]>["set"]; | ||||
| 
 | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 注册一个 Getter 到键值上 | ||||
|      * @param key 注册的键值 | ||||
|      * @param getter 注册的 Getter | ||||
|      * @param isAsync 是否为异步函数 | ||||
|      */ | ||||
|     protected getter<KEY extends keyof D> (key: KEY, getter: IRegistryItem<D[KEY]>["get"]): this | ||||
|     protected getter<KEY extends keyof D> (key: KEY, getter: IRegistryItem<D[KEY]>["getAsync"], isAsync: true): this | ||||
|     protected getter< | ||||
|         KEY extends keyof D,  | ||||
|         ASYNC extends boolean | ||||
|     > ( | ||||
|         key: KEY,  | ||||
|         getter: IAutoSelect<ASYNC, IRegistryItem<D[KEY]>["getAsync"], IRegistryItem<D[KEY]>["get"]>,  | ||||
|         isAsync?: ASYNC | ||||
|     ): this { | ||||
|   | ||||
|         // 如果此键值不存在,新建
 | ||||
|         if (!this.registryList[key]) this.registryList[key] = {}; | ||||
| 
 | ||||
|         // 设置异步 getter
 | ||||
|         if (isAsync) this.registryList[key]!.getAsync = getter as IRegistryItem<D[KEY]>["getAsync"]; | ||||
| 
 | ||||
|         // 设置同步 getter
 | ||||
|         else this.registryList[key]!.get = getter as IRegistryItem<D[KEY]>["get"]; | ||||
| 
 | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 将对象的键值关联到 Data 层中 | ||||
|      * @param key Data 层键值 | ||||
|      * @param keyFromObject 对象源键值 | ||||
|      * @param object 关联对象 | ||||
|      */ | ||||
|     protected registerKeyFromObject< | ||||
|         KEY extends keyof D,  | ||||
|         F extends keyof O,  | ||||
|         O extends {[K in F]: D[KEY]["type"]} | ||||
|     > (key: KEY, keyFromObject: F, object: O) { | ||||
| 
 | ||||
|         // 注册同步获取
 | ||||
|         this.getter(key, () => { | ||||
|             return object[keyFromObject] | ||||
|         }); | ||||
| 
 | ||||
|         // 注册同步设置
 | ||||
|         this.setter(key, (data: any) => { | ||||
|             object[keyFromObject] = data | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 导出数据对象 | ||||
|      * @returns 数据对象 | ||||
|      */ | ||||
|     public export(): IRegistry<D> { | ||||
|         this.autoFillFunction(); | ||||
|         return this.registryList as IRegistry<D>; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 自动填充缺失的异步函数 | ||||
|      * 在注册表中搜索全部的同步函数 | ||||
|      * 并自动填充对应的异步函数 | ||||
|      * 此函数会在 export 前静默执行 | ||||
|      */ | ||||
|     protected autoFillFunction(): void { | ||||
| 
 | ||||
|         // 填充函数
 | ||||
|         const fillFunction = <KEY extends keyof D>(key: KEY): void => { | ||||
| 
 | ||||
|             const item = this.registryList[key] as IRegistryItem<D[KEY]>; | ||||
|             if (!item) return; | ||||
|      | ||||
|             // 检验 getter
 | ||||
|             if (item.get && !item.getAsync) { | ||||
|                 item.getAsync = this.syncFn2AsyncFn(item.get as IGet<D[KEY]["type"]>); | ||||
|             } | ||||
|      | ||||
|             // 检验 setter
 | ||||
|             if (item.set && !item.setAsync) { | ||||
|                 item.setAsync = this.syncFn2AsyncFn(item.set as ISet<D[KEY]["type"]>); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // 在注册表中查找
 | ||||
|         for (const key in this.registryList) fillFunction(key); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 将同步函数转换为异步函数 | ||||
|      * @param fn 同步函数 | ||||
|      */ | ||||
|     protected syncFn2AsyncFn<D, H extends (IGet<D> | ISet<D>)> (fn: H):  | ||||
|     IAutoSelect<H extends IGet<D> ? true : false, IGetAsync<D>, ISetAsync<D>> { | ||||
|         const asyncFn = async (...param: [D]) => { | ||||
|             return fn(...param); | ||||
|         }; | ||||
|         return asyncFn as any; | ||||
|     } | ||||
| } | ||||
| @ -491,6 +491,27 @@ class Manager<WXC extends AnyWXContext = AnyWXContext> { | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 异步页面加载 | ||||
|      *  | ||||
|      * *注意* | ||||
|      * 页面模块加载后,必须手动执行 loadAllModule | ||||
|      * loadAllModule Modular 才会真正的被加载 | ||||
|      * 模块加载后可以处理逻辑绑定 | ||||
|      */ | ||||
|     public static async PageAsync(): Promise<{ | ||||
|         manager: Manager<AnyWXContext>,  | ||||
|         query: Record<string, string | undefined> | ||||
|     }> { | ||||
|         return new Promise((solve) => { | ||||
|             Page({ | ||||
|                 async onLoad(query) { | ||||
|                     let manager = new Manager(this); | ||||
|                     await solve({ manager, query }); | ||||
|                 } | ||||
|             }) | ||||
|         }); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export { Manager, Modular, AnyWXContext, WXContext, ILifetime} | ||||
							
								
								
									
										0
									
								
								miniprogram/data/StudentInfo.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								miniprogram/data/StudentInfo.ts
									
									
									
									
									
										Normal file
									
								
							| @ -1,3 +1,4 @@ | ||||
| @import "../../app.scss"; | ||||
| 
 | ||||
| view.mask { | ||||
| 	position: fixed; | ||||
| @ -5,4 +6,40 @@ view.mask { | ||||
| 	height: 100%; | ||||
| 	background-color: rgba($color: #000000, $alpha: .2); | ||||
| 	z-index: 1; | ||||
| } | ||||
| 
 | ||||
| view.mask.block { | ||||
|     display: block; | ||||
| } | ||||
| 
 | ||||
| view.mask.none { | ||||
|     display: none; | ||||
| } | ||||
| 
 | ||||
| view.mask.show { | ||||
|     animation: show .1s cubic-bezier(0, 0, 1, 1) both; | ||||
|     opacity: 1; | ||||
| } | ||||
| 
 | ||||
| view.mask.hide { | ||||
|     animation: hide .1s cubic-bezier(0, 0, 1, 1) both; | ||||
|     opacity: 0; | ||||
| } | ||||
| 
 | ||||
| @keyframes show{ | ||||
|     from { | ||||
|         opacity: 0; | ||||
|     } | ||||
|     to { | ||||
|         opacity: 1; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @keyframes hide{ | ||||
|     from { | ||||
|         opacity: 1; | ||||
|     } | ||||
|     to { | ||||
|         opacity: 0; | ||||
|     } | ||||
| } | ||||
| @ -1,6 +1,40 @@ | ||||
| import { Modular, Manager } from "../../core/Module"; | ||||
| 
 | ||||
| class Mask<M extends Manager> extends Modular<M> { | ||||
| /** | ||||
|  * 蒙版事件 | ||||
|  */ | ||||
| type IMaskEvent = { | ||||
| 
 | ||||
|     /** | ||||
|      * 蒙版显示事件 | ||||
|      */ | ||||
|     show: void; | ||||
| 
 | ||||
|     /** | ||||
|      * 蒙版隐藏事件 | ||||
|      */ | ||||
|     hide: void; | ||||
| 
 | ||||
|     /** | ||||
|      * 蒙版状态改变事件 | ||||
|      */ | ||||
|     change: boolean; | ||||
| 
 | ||||
|     /** | ||||
|      * 蒙版点击事件 | ||||
|      */ | ||||
|     click: void; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * 蒙版 Modular | ||||
|  */ | ||||
| class Mask<M extends Manager> extends Modular<M, {}, IMaskEvent> { | ||||
| 
 | ||||
|     /** | ||||
|      * 动画运行时间 | ||||
|      */ | ||||
|     public static readonly animateTime: number = 100; | ||||
| 
 | ||||
| 	public data? = { | ||||
| 
 | ||||
| @ -9,35 +43,71 @@ class Mask<M extends Manager> extends Modular<M> { | ||||
| 		 */ | ||||
| 		zIndex: 1, | ||||
| 
 | ||||
|         /** | ||||
|          * 蒙版是否显示 | ||||
|          */ | ||||
|         isDisplay: false, | ||||
| 
 | ||||
| 		/** | ||||
| 		 * 蒙版是否显示 | ||||
| 		 */ | ||||
| 		isShow: false | ||||
| 	}; | ||||
| 
 | ||||
|     /** | ||||
|      * 当点击时是否自动关闭蒙版 | ||||
|      */ | ||||
|     public autoCloseOnClick: boolean = true; | ||||
| 
 | ||||
|     /** | ||||
|      * 消失动画计时器 | ||||
|      */ | ||||
| 	private disappearTimer?: number; | ||||
| 
 | ||||
|     public override onLoad() { | ||||
| 		this.setFunc(this.handleClickMask, "handleClickMask"); | ||||
|         this.on("show", this.showMask); | ||||
|         this.on("hide", this.hideMask); | ||||
|     } | ||||
| 	 | ||||
| 	/** | ||||
| 	 * 显示蒙版 | ||||
| 	 */ | ||||
| 	public showMask() { | ||||
| 		this.setData({ isShow: true }); | ||||
| 	private showMask = () => { | ||||
| 
 | ||||
|         this.disappearTimer && clearTimeout(this.disappearTimer); | ||||
| 
 | ||||
| 		this.setData({  | ||||
|             isShow: true, | ||||
|             isDisplay: true | ||||
|         }); | ||||
| 
 | ||||
|         this.emit("change", true); | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * 隐藏蒙版 | ||||
| 	 */ | ||||
| 	public hideMask() { | ||||
| 		this.setData({ isShow: false }); | ||||
| 	} | ||||
| 	 | ||||
| 	public override onLoad() { | ||||
| 		this.setFunc(this.handleClickMask, "handleClickMask"); | ||||
|         // Do something
 | ||||
|     } | ||||
|     private hideMask = () => { | ||||
| 		this.setData({  | ||||
|             isShow: false  | ||||
|         }); | ||||
| 
 | ||||
|         this.disappearTimer = setTimeout(() => { | ||||
|             this.setData({  | ||||
|                 isDisplay: false | ||||
|             }); | ||||
|         }, Mask.animateTime); | ||||
| 
 | ||||
|         this.emit("change", false); | ||||
| 	} | ||||
| 
 | ||||
|     /** | ||||
|      * 处理蒙版点击事件 | ||||
|      */ | ||||
| 	private handleClickMask() { | ||||
| 		this.hideMask(); | ||||
| 		if (this.autoCloseOnClick) this.emit("hide", void 0); | ||||
|         this.emit("click", void 0); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -5,10 +5,25 @@ import { FunctionList } from "./FunctionList"; | ||||
| import { Mask } from "../../modular/Mask/Mask"; | ||||
| import {Popups} from "../../modular/Popups/Popups" | ||||
| 
 | ||||
| Manager.Page((manager) => { | ||||
|     const popups = manager.addModule(Popups,"popups") | ||||
| (async () => { | ||||
| 
 | ||||
|     // 初始化页面
 | ||||
|     const { manager, query } = await Manager.PageAsync(); | ||||
| 
 | ||||
|     // 添加蒙版 Modular
 | ||||
|     const mask = manager.addModule(Mask, "mask"); | ||||
|     manager.addModule(UserCard, "userCard", { mask,popups }); | ||||
| 
 | ||||
|     const popups = manager.addModule(Popups,"popups") | ||||
| 
 | ||||
|     // 添加 UserCard Modular
 | ||||
|     manager.addModule(UserCard, "userCard", { mask, popups }); | ||||
| 
 | ||||
|     // 添加 MainFunction Modular
 | ||||
|     manager.addModule(MainFunction, "mainFunction"); | ||||
| 
 | ||||
|     // 添加 FunctionList Modular
 | ||||
|     manager.addModule(FunctionList, "functionList"); | ||||
| }); | ||||
| 
 | ||||
|     // 初始化全部 Modular
 | ||||
|     await manager.loadAllModule(query); | ||||
| })(); | ||||
| @ -1,5 +1,6 @@ | ||||
| <!-- 蒙版 --> | ||||
| <view class="mask" bindtap="mask$handleClickMask" style="display:{{mask$isShow ? 'block' : 'none'}}"></view> | ||||
| <view class="mask {{  mask$isShow ? 'show' : 'hide' }} {{ mask$isDisplay ? 'block' : 'none' }}"  | ||||
|     bindtap="mask$handleClickMask"></view> | ||||
| 
 | ||||
| <!-- 弹出层 --> | ||||
| <view class="popups" style="display:{{popups$isShow ? 'block' : 'none'}}"></view> | ||||
| @ -47,42 +48,27 @@ | ||||
|     </view> | ||||
| 
 | ||||
|     <!--主要功能--> | ||||
|     <view class="card main-function"><!--四个功能合在一起的容器--> | ||||
|         <view class="branch-funtion"> | ||||
|             <view><!--每个功能的容器--> | ||||
|                 <image class="icon" src="../../image/account/Account_UserInfo.svg"></image><!--每个功能的图片--> | ||||
|                 <view>账号信息</view><!--每个功能的文字--> | ||||
|             </view> | ||||
|         </view> | ||||
| 
 | ||||
|         <view class="branch-funtion"> | ||||
|             <view> | ||||
|                 <image class="icon" src="../../image/account/Account_DateList.svg"></image> | ||||
|                 <view>课表缓存</view> | ||||
|             </view> | ||||
|         </view> | ||||
| 
 | ||||
|         <view class="branch-funtion"> | ||||
|             <view> | ||||
|                 <image class="icon" src="../../image/account/Account_Customer.svg"></image> | ||||
|                 <view>功能定制</view> | ||||
|             </view> | ||||
|         </view> | ||||
| 
 | ||||
|         <view class="branch-funtion"> | ||||
|             <view style="border-right: 0px;"> | ||||
|                 <image class="icon" src="../../image/account/Account_Settings.svg"></image> | ||||
|                 <view>更多设置</view> | ||||
|     <view class="card main-function"> | ||||
|          | ||||
|         <!--每个功能的容器--> | ||||
|         <view class="branch-funtion" wx:for="{{ mainFunction$mainFunctionList }}" wx:key="index"> | ||||
|             <view style="{{ index == (mainFunction$mainFunctionList - 1) ? 'border-bottom: 0px' : '' }}"> | ||||
|                 <!--每个功能的图片--> | ||||
|                 <image class="icon" src="../../image/account/Account_{{ item.iconUrl }}.svg"></image> | ||||
|                 <!--每个功能的文字--> | ||||
|                 <view>{{ item.displayName }}</view> | ||||
|             </view> | ||||
|         </view> | ||||
|     </view> | ||||
| 
 | ||||
|     <!-- 功能列表 --> | ||||
|     <view class="card function-list"> | ||||
|         <view class="function" wx:for="{{functionList$functionList}}" wx:for-index="index" wx:key="id"> | ||||
|             <view style="{{item.id == (functionList$functionList.length - 1) ? 'border-bottom: 0px' : ''}}"> | ||||
|                 <image class="icon func-icon" src="../../image/account/Account_{{item.iconUrl}}.svg" /> | ||||
|                 <view>{{item.displayName}}</view> | ||||
| 
 | ||||
|         <!-- 每一行 --> | ||||
|         <view class="function" wx:for="{{ functionList$functionList }}" wx:key="index"> | ||||
|             <view style="{{ index == (functionList$functionList.length - 1) ? 'border-bottom: 0px' : '' }}"> | ||||
|                 <image class="icon func-icon" src="../../image/account/Account_{{ item.iconUrl }}.svg" /> | ||||
|                 <view>{{ item.displayName }}</view> | ||||
|                 <image class="icon-sub arrow" src="../../image/account/Account_Arrow.svg" /> | ||||
|             </view> | ||||
|         </view> | ||||
|  | ||||
| @ -2,11 +2,6 @@ import { Modular, Manager } from "../../core/Module"; | ||||
| 
 | ||||
| interface IFunctionListItem { | ||||
| 
 | ||||
|     /** | ||||
|      * id | ||||
|      */ | ||||
|     id?: number | ||||
| 
 | ||||
|     /** | ||||
|      * 显示名称 | ||||
|      */ | ||||
| @ -18,10 +13,6 @@ interface IFunctionListItem { | ||||
|     iconUrl: string; | ||||
| } | ||||
| 
 | ||||
| interface IFunctionListData { | ||||
|     functionList?: IFunctionListItem[]; | ||||
| }; | ||||
| 
 | ||||
| class FunctionList<M extends Manager> extends Modular<M> { | ||||
| 
 | ||||
|     public static readonly functionList: IFunctionListItem[] = [ | ||||
| @ -32,18 +23,12 @@ class FunctionList<M extends Manager> extends Modular<M> { | ||||
|         { displayName: "联系客服", iconUrl: "Support" } | ||||
|     ]; | ||||
| 
 | ||||
|     public data: IFunctionListData = { | ||||
|         functionList: undefined | ||||
|     public data = { | ||||
|         functionList: FunctionList.functionList | ||||
|     }; | ||||
|      | ||||
|     public override onLoad() { | ||||
|         console.log(FunctionList.functionList) | ||||
|         this.setData({ | ||||
|             functionList: FunctionList.functionList.map((value, index) => { | ||||
|                 value.id = index; | ||||
|                 return value; | ||||
|             }) | ||||
|         }) | ||||
|         // Do something
 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -1,6 +1,30 @@ | ||||
| import { Modular, Manager } from "../../core/Module"; | ||||
| 
 | ||||
| interface IMainFunctionItem { | ||||
| 
 | ||||
|     /** | ||||
|      * 显示名称 | ||||
|      */ | ||||
|     displayName: string; | ||||
| 
 | ||||
|     /** | ||||
|      * 图标路径 | ||||
|      */ | ||||
|     iconUrl: string; | ||||
| } | ||||
| 
 | ||||
| class MainFunction<M extends Manager> extends Modular<M> { | ||||
| 
 | ||||
|     public static readonly MainFunctionList: IMainFunctionItem[] = [ | ||||
|         { displayName: "账号信息", iconUrl: "UserInfo" }, | ||||
|         { displayName: "课表缓存", iconUrl: "DateList" }, | ||||
|         { displayName: "功能定制", iconUrl: "Customer" }, | ||||
|         { displayName: "更多设置", iconUrl: "Settings" } | ||||
|     ]; | ||||
| 
 | ||||
|     public data? = { | ||||
|         mainFunctionList: MainFunction.MainFunctionList | ||||
|     } | ||||
|      | ||||
|     public override onLoad() { | ||||
|         // Do something
 | ||||
|  | ||||
| @ -4,10 +4,18 @@ import { Mask } from "../../modular/Mask/Mask"; | ||||
| 
 | ||||
| type IUserCardDependent<M extends Manager> = { | ||||
|     mask: Mask<M> | ||||
|     popups:Popups<M> | ||||
|     popups: Popups<M> | ||||
| } | ||||
| 
 | ||||
| class UserCard<M extends Manager> extends Modular<M, IUserCardDependent<M>> { | ||||
| type IUserCardEvent = { | ||||
| 
 | ||||
|     /** | ||||
|      * 主题更换按钮点击事件 | ||||
|      */ | ||||
|     clickChangeTheme: void; | ||||
| } | ||||
| 
 | ||||
| class UserCard<M extends Manager> extends Modular<M, IUserCardDependent<M>, IUserCardEvent> { | ||||
| 
 | ||||
|     public override onLoad() { | ||||
|         this.setFunc(this.handleChangeTheme, "changeTheme") | ||||
| @ -17,8 +25,9 @@ class UserCard<M extends Manager> extends Modular<M, IUserCardDependent<M>> { | ||||
|      * 处理主题更换 | ||||
|      */ | ||||
|     private handleChangeTheme() { | ||||
|         this.depends?.mask.showMask(); | ||||
|         this.depends?.popups.showPopups(); | ||||
|         this.depends?.mask.emit("show", void 0); | ||||
|         this.emit("clickChangeTheme", void 0); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -1,5 +1,6 @@ | ||||
| import { Modular, Manager, ILifetime } from "../../core/Module"; | ||||
| import { Login } from "../../api/Login"; | ||||
| import { Schedlue } from "../../api/Schedule" | ||||
| import { Storage } from "../../core/Storage"; | ||||
| 
 | ||||
| /** | ||||
| @ -28,12 +29,14 @@ implements Partial<ILifetime> { | ||||
|             s.set("be", 12); | ||||
|         }, 1000) | ||||
|          | ||||
|         new Login().param({studentId: "1806240113", password: "qazxsw123"}) | ||||
|         .request().wait({ | ||||
|             ok: (w) => {console.log("ok", w)}, | ||||
|             no: (w) => {console.log("no", w)}, | ||||
|             done: (w) => {console.log("done", w)} | ||||
|         }); | ||||
|         // new Login().param({studentId: "2017060129", password: ""})
 | ||||
|         // .request().wait({
 | ||||
|         //     ok: (w) => {console.log("ok", w)},
 | ||||
|         //     no: (w) => {console.log("no", w)},
 | ||||
|         //     done: (w) => {console.log("done", w)}
 | ||||
|         // });
 | ||||
|         // new Schedlue().param({cookie:"C729D1AB1B17077485ACCD9279135C22",semester:"2020-2021-2"})
 | ||||
|         // .request()
 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -6,7 +6,17 @@ import { TestCore } from "./TestCore"; | ||||
|  * 此页面使用 Manager 进行模块化管理 | ||||
|  * 若要添加先功能请先定义 Modular 并添加至 Manager | ||||
|  */ | ||||
| Manager.Page((manager)=>{ | ||||
| (async () => { | ||||
| 
 | ||||
|     // 初始化页面
 | ||||
|     const { manager, query } = await Manager.PageAsync(); | ||||
| 
 | ||||
|     // 添加 StatusBar Modular
 | ||||
|     manager.addModule(StatusBar, "statusBar"); | ||||
|      | ||||
|     // 添加 TestCore Modular
 | ||||
|     manager.addModule(TestCore, "testCore"); | ||||
| }) | ||||
| 
 | ||||
|     // 初始化全部 Modular
 | ||||
|     await manager.loadAllModule(query); | ||||
| })() | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user