import _ from 'lodash' import {invokeMethod} from "./utils" import {windows} from './Defaults' export class Scope { /** * 业务模块的唯一编号 */ id = _.uniqueId('scope_') originalVjson /** * 一个 ExtJS 能接受的配置对象 */ vjson /** * 原始 vjsonModel 对象 */ model /** * 双向绑定的模型对象 */ viewModel /** * 构建完成之后的 Ext控件句柄 */ _handle /** * 与 watch 装饰器配合使用. * viewModel 属性更改时触发成员方法 */ _watchList /** * 页面显示的时候带的参数 在设计刷新的时候使用 */ _vjsonOption _dataOption _addWatch(tplExpress, fn) { if (!this._watchList) { this._watchList = [] } this._watchList.push({watch: tplExpress, fn}) } _applyWatchList() { _.forEach(this._watchList, item => { this.viewModel.bind(item.watch, item.fn.bind(this)) }) } /** * 产生一个当前模块有效的唯一id * @param key 唯一编号 */ uid(key) { return this.id + key } /** * 以对话框模式打开当前模块 * @param sender 发送者(按钮或Scope对象) * @param vjsonOption 界面覆盖选项(可以为空) * @param dataOption 数据覆盖选项(可以为空) */ showDialog(sender, vjsonOption, dataOption) { const that = this const vmodel = _.defaultsDeep({ data: {} }, dataOption, that.model) this.viewModel = new Ext.app.ViewModel(vmodel); this.viewModel.yvanScope = this this._applyWatchList() const config = _.defaultsDeep({ animateTarget: sender, viewModel: this.viewModel, listeners: { show(sender) { // 记录句柄 if (sender && !that._handle) { that._handle = sender } // 调用onLoad回调 try { that.onLoad() } catch (e) { console.error('errorAt onLoad', e) } // 如果vjson中配置了 afterrender ,需要恢复状态 invokeMethod(that.vjson.listeners?.show, that, arguments) } }, }, vjsonOption, that.vjson, windows) if (config.height === 'unset') { delete config.height } if (config.width === 'unset') { delete config.width } const holder = sender?.lookupReferenceHolder() // delete config.constrain const win = new Ext.Window(config); if (holder) { holder.add(win) } win.show(); } /** * 以标签模式打开当前模块 * @param vjsonOption 界面覆盖选项(可以为空) * @param dataOption 数据覆盖选项(可以为空) */ showPage(vjsonOption, dataOption, reload = false) { const that = this this._vjsonOption = vjsonOption; this._dataOption = dataOption; const vmodel = _.defaultsDeep({ data: {} }, dataOption, that.model) this.viewModel = new Ext.app.ViewModel(vmodel); this.viewModel.yvanScope = this this._applyWatchList() const config = _.defaultsDeep({ viewModel: this.viewModel, listeners: { added(sender) { // 记录句柄 if (sender && !that._handle) { that._handle = sender } // 调用onLoad回调 try { that.onLoad() } catch (e) { console.error('errorAt onLoad', e) } // 如果vjson中配置了 afterrender ,需要恢复状态 invokeMethod(that.vjson.listeners?.added, that, arguments) } }, }, vjsonOption, that.vjson) const tt = Ext.getCmp('TT') tt.addScope(this, config, reload) return config } /** * 直接渲染到元素 * @param element 渲染目标 * @param vjsonOption 界面覆盖选项(可以为空) * @param dataOption 数据覆盖选项(可以为空) */ renderTo(element, vjsonOption, dataOption) { const that = this this._vjsonOption = vjsonOption; this._dataOption = dataOption; const vmodel = _.defaultsDeep({ data: {} }, dataOption, that.model) this.viewModel = new Ext.app.ViewModel(vmodel); this.viewModel.yvanScope = this this._applyWatchList() const config = _.defaultsDeep({ viewModel: this.viewModel, renderTo: element, listeners: { afterrender(sender) { // 记录句柄 if (sender && !that._handle) { that._handle = sender } // 调用onLoad回调 try { that.onLoad() } catch (e) { console.error('errorAt onLoad', e) } // 如果vjson中配置了 afterrender ,需要恢复状态 invokeMethod(that.vjson.listeners?.afterrender, that, arguments) } }, }, vjsonOption, that.vjson) new Ext.container.Viewport(config); } reRender() { this.vjson = this.buildVjson() this.showPage(this._vjsonOption, this._dataOption, true) } /** * 关闭对话框(或标签页) */ close() { this._handle.close() } /** * 获取 viewModel 里包含的数据(只读) */ get data() { return this.viewModel.getData() } /** * 设置 viewModel 中的数据 * 可以是 key, value 模式 * 也可以是 {key:value} 模式 */ set(path, value) { return this.viewModel.set(path, value) } /** * 寻找模块内所有的 xtype 对应的对象 * @param xtypeKey */ down(xtypeKey) { return this._handle.down(xtypeKey) } /** * 获取所有设置过 Reference 名称的组件 */ get refs() { return this._handle.getReferences() } constructor({model, vjson}) { const that = this this.model = model this.originalVjson = _.cloneDeep(vjson) this.vjson = this.buildVjson() } buildVjson() { const that = this return _.defaultsDeep({ closable: true, listeners: { afterrender(sender) { // 记录句柄 if (sender && !that._handle) { that._handle = sender } // 调用 onRender 回调 that.onRender() // 如果vjson中配置了 afterrender ,需要恢复状态 invokeMethod(that.originalVjson.listeners?.afterrender, that, arguments) }, activate(sender) { // 调用 onActivate 回调 that.onActivate() const vm = sender.lookupViewModel() if (vm) { window['currentScope'] = sender.lookupViewModel().yvanScope } // 如果vjson中配置了 afterrender ,需要恢复状态 invokeMethod(that.originalVjson.listeners?.activate, that, arguments) }, deactivate(sender) { // 调用 onActivate 回调 that.onDeactivate() // 如果vjson中配置了 afterrender ,需要恢复状态 invokeMethod(that.originalVjson.listeners?.deactivate, that, arguments) }, beforedestroy(sender) { // 调用 onBeforeDestroy 回调 that.onBeforeDestroy() // 如果vjson中配置了 afterrender ,需要恢复状态 invokeMethod(that.originalVjson.listeners?.beforedestroy, that, arguments) }, beforeclose(sender) { // 调用 onActivate 回调 that.onBeforeClose() // 如果vjson中配置了 afterrender ,需要恢复状态 invokeMethod(that.originalVjson.listeners?.beforeclose, that, arguments) }, destroy(sender) { // 调用 onActivate 回调 that.onDestroy() // 销毁 viewModel // that.viewModel.destroy() // 如果vjson中配置了 afterrender ,需要恢复状态 invokeMethod(that.originalVjson.listeners?.destroy, that, arguments) // delete that.viewModel // delete that.vjson // delete that.originalVjson delete that._watchList delete that._handle }, }, yvanScope: this, referenceHolder: true, }, this.originalVjson) } /** * 模块载入完成之后的回调 */ onLoad() { } /** * 渲染完成之后的回调 */ onRender() { } /** * 被激活时的回调(可能执行多次) */ onActivate() { } /** * 进入未激活状态之前回调的函数 */ onDeactivate() { } /** * 关闭之前的回调(只有 tab 选项卡有这个选项) */ onBeforeClose() { } /** * 组件被卸载之前的回调 */ onBeforeDestroy() { } /** * 组件卸载之后的回调 */ onDestroy() { } } /** * 观察装饰器,viewModel 属性更改时触发成员方法 * @param tplExpress tpl表达式,例如 "{form.f1}" */ export function watch(tplExpress, deep = false) { return function (target, propertyKey, pd) { target._addWatch({bindTo: tplExpress, deep}, target[propertyKey]) return target[propertyKey] } }