Compare commits
	
		
			No commits in common. "master" and "dev-sanaesy" have entirely different histories.
		
	
	
		
			master
			...
			dev-sanaes
		
	
		
| @ -1,95 +0,0 @@ | |||||||
| import { API, IAnyData, GeneralCallbackResult } from "../core/Api"; |  | ||||||
| import { EventType, Emitter } from "../core/Emitter"; |  | ||||||
| 
 |  | ||||||
| type 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 as Emitter<IAnyData>).emit("badData", { errMsg }); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if (isSuccess) switch (info.code) { |  | ||||||
|                 case (1): |  | ||||||
|                     res = parseFunction(info.data); |  | ||||||
|                     errMsg = info.err_msg ?? "Success"; |  | ||||||
|                     (this as Emitter<IAnyData>).emit("ok", res!); |  | ||||||
|                     break; |  | ||||||
| 
 |  | ||||||
|                 case (2): |  | ||||||
|                     isSuccess = false; |  | ||||||
|                     errMsg = info.err_msg ?? "Session Expire"; |  | ||||||
|                     (this as Emitter<IAnyData>).emit("expire", { errMsg }); |  | ||||||
|                     break; |  | ||||||
| 
 |  | ||||||
|                 case (3): |  | ||||||
|                     isSuccess = false; |  | ||||||
|                     errMsg = info.err_msg ?? "Unauthorized"; |  | ||||||
|                     (this as Emitter<IAnyData>).emit("unauthorized", { errMsg }); |  | ||||||
|                     break; |  | ||||||
| 
 |  | ||||||
|                 case (4): |  | ||||||
|                     isSuccess = false; |  | ||||||
|                     errMsg = info.err_msg ?? "Error"; |  | ||||||
|                     (this as Emitter<IAnyData>).emit("error", { errMsg }); |  | ||||||
|                     break; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if (!isSuccess) (this as Emitter<IAnyData>).emit("no", { errMsg }); |  | ||||||
|             (this as Emitter<IAnyData>).emit("done", { errMsg, data: res }); |  | ||||||
|         }); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export { EduBase }; |  | ||||||
| export default EduBase; |  | ||||||
| @ -1,5 +1,4 @@ | |||||||
| import { HTTPMethod, IParamSetting } from "../core/Api"; | import { API, HTTPMethod, IParamSetting, GeneralCallbackResult } from "../core/Api"; | ||||||
| import { EduBase } from "./EduBase"; |  | ||||||
| 
 | 
 | ||||||
