Scope.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. import _ from 'lodash'
  2. import {invokeMethod} from "./utils"
  3. import {windows} from './Defaults'
  4. export class Scope {
  5. /**
  6. * 业务模块的唯一编号
  7. */
  8. id = _.uniqueId('scope_')
  9. originalVjson
  10. /**
  11. * 一个 ExtJS 能接受的配置对象
  12. */
  13. vjson
  14. /**
  15. * 原始 vjsonModel 对象
  16. */
  17. model
  18. /**
  19. * 双向绑定的模型对象
  20. */
  21. viewModel
  22. /**
  23. * 构建完成之后的 Ext控件句柄
  24. */
  25. _handle
  26. /**
  27. * 与 watch 装饰器配合使用.
  28. * viewModel 属性更改时触发成员方法
  29. */
  30. _watchList
  31. /**
  32. * 页面显示的时候带的参数 在设计刷新的时候使用
  33. */
  34. _vjsonOption
  35. _dataOption
  36. _addWatch(tplExpress, fn) {
  37. if (!this._watchList) {
  38. this._watchList = []
  39. }
  40. this._watchList.push({watch: tplExpress, fn})
  41. }
  42. _applyWatchList() {
  43. _.forEach(this._watchList, item => {
  44. this.viewModel.bind(item.watch, item.fn.bind(this))
  45. })
  46. }
  47. /**
  48. * 产生一个当前模块有效的唯一id
  49. * @param key 唯一编号
  50. */
  51. uid(key) {
  52. return this.id + key
  53. }
  54. /**
  55. * 以对话框模式打开当前模块
  56. * @param sender 发送者(按钮或Scope对象)
  57. * @param vjsonOption 界面覆盖选项(可以为空)
  58. * @param dataOption 数据覆盖选项(可以为空)
  59. */
  60. showDialog(sender, vjsonOption, dataOption) {
  61. const that = this
  62. const vmodel = _.defaultsDeep({
  63. data: {}
  64. }, dataOption, that.model)
  65. this.viewModel = new Ext.app.ViewModel(vmodel);
  66. this.viewModel.yvanScope = this
  67. this._applyWatchList()
  68. const config = _.defaultsDeep({
  69. animateTarget: sender,
  70. viewModel: this.viewModel,
  71. listeners: {
  72. show(sender) {
  73. // 记录句柄
  74. if (sender && !that._handle) {
  75. that._handle = sender
  76. }
  77. // 调用onLoad回调
  78. try {
  79. that.onLoad()
  80. } catch (e) {
  81. console.error('errorAt onLoad', e)
  82. }
  83. // 如果vjson中配置了 afterrender ,需要恢复状态
  84. invokeMethod(that.vjson.listeners?.show, that, arguments)
  85. }
  86. },
  87. }, vjsonOption, that.vjson, windows)
  88. if (config.height === 'unset') {
  89. delete config.height
  90. }
  91. if (config.width === 'unset') {
  92. delete config.width
  93. }
  94. const holder = sender?.lookupReferenceHolder()
  95. // delete config.constrain
  96. const win = new Ext.Window(config);
  97. if (holder) {
  98. holder.add(win)
  99. }
  100. win.show();
  101. }
  102. /**
  103. * 以标签模式打开当前模块
  104. * @param vjsonOption 界面覆盖选项(可以为空)
  105. * @param dataOption 数据覆盖选项(可以为空)
  106. */
  107. showPage(vjsonOption, dataOption, reload = false) {
  108. const that = this
  109. this._vjsonOption = vjsonOption;
  110. this._dataOption = dataOption;
  111. const vmodel = _.defaultsDeep({
  112. data: {}
  113. }, dataOption, that.model)
  114. this.viewModel = new Ext.app.ViewModel(vmodel);
  115. this.viewModel.yvanScope = this
  116. this._applyWatchList()
  117. const config = _.defaultsDeep({
  118. viewModel: this.viewModel,
  119. listeners: {
  120. added(sender) {
  121. // 记录句柄
  122. if (sender && !that._handle) {
  123. that._handle = sender
  124. }
  125. // 调用onLoad回调
  126. try {
  127. that.onLoad()
  128. } catch (e) {
  129. console.error('errorAt onLoad', e)
  130. }
  131. // 如果vjson中配置了 afterrender ,需要恢复状态
  132. invokeMethod(that.vjson.listeners?.added, that, arguments)
  133. }
  134. },
  135. }, vjsonOption, that.vjson)
  136. const tt = Ext.getCmp('TT')
  137. tt.addScope(this, config, reload)
  138. return config
  139. }
  140. /**
  141. * 直接渲染到元素
  142. * @param element 渲染目标
  143. * @param vjsonOption 界面覆盖选项(可以为空)
  144. * @param dataOption 数据覆盖选项(可以为空)
  145. */
  146. renderTo(element, vjsonOption, dataOption) {
  147. const that = this
  148. this._vjsonOption = vjsonOption;
  149. this._dataOption = dataOption;
  150. const vmodel = _.defaultsDeep({
  151. data: {}
  152. }, dataOption, that.model)
  153. this.viewModel = new Ext.app.ViewModel(vmodel);
  154. this.viewModel.yvanScope = this
  155. this._applyWatchList()
  156. const config = _.defaultsDeep({
  157. viewModel: this.viewModel,
  158. renderTo: element,
  159. listeners: {
  160. afterrender(sender) {
  161. // 记录句柄
  162. if (sender && !that._handle) {
  163. that._handle = sender
  164. }
  165. // 调用onLoad回调
  166. try {
  167. that.onLoad()
  168. } catch (e) {
  169. console.error('errorAt onLoad', e)
  170. }
  171. // 如果vjson中配置了 afterrender ,需要恢复状态
  172. invokeMethod(that.vjson.listeners?.afterrender, that, arguments)
  173. }
  174. },
  175. }, vjsonOption, that.vjson)
  176. new Ext.container.Viewport(config);
  177. }
  178. reRender() {
  179. this.vjson = this.buildVjson()
  180. this.showPage(this._vjsonOption, this._dataOption, true)
  181. }
  182. /**
  183. * 关闭对话框(或标签页)
  184. */
  185. close() {
  186. this._handle.close()
  187. }
  188. /**
  189. * 获取 viewModel 里包含的数据(只读)
  190. */
  191. get data() {
  192. return this.viewModel.getData()
  193. }
  194. /**
  195. * 设置 viewModel 中的数据
  196. * 可以是 key, value 模式
  197. * 也可以是 {key:value} 模式
  198. */
  199. set(path, value) {
  200. return this.viewModel.set(path, value)
  201. }
  202. /**
  203. * 寻找模块内所有的 xtype 对应的对象
  204. * @param xtypeKey
  205. */
  206. down(xtypeKey) {
  207. return this._handle.down(xtypeKey)
  208. }
  209. /**
  210. * 获取所有设置过 Reference 名称的组件
  211. */
  212. get refs() {
  213. return this._handle.getReferences()
  214. }
  215. constructor({model, vjson}) {
  216. const that = this
  217. this.model = model
  218. this.originalVjson = _.cloneDeep(vjson)
  219. this.vjson = this.buildVjson()
  220. }
  221. buildVjson() {
  222. const that = this
  223. return _.defaultsDeep({
  224. closable: true,
  225. listeners: {
  226. afterrender(sender) {
  227. // 记录句柄
  228. if (sender && !that._handle) {
  229. that._handle = sender
  230. }
  231. // 调用 onRender 回调
  232. that.onRender()
  233. // 如果vjson中配置了 afterrender ,需要恢复状态
  234. invokeMethod(that.originalVjson.listeners?.afterrender, that, arguments)
  235. },
  236. activate(sender) {
  237. // 调用 onActivate 回调
  238. that.onActivate()
  239. const vm = sender.lookupViewModel()
  240. if (vm) {
  241. window['currentScope'] = sender.lookupViewModel().yvanScope
  242. }
  243. // 如果vjson中配置了 afterrender ,需要恢复状态
  244. invokeMethod(that.originalVjson.listeners?.activate, that, arguments)
  245. },
  246. deactivate(sender) {
  247. // 调用 onActivate 回调
  248. that.onDeactivate()
  249. // 如果vjson中配置了 afterrender ,需要恢复状态
  250. invokeMethod(that.originalVjson.listeners?.deactivate, that, arguments)
  251. },
  252. beforedestroy(sender) {
  253. // 调用 onBeforeDestroy 回调
  254. that.onBeforeDestroy()
  255. // 如果vjson中配置了 afterrender ,需要恢复状态
  256. invokeMethod(that.originalVjson.listeners?.beforedestroy, that, arguments)
  257. },
  258. beforeclose(sender) {
  259. // 调用 onActivate 回调
  260. that.onBeforeClose()
  261. // 如果vjson中配置了 afterrender ,需要恢复状态
  262. invokeMethod(that.originalVjson.listeners?.beforeclose, that, arguments)
  263. },
  264. destroy(sender) {
  265. // 调用 onActivate 回调
  266. that.onDestroy()
  267. // 销毁 viewModel
  268. // that.viewModel.destroy()
  269. // 如果vjson中配置了 afterrender ,需要恢复状态
  270. invokeMethod(that.originalVjson.listeners?.destroy, that, arguments)
  271. // delete that.viewModel
  272. // delete that.vjson
  273. // delete that.originalVjson
  274. delete that._watchList
  275. delete that._handle
  276. },
  277. },
  278. yvanScope: this,
  279. referenceHolder: true,
  280. }, this.originalVjson)
  281. }
  282. /**
  283. * 模块载入完成之后的回调
  284. */
  285. onLoad() {
  286. }
  287. /**
  288. * 渲染完成之后的回调
  289. */
  290. onRender() {
  291. }
  292. /**
  293. * 被激活时的回调(可能执行多次)
  294. */
  295. onActivate() {
  296. }
  297. /**
  298. * 进入未激活状态之前回调的函数
  299. */
  300. onDeactivate() {
  301. }
  302. /**
  303. * 关闭之前的回调(只有 tab 选项卡有这个选项)
  304. */
  305. onBeforeClose() {
  306. }
  307. /**
  308. * 组件被卸载之前的回调
  309. */
  310. onBeforeDestroy() {
  311. }
  312. /**
  313. * 组件卸载之后的回调
  314. */
  315. onDestroy() {
  316. }
  317. }
  318. /**
  319. * 观察装饰器,viewModel 属性更改时触发成员方法
  320. * @param tplExpress tpl表达式,例如 "{form.f1}"
  321. */
  322. export function watch(tplExpress, deep = false) {
  323. return function (target, propertyKey, pd) {
  324. target._addWatch({bindTo: tplExpress, deep}, target[propertyKey])
  325. return target[propertyKey]
  326. }
  327. }