Scope.ts 9.0 KB

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