schedule_plan.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  1. /**
  2. * 进度台账相关js
  3. *
  4. * @author Ellisran
  5. * @date 2020/11/6
  6. * @version
  7. */
  8. function getTenderId() {
  9. return window.location.pathname.split('/')[2];
  10. }
  11. $(function () {
  12. autoFlashHeight();
  13. if(schedule && !schedule.mode) {
  14. $('#mode').modal('show');
  15. }
  16. // 初始化台账
  17. const ledgerSpread = SpreadJsObj.createNewSpread($('#ledger-spread')[0]);
  18. const treeSetting = {
  19. id: 'ledger_id',
  20. pid: 'ledger_pid',
  21. order: 'order',
  22. level: 'level',
  23. rootId: -1,
  24. fullPath: 'full_path',
  25. //treeCacheKey: 'ledger_bills_fold' + '_' + getTenderId(),
  26. // markFoldKey: 'bills-fold',
  27. // markFoldSubKey: window.location.pathname.split('/')[2],
  28. };
  29. const ledgerTree = createNewPathTree('filter', treeSetting);
  30. const static_cols = [
  31. {title: '编号', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 145, formatter: '@', readOnly: true, cellType: 'tree'},
  32. {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 185, formatter: '@', readOnly: true},
  33. {title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 50, formatter: '@', readOnly: true},
  34. {title: '经济指标', colSpan: '1', rowSpan: '2', field: 'dgn_price', hAlign: 2, width: 60, type: 'Number', readOnly: true},
  35. {title: '总设计|工程量', colSpan: '2|1', rowSpan: '1|1', field: 'dgn_qty1', hAlign: 2, width: 70, type: 'Number', readOnly: true},
  36. {title: '|金额(万元)', colSpan: '|1', rowSpan: '|1', field: 'total_price', hAlign: 2, width: 70, type: 'Number', readOnly: true},
  37. ];
  38. const ledgerSpreadSetting = {
  39. emptyRows: 0,
  40. headRows: 2,
  41. headRowHeight: [25, 25],
  42. defaultRowHeight: 21,
  43. headerFont: '12px 微软雅黑',
  44. font: '12px 微软雅黑',
  45. // readOnly: true,
  46. localCache: {
  47. key: 'ledger-bills',
  48. colWidth: true,
  49. }
  50. };
  51. const monthsCols = [];
  52. if(scheduleMonth.length > 0) {
  53. for (const sm of scheduleMonth) {
  54. const readOnly = sm.sj_gcl !== null || sm.sj_tp !== null;
  55. const yearmonth = sm.yearmonth.split('-')[0] + '年' + parseInt(sm.yearmonth.split('-')[1]) + '月';
  56. const cols = {title: yearmonth + '|计划工程量', colSpan: '2|1', rowSpan: '1|1', field: sm.yearmonth+'_gcl', hAlign: 2, width: 90, type: 'Number', readOnly: readOnly ? readOnly : 'readOnly.gcl'};
  57. const cols2 = {title: '|计划金额(万元)', colSpan: '|1', rowSpan: '|1', field: sm.yearmonth+'_tp', hAlign: 2, width: 90, type: 'Number', readOnly: readOnly ? readOnly : 'readOnly.tp'};
  58. monthsCols.push(cols);
  59. monthsCols.push(cols2);
  60. }
  61. }
  62. const spreadHeaderCols = static_cols.concat(monthsCols);
  63. ledgerSpreadSetting.cols = spreadHeaderCols;
  64. const ledgerCol = {
  65. readOnly: {
  66. tp: function (data) {
  67. let flag = data.is_leaf;
  68. if (data.is_leaf) {
  69. flag = schedule && schedule.mode === mode.tp;
  70. }
  71. return !flag;
  72. },
  73. gcl: function (data) {
  74. let flag = data.is_leaf;
  75. if (data.is_leaf) {
  76. flag = schedule && schedule.mode === mode.gcl;
  77. }
  78. return !flag;
  79. },
  80. },
  81. };
  82. sjsSettingObj.setFxTreeStyle(ledgerSpreadSetting, sjsSettingObj.FxTreeStyle.jz);
  83. if (thousandth) sjsSettingObj.setTpThousandthFormat(ledgerSpreadSetting);
  84. SpreadJsObj.initSpreadSettingEvents(ledgerSpreadSetting, ledgerCol);
  85. SpreadJsObj.initSheet(ledgerSpread.getActiveSheet(), ledgerSpreadSetting);
  86. SpreadJsObj.selChangedRefreshBackColor(ledgerSpread.getActiveSheet());
  87. postData('/tender/' + getTenderId() + '/schedule/ledger/load', {}, function (data) {
  88. // let treeData = [];
  89. // for(const sl of selectedLedgerList) {
  90. // const one = _.find(data, { 'ledger_id' : sl });
  91. // treeData.push(one);
  92. // }
  93. // treeData = setLeafData(treeData);
  94. // console.log(treeData);
  95. // let treeData = data;
  96. const baseLedgerTree = createNewPathTree('base', {
  97. id: 'ledger_id',
  98. pid: 'ledger_pid',
  99. order: 'order',
  100. level: 'level',
  101. rootId: -1,
  102. fullPath: 'full_path',
  103. calcFields: ['total_price'],
  104. calcFun: function (node) {
  105. node.dgn_price = ZhCalc.round(ZhCalc.div(node.total_price, node.dgn_qty1), 2);
  106. }
  107. });
  108. baseLedgerTree.loadDatas(data);
  109. treeCalc.calculateAll(baseLedgerTree);
  110. for (const d of baseLedgerTree.nodes) {
  111. if (!d.b_code) {
  112. const one = _.find(selectedLedgerList, function (item) {
  113. return item === d.ledger_id;
  114. });
  115. if(one) {
  116. ledgerTree.addData(d, ['ledger_id', 'ledger_pid', 'order', 'level', 'tender_id', 'full_path',
  117. 'code', 'name', 'unit', 'dgn_qty1', 'dgn_qty2', 'dgn_price', 'quantity', 'total_price']);
  118. }
  119. }
  120. }
  121. ledgerTree.sortTreeNode(true);
  122. // console.log(ledgerTree);
  123. SpreadJsObj.loadSheetData(ledgerSpread.getActiveSheet(), SpreadJsObj.DataType.Tree, ledgerTree);
  124. }, null, true);
  125. const ledgerSpreadObj = {
  126. refreshTree: function (sheet, data) {
  127. SpreadJsObj.massOperationSheet(sheet, function () {
  128. const tree = sheet.zh_tree;
  129. // 处理删除
  130. if (data.delete) {
  131. data.delete.sort(function (x, y) {
  132. return y.deleteIndex - x.deleteIndex;
  133. });
  134. for (const d of data.delete) {
  135. sheet.deleteRows(d.deleteIndex, 1);
  136. }
  137. }
  138. // 处理新增
  139. if (data.create) {
  140. const newNodes = data.create;
  141. if (newNodes) {
  142. newNodes.sort(function (a, b) {
  143. return a.index - b.index;
  144. });
  145. for (const node of newNodes) {
  146. sheet.addRows(node.index, 1);
  147. SpreadJsObj.reLoadRowData(sheet, tree.nodes.indexOf(node), 1);
  148. }
  149. }
  150. }
  151. // 处理更新
  152. if (data.update) {
  153. const rows = [];
  154. for (const u of data.update) {
  155. rows.push(tree.nodes.indexOf(u));
  156. }
  157. SpreadJsObj.reLoadRowsData(sheet, rows);
  158. }
  159. // 处理展开
  160. if (data.expand) {
  161. const expanded = [];
  162. for (const e of data.expand) {
  163. if (expanded.indexOf(e) === -1) {
  164. const posterity = tree.getPosterity(e);
  165. for (const p of posterity) {
  166. sheet.setRowVisible(tree.nodes.indexOf(p), p.visible);
  167. expanded.push(p);
  168. }
  169. }
  170. }
  171. }
  172. });
  173. },
  174. editEnded: function (e, info) {
  175. if (info.sheet.zh_setting) {
  176. const select = SpreadJsObj.getSelectObject(info.sheet);
  177. const col = info.sheet.zh_setting.cols[info.col];
  178. const validText = is_numeric(info.editingText) ? parseFloat(info.editingText) : (info.editingText ? trimInvalidChar(info.editingText) : null);
  179. const orgValue = select[col.field];
  180. if (orgValue == validText || ((!orgValue || orgValue === '') && (validText === ''))) {
  181. SpreadJsObj.reLoadRowData(info.sheet, info.row);
  182. return;
  183. }
  184. if (isNaN(validText)) {
  185. toastr.error('不能输入其它非数字类型字符');
  186. SpreadJsObj.reLoadRowData(info.sheet, info.row);
  187. return;
  188. }
  189. const yearmonth = col.field.split('_')[0];
  190. const mode = col.field.split('_')[1];
  191. let plan_gcl = 0;
  192. let plan_tp = 0;
  193. // 判断输入位数,提示
  194. if (mode === 'tp') {
  195. const reg = new RegExp('^([-]?)\\d+(\\.\\d{0,'+ parseInt(tenderInfo.decimal.tp) +'})?$');
  196. console.log(reg);
  197. console.log(reg.test(validText));
  198. if (validText !== null && (!reg.test(validText))) {
  199. toastr.error('输入金额小数位数不能大于' + tenderInfo.decimal.tp + '位');
  200. SpreadJsObj.reLoadRowData(info.sheet, info.row);
  201. return;
  202. }
  203. plan_gcl = select.dgn_price && select.dgn_price !== 0 ? ZhCalc.round(ZhCalc.div(validText, select.dgn_price), tenderInfo.decimal.up) : 0;
  204. plan_tp = validText;
  205. select[yearmonth + '_gcl'] = plan_gcl;
  206. } else {
  207. const reg = new RegExp('^([-]?)\\d+(\\.\\d{0,'+ parseInt(tenderInfo.decimal.up) +'})?$');
  208. if (validText !== null && (!reg.test(validText))) {
  209. toastr.error('输入工程量小数位数不能大于' + tenderInfo.decimal.up + '位');
  210. SpreadJsObj.reLoadRowData(info.sheet, info.row);
  211. return;
  212. }
  213. plan_gcl = validText;
  214. plan_tp = select.dgn_price && select.dgn_price !== 0 ? ZhCalc.round(ZhCalc.mul(validText, select.dgn_price), tenderInfo.decimal.tp) : 0;
  215. select[yearmonth + '_tp'] = plan_tp;
  216. }
  217. select[col.field] = validText;
  218. const updateData = {
  219. lid: select.ledger_id,
  220. yearmonth,
  221. plan_gcl,
  222. plan_tp,
  223. };
  224. console.log(updateData);
  225. // postData(window.location.pathname + '/save', {type: 'mode', postData: updateData}, function (result) {
  226. SpreadJsObj.reLoadRowData(info.sheet, info.row);
  227. // })
  228. }
  229. },
  230. deletePress: function (sheet) {
  231. return;
  232. },
  233. clipboardPasted(e, info) {
  234. const hint = {
  235. cellError: {type: 'error', msg: '粘贴内容超出了表格范围'},
  236. numberExpr: {type: 'error', msg: '不能粘贴其它非数字类型字符'},
  237. numberCan: {type: 'error', msg: '请粘贴大于0并且小于3位小数的浮点数'},
  238. };
  239. const range = info.cellRange;
  240. const sortData = info.sheet.zh_data || [];
  241. if (info.cellRange.row + info.cellRange.rowCount > sortData.length) {
  242. toastMessageUniq(hint.cellError);
  243. SpreadJsObj.reLoadSheetHeader(materialMonthSpread.getActiveSheet());
  244. SpreadJsObj.reLoadSheetData(materialMonthSpread.getActiveSheet());
  245. return;
  246. }
  247. if (sortData.length > 0 && range.col + range.colCount > 4 + months.length) {
  248. toastMessageUniq(hint.cellError);
  249. SpreadJsObj.reLoadSheetHeader(materialMonthSpread.getActiveSheet());
  250. SpreadJsObj.reLoadSheetData(materialMonthSpread.getActiveSheet());
  251. return;
  252. }
  253. const data = [];
  254. for (let iRow = 0; iRow < range.rowCount; iRow++) {
  255. let bPaste = true;
  256. const curRow = range.row + iRow;
  257. const materialMonthData = sortData[curRow];
  258. const hintRow = range.rowCount > 1 ? curRow : '';
  259. let sameCol = 0;
  260. for (let iCol = 0; iCol < range.colCount; iCol++) {
  261. const curCol = range.col + iCol;
  262. const colSetting = info.sheet.zh_setting.cols[curCol];
  263. if (!colSetting) continue;
  264. let validText = info.sheet.getText(curRow, curCol);
  265. validText = is_numeric(validText) ? parseFloat(validText) : (validText ? trimInvalidChar(validText) : null);
  266. const orgValue = sortData[curRow][colSetting.field];
  267. if (orgValue == validText || ((!orgValue || orgValue === '') && (validText === ''))) {
  268. sameCol++;
  269. if (range.colCount === sameCol) {
  270. bPaste = false;
  271. }
  272. continue;
  273. }
  274. const num = parseFloat(validText);
  275. if (isNaN(validText)) {
  276. toastMessageUniq(getPasteHint(hint.numberExpr, hintRow));
  277. bPaste = false;
  278. continue;
  279. }
  280. if (validText !== null && (num < 0 || !/^\d+(\.\d{1,3})?$/.test(num))) {
  281. toastMessageUniq(getPasteHint(hint.numberCan, hintRow));
  282. bPaste = false;
  283. continue;
  284. }
  285. materialMonthData[colSetting.field] = validText;
  286. sortData[curRow][colSetting.field] = validText;
  287. }
  288. if (bPaste) {
  289. data.push(materialMonthData);
  290. } else {
  291. SpreadJsObj.reLoadRowData(info.sheet, curRow);
  292. }
  293. }
  294. if (data.length === 0) {
  295. SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
  296. return;
  297. }
  298. // // 更新至服务器
  299. // postData(window.location.pathname + '/month/save', { type:'paste', updateData: data }, function (result) {
  300. // SpreadJsObj.reLoadSheetData(materialMonthSpread.getActiveSheet());
  301. // materialBillsData = result.materialBillsData;
  302. // SpreadJsObj.loadSheetData(materialSpread.getActiveSheet(), SpreadJsObj.DataType.Data, materialBillsData);
  303. // m_tp = result.m_tp;
  304. // resetTpTable();
  305. // }, function () {
  306. // SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
  307. // return;
  308. // });
  309. },
  310. };
  311. ledgerSpread.bind(spreadNS.Events.EditEnded, ledgerSpreadObj.editEnded);
  312. SpreadJsObj.addDeleteBind(ledgerSpread, ledgerSpreadObj.deletePress);
  313. // 进度计算方式选择
  314. $('.mode-select').on('click', function () {
  315. const _self = $(this);
  316. postData(window.location.pathname + '/save', {type: 'mode', postData: $(this).data('mode')}, function (result) {
  317. _self.addClass('disabled').attr('disabled', true);
  318. _self.parents('.col-6').siblings('.col-6').find('button').removeClass('disabled').removeAttr('disabled');
  319. $('#mode-tips').show();
  320. $('#mode-cancel').show();
  321. $('#mode').modal('hide');
  322. schedule.mode = _self.data('mode');
  323. SpreadJsObj.reLoadSheetData(ledgerSpread.getActiveSheet());
  324. })
  325. });
  326. // 月份添加
  327. $('#add-month').click(function () {
  328. const range = $('#month-range').val();
  329. if(range === '') {
  330. toastr.error('请选择计划周期时间');
  331. return;
  332. }
  333. const addMonthList = [];
  334. const cycle = range.split(' ~ ');
  335. if(cycle.length === 1) {
  336. addMonthList.push(cycle[0]);
  337. } else {
  338. // 多个月份
  339. const back_year = parseInt(cycle[1].split('-')[0]);
  340. const back_month = parseInt(cycle[1].split('-')[1]);
  341. const front_year = parseInt(cycle[0].split('-')[0]);
  342. const front_month = parseInt(cycle[0].split('-')[1]);
  343. if(back_year > front_year) {
  344. const num = getDistanceMonth(cycle[0], cycle[1]);
  345. let j = 1;
  346. for (let i = 0; i <= num; i++) {
  347. if(front_month + i > 12*j) {
  348. j = j + 1;
  349. }
  350. const m = (front_month + i)%12 === 0 ? 12 : (front_month + i)%12;
  351. addMonthList.push((front_year + (j-1)) + '-' + (m < 10 ? '0' + m : m));
  352. }
  353. } else if (back_year === front_year) {
  354. // 小于1年并没有跨年
  355. for (let i = front_month; i <= back_month; i++) {
  356. addMonthList.push(back_year + '-' + (i < 10 ? '0' + i : i));
  357. }
  358. }
  359. }
  360. // 判断是否已添加本月份
  361. if (addMonthList.length > 0) {
  362. const hadmonth = [];
  363. for (const m of addMonthList) {
  364. const one = _.find(scheduleMonth, { yearmonth: m });
  365. console.log(one, m);
  366. if (one) {
  367. hadmonth.push(m);
  368. }
  369. }
  370. if (hadmonth.length > 0) {
  371. let html = '';
  372. for (const hm of hadmonth) {
  373. html += `<div class="alert alert-danger">${hm} 已创建</div>`;
  374. }
  375. $('#add-month-error-list').html(html);
  376. $('#add-month-error-list').show();
  377. return;
  378. }
  379. } else {
  380. toastr.error('请选择计划周期时间');
  381. return;
  382. }
  383. $('#add-month-error-list').html('');
  384. $('#add-month-error-list').hide();
  385. const _self = $(this);
  386. postData(window.location.pathname + '/save', {type: 'addmonth', postData: addMonthList}, function (result) {
  387. _self.addClass('disabled').attr('disabled', true);
  388. toastr.success('新增成功');
  389. setTimeout(function () {
  390. window.location.reload();
  391. }, 500)
  392. })
  393. });
  394. $('#month-table input[type="checkbox"]').click(function () {
  395. const selectedMonth = [];
  396. $('#month-table input:checkbox:checked').each(function () {
  397. selectedMonth.push('「' + $(this).parents('td').siblings('td').text() + '」');
  398. });
  399. if(selectedMonth.length > 0) {
  400. $('#del-month-list').text(selectedMonth.join(''));
  401. $('#del-month-list').parent().show();
  402. $('#del-month').removeAttr('disabled');
  403. } else {
  404. $('#del-month-list').parent().hide();
  405. $('#del-month').attr('disabled', true);
  406. }
  407. });
  408. $('#del-month').click(function () {
  409. const selectedMonth = [];
  410. $('#month-table input:checkbox:checked').each(function () {
  411. selectedMonth.push($(this).parents('td').siblings().text());
  412. });
  413. if (selectedMonth.length === 0) {
  414. toastr.error('请选择删除的计划周期');
  415. return;
  416. }
  417. const _self = $(this);
  418. postData(window.location.pathname + '/save', {type: 'delmonth', postData: selectedMonth}, function (result) {
  419. _self.addClass('disabled').attr('disabled', true);
  420. toastr.success('删除成功');
  421. setTimeout(function () {
  422. window.location.reload();
  423. }, 500)
  424. })
  425. });
  426. $.subMenu({
  427. menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
  428. toMenu: '#to-menu', toMiniMenu: '#to-mini-menu',
  429. key: 'menu.1.0.0',
  430. miniHint: '#sub-mini-hint', hintKey: 'menu.hint.1.0.1',
  431. callback: function (info) {
  432. if (info.mini) {
  433. $('.panel-title').addClass('fluid');
  434. $('#sub-menu').removeClass('panel-sidebar');
  435. } else {
  436. $('.panel-title').removeClass('fluid');
  437. $('#sub-menu').addClass('panel-sidebar');
  438. }
  439. ledgerSpread.refresh();
  440. autoFlashHeight();
  441. }
  442. });
  443. });
  444. // 月份间隔
  445. function getDistanceMonth(startTime,endTime){
  446. startTime = new Date(startTime);
  447. endTime = new Date(endTime);
  448. var dateToMonth = 0;
  449. var startDate=startTime.getDate() + startTime.getHours()/24 + startTime.getMinutes()/24/60;
  450. var endDate=endTime.getDate() +endTime.getHours()/24 + endTime.getMinutes()/24/60;
  451. if(endDate >= startDate){
  452. dateToMonth = 0;
  453. }else{
  454. dateToMonth = -1;
  455. }
  456. let yearToMonth = (endTime.getYear() - startTime.getYear()) * 12;
  457. let monthToMonth = endTime.getMonth() - startTime.getMonth();
  458. return yearToMonth + monthToMonth + dateToMonth;
  459. }
  460. function setLeafData(tree) {
  461. const newtree = [];
  462. for (const t of tree) {
  463. const child = _.find(tree, { 'ledger_pid': t.ledger_id });
  464. if (!child && !t.is_leaf) {
  465. t.is_leaf = true;
  466. }
  467. newtree.push(t);
  468. }
  469. return newtree;
  470. }
  471. const is_numeric = (value) => {
  472. if (typeof(value) === 'object') {
  473. return false;
  474. } else {
  475. return !Number.isNaN(Number(value)) && value.toString().trim() !== '';
  476. }
  477. };