budgetSummarySheet.js 25 KB

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