budget_compare.js 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754
  1. 'use strict';
  2. /**
  3. * 从cookie中读取缓存的列显示设置,没有则取默认
  4. * @returns {*[]}
  5. */
  6. function customColDisplay () {
  7. const defaultSetting = [
  8. { title: '投资估算', fields: ['gu_dgn_qty', 'gu_dgn_price', 'gu_tp'], visible: true },
  9. { title: '设计概算', fields: ['gai_dgn_qty', 'gai_dgn_price', 'gai_tp'], visible: true },
  10. { title: '施工图预算', fields: ['yu_dgn_qty', 'yu_dgn_price', 'yu_tp'], visible: true },
  11. { title: '招标预算', fields: ['zb_dgn_qty', 'zb_dgn_price', 'zb_tp'], visible: true },
  12. { title: '控制目标', fields: ['ctrl_dgn_qty', 'ctrl_dgn_price', 'ctrl_tp'], visible: false },
  13. { title: '实际投资', fields: ['real_dgn_qty', 'real_dgn_price', 'real_tp'], visible: false },
  14. { title: '台账', fields: ['dgn_qty', 'dgn_price', 'total_price'], visible: true },
  15. { title: '预估决算', fields: ['final_dgn_qty', 'final_dgn_price', 'final_tp'], visible: true },
  16. ];
  17. const settingStr = Cookies.get(ckColSetting);
  18. if (settingStr) {
  19. const customSetting = JSON.parse(settingStr);
  20. for (const ds of defaultSetting) {
  21. const cs = customSetting.find(x => {return x.title === ds.title});
  22. if (cs) ds.visible = cs.visible;
  23. }
  24. }
  25. return defaultSetting;
  26. }
  27. /**
  28. * 根据列显示设置,调整setting中的列是否显示
  29. * @param setting
  30. * @param customDisplay
  31. */
  32. function customizeTreeSetting(setting, customDisplay) {
  33. for (const cd of customDisplay) {
  34. for (const c of setting.cols) {
  35. if (cd.fields.indexOf(c.field) !== -1) {
  36. c.defaultVisible = cd.visible;
  37. }
  38. }
  39. }
  40. }
  41. $(document).ready(() => {
  42. const compareTypeKey = 'budget-compareType';
  43. const stackedBarCoverKey = 'budget-stackedBarCover';
  44. const stackedBarKey = 'budget-stackedBar';
  45. const comparePhaseKey = 'budget-comparePhase';
  46. autoFlashHeight();
  47. const compareSpread = SpreadJsObj.createNewSpread($('#cost-compare')[0]);
  48. const compareSheet = compareSpread.getActiveSheet();
  49. const getStackedBarTip = function (data) {
  50. return data.stackedBarTips.join('\n');
  51. };
  52. const comparePhase = [
  53. { key: 'gu', name: '投资估算', dgn1: 'gu_dgn_qty1', dgn2: 'gu_dgn_qty2', tp: 'gu_tp'},
  54. { key: 'gai', name: '设计概算', dgn1: 'gai_dgn_qty1', dgn2: 'gai_dgn_qty2', tp: 'gai_tp'},
  55. { key: 'yu', name: '施工图预算', dgn1: 'yu_dgn_qty1', dgn2: 'yu_dgn_qty2', tp: 'yu_tp'},
  56. { key: 'zb', name: '招标预算', dgn1: 'zb_dgn_qty1', dgn2: 'zb_dgn_qty2', tp: 'zb_tp'},
  57. { key: 'ctrl', name: '控制目标', dgn1: 'ctrl_dgn_qty1', dgn2: 'ctrl_dgn_qty2', tp: 'ctrl_tp'},
  58. { key: 'real', name: '实际投资', dgn1: 'real_dgn_qty1', dgn2: 'real_dgn_qty2', tp: 'real_tp'},
  59. { key: 'tz', name: '台账/合同', dgn1: 'dgn_qty1', dgn2: 'dgn_qty2', tp: 'total_price'},
  60. { key: 'final', name: '计量/支付', dgn1: 'final_dgn_qty1', dgn2: 'final_dgn_qty2', tp: 'final_tp'},
  61. ];
  62. const spreadSetting = {
  63. cols: [
  64. {title: '项目节编号', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 150, formatter: '@', cellType: 'tree'},
  65. {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 230, formatter: '@'},
  66. {title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 50, formatter: '@', cellType: 'unit'},
  67. {title: '投资估算|数量1/数量2', colSpan: '3|1', rowSpan: '1|1', field: 'gu_dgn_qty', hAlign: 2, width: 80, bc_type: 'number'},
  68. {title: '|经济指标', colSpan: '|1', rowSpan: '|1', field: 'gu_dgn_price', hAlign: 2, width: 80, type: 'Number', bc_type: 'number'},
  69. {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'gu_tp', hAlign: 2, width: 80, type: 'Number', bc_type: 'number'},
  70. {title: '设计概算|数量1/数量2', colSpan: '3|1', rowSpan: '1|1', field: 'gai_dgn_qty', hAlign: 2, width: 80, bc_type: 'number'},
  71. {title: '|经济指标', colSpan: '|1', rowSpan: '|1', field: 'gai_dgn_price', hAlign: 2, width: 80, type: 'Number', bc_type: 'number'},
  72. {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'gai_tp', hAlign: 2, width: 80, type: 'Number', bc_type: 'number'},
  73. {title: '施工图预算|数量1/数量2', colSpan: '3|1', rowSpan: '1|1', field: 'yu_dgn_qty', hAlign: 2, width: 80, bc_type: 'number'},
  74. {title: '|经济指标', colSpan: '|1', rowSpan: '|1', field: 'yu_dgn_price', hAlign: 2, width: 80, type: 'Number', bc_type: 'number'},
  75. {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'yu_tp', hAlign: 2, width: 80, type: 'Number', bc_type: 'number'},
  76. {title: '招标预算|数量1/数量2', colSpan: '3|1', rowSpan: '1|1', field: 'zb_dgn_qty', hAlign: 2, width: 80, bc_type: 'number'},
  77. {title: '|经济指标', colSpan: '|1', rowSpan: '|1', field: 'zb_dgn_price', hAlign: 2, width: 80, type: 'Number', bc_type: 'number'},
  78. {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'zb_tp', hAlign: 2, width: 80, type: 'Number', bc_type: 'number'},
  79. {title: '控制目标|数量1/数量2', colSpan: '3|1', rowSpan: '1|1', field: 'ctrl_dgn_qty', hAlign: 2, width: 80, bc_type: 'number'},
  80. {title: '|经济指标', colSpan: '|1', rowSpan: '|1', field: 'ctrl_dgn_price', hAlign: 2, width: 80, type: 'Number', bc_type: 'number'},
  81. {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'ctrl_tp', hAlign: 2, width: 80, type: 'Number', bc_type: 'number'},
  82. {title: '实际投资|数量1/数量2', colSpan: '3|1', rowSpan: '1|1', field: 'real_dgn_qty', hAlign: 2, width: 80, bc_type: 'number'},
  83. {title: '|经济指标', colSpan: '|1', rowSpan: '|1', field: 'real_dgn_price', hAlign: 2, width: 80, type: 'Number', bc_type: 'number'},
  84. {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'real_tp', hAlign: 2, width: 80, type: 'Number', bc_type: 'number'},
  85. {title: '台账/合同|数量1/数量2', colSpan: '3|1', rowSpan: '1|1', field: 'dgn_qty', hAlign: 2, width: 80, bc_type: 'number', visible: false},
  86. {title: '|经济指标', colSpan: '|1', rowSpan: '|1', field: 'dgn_price', hAlign: 2, width: 80, type: 'Number', bc_type: 'number', visible: false},
  87. {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'total_price', hAlign: 2, width: 80, type: 'Number', bc_type: 'number', visible: false},
  88. {title: '计量/支付|数量1/数量2', colSpan: '3|1', rowSpan: '1|1', field: 'final_dgn_qty', hAlign: 2, width: 80, bc_type: 'number', visible: false},
  89. {title: '|经济指标', colSpan: '|1', rowSpan: '|1', field: 'final_dgn_price', hAlign: 2, width: 80, type: 'Number', bc_type: 'number', visible: false},
  90. {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'final_tp', hAlign: 2, width: 80, type: 'Number', bc_type: 'number', visible: false},
  91. {title: '数据对比', colSpan: '1', rowSpan: '2', field: 'stackedBar', hAlign: 0, width: 300, cellType: 'stackedBar', stackedBarCover: false, bc_type: 'grid', defaultVisible: true, visible: false, getTip: getStackedBarTip},
  92. {title: '增幅%|数量1/数量2', colSpan: '2|1', rowSpan: '1|1', field: 'compare_dgn_qty', hAlign: 2, width: 80},
  93. {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'compare_tp', hAlign: 2, width: 80, type: 'Number'},
  94. ],
  95. emptyRows: 0,
  96. headRows: 2,
  97. headRowHeight: [25, 25],
  98. defaultRowHeight: 21,
  99. headerFont: '12px 微软雅黑',
  100. font: '12px 微软雅黑',
  101. readOnly: true,
  102. frozenColCount: 3,
  103. frozenLineColor: '#93b5e4',
  104. localCache: { key: 'budget-compare', colWidth: true },
  105. };
  106. sjsSettingObj.setFxTreeStyle(spreadSetting, sjsSettingObj.FxTreeStyle.jz);
  107. let sfSelect;
  108. const compareObj = {
  109. comparePhase1: 'gai',
  110. comparePhase2: 'final',
  111. getCompareSelectHtml() {
  112. const html = [];
  113. for (const cp of comparePhase) {
  114. html.push(`<option value="${cp.key}">${cp.name}</option>`);
  115. }
  116. return html.join('');
  117. },
  118. initCompare() {
  119. const selectHtml = this.getCompareSelectHtml();
  120. $('#compare1').html(selectHtml);
  121. $('#compare2').html(selectHtml);
  122. const comparePhase = Cookies.get(comparePhaseKey).split(',');
  123. this.setComparePhase(comparePhase[0] || this.comparePhase1, comparePhase[1] || this.comparePhase2);
  124. },
  125. getCompareHint(phase1, phase2) {
  126. if (!phase1) phase1 = this.comparePhase1;
  127. if (!phase2) phase2 = this.comparePhase2;
  128. const cp1 = comparePhase.find(x => { return x.key === phase1; });
  129. const cp2 = comparePhase.find(x => { return x.key === phase2; });
  130. return `增减幅度=(${cp2.name}-${cp1.name})÷${cp1.name}*100%`;
  131. },
  132. setComparePhase(phase1, phase2) {
  133. compareObj.comparePhase1 = phase1;
  134. compareObj.comparePhase2 = phase2;
  135. $('#compare-hint').html(compareObj.getCompareHint());
  136. compareObj.reCalculateCompareData();
  137. $('#compare-set').modal('hide');
  138. Cookies.set(comparePhaseKey, phase1 + ',' + phase2);
  139. },
  140. reCalcColVisible() {
  141. const type = this.compareType;
  142. spreadSetting.cols.forEach(x => {
  143. if (!x.bc_type) return;
  144. x.visible = x.bc_type === type && x.defaultVisible;
  145. });
  146. },
  147. curFinalId() {
  148. return this.finalInfo ? this.finalInfo.id : undefined;
  149. },
  150. initFinalCol() {
  151. const finalColField = ['dgn_qty', 'dgn_price', 'total_price', 'final_dgn_qty', 'final_dgn_price', 'final_tp', 'grow_dgn_qty', 'grow_tp'];
  152. const finalCol = spreadSetting.cols.filter(x => { return finalColField.indexOf(x.field) >= 0; });
  153. if (finalCol.length > 0 && !finalCol[0].visible) {
  154. finalCol.forEach(x => { x.visible = true; });
  155. this.initShowType();
  156. SpreadJsObj.refreshColumnVisible(compareSheet);
  157. }
  158. },
  159. initShowType() {
  160. this.reCalcColVisible();
  161. const colIndex = spreadSetting.cols.findIndex(x => { return x.field === 'stackedBar'});
  162. spreadSetting.cols[colIndex].stackedBarCover = parseInt(this.stackedBarCover);
  163. },
  164. expand(tree, tag) {
  165. switch (tag) {
  166. case "1":
  167. case "2":
  168. case "3":
  169. case "4":
  170. case "5":
  171. tree.expandByLevel(parseInt(tag));
  172. break;
  173. case "last":
  174. tree.expandByCustom(() => { return true; });
  175. break;
  176. }
  177. },
  178. calcStackedBar(tree) {
  179. const calcField = this.stackedBarField;
  180. const calcFieldColor = { 'gu_tp': '#657798', 'gai_tp': '#EE6666', 'yu_tp': '#74CBED', 'total_price': '#FAC858', 'final_tp': '#62DAAB' };
  181. const calcFieldCaption = { 'gu_tp': '估算', 'gai_tp': '概算', 'yu_tp': '预算', 'total_price': '台账', 'final_tp': '决算' };
  182. const calc = function(node, base){
  183. // const parent = tree.getParent(node);
  184. // if (!parent) {
  185. // base = 0;
  186. // for (const cf of calcField) {
  187. // base = Math.max(node[cf], base);
  188. // }
  189. // }
  190. node.stackedBar = [];
  191. node.stackedBarTips = [];
  192. for (const cf of calcField) {
  193. node.stackedBar.push({color: calcFieldColor[cf], percent: ZhCalc.div(node[cf], base), field: cf});
  194. node.stackedBarTips.push(`${calcFieldCaption[cf]}: ${node[cf] || 0}`);
  195. }
  196. if (node.children) {
  197. for (const child of node.children) {
  198. calc(child, base);
  199. }
  200. }
  201. };
  202. let commonBase = 0;
  203. tree.children.forEach(x => {
  204. for (const cf of calcField) {
  205. commonBase = Math.max(x[cf] || 0, commonBase);
  206. }
  207. });
  208. for (const child of tree.children) {
  209. calc(child, commonBase);
  210. }
  211. },
  212. loadBudgetData(result) {
  213. const compareTree = createNewPathTree('final', {
  214. id: 'id',
  215. pid: 'pid',
  216. order: 'order',
  217. level: 'level',
  218. rootId: -1,
  219. });
  220. const setting = { id: 'tree_id', pid: 'tree_pid', order: 'order', level: 'level', rootId: -1, calcFields: ['total_price'] };
  221. const realTree = createNewPathTree('ledger', setting);
  222. realTree.loadDatas(result.real);
  223. treeCalc.calculateAll(realTree);
  224. compareTree.loadTree(realTree, function (cur, source) {
  225. cur.base = true;
  226. cur.real_dgn_qty1 = ZhCalc.add(cur.real_dgn_qty1, source.dgn_qty1);
  227. cur.real_dgn_qty2 = ZhCalc.add(cur.real_dgn_qty2, source.dgn_qty2);
  228. cur.real_tp = ZhCalc.add(cur.real_tp, source.total_price);
  229. });
  230. const ctrlTree = createNewPathTree('ledger', setting);
  231. ctrlTree.loadDatas(result.ctrl);
  232. treeCalc.calculateAll(ctrlTree);
  233. compareTree.loadTree(ctrlTree, function (cur, source) {
  234. cur.base = true;
  235. cur.ctrl_dgn_qty1 = ZhCalc.add(cur.ctrl_dgn_qty1, source.dgn_qty1);
  236. cur.ctrl_dgn_qty2 = ZhCalc.add(cur.ctrl_dgn_qty2, source.dgn_qty2);
  237. cur.ctrl_tp = ZhCalc.add(cur.ctrl_tp, source.total_price);
  238. });
  239. const zbTree = createNewPathTree('ledger', setting);
  240. zbTree.loadDatas(result.zb);
  241. treeCalc.calculateAll(zbTree);
  242. compareTree.loadTree(zbTree, function (cur, source) {
  243. cur.base = true;
  244. cur.zb_dgn_qty1 = ZhCalc.add(cur.zb_dgn_qty1, source.dgn_qty1);
  245. cur.zb_dgn_qty2 = ZhCalc.add(cur.zb_dgn_qty2, source.dgn_qty2);
  246. cur.zb_tp = ZhCalc.add(cur.zb_tp, source.total_price);
  247. });
  248. const yuTree = createNewPathTree('ledger', setting);
  249. yuTree.loadDatas(result.yu);
  250. treeCalc.calculateAll(yuTree);
  251. compareTree.loadTree(yuTree, function (cur, source) {
  252. cur.base = true;
  253. cur.yu_dgn_qty1 = ZhCalc.add(cur.yu_dgn_qty1, source.dgn_qty1);
  254. cur.yu_dgn_qty2 = ZhCalc.add(cur.yu_dgn_qty2, source.dgn_qty2);
  255. cur.yu_tp = ZhCalc.add(cur.yu_tp, source.total_price);
  256. });
  257. const gaiTree = createNewPathTree('ledger', setting);
  258. gaiTree.loadDatas(result.gai);
  259. treeCalc.calculateAll(gaiTree);
  260. compareTree.loadTree(gaiTree, function (cur, source) {
  261. cur.base = true;
  262. cur.gai_dgn_qty1 = ZhCalc.add(cur.gai_dgn_qty1, source.dgn_qty1);
  263. cur.gai_dgn_qty2 = ZhCalc.add(cur.gai_dgn_qty2, source.dgn_qty2);
  264. cur.gai_tp = ZhCalc.add(cur.gai_tp, source.total_price);
  265. });
  266. const guTree = createNewPathTree('ledger', setting);
  267. guTree.loadDatas(result.gu);
  268. treeCalc.calculateAll(guTree);
  269. compareTree.loadTree(guTree, function (cur, source) {
  270. cur.base = true;
  271. cur.gu_dgn_qty1 = ZhCalc.add(cur.gu_dgn_qty1, source.dgn_qty1);
  272. cur.gu_dgn_qty2 = ZhCalc.add(cur.gu_dgn_qty2, source.dgn_qty2);
  273. cur.gu_tp = ZhCalc.add(cur.gu_tp, source.total_price);
  274. });
  275. compareTree.afterLoad(node => {
  276. node.gu_dgn_price = ZhCalc.div(node.gu_tp, node.gu_dgn_qty1, 2);
  277. node.gu_dgn_qty = node.gu_dgn_qty1
  278. ? (node.gu_dgn_qty2 ? node.gu_dgn_qty1 + '/' + node.gu_dgn_qty2 : node.gu_dgn_qty1)
  279. : (node.gu_dgn_qty2 ? '/' + node.gu_dgn_qty2 : '');
  280. node.gai_dgn_price = ZhCalc.div(node.gai_tp, node.gai_dgn_qty1, 2);
  281. node.gai_dgn_qty = node.gai_dgn_qty1
  282. ? (node.gai_dgn_qty2 ? node.gai_dgn_qty1 + '/' + node.gai_dgn_qty2 : node.gai_dgn_qty1)
  283. : (node.gai_dgn_qty2 ? '/' + node.gai_dgn_qty2 : '');
  284. node.yu_dgn_price = ZhCalc.div(node.yu_tp, node.yu_dgn_qty1, 2);
  285. node.yu_dgn_qty = node.yu_dgn_qty1
  286. ? (node.yu_dgn_qty2 ? node.yu_dgn_qty1 + '/' + node.yu_dgn_qty2 : node.yu_dgn_qty1)
  287. : (node.yu_dgn_qty2 ? '/' + node.yu_dgn_qty2 : '');
  288. node.zb_dgn_price = ZhCalc.div(node.zb_tp, node.zb_dgn_qty1, 2);
  289. node.zb_dgn_qty = node.zb_dgn_qty1
  290. ? (node.zb_dgn_qty2 ? node.zb_dgn_qty1 + '/' + node.zb_dgn_qty2 : node.zb_dgn_qty1)
  291. : (node.zb_dgn_qty2 ? '/' + node.zb_dgn_qty2 : '');
  292. node.ctrl_dgn_price = ZhCalc.div(node.ctrl_tp, node.ctrl_dgn_qty1, 2);
  293. node.ctrl_dgn_qty = node.ctrl_dgn_qty1
  294. ? (node.ctrl_dgn_qty2 ? node.ctrl_dgn_qty1 + '/' + node.ctrl_dgn_qty2 : node.ctrl_dgn_qty1)
  295. : (node.ctrl_dgn_qty2 ? '/' + node.ctrl_dgn_qty2 : '');
  296. node.real_dgn_price = ZhCalc.div(node.real_tp, node.real_dgn_qty1, 2);
  297. node.real_dgn_qty = node.real_dgn_qty1
  298. ? (node.real_dgn_qty2 ? node.real_dgn_qty1 + '/' + node.real_dgn_qty2 : node.real_dgn_qty1)
  299. : (node.real_dgn_qty2 ? '/' + node.real_dgn_qty2 : '');
  300. });
  301. compareTree.resortChildrenByCustom(function (x, y) {
  302. const iCode = compareCode(x.code, y.code);
  303. if (iCode) return iCode;
  304. if (!x.name) return -1;
  305. if (!y.name) return 1;
  306. return x.name.localeCompare(y.name);
  307. });
  308. const expandTag = getLocalCache('revise-compare-level');
  309. if (expandTag) compareObj.expand(compareTree, expandTag);
  310. this.calcStackedBar(compareTree);
  311. this.reCalculateCompareData(compareTree);
  312. SpreadJsObj.loadSheetData(compareSheet, SpreadJsObj.DataType.Tree, compareTree);
  313. },
  314. loadFinalData(result, msg) {
  315. if (msg) toastr.warning(msg);
  316. this.finalInfo = result.finalInfo;
  317. $('#final-info').html(`${moment(result.finalInfo.update_time).format('YYYY-MM-DD HH:mm:ss')} ${result.finalInfo.u_name}(${result.finalInfo.u_role})`);
  318. this.initFinalCol();
  319. const finalTree = createNewPathTree('ledger', {
  320. id: 'tree_id',
  321. pid: 'tree_pid',
  322. order: 'order',
  323. level: 'level',
  324. rootId: -1,
  325. });
  326. finalTree.loadDatas(result.final);
  327. const expandTag = getLocalCache('revise-compare-level');
  328. if (expandTag) compareObj.expand(finalTree, expandTag);
  329. this.calcStackedBar(finalTree);
  330. this.reCalculateCompareData(finalTree);
  331. SpreadJsObj.loadSheetData(compareSheet, SpreadJsObj.DataType.Tree, finalTree);
  332. if (sfSelect) sfSelect.reloadSelect(this.finalInfo.tender, this.finalInfo.contract);
  333. },
  334. reCalculateCompareData(calcTree) {
  335. const tree = calcTree || compareSheet.zh_tree;
  336. if (!tree) return;
  337. const cp1 = comparePhase.find(x => { return x.key === this.comparePhase1; });
  338. const cp2 = comparePhase.find(x => { return x.key === this.comparePhase2; });
  339. treeCalc.calculateAll(tree, function(node) {
  340. node.compare_dgn_qty1 = node[cp1.dgn1] ? ZhCalc.mul(ZhCalc.div(ZhCalc.sub(node[cp2.dgn1], node[cp1.dgn1]), node[cp1.dgn1], 4), 100) : 0;
  341. node.compare_dgn_qty2 = node[cp1.dgn2] ? ZhCalc.mul(ZhCalc.div(ZhCalc.sub(node[cp2.dgn1], node[cp1.dgn1]), node[cp1.dgn1], 4), 100) : 0;
  342. node.compare_dgn_qty = node.compare_dgn_qty1
  343. ? (node.compare_dgn_qty2 ? node.compare_dgn_qty1 + '/' + node.compare_dgn_qty2 : node.compare_dgn_qty1)
  344. : (node.compare_dgn_qty2 ? '/' + node.compare_dgn_qty2 : '');
  345. node.compare_tp = node[cp1.tp] ? ZhCalc.mul(ZhCalc.div(ZhCalc.sub(node[cp2.tp], node[cp1.tp]), node[cp1.tp], 4), 100) : 0;
  346. });
  347. const index = spreadSetting.cols.findIndex(x => { return x.field === 'compare_dgn_qty'});
  348. SpreadJsObj.reloadColData(compareSheet, index, 2);
  349. },
  350. loadCacheData(){
  351. let stackedBarCache = getLocalCache(stackedBarKey);
  352. if (stackedBarCache === null) stackedBarCache = 'gai_tp,total_price,final_tp';
  353. this.setStackedBarField(stackedBarCache ? stackedBarCache.split(',') : []);
  354. this.setCompareType(getLocalCache(compareTypeKey));
  355. this.setStackedBarCover(getLocalCache(stackedBarCoverKey));
  356. this.initShowType();
  357. },
  358. setStackedBarField(field){
  359. this.stackedBarField = field;
  360. setLocalCache(stackedBarKey, field.join(','));
  361. if (compareSheet.zh_tree) this.calcStackedBar(compareSheet.zh_tree);
  362. const colIndex = spreadSetting.cols.findIndex(x => { return x.field === 'stackedBar'});
  363. SpreadJsObj.reloadColData(compareSheet, colIndex);
  364. },
  365. setCompareType(type) {
  366. this.compareType = type || 'number';
  367. $('[name=showType]').removeClass('active');
  368. $(`[tag=${this.compareType}]`).addClass('active');
  369. if (this.compareType === 'grid') {
  370. $('.ml-grid').show();
  371. $('.ml-number').hide();
  372. } else {
  373. $('.ml-grid').hide();
  374. $('.ml-number').show();
  375. }
  376. this.reCalcColVisible();
  377. setLocalCache(compareTypeKey, this.compareType);
  378. },
  379. setStackedBarCover(cover){
  380. this.stackedBarCover = '1'; //cover || '1';
  381. const colIndex = spreadSetting.cols.findIndex(x => { return x.field === 'stackedBar'});
  382. spreadSetting.cols[colIndex].stackedBarCover = parseInt(this.stackedBarCover);
  383. SpreadJsObj.reloadColData(compareSheet, colIndex);
  384. setLocalCache(stackedBarCoverKey, this.stackedBarCover);
  385. }
  386. };
  387. customizeTreeSetting(spreadSetting, customColDisplay());
  388. compareObj.loadCacheData();
  389. compareObj.initCompare();
  390. SpreadJsObj.initSheet(compareSheet, spreadSetting);
  391. function compareCode(str1, str2, symbol = '-') {
  392. if (!str1) {
  393. return 1;
  394. } else if (!str2) {
  395. return -1;
  396. }
  397. function compareSubCode(code1, code2) {
  398. if (numReg.test(code1)) {
  399. if (numReg.test(code2)) {
  400. return parseInt(code1) - parseInt(code2);
  401. } else {
  402. return -1
  403. }
  404. } else {
  405. if (numReg.test(code2)) {
  406. return 1;
  407. } else {
  408. return code1 === code2 ? 0 : (code1 < code2 ? -1 : 1); //code1.localeCompare(code2);
  409. }
  410. }
  411. }
  412. const numReg = /^[0-9]+$/;
  413. const aCodes = str1.split(symbol), bCodes = str2.split(symbol);
  414. for (let i = 0, iLength = Math.min(aCodes.length, bCodes.length); i < iLength; ++i) {
  415. const iCompare = compareSubCode(aCodes[i], bCodes[i]);
  416. if (iCompare !== 0) {
  417. return iCompare;
  418. }
  419. }
  420. return aCodes.length - bCodes.length;
  421. }
  422. postData(window.location.pathname + '/load', {}, function (result, msg) {
  423. if (result.final) {
  424. compareObj.loadFinalData(result, msg);
  425. } else {
  426. compareObj.loadBudgetData(result);
  427. }
  428. });
  429. $.subMenu({
  430. menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
  431. toMenu: '#to-menu', toMiniMenu: '#to-mini-menu',
  432. key: 'menu.1.0.0',
  433. miniHint: '#sub-mini-hint', hintKey: 'menu.hint.1.0.1',
  434. callback: function (info) {
  435. if (info.mini) {
  436. $('.panel-title').addClass('fluid');
  437. $('#sub-menu').removeClass('panel-sidebar');
  438. } else {
  439. $('.panel-title').removeClass('fluid');
  440. $('#sub-menu').addClass('panel-sidebar');
  441. }
  442. autoFlashHeight();
  443. compareSpread.refresh();
  444. }
  445. });
  446. // 显示层次
  447. (function (select, sheet) {
  448. $(select).click(function () {
  449. if (!sheet.zh_tree) return;
  450. const tag = $(this).attr('tag');
  451. const tree = sheet.zh_tree;
  452. setTimeout(() => {
  453. showWaitingView();
  454. compareObj.expand(tree, tag);
  455. SpreadJsObj.refreshTreeRowVisible(sheet);
  456. setLocalCache('revise-compare-level', tag);
  457. closeWaitingView();
  458. }, 100);
  459. });
  460. })('a[name=showLevel]', compareSheet);
  461. class sfObject {
  462. constructor() {
  463. const self = this;
  464. this.selectTree = Tender2Tree.convert(category, tenderList, null, null, function (node, source) {
  465. node.lastStageOrder =`第${source.lastStageOrder}期`;
  466. node.lastStageStatus = source.lastStageStatus;
  467. });
  468. if (compareObj.finalInfo) {
  469. this.selectTree.datas.forEach(x => {
  470. x.selected = compareObj.finalInfo.tender.indexOf(x.tid + '') >= 0;
  471. });
  472. }
  473. const sfSpreadSetting = {
  474. cols: [
  475. {title: '选择', field: 'selected', hAlign: 1, width: 40, formatter: '@', cellType: 'checkbox'},
  476. {title: '名称', field: 'name', hAlign: 0, width: 180, formatter: '@', cellType: 'tree'},
  477. {title: '期数', field: 'lastStageOrder', hAlign: 1, width: 60, formatter: '@'},
  478. {title: '审批状态', field: 'lastStageStatus', hAlign: 1, width: 60, formatter: '@'},
  479. ],
  480. emptyRows: 0,
  481. headRows: 1,
  482. headRowHeight: [32],
  483. defaultRowHeight: 21,
  484. headerFont: '12px 微软雅黑',
  485. font: '12px 微软雅黑',
  486. headColWidth: [30],
  487. selectedBackColor: '#fffacd',
  488. readOnly: true,
  489. };
  490. this.spread = SpreadJsObj.createNewSpread($('#sf-tender-spread')[0]);
  491. this.sheet = this.spread.getActiveSheet();
  492. SpreadJsObj.initSheet(this.sheet, sfSpreadSetting);
  493. SpreadJsObj.loadSheetData(this.sheet, SpreadJsObj.DataType.Tree, this.selectTree);
  494. this.contractTree = createNewPathTree('base', { id: 'contract_id', pid: 'contract_pid', order: 'order', level: 'level', fullPath: 'full_path', rootId: -1 });
  495. this.contractTree.loadDatas(contract);
  496. if (compareObj.finalInfo) {
  497. this.contractTree.datas.forEach(x => {
  498. x.selected = compareObj.finalInfo.contract.indexOf(x.id) >= 0;
  499. });
  500. }
  501. this.contractSpread = SpreadJsObj.createNewSpread($('#sf-contract-spread')[0]);
  502. this.contractSheet = this.contractSpread.getActiveSheet();
  503. SpreadJsObj.initSheet(this.contractSheet, {
  504. cols: [
  505. {title: '选择', colSpan: '1', rowSpan: '1', field: 'selected', hAlign: 1, width: 40, formatter: '@', cellType: 'checkbox'},
  506. {title: '编号', colSpan: '1', rowSpan: '1', field: 'code', hAlign: 0, width: 90, formatter: '@', cellType: 'tree'},
  507. {title: '合同编号', colSpan: '1', rowSpan: '1', field: 'c_code', hAlign: 0, width: 90, formatter: '@'},
  508. {title: '名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 180, formatter: '@'},
  509. ],
  510. emptyRows: 0,
  511. headRows: 1,
  512. headRowHeight: [32],
  513. defaultRowHeight: 21,
  514. headerFont: '12px 微软雅黑',
  515. font: '12px 微软雅黑',
  516. readOnly: true,
  517. });
  518. SpreadJsObj.loadSheetData(this.contractSheet, SpreadJsObj.DataType.Tree, this.contractTree);
  519. this.spread.bind(spreadNS.Events.ButtonClicked, function (e, info) {
  520. if (!info.sheet.zh_setting) return;
  521. const col = info.sheet.zh_setting.cols[info.col];
  522. if (col.field !== 'selected') return;
  523. const node = SpreadJsObj.getSelectObject(info.sheet);
  524. self.selectNode(node, !node[col.field]);
  525. SpreadJsObj.reloadColData(info.sheet, 0);
  526. });
  527. this.contractSpread.bind(spreadNS.Events.ButtonClicked, function (e, info) {
  528. if (!info.sheet.zh_setting) return;
  529. const col = info.sheet.zh_setting.cols[info.col];
  530. if (col.field !== 'selected') return;
  531. const node = SpreadJsObj.getSelectObject(info.sheet);
  532. self.selectContract(node, !node[col.field]);
  533. SpreadJsObj.reloadColData(info.sheet, 0);
  534. });
  535. $('[name=sf-tab]').click(function() {
  536. const content = this.getAttribute('href');
  537. setTimeout(function() {
  538. if (content === '#sf-contract') self.contractSpread.refresh();
  539. if (content === '#sf-tender') self.spread.refresh();
  540. }, 200);
  541. });
  542. // 说是bootstrap v4有,但是不生效
  543. // $('#sf-contract').on('shown.bs.tab', function() {
  544. // self.contractSpread.refresh();
  545. // });
  546. // $('#sf-tender').on('shown.bs.tab', function() {
  547. // self.spread.refresh();
  548. // });
  549. $('#sf-select-all').click(function() {
  550. for (const n of self.selectTree.nodes) {
  551. n.selected = this.checked;
  552. }
  553. SpreadJsObj.reloadColData(self.sheet, 0);
  554. });
  555. $('#select-final-ok').click(() => {
  556. const rela = self.getSelects();
  557. if (rela.length === 0) return;
  558. const checked = $('#tsm-filter-checked')[0].checked;
  559. const stage = { type: $('[name=tsm-source]:checked').val(), checked };
  560. if (stage.type === 'month') {
  561. stage.month = $('#gather-month').val();
  562. if (stage.month === '') {
  563. toastr.warning('请选择 汇总年月');
  564. return;
  565. }
  566. } else if (stage.type === 'under-month') {
  567. stage.month = $('#gather-under-month').val();
  568. if (stage.month === '') {
  569. toastr.warning('请选择 汇总年月');
  570. return;
  571. }
  572. }
  573. const contracts = self.getSelectContracts();
  574. const data = { final_id: compareObj.curFinalId(), id: rela, final_type: $('[name=final_type]:checked').val(), stage, contracts };
  575. postData(window.location.pathname + '/final', data, function(result, msg) {
  576. compareObj.loadFinalData(result, msg);
  577. $('#select-final').modal('hide');
  578. });
  579. });
  580. }
  581. reloadSelect(select, contract) {
  582. if (compareObj.finalInfo) {
  583. this.selectTree.datas.forEach(x => {
  584. x.selected = select.indexOf(x.tid + '') >= 0;
  585. });
  586. this.contractTree.datas.forEach(x => {
  587. x.selected = contract.indexOf(x.id) >= 0;
  588. });
  589. }
  590. SpreadJsObj.reloadColData(this.sheet, 0);
  591. }
  592. selectNode(node, select) {
  593. const posterity = this.selectTree.getPosterity(node);
  594. posterity.unshift(node);
  595. for (const p of posterity) {
  596. p.selected = select;
  597. }
  598. }
  599. selectContract(node, select) {
  600. const posterity = this.contractTree.getPosterity(node);
  601. posterity.unshift(node);
  602. for (const p of posterity) {
  603. if (p) p.selected = select;
  604. }
  605. if (select) {
  606. const parents = this.contractTree.getAllParents(node);
  607. for(const p of parents) {
  608. if (p) p.selected = select;
  609. }
  610. }
  611. }
  612. getSelects() {
  613. const select = [];
  614. for (const n of this.selectTree.nodes) {
  615. if ((!n.children || n.children.length === 0) && n.selected) select.push(n.tid);
  616. }
  617. return select;
  618. }
  619. getSelectContracts() {
  620. const select = this.contractTree.nodes.filter(x => { return !!x.selected; }).map(x => { return x.id; });
  621. return select;
  622. }
  623. }
  624. $('#select-final').on('shown.bs.modal', () => {
  625. if (!sfSelect) sfSelect = new sfObject();
  626. });
  627. $('#stackedBar-ok').click(function() {
  628. const checked = $('[name=stackedBar]:checked');
  629. const field = [];
  630. checked.each((i, x) => { field.push(x.value)});
  631. compareObj.setStackedBarField(field);
  632. });
  633. $('#dp-stackedBar').click(function() {
  634. const field = compareObj.stackedBarField;
  635. const checked = $('[name=stackedBar]');
  636. checked.each((i, x) => { x.checked = field.indexOf(x.value) >= 0; });
  637. });
  638. $('a[name=showType]').click(function () {
  639. const type = this.getAttribute('tag');
  640. compareObj.setCompareType(type);
  641. SpreadJsObj.refreshColumnVisible(compareSheet);
  642. });
  643. $('a[name=stackedBarCover]').click(function() {
  644. const cover = this.getAttribute('tag');
  645. compareObj.setStackedBarCover(cover);
  646. });
  647. $('#dp-cover').click(function() {
  648. const cover = compareObj.stackedBarCover;
  649. const checked = $('a[name=stackedBarCover]');
  650. checked.each((i, x) => {
  651. if (x.getAttribute('tag') === cover) {
  652. $('i', x).addClass('text-primary').removeClass('text-white');
  653. } else {
  654. $('i', x).removeClass('text-primary').addClass('text-white');
  655. }
  656. });
  657. });
  658. $('#row-view').on('show.bs.modal', function () {
  659. const html = [], customDisplay = customColDisplay();
  660. for (const cd of customDisplay) {
  661. html.push('<tr>');
  662. html.push('<td>', cd.title, '</td>');
  663. html.push('<td>', '<input type="checkbox"' + (cd.visible ? ' checked=""' : '') + '>', '</td>');
  664. html.push('</tr>');
  665. }
  666. $('#row-view-list').html(html.join(''));
  667. });
  668. $('#row-view-ok').click(function () {
  669. const customDisplay = customColDisplay();
  670. const cvl = $('#row-view-list').children();
  671. for (const cv of cvl) {
  672. const title = $(cv).children()[0].innerHTML;
  673. const check = $('input', cv)[0].checked;
  674. const cd = customDisplay.find(function (c) {
  675. return c.title === title;
  676. });
  677. cd.visible = check;
  678. }
  679. customizeTreeSetting(spreadSetting, customDisplay);
  680. compareObj.loadCacheData();
  681. SpreadJsObj.refreshColumnVisible(compareSheet);
  682. Cookies.set(ckColSetting, JSON.stringify(customDisplay), 30*24*60*60*1000);
  683. $('#row-view').modal('hide');
  684. });
  685. $('#compare-set').on('show.bs.modal', function() {
  686. $('#compare1').val(compareObj.comparePhase1);
  687. $('#compare2').val(compareObj.comparePhase2);
  688. $('#compare-active-hint').html(compareObj.getCompareHint());
  689. });
  690. $('#compare1').change(() => {
  691. const phase1 = $('#compare1').val();
  692. const phase2 = $('#compare2').val();
  693. $('#compare-active-hint').html(compareObj.getCompareHint(phase1, phase2));
  694. });
  695. $('#compare2').change(() => {
  696. const phase1 = $('#compare1').val();
  697. const phase2 = $('#compare2').val();
  698. $('#compare-active-hint').html(compareObj.getCompareHint(phase1, phase2));
  699. });
  700. $('#compare-set-ok').click(() => {
  701. compareObj.setComparePhase($('#compare1').val(), $('#compare2').val());
  702. });
  703. $('#export-excel').click(function() {
  704. const excelSetting = JSON.parse(JSON.stringify(compareSheet.zh_setting));
  705. const gridIndex = excelSetting.cols.findIndex(x => { return x.field === 'stackedBar'});
  706. if (gridIndex >= 0) excelSetting.cols.splice(gridIndex, 1);
  707. SpreadExcelObj.exportSimpleXlsxSheet(excelSetting, compareSheet.zh_tree.nodes, $('.sidebar-title').attr('data-original-title') + "-造价对比.xlsx");
  708. });
  709. $('[name=tsm-source]').click(function() {
  710. $('[name=gather-type]').hide();
  711. const type = this.value;
  712. const gatherBy = $(`#gather-by-${type}`);
  713. if (gatherBy.length > 0) {
  714. $('#tsm-stage-info-detail').show();
  715. $(`#gather-by-${type}`).show();
  716. } else {
  717. $('#tsm-stage-info-detail').hide();
  718. }
  719. });
  720. $('.datepicker-here').datepicker({
  721. autoClose: true,
  722. });
  723. });