budgetSummarySheet.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809
  1. /* 建设其他费表格相关 */
  2. const budgetSummaryObj = (() => {
  3. const { isEmptyVal, isDef, isNumber } = window.commonUtil;
  4. const { fixedFlag, BudgetArea } = window.commonConstants;
  5. // 原始数据
  6. let rawData = [];
  7. // ID与原始数据映射表(主要是恢复用)
  8. const orgMap = {};
  9. // 建设其他费表格对象
  10. let spread = null;
  11. // 建设其他费树
  12. let tree = null;
  13. // 计算相关
  14. const calcSetting = {
  15. costGrowthRate: 0,
  16. growthPeriod: 0,
  17. };
  18. // 单位设置下拉框
  19. const setUnitCombo = (sheet, data) => {
  20. const unitCol = budgetSummaryTreeSetting.cols.findIndex(item => item.data.field === 'unit');
  21. if (unitCol >= 0) {
  22. TREE_SHEET_HELPER.massOperationSheet(sheet, () => {
  23. const comboBox = sheetCommonObj.getDynamicCombo();
  24. comboBox
  25. .itemHeight(10)
  26. .items(['m', 'm2', 'm3', 'km', 't', 'kg', '台班', '工日', '昼夜', '元', '项', '处', '个', '件',
  27. '根', '组', '系统', '台', '套', '株', '丛', '缸', '支', '只', '块', '座', '对', '份', '樘', '攒', '榀'])
  28. .editable(true);
  29. data.forEach((item, index) => {
  30. sheet.getCell(index, unitCol).cellType(comboBox);
  31. })
  32. });
  33. }
  34. }
  35. const getFieldByCol = (col) => {
  36. const item = budgetSummaryTreeSetting.cols[col];
  37. return item && item.data && item.data.field || null;
  38. }
  39. // 单元格值验证器
  40. const validator = {
  41. text() {
  42. return true
  43. },
  44. number(val) {
  45. return !isDef(val) || isNumber(val);
  46. },
  47. };
  48. const getValidator = (col) => {
  49. const item = budgetSummaryTreeSetting.cols[col];
  50. if (!item) {
  51. return 'text';
  52. }
  53. return validator[item.data.type || 'text'];
  54. }
  55. // 单元格文本转换处理
  56. const textFactory = {
  57. calcBase(node) {
  58. if (node.data.calcBase && node.data.calcBase !== "") {
  59. return cbParser.toFExpr(node.data.calcBase);
  60. }
  61. return '';
  62. },
  63. 'feesIndex.common.unitFee': (node) => {
  64. return _.get(node, 'data.feesIndex.common.unitFee', '') || '';
  65. },
  66. 'feesIndex.common.totalFee': (node) => {
  67. return _.get(node, 'data.feesIndex.common.totalFee', '') || '';
  68. },
  69. 'feesIndex.building.totalFee': (node) => {
  70. return _.get(node, 'data.feesIndex.building.totalFee', '') || '';
  71. },
  72. 'feesIndex.installation.totalFee': (node) => {
  73. return _.get(node, 'data.feesIndex.installation.totalFee', '') || '';
  74. },
  75. 'feesIndex.equipment.totalFee': (node) => {
  76. return _.get(node, 'data.feesIndex.equipment.totalFee', '') || '';
  77. },
  78. 'feesIndex.other.totalFee': (node) => {
  79. return _.get(node, 'data.feesIndex.other.totalFee', '') || '';
  80. },
  81. };
  82. /* 表格事件相关 */
  83. // 根据节点数据刷新表格数据
  84. const refreshData = (sheet, changedCells) => {
  85. if (!tree) {
  86. return;
  87. }
  88. TREE_SHEET_HELPER.massOperationSheet(sheet, () => {
  89. changedCells.forEach(({ row, col }) => {
  90. const node = tree.items[row];
  91. const field = getFieldByCol(col);
  92. if (!field || !node) {
  93. return;
  94. }
  95. const textFunc = textFactory[field];
  96. const val = textFunc ? textFunc(node) : node.data[field] || '';
  97. sheet.setValue(row, col, val);
  98. });
  99. });
  100. }
  101. // 刷新整个表格
  102. const refreshAll = (sheet) => {
  103. const changedCells = [];
  104. const colCount = budgetSummaryTreeSetting.cols.length;
  105. for (let row = 0; row < tree.items.length; row++) {
  106. for (let col = 0; col < colCount; col++) {
  107. changedCells.push({ row, col })
  108. }
  109. }
  110. refreshData(sheet, changedCells);
  111. }
  112. // 更新数据
  113. const bulkOperation = async (bulkData) => {
  114. if (bulkData.length) {
  115. await ajaxPost('/bills/bulkOperation', { bulkData });
  116. }
  117. };
  118. // 编辑相关
  119. const edit = async (sheet, changedCells, calcNodes = null) => {
  120. if (!changedCells.length && !calcNodes) {
  121. return;
  122. }
  123. if (changedCells.length) {
  124. // 单元格值验证
  125. const isValid = changedCells.every(({ row, col }) => {
  126. const val = sheet.getValue(row, col);
  127. return getValidator(col)(val);
  128. });
  129. // 验证不通过,恢复
  130. if (!isValid) {
  131. refreshData(sheet, changedCells);
  132. }
  133. }
  134. let needCalc = false;
  135. const nodes = [];
  136. try {
  137. $.bootstrapLoading.start();
  138. const IDMap = {};
  139. const bulkData = [];
  140. changedCells.forEach(({ row, col }) => {
  141. const node = tree.items[row];
  142. if (!node) {
  143. return;
  144. }
  145. if (!nodes.find(n => n.data.ID !== node.data.ID)) {
  146. nodes.push(node);
  147. }
  148. const field = getFieldByCol(col);
  149. const value = sheet.getValue(row, col) || '';
  150. const data = (IDMap[node.data.ID] || (IDMap[node.data.ID] = {}));
  151. if (['feesIndex.common.unitFee'].includes(field)) {
  152. const fees = node.data.fees || [];
  153. const feeItem = fees.find(item => item.fieldName === 'common');
  154. if (feeItem) {
  155. feeItem.unitFee = value;
  156. } else {
  157. fees.push({ fieldName: 'common', totalFee: 0, unitFee: +value });
  158. }
  159. data[field] = fees;
  160. node.data[field] = fees;
  161. node.data.feesIndex = getFeeIndex(node.data.fees);
  162. } else {
  163. data[field] = value;
  164. node.data[field] = value;
  165. }
  166. if (field === 'calcBase') {
  167. node.data.userCalcBase = value;
  168. projectObj.project.calcBase.calculate(node, null, false);
  169. if (!projectObj.project.calcBase.success) {
  170. throw projectObj.project.calcBase.errMsg;
  171. } else if (isEmptyVal(value)) {
  172. // 删除清单基数,单价要清空
  173. calcTools.setFieldValue(node, 'feesIndex.common.unitFee', 0);
  174. }
  175. data.calcBase = node.data.calcBase;
  176. data.calcBaseValue = node.data.calcBaseValue;
  177. data.tenderCalcBaseValue = node.data.tenderCalcBaseValue;
  178. }
  179. if (['quantity', 'feesIndex.common.unitFee', 'calcBase', 'feeRate'].includes(field)) {
  180. needCalc = true;
  181. }
  182. });
  183. calcNodes = calcNodes ? calcNodes : nodes;
  184. // 重算节点
  185. const dataArr = [];
  186. if ((needCalc && nodes.length) || (calcNodes && calcNodes.length)) {
  187. const changedNodes = projectObj.project.calcProgram.calcNodes(calcNodes, false, tree);
  188. for (const node of changedNodes) {
  189. nodes.push(node);
  190. if (node.changed) {
  191. const data = calcTools.cutNodeForSave(node);
  192. dataArr.push(data);
  193. }
  194. }
  195. }
  196. dataArr.forEach(item => {
  197. delete item.projectID;
  198. const data = IDMap[item.ID];
  199. if (data) {
  200. Object.assign(data, item);
  201. } else {
  202. IDMap[item.ID] = item;
  203. }
  204. });
  205. // 保存节点
  206. Object
  207. .entries(IDMap)
  208. .forEach(([ID, data]) => {
  209. const node = tree.findNode(ID);
  210. if (!node) {
  211. return;
  212. }
  213. // 处理其他费用
  214. if (node.isBelongToFlags([fixedFlag.CONSTRUCTION_OTHER_FEE])) {
  215. const fees = data.fees || [];
  216. const commonFeeItem = fees.find(item => item.fieldName === 'common');
  217. const otherFeeItem = fees.find(item => item.fieldName === 'other');
  218. if (otherFeeItem) {
  219. otherFeeItem.totalFee = commonFeeItem && commonFeeItem.totalFee || 0;
  220. } else {
  221. fees.push({ fieldName: 'other', totalFee: commonFeeItem && commonFeeItem.totalFee || 0 });
  222. }
  223. }
  224. bulkData.push({ type: 'update', data: { ID, ...data } });
  225. });
  226. await bulkOperation(bulkData);
  227. Object
  228. .entries(IDMap)
  229. .forEach(([ID, data]) => {
  230. const node = tree.findNode(ID);
  231. if (node) {
  232. Object.assign(node.data, data);
  233. node.data.feesIndex = getFeeIndex(node.data.fees);
  234. orgMap[ID] = _.cloneDeep(node.data);
  235. }
  236. });
  237. refreshAll(sheet);
  238. } catch (err) {
  239. console.log(err);
  240. nodes.forEach(node => {
  241. const orgItem = orgMap[node.data.ID];
  242. if (orgItem) {
  243. node.data = _.cloneDeep(orgItem);
  244. // Object.assign(node.data, orgItem);
  245. }
  246. });
  247. refreshData(sheet, changedCells);
  248. alert(err);
  249. } finally {
  250. $.bootstrapLoading.end();
  251. }
  252. }
  253. // 是否是属于工程费用区域的节点
  254. const isConstructionFeeArea = (node) => {
  255. return node && node.data && node.data.area === BudgetArea.CONSTRUCTION_FEE;
  256. }
  257. // 工具栏可操作性
  258. let upLevelDisabled = false;
  259. let downLevelDisabled = false;
  260. let upMoveDisabled = false;
  261. let downMoveDisabled = false;
  262. const refreshToolsBar = (node) => {
  263. upLevelDisabled = !node || !node.canUpLevel() || isConstructionFeeArea(node) || (node.nextSibling && node.data.calcBase);
  264. downLevelDisabled = !node || !node.canDownLevel() || isConstructionFeeArea(node) || isConstructionFeeArea(node.preSibling) || (node.preSibling && node.preSibling.data.calcBase);
  265. upMoveDisabled = !node || !node.canUpMove() || isConstructionFeeArea(node) || isConstructionFeeArea(node.preSibling);
  266. downMoveDisabled = !node || !node.canDownMove() || isConstructionFeeArea(node) || isConstructionFeeArea(node.nextSibling);
  267. if (upLevelDisabled) {
  268. $('#budget-upLevel').addClass('disabled');
  269. } else {
  270. $('#budget-upLevel').removeClass('disabled');
  271. }
  272. if (downLevelDisabled) {
  273. $('#budget-downLevel').addClass('disabled');
  274. } else {
  275. $('#budget-downLevel').removeClass('disabled');
  276. }
  277. if (upMoveDisabled) {
  278. $('#budget-upMove').addClass('disabled');
  279. } else {
  280. $('#budget-upMove').removeClass('disabled');
  281. }
  282. if (downMoveDisabled) {
  283. $('#budget-downMove').addClass('disabled');
  284. } else {
  285. $('#budget-downMove').removeClass('disabled');
  286. }
  287. };
  288. // 表格选中相关
  289. const selectCell = (row, col, setCell = false) => {
  290. const node = tree.items[row];
  291. refreshToolsBar(node);
  292. tree.selected = node;
  293. console.log(node);
  294. const sheet = spread.getSheet(0);
  295. if (setCell) {
  296. sheet.setActiveCell(row, col);
  297. }
  298. //设置选中行底色和恢复前选中行底色
  299. const refreshNodes = [node];
  300. if (!tree.preSelected) {
  301. refreshNodes.push(tree.items[0]);
  302. } else {
  303. refreshNodes.push(tree.preSelected);
  304. }
  305. tree.preSelected = node;
  306. projectObj.setNodesStyle(sheet, refreshNodes, tree);
  307. };
  308. // 事件列表
  309. const events = {
  310. EnterCell(sender, args) {
  311. args.sheet.repaint();
  312. },
  313. SelectionChanged(sender, args) {
  314. const newSel = args.newSelections[0] ? { row: args.newSelections[0].row, col: args.newSelections[0].col } : { row: 0, col: 0 };
  315. selectCell(newSel.row, newSel.col);
  316. },
  317. ValueChanged(sender, args) {
  318. if (isEmptyVal(args.oldValue) && isEmptyVal(args.newValue)) {
  319. return;
  320. }
  321. edit(args.sheet, [{ row: args.row, col: args.col }]);
  322. },
  323. RangeChanged(sender, args) {
  324. edit(args.sheet, args.changedCells);
  325. }
  326. }
  327. const bindEvents = (sheet) => {
  328. Object.entries(events).forEach(([ev, evFunc]) => {
  329. sheet.bind(GC.Spread.Sheets.Events[ev], evFunc);
  330. });
  331. }
  332. /* 只读相关 */
  333. // 单元格锁定判断
  334. const lockFactory = {
  335. code(node) {
  336. return !!(node && node.getFlag());
  337. },
  338. name(node) {
  339. return !!(node && node.getFlag());
  340. },
  341. calcBase(node) {
  342. return !!(node && node.children.length);
  343. },
  344. feeRate(node) {
  345. return !!(node && node.children.length);
  346. }
  347. };
  348. const lockData = (sheet, nodes, isMass = true) => {
  349. const lock = () => {
  350. if (projectReadOnly) {
  351. sheet.getRange(0, 0, nodes.length, budgetSummaryTreeSetting.cols.length, GC.Spread.Sheets.SheetArea.viewport).locked(true);
  352. return 0;
  353. }
  354. // 工程费用区域,只读
  355. const constructionFeeNode = nodes.find(node => node.getFlag() === fixedFlag.CONSTRUCTION_FEE);
  356. if (!constructionFeeNode) {
  357. return 0;
  358. }
  359. const endIndex = constructionFeeNode.posterityCount() + 1;
  360. sheet.getRange(0, 0, endIndex, budgetSummaryTreeSetting.cols.length, GC.Spread.Sheets.SheetArea.viewport).locked(true);
  361. return endIndex;
  362. }
  363. if (isMass) {
  364. TREE_SHEET_HELPER.massOperationSheet(sheet, () => {
  365. return lock();
  366. });
  367. } else {
  368. return lock();
  369. }
  370. }
  371. /* 单元格类型 */
  372. const calcBaseButtonCallback = (hitInfo) => {
  373. calcBaseView.onCalcBaseButtonClick(hitInfo, 'budget');
  374. };
  375. const cellTypeFactory = {
  376. calcBase(node) {
  377. const locked = lockFactory.calcBase(node);
  378. return sheetCommonObj.getCusButtonCellType(calcBaseButtonCallback, locked);
  379. }
  380. }
  381. /* 设置可编辑区域节点的只读性和单元格类型 */
  382. const setCells = (sheet, startRow, nodes) => {
  383. for (let row = startRow; row < nodes.length; row++) {
  384. const node = nodes[row];
  385. budgetSummaryTreeSetting.cols.forEach((item, col) => {
  386. const field = item.data.field;
  387. // 锁定单元格
  388. const lockFunc = lockFactory[field];
  389. if (lockFunc) {
  390. const isLocked = lockFunc(node);
  391. sheet.getCell(row, col).locked(isLocked);
  392. }
  393. // 设置单元格类型
  394. const cellTypeFunc = cellTypeFactory[field];
  395. if (cellTypeFunc) {
  396. const cellType = cellTypeFunc(node);
  397. sheet.getCell(row, col).cellType(cellType);
  398. }
  399. // 单元格文本转换
  400. const textFunc = textFactory[field];
  401. if (textFunc) {
  402. sheet.setValue(row, col, textFunc(node));
  403. }
  404. });
  405. }
  406. }
  407. /* 初始化表格 */
  408. const initSpread = () => {
  409. if (!spread) {
  410. // spread = sheetCommonObj.createSpread($('#budget-summary-sheet')[0], 1);
  411. spread = SheetDataHelper.createNewSpread($('#budget-summary-sheet')[0]);
  412. sheetCommonObj.spreadDefaultStyle(spread);
  413. // 设置表头
  414. const sheet = spread.getSheet(0);
  415. bindEvents(sheet);
  416. const headers = sheetCommonObj.getHeadersFromTreeSetting(budgetSummaryTreeSetting);
  417. sheetCommonObj.setHeader(sheet, headers);
  418. // 右键菜单
  419. initContextMenu();
  420. } else {
  421. spread.refresh();
  422. }
  423. return spread;
  424. }
  425. // 初始化树
  426. const initTree = (data, sheet, setting) => {
  427. tree = idTree.createNew({ id: 'ID', pid: 'ParentID', nid: 'NextSiblingID', rootId: -1, autoUpdate: true });
  428. const controller = TREE_SHEET_CONTROLLER.createNew(tree, sheet, setting, false, true);
  429. tree.loadDatas(data);
  430. tree.items.forEach(node => {
  431. node.source = node;
  432. node.sourceType = ModuleNames.bills;
  433. });
  434. controller.showTreeData();
  435. sheet.setRowCount(data.length);
  436. setUnitCombo(sheet, data);
  437. TREE_SHEET_HELPER.massOperationSheet(sheet, () => {
  438. const startRow = lockData(sheet, tree.items, false);
  439. setCells(sheet, startRow, tree.items);
  440. // 表格格式化
  441. budgetSummaryTreeSetting.cols.forEach((item, index) => {
  442. sheet.setFormatter(-1, index, item.formatter || '@', GC.Spread.Sheets.SheetArea.viewport);
  443. });
  444. });
  445. selectCell(sheet.getActiveRowIndex(), sheet.getActiveColumnIndex());
  446. }
  447. /* 右键菜单 */
  448. // 更新树结构数据
  449. const updateTree = (sheet, updateData) => {
  450. // 更新数据
  451. updateData.forEach(item => {
  452. if (item.type === 'new') {
  453. orgMap[item.data.ID] = _.cloneDeep(item.data);
  454. rawData.push(item.data)
  455. } else if (item.type === 'update') {
  456. if (orgMap[item.data.ID]) {
  457. Object.assign(orgMap[item.data.ID], item.data);
  458. }
  459. const node = tree.findNode(item.data.ID);
  460. if (node) {
  461. Object.assign(node.data, item.data);
  462. }
  463. } else {
  464. delete orgMap[item.data.ID];
  465. const removeIndex = rawData.findIndex(d => d.ID === item.data.ID);
  466. if (removeIndex > -1) {
  467. rawData.splice(removeIndex, 1);
  468. }
  469. }
  470. });
  471. // 重新初始化树
  472. initTree(rawData, sheet, budgetSummaryTreeSetting);
  473. }
  474. let loading = false;
  475. // 插入
  476. const insert = async (sheet, selected) => {
  477. try {
  478. if (loading) {
  479. return;
  480. }
  481. loading = true;
  482. $.bootstrapLoading.start();
  483. const updateData = tree.getInsertData(selected.data.ParentID, selected.data.NextSiblingID, uuid.v1());
  484. const newData = updateData.filter(item => item.type === 'new');
  485. newData.forEach(item => {
  486. item.data.fees = [];
  487. item.data.flags = [];
  488. item.feesIndex = {};
  489. item.flagsIndex = {};
  490. item.data.type = selected.data.type;
  491. item.data.projectID = projectObj.project.property.rootProjectID;
  492. });
  493. await bulkOperation(updateData);
  494. updateTree(sheet, updateData);
  495. selectCell(sheet.getActiveRowIndex() + selected.posterityCount() + 1, sheet.getActiveColumnIndex(), true);
  496. } catch (err) {
  497. alert(err);
  498. } finally {
  499. $.bootstrapLoading.end();
  500. loading = false;
  501. }
  502. }
  503. // 删除
  504. const remove = async (sheet, selected) => {
  505. try {
  506. if (loading) {
  507. return;
  508. }
  509. loading = true;
  510. $.bootstrapLoading.start();
  511. const updateData = tree.getDeleteData(selected);
  512. await bulkOperation(updateData);
  513. updateTree(sheet, updateData);
  514. } catch (err) {
  515. alert(err);
  516. } finally {
  517. $.bootstrapLoading.end();
  518. loading = false;
  519. }
  520. }
  521. // 升级
  522. const upLevel = async (selected) => {
  523. if (!spread || !tree) {
  524. return;
  525. }
  526. const sheet = spread.getSheet(0);
  527. selected = selected ? selected : tree.selected;
  528. try {
  529. if (loading || upLevelDisabled) {
  530. return;
  531. }
  532. loading = true;
  533. $.bootstrapLoading.start();
  534. const updateData = selected.getUpLevelData();
  535. await bulkOperation(updateData);
  536. updateTree(sheet, updateData);
  537. } catch (err) {
  538. alert(err);
  539. } finally {
  540. $.bootstrapLoading.end();
  541. loading = false;
  542. }
  543. }
  544. // 降级
  545. const downLevel = async (selected) => {
  546. if (!spread || !tree) {
  547. return;
  548. }
  549. const sheet = spread.getSheet(0);
  550. selected = selected ? selected : tree.selected;
  551. try {
  552. if (loading || downLevelDisabled) {
  553. return;
  554. }
  555. loading = true;
  556. $.bootstrapLoading.start();
  557. const updateData = selected.getDownLevelData();
  558. await bulkOperation(updateData);
  559. updateTree(sheet, updateData);
  560. } catch (err) {
  561. alert(err);
  562. } finally {
  563. $.bootstrapLoading.end();
  564. loading = false;
  565. }
  566. }
  567. // 上移
  568. const upMove = async (selected) => {
  569. if (!spread || !tree) {
  570. return;
  571. }
  572. const sheet = spread.getSheet(0);
  573. selected = selected ? selected : tree.selected;
  574. try {
  575. if (loading || upMoveDisabled) {
  576. return;
  577. }
  578. loading = true;
  579. $.bootstrapLoading.start();
  580. const updateData = selected.getUpMoveData();
  581. await bulkOperation(updateData);
  582. updateTree(sheet, updateData);
  583. const prev = selected.preSibling;
  584. const row = sheet.getActiveRowIndex() - prev.posterityCount() - 1;
  585. selectCell(row, sheet.getActiveColumnIndex(), true);
  586. sheet.showRow(row, GC.Spread.Sheets.VerticalPosition.center);
  587. } catch (err) {
  588. alert(err);
  589. } finally {
  590. $.bootstrapLoading.end();
  591. loading = false;
  592. }
  593. }
  594. // 下移
  595. const downMove = async (selected) => {
  596. if (!spread || !tree) {
  597. return;
  598. }
  599. const sheet = spread.getSheet(0);
  600. selected = selected ? selected : tree.selected;
  601. try {
  602. if (loading || downMoveDisabled) {
  603. return;
  604. }
  605. loading = true;
  606. $.bootstrapLoading.start();
  607. const updateData = selected.getDownMoveData();
  608. await bulkOperation(updateData);
  609. updateTree(sheet, updateData);
  610. const next = selected.nextSibling;
  611. const row = sheet.getActiveRowIndex() + next.posterityCount() + 1;
  612. selectCell(row, sheet.getActiveColumnIndex(), true);
  613. sheet.showRow(row, GC.Spread.Sheets.VerticalPosition.center);
  614. } catch (err) {
  615. alert(err);
  616. } finally {
  617. $.bootstrapLoading.end();
  618. loading = false;
  619. }
  620. }
  621. // 初始化右键菜单
  622. const initContextMenu = () => {
  623. if (!spread) {
  624. return;
  625. }
  626. let curRow;
  627. let curNode;
  628. const sheet = spread.getSheet(0);
  629. $.contextMenu({
  630. selector: '#budget-summary-sheet',
  631. build: function ($trigger, e) {
  632. const target = SheetDataHelper.safeRightClickSelection($trigger, e, spread);
  633. curRow = target.row;
  634. curNode = tree && tree.items[curRow] || null;
  635. selectCell(target.row, target.col, true);
  636. return target.hitTestType === GC.Spread.Sheets.SheetArea.viewport || target.hitTestType === GC.Spread.Sheets.SheetArea.rowHeader;
  637. },
  638. items: {
  639. insert: {
  640. name: '插入行',
  641. icon: 'fa-sign-in',
  642. disabled() {
  643. return !curNode || (curNode.data.area === BudgetArea.CONSTRUCTION_FEE && curNode.getFlag() !== fixedFlag.CONSTRUCTION_FEE);
  644. },
  645. callback() {
  646. insert(sheet, curNode);
  647. }
  648. },
  649. remove: {
  650. name: '删除行',
  651. icon: 'fa-remove',
  652. disabled() {
  653. return !curNode || isConstructionFeeArea(curNode) || curNode.getFlag();
  654. },
  655. callback() {
  656. remove(sheet, curNode);
  657. }
  658. },
  659. refresh: {
  660. name: '刷新数据',
  661. icon: 'fa-refresh',
  662. callback() {
  663. init(projectObj.project.property.rootProjectID);
  664. }
  665. },
  666. }
  667. });
  668. }
  669. // 初始化
  670. const init = async (constructionID) => {
  671. try {
  672. $.bootstrapLoading.start();
  673. // 得先计算费用汇总(概算汇总计算基于费用汇总算出来的总金额)
  674. await projectObj.project.calcProgram.getGatherFeeData();
  675. const { treeData, costGrowthRate, growthPeriod } = await ajaxPost('/bills/initialBudgetSummary', { constructionID });
  676. calcSetting.costGrowthRate = costGrowthRate;
  677. calcSetting.growthPeriod = growthPeriod;
  678. $('#costGrowthRate').val(costGrowthRate);
  679. $('#growthPeriod').val(growthPeriod);
  680. rawData = treeData;
  681. rawData.forEach((item) => {
  682. if (item.quantity) {
  683. item.quantity = parseFloat(item.quantity);
  684. }
  685. item.feesIndex = getFeeIndex(item.fees);
  686. item.flagsIndex = {};
  687. if (item.flags) {
  688. item.flags.forEach((flag) => {
  689. item.flagsIndex[flag.fieldName] = flag;
  690. });
  691. }
  692. });
  693. const spread = initSpread();
  694. const sheet = spread.getSheet(0);
  695. rawData.forEach(item => {
  696. orgMap[item.ID] = _.cloneDeep(item);
  697. });
  698. initTree(rawData, sheet, budgetSummaryTreeSetting);
  699. calcBase.initBudget();
  700. // 造价计算
  701. await edit(sheet, [], tree.roots.slice(1));
  702. } catch (err) {
  703. console.log(err);
  704. alert(err);
  705. } finally {
  706. $.bootstrapLoading.end();
  707. }
  708. }
  709. // 点击tab,重新初始化
  710. $('#tab-budget-summary').click(function () {
  711. if (!$(this).hasClass('active')) {
  712. init(projectObj.project.property.rootProjectID);
  713. }
  714. });
  715. $('#budget-upLevel').click(() => {
  716. upLevel();
  717. });
  718. $('#budget-downLevel').click(() => {
  719. downLevel();
  720. });
  721. $('#budget-upMove').click(() => {
  722. upMove();
  723. });
  724. $('#budget-downMove').click(() => {
  725. downMove();
  726. });
  727. /* 建设项目设置 */
  728. $('#openConstructionSet').click(() => {
  729. $('#constructionSet').modal('show');
  730. });
  731. function isKeyNumber(keyCode) {
  732. // 数字
  733. if (keyCode >= 48 && keyCode <= 57) {
  734. return true;
  735. } else if (keyCode >= 96 && keyCode <= 105) { //小键盘数字
  736. return true;
  737. } else if (keyCode == 8 || keyCode == 46 || keyCode == 37 || keyCode == 39 || keyCode == 108 || keyCode == 110) { // Backspace, del, 左右方向键
  738. return true;
  739. } else if (keyCode >= 112 && keyCode <= 123) { //F1 -F12
  740. return true;
  741. }
  742. return false;
  743. }
  744. //年造价增涨率
  745. $('#costGrowthRate').keydown(function (e) {
  746. return isKeyNumber(e.keyCode);
  747. });
  748. //计费年限
  749. $("#growthPeriod").keydown(function (e) {
  750. return isKeyNumber(e.keyCode);
  751. });
  752. // 确认设置
  753. $('#construction-set-ok').click(async () => {
  754. try {
  755. $.bootstrapLoading.start();
  756. const curCostGrowthRate = $('#costGrowthRate').val();
  757. const curGrowthPeriod = $('#growthPeriod').val();
  758. const bulkData = [
  759. {
  760. type: 'updateProject',
  761. data: {
  762. ID: projectObj.project.property.rootProjectID,
  763. 'property.costGrowthRate': curCostGrowthRate,
  764. 'property.growthPeriod': curGrowthPeriod,
  765. }
  766. }
  767. ];
  768. await bulkOperation(bulkData);
  769. calcSetting.costGrowthRate = curCostGrowthRate;
  770. calcSetting.growthPeriod = curGrowthPeriod;
  771. } catch (err) {
  772. alert(err);
  773. $('#costGrowthRate').val(calcSetting.costGrowthRate);
  774. $('#growthPeriod').val(calcSetting.growthPeriod);
  775. } finally {
  776. $.bootstrapLoading.end();
  777. }
  778. });
  779. // 对外暴露
  780. return {
  781. getTree: () => tree,
  782. getSheet: () => spread.getSheet(0),
  783. calcSetting,
  784. edit,
  785. };
  786. })();