ProjectGanttPan.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665
  1. Ext.define("App.ProjectGanttPan", {
  2. extend : "Gnt.panel.Gantt",
  3. requires : [
  4. 'Gnt.plugin.TaskContextMenu',
  5. 'Gnt.column.StartDate',
  6. 'Gnt.column.EndDate',
  7. 'Gnt.column.Duration',
  8. 'Gnt.column.PercentDone',
  9. 'Gnt.column.ResourceAssignment',
  10. 'Sch.plugin.TreeCellEditing',
  11. 'Sch.plugin.Pan'
  12. ],
  13. //rightLabelField : 'Responsible',
  14. highlightWeekends : true,
  15. showTodayLine : true,
  16. loadMask : true,
  17. enableProgressBarResize : true,
  18. listeners:{
  19. 'lockedGrid':{
  20. itemclick:function(a,b,c,d){
  21. },
  22. itemcontextmenu: function(view, record, node, rowIndex, e){
  23. a=window.textconttextmenu;
  24. a.rec=record,
  25. a.grid=this.up();
  26. e.preventDefault();
  27. a.showAt(e.getX(), e.getY());
  28. }
  29. },
  30. 'taskdblclick':function(gantt,record){
  31. var keyValue=record.data.id;
  32. var formCondition = "id IS" + keyValue ;
  33. var gridCondition="ra_taskid IS"+keyValue;
  34. var panel = Ext.getCmp("id=" +keyValue);
  35. var caller= 'MProjectTaskGanttEdit';
  36. var url= basePath+'jsps/crm/marketmgr/marketresearch/task.jsp';
  37. var main = parent.Ext.getCmp("content-panel");
  38. if(!panel){
  39. var title = "";
  40. panel = {
  41. //title : main.getActiveTab().title+'('+title+')',
  42. title:'项目任务: ('+keyValue+')',
  43. tag : 'iframe',
  44. tabConfig:{tooltip:'项目任务('+title+')'},
  45. frame : true,
  46. border : false,
  47. layout : 'fit',
  48. iconCls : 'x-tree-icon-tab-tab',
  49. html : '<iframe id="iframe_maindetail_'+caller+"_"+keyValue+'" src="'+url+'?formCondition='+formCondition+'&gridCondition='+gridCondition+'" height="100%" width="100%" frameborder="0" scrolling="no"></iframe>',
  50. closable : true,
  51. listeners : {
  52. close : function(){
  53. main.setActiveTab(main.getActiveTab().id);
  54. }
  55. }
  56. };
  57. this.openTab(panel,"id=" + keyValue);
  58. }
  59. else{
  60. main.setActiveTab(panel);
  61. }
  62. }} ,
  63. initComponent : function() {
  64. Ext.apply(this, {
  65. lockedGridConfig : {
  66. width: 210,
  67. title : '任务列表',
  68. collapsible : false
  69. },
  70. lockedViewConfig: {
  71. plugins: {
  72. ptype: 'treeviewdragdrop'
  73. }
  74. },
  75. schedulerConfig : {
  76. collapsible : true,
  77. title : '日程安排'
  78. },
  79. leftLabelField : {
  80. dataIndex : 'Name',
  81. editor : { xtype : 'textfield' }
  82. },
  83. rightLabelField : {
  84. renderer : function(value, record) {
  85. //get resources of the task
  86. var resources = record.getResources(),
  87. names = [];
  88. for(var i=0, l=resources.length; i<l; i++){
  89. var res = resources[i];
  90. if(res){
  91. names.push(res.getName());
  92. }
  93. }
  94. //return the string with resources names
  95. return names.join(', ');
  96. }
  97. },
  98. // Add some extra functionality
  99. selModel : new Ext.selection.TreeModel({ ignoreRightMouseSelection : false }),
  100. plugins : [
  101. textconttextmenu=Ext.create("App.TaskContextMenu"),
  102. Ext.create("Sch.plugin.Pan"),
  103. Ext.create('Sch.plugin.TreeCellEditing', { clicksToEdit: 2 }),
  104. Ext.create('Gnt.plugin.Printable',{
  105. printableMilestoneTpl : new Gnt.template.Milestone({
  106. prefix : 'foo',
  107. printable : true,
  108. imgSrc : 'images/milestone.png'
  109. }),
  110. printRenderer : function(task, tplData) {
  111. if (task.isMilestone()) {
  112. return;
  113. } else if (task.isLeaf()) {
  114. var availableWidth = tplData.width - 4,
  115. progressWidth = Math.floor(availableWidth*task.get('PercentDone')/100);
  116. return {
  117. progressBarStyle : Ext.String.format('width:{2}px;border-left:{0}px solid #7971E2;border-right:{1}px solid #E5ECF5;', progressWidth, availableWidth - progressWidth, availableWidth)
  118. };
  119. } else {
  120. var availableWidth = tplData.width - 2,
  121. progressWidth = Math.floor(availableWidth*task.get('PercentDone')/100);
  122. return {
  123. progressBarStyle : Ext.String.format('width:{2}px;border-left:{0}px solid #FFF3A5;border-right:{1}px solid #FFBC00;', progressWidth, availableWidth - progressWidth, availableWidth)
  124. };
  125. }
  126. },
  127. beforePrint : function(sched) {
  128. var v = sched.getSchedulingView();
  129. this.oldRenderer = v.eventRenderer;
  130. this.oldMilestoneTemplate = v.milestoneTemplate;
  131. v.milestoneTemplate = this.printableMilestoneTpl;
  132. v.eventRenderer = this.printRenderer;
  133. },
  134. afterPrint : function(sched) {
  135. var v = sched.getSchedulingView();
  136. v.eventRenderer = this.oldRenderer;
  137. v.milestoneTemplate = this.oldMilestoneTemplate;
  138. }
  139. })
  140. ],
  141. // Define an HTML template for the tooltip
  142. tooltipTpl : new Ext.XTemplate(
  143. '<h4 class="tipHeader">{Name}....{Id}</h4>',
  144. '<table class="taskTip">',
  145. '<tr><td>开始:</td> <td align="right">{[Ext.Date.format(values.StartDate, "y-m-d")]}</td></tr>',
  146. '<tr><td>结束:</td> <td align="right">{[Ext.Date.format(Ext.Date.add(values.EndDate, Ext.Date.MILLI, -1), "y-m-d")]}</td></tr>',
  147. '<tr><td>进度:</td><td align="right">{PercentDone}%</td></tr>',
  148. '</table>'
  149. ).compile(),
  150. eventRenderer: function(task){
  151. return {
  152. style : 'background-color: #'+task.data.TaskColor
  153. };
  154. },
  155. // Define the static columns
  156. columns : [
  157. {
  158. xtype : 'treecolumn',
  159. header: '项目任务',
  160. sortable: true,
  161. dataIndex: 'Name',
  162. width: 210,
  163. field: {
  164. allowBlank: false
  165. },
  166. renderer : function(v, meta, r) {
  167. if (!r.data.leaf) meta.tdCls = 'sch-gantt-parent-cell';
  168. return v;
  169. }
  170. },
  171. {
  172. xtype : 'startdatecolumn',
  173. header:'开始日期'
  174. },
  175. {
  176. xtype : 'enddatecolumn',
  177. header:'完成日期'
  178. // hidden : true
  179. },
  180. {
  181. xtype : 'durationcolumn',
  182. header:'需要时间'
  183. },
  184. {
  185. xtype : 'percentdonecolumn',
  186. header:'完成率',
  187. width : 50
  188. },
  189. {
  190. header : '资源分配',
  191. width:150,
  192. xtype : 'resourceassignmentcolumn',
  193. editor : Ext.create('Gnt.widget.AssignmentCellEditor', {
  194. assignmentStore : this.assignmentStore,
  195. resourceStore : this.resourceStore
  196. })
  197. },
  198. //column displaying task color
  199. {
  200. header: '色彩',
  201. xtype: 'templatecolumn',
  202. width: 50,
  203. tdCls: 'sch-column-color',
  204. field: {
  205. allowBlank: false
  206. },
  207. tpl: '<div class="color-column-inner" style="background-color:#{TaskColor}">&nbsp;</div>',
  208. listeners: {
  209. click : function(panel, el, a, b, event, record) {
  210. event.stopEvent();
  211. this.rec = record;
  212. this.showColumnMenu(el, event, record);
  213. }
  214. },
  215. showColumnMenu: function(el, event, rec){
  216. if(!this.colorMenu){
  217. this.colorMenu = new Ext.menu.Menu({
  218. cls: 'gnt-locked-colormenu',
  219. plain: true,
  220. items: [
  221. {
  222. text: '调整任务颜色',
  223. menu: {
  224. showSeparator: false,
  225. items: [
  226. Ext.create('Ext.ColorPalette', {
  227. listeners: {
  228. select: function(cp, color){
  229. this.rec.set('TaskColor', color);
  230. },
  231. scope: this
  232. }
  233. })
  234. ]
  235. }
  236. }
  237. ]
  238. });
  239. }
  240. this.colorMenu.showAt(event.xy);
  241. }
  242. }
  243. ],
  244. tbar : this.createToolbar()
  245. });
  246. this.callParent(arguments);
  247. },
  248. createToolbar : function() {
  249. return [{
  250. xtype: 'buttongroup',
  251. title: '编辑工具',
  252. columns: 4,
  253. items: [
  254. {
  255. iconCls : 'icon-prev',
  256. text : '向上添加任务',
  257. scope : this,
  258. handler : function() {
  259. task=this.getSelectionModel().getSelection()[0];
  260. newTask=this.copyTask(task);
  261. task.parentNode.insertBefore(newTask, task);
  262. }
  263. },
  264. {
  265. iconCls : 'icon-next',
  266. text : '向下添加任务',
  267. scope : this,
  268. handler : function() {
  269. task=this.getSelectionModel().getSelection()[0];
  270. newTask=this.copyTask(task);
  271. if (task.nextSibling) {
  272. task.parentNode.insertBefore(newTask, task.nextSibling)
  273. } else {
  274. task.parentNode.appendChild(newTask)
  275. }
  276. }
  277. },
  278. {
  279. text : '添加子任务',
  280. iconCls : 'icon-collapseall',
  281. scope : this,
  282. handler : function() {
  283. task=this.getSelectionModel().getSelection()[0];
  284. task.set("leaf", false);
  285. task.appendChild(this.copyTask(task));
  286. task.expand();
  287. }
  288. },
  289. {
  290. text : '添加里程碑',
  291. iconCls : 'zoomfit',
  292. handler : function() {
  293. task=this.getSelectionModel().getSelection()[0];
  294. var b = task.getTaskStore();
  295. newTask = this.copyTask(task);
  296. if (task.nextSibling) {
  297. task.parentNode.insertBefore(newTask, task.nextSibling)
  298. } else {
  299. task.parentNode.appendChild(newTask)
  300. };
  301. var a = task.getEndDate();
  302. if (a) {
  303. newTask.calendar =newTask.calendar|| task.getCalendar();
  304. newTask.setStartEndDate(a, a, b.skipWeekendsDuringDragDrop)
  305. };
  306. },
  307. scope : this
  308. },
  309. {
  310. text : '删除任务',
  311. iconCls : 'icon-expandall',
  312. scope : this,
  313. handler : function() {
  314. var a = this.getSelectionModel().selected;
  315. this.taskStore.remove(a.items)
  316. }
  317. },
  318. {
  319. text : '添加前置任务 ',
  320. iconCls : 'zoomfit',
  321. scope : this,
  322. handler : function() {
  323. task=this.getSelectionModel().getSelection()[0];
  324. var b = task.getDependencyStore();
  325. newTask = new MyTaskModel({leaf:true,Name:'新建的任务'});
  326. c=newTask;
  327. newTask.calendar = newTask.calendar || task.getCalendar();
  328. newTask.beginEdit();
  329. newTask.set(task.startDateField, newTask.calculateStartDate(task.getStartDate(), 1,
  330. Sch.util.Date.DAY));
  331. newTask.set(task.endDateField, task.getStartDate());
  332. newTask.set(task.durationField, 1);
  333. newTask.set(task.durationUnitField, Sch.util.Date.DAY);
  334. newTask.endEdit();
  335. task.parentNode.insertBefore(newTask, task);
  336. var a = new b.model({
  337. fromTask : newTask,
  338. toTask : task,
  339. type : b.model.Type.EndToStart
  340. });
  341. b.add(a);
  342. }
  343. },
  344. {
  345. text : '添加后续任务',
  346. iconCls : 'icon-expandall',
  347. scope : this,
  348. handler : function() {
  349. task=this.getSelectionModel().getSelection()[0];
  350. e = task.getTaskStore(), d = task.getDependencyStore();
  351. newTask = new MyTaskModel({leaf:true,Name:'新建的任务'});
  352. newTask.calendar = newTask.calendar || task.getCalendar();
  353. newTask.taskStore = e;
  354. newTask.setStartDate(task.getEndDate(), true, e.skipWeekendsDuringDragDrop);
  355. newTask.setDuration(1, Sch.util.Date.DAY);
  356. if (task.nextSibling) {
  357. task.parentNode.insertBefore(newTask, task.nextSibling)
  358. } else {
  359. task.parentNode.appendChild(newTask)
  360. }
  361. var a = new d.model({
  362. fromTask : task,
  363. toTask : newTask,
  364. type : d.model.Type.EndToStart
  365. });
  366. d.add(a);
  367. }
  368. },
  369. {
  370. text : '保存',
  371. iconCls : 'icon-save',
  372. scope : this,
  373. handler : function() {
  374. this.assignmentStore.sync();
  375. this.resourceStore.sync();
  376. this.dependencyStore.sync();
  377. taskstore=this.taskStore;
  378. taskstore.sync({
  379. success:function(taskStore){
  380. taskstore.load();
  381. }
  382. });
  383. }
  384. }
  385. ]
  386. },{
  387. xtype: 'buttongroup',
  388. title: '显示工具',
  389. columns: 3,
  390. items: [
  391. {
  392. iconCls : 'icon-prev',
  393. text : '向前',
  394. scope : this,
  395. handler : function() {
  396. this.shiftPrevious();
  397. }
  398. },
  399. {
  400. iconCls : 'icon-next',
  401. text : '向后',
  402. scope : this,
  403. handler : function() {
  404. this.shiftNext();
  405. }
  406. },
  407. {
  408. text : '全部收起',
  409. iconCls : 'icon-collapseall',
  410. scope : this,
  411. handler : function() {
  412. this.collapseAll();
  413. }
  414. },
  415. {
  416. text : '全部任务',
  417. iconCls : 'zoomfit',
  418. handler : function() {
  419. this.zoomToFit();
  420. },
  421. scope : this
  422. },
  423. {
  424. text : '全部展开',
  425. iconCls : 'icon-expandall',
  426. scope : this,
  427. handler : function() {
  428. this.expandAll();
  429. }
  430. },
  431. {
  432. text : '打印',
  433. iconCls : 'icon-save',
  434. scope : this,
  435. handler : function() {
  436. this.zoomToFit();
  437. this.print();
  438. }
  439. }
  440. ]
  441. },
  442. {
  443. xtype: 'buttongroup',
  444. title: '显示精度',
  445. columns: 2,
  446. items: [{
  447. text: '6 周',
  448. scope : this,
  449. handler : function() {
  450. this.switchViewPreset('weekAndMonth');
  451. }
  452. },
  453. {
  454. text: '10 周',
  455. scope : this,
  456. handler : function() {
  457. this.switchViewPreset('weekAndDayLetter');
  458. }
  459. },
  460. {
  461. text: '1 年',
  462. scope : this,
  463. handler : function() {
  464. this.switchViewPreset('monthAndYear');
  465. }
  466. },
  467. {
  468. text: '5 年',
  469. scope : this,
  470. handler : function() {
  471. var start = new Date(this.getStart().getFullYear(), 0);
  472. this.switchViewPreset('monthAndYear', start, Ext.Date.add(start, Ext.Date.YEAR, 5));
  473. }
  474. }
  475. ]},
  476. {
  477. xtype: 'buttongroup',
  478. title: '设置任务完成百分比',
  479. columns: 5,
  480. defaults : { scale : "large" },
  481. items: [{
  482. text: '0%<div class="percent percent0"></div>',
  483. scope : this,
  484. handler : function() {
  485. this.applyPercentDone(0);
  486. }
  487. },
  488. {
  489. text: '25%<div class="percent percent25"><div></div></div>',
  490. scope : this,
  491. handler : function() {
  492. this.applyPercentDone(25);
  493. }
  494. },
  495. {
  496. text: '50%<div class="percent percent50"><div></div></div>',
  497. scope : this,
  498. handler : function() {
  499. this.applyPercentDone(50);
  500. }
  501. },
  502. {
  503. text: '75%<div class="percent percent75"><div></div></div>',
  504. scope : this,
  505. handler : function() {
  506. this.applyPercentDone(75);
  507. }
  508. },
  509. {
  510. text: '100%<div class="percent percent100"><div></div></div>',
  511. scope : this,
  512. handler : function() {
  513. this.applyPercentDone(100);
  514. }
  515. }
  516. ]
  517. },
  518. {
  519. xtype: 'buttongroup',
  520. title: '其他',
  521. columns: 2,
  522. defaults : { scale : "large" },
  523. items: [{
  524. text: '资源调整',
  525. scope : this,
  526. handler : function() {
  527. this.switchViewPreset('weekAndMonth');
  528. }
  529. },{
  530. text: '资源负荷',
  531. scope : this,
  532. handler : function() {
  533. this.switchViewPreset('weekAndMonth');
  534. }
  535. }]
  536. },
  537. '->',
  538. {
  539. xtype: 'buttongroup',
  540. title: '查找',
  541. columns : 1,
  542. items: [
  543. {
  544. iconCls : 'action',
  545. text : '滚动到最后一个任务',
  546. scope : this,
  547. handler : function(btn) {
  548. var latestEndDate = new Date(0),
  549. latest;
  550. this.taskStore.getRootNode().cascadeBy(function(task) {
  551. if (task.get('EndDate') >= latestEndDate) {
  552. latestEndDate = task.get('EndDate');
  553. latest = task;
  554. }
  555. });
  556. this.getSchedulingView().scrollEventIntoView(latest, true);
  557. }
  558. },
  559. {
  560. xtype : 'textfield',
  561. emptyText : '查找任务...',
  562. scope : this,
  563. width:150,
  564. enableKeyEvents : true,
  565. listeners : {
  566. keyup : {
  567. fn : function(field, e) {
  568. var value = field.getValue();
  569. if (value) {
  570. this.taskStore.filter('Name', field.getValue(), true, false);
  571. } else {
  572. this.taskStore.clearFilter();
  573. }
  574. },
  575. scope : this
  576. },
  577. specialkey : {
  578. fn : function(field, e) {
  579. if (e.getKey() === e.ESC) {
  580. field.reset();
  581. }
  582. this.taskStore.clearFilter();
  583. },
  584. scope : this
  585. }
  586. }
  587. }]
  588. }
  589. ];
  590. },
  591. copyTask : function(c) {
  592. var a=new MyTaskModel({leaf:true,Name:'新建的任务'});
  593. a.setPercentDone(0);
  594. a.set(a.startDateField, (c && c.getStartDate()) || null);
  595. a.set(a.endDateField, (c && c.getEndDate()) || null);
  596. a.set(a.durationField, (c && c.getDuration()) || null);
  597. a.set(a.durationUnitField, (c && c.getDurationUnit()) || "d");
  598. return a
  599. },
  600. applyPercentDone : function(value) {
  601. this.getSelectionModel().selected.each(function(task) { task.setPercentDone(value); });
  602. },
  603. showFullScreen : function() {
  604. this.el.down('.x-panel-body').dom[this._fullScreenFn]();
  605. },
  606. openTab : function (panel,id){
  607. var o = (typeof panel == "string" ? panel : id || panel.id);
  608. var main = parent.Ext.getCmp("content-panel");
  609. var tab = main.getComponent(o);
  610. if (tab) {
  611. main.setActiveTab(tab);
  612. } else if(typeof panel!="string"){
  613. panel.id = o;
  614. var p = main.add(panel);
  615. main.setActiveTab(p);
  616. }
  617. } ,
  618. // Experimental, not X-browser
  619. _fullScreenFn : (function() {
  620. var docElm = document.documentElement;
  621. if (docElm.requestFullscreen) {
  622. return "requestFullscreen";
  623. }
  624. else if (docElm.mozRequestFullScreen) {
  625. return "mozRequestFullScreen";
  626. }
  627. else if (docElm.webkitRequestFullScreen) {
  628. return "webkitRequestFullScreen";
  629. }
  630. })()
  631. });