budgetSummarySheet.js 25 KB

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