std_billsGuidance_lib.js 56 KB

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