std_billsGuidance_lib.js 47 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139
  1. 'use strict';
  2. /**
  3. *
  4. *
  5. * @author Zhong
  6. * @date 2018/6/11
  7. * @version
  8. */
  9. //清单指引/精灵获取完清单数据后的回调函数
  10. let doAfterLoadGuidance = null;
  11. const billsGuidance = (function () {
  12. //更新类型
  13. const updateType = {update: 'update', create: 'create'};
  14. let currentLib = null;
  15. //库类型
  16. const libType = {'guidance': 1, 'elf': 2}; //清单指引、清单精灵
  17. const libSel = $('#stdBillsGuidanceLibSelect');
  18. //工作内容
  19. let stdBillsJobData = [];
  20. //项目特征
  21. let stdBillsFeatureData = [];
  22. //正在插入
  23. let isInserting = false;
  24. const bills = {
  25. dom: $('#billsGuidance_bills'),
  26. workBook: null,
  27. cache: [],
  28. tree: null,
  29. controller: null,
  30. treeSetting: {
  31. emptyRowHeader: true,
  32. rowHeaderWidth: 15,
  33. treeCol: 0,
  34. emptyRows: 0,
  35. headRows: 1,
  36. headRowHeight: [40],
  37. defaultRowHeight: 21,
  38. cols: [{
  39. width: 140,
  40. readOnly: true,
  41. showHint: true,
  42. head: {
  43. titleNames: ["项目编码"],
  44. spanCols: [1],
  45. spanRows: [1],
  46. vAlign: [1],
  47. hAlign: [1],
  48. font: ["Arial"]
  49. },
  50. data: {
  51. field: "code",
  52. vAlign: 1,
  53. hAlign: 0,
  54. font: "Arial"
  55. }
  56. }, {
  57. width: 190,
  58. readOnly: true,
  59. head: {
  60. titleNames: ["项目名称"],
  61. spanCols: [1],
  62. spanRows: [1],
  63. vAlign: [1],
  64. hAlign: [1],
  65. font: ["Arial"]
  66. },
  67. data: {
  68. field: "name",
  69. vAlign: 1,
  70. hAlign: 0,
  71. font: "Arial"
  72. }
  73. },
  74. {
  75. width: 45,
  76. readOnly: true,
  77. head: {
  78. titleNames: ["计量单位"],
  79. spanCols: [1],
  80. spanRows: [1],
  81. vAlign: [1],
  82. hAlign: [1],
  83. font: ["Arial"]
  84. },
  85. data: {
  86. field: "unit",
  87. vAlign: 1,
  88. hAlign: 1,
  89. font: "Arial"
  90. }
  91. }
  92. ]
  93. },
  94. headers: [
  95. {name: '项目编码', dataCode: 'code', width: 140, vAlign: 'center', hAlign: 'left', formatter: '@'},
  96. {name: '项目名称', dataCode: 'name', width: 190, vAlign: 'center', hAlign: 'left', formatter: '@'},
  97. {name: '单位', dataCode: 'unit', width: 45, vAlign: 'center', hAlign: 'center', formatter: '@'},
  98. ],
  99. rowHeaderWidth:1,
  100. events: {
  101. CellDoubleClick: function (sender, args) {
  102. if(!bills.tree){
  103. return;
  104. }
  105. let node = bills.tree.items[args.row];
  106. if(!node){
  107. return;
  108. }
  109. //展开收起(非最底层节点且双击的是第一列)
  110. if (args.col === 0 && node.children.length > 0) {
  111. node.setExpanded(!node.expanded);
  112. //设置展开收起状态
  113. sessionStorage.setItem('stdBillsGuidanceExpState', bills.tree.getExpState(bills.tree.items));
  114. renderSheetFunc(args.sheet, function () {
  115. let iCount = node.posterityCount(), i, child;
  116. for (i = 0; i < iCount; i++) {
  117. child = bills.tree.items[args.row + i + 1];
  118. args.sheet.setRowVisible(args.row + i + 1, child.visible, args.sheetArea);
  119. }
  120. args.sheet.invalidateLayout();
  121. });
  122. args.sheet.repaint();
  123. } else if (!projectReadOnly && !isInserting && (args.col !== 0 || node.children.length === 0)) {
  124. //选中部分的最底层(只是选中部分的最底)
  125. let lowestNodes = [bills.tree.items[args.row]];
  126. insertBills(lowestNodes);
  127. }
  128. }
  129. }
  130. };
  131. //插入清单
  132. function insertBills(lowestNodes) {
  133. let selTree = getSelTree(lowestNodes);
  134. let compareData = compareTree(projectObj.project.Bills.tree, selTree);
  135. let sheet = projectObj.mainSpread.getActiveSheet(),
  136. row = sheet.getActiveColumnIndex(),
  137. col = sheet.getActiveColumnIndex();
  138. if (compareData.postData.length > 0) {
  139. isInserting = true;
  140. CommonAjax.post('/bills/insertBills', {postData: compareData.postData}, function () {
  141. //插入
  142. let insertData = _.filter(compareData.postData, {updateType: updateType.create});
  143. let treeData = [];
  144. for (let data of insertData) {
  145. treeData.push(data.updateData);
  146. }
  147. //插入清单节点
  148. projectObj.project.Bills.tree.insertByDatas(treeData);
  149. projectObj.project.Bills.datas = projectObj.project.Bills.datas.concat(treeData);
  150. //插入主树节点
  151. let newNodes = projectObj.project.mainTree.insertByDatas(treeData);
  152. for (let node of newNodes) {
  153. node.source = projectObj.project.Bills.tree.nodes[projectObj.project.Bills.tree.prefix + node.getID()];
  154. node.data = node.source.data;
  155. node.sourceType = projectObj.project.Bills.getSourceType();
  156. }
  157. ProjectController.syncDisplayNewNodes(projectObj.mainController, newNodes, true);
  158. row = newNodes[newNodes.length - 1].serialNo();
  159. //有新的节点插入,也有可能定位至旧节点(批量选用的情况下)
  160. if (compareData.locateNode) {
  161. row = compareData.locateNode.serialNo();
  162. }
  163. sheet.setSelection(row, col, 1, 1);
  164. projectObj.mainController.setTreeSelected(projectObj.mainController.tree.items[row]);//触发树节点选中事件
  165. sheet.showRow(row, GC.Spread.Sheets.VerticalPosition.center);
  166. isInserting = false;
  167. }, function () {
  168. isInserting = false;
  169. });
  170. } else if (compareData.locateNode) {
  171. row = compareData.locateNode.serialNo();
  172. sheet.setSelection(row, col, 1, 1);
  173. projectObj.mainController.setTreeSelected(projectObj.mainController.tree.items[row]);//触发树节点选中事件
  174. sheet.showRow(row, GC.Spread.Sheets.VerticalPosition.center);
  175. }
  176. }
  177. /*
  178. *
  179. * 1.选中的树结构(清单规则选中的节点及其所有父项)与主树对比(主树中节点“编码-名称-单位”与选中树~组合相同视为同一节点),
  180. * 将主树中不含有的选中节点全部插入
  181. * 2.插入位置由1对比得出,主树有与选中树结构相同层次结构的片段,这个相同片段为插入位置
  182. * 3.选中树结构除去相同片段,为需要插入的节点,插入时,遇到同层节点,根据编码的字符编码值确定顺序
  183. * a.code <= b.code,则a节点在b节点前
  184. * */
  185. //获取选中的树(将选中的节点及其所有父项,组合成一棵树,没有重复节点)
  186. //@param {Array}lowestNodes(选中的最底层节点) @return {Array}
  187. function getSelTree(lowestNodes) {
  188. let allNodes = [];
  189. //获取树上所有的节点
  190. for (let node of lowestNodes) {
  191. while (node && !allNodes.includes(node)) {
  192. allNodes.push(node);
  193. node = node.parent;
  194. }
  195. }
  196. //根据原节点serialNo排序,排序后,后一项节点只可能前节点的后兄弟项,或子项(根据原节点ID-ParentID判定)
  197. allNodes.sort(function (a, b) {
  198. let aV = a.serialNo(),
  199. bV = b.serialNo();
  200. if (aV > bV) {
  201. return 1;
  202. } else if (aV < bV) {
  203. return -1;
  204. }
  205. return 0;
  206. });
  207. //生成树数据
  208. let treeData = [];
  209. //旧ID与新ID映射
  210. let IDMapping = {};
  211. for (let i = 0; i < allNodes.length; i++) {
  212. //原节点
  213. let preN = allNodes[i - 1],
  214. thisN = allNodes[i];
  215. let newNodeData = {ID: uuid.v1(), NextSiblingID: -1, ParentID: -1, orgID: thisN.data.ID};
  216. IDMapping[newNodeData.orgID] = newNodeData.ID;
  217. treeData.push(newNodeData);
  218. //新树数据
  219. let preData = treeData[i - 1],
  220. thisData = treeData[i];
  221. //节点与上节点为同层节点,则此节点设为上节点的后兄弟
  222. if (preN && preN.data.ParentID === thisN.data.ParentID) {
  223. preData.NextSiblingID = thisData.ID;
  224. //节点与上节点不为同层节点,则此节点为某上节点的子几点
  225. } else if (preN && preN.data.ParentID !== thisN.data.ParentID) {
  226. let parentID = IDMapping[thisN.data.ParentID];
  227. thisData.ParentID = parentID;
  228. }
  229. }
  230. let selTree = idTree.createNew({id: 'ID', pid: 'ParentID', nid: 'NextSiblingID', rootId: -1, autoUpdate: true});
  231. selTree.loadDatas(treeData);
  232. return selTree;
  233. }
  234. /*
  235. * 选中树与清单主树进行对比,(自上而下,从roots开始)获取插入、更新数据
  236. * 找到的清单树中清单子项含有非清单项或输入了计算基数,则中止该清单的对比,不可插入数据
  237. * */
  238. //获取节点的匹配数据:编码-名称-单位
  239. //@param {Object}node @return {String}
  240. function getMatchContent(node) {
  241. return `${node.data.code ? node.data.code : '*'}-${node.data.name ? node.data.name : '*'}-${node.data.unit ? node.data.unit: '*'}`;
  242. }
  243. //**对比清单主树与选中树,获取需要更新和插入的数据**
  244. //@param {Object}mainTree {Object}selTree
  245. function compareTree(billsTree, selTree) {
  246. //需要插入、更新的数据
  247. let postData = [],
  248. //跟树结构自动定位至的清单节点(最近匹配到的节点)
  249. locateNode = null;
  250. if (!billsTree || !selTree) {
  251. return postData;
  252. }
  253. comparePeer(null, billsTree.roots, selTree.roots);
  254. return {postData, locateNode};
  255. /*
  256. * 该清单节点是否可以继续往下递归匹配,即该节点是否还可插入子项(在该层匹配到的时候判断)
  257. * 1.该底层节点不能含有非清单子项
  258. * 2.该节点不能含有计算基数
  259. * todo 3.数量单价等相关概念做完了再补上
  260. * @param {Object}billsNode(清单树中的某节点) @return {Boolean}
  261. * */
  262. function canRecursive(billsNode) {
  263. //不含有子清单节点但却含有子节点,说明该节点含有非清单子项
  264. if (billsNode.children > 0 && billsNode.source.children === 0) {
  265. return false;
  266. } else if (billsNode.data.calcBase) {
  267. return false;
  268. }
  269. return true;
  270. }
  271. //获取某选中节点要往清单主树同层插入的位置
  272. function insertPos(peerNodes, node) {
  273. //node选中树中没有记录原清单的数据,只记录了原清单ID,需要node的数据,则要找回原清单
  274. let orgNode = bills.tree.nodes[`${bills.tree.prefix}${node.data.orgID}`];
  275. if (!orgNode) {
  276. return -1;
  277. }
  278. let insertCode = orgNode.data.code ? orgNode.data.code : '';
  279. //插入最顶层节点或者无编码,则直接插入到清单主树最后面
  280. if (orgNode.depth() === 0 || insertCode === '') {
  281. return peerNodes.length;
  282. }
  283. for (let i = 0; i < peerNodes.length; i++) {
  284. let thisNode = peerNodes[i];
  285. let thisCode = thisNode.data.code ? thisNode.data.code : '';
  286. //确定同层节点的顺序,编码小于等于在前,大于在后
  287. if (insertCode <= thisCode) {
  288. return i;
  289. } else if (insertCode > thisCode) {
  290. continue;
  291. }
  292. }
  293. return peerNodes.length;
  294. }
  295. //获取插入清单数据
  296. function getInsertData(insertObj) {
  297. let stdNode = bills.tree.nodes[`${bills.tree.prefix}${insertObj.orgID}`];
  298. if (!stdNode) {
  299. return null;
  300. }
  301. let stdData = {};
  302. stdData.projectID = projectInfoObj.projectInfo.ID;
  303. stdData.isAdd = 1;
  304. stdData.ID = insertObj.ID;
  305. stdData.ParentID = insertObj.ParentID;
  306. stdData.NextSiblingID = insertObj.NextSiblingID;
  307. //顶层节点是大项费用
  308. stdData.type = stdNode.parent ? billType.BILL : billType.DXFY;
  309. stdData.code = stdNode.data.code;
  310. stdData.name = stdNode.data.name;
  311. stdData.unit = stdNode.data.unit;
  312. stdData.ruleText = stdNode.data.ruleText;
  313. stdData.comments = stdNode.data.comments;
  314. stdData.programID = stdNode.data.engineering;
  315. stdData.billsLibId = stdNode.data.billsLibId;
  316. return stdData;
  317. }
  318. //从同层节点中获取更新数据
  319. //@param {Object}parentNode(同层节点挂载的父节点,清单主树中) {Array}peerNodes(同层节点,含有清单树和选中树节点)
  320. // {Array}billsNodes(该层清单树节点) {Array}selNodes(该层选中树节点) @return {Array}
  321. function getUpdateDataFromPeer(parentNode, peerNodes, billsNodes, selNodes) {
  322. let rst = [];
  323. //向下获取插入数据直到最底层
  324. function getDataTillDeepest(node) {
  325. if (node) {
  326. let insertData = getInsertData(node.data);
  327. if (insertData) {
  328. rst.push({updateType: updateType.create, updateData: insertData});
  329. }
  330. for (let child of node.children) {
  331. getDataTillDeepest(child);
  332. }
  333. }
  334. }
  335. for (let i = 0; i < peerNodes.length; i++) {
  336. let thisNode = peerNodes[i],
  337. nextNode = peerNodes[i + 1];
  338. //更新原清单节点NextSiblingID
  339. if (billsNodes.includes(thisNode) && selNodes.includes(nextNode)) {
  340. rst.push({updateType: updateType.update, updateData: {ID: thisNode.data.ID, NextSiblingID: nextNode.data.ID}});
  341. } else if (selNodes.includes(thisNode) && billsNodes.includes(nextNode)) { //变更选中节点的NextSiblingID
  342. thisNode.data.NextSiblingID = nextNode.data.ID;
  343. }
  344. //所有选中的同层节点设为清单树父节点的子项,获取插入数据(插入该同层节点及其所有子节点)
  345. if (selNodes.includes(thisNode)) {
  346. thisNode.data.ParentID = parentNode ? parentNode.data.ID : -1;
  347. getDataTillDeepest(thisNode);
  348. }
  349. }
  350. return rst;
  351. }
  352. //同层节点之间比较,匹配到的则继续往下匹配,匹配不到的节点则按照规定的顺序进行排序,插入更新(更新清单树节点中NextSiblingID改变的节点)
  353. function comparePeer(parentNode, billsNodes, selNodes) {
  354. let peerNodes = [].concat(billsNodes); //同层节点
  355. let matchNode = null; //匹配到的清单主树节点
  356. for (let selNode of selNodes) {
  357. let stdNode = bills.tree.nodes[`${bills.tree.prefix}${selNode.data.orgID}`],
  358. selMatch = getMatchContent(stdNode),
  359. isMatched = false;
  360. for( let billsNode of billsNodes) {
  361. let billsMatch = getMatchContent(billsNode);
  362. if (selMatch === billsMatch) {//只进行一次成功匹配
  363. matchNode = billsNode;
  364. isMatched = true;
  365. if (selNode.children.length === 0) {//成功匹配且为选中的最底节点,则为自动定位节点
  366. locateNode = matchNode;
  367. }
  368. break;
  369. }
  370. }
  371. if (isMatched && canRecursive(matchNode)) {//匹配成功,且该匹配到的节点可插入子项,递归匹配子项
  372. comparePeer(matchNode, matchNode.children, selNode.children);
  373. } else if (!isMatched) { //匹配不成功,其节点与该层清单节点同层,根据编码插入同层数组中
  374. let pos = insertPos(peerNodes, selNode);
  375. if (pos >= 0) {
  376. peerNodes.splice(pos, 0, selNode);
  377. }
  378. }
  379. }
  380. //同层节点比清单树节点多了,说明在该层有选中节点插入清单树中
  381. if (peerNodes.length > billsNodes.length) {
  382. let updateData = getUpdateDataFromPeer(parentNode, peerNodes, billsNodes, selNodes);
  383. postData = postData.concat(updateData);
  384. }
  385. }
  386. }
  387. //项目指引类型
  388. const itemType = {
  389. job: 0,
  390. ration: 1
  391. };
  392. const guideItem = {
  393. dom: $('#billsGuidance_items'),
  394. workBook: null,
  395. tree: null,
  396. controller: null,
  397. treeSetting: {
  398. treeCol: 1,
  399. emptyRows: 0,
  400. headRows: 1,
  401. headRowHeight: [40],
  402. defaultRowHeight: 21,
  403. cols: [
  404. {
  405. width: 35,
  406. readOnly: false,
  407. head: {
  408. titleNames: ["选择"],
  409. spanCols: [1],
  410. spanRows: [1],
  411. vAlign: [1],
  412. hAlign: [1],
  413. font: ["Arial"]
  414. },
  415. data: {
  416. field: "select",
  417. vAlign: 1,
  418. hAlign: 1,
  419. font: "Arial"
  420. }
  421. },
  422. {
  423. width: 420,
  424. readOnly: false,
  425. head: {
  426. titleNames: ["项目指引"],
  427. spanCols: [1],
  428. spanRows: [1],
  429. vAlign: [1],
  430. hAlign: [1],
  431. font: ["Arial"]
  432. },
  433. data: {
  434. field: "name",
  435. vAlign: 1,
  436. hAlign: 0,
  437. font: "Arial"
  438. }
  439. }
  440. ]
  441. },
  442. headers: [
  443. {name: '选择', dataCode: 'select', width: 35, vAlign: 'center', hAlign: 'center', formatter: '@'},
  444. {name: '项目指引', dataCode: 'name', width: 300, vAlign: 'center', hAlign: 'left', formatter: '@'},
  445. ],
  446. rowHeaderWidth:25,
  447. events: {
  448. EditStarting: function (sender, args) {
  449. if(!bills.tree || guideItem.headers[args.col]['dataCode'] === 'name'){
  450. args.cancel = true;
  451. }
  452. },
  453. ButtonClicked: function (sender, args) {
  454. if(args.sheet.isEditing()){
  455. args.sheet.endEdit(true);
  456. }
  457. refreshInsertRation();
  458. },
  459. CellDoubleClick: function (sender, args) {
  460. if(!bills.tree || !bills.tree.selected){
  461. return;
  462. }
  463. let node = bills.tree.selected.guidance.tree.selected;
  464. if(!node){
  465. return;
  466. }
  467. if(node.children.length === 0){
  468. if(guideItem.headers[args.col]['dataCode'] === 'name'){
  469. insertRations(getInsertRationData([args.row]));
  470. }
  471. }
  472. else {
  473. node.setExpanded(!node.expanded);
  474. renderSheetFunc(args.sheet, function () {
  475. let iCount = node.posterityCount(), i, child;
  476. for (i = 0; i < iCount; i++) {
  477. child = bills.tree.selected.guidance.tree.items[args.row + i + 1];
  478. args.sheet.setRowVisible(args.row + i + 1, child.visible, args.sheetArea);
  479. }
  480. args.sheet.invalidateLayout();
  481. });
  482. args.sheet.repaint();
  483. }
  484. }
  485. }
  486. };
  487. const elfItem = {
  488. dom: $('#billsGuidance_items'),
  489. workBook: null,
  490. tree: null,
  491. controller: null,
  492. treeSetting: {
  493. treeCol: 0,
  494. emptyRows: 0,
  495. headRows: 1,
  496. headRowHeight: [40],
  497. defaultRowHeight: 21,
  498. cols: [
  499. {
  500. width: 250,
  501. readOnly: true,
  502. head: {
  503. titleNames: ["施工工序"],
  504. spanCols: [1],
  505. spanRows: [1],
  506. vAlign: [1],
  507. hAlign: [1],
  508. font: ["Arial"]
  509. },
  510. data: {
  511. field: "name",
  512. vAlign: 1,
  513. hAlign: 0,
  514. font: "Arial"
  515. }
  516. },
  517. {
  518. width: 250,
  519. readOnly: false,
  520. head: {
  521. titleNames: ["选项"],
  522. spanCols: [1],
  523. spanRows: [1],
  524. vAlign: [1],
  525. hAlign: [1],
  526. font: ["Arial"]
  527. },
  528. data: {
  529. field: "options",
  530. vAlign: 1,
  531. hAlign: 0,
  532. font: "Arial"
  533. }
  534. }
  535. ]
  536. },
  537. headers: [
  538. {name: '施工工序', dataCode: 'name', width: 250, rateWidth: 0.5, vAlign: 'center', hAlign: 'center', formatter: '@'},
  539. {name: '选项', dataCode: 'options', width: 250, rateWidth: 0.5, vAlign: 'center', hAlign: 'left', formatter: '@'},
  540. ],
  541. rowHeaderWidth:25,
  542. events: {
  543. CellClick: function (sender, args) {
  544. if(elfItem.headers[args.col]['dataCode'] === 'options' && args.sheetArea === 3){
  545. if(!args.sheet.getCell(args.row, args.col).locked() && !args.sheet.isEditing()){
  546. args.sheet.startEdit();
  547. }
  548. }
  549. },
  550. ClipboardPasting: function (sender, info) {
  551. info.cancel = true;
  552. }
  553. }
  554. };
  555. const options = {
  556. workBook: {
  557. tabStripVisible: false,
  558. allowContextMenu: false,
  559. allowCopyPasteExcelStyle : false,
  560. allowExtendPasteRange: false,
  561. allowUserDragDrop : false,
  562. allowUserDragFill: false,
  563. scrollbarMaxAlign : true
  564. },
  565. sheet: {
  566. protectionOptions: {allowResizeRows: true, allowResizeColumns: true},
  567. clipBoardOptions: GC.Spread.Sheets.ClipboardPasteOptions.values
  568. }
  569. };
  570. //渲染时方法,停止渲染
  571. //@param {Object}sheet {Function}func @return {void}
  572. function renderSheetFunc(sheet, func){
  573. sheet.suspendEvent();
  574. sheet.suspendPaint();
  575. if(func){
  576. func();
  577. }
  578. sheet.resumeEvent();
  579. sheet.resumePaint();
  580. }
  581. //设置表选项
  582. //@param {Object}workBook {Object}opts @return {void}
  583. function setOptions (workBook, opts) {
  584. for(let opt in opts.workBook){
  585. workBook.options[opt] = opts.workBook[opt];
  586. }
  587. for(let opt in opts.sheet){
  588. workBook.getActiveSheet().options[opt] = opts.sheet[opt];
  589. }
  590. }
  591. //建表头
  592. //@param {Object}sheet {Array}headers @return {void}
  593. function buildHeader(sheet, headers) {
  594. let fuc = function () {
  595. sheet.setColumnCount(headers.length);
  596. sheet.setRowHeight(0, 30, GC.Spread.Sheets.SheetArea.colHeader);
  597. //sheet.setColumnWidth(0, sheet.getParent() === bills.workBook ? 15 : 25, GC.Spread.Sheets.SheetArea.rowHeader);
  598. if(sheet.getParent() === elfItem.workBook || sheet.getParent() === guideItem.workBook){
  599. sheet.setRowHeight(0, 20, GC.Spread.Sheets.SheetArea.colHeader);
  600. }
  601. for(let i = 0, len = headers.length; i < len; i++){
  602. sheet.setValue(0, i, headers[i].name, GC.Spread.Sheets.SheetArea.colHeader);
  603. sheet.setColumnWidth(i, headers[i].width, GC.Spread.Sheets.SheetArea.colHeader);
  604. if(headers[i].formatter){
  605. sheet.setFormatter(-1, i, headers[i].formatter);
  606. }
  607. sheet.getRange(-1, i, -1, 1).hAlign(GC.Spread.Sheets.HorizontalAlign[headers[i]['hAlign']]);
  608. sheet.getRange(-1, i, -1, 1).vAlign(GC.Spread.Sheets.VerticalAlign[headers[i]['vAlign']]);
  609. }
  610. };
  611. renderSheetFunc(sheet, fuc);
  612. }
  613. //表监听事件
  614. //@param {Object}workBook @return {void}
  615. function bindEvent(workBook, events) {
  616. if(Object.keys(events).length === 0){
  617. return;
  618. }
  619. const Events = GC.Spread.Sheets.Events;
  620. for(let event in events){
  621. workBook.bind(Events[event], events[event]);
  622. }
  623. }
  624. //根据宽度比例设置列宽
  625. //@param {Object}workBook {Number}workBookWidth {Array}headers @return {void}
  626. function setColumnWidthByRate(workBook, workBookWidth, headers) {
  627. if(workBook){
  628. workBookWidth -= 48;
  629. const sheet = workBook.getActiveSheet();
  630. sheet.suspendEvent();
  631. sheet.suspendPaint();
  632. for(let col = 0; col < headers.length; col++){
  633. if(headers[col]['rateWidth'] !== undefined && headers[col]['rateWidth'] !== null && headers[col]['rateWidth'] !== ''){
  634. let width = workBookWidth * headers[col]['rateWidth'];
  635. if(headers[col]['dataCode'] === 'options'){
  636. width = width;
  637. }
  638. sheet.setColumnWidth(col, width, GC.Spread.Sheets.SheetArea.colHeader)
  639. }
  640. else {
  641. if(headers[col]['headerWidth'] !== undefined && headers[col]['headerWidth'] !== null && headers[col]['headerWidth'] !== ''){
  642. sheet.setColumnWidth(col, headers[col]['headerWidth'], GC.Spread.Sheets.SheetArea.colHeader)
  643. }
  644. }
  645. }
  646. sheet.resumeEvent();
  647. sheet.resumePaint();
  648. }
  649. }
  650. //建表
  651. //@param {Object}module @return {void}
  652. function buildSheet(module) {
  653. if(!module.workBook){
  654. module.workBook = new GC.Spread.Sheets.Workbook(module.dom[0], {sheetCount: 1});
  655. sheetCommonObj.spreadDefaultStyle(module.workBook);
  656. let sheet = module.workBook.getActiveSheet();
  657. if(module === bills){
  658. //默认初始可控制焦点在清单表中
  659. sheet.options.rowHeaderVisible = false;
  660. module.workBook.focus();
  661. sheet.options.isProtected = true;
  662. sheet.name('stdBillsGuidance_bills');
  663. //设置悬浮提示
  664. TREE_SHEET_HELPER.initSetting(bills.dom[0], bills.treeSetting);
  665. }
  666. if(module === guideItem){
  667. sheet.options.isProtected = true;
  668. sheet.getRange(-1, 0, -1, 1).locked(false);
  669. sheet.getRange(-1, 1, -1, 1).locked(true);
  670. }
  671. if(module === elfItem){
  672. sheet.options.isProtected = true;
  673. sheet.getRange(-1, 0, -1, 1).locked(true);
  674. sheet.getRange(-1, 1, -1, 1).locked(false);
  675. }
  676. if(module.rowHeaderWidth) {
  677. sheet.setColumnWidth(0, module.rowHeaderWidth, GC.Spread.Sheets.SheetArea.rowHeader);
  678. }
  679. setOptions(module.workBook, options);
  680. buildHeader(module.workBook.getActiveSheet(), module.headers);
  681. if(module === elfItem){
  682. setColumnWidthByRate(elfItem.workBook, $('#zy').width(), elfItem.headers)
  683. }
  684. bindEvent(module.workBook, module.events);
  685. }
  686. }
  687. //清空表数据
  688. //@param {Object}sheet {Array}headers {Number}rowCount @return {void}
  689. function cleanData(sheet, headers, rowCount){
  690. renderSheetFunc(sheet, function () {
  691. sheet.clear(-1, 0, -1, headers.length, GC.Spread.Sheets.SheetArea.viewport, GC.Spread.Sheets.StorageType.data);
  692. if (rowCount > 0) {
  693. sheet.setRowCount(rowCount);
  694. }
  695. });
  696. }
  697. //初始化各工作表
  698. //@param {Array}modules @return {void}
  699. function initWorkBooks(modules){
  700. for(let module of modules){
  701. buildSheet(module);
  702. }
  703. }
  704. //点击清单名称后面的问号,弹出补注窗口并设置当前节点(或xxx父节点)的补注
  705. //@param {Number}row(当前焦点行) @return {void}
  706. function initRechargeModal(row) {
  707. let node = bills.tree.items[row];
  708. while (node && !node.data.recharge){
  709. node = node.parent;
  710. }
  711. let recharge = node && node.data.recharge ? node.data.recharge : '无内容';
  712. node = bills.tree.items[row];
  713. while (node && !node.data.ruleText){
  714. node = node.parent;
  715. }
  716. let ruleText = node && node.data.ruleText ? node.data.ruleText : '无内容';
  717. $('#questionTab1').text('补注');
  718. $('#questionTab2').text('工程量计算规则');
  719. $('#questionContent1').html(recharge);
  720. $('#questionContent2').html(ruleText);
  721. $('#questionModal').modal('show');
  722. }
  723. //节点链上含有补注或工程量计算规则数据
  724. //@param {Number}row(行当前行) @return {Boolean}
  725. function hasRechargeRuleText(row) {
  726. let node = bills.tree.items[row];
  727. if (!node) {
  728. return false;
  729. }
  730. while (node) {
  731. if (node.data.recharge || node.data.ruleText) {
  732. return true;
  733. }
  734. node = node.parent;
  735. }
  736. return false;
  737. }
  738. //初始化并输出树
  739. //@param {Object}module {Object}sheet {Object}treeSetting {Array}datas
  740. function initTree(module, sheet, treeSetting, datas){
  741. module.tree = idTree.createNew({id: 'ID', pid: 'ParentID', nid: 'NextSiblingID', rootId: -1, autoUpdate: true});
  742. module.controller = TREE_SHEET_CONTROLLER.createNew(module.tree, sheet, treeSetting, false);
  743. module.tree.loadDatas(datas);
  744. if(module === bills){
  745. initExpandStat();
  746. }
  747. module.controller.showTreeData();
  748. if(module === bills){
  749. module.workBook.getSheet(0).options.rowHeaderVisible = true;
  750. setBillsHint(bills.tree.items, stdBillsJobData, stdBillsFeatureData);
  751. renderSheetFunc(sheet, function () {
  752. for(let i = 0; i < bills.tree.items.length; i++){
  753. sheet.setCellType(i, 1, TREE_SHEET_HELPER.getQuestionCellType(initRechargeModal, hasRechargeRuleText));
  754. }
  755. });
  756. }
  757. }
  758. //项目指引表焦点控制
  759. //@param {Number}row @return {void}
  760. function guideItemInitSel(row){
  761. let billsNode = bills.tree.selected;
  762. let node = null;
  763. if(billsNode && billsNode.guidance.tree){
  764. node = billsNode.guidance.tree.items[row];
  765. if(node){
  766. billsNode.guidance.tree.selected = node;
  767. }
  768. }
  769. }
  770. //清单精灵表焦点控制
  771. //@param {Number}row @return {void}
  772. function elfItemInitSel(row){
  773. let billsNode = bills.tree.selected;
  774. let node = null;
  775. if(billsNode && billsNode.elf.tree){
  776. node = billsNode.elf.tree.items[row];
  777. if(node){
  778. billsNode.elf.tree.selected = node;
  779. }
  780. }
  781. }
  782. //根据项目指引的类型设置单元格类型,定额类型的项目指引为复选框
  783. //@param {Array}nodes @return {void}
  784. function setItemCellType(nodes){
  785. //设置单元格类型
  786. const base = new GC.Spread.Sheets.CellTypes.Base();
  787. const checkBox = new GC.Spread.Sheets.CellTypes.CheckBox();
  788. const sheet = guideItem.workBook.getActiveSheet();
  789. renderSheetFunc(sheet, function(){
  790. for(let node of nodes){
  791. sheet.setCellType(node.serialNo(), 0, node.data.type === itemType.ration ? checkBox : base);
  792. }
  793. });
  794. }
  795. //初始化清单的工作内容和项目特征
  796. //@param {Number}billsLibId {Function}callback @return {void}
  797. function initJobAndCharacter(billsLibId, callback){
  798. CommonAjax.post('/stdBillsEditor/getJobContent', {userId: userID, billsLibId: billsLibId}, function (datas) {
  799. stdBillsJobData = datas;
  800. CommonAjax.post('/stdBillsEditor/getItemCharacter', {userId: userID, billsLibId: billsLibId}, function (datas) {
  801. stdBillsFeatureData = datas;
  802. if(callback){
  803. callback();
  804. }
  805. });
  806. });
  807. }
  808. //初始化清单展开收起状态
  809. //@return {void}
  810. function initExpandStat(){
  811. //读取展开收起状态
  812. let currentExpState = sessionStorage.getItem('stdBillsGuidanceExpState');
  813. if(currentExpState){
  814. bills.tree.setExpandedByState(bills.tree.items, currentExpState);
  815. }
  816. //非叶子节点默认收起
  817. else{
  818. bills.tree.setRootExpanded(bills.tree.roots, false);
  819. }
  820. }
  821. //设置tag以悬浮提示
  822. function setTagForHint(nodes){
  823. let sheet = bills.workBook.getActiveSheet();
  824. renderSheetFunc(sheet, function () {
  825. for(let node of nodes){
  826. sheet.setTag(node.serialNo(), 2, node.data.ruleText ? node.data.ruleText : '');
  827. }
  828. });
  829. }
  830. //根据编码定位至清单精灵库中
  831. //@param {String}code @return {void}
  832. function locateAtBills(code) {
  833. let nineCode = code.substring(0, 9);
  834. let items = bills.tree.items;
  835. let locateBills = _.find(items, function(item){
  836. return item.data.code === nineCode;
  837. });
  838. if(locateBills){
  839. expandSearchNodes([locateBills]);
  840. sessionStorage.setItem('stdBillsGuidanceExpState', bills.tree.getExpState(bills.tree.items));
  841. }
  842. let sheet = bills.workBook.getActiveSheet();
  843. let locateRow = locateBills ? locateBills.serialNo() : 0;
  844. sheet.setActiveCell(locateRow, 0);
  845. sheet.showRow(locateRow, GC.Spread.Sheets.VerticalPosition.center);
  846. }
  847. //清单设置悬浮提示信息
  848. //@param {Array}billsNodes(清单节点) {Array}jobs(总的工作内容数据) {Array}items(总的项目特征数据)
  849. function setBillsHint(billsNodes, jobs, items) {
  850. let jobsMapping = {},
  851. itemsMapping = {};
  852. for(let job of jobs){
  853. jobsMapping[job.id] = job;
  854. }
  855. for(let item of items){
  856. itemsMapping[item.id] = item;
  857. }
  858. let tagInfo = [];
  859. for(let billsNode of billsNodes){
  860. let hintArr = [];
  861. let billsItems = billsNode.data.items;
  862. if(billsItems.length > 0){
  863. //项目特征
  864. hintArr.push('项目特征:');
  865. }
  866. let itemCount = 1,
  867. jobCount = 1;
  868. for(let billsItem of billsItems){
  869. let itemData = itemsMapping[billsItem.id];
  870. if(itemData){
  871. //特征值
  872. let eigens = [];
  873. for(let eigen of itemData.itemValue){
  874. eigens.push(eigen.value);
  875. }
  876. eigens = eigens.join(';');
  877. hintArr.push(`${itemCount}.${itemData.content}${eigens === '' ? '' : ': ' + eigens}`);
  878. itemCount ++;
  879. }
  880. }
  881. //工作内容
  882. let billsJobs = billsNode.data.jobs;
  883. if(billsJobs.length > 0){
  884. hintArr.push('工作内容:');
  885. }
  886. for(let billsJob of billsJobs){
  887. let jobData = jobsMapping[billsJob.id];
  888. if(jobData){
  889. hintArr.push(`${jobCount}.${jobData.content}`);
  890. jobCount ++;
  891. }
  892. }
  893. /*if(billsNode.data.ruleText && billsNode.data.ruleText !== ''){
  894. hintArr.push('工程量计算规则:');
  895. hintArr.push(billsNode.data.ruleText);
  896. }
  897. if(billsNode.data.recharge && billsNode.data.recharge !== ''){
  898. hintArr.push('补注:');
  899. hintArr.push(billsNode.data.recharge);
  900. }*/
  901. if(hintArr.length > 0){
  902. tagInfo.push({row: billsNode.serialNo(), value: hintArr.join('\n')});
  903. }
  904. }
  905. let sheet = bills.workBook.getActiveSheet();
  906. renderSheetFunc(sheet, function () {
  907. for(let tagI of tagInfo){
  908. sheet.setTag(tagI.row, 0, tagI.value);
  909. }
  910. });
  911. }
  912. //初始选择清单指引库
  913. //@param {Number}libID @return {void}
  914. function libInitSel(libID){
  915. //获取清单
  916. $.bootstrapLoading.start();
  917. CommonAjax.post('/billsGuidance/api/getLibWithBills', {libID: libID}, function(rstData){
  918. currentLib = rstData.guidanceLib;
  919. if(guideItem.workBook){
  920. guideItem.workBook.destroy();
  921. guideItem.workBook = null;
  922. }
  923. if(elfItem.workBook){
  924. elfItem.workBook.destroy();
  925. elfItem.workBook = null;
  926. }
  927. initViews();
  928. let callback = function () {
  929. initTree(bills, bills.workBook.getActiveSheet(), bills.treeSetting, rstData.bills);
  930. if(doAfterLoadGuidance){
  931. doAfterLoadGuidance();
  932. }
  933. $.bootstrapLoading.end();
  934. };
  935. //获取清单库中的工作内容和项目特征
  936. initJobAndCharacter(rstData.guidanceLib.billsLibId, callback);
  937. }, function () {
  938. $.bootstrapLoading.end();
  939. });
  940. }
  941. //初始化清单指引库
  942. //@param {Array}libDats @return {void}
  943. function initLibs(libDatas){
  944. libSel.empty();
  945. if(!libDatas){
  946. return;
  947. }
  948. let selectedLib = sessionStorage.getItem('stdBillsGuidance');
  949. for(let libData of libDatas){
  950. let opt = $('<option>').val(libData.id).text(libData.name);
  951. if(selectedLib && libData.id == selectedLib){
  952. opt.attr('selected', 'selected');
  953. }
  954. libSel.append(opt);
  955. }
  956. //初始默认选择
  957. libInitSel(libSel.select().val());
  958. }
  959. //初始化视图
  960. //@param {void} @return {void}
  961. function initViews(){
  962. //赋初始高度
  963. if($('#billsGuidance_bills').height() === 0 || $('#billsGuidance_items').height() === 0){
  964. let height = $(window).height()-$(".header").height()-$(".toolsbar").height()-$(".tools-bar-height-z").height();
  965. $('#billsGuidance_bills').height(height / 2);
  966. $('#billsGuidance_items').height(height / 2);
  967. }
  968. let modules = [bills];
  969. if(currentLib.type && currentLib.type === libType.elf){
  970. modules.push(elfItem);
  971. }
  972. else {
  973. modules.push(guideItem);
  974. }
  975. initWorkBooks(modules);
  976. }
  977. //展开至搜索出来点的节点
  978. //@param {Array}nodes @return {void}
  979. function expandSearchNodes(nodes){
  980. let that = this;
  981. let billsSheet = bills.workBook.getActiveSheet();
  982. renderSheetFunc(billsSheet, function () {
  983. function expParentNode(node){
  984. if(node.parent){
  985. if (!node.parent.expanded) {
  986. node.parent.setExpanded(true);
  987. }
  988. expParentNode(node.parent);
  989. }
  990. }
  991. for(let node of nodes){
  992. expParentNode(node);
  993. }
  994. TREE_SHEET_HELPER.refreshTreeNodeData(bills.treeSetting, billsSheet, bills.tree.roots, true);
  995. TREE_SHEET_HELPER.refreshNodesVisible(bills.tree.roots, billsSheet, true);
  996. });
  997. }
  998. //各按钮监听事件
  999. //@return {void}
  1000. function bindBtn(){
  1001. //打开清单指引库
  1002. $('#stdBillsGuidanceTab').click(function () {
  1003. if(libSel.children().length === 0 && !$(this).hasClass('disabled')){
  1004. initLibs(projectInfoObj.projectInfo.engineeringInfo.billsGuidance_lib);
  1005. }
  1006. });
  1007. //更改清单指引库
  1008. $('#stdBillsGuidanceLibSelect').change(function () {
  1009. //关闭搜索窗口
  1010. $('#billsGuidanceSearchResult').hide();
  1011. billsLibObj.clearHighLight(bills.workBook);
  1012. libInitSel($(this).select().val());
  1013. //记住选项
  1014. sessionStorage.setItem('stdBillsGuidance', $(this).select().val());
  1015. //清除展开收起状态sessionStorage
  1016. sessionStorage.removeItem('stdBillsGuidanceExpState');
  1017. });
  1018. //搜索
  1019. $('#stdBillsGuidanceSearch>div>button').click(function () {
  1020. if(!bills.tree){
  1021. return;
  1022. }
  1023. let billsSheet = bills.workBook.getActiveSheet();
  1024. billsLibObj.clearHighLight(bills.workBook);
  1025. let keyword = $('#stdBillsGuidanceSearch>input').val();
  1026. if (!keyword || keyword === '') {
  1027. $('#billsGuidanceSearchResult').hide();
  1028. return;
  1029. }
  1030. let result = bills.tree.items.filter(function (item) {
  1031. let codeIs = item.data.code ? item.data.code.indexOf(keyword) !== -1 : false;
  1032. let nameIs = item.data.name ? item.data.name.indexOf(keyword) !== -1 : false;
  1033. return codeIs || nameIs;
  1034. });
  1035. result.sort(function (x, y) {
  1036. return x.serialNo() - y.serialNo();
  1037. });
  1038. if (result.length !== 0) {
  1039. //展开搜索出来的节点
  1040. expandSearchNodes(result);
  1041. //设置记住展开
  1042. sessionStorage.setItem('stdBillsGuidanceExpState', bills.tree.getExpState(bills.tree.items));
  1043. let sel = billsSheet.getSelections();
  1044. renderSheetFunc(billsSheet, function () {
  1045. bills.controller.setTreeSelected(result[0]);
  1046. billsSheet.setSelection(result[0].serialNo(), sel[0].col, 1, 1);
  1047. for (let node of result) {
  1048. billsSheet.getRange(node.serialNo(), -1, 1, -1).backColor('lemonChiffon');
  1049. }
  1050. });
  1051. //搜索初始定位
  1052. billsSheet.showRow(result[0].serialNo(), GC.Spread.Sheets.VerticalPosition.center);
  1053. //查找下一条
  1054. $('#nextBillsGuidance').show();
  1055. $('#nextBillsGuidance').unbind('click');
  1056. $('#nextBillsGuidance').bind('click', function () {
  1057. let cur = bills.tree.selected, resultIndex = result.indexOf(cur), sel = billsSheet.getSelections();
  1058. if (resultIndex === result.length - 1) {
  1059. bills.controller.setTreeSelected(result[0]);
  1060. billsSheet.setSelection(result[0].serialNo(), sel[0].col, 1, 1);
  1061. billsSheet.showRow(result[0].serialNo(), GC.Spread.Sheets.VerticalPosition.center);
  1062. } else {
  1063. bills.controller.setTreeSelected(result[resultIndex + 1]);
  1064. billsSheet.setSelection(result[resultIndex + 1].serialNo(), sel[0].col, 1, 1);
  1065. billsSheet.showRow(result[resultIndex + 1].serialNo(), GC.Spread.Sheets.VerticalPosition.center);
  1066. }
  1067. });
  1068. //查找上一条
  1069. $('#preBillsGuidance').show();
  1070. $('#preBillsGuidance').unbind('click');
  1071. $('#preBillsGuidance').bind('click', function () {
  1072. let cur = bills.tree.selected, resultIndex = result.indexOf(cur), sel = billsSheet.getSelections();
  1073. if (resultIndex === 0) {
  1074. bills.controller.setTreeSelected(result[result.length - 1]);
  1075. billsSheet.setSelection(result[result.length - 1].serialNo(), sel[0].col, 1, 1);
  1076. billsSheet.showRow(result[result.length - 1].serialNo(), GC.Spread.Sheets.VerticalPosition.center);
  1077. } else {
  1078. bills.controller.setTreeSelected(result[resultIndex - 1]);
  1079. billsSheet.setSelection(result[resultIndex - 1].serialNo(), sel[0].col, 1, 1);
  1080. billsSheet.showRow(result[resultIndex - 1].serialNo(), GC.Spread.Sheets.VerticalPosition.center);
  1081. }
  1082. });
  1083. } else {
  1084. billsLibObj.clearHighLight(bills.workBook);
  1085. $('#nextBillsGuidance').hide();
  1086. $('#preBillsGuidance').hide();
  1087. }
  1088. $('#billsGuidanceSearchResultCount').text('搜索结果:' + result.length);
  1089. $('#billsGuidanceSearchResult').show();
  1090. autoFlashHeight();
  1091. refreshWorkBook();
  1092. });
  1093. //搜索框回车
  1094. $('#stdBillsGuidanceSearch>input').bind('keypress', function (event) {
  1095. if(event.keyCode === 13){
  1096. $(this).blur();
  1097. $('#stdBillsGuidanceSearch>div>button').click();
  1098. }
  1099. });
  1100. // 关闭搜索结果
  1101. $('#closeSearchBillsGuidance').click(function () {
  1102. $('#billsGuidanceSearchResult').hide();
  1103. billsLibObj.clearHighLight(bills.workBook);
  1104. autoFlashHeight();
  1105. refreshWorkBook();
  1106. });
  1107. }
  1108. //刷新表
  1109. //@return {void}
  1110. function refreshWorkBook(){
  1111. if(bills.workBook){
  1112. bills.workBook.refresh();
  1113. }
  1114. if(guideItem.workBook){
  1115. guideItem.workBook.refresh();
  1116. }
  1117. if(elfItem.workBook){
  1118. elfItem.workBook.refresh();
  1119. }
  1120. }
  1121. return {initViews, bindBtn, refreshWorkBook, setColumnWidthByRate, locateAtBills, bills, elfItem};
  1122. })();
  1123. $(document).ready(function(){
  1124. billsGuidance.bindBtn();
  1125. });