Scope.ts 8.3 KB

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