| interface ILoginInput { | interface ILoginInput { | ||||||
| 
 | 
 | ||||||
| @ -32,18 +31,46 @@ interface ILoginOutput { | |||||||
|      */ |      */ | ||||||
|     actualName: string; |     actualName: string; | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * 用户是否关注了公共号 | ||||||
|  |      */ | ||||||
|  |     isSubscribeWxAccount: boolean; | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * 教务处的 session |      * 教务处的 session | ||||||
|      */ |      */ | ||||||
|     eduSession: string; |     eduSession: string; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | interface ILoginEvent { | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * session 过期 | ||||||
|  |      */ | ||||||
|  |     expire: GeneralCallbackResult; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 登录失败 | ||||||
|  |      */ | ||||||
|  |     unauthorized: GeneralCallbackResult; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 未知的问题 | ||||||
|  |      */ | ||||||
|  |     error: GeneralCallbackResult; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 数据损坏或丢失 | ||||||
|  |      */ | ||||||
|  |     badData: GeneralCallbackResult; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  * Login API |  * Login API | ||||||
|  * 此 API 用来向教务处发起登录请求 |  * 此 API 用来向教务处发起登录请求 | ||||||
|  * 请求成功后将获得教务处返回的 session |  * 请求成功后将获得教务处返回的 session | ||||||
|  */ |  */ | ||||||
| class Login extends EduBase<ILoginInput, ILoginOutput> { | class Login extends API<ILoginInput, ILoginOutput, ILoginEvent> { | ||||||
| 
 | 
 | ||||||
|     public override url: string = "/login"; |     public override url: string = "/login"; | ||||||
| 
 | 
 | ||||||
| @ -65,13 +92,57 @@ class Login extends EduBase<ILoginInput, ILoginOutput> { | |||||||
|         super(); |         super(); | ||||||
|         this.initDebugLabel("Login"); |         this.initDebugLabel("Login"); | ||||||
| 
 | 
 | ||||||
|         this.useEduCallback((data) => ({ |         this.addFailedCallBack(); | ||||||
|             idCardLast6: data.idCard, | 
 | ||||||
|             eduService: data.ip, |         this.on("success", (data) => { | ||||||
|             actualName: data.name, | 
 | ||||||
|             isSubscribeWxAccount: data.official, |             let isSuccess = true; | ||||||
|             eduSession: data.session |             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 }); | ||||||
|  |         }); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,110 +0,0 @@ | |||||||
| 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; |  | ||||||
| @ -16,14 +16,10 @@ $black-filter: brightness(0) opacity(.65); | |||||||
| $white-filter: brightness(100) opacity(.65); | $white-filter: brightness(100) opacity(.65); | ||||||
| $blue-filter: opacity(1); | $blue-filter: opacity(1); | ||||||
| 
 | 
 | ||||||
| @mixin container { |  | ||||||
|     width: 88%; |  | ||||||
|     padding: 0 6%; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // 页面容器外边距 | // 页面容器外边距 | ||||||
| view.container { | view.container { | ||||||
|     @include container; |     width: 88%; | ||||||
|  |     padding: 0 6%; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // 带阴影的 card | // 带阴影的 card | ||||||
|  | |||||||
| @ -1,9 +1,9 @@ | |||||||
| import { IAppAPIParam, IAnyData } from "./core/Api"; | import { IAppAPIParam } from "./core/Api"; | ||||||
| import { IAppStorageParam, Storage, IStorageData } from "./core/Storage"; | import { IAppStorageParam, Storage, IStorageData } from "./core/Storage"; | ||||||
| import { Logger, LevelLogLabel, LifeCycleLogLabel } from "./core/Logger"; | import { Logger, LevelLogLabel, LifeCycleLogLabel } from "./core/Logger"; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| App<IAppAPIParam & IAppStorageParam & IAnyData>({ | App<IAppAPIParam & IAppStorageParam>({ | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * API 模块需要的全局数据 |      * API 模块需要的全局数据 | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| import { Emitter, EventType, EventMixin } from "./Emitter"; | import { Emitter, EventType } from "./Emitter"; | ||||||
| import { API_FAILED_SHOW_MESSAGE } from "./Config"; | import { API_FAILED_SHOW_MESSAGE } from "./Config"; | ||||||
| import { Logger, LogLabel, LevelLogLabel, colorRadio, StatusLabel } from "./Logger"; | import { Logger, LogLabel, LevelLogLabel, colorRadio, StatusLabel } from "./Logger"; | ||||||
| interface IAppAPIParam { | interface IAppAPIParam { | ||||||
| @ -156,20 +156,28 @@ class API< | |||||||
|     O extends IAnyData = IAnyData, |     O extends IAnyData = IAnyData, | ||||||
|     E extends Record<EventType, any> = Record<EventType, any>, |     E extends Record<EventType, any> = Record<EventType, any>, | ||||||
|     U extends IAnyData = IAnyData |     U extends IAnyData = IAnyData | ||||||
| > extends Emitter<EventMixin<IAPIEvent<I, O> & IAPIResultEvent<O, U>, E>> { | > extends Emitter < | ||||||
| 
 |     { | ||||||
|     /** |         // 这个复杂的泛型是为了 MixIn 用户自定义事件类型
 | ||||||
|      * 默认调试标签 |         // 懂得如何使用就可以了
 | ||||||
|      */ |         // 不要试图去理解下面这三行代码,真正的恶魔在等着你
 | ||||||
|     public static defaultLogLabel:LogLabel = new LogLabel( |         [P in (keyof (IAPIEvent<I, O> & IAPIResultEvent<O, U>) | keyof E)] :  | ||||||
|         `API:API`, colorRadio(200, 120, 222) |         P extends keyof IAPIEvent<I, O> ? IAPIEvent<I, O>[P] :  | ||||||
|     ); |         P extends keyof IAPIResultEvent<O, U> ? IAPIResultEvent<O, U>[P] : E[P] | ||||||
|  |     } | ||||||
|  | > { | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * 基础 URL |      * 基础 URL | ||||||
|      * TODO: 这里可能涉及负载均衡 |      * TODO: 这里可能涉及负载均衡 | ||||||
|      */ |      */ | ||||||
|     public baseUrl: string = "https://jwc.nogg.cn"; |     public static get baseUrl():string { | ||||||
|  |         return "https://jwc.nogg.cn"; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public static defaultLogLabel:LogLabel = new LogLabel( | ||||||
|  |         `API:API`, colorRadio(200, 120, 222) | ||||||
|  |     ); | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Logger 使用的标签 |      * Logger 使用的标签 | ||||||
| @ -307,11 +315,11 @@ class API< | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // 触发数据初始化事件
 |         // 触发数据初始化事件
 | ||||||
|         (this as Emitter<IAnyData>).emit("initData", this.data); |         this.emit("initData", this.data); | ||||||
| 
 | 
 | ||||||
|         // 重置请求数据
 |         // 重置请求数据
 | ||||||
|         const requestData:IWxRequestOption<O> = this.requestData = { |         const requestData:IWxRequestOption<O> = this.requestData = { | ||||||
|             url: this.baseUrl + this.url, |             url: API.baseUrl + this.url, | ||||||
|             data: {}, header: {}, |             data: {}, header: {}, | ||||||
|             timeout: this.timeout, |             timeout: this.timeout, | ||||||
|             method: this.method, |             method: this.method, | ||||||
| @ -334,7 +342,7 @@ class API< | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // 触发数据解析
 |         // 触发数据解析
 | ||||||
|         (this as Emitter<IAnyData>).emit("parseRequestData", this.data); |         this.emit("parseRequestData", this.data); | ||||||
| 
 | 
 | ||||||
|         // 数据收集
 |         // 数据收集
 | ||||||
|         for (let key in this.params) { |         for (let key in this.params) { | ||||||
| @ -452,18 +460,18 @@ class API< | |||||||
|         let request = () => { |         let request = () => { | ||||||
| 
 | 
 | ||||||
|             // 触发请求发送事件
 |             // 触发请求发送事件
 | ||||||
|             (this as Emitter<IAnyData>).emit("request", this.requestData!) |             this.emit("request", this.requestData!) | ||||||
| 
 | 
 | ||||||
|             wx.request<O>({ |             wx.request<O>({ | ||||||
|                 ...this.requestData!, |                 ...this.requestData!, | ||||||
|                 success: (e) => { |                 success: (e) => { | ||||||
|                     (this as Emitter<IAnyData>).emit("success", e); |                     this.emit("success", e); | ||||||
|                 }, |                 }, | ||||||
|                 fail: (e) => { |                 fail: (e) => { | ||||||
|                     (this as Emitter<IAnyData>).emit("fail", e); |                     this.emit("fail", e); | ||||||
|                 }, |                 }, | ||||||
|                 complete: (e) => { |                 complete: (e) => { | ||||||
|                     (this as Emitter<IAnyData>).emit("complete", e); |                     this.emit("complete", e); | ||||||
|                 } |                 } | ||||||
|             }); |             }); | ||||||
|         } |         } | ||||||
| @ -481,12 +489,12 @@ class API< | |||||||
|                 // 使用上次请求结果
 |                 // 使用上次请求结果
 | ||||||
|                 if (this.policy === RequestPolicy.useLastRequest) { |                 if (this.policy === RequestPolicy.useLastRequest) { | ||||||
|                     lastAPI.on("success", (e) => { |                     lastAPI.on("success", (e) => { | ||||||
|                         (this as Emitter<IAnyData>).emit("success", e as SuccessCallbackResult<O>); |                         this.emit("success", e as SuccessCallbackResult<O>); | ||||||
|                         (this as Emitter<IAnyData>).emit("complete", {errMsg: e.errMsg}); |                         this.emit("complete", {errMsg: e.errMsg}); | ||||||
|                     }); |                     }); | ||||||
|                     lastAPI.on("fail", (e) => { |                     lastAPI.on("fail", (e) => { | ||||||
|                         (this as Emitter<IAnyData>).emit("fail", e); |                         this.emit("fail", e); | ||||||
|                         (this as Emitter<IAnyData>).emit("complete", {errMsg: e.errMsg}); |                         this.emit("complete", {errMsg: e.errMsg}); | ||||||
|                     }); |                     }); | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
| @ -596,8 +604,8 @@ class API< | |||||||
|      */ |      */ | ||||||
|     public addFailedCallBack(): this { |     public addFailedCallBack(): this { | ||||||
|         this.on("fail", (e) => { |         this.on("fail", (e) => { | ||||||
|             (this as Emitter<IAnyData>).emit("no", e as any); |             this.emit("no", e as any); | ||||||
|             (this as Emitter<IAnyData>).emit("done", e as any); |             this.emit("done", e as any); | ||||||
|         }); |         }); | ||||||
|         return this; |         return this; | ||||||
|     } |     } | ||||||
| @ -689,4 +697,4 @@ enum HTTPMethod { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export default API; | export default API; | ||||||
| export { API, IParamSetting, IAppAPIParam, IAnyData, ICallBack, HTTPMethod, RequestPolicy, GeneralCallbackResult } | export { API, IParamSetting, IAppAPIParam, ICallBack, HTTPMethod, RequestPolicy, GeneralCallbackResult } | ||||||
| @ -1,277 +0,0 @@ | |||||||
| import { Storage } from "./Storage"; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 数据层键值设置 |  | ||||||
|  */ |  | ||||||
| interface IDataParamSettingItem { |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 类型 |  | ||||||
|      */ |  | ||||||
|     type: any; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 键值是否可以获取 |  | ||||||
|      */ |  | ||||||
|     get?: ((...P: any) => this["type"]) | INone; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 键值是否可以设置 |  | ||||||
|      */ |  | ||||||
|     set?: ((...P: [this["type"], ...any]) => any) | INone; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 是否仅为异步获取 |  | ||||||
|      */ |  | ||||||
|     getAsync?: ((...P: any) => Promise<this["type"]>) | INone; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 是否仅为异步设置 |  | ||||||
|      */ |  | ||||||
|     setAsync?: ((...P: [this["type"], ...any]) => Promise<any>) | INone; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 数据层参数类型 |  | ||||||
|  */ |  | ||||||
| interface IDataParamSetting { |  | ||||||
|     [x: string]: IDataParamSettingItem |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type INone = undefined | void; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 注册表结构 |  | ||||||
|  */ |  | ||||||
| type IRegistryItem<S extends IDataParamSettingItem> = { |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 获取方法 |  | ||||||
|      */ |  | ||||||
|     get: S["get"]; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 异步获取方法 |  | ||||||
|      */ |  | ||||||
|     getAsync: S["getAsync"] extends Function ? S["getAsync"] :  |  | ||||||
|     S["get"] extends ((...P: any) => S["type"]) ? (...param: Parameters<S["get"]>) => Promise<S["type"]> : INone; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 设置方法 |  | ||||||
|      */ |  | ||||||
|     set: S["set"]; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 异步设置方法 |  | ||||||
|      */ |  | ||||||
|     setAsync: S["setAsync"] extends Function ? S["setAsync"] :  |  | ||||||
|     S["set"] extends ((...P: [S["type"], ...any]) => any) ? (...param: Parameters<S["set"]>) => Promise<ReturnType<S["set"]>> : 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 |  | ||||||
|  *         get: () => number, |  | ||||||
|  *         set: (val: number) => void, |  | ||||||
|  *         getAsync: () => Promise<number> |  | ||||||
|  *     } |  | ||||||
|  *     }> { |  | ||||||
|  *         public onLoad() { |  | ||||||
|  *         let dataObject = {key: 1} |  | ||||||
|  *         this.getter("test", () => 1); |  | ||||||
|  *         this.registerKeyFromObject(dataObject, "test", "key"); |  | ||||||
|  *     } |  | ||||||
|  * } |  | ||||||
|  * ``` |  | ||||||
|  */ |  | ||||||
| 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 string,  |  | ||||||
|         O extends {[K in F]: D[KEY]["type"]} |  | ||||||
|     > (object: O, key: KEY, keyFromObject: F = key as any) { |  | ||||||
| 
 |  | ||||||
|         // 注册同步获取
 |  | ||||||
|         this.getter(key, () => { |  | ||||||
|             return object[keyFromObject] |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|         // 注册同步设置
 |  | ||||||
|         this.setter(key, (data: any) => { |  | ||||||
|             object[keyFromObject] = data |  | ||||||
|         }) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 关联 Storage 中的 key |  | ||||||
|      * @param key Data 层键值 |  | ||||||
|      * @param keyFromStorage StorageKey |  | ||||||
|      */ |  | ||||||
|     protected registerKeyFromStorage< |  | ||||||
|         KEY extends keyof D, |  | ||||||
|         F extends string,  |  | ||||||
|         S extends Storage<{[K in F]: D[KEY]["type"]}> |  | ||||||
|     > (storage: S, key: KEY, keyFromStorage: F = key as any) { |  | ||||||
| 
 |  | ||||||
|         // 同步获取
 |  | ||||||
|         this.getter(key, () => { |  | ||||||
|             return storage.get(keyFromStorage); |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|         // 同步设置
 |  | ||||||
|         this.setter(key, (data: any) => { |  | ||||||
|             storage.set(keyFromStorage, data); |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|         // 异步设置
 |  | ||||||
|         this.setter(key, (async (data: any) => { |  | ||||||
|             await storage.set(keyFromStorage, data); |  | ||||||
|         }) as any, true); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 导出数据对象 |  | ||||||
|      * @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 any; |  | ||||||
|             } |  | ||||||
|      |  | ||||||
|             // 检验 setter
 |  | ||||||
|             if (item.set && !item.setAsync) { |  | ||||||
|                 item.setAsync = this.syncFn2AsyncFn(item.set) as any; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // 在注册表中查找
 |  | ||||||
|         for (const key in this.registryList) fillFunction(key); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 将同步函数转换为异步函数 |  | ||||||
|      * @param fn 同步函数 |  | ||||||
|      */ |  | ||||||
|     protected syncFn2AsyncFn<F extends (...p: any) => any> (fn: F): (...param: Parameters<F>) => ReturnType<F> { |  | ||||||
|         const asyncFn = async (...param: [D]) => { |  | ||||||
|             return fn(...param); |  | ||||||
|         }; |  | ||||||
|         return asyncFn as any; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export { Data }; |  | ||||||
| export default Data; |  | ||||||
| @ -13,21 +13,6 @@ export type EventHandlerMap<Events extends Record<EventType, any>> = Map< | |||||||
| 	EventHandlerList<Events[keyof Events]> | 	EventHandlerList<Events[keyof Events]> | ||||||
| >; | >; | ||||||
| 
 | 
 | ||||||
| // Emitter function type
 |  | ||||||
| type IEmitParamType<E extends Record<EventType, any>, K extends keyof E> =  |  | ||||||
| 	E[K] extends ( undefined | void ) ? [type: K] : [type: K, evt: E[K]]; |  | ||||||
| 
 |  | ||||||
| // Mixin to event object
 |  | ||||||
| export type EventMixin<A extends Record<EventType, any>, B extends Record<EventType, any>> = { |  | ||||||
| 	[P in (keyof A | keyof B)] : |  | ||||||
| 		P extends (keyof A & keyof B) ?  |  | ||||||
| 			A[P] :  |  | ||||||
| 			P extends keyof A ?  |  | ||||||
| 				A[P] :  |  | ||||||
| 				P extends keyof B ? B[P] :  |  | ||||||
| 			never; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export class Emitter<Events extends Record<EventType, any>> { | export class Emitter<Events extends Record<EventType, any>> { | ||||||
| 	 | 	 | ||||||
| 	/** | 	/** | ||||||
| @ -89,8 +74,7 @@ export class Emitter<Events extends Record<EventType, any>> { | |||||||
| 	 * @param {Any} [evt] Any value (object is recommended and powerful), passed to each handler | 	 * @param {Any} [evt] Any value (object is recommended and powerful), passed to each handler | ||||||
| 	 * @memberOf mitt | 	 * @memberOf mitt | ||||||
| 	 */ | 	 */ | ||||||
| 	public emit<Key extends keyof Events>(...param: IEmitParamType<Events, Key>): this { | 	emit<Key extends keyof Events>(type: Key, evt: Events[Key]) { | ||||||
| 		const [ type, evt ] = param; |  | ||||||
| 		let handlers = this.all!.get(type); | 		let handlers = this.all!.get(type); | ||||||
| 		if (handlers) { | 		if (handlers) { | ||||||
| 			(handlers as EventHandlerList<Events[keyof Events]>) | 			(handlers as EventHandlerList<Events[keyof Events]>) | ||||||
| @ -99,6 +83,5 @@ export class Emitter<Events extends Record<EventType, any>> { | |||||||
| 					handler(evt!); | 					handler(evt!); | ||||||
| 				}); | 				}); | ||||||
| 		} | 		} | ||||||
| 		return this; |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @ -491,27 +491,6 @@ 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} | export { Manager, Modular, AnyWXContext, WXContext, ILifetime} | ||||||
| @ -1,154 +0,0 @@ | |||||||
| import { Data } from "../core/Data"; |  | ||||||
| import { Storage } from "../core/Storage"; |  | ||||||
| import { Login, ILoginOutput } from "../api/Login"; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 登录状态 |  | ||||||
|  */ |  | ||||||
| enum LoginStatus { |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 已认证 |  | ||||||
|      */ |  | ||||||
|     verified = 1, |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 失效的认证 |  | ||||||
|      * 通常为用户名密码错误 |  | ||||||
|      */ |  | ||||||
|     invalid = 2, |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 没有登录信息 |  | ||||||
|      */ |  | ||||||
|     none = 3 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * API 返回数据 |  | ||||||
|  */ |  | ||||||
| type ILoginApiData = { |  | ||||||
|     [P in keyof ILoginOutput]: { |  | ||||||
|         type: ILoginOutput[P]; |  | ||||||
|         getAsync: () => Promise<ILoginOutput[P]>; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Storage 缓存数据类型 |  | ||||||
|  */ |  | ||||||
| type IStudentInfoStorageData = ILoginOutput & { |  | ||||||
|     [P in keyof IStudentInfoData]: IStudentInfoData[P]["type"]; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 学生信息数据结构 |  | ||||||
|  */ |  | ||||||
| type IStudentInfoData = { |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 学号 |  | ||||||
|      */ |  | ||||||
|     studentId: { |  | ||||||
|         type: string, |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 教务处密码 |  | ||||||
|      */ |  | ||||||
|     password: { |  | ||||||
|         type: string |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 登录状态 |  | ||||||
|      */ |  | ||||||
|     loginStatus: { |  | ||||||
|         type: LoginStatus |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 上次登录时间 |  | ||||||
|      * 时间戳 |  | ||||||
|      */ |  | ||||||
|     lastLoginTime: { |  | ||||||
|         type: number |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 距离上次登录后 |  | ||||||
|      * 学号和密码是否发生过改变 |  | ||||||
|      */ |  | ||||||
|     isUserInfoChange: { |  | ||||||
|         type: boolean |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 学生信息 |  | ||||||
|  */ |  | ||||||
| class StudentInfo extends Data<IStudentInfoData & ILoginApiData> { |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 学生信息缓存 |  | ||||||
|      */ |  | ||||||
|     private eduStorage = new Storage<IStudentInfoStorageData>("StudentInfo", { |  | ||||||
|         idCardLast6: "", |  | ||||||
|         eduService: "", |  | ||||||
|         actualName: "", |  | ||||||
|         eduSession: "", |  | ||||||
|         studentId: "", |  | ||||||
|         password: "", |  | ||||||
|         loginStatus: LoginStatus.none, |  | ||||||
|         lastLoginTime: 0, |  | ||||||
|         isUserInfoChange: false |  | ||||||
|     }); |  | ||||||
| 
 |  | ||||||
|     public override onLoad() { |  | ||||||
|          |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 用户登录 |  | ||||||
|      */ |  | ||||||
|     private async login(): Promise<boolean> { |  | ||||||
| 
 |  | ||||||
|         // 获取账号密码
 |  | ||||||
|         const stuId = this.eduStorage.get("studentId"); |  | ||||||
|         const pwd = this.eduStorage.get("password"); |  | ||||||
| 
 |  | ||||||
|         if (!stuId || !pwd) return false; |  | ||||||
| 
 |  | ||||||
|         // 发送请求
 |  | ||||||
|         const data = await new Login().param({ |  | ||||||
|             studentId: stuId, |  | ||||||
|             password: pwd |  | ||||||
|         }).request().wait(); |  | ||||||
| 
 |  | ||||||
|         // 请求成功
 |  | ||||||
|         let res = data.data; |  | ||||||
|         if (res) { |  | ||||||
| 
 |  | ||||||
|             // 保存数据
 |  | ||||||
|             this.eduStorage.set("actualName", res.actualName); |  | ||||||
|             this.eduStorage.set("eduService", res.eduService); |  | ||||||
|             this.eduStorage.set("eduSession", res.eduSession); |  | ||||||
|             this.eduStorage.set("idCardLast6", res.idCardLast6); |  | ||||||
| 
 |  | ||||||
|             // 记录时间
 |  | ||||||
|             this.eduStorage.set("lastLoginTime", new Date().getTime()); |  | ||||||
| 
 |  | ||||||
|             return true; |  | ||||||
|         } else { |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 获取状态 |  | ||||||
|      */ |  | ||||||
|     private async getStatus() {} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export { StudentInfo }; |  | ||||||
| export default StudentInfo; |  | ||||||
| @ -1,99 +0,0 @@ | |||||||
| @import "../app.scss"; |  | ||||||
| 
 |  | ||||||
| view.mask { |  | ||||||
| 	position: fixed; |  | ||||||
| 	width: 100%; |  | ||||||
| 	height: 100%; |  | ||||||
| 	background-color: rgba($color: #000000, $alpha: .2); |  | ||||||
| 	z-index: 1; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| view.layer { |  | ||||||
| 	position: fixed; |  | ||||||
| 	@include container; |  | ||||||
| 	height: 100%; |  | ||||||
| 	z-index: 2; |  | ||||||
|     justify-content: center; |  | ||||||
|     align-items: center; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| view.occlude { |  | ||||||
| 	position: fixed; |  | ||||||
| 	width: 100%; |  | ||||||
| 	height: 100%; |  | ||||||
| 	z-index: 3; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| view.mask.block, view.layer.block, view.occlude.block { |  | ||||||
|     display: flex; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| view.mask.none, view.layer.none, view.occlude.none { |  | ||||||
|     display: none; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| view.mask.show-fade, view.layer.show-fade, view.occlude.show-fade { |  | ||||||
|     animation: show-fade .1s cubic-bezier(0, 0, 1, 1) both; |  | ||||||
|     opacity: 1; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| view.mask.hide-fade, view.layer.hide-fade, view.occlude.hide-fade { |  | ||||||
|     animation: hide-fade .1s cubic-bezier(0, 0, 1, 1) both; |  | ||||||
|     opacity: 0; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| view.mask.show-scale, view.layer.show-scale, view.occlude.show-scale { |  | ||||||
|     animation: show-scale .3s cubic-bezier(.1, .9, .2, 1) both, |  | ||||||
|     show-fade .1s cubic-bezier(0, 0, 1, 1) both; |  | ||||||
|     transform: scale3d(1, 1, 1); |  | ||||||
|     opacity: 1; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| view.mask.hide-scale, view.layer.hide-scale, view.occlude.hide-scale { |  | ||||||
|     animation: hide-scale .3s cubic-bezier(.1, .9, .2, 1) both,  |  | ||||||
|     hide-fade .1s cubic-bezier(0, 0, 1, 1) both; |  | ||||||
|     transform: scale3d(.9, .9, 1); |  | ||||||
|     opacity: 0; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| @media (prefers-color-scheme: dark) { |  | ||||||
|     view.mask { |  | ||||||
|         background-color: rgba($color: #000000, $alpha: .5); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| @keyframes show-fade{ |  | ||||||
|     from { |  | ||||||
|         opacity: 0; |  | ||||||
|     } |  | ||||||
|     to { |  | ||||||
|         opacity: 1; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| @keyframes hide-fade{ |  | ||||||
|     from { |  | ||||||
|         opacity: 1; |  | ||||||
|     } |  | ||||||
|     to { |  | ||||||
|         opacity: 0; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| @keyframes show-scale{ |  | ||||||
|     from { |  | ||||||
|         transform: scale3d(1.15, 1.15, 1); |  | ||||||
|     } |  | ||||||
|     to { |  | ||||||
|         transform: scale3d(1, 1, 1); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| @keyframes hide-scale{ |  | ||||||
|     from { |  | ||||||
|         transform: scale3d(1, 1, 1); |  | ||||||
|     } |  | ||||||
|     to { |  | ||||||
|         transform: scale3d(.9, .9, 1); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,309 +0,0 @@ | |||||||
| import { IAnyData } from "../core/Api"; |  | ||||||
| import { Emitter } from "../core/Emitter"; |  | ||||||
| import { Modular, Manager } from "../core/Module"; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 动画类型 |  | ||||||
|  */ |  | ||||||
| enum AnimateType { |  | ||||||
|     fade = 1, |  | ||||||
|     scale = 2, |  | ||||||
|     none = 3, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 显示层 |  | ||||||
|  */ |  | ||||||
| class DisplayLayer { |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 类名 |  | ||||||
|      */ |  | ||||||
|     public get className(): string { |  | ||||||
|         let res = this.isDisplay ? "block" : "none"; |  | ||||||
| 
 |  | ||||||
|         switch (this.animateType) { |  | ||||||
|             case AnimateType.fade: |  | ||||||
|                 res += " mask"; |  | ||||||
|                 break; |  | ||||||
| 
 |  | ||||||
|             case AnimateType.scale: |  | ||||||
|                 res += " layer"; |  | ||||||
|                 break; |  | ||||||
| 
 |  | ||||||
|             case AnimateType.none: |  | ||||||
|                 res += " occlude"; |  | ||||||
|                 break; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         switch (this.animateType) { |  | ||||||
|             case AnimateType.fade: |  | ||||||
|                 res += this.isShow ? " show-fade" : " hide-fade"; |  | ||||||
|                 break; |  | ||||||
| 
 |  | ||||||
|             case AnimateType.scale: |  | ||||||
|                 res += this.isShow ? " show-scale" : " hide-scale"; |  | ||||||
|                 break; |  | ||||||
| 
 |  | ||||||
|             case AnimateType.none: |  | ||||||
|                 break; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return res; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * Layer 使用的 key |  | ||||||
| 	 */ |  | ||||||
| 	public key: string = ""; |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 使用的动画类型 |  | ||||||
| 	 */ |  | ||||||
| 	public animateType: AnimateType = AnimateType.scale; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 动画时间 |  | ||||||
|      */ |  | ||||||
|     public get animateTime(): number { |  | ||||||
|         switch (this.animateType) { |  | ||||||
|             case AnimateType.fade: |  | ||||||
|                 return 100; |  | ||||||
|             case AnimateType.scale: |  | ||||||
|                 return 300; |  | ||||||
|             case AnimateType.none: |  | ||||||
|                 return -1; |  | ||||||
|         } |  | ||||||
|     }; |  | ||||||
| 	 |  | ||||||
| 	/** |  | ||||||
| 	 * 是否显示 |  | ||||||
| 	 */ |  | ||||||
| 	public isDisplay: boolean = false; |  | ||||||
| 
 |  | ||||||
| 	 /** |  | ||||||
| 	  * 是否显示 |  | ||||||
| 	  */ |  | ||||||
| 	public isShow: boolean = false; |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
|      * 消失动画计时器 |  | ||||||
|      */ |  | ||||||
| 	public disappearTimer?: number; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 弹出层事件 |  | ||||||
|  */ |  | ||||||
| type IPopupLayerEvent<L extends string> = { |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 点击蒙版时 |  | ||||||
| 	 */ |  | ||||||
| 	clickMask: void |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 显示层 |  | ||||||
| 	 */ |  | ||||||
| 	show: L; |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 隐藏层 |  | ||||||
| 	 */ |  | ||||||
| 	hide: L; |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 层状态改变 |  | ||||||
| 	 */ |  | ||||||
| 	change: Readonly<DisplayLayer>; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type commonLayerType = "mask" | "occlude"; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 弹出层 |  | ||||||
|  */ |  | ||||||
| class PopupLayer< |  | ||||||
| 	L extends string, |  | ||||||
| 	M extends Manager = Manager |  | ||||||
| > extends Modular<M, {}, IPopupLayerEvent<L | commonLayerType>> { |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 当点击时是否自动关闭蒙版 |  | ||||||
|      */ |  | ||||||
|     public autoCloseOnClick: boolean = true; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 关闭动画执行时是否屏蔽用户点击 |  | ||||||
|      */ |  | ||||||
|     public showOccludeWhenHide: boolean = true; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 显示 Layer 时自动关闭其他层 |  | ||||||
|      */ |  | ||||||
|     public hideOtherWhenShow: boolean = true; |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 层列表 |  | ||||||
| 	 */ |  | ||||||
| 	private layers: Map<L | commonLayerType, DisplayLayer> = new Map(); |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 初始化层 |  | ||||||
|      * @param key 初始化关键字 |  | ||||||
|      */ |  | ||||||
|     public initLayers(key: L[]) { |  | ||||||
|         for (let i = 0; i < key.length; i++) { |  | ||||||
|             this.render(this.getDisplayLayer(key[i])); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| 	public onLoad(): void { |  | ||||||
| 		this.on("show", this.handleShowLayer); |  | ||||||
| 		this.on("hide", this.handleHideLayer); |  | ||||||
|         this.setFunc(this.handleClickMask, "clickMask"); |  | ||||||
| 
 |  | ||||||
|         // 添加蒙版层
 |  | ||||||
|         const maskLayer = this.getDisplayLayer("mask"); |  | ||||||
|         maskLayer.animateType = AnimateType.fade; |  | ||||||
|         this.render(maskLayer); |  | ||||||
| 
 |  | ||||||
|         // 添加遮蔽层
 |  | ||||||
|         const occludeLayer = this.getDisplayLayer("occlude"); |  | ||||||
|         occludeLayer.animateType = AnimateType.none; |  | ||||||
|         this.render(occludeLayer); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 渲染 Layers |  | ||||||
|      */ |  | ||||||
|     private render(layer: DisplayLayer) { |  | ||||||
|         this.setData({ [`${ layer.key }$className`]: layer.className }); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 获取显示层 |  | ||||||
| 	 */ |  | ||||||
| 	private getDisplayLayer<K extends L | commonLayerType>(e: K): DisplayLayer { |  | ||||||
| 		let displayLayer = this.layers.get(e); |  | ||||||
| 		if (!displayLayer) { |  | ||||||
| 			displayLayer = new DisplayLayer(); |  | ||||||
| 			displayLayer.key = e; |  | ||||||
| 			this.layers.set(e, displayLayer); |  | ||||||
| 		} |  | ||||||
| 		return displayLayer; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 响应蒙版点击事件 |  | ||||||
|      */ |  | ||||||
|     private handleClickMask = () => { |  | ||||||
| 
 |  | ||||||
|         if (!this.autoCloseOnClick) return; |  | ||||||
| 
 |  | ||||||
|         // 关闭全部开启的层
 |  | ||||||
|         this.layers.forEach((layer) => { |  | ||||||
|             if (layer.isShow) (this as Emitter<IAnyData>).emit("hide", layer.key); |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|         // 关闭蒙版
 |  | ||||||
|         (this as Emitter<IAnyData>).emit("hide", "mask"); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 响应显示层事件 |  | ||||||
| 	 */ |  | ||||||
| 	private handleShowLayer = <K extends L | commonLayerType>(e: K) => { |  | ||||||
| 		let displayLayer = this.getDisplayLayer(e); |  | ||||||
| 
 |  | ||||||
|         // 阻止未发生的变化
 |  | ||||||
|         if (displayLayer.isShow) return; |  | ||||||
| 
 |  | ||||||
|         // 关闭其他层
 |  | ||||||
|         if (e !== "mask" && e !== "occlude" && this.hideOtherWhenShow) { |  | ||||||
|             this.layers.forEach((layer) => { |  | ||||||
|                 if (layer.key === "mask" || layer.key === "occlude") return; |  | ||||||
|                 if (layer.isShow) { |  | ||||||
|                     (this as Emitter<IAnyData>).emit("hide", layer.key); |  | ||||||
|                 } |  | ||||||
|             }); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // 显示蒙版
 |  | ||||||
|         if (e !== "mask" && e !== "occlude") |  | ||||||
|         (this as Emitter<IAnyData>).emit("show", "mask"); |  | ||||||
| 
 |  | ||||||
| 		// 取消消失定时
 |  | ||||||
| 		displayLayer.disappearTimer &&  |  | ||||||
| 		clearTimeout(displayLayer.disappearTimer); |  | ||||||
| 
 |  | ||||||
|         displayLayer.isShow = true; |  | ||||||
|         displayLayer.isDisplay = true; |  | ||||||
|         this.render(displayLayer); |  | ||||||
| 
 |  | ||||||
|         this.emit("change", displayLayer); |  | ||||||
| 	}; |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * 响应隐藏层事件 |  | ||||||
| 	 */ |  | ||||||
| 	private handleHideLayer = <K extends L | commonLayerType>(e: K) => { |  | ||||||
| 		let displayLayer = this.getDisplayLayer(e); |  | ||||||
| 
 |  | ||||||
|         // 阻止未发生的变化
 |  | ||||||
|         if (!displayLayer.isShow) return; |  | ||||||
| 
 |  | ||||||
|         if (displayLayer.animateTime <= 0) { |  | ||||||
| 
 |  | ||||||
|             displayLayer.isShow = false; |  | ||||||
|             displayLayer.isDisplay = false; |  | ||||||
|             this.render(displayLayer); |  | ||||||
|         } else { |  | ||||||
| 
 |  | ||||||
|             displayLayer.isShow = false; |  | ||||||
|             this.render(displayLayer); |  | ||||||
| 
 |  | ||||||
|             // 开启遮蔽
 |  | ||||||
|             if (this.showOccludeWhenHide) { |  | ||||||
|                 (this as Emitter<IAnyData>).emit("show", "occlude"); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             displayLayer.disappearTimer = setTimeout(() => { |  | ||||||
|                 displayLayer.isDisplay = false; |  | ||||||
|                 this.render(displayLayer); |  | ||||||
| 
 |  | ||||||
|                 // 取消 timer
 |  | ||||||
|                 displayLayer.disappearTimer = undefined; |  | ||||||
| 
 |  | ||||||
|                 // 检测是否关闭遮蔽
 |  | ||||||
|                 let needOcclude = true; |  | ||||||
|                 this.layers.forEach((layer) => { |  | ||||||
|                     if (layer === displayLayer) return; |  | ||||||
|                     if (layer.disappearTimer) needOcclude = false; |  | ||||||
|                 }); |  | ||||||
| 
 |  | ||||||
|                 // 关闭遮蔽
 |  | ||||||
|                 if (needOcclude && this.showOccludeWhenHide) { |  | ||||||
|                     (this as Emitter<IAnyData>).emit("hide", "occlude"); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|             }, displayLayer.animateTime); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // 关闭蒙版
 |  | ||||||
|         if (e !== "mask" && e !== "occlude") { |  | ||||||
|             let needMask = true; |  | ||||||
|             this.layers.forEach((layer) => { |  | ||||||
|                 if (layer === displayLayer) return; |  | ||||||
|                 if (layer.isShow) needMask = false; |  | ||||||
|             }); |  | ||||||
| 
 |  | ||||||
|             if (needMask) (this as Emitter<IAnyData>).emit("hide", "mask"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         this.emit("change", displayLayer); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export { PopupLayer }; |  | ||||||
| export default PopupLayer; |  | ||||||
| @ -1,7 +1,7 @@ | |||||||
| @import "./UserCard.scss"; | @import "./UserCard.scss"; | ||||||
| @import "./MainFunction.scss"; | @import "./MainFunction.scss"; | ||||||
| @import "./FunctionList.scss"; | @import "./FunctionList.scss"; | ||||||
| @import "../../modular/PopupLayer.scss"; | 
 | ||||||
| 
 | 
 | ||||||
| view.container{ | view.container{ | ||||||
|     padding-top: 50rpx; |     padding-top: 50rpx; | ||||||
|  | |||||||
| @ -2,37 +2,9 @@ import { Manager } from "../../core/Module"; | |||||||
| import { UserCard } from "./UserCard"; | import { UserCard } from "./UserCard"; | ||||||
| import { MainFunction } from "./MainFunction"; | import { MainFunction } from "./MainFunction"; | ||||||
| import { FunctionList } from "./FunctionList"; | import { FunctionList } from "./FunctionList"; | ||||||
| import { PopupLayer } from "../../modular/PopupLayer"; |  | ||||||
| import { TestLayerA } from "./TestLayerA"; |  | ||||||
| 
 | 
 | ||||||
| (async () => { | Manager.Page((manager) => { | ||||||
| 
 |     manager.addModule(UserCard, "userCard"); | ||||||
|     // 初始化页面
 |  | ||||||
|     const { manager, query } = await Manager.PageAsync(); |  | ||||||
| 
 |  | ||||||
|     // 添加弹出层 Modular
 |  | ||||||
|     const popupLayer: PopupLayer<"layerA" | "layerB"> = manager.addModule(PopupLayer, "mask") as any; |  | ||||||
| 
 |  | ||||||
|     // 添加 UserCard Modular
 |  | ||||||
|     const userCard = manager.addModule(UserCard, "userCard"); |  | ||||||
| 
 |  | ||||||
|     //#region test layer
 |  | ||||||
|     popupLayer.initLayers(["layerA", "layerB"]); |  | ||||||
|     const testLayerA = manager.addModule(TestLayerA, "testLayerA"); |  | ||||||
|     userCard.on("clickChangeTheme", () => { |  | ||||||
|         popupLayer.emit("show", "layerA"); |  | ||||||
|     }) |  | ||||||
|     testLayerA.on("click", () => { |  | ||||||
|         popupLayer.emit("show", "layerB"); |  | ||||||
|     }) |  | ||||||
|     //#endregion
 |  | ||||||
| 
 |  | ||||||
|     // 添加 MainFunction Modular
 |  | ||||||
|     manager.addModule(MainFunction, "mainFunction"); |     manager.addModule(MainFunction, "mainFunction"); | ||||||
| 
 |  | ||||||
|     // 添加 FunctionList Modular
 |  | ||||||
|     manager.addModule(FunctionList, "functionList"); |     manager.addModule(FunctionList, "functionList"); | ||||||
| 
 | }); | ||||||
|     // 初始化全部 Modular
 |  | ||||||
|     await manager.loadAllModule(query); |  | ||||||
| })(); |  | ||||||
| @ -1,18 +1,3 @@ | |||||||
| <!-- 层遮蔽层 --> |  | ||||||
| <view class="{{ mask$occlude$className }}" bindtap="mask$clickMask"></view> |  | ||||||
| 
 |  | ||||||
| <!-- 蒙版 --> |  | ||||||
| <view class="{{ mask$mask$className }}" bindtap="mask$clickMask"></view> |  | ||||||
| 
 |  | ||||||
| <!-- 层A --> |  | ||||||
| <view class="{{ mask$layerA$className }}" bindtap="mask$clickMask"> |  | ||||||
|     <view class="card" style="height: 300px; line-height: 300px; text-align:center" catchtap="testLayerA$click">layerA(点击显示layerB)</view> |  | ||||||
| </view> |  | ||||||
| 
 |  | ||||||
| <!-- 层B --> |  | ||||||
| <view class="{{ mask$layerB$className }}" bindtap="mask$clickMask"> |  | ||||||
|     <view class="card" style="height: 200px; line-height: 200px; text-align:center" catchtap>layerB</view> |  | ||||||
| </view> |  | ||||||
| 
 | 
 | ||||||
| <!-- 顶部的阴影 --> | <!-- 顶部的阴影 --> | ||||||
| <view class="top-shadow"></view> | <view class="top-shadow"></view> | ||||||
| @ -29,10 +14,8 @@ | |||||||
| 
 | 
 | ||||||
|             <!-- 主题变换按钮 --> |             <!-- 主题变换按钮 --> | ||||||
|             <view class="theme"> |             <view class="theme"> | ||||||
|                 <view bindtap="userCard$changeTheme"> |  | ||||||
|                 <image class="icon-sub" src="../../image/account/Account_Theme.svg" /> |                 <image class="icon-sub" src="../../image/account/Account_Theme.svg" /> | ||||||
|             </view> |             </view> | ||||||
|             </view> |  | ||||||
|              |              | ||||||
|             <!-- 用户昵称 --> |             <!-- 用户昵称 --> | ||||||
|             <view class="nick h1"> |             <view class="nick h1"> | ||||||
| @ -44,7 +27,7 @@ | |||||||
|                 <view class="name">秦浩轩</view> |                 <view class="name">秦浩轩</view> | ||||||
|                 <view class="certified"> |                 <view class="certified"> | ||||||
|                     <view class="certifi-info">已认证</view> |                     <view class="certifi-info">已认证</view> | ||||||
|                     <image class="text-icon" src="../../image/account/Account_OK.svg"></image> |                     <view class="text-icon">√</view> | ||||||
|                 </view> |                 </view> | ||||||
|             </view> |             </view> | ||||||
| 
 | 
 | ||||||
| @ -57,27 +40,74 @@ | |||||||
|     </view> |     </view> | ||||||
| 
 | 
 | ||||||
|     <!--主要功能--> |     <!--主要功能--> | ||||||
|     <view class="card main-function"> |     <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 class="branch-funtion" wx:for="{{ mainFunction$mainFunctionList }}" wx:key="index"> |             <view> | ||||||
|             <view style="{{ index == (mainFunction$mainFunctionList - 1) ? 'border-bottom: 0px' : '' }}"> |                 <image class="icon" src="../../image/account/Account_DateList.svg"></image> | ||||||
|                 <!--每个功能的图片--> |                 <view>课表缓存</view> | ||||||
|                 <image class="icon" src="../../image/account/Account_{{ item.iconUrl }}.svg"></image> |             </view> | ||||||
|                 <!--每个功能的文字--> |         </view> | ||||||
|                 <view>{{ item.displayName }}</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> |             </view> | ||||||
|         </view> |         </view> | ||||||
|     </view> |     </view> | ||||||
| 
 | 
 | ||||||
|     <!-- 功能列表 --> |     <!-- 功能列表 --> | ||||||
|     <view class="card function-list"> |     <view class="card function-list"> | ||||||
|  |         <view class="function"> | ||||||
|  |             <view> | ||||||
|  |                 <image class="icon func-icon" src="../../image/account/Account_Sponsor.svg" /> | ||||||
|  |                 <view>赞助计划</view> | ||||||
|  |                 <image class="icon-sub arrow" src="../../image/account/Account_Arrow.svg" /> | ||||||
|  |             </view> | ||||||
|  |         </view> | ||||||
| 
 | 
 | ||||||
|         <!-- 每一行 --> |         <view class="function"> | ||||||
|         <view class="function" wx:for="{{ functionList$functionList }}" wx:key="index"> |             <view> | ||||||
|             <view style="{{ index == (functionList$functionList.length - 1) ? 'border-bottom: 0px' : '' }}"> |                 <image class="icon func-icon" src="../../image/account/Account_PubilcAccount.svg" /> | ||||||
|                 <image class="icon func-icon" src="../../image/account/Account_{{ item.iconUrl }}.svg" /> |                 <view>公众号</view> | ||||||
|                 <view>{{ item.displayName }}</view> |                 <image class="icon-sub arrow" src="../../image/account/Account_Arrow.svg" /> | ||||||
|  |             </view> | ||||||
|  |         </view> | ||||||
|  | 
 | ||||||
|  |         <view class="function"> | ||||||
|  |             <view> | ||||||
|  |                 <image class="icon func-icon" src="../../image/account/Account_FAQ.svg" /> | ||||||
|  |                 <view>自助问答</view> | ||||||
|  |                 <image class="icon-sub arrow" src="../../image/account/Account_Arrow.svg" /> | ||||||
|  |             </view> | ||||||
|  |         </view> | ||||||
|  | 
 | ||||||
|  |         <view class="function"> | ||||||
|  |             <view> | ||||||
|  |                 <image class="icon func-icon" src="../../image/account/Account_AboutUs.svg" /> | ||||||
|  |                 <view>关于我们</view> | ||||||
|  |                 <image class="icon-sub arrow" src="../../image/account/Account_Arrow.svg" /> | ||||||
|  |             </view> | ||||||
|  |         </view> | ||||||
|  | 
 | ||||||
|  |         <view class="function"> | ||||||
|  |             <view style="border-bottom: 0px;"> | ||||||
|  |                 <image class="icon func-icon" src="../../image/account/Account_Support.svg" /> | ||||||
|  |                 <view>联系客服</view> | ||||||
|                 <image class="icon-sub arrow" src="../../image/account/Account_Arrow.svg" /> |                 <image class="icon-sub arrow" src="../../image/account/Account_Arrow.svg" /> | ||||||
|             </view> |             </view> | ||||||
|         </view> |         </view> | ||||||
|  | |||||||
| @ -1,32 +1,7 @@ | |||||||
| import { Modular, Manager } from "../../core/Module"; | import { Modular, Manager } from "../../core/Module"; | ||||||
| 
 | 
 | ||||||
| interface IFunctionListItem { |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 显示名称 |  | ||||||
|      */ |  | ||||||
|     displayName: string; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 图标路径 |  | ||||||
|      */ |  | ||||||
|     iconUrl: string; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| class FunctionList<M extends Manager> extends Modular<M> { | class FunctionList<M extends Manager> extends Modular<M> { | ||||||
|      |      | ||||||
|     public static readonly functionList: IFunctionListItem[] = [ |  | ||||||
|         { displayName: "赞助计划", iconUrl: "Sponsor" }, |  | ||||||
|         { displayName: "公众号", iconUrl: "PubilcAccount" }, |  | ||||||
|         { displayName: "自助问答", iconUrl: "FAQ" }, |  | ||||||
|         { displayName: "关于我们", iconUrl: "AboutUs" }, |  | ||||||
|         { displayName: "联系客服", iconUrl: "Support" } |  | ||||||
|     ]; |  | ||||||
| 
 |  | ||||||
|     public data = { |  | ||||||
|         functionList: FunctionList.functionList |  | ||||||
|     }; |  | ||||||
|      |  | ||||||
|     public override onLoad() { |     public override onLoad() { | ||||||
|         // Do something
 |         // Do something
 | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -1,31 +1,7 @@ | |||||||
| import { Modular, Manager } from "../../core/Module"; | import { Modular, Manager } from "../../core/Module"; | ||||||
| 
 | 
 | ||||||
| interface IMainFunctionItem { |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 显示名称 |  | ||||||
|      */ |  | ||||||
|     displayName: string; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 图标路径 |  | ||||||
|      */ |  | ||||||
|     iconUrl: string; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| class MainFunction<M extends Manager> extends Modular<M> { | 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() { |     public override onLoad() { | ||||||
|         // Do something
 |         // Do something
 | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -1,26 +0,0 @@ | |||||||
| import { Modular, Manager } from "../../core/Module"; |  | ||||||
| 
 |  | ||||||
| type IUserCardEvent = { |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 主题更换按钮点击事件 |  | ||||||
|      */ |  | ||||||
|     click: void; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| class TestLayerA<M extends Manager> extends Modular<M, {}, IUserCardEvent> { |  | ||||||
| 
 |  | ||||||
|     public override onLoad() { |  | ||||||
|         this.setFunc(this.handleClick, "click"); |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     /** |  | ||||||
|      * 弹窗点击时 |  | ||||||
|      */ |  | ||||||
|     private handleClick() { |  | ||||||
|         this.emit("click"); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export { TestLayerA }; |  | ||||||
| export default TestLayerA; |  | ||||||
| @ -27,17 +27,9 @@ view.user-card { | |||||||
|             display: flex; |             display: flex; | ||||||
|             justify-content: flex-end; |             justify-content: flex-end; | ||||||
| 
 | 
 | ||||||
|             view { |             image { | ||||||
|                 width: 23px; |                 width: 23px; | ||||||
|                 height: 23px; |                 height: 23px; | ||||||
|                 padding: 20px; |  | ||||||
|                 margin: -20px; |  | ||||||
|                 border-radius: 20px; |  | ||||||
|                  |  | ||||||
|                 image { |  | ||||||
|                     width: 100%; |  | ||||||
|                     height: 100%; |  | ||||||
|                 } |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|          |          | ||||||
| @ -49,7 +41,6 @@ view.user-card { | |||||||
|             text-overflow: ellipsis; |             text-overflow: ellipsis; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // 学生信息 |  | ||||||
|         view.student { |         view.student { | ||||||
|             display: flex; |             display: flex; | ||||||
|             align-items: center; |             align-items: center; | ||||||
| @ -66,10 +57,8 @@ view.user-card { | |||||||
|                 justify-content: center; |                 justify-content: center; | ||||||
|                 align-items: center; |                 align-items: center; | ||||||
| 
 | 
 | ||||||
|                 image.text-icon { |                 view.text-icon { | ||||||
|                     margin-left: .25em; |                     margin-left: .3em; | ||||||
|                     width: 10px; |  | ||||||
|                     height: 10px; |  | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -1,24 +1,9 @@ | |||||||
| import { Modular, Manager } from "../../core/Module"; | import { Modular, Manager } from "../../core/Module"; | ||||||
| 
 | 
 | ||||||
| type IUserCardEvent = { | class UserCard<M extends Manager> extends Modular<M> { | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * 主题更换按钮点击事件 |  | ||||||
|      */ |  | ||||||
|     clickChangeTheme: void; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| class UserCard<M extends Manager> extends Modular<M, {}, IUserCardEvent> { |  | ||||||
|      |      | ||||||
|     public override onLoad() { |     public override onLoad() { | ||||||
|         this.setFunc(this.handleChangeTheme, "changeTheme"); |         // Do something
 | ||||||
|     } |  | ||||||
|      |  | ||||||
|     /** |  | ||||||
|      * 处理主题更换 |  | ||||||
|      */ |  | ||||||
|     private handleChangeTheme() { |  | ||||||
|         this.emit("clickChangeTheme"); |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,6 +1,5 @@ | |||||||
| import { Modular, Manager, ILifetime } from "../../core/Module"; | import { Modular, Manager, ILifetime } from "../../core/Module"; | ||||||
| import { Login } from "../../api/Login"; | import { Login } from "../../api/Login"; | ||||||
| import { Schedlue } from "../../api/Schedule" |  | ||||||
| import { Storage } from "../../core/Storage"; | import { Storage } from "../../core/Storage"; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @ -29,14 +28,12 @@ implements Partial<ILifetime> { | |||||||
|             s.set("be", 12); |             s.set("be", 12); | ||||||
|         }, 1000) |         }, 1000) | ||||||
|          |          | ||||||
|         // new Login().param({studentId: "2017060129", password: ""})
 |         new Login().param({studentId: "1806240113", password: "qazxsw123"}) | ||||||
|         // .request().wait({
 |         .request().wait({ | ||||||
|         //     ok: (w) => {console.log("ok", w)},
 |             ok: (w) => {console.log("ok", w)}, | ||||||
|         //     no: (w) => {console.log("no", w)},
 |             no: (w) => {console.log("no", w)}, | ||||||
|         //     done: (w) => {console.log("done", w)}
 |             done: (w) => {console.log("done", w)} | ||||||
|         // });
 |         }); | ||||||
|         // new Schedlue().param({cookie:"C729D1AB1B17077485ACCD9279135C22",semester:"2020-2021-2"})
 |  | ||||||
|         // .request()
 |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -6,17 +6,7 @@ import { TestCore } from "./TestCore"; | |||||||
|  * 此页面使用 Manager 进行模块化管理 |  * 此页面使用 Manager 进行模块化管理 | ||||||
|  * 若要添加先功能请先定义 Modular 并添加至 Manager |  * 若要添加先功能请先定义 Modular 并添加至 Manager | ||||||
|  */ |  */ | ||||||
| (async () => { | Manager.Page((manager)=>{ | ||||||
| 
 |  | ||||||
|     // 初始化页面
 |  | ||||||
|     const { manager, query } = await Manager.PageAsync(); |  | ||||||
| 
 |  | ||||||
|     // 添加 StatusBar Modular
 |  | ||||||
|     manager.addModule(StatusBar, "statusBar"); |     manager.addModule(StatusBar, "statusBar"); | ||||||
|      |  | ||||||
|     // 添加 TestCore Modular
 |  | ||||||
|     manager.addModule(TestCore, "testCore"); |     manager.addModule(TestCore, "testCore"); | ||||||
| 
 | }) | ||||||
|     // 初始化全部 Modular
 |  | ||||||
|     await manager.loadAllModule(query); |  | ||||||
| })() |  | ||||||
| @ -1,8 +1,7 @@ | |||||||
| { | { | ||||||
|   "description": "项目配置文件,详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html", |   "description": "项目配置文件", | ||||||
|   "packOptions": { |   "packOptions": { | ||||||
|     "ignore": [], |     "ignore": [] | ||||||
|     "include": [] |  | ||||||
|   }, |   }, | ||||||
|   "miniprogramRoot": "miniprogram/", |   "miniprogramRoot": "miniprogram/", | ||||||
|   "compileType": "miniprogram", |   "compileType": "miniprogram", | ||||||
| @ -47,16 +46,13 @@ | |||||||
|     "useCompilerPlugins": [ |     "useCompilerPlugins": [ | ||||||
|       "typescript", |       "typescript", | ||||||
|       "sass" |       "sass" | ||||||
|     ], |     ] | ||||||
|     "useStaticServer": true |  | ||||||
|   }, |   }, | ||||||
|   "simulatorType": "wechat", |   "simulatorType": "wechat", | ||||||
|   "simulatorPluginLibVersion": {}, |   "simulatorPluginLibVersion": {}, | ||||||
|   "appid": "wx7d809f5e8955843d", |   "appid": "wx7d809f5e8955843d", | ||||||
|   "condition": {}, |   "scripts": { | ||||||
|   "srcMiniprogramRoot": "miniprogram/", |     "beforeCompile": "" | ||||||
|   "editorSetting": { |   }, | ||||||
|     "tabIndent": "insertSpaces", |   "condition": {} | ||||||
|     "tabSize": 2 |  | ||||||
|   } |  | ||||||
| } | } | ||||||
| @ -1,5 +1,14 @@ | |||||||
| { | { | ||||||
|   "condition": { |   "condition": { | ||||||
|  |     "plugin": { | ||||||
|  |       "list": [] | ||||||
|  |     }, | ||||||
|  |     "game": { | ||||||
|  |       "list": [] | ||||||
|  |     }, | ||||||
|  |     "gamePlugin": { | ||||||
|  |       "list": [] | ||||||
|  |     }, | ||||||
|     "miniprogram": { |     "miniprogram": { | ||||||
|       "list": [ |       "list": [ | ||||||
|         { |         { | ||||||
| @ -10,10 +19,5 @@ | |||||||
|         } |         } | ||||||
|       ] |       ] | ||||||
|     } |     } | ||||||
|   }, |   } | ||||||
|   "projectname": "mini-dlpu-v3", |  | ||||||
|   "setting": { |  | ||||||
|     "compileHotReLoad": true |  | ||||||
|   }, |  | ||||||
|   "description": "项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html" |  | ||||||
| } | } | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user