budgetSummarySheet.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. /* 建设其他费表格相关 */
  2. const budgetSummaryObj = (() => {
  3. const { isDef, isNumber } = window.commonUtil;
  4. const { fixedFlag, BudgetArea } = window.commonConstants;
  5. // 原始数据
  6. let rawData = [];
  7. // 建设其他费表格对象
  8. let spread = null;
  9. // 建设其他费树
  10. let tree = null;
  11. // 单位设置下拉框
  12. const setUnitCombo = (sheet, data) => {
  13. const unitCol = budgetSummaryTreeSetting.cols.findIndex(item => item.data.field === 'unit');
  14. if (unitCol >= 0) {
  15. TREE_SHEET_HELPER.massOperationSheet(sheet, () => {
  16. const comboBox = sheetCommonObj.getDynamicCombo();
  17. comboBox
  18. .itemHeight(10)
  19. .items(['m', 'm2', 'm3', 'km', 't', 'kg', '台班', '工日', '昼夜', '元', '项', '处', '个', '件',
  20. '根', '组', '系统', '台', '套', '株', '丛', '缸', '支', '只', '块', '座', '对', '份', '樘', '攒', '榀'])
  21. .editable(true);
  22. data.forEach((item, index) => {
  23. sheet.getCell(index, unitCol).cellType(comboBox);
  24. })
  25. });
  26. }
  27. }
  28. const getFieldByCol = (col) => {
  29. const item = budgetSummaryTreeSetting.cols[col];
  30. return item && item.data && item.data.field || null;
  31. }
  32. // 单元格值验证器
  33. const validator = {
  34. text() {
  35. return true
  36. },
  37. number(val) {
  38. return !isDef(val) || isNumber(val);
  39. },
  40. };
  41. const getValidator = (col) => {
  42. const item = budgetSummaryTreeSetting.cols[col];
  43. if (!item) {
  44. return 'text';
  45. }
  46. return validator[item.data.type || 'text'];
  47. }
  48. /* 表格事件相关 */
  49. // 恢复数据
  50. const recover = (sheet, changedCells) => {
  51. if (!tree) {
  52. return;
  53. }
  54. changedCells.forEach(({ row, col }) => {
  55. const node = tree.items[row];
  56. const field = getFieldByCol(col);
  57. if (!field || !node) {
  58. return;
  59. }
  60. const orgVal = node.data[field];
  61. sheet.setValue(row, col, orgVal);
  62. })
  63. }
  64. // 编辑相关
  65. const edit = (sheet, changedCells) => {
  66. // 单元格值验证
  67. const isValid = changedCells.every(({ row, col }) => {
  68. const val = sheet.getValue(row, col);
  69. return getValidator(col)(val);
  70. });
  71. // 验证不通过,恢复
  72. if (!isValid) {
  73. recover(sheet, changedCells);
  74. }
  75. }
  76. const events = {
  77. EnterCell(sender, args) {
  78. args.sheet.repaint();
  79. },
  80. ValueChanged(sender, args) {
  81. edit(args.sheet, [{ row: args.row, col: args.col }]);
  82. },
  83. RangeChanged(sender, args) {
  84. edit(args.sheet, args.changedCells);
  85. }
  86. }
  87. const bindEvents = (sheet) => {
  88. Object.entries(events).forEach(([ev, evFunc]) => {
  89. sheet.bind(GC.Spread.Sheets.Events[ev], evFunc)
  90. })
  91. }
  92. /* 只读相关 */
  93. const lockData = (sheet, nodes) => {
  94. TREE_SHEET_HELPER.massOperationSheet(sheet, () => {
  95. // 工程费用区域,只读
  96. const equipmentNode = nodes.find(node => node.getFlag() === fixedFlag.CONSTRUCTION_EQUIPMENT_FEE);
  97. if (equipmentNode) {
  98. sheet.getRange(0, 0, equipmentNode.serialNo() + 1, budgetSummaryTreeSetting.cols.length, GC.Spread.Sheets.SheetArea.viewport).locked(true);
  99. }
  100. });
  101. }
  102. /* 初始化表格 */
  103. const initSpread = () => {
  104. if (!spread) {
  105. // spread = sheetCommonObj.createSpread($('#budget-summary-sheet')[0], 1);
  106. spread = SheetDataHelper.createNewSpread($('#budget-summary-sheet')[0]);
  107. sheetCommonObj.spreadDefaultStyle(spread);
  108. // 设置表头
  109. const sheet = spread.getSheet(0);
  110. bindEvents(sheet);
  111. const headers = sheetCommonObj.getHeadersFromTreeSetting(budgetSummaryTreeSetting);
  112. sheetCommonObj.setHeader(sheet, headers);
  113. initContextMenu();
  114. } else {
  115. spread.refresh();
  116. }
  117. return spread;
  118. }
  119. // 初始化树
  120. const initTree = (data, sheet, setting) => {
  121. tree = idTree.createNew({ id: 'ID', pid: 'ParentID', nid: 'NextSiblingID', rootId: -1, autoUpdate: true });
  122. const controller = TREE_SHEET_CONTROLLER.createNew(tree, sheet, setting, false);
  123. tree.loadDatas(data);
  124. controller.showTreeData();
  125. sheet.setRowCount(data.length);
  126. setUnitCombo(sheet, data);
  127. lockData(sheet, tree.items);
  128. }
  129. /* 右键菜单 */
  130. // 更新树结构数据
  131. const updateTree = (sheet, updateData) => {
  132. // 更新数据
  133. updateData.forEach(item => {
  134. if (item.type === 'new') {
  135. rawData.push(item.data)
  136. } else if (item.type === 'update') {
  137. const node = tree.findNode(item.data.ID);
  138. if (node) {
  139. Object.assign(node.data, item.data);
  140. }
  141. } else {
  142. const removeIndex = rawData.findIndex(d => d.ID === item.data.ID);
  143. if (removeIndex > -1) {
  144. rawData.splice(removeIndex, 1);
  145. }
  146. }
  147. });
  148. // 重新初始化树
  149. initTree(rawData, sheet, budgetSummaryTreeSetting);
  150. }
  151. // 插入
  152. const insert = (sheet, selected) => {
  153. const updateData = tree.getInsertData(selected.data.ParentID, selected.data.NextSiblingID, uuid.v1());
  154. updateTree(sheet, updateData);
  155. sheet.setActiveCell(sheet.getActiveRowIndex() + selected.posterityCount() + 1, sheet.getActiveColumnIndex())
  156. }
  157. // 删除
  158. const remove = (sheet, selected) => {
  159. const updateData = tree.getDeleteData(selected);
  160. updateTree(sheet, updateData);
  161. }
  162. // 升级
  163. const upLevel = (sheet, selected) => {
  164. const updateData = selected.getUpLevelData();
  165. updateTree(sheet, updateData);
  166. }
  167. // 降级
  168. const downLevel = (sheet, selected) => {
  169. const updateData = selected.getDownLevelData();
  170. updateTree(sheet, updateData);
  171. }
  172. // 上移
  173. const upMove = (sheet, selected) => {
  174. const updateData = selected.getUpMoveData();
  175. updateTree(sheet, updateData);
  176. const prev = selected.preSibling;
  177. const row = sheet.getActiveRowIndex() - prev.posterityCount() - 1;
  178. sheet.setActiveCell(row, sheet.getActiveColumnIndex());
  179. sheet.showRow(row, GC.Spread.Sheets.VerticalPosition.center);
  180. }
  181. // 下移
  182. const downMove = (sheet, selected) => {
  183. const updateData = selected.getDownMoveData();
  184. updateTree(sheet, updateData);
  185. const next = selected.nextSibling;
  186. const row = sheet.getActiveRowIndex() + next.posterityCount() + 1;
  187. sheet.setActiveCell(row, sheet.getActiveColumnIndex())
  188. sheet.showRow(row, GC.Spread.Sheets.VerticalPosition.center);
  189. }
  190. // 是否是属于工程费用区域的节点
  191. const isConstructionFeeArea = (node) => {
  192. return node && node.data && node.data.area === BudgetArea.CONSTRUCTION_FEE;
  193. }
  194. // 初始化右键菜单
  195. const initContextMenu = () => {
  196. if (!spread) {
  197. return;
  198. }
  199. let curRow;
  200. let curNode;
  201. const sheet = spread.getSheet(0);
  202. $.contextMenu({
  203. selector: '#budget-summary-sheet',
  204. build: function ($trigger, e) {
  205. const target = SheetDataHelper.safeRightClickSelection($trigger, e, spread);
  206. curRow = target.row;
  207. curNode = tree && tree.items[curRow] || null;
  208. sheet.setActiveCell(target.row, target.col);
  209. return target.hitTestType === GC.Spread.Sheets.SheetArea.viewport || target.hitTestType === GC.Spread.Sheets.SheetArea.rowHeader;
  210. },
  211. items: {
  212. insert: {
  213. name: '插入行',
  214. icon: 'fa-sign-in',
  215. disabled() {
  216. return !curNode || (curNode.data.area === BudgetArea.CONSTRUCTION_FEE && curNode.getFlag() !== fixedFlag.CONSTRUCTION_FEE);
  217. },
  218. callback() {
  219. insert(sheet, curNode);
  220. }
  221. },
  222. remove: {
  223. name: '删除',
  224. icon: 'fa-remove',
  225. disabled() {
  226. return !curNode || isConstructionFeeArea(curNode);
  227. },
  228. callback() {
  229. remove(sheet, curNode);
  230. }
  231. },
  232. upLevel: {
  233. name: '升级',
  234. icon: 'fa-arrow-left',
  235. disabled() {
  236. return !curNode || !curNode.canUpLevel() || isConstructionFeeArea(curNode);
  237. },
  238. callback() {
  239. upLevel(sheet, curNode);
  240. }
  241. },
  242. downLevel: {
  243. name: '降级',
  244. icon: 'fa-arrow-right',
  245. disabled() {
  246. return !curNode || !curNode.canDownLevel() || isConstructionFeeArea(curNode);
  247. },
  248. callback() {
  249. downLevel(sheet, curNode);
  250. }
  251. },
  252. upMove: {
  253. name: '上移',
  254. icon: 'fa-arrow-up',
  255. disabled() {
  256. return !curNode || !curNode.canUpMove() || isConstructionFeeArea(curNode) || isConstructionFeeArea(curNode.preSibling);
  257. },
  258. callback() {
  259. upMove(sheet, curNode);
  260. }
  261. },
  262. downMove: {
  263. name: '下移',
  264. icon: 'fa-arrow-down',
  265. disabled() {
  266. return !curNode || !curNode.canDownMove() || isConstructionFeeArea(curNode) || isConstructionFeeArea(curNode.nextSibling);
  267. },
  268. callback() {
  269. downMove(sheet, curNode);
  270. }
  271. },
  272. refresh: {
  273. name: '刷新数据',
  274. icon: 'fa-refresh',
  275. callback() {
  276. init(projectObj.project.property.rootProjectID);
  277. }
  278. },
  279. }
  280. });
  281. }
  282. // 初始化
  283. const init = async (constructionID) => {
  284. try {
  285. $.bootstrapLoading.start();
  286. rawData = await ajaxPost('/bills/getBudgetSummary', { constructionID });
  287. rawData.forEach((item) => {
  288. if (item.quantity) {
  289. item.quantity = parseFloat(item.quantity);
  290. }
  291. item.feesIndex = getFeeIndex(item.fees);
  292. item.flagsIndex = {};
  293. if (item.flags) {
  294. item.flags.forEach((flag) => {
  295. item.flagsIndex[flag.fieldName] = flag;
  296. });
  297. }
  298. });
  299. const spread = initSpread();
  300. const sheet = spread.getSheet(0);
  301. initTree(rawData, sheet, budgetSummaryTreeSetting);
  302. } catch (err) {
  303. console.log(err);
  304. alert(err);
  305. } finally {
  306. $.bootstrapLoading.end();
  307. }
  308. }
  309. // 点击tab,重新初始化
  310. $('#tab-budget-summary').click(function () {
  311. if (!$(this).hasClass('active')) {
  312. init(projectObj.project.property.rootProjectID);
  313. }
  314. });
  315. // 对外暴露
  316. return {
  317. getTree: () => tree,
  318. };
  319. })();