std_billsGuidance_lib.js 51 KB

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