pm_share.js 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012
  1. 'use strict';
  2. /**
  3. *
  4. *
  5. * @author Zhong
  6. * @date 2018/6/28
  7. * @version
  8. */
  9. const pmShare = (function () {
  10. const spreadDom = $('#shareSpread');
  11. let shareSeleted = null;
  12. let spreadObj = {workBook: null, sheet: null};
  13. let preSelection = null;
  14. //项目分享类型,由别人分享给自己的,和自己分享给别人的
  15. const shareType = {receive: 'receive', shareTo: 'shareTo'};
  16. //操作类型
  17. const oprType = {copy: 'copy', cancel: 'cancel'};
  18. let tree = null,
  19. actualIDShareInfo = {};//项目真实树ID与项目分享信息映射
  20. const treeCol = 0;
  21. const treeSetting = {
  22. tree: {
  23. id: 'ID',
  24. pid: 'ParentID',
  25. nid: 'NextSiblingID',
  26. rootId: -1,
  27. autoUpdate: false
  28. }
  29. };
  30. const headers = [
  31. {name: '工程列表', dataCode: 'name', width: 300, rateWidth: 0.55, vAlign: 'center', hAlign: 'left'},
  32. {name: '来自', dataCode: 'from', width: 80, rateWidth: 0.15, vAlign: 'center', hAlign: 'left'},
  33. {name: '分享时间', dataCode: 'shareDate', width: 140, rateWidth: 0.15, vAlign: 'center', hAlign: 'left'},
  34. {name: '总造价', dataCode: 'totalCost', width: 100, vAlign: 'center', hAlign: 'right', formatter: '0.00'},
  35. {name: '项目类别', dataCode: 'valuationType', width: 100, vAlign: 'center', hAlign: 'left'},
  36. ];
  37. const spreadOpts = {
  38. workBook: {
  39. tabStripVisible: false,
  40. allowContextMenu: false,
  41. allowCopyPasteExcelStyle : false,
  42. allowExtendPasteRange: false,
  43. allowUserDragDrop : false,
  44. allowUserDragFill: false,
  45. scrollbarMaxAlign : true
  46. },
  47. sheet: {
  48. isProtected: true,
  49. frozenlineColor : '#ababab',
  50. protectionOptions: {allowResizeRows: true, allowResizeColumns: true},
  51. clipBoardOptions: GC.Spread.Sheets.ClipboardPasteOptions.values
  52. }
  53. };
  54. const spreadEvents = {
  55. SelectionChanging: function (sender, info) {
  56. initSelection(info.newSelections[0], info.oldSelections[0]);
  57. }
  58. };
  59. //设置选中行底色
  60. //@param
  61. function setSelStyle(sel, backColor,sheet) {
  62. sel.row = sel.row === -1 ? 0 : sel.row;
  63. renderSheetFunc(sheet, function () {
  64. let style = projTreeObj.getSelStyle(backColor);
  65. for(let i = 0; i < sel.rowCount; i++){
  66. let row = i + sel.row;
  67. sheet.setStyle(row, -1, style);
  68. }
  69. });
  70. }
  71. //初始化焦点
  72. //@param {Object}newSel {Object}oldSel @return {void}
  73. function initSelection(newSel, oldSel = null) {
  74. let node = tree.items[newSel.row];
  75. tree.selected = node;
  76. shareSeleted = node;
  77. //恢复底色
  78. if(oldSel){
  79. setSelStyle(oldSel, projTreeObj.setting.style.defalutBackColor, spreadObj.sheet);
  80. }
  81. //设置选中行底色
  82. if(newSel){
  83. setSelStyle(newSel, projTreeObj.setting.style.selectedColor, spreadObj.sheet);
  84. }
  85. preSelection = newSel;
  86. }
  87. //渲染时方法,停止渲染
  88. //@param {Object}sheet {Function}func @return {void}
  89. function renderSheetFunc(sheet, func){
  90. sheet.suspendEvent();
  91. sheet.suspendPaint();
  92. if(func){
  93. func();
  94. }
  95. sheet.resumeEvent();
  96. sheet.resumePaint();
  97. }
  98. //设置表选项
  99. //@param {Object}workBook {Object}opts @return {void}
  100. function setSpreadOptions (workBook, opts) {
  101. for(let opt in opts.workBook){
  102. workBook.options[opt] = opts.workBook[opt];
  103. }
  104. for(let opt in opts.sheet){
  105. workBook.getActiveSheet().options[opt] = opts.sheet[opt];
  106. }
  107. }
  108. //建表头
  109. //@param {Object}sheet {Array}headers @return {void}
  110. function buildHeader(sheet, headers) {
  111. let fuc = function () {
  112. sheet.setColumnCount(headers.length);
  113. sheet.setRowHeight(0, 40, GC.Spread.Sheets.SheetArea.colHeader);
  114. //let spreadWidth = getWorkBookWidth();
  115. for(let i = 0, len = headers.length; i < len; i++){
  116. sheet.setColumnWidth(i, headers[i].width, GC.Spread.Sheets.SheetArea.colHeader);
  117. if(headers[i].formatter){
  118. sheet.setFormatter(-1, i, headers[i].formatter);
  119. }
  120. sheet.setValue(0, i, headers[i].name, GC.Spread.Sheets.SheetArea.colHeader);
  121. sheet.getRange(-1, i, -1, 1).hAlign(GC.Spread.Sheets.HorizontalAlign[headers[i]['hAlign']]);
  122. sheet.getRange(-1, i, -1, 1).vAlign(GC.Spread.Sheets.VerticalAlign[headers[i]['vAlign']]);
  123. }
  124. };
  125. renderSheetFunc(sheet, fuc);
  126. }
  127. //表监听事件
  128. //@param {Object}workBook @return {void}
  129. function bindEvent(workBook, events) {
  130. if(Object.keys(events).length === 0){
  131. return;
  132. }
  133. const Events = GC.Spread.Sheets.Events;
  134. for(let event in events){
  135. workBook.bind(Events[event], events[event]);
  136. }
  137. }
  138. //建表
  139. //@return {void}
  140. function buildSheet(){
  141. spreadObj.workBook = new GC.Spread.Sheets.Workbook(spreadDom[0], {sheetCount: 1});
  142. sheetCommonObj.spreadDefaultStyle(spreadObj.workBook);
  143. spreadObj.sheet = spreadObj.workBook.getActiveSheet();
  144. setSpreadOptions(spreadObj.workBook, spreadOpts);
  145. bindEvent(spreadObj.workBook, spreadEvents);
  146. buildHeader(spreadObj.sheet, headers);
  147. //全表不可编辑
  148. spreadObj.sheet.getRange(-1, -1, -1, -1).locked(true);
  149. }
  150. //此项目的分享权限信息(可能会被父级项目覆盖,以新为准)
  151. //@param {String}userID(本用户id) {Object}project(项目) @return {Object} || {Null}
  152. function getShareInfo(userID, project) {
  153. if (!project.actualTreeInfo) {
  154. return null;
  155. }
  156. //获取跟本用户和选中项目相关的分享信息
  157. let shareList = [];
  158. let actualID = project.actualTreeInfo.ID,
  159. actualData = actualIDShareInfo[actualID];
  160. while (actualData) {
  161. for (let data of actualData.shareInfo) {
  162. if (data.userID === userID) {
  163. shareList.push(data);
  164. break;
  165. }
  166. }
  167. actualData = actualIDShareInfo[actualData.ParentID];
  168. }
  169. //获取最新分享
  170. shareList.sort(function (a, b) {
  171. return Date.parse(b.updateDate) - Date.parse(a.updateDate);
  172. });
  173. return shareList[0] || null;
  174. }
  175. //此项目是否可以拷贝
  176. //@param {String}userID {Object}project @return {Boolean}
  177. function isAllowCopy(userID, project){
  178. let myShareInfo = getShareInfo(userID, project);
  179. if (!myShareInfo) {
  180. return false;
  181. }
  182. return !!myShareInfo.allowCopy;
  183. }
  184. //此项目是否可以协作
  185. //@param {String}userID {Object}project @return {Boolean}
  186. function isAllowCoop(userID, project) {
  187. let myShareInfo = getShareInfo(userID, project);
  188. if (!myShareInfo) {
  189. return false;
  190. }
  191. return !!myShareInfo.allowCooperate;
  192. }
  193. //获取树节点
  194. //@param {Object}tree @return {Object}
  195. function getTreeNodeCell(tree){
  196. let indent = 20;
  197. let levelIndent = -5;
  198. let halfBoxLength = 5;
  199. let halfExpandLength = 3;
  200. let imgWidth = 18;
  201. let imgHeight = 14;
  202. let TreeNodeCellType = function () {
  203. };
  204. TreeNodeCellType.prototype = new GC.Spread.Sheets.CellTypes.Text();
  205. TreeNodeCellType.prototype.paint = function (ctx, value, x, y, w, h, style, options) {
  206. if (style.backColor) {
  207. ctx.save();
  208. ctx.fillStyle = style.backColor;
  209. ctx.fillRect(x, y, w, h);
  210. ctx.restore();
  211. } else {
  212. ctx.clearRect(x, y, w, h);
  213. }
  214. let drawLine = function (canvas, x1, y1, x2, y2, color) {
  215. ctx.save();
  216. ctx.translate(0.5, 0.5);
  217. ctx.beginPath();
  218. ctx.moveTo(x1, y1);
  219. ctx.lineTo(x2, y2);
  220. ctx.strokeStyle = color;
  221. ctx.stroke();
  222. ctx.restore();
  223. };
  224. let drawExpandBox = function (ctx, x, y, w, h, centerX, centerY, expanded) {
  225. let rect = {}, h1, h2, offset = 1;
  226. rect.top = centerY - halfBoxLength;
  227. rect.bottom = centerY + halfBoxLength;
  228. rect.left = centerX - halfBoxLength;
  229. rect.right = centerX + halfBoxLength;
  230. if (rect.left < x + w) {
  231. rect.right = Math.min(rect.right, x + w);
  232. ctx.save();
  233. ctx.translate(0.5, 0.5);
  234. ctx.strokeStyle = 'black';
  235. ctx.beginPath();
  236. ctx.moveTo(rect.left, rect.top);
  237. ctx.lineTo(rect.left, rect.bottom);
  238. ctx.lineTo(rect.right, rect.bottom);
  239. ctx.lineTo(rect.right, rect.top);
  240. ctx.lineTo(rect.left, rect.top);
  241. ctx.stroke();
  242. ctx.fillStyle = 'white';
  243. ctx.fill();
  244. ctx.restore();
  245. // Draw Horizontal Line
  246. h1 = centerX - halfExpandLength;
  247. h2 = Math.min(centerX + halfExpandLength, x + w);
  248. if (h2 > h1) {
  249. drawLine(ctx, h1, centerY, h2, centerY, 'black');
  250. }
  251. // Draw Vertical Line
  252. if (!expanded && (centerX < x + w)) {
  253. drawLine(ctx, centerX, centerY - halfExpandLength, centerX, centerY + halfExpandLength, 'black');
  254. }
  255. }
  256. }
  257. let node = tree.items[options.row];
  258. let showTreeLine = true;
  259. if (!node) { return; }
  260. let centerX = Math.floor(x) + node.depth() * indent + node.depth() * levelIndent + indent / 2;
  261. let x1 = centerX + indent / 2;
  262. let centerY = Math.floor((y + (y + h)) / 2);
  263. let y1;
  264. // Draw Sibling Line
  265. if (showTreeLine) {
  266. // Draw Horizontal Line
  267. if (centerX < x + w) {
  268. drawLine(ctx, centerX, centerY, Math.min(x1, x + w), centerY, 'gray');
  269. let img;
  270. if(node.data.projType === projectType.folder){
  271. img = document.getElementById('folder_open_pic');
  272. imgWidth = 15;
  273. }
  274. else if(node.data.projType === projectType.project){
  275. img = document.getElementById('proj_pic');
  276. imgWidth = 18;
  277. }
  278. else if(node.data.projType === projectType.engineering){
  279. img = document.getElementById('eng_pic');
  280. imgWidth = 14;
  281. }
  282. else if(node.data.projType === projectType.tender){
  283. img = document.getElementById('tender_pic');
  284. imgWidth = 14;
  285. }
  286. ctx.drawImage(img, centerX+indent/2+3, centerY - 7, imgWidth,imgHeight);
  287. }
  288. // Draw Vertical Line
  289. if (centerX < x + w) {
  290. y1 = node.isLast() ? centerY : y + h;
  291. if (node.isFirst() && !node.parent.parent) {
  292. drawLine(ctx, centerX, centerY, centerX, y1, 'gray');
  293. } else {
  294. drawLine(ctx, centerX, y, centerX, y1, 'gray');
  295. }
  296. }
  297. }
  298. // Draw Expand Box
  299. if (node.children.length > 0) {
  300. drawExpandBox(ctx, x, y, w, h, centerX, centerY, node.expanded);
  301. }
  302. // Draw Parent Line
  303. if (showTreeLine) {
  304. var parent = node.parent, parentCenterX = centerX - indent - levelIndent;
  305. while (parent.parent) {
  306. if (!parent.isLast()) {
  307. if (parentCenterX < x + w) {
  308. drawLine(ctx, parentCenterX, y, parentCenterX, y + h, 'gray');
  309. }
  310. }
  311. parent = parent.parent;
  312. parentCenterX -= (indent + levelIndent);
  313. }
  314. };
  315. // Draw Text
  316. arguments[2] = x + (node.depth() + 1) * indent + node.depth() * levelIndent + imgWidth + 3;
  317. arguments[4] = w - (node.depth() + 1) * indent - node.depth() * levelIndent - imgWidth - 3;
  318. GC.Spread.Sheets.CellTypes.Text.prototype.paint.apply(this, arguments);
  319. };
  320. TreeNodeCellType.prototype.getHitInfo = function (x, y, cellStyle, cellRect, context) {
  321. let info = {x: x, y: y, row: context.row, col: context.col, cellStyle: cellStyle, cellRect: cellRect, sheetArea: context.sheetArea};
  322. let node = tree.items[info.row];
  323. if (node && node.data.projType === projectType.tender) {
  324. info.isReservedLocation = true;
  325. }
  326. return info;
  327. };
  328. TreeNodeCellType.prototype.processMouseDown = function (hitinfo) {
  329. let offset = -1;
  330. let node = tree.items[hitinfo.row];
  331. let centerX = hitinfo.cellRect.x + offset + node.depth() * indent + node.depth() * levelIndent + indent / 2;
  332. let centerY = (hitinfo.cellRect.y + offset + (hitinfo.cellRect.y + offset + hitinfo.cellRect.height)) / 2;
  333. let text = hitinfo.sheet.getText(hitinfo.row, hitinfo.col);
  334. let value = hitinfo.sheet.getValue(hitinfo.row, hitinfo.col);
  335. let acStyle = hitinfo.sheet.getActualStyle(hitinfo.row, hitinfo.col),
  336. zoom = hitinfo.sheet.zoom();
  337. let textLength = this.getAutoFitWidth(value, text, acStyle, zoom, {sheet: hitinfo.sheet, row: hitinfo.row, col: hitinfo.col, sheetArea: GC.Spread.Sheets.SheetArea.viewport});
  338. //(图标+名字)区域
  339. function withingClickArea(){
  340. return hitinfo.x > centerX + halfBoxLength && hitinfo.x < centerX + halfBoxLength + imgWidth + indent/2+3 + textLength;
  341. }
  342. //点击单位工程
  343. if(node.data.projType === projectType.tender){
  344. /*let newTab = window.open('about:blank');
  345. //打开项目的实际ID
  346. BeforeOpenProject(node.data.actualTreeInfo.ID, {'fullFolder': GetFullFolder(node.parent)}, function () {
  347. let mainUrl = `/main?project=${node.data.actualTreeInfo.ID}`;
  348. CommonAjax.get(mainUrl, [], function () {
  349. newTab.location.href = mainUrl;
  350. });
  351. });*/
  352. let thisClick = Date.now(),
  353. open = false;
  354. if (this.preNode === node && this.preClick && thisClick - this.preClick <= 300) {
  355. open = true;
  356. projTreeObj.openTender(node.data.actualTreeInfo.ID, node.parent, 200);
  357. }
  358. this.preClick = open ? null : thisClick;
  359. this.preNode = open ? null : node;
  360. }
  361. if (!node || node.children.length === 0) {
  362. return;
  363. }
  364. if (hitinfo.x >= centerX - halfBoxLength - 2 && hitinfo.x <= centerX + halfBoxLength + 2 &&
  365. hitinfo.y >= centerY - halfBoxLength - 2 && hitinfo.y <= centerY + halfBoxLength + 2) {
  366. node.setExpanded(!node.expanded);
  367. TREE_SHEET_HELPER.massOperationSheet(hitinfo.sheet, function () {
  368. let iCount = node.posterityCount(), i, child;
  369. for (i = 0; i < iCount; i++) {
  370. child = tree.items[hitinfo.row + i + 1];
  371. hitinfo.sheet.setRowVisible(hitinfo.row + i + 1, child.visible, hitinfo.sheetArea);
  372. }
  373. hitinfo.sheet.invalidateLayout();
  374. });
  375. hitinfo.sheet.repaint();
  376. }
  377. };
  378. /*TreeNodeCellType.prototype.processMouseMove = function (hitInfo) {
  379. let sheet = hitInfo.sheet;
  380. let div = sheet.getParent().getHost();
  381. let canvasId = div.id + "vp_vp";
  382. let canvas = $(`#${canvasId}`)[0];
  383. //改变鼠标图案
  384. if (sheet && hitInfo.isReservedLocation) {
  385. canvas.style.cursor='pointer';
  386. return true;
  387. }else{
  388. canvas.style.cursor='default';
  389. }
  390. return false;
  391. };*/
  392. TreeNodeCellType.prototype.processMouseEnter = function (hitinfo) {
  393. let text = hitinfo.sheet.getText(hitinfo.row, hitinfo.col);
  394. let value = hitinfo.sheet.getValue(hitinfo.row, hitinfo.col);
  395. let tag = hitinfo.sheet.getTag(hitinfo.row, hitinfo.col);
  396. let acStyle = hitinfo.sheet.getActualStyle(hitinfo.row, hitinfo.col),
  397. zoom = hitinfo.sheet.zoom();
  398. let node = tree.items[hitinfo.row];
  399. let nodeIndent = node ? (node.depth() + 1) * indent + node.depth() * levelIndent + imgWidth + 3 : 0;
  400. let textLength = this.getAutoFitWidth(value, text, acStyle, zoom, {sheet: hitinfo.sheet, row: hitinfo.row, col: hitinfo.col, sheetArea: GC.Spread.Sheets.SheetArea.viewport});
  401. let cellWidth = hitinfo.sheet.getCell(-1, hitinfo.col).width();
  402. if(textLength > cellWidth - nodeIndent){
  403. TREE_SHEET_HELPER.showTipsDiv(text,{pos: {}},hitinfo);
  404. }
  405. };
  406. TreeNodeCellType.prototype.processMouseLeave = function (hitinfo) {
  407. TREE_SHEET_HELPER.tipDiv = 'hide';
  408. if (TREE_SHEET_HELPER._toolTipElement) {
  409. $(TREE_SHEET_HELPER._toolTipElement).hide();
  410. TREE_SHEET_HELPER._toolTipElement = null;
  411. };
  412. TREE_SHEET_HELPER.tipDivCheck();//延时检查:当tips正在show的时候,就调用了hide方法,会导致tips一直存在,所以设置一个超时处理
  413. }
  414. return new TreeNodeCellType();
  415. }
  416. //互动单元格
  417. function getInteractionCell() {
  418. let editImg = document.getElementById('edit_pic'),
  419. editImgWidth = 13,
  420. editImgHeight = 13,
  421. copyImg = document.getElementById('copy_pic'),
  422. copyImgWidth = 13,
  423. copyImgHeight = 13;
  424. let InteractionCell = function () {
  425. };
  426. InteractionCell.prototype = new GC.Spread.Sheets.CellTypes.Text();
  427. InteractionCell.prototype.paint = function (ctx, value, x, y, w, h, style, options) {
  428. if (style.backColor) {
  429. ctx.save();
  430. ctx.fillStyle = style.backColor;
  431. ctx.fillRect(x, y, w, h);
  432. ctx.restore();
  433. } else {
  434. ctx.clearRect(x, y, w, h);
  435. }
  436. let node = tree.items[options.row];
  437. // Draw Text
  438. GC.Spread.Sheets.CellTypes.Text.prototype.paint.apply(this, arguments);
  439. if (node && node.data.projType === projectType.tender) {
  440. let text = options.sheet.getText(options.row, options.col);
  441. let acStyle = options.sheet.getActualStyle(options.row, options.col),
  442. zoom = options.sheet.zoom();
  443. let textLength = this.getAutoFitWidth(value, text, acStyle, zoom, {sheet: options.sheet, row: options.row, col: options.col, sheetArea: GC.Spread.Sheets.SheetArea.viewport});
  444. const imgIndent = 5;
  445. let nowX = Math.floor(x) + textLength + imgIndent,
  446. nowY = Math.floor((y + (y + h)) / 2) - 7;
  447. if (node.data.allowCooperate) {
  448. ctx.drawImage(editImg, nowX, nowY, editImgWidth, editImgHeight);
  449. nowX += editImgWidth;
  450. }
  451. if (node.data.allowCopy) {
  452. ctx.drawImage(copyImg, nowX, nowY, copyImgWidth, copyImgHeight);
  453. }
  454. }
  455. };
  456. InteractionCell.prototype.getHitInfo = function (x, y, cellStyle, cellRect, context) {
  457. return {
  458. x: x,
  459. y: y,
  460. row: context.row,
  461. col: context.col,
  462. cellStyle: cellStyle,
  463. cellRect: cellRect,
  464. sheetArea: context.sheetArea,
  465. isReservedLocation: true
  466. };
  467. };
  468. InteractionCell.prototype.processMouseDown = function (hitinfo) {
  469. let dataCode = headers[hitinfo.col]['dataCode'];
  470. let node = tree.items[hitinfo.row];
  471. let text = hitinfo.sheet.getText(hitinfo.row, hitinfo.col);
  472. let value = hitinfo.sheet.getValue(hitinfo.row, hitinfo.col);
  473. let acStyle = hitinfo.sheet.getActualStyle(hitinfo.row, hitinfo.col),
  474. zoom = hitinfo.sheet.zoom();
  475. let textLength = this.getAutoFitWidth(value, text, acStyle, zoom, {sheet: hitinfo.sheet, row: hitinfo.row, col: hitinfo.col, sheetArea: GC.Spread.Sheets.SheetArea.viewport});
  476. if(hitinfo.x - hitinfo.cellRect.x > 0 && hitinfo.x - hitinfo.cellRect.x < textLength){
  477. //由..分享,弹出分享者信息
  478. if(dataCode === 'from'){
  479. if(node.data.shareType === shareType.receive){
  480. $('#userinfo').find('h4').text(node.data.userInfo.name);
  481. $('#userinfo').find('h6').text(node.data.userInfo.company);
  482. let mobileHtml = `<i class="fa fa-tablet"> ${node.data.userInfo.mobile ? node.data.userInfo.mobile : ''}</i>`;
  483. $('#userinfo').find('li:first-child').html(mobileHtml);
  484. let emailHtml = `<i class="fa fa-envelope-o"> ${node.data.userInfo.email ? node.data.userInfo.email : ''}</i>`;
  485. $('#userinfo').find('li:last-child').html(emailHtml);
  486. $('#userinfo').modal('show');
  487. }
  488. }
  489. }
  490. };
  491. InteractionCell.prototype.processMouseMove = function (hitInfo) {
  492. let dataCode = headers[hitInfo.col]['dataCode'];
  493. let node = tree.items[hitInfo.row];
  494. let sheet = hitInfo.sheet;
  495. let div = sheet.getParent().getHost();
  496. let canvasId = div.id + "vp_vp";
  497. let canvas = $(`#${canvasId}`)[0];
  498. //改变鼠标图案
  499. let text = hitInfo.sheet.getText(hitInfo.row, hitInfo.col);
  500. let value = hitInfo.sheet.getValue(hitInfo.row, hitInfo.col);
  501. let acStyle = hitInfo.sheet.getActualStyle(hitInfo.row, hitInfo.col),
  502. zoom = hitInfo.sheet.zoom();
  503. let textLength = this.getAutoFitWidth(value, text, acStyle, zoom, {sheet: hitInfo.sheet, row: hitInfo.row, col: hitInfo.col, sheetArea: GC.Spread.Sheets.SheetArea.viewport});
  504. if (sheet && hitInfo.x - hitInfo.cellRect.x > 0 && hitInfo.x - hitInfo.cellRect.x < textLength) {
  505. canvas.style.cursor='pointer';
  506. return true;
  507. }else{
  508. canvas.style.cursor='default';
  509. }
  510. return false;
  511. };
  512. return new InteractionCell();
  513. }
  514. const foreColor = '#007bff';
  515. const cancelForeColor = 'red';
  516. //显示树结构数据
  517. //@param {Array}nodes {Array}headers @return {void}
  518. function showTreeData(nodes, headers){
  519. let sheet = spreadObj.workBook.getActiveSheet();
  520. let fuc = function(){
  521. sheet.setRowCount(nodes.length);
  522. for(let i = 0; i < nodes.length; i++){
  523. let treeNodeCell = getTreeNodeCell(tree);
  524. sheet.getCell(i, treeCol).cellType(treeNodeCell);
  525. for(let j = 0; j < headers.length; j++){
  526. sheet.getRange(-1, j, -1, 1).hAlign(GC.Spread.Sheets.HorizontalAlign[headers[j]['hAlign']]);
  527. sheet.getRange(-1, j, -1, 1).vAlign(GC.Spread.Sheets.VerticalAlign[headers[j]['vAlign']]);
  528. let dataCode = headers[j].dataCode;
  529. if(dataCode === 'from'){
  530. let style = new GC.Spread.Sheets.Style();
  531. style.foreColor = foreColor;
  532. sheet.setStyle(i, j, style);
  533. sheet.getCell(i, j).cellType(getInteractionCell());
  534. }
  535. sheet.setValue(i, j, nodes[i].data[dataCode] !== null && typeof nodes[i].data[dataCode] !== 'undefined' ? nodes[i].data[dataCode] : '');
  536. }
  537. }
  538. };
  539. renderSheetFunc(sheet, fuc);
  540. }
  541. //同一棵树,可能存在相同数据显示多条的问题(传入的datas中不存在相同数据)
  542. //将真实树结构数据存在actualTreeInfo中,外部树结构数据用uuid重置。
  543. //@param {Array}datas
  544. function setTreeInfo(datas) {
  545. let IDMapping = {};
  546. for (let data of datas) {
  547. //项目真实ID与项目分享信息映射,方便确定项目的权限
  548. if (!actualIDShareInfo[data.ID]) {
  549. actualIDShareInfo[data.ID] = {ID: data.ID, ParentID: data.ParentID, NextSiblingID: data.NextSiblingID, shareInfo: data.shareInfo};
  550. }
  551. IDMapping[data.ID] = uuid.v1();
  552. }
  553. for (let data of datas) {
  554. data.actualTreeInfo = {ID: data.ID, ParentID: data.ParentID, NextSiblingID: data.NextSiblingID};
  555. data.ID = IDMapping[data.ID];
  556. data.NextSiblingID = IDMapping[data.NextSiblingID] ? IDMapping[data.NextSiblingID] : -1;
  557. data.ParentID = IDMapping[data.ParentID] ? IDMapping[data.ParentID] : -1;
  558. }
  559. }
  560. //整理同层数据的NextSiblingID,ParentID
  561. //@param {Array}datas {Number || String}pid @return {void}
  562. function sortSameDepthData(datas, pid) {
  563. for (let i = 0; i < datas.length; i++) {
  564. let data = datas[i],
  565. nextData = datas[i + 1];
  566. data.NextSiblingID = nextData ? nextData.ID : -1;
  567. data.ParentID = pid;
  568. }
  569. }
  570. //给项目设置分享信息:由xx分享、分享时间、分享给我,含有userInfo信息的文件为他人直接分享的文件,他人分享父级文件,子文件不含有userInfo信息
  571. //@param {Array}datas @return {void}
  572. function setShareInfo(datas) {
  573. for (let data of datas) {
  574. if (data.userInfo) {
  575. //shareInfo中我的条目
  576. let selfInfo = _.find(data.shareInfo, {userID: userID});
  577. data.shareDate = selfInfo ? selfInfo.shareDate : ''
  578. data.from = data.userInfo.name;
  579. data.to = '分享给 我';
  580. data.cancel = '清除';
  581. }
  582. }
  583. }
  584. //给项目设置汇总信息
  585. //@param {Array}projs {Object}summaryInfo
  586. function setSummaryInfo(grouped, summaryInfo) {
  587. if (!summaryInfo) {
  588. return;
  589. }
  590. let allDatas = [];
  591. for (let data of grouped) {
  592. allDatas.push(data);
  593. if (data.children && data.children.length > 0) {
  594. allDatas = allDatas.concat(data.children);
  595. }
  596. }
  597. for(let proj of allDatas){
  598. let summaryProj = summaryInfo[proj.ID];
  599. if(summaryProj){
  600. proj.totalCost = summaryProj.totalCost;
  601. }
  602. }
  603. }
  604. //从同层树数据获取第一个节点ID
  605. //@param {Array}treeDatas树的数据 @return {String}第一个节点的虚拟树ID
  606. function getFirstID(treeDatas) {
  607. let treeMapping = {};
  608. //建立ID索引
  609. for (let data of treeDatas) {
  610. //新建一个简单对象,防止污染treeDatas的数据
  611. treeMapping[data.ID] = {ID: data.ID, prev: null, next: null};
  612. }
  613. //绑定prev next
  614. for (let data of treeDatas) {
  615. let me = treeMapping[data.ID],
  616. next = treeMapping[data.NextSiblingID];
  617. if (next) {
  618. me.next = next;
  619. next.prev = me;
  620. }
  621. }
  622. //返回没有prev属性的数据
  623. let result = _.find(treeDatas, function (data) {
  624. return !treeMapping[data.ID].prev
  625. });
  626. return result ? result.ID : -1;
  627. }
  628. //获取可成树的数据
  629. //@param {Array}datas @return {Array}
  630. function getTreeDatas(groupedDatas, ungroupedDatas){
  631. //设置新的树结构数据
  632. for (let data of groupedDatas) {
  633. setTreeInfo([data].concat(data.children));
  634. }
  635. //未分类分段
  636. let tenders = _.filter(ungroupedDatas, {projType: projectType.tender});
  637. setTreeInfo(tenders);
  638. let rst = [];
  639. //整理树结构
  640. sortSameDepthData(groupedDatas, -1);
  641. //第一个根节点数据
  642. let firstID = getFirstID(groupedDatas);
  643. //新建未分类建设项目及单项工程
  644. let ungroupedProj = {ID: uuid.v1(), ParentID: -1, NextSiblingID: firstID, name: '未分类建设项目', projType: projectType.project};
  645. /*if (groupedDatas.length > 0) {
  646. groupedDatas[groupedDatas.length - 1].NextSiblingID = ungroupedProj.ID;
  647. }*/
  648. //将未分类的数据归类
  649. sortSameDepthData(tenders, ungroupedProj.ID);
  650. let allDatas = groupedDatas.concat(ungroupedDatas);
  651. //设置分享信息及操作信息
  652. setShareInfo(allDatas);
  653. for (let data of allDatas) {
  654. rst.push(data);
  655. if (data.children) {
  656. rst = rst.concat(data.children);
  657. }
  658. }
  659. rst.push(ungroupedProj);
  660. return rst;
  661. }
  662. //按照时间排序
  663. //@param {Array}datas @return {void}
  664. function sortByDate(datas){
  665. datas.sort(function (a, b) {
  666. let shareInfoA = _.find(a.shareInfo, {userID}),
  667. shareInfoB = _.find(b.shareInfo, {userID});
  668. let aV = shareInfoA ? Date.parse(shareInfoA.shareDate) : 0,
  669. bV = shareInfoB ? Date.parse(shareInfoB.shareDate) : 0;
  670. //时间越晚越靠前
  671. if (aV > bV) {
  672. return -1;
  673. } else if (aV < bV) {
  674. return 1;
  675. }
  676. return 0;
  677. });
  678. }
  679. //设置节点数据权限
  680. //@param {Array}datas项目数据
  681. function setPermissionsInfo(datas) {
  682. //data.allowCopy与shareInfo里allowCopy的区别:
  683. //data.allowCopy为该单位实际的权限(跟着最新的分享信息走,可能随着父项)
  684. for (let data of datas) {
  685. if (data.projType === projectType.tender) {
  686. data.allowCopy = isAllowCopy(userID, data);
  687. data.allowCooperate = isAllowCoop(userID, data);
  688. }
  689. }
  690. }
  691. //建立树
  692. //@return void
  693. function initShareTree(){
  694. $.bootstrapLoading.start();
  695. //获取分享数据
  696. CommonAjax.post('/pm/api/receiveProjects', {user_id: userID}, function (rstData) {
  697. // 排序 --分享的文件按照时间先后顺序排序,分享文件下的子文件,按照原本树结构显示,不需要排序
  698. sortByDate(rstData.grouped);
  699. sortByDate(rstData.ungrouped);
  700. //设置汇总信息
  701. if (rstData.summaryInfo) {
  702. setSummaryInfo(rstData.grouped, rstData.summaryInfo.grouped);
  703. setSummaryInfo(rstData.ungrouped, rstData.summaryInfo.ungrouped);
  704. }
  705. let treeDatas = getTreeDatas(rstData.grouped, rstData.ungrouped);
  706. setPermissionsInfo(treeDatas);
  707. tree = pmTree.createNew(treeSetting, treeDatas);
  708. tree.selected = tree.items[0];
  709. showTreeData(tree.items, headers);
  710. //初始选择
  711. let initSel = spreadObj.sheet.getSelections()[0] ? spreadObj.sheet.getSelections()[0] : {row: 0, rowCount: 1};
  712. initSelection(initSel);
  713. autoFlashHeight();
  714. spreadObj.sheet.frozenColumnCount(4);
  715. spreadObj.workBook.refresh();
  716. $.bootstrapLoading.end();
  717. });
  718. }
  719. //初始化右键菜单
  720. function initContextMenu() {
  721. $.contextMenu({
  722. selector: '#shareSpread',
  723. build: function ($trigger, e) {
  724. let target = SheetDataHelper.safeRightClickSelection($trigger, e, spreadObj.workBook);
  725. initSelection({row: target.row, rowCount: 1}, preSelection ? preSelection : null, spreadObj.sheet);
  726. return target.hitTestType === GC.Spread.Sheets.SheetArea.viewport || target.hitTestType === GC.Spread.Sheets.SheetArea.rowHeader;
  727. },
  728. items: {
  729. "copy": {
  730. name: "拷贝工程",
  731. icon: 'fa-copy',
  732. disabled: function () {
  733. let selected = tree.selected;
  734. return !(selected && selected.data.allowCopy);
  735. },
  736. callback: function (key, opt) {
  737. $('#copyShare').modal('show');
  738. }
  739. },
  740. "cancel": {
  741. name: "清除",
  742. icon: 'fa-remove',
  743. disabled: function () {
  744. let selected = tree.selected;
  745. return !(selected && selected.data.cancel && selected.data.cancel === '清除');
  746. },
  747. callback: function (key, opt) {
  748. let $p = $('<p>').text(`点“确定”按钮,确认清除分享文件 “${tree.selected.data.name}”。`);
  749. $('#cancelShare').find('.modal-body').empty();
  750. $('#cancelShare').find('.modal-body').append($p);
  751. $('#cancelShare').modal('show');
  752. }
  753. }
  754. }
  755. });
  756. }
  757. //初始化视图
  758. //@return void
  759. function initView(){
  760. if(tree){
  761. tree = null;
  762. }
  763. if(spreadObj.workBook){
  764. spreadObj.workBook.destroy();
  765. spreadObj.workBook = null;
  766. }
  767. initContextMenu();
  768. buildSheet();
  769. initShareTree();
  770. }
  771. //根据建设项目获取单项工程
  772. //@param {Number}projID @return {void}
  773. function setEng(projID){
  774. let engQuery = {$or: [{deleteInfo: null}, {'deleteInfo.deleted': false}], projType: projectType.engineering, userID: userID, ParentID: projID};
  775. CommonAjax.post('/pm/api/getProjectsByQuery', {user_id: userID, query: engQuery, options: '-_id -property'}, function (rstData) {
  776. $('#copyShare_selectEng').empty();
  777. for(let eng of rstData){
  778. let opt = $('<option>').val(eng.ID).text(eng.name);
  779. $('#copyShare_selectEng').append(opt);
  780. }
  781. });
  782. }
  783. //从其他建设项目中复制中,建设项目的文件层次结构名称和顺序
  784. //@param {Array}treeData @return {Array}
  785. function getFileHierarchyInfo(treeData){
  786. let tree = idTree.createNew({id: 'ID', pid: 'ParentID', nid: 'NextSiblingID', rootId: -1});
  787. tree.loadDatas(treeData);
  788. let items = tree.items;
  789. let rst = [];
  790. function getFileHierarchyName(node){
  791. let nodeName = node.data.name;
  792. let name = [];
  793. while (node.parent){
  794. name.push(node.parent.data.name ? node.parent.data.name : '');
  795. node = node.parent;
  796. }
  797. name = name.reverse();
  798. name.push(nodeName);
  799. return name.join('\\');
  800. }
  801. for(let node of items){
  802. if(node.children.length === 0 ){//project
  803. rst.push({ID: node.data.ID, fileHierarchyName: getFileHierarchyName(node)})
  804. }
  805. }
  806. return rst;
  807. }
  808. //设置拷贝工程下拉选择
  809. //@return {void}
  810. function setCopyModal(){
  811. //获取建设项目
  812. let projQuery = {$or: [{deleteInfo: null}, {'deleteInfo.deleted': false}], projType: {$in: [projectType.project, projectType.folder]}, userID: userID};
  813. CommonAjax.post('/pm/api/getProjectsByQuery', {user_id: userID, query: projQuery, options: '-_id -property'}, function (rstData) {
  814. let fileHierarchyData = getFileHierarchyInfo(rstData);
  815. $('#copyShare_selectProj').empty();
  816. for(let proj of fileHierarchyData){
  817. let opt = $('<option>').val(proj.ID).text(proj.fileHierarchyName);
  818. $('#copyShare_selectProj').append(opt);
  819. }
  820. //初始选择
  821. /*if(fileHierarchyData.length > 0){
  822. setEng(fileHierarchyData[0].ID);
  823. }*/
  824. });
  825. }
  826. //拷贝分享的工程
  827. //@param {Object}selected {Number}parentID @return {void}
  828. async function copyShareProject(selected, projID) {
  829. try {
  830. if (!projID || !selected) {
  831. return;
  832. }
  833. let copyMap = { copy: null, update: null };
  834. let newName = getCopyName(selected);
  835. //获取建设项目的分段
  836. let tenderQuery = { $or: [{ deleteInfo: null }, { 'deleteInfo.deleted': false }], userID: userID, ParentID: projID };
  837. const rstData = await ajaxPost('/pm/api/getProjectsByQuery', { user_id: userID, query: tenderQuery, options: '-_id -property' }, 10000);
  838. let updateTender = null;
  839. for (let tender of rstData) {
  840. if (tender.name === newName) {
  841. $('#copyShare_name').text('已存在此单位工程。');
  842. $('#copyShare_name').addClass('text-danger');
  843. return;
  844. }
  845. if (tender.NextSiblingID == -1) {
  846. updateTender = tender;
  847. }
  848. }
  849. //更新前节点
  850. if (updateTender) {
  851. copyMap.update = { query: { ID: updateTender.ID } };
  852. }
  853. //拷贝
  854. let copyData = {
  855. userID: userID,
  856. ID: selected.data.actualTreeInfo.ID,
  857. NextSiblingID: -1,
  858. ParentID: projID,
  859. name: newName,
  860. shareInfo: [],
  861. compilation: selected.data.compilation,
  862. createDateTime: selected.data.createDateTime,
  863. fileVer: selected.data.fileVer ? selected.data.fileVer : '',
  864. projType: selected.data.projType,
  865. property: {},
  866. recentDateTime: selected.data.recentDateTime,
  867. fullFolder: selected.data.fullFolder
  868. };
  869. copyData.property.rootProjectID = projID;
  870. copyMap.copy = { document: copyData };
  871. $('#copyShare').modal('hide');
  872. $.bootstrapLoading.progressStart('拷贝项目', true);
  873. $("#progress_modal_body").text('正在拷贝项目,请稍候……');
  874. await ajaxPost('/pm/api/copyProjects', { projectMap: copyMap, user_id: userID, tenderCount: 1 });
  875. importProcessChecking();
  876. } catch (err) {
  877. alert(err);
  878. }
  879. }
  880. //获取拷贝后的名称
  881. //@param {Object}node @return {String}
  882. function getCopyName(node) {
  883. //当前单位工程可能没有分享的用户信息,可能他人分享的是父级文件,userInfo在父级文件中
  884. let orgName = node.data.name,
  885. userInfo = node.data.userInfo;
  886. while (node && !userInfo) {
  887. node = node.parent;
  888. userInfo = node.data.userInfo;
  889. }
  890. return `${orgName} (${userInfo.name}分享拷贝)`;
  891. }
  892. //事件监听器
  893. //@return void
  894. function eventListener(){
  895. //tab
  896. $('#tab_pm_share').on('shown.bs.tab', function () {
  897. //侧滑隐藏
  898. $('.slide-sidebar').removeClass('open');
  899. $('.slide-sidebar').css('width', '0');
  900. projTreeObj.tree = null;
  901. if(projTreeObj.workBook){
  902. projTreeObj.workBook.destroy();
  903. projTreeObj.workBook = null;
  904. }
  905. gcTreeObj.tree = null;
  906. if(gcTreeObj.workBook){
  907. gcTreeObj.workBook.destroy();
  908. gcTreeObj.workBook = null;
  909. }
  910. initView();
  911. });
  912. //关闭拷贝工程
  913. $('#copyShare').on('hidden.bs.modal', function () {
  914. $('#copyShareProj-info').hide();
  915. $('#copyShareEng-info').hide();
  916. });
  917. //打开拷贝工程
  918. $('#copyShare').on('shown.bs.modal', function () {
  919. setCopyModal();
  920. //更改显示名称
  921. let newName = getCopyName(shareSeleted);
  922. $('#copyShare_name').html(`拷贝后,工程将重命名为 "<b>${newName}</b>"`);
  923. $('#copyShare_name').removeClass('text-danger');
  924. });
  925. //拷贝工程改变选择建设项目
  926. $('#copyShare_selectProj').change(function () {
  927. //更改显示名称
  928. let newName = getCopyName(shareSeleted);
  929. $('#copyShare_name').html(`拷贝后,工程将重命名为 "<b>${newName}</b>"`);
  930. $('#copyShare_name').removeClass('text-danger');
  931. $('#copyShareProj-info').hide();
  932. $('#copyShareEng-info').hide();
  933. let curSelID = $(this).select().val();
  934. setEng(parseInt(curSelID));
  935. });
  936. //拷贝工程改变选择单项工程
  937. $('#copyShare_selectEng').change(function () {
  938. //更改显示名称
  939. let newName = getCopyName(shareSeleted);
  940. $('#copyShare_name').html(`拷贝后,工程将重命名为 "<b>${newName}</b>"`);
  941. $('#copyShare_name').removeClass('text-danger');
  942. });
  943. //确认拷贝
  944. $('#copyShare_confirm').click(function () {
  945. let selProj = $('#copyShare_selectProj').select().val();
  946. if(!selProj){
  947. $('#copyShareProj-info').show();
  948. return;
  949. }
  950. copyShareProject(tree.selected, parseInt(selProj));
  951. });
  952. //清除分享
  953. //清除了该节点后,可能还有该节点的数据在树上(树允许有重复数据),需要更新分享信息
  954. function updateAfterCancel(userID, projectID) {
  955. for (let item of tree.items) {
  956. if (item.data.actualTreeInfo && item.data.actualTreeInfo.ID === projectID) {
  957. _.remove(item.data.shareInfo, function (data) {
  958. return data.userID === userID;
  959. });
  960. }
  961. }
  962. }
  963. $('#cancelShareConfirm').click(function () {
  964. $.bootstrapLoading.start();
  965. let cancelProjID = tree.selected.data.actualTreeInfo.ID;
  966. CommonAjax.post('/pm/api/share', {user_id: userID, type: oprType.cancel, projectID: cancelProjID, shareData:[{userID: userID}]}, function (rstData) {
  967. tree.removeNode(tree.selected);
  968. //更新与清除节点数据相同,且为被清除缓存分享信息
  969. updateAfterCancel(userID, cancelProjID);
  970. //重新设置actualIDShareInfo,以正确更新权限(清除了分享信息后,可能会导致权限变化 eg:清除了新的分享,则存留的分享项目采用旧的)
  971. actualIDShareInfo = {};
  972. let treeDatas = [];
  973. for (let item of tree.items) {
  974. treeDatas.push(item.data);
  975. let actualTreeInfo = item.data.actualTreeInfo;
  976. if (actualTreeInfo && !actualIDShareInfo[actualTreeInfo.ID]) {
  977. actualIDShareInfo[actualTreeInfo.ID] = {
  978. ID: actualTreeInfo.ID,
  979. ParentID: actualTreeInfo.ParentID,
  980. NextSiblingID: actualTreeInfo.NextSiblingID,
  981. shareInfo: item.data.shareInfo
  982. };
  983. }
  984. }
  985. //重新设置权限
  986. setPermissionsInfo(treeDatas);
  987. showTreeData(tree.items, headers);
  988. $.bootstrapLoading.end();
  989. }, function () {
  990. $.bootstrapLoading.end();
  991. });
  992. });
  993. }
  994. return {spreadObj, headers, initView, eventListener}
  995. })();
  996. $(document).ready(function () {
  997. pmShare.eventListener();
  998. });