billsGuidance.js 69 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687
  1. 'use strict';
  2. /**
  3. *
  4. *
  5. * @author Zhong
  6. * @date 2018/6/1
  7. * @version
  8. */
  9. const billsGuidance = (function () {
  10. function _isDef(v) {
  11. return typeof v !== 'undefined' && v !== null;
  12. }
  13. let moduleName = 'stdBillsGuidance';
  14. //上下拖动的拖动条高度
  15. const verticalResize = 10;
  16. //自执行函数全局变量定义
  17. const libID = getQueryString('libID');
  18. //总工作内容数据
  19. let stdBillsJobData = [];
  20. //总项目特征数据
  21. let stdBillsFeatureData = [];
  22. const bills = {
  23. dom: $('#billsSpread'),
  24. workBook: null,
  25. cache: [],
  26. tree: null,
  27. controller: null,
  28. treeSetting: {
  29. treeCol: 0,
  30. emptyRows: 0,
  31. headRows: 1,
  32. headRowHeight: [40],
  33. defaultRowHeight: 21,
  34. cols: [{
  35. width: 200,
  36. readOnly: true,
  37. showHint: true,
  38. head: {
  39. titleNames: ["项目编码"],
  40. spanCols: [1],
  41. spanRows: [1],
  42. vAlign: [1],
  43. hAlign: [1],
  44. font: ["Arial"]
  45. },
  46. data: {
  47. field: "code",
  48. vAlign: 1,
  49. hAlign: 0,
  50. font: "Arial"
  51. }
  52. }, {
  53. width: 200,
  54. readOnly: true,
  55. head: {
  56. titleNames: ["项目名称"],
  57. spanCols: [1],
  58. spanRows: [1],
  59. vAlign: [1],
  60. hAlign: [1],
  61. font: ["Arial"]
  62. },
  63. data: {
  64. field: "name",
  65. vAlign: 1,
  66. hAlign: 0,
  67. font: "Arial"
  68. }
  69. }]
  70. },
  71. headers: [
  72. {name: '项目编码', dataCode: 'code', width: 200, vAlign: 'center', hAlign: 'left', formatter: '@'},
  73. {name: '项目名称', dataCode: 'name', width: 200, vAlign: 'center', hAlign: 'left', formatter: '@'}
  74. ],
  75. events: {
  76. SelectionChanged: function (sender, info) {
  77. billsInitSel(info.newSelections[0].row);
  78. }
  79. }
  80. };
  81. //项目指引类型
  82. const itemType = {
  83. job: 0,
  84. ration: 1
  85. };
  86. //项目指引复制整块localStorage key
  87. const itemCopyBlockKey = 'guideItemCopyBlock';
  88. const updateType = {
  89. create: 'create',
  90. update: 'update',
  91. del: 'delete'
  92. };
  93. //项目指引节点状态:展开全部、收起定额
  94. const itemExpandState = {
  95. expand: 1,
  96. contract: 0
  97. };
  98. //项目指引当前节点展开收缩状态,默认展开全部
  99. let curExpandState = 1;
  100. const guideItem = {
  101. dom: $('#guideItemSpread'),
  102. workBook: null,
  103. tree: null,
  104. controller: null,
  105. treeSetting: {
  106. treeCol: 0,
  107. emptyRows: 0,
  108. headRows: 1,
  109. headRowHeight: [40],
  110. defaultRowHeight: 21,
  111. cols: [{
  112. width: 400,
  113. readOnly: false,
  114. head: {
  115. titleNames: ["项目指引"],
  116. spanCols: [1],
  117. spanRows: [1],
  118. vAlign: [1],
  119. hAlign: [1],
  120. font: ["Arial"]
  121. },
  122. data: {
  123. field: "name",
  124. vAlign: 1,
  125. hAlign: 0,
  126. font: "Arial"
  127. }
  128. }]
  129. },
  130. headers: [
  131. {name: '项目指引', dataCode: 'name', width: 400, vAlign: 'center', hAlign: 'left', formatter: '@'},
  132. ],
  133. events: {
  134. SelectionChanged: function (sender, info) {
  135. guideItemInitSel(info.newSelections[0].row)
  136. },
  137. EditEnded: function (sender, args) {
  138. edit(args.sheet, [{row: args.row, col: args.col}]);
  139. },
  140. RangeChanged: function (sender, args) {
  141. edit(args.sheet, args.changedCells);
  142. }
  143. }
  144. };
  145. //定额章节树
  146. const section = {
  147. dom: $('#sectionSpread'),
  148. workBook: null,
  149. cache: [],
  150. tree: null,
  151. controller: null,
  152. treeSetting: {
  153. treeCol: 0,
  154. emptyRows: 0,
  155. headRows: 1,
  156. headRowHeight: [40],
  157. defaultRowHeight: 21,
  158. cols: [{
  159. width: 400,
  160. readOnly: true,
  161. head: {
  162. titleNames: ["名称"],
  163. spanCols: [1],
  164. spanRows: [1],
  165. vAlign: [1],
  166. hAlign: [1],
  167. font: ["Arial"]
  168. },
  169. data: {
  170. field: "name",
  171. vAlign: 1,
  172. hAlign: 0,
  173. font: "Arial"
  174. }
  175. }]
  176. },
  177. headers: [
  178. {name: '名称', dataCode: 'name', width: 400, vAlign: 'center', hAlign: 'left', formatter: '@'},
  179. ],
  180. events: {
  181. SelectionChanged: function (sender, info) {
  182. sectionInitSel(info.newSelections[0].row)
  183. }
  184. }
  185. };
  186. const ration = {
  187. dom: $('#rationSpread'),
  188. workBook: null,
  189. datas: [],//所有的数据,搜索定额时,从所有数据中筛选
  190. cache: [],//显示在表格上的数据,添加定额可以有效根据行识别定额
  191. headers: [
  192. {name: '选择', dataCode: 'select', width: 50, vAlign: 'center', hAlign: 'center'},
  193. {name: '编码', dataCode: 'code', width: 110, vAlign: 'center', hAlign: 'left', formatter: '@'},
  194. {name: '名称', dataCode: 'name', width: 250, vAlign: 'center', hAlign: 'left', formatter: '@'},
  195. {name: '单位', dataCode: 'unit', width: 100, vAlign: 'center', hAlign: 'left', formatter: '@'}
  196. ],
  197. events: {
  198. ButtonClicked: function (sender, args) {
  199. if(args.sheet.isEditing()){
  200. args.sheet.endEdit(true);
  201. }
  202. },
  203. CellDoubleClick: function (sender, args) {
  204. if(ration.headers[args.col].dataCode === 'name'){
  205. let insertDatas = getInsertRations([args.row]);
  206. if(insertDatas.length > 0){
  207. insert(insertDatas, false);
  208. }
  209. }
  210. }
  211. }
  212. };
  213. const options = {
  214. workBook: {
  215. tabStripVisible: false,
  216. allowContextMenu: false,
  217. allowCopyPasteExcelStyle : false,
  218. allowExtendPasteRange: false,
  219. allowUserDragDrop : false,
  220. allowUserDragFill: false,
  221. scrollbarMaxAlign : true
  222. },
  223. sheet: {
  224. protectionOptions: {allowResizeRows: true, allowResizeColumns: true},
  225. clipBoardOptions: GC.Spread.Sheets.ClipboardPasteOptions.values
  226. }
  227. };
  228. //渲染时方法,停止渲染
  229. //@param {Object}sheet {Function}func @return {void}
  230. function renderSheetFunc(sheet, func){
  231. sheet.suspendEvent();
  232. sheet.suspendPaint();
  233. if(func){
  234. func();
  235. }
  236. sheet.resumeEvent();
  237. sheet.resumePaint();
  238. }
  239. //设置表选项
  240. //@param {Object}workBook {Object}opts @return {void}
  241. function setOptions (workBook, opts) {
  242. for(let opt in opts.workBook){
  243. workBook.options[opt] = opts.workBook[opt];
  244. }
  245. for(let opt in opts.sheet){
  246. workBook.getActiveSheet().options[opt] = opts.sheet[opt];
  247. }
  248. }
  249. //建表头
  250. //@param {Object}sheet {Array}headers @return {void}
  251. function buildHeader(sheet, headers) {
  252. let fuc = function () {
  253. sheet.setColumnCount(headers.length);
  254. sheet.setRowHeight(0, 40, GC.Spread.Sheets.SheetArea.colHeader);
  255. for(let i = 0, len = headers.length; i < len; i++){
  256. sheet.setValue(0, i, headers[i].name, GC.Spread.Sheets.SheetArea.colHeader);
  257. sheet.setColumnWidth(i, headers[i].width, GC.Spread.Sheets.SheetArea.colHeader);
  258. if(headers[i].formatter){
  259. sheet.setFormatter(-1, i, headers[i].formatter);
  260. }
  261. sheet.getRange(-1, i, -1, 1).hAlign(GC.Spread.Sheets.HorizontalAlign[headers[i]['hAlign']]);
  262. sheet.getRange(-1, i, -1, 1).vAlign(GC.Spread.Sheets.VerticalAlign[headers[i]['vAlign']]);
  263. }
  264. };
  265. renderSheetFunc(sheet, fuc);
  266. }
  267. //表监听事件
  268. //@param {Object}workBook @return {void}
  269. function bindEvent(workBook, events) {
  270. if(Object.keys(events).length === 0){
  271. return;
  272. }
  273. const Events = GC.Spread.Sheets.Events;
  274. let sheet = workBook.getActiveSheet();
  275. for(let event in events){
  276. workBook.bind(Events[event], events[event]);
  277. }
  278. }
  279. //建表
  280. //@param {Object}module @return {void}
  281. function buildSheet(module) {
  282. if(!module.workBook){
  283. module.workBook = new GC.Spread.Sheets.Workbook(module.dom[0], {sheetCount: 1});
  284. let sheet = module.workBook.getActiveSheet();
  285. if(module === bills){
  286. sheet.name('stdBillsGuidance_bills');
  287. //默认初始可控制焦点在清单表中
  288. module.workBook.focus();
  289. sheet.options.isProtected = true;
  290. }
  291. else if(module === ration){
  292. sheet.options.isProtected = true;
  293. sheet.getRange(-1, 0, -1, 1).locked(false);
  294. sheet.getRange(-1, 1, -1, -1).locked(true);
  295. }
  296. else if(module === guideItem){
  297. sheetCommonObj.bindEscKey(module.workBook, [{sheet: sheet, editStarting: null, editEnded: module.events.EditEnded}]);
  298. }
  299. setOptions(module.workBook, options);
  300. buildHeader(module.workBook.getActiveSheet(), module.headers);
  301. bindEvent(module.workBook, module.events);
  302. }
  303. }
  304. //清空表数据
  305. //@param {Object}sheet {Array}headers {Number}rowCount @return {void}
  306. function cleanData(sheet, headers, rowCount){
  307. renderSheetFunc(sheet, function () {
  308. sheet.clear(-1, 0, -1, headers.length, GC.Spread.Sheets.SheetArea.viewport, GC.Spread.Sheets.StorageType.data);
  309. if (rowCount >= 0) {
  310. sheet.setRowCount(rowCount);
  311. }
  312. });
  313. }
  314. //根据清单获取项目指引
  315. //@param {String}guidanceLibID {Number}billsID {Function}callback @return {void}
  316. function getItemsByBills(guidanceLibID, billsID, callback){
  317. CommonAjax.post('/billsGuidance/api/getItemsByBills', {guidanceLibID: guidanceLibID, billsID: billsID}, function (rstData) {
  318. if(callback){
  319. callback(rstData);
  320. }
  321. });
  322. }
  323. //清单表焦点控制
  324. //@param {Number}row @return {void}
  325. function billsInitSel(row){
  326. let guideSheet = guideItem.workBook.getActiveSheet();
  327. cleanData(guideSheet, guideItem.headers, -1);
  328. let node = bills.tree.items[row];
  329. if(!node){
  330. return;
  331. }
  332. bills.tree.selected = node;
  333. //显示备注
  334. $('.main-side-bottom').find('textarea').val(node.data.comment ? node.data.comment : '');
  335. if(!node.guidance.tree){
  336. getItemsByBills(libID, node.data.ID, function (rstData) {
  337. initTree(node.guidance, guideSheet, guideItem.treeSetting, rstData);
  338. setNodesExpandState(node.guidance.tree.items, curExpandState);
  339. renderSheetFunc(guideSheet, function () {
  340. TREE_SHEET_HELPER.refreshNodesVisible(node.guidance.tree.roots, guideSheet, true);
  341. });
  342. //设置底色
  343. setNodesColor(guideSheet, node.guidance.tree.items);
  344. //项目指引初始焦点
  345. guideItemInitSel(guideSheet.getActiveRowIndex() ? guideSheet.getActiveRowIndex() : 0);
  346. });
  347. } else{
  348. setNodesExpandState(node.guidance.tree.items, curExpandState);
  349. node.guidance.controller.showTreeData();
  350. //设置底色
  351. setNodesColor(guideSheet, node.guidance.tree.items);
  352. //项目指引初始焦点
  353. guideItemInitSel(guideSheet.getActiveRowIndex() ? guideSheet.getActiveRowIndex() : 0);
  354. }
  355. }
  356. //设置项目节点展开收起状态:展开全部、收起定额
  357. //@param {Array}nodes(当前清单下的所有项目指引节点) {Number}expandState(展开全部1或收起定额0).
  358. function setNodesExpandState(nodes, expandState) {
  359. if(expandState === itemExpandState.contract) {
  360. //找出所有定额的父节点
  361. let rations = _.filter(nodes, function (node) {
  362. return node.data.type === itemType.ration;
  363. });
  364. let rationParentIDs = [];
  365. for(let ration of rations){
  366. if(ration.data.ParentID != -1){
  367. rationParentIDs.push(ration.data.ParentID);
  368. }
  369. }
  370. rationParentIDs = Array.from(new Set(rationParentIDs));
  371. let rationParentNodes = _.filter(nodes, function (node) {
  372. return rationParentIDs.includes(node.data.ID);
  373. });
  374. //收起定额
  375. for(let node of rationParentNodes){
  376. node.setExpanded(false);
  377. }
  378. } else {
  379. for(let node of nodes){
  380. node.setExpanded(true);
  381. }
  382. }
  383. }
  384. //根据奇偶层级设置节点底色,奇数层为蓝色(树节点深度为偶数)
  385. //@param {Object}sheet {Array}nodes @return {void}
  386. function setNodesColor(sheet, nodes) {
  387. const color = '#DFE8F9';
  388. renderSheetFunc(sheet, function () {
  389. for(let node of nodes){
  390. let style = new GC.Spread.Sheets.Style();
  391. style.borderLeft = new GC.Spread.Sheets.LineBorder("#D4D4D4", GC.Spread.Sheets.LineStyle.thin);
  392. style.borderTop = new GC.Spread.Sheets.LineBorder("#D4D4D4", GC.Spread.Sheets.LineStyle.thin);
  393. style.borderRight = new GC.Spread.Sheets.LineBorder("#D4D4D4", GC.Spread.Sheets.LineStyle.thin);
  394. style.borderBottom = new GC.Spread.Sheets.LineBorder("#D4D4D4", GC.Spread.Sheets.LineStyle.thin);
  395. let nDepth = node.depth();
  396. style.backColor = nDepth % 2 == 0 && _isDef(node.data.type) && node.data.type === itemType.job ? color : 'White';
  397. sheet.setStyle(node.serialNo(), -1, style);
  398. }
  399. });
  400. }
  401. //选中的节点是否全是同层节点
  402. //@param {Object}sheet {Array}items @return {Boolean}
  403. function itemsSameDepth(sheet, items) {
  404. let sels = sheet.getSelections();
  405. if(sels.length === 0 || items.length === 0){
  406. return false;
  407. }
  408. let depths = [];
  409. for(let i = 0; i < sels[0].rowCount; i++){
  410. let row = sels[0].row + i;
  411. let node = items[row];
  412. if(node){
  413. depths.push(node.depth());
  414. }
  415. }
  416. }
  417. //节点子项是否全是工作内容
  418. //@param {Object}node @return {Boolean}
  419. function allJobChildren(node){
  420. for(let c of node.children){
  421. if(c.data.type === itemType.ration){
  422. return false;
  423. }
  424. }
  425. return true;
  426. }
  427. //节点子项是否全是定额
  428. //@param {Object}node @return {Boolean}
  429. function allRationChildren(node){
  430. for(let c of node.children){
  431. if(c.data.type === itemType.job){
  432. return false;
  433. }
  434. }
  435. return true;
  436. }
  437. //刷新按钮有效性
  438. //@param {Object}node @return {void}
  439. function refreshBtn(node){
  440. //全部设为无效
  441. $('.tools-btn').children().addClass('disabled');
  442. $('#insertRation').addClass('disabled');
  443. $('.main-bottom-content').find('textarea').attr('readonly', true);
  444. //插入
  445. if(bills.tree.selected && bills.tree.selected.guidance.tree){
  446. $('#insert').removeClass('disabled');
  447. if(node && node.data.type === itemType.ration){
  448. $('#insert').addClass('disabled');
  449. }
  450. }
  451. //删除
  452. if(node){
  453. $('#del').removeClass('disabled');
  454. }
  455. if(node && node.data.type === itemType.job){
  456. //升级
  457. if(node.parent){
  458. $('#upLevel').removeClass('disabled');
  459. if(node.nextSibling && node.children.length > 0 && !allJobChildren(node)){
  460. $('#upLevel').addClass('disabled');
  461. }
  462. }
  463. //降级
  464. if(node.preSibling){
  465. $('#downLevel').removeClass('disabled');
  466. if(node.preSibling.children.length > 0 && !allJobChildren(node.preSibling)){
  467. $('#downLevel').addClass('disabled');
  468. }
  469. }
  470. }
  471. //上移
  472. if(node && node.preSibling){
  473. $('#upMove').removeClass('disabled')
  474. }
  475. //下移
  476. if(node && node.nextSibling){
  477. $('#downMove').removeClass('disabled');
  478. }
  479. //收起定额、展开全部
  480. $('#expandContract').removeClass('disabled');
  481. //插入定额
  482. if(node && (node.children.length === 0 || allRationChildren(node))){
  483. $('#insertRation').removeClass('disabled');
  484. }
  485. //备注,奇数节点可用
  486. if(node && (node.depth() + 1) % 2 === 1 && node.data.type !== itemType.ration){
  487. $('.main-bottom-content').find('textarea').attr('readonly', false);
  488. }
  489. }
  490. //项目指引表焦点控制
  491. //@param {Number}row @return {void}
  492. function guideItemInitSel(row){
  493. let billsNode = bills.tree.selected;
  494. let node = null;
  495. if(billsNode && billsNode.guidance.tree){
  496. node = billsNode.guidance.tree.items[row];
  497. if(node){
  498. billsNode.guidance.tree.selected = node;
  499. //显示备注
  500. $('.main-bottom-content').find('textarea').val(node.data.comment ? node.data.comment : '');
  501. }
  502. }
  503. refreshBtn(node);
  504. }
  505. //初始化当前库名
  506. //@param {String} @return {void}
  507. function initLibName(libName) {
  508. $('#libName')[0].outerHTML = $('#libName')[0].outerHTML.replace("XXX清单指引", libName);
  509. }
  510. //初始化各工作表
  511. //@param {Array}modules @return {void}
  512. function initWorkBooks(modules){
  513. for(let module of modules){
  514. buildSheet(module);
  515. }
  516. }
  517. function tipDivCheck(){
  518. setTimeout(function () {
  519. let tips = $('#autoTip');
  520. if(ration.tipDiv == 'show'){
  521. return;
  522. } else if(ration.tipDiv == 'hide'&&tips){
  523. tips.hide();
  524. ration._toolTipElement = null;
  525. }
  526. },600)
  527. }
  528. //获取悬浮提示单元格
  529. //@param {Object}sheet @return {Object}
  530. function getTipCellType(sheet) {
  531. let setting = {};
  532. let TipCellType = function () {};
  533. TipCellType.prototype = new GC.Spread.Sheets.CellTypes.Text();
  534. TipCellType.prototype.getHitInfo = function (x, y, cellStyle, cellRect, context) {
  535. return {
  536. x: x,
  537. y: y,
  538. row: context.row,
  539. col: context.col,
  540. cellStyle: cellStyle,
  541. cellRect: cellRect,
  542. sheet: context.sheet,
  543. sheetArea: context.sheetArea
  544. };
  545. };
  546. TipCellType.prototype.processMouseEnter = function (hitinfo) {
  547. let text = hitinfo.sheet.getText(hitinfo.row, hitinfo.col);
  548. let tag = hitinfo.sheet.getTag(hitinfo.row, hitinfo.col);
  549. if(tag !== undefined && tag){
  550. text = tag;
  551. }
  552. if(sheet && sheet.getParent().qo){
  553. setting.pos = SheetDataHelper.getObjPos(sheet.getParent().qo);
  554. }
  555. let delayTimes = 500; //延时时间
  556. let now_timeStamp = +new Date();
  557. this.tipTimeStamp = now_timeStamp;
  558. let me = this;
  559. setTimeout(function () {
  560. if(now_timeStamp - me.tipTimeStamp == 0){//鼠标停下的时候才显示
  561. if (setting.pos && text && text !== '') {
  562. //固定不显示的div,存储文本获取固定div宽度,toolTipElement由于显示和隐藏,获取宽度不正确
  563. if(!me._fixedTipElement){
  564. let div = $('#fixedTip1')[0];
  565. if (!div) {
  566. div = document.createElement("div");
  567. $(div).css("padding", 5)
  568. .attr("id", 'fixedTip');
  569. $(div).hide();
  570. document.body.insertBefore(div, null);
  571. }
  572. me._fixedTipElement = div;
  573. }
  574. $(me._fixedTipElement).width('');
  575. $(me._fixedTipElement).html(text);
  576. if (!me._toolTipElement) {
  577. let div = $('#autoTip1')[0];
  578. if (!div) {
  579. div = document.createElement("div");
  580. $(div).css("position", "absolute")
  581. .css("border", "1px #C0C0C0 solid")
  582. .css("box-shadow", "1px 2px 5px rgba(0,0,0,0.4)")
  583. .css("font", "0.9rem Calibri")
  584. .css("background", "white")
  585. .css("padding", 5)
  586. .attr("id", 'autoTip1');
  587. $(div).hide();
  588. document.body.insertBefore(div, null);
  589. }
  590. me._toolTipElement = div;
  591. $(me._toolTipElement).width('');
  592. //实时读取位置信息
  593. if(hitinfo.sheet && hitinfo.sheet.getParent().qo){
  594. setting.pos = SheetDataHelper.getObjPos(hitinfo.sheet.getParent().qo);
  595. }
  596. $(me._toolTipElement).html(text);
  597. //定额库定额特殊处理
  598. if($(hitinfo.sheet.getParent().qo).attr('id') === 'rationSpread'){
  599. let divWidth = $(me._fixedTipElement).width(),
  600. divHeight = $(me._fixedTipElement).height();
  601. if(divWidth > 600){
  602. divWidth = 590;
  603. $(me._toolTipElement).width(divWidth);
  604. }
  605. let top = setting.pos.y + hitinfo.y - divHeight < 0 ? 0 : setting.pos.y + hitinfo.cellRect.y - divHeight;
  606. $(me._toolTipElement).css("top", top).css("left", setting.pos.x - divWidth);
  607. }
  608. else{
  609. $(me._toolTipElement).css("top", setting.pos.y + hitinfo.y +15).css("left", setting.pos.x + hitinfo.x + 15);
  610. }
  611. //名称
  612. if(hitinfo.col === 2){
  613. let acStyle = hitinfo.sheet.getActualStyle(hitinfo.row, hitinfo.col),
  614. zoom = hitinfo.sheet.zoom();
  615. let value = hitinfo.sheet.getValue(hitinfo.row, hitinfo.col);
  616. let textLength = me.getAutoFitWidth(value, text, acStyle, zoom, {sheet: hitinfo.sheet, row: hitinfo.row, col: hitinfo.col, sheetArea: GC.Spread.Sheets.SheetArea.viewport});
  617. let cellWidth = hitinfo.sheet.getCell(-1, hitinfo.col).width();
  618. if(textLength > cellWidth){
  619. $(me._toolTipElement).css("top", setting.pos.y + hitinfo.y +15).css("left", setting.pos.x + hitinfo.x + 15);
  620. $(me._toolTipElement).show("fast");
  621. ration.tipDiv = 'show';//做个标记
  622. }
  623. }
  624. else {
  625. $(me._toolTipElement).show("fast");
  626. ration.tipDiv = 'show';//做个标记
  627. }
  628. }
  629. }
  630. }
  631. },delayTimes);
  632. };
  633. TipCellType.prototype.processMouseLeave = function (hininfo) {
  634. this.tipTimeStamp = +new Date();
  635. ration.tipDiv = 'hide';
  636. if (this._toolTipElement) {
  637. $(this._toolTipElement).hide();
  638. this._toolTipElement = null;
  639. }
  640. tipDivCheck();//延时检查:当tips正在show的时候,就调用了hide方法,会导致tips一直存在,所以设置一个超时处理
  641. }
  642. return new TipCellType();
  643. }
  644. //输出表数据(定额表)
  645. //@param {Object}sheet {Array}headers {Array}datas @return {void}
  646. function showData(sheet, headers, datas){
  647. let fuc = function () {
  648. sheet.setRowCount(datas.length);
  649. //复选框
  650. let checkBoxType = new GC.Spread.Sheets.CellTypes.CheckBox();
  651. let tipCellType = getTipCellType(sheet);
  652. sheet.setCellType(-1, 0, checkBoxType);
  653. for(let col = 0, cLen = headers.length; col < cLen; col++){
  654. for(let row = 0, rLen = datas.length; row < rLen; row++){
  655. sheet.setValue(row, col, datas[row][headers[col]['dataCode']]);
  656. if(col === 1){
  657. sheet.setTag(row, col, datas[row]['hint']);
  658. }
  659. }
  660. }
  661. sheet.setCellType(-1, 1, tipCellType);
  662. sheet.setCellType(-1, 2, tipCellType);
  663. };
  664. renderSheetFunc(sheet, fuc);
  665. }
  666. //根据定额章节树ID获取定额(从数据缓存中获取,定额数据一开始一次性拉取)
  667. //@param {Number}sectionId {Array}rations @return {Array}
  668. function getRationsBySectionId(sectionId, rations) {
  669. if(!sectionId || !rations){
  670. return [];
  671. }
  672. return _.filter(rations, {sectionId});
  673. }
  674. //定额章节树焦点控制
  675. //@param {Number}row @return {void}
  676. function sectionInitSel(row) {
  677. let rationSheet = ration.workBook.getActiveSheet();
  678. let sectionNode = section.tree ? section.tree.items[row] : null;
  679. if(sectionNode && sectionNode.children.length === 0){
  680. let sectionRations = getRationsBySectionId(sectionNode.data.ID, ration.datas);
  681. ration.cache = sectionRations;
  682. showData(rationSheet, ration.headers, sectionRations);
  683. }
  684. else {
  685. cleanData(rationSheet, ration.headers, 0);
  686. }
  687. }
  688. //初始化定额条目
  689. //@param {Number}rationLibId @return {void}
  690. function initRationItems(rationLibId){
  691. $.bootstrapLoading.start();
  692. //获取定额章节树
  693. let sectionSheet = section.workBook.getActiveSheet();
  694. CommonAjax.post('/rationRepository/api/getRationTree', {rationLibId: rationLibId}, function (sectionDatas) {
  695. //获取所有定额数据
  696. let reqEntity = {rationLibId: rationLibId, showHint: true, returnFields: '-_id code ID sectionId name unit basePrice rationGljList jobContent annotation'};
  697. CommonAjax.post('/rationRepository/api/getRationItemsByLib', reqEntity, function (rstData) {
  698. section.cache = sectionDatas;
  699. initTree(section, section.workBook.getActiveSheet(), section.treeSetting, sectionDatas);
  700. //初始焦点在第一行(切换库)
  701. sectionSheet.setActiveCell(0, 0);
  702. rstData.sort(function (a, b) {
  703. let rst = 0;
  704. if(a.code > b.code){
  705. rst = 1;
  706. }
  707. else if(a.code < b.code){
  708. rst = -1;
  709. }
  710. return rst;
  711. });
  712. ration.datas = rstData;
  713. sectionInitSel(0);
  714. $.bootstrapLoading.end();
  715. }, function () {
  716. $.bootstrapLoading.end();
  717. });
  718. }, function () {
  719. $.bootstrapLoading.end();
  720. });
  721. }
  722. //初始化定额库选择
  723. //@param {String}compilationId @return {void}
  724. function initRationLibs(compilationId){
  725. CommonAjax.post('/rationRepository/api/getRationLibsByCompilation', {compilationId: compilationId}, function (rstData) {
  726. $('#rationLibSel').empty();
  727. for(let rationLib of rstData){
  728. let opt = `<option value="${rationLib.ID}">${rationLib.dispName}</option>`;
  729. $('#rationLibSel').append(opt);
  730. }
  731. //初始选择
  732. initRationItems(parseInt($('#rationLibSel').select().val()));
  733. $('#rationLibSel').change(function () {
  734. let rationLibId = parseInt($(this).select().val());
  735. initRationItems(rationLibId);
  736. })
  737. });
  738. }
  739. //清单设置悬浮提示信息
  740. //@param {Array}billsNodes(清单节点) {Array}jobs(总的工作内容数据) {Array}items(总的项目特征数据)
  741. function setBillsHint(billsNodes, jobs, items) {
  742. let jobsMapping = {},
  743. itemsMapping = {};
  744. for(let job of jobs){
  745. jobsMapping[job.id] = job;
  746. }
  747. for(let item of items){
  748. itemsMapping[item.id] = item;
  749. }
  750. let tagInfo = [];
  751. for(let billsNode of billsNodes){
  752. let hintArr = [];
  753. let billsItems = billsNode.data.items;
  754. if(billsItems.length > 0){
  755. //项目特征
  756. hintArr.push('项目特征:');
  757. }
  758. let itemCount = 1,
  759. jobCount = 1;
  760. for(let billsItem of billsItems){
  761. let itemData = itemsMapping[billsItem.id];
  762. if(itemData){
  763. //特征值
  764. let eigens = [];
  765. for(let eigen of itemData.itemValue){
  766. eigens.push(eigen.value);
  767. }
  768. eigens = eigens.join(';');
  769. hintArr.push(`${itemCount}.${itemData.content}${eigens === '' ? '' : ': ' + eigens}`);
  770. itemCount ++;
  771. }
  772. }
  773. //工作内容
  774. let billsJobs = billsNode.data.jobs;
  775. if(billsJobs.length > 0){
  776. hintArr.push('工作内容:');
  777. }
  778. for(let billsJob of billsJobs){
  779. let jobData = jobsMapping[billsJob.id];
  780. if(jobData){
  781. hintArr.push(`${jobCount}.${jobData.content}`);
  782. jobCount ++;
  783. }
  784. }
  785. /*if(billsNode.data.ruleText && billsNode.data.ruleText !== ''){
  786. hintArr.push('工程量计算规则:');
  787. hintArr.push(billsNode.data.ruleText);
  788. }
  789. if(billsNode.data.recharge && billsNode.data.recharge !== ''){
  790. hintArr.push('补注:');
  791. hintArr.push(billsNode.data.recharge);
  792. }*/
  793. if(hintArr.length > 0){
  794. tagInfo.push({row: billsNode.serialNo(), value: hintArr.join('\n')});
  795. }
  796. }
  797. let sheet = bills.workBook.getActiveSheet();
  798. renderSheetFunc(sheet, function () {
  799. for(let tagI of tagInfo){
  800. sheet.setTag(tagI.row, 0, tagI.value);
  801. }
  802. });
  803. }
  804. //初始化清单的工作内容和项目特征
  805. //@param {Number}billsLibId {Function}callback @return {void}
  806. function initJobAndCharacter(billsLibId, callback){
  807. CommonAjax.post('/stdBillsEditor/getJobContent', {billsLibId: billsLibId}, function (datas) {
  808. stdBillsJobData = datas;
  809. CommonAjax.post('/stdBillsEditor/getItemCharacter', {billsLibId: billsLibId}, function (datas) {
  810. stdBillsFeatureData = datas;
  811. if(callback){
  812. callback();
  813. }
  814. });
  815. });
  816. }
  817. let billsLibId = 0;
  818. //获取指引库信息及关联的清单
  819. //@param {Number}libID {Function}callback @return {Object}
  820. function getLibWithBills(libID, callback){
  821. CommonAjax.post('/billsGuidance/api/getLibWithBills', {libID: libID}, function (rstData) {
  822. billsLibId = rstData.guidanceLib.billsLibId;
  823. initRationLibs(rstData.guidanceLib.compilationId);
  824. bills.cache = rstData.bills;
  825. initLibName(rstData.guidanceLib.name);
  826. /*initTree(bills, bills.workBook.getActiveSheet(), bills.treeSetting, bills.cache);
  827. //每一棵项目指引树挂在清单节点上
  828. for(let node of bills.tree.items){
  829. node.guidance = {tree: null, controller: null};
  830. }
  831. //默认初始节点
  832. billsInitSel(0);
  833. if(callback){
  834. callback(rstData);
  835. }*/
  836. let initDataCallback = function () {
  837. initTree(bills, bills.workBook.getActiveSheet(), bills.treeSetting, bills.cache);
  838. //每一棵项目指引树挂在清单节点上
  839. for(let node of bills.tree.items){
  840. node.guidance = {tree: null, controller: null};
  841. }
  842. //默认初始节点
  843. billsInitSel(0);
  844. if(callback){
  845. callback(rstData);
  846. }
  847. };
  848. initJobAndCharacter(rstData.guidanceLib.billsLibId, initDataCallback);
  849. }, function (msg) {
  850. window.location.href = '/billsGuidance/main';
  851. });
  852. }
  853. //初始化并输出树
  854. //@param {Object}module {Object}sheet {Object}treeSetting {Array}datas
  855. function initTree(module, sheet, treeSetting, datas){
  856. module.tree = idTree.createNew({id: 'ID', pid: 'ParentID', nid: 'NextSiblingID', rootId: -1, autoUpdate: true});
  857. module.controller = TREE_SHEET_CONTROLLER.createNew(module.tree, sheet, treeSetting);
  858. module.tree.loadDatas(datas);
  859. module.controller.showTreeData();
  860. if(module === bills){
  861. setBillsHint(bills.tree.items, stdBillsJobData, stdBillsFeatureData);
  862. }
  863. }
  864. //更新清单备注
  865. function updateBillsComment(updateData, callback) {
  866. CommonAjax.post('/stdBillsEditor/updateBills', updateData, function () {
  867. if (callback) {
  868. callback();
  869. }
  870. });
  871. }
  872. //更新项目指引
  873. //@param {Array}updateDatas {Function}callback @return {void}
  874. function updateGuideItems(updateDatas, callback){
  875. CommonAjax.post('/billsGuidance/api/updateItems', {updateDatas: updateDatas}, function (rstData) {
  876. if(callback){
  877. callback(rstData);
  878. }
  879. });
  880. }
  881. //编辑后自动去除换行符回车符
  882. const deESC = /[\r, \n]/g;
  883. //项目指引编辑
  884. //@param {Object}sheet {Array}cells
  885. function edit(sheet, cells){
  886. let updateDatas = [];
  887. //同步节点数据
  888. let syncDatas = [];
  889. for(let cell of cells){
  890. let text = sheet.getValue(cell.row, cell.col);
  891. text = text ? text.toString() : '';
  892. text = text.replace(deESC, '');
  893. sheet.setValue(cell.row, cell.col, text);
  894. let node = bills.tree.selected.guidance.tree.items[cell.row];
  895. if(node.data.name != text){
  896. syncDatas.push({node: node, text: text});
  897. updateDatas.push({updateType: updateType.update, findData: {ID: node.getID()}, updateData: {name: text}});
  898. }
  899. }
  900. if(updateDatas.length > 0){
  901. updateGuideItems(updateDatas, function () {
  902. for(let syncData of syncDatas){
  903. syncData.node.data.name = syncData.text;
  904. }
  905. }, function () {
  906. //失败恢复
  907. renderSheetFunc(sheet, function () {
  908. for(let syncData of syncDatas){
  909. sheet.setValue(syncData.node.serialNo(), 0, syncData.node.data.name ? syncData.node.data.name : '');
  910. }
  911. });
  912. });
  913. }
  914. }
  915. //项目指引插入,支持一次插入多条数据
  916. //@param {Array}datas {Boolean}tobeChild(插入成为子项) {Function}callback @return {void}
  917. function insert(datas, tobeChild, callback = null){
  918. $.bootstrapLoading.start();
  919. let sheet = guideItem.workBook.getActiveSheet();
  920. let controller = bills.tree.selected.guidance.controller;
  921. let selected = bills.tree.selected.guidance.tree.selected;
  922. let updateDatas = [];
  923. //建立数组下标索引
  924. let newDataIndex = {};
  925. for(let i = 0; i < datas.length; i++){
  926. let newNodeData = {
  927. libID: libID, ID: uuid.v1(), ParentID: selected ? selected.getParentID() : -1, NextSiblingID: selected ? selected.getNextSiblingID() : -1,
  928. billsID: bills.tree.selected.getID()
  929. };
  930. //定额类型插入当前工作内容焦点行,
  931. if(selected && ((selected.data.type === itemType.job && datas[i].type === itemType.ration) || tobeChild)){
  932. newNodeData.ParentID = selected.getID();
  933. newNodeData.NextSiblingID = -1;
  934. }
  935. Object.assign(newNodeData, datas[i]);
  936. newDataIndex[i] = newNodeData;
  937. }
  938. for(let i = 0; i < datas.length; i++){
  939. //第一个节点
  940. if(i === 0){
  941. //非插入成子节点,更新选中节点NestSiblingID
  942. if(selected && !((selected.data.type === itemType.job && datas[i].type === itemType.ration) || tobeChild)){
  943. updateDatas.push({updateType: updateType.update, findData: {ID: selected.getID()}, updateData: {NextSiblingID: newDataIndex[i].ID}});
  944. }
  945. }
  946. //非最后一个节点
  947. if(i !== datas.length - 1){
  948. newDataIndex[i].NextSiblingID = newDataIndex[i + 1].ID;
  949. }
  950. updateDatas.push({updateType: updateType.create, updateData: newDataIndex[i]});
  951. }
  952. updateGuideItems(updateDatas, function () {
  953. for(let updateData of updateDatas){
  954. if(updateData.updateType === updateType.create){
  955. let newNode = controller.insertByIDS(updateData.updateData.ID, updateData.updateData.ParentID, updateData.updateData.NextSiblingID);
  956. //同步data
  957. Object.assign(newNode.data, updateData.updateData);
  958. sheet.setValue(newNode.serialNo(), 0, newNode.data.name);
  959. refreshBtn(newNode);
  960. }
  961. }
  962. if(callback){
  963. callback();
  964. }
  965. setNodesColor(sheet, bills.tree.selected.guidance.tree.items);
  966. guideItem.workBook.focus(true);
  967. $.bootstrapLoading.end();
  968. });
  969. }
  970. //项目指引删除操作
  971. //@return {void}
  972. function del(){
  973. $.bootstrapLoading.start();
  974. let controller = bills.tree.selected.guidance.controller;
  975. let selNodes = [];
  976. let sheet = guideItem.workBook.getSheet(0);
  977. let sel = sheet.getSelections()[0];
  978. if(sel){
  979. sel.row = sel.row === -1 ? 0 : sel.row;
  980. for(let i = 0; i < sel.rowCount; i++){
  981. if(bills.tree.selected.guidance.tree.items[sel.row + i]){
  982. selNodes.push(bills.tree.selected.guidance.tree.items[sel.row + i]);
  983. }
  984. }
  985. }
  986. //选中的块节点
  987. let blockNodes = getBlockNodes(selNodes);
  988. let updateDatas = [];
  989. function getDelDatas(nodes){
  990. for (let node of nodes) {
  991. updateDatas.push({updateType: updateType.del, findData: {ID: node.getID()}});
  992. if (node.children.length > 0) {
  993. getDelDatas(node.children);
  994. }
  995. }
  996. }
  997. getDelDatas(blockNodes);
  998. //更新相关的前节点
  999. for (let node of blockNodes) {
  1000. if (node.preSibling && !blockNodes.includes(node.preSibling)) {
  1001. let next = node;
  1002. while (next.nextSibling && blockNodes.includes(next.nextSibling)) {
  1003. next = next.nextSibling;
  1004. }
  1005. updateDatas.push({updateType: updateType.update, findData: {ID: node.preSibling.getID()}, updateData: {NextSiblingID: next.getNextSiblingID()}});
  1006. }
  1007. }
  1008. updateGuideItems(updateDatas, function () {
  1009. controller.m_delete(blockNodes);
  1010. guideItemInitSel(sheet.getActiveRowIndex());
  1011. refreshBtn(bills.tree.selected.guidance.tree.selected);
  1012. setNodesColor(guideItem.workBook.getActiveSheet(), bills.tree.selected.guidance.tree.items);
  1013. $.bootstrapLoading.end();
  1014. guideItem.workBook.focus(true)
  1015. });
  1016. }
  1017. //项目指引升级
  1018. //@return {void}
  1019. function upLevel(){
  1020. $.bootstrapLoading.start();
  1021. let controller = bills.tree.selected.guidance.controller;
  1022. let selected = bills.tree.selected.guidance.tree.selected;
  1023. let updateDatas = [];
  1024. //更新父节点
  1025. updateDatas.push({updateType: updateType.update, findData: {ID: selected.getParentID()}, updateData: {NextSiblingID: selected.getID()}});
  1026. //更新选中节点
  1027. updateDatas.push({updateType: updateType.update, findData: {ID: selected.getID()},
  1028. updateData: {ParentID: selected.parent.getParentID(), NextSiblingID: selected.parent.getNextSiblingID()}});
  1029. if(selected.nextSibling && selected.children.length > 0){
  1030. //更新选中节点最末子节点
  1031. let lastChild = selected.children[selected.children.length - 1];
  1032. updateDatas.push({updateType: updateType.update, findData: {ID: lastChild.getID()}, updateData: {NextSiblingID: -1}});
  1033. }
  1034. //选中节点的所有后兄弟节点成为选中节点的子项
  1035. let selectedNextIDs = [];
  1036. let sNext = selected.nextSibling;
  1037. while(sNext){
  1038. selectedNextIDs.push(sNext.getID());
  1039. sNext = sNext.nextSibling;
  1040. }
  1041. for(let sID of selectedNextIDs){
  1042. updateDatas.push({updateType: updateType.update, findData: {ID: sID}, updateData: {ParentID: selected.getID()}});
  1043. }
  1044. updateGuideItems(updateDatas, function () {
  1045. controller.upLevel();
  1046. refreshBtn(bills.tree.selected.guidance.tree.selected);
  1047. setNodesColor(guideItem.workBook.getActiveSheet(), bills.tree.selected.guidance.tree.items);
  1048. $.bootstrapLoading.end();
  1049. guideItem.workBook.focus(true)//31574
  1050. });
  1051. }
  1052. //项目指引降级
  1053. //@return {void}
  1054. function downLevel(){
  1055. $.bootstrapLoading.start();
  1056. let controller = bills.tree.selected.guidance.controller;
  1057. let selected = bills.tree.selected.guidance.tree.selected;
  1058. let updateDatas = [];
  1059. //更新前兄弟节点
  1060. updateDatas.push({updateType: updateType.update, findData: {ID: selected.preSibling.getID()}, updateData: {NextSiblingID: selected.getNextSiblingID()}});
  1061. //更新前兄弟节点最末子节点
  1062. if(selected.preSibling.children.length > 0){
  1063. let lastChild = selected.preSibling.children[selected.preSibling.children.length - 1];
  1064. updateDatas.push({updateType: updateType.update, findData: {ID: lastChild.getID()}, updateData: {NextSiblingID: selected.getID()}});
  1065. }
  1066. //更新选中节点
  1067. updateDatas.push({updateType: updateType.update, findData: {ID: selected.getID()}, updateData: {ParentID: selected.preSibling.getID(), NextSiblingID: -1}});
  1068. updateGuideItems(updateDatas, function () {
  1069. controller.downLevel();
  1070. refreshBtn(bills.tree.selected.guidance.tree.selected);
  1071. setNodesColor(guideItem.workBook.getActiveSheet(), bills.tree.selected.guidance.tree.items);
  1072. $.bootstrapLoading.end();
  1073. guideItem.workBook.focus(true)
  1074. });
  1075. }
  1076. //项目指引上移
  1077. //@return {void}
  1078. function upMove(){
  1079. $.bootstrapLoading.start();
  1080. let controller = bills.tree.selected.guidance.controller;
  1081. let selected = bills.tree.selected.guidance.tree.selected;
  1082. let updateDatas = [];
  1083. //更新前节点
  1084. updateDatas.push({updateType: updateType.update, findData: {ID: selected.preSibling.getID()}, updateData: {NextSiblingID: selected.getNextSiblingID()}});
  1085. //更新前前节点
  1086. if(selected.preSibling.preSibling){
  1087. updateDatas.push({updateType: updateType.update, findData: {ID: selected.preSibling.preSibling.getID()}, updateData: {NextSiblingID: selected.getID()}});
  1088. }
  1089. //更新选中节点
  1090. updateDatas.push({updateType: updateType.update, findData: {ID: selected.getID()}, updateData: {NextSiblingID: selected.preSibling.getID()}});
  1091. updateGuideItems(updateDatas, function () {
  1092. controller.upMove();
  1093. refreshBtn(bills.tree.selected.guidance.tree.selected);
  1094. setNodesColor(guideItem.workBook.getActiveSheet(), bills.tree.selected.guidance.tree.items);
  1095. $.bootstrapLoading.end();
  1096. guideItem.workBook.focus(true)
  1097. });
  1098. }
  1099. //项目指引下移
  1100. //@return {void}
  1101. function downMove(){
  1102. $.bootstrapLoading.start();
  1103. let controller = bills.tree.selected.guidance.controller;
  1104. let selected = bills.tree.selected.guidance.tree.selected;
  1105. let updateDatas = [];
  1106. //更新下节点
  1107. updateDatas.push({updateType: updateType.update, findData: {ID: selected.getNextSiblingID()}, updateData: {NextSiblingID: selected.getID()}});
  1108. //更新前节点
  1109. if(selected.preSibling){
  1110. updateDatas.push({updateType: updateType.update, findData: {ID: selected.preSibling.getID()}, updateData: {NextSiblingID: selected.getNextSiblingID()}});
  1111. }
  1112. //更新选中节点
  1113. updateDatas.push({updateType: updateType.update, findData: {ID: selected.getID()}, updateData: {NextSiblingID: selected.nextSibling.getNextSiblingID()}});
  1114. updateGuideItems(updateDatas, function () {
  1115. controller.downMove();
  1116. refreshBtn(bills.tree.selected.guidance.tree.selected);
  1117. setNodesColor(guideItem.workBook.getActiveSheet(), bills.tree.selected.guidance.tree.items);
  1118. $.bootstrapLoading.end();
  1119. guideItem.workBook.focus(true)
  1120. });
  1121. }
  1122. //获取定额类型的项目指引名称,通过定额转换
  1123. //@param {Object}ration @return {String}
  1124. function getRationItemName(ration){
  1125. let arr = [];
  1126. arr.push(ration.code ? ration.code : '');
  1127. arr.push(ration.name ? ration.name : '');
  1128. arr.push(ration.basePrice ? ration.basePrice : '');
  1129. let rst = arr.join(' ');
  1130. rst += `/${ration.unit ? ration.unit : ''}`;
  1131. return rst;
  1132. }
  1133. //获取选中的定额表行
  1134. //@return {Array}
  1135. function getCheckedRationRows(){
  1136. let rst = [];
  1137. let sheet = ration.workBook.getActiveSheet();
  1138. for(let i = 0; i < sheet.getRowCount(); i++){
  1139. let checked = sheet.getValue(i, 0);
  1140. if(checked){
  1141. rst.push(i);
  1142. }
  1143. }
  1144. return rst;
  1145. }
  1146. //清空选中定额表行
  1147. //@param {Array}rows @return {void}
  1148. function clearCheckedRation(rows) {
  1149. let sheet = ration.workBook.getActiveSheet();
  1150. renderSheetFunc(sheet, function () {
  1151. for(let row of rows){
  1152. sheet.setValue(row, 0, 0);
  1153. }
  1154. });
  1155. }
  1156. //获取要插入的定额数据
  1157. //@param {Array}rows @return {Array}
  1158. function getInsertRations(rows){
  1159. let rst = [];
  1160. //当前已存在定额
  1161. let curRationIems = [];
  1162. let selected = bills.tree.selected.guidance.tree.selected;
  1163. if(selected){
  1164. if(selected.data.type === itemType.job){
  1165. curRationIems = selected.children;
  1166. }
  1167. else {
  1168. curRationIems = selected.parent ? selected.parent.children : selected.tree.roots;
  1169. }
  1170. }
  1171. for(let row of rows){
  1172. let selRation = ration.cache[row];
  1173. if(selRation){
  1174. //添加的定额是否已存在,不重复添加
  1175. let isExist = false;
  1176. for(let curRation of curRationIems){
  1177. if(curRation.data.rationID == selRation.ID){
  1178. isExist = true;
  1179. break;
  1180. }
  1181. }
  1182. if(!isExist){
  1183. rst.push({type: itemType.ration, name: getRationItemName(selRation), rationID: selRation.ID});
  1184. }
  1185. }
  1186. }
  1187. return rst;
  1188. }
  1189. //获取块节点父项不存在于选中节点中的节点
  1190. //@param {Array}nodes(选中的节点) @return {Array}
  1191. function getBlockNodes(nodes) {
  1192. let nodeMapping = {};
  1193. for(let node of nodes){
  1194. nodeMapping[node.data.ID] = node;
  1195. }
  1196. //块节点,父项不存在于选中节点中的节点
  1197. let blockNodes = [];
  1198. for(let node of nodes){
  1199. if(!nodeMapping[node.data.ParentID]){
  1200. blockNodes.push(node);
  1201. }
  1202. }
  1203. return blockNodes;
  1204. }
  1205. //允许复制整块,如果有多个块节点,且块节点的父项不同,则不可复制
  1206. //@param {Array}nodes(块节点) @return {Boolean}
  1207. function canCopyBlock(nodes) {
  1208. if(!nodes || nodes.length === 0){
  1209. return false;
  1210. }
  1211. let pID = nodes[0].data.ParentID;
  1212. for(let node of nodes){
  1213. if(node.data.ParentID !== pID){
  1214. return false;
  1215. }
  1216. }
  1217. return true;
  1218. }
  1219. //允许粘贴整块 有粘贴数据,节点存在,如果粘贴到的节点为定额数据,粘贴数据为全定额数据
  1220. //@param {Object}node(粘贴到的节点)
  1221. function canPasteBlock(node) {
  1222. let pasteDatas = JSON.parse(getLocalCache(itemCopyBlockKey));
  1223. if(!pasteDatas || pasteDatas.length === 0){
  1224. return false;
  1225. }
  1226. if(!node){
  1227. return false;
  1228. }
  1229. //若粘贴到定额节点,则数据须全为定额
  1230. if(node.data.type === itemType.ration){
  1231. for(let data of pasteDatas){
  1232. if(data.type !== itemType.ration){
  1233. return false;
  1234. }
  1235. }
  1236. }
  1237. //若粘贴到非定额节点,则粘贴的顶层数据须全为非定额
  1238. else {
  1239. let topDatas = _.filter(pasteDatas, {ParentID: -1});
  1240. for(let topData of topDatas){
  1241. if(topData.type === itemType.ration){
  1242. return false;
  1243. }
  1244. }
  1245. }
  1246. return true;
  1247. }
  1248. //复制整块,将块节点下所有节点数据复制一份,并且重新生成ID、ParentID、NextSiblingID,使用localStorage存储
  1249. //@param {Array}nodes(块节点) @return {void}
  1250. function copyBlocks(nodes) {
  1251. nodes = _.cloneDeep(nodes);
  1252. //将块节点的ParentID暂时设置为-1
  1253. for(let topNode of nodes){
  1254. topNode.data.ParentID = -1;
  1255. }
  1256. let copyDatas = [];
  1257. let copyNodes = [];
  1258. //获取块节点包含的所有节点(包括自己)
  1259. function containNodes(nodes) {
  1260. for(let node of nodes){
  1261. copyNodes.push(node);
  1262. if(node.children.length > 0){
  1263. containNodes(node.children);
  1264. }
  1265. }
  1266. }
  1267. containNodes(nodes);
  1268. for(let node of copyNodes){
  1269. copyDatas.push(node.data);
  1270. }
  1271. console.log(`copyDatas`);
  1272. console.log(copyDatas);
  1273. setLocalCache(itemCopyBlockKey, JSON.stringify(copyDatas));
  1274. }
  1275. //粘贴整块,整块数据粘贴到相关节点,并成为其后项
  1276. //@param {Object}node(粘贴到的节点) @return {void}
  1277. function pasteBlock(node) {
  1278. let itemObj = bills.tree.selected.guidance;
  1279. let pasteDatas = JSON.parse(getLocalCache(itemCopyBlockKey));
  1280. //整理ID
  1281. let IDMapping = {};
  1282. for(let data of pasteDatas){
  1283. data.newID = uuid.v1();
  1284. IDMapping[data.ID] = data;
  1285. }
  1286. for(let data of pasteDatas){
  1287. let nextData = IDMapping[data.NextSiblingID];
  1288. data.NextSiblingID = nextData ? nextData.newID : -1;
  1289. let parentData = IDMapping[data.ParentID];
  1290. data.ParentID = parentData ? parentData.newID : -1;
  1291. }
  1292. for(let data of pasteDatas){
  1293. data.ID = data.newID;
  1294. delete data.newID;
  1295. }
  1296. let updateDatas = [];
  1297. //将最顶层的块数据的ParentID设置成粘贴到节点的ParentID,并设置新的billsID
  1298. let topDatas = _.filter(pasteDatas, {ParentID: -1});
  1299. for(let topData of topDatas){
  1300. topData.ParentID = node.getParentID();
  1301. }
  1302. //更新数据
  1303. //更新插入的最末顶层数据NextSiblingID
  1304. if(node.nextSibling){
  1305. topDatas[topDatas.length - 1].NextSiblingID = node.getNextSiblingID();
  1306. }
  1307. //新建节点
  1308. for(let data of pasteDatas){
  1309. data.libID = libID;
  1310. data.billsID = node.data.billsID;
  1311. delete data._id;
  1312. updateDatas.push({updateType: updateType.create, updateData: data});
  1313. }
  1314. console.log(`pasteDatas`);
  1315. console.log(pasteDatas);
  1316. //更新粘贴到的节点的NextSiblingID
  1317. updateDatas.push({updateType: updateType.update, findData: {ID: node.data.ID}, updateData: {NextSiblingID: topDatas[0].ID}})
  1318. $.bootstrapLoading.start();
  1319. updateGuideItems(updateDatas, function (rstData) {
  1320. $.bootstrapLoading.end();
  1321. node.data.NextSiblingID = topDatas[0].ID;
  1322. let newNodes = itemObj.tree.insertDatasTo(node.data, pasteDatas);
  1323. cleanData(guideItem.workBook.getActiveSheet(), guideItem.headers, -1);
  1324. itemObj.controller.showTreeData();
  1325. setNodesColor(guideItem.workBook.getActiveSheet(), bills.tree.selected.guidance.tree.items);
  1326. }, function () {
  1327. $.bootstrapLoading.end();
  1328. });
  1329. }
  1330. //初始化右键菜单
  1331. //@return {void}
  1332. function initContextMenu() {
  1333. $.contextMenu({
  1334. selector: '#guideItemSpread',
  1335. build: function($triggerElement, e){
  1336. //控制允许右键菜单在哪个位置出现
  1337. let sheet = guideItem.workBook.getSheet(0);
  1338. let offset = $("#guideItemSpread").offset(),
  1339. x = e.pageX - offset.left,
  1340. y = e.pageY - offset.top;
  1341. let target = sheet.hitTest(x, y);
  1342. if(target.hitTestType === 3 && typeof target.row !== 'undefined' && typeof target.col !== 'undefined'){//在表格内
  1343. let sel = sheet.getSelections()[0];
  1344. if(sel && sel.rowCount === 1){
  1345. sheet.setActiveCell(target.row, target.col);
  1346. }
  1347. sel = sheet.getSelections()[0];
  1348. let selNodes = [];
  1349. if(sel){
  1350. sel.row = sel.row === -1 ? 0 : sel.row;
  1351. for(let i = 0; i < sel.rowCount; i++){
  1352. if(bills.tree.selected.guidance.tree.items[sel.row + i]){
  1353. selNodes.push(bills.tree.selected.guidance.tree.items[sel.row + i]);
  1354. }
  1355. }
  1356. }
  1357. //块节点
  1358. let blockNodes = getBlockNodes(selNodes);
  1359. //右键在多选内则不重设焦点
  1360. if(!sel || sel.rowCount === 1 || !(target.row >= sel.row && target.row <= sel.row + sel.rowCount - 1)){
  1361. sheet.setActiveCell(target.row, target.col);
  1362. }
  1363. guideItemInitSel(target.row);
  1364. return {
  1365. callback: function(){},
  1366. items: {
  1367. "copy": {
  1368. name: "复制整块",
  1369. disabled: function () {
  1370. return !canCopyBlock(blockNodes);
  1371. },
  1372. icon: "fa-copy",
  1373. callback: function (key, opt) {
  1374. copyBlocks(blockNodes);
  1375. }},
  1376. "paste": {
  1377. name: "粘贴整块",
  1378. disabled: function () {
  1379. let pasteNode = bills.tree.selected.guidance.tree.items[target.row];
  1380. return !canPasteBlock(pasteNode);
  1381. },
  1382. icon: "fa-paste",
  1383. callback: function (key, opt) {
  1384. let pasteNode = bills.tree.selected.guidance.tree.items[target.row];
  1385. pasteBlock(pasteNode);
  1386. }},
  1387. "del": {
  1388. name: '删除',
  1389. disabled: function () {
  1390. let node = bills.tree.selected.guidance.tree.items[target.row];
  1391. return !node
  1392. },
  1393. icon: "fa-arrow-left",
  1394. callback: function (key, opt) {
  1395. $('#delAlert').modal('show');
  1396. }
  1397. },
  1398. "insertSibling": {
  1399. name: '插入行',
  1400. disabled: function () {
  1401. let node = bills.tree.selected.guidance.tree.items[target.row];
  1402. return !node || node.data.type !== itemType.job;
  1403. },
  1404. icon: "fa-arrow-left",
  1405. callback: function (key, opt) {
  1406. insert([{type: itemType.job, name: ''}], false);
  1407. }
  1408. },
  1409. "insertChild": {
  1410. name: '插入子项',
  1411. disabled: function () {
  1412. let node = bills.tree.selected.guidance.tree.items[target.row];
  1413. return !node || node.data.type !== itemType.job || !allJobChildren(node);
  1414. },
  1415. icon: 'fa-arrow-left',
  1416. callback: function (key, opt) {
  1417. insert([{type: itemType.job, name: ''}], true);
  1418. }
  1419. }
  1420. }
  1421. };
  1422. }
  1423. else{
  1424. return false;
  1425. }
  1426. }
  1427. });
  1428. }
  1429. //初始化个按钮点击
  1430. //@return {void}
  1431. function initBtn(){
  1432. $('#insert').click(function () {
  1433. insert([{type: itemType.job, name: ''}], false);
  1434. });
  1435. $('#delConfirm').click(function () {
  1436. del();
  1437. $('#delAlert').modal('hide');
  1438. });
  1439. $('#del').click(function () {
  1440. $('#delAlert').modal('show');
  1441. });
  1442. $('#upLevel').click(function () {
  1443. upLevel();
  1444. });
  1445. $('#downLevel').click(function () {
  1446. downLevel();
  1447. });
  1448. $('#upMove').click(function () {
  1449. upMove();
  1450. });
  1451. $('#downMove').click(function () {
  1452. downMove();
  1453. });
  1454. //收起定额、展开全部
  1455. $('#expandContract').click(function () {
  1456. //目前状态时展开全部节点状态,点击则收起定额
  1457. let tree = bills.tree.selected.guidance.tree,
  1458. itemSheet = guideItem.workBook.getActiveSheet();
  1459. if(curExpandState === itemExpandState.expand){
  1460. curExpandState = itemExpandState.contract;
  1461. $(this).html('<i class="fa fa-plus-square-o" aria-hidden="true"></i> 展开全部');
  1462. setNodesExpandState(tree.items, itemExpandState.contract);
  1463. } else {
  1464. curExpandState = itemExpandState.expand;
  1465. $(this).html('<i class="fa fa-minus-square-o" aria-hidden="true"></i> 收起定额');
  1466. setNodesExpandState(tree.items, itemExpandState.expand);
  1467. }
  1468. renderSheetFunc(itemSheet, function () {
  1469. TREE_SHEET_HELPER.refreshNodesVisible(tree.roots, itemSheet, true);
  1470. });
  1471. });
  1472. $('#insertRation').click(function () {
  1473. let checkedRows = getCheckedRationRows();
  1474. let insertDatas = getInsertRations(checkedRows);
  1475. if(insertDatas.length > 0){
  1476. insert(insertDatas, false, function () {
  1477. //清空选择
  1478. clearCheckedRation(checkedRows);
  1479. });
  1480. }
  1481. else {
  1482. clearCheckedRation(checkedRows);
  1483. }
  1484. });
  1485. //搜索定额
  1486. $('#searchBtn').click(function () {
  1487. let searchStr = $('#searchText').val();
  1488. if(!searchStr || searchStr === ''){
  1489. ration.cache = ration.datas;
  1490. }
  1491. else{
  1492. let reg = new RegExp(searchStr, 'i');
  1493. ration.cache = _.filter(ration.datas, function (data) {
  1494. return reg.test(data.code) || reg.test(data.name);
  1495. });
  1496. }
  1497. $('.top-content').hide();
  1498. $('#searchCount').text(`搜索结果: ${ration.cache.length}`);
  1499. $('#rationSearchResult').show();
  1500. autoFlashHeight();
  1501. ration.workBook.refresh();
  1502. let rationSheet = ration.workBook.getActiveSheet();
  1503. renderSheetFunc(rationSheet, function () {
  1504. clearCheckedRation(getCheckedRationRows());
  1505. showData(rationSheet, ration.headers, ration.cache);
  1506. })
  1507. });
  1508. //关闭搜索
  1509. $('#rationSearchResult a').click(function () {
  1510. $('.top-content').show();
  1511. $('#rationSearchResult').hide();
  1512. autoFlashHeight();
  1513. renderSheetFunc(ration.workBook.getActiveSheet(), function () {
  1514. clearCheckedRation(getCheckedRationRows());
  1515. });
  1516. section.workBook.refresh();
  1517. ration.workBook.refresh();
  1518. $('#searchText').val('');
  1519. //恢复章节树下的定额
  1520. sectionInitSel(section.workBook.getActiveSheet().getActiveRowIndex());
  1521. });
  1522. let keyupTime = 0,
  1523. delayTime = 500;
  1524. function delayKeyup(callback) {
  1525. let nowTime = Date.now();
  1526. keyupTime = nowTime;
  1527. setTimeout(function () {
  1528. if (nowTime - keyupTime == 0) {
  1529. callback();
  1530. }
  1531. }, delayTime);
  1532. }
  1533. //执行搜索
  1534. $('#searchText').keyup(function (e) {
  1535. delayKeyup(function () {
  1536. $('#searchBtn').click();
  1537. });
  1538. });
  1539. //编辑清单备注
  1540. $('.main-side-bottom').find('textarea').keyup(function () {
  1541. let me = this;
  1542. let node = bills.tree.selected;
  1543. let comment = $(me).val();
  1544. delayKeyup(function () {
  1545. if (node) {
  1546. let updateData = {lastOperator: userAccount, billsLibId: billsLibId, updateId: node.getID(), field: 'comment', data: comment};
  1547. updateBillsComment(updateData, function () {
  1548. node.data.comment = comment;
  1549. })
  1550. }
  1551. });
  1552. });
  1553. //编辑选项备注
  1554. $('.main-bottom-content').find('textarea').keyup(function () {
  1555. let me = this;
  1556. let node = bills.tree.selected.guidance.tree.selected;
  1557. let comment = $(me).val();
  1558. delayKeyup(function () {
  1559. if(node){
  1560. let updateDatas = [{updateType: updateType.update, findData: {ID: node.getID()}, updateData: {comment: comment}}];
  1561. updateGuideItems(updateDatas, function (rstData) {
  1562. node.data.comment = comment;
  1563. });
  1564. }
  1565. });
  1566. });
  1567. //定额高度拖动调整
  1568. let heightEleObj = {
  1569. module: moduleName,
  1570. resize: $('#deResize'),
  1571. top: $('#topContent'),
  1572. topSpread: $('#sectionSpread'),
  1573. bottom: $('#bottomContent'),
  1574. bottomSpread: $('#rationSpread')
  1575. },
  1576. heightLimit = {
  1577. min: 150,
  1578. max: `$(window).height()-$('.header').height()-$('.sidebar-tools-bar').height()-150-10`,
  1579. notTopSpread: 0,
  1580. notBottomSpread: 0,
  1581. };
  1582. SlideResize.verticalSlide(heightEleObj, heightLimit, function () {
  1583. if(section.workBook){
  1584. section.workBook.refresh();
  1585. }
  1586. if(ration.workBook){
  1587. ration.workBook.refresh();
  1588. }
  1589. });
  1590. /*slideResize(rationLibResizeEles, {min: 147, max: 680}, 'height', function() {
  1591. //autoFlashHeight();
  1592. if(section.workBook){
  1593. section.workBook.refresh();
  1594. }
  1595. if(ration.workBook){
  1596. ration.workBook.refresh();
  1597. }
  1598. });*/
  1599. //左右拖动
  1600. //清单表与项目指引表
  1601. let leftElesObj = {};
  1602. leftElesObj.module = moduleName;
  1603. leftElesObj.resize = $('#slideResizeLeft');
  1604. leftElesObj.parent = $('#dataRow');
  1605. leftElesObj.left = $('#leftContent');
  1606. leftElesObj.right = $('#midContent');
  1607. SlideResize.horizontalSlide(leftElesObj, {min: 200, max: `$('#dataRow').width() - $('#rightContent').width() - 200`}, function () {
  1608. refreshALlWorkBook();
  1609. });
  1610. //人材机表与人材机组成物表
  1611. let rightElesObj = {};
  1612. rightElesObj.module = moduleName;
  1613. rightElesObj.resize = $('#slideResizeRight');
  1614. rightElesObj.parent = $('#dataRow');
  1615. rightElesObj.left = $('#midContent');
  1616. rightElesObj.right = $('#rightContent');
  1617. SlideResize.horizontalSlide(rightElesObj, {min: 200, max: `$('#dataRow').width() - $('#leftContent').width() - 200`}, function () {
  1618. refreshALlWorkBook();
  1619. });
  1620. }
  1621. //刷新全部工作簿
  1622. //@return {void}
  1623. function refreshALlWorkBook() {
  1624. if (bills.workBook) {
  1625. bills.workBook.refresh();
  1626. }
  1627. if (guideItem.workBook) {
  1628. guideItem.workBook.refresh();
  1629. }
  1630. if (section.workBook) {
  1631. section.workBook.refresh();
  1632. }
  1633. if (ration.workBook) {
  1634. ration.workBook.refresh();
  1635. }
  1636. $('.main-side-bottom').find('textarea').height($('.main-side-bottom').height() - 20);
  1637. $('.main-side-bottom').find('textarea').width($('.main-side-bottom').width() - 25);
  1638. $('.main-bottom-content').find('textarea').height($('.main-bottom-content').height() - 20);
  1639. $('.main-bottom-content').find('textarea').width($('.main-bottom-content').width() - 25);
  1640. }
  1641. //读取拖动相关
  1642. //@return {void}
  1643. function initSlideSize() {
  1644. //定额表上下
  1645. let heightEleObj = {
  1646. module: moduleName,
  1647. top: $('#topContent'),
  1648. topSpread: $('#sectionSpread'),
  1649. bottom: $('#bottomContent'),
  1650. bottomSpread: $('#rationSpread')
  1651. };
  1652. SlideResize.loadVerticalHeight(heightEleObj.module, heightEleObj,
  1653. {totalHeight: `$(window).height()-$('.header').height()-$('.sidebar-tools-bar').height()-10`,
  1654. notTopSpread: 0, notBottomSpread: 0}, function () {
  1655. if(section.workBook){
  1656. section.workBook.refresh();
  1657. }
  1658. if(ration.workBook){
  1659. ration.workBook.refresh();
  1660. }
  1661. });
  1662. //水平
  1663. SlideResize.loadHorizonWidth(moduleName, [$('#slideResizeLeft'), $('#slideResizeRight')], [$('#leftContent'), $('#midContent'), $('#rightContent')], function () {
  1664. refreshALlWorkBook();
  1665. });
  1666. }
  1667. //初始化视图
  1668. //@param {void} @return {void}
  1669. function initViews(){
  1670. let modules = [bills, guideItem, section, ration];
  1671. initWorkBooks(modules);
  1672. getLibWithBills(libID);
  1673. initBtn();
  1674. initContextMenu();
  1675. initSlideSize();
  1676. }
  1677. return {initViews, initSlideSize};
  1678. })();
  1679. $(document).ready(function () {
  1680. billsGuidance.initViews();
  1681. });