overHeight.js 47 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207
  1. /*
  2. * @Descripttion: 超高降效相关
  3. * @Author: Zhong
  4. * @Date: 2019-12-19 17:45:40
  5. */
  6. /*
  7. * 整体逻辑为:
  8. * 1.一个请求中
  9. * {删除}所有超高子目
  10. * {更新}清单、定额下拉列文本
  11. * {新增}清单、定额、定额人材机、项目人材机
  12. * 2.一个请求{重算被删除的定额清单节点、新增的定额节点
  13. */
  14. const OVER_HEIGHT = (() => {
  15. // 选项类型,生成的超高子目所在位置
  16. const Option = {
  17. // 对应清单或分部下(默认)
  18. SEPARATION: 1,
  19. // 指定措施清单011704001
  20. MEASURE: 2,
  21. // 指定具体位置,显示分部分项以及措施项目的树结构显示叶子清单(分项)供勾选
  22. SPECIFIC: 3,
  23. };
  24. // 系数类别,类别不同,生成的定额人材机和算法不同
  25. const RateType = {
  26. LABOUR: 1,
  27. MACHINE: 2,
  28. LABOUR_MACHINE: 3,
  29. };
  30. // 系数类别对应的汇总定额金额字段(feeIndex里的字段)
  31. const FeeField = {
  32. [RateType.LABOUR]: 'labour',
  33. [RateType.MACHINE]: 'machine',
  34. [RateType.LABOUR_MACHINE]: 'material',
  35. };
  36. // 系数类别的定额人材机基础数据,不同系数类别,对应的人材机编码、名称等不同
  37. const BaseRatoinGLJ = {
  38. [RateType.LABOUR]: {
  39. code: '00010052',
  40. original_code: '00010052',
  41. name: '人工降效',
  42. type: gljType.LABOUR,
  43. shorName: '人',
  44. },
  45. [RateType.MACHINE]: {
  46. code: '99918004',
  47. original_code: '99918004',
  48. name: '机械降效',
  49. type: gljType.GENERAL_MACHINE,
  50. shorName: '机',
  51. },
  52. [RateType.LABOUR_MACHINE]: {
  53. code: '00010053',
  54. original_code: '00010053',
  55. name: '人工、机械降效增加费',
  56. type: gljType.GENERAL_MATERIAL,
  57. shorName: '材',
  58. },
  59. }
  60. // 选项二时的前九位清单编号
  61. const fixedCode = '011704001';
  62. const fixedCodeReg = new RegExp(`^${fixedCode}`);
  63. // 取费专业名称
  64. const programName = '超高降效';
  65. //const programName = '公共建筑工程'; // for Test
  66. // 指定清单表格
  67. let specificSpread = null;
  68. // 指定清单树
  69. let specificTree = null;
  70. // 指定清单窗口界面设置
  71. const specificTreeSetting = {
  72. emptyRowHeader: true,
  73. rowHeaderWidth: 15,
  74. treeCol: 0,
  75. emptyRows: 0,
  76. headRows: 1,
  77. headRowHeight: [40],
  78. defaultRowHeight: 21,
  79. cols: [{
  80. width: 140,
  81. readOnly: true,
  82. head: {
  83. titleNames: ["编码"],
  84. spanCols: [1],
  85. spanRows: [1],
  86. vAlign: [1],
  87. hAlign: [1],
  88. font: ["Arial"]
  89. },
  90. data: {
  91. field: "code",
  92. vAlign: 1,
  93. hAlign: 0,
  94. font: "Arial"
  95. }
  96. }, {
  97. width: 45,
  98. readOnly: true,
  99. head: {
  100. titleNames: ["类型"],
  101. spanCols: [1],
  102. spanRows: [1],
  103. vAlign: [1],
  104. hAlign: [1],
  105. font: ["Arial"]
  106. },
  107. data: {
  108. field: "subType",
  109. vAlign: 1,
  110. hAlign: 1,
  111. font: "Arial"
  112. }
  113. },
  114. {
  115. width: 205,
  116. readOnly: true,
  117. head: {
  118. titleNames: ["名称"],
  119. spanCols: [1],
  120. spanRows: [1],
  121. vAlign: [1],
  122. hAlign: [1],
  123. font: ["Arial"]
  124. },
  125. data: {
  126. field: "name",
  127. vAlign: 1,
  128. hAlign: 0,
  129. font: "Arial"
  130. }
  131. },
  132. {
  133. width: 60,
  134. readOnly: true,
  135. head: {
  136. titleNames: ["具体位置"],
  137. spanCols: [1],
  138. spanRows: [1],
  139. vAlign: [1],
  140. hAlign: [1],
  141. font: ["Arial"]
  142. },
  143. data: {
  144. field: "specific",
  145. vAlign: 1,
  146. hAlign: 1,
  147. font: "Arial"
  148. }
  149. },
  150. ]
  151. };
  152. // 源数据
  153. let sourceData;
  154. // 下拉项
  155. let comboData;
  156. // 初始化源数据和下拉项数据
  157. function init(source) {
  158. sourceData = source || [];
  159. comboData = sourceData
  160. ? sourceData
  161. .filter(item => !item.extra || !JSON.parse(item.extra))
  162. .map(item => item.name)
  163. : [];
  164. }
  165. // 获取下拉项
  166. function getComboData() {
  167. return comboData;
  168. }
  169. // 根据名称获取下拉项索引
  170. function getIndex(name) {
  171. return comboData.findIndex(item => item === name);
  172. }
  173. function getOverHeightItem(value) {
  174. return sourceData.find(item => item.name === value);
  175. }
  176. // 获取系数
  177. function getRate(overHeightItem) {
  178. return overHeightItem.labourRate
  179. || overHeightItem.machineRate
  180. || overHeightItem.labourMachineRate
  181. || null;
  182. }
  183. // 下拉项是否需要计算(生成子目)
  184. function isNeedToCalc(overHeightItem) {
  185. if (!overHeightItem) {
  186. return false;
  187. }
  188. const rate = getRate(overHeightItem);
  189. return !!rate;
  190. }
  191. // 是否是超高子目
  192. function isOverHeight(node) {
  193. return node
  194. && node.sourceType === ModuleNames.ration
  195. && node.data.type === rationType.overHeight;
  196. }
  197. // 获取超高降效列号
  198. function getOverHeightCol() {
  199. return projectObj.project.projSetting.main_tree_col.cols.findIndex(item => item.data.field === 'overHeight');
  200. }
  201. // 获取指定清单指定列
  202. function getSpecificCol() {
  203. return specificTreeSetting.cols.findIndex(item => item.data.field === 'specific');
  204. }
  205. // 获取触发动作:选项1、选项2、选项3,选项3时需要指定清单ID(specificID)
  206. function getAction() {
  207. return {
  208. option: projectObj.project.projectInfo.property.overHeightOption || Option.SEPARATION,
  209. specificID: projectObj.project.projectInfo.property.overHeightSpecificID || null,
  210. };
  211. }
  212. // 超高降效列的控制,右键计取触发
  213. function switchVisible(visible) {
  214. const curVisible = colSettingObj.getVisible('overHeight');
  215. if (curVisible === visible) {
  216. return;
  217. }
  218. colSettingObj.setVisible('overHeight', visible);
  219. colSettingObj.updateColSetting(true);
  220. }
  221. // 获取系数类型列表
  222. function getRateTypeList(overHeightItem) {
  223. const rst = [];
  224. if (commonUtil.isNumber(overHeightItem.labourRate)) {
  225. rst.push({ type: RateType.LABOUR, rate: overHeightItem.labourRate });
  226. }
  227. if (commonUtil.isNumber(overHeightItem.machineRate)) {
  228. rst.push({ type: RateType.MACHINE, rate: overHeightItem.machineRate });
  229. }
  230. if (commonUtil.isNumber(overHeightItem.labourMachineRate)) {
  231. rst.push({ type: RateType.LABOUR_MACHINE, rate: overHeightItem.labourMachineRate });
  232. }
  233. return rst;
  234. }
  235. // 有效化变化节点的值,若值为无效值(下拉项中不存在),则将变化节点的值设成原值
  236. function validateData(changedData) {
  237. changedData.forEach(item => {
  238. if (!comboData.includes(item.value)) {
  239. item.value = item.node.data.overHeight;
  240. }
  241. });
  242. }
  243. // 简化变化节点:由于子项值继承父项,且变更节点中可能存在父子关系,因此需要去除子项节点
  244. function simplifyData(changedData) {
  245. const rst = [];
  246. const nodes = changedData.map(item => item.node);
  247. changedData.forEach(item => {
  248. let parent = item.parent;
  249. // 父项不存在变化节点中才将此数据放入返回数组中
  250. while (parent) {
  251. if (nodes.includes(parent)) {
  252. return;
  253. }
  254. parent = parent.parent;
  255. }
  256. rst.push(item);
  257. });
  258. return rst;
  259. }
  260. // 设置单元格文本,单元格文本数据为暂存数据,方便后续获取更新、新增数据,若后续操作失败,则可用节点数据恢复单元格文本内容。
  261. function setTexts(changedData) {
  262. const sheet = projectObj.mainController.sheet;
  263. const func = () => {
  264. const overHeightCol = getOverHeightCol();
  265. changedData.forEach(item => {
  266. // 子项值随父项
  267. const nodes = [item.node, ...item.node.getPosterity()];
  268. nodes.forEach(node => {
  269. const row = node.serialNo();
  270. // 单元格没被锁定才填写暂存值
  271. const locked = sheet.getCell(row, overHeightCol).locked();
  272. if (!locked) {
  273. sheet.setText(row, overHeightCol, item.value)
  274. }
  275. });
  276. });
  277. };
  278. TREE_SHEET_HELPER.massOperationSheet(sheet, func);
  279. }
  280. // 获取其他措施费项目底下固定的节点(011704001...): 选项二时
  281. function getMeasureFixedNode() {
  282. const otherMeasureNode = projectObj.project.mainTree.items.find(node => node.getFlag() === fixedFlag.OTHER_MEASURE_FEE);
  283. const measureChildren = otherMeasureNode.getPosterity();
  284. return measureChildren.find(node => node.data.code && fixedCodeReg.test(node.data.code));
  285. }
  286. // 获取指定清单节点:选项三时
  287. function getSpecificNode(specificID) {
  288. return specificID
  289. ? projectObj.project.mainTree.nodes[`id_${specificID}`]
  290. : null;
  291. }
  292. // 变更超高降效列的操作检验,若选项为2、3时,需检验指定清单是否还存在,不存在则取消操作和提示
  293. function checkAction(action) {
  294. const { option, specificID } = action;
  295. if (option === Option.SEPARATION) {
  296. return true;
  297. } else if (option === Option.MEASURE) {
  298. const isValid = !!getMeasureFixedNode();
  299. if (!isValid) {
  300. $('#overHeightMeasure').modal('show');
  301. }
  302. return isValid;
  303. } else if (option) {
  304. const isValid = !!getSpecificNode(specificID);
  305. if (!isValid) {
  306. $('#overHeightSpecific').modal('show');
  307. }
  308. return isValid;
  309. }
  310. }
  311. // 设置指定清单界面选择框
  312. function setCheckBox(sheet, specificNodes) {
  313. const checkBox = new GC.Spread.Sheets.CellTypes.CheckBox();
  314. const specificCol = getSpecificCol();
  315. // 只有叶子节点和非计算基数节点才是选择框
  316. const func = () => {
  317. specificNodes.forEach((node, index) => {
  318. if (!node.children.length && !node.data.calcBase) {
  319. sheet.getCell(index, specificCol).cellType(checkBox);
  320. }
  321. });
  322. };
  323. TREE_SHEET_HELPER.massOperationSheet(sheet, func);
  324. }
  325. // 初始化指定清单选择树
  326. function initSpecificTree(data, sheet, setting) {
  327. specificTree = idTree.createNew({ id: 'ID', pid: 'ParentID', nid: 'NextSiblingID', rootId: -1, autoUpdate: true });
  328. const controller = TREE_SHEET_CONTROLLER.createNew(specificTree, sheet, setting, false);
  329. specificTree.loadDatas(data);
  330. controller.showTreeData();
  331. sheet.setRowCount(data.length);
  332. setCheckBox(sheet, specificTree.items);
  333. }
  334. // 表格点击事件,只有checkbox单元格会触发这个方法
  335. function handleSpreadButtonClick(e, args) {
  336. const { sheet, row, col } = args;
  337. // 只能单选,清空其他单元格的值并设置当前值
  338. const func = () => {
  339. const rowCount = sheet.getRowCount();
  340. const oldValue = sheet.getValue(row, col);
  341. for (let row = 0; row < rowCount; row++) {
  342. sheet.setValue(row, col, '');
  343. }
  344. sheet.setValue(row, col, !oldValue);
  345. }
  346. TREE_SHEET_HELPER.massOperationSheet(sheet, func);
  347. }
  348. // 初始化表格
  349. function initWorkbook() {
  350. specificSpread = sheetCommonObj.createSpread($('#specificArea')[0], 1);
  351. specificSpread.options.allowUserDragDrop = false; // 不允许拖填充,影响点击
  352. sheetCommonObj.spreadDefaultStyle(specificSpread);
  353. specificSpread.bind(GC.Spread.Sheets.Events.ButtonClicked, handleSpreadButtonClick);
  354. // 设置表头
  355. const sheet = specificSpread.getSheet(0);
  356. const headers = sheetCommonObj.getHeadersFromTreeSetting(specificTreeSetting);
  357. sheetCommonObj.setHeader(sheet, headers);
  358. return specificSpread;
  359. }
  360. // 从造价书界面筛选获取指定清单界面数据
  361. function getSpecificData() {
  362. // 只显示分部分项、措施项目的清单节点
  363. const billsNodes = projectObj.project.Bills.tree.items;
  364. const flags = [fixedFlag.SUB_ENGINERRING, fixedFlag.MEASURE];
  365. const billsData = flags.reduce((allData, flag) => {
  366. const fixedNode = billsNodes.find(node => node.getFlag() === flag);
  367. const posterity = fixedNode.getPosterity();
  368. const data = [fixedNode, ...posterity].map(node =>
  369. ({
  370. ID: node.data.ID,
  371. ParentID: node.data.ParentID,
  372. NextSiblingID: node.data.NextSiblingID,
  373. code: node.data.code,
  374. subType: billText[node.data.type],
  375. name: node.data.name,
  376. calcBase: node.data.calcBase,
  377. }));
  378. allData.push(...data);
  379. return allData;
  380. }, []);
  381. return billsData;
  382. }
  383. // 设置指定ID选中
  384. function setSpecific(ID) {
  385. if (!ID) {
  386. return;
  387. }
  388. const sheet = specificSpread.getSheet(0);
  389. const nodes = specificTree.items;
  390. const index = nodes.findIndex(node => node.data.ID === ID);
  391. if (!~index) {
  392. return;
  393. }
  394. const col = getSpecificCol();
  395. sheet.setValue(index, col, true);
  396. }
  397. // 从表格中获取勾选的指定ID
  398. function getSpecificFromSheet() {
  399. const sheet = specificSpread.getSheet(0);
  400. const nodes = specificTree.items;
  401. const rowCount = sheet.getRowCount();
  402. const col = getSpecificCol();
  403. for (let row = 0; row < rowCount; row++) {
  404. const value = sheet.getValue(row, col);
  405. if (value) {
  406. return nodes[row].data.ID;
  407. }
  408. }
  409. return null;
  410. }
  411. // 初始化指定清单选择界面
  412. function initSpecificModal() {
  413. $('#specificArea').show();
  414. const spread = initWorkbook();
  415. const sheet = spread.getSheet(0);
  416. const data = getSpecificData();
  417. initSpecificTree(data, sheet, specificTreeSetting);
  418. const { specificID } = getAction();
  419. setSpecific(specificID);
  420. }
  421. // 隐藏指定清单选择界面
  422. function hideSpecificModal() {
  423. $('#specificArea').hide();
  424. if (specificSpread) {
  425. specificSpread.destroy();
  426. specificSpread = null;
  427. }
  428. }
  429. // 初始化子目生成方式选项设置窗口
  430. function initModal() {
  431. const { option, specificID } = getAction();
  432. $(`input[name=cgx][value=${option}]`).prop('checked', 'checked');
  433. $('#specificArea').hide();
  434. if (option === Option.SPECIFIC) {
  435. initSpecificModal(specificID);
  436. }
  437. }
  438. // 超高降效下拉项或选项是否改变了
  439. function isValueChanged() {
  440. const updateData = getUpdateData();
  441. return !!(updateData.bills.length || updateData.ration.length);
  442. }
  443. // 对比两个选项行为,获取更新选项数据
  444. function getUpdateProjectData(oldAction, newAction) {
  445. if (!oldAction || !newAction) {
  446. return {};
  447. }
  448. const optionDiff = oldAction.option !== newAction.option;
  449. const specificDiff = oldAction.specificID !== newAction.specificID;
  450. const updateData = {
  451. ID: projectObj.project.ID(),
  452. overHeightOption: newAction.option,
  453. overHeightSpecificID: newAction.specificID,
  454. };
  455. return optionDiff || specificDiff
  456. ? updateData
  457. : {};
  458. }
  459. /**
  460. * 获取更新数据:对比项目节点中超高降效的新旧值,新值为暂存的单元格文本,旧值为节点data数据
  461. * @param {Object} newAction - 选项行为
  462. * @return {Object} - {
  463. * project: {ID: Number,overHeightOp: Number, overHeightSpecificID: Number||Null},
  464. * bills: [{ID: Number, overHeight: String}],
  465. * ration: [{ID: Number, overHeight: String}]
  466. * }
  467. */
  468. function getUpdateData(newAction) {
  469. const update = {
  470. project: {}, // 可能会更改项目属性的超高降效设置
  471. bills: [],
  472. ration: [],
  473. };
  474. const oldAction = getAction();
  475. const updateProjectData = getUpdateProjectData(oldAction, newAction);
  476. Object.assign(update.project, updateProjectData);
  477. const nodes = projectObj.project.mainTree.items;
  478. const sheet = projectObj.mainController.sheet;
  479. const overHeightCol = getOverHeightCol();
  480. nodes.forEach((node, index) => {
  481. const newValue = sheet.getText(index, overHeightCol);
  482. const oldValue = node.data.overHeight;
  483. // 非严等
  484. if (!commonUtil.similarEqual(newValue, oldValue)) {
  485. const type = node.sourceType === projectObj.project.Bills.getSourceType()
  486. ? 'bills'
  487. : 'ration';
  488. update[type].push({
  489. ID: node.data.ID,
  490. overHeight: newValue
  491. });
  492. }
  493. });
  494. return update;
  495. }
  496. /**
  497. * 获取删除数据:项目中所有超高子目
  498. * @return {Object} - {ration: [{ID: Number}]}
  499. */
  500. function getDeleteData() {
  501. const del = {
  502. ration: [],
  503. };
  504. const rations = projectObj.project.Ration.datas;
  505. del.ration = rations
  506. .filter(ration => ration.type === rationType.overHeight)
  507. .map(ration => ({ ID: ration.ID }));
  508. return del;
  509. }
  510. /**
  511. * 获取需要生成超高子目的定额节点
  512. * @return {Array} - [{node: Object, overHeight: String}]
  513. */
  514. function getNeedCalcRationItems() {
  515. // 从整个项目中筛选当前下拉项单元格的文本是需要计算的定额节点
  516. const nodes = projectObj.project.mainTree.items;
  517. const sheet = projectObj.mainController.sheet;
  518. const overHeightCol = getOverHeightCol();
  519. const rst = [];
  520. nodes.forEach(node => {
  521. // 非超高子目的定额节点才生成
  522. const notOverHeightRationNode = node.sourceType !== projectObj.project.Ration.getSourceType()
  523. || node.data.type === rationType.overHeight;
  524. if (notOverHeightRationNode) {
  525. return;
  526. }
  527. const overHeight = sheet.getText(node.serialNo(), overHeightCol);
  528. const overHeightItem = getOverHeightItem(overHeight);
  529. if (isNeedToCalc(overHeightItem)) {
  530. rst.push({ node, overHeight });
  531. }
  532. });
  533. return rst;
  534. }
  535. // 根据选项获取超高子目挂载的清单
  536. function getMountedBills(action) {
  537. const { option, specificID } = action;
  538. // 生成清单数据
  539. function initMountedBills() {
  540. // 生成的清单位置为其他措施项目的首项
  541. const measureNode = projectObj.project.mainTree.items.find(node => node.getFlag() === fixedFlag.OTHER_MEASURE_FEE);
  542. const firstNode = measureNode.children[0];
  543. //const parent = measureNode.children[measureNode.children.length - 1];
  544. // 具体完整数据需要在后端跟标准数据对比完善
  545. return {
  546. projectID: projectObj.project.ID(),
  547. billsLibId: +projectObj.project.projectInfo.engineeringInfo.bill_lib[0].id,
  548. ID: uuid.v1(),
  549. ParentID: measureNode.data.ID,
  550. NextSiblingID: firstNode ? firstNode.data.ID : -1,
  551. type: billType.BILL,
  552. code: fixedCode,
  553. name: '超高施工增加',
  554. unit: 'm2',
  555. quantity: '1',
  556. };
  557. }
  558. // 选项一
  559. if (option === Option.SEPARATION) {
  560. return {
  561. isNew: false,
  562. bills: null,
  563. };
  564. } else if (option === Option.MEASURE) { // 选项二且造价书没有相关清单,需要插入清单
  565. const fixedNode = getMeasureFixedNode();
  566. return {
  567. isNew: !fixedNode,
  568. bills: fixedNode ? fixedNode.data : initMountedBills(),
  569. };
  570. } else {
  571. const specificNode = getSpecificNode(specificID);
  572. return {
  573. isNew: false,
  574. bills: specificNode.data,
  575. }
  576. }
  577. }
  578. // 获取清单节点下最末非超高子目定额
  579. function getLastNormalRationNode(billsNode) {
  580. const nodes = billsNode && billsNode.children
  581. ? billsNode.children.filter(node => node.data.type !== rationType.overHeight)
  582. : [];
  583. return nodes[nodes.length - 1]
  584. }
  585. // 将需要生成超高子目的定额数据按照挂载清单和超高名称进行分组,其下数组为关联的主定额
  586. // @return {Object} {'billsID@overHeight': [node: rationNode]}
  587. function getGroupData(rationItems, mountedBillsID) {
  588. // mapping: billsID-overHeightName
  589. const group = {};
  590. rationItems.forEach(item => {
  591. // 无指定的清单ID,则挂载在各自的清单上
  592. const billsID = mountedBillsID || item.node.data.billsItemID
  593. const key = `${billsID}@${item.overHeight}`;
  594. if (!group[key]) {
  595. group[key] = [];
  596. }
  597. group[key].push(item.node);
  598. });
  599. return group;
  600. }
  601. // 根据系数类型获取汇总的定额金额,这个金额作为计算定额人材机的基数
  602. function getFeeByRateType(rateType, referenceRations) {
  603. const feeField = FeeField[rateType];
  604. // 汇总定额节点相关综合合价
  605. return referenceRations.reduce((sum, rationNode) => {
  606. const feeObj = rationNode.data.feesIndex;
  607. const totalFee = feeObj && feeObj[feeField]
  608. ? feeObj[feeField].totalFee
  609. : 0;
  610. return scMathUtil.roundForObj(sum + totalFee, decimalObj.process);
  611. }, 0);
  612. }
  613. // 通过超高降效计算得来的定额人材机消耗量:消耗量=定额消耗量=算出来的值
  614. function getQuantity(rate, fee) {
  615. return String(scMathUtil.roundForObj(rate * fee, decimalObj.glj.quantity));
  616. }
  617. /**
  618. * 获取定额喝定额人材机数据
  619. * @param {Array} rationItems - 需要生成超高子目的定额数据{node: Object, overHtight: String}
  620. * @param {Object} mountedBills - 挂载到的清单数据
  621. * @return {Object} - {ration: Array, rationGLJ: Array}
  622. */
  623. function getAddRationAndRationGLJData(rationItems, mountedBills) {
  624. // 生成定额数据
  625. function initRation(bills, overHeightItem, serialNo) {
  626. const programID = projectObj.project.calcProgram.compiledTemplateMaps[programName];
  627. // 生成的超高子目消耗量为1
  628. const quantity = '1';
  629. // 含量为 定额消耗量 / 清单消耗量
  630. const tempV = quantity / bills.quantity;
  631. const contain = isFinite(tempV)
  632. ? scMathUtil.roundForObj(tempV, decimalObj.ration.quantity)
  633. : '0';
  634. return {
  635. projectID: projectObj.project.ID(),
  636. billsItemID: bills.ID,
  637. ID: uuid.v1(),
  638. programID,
  639. serialNo,
  640. code: overHeightItem.code,
  641. name: overHeightItem.name,
  642. unit: overHeightItem.unit,
  643. type: rationType.overHeight,
  644. quantity,
  645. contain,
  646. }
  647. }
  648. // 根据分组的keys获取定额serialNo值映射表 billsID@overHeight
  649. function getSerialNoMappig(groupKeys) {
  650. // 清单ID - serialNo映射
  651. const billsIDMap = {};
  652. // 完整的key - serialNo映射
  653. const keyMap = {};
  654. const nodes = projectObj.project.mainTree.nodes;
  655. // 先给根据清单ID设置上第一个serialNo值(基准值),和超高项数据
  656. groupKeys.forEach(key => {
  657. const [billsID, overHeight] = key.split('@');
  658. if (billsIDMap[billsID]) {
  659. billsIDMap[billsID].items.push(overHeight);
  660. return;
  661. }
  662. const billsNode = nodes[`id_${billsID}`];
  663. const lastNormalRationNode = billsNode ? getLastNormalRationNode(billsNode) : null;
  664. const serialNo = lastNormalRationNode ? lastNormalRationNode.data.serialNo + 1 : 1;
  665. billsIDMap[billsID] = { serialNo, items: [overHeight] };
  666. });
  667. // 将同一清单下的超高项按照下拉项位置排序
  668. Object.entries(billsIDMap).forEach(([billsID, { serialNo, items }]) => {
  669. items.sort((a, b) => getIndex(a) - getIndex(b));
  670. items.forEach((overHeight, index) => {
  671. const key = `${billsID}@${overHeight}`;
  672. keyMap[key] = serialNo + index;
  673. });
  674. });
  675. return keyMap;
  676. }
  677. // 生成超高子目的定额人材机数据,定额人材机的属性只是一部分,还有部分数据需要在后端处理
  678. function initRationGLJ(ration, typeItem, referenceRationNodes) {
  679. const { type, rate } = typeItem;
  680. const sumFee = getFeeByRateType(type, referenceRationNodes);
  681. // 不同类型的基础人材机属性
  682. const quantity = getQuantity(rate, sumFee);
  683. const baseObj = BaseRatoinGLJ[type];
  684. // 补全定额人材机属性,共性属性
  685. const extendObj = {
  686. projectID: projectObj.project.ID(),
  687. ID: uuid.v1(),
  688. billsItemID: ration.billsItemID,
  689. rationID: ration.ID,
  690. repositoryId: -1,
  691. GLJID: -1,
  692. unit: '%',
  693. specs: '',
  694. // 定额人材机没有价格字段,但是生成单价文件需要需要这两个价格字段,默认为“1”
  695. basePrice: '1',
  696. marketPrice: '1',
  697. quantity,
  698. rationItemQuantity: quantity,
  699. };
  700. return { ...baseObj, ...extendObj };
  701. }
  702. const add = {
  703. ration: [],
  704. rationGLJ: []
  705. };
  706. if (!rationItems.length) {
  707. return add;
  708. }
  709. const mountedBillsID = mountedBills
  710. ? mountedBills.ID
  711. : null;
  712. // 分析分组数据,获取定额及定额人材机数据
  713. const group = getGroupData(rationItems, mountedBillsID);
  714. const rationSerialNoMapping = getSerialNoMappig(Object.keys(group));
  715. // 获取定额及定额人材机数据
  716. Object.entries(group).forEach(([key, referenceRationNodes]) => {
  717. const [billsID, overHeight] = key.split('@');
  718. const overHeightItem = getOverHeightItem(overHeight);
  719. const bills = billsID === mountedBillsID
  720. ? mountedBills
  721. : projectObj.project.Bills.datas.find(item => item.ID === billsID);
  722. const serialNo = rationSerialNoMapping[key];
  723. const overHeightRation = initRation(bills, overHeightItem, serialNo);
  724. // 给生成的超高子目定额,设置关联定额列表(关联定额工程量等发生变化,需要重算超高子目)
  725. overHeightRation.referenceRationList = referenceRationNodes.map(node => node.data.ID);
  726. add.ration.push(overHeightRation);
  727. // 根据超高项获取系数列表,系数列表一个元素会根据系数类别生成一条定额人材机(人、机、材料)
  728. const rateTypeList = getRateTypeList(overHeightItem);
  729. const rationGLJs = rateTypeList.map(rateTypeItem => initRationGLJ(overHeightRation, rateTypeItem, referenceRationNodes));
  730. add.rationGLJ.push(...rationGLJs);
  731. });
  732. return add;
  733. }
  734. /**
  735. * 获取插入数据:超高子目(定额)、清单(选项2、3时可能会插入)
  736. * @param {Object} action - {option: Number, specificID: Undefined||Null||Number}
  737. * @return {Array} - {bills: Array, ration: Array, rationGLJ: Array}
  738. */
  739. function getAddData(action) {
  740. const add = {
  741. bills: [],
  742. ration: [],
  743. rationGLJ: [],
  744. };
  745. // 挂载到的清单,新增或已有的
  746. const mountedBills = getMountedBills(action);
  747. if (mountedBills.isNew) {
  748. add.bills.push(mountedBills.bills);
  749. }
  750. // 获取需要生成超高子目的定额数据
  751. const needCalcRationItems = getNeedCalcRationItems();
  752. const subData = getAddRationAndRationGLJData(needCalcRationItems, mountedBills.bills);
  753. add.ration = subData.ration;
  754. add.rationGLJ = subData.rationGLJ;
  755. return add;
  756. }
  757. /**
  758. * 生成传输数据
  759. * @param {Boolean} isCancelCalc - 是否取消计取
  760. * @param {Object} action - 新的选项行为
  761. * @return {Object}
  762. */
  763. function generatePostData(isCancelCalc, action) {
  764. // 默认模板
  765. const postData = {
  766. addData: {
  767. bills: [],
  768. ration: [],
  769. rationGLJ: [],
  770. },
  771. updateData: {
  772. project: {},
  773. bills: [],
  774. ration: [],
  775. },
  776. deleteData: {
  777. ration: [],
  778. },
  779. };
  780. // 取消计取费用,只删除超高子目
  781. if (isCancelCalc) {
  782. postData.deleteData = getDeleteData();
  783. postData.updateData = getUpdateData();
  784. return postData;
  785. }
  786. // 没有新的选项行为,获取当前项目的选项行为
  787. if (!action) {
  788. action = getAction();
  789. }
  790. const addData = getAddData(action);
  791. const updateData = getUpdateData(action);
  792. const deleteData = getDeleteData();
  793. return Object.assign(postData, { addData, updateData, deleteData });
  794. }
  795. /**
  796. * 更改了超高降效列(edited、rangeChanged),触发事件
  797. * @param {Array} changedData - 变化的数据 [{node: Object, value: String}]
  798. * @return {void}
  799. */
  800. function handleValueChanged(changedData) {
  801. validateData(changedData);
  802. changedData = simplifyData(changedData);
  803. setTexts(changedData);
  804. const valuedChanged = isValueChanged();
  805. if (!valuedChanged) {
  806. return;
  807. }
  808. // 选项2、选项3情况下下拉可能会遇到,相关清单已经被删除,需要检测行为
  809. const action = getAction();
  810. const actionValid = checkAction(action);
  811. // actionValid为false的时候,可能后续需要恢复单元格文本值,根据后续用户在弹窗中的选择(后文ready事件中绑定)
  812. if (!actionValid) {
  813. return;
  814. }
  815. handleConfirmed();
  816. }
  817. // 确认事件
  818. async function handleConfirmed(isCancelCalc = false, action = null) {
  819. $.bootstrapLoading.start();
  820. try {
  821. const postData = generatePostData(isCancelCalc, action);
  822. const { addData, updateData, deleteData } = postData;
  823. // 更新、删除、新增数据
  824. // 返回的是新增的清单、定额人材机、项目人材机 rst = {bills: [], rationGLJ: [], projectGLJ: []}
  825. const rst = await ajaxPost('/project/calcOverHeightFee', postData);
  826. // 后续获取重算节点相关:
  827. // 新增的定额节点要在同步数据后才有,删除的定额节点在同步前找不到,因此同步前先获取被删除定额节点的清单ID列表
  828. const rationIDList = deleteData.ration.map(item => item.ID);
  829. let billsIDList = projectObj.project.Ration.datas
  830. .filter(item => rationIDList.includes(item.ID))
  831. .map(item => item.billsItemID);
  832. billsIDList = [...new Set(billsIDList)];
  833. // 同步数据
  834. const newAddData = { ...addData, ...rst };
  835. syncData(deleteData, updateData, newAddData);
  836. // 获取重算节点
  837. const reCalcNodes = getReCalcNodes(billsIDList, newAddData.ration);
  838. if (!reCalcNodes.length) {
  839. $.bootstrapLoading.end();
  840. return;
  841. }
  842. // 获取项目人材机数据,更新缓存
  843. $.bootstrapLoading.end(); // 重算节点相关方法里有loading,防止提前结束了loading
  844. // 重算相关节点
  845. projectObj.project.calcProgram.calcNodesAndSave(reCalcNodes);
  846. } catch (err) {
  847. alert(err);
  848. console.log(err);
  849. recoverCellsText();
  850. $.bootstrapLoading.end();
  851. }
  852. }
  853. /**
  854. * 获取需要重算的节点
  855. * @param {Array} billsIDList - 根据删除的定额ID数据获取的清单ID列表
  856. * @param {Array} rationData - 新增的定额数据
  857. * @return {Array}
  858. */
  859. function getReCalcNodes(billsIDList, rationData) {
  860. // 获取被删除定额的清单节点
  861. const billsNodes = billsIDList.map(ID => projectObj.project.mainTree.nodes[`id_${ID}`]);
  862. // 获取新增的定额数据
  863. const rationNodes = rationData.map(ration => projectObj.project.mainTree.nodes[`id_${ration.ID}`]);
  864. return [...billsNodes, ...rationNodes];
  865. }
  866. /**
  867. * 同步、更新缓存的数据、节点
  868. * @param {Object} delData - 删除的数据,包含定额ID列表
  869. * @param {Object} updateData - 更新的数据
  870. * @param {Object} addData - 新增的完整清单和定额人材机数据,经过后端加工返回
  871. * @return {void}
  872. */
  873. function syncData(delData, updateData, addData) {
  874. // 被删除数据的清单ID@定额serialNo - 价格字段映射
  875. // 在新增节点时,给上原本该行的feesIndex字段,不然会出现新增节点价格为空,重算后价格有值,造成价格字段闪烁的情况
  876. // 新增节点赋上原本该行的feesIndex字段只是为了避免闪烁的情况。
  877. // 新节点fees数组没有赋值,后续计算的时候节点的价格字段会被初始化(calcTools.initFees中),因此这个操作不会对计算结果有影响
  878. function getSerialNoFeesIndexMapping({ ration }) {
  879. const mapping = {};
  880. const rationIDList = ration.map(item => item.ID);
  881. projectObj.project.Ration.datas
  882. .filter(item => rationIDList.includes(item.ID))
  883. .forEach(item => mapping[`${item.billsItemID}@${item.serialNo}`] = item.feesIndex);
  884. return mapping;
  885. }
  886. // 新增定额设置上暂时显示的价格字段
  887. function setTemporaryFeesIndex({ ration }, feesIndexMapping) {
  888. ration.forEach(item => {
  889. const key = `${item.billsItemID}@${item.serialNo}`;
  890. const oldFeesIndex = feesIndexMapping[key];
  891. if (oldFeesIndex) {
  892. item.feesIndex = oldFeesIndex;
  893. }
  894. });
  895. }
  896. // 删除数据
  897. function del({ ration }) {
  898. const sheet = projectObj.mainController.sheet;
  899. const func = () => {
  900. // 删除定额数据、定额节点及子数据
  901. if (ration.length) {
  902. const rationIDList = ration.map(item => item.ID);
  903. projectObj.project.Ration.deleteDataSimply(rationIDList);
  904. // 由于cacheTree delete方法会将preSelected设置成null
  905. // 会导致变更焦点行时,清除不了原焦点行的选中色,因此这里重设下preSelected,在这里处理避免影响其他已有代码
  906. projectObj.project.mainTree.preSelected = projectObj.project.mainTree.selected;
  907. }
  908. };
  909. TREE_SHEET_HELPER.massOperationSheet(sheet, func);
  910. }
  911. // 更新数据
  912. function update({ project, bills, ration }) {
  913. // 更新项目属性
  914. if (project) {
  915. const property = projectObj.project.projectInfo.property;
  916. Object.assign(property, project);
  917. // 更新节点(控制基数只读性,指定清单基数列只读)
  918. const specificNode = getSpecificNode(property.overHeightSpecificID);
  919. if (specificNode) {
  920. TREE_SHEET_HELPER.refreshTreeNodeData(projectObj.mainController.setting, projectObj.mainController.sheet, [specificNode], false);
  921. }
  922. }
  923. const mainTree = projectObj.project.mainTree;
  924. // 更新节点超高降效
  925. [...bills, ...ration].forEach(({ ID, overHeight }) => {
  926. const node = mainTree.nodes[`id_${ID}`];
  927. if (node) {
  928. node.data.overHeight = overHeight;
  929. }
  930. });
  931. }
  932. // 插入数据
  933. function add({ bills, ration, rationGLJ, projectGLJ }) {
  934. const sheet = projectObj.mainController.sheet;
  935. const func = () => {
  936. // 插入清单数据和清单节点主树节点
  937. if (bills.length) {
  938. projectObj.project.Bills.addNewDataSimply(bills);
  939. }
  940. // 插入定额数据和定额节点
  941. if (ration.length) {
  942. // 按照serialNo排序
  943. ration.sort((a, b) => a.serialNo - b.serialNo);
  944. projectObj.project.Ration.addNewDataSimply(ration);
  945. }
  946. // 插入定额人材机数据
  947. if (rationGLJ.length) {
  948. projectObj.project.ration_glj.addDatasToList(rationGLJ);
  949. }
  950. // 插入项目人材机数据
  951. if (projectGLJ.length) {
  952. projectObj.project.projectGLJ.loadNewProjectGLJToCaches(projectGLJ);
  953. // 重算消耗量
  954. projectObj.project.projectGLJ.calcQuantity();
  955. }
  956. };
  957. TREE_SHEET_HELPER.massOperationSheet(sheet, func);
  958. }
  959. const feesIndexMapping = getSerialNoFeesIndexMapping(delData);
  960. setTemporaryFeesIndex(addData, feesIndexMapping);
  961. del(delData);
  962. update(updateData);
  963. add(addData);
  964. }
  965. // 恢复暂存的单元格文本
  966. function recoverCellsText() {
  967. const sheet = projectObj.mainController.sheet;
  968. const func = () => {
  969. const nodes = projectObj.project.mainTree.items;
  970. const overHeightCol = getOverHeightCol();
  971. nodes.forEach((node, index) => {
  972. const newValue = sheet.getText(index, overHeightCol);
  973. const oldValue = node.data.overHeight;
  974. if (!commonUtil.similarEqual(newValue, oldValue)) {
  975. sheet.setText(index, overHeightCol, oldValue);
  976. }
  977. });
  978. };
  979. TREE_SHEET_HELPER.massOperationSheet(sheet, func);
  980. }
  981. // 请空所有清单和定额的超高文本
  982. function clearOverHeightText() {
  983. const sheet = projectObj.mainController.sheet;
  984. const func = () => {
  985. const nodes = projectObj.project.mainTree.items;
  986. const overHeightCol = getOverHeightCol();
  987. nodes.forEach((node, index) => {
  988. if (node.data.overHeight) {
  989. sheet.setText(index, overHeightCol, '');
  990. }
  991. });
  992. };
  993. TREE_SHEET_HELPER.massOperationSheet(sheet, func);
  994. }
  995. // 取消超高降效,删除所有超高子目
  996. function cancelCalc() {
  997. switchVisible(false);
  998. clearOverHeightText();
  999. handleConfirmed(true);
  1000. }
  1001. // 取消事件,触发了取消操作,需要恢复单元格文本
  1002. function handleCancel() {
  1003. $.bootstrapLoading.start();
  1004. recoverCellsText();
  1005. $.bootstrapLoading.end();
  1006. }
  1007. /**
  1008. * 返回清单与超高子目和其定额人材机映射
  1009. * @param {Array} rations - 全部超高子目数据
  1010. * @param {Array} rationGLJs - 全部超高子目定额人材机数据
  1011. * @return {Object} - {billsItemID@超高子目编码@定额人材机编码: {quanqity, rationItemQuantity}}
  1012. */
  1013. function getRationGLJMap(rations, rationGLJs) {
  1014. const mapping = {};
  1015. rationGLJs.forEach(rGLJ => {
  1016. const ration = rations.find(ration => ration.ID === rGLJ.rationID);
  1017. const rationCode = ration ? ration.code : '';
  1018. // 由于一个清单下不会存在两个相同编号的超高子目(相同编号会被自动汇总),因此这个key能确定唯一一条定额人材机
  1019. const key = `${rGLJ.billsItemID}@${rationCode}@${rGLJ.original_code}`;
  1020. mapping[key] = { quantity: rGLJ.quantity, rationItemQuantity: rGLJ.rationItemQuantity };
  1021. });
  1022. return mapping;
  1023. }
  1024. // 比较两个清单与超高子目和其定额人材机映射表,看是否相同
  1025. function isMappingEqual(oldMapping, newMapping) {
  1026. const oldKeys = Object.keys(oldMapping);
  1027. const newKeys = Object.keys(newMapping);
  1028. if (oldKeys.length !== newKeys.length) {
  1029. return false;
  1030. }
  1031. const isEveryKeySame = oldKeys.every(key => {
  1032. const oldData = oldMapping[key];
  1033. const newData = newMapping[key];
  1034. if (!newData) {
  1035. return false;
  1036. }
  1037. const quantitySame = commonUtil.similarEqual(oldData.quantity, newData.quantity);
  1038. const rationQuantitySame = commonUtil.similarEqual(oldData.rationItemQuantity, newData.rationItemQuantity);
  1039. return quantitySame && rationQuantitySame;
  1040. });
  1041. return isEveryKeySame;
  1042. }
  1043. /**
  1044. * 重新计取项目超高子目,超高子目的值与关联定额相关
  1045. * 因此各种操作下改变了相关定额,都要重新计算超高子目
  1046. * 为了降低复杂度和保证逻辑统一性,重新计取为重新走(删除新增逻辑)
  1047. * 需要尽可能地降低操作的触发率
  1048. */
  1049. async function reCalcOverHeightFee() {
  1050. const project = projectObj.project;
  1051. // 如果项目没有超高降效数据,项目不可用超高降效,返回
  1052. if (!project.isOverHeightProject()) {
  1053. return;
  1054. }
  1055. // 如果没有超高定额,返回(因此删除了选项二三、指定的清单不会触发)
  1056. const overHeightRations = project.Ration.datas.filter(ration => ration.type === rationType.overHeight);
  1057. if (!overHeightRations.length) {
  1058. return;
  1059. }
  1060. // 获取新旧超高数据映射表,不同才需要计算
  1061. const overHeightRationIDs = overHeightRations.map(ration => ration.ID);
  1062. const overHeigtRationGLJs = project.ration_glj.datas.filter(rGLJ => overHeightRationIDs.includes(rGLJ.rationID));
  1063. const action = getAction();
  1064. const { ration, rationGLJ } = getAddData(action);
  1065. const oldMapping = getRationGLJMap(overHeightRations, overHeigtRationGLJs);
  1066. const newMapping = getRationGLJMap(ration, rationGLJ);
  1067. if (isMappingEqual(oldMapping, newMapping)) {
  1068. return;
  1069. }
  1070. // 存在不同,重算
  1071. await handleConfirmed();
  1072. }
  1073. // 事件监听
  1074. $(document).ready(() => {
  1075. // 设置窗口显示事件
  1076. $('#overHeightOpt').on('shown.bs.modal', () => {
  1077. initModal();
  1078. });
  1079. // 设置窗口隐藏事件
  1080. $('#overHeightOpt').on('hide.bs.modal', () => {
  1081. if (specificSpread) {
  1082. specificSpread.destroy();
  1083. specificSpread = null;
  1084. }
  1085. specificTree = null;
  1086. // 指定清单时的选项,指定清单被删除,用户下拉改变超高降效列,
  1087. // 弹出选中指定清单窗口,用户没有选定直接关闭窗口,此时需要把造价书暂存的值恢复
  1088. handleCancel();
  1089. });
  1090. // 设置窗口单选变更事件
  1091. $('input[name=cgx]').change(function () {
  1092. const option = +$(this).val();
  1093. switch (option) {
  1094. case Option.SEPARATION:
  1095. case Option.MEASURE:
  1096. hideSpecificModal();
  1097. break;
  1098. case Option.SPECIFIC:
  1099. initSpecificModal();
  1100. break;
  1101. }
  1102. });
  1103. // 设置窗口确认事件
  1104. $('#overHeightOptConfirmed').click(() => {
  1105. const option = +$('input[name=cgx]:checked').val();
  1106. switch (option) {
  1107. case Option.SEPARATION:
  1108. $('#overHeightOpt').modal('hide');
  1109. handleConfirmed(false, { option, specificID: null });
  1110. break;
  1111. case Option.MEASURE:
  1112. const fixedNode = getMeasureFixedNode();
  1113. // 造价书不存在相关清单,提示是否新增清单,由提示窗口进行后续操作
  1114. if (!fixedNode) {
  1115. $('#overHeightMeasure').modal('show');
  1116. return;
  1117. }
  1118. // 存在相关清单
  1119. $('#overHeightOpt').modal('hide');
  1120. handleConfirmed(false, { option, specificID: null });
  1121. break;
  1122. case Option.SPECIFIC:
  1123. const specificID = getSpecificFromSheet();
  1124. if (!specificID) {
  1125. alert('请指定清单');
  1126. return;
  1127. }
  1128. $('#overHeightOpt').modal('hide');
  1129. handleConfirmed(false, { option, specificID });
  1130. break;
  1131. }
  1132. });
  1133. // 选项二下,改变超高降效的值,且措施项目下指定清单被删除,弹窗按钮事件
  1134. $('#overHeightMeasureY').click(() => { // 确认 - 新增指定清单和相关数据
  1135. const action = { option: Option.MEASURE, specificID: null };
  1136. $('#overHeightOpt').modal('hide');
  1137. $('#overHeightMeasure').modal('hide');
  1138. handleConfirmed(false, action);
  1139. });
  1140. $('#overHeightMeasureN').click(handleCancel); // 取消
  1141. // 选项三下,改变超高降效的值,且指定清单被删除,弹窗按钮事件
  1142. $('#overHeightSpecificY').click(() => { // 确认 - 弹出设置窗口
  1143. $('#overHeightOpt').modal('show');
  1144. });
  1145. $('#overHeightSpecificN').click(handleCancel); // 取消
  1146. });
  1147. return {
  1148. init,
  1149. getComboData,
  1150. isOverHeight,
  1151. switchVisible,
  1152. handleValueChanged,
  1153. cancelCalc,
  1154. reCalcOverHeightFee,
  1155. };
  1156. })();