billsElf.js 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983
  1. 'use strict';
  2. /**
  3. *
  4. *
  5. * @author Zhong
  6. * @date 2018/11/25
  7. * @version
  8. */
  9. /*
  10. * 造价书下方清单精灵、清单指引
  11. * */
  12. //选项单选多选状态(按住alt为多选) 单选:0 多选:1
  13. let billsGuidanceSelMode = 0;
  14. const BillsSub = (function() {
  15. //清单子树挂载的地方,selected:当前选中的清单,mapping:以前九位清单编码为索引, 'xxx' : {sub: {datas, tree, controller}}
  16. let bills = {selected: null, mapping: {}};
  17. // 指引类型
  18. const itemType = {
  19. // 工作内容
  20. job: 0,
  21. // 定额
  22. ration: 1
  23. };
  24. // 库类型
  25. const libType = {
  26. // 清单指引
  27. guidance: 1,
  28. // 清单精灵
  29. elf: 2
  30. };
  31. // 清单精灵
  32. const elfItem = {
  33. dom: $('#billsSubItems_JL'),
  34. workBook: null,
  35. tree: null,
  36. controller: null,
  37. treeSetting: {
  38. treeCol: 0,
  39. emptyRows: 0,
  40. headRows: 1,
  41. headRowHeight: [40],
  42. defaultRowHeight: 21,
  43. cols: [
  44. {
  45. width: 250,
  46. readOnly: true,
  47. head: {
  48. titleNames: ["施工工序"],
  49. spanCols: [1],
  50. spanRows: [1],
  51. vAlign: [1],
  52. hAlign: [1],
  53. font: ["Arial"]
  54. },
  55. data: {
  56. field: "name",
  57. vAlign: 1,
  58. hAlign: 0,
  59. font: "Arial"
  60. }
  61. },
  62. {
  63. width: 250,
  64. readOnly: false,
  65. head: {
  66. titleNames: ["选项"],
  67. spanCols: [1],
  68. spanRows: [1],
  69. vAlign: [1],
  70. hAlign: [1],
  71. font: ["Arial"]
  72. },
  73. data: {
  74. field: "options",
  75. vAlign: 1,
  76. hAlign: 0,
  77. font: "Arial"
  78. }
  79. }
  80. ]
  81. },
  82. headers: [
  83. {name: '施工工序', dataCode: 'name', width: 180, rateWidth: 0.5, vAlign: 'center', hAlign: 'center', formatter: '@'},
  84. {name: '选项', dataCode: 'options', width: 180, rateWidth: 0.5, vAlign: 'center', hAlign: 'left', formatter: '@'},
  85. ],
  86. rowHeaderWidth:25,
  87. events: {
  88. SelectionChanging: function (sender, info) {
  89. elfItemInitSel(info.newSelections[0].row);
  90. },
  91. CellClick: function (sender, args) {
  92. if(elfItem.headers[args.col]['dataCode'] === 'options' && args.sheetArea === 3){
  93. if(!args.sheet.getCell(args.row, args.col).locked() && !args.sheet.isEditing()){
  94. args.sheet.startEdit();
  95. }
  96. }
  97. },
  98. ClipboardPasting: function (sender, info) {
  99. info.cancel = true;
  100. }
  101. }
  102. };
  103. // 清单指引
  104. const guideItem = {
  105. dom: $('#billsSubItems_ZY'),
  106. workBook: null,
  107. tree: null,
  108. controller: null,
  109. treeSetting: {
  110. treeCol: 0,
  111. emptyRows: 0,
  112. headRows: 1,
  113. headRowHeight: [40],
  114. defaultRowHeight: 21,
  115. cols: [
  116. {
  117. width: 420,
  118. readOnly: true,
  119. head: {
  120. titleNames: ["项目指引"],
  121. spanCols: [1],
  122. spanRows: [1],
  123. vAlign: [1],
  124. hAlign: [1],
  125. font: ["Arial"]
  126. },
  127. data: {
  128. field: "name",
  129. vAlign: 1,
  130. hAlign: 0,
  131. font: "Arial"
  132. }
  133. },
  134. {
  135. width: 35,
  136. readOnly: false,
  137. head: {
  138. titleNames: ["选择"],
  139. spanCols: [1],
  140. spanRows: [1],
  141. vAlign: [1],
  142. hAlign: [1],
  143. font: ["Arial"]
  144. },
  145. data: {
  146. field: "select",
  147. vAlign: 1,
  148. hAlign: 1,
  149. font: "Arial"
  150. }
  151. }
  152. ]
  153. },
  154. headers: [
  155. {name: '项目指引', dataCode: 'name', width: 300, vAlign: 'center', hAlign: 'left', formatter: '@'},
  156. {name: '选择', dataCode: 'select', width: 35, vAlign: 'center', hAlign: 'center', formatter: '@'},
  157. ],
  158. rowHeaderWidth:25,
  159. events: {
  160. /*EditStarting: function (sender, args) {
  161. if(!bills.tree || guideItem.headers[args.col]['dataCode'] === 'name'){
  162. args.cancel = true;
  163. }
  164. },*/
  165. ButtonClicked: function (sender, args) {
  166. if(args.sheet.isEditing()){
  167. args.sheet.endEdit(true);
  168. }
  169. //refreshInsertRation();
  170. },
  171. }
  172. };
  173. // 目前的模块:清单精灵或清单指引,默认是清单指引
  174. let curModule = guideItem;
  175. let curModules = [];// 清单指引和清单精灵共存
  176. let isFirst = true;
  177. // 切换目前的模块
  178. // 1:清单指引 2:清单精灵
  179. function switchModule(type) {
  180. if(type === libType.guidance){
  181. curModules.push(guideItem);
  182. if(isFirst) gljOprObj.billsTab = "#linkQDZY";
  183. }else{
  184. curModules.push(elfItem);
  185. if(isFirst) gljOprObj.billsTab = "#linkQDJL";
  186. }
  187. //2020-03-20清单指引和清单精灵并且,暂不用切换显示,只需绑定事件
  188. /* let libText;
  189. if (type === libType.guidance) {
  190. curModule = guideItem;
  191. libText = '清单指引';
  192. // 动态按钮
  193. $('#guidanceInsertRation').show();
  194. $('#elfInsertRation').hide();
  195. $('#elfInsertSingle').hide();
  196. } else {
  197. curModule = elfItem;
  198. libText = '清单精灵';
  199. // 动态按钮
  200. $('#guidanceInsertRation').hide();
  201. $('#elfInsertRation').show();
  202. $('#elfInsertSingle').show();
  203. }
  204. $('#qdjlTools').show();
  205. // 库名称、清单子菜单名称动态显示
  206. $('#linkQDJL').text(libText); */
  207. // 监听按钮事件
  208. if(isFirst == true) bindListener();//绑定一次事件就行了
  209. isFirst = false;
  210. }
  211. const options = {
  212. workBook: {
  213. tabStripVisible: false,
  214. allowContextMenu: false,
  215. allowCopyPasteExcelStyle : false,
  216. allowExtendPasteRange: false,
  217. allowUserDragDrop : false,
  218. allowUserDragFill: false,
  219. scrollbarMaxAlign : true
  220. },
  221. sheet: {
  222. protectionOptions: {allowResizeRows: true, allowResizeColumns: true},
  223. clipBoardOptions: GC.Spread.Sheets.ClipboardPasteOptions.values
  224. }
  225. };
  226. //渲染时方法,停止渲染
  227. //@param {Object}sheet {Function}func @return {void}
  228. function renderSheetFunc(sheet, func){
  229. sheet.suspendEvent();
  230. sheet.suspendPaint();
  231. if(func){
  232. func();
  233. }
  234. sheet.resumeEvent();
  235. sheet.resumePaint();
  236. }
  237. //设置表选项
  238. //@param {Object}workBook {Object}opts @return {void}
  239. function setOptions(workBook, opts) {
  240. for(let opt in opts.workBook){
  241. workBook.options[opt] = opts.workBook[opt];
  242. }
  243. for(let opt in opts.sheet){
  244. workBook.getActiveSheet().options[opt] = opts.sheet[opt];
  245. }
  246. }
  247. //建表头
  248. //@param {Object}sheet {Array}headers @return {void}
  249. function buildHeader(sheet, headers) {
  250. let fuc = function () {
  251. sheet.setColumnCount(headers.length);
  252. sheet.setRowHeight(0, 30, GC.Spread.Sheets.SheetArea.colHeader);
  253. sheet.setColumnWidth(0, sheet.getParent() === bills.workBook ? 15 : 25, GC.Spread.Sheets.SheetArea.rowHeader);
  254. if(sheet.getParent() === elfItem.workBook || sheet.getParent() === guideItem.workBook){
  255. sheet.setRowHeight(0, 20, GC.Spread.Sheets.SheetArea.colHeader);
  256. }
  257. for(let i = 0, len = headers.length; i < len; i++){
  258. sheet.setValue(0, i, headers[i].name, GC.Spread.Sheets.SheetArea.colHeader);
  259. sheet.setColumnWidth(i, headers[i].width, GC.Spread.Sheets.SheetArea.colHeader);
  260. if(headers[i].formatter){
  261. sheet.setFormatter(-1, i, headers[i].formatter);
  262. }
  263. sheet.getRange(-1, i, -1, 1).hAlign(GC.Spread.Sheets.HorizontalAlign[headers[i]['hAlign']]);
  264. sheet.getRange(-1, i, -1, 1).vAlign(GC.Spread.Sheets.VerticalAlign[headers[i]['vAlign']]);
  265. }
  266. };
  267. renderSheetFunc(sheet, fuc);
  268. }
  269. //表监听事件
  270. //@param {Object}workBook @return {void}
  271. function bindEvent(workBook, events) {
  272. if(Object.keys(events).length === 0){
  273. return;
  274. }
  275. const Events = GC.Spread.Sheets.Events;
  276. for(let event in events){
  277. workBook.bind(Events[event], events[event]);
  278. }
  279. }
  280. //根据宽度比例设置列宽
  281. //@param {Object}workBook {Number}workBookWidth {Array}headers @return {void}
  282. function setColumnWidthByRate() {
  283. let workBook = elfItem.workBook,
  284. workBookWidth = ($(window).width() - $('.main').find('.main-nav').width() - $('.main-side').width()) * 5 / 6,
  285. headers = elfItem.headers;
  286. if(workBook){
  287. workBookWidth -= 55;
  288. const sheet = workBook.getActiveSheet();
  289. sheet.suspendEvent();
  290. sheet.suspendPaint();
  291. for(let col = 0; col < headers.length; col++){
  292. if(headers[col]['rateWidth'] !== undefined && headers[col]['rateWidth'] !== null && headers[col]['rateWidth'] !== ''){
  293. let width = workBookWidth * headers[col]['rateWidth'];
  294. if(headers[col]['dataCode'] === 'options'){
  295. width = width;
  296. }
  297. sheet.setColumnWidth(col, width, GC.Spread.Sheets.SheetArea.colHeader)
  298. }
  299. else {
  300. if(headers[col]['headerWidth'] !== undefined && headers[col]['headerWidth'] !== null && headers[col]['headerWidth'] !== ''){
  301. sheet.setColumnWidth(col, headers[col]['headerWidth'], GC.Spread.Sheets.SheetArea.colHeader)
  302. }
  303. }
  304. }
  305. sheet.resumeEvent();
  306. sheet.resumePaint();
  307. }
  308. }
  309. //建表
  310. //@param {Object}module @return {void}
  311. function buildSheet(cmodule) {
  312. if(!cmodule.workBook){
  313. cmodule.workBook = new GC.Spread.Sheets.Workbook(cmodule.dom[0], {sheetCount: 1});
  314. sheetCommonObj.spreadDefaultStyle(cmodule.workBook);
  315. let sheet = cmodule.workBook.getActiveSheet();
  316. /*sheet.options.isProtected = true;
  317. sheet.getRange(-1, 0, -1, 1).locked(true);
  318. sheet.getRange(-1, 1, -1, 1).locked(false);*/
  319. if(cmodule.rowHeaderWidth) {
  320. sheet.setColumnWidth(0, cmodule.rowHeaderWidth, GC.Spread.Sheets.SheetArea.rowHeader);
  321. }
  322. setOptions(cmodule.workBook, options);
  323. buildHeader(cmodule.workBook.getActiveSheet(), cmodule.headers);
  324. bindEvent(cmodule.workBook, cmodule.events);
  325. }
  326. }
  327. function buildSheets(){
  328. for(let c of curModules){
  329. buildSheet(c);
  330. }
  331. }
  332. //刷新表
  333. //@return {void}
  334. function refreshWorkBook(){
  335. let eleID = "";
  336. let spreadEleID = "";
  337. let toolsID = "";
  338. if(gljOprObj.activeTab=='#linkQDZY'){
  339. curModule = guideItem;
  340. eleID = "qdzy";
  341. spreadEleID = "billsSubItems_ZY";
  342. toolsID = "qdzyTools";
  343. }else if(gljOprObj.activeTab=='#linkQDJL'){
  344. eleID = "qdjl";
  345. spreadEleID = "billsSubItems_JL";
  346. toolsID = "qdjlTools";
  347. curModule = elfItem;
  348. }
  349. if(eleID == "") return;
  350. //计算内部的表格高度
  351. if ($('#'+eleID).is(':visible')) {
  352. let totalHeight = $('#'+eleID).height(),
  353. elfToolsHeight = $('#'+toolsID).height();
  354. $('#'+spreadEleID).height(totalHeight - elfToolsHeight);
  355. if (curModule.workBook) {
  356. curModule.workBook.refresh();
  357. }
  358. }
  359. }
  360. //清空表数据
  361. //@param {Object}sheet {Array}headers {Number}rowCount @return {void}
  362. function cleanData(sheet, headers, rowCount){
  363. renderSheetFunc(sheet, function () {
  364. sheet.clear(-1, 0, -1, headers.length, GC.Spread.Sheets.SheetArea.viewport, GC.Spread.Sheets.StorageType.data);
  365. if (rowCount > 0) {
  366. sheet.setRowCount(rowCount);
  367. } else {
  368. sheet.setRowCount(0);
  369. }
  370. });
  371. }
  372. //初始化并输出树
  373. //@param {Object}module {Object}sheet {Object}treeSetting {Array}datas
  374. function initTree(module, sheet, treeSetting, datas){
  375. module.tree = idTree.createNew({id: 'ID', pid: 'ParentID', nid: 'NextSiblingID', rootId: -1, autoUpdate: true});
  376. module.controller = TREE_SHEET_CONTROLLER.createNew(module.tree, sheet, treeSetting, false);
  377. module.tree.loadDatas(datas);
  378. module.controller.showTreeData();
  379. }
  380. //清单精灵表焦点控制
  381. //@param {Number}row @return {void}
  382. function elfItemInitSel(row){
  383. let billsNode = bills.selected;
  384. let node = null;
  385. if(billsNode && billsNode.sub.tree){
  386. node = billsNode.sub.tree.items[row];
  387. if(node){
  388. billsNode.sub.tree.selected = node;
  389. }
  390. }
  391. }
  392. // 清单精灵数据初始化
  393. function initElf(sheet, elf, treeData) {
  394. //定额数据删除编号信息,(编码后+空格才会去除编码)
  395. for(let rData of treeData){
  396. if(rData.type === itemType.ration){
  397. let nameArr = rData.name.split(' ');
  398. if(nameArr.length > 1){
  399. nameArr.splice(0, 1);
  400. rData.name = nameArr.join(' ');
  401. }
  402. }
  403. }
  404. elf.sub.datas = treeData;
  405. //第一层节点数据
  406. let firstLevelDatas = _.filter(treeData, function (data) {
  407. return data.ParentID == -1;
  408. });
  409. //第一层初始数据的选项显示
  410. for(let fData of firstLevelDatas){
  411. let options = getOptions(fData, treeData);
  412. fData.options = options.length > 0 ? options[0].name : '';
  413. //下挂的选项
  414. fData.optionsData = options && options.length > 0 ? _.cloneDeep(options) : [];
  415. fData.optionChecked = options && options.length > 0 ? [_.cloneDeep(options[0])] : [];
  416. }
  417. renderSheetFunc(sheet, function () {
  418. initTree(elf.sub, sheet, elfItem.treeSetting, firstLevelDatas);
  419. //初始选择选项
  420. let initOptsOpr = [];
  421. for(let elfNode of elf.sub.tree.items){
  422. if(elfNode.data.optionsData.length > 0){
  423. initOptsOpr.push({node: elfNode, data: elfNode.data.optionsData[0]});
  424. }
  425. }
  426. for(let opr of initOptsOpr){
  427. insertNodeByData(opr.node, opr.data);
  428. }
  429. TREE_SHEET_HELPER.refreshTreeNodeData(elfItem.treeSetting, sheet, elf.sub.tree.items, false);
  430. setOptionsCellType(elf.sub.tree.items);
  431. //项目指引初始焦点
  432. elfItemInitSel(sheet.getActiveRowIndex() ? sheet.getActiveRowIndex() : 0);
  433. });
  434. }
  435. // 已经初始化过的清单精灵数据,重新展示
  436. function reshowElf(sheet, elf) {
  437. renderSheetFunc(sheet, function () {
  438. elf.sub.controller.showTreeData();
  439. setOptionsCellType(elf.sub.tree.items);
  440. //清单精灵初始焦点
  441. elfItemInitSel(sheet.getActiveRowIndex() ? sheet.getActiveRowIndex() : 0);
  442. });
  443. }
  444. //根据项目指引的类型设置单元格类型,定额类型的项目指引为复选框
  445. //@param {Array}nodes @return {void}
  446. function setItemCellType(nodes){
  447. //设置单元格类型
  448. const base = new GC.Spread.Sheets.CellTypes.Base();
  449. const checkBox = new GC.Spread.Sheets.CellTypes.CheckBox();
  450. const sheet = guideItem.workBook.getActiveSheet();
  451. renderSheetFunc(sheet, function(){
  452. for(let node of nodes){
  453. sheet.setCellType(node.serialNo(), 1, node.data.type === itemType.ration ? checkBox : base);
  454. }
  455. });
  456. }
  457. // 清单指引数据初始化
  458. function initGuidance(sheet, guidance, treeData) {
  459. guidance.sub.datas = treeData;
  460. renderSheetFunc(sheet, function () {
  461. initTree(guidance.sub, sheet, guideItem.treeSetting, treeData);
  462. //TREE_SHEET_HELPER.refreshTreeNodeData(guideItem.treeSetting, sheet, guidance.sub.tree.items, false);
  463. setItemCellType(guidance.sub.tree.items);
  464. });
  465. }
  466. // 已经初始化过的清单指引数据,重新展示
  467. function reshowGuidance(sheet, guidance) {
  468. renderSheetFunc(sheet, function () {
  469. guidance.sub.controller.showTreeData();
  470. setItemCellType(guidance.sub.tree.items);
  471. });
  472. }
  473. //清单焦点变换-清单子界面操作,获取清单前九位编码的标准清单清单精灵选项 或 清单指引数据
  474. //@param {String}code @return {void}
  475. function billsSelSub(code) {
  476. let type = libType.guidance;//指引和精灵可共存,所以需要类型区分
  477. curModule = guideItem;
  478. if(gljOprObj.activeTab=='#linkQDJL'){
  479. type = libType.elf;
  480. curModule = elfItem;
  481. }
  482. let sheet = curModule.workBook.getActiveSheet();
  483. cleanData(sheet, curModule.headers, -1);
  484. if (!code || code === '') {
  485. return;
  486. }
  487. let nineCode = code.substr(0, 9);
  488. //查看此清单映射是否存在此编码映射数据,不存在,则新建映射
  489. if (!bills.mapping[nineCode+type]) {
  490. bills.mapping[nineCode+type] = {sub: {datas: [], tree: null, controller: null}};
  491. }
  492. let container = bills.mapping[nineCode+type];
  493. bills.selected = container;
  494. if(!container.sub.tree){
  495. let guidanceLibID;
  496. if (projectObj.project.projectInfo.engineeringInfo && projectObj.project.projectInfo.engineeringInfo.billsGuidance_lib) {
  497. for(let lib of projectObj.project.projectInfo.engineeringInfo.billsGuidance_lib){
  498. if(lib && lib.type == type) guidanceLibID = lib.id;
  499. }
  500. }
  501. CommonAjax.post('/billsGuidance/api/getItemsByCode', {guidanceLibID: guidanceLibID, code: nineCode}, function (rstData) {
  502. curModule === elfItem
  503. ? initElf(sheet, container, rstData)
  504. : initGuidance(sheet, container, rstData);
  505. });
  506. } else{
  507. curModule === elfItem
  508. ? reshowElf(sheet, container)
  509. : reshowGuidance(sheet, container);
  510. }
  511. }
  512. //获取选项的深度
  513. //@param {Object}opt {Array}options(当前清单所有选项) @return {Array}
  514. function getOptionDepth(opt, options) {
  515. let parent = _.find(options, {ID: opt.ParentID});
  516. let depth = 0;
  517. while (parent){
  518. depth++;
  519. parent = _.find(options, {ID: parent.ParentID});
  520. }
  521. return depth;
  522. }
  523. //获取施工工序含有的选项(即当前施工工序的子项),获取的顺序按照NextSiblingID排序
  524. //@param {Object}process {Array}datas @return {Array}
  525. function getOptions(process, datas) {
  526. let rst = [];
  527. if(!process || !process.ID){
  528. return [];
  529. }
  530. let options = _.filter(datas, function (data) {
  531. return data.ParentID == process.ID;
  532. });
  533. if(options.length === 0){
  534. return [];
  535. }
  536. //根据NextSiblingID排序
  537. let IDMapping = {};
  538. for(let opt of options){
  539. IDMapping[opt.ID] = {self: opt, next: null, pre: null};
  540. }
  541. for(let opt of options){
  542. let next = IDMapping[opt.NextSiblingID] ? IDMapping[opt.NextSiblingID] : null;
  543. if(next){
  544. next.pre = IDMapping[opt.ID];
  545. IDMapping[opt.ID]['next'] = next;
  546. }
  547. }
  548. let first = null,
  549. rank = 0;
  550. for(let ID in IDMapping){
  551. let obj = IDMapping[ID];
  552. if(!obj.pre){
  553. first = obj;
  554. break;
  555. }
  556. }
  557. while(first){
  558. rank++;
  559. first.self.rank = rank;
  560. rst.push(first.self);
  561. first = first.next;
  562. }
  563. //兼容同层节点NextSibling错误的情况下,同层节点还是能显示出来(但是无法保证正确的顺序)
  564. //兼容模式下,不按照NextSibling排序,直接按照options元素位置排序
  565. if (rank !== options.length) {
  566. rst = [];
  567. rank = 0;
  568. for (let opt of options) {
  569. rank++;
  570. opt.rank = rank;
  571. rst.push(opt);
  572. }
  573. }
  574. return rst;
  575. }
  576. //设置清单精灵选项单元格
  577. //@param {Array}nodes @return {void}
  578. function setOptionsCellType(nodes) {
  579. let elfSheet = elfItem.workBook.getActiveSheet();
  580. for(let node of nodes){
  581. if(node.data.optionsData && node.data.optionsData.length > 0){
  582. elfSheet.getCell(node.serialNo(), 1).locked(false).cellType(getOptionsCellType());
  583. }
  584. else {
  585. elfSheet.getCell(node.serialNo(), 1).locked(true).cellType(new GC.Spread.Sheets.CellTypes.Base());
  586. }
  587. }
  588. }
  589. //递归插入节点:原始项目指引数据奇数层为需要插入的节点,偶数层为下拉选项
  590. //@param {Object}node(当前操作的节点) {Object}data(选项) @return {void}
  591. function insertNodeByData(node, data) {
  592. let elfSheet = elfItem.workBook.getActiveSheet();
  593. let sameDepthNodes = node.children;
  594. let insertNextSiblingID = -1,
  595. insertParentID = node.data.ID;
  596. //当前操作节点的选项
  597. let nodeOpts = getOptions(node.data, bills.selected.sub.datas);
  598. let subOpts = getOptions(data, bills.selected.sub.datas);
  599. let dataDepth = getOptionDepth(data, bills.selected.sub.datas);
  600. if(subOpts.length >0 && subOpts[0].type !== itemType.ration){
  601. if((dataDepth + 1) % 2 === 0){
  602. //排序后的数据
  603. let dataWithRank = _.find(nodeOpts, {ID: data.ID});
  604. //确定插入位置
  605. for(let subOpt of subOpts){
  606. for(let subNode of sameDepthNodes){
  607. //同层节点原本选项数据
  608. let subNodeOptData = _.find(bills.selected.sub.datas, {ID: subNode.data.ID});
  609. //同层节点原本父选项数据
  610. let subNodeOptParent = _.find(bills.selected.sub.datas, {ID: subNodeOptData.ParentID});
  611. let subNodeOptParentWithRank = _.find(nodeOpts, {ID: subNodeOptParent.ID});
  612. //父项顺序决定插入位置
  613. if(dataWithRank.rank < subNodeOptParentWithRank.rank){
  614. insertNextSiblingID = subNode.data.ID;
  615. break;
  616. }
  617. //父项顺序相同,根据子项顺序决定插入位置
  618. else if(dataWithRank.rank = subNodeOptParentWithRank.rank){
  619. if(subOpt.rank < subNode.data.rank){
  620. insertNextSiblingID = subNode.data.ID;
  621. break;
  622. }
  623. }
  624. }
  625. let sub2Opts = getOptions(subOpt, bills.selected.sub.datas);
  626. subOpt.options = sub2Opts.length > 0 ? sub2Opts[0].name : '';
  627. let cloneOpt = _.cloneDeep(subOpt);//不改变原本的数据,比如ParentID
  628. cloneOpt.optionChecked = sub2Opts.length > 0 ? [_.cloneDeep(sub2Opts[0])] : [];
  629. cloneOpt.optionsData = sub2Opts.length > 0 ? _.cloneDeep(sub2Opts) : [];
  630. let newNode = node.tree.insertByData(cloneOpt, insertParentID, insertNextSiblingID);
  631. elfSheet.addRows(newNode.serialNo(), 1);
  632. node.tree.selected = newNode;
  633. elfSheet.setSelection(newNode.serialNo(), elfSheet.getSelections()[0].col, 1, 1);
  634. if(sub2Opts.length > 0 && sub2Opts[0].type !== itemType.ration){
  635. insertNodeByData(newNode, sub2Opts[0]);
  636. }
  637. }
  638. }
  639. else {
  640. insertNodeByData(node, subOpts[0]);
  641. }
  642. }
  643. }
  644. //获取选项下拉多选单元格
  645. //@param {void} @return {void}
  646. function getOptionsCellType() {
  647. let me = this;
  648. let elfSheet= elfItem.workBook.getActiveSheet();
  649. function OptionsCellType() {
  650. this.isEscKey=false;
  651. this.displayText='';
  652. }
  653. function setOptionsDiv($editor, node, cellRect, cellStyle, top) {
  654. if(!node){
  655. return '';
  656. }
  657. let height = cellRect.height;
  658. let options = getOptions(node.data, bills.selected.sub.datas);
  659. top = options.length > 6 ? top - 6 * height : top - options.length * height;
  660. let $editInput = $(`<div style="height: ${height}px; background: ${cellStyle.backColor};overflow: hidden; white-space: nowrap; text-overflow: ellipsis">${node.data.options}</div>`),
  661. $optDiv = $(`<div style="position: fixed; width: ${cellRect.width}px; top: ${top}px;background: ${cellStyle.backColor};border: 1px solid; overflow: auto; height: ${options.length > 6 ? height*6+5 : height*options.length+5}px; font-size: 14px;"></div>`);
  662. for(let opt of options){
  663. let $opt = $(`<div title="${opt.name ? opt.name : ''}" class="elf-options" style="cursor: pointer; height: ${height}px;overflow: hidden; white-space: nowrap; text-overflow: ellipsis"></div>`),
  664. $optInput = $(`<input rank="${opt.rank}" value="${opt.ID}" style="cursor: pointer; margin-left: 5px; vertical-align: middle" type="checkbox"
  665. ${node.data.optionChecked && _.find(node.data.optionChecked, {ID: opt.ID}) ? 'checked' : ''} ${projectReadOnly ? 'disabled' : ''}>`);
  666. $opt.text(`${opt.name ? opt.name : ''}`);
  667. $opt.prepend($optInput);
  668. $optDiv.append($opt);
  669. //选项复选框点击监听
  670. if (!projectReadOnly) {
  671. $opt.click(function () {
  672. //单选
  673. if(billsGuidanceSelMode === 0){
  674. let $allInput = $optDiv.find('input');
  675. for(let input of $allInput){
  676. $(input).prop('checked', false);
  677. }
  678. $($optInput).prop('checked', 'checked');
  679. elfItem.workBook.getSheet(0).endEdit();
  680. } else {//多选
  681. }
  682. });
  683. }
  684. }
  685. $editor.append($editInput);
  686. $editor.append($optDiv);
  687. }
  688. //选择后处理
  689. function doAfterSel(node) {
  690. let checkedSels = $('.elf-options').find('input:checked');
  691. let checkedNameArr = [],
  692. optionChecked= [];
  693. for(let checkSel of checkedSels){
  694. let opt = _.cloneDeep(_.find(bills.selected.sub.datas, {ID: $(checkSel).val()}));
  695. opt.rank = $(checkSel).attr('rank');
  696. checkedNameArr.push(opt.name);
  697. optionChecked.push(opt);
  698. }
  699. this.displayText = checkedNameArr.length > 0 ? checkedNameArr.join(';') : '';
  700. node.data.options = this.displayText;
  701. node.data.optionChecked = optionChecked;
  702. //删除节点
  703. let deleteNodes = getDeleteNodes(node, optionChecked);
  704. for(let dNode of deleteNodes){
  705. elfSheet.deleteRows(dNode.serialNo(), dNode.posterityCount() + 1);
  706. node.tree.delete(dNode);
  707. }
  708. //插入节点
  709. for(let perCheked of optionChecked){
  710. let exist = false;
  711. let subOpts = getOptions(perCheked, bills.selected.sub.datas);
  712. for(let subNode of node.children){
  713. for(let subOpt of subOpts){
  714. if(subNode.data.ID === subOpt.ID){
  715. exist = true;
  716. break;
  717. }
  718. }
  719. }
  720. //不重复且不为定额时插入
  721. if(!exist && perCheked.type !== itemType.ration){
  722. insertNodeByData(node, perCheked);//这里递归,默认第一个
  723. }
  724. }
  725. TREE_SHEET_HELPER.refreshTreeNodeData(elfItem.treeSetting, elfSheet, node.tree.items, false);
  726. setOptionsCellType(node.tree.items);
  727. }
  728. //获取删除节点
  729. function getDeleteNodes(node, optionChecked) {
  730. let rst = [];
  731. for(let subNode of node.children){
  732. let exist = false;
  733. for(let perChecked of optionChecked){
  734. let subOpts = getOptions(perChecked, bills.selected.sub.datas);
  735. for(let subOpt of subOpts){
  736. if(subNode.data.ID === subOpt.ID){
  737. exist = true;
  738. break;
  739. }
  740. }
  741. }
  742. if(!exist){
  743. rst.push(subNode);
  744. }
  745. }
  746. return rst;
  747. }
  748. OptionsCellType.prototype = new GC.Spread.Sheets.CellTypes.Base();
  749. OptionsCellType.prototype.createEditorElement = function (context) {
  750. let element = document.createElement("div");//这里创建的,会自动销毁
  751. return element
  752. };
  753. OptionsCellType.prototype.activateEditor = function (editorContext, cellStyle, cellRect, context) {
  754. if (editorContext) {
  755. let $editor = $(editorContext);
  756. $editor.css("position", "fixed");
  757. $editor.css("background", "white");
  758. $editor.css("width", cellRect.width);
  759. $editor.attr("gcUIElement", "gcEditingInput");
  760. //编辑文本框距离浏览器的top
  761. let top = $('.header').height() + $('#zaojiashu').find('.toolsbar').height() + $('#top_div').height() + $('#bottom_div_ul').height() + $('#qdjlTools').height() + $('.resize-y').height();
  762. let node = bills.selected.sub.tree.items[elfSheet.getActiveRowIndex()];
  763. setOptionsDiv($editor, node, cellRect, cellStyle, top + cellRect.y);
  764. this.isEscKey = false;
  765. }
  766. }
  767. OptionsCellType.prototype.deactivateEditor = function (editorContext, context) {
  768. };
  769. OptionsCellType.prototype.setEditorValue = function (editor, value, context) {
  770. this.displayText = value;
  771. };
  772. OptionsCellType.prototype.getEditorValue = function (editor, context) {
  773. let me = this;
  774. let node = bills.selected.sub.tree.items[elfSheet.getActiveRowIndex()];
  775. if(this.isEscKey !=true){
  776. renderSheetFunc(elfSheet, function () {
  777. doAfterSel.call(me, node);
  778. });
  779. }
  780. this.isEscKey = false;
  781. return this.displayText;
  782. };
  783. OptionsCellType.prototype.updateEditor = function (editorContext, cellStyle, cellRect, context) {
  784. };
  785. OptionsCellType.prototype.isReservedKey = function (e, context) {
  786. //cell type handle tab key by itself
  787. this.isEscKey = e.keyCode === GC.Spread.Commands.Key.esc;
  788. return false;
  789. };
  790. OptionsCellType.prototype.getHitInfo = function (x, y, cellStyle, cellRect, context) {
  791. return {
  792. x: x,
  793. y: y,
  794. row: context.row,
  795. col: context.col,
  796. cellStyle: cellStyle,
  797. cellRect: cellRect,
  798. sheetArea: context.sheetArea
  799. };
  800. };
  801. return new OptionsCellType();
  802. }
  803. //原本清单存在此定额
  804. function existTheRation(nodes, rationID) {
  805. return nodes.find(node => node.data && node.data.stdID == rationID)
  806. }
  807. // 获取清单指生成的定额数据(不允许重复插入)
  808. function getInsertGuidanceRationData() {
  809. let rst = [];
  810. if(!bills.selected || !bills.selected.sub){
  811. return rst;
  812. }
  813. let tree = bills.selected.sub.tree;
  814. if(!tree){
  815. return rst;
  816. }
  817. let mainSelected = projectObj.project.mainTree.selected,
  818. mainSelRationNodes = mainSelected.children.filter(node => node.data && node.data.type === rationType.ration);
  819. // 从指引表从获取勾选的定额数据
  820. let sheet = guideItem.workBook.getSheet(0),
  821. rowCount = sheet.getRowCount();
  822. for (let row = 0; row < rowCount; row++) {
  823. let data = tree.items[row].data;
  824. // 勾选的定额,且该定额在目标清单下不存在才插入
  825. let isChecked = sheet.getValue(row, 1);
  826. if (isChecked
  827. && data.type === itemType.ration
  828. && !existTheRation(mainSelRationNodes, data.rationID)) {
  829. rst.push({
  830. itemQuery: {
  831. userID,
  832. ID: data.rationID
  833. },
  834. rationType: rationType.ration
  835. });
  836. }
  837. }
  838. return rst;
  839. }
  840. //获取清单精灵生成的定额数据(跳过重复,不允许重复插入)
  841. //@return {Array}
  842. function getInsertElfRationData() {
  843. let rst = [];
  844. if(!bills.selected || !bills.selected.sub){
  845. return rst;
  846. }
  847. let tree = bills.selected.sub.tree;
  848. if(!tree){
  849. return rst;
  850. }
  851. let mainSelected = projectObj.project.mainTree.selected,
  852. mainSelRationNodes = mainSelected.children.filter(node => node.data && node.data.type === rationType.ration);
  853. //造价书当前选中清单下的定额
  854. for(let node of tree.items){
  855. for(let perChecked of node.data.optionChecked){
  856. //选项直接是定额
  857. if(perChecked.type === itemType.ration && !existTheRation(mainSelRationNodes, perChecked.rationID)){
  858. rst.push({itemQuery: {userID: userID, ID: perChecked.rationID}, rationType: rationType.ration});
  859. }
  860. //选项下子选项是定额
  861. else {
  862. let rationOpts = getOptions(perChecked, bills.selected.sub.datas);
  863. for(let ration of rationOpts){
  864. if(ration.type === itemType.ration && !existTheRation(mainSelRationNodes, ration.rationID)){
  865. rst.push({itemQuery: {userID: userID, ID: ration.rationID}, rationType: rationType.ration});
  866. }
  867. }
  868. }
  869. }
  870. }
  871. return rst;
  872. }
  873. //获取清单精灵插入单条定额的数据
  874. //@return {Array}
  875. function getInsertElfSingleRation() {
  876. let rst = [];
  877. if (!bills.selected || !bills.selected.sub) {
  878. return rst;
  879. }
  880. let tree = bills.selected.sub.tree;
  881. if (!tree) {
  882. return rst;
  883. }
  884. let elfSelected = tree.selected;
  885. if (!elfSelected || !elfSelected.data.optionChecked || !elfSelected.data.optionChecked[0]) {
  886. return rst;
  887. }
  888. let mainSelected = projectObj.project.mainTree.selected,
  889. mainSelRationNodes = mainSelected.children.filter(node => node.data && node.data.type === rationType.ration);
  890. //选中的节点第一个选项时定额选项或第一个选项下的子选项时定额选项
  891. let firstOptionChecked = elfSelected.data.optionChecked[0];
  892. if (firstOptionChecked.type === itemType.ration && !existTheRation(mainSelRationNodes, firstOptionChecked.rationID)) {
  893. rst.push({itemQuery: {userID: userID, ID: firstOptionChecked.rationID}, rationType: rationType.ration});
  894. } else {
  895. let rationOpts = getOptions(firstOptionChecked, bills.selected.sub.datas);
  896. for(let ration of rationOpts){
  897. if(ration.type === itemType.ration && !existTheRation(mainSelRationNodes, ration.rationID)){
  898. rst.push({itemQuery: {userID: userID, ID: ration.rationID}, rationType: rationType.ration});
  899. break;
  900. }
  901. }
  902. }
  903. return rst;
  904. }
  905. //插入定额
  906. //@return {void}
  907. async function insertRations(addRationDatas){
  908. if(addRationDatas.length > 0){
  909. try {
  910. $.bootstrapLoading.start();
  911. await projectObj.project.Ration.addMultiRation(addRationDatas);
  912. projectObj.setActiveCell('quantity', true);
  913. } catch (err) {
  914. console.log(err);
  915. if (!$('hintBox_form').is(':visible')) {
  916. alert(err);
  917. }
  918. } finally {
  919. $.bootstrapLoading.end();
  920. }
  921. }
  922. }
  923. function handleClick(getRationFunc) {
  924. if (!projectObj.project.Ration.canAdd(projectObj.project.mainTree.selected)) {
  925. return;
  926. }
  927. let addRationDatas = getRationFunc();
  928. insertRations(addRationDatas);
  929. }
  930. //各监听事件
  931. //@return {void}
  932. function bindListener(){
  933. // 插入定额
  934. $('#guidanceInsertRation').click(function () {
  935. handleClick(getInsertGuidanceRationData);
  936. });
  937. // 应用选项
  938. $('#elfInsertRation').click(function () {
  939. handleClick(getInsertElfRationData);
  940. });
  941. // 应用单条
  942. $('#elfInsertSingle').click(function () {
  943. handleClick(getInsertElfSingleRation);
  944. });
  945. }
  946. function toggleDiv(action){
  947. if(action=='hide'){
  948. $('#QDZY_div').hide();
  949. $('#QDJL_div').hide();
  950. }else{
  951. for(let c of curModules){
  952. if(c == guideItem) $('#QDZY_div').show();
  953. if(c == elfItem) $('#QDJL_div').show();
  954. }
  955. }
  956. }
  957. return {
  958. toggleDiv,
  959. switchModule,
  960. buildSheets,
  961. refreshWorkBook,
  962. billsSelSub,
  963. setColumnWidthByRate
  964. };
  965. })();