common.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. var filterValues = {};
  2. /**
  3. * Listen filter change.
  4. *
  5. * @param string field
  6. * @param string dateSelect
  7. * @param string $filterValue
  8. * @access public
  9. * @return void
  10. */
  11. function filterChange(field, dateSelect, $filterValue)
  12. {
  13. var parse = field.split('.');
  14. field = parse[0];
  15. var type = '';
  16. if(parse.length == 2) type = parse[1];
  17. var id = type.length == 0 ? field : field + '\\[' + type + '\\]';
  18. var value = $('#' + id).val();
  19. if(type.length == 0)
  20. {
  21. filterValues[field] = value;
  22. ajaxGetChart();
  23. }
  24. else
  25. {
  26. var check = checkDate(dateSelect, $filterValue);
  27. if(!check) return;
  28. if(!filterValues[field]) filterValues[field] = {begin: '', end: ''};
  29. filterValues[field][type] = value;
  30. var begin = filterValues[field].begin;
  31. var end = filterValues[field].end;
  32. if((begin.length > 0 && end.length > 0) || (begin.length == 0 && end.length == 0)) ajaxGetChart();
  33. }
  34. }
  35. /**
  36. * Ajax get pivot data.
  37. *
  38. * @access public
  39. * @return bool
  40. */
  41. function ajaxGetChart()
  42. {
  43. /* If the same value is detected for the x and y axes, throw error and clear the echart, then return.*/
  44. /* 如果x轴和y轴重复了,那么抛出错误并返回。*/
  45. if(!checkFormSetting()) return;
  46. var chartParams = JSON.parse(JSON.stringify(DataStorage.pivot));
  47. chartParams.filterValues = filterValues;
  48. /* Redraw echart. */
  49. /* 拿数据并重绘图表。*/
  50. $.post(createLink('pivot', 'ajaxGetChart'), chartParams, function(resp)
  51. {
  52. var data = JSON.parse(resp);
  53. echart.clear();
  54. echart.setOption(data, true);
  55. });
  56. }
  57. function getQueryFilters(pivot)
  58. {
  59. var filters = '';
  60. filters = pivot.filters;
  61. filters.forEach(function(filter, index)
  62. {
  63. if(filter.from == 'query')
  64. {
  65. var find = $('#queryFilters').find('.filter-item-' + index + ' .form-' + filter.type);
  66. if(find.length != 0) filter.default = find.val();
  67. }
  68. });
  69. return filters;
  70. }
  71. /**
  72. * If the same value is detected for the x and y axias, throw error and clear the echart, then return.
  73. *
  74. * @access public
  75. * @return bool ture|false
  76. */
  77. function checkFormSetting()
  78. {
  79. var pivot = DataStorage.pivot;
  80. var type = pivot.settings[0].type;
  81. var result = 'success';
  82. var errorMsg = '';
  83. if(checkForm[type])
  84. {
  85. for(var key in checkForm[type])
  86. {
  87. var fields = checkForm[type][key].split(',');
  88. var former = fields[0];
  89. var latter = fields[1];
  90. if(key == 'cantequal')
  91. {
  92. if(pivot.settings[0][latter].includes(pivot.settings[0][former]))
  93. {
  94. result = 'fail';
  95. errorMsg = pivotLang['errorList']['cantequal'];
  96. errorMsg = errorMsg.replace(/%s/, pivotLang[type][latter]);
  97. errorMsg = errorMsg.replace(/%s/, pivotLang[type][former]);
  98. }
  99. }
  100. }
  101. }
  102. if(result == 'fail')
  103. {
  104. var message = new $.zui.Messager(errorMsg,
  105. {
  106. html: true,
  107. icon: 'exclamation-sign',
  108. type: 'danger',
  109. close: true,
  110. });
  111. message.show();
  112. echart.clear();
  113. return false;
  114. }
  115. return true;
  116. }
  117. /**
  118. * Validate form required.
  119. *
  120. * @access public
  121. * @return void
  122. */
  123. function validate(showError = false)
  124. {
  125. var pivot = DataStorage.pivot;
  126. var formSettings = pivot.settings;
  127. var isReady = true;
  128. var firstErrDom = null;
  129. if("summary" in formSettings && formSettings.summary == 'notuse')
  130. {
  131. if(isReady) $('#datagrid-tip').addClass('hidden');
  132. return true;
  133. }
  134. /* check group settings. */
  135. var exist = false;
  136. for(var key in formSettings)
  137. {
  138. if(key.indexOf('group') != '-1' && formSettings[key].length > 0) exist = true;
  139. }
  140. var tr = $('#step2Content form#groupForm table tbody tr:nth-child(1)');
  141. tr.find('#groupLabel').remove();
  142. tr.find('#group1').removeClass('has-error');
  143. if(!exist)
  144. {
  145. isReady = false;
  146. if(showError)
  147. {
  148. var error = '<div id="groupLabel" class="text-danger help-text">' + moreThanOneLang + '</div>';
  149. tr.find('#group1').addClass('has-error');
  150. tr.find('#group1').next().after(error);
  151. firstErrDom = tr.find('#group1').parents('tr');
  152. }
  153. }
  154. /* check columns settings. */
  155. formSettings.columns.forEach(function(value, index)
  156. {
  157. var $column = $('#step2Content form#columnForm .column-' + index);
  158. $column.find('#column' + index + 'Label').remove();
  159. $column.find('#stat' + index + 'Label').remove();
  160. $column.find('#column').removeClass('has-error');
  161. $column.find('#stat').removeClass('has-error');
  162. if(!value.field || value.field.length == 0)
  163. {
  164. isReady = false;
  165. if(showError)
  166. {
  167. var error = '<div id="column' + index + 'Label" class="text-danger help-text">' + notemptyLang.replace('%s', pivotLang.step2.columnField) + '</div>';
  168. $column.find('#column').addClass('has-error');
  169. $column.find('#column').parent().after(error);
  170. if(!firstErrDom) firstErrDom = $column.find('#column').parents('.column');
  171. }
  172. }
  173. if(value.showOrigin != '1' && (!value.stat || value.stat.length == 0 || value.stat === ''))
  174. {
  175. isReady = false;
  176. if(showError)
  177. {
  178. var error = '<div id="stat' + index + 'Label" class="text-danger help-text">' + notemptyLang.replace('%s', pivotLang.step2.calcMode) + '</div>';
  179. $column.find('#stat').addClass('has-error');
  180. $column.find('#stat').next().after(error);
  181. if(!firstErrDom) firstErrDom = $column.find('#stat').parents('.column');
  182. }
  183. }
  184. });
  185. if(isReady) $('#datagrid-tip').addClass('hidden');
  186. if(!isReady && firstErrDom) firstErrDom[0].scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'start' })
  187. return isReady;
  188. }
  189. /**
  190. * Multi Validate.
  191. *
  192. * @param setting $setting
  193. * @param showError $showError
  194. * @access public
  195. * @return void
  196. */
  197. function multiValidate(setting, showError)
  198. {
  199. var pivot = DataStorage.pivot;
  200. var type = pivot.settings[0].type;
  201. var isReady = true;
  202. var field = setting.field;
  203. var error = '<div id="' + field + 'Label"' + ' class="text-danger help-text">' + notemptyLang.replace('%s', pivotLang[type][field]) + '</div>';
  204. $('#pivotForm .table-form').find('.multi-' + field).each(function()
  205. {
  206. $(this).parent('td').find('#' + field + 'Label').remove();
  207. $(this).parent('td').find('#' + field + '_chosen a').removeClass('has-error');
  208. if(setting.required && $(this).val().length == 0)
  209. {
  210. isReady = false;
  211. if(showError)
  212. {
  213. $(this).parent('td').find('#' + field + '_chosen a').addClass('has-error');
  214. $(this).parent('td').find('#' + field + '_chosen').after(error);
  215. }
  216. }
  217. });
  218. return isReady;
  219. }
  220. /**
  221. * Check date.
  222. *
  223. * @param string dateSelect
  224. * @param object $filterValue
  225. * @access public
  226. * @return void
  227. */
  228. function checkDate(dateSelect, $filterValue)
  229. {
  230. var begin = new Date($(dateSelect).parent().find('.default-begin').val().replace(/-/g, "\/")).getTime();
  231. var end = new Date($(dateSelect).parent().find('.default-end').val().replace(/-/g, "\/")).getTime();
  232. if(begin > end)
  233. {
  234. $(dateSelect).val('');
  235. if(typeof $filterValue == 'object') $filterValue.val('');
  236. bootbox.alert(pivotLang.beginGtEnd);
  237. return false;
  238. }
  239. return true;
  240. }
  241. function waitForRepaint(callback)
  242. {
  243. window.requestAnimationFrame(function()
  244. {
  245. window.requestAnimationFrame(callback);
  246. });
  247. }
  248. /**
  249. * Init picker.
  250. *
  251. * @access public
  252. * @return void
  253. */
  254. function initPicker($row, pickerName = 'picker-select', onready = false)
  255. {
  256. $row.find('.' + pickerName).picker(
  257. {
  258. maxDropHeight: pickerHeight,
  259. onReady: function()
  260. {
  261. if(!onready) return;
  262. if(!$row.find('.picker')) return;
  263. if(window.getComputedStyle($row.find('.picker').find('.picker-selections')[0]).getPropertyValue('width') !== 'auto')
  264. {
  265. var pickerWidth = $row.find('.picker')[0].getBoundingClientRect().width;
  266. $row.find('.picker').find('.picker-selections').css('width', pickerWidth);
  267. }
  268. }
  269. });
  270. $row.find("." + pickerName).each(function(index)
  271. {
  272. if($(this).hasClass('required')) $(this).siblings("div .picker").addClass('required');
  273. });
  274. }
  275. /**
  276. * Init datapicker.
  277. *
  278. * @param object $obj
  279. * @param function callback
  280. * @access public
  281. * @return void
  282. */
  283. function initDatepicker($obj, callback)
  284. {
  285. $obj.find('.form-date').datepicker();
  286. $obj.find('.form-datetime').datetimepicker();
  287. if(typeof callback == 'function') callback($obj);
  288. }
  289. /**
  290. * Attr date check.
  291. *
  292. * @param object $obj
  293. * @access public
  294. * @return void
  295. */
  296. function attrDateCheck($obj)
  297. {
  298. $obj.find('.form-date').attr('onchange', 'checkDate(this, this.value)');
  299. $obj.find('.form-datetime').attr('onchange', 'checkDate(this, this.value)');
  300. }
  301. /**
  302. * Export file.
  303. *
  304. * @param object $domObj
  305. * @access public
  306. * @return void
  307. */
  308. function exportFile($domObj)
  309. {
  310. if(typeof $domObj == 'undefined') return;
  311. var fileName = $('#fileName').val().trim() ? $('#fileName').val().trim() : untitled;
  312. var fileType = $('#fileType').val();
  313. var tableName = fileName + '.' + fileType;
  314. if(fileType == 'xlsx' || fileType == 'xls')
  315. {
  316. const new_sheet = XLSX.utils.table_to_book($domObj, {raw: true});
  317. XLSX.writeFile(new_sheet, tableName);
  318. }
  319. else if(fileType == 'html' || fileType == 'mht')
  320. {
  321. const htmlContent = $domObj.outerHTML;
  322. const $temp = $('<div>').html(htmlContent);
  323. $temp.find('*').removeAttr('style');
  324. $temp.find('*').removeAttr('class');
  325. $temp.find('*').removeAttr('data-flex');
  326. $temp.find('*').removeAttr('data-width');
  327. $temp.find('*').removeAttr('data-type');
  328. $temp.find('*').removeAttr('data-fixed-left-width');
  329. const cleanTableHTML = $temp.html();
  330. var head = '<head><meta http-equiv="Content-Type" content="text/html; charset=utf-8">';
  331. var style = '<style>table, th, td {font-size: 12px; border: 1px solid gray; border-collapse: collapse;}table th, table td {padding: 5px;}</style>';
  332. var title = '<title>' + fileName + '</title></head>';
  333. var body = '<body>' + cleanTableHTML + '</body>';
  334. const finalHTML = head + style + title + body;
  335. if(fileType == 'html')
  336. {
  337. const blob = new Blob([finalHTML], { type: 'text/html;charset=utf-8' });
  338. saveAs(blob, tableName);
  339. }
  340. else if(fileType == 'mht')
  341. {
  342. const data = {html: finalHTML, fileName: fileName};
  343. $.post(createLink('file', 'ajaxExport2mht'), data, function(resp)
  344. {
  345. const blob = new Blob([resp], { type: "application/x-mimearchive" });
  346. saveAs(blob, tableName);
  347. });
  348. }
  349. }
  350. $('#export').modal('hide');
  351. }
  352. /**
  353. * Zui messager alert.
  354. *
  355. * @param string result success|fail
  356. * @param string mes
  357. * @access public
  358. * @return void
  359. */
  360. function zuiMessage(result, mes)
  361. {
  362. var icon = result == 'success' ? 'check-circle' : 'exclamation-sign';
  363. var type = result == 'success' ? 'success' : 'danger';
  364. var message = new $.zui.Messager(mes,
  365. {
  366. html: true,
  367. icon: icon,
  368. type: type,
  369. close: true,
  370. });
  371. message.show();
  372. }
  373. function select(name, options, selected, attrib, callback)
  374. {
  375. var type = 'option';
  376. if(!Array.isArray(options))
  377. {
  378. type = options;
  379. options = [];
  380. }
  381. $.post(createLink('pivot', 'ajaxGetSelect'),
  382. {
  383. name: name,
  384. type: type,
  385. selectedItems: selected,
  386. attrib: attrib,
  387. options: options
  388. }, function(resp)
  389. {
  390. if(typeof callback == 'function') callback(resp);
  391. });
  392. }
  393. function setDateField(control)
  394. {
  395. var $period = $('#selectPeriod');
  396. if(!$period.length)
  397. {
  398. $period = $("<ul id='selectPeriod' class='dropdown-menu'><li><a href='#MONDAY'>" + datepickerText.TEXT_WEEK_MONDAY + "</a></li><li><a href='#SUNDAY'>" + datepickerText.TEXT_WEEK_SUNDAY + "</a></li><li><a href='#MONTHBEGIN'>" + datepickerText.TEXT_MONTH_BEGIN + "</a></li><li><a href='#MONTHEND'>" + datepickerText.TEXT_MONTH_END + "</a></li></ul>").appendTo('body');
  399. $period.find('li > a').click(function(event)
  400. {
  401. var target = control.parents('table, #queryFilters, #filterItems,#filterForm').find('[data-index="' + $period.attr('data-index') + '"]').find('#default:visible');
  402. if(target.length)
  403. {
  404. target.val($(this).attr('href').replace('#', '$')).trigger('change');
  405. $period.hide();
  406. }
  407. event.stopPropagation();
  408. return false;
  409. });
  410. }
  411. if(control.hasClass('form-date')) control.datepicker();
  412. if(control.hasClass('form-datetime')) control.datetimepicker();
  413. var dateVal = control.val();
  414. control.val('').datetimepicker('update').trigger('mousedown');
  415. control.val(dateVal);
  416. control.on('show', function(e)
  417. {
  418. var $e = $(e.target);
  419. var ePos = $e.offset();
  420. $period.css({'left': ePos.left + 210, 'top': ePos.top + 29, 'min-height': $('.datetimepicker').outerHeight(), 'z-index': 1110}).show().attr('data-index', $e.parents('.filter-item, .filter').data('index')).find('li.active').removeClass('active');
  421. $period.find("li > a[href='" + $e.val().replace('$', '#') + "']").closest('li').addClass('active');
  422. }).on('changeDate', function()
  423. {
  424. $period.hide();
  425. }).on('hide', function(){setTimeout(function(){$period.hide();}, 200);});
  426. }