vectorized.ui.js 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. class VectorizedPanel extends zui.Component
  2. {
  3. static NAME = 'VectorizedPanel';
  4. get actualStatus()
  5. {
  6. if(!this.isStoreOk) return 'unavailable';
  7. let status = this.options.status;
  8. if(status === 'syncing' && !this.syncing) status = 'paused';
  9. return status;
  10. }
  11. init()
  12. {
  13. this.commandScope = 'vectorizedpanel';
  14. this.isLoading = false;
  15. this.syncing = false;
  16. this.syncingTimer = 0;
  17. this.progressBarWidth = 700;
  18. this.waitTime = 0;
  19. this.syncCouter = 0;
  20. let store = null;
  21. if(window !== window.parent)
  22. {
  23. const zaiPanel = window.parent.zui.AIPanel.shared;
  24. store = zaiPanel ? zaiPanel.store : null;
  25. }
  26. else if(this.options.zaiSetting)
  27. {
  28. try {
  29. store = zui.ZentaoAIStore.createFromZentao(this.options.zaiSetting);
  30. } catch (error) {
  31. console.error('create zentao ai store failed', error);
  32. }
  33. }
  34. this.store = store;
  35. }
  36. async afterInit()
  37. {
  38. this.$panel = this.$element.closest('.vectorized-panel');
  39. this.toggleLoading();
  40. if (this.store) this.isStoreOk = await this.store.isOK();
  41. this.toggleLoading(false);
  42. zui.bindCommands(this.$panel, {
  43. scope: this.commandScope,
  44. onCommand: this.executeCommand.bind(this),
  45. });
  46. this.render();
  47. }
  48. updateProgresses(status)
  49. {
  50. status = status || this.actualStatus;
  51. const options = this.options;
  52. const syncDetails = options.syncDetails || {};
  53. const $progresses = this.$panel.find('.vectorized-progress');
  54. const maxCount = Math.max(100, Object.values(syncDetails).reduce((maxVal, info) => {
  55. maxVal = Math.max(maxVal, (info.failed || 0) + (info.synced || 0));
  56. return maxVal;
  57. }, 0));
  58. const maxWidth = this.progressBarWidth || 600;
  59. const syncing = !!this.syncing;
  60. $progresses.each(function()
  61. {
  62. const $progress = $(this);
  63. const type = $progress.data('type');
  64. const info = syncDetails[type] || {failed: 0, synced: 0};
  65. const isSyncing = type === options.syncingType && syncing;
  66. const synced = info.synced || 0;
  67. const failed = info.failed || 0;
  68. const total = synced + failed;
  69. $progress.toggleClass('is-syncing-type', isSyncing);
  70. $progress.find('.vectorized-failed-info').toggleClass('hidden', !info.failed);
  71. $progress.find('.vectorized-finished-count').text(synced);
  72. $progress.find('.vectorized-failed-count').text(failed);
  73. $progress.find('.vectorized-loading-icon').toggleClass('hidden', !isSyncing);
  74. $progress.find('.vectorized-sync-type').toggleClass('font-bold', !!syncDetails[type]).toggleClass('text-gray', !syncDetails[type]).toggleClass('text-primary', isSyncing);
  75. $progress.find('.vectorized-sync-progress').toggleClass('active progress-striped', isSyncing).css('width', Math.max(1, maxWidth * total / maxCount));
  76. $progress.find('.progress-bar.is-synced').css('width', total ? `${100 * synced / total}%` : '1px');
  77. $progress.find('.progress-bar.is-failed').css('width', total ? `${100 * failed / total}%` : '0');
  78. });
  79. }
  80. render(newOptions)
  81. {
  82. super.render(newOptions);
  83. const $panel = this.$panel;
  84. const options = this.options;
  85. const status = this.actualStatus;
  86. const lang = options.langData;
  87. const syncFailed = status === 'synced' && !this.options.syncedCount && !!this.options.syncFailedCount;
  88. Object.keys(lang.vectorizedStatusList).forEach(statusType =>
  89. {
  90. $panel.toggleClass(`is-status-${statusType}`, status === statusType).toggleClass(`not-status-${statusType}`, status !== statusType);
  91. });
  92. $panel.removeClass('loading');
  93. $panel.find('.vectorized-status').text(lang.vectorizedStatusList[syncFailed ? 'failed' : status]);
  94. $panel.toggleClass('is-syncing-loop', !!this.syncing);
  95. $panel.toggleClass('not-syncing-loop', !this.syncing);
  96. $panel.toggleClass('is-synced-failed', syncFailed).toggleClass('not-synced-failed', !syncFailed);
  97. $panel.find('.vectorized-finished-total-count').text(options.syncedCount);
  98. $panel.find('.vectorized-failed-total-count').text(options.syncFailedCount);
  99. $panel.find('.vectorized-last-sync-info').toggleClass('hidden', status !== 'paused' && status !== 'synced').find('.vectorized-last-sync-time').text(zui.formatDate(status === 'synced' ? (options.syncedTime || options.syncTime) : options.syncTime));
  100. $panel.find('.vectorized-synced-with-failed').toggleClass('hidden', options.syncFailedCount === 0 || status !== 'synced');
  101. this.updateProgresses();
  102. }
  103. executeCommand(context, params)
  104. {
  105. if(typeof context === 'string') context = {name: context};
  106. if (!context.scope || context.scope === this.commandScope)
  107. {
  108. return zui.deepCall(this, context.name, params);
  109. }
  110. }
  111. toggleLoading(loading)
  112. {
  113. loading = loading === undefined ? true : !!loading;
  114. this.isLoading = loading;
  115. this.$panel.toggleClass('loading', loading);
  116. }
  117. getLang(key)
  118. {
  119. return this.options.langData[key];
  120. }
  121. async request(url, options)
  122. {
  123. if(Array.isArray(url)) url = $.createLink.apply(null, url);
  124. const result = await $.ajaxSubmit($.extend({url: url}, options));
  125. const response = result[0];
  126. const error = result[1];
  127. if(!error && response && response.info) this.render(response.info);
  128. return response;
  129. }
  130. async post(url, data, options)
  131. {
  132. return this.request(url, $.extend({method: 'POST', data: data}, options));
  133. }
  134. async enable()
  135. {
  136. this.toggleLoading(true);
  137. const result = await this.post(['zai', 'ajaxEnableVectorization']);
  138. this.toggleLoading(false);
  139. if(result) return true;
  140. }
  141. async syncNext(data)
  142. {
  143. this.syncCouter++;
  144. const result = await this.post(['zai', 'ajaxSyncVectorization'], data);
  145. let waitTime = result.data.lastSync ? (result.data.lastSync.time * 3) : 100;
  146. if(this.syncCouter % 200 === 0) waitTime += 2000;
  147. if(this.syncCouter % 1000 === 0) waitTime += 2000;
  148. await zui.delay(waitTime);
  149. return result;
  150. }
  151. async sync(options)
  152. {
  153. this.syncing = true;
  154. this.render();
  155. const force = options && options.force;
  156. const result = await this.syncNext({force: !!force});
  157. if(!result || typeof result !== 'object')
  158. {
  159. zui.Messager.fail(this.getLang('syncRequestFailed'));
  160. this.syncing = false;
  161. this.render();
  162. return;
  163. }
  164. if(result.result !== 'success')
  165. {
  166. this.syncing = false;
  167. this.render(result.data);
  168. return;
  169. }
  170. if(result.data)
  171. {
  172. if(result.data.status === 'synced')
  173. {
  174. this.syncing = false;
  175. }
  176. else
  177. {
  178. this.syncingTimer = setTimeout(() => {
  179. this.syncingTimer = 0;
  180. if(this.syncing) this.sync();
  181. }, this.waitTime || 0);
  182. }
  183. this.render(result.data);
  184. }
  185. }
  186. async startSync()
  187. {
  188. if(this.syncing) return;
  189. this.sync();
  190. }
  191. resumeSync()
  192. {
  193. return this.startSync();
  194. }
  195. pauseSync()
  196. {
  197. if(this.syncingTimer)
  198. {
  199. clearTimeout(this.syncingTimer);
  200. this.syncingTimer = 0;
  201. }
  202. this.syncing = false;
  203. }
  204. resync()
  205. {
  206. if(this.syncing) return;
  207. return this.sync({force: true});
  208. }
  209. async resetSync()
  210. {
  211. const confirmed = await zui.Modal.confirm(this.getLang('confirmResetSync'));
  212. if(!confirmed) return;
  213. this.toggleLoading(true);
  214. const result = await this.post(['zai', 'ajaxEnableVectorization'], {force: true});
  215. this.toggleLoading(false);
  216. return result;
  217. }
  218. destroy()
  219. {
  220. super.destroy();
  221. zui.unbindCommands(this.$panel, this.commandScope);
  222. }
  223. };
  224. VectorizedPanel.register();
  225. window.updateVectorizedState = function($element)
  226. {
  227. const panel = $element.find('[z-use-vectorizedpanel]').zui();
  228. if(panel) panel.render();
  229. };