spss_compare_ledger.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. const billsCompareField = ['quantity', 'total_price'];
  2. const posCompareField = ['quantity'];
  3. $(document).ready(() => {
  4. autoFlashHeight();
  5. let isCache = false, selectTenders = [];
  6. const billsSpreadSetting = {
  7. cols: [
  8. {title: '项目节编号', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 185, formatter: '@', cellType: 'tree'},
  9. {title: '清单编号', colSpan: '1', rowSpan: '2', field: 'b_code', hAlign: 0, width: 100, formatter: '@'},
  10. {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 235, formatter: '@'},
  11. {title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 60, formatter: '@'},
  12. {title: '单价', colSpan: '1', rowSpan: '2', field: 'unit_price', hAlign: 2, width: 60, type: 'Number'},
  13. {title: '标段1|数量', colSpan: '2|1', rowSpan: '1|1', field: 'quantity_1', hAlign: 2, width: 80, type: 'Number', formatTitle: '%s|数量'},
  14. {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'total_price_1', hAlign: 2, width: 80, type: 'Number'},
  15. {title: '标段2|数量', colSpan: '2|1', rowSpan: '1|1', field: 'quantity_2', hAlign: 2, width: 80, type: 'Number', formatTitle: '%s|数量'},
  16. {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'total_price_2', hAlign: 2, width: 80, type: 'Number'},
  17. {title: '差值|数量', colSpan: '2|1', rowSpan: '1|1', field: 'differ_quantity', hAlign: 2, width: 80, type: 'Number'},
  18. {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'differ_total_price', hAlign: 2, width: 80, type: 'Number'},
  19. ],
  20. emptyRows: 0,
  21. headRows: 2,
  22. headRowHeight: [32, 32],
  23. defaultRowHeight: 21,
  24. headerFont: '12px 微软雅黑',
  25. font: '12px 微软雅黑',
  26. readOnly: true,
  27. };
  28. const posSpreadSetting = {
  29. cols: [
  30. {title: '名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 235, formatter: '@'},
  31. {title: '标段1', colSpan: '1', rowSpan: '1', field: 'quantity_1', hAlign: 2, width: 100, type: 'Number', formatTitle: '%s'},
  32. {title: '标段2', colSpan: '1', rowSpan: '1', field: 'quantity_2', hAlign: 2, width: 100, type: 'Number', formatTitle: '%s'},
  33. {title: '差值', colSpan: '1', rowSpan: '1', field: 'differ_quantity', hAlign: 2, width: 100, type: 'Number'},
  34. ],
  35. emptyRows: 0,
  36. headRows: 1,
  37. headRowHeight: [32],
  38. defaultRowHeight: 21,
  39. headerFont: '12px 微软雅黑',
  40. font: '12px 微软雅黑',
  41. readOnly: true,
  42. };
  43. const billsSpread = SpreadJsObj.createNewSpread($('#bills-spread')[0]);
  44. const billsSheet = billsSpread.getActiveSheet();
  45. sjsSettingObj.setFxTreeStyle(billsSpreadSetting, sjsSettingObj.FxTreeStyle.jz);
  46. SpreadJsObj.initSheet(billsSheet, billsSpreadSetting);
  47. const posSpread = SpreadJsObj.createNewSpread($('#pos-spread')[0]);
  48. const posSheet = posSpread.getActiveSheet();
  49. SpreadJsObj.initSheet(posSheet, posSpreadSetting);
  50. const treeSetting = {
  51. id: 'ledger_id',
  52. pid: 'ledger_pid',
  53. order: 'order',
  54. level: 'level',
  55. full_path: 'full_path',
  56. rootId: -1,
  57. keys: ['id', 'tender_id', 'ledger_id'],
  58. calcFields: ['total_price_1', 'total_price_2', 'differ_total_price'],
  59. // todo 判断同一清单时,是否应区分 清单是否含计量单元?
  60. // findNode: function(tree, node, parent, source) {
  61. // const siblings = parent ? parent.children : tree.children;
  62. // const checkData = { code: node.code || '', b_code: node.b_code || '', name: node.name || '', unit: node.unit || '', unit_price: node.unit_price || 0 };
  63. // return siblings.find(function (x) {
  64. // if (!checkData.b_code) return x.code === checkData.code && x.name === checkData.name;
  65. // const posRange = source.pos.getLedgerPos(node.id);
  66. // const hasPos = posRange && posRange.length > 0;
  67. // const xHasPos = x.pos && x.pos.length > 0;
  68. // return hasPos === xHasPos && x.b_code === checkData.b_code && x.name === checkData.name && x.unit === checkData.unit && x.unit_price === checkData.unit_price;
  69. // });
  70. // },
  71. loadInfo1: function (node, sourceNode, source) {
  72. for (const f of billsCompareField) {
  73. node[f + '_1'] = sourceNode[f];
  74. }
  75. const posRange = source.pos.getLedgerPos(sourceNode.id);
  76. if (posRange && posRange.length > 0) {
  77. if (!node.pos) node.pos = [];
  78. for (const p of posRange) {
  79. let nP = _.find(node.pos, {name: p.name || ''});
  80. if (!nP) {
  81. nP = {name: p.name || ''};
  82. node.pos.push(nP);
  83. }
  84. for (const f of posCompareField) {
  85. nP[f + '_1'] = p[f];
  86. }
  87. }
  88. }
  89. },
  90. loadInfo2: function (node, sourceNode, source) {
  91. for (const f of billsCompareField) {
  92. node[f + '_2'] = sourceNode[f];
  93. }
  94. const posRange = source.pos.getLedgerPos(sourceNode.id);
  95. if (posRange && posRange.length > 0) {
  96. if (!node.pos) node.pos = [];
  97. for (const p of posRange) {
  98. let nP = _.find(node.pos, {name: p.name || ''});
  99. if (!nP) {
  100. nP = {name: p.name || ''};
  101. node.pos.push(nP);
  102. }
  103. for (const f of posCompareField) {
  104. nP[f + '_2'] = p[f];
  105. }
  106. }
  107. }
  108. },
  109. afterLoad: function (tree) {
  110. for (const data of tree.datas) {
  111. for (const f of billsCompareField) {
  112. data['differ_' + f] = ZhCalc.sub(data[f + '_1'], data[f + '_2']);
  113. }
  114. for (const p of data.pos) {
  115. for (const f of posCompareField) {
  116. p['differ_' + f] = ZhCalc.sub(p[f + '_1'], p[f + '_2']);
  117. }
  118. }
  119. }
  120. },
  121. };
  122. const billsTree = createNewPathTree('compare', treeSetting);
  123. const billsTreeSpreadObj = {
  124. rebuildSpreadSetting: function(tenders) {
  125. selectTenders.length = 0;
  126. for (const [i, t] of tenders.entries()) {
  127. selectTenders.push({ id: t.id, name: t.name, stage_filter: t.stage_filter });
  128. const fieldName = 'quantity_' + (i + 1);
  129. const billsQty = billsSpreadSetting.cols.find(x => { return x.field === fieldName; });
  130. billsQty.title = billsQty.formatTitle.replace('%s', t.name);
  131. const posQty = posSpreadSetting.cols.find(x => { return x.field === fieldName; });
  132. posQty.title = posQty.formatTitle.replace('%s', t.name);
  133. }
  134. SpreadJsObj.reLoadSheetHeader(billsSheet);
  135. SpreadJsObj.reLoadSheetHeader(posSheet);
  136. },
  137. selectionChanged: function (e, info) {
  138. if (info.newSelections) {
  139. if (!info.oldSelections || info.newSelections[0].row !== info.oldSelections[0].row) {
  140. posSpreadObj.loadCurPosData();
  141. }
  142. }
  143. },
  144. };
  145. billsSpread.bind(spreadNS.Events.SelectionChanged, billsTreeSpreadObj.selectionChanged);
  146. const posSpreadObj = {
  147. loadCurPosData: function () {
  148. const node = SpreadJsObj.getSelectObject(billsSheet);
  149. if (node) {
  150. SpreadJsObj.loadSheetData(posSheet, 'data', node.pos || []);
  151. } else {
  152. SpreadJsObj.loadSheetData(posSheet, 'data', []);
  153. }
  154. },
  155. };
  156. const tenderSelect = TenderSelectMulti({
  157. title: '对比标段',
  158. type: 'compare',
  159. dataType: 'ledger',
  160. afterSelect: function(select) {
  161. const data = { filter: 'ledger', tender: select };
  162. postData(`/sp/${spid}/spss/load`, data, function(result) {
  163. isCache = false;
  164. const tenderTreeSetting = {
  165. id: 'ledger_id',
  166. pid: 'ledger_pid',
  167. order: 'order',
  168. level: 'level',
  169. rootId: -1,
  170. keys: ['id', 'tender_id', 'ledger_id'],
  171. calcFields: ['total_price'],
  172. };
  173. const tenders = [];
  174. billsTreeSpreadObj.rebuildSpreadSetting(result);
  175. for (const t of result) {
  176. const tender = {
  177. billsTree: createNewPathTree('ledger', tenderTreeSetting),
  178. pos: new PosData({ id: 'id', ledgerId: 'lid', }),
  179. };
  180. tender.billsTree.loadDatas(t.bills);
  181. tender.pos.loadDatas(t.pos);
  182. tenders.push(tender);
  183. }
  184. billsTree.loadCompareData(tenders[0], tenders[1]);
  185. treeCalc.calculateAll(billsTree);
  186. SpreadJsObj.loadSheetData(billsSheet, SpreadJsObj.DataType.Tree, billsTree);
  187. });
  188. },
  189. });
  190. $('#gather-select').click(tenderSelect.showSelect);
  191. $('#export-excel').click(function() {
  192. const excelData = [];
  193. for (const node of billsTree.nodes) {
  194. const data = {};
  195. for (const c of billsSpreadSetting.cols) {
  196. data[c.field] = node[c.field];
  197. }
  198. excelData.push(data);
  199. for (const p of node.pos) {
  200. const pData = {};
  201. for (const c of posSpreadSetting.cols) {
  202. pData[c.field] = p[c.field];
  203. }
  204. excelData.push(pData);
  205. }
  206. }
  207. SpreadExcelObj.exportSimpleXlsxSheet(billsSpreadSetting, excelData, "台账对比.xlsx");
  208. });
  209. $.divResizer({
  210. select: '#pos-resize',
  211. callback: function () {
  212. billsSpread.refresh();
  213. let bcontent = $(".bcontent-wrap") ? $(".bcontent-wrap").height() : 0;
  214. $(".sp-wrap").height(bcontent-30);
  215. posSpread.refresh();
  216. }
  217. });
  218. // 显示层次
  219. (function (select, sheet) {
  220. $(select).click(function () {
  221. if (!sheet.zh_tree) return;
  222. const tag = $(this).attr('tag');
  223. const tree = sheet.zh_tree;
  224. switch (tag) {
  225. case "1":
  226. case "2":
  227. case "3":
  228. case "4":
  229. case "5":
  230. tree.expandByLevel(parseInt(tag));
  231. SpreadJsObj.refreshTreeRowVisible(sheet);
  232. break;
  233. case "last":
  234. tree.expandByCustom(() => { return true; });
  235. SpreadJsObj.refreshTreeRowVisible(sheet);
  236. break;
  237. case "leafXmj":
  238. tree.expandToLeafXmj();
  239. SpreadJsObj.refreshTreeRowVisible(sheet);
  240. break;
  241. case "differ":
  242. tree.expandByCustom(function (x) {
  243. const posterity = tree.getPosterity(x);
  244. if (posterity.length === 0) return x.differ_qty || x.differ_tp;
  245. for (const p of posterity) {
  246. if (x.differ_qty || x.differ_tp) return true;
  247. }
  248. return false;
  249. });
  250. SpreadJsObj.refreshTreeRowVisible(sheet);
  251. }
  252. });
  253. })('a[name=showLevel]', billsSheet);
  254. $.subMenu({
  255. menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
  256. toMenu: '#to-menu', toMiniMenu: '#to-mini-menu',
  257. key: 'menu.1.0.0',
  258. miniHint: '#sub-mini-hint', hintKey: 'menu.hint.1.0.1',
  259. callback: function (info) {
  260. if (info.mini) {
  261. $('.panel-title').addClass('fluid');
  262. $('#sub-menu').removeClass('panel-sidebar');
  263. $('.c-body table thead').css('left', '56px');
  264. } else {
  265. $('.panel-title').removeClass('fluid');
  266. $('#sub-menu').addClass('panel-sidebar');
  267. $('.c-body table thead').css('left', '176px');
  268. }
  269. autoFlashHeight();
  270. billsSpread.refresh();
  271. posSpread.refresh();
  272. }
  273. });
  274. const spssStash = SpssStash({
  275. type: 'compare_ledger',
  276. url: `/sp/${spid}/spss/stash`,
  277. getCurStashData: function() {
  278. if (!billsTree || billsTree.nodes.length === 0) {
  279. toastr.warning('当前无数据可暂存,请先对比标段后,再操作');
  280. return null;
  281. }
  282. if (isCache) {
  283. toastr.warning('当前数据为暂存数据,请勿重复保存');
  284. return null;
  285. }
  286. const data = { result: { bills: billsTree.getDefaultData(billsTree.nodes) } };
  287. data.select = selectTenders;
  288. return data;
  289. },
  290. loadStashData: function(data) {
  291. isCache = true;
  292. billsTreeSpreadObj.rebuildSpreadSetting(data.spss_select);
  293. billsTree.clearDatas();
  294. billsTree.loadDatas(data.spss_result.bills);
  295. SpreadJsObj.loadSheetData(billsSheet, SpreadJsObj.DataType.Tree, billsTree, true);
  296. posSpreadObj.loadCurPosData();
  297. $('#stash-hint').html(`当前显示:${moment(data.create_time).format('YYYY-MM-DD HH:mm:ss')} (${data.user_name})`);
  298. }
  299. });
  300. $('#stash').click(() => { spssStash.showStash(); });
  301. });