phase_pay_detail.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. 'use strict';
  2. math.config({
  3. number: 'BigNumber',
  4. });
  5. $(document).ready(() => {
  6. const payUtils = {
  7. tips: {
  8. name: function(data) {
  9. const tips = [];
  10. if (data) {
  11. if (data.pause) tips.push('当前项已停用');
  12. if (!data.is_yf) tips.push('当前项不参与本期应付计算');
  13. }
  14. return tips.join('<br/>');
  15. },
  16. range_tp: function (data) {
  17. if (!data || (!data.range_expr && !data.range_tp) || !data.dl_type) return '';
  18. if (data.dl_type === 1) {
  19. return '计提期限为(当 计量期数 ≥ ' + data.dl_count + ')';
  20. } else if (data.dl_type === 2) {
  21. switch (data.dl_tp_type) {
  22. case 'contract':
  23. return '计提期限为(累计合同计量 ≥ ' + data.dl_tp + ')';
  24. case 'qc':
  25. return '计提期限为(累计变更计量 ≥ ' + data.dl_tp + ')';
  26. case 'gather':
  27. return '计提期限为(累计完成计量 ≥ ' + data.dl_tp + ')';
  28. }
  29. }
  30. }
  31. },
  32. check: {
  33. isFixed: function(data) {
  34. return data.is_fixed;
  35. },
  36. isStarted: function (data) {
  37. return data.pre_used;
  38. },
  39. isYf: function(data) {
  40. return data.pay_type === 'bqyf';
  41. },
  42. isSf: function(data) {
  43. return data.pay_type === 'bqsf';
  44. }
  45. }
  46. };
  47. const payCalc = (function (b) {
  48. class PayCalc {
  49. constructor (bases) {
  50. this.percentReg = /((\d+)|((\d+)(\.\d+)))%/g;
  51. this.bases = bases;
  52. this.bases.sort(function (a, b) {
  53. return a.sort - b.sort;
  54. });
  55. for (const b of this.bases) {
  56. b.reg = new RegExp(b.code, 'igm');
  57. }
  58. this.orderReg = /f\d+/ig;
  59. this.nodeReg = /<<[a-z0-9\-]+>>/ig;
  60. }
  61. trans2OrderExpr(expr, payTree) {
  62. const nodeParam = expr.match(this.nodeReg);
  63. if (nodeParam) {
  64. for (const op of nodeParam) {
  65. const id = op.substring(2, op.length - 2);
  66. const payNode = payTree.nodes.find(x => { return x.uuid === id; });
  67. expr = expr.replace(op, payNode ? `f${payTree.getNodeIndex(payNode) + 1}` || '' : 0);
  68. }
  69. }
  70. return expr;
  71. }
  72. trans2NodeExpr(expr, payTree) {
  73. const orderParam = expr.match(this.orderReg);
  74. if (orderParam) {
  75. for (const op of orderParam) {
  76. const order = parseInt(op.substring(1, op.length));
  77. const payNode = payTree.nodes[order - 1];
  78. expr = expr.replace(op, payNode ? `<<${payNode.uuid}>>` || '' : 0);
  79. }
  80. }
  81. return expr;
  82. }
  83. checkSfExpr() {
  84. }
  85. checkExpr() {
  86. }
  87. checkRangeExpr() {
  88. }
  89. checkStartExpr() {
  90. }
  91. getExprInfo(field, converse = false) {
  92. const exprField = [
  93. {qty: 'tp', expr: 'expr'},
  94. {qty: 'start_tp', expr: 'start_expr'},
  95. {qty: 'range_qty', expr: 'range_expr'},
  96. ];
  97. if (converse) return _.find(exprField, { expr: field });
  98. return _.find(exprField, {qty: field});
  99. }
  100. getLeafOrder(data, parentOrder) {
  101. if (!data) return [];
  102. const defaultResult = data.order ? [`f${data.order}`] : [];
  103. if (!data.expr) return defaultResult;
  104. const orderParam = data.expr.match(this.orderReg);
  105. if (!orderParam || orderParam.length === 0) return defaultResult;
  106. const result = [], payData = paySheet.zh_data || [];
  107. for (const op of orderParam) {
  108. const order = op.substring(1, op.length);
  109. if (data.order === parseInt(order) || op === parentOrder) {
  110. result.push(op);
  111. } else {
  112. const subOrderParam = this.getLeafOrder(payData[parseInt(order) -1], data.order ? `f${data.order}` : parentOrder);
  113. result.push(...subOrderParam);
  114. }
  115. }
  116. return result;
  117. }
  118. checkCircularExpr(expr, selfOrder) {
  119. const leafOrder = this.getLeafOrder({expr}, `f${selfOrder}`);
  120. if (leafOrder.indexOf(`f${selfOrder}`) >= 0 || leafOrder.indexOf(`F${selfOrder}`) >= 0) return true;
  121. return false;
  122. }
  123. calculateExpr(expr) {
  124. let formula = expr;
  125. for (const b of this.bases) {
  126. formula = formula.replace(b.reg, b.value);
  127. }
  128. const percent = formula.match(this.percentReg);
  129. if (percent) {
  130. for (const p of percent) {
  131. const v = math.evaluate(p.replace(new RegExp('%', 'gm'), '/100'));
  132. formula = formula.replace(p, v);
  133. }
  134. }
  135. try {
  136. const value = parseFloat(math.evaluate(formula));
  137. return value;
  138. } catch(err) {
  139. return 0;
  140. }
  141. }
  142. }
  143. return new PayCalc(b);
  144. })(calcBase);
  145. const payObj = (function() {
  146. const spread = SpreadJsObj.createNewSpread($('#pay-spread')[0]);
  147. const sheet = spread.getActiveSheet();
  148. const spreadSetting = {
  149. cols: [
  150. {title: '名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 230, formatter: '@', cellType: 'tree', getTip: payUtils.tips.name},
  151. {title: '本期金额(F)', colSpan: '1', rowSpan: '1', field: 'tp', hAlign: 2, width: 100, type: 'Number'},
  152. {title: '截止上期金额', colSpan: '1', rowSpan: '1', field: 'pre_tp', hAlign: 2, width: 100, readOnly: true, type: 'Number'},
  153. {title: '截止本期金额', colSpan: '1', rowSpan: '1', field: 'end_tp', hAlign: 2, width: 100, readOnly: true, type: 'Number'},
  154. {title: '起扣金额', colSpan: '1', rowSpan: '1', field: 'start_tp', hAlign: 2, width: 100, type: 'Number'},
  155. {title: '付(扣)款限额', colSpan: '1', rowSpan: '1', field: 'range_tp', hAlign: 2, width: 100, cellType: 'tip', type: 'Number', getTip: payUtils.tips.range_tp},
  156. {title: '汇总', colSpan: '1', rowSpan: '1', field: 'is_yf', hAlign: 2, width: 60, readOnly: true, type: 'checkbox'},
  157. {title: '附件', colSpan: '1', rowSpan: '1', field: 'attachment', hAlign: 0, width: 60, readOnly: true, cellType: 'imageBtn', normalImg: '#rela-file-icon', hoverImg: '#rela-file-hover', getValue: 'getValue.attachment'},
  158. {title: '本期批注', colSpan: '1', rowSpan: '1', field: 'postil', hAlign: 0, width: 120, formatter: '@', cellType: 'autoTip'},
  159. ],
  160. emptyRows: 0,
  161. headRows: 1,
  162. headRowHeight: [32],
  163. headColWidth: [30],
  164. defaultRowHeight: 21,
  165. headerFont: '12px 微软雅黑',
  166. font: '12px 微软雅黑',
  167. readOnly: readOnly,
  168. localCache: {
  169. key: 'phase-pay',
  170. colWidth: true,
  171. },
  172. pos: SpreadJsObj.getObjPos($('#pay-spread')[0]),
  173. };
  174. sjsSettingObj.setFxTreeStyle(spreadSetting, sjsSettingObj.FxTreeStyle.phasePay);
  175. SpreadJsObj.initSheet(sheet, spreadSetting);
  176. const payTree = createNewPathTree('base', {
  177. id: 'tree_id', pid: 'tree_pid', order: 'tree_order',
  178. level: 'tree_level', isLeaf: 'tree_is_leaf', fullPath: 'tree_full_path',
  179. rootId: -1,
  180. });
  181. const payEvent = {
  182. refreshActn: function() {
  183. const setObjEnable = function (obj, enable) {
  184. if (enable) {
  185. obj.removeClass('disabled');
  186. } else {
  187. obj.addClass('disabled');
  188. }
  189. };
  190. const select = SpreadJsObj.getSelectObject(sheet);
  191. const preNode = payTree.getPreSiblingNode(select);
  192. setObjEnable($('a[name=base-opr][type=add]'), !readOnly && !payUtils.check.isSf(select) && !payUtils.check.isYf(select));
  193. const delValid = !payUtils.check.isFixed(select) && !payUtils.check.isStarted(select);
  194. setObjEnable($('a[name=base-opr][type=del]'), !readOnly && select && delValid);
  195. setObjEnable($('a[name=base-opr][type=up-move]'), !readOnly && select && !payUtils.check.isFixed(select) && preNode);
  196. setObjEnable($('a[name=base-opr][type=down-move]'), !readOnly && select && !payUtils.check.isFixed(select) && !payTree.isLastSibling(select));
  197. },
  198. loadExprToInput: function() {
  199. const sel = sheet.getSelections()[0];
  200. const col = sheet.zh_setting.cols[sel.col];
  201. const data = SpreadJsObj.getSelectObject(this.sheet);
  202. if (data) {
  203. if (col.field === 'tp') {
  204. $('#expr').val(data.expr).attr('field', 'expr').attr('org', data.expr)
  205. .attr('readOnly', readOnly|| payCol.readOnly.tp(data));
  206. } else if (col.field === 'stage_tp') {
  207. $('#expr').val(data.start_expr).attr('field', 'start_expr').attr('org', data.start_expr)
  208. .attr('readOnly', readOnly|| payCol.readOnly.sprice(data) || payBase.isYF(data));
  209. } else if (col.field === 'rprice') {
  210. $('#expr').val(data.range_expr).attr('field', 'range_expr').attr('org', data.range_expr)
  211. .attr('readOnly', readOnly|| payCol.readOnly.rprice(data) || payBase.isYF(data));
  212. } else {
  213. $('#expr').val('').attr('readOnly', true);
  214. }
  215. $('#expr').attr('data-row', sel.row);
  216. } else {
  217. $('#expr').val('').attr('readOnly', true);
  218. $('#expr').removeAttr('data-row');
  219. }
  220. },
  221. refreshTree: function (data) {
  222. SpreadJsObj.massOperationSheet(sheet, function () {
  223. const tree = sheet.zh_tree;
  224. // 处理删除
  225. if (data.delete) {
  226. data.delete.sort(function (a, b) {
  227. return b.deleteIndex - a.deleteIndex;
  228. });
  229. for (const d of data.delete) {
  230. sheet.deleteRows(d.deleteIndex, 1);
  231. }
  232. }
  233. // 处理新增
  234. if (data.create) {
  235. const newNodes = data.create;
  236. if (newNodes) {
  237. newNodes.sort(function (a, b) {
  238. return a.index - b.index;
  239. });
  240. for (const node of newNodes) {
  241. sheet.addRows(node.index, 1);
  242. SpreadJsObj.reLoadRowData(sheet, tree.nodes.indexOf(node), 1);
  243. }
  244. }
  245. }
  246. // 处理更新
  247. if (data.update) {
  248. const rows = [];
  249. for (const u of data.update) {
  250. rows.push(tree.nodes.indexOf(u));
  251. billsTag.refreshBillsTagView(u);
  252. }
  253. SpreadJsObj.reLoadRowsData(sheet, rows);
  254. }
  255. });
  256. },
  257. editStarting: function(e, info) {
  258. const col = info.sheet.zh_setting.cols[info.col];
  259. const select = SpreadJsObj.getSelectObject(info.sheet);
  260. switch (col.field) {
  261. case 'name':
  262. info.cancel = payUtils.check.isFixed(select);
  263. break;
  264. case 'tp':
  265. case 'is_gather':
  266. info.cancel = select.children && select.children.length > 0;
  267. break;
  268. case 'start_tp':
  269. case 'range_tp':
  270. info.cancel = (select.children && select.children.length > 0) || payUtils.check.isYf(select);
  271. break;
  272. }
  273. if (col.field === 'tp') {
  274. if (select.expr && select.expr !== '') {
  275. info.sheet.getCell(info.row, info.col).text(payCalc.trans2OrderExpr(select.expr, payTree));
  276. }
  277. } else if (col.field === 'start_tp') {
  278. if (select.start_expr && select.start_expr !== '') {
  279. info.sheet.getCell(info.row, info.col).text(select.start_expr);
  280. }
  281. } else if (col.field === 'range_tp') {
  282. if (select.range_expr && select.range_expr !== '') {
  283. info.sheet.getCell(info.row, info.col).text(select.range_expr);
  284. }
  285. }
  286. },
  287. editEnded: function(e, info) {
  288. if (!info.sheet.zh_setting) return;
  289. const select = SpreadJsObj.getSelectObject(info.sheet);
  290. const col = info.sheet.zh_setting.cols[info.col];
  291. if (col.field === 'is_gather') return;
  292. // 未改变值则不提交
  293. const validText = info.editingText ? info.editingText.replace('\n', '') : null;
  294. let orgValue;
  295. if (col.field === 'tp') {
  296. orgValue = select.expr ? payCalc.trans2OrderExpr(select.expr, payTree) : select.tp;
  297. } else if (col.field === 'start_tp') {
  298. orgValue = select.start_expr ? select.start_expr : select.start_tp;
  299. } else if (col.field === 'range_tp') {
  300. orgValue = select.range_expr ? select.range_expr : select.range_tp;
  301. } else {
  302. orgValue = select[col.field];
  303. }
  304. if (orgValue == validText || ((!orgValue || orgValue === '') && (validText === ''))) {
  305. SpreadJsObj.reLoadRowData(info.sheet, info.row);
  306. return;
  307. }
  308. const data = { postType: 'update', postData: {} };
  309. switch(col.field) {
  310. case 'tp':
  311. const [tpValid, tpMsg] = payUtils.isSF(select)
  312. ? payCalc.checkSfExpr(validText, data.updateData, select.order)
  313. : payCalc.checkExpr(validText, data.updateData, select.order);
  314. if (!tpValid) {
  315. toastr.warning(tpMsg);
  316. SpreadJsObj.reLoadRowData(info.sheet, info.row);
  317. return;
  318. }
  319. break;
  320. case 'start_tp':
  321. const [sValid, sMsg] = payCalc.checkStartExpr(select, validText, data.updateData);
  322. if (!sValid) {
  323. toastr.warning(sMsg);
  324. SpreadJsObj.reLoadRowData(info.sheet, info.row);
  325. return;
  326. }
  327. break;
  328. case 'range_tp':
  329. const [rValid, rMsg] = payCalc.checkRangeExpr(select, validText, data.updateData);
  330. if (!rValid) {
  331. toastr.warning(rMsg);
  332. SpreadJsObj.reLoadRowData(info.sheet, info.row);
  333. return;
  334. }
  335. break;
  336. default:
  337. if (col.type === 'Number') {
  338. data.postData[col.field] = _.toNumber(validText) || 0;
  339. } else {
  340. data.postData[col.field] = validText || '';
  341. }
  342. break;
  343. }
  344. postData('update', data, function (result) {
  345. payEvent.reloadPays(result.reload);
  346. }, function () {
  347. SpreadJsObj.reLoadRowData(info.sheet, info.row);
  348. });
  349. },
  350. selectionChanged: function(e, info) {
  351. if (info.newSelections) {
  352. if (!info.oldSelections || info.newSelections[0].row !== info.oldSelections[0].row) {
  353. payEvent.refreshActn();
  354. }
  355. }
  356. payEvent.loadExprToInput();
  357. },
  358. baseOpr: function(type) {
  359. const self = this;
  360. const node = SpreadJsObj.getSelectObject(sheet);
  361. if (type === 'delete') {
  362. postData('update', { postType: 'delete', postData: { id: node.tree_id }}, function(result) {
  363. payEvent.reloadPays(result.reload);
  364. });
  365. } else {
  366. postData('update', { postType: type, postData: { id: node.tree_id }}, function (result) {
  367. const refreshData = payTree.loadPostData(result);
  368. payEvent.refreshTree(refreshData);
  369. const sel = sheet.getSelections()[0];
  370. if (sel) {
  371. if (['up-move', 'down-move'].indexOf(type) > -1) {
  372. sheet.setSelection(payTree.getNodeIndex(node), sel.col, sel.rowCount, sel.colCount);
  373. SpreadJsObj.reloadRowsBackColor(sheet, [sel.row, payTree.getNodeIndex(node)]);
  374. } else if (type === 'add') {
  375. sheet.setSelection(payTree.getNodeIndex(refreshData.create[0]), sel.col, sel.rowCount, sel.colCount);
  376. SpreadJsObj.reloadRowsBackColor(sheet, [sel.row, payTree.getNodeIndex(refreshData.create[0])]);
  377. }
  378. }
  379. self.refreshActn(sheet);
  380. });
  381. }
  382. },
  383. reloadPays: function(datas){
  384. payTree.loadDatas(datas);
  385. SpreadJsObj.loadSheetData(sheet, SpreadJsObj.DataType.Tree, payTree);
  386. payEvent.refreshActn();
  387. }
  388. };
  389. spread.bind(spreadNS.Events.SelectionChanged, payEvent.selectionChanged);
  390. if (!readOnly) {
  391. spread.bind(spreadNS.Events.EditStarting, payEvent.editStarting);
  392. spread.bind(spreadNS.Events.EditEnded, payEvent.editEnded);
  393. $('a[name="base-opr"]').click(function () {
  394. payEvent.baseOpr(this.getAttribute('type'));
  395. });
  396. }
  397. return { spread, sheet, payTree, loadDatas: payEvent.reloadPays }
  398. })();
  399. payObj.loadDatas(details);
  400. // todo 加载审批列表
  401. $.subMenu({
  402. menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
  403. toMenu: '#to-menu', toMiniMenu: '#to-mini-menu',
  404. key: 'menu.1.0.0',
  405. miniHint: '#sub-mini-hint', hintKey: 'menu.hint.1.0.1',
  406. callback: function (info) {
  407. if (info.mini) {
  408. $('.panel-title').addClass('fluid');
  409. $('#sub-menu').removeClass('panel-sidebar');
  410. } else {
  411. $('.panel-title').removeClass('fluid');
  412. $('#sub-menu').addClass('panel-sidebar');
  413. }
  414. autoFlashHeight();
  415. payObj.spread.refresh();
  416. }
  417. });
  418. });