std_billsGuidance_lib.js 53 KB

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