├── .DS_Store ├── .gitignore ├── README.md ├── examples └── LightmvcDemo.zip ├── lightMVC ├── .DS_Store ├── core │ ├── Constants.ts │ ├── Facade.ts │ ├── FrameworkCfg.ts │ ├── base │ │ ├── BaseCommand.ts │ │ ├── BaseMediator.ts │ │ ├── BaseModel.ts │ │ ├── BaseScene.ts │ │ ├── BaseView.ts │ │ └── ViewEvent.ts │ ├── command │ │ ├── AsyncMacroCommand.ts │ │ ├── MacroCommand.ts │ │ ├── SimpleCommand.ts │ │ └── SyncMacroCommand.ts │ └── manager │ │ ├── CommandManager.ts │ │ ├── ModelManager.ts │ │ ├── NotificationManager.ts │ │ └── ViewManager.ts └── util │ └── UIUtils.ts ├── lightMVC_ex └── README.md ├── light_mvc.d.ts └── mvc.png /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yue19870813/lightMVC/832b4f652e701b0398abe81b312e2373a2de24e4/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.meta 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | https://github.com/yue19870813/ituuz-x 2 | # lightMVC 3 | 后续lightMVC会集成到这个项目下,请关注这个项目[https://github.com/yue19870813/ituuz-x](https://github.com/yue19870813/ituuz-x) 4 | --- 5 | framework for cocos creator 6 | --- 7 | 简易轻量级MVC框架,适用于中小型项目使用。后续会拓展[lightMVC_ex](./lightMVC_ex/README.md)内容来适应大型项目的开发。这套轻量级MVC框架可以帮助开发者组织代码,以及业务结构,让项目更好维护和拓展,提高开发效率。examples目录下有完整的例子Demo。 8 | 9 | #### 架构图 10 | ![架构图](./mvc.png) 11 | #### 节点功能 12 | - Facade:全局控制类,持有对MVC各层的管理对象。原则上来说,除了初始化框架调用init和运行第一个场景外,都不应该引用和调用Facada中的任何接口和属性。该类是个全局的单例对象,包含几个重要的接口如下: 13 | ``` javascript 14 | /** 15 | * 初始化框架配置 16 | * @param {boolean} debug 是否是调试状态 17 | * @param {cc.Size} designResolution 设计分辨率 18 | * @param {boolean} fitHeight 是否高适配 19 | * @param {boolean} fitWidth 是否宽适配 20 | */ 21 | public init(debug: boolean, designResolution: cc.Size, fitHeight: boolean, fitWidth: boolean): void; 22 | 23 | /** 24 | * 运行场景 25 | * @param {{new(): BaseMediator}} mediator 场景mediator类型,类类型。 26 | * @param {{new(): BaseScene}} view 场景mediator类型,类类型。 27 | * @param {Object} data 自定义的任意类型透传数据。(可选) 28 | * @param {()=>void} cb 加载完成回调. 29 | */ 30 | public runScene(mediator: {new(): BaseMediator}, view: {new(): BaseScene}, data?: any, cb?: ()=>void): void; 31 | ``` 32 | - Model:数据对象,用于处理数据逻辑以及存储数据,常用来与服务器做数据交互,同时通过消息通知View层刷新显示。主要接口如下: 33 | ``` javascript 34 | /** Model初始化时会调用的接口,可以用来初始化一些数据 */ 35 | public init(): void; 36 | /** 37 | * 发送消息接口,当数据变化时需要调用此接口发送消息刷新View层。 38 | * @param {string} noti 消息名称 39 | * @param {Object} data 消息数据 40 | */ 41 | public sendNoti(noti: string, data?: any): void; 42 | /** 清理接口,子类可以实现清理逻辑 */ 43 | public clear(): void; 44 | ``` 45 | - View:显示层,根据业务逻辑及数据显示,同时处理用户输入,通过事件与其他层交互。主要接口如下: 46 | ``` javascript 47 | /** View创建时会被调用,子类可以重写 */ 48 | public init(): void; 49 | /** 50 | * 发送UI事件,逻辑层接收事件处理逻辑。 51 | * @param {string} event 事件名称 52 | * @param {Object} body 事件参数 53 | */ 54 | public sendEvent(event: string, body?: any): void; 55 | /** 关闭当前的界面 */ 56 | public closeView(): void; 57 | /** 关闭所有弹出的界面 */ 58 | public closeAllPopView(): void; 59 | /** 当界面被关闭时会被调用,子类可以重写该方法 */ 60 | public onClose(): void; 61 | /** 子类覆盖,返回UI的prefab路径,默认是空节点 */ 62 | public static path(): string; 63 | ``` 64 | - Mediator:逻辑层中介者,负责接收Model层通知来刷新View层显示,同时还要接收View层事件来处理用户输入,并通过Command处理数据层数据。主要接口如下: 65 | ``` javascript 66 | /** 67 | * 初始化接口,此时视图还没有创建,如果想操作视图view请在viewDidAppear函数中进行。 68 | * @param {Object} data 自定义的任意类型透传数据。(可选) 69 | * @override 70 | * */ 71 | public init(data?: any): void; 72 | /** 73 | * 视图显示后会调用的接口 74 | * @override 75 | */ 76 | public viewDidAppear(): void; 77 | /** 78 | * 绑定UI事件,接收view层派发的事件 79 | * @param {string} name 事件名称 80 | * @param {(any)=>void} cb 事件回调 81 | * @param {BaseMediator} target 回调绑定对象 82 | */ 83 | public bindEvent(name: string, cb: (body: any)=>void, target: BaseMediator): void; 84 | /** 85 | * 注册消息监听 86 | * @param {string} noti 通知key值 87 | * @param {(data: any)=>void} cb 通知监听的回调函数 88 | * @param {Object} target 回调绑定的对象 89 | */ 90 | public registerNoti(noti: string, cb: (data: any)=>void, target: any): void; 91 | /** 92 | * 发送消息通知 93 | * @param {string} noti 通知key值 94 | * @param {Object} body 消息传递的参数 95 | */ 96 | public sendNoti(noti: string, body: any): void; 97 | /** 98 | * 发送命令接口 99 | * @param {{new (): BaseCommand}} cmd 命令类 100 | * @param {Object} data 命令参数 101 | */ 102 | public sendCmd(cmd: {new (): T}, data?: any): void; 103 | /** 104 | * 打开新场景 105 | * @param data {Object} data 自定义的任意类型透传数据。(可选) 106 | */ 107 | public runScene(mediator: {new(): BaseMediator}, view: {new(): BaseScene}, data?: any): void; 108 | /** 109 | * 返回上一场景 110 | * @returns {boolean}是否存在上一个场景 111 | */ 112 | public backScene(): boolean; 113 | /** 114 | * 打开view界面 115 | * @param {{new(): BaseMediator}} mediator 界面mediator类型,类类型。 116 | * @param {{new(): BaseView}} view view 场景mediator类型,类类型。 117 | * @param {Object} data 自定义的任意类型透传数据。(可选) 118 | */ 119 | public popView(mediator: {new(): BaseMediator}, view: {new(): BaseView}, data?: any): void; 120 | /** 121 | * 添加层级 122 | * @param {{new(): BaseMediator}} mediator 界面mediator类型,类类型。 123 | * @param {{new(): BaseView}} view view 场景mediator类型,类类型。 124 | * @param {number} zOrder 层级。(可选) 125 | * @param {Object} data 自定义的任意类型透传数据。(可选) 126 | */ 127 | public addLayer(mediator: {new(): BaseMediator}, view: {new(): BaseView}, zOrder?: number, data?: any): void; 128 | /** 获取model对象 */ 129 | public getModel(model: {new (): T}): T; 130 | /** 销毁接口 */ 131 | public destroy(): void; 132 | ``` 133 | 134 | #### 使用方式 135 | 1. 初始化框架: 136 | ```javascript 137 | // 调试模式为false、设计分辨率为1080*2048、宽适配。 138 | Facade.getInstance().init(false, cc.size(1080, 2048), false, true); 139 | ``` 140 | 2. 注册model数据对象: 141 | ```javascript 142 | // 如果需要数据层,那么应该首先将所有需要的model在开始就都注册上。 143 | Facade.getInstance().registerModel(PlayerModel); 144 | ``` 145 | 3. 运行第一个场景: 146 | ```javascript 147 | // 运行第一个场景时调用Facade的runScene接口,传入要运行的Mediator和Scene,还可选传入参数。 148 | Facade.getInstance().runScene(DefaultSceneMediator, DefaultScene, "测试参数999"); 149 | ``` 150 | 4. 原则上说,除了上述三步需要引用Facade外,后面场景运行起来后就不需要再调用Facade了,在MVC的不同层级做对应的逻辑处理,父类接口都做了支持。 151 | 5. 场景运行后,可以在场景Mediator中创建层级view,或者pop出view。Layer view与pop view的区别就是,他们是两个管理器在进行管理,我们认为Layer是场景内初始化创建并且不会关闭的view界面,而pop view是可以随时打开或者关闭的view界面,当然具体怎么使用可以灵活处理。例如在DefaultSceneMediator中: 152 | ```javascript 153 | /** 154 | * 创建一个常驻的view界面FirstView 155 | * this.addLayer是BaseMediator中提供的基础功能接口(更多接口可以查看源码)。 156 | * 层级为1,并且传入参数:this._data 157 | * */ 158 | this.addLayer(FirstMediator, FirstView, 1, this._data); 159 | ``` 160 | 6. View层的UI节点操作接口。在View里有个成员属性ui,该界面的UI节点会在初始化时自动初始化到这个成员属性上,在操作UI节点时可以通过这个属性进行操作,该属性类型是UIContainer,常用接口是getNode和getComponent,示例代码如下: 161 | ```javascript 162 | // 获取node节点 163 | let closeBtnNode = this.ui.getNode("close_btn"); 164 | closeBtnNode.on(cc.Node.EventType.TOUCH_END, this.closeAllView, this); 165 | // 获取Component组件 166 | let desLabel = this.ui.getComponent("des_label", cc.Label); 167 | desLabel.string = "test"; 168 | ``` 169 | 7. View层与Mediator层的事件交互。Mediator直接持有View的引用,所以可以直接调用View中的接口,而View与Mediator就需要通过事件(Event)来进行交互了。首先需要在Mediator中注册监听: 170 | ```javascript 171 | this.bindEvent(FirstView.OPEN_B, (str: string)=>{ 172 | // todo something... 173 | }, this); 174 | ``` 175 | 然后在View中通过sendEvent接口发送事件来通知Mediator: 176 | ```javascript 177 | // 第一个参数是事件名称,第二个参数是传递的参数。 178 | this.sendEvent(FirstView.OPEN_B, "BBB"); 179 | ``` 180 | 8. Mediator操作Model数据。在Mediator中可以通过getModel接口获取到指定的Model对象,通过直接引用来读取Model中的数据。而在修改数据的时候有两种方式,一种是通过Model的引用直接进行修改,这种情况大多是比较简单直接修改某个数值等;另一种比较复杂,比如要获取多个Model的数据进行复杂的逻辑操作并且修改多个值的情况,这种就适合将逻辑封装到一个命令(Command)中,通过发送命令来处理数据,这样可以减少Mediator中逻辑复杂度和耦合度。例子如下: 181 | ```javascript 182 | // 直接通过引用进行修改的情况 183 | let playerModel = this.getModel(PlayerModel); 184 | this.view.setLevelDisplay(playerModel.getPlayerLv()); 185 | 186 | // 通过命令进行操作的情况 187 | this.sendCmd(UpdateExpCommand, exp); 188 | ``` 189 | 9. Model数据修改通知View刷新逻辑。大多数情况下,Model用来处理纯数据逻辑和与服务器交互的数据接口,当数据有变化时我们希望通知View刷新显示,这是我们只能通过抛出消息通知来告诉Mediator,然后通过Mediator来修改View显示,首先需要在Mediator中注册消息通知: 190 | ```javascript 191 | this.registerNoti(Notification.UPDATE_EXP_FINISH, ()=>{ 192 | // todo something ... 193 | }, this); 194 | ``` 195 | 然后我们在Model中通过发送这个消息通知来告诉Mediator: 196 | ```javascript 197 | // 该接口第二个参数可以传递参数 198 | this.sendNoti(Notification.UPDATE_EXP_FINISH); 199 | ``` 200 | 10. Mediator与Mediator之间的交互很简单,就是使用上面介绍Model向Mediator发送通知的方式。 201 | 202 | #### 其他 203 | 简单的交互规则和接口调用介绍就这么多,还有就是代码结构的组织也很重要,这个就是看每个人或者项目的合理安排了,毕竟也是仁者见仁,智者见智的事情。**同时在examples目录下有完整的例子Demo**。 204 | 205 | lightMVC目前仅适合中小型项目使用,过于复杂的大型项目可能应付起来就会有些吃力,不过后续会继续维护并拓展到[lightMVC_ex](./lightMVC_ex/README.md)中来支持大型项目开发,lightMVC会始终保持简单轻量。 206 | 207 | 最后框架中有什么问题或者需要改进的问题欢迎反馈。 208 | -------------------------------------------------------------------------------- /examples/LightmvcDemo.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yue19870813/lightMVC/832b4f652e701b0398abe81b312e2373a2de24e4/examples/LightmvcDemo.zip -------------------------------------------------------------------------------- /lightMVC/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yue19870813/lightMVC/832b4f652e701b0398abe81b312e2373a2de24e4/lightMVC/.DS_Store -------------------------------------------------------------------------------- /lightMVC/core/Constants.ts: -------------------------------------------------------------------------------- 1 | /** 打开view选项枚举 */ 2 | export enum OPEN_VIEW_OPTION { 3 | /** 固定UI层,与其它ui分开管理,需要设置zOrder */ 4 | LAYER, 5 | /** 叠加在其他view上 */ 6 | OVERLAY, 7 | /** 独立打开,关闭其他view */ 8 | SINGLE 9 | } -------------------------------------------------------------------------------- /lightMVC/core/Facade.ts: -------------------------------------------------------------------------------- 1 | import {ViewManager} from "./manager/ViewManager"; 2 | import BaseMediator from "./base/BaseMediator"; 3 | import BaseScene from "./base/BaseScene"; 4 | import {BaseView} from "./base/BaseView"; 5 | import {OPEN_VIEW_OPTION} from "./Constants"; 6 | import BaseCommand from "./base/BaseCommand"; 7 | import CommandManager from "./manager/CommandManager"; 8 | import ModelManager from "./manager/ModelManager"; 9 | import BaseModel from "./base/BaseModel"; 10 | import NotificationManager from "./manager/NotificationManager"; 11 | import FrameworkCfg from "./FrameworkCfg"; 12 | 13 | export class Facade { 14 | /** 实例对象 */ 15 | private static _instance: Facade = new Facade(); 16 | /** 框架是否被初始化 */ 17 | private static _isInit: boolean = false; 18 | 19 | private constructor () { 20 | 21 | } 22 | 23 | public static getInstance(): Facade { 24 | return this._instance; 25 | } 26 | 27 | /** 28 | * 初始化框架配置 29 | * @param {boolean} debug 是否是调试状态 30 | * @param {cc.Size} designResolution 设计分辨率 31 | * @param {boolean} fitHeight 是否高适配 32 | * @param {boolean} fitWidth 是否宽适配 33 | */ 34 | public init(debug: boolean, designResolution: cc.Size, fitHeight: boolean, fitWidth: boolean): void { 35 | if (Facade._isInit) { 36 | console.warn("框架已经初始化,不需要重复初始化。"); 37 | return; 38 | } 39 | Facade._isInit = true; 40 | FrameworkCfg.DEBUG = debug; 41 | FrameworkCfg.DESIGN_RESOLUTION = designResolution; 42 | FrameworkCfg.FIT_HEIGHT = fitHeight; 43 | FrameworkCfg.FIT_WIDTH = fitWidth; 44 | } 45 | 46 | /** 47 | * 运行场景 48 | * @param {{new(): BaseMediator}} mediator 场景mediator类型,类类型。 49 | * @param {{new(): BaseScene}} view 场景mediator类型,类类型。 50 | * @param {Object} data 自定义的任意类型透传数据。(可选) 51 | * @param {()=>void} cb 加载完成回调. 52 | */ 53 | public runScene(mediator: {new(): BaseMediator}, view: {new(): BaseScene}, data?: any, cb?: ()=>void): void { 54 | if (Facade._isInit) { 55 | ViewManager.getInstance().__runScene__(mediator, view, data, cb); 56 | } else { 57 | console.warn("框架没有初始化,请先调用init接口进行初始化。"); 58 | } 59 | } 60 | 61 | /** 62 | * 返回上一场景 63 | * @returns {boolean}是否存在上一个场景 64 | */ 65 | public backScene(): boolean { 66 | return ViewManager.getInstance().__backScene__(); 67 | } 68 | 69 | /** 70 | * 打开view界面,弹出界面 71 | * @param {{new(): BaseMediator}} mediator 界面mediator类型,类类型。 72 | * @param {{new(): BaseView}} view view 场景mediator类型,类类型。 73 | * @param {Object} data 自定义的任意类型透传数据。(可选) 74 | * @param {()=>void} cb 加载完成回调. 75 | */ 76 | public popView(mediator: {new(): BaseMediator}, view: {new(): BaseView}, data?: any, cb?: ()=>void): void { 77 | ViewManager.getInstance().__showView__(mediator, view, data, OPEN_VIEW_OPTION.OVERLAY, 0, cb); 78 | } 79 | 80 | /** 81 | * 创建view层,此接口用于初始不会被关闭和再次打开的常驻界面,所以它也不会受到pooView影响和管理。 82 | * @param {{new(): BaseMediator}} mediator 界面mediator类型,类类型。 83 | * @param {{new(): BaseView}} view view 场景mediator类型,类类型。 84 | * @param {number} zOrder ui层级 85 | * @param {Object} data 自定义的任意类型透传数据。(可选) 86 | * @param {()=>void} cb 加载完成回调. 87 | */ 88 | public addLayer(mediator: {new(): BaseMediator}, view: {new(): BaseView}, zOrder?: number, data?: any, cb?: ()=>void): void { 89 | ViewManager.getInstance().__showView__(mediator, view, data, OPEN_VIEW_OPTION.LAYER, zOrder, cb); 90 | } 91 | 92 | /** 93 | * 撤销命令 94 | * @param {{new (): BaseCommand}} command 命令对象 95 | * @param {Object} body 命令参数 96 | */ 97 | public __undoCommand__(command: {new (): BaseCommand}, body?: any): void { 98 | CommandManager.getInstance().__undoCommand__(command, body); 99 | } 100 | 101 | /** 102 | * 注册数据model 103 | * @param {{new (): BaseModel}} model 104 | */ 105 | public registerModel(model: {new (): BaseModel}): void { 106 | ModelManager.getInstance().registerModel(model); 107 | } 108 | 109 | /** 110 | * 获取model对象 111 | * @param {{new (): BaseModel}} model 112 | */ 113 | public getModel(model: {new (): T}): T { 114 | return ModelManager.getInstance().getModel(model); 115 | } 116 | } 117 | 118 | /** 导入到全局属性mvc中的对外接口和属性等api */ 119 | ((window)).mvc = { 120 | appFacade: Facade.getInstance(), 121 | }; 122 | 123 | -------------------------------------------------------------------------------- /lightMVC/core/FrameworkCfg.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 框架配置,通过init接口初始化这些配置。 3 | */ 4 | export default class FrameworkCfg { 5 | /** 是否是测试环境 */ 6 | public static DEBUG = false; 7 | /** 默认的设计分辨率 */ 8 | public static DESIGN_RESOLUTION: cc.Size = cc.size(640, 960); 9 | /** 是否高适配 */ 10 | public static FIT_HEIGHT: boolean = true; 11 | /** 是否宽适配 */ 12 | public static FIT_WIDTH: boolean = false; 13 | } -------------------------------------------------------------------------------- /lightMVC/core/base/BaseCommand.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 命令基类 3 | */ 4 | 5 | export default class BaseCommand { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /lightMVC/core/base/BaseMediator.ts: -------------------------------------------------------------------------------- 1 | import BaseCommand from "./BaseCommand"; 2 | import { BaseView } from "./BaseView"; 3 | import BaseModel from "./BaseModel"; 4 | import {Facade} from "../Facade"; 5 | import NotificationManager from "../manager/NotificationManager"; 6 | import CommandManager from "../manager/CommandManager"; 7 | import BaseScene from "./BaseScene"; 8 | 9 | /** 10 | * 视图中介者基类 11 | * @author Yue 12 | * @description 生命周期 13 | * BaseMediator.init 14 | * BaseView.__init__ 15 | * BaseMediator.viewDidAppear 16 | */ 17 | export default abstract class BaseMediator { 18 | /** 当前中介者持有的view视图 */ 19 | public view: BaseView; 20 | /** 当前中介者中注册的消息列表 */ 21 | private _notiMap: Mapvoid, target: any}>; 22 | 23 | /** 24 | * 初始化接口,此时视图还没有创建,如果想操作视图view请在viewDidAppear函数中进行。 25 | * @param {Object} data 自定义的任意类型透传数据。(可选) 26 | * @override 27 | * */ 28 | public abstract init(data?: any): void; 29 | 30 | /** 31 | * 内部初始化使用,外部不要调用。 32 | * @private 33 | */ 34 | private __init__(): void { 35 | this._notiMap = new Mapvoid, target: any}>(); 36 | } 37 | 38 | /** 39 | * 视图显示后会调用的接口 40 | * @override 41 | */ 42 | public abstract viewDidAppear(): void; 43 | 44 | /** 45 | * 绑定UI事件,接收view层派发的事件。 46 | * @param {string} name 事件名称 47 | * @param {(any)=>void} cb 事件回调 48 | * @param {BaseMediator} target 回调绑定对象 49 | */ 50 | public bindEvent(name: string, cb: (body: any)=>void, target: BaseMediator): void { 51 | this.view.__bindEvent__(name, cb, target); 52 | } 53 | 54 | /** 55 | * 注册消息监听 56 | * @param {string} noti 通知key值 57 | * @param {(data: any)=>void} cb 通知监听的回调函数 58 | * @param {Object} target 回调绑定的对象 59 | */ 60 | public registerNoti(noti: string, cb: (data: any)=>void, target: any): void { 61 | this._notiMap.set(noti, {key: noti, cb: cb, target: target}); 62 | } 63 | 64 | /** 65 | * 发送消息通知 66 | * @param {string} noti 通知key值 67 | * @param {Object} body 消息传递的参数 68 | */ 69 | public sendNoti(noti: string, body: any): void { 70 | NotificationManager.getInstance().__sendNotification__(noti, body); 71 | } 72 | 73 | /** 74 | * 发送命令接口 75 | * @param {{new (): BaseCommand}} cmd 命令类 76 | * @param {Object} data 命令参数 77 | */ 78 | public sendCmd(cmd: {new (): T}, data?: any): void { 79 | CommandManager.getInstance().__executeCommand__(cmd, data); 80 | } 81 | 82 | /** 83 | * 打开新场景 84 | * @param mediator 85 | * @param view 86 | * @param data {Object} data 自定义的任意类型透传数据。(可选) 87 | */ 88 | public runScene(mediator: {new(): BaseMediator}, view: {new(): BaseScene}, data?: any): void { 89 | Facade.getInstance().runScene(mediator, view, data); 90 | } 91 | 92 | /** 93 | * 返回上一场景 94 | * @returns {boolean}是否存在上一个场景 95 | */ 96 | public backScene(): boolean { 97 | return Facade.getInstance().backScene(); 98 | } 99 | 100 | /** 101 | * 打开view界面 102 | * @param {{new(): BaseMediator}} mediator 界面mediator类型,类类型。 103 | * @param {{new(): BaseView}} view view 场景mediator类型,类类型。 104 | * @param {Object} data 自定义的任意类型透传数据。(可选) 105 | */ 106 | public popView(mediator: {new(): BaseMediator}, view: {new(): BaseView}, data?: any): void { 107 | Facade.getInstance().popView(mediator, view, data); 108 | } 109 | 110 | /** 111 | * 添加层级 112 | * @param {{new(): BaseMediator}} mediator 界面mediator类型,类类型。 113 | * @param {{new(): BaseView}} view view 场景mediator类型,类类型。 114 | * @param {number} zOrder 层级。(可选) 115 | * @param {Object} data 自定义的任意类型透传数据。(可选) 116 | */ 117 | public addLayer(mediator: {new(): BaseMediator}, view: {new(): BaseView}, zOrder?: number, data?: any): void { 118 | Facade.getInstance().addLayer(mediator, view, zOrder, data); 119 | } 120 | 121 | /** 122 | * 获取model对象 123 | * @param {{new (): BaseModel}} model 124 | */ 125 | public getModel(model: {new (): T}): T { 126 | return Facade.getInstance().getModel(model); 127 | } 128 | 129 | /** 130 | * 销毁接口 131 | * @override 132 | */ 133 | public abstract destroy(): void; 134 | } 135 | -------------------------------------------------------------------------------- /lightMVC/core/base/BaseModel.ts: -------------------------------------------------------------------------------- 1 | import NotificationManager from "../manager/NotificationManager"; 2 | 3 | /** 4 | * 数据模型基类 5 | * init和clear接口需要子类重写 6 | */ 7 | export default abstract class BaseModel { 8 | 9 | /** 10 | * 数据对象初始化接口,创建时会调用。 11 | */ 12 | public abstract init(): void; 13 | 14 | /** 15 | * 发送消息接口 16 | * @param {string} noti 消息名称 17 | * @param {Object} data 消息数据 18 | */ 19 | public sendNoti(noti: string, data?: any): void { 20 | NotificationManager.getInstance().__sendNotification__(noti, data); 21 | } 22 | 23 | /** 24 | * 清理接口,子类可以实现清理逻辑。 25 | */ 26 | public abstract clear(): void; 27 | } 28 | -------------------------------------------------------------------------------- /lightMVC/core/base/BaseScene.ts: -------------------------------------------------------------------------------- 1 | import { BaseView } from "./BaseView"; 2 | 3 | const {ccclass, property} = cc._decorator; 4 | 5 | @ccclass 6 | export default class BaseScene extends BaseView { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /lightMVC/core/base/BaseView.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 视图基类 3 | */ 4 | import ViewEvent from "./ViewEvent"; 5 | import {ViewManager} from "../manager/ViewManager"; 6 | import UIUtils, { UIContainer } from "../../util/UIUtils"; 7 | 8 | const {ccclass, property} = cc._decorator; 9 | 10 | @ccclass 11 | export class BaseView extends cc.Component { 12 | 13 | /** 当前视图的事件对象 */ 14 | private __event__: ViewEvent; 15 | /** UI节点引用 */ 16 | public ui: UIContainer; 17 | 18 | public __init__(): void { 19 | this.__event__ = new ViewEvent(); 20 | this.ui = UIUtils.seekAllSubView(this.node); 21 | this.init(); 22 | } 23 | 24 | /** 25 | * view 创建时会被调用,子类可以重写. 26 | */ 27 | public init(): void { 28 | 29 | } 30 | 31 | /** 32 | * 发送UI事件 33 | * @param {string} event 事件名称 34 | * @param {Object} body 事件参数 35 | */ 36 | public sendEvent(event: string, body?: any): void { 37 | this.__event__.emit(event, body) 38 | } 39 | 40 | /** 41 | * 绑定UI事件 42 | * @param {string} name 事件名称 43 | * @param {(body: any)=>void} cb 事件回调 44 | * @param {BaseMediator} target 事件回调绑定对象 45 | * @private 私有函数,不得调用。 46 | */ 47 | public __bindEvent__(name: string, cb: (body: any)=>void, target): void { 48 | this.__event__.on(name, cb, target); 49 | } 50 | 51 | /** 52 | * 关闭当前的界面 53 | */ 54 | public closeView(): void { 55 | ViewManager.getInstance().__closeView__(this); 56 | this.__onClose__(); 57 | } 58 | 59 | /** 60 | * 关闭所有弹出的界面 61 | */ 62 | public closeAllPopView(): void { 63 | ViewManager.getInstance().__closeAllPopView__(); 64 | } 65 | 66 | private __onClose__(): void { 67 | this.__event__.destroy(); 68 | this.onClose(); 69 | this.node.destroy(); 70 | } 71 | 72 | /** 73 | * 当界面被关闭时会被调用,子类可以重写该方法。 74 | * @override 75 | */ 76 | public onClose(): void { 77 | 78 | } 79 | 80 | /** 81 | * 子类覆盖,返回UI的prefab路径 82 | * @return {string} 83 | */ 84 | public static path(): string { 85 | return ""; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /lightMVC/core/base/ViewEvent.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 视图事件类,负责处理view与mediator之间的事件监听和派发 3 | * @author Yue 4 | */ 5 | import BaseMediator from "./BaseMediator"; 6 | 7 | export default class ViewEvent{ 8 | 9 | /** 事件列表 */ 10 | private _eventList: __ViewEvent__[]; 11 | 12 | public constructor() { 13 | // 初始化事件列表 14 | this._eventList = []; 15 | } 16 | 17 | /** 18 | * 注册事件 19 | * @param {string} name 事件名称 20 | * @param {(any)=>void} cb 事件回调 21 | * @param {BaseMediator} target 绑定事件的对象 22 | */ 23 | public on(name: string, cb: (body: any)=>void, target: BaseMediator): void { 24 | let event = new __ViewEvent__(name, cb, target); 25 | for (let e of this._eventList) { 26 | if (e.equals(event)) { 27 | console.log("事件[" + name + "]已存在!"); 28 | return; 29 | } 30 | } 31 | this._eventList.push(event); 32 | } 33 | 34 | /** 35 | * 派发事件 36 | * @param {string} name 事件名称 37 | * @param {Object} body 事件参数,动态参数列表 38 | */ 39 | public emit(name: string, body?: any): void { 40 | for (let e of this._eventList) { 41 | if (e.name === name) { 42 | e.cb && e.cb.call(e.target, body); 43 | break; 44 | } 45 | } 46 | } 47 | 48 | /** 49 | * 移除指定事件 50 | * @param {string} name 事件名称 51 | * @return {boolean} 是否移除 52 | */ 53 | public remove(name: string): boolean { 54 | // 移除指定事件 55 | for (let i = 0; i < this._eventList.length; i++) { 56 | if (name === this._eventList[i].name) { 57 | this._eventList.splice(i, 1); 58 | return true; 59 | } 60 | } 61 | return false; 62 | } 63 | 64 | /** 65 | * 销毁接口 66 | */ 67 | public destroy(): void { 68 | 69 | } 70 | } 71 | 72 | /** 73 | * 事件对象结构,用于内部使用。 74 | * @private 75 | */ 76 | class __ViewEvent__ { 77 | /** 事件名称 */ 78 | public name: string; 79 | /** 事件回调 */ 80 | public cb: (...args)=>void; 81 | /** 绑定事件的对象 */ 82 | public target: BaseMediator; 83 | 84 | /*** 85 | * @param {string} name 事件名称 86 | * @param {(...args)=>void} cb 事件回调 87 | * @param {BaseMediator} target 绑定事件的对象 88 | */ 89 | public constructor(name: string, cb: (...args)=>void, target: BaseMediator) { 90 | this.name = name; 91 | this.cb = cb; 92 | this.target = target; 93 | } 94 | 95 | /** 96 | * 判断两个对象是否相等 97 | * @param {__ViewEvent__} event 目标事件对象 98 | * @return {boolean} 是否相等 99 | */ 100 | public equals(event: __ViewEvent__): boolean { 101 | return this.name === event.name; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /lightMVC/core/command/AsyncMacroCommand.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 异步组合宏命令基类 3 | * 该组合宏中的子命令是同步执行的,会按照添加的顺序同步执行。 4 | */ 5 | import MacroCommand from "./MacroCommand"; 6 | 7 | export default abstract class AsyncMacroCommand extends MacroCommand { 8 | 9 | /** 10 | * 异步执行组合命令 11 | */ 12 | private asyncExecute(): void { 13 | let cmdList = this["_commandList"]; 14 | for (let cmd of cmdList) { 15 | let tempCmd = new cmd.cmd(); 16 | tempCmd.execute(cmd.body); 17 | } 18 | } 19 | 20 | /** 21 | * 异步撤销组合命令 22 | */ 23 | private asyncUndo(): void { 24 | let cmdList = this["_commandList"]; 25 | for (let cmd of cmdList) { 26 | let tempCmd = new cmd.cmd(); 27 | tempCmd.undo(cmd.body); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /lightMVC/core/command/MacroCommand.ts: -------------------------------------------------------------------------------- 1 | import BaseCommand from "../base/BaseCommand"; 2 | import SimpleCommand from "./SimpleCommand"; 3 | 4 | /** 5 | * 组合类型命令基类 6 | */ 7 | export default abstract class MacroCommand extends BaseCommand { 8 | 9 | /** 组合命令列表 */ 10 | private _commandList: {cmd: {new (): SimpleCommand}, body: any}[] = []; 11 | 12 | /** 13 | * 初始化组合命令接口,子类必须实现,用于组合命令。 14 | * @example 15 | * this.addSubCommand(Test1Command); 16 | * this.addSubCommand(Test2Command); 17 | */ 18 | protected abstract initialize(): void; 19 | 20 | /** 21 | * 向组合宏中添加子命令 22 | * @param {{new (): SimpleCommand}} command 子命令 23 | * @param {Object} body 命令参数 24 | */ 25 | public addSubCommand(command: {new (): SimpleCommand}, body?: any): void { 26 | this._commandList.push({cmd: command, body: body}); 27 | } 28 | } -------------------------------------------------------------------------------- /lightMVC/core/command/SimpleCommand.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 命令基类 3 | */ 4 | import BaseCommand from "../base/BaseCommand"; 5 | import BaseModel from "../base/BaseModel"; 6 | import {Facade} from "../Facade"; 7 | import CommandManager from "../manager/CommandManager"; 8 | 9 | export default abstract class SimpleCommand extends BaseCommand { 10 | 11 | /** 12 | * 执行命令接口 13 | * @param {Object} body 命令参数 14 | */ 15 | public abstract execute(body?: any): void; 16 | 17 | /** 18 | * 撤销命令接口 19 | * @param {Object} body 命令参数 20 | */ 21 | public abstract undo(body?: any): void; 22 | 23 | /** 24 | * 获取model对象 25 | * @param {{new (): BaseModel}} model 26 | */ 27 | public getModel(model: {new (): T}): T { 28 | return Facade.getInstance().getModel(model); 29 | } 30 | 31 | /** 32 | * 执行命令 33 | * @param {{new (): BaseCommand}} command 命令对象 34 | * @param {Object} body 命令参数 35 | */ 36 | public sendCmd(command: {new (): BaseCommand}, body?: any): void { 37 | CommandManager.getInstance().__executeCommand__(command, body); 38 | } 39 | 40 | /** 41 | * 撤销命令 42 | * @param {{new (): BaseCommand}} command 命令对象 43 | * @param {Object} body 命令参数 44 | */ 45 | public undoCmd(command: {new (): BaseCommand}, body?: any): void { 46 | Facade.getInstance().__undoCommand__(command, body); 47 | } 48 | 49 | public sendNoti(): void { 50 | 51 | } 52 | } -------------------------------------------------------------------------------- /lightMVC/core/command/SyncMacroCommand.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 同步组合宏命令基类 3 | * 该组合宏中的子命令是同步执行的,会按照添加的顺序同步执行。 4 | */ 5 | import MacroCommand from "./MacroCommand"; 6 | 7 | export default abstract class SyncMacroCommand extends MacroCommand { 8 | 9 | 10 | } -------------------------------------------------------------------------------- /lightMVC/core/manager/CommandManager.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 命令控制类 3 | * @author ituuz 4 | * @description 负责控制和维护命令。 5 | */ 6 | import BaseCommand from "../base/BaseCommand"; 7 | import SimpleCommand from "../command/SimpleCommand"; 8 | import SyncMacroCommand from "../command/SyncMacroCommand"; 9 | import AsyncMacroCommand from "../command/AsyncMacroCommand"; 10 | 11 | export default class CommandManager { 12 | 13 | // 实例 14 | private static _instance: CommandManager = new CommandManager(); 15 | 16 | /** 17 | * @constructor 18 | * @private 19 | */ 20 | private constructor () { 21 | 22 | } 23 | 24 | /** 25 | * 单例获取类 26 | */ 27 | public static getInstance(): CommandManager { 28 | return this._instance; 29 | } 30 | 31 | /** 32 | * 执行命令 33 | * @param {{new (): BaseCommand}} command 命令对象 34 | * @param {Object} body 命令参数 35 | */ 36 | public __executeCommand__(command: {new (): BaseCommand}, body?: any): void { 37 | if (cc.js.isChildClassOf(command, SimpleCommand)) { 38 | let cmd: SimpleCommand = new command() as SimpleCommand; 39 | cmd.execute(body); 40 | } else if (cc.js.isChildClassOf(command, SyncMacroCommand)) { 41 | // TODO: 同步按顺序执行的命令组合宏 42 | } else if (cc.js.isChildClassOf(command, AsyncMacroCommand)) { 43 | let cmd: AsyncMacroCommand = new command() as AsyncMacroCommand; 44 | // 初始化宏 45 | cmd["initialize"](); 46 | // 执行 47 | cmd["asyncExecute"](); 48 | } else { 49 | console.log(command.prototype + " 不是可执行的命令!"); 50 | } 51 | } 52 | 53 | /** 54 | * 撤销命令 55 | * @param {{new (): BaseCommand}} command 命令对象 56 | * @param {Object} body 命令参数 57 | */ 58 | public __undoCommand__(command: {new (): BaseCommand}, body?: any): void { 59 | if (cc.js.isChildClassOf(command, SimpleCommand)) { 60 | let cmd: SimpleCommand = new command() as SimpleCommand; 61 | cmd.undo(body); 62 | } else if (cc.js.isChildClassOf(command, SyncMacroCommand)) { 63 | // TODO: 同步按顺序撤销的命令组合宏 64 | } else if (cc.js.isChildClassOf(command, AsyncMacroCommand)) { 65 | let cmd: AsyncMacroCommand = new command() as AsyncMacroCommand; 66 | // 初始化宏 67 | cmd["initialize"](); 68 | // 执行 69 | cmd["asyncUndo"](); 70 | } else { 71 | console.log(command.prototype + " 不是可执行的命令!"); 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /lightMVC/core/manager/ModelManager.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 数据模型管理类 3 | */ 4 | import BaseModel from "../base/BaseModel"; 5 | 6 | export default class ModelManager { 7 | /** 实例 */ 8 | private static _instance: ModelManager = new ModelManager(); 9 | 10 | /** 数据model集合 */ 11 | private _modelList: {}; 12 | 13 | /** 14 | * @constructor 15 | * @private 16 | */ 17 | private constructor () { 18 | this._modelList = {}; 19 | } 20 | 21 | /** 22 | * 单例获取类 23 | */ 24 | public static getInstance(): ModelManager { 25 | return this._instance; 26 | } 27 | 28 | /** 29 | * 获取model对象 30 | * @param {{new (): BaseModel}} model 31 | */ 32 | public getModel(model: {new (): T}): T { 33 | let key = cc.js.getClassName(model); 34 | return this._modelList[key]; 35 | } 36 | 37 | /** 38 | * 注册数据model 39 | * @param {{new (): BaseModel}} model 40 | */ 41 | public registerModel(model: {new (): BaseModel}): void { 42 | let key = cc.js.getClassName(model); 43 | if (this._modelList.hasOwnProperty(key)) { 44 | console.log(key, "已经存在,不可重复注册!"); 45 | } else { 46 | let m = new model(); 47 | m.init(); 48 | this._modelList[key] = m; 49 | } 50 | } 51 | 52 | /** 53 | * 移除注册 54 | * @param {{new (): BaseModel}} model 55 | */ 56 | public unregisterModel(model: {new (): BaseModel}): void { 57 | let key = cc.js.getClassName(model); 58 | if (this._modelList.hasOwnProperty(key)) { 59 | let m: BaseModel = this._modelList[key]; 60 | m.clear(); 61 | delete this._modelList[key]; 62 | } else { 63 | console.warn(key, "不存在!"); 64 | } 65 | } 66 | 67 | /** 68 | * 释放并移除所有model 69 | */ 70 | public removeAllModel(): void { 71 | // 释放并移除所有model 72 | for (let key in this._modelList) { 73 | let model: BaseModel = this._modelList[key]; 74 | model.clear(); 75 | delete this._modelList[key]; 76 | } 77 | this._modelList = {}; 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /lightMVC/core/manager/NotificationManager.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 消息管理类 3 | */ 4 | import {ViewManager} from "./ViewManager"; 5 | import BaseMediator from "../base/BaseMediator"; 6 | 7 | export default class NotificationManager { 8 | 9 | // 实例 10 | private static _instance: NotificationManager = new NotificationManager(); 11 | 12 | /** 13 | * @constructor 14 | * @private 15 | */ 16 | private constructor () { 17 | 18 | } 19 | 20 | /** 21 | * 单例获取类 22 | */ 23 | public static getInstance(): NotificationManager { 24 | return this._instance; 25 | } 26 | 27 | /** 28 | * 发送消息通知, 框架使用,外部不得调用。 29 | * @param {string} noti 通知key值 30 | * @param {Object} body 消息传递的参数 31 | * @private 32 | */ 33 | public __sendNotification__(noti: string, body?: any): void { 34 | // pop view 35 | let popViewList: BaseMediator[] = ViewManager.getInstance().popViewList; 36 | this.loopMap(popViewList, noti, body); 37 | // add layer view 38 | let layerViewList: BaseMediator[] = ViewManager.getInstance().layerViewList; 39 | this.loopMap(layerViewList, noti, body); 40 | // scene 41 | let curScene: BaseMediator = ViewManager.getInstance().curScene; 42 | this.loopMap([curScene], noti, body); 43 | } 44 | 45 | /** 46 | * 循环遍历map检索通知对象 47 | * @param {BaseMediator[]} list view mediator list 48 | * @param {string} noti 消息名称 49 | * @param {Object} body 消息传递的参数 50 | */ 51 | private loopMap(list: BaseMediator[], noti: string, body?: any): void { 52 | for (let med of list) { 53 | let notiMap: Mapvoid, target: any}> = med["_notiMap"]; 54 | notiMap.forEach((value: {key: string, cb: (data: any)=>void, target: any}, key: string)=>{ 55 | if (key === noti) { 56 | value.cb.call(value.target, body); 57 | } 58 | }, this); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /lightMVC/core/manager/ViewManager.ts: -------------------------------------------------------------------------------- 1 | import {BaseView} from "../base/BaseView"; 2 | import BaseMediator from "../base/BaseMediator"; 3 | import BaseScene from "../base/BaseScene"; 4 | import {OPEN_VIEW_OPTION} from "../Constants"; 5 | import Canvas = cc.Canvas; 6 | import FrameworkCfg from "../FrameworkCfg"; 7 | 8 | /** 9 | * mvc框架控制类 10 | * @author ituuz 11 | * @description 负责控制和维护框架各个节点和结构间的跳转和关联。 12 | */ 13 | export class ViewManager { 14 | 15 | // 实例 16 | private static _instance: ViewManager = new ViewManager(); 17 | 18 | /** 上一场景类 */ 19 | private _preSceneMediatorCls: {new(): BaseMediator} = null; 20 | private _preSceneViewCls: {new(): BaseScene} = null; 21 | /** 当前场景类 */ 22 | private _curSceneMediatorCls: {new(): BaseMediator} = null; 23 | private _curSceneViewCls: {new(): BaseScene} = null; 24 | 25 | /** 当前场景 */ 26 | private _curScene: BaseMediator; 27 | /** 当前显示的pop view列表 */ 28 | private _popViewList: BaseMediator[]; 29 | /** 当前显示的layer view列表 */ 30 | private _layerViewList: BaseMediator[]; 31 | 32 | /** 33 | * @constructor 34 | * @private 35 | */ 36 | private constructor () { 37 | this._popViewList = []; 38 | this._layerViewList = []; 39 | } 40 | 41 | /** 42 | * 单例获取类 43 | */ 44 | public static getInstance(): ViewManager { 45 | return this._instance; 46 | } 47 | 48 | /** 49 | * 运行场景 50 | * @param {{new(): BaseMediator}} mediator 场景mediator类型,类类型。 51 | * @param {{new(): BaseScene}} view 场景mediator类型,类类型。 52 | * @param {Object} data 自定义的任意类型透传数据。(可选) 53 | * @param {()=>void} cb 加载完成回调. 54 | * @private 55 | */ 56 | public __runScene__(mediator: {new(): BaseMediator}, view: {new(): BaseScene}, data?: any, cb?: ()=>void): void { 57 | // 创建并绑定场景 58 | let sceneMediator: BaseMediator = new mediator(); 59 | sceneMediator["__init__"](); 60 | sceneMediator.init(data); 61 | 62 | // 如果前一场景不为空则进行清理 63 | if (this._curScene) { 64 | this._curScene.destroy(); 65 | } 66 | 67 | // 保存当前场景 68 | this._curScene = sceneMediator; 69 | 70 | if (this._curSceneMediatorCls != null && this._curSceneViewCls != null) { 71 | this._preSceneMediatorCls = this._curSceneMediatorCls; 72 | this._preSceneViewCls = this._curSceneViewCls; 73 | } 74 | 75 | this._curSceneMediatorCls = mediator; 76 | this._curSceneViewCls = view; 77 | 78 | // 处理场景显示逻辑 79 | let scenePath: string = ((view)).path(); 80 | if (scenePath === "") { 81 | let ccs = new cc.Scene(); 82 | ccs.name = "Scene"; 83 | let canvasNode = new cc.Node(); 84 | canvasNode.name = "Canvas"; 85 | let canvas = canvasNode.addComponent(cc.Canvas); 86 | canvas.designResolution = FrameworkCfg.DESIGN_RESOLUTION; 87 | canvas.fitHeight = FrameworkCfg.FIT_HEIGHT; 88 | canvas.fitWidth = FrameworkCfg.FIT_WIDTH; 89 | sceneMediator.view = canvasNode.addComponent(view); 90 | sceneMediator.view.__init__(); 91 | ccs.addChild(canvasNode); 92 | // 这里延迟1ms是为了让场景在下一帧加载 93 | setTimeout(() => { 94 | this.__closeAllView__(); 95 | cc.director.runSceneImmediate(ccs); 96 | sceneMediator.viewDidAppear(); 97 | cb && cb(); 98 | }, 1); 99 | } else { 100 | cc.director.loadScene(scenePath, ()=>{ 101 | this.__closeAllView__(); 102 | let canvas = cc.director.getScene().getChildByName('Canvas'); 103 | if (canvas) { 104 | sceneMediator.view = canvas.addComponent(view); 105 | sceneMediator.view.__init__(); 106 | sceneMediator.viewDidAppear(); 107 | cb && cb(); 108 | } else { 109 | console.log("场景中必须包含默认的Canvas节点!"); 110 | } 111 | }); 112 | } 113 | } 114 | 115 | /** 116 | * 返回上一场景 117 | * @returns {boolean}是否存在上一个场景 118 | */ 119 | public __backScene__(): boolean { 120 | if (this._preSceneMediatorCls && this._preSceneViewCls) { 121 | this.__runScene__(this._preSceneMediatorCls, this._preSceneViewCls); 122 | return true; 123 | } 124 | return false; 125 | } 126 | 127 | /** 128 | * 打开view界面 129 | * @param {{new(): BaseMediator}} mediator 界面mediator类型,类类型。 130 | * @param {{new(): BaseView}} view view 场景mediator类型,类类型。 131 | * @param {Object} data 自定义的任意类型透传数据。(可选) 132 | * @param {OPEN_VIEW_OPTION} option 打开ui的操作选项,枚举类型。 133 | * @param {number} zOrder 层级。 134 | * @param {()=>void} cb 加载完成回调. 135 | */ 136 | public __showView__(mediator: {new(): BaseMediator}, view: {new(): BaseView}, 137 | data?: any, option?: OPEN_VIEW_OPTION, zOrder?: number, cb?: ()=>void): void { 138 | // 处理打开UI的其他操作 139 | this.openViewOptionHandler(option); 140 | 141 | // 创建并绑定view 142 | let viewMediator: BaseMediator = new mediator(); 143 | viewMediator["__init__"](); 144 | 145 | // 处理场景显示逻辑 146 | let viewPath: string = ((view)).path(); 147 | if (viewPath === "") { 148 | let viewNode = new cc.Node(); 149 | this.initViewMediator(viewMediator, viewNode, view, option); 150 | viewMediator.init(data); 151 | viewMediator.viewDidAppear(); 152 | cb && cb(); 153 | } else { 154 | cc.loader.loadRes(viewPath, cc.Prefab, (err, prefab)=>{ 155 | if (err) { 156 | console.error(err); 157 | return; 158 | } 159 | let viewNode = cc.instantiate(prefab); 160 | this.initViewMediator(viewMediator, viewNode, view, option); 161 | viewMediator.init(data); 162 | viewMediator.viewDidAppear(); 163 | cb && cb(); 164 | }); 165 | } 166 | } 167 | 168 | /** 169 | * 初始化ViewMediator 170 | * @param {BaseMediator} mediator ViewMediator 171 | * @param {cc.Node} viewNode view显示节点 172 | * @param {{new(): BaseView}} view view显示组件类 173 | * @param {OPEN_VIEW_OPTION} option 打开选项 174 | * @param {number} zOrder 层级排序 175 | */ 176 | private initViewMediator(mediator: BaseMediator, viewNode: cc.Node, view: {new(): BaseView}, 177 | option?: OPEN_VIEW_OPTION, zOrder?: number): void { 178 | viewNode.zIndex = zOrder; 179 | mediator.view = viewNode.addComponent(view); 180 | cc.director.getScene().getChildByName('Canvas').addChild(viewNode); 181 | mediator.view.__init__(); 182 | // 根据不同打开类型,存储到不同队列中。 183 | if (option === OPEN_VIEW_OPTION.OVERLAY || option === OPEN_VIEW_OPTION.SINGLE) { 184 | this._popViewList.push(mediator); 185 | } else if (option === OPEN_VIEW_OPTION.LAYER) { 186 | this._layerViewList.push(mediator); 187 | } 188 | } 189 | 190 | /** 191 | * 关闭指定View 192 | * @param view 193 | * @private 194 | */ 195 | public __closeView__(view: BaseView): void { 196 | for (let i = 0; i < this._popViewList.length; i++) { 197 | if (this._popViewList[i].view === view) { 198 | this._popViewList.splice(i, 1); 199 | return; 200 | } 201 | } 202 | for (let i = 0; i < this._layerViewList.length; i++) { 203 | if (this._layerViewList[i].view === view) { 204 | this._layerViewList.splice(i, 1); 205 | return; 206 | } 207 | } 208 | } 209 | 210 | /** 211 | * 关闭所有弹出窗口 212 | * @private 213 | */ 214 | public __closeAllPopView__(): void { 215 | for (let i = 0; i < this._popViewList.length; i++) { 216 | this._popViewList[i].view["__onClose__"](); 217 | } 218 | this._popViewList = []; 219 | } 220 | 221 | /** 222 | * 关闭所有添加层级 223 | * @private 224 | */ 225 | public __closeAllAddLayer__(): void { 226 | for (let i = 0; i < this._layerViewList.length; i++) { 227 | this._layerViewList[i].view["__onClose__"](); 228 | } 229 | this._layerViewList = []; 230 | } 231 | 232 | /** 233 | * 关闭所有view 234 | * @private 235 | */ 236 | public __closeAllView__(): void { 237 | this.__closeAllPopView__(); 238 | this.__closeAllAddLayer__(); 239 | } 240 | 241 | /** 242 | * 根据参数处理ui的打开方式 243 | * @param option 244 | * @private 245 | */ 246 | private openViewOptionHandler(option: OPEN_VIEW_OPTION): void { 247 | // 设置默认值 248 | if (!option) { 249 | option = OPEN_VIEW_OPTION.OVERLAY; 250 | } 251 | // 根据不同操作做不同处理 252 | if (option === OPEN_VIEW_OPTION.SINGLE) { 253 | // TODO:暂时不提供这种关闭其他view的打开方式,可以通过BaseView.closeAllPopView()来实现。 254 | } 255 | } 256 | 257 | /**************************** getter and setter ******************************/ 258 | get popViewList(): BaseMediator[] { 259 | return this._popViewList; 260 | } 261 | get layerViewList(): BaseMediator[] { 262 | return this._layerViewList; 263 | } 264 | get curScene(): BaseMediator { 265 | return this._curScene; 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /lightMVC/util/UIUtils.ts: -------------------------------------------------------------------------------- 1 | 2 | export default class UIUtils { 3 | 4 | /*** 5 | * 生成子节点的唯一标识快捷访问 6 | * @param node 7 | * @param map 8 | */ 9 | public static createSubNodeMap(node: cc.Node, map: Map) { 10 | let children = node.children; 11 | if (!children) { 12 | return; 13 | } 14 | for (let t = 0, len = children.length; t < len; ++t) { 15 | let subChild = children[t]; 16 | map.set(subChild.name, subChild); 17 | UIUtils.createSubNodeMap(subChild, map); 18 | } 19 | } 20 | 21 | /*** 22 | * 返回当前节点所有节点,一唯一标识存在 23 | * @param node 父节点 24 | * @return {Object} 所有子节点的映射map 25 | */ 26 | public static seekAllSubView(node: cc.Node): UIContainer { 27 | let map = new Map(); 28 | UIUtils.createSubNodeMap(node, map); 29 | return new UIContainer(map); 30 | } 31 | } 32 | 33 | export class UIContainer { 34 | /** 所有节点集合 */ 35 | private _uiNodesMap: Map; 36 | 37 | public constructor(nodesMap: Map) { 38 | this._uiNodesMap = nodesMap; 39 | } 40 | 41 | /** 42 | * 根据节点名字获取节点 43 | * @param {string}name 节点名字 44 | * @return {cc.Node} 45 | */ 46 | public getNode(name: string): cc.Node { 47 | return this._uiNodesMap.get(name); 48 | } 49 | 50 | /** 51 | * 根据节点名字和组件类型获取组件对象 52 | * @param {string}name 节点名字 53 | * @param {{prototype: cc.Component}}com 组建类型 54 | * @return {cc.Component} 55 | */ 56 | public getComponent(name: string, com: { prototype: T }): T { 57 | let node = this._uiNodesMap.get(name); 58 | if (node) { 59 | return node.getComponent(com); 60 | } 61 | return null; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /lightMVC_ex/README.md: -------------------------------------------------------------------------------- 1 | lightMVC_ex 2 | --- 3 | To be continue... 4 | -------------------------------------------------------------------------------- /light_mvc.d.ts: -------------------------------------------------------------------------------- 1 | 2 | // declare class mvc { 3 | // public static facade: Facade; 4 | // } 5 | 6 | // declare class Facade { 7 | // public popView(): void; 8 | // public runScene(mediator: {new(): BaseMediator}, view: {new(): BaseScene}, data:any = null): void; 9 | // } 10 | // 11 | // declare class BaseMediator { 12 | // 13 | // } 14 | // 15 | // declare class BaseScene { 16 | // 17 | // } -------------------------------------------------------------------------------- /mvc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yue19870813/lightMVC/832b4f652e701b0398abe81b312e2373a2de24e4/mvc.png --------------------------------------------------------------------------------