phase_pay_detail.js 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992
  1. 'use strict';
  2. $(document).ready(() => {
  3. const payUtils = {
  4. tips: {
  5. name: function(data) {
  6. const tips = [];
  7. if (data) {
  8. if (data.pause) tips.push('当前项已停用');
  9. if (!data.is_yf) tips.push('当前项不参与本期应付计算');
  10. }
  11. return tips.join('<br/>');
  12. },
  13. range_tp: function (data) {
  14. if (!data || (!data.range_expr && !data.range_tp) || !data.dl_type) return '';
  15. if (data.dl_type === 1) {
  16. return '计提期限为(当 计量期数 ≥ ' + data.dl_count + ')';
  17. } else if (data.dl_type === 2) {
  18. switch (data.dl_tp_type) {
  19. case 'contract':
  20. return '计提期限为(累计合同计量 ≥ ' + data.dl_tp + ')';
  21. case 'qc':
  22. return '计提期限为(累计变更计量 ≥ ' + data.dl_tp + ')';
  23. case 'gather':
  24. return '计提期限为(累计完成计量 ≥ ' + data.dl_tp + ')';
  25. }
  26. }
  27. }
  28. },
  29. check: {
  30. isFixed: function(data) {
  31. return data.is_fixed;
  32. },
  33. isStarted: function (data) {
  34. return data.pre_used;
  35. },
  36. isYf: function(data) {
  37. return data.pay_type === 'bqyf';
  38. },
  39. isSf: function(data) {
  40. return data.pay_type === 'bqsf';
  41. },
  42. isGatherValid: function(data) {
  43. return !data.pay_type && (!data.children || data.children.length === 0);
  44. },
  45. isOwner: function(data) {
  46. return data.create_user_id === userID;
  47. },
  48. isFinish: function(data) {
  49. return data.pre_finish;
  50. },
  51. isYB: function() {
  52. return userID === phasePay.create_user_id;
  53. },
  54. isOld: function(data) {
  55. return data.phase_id !== data.create_phase_id;
  56. },
  57. isLock: function (data) {
  58. const result = !!lockPayExpr && payUtils.check.isStarted(data) && payCalc.hasBase(data.expr);
  59. return result;
  60. },
  61. tpReadOnly: function(data) {
  62. return payUtils.check.isYf(data) || payUtils.check.isLock(data);
  63. },
  64. startTpReadOnly: function(data) {
  65. if (payUtils.check.isOld(data)) {
  66. return payUtils.check.isStarted(data) || !payUtils.check.isYB(data) || payUtils.check.isLock(data);
  67. } else {
  68. return payUtils.check.isWC(data) || payUtils.check.isSF(data) || payUtils.check.isYf(data) || !(payUtils.check.isOwner(data) || payUtils.check.isYB());
  69. }
  70. }
  71. },
  72. menuVisible: {
  73. pause: function (data) {
  74. if (payUtils.check.isOld(data)) {
  75. return payUtils.check.isYB();
  76. } else {
  77. return payUtils.check.isOwner(data) || payUtils.check.isYB();
  78. }
  79. },
  80. deadline: function (data) {
  81. if (payUtils.check.isOld(data)) {
  82. return !payUtils.check.isFinish(data) && payUtils.check.isYB();
  83. } else {
  84. return payUtils.check.isOwner(data) || payUtils.check.isYB();
  85. }
  86. }
  87. },
  88. };
  89. const payCalc = (function (b, a) {
  90. class PayCalc {
  91. constructor (bases, add) {
  92. this.percentReg = /((\d+)|((\d+)(\.\d+)))%/g;
  93. this.bases = bases;
  94. this.bases.sort(function (a, b) {
  95. return a.sort - b.sort;
  96. });
  97. for (const b of this.bases) {
  98. b.reg = new RegExp(b.code, 'igm');
  99. }
  100. this.addBase = add;
  101. this.orderReg = /f\d+/ig;
  102. this.nodeReg = /<<[a-z0-9\-]+>>/ig;
  103. }
  104. hasBase(expr) {
  105. if (!expr) return false;
  106. for (const b of this.bases) {
  107. if (data.expr.indexOf(b.code) >= 0) return true;
  108. }
  109. return false;
  110. }
  111. trans2OrderExpr(expr, payTree) {
  112. const nodeParam = expr.match(this.nodeReg);
  113. if (nodeParam) {
  114. for (const op of nodeParam) {
  115. const id = op.substring(2, op.length - 2);
  116. const payNode = payTree.nodes.find(x => { return x.uuid === id; });
  117. expr = expr.replace(op, payNode ? `f${payTree.getNodeIndex(payNode) + 1}` || '' : 0);
  118. }
  119. }
  120. return expr;
  121. }
  122. trans2NodeExpr(expr, payTree) {
  123. const orderParam = expr.match(this.orderReg);
  124. if (orderParam) {
  125. for (const op of orderParam) {
  126. const order = parseInt(op.substring(1, op.length));
  127. const payNode = payTree.nodes[order - 1];
  128. expr = expr.replace(op, payNode ? `<<${payNode.uuid}>>` || '' : 0);
  129. }
  130. }
  131. return expr;
  132. }
  133. checkExprValid(expr, invalidParam, selfId, payTree) {
  134. if (!expr) return [true, ''];
  135. const param = [];
  136. let num = '', base = '';
  137. let fixedIdParam;
  138. for (let i = 0, iLen = expr.length; i < iLen; i++) {
  139. const subExpr = expr.substring(i, expr.length);
  140. if (/^[\d\.%]+/.test(expr[i])) {
  141. if (base !== '') {
  142. param.push({type: 'base', value: base});
  143. base = '';
  144. }
  145. num = num + expr[i];
  146. } else if (this.nodeReg.test(subExpr)) {
  147. if (num !== '') {
  148. param.push({type: 'num', value: num});
  149. num = '';
  150. }
  151. if (base !== '') {
  152. param.push({type: 'base', value: base});
  153. base = '';
  154. }
  155. // const node = this.nodeReg.exec(subExpr);
  156. const node = subExpr.match(this.nodeReg);
  157. param.push({type: 'node', value: node[0]});
  158. i = i + node[0].length - 1;
  159. } else if (/^[a-z]/.test(expr[i])) {
  160. if (num !== '') {
  161. param.push({type: 'num', value: num});
  162. num = '';
  163. }
  164. base = base + expr[i];
  165. } else if (expr[i] === '(') {
  166. if (num !== '') {
  167. param.push({type: 'num', value: num});
  168. num = '';
  169. }
  170. if (base !== '') {
  171. param.push({type: 'base', value: base});
  172. base = '';
  173. }
  174. param.push({type: 'left', value: '('});
  175. } else if (expr[i] === ')') {
  176. if (num !== '') {
  177. param.push({type: 'num', value: num});
  178. num = '';
  179. }
  180. if (base !== '') {
  181. param.push({type: 'base', value: base});
  182. base = '';
  183. }
  184. param.push({type: 'right', value: ')'});
  185. } else if (/^[\+\-*\/]/.test(expr[i])) {
  186. if (num !== '') {
  187. param.push({type: 'num', value: num});
  188. num = '';
  189. }
  190. if (base !== '') {
  191. param.push({type: 'base', value: base});
  192. base = '';
  193. }
  194. param.push({type: 'calc', value: expr[i]});
  195. } else {
  196. return [false, '输入的表达式含有非法字符: ' + expr[i]];
  197. }
  198. }
  199. if (num !== '') {
  200. param.push({type: 'num', value: num});
  201. num = '';
  202. }
  203. if (base !== '') {
  204. param.push({type: 'base', value: base});
  205. base = '';
  206. }
  207. if (param.length === 0) return [true, ''];
  208. if (param.length > 1) {
  209. if (param[0].value === '-' && param[1].type === 'num') {
  210. param[1].value = '-' + param[1].value;
  211. param.shift();
  212. }
  213. }
  214. const iLen = param.length;
  215. let iLeftCount = 0, iRightCount = 0;
  216. for (const [i, p] of param.entries()) {
  217. if (p.type === 'calc') {
  218. if (i === 0 || i === iLen - 1)
  219. return [false, '输入的表达式非法:计算符号' + p.value + '前后应有数字或计算基数'];
  220. }
  221. if (p.type === 'num') {
  222. num = p.value.replace('%', '');
  223. if (p.value.length - num.length > 1)
  224. return [false, '输入的表达式非法:' + p.value + '不是一个有效的数字'];
  225. num = _.toNumber(num);
  226. if (num === undefined || num === null || _.isNaN(num))
  227. return [false, '输入的表达式非法:' + p.value + '不是一个有效的数字'];
  228. if (i > 0) {
  229. if (param[i - 1].type !== 'calc' && param[i - 1].type !== 'left') {
  230. return [false, '输入的表达式非法:' + p.value + '前应有运算符'];
  231. } else if (param[i - 1].value === '/' && num === 0) {
  232. return [false, '输入的表达式非法:请勿除0'];
  233. }
  234. }
  235. }
  236. if (p.type === 'base') {
  237. const baseParam = _.find(calcBase, {code: p.value});
  238. if (!baseParam)
  239. return [false, '输入的表达式非法:不存在计算基数' + p.value];
  240. if (invalidParam && invalidParam.indexOf(p.value) >= 0)
  241. return [false, '不可使用计算基数' + p.value];
  242. if (i > 0 && (param[i - 1].type === 'num' || param[i - 1].type === 'right'))
  243. return [false, '输入的表达式非法:' + p.value + '前应有运算符'];
  244. }
  245. if (p.type === 'node') {
  246. if (!selfId) return [false, '输入的表达式错误:不支持行号引用'];
  247. if ([`<<${selfId}>>`].indexOf(p.value) >= 0) return [false, '输入的表达式非法:请勿引用自己'];
  248. if (!fixedIdParam) {
  249. fixedIdParam = payTree.nodes.filter(x => { return x.is_fixed; }).map(x => { return `<<${x.uuid}>>`});
  250. }
  251. if (fixedIdParam.indexOf(p.value) >= 0) return [false, '输入的表达式非法:请勿引用固定项'];
  252. }
  253. if (p.type === 'left') {
  254. iLeftCount += 1;
  255. if (i !== 0 && param[i-1].type !== 'calc')
  256. return [false, '输入的表达式非法:(前应有运算符'];
  257. }
  258. if (p.type === 'right') {
  259. iRightCount += 1;
  260. if (i !== iLen - 1 && param[i+1].type !== 'calc')
  261. return [false, '输入的表达式非法:)后应有运算符'];
  262. if (iRightCount > iLeftCount)
  263. return [false, '输入的表达式非法:")"前无对应的"("'];
  264. }
  265. }
  266. if (iLeftCount > iRightCount)
  267. return [false, '输入的表达式非法:"("后无对应的")"'];
  268. if (selfId) {
  269. const circular = payCalc.checkCircularExpr(expr, selfId, payTree);
  270. // 当前循环计算不检查父项
  271. if (circular) return [false, '输入的表达式非法:循环引用'];
  272. }
  273. return [true, ''];
  274. }
  275. checkSfExpr(text, data, payNode, payTree) {
  276. if (text) {
  277. const num = _.toNumber(text);
  278. if (num) {
  279. data.expr = num;
  280. } else {
  281. const expr = this.trans2NodeExpr($.trim(text).replace('\t', '').replace('=', '').toLowerCase(), payTree);
  282. const [valid, msg] = this.checkExprValid(expr, [], payNode.uuid, payTree);
  283. if (!valid) return [valid, msg];
  284. data.expr = expr;
  285. }
  286. } else {
  287. data.tp = 0;
  288. data.expr = '';
  289. }
  290. return [true, ''];
  291. }
  292. checkExpr(text, data, payNode, payTree) {
  293. if (text) {
  294. const num = _.toNumber(text);
  295. if (num) {
  296. data.tp = num;
  297. data.expr = '';
  298. } else {
  299. const expr = this.trans2NodeExpr($.trim(text).replace('\t', '').replace('=', '').toLowerCase(), payTree);
  300. const [valid, msg] = this.checkExprValid(expr, ['bqyf'], payNode.uuid, payTree);
  301. if (!valid) return [valid, msg];
  302. data.expr = expr;
  303. data.tp = 0;
  304. }
  305. } else {
  306. data.tp = 0;
  307. data.expr = '';
  308. }
  309. return [true, ''];
  310. }
  311. checkRangeExpr(payNode, text, data) {
  312. if (!payNode) return [false, '数据错误'];
  313. const num = text ? _.toNumber(text) : 0;
  314. let expr = text ? (num ? '' : text) : '';
  315. expr = expr ? $.trim(expr).replace('\t', '').replace('=', '').toLowerCase() : '';
  316. const [valid, msg] = this.checkExprValid(expr, ['bqwc', 'ybbqwc', 'bqht', 'bqbg', 'bqyf']);
  317. if (!valid) return [valid, msg];
  318. if (payUtils.check.isStarted(payNode)) {
  319. if (payUtils.check.isSf(payNode)) {
  320. const value = expr ? payCalc.calculateExpr(expr) : num;
  321. if (payNode.pre_tp && value < payNode.pre_tp) return [false, '截止上期已计量' + payNode.pre_tp + ',扣款限额请勿少于改值'];
  322. data.range_tp = num;
  323. data.range_expr = expr;
  324. return [true, ''];
  325. } else {
  326. // if (payNode.pre_finish) return [false, '已达扣款限额,请勿修改'];
  327. // const value = expr ? payCalc.calculateExpr(expr) : num;
  328. // if (payNode.pre_tp && value < payNode.pre_tp) return [false, '截止上期已计量' + payNode.pre_tp + ',扣款限额请勿少于改值'];
  329. // data.range_tp = num;
  330. // data.range_expr = expr;
  331. return [false, '已经开始使用,请勿修改扣款限额'];
  332. }
  333. } else {
  334. data.range_tp = num;
  335. data.range_expr = expr;
  336. return [true, ''];
  337. }
  338. }
  339. checkStartExpr(payNode, text, data) {
  340. if (!payNode) return [false, '数据错误'];
  341. const num = text ? _.toNumber(text) : 0;
  342. let expr = text ? (num ? '' : text) : '';
  343. expr = expr ? $.trim(expr).replace('\t', '').replace('=', '').toLowerCase() : '';
  344. const [valid, msg] = this.checkExprValid(expr, ['bqwc', 'ybbqwc', 'bqht', 'bqbg', 'bqyf']);
  345. if (!valid) return [valid, msg];
  346. if (payUtils.check.isStarted(payNode)) {
  347. return [false, '已经开始计量,请勿修改起扣金额'];
  348. } else {
  349. if (this.addBase.pre_gather_tp) {
  350. const value = expr ? payCalc.calculateExpr(expr) : num;
  351. if (this.addBase.pre_gather_tp && value < this.addBase.pre_gather_tp)
  352. return [false, '起扣金额请勿少于本期完成截止上期计量金额' + this.addBase.pre_gather_tp];
  353. data.start_tp = num;
  354. data.start_expr = expr;
  355. return [true, ''];
  356. } else {
  357. data.start_tp = num;
  358. data.start_expr = expr;
  359. return [true, ''];
  360. }
  361. }
  362. }
  363. getExprInfo(field, converse = false) {
  364. const exprField = [
  365. {qty: 'tp', expr: 'expr'},
  366. {qty: 'start_tp', expr: 'start_expr'},
  367. {qty: 'range_qty', expr: 'range_expr'},
  368. ];
  369. if (converse) return _.find(exprField, { expr: field });
  370. return _.find(exprField, {qty: field});
  371. }
  372. getLeafOrder(data, parentReg, tree) {
  373. if (!data) return [];
  374. const defaultResult = data.uuid ? [`<<${data.uuid}>>`] : [];
  375. if (!data.expr) return defaultResult;
  376. const nodeParam = data.expr.match(this.nodeReg);
  377. if (!nodeParam || nodeParam.length === 0) return defaultResult;
  378. const result = [];
  379. for (const op of nodeParam) {
  380. const id = op.substring(2, op.length - 2);
  381. if (data.uuid === id || op === parentReg) {
  382. result.push(op);
  383. } else {
  384. const payNode = tree.nodes.find(x => {return x.uuid === id; });
  385. const subOrderParam = this.getLeafOrder(payNode, data.uuid ? `<<${data.uuid}>>` : parentReg, tree);
  386. result.push(...subOrderParam);
  387. }
  388. }
  389. return result;
  390. }
  391. checkCircularExpr(expr, selfId, tree) {
  392. const leafOrder = this.getLeafOrder({expr}, `<<${selfId}>>`, tree);
  393. if (leafOrder.indexOf(`<<${selfId}>>`) >= 0) return true;
  394. return false;
  395. }
  396. calculateExpr(expr) {
  397. let formula = expr;
  398. for (const b of this.bases) {
  399. formula = formula.replace(b.reg, b.value);
  400. }
  401. const percent = formula.match(this.percentReg);
  402. if (percent) {
  403. for (const p of percent) {
  404. const v = math.evaluate(p.replace(new RegExp('%', 'gm'), '/100'));
  405. formula = formula.replace(p, v);
  406. }
  407. }
  408. try {
  409. const value = ZhCalc.mathCalcExpr(formula);
  410. return value;
  411. } catch(err) {
  412. return 0;
  413. }
  414. }
  415. refreshBaseHtml() {
  416. const html = [];
  417. for (const [i, b] of this.bases.entries()) {
  418. html.push('<tr>');
  419. html.push(`<td>${i+1}</td><td>${b.name}</td><td>${b.code}</td>`);
  420. if (b.code === 'bqyf') {
  421. html.push('<td class="text-right">--</td>');
  422. } else {
  423. html.push(`<td class="text-right">${b.formatValue}</td>`);
  424. }
  425. html.push('</tr>');
  426. }
  427. $('#base-list').html(html.join(''));
  428. }
  429. reloadBase(bases, add) {
  430. this.bases = bases;
  431. this.refreshBaseHtml();
  432. this.bases.sort(function (a, b) {
  433. return a.sort - b.sort;
  434. });
  435. for (const b of this.bases) {
  436. b.reg = new RegExp(b.code, 'igm');
  437. }
  438. this.addBase = add;
  439. }
  440. }
  441. return new PayCalc(b, a);
  442. })(calcBase, addBase);
  443. const payObj = (function() {
  444. const spread = SpreadJsObj.createNewSpread($('#pay-spread')[0]);
  445. const sheet = spread.getActiveSheet();
  446. const spreadSetting = {
  447. cols: [
  448. {title: '名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 230, formatter: '@', cellType: 'tree', getTip: payUtils.tips.name},
  449. {title: '本期金额(F)', colSpan: '1', rowSpan: '1', field: 'tp', hAlign: 2, width: 100, type: 'Number'},
  450. {title: '截止上期金额', colSpan: '1', rowSpan: '1', field: 'pre_tp', hAlign: 2, width: 100, readOnly: true, type: 'Number'},
  451. {title: '截止本期金额', colSpan: '1', rowSpan: '1', field: 'end_tp', hAlign: 2, width: 100, readOnly: true, type: 'Number'},
  452. {title: '起扣金额', colSpan: '1', rowSpan: '1', field: 'start_tp', hAlign: 2, width: 100, type: 'Number'},
  453. {title: '付(扣)款限额', colSpan: '1', rowSpan: '1', field: 'range_tp', hAlign: 2, width: 100, cellType: 'tip', type: 'Number', getTip: payUtils.tips.range_tp},
  454. {title: '汇总', colSpan: '1', rowSpan: '1', field: 'is_gather', hAlign: 1, width: 60, cellType: 'signalCheckbox', show: payUtils.check.isGatherValid},
  455. {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'},
  456. {title: '本期批注', colSpan: '1', rowSpan: '1', field: 'postil', hAlign: 0, width: 120, formatter: '@', cellType: 'autoTip'},
  457. ],
  458. emptyRows: 0,
  459. headRows: 1,
  460. headRowHeight: [32],
  461. headColWidth: [30],
  462. defaultRowHeight: 21,
  463. headerFont: '12px 微软雅黑',
  464. font: '12px 微软雅黑',
  465. readOnly: readOnly,
  466. localCache: {
  467. key: 'phase-pay',
  468. colWidth: true,
  469. },
  470. pos: SpreadJsObj.getObjPos($('#pay-spread')[0]),
  471. };
  472. sjsSettingObj.setFxTreeStyle(spreadSetting, sjsSettingObj.FxTreeStyle.phasePay);
  473. SpreadJsObj.initSheet(sheet, spreadSetting);
  474. const payTree = createNewPathTree('base', {
  475. id: 'tree_id', pid: 'tree_pid', order: 'tree_order',
  476. level: 'tree_level', isLeaf: 'tree_is_leaf', fullPath: 'tree_full_path',
  477. rootId: -1,
  478. });
  479. const payEvent = {
  480. refreshActn: function() {
  481. const setObjEnable = function (obj, enable) {
  482. if (enable) {
  483. obj.removeClass('disabled');
  484. } else {
  485. obj.addClass('disabled');
  486. }
  487. };
  488. const select = SpreadJsObj.getSelectObject(sheet);
  489. if (!select) {
  490. setObjEnable($('a[name=base-opr][type=add]'), false);
  491. setObjEnable($('a[name=base-opr][type=del]'), false);
  492. setObjEnable($('a[name=base-opr][type=up-move]'), false);
  493. setObjEnable($('a[name=base-opr][type=down-move]'), false);
  494. return;
  495. }
  496. const preNode = payTree.getPreSiblingNode(select);
  497. setObjEnable($('a[name=base-opr][type=add]'), !readOnly && !payUtils.check.isSf(select) && !payUtils.check.isYf(select));
  498. const delValid = !payUtils.check.isFixed(select) && !payUtils.check.isStarted(select);
  499. setObjEnable($('a[name=base-opr][type=delete]'), !readOnly && delValid);
  500. setObjEnable($('a[name=base-opr][type=up-move]'), !readOnly && !payUtils.check.isFixed(select) && preNode);
  501. setObjEnable($('a[name=base-opr][type=down-move]'), !readOnly && !payUtils.check.isFixed(select) && !payTree.isLastSibling(select));
  502. },
  503. loadExprToInput: function() {
  504. const sel = sheet.getSelections()[0];
  505. const col = sheet.zh_setting.cols[sel.col];
  506. const data = SpreadJsObj.getSelectObject(sheet);
  507. if (data && (!data.children || data.children.length === 0)) {
  508. if (col.field === 'tp') {
  509. const expr = payCalc.trans2OrderExpr(data.expr, payTree);
  510. $('#pay-expr').val(expr).attr('field', 'expr').attr('org', expr)
  511. .attr('readOnly', readOnly|| payUtils.check.tpReadOnly(data));
  512. } else if (col.field === 'start_tp') {
  513. const expr = payCalc.trans2OrderExpr(data.start_expr, payTree) || data.start_tp;
  514. $('#pay-expr').val(expr).attr('field', 'start_expr').attr('org', expr)
  515. .attr('readOnly', readOnly|| payUtils.check.startTpReadOnly(data) || payUtils.check.isYf(data));
  516. } else if (col.field === 'range_tp') {
  517. const expr = payCalc.trans2OrderExpr(data.range_expr, payTree);
  518. $('#pay-expr').val(expr).attr('field', 'range_expr').attr('org', expr)
  519. .attr('readOnly', readOnly|| payUtils.check.rangeTpReadOnly(data) || payUtils.check.isYf(data));
  520. } else {
  521. $('#pay-expr').val('').attr('readOnly', true);
  522. }
  523. $('#pay-expr').attr('data-row', sel.row);
  524. } else {
  525. $('#pay-expr').val('').attr('readOnly', true);
  526. $('#pay-expr').removeAttr('data-row');
  527. }
  528. },
  529. refreshTree: function (data) {
  530. SpreadJsObj.massOperationSheet(sheet, function () {
  531. const tree = sheet.zh_tree;
  532. // 处理删除
  533. if (data.delete) {
  534. data.delete.sort(function (a, b) {
  535. return b.deleteIndex - a.deleteIndex;
  536. });
  537. for (const d of data.delete) {
  538. sheet.deleteRows(d.deleteIndex, 1);
  539. }
  540. }
  541. // 处理新增
  542. if (data.create) {
  543. const newNodes = data.create;
  544. if (newNodes) {
  545. newNodes.sort(function (a, b) {
  546. return a.index - b.index;
  547. });
  548. for (const node of newNodes) {
  549. sheet.addRows(node.index, 1);
  550. SpreadJsObj.reLoadRowData(sheet, tree.nodes.indexOf(node), 1);
  551. }
  552. }
  553. }
  554. // 处理更新
  555. if (data.update) {
  556. const rows = [];
  557. for (const u of data.update) {
  558. rows.push(tree.nodes.indexOf(u));
  559. }
  560. SpreadJsObj.reLoadRowsData(sheet, rows);
  561. }
  562. });
  563. },
  564. editStarting: function(e, info) {
  565. const col = info.sheet.zh_setting.cols[info.col];
  566. const select = SpreadJsObj.getSelectObject(info.sheet);
  567. switch (col.field) {
  568. case 'name':
  569. info.cancel = payUtils.check.isFixed(select);
  570. break;
  571. case 'tp':
  572. case 'is_gather':
  573. info.cancel = select.children && select.children.length > 0;
  574. break;
  575. case 'start_tp':
  576. case 'range_tp':
  577. info.cancel = (select.children && select.children.length > 0) || payUtils.check.isYf(select);
  578. break;
  579. case 'is_gather':
  580. info.cancel = true;
  581. break;
  582. }
  583. if (col.field === 'tp') {
  584. if (select.expr && select.expr !== '') {
  585. info.sheet.getCell(info.row, info.col).text(payCalc.trans2OrderExpr(select.expr, payTree));
  586. }
  587. } else if (col.field === 'start_tp') {
  588. if (select.start_expr && select.start_expr !== '') {
  589. info.sheet.getCell(info.row, info.col).text(select.start_expr);
  590. }
  591. } else if (col.field === 'range_tp') {
  592. if (select.range_expr && select.range_expr !== '') {
  593. info.sheet.getCell(info.row, info.col).text(select.range_expr);
  594. }
  595. }
  596. },
  597. editEnded: function(e, info) {
  598. if (!info.sheet.zh_setting) return;
  599. const select = SpreadJsObj.getSelectObject(info.sheet);
  600. const col = info.sheet.zh_setting.cols[info.col];
  601. if (col.field === 'is_gather') return;
  602. // 未改变值则不提交
  603. const validText = info.editingText ? info.editingText.replace('\n', '') : '';
  604. let orgValue;
  605. if (col.field === 'tp') {
  606. orgValue = select.expr ? payCalc.trans2OrderExpr(select.expr, payTree) : select.tp;
  607. } else if (col.field === 'start_tp') {
  608. orgValue = select.start_expr ? select.start_expr : select.start_tp;
  609. } else if (col.field === 'range_tp') {
  610. orgValue = select.range_expr ? select.range_expr : select.range_tp;
  611. } else {
  612. orgValue = select[col.field];
  613. }
  614. orgValue = orgValue || '';
  615. if (orgValue == validText) {
  616. SpreadJsObj.reLoadRowData(info.sheet, info.row);
  617. return;
  618. }
  619. const data = { postType: 'update', postData: { id: select.id } };
  620. switch(col.field) {
  621. case 'tp':
  622. const [tpValid, tpMsg] = payUtils.check.isSf(select)
  623. ? payCalc.checkSfExpr(validText, data.postData, select, payTree)
  624. : payCalc.checkExpr(validText, data.postData, select, payTree);
  625. if (!tpValid) {
  626. toastr.warning(tpMsg);
  627. SpreadJsObj.reLoadRowData(info.sheet, info.row);
  628. return;
  629. }
  630. break;
  631. case 'start_tp':
  632. const [sValid, sMsg] = payCalc.checkStartExpr(select, validText, data.postData);
  633. if (!sValid) {
  634. toastr.warning(sMsg);
  635. SpreadJsObj.reLoadRowData(info.sheet, info.row);
  636. return;
  637. }
  638. break;
  639. case 'range_tp':
  640. const [rValid, rMsg] = payCalc.checkRangeExpr(select, validText, data.postData);
  641. if (!rValid) {
  642. toastr.warning(rMsg);
  643. SpreadJsObj.reLoadRowData(info.sheet, info.row);
  644. return;
  645. }
  646. break;
  647. default:
  648. if (col.type === 'Number') {
  649. data.postData[col.field] = _.toNumber(validText) || 0;
  650. } else {
  651. data.postData[col.field] = validText || '';
  652. }
  653. break;
  654. }
  655. postData('update', data, function (result) {
  656. if (result.reload) {
  657. payEvent.reloadPays(result.reload);
  658. } else {
  659. const refreshData = payTree.loadPostData(result);
  660. payEvent.refreshTree(refreshData);
  661. }
  662. }, function () {
  663. SpreadJsObj.reLoadRowData(info.sheet, info.row);
  664. });
  665. },
  666. selectionChanged: function(e, info) {
  667. if (info.newSelections) {
  668. if (!info.oldSelections || info.newSelections[0].row !== info.oldSelections[0].row) {
  669. payEvent.refreshActn();
  670. }
  671. }
  672. payEvent.loadExprToInput();
  673. },
  674. buttonClicked: function (e, info) {
  675. if (!info.sheet.zh_setting) return;
  676. const select = SpreadJsObj.getSelectObject(info.sheet);
  677. const col = info.sheet.zh_setting.cols[info.col];
  678. if (col.field !== 'is_gather') return;
  679. if (!payUtils.check.isGatherValid(select)) return;
  680. if (info.sheet.isEditing()) info.sheet.endEdit(true);
  681. const data = {
  682. postType: 'update',
  683. postData: { id: select.id },
  684. };
  685. data.postData[col.field] = info.sheet.getValue(info.row, info.col) || 0;
  686. // 更新至服务器
  687. postData('update', data, function (result) {
  688. if (result.reload) payEvent.reloadPays(result.reload);
  689. });
  690. },
  691. deletePress: function (sheet) {
  692. if (!sheet.zh_setting) return;
  693. const sel = sheet.getSelections()[0];
  694. if (!sel) return;
  695. const col = sheet.zh_setting.cols[sel.col];
  696. if (col.readOnly === true) return;
  697. if (sel.colCount > 1) toastr.warning('请勿同时删除多列数据');
  698. const data = { postType: 'update', postData: [] };
  699. for (let iRow = sel.row; iRow < sel.row + sel.rowCount; iRow ++) {
  700. const node = sheet.zh_tree.nodes[iRow];
  701. if (node && (!node.pay_type || col.field === 'postil')) {
  702. const updateData = { id: node.id };
  703. switch(col.field) {
  704. case 'tp':
  705. updateData.tp = 0;
  706. updateData.expr = '';
  707. break;
  708. case 'start_tp':
  709. updateData.start_tp = 0;
  710. updateData.start_expr = '';
  711. break;
  712. case 'range_tp':
  713. updateData.range_tp = 0;
  714. updateData.range_expr = '';
  715. break;
  716. case 'is_gather':
  717. updateData.is_gather = 0;
  718. break;
  719. default:
  720. updateData[col.field] = col.type === 'Number' ? 0 : '';
  721. }
  722. data.postData.push(updateData);
  723. }
  724. }
  725. postData('update', data, function (result) {
  726. if (result.reload) {
  727. payEvent.reloadPays(result.reload);
  728. } else {
  729. const refreshData = payTree.loadPostData(result);
  730. payEvent.refreshTree(refreshData);
  731. }
  732. }, function () {
  733. SpreadJsObj.reLoadRowData(sheet, sel.row, sel.rowCount);
  734. });
  735. },
  736. baseOpr: function(type) {
  737. const self = this;
  738. const node = SpreadJsObj.getSelectObject(sheet);
  739. if (type === 'delete') {
  740. postData('update', { postType: 'delete', postData: { id: node.tree_id }}, function(result) {
  741. payEvent.reloadPays(result.reload);
  742. });
  743. } else {
  744. postData('update', { postType: type, postData: { id: node.tree_id }}, function (result) {
  745. const refreshData = payTree.loadPostData(result);
  746. payEvent.refreshTree(refreshData);
  747. const sel = sheet.getSelections()[0];
  748. if (sel) {
  749. if (['up-move', 'down-move'].indexOf(type) > -1) {
  750. sheet.setSelection(payTree.getNodeIndex(node), sel.col, sel.rowCount, sel.colCount);
  751. SpreadJsObj.reloadRowsBackColor(sheet, [sel.row, payTree.getNodeIndex(node)]);
  752. } else if (type === 'add') {
  753. sheet.setSelection(payTree.getNodeIndex(refreshData.create[0]), sel.col, sel.rowCount, sel.colCount);
  754. SpreadJsObj.reloadRowsBackColor(sheet, [sel.row, payTree.getNodeIndex(refreshData.create[0])]);
  755. }
  756. }
  757. self.refreshActn(sheet);
  758. });
  759. }
  760. },
  761. calculateAll: function() {
  762. postData('update', { postType: 'calc', postData: {}}, function (result) {
  763. payEvent.reloadPays(result.reload);
  764. });
  765. },
  766. reloadCalcBase: function() {
  767. postData('update', { postType: 'refreshBase', postData: {}}, function (result) {
  768. payEvent.reloadPays(result.reload);
  769. payCalc.reloadBase(result.calcBase, result.addBase);
  770. });
  771. },
  772. reloadPays: function(datas){
  773. payTree.loadDatas(datas);
  774. SpreadJsObj.loadSheetData(sheet, SpreadJsObj.DataType.Tree, payTree);
  775. payEvent.refreshActn();
  776. }
  777. };
  778. spread.bind(spreadNS.Events.SelectionChanged, payEvent.selectionChanged);
  779. if (!readOnly) {
  780. spread.bind(spreadNS.Events.EditStarting, payEvent.editStarting);
  781. spread.bind(spreadNS.Events.EditEnded, payEvent.editEnded);
  782. spread.bind(spreadNS.Events.ButtonClicked, payEvent.buttonClicked);
  783. SpreadJsObj.addDeleteBind(spread, payEvent.deletePress);
  784. $('a[name="base-opr"]').click(function () {
  785. payEvent.baseOpr(this.getAttribute('type'));
  786. });
  787. $('#calc-all').click(function() {
  788. payEvent.calculateAll();
  789. });
  790. $('#reload-calc-base').click(function() {
  791. payEvent.reloadCalcBase();
  792. });
  793. const deadlineObj = {
  794. payNode: null,
  795. refreshHint: function() {
  796. const dlType = $('[name=dl-type]:checked').val();
  797. const dt = deadlineType[dlType];
  798. if (dlType && dt) {
  799. const dlValue = $('#dl-value').val();
  800. $('#range-hint').text(`当 ${dt.name} >= ${dlValue} 时 `);
  801. $('#dl-hint').show();
  802. } else {
  803. $('#dl-hint').hide();
  804. }
  805. },
  806. initView: function(data) {
  807. this.payNode = data;
  808. $('#dl-pay-name').html(data.name);
  809. // 模式
  810. if (data.dl_type) {
  811. $('[name=dl-type][value=' + data.dl_type +']')[0].checked = true;
  812. } else {
  813. $('#dl-type-none')[0].checked = true;
  814. }
  815. $('#dl-value').val(data.dl_value);
  816. this.refreshHint();
  817. },
  818. getDlCount: function() {
  819. try {
  820. const result = parseInt($('#dl-value').val());
  821. if (result <= 0) throw '限制值请输入正整数';
  822. return result;
  823. } catch (err) {
  824. toastr.warning('限制值请输入正整数');
  825. return 0;
  826. }
  827. },
  828. getDlTp: function() {
  829. try {
  830. const result = parseFloat($('#dl-value').val());
  831. return result;
  832. } catch (err) {
  833. toastr.warning('限制值请输入数值');
  834. return 0;
  835. }
  836. },
  837. getUpdateData: function() {
  838. const result = { postType: 'update', postData: { id: this.payNode.id } };
  839. result.postData.dl_type = $('[name=dl-type]:checked').val();
  840. if (result.postData.dl_type) {
  841. if (result.postData.dl_type === deadlineType.phaseCount.key) {
  842. result.postData.dl_value = this.getDlCount();
  843. if (result.postData.dl_value < phasePay.phase_order) {
  844. toastr.warning(`已计量至第${phasePay.phase_order}期,计提期限不可小于该期`);
  845. return null;
  846. }
  847. } else if (result.postData.dl_type === deadlineType.stageCount.key) {
  848. result.postData.dl_value = this.getDlCount();
  849. if (result.postData.dl_value < maxStageOrder) {
  850. toastr.warning(`已计量至第${maxStageOrder}期,计提期限不可小于该期`);
  851. return null;
  852. }
  853. } else {
  854. result.postData.dl_value = this.getDlTp();
  855. const dt = deadlineType[result.postData.dl_type];
  856. if (!dt) {
  857. toastr.warning('限制模式错误,请刷新页面重试');
  858. return null;
  859. }
  860. const compareValue = payCalc.addBase[`pre_${dt.key}_tp`];
  861. if (result.postData.dl_type < compareValue) {
  862. toastr.warning(`截止上期,${dt.name}已计量${compareValue},计提期限不可小于该值`);
  863. return null;
  864. }
  865. }
  866. } else {
  867. result.postData.dl_value = 0;
  868. }
  869. return result;
  870. },
  871. };
  872. $('[name=dl-type]').change(deadlineObj.refreshHint);
  873. $('#dl-value').change(deadlineObj.refreshHint);
  874. $('#deadline-ok').click(function() {
  875. const updateData = deadlineObj.getUpdateData();
  876. if (!updateData) return;
  877. postData('update', updateData, function(result) {
  878. payEvent.reloadPays(result.reload);
  879. $('#deadline').modal('hide');
  880. });
  881. });
  882. // 右键菜单
  883. $.contextMenu({
  884. selector: '#pay-spread',
  885. build: function ($trigger, e) {
  886. const target = SpreadJsObj.safeRightClickSelection($trigger, e, spread);
  887. return target.hitTestType === GC.Spread.Sheets.SheetArea.viewport || target.hitTestType === GC.Spread.Sheets.SheetArea.rowHeader;
  888. },
  889. items: {
  890. 'start': {
  891. name: '启用',
  892. icon: 'fa-play',
  893. callback: function (key, opt) {
  894. const select = SpreadJsObj.getSelectObject(sheet);
  895. const data = {
  896. postType: 'update',
  897. postData: { id: select.id, is_pause: 0 }
  898. };
  899. // 更新至服务器
  900. postData('update', data, function (result) {
  901. payEvent.reloadPays(result.reload);
  902. });
  903. },
  904. visible: function (key, opt) {
  905. const select = SpreadJsObj.getSelectObject(sheet);
  906. return (!select.children || select.children.length ===0) && !readOnly && select.is_pause && payUtils.menuVisible.pause(select);
  907. }
  908. },
  909. 'stop': {
  910. name: '停用',
  911. icon: 'fa-pause',
  912. callback: function (key, opt) {
  913. const select = SpreadJsObj.getSelectObject(sheet);
  914. const data = {
  915. postType: 'update',
  916. postData: { id: select.id, is_pause: 1 }
  917. };
  918. // 更新至服务器
  919. postData('update', data, function (result) {
  920. payEvent.reloadPays(result.reload);
  921. });
  922. },
  923. visible: function (key, opt) {
  924. const select = SpreadJsObj.getSelectObject(sheet);
  925. return (!select.children || select.children.length === 0) && !readOnly && !select.is_pause && payUtils.menuVisible.pause(select);
  926. },
  927. },
  928. 'setDeadline': {
  929. name: '设置计提期限',
  930. icon: 'fa-clipboard',
  931. callback: function (key, opt) {
  932. const select = SpreadJsObj.getSelectObject(sheet);
  933. if (select.range_tp) {
  934. deadlineObj.initView(select);
  935. $('#deadline').modal('show');
  936. } else {
  937. toastr.warning('计提期限用于达到条件时,即刻计量至付(扣)款限额,应先设置付(扣)款限额');
  938. }
  939. },
  940. visible: function (key, opt) {
  941. const select = SpreadJsObj.getSelectObject(sheet);
  942. return (!select.children || select.children.length === 0) && !readOnly && payUtils.menuVisible.deadline(select);
  943. }
  944. },
  945. }
  946. });
  947. }
  948. return { spread, sheet, payTree, loadDatas: payEvent.reloadPays }
  949. })();
  950. payObj.loadDatas(details);
  951. // todo 加载审批列表
  952. $.subMenu({
  953. menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
  954. toMenu: '#to-menu', toMiniMenu: '#to-mini-menu',
  955. key: 'menu.1.0.0',
  956. miniHint: '#sub-mini-hint', hintKey: 'menu.hint.1.0.1',
  957. callback: function (info) {
  958. if (info.mini) {
  959. $('.panel-title').addClass('fluid');
  960. $('#sub-menu').removeClass('panel-sidebar');
  961. } else {
  962. $('.panel-title').removeClass('fluid');
  963. $('#sub-menu').addClass('panel-sidebar');
  964. }
  965. autoFlashHeight();
  966. payObj.spread.refresh();
  967. }
  968. });
  969. });