最近項目中剛好使用了甘特圖,第一次使用,在此記錄一下,有需要的同學可以借鑒一下。
首先來個效果圖吧,哈哈哈~~~
因為這里用到了dhtmlx這個甘特圖的組件,有些功能要專業版才能使用,但是免費的基本上符合需求了
插件文檔地址:https://docs.dhtmlx.com/gantt/api__refs__gantt.html
代碼如下:
1 <template> 2 <div class="container"> 3 <div class="select-wrap"> 4 <el-select v-model="value" placeholder="請選擇" @change="selectChange"> 5 <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> 6 </el-option> 7 </el-select> 8 </div> 9 <div ref="gantt" class="gantt-container"></div> 10 </div> 11 </template> 12 <script> 13 import { 14 gantt 15 } from 'dhtmlx-gantt'; 16 import "dhtmlx-gantt/codebase/dhtmlxgantt.css" 17 18 export default { 19 name: 'gantt', 20 data() { 21 return { 22 tasks: { 23 data: [] 24 }, 25 options: [{ 26 value: '1', 27 label: '全部' 28 }, { 29 value: '2', 30 label: '完成' 31 }, { 32 value: '3', 33 label: '正常' 34 }, { 35 value: '4', 36 label: '異常' 37 }, { 38 value: '5', 39 label: '未啟動' 40 }], 41 value: '1' 42 } 43 }, 44 methods: { 45 //開始時間-結束時間參數 46 DateDifference: function(strDateStart, strDateEnd) { 47 var begintime_ms = Date.parse(new Date(strDateStart.replace(/-/g, '/'))) //begintime 為開始時間 48 var endtime_ms = Date.parse(new Date(strDateEnd.replace(/-/g, '/'))) // endtime 為結束時間 49 var date3 = endtime_ms - begintime_ms //時間差的毫秒數 50 var days = Math.floor(date3 / (24 * 3600 * 1000)) 51 return days 52 }, 53 initData: function() { 54 this.tasks.data = [{ 55 id: 1, 56 text: '概念設計', 57 start_date: '2020-04-08', 58 duration: 10, 59 open: true, //默認打開, 60 toolTipsTxt: 'xxx項目概念設計', 61 progress: 0.6, 62 status: "parent" 63 }, 64 { 65 toolTipsTxt: 'xxx項目-項目啟動會', 66 text: '項目啟動會-外部', // 任務名 67 start_date: '2020-04-08', // 開始時間 68 id: 11, // 任務id 69 duration: 3, // 任務時長,從start_date開始計算 70 parent: 1, // 父任務ID 71 type: 1, 72 progress: 0.5, 73 status: "yellow" 74 }, 75 { 76 toolTipsTxt: 'xxx項目-項目啟動會議', 77 text: '項目啟動會-內部', 78 start_date: '2020-04-11', 79 id: 12, 80 duration: 2, 81 parent: 1, 82 type: 2, 83 progress: 0.6, 84 status: "pink" 85 }, 86 { 87 toolTipsTxt: 'xxx項目開工會', 88 text: '項目開工會', 89 start_date: '2020-04-13', 90 id: 13, 91 duration: 4, 92 parent: 1, 93 type: 3, 94 progress: 1, 95 status: "green" 96 97 }, { 98 toolTipsTxt: 'xxx項目-項目分析', 99 text: '項目分析', 100 start_date: '2020-04-13', 101 id: 14, 102 duration: 4, 103 parent: 1, 104 type: 4, 105 progress: 0.6, 106 status: "popular" 107 }, 108 109 { 110 id: 2, 111 text: '方案設計', 112 start_date: '2020-04-08', 113 duration: 8, 114 open: true, 115 toolTipsTxt: 'xxx方案設計', 116 state: 'default', 117 // color:"#409EFF", //設置顏色 118 progress: 0.6, 119 status: "parent" 120 }, 121 { 122 toolTipsTxt: 'xxx新項目原型圖設計', 123 text: '原型圖設計', 124 start_date: '2020-04-08', 125 id: 21, 126 duration: 2, 127 parent: 2, 128 type: 1, 129 progress: 0.6, 130 status: "yellow" 131 }, 132 { 133 toolTipsTxt: 'xxx項目-項目設計圖', 134 text: '設計圖設計', 135 start_date: '2020-04-09', 136 id: 22, 137 duration: 2, 138 parent: 2, 139 type: 2, 140 progress: 0.6, 141 status: "pink" 142 }, 143 { 144 toolTipsTxt: 'xxx項目-項目確認', 145 text: '項目確認', 146 start_date: '2020-04-11', 147 id: 23, 148 duration: 2, 149 parent: 2, 150 type: 3, 151 progress: 1, 152 status: "green" 153 } 154 155 ].map(function(current, ind, arry) { 156 var newObj = {} 157 if (current.type) { //存在type字段 說明非一級菜單,判斷階段的具體類型 設置不同顏色 158 if (current.type == 1) { //冒煙 159 newObj = Object.assign({}, current, { 160 'color': '#fcca02' 161 }) 162 } else if (current.type == 2) { //單元 163 newObj = Object.assign({}, current, { 164 'color': '#fec0dc' 165 }) 166 } else if (current.type == 3) { //回歸 167 newObj = Object.assign({}, current, { 168 'color': '#62ddd4' 169 }) 170 } else if (current.type == 4) { 171 newObj = Object.assign({}, current, { 172 'color': '#d1a6ff' 173 }) 174 } 175 } else { //一級菜單是藍色的 176 newObj = Object.assign({}, current, { 177 'color': '#5692f0' 178 }) 179 } 180 181 return newObj 182 }) 183 }, 184 selectChange(val){ 185 console.log(val) 186 187 //測試用例 188 var obj = { 189 toolTipsTxt: '新增任務', 190 text: '新增任務', // 任務名 191 start_date: '2020-04-15', // 開始時間 192 id: 24, // 任務id 193 duration: 2, // 任務時長,從start_date開始計算 194 parent: 2, // 父任務ID 195 type: 4, 196 progress:0, 197 status: "popular" 198 } 199 this.tasks.data.push(obj) 200 201 // 數據解析 202 gantt.parse(this.tasks) 203 // 刷新數據 204 gantt.refreshData(); 205 } 206 }, 207 mounted() { 208 this.initData() 209 210 211 //自適應甘特圖的尺寸大小, 使得在不出現滾動條的情況下, 顯示全部任務 212 gantt.config.autosize = true 213 //只讀模式 214 gantt.config.readonly = true 215 //是否顯示左側樹表格 216 gantt.config.show_grid = true 217 //表格列設置 218 gantt.config.columns = [{ 219 name: 'text', 220 label: '階段名字', 221 tree: true, 222 width: '280', 223 onrender: function(task, node) { 224 node.setAttribute("class", "gantt_cell gantt_last_cell gantt_cell_tree " + task.status); 225 } 226 }, 227 { 228 name: 'duration', 229 label: '時長', 230 align: 'center', 231 template: function(obj) { 232 return obj.duration + '天' 233 }, 234 hide: true 235 } 236 ] 237 238 239 240 var weekScaleTemplate = function(date) { 241 var dateToStr = gantt.date.date_to_str("%m %d"); 242 var endDate = gantt.date.add(gantt.date.add(date, 1, "week"), -1, "day"); 243 var weekNum = gantt.date.date_to_str("第 %W 周"); 244 return weekNum(date) 245 }; 246 var daysStyle = function(date) { 247 var dateToStr = gantt.date.date_to_str("%D"); 248 if (dateToStr(date) == "六" || dateToStr(date) == "日") return "weekend"; 249 return ""; 250 }; 251 gantt.config.subscales = [{ 252 unit: "week", 253 step: 1, 254 template: weekScaleTemplate 255 }, 256 { 257 unit: "day", 258 step: 1, 259 format: "%d" 260 } 261 ]; 262 263 gantt.plugins({ 264 tooltip: true 265 }); 266 gantt.attachEvent("onGanttReady", function() { 267 var tooltips = gantt.ext.tooltips; 268 gantt.templates.tooltip_text = function(start, end, task) { 269 270 return task.toolTipsTxt + "<br/>" + 271 "階段:" + task.text + "<br/>" + 272 gantt.templates.tooltip_date_format(start) 273 }; 274 275 276 }); 277 278 279 //設置任務條進度內容 280 gantt.templates.progress_text = function(start, end, task) { 281 return "<div style='text-align:left;color:#fff;padding-left:20px'>" + Math.round(task.progress * 100) + 282 "% </div>"; 283 }; 284 285 //任務條顯示內容 286 gantt.templates.task_text = function(start, end, task) { 287 // return task.text + '(' + task.duration + '天)'; 288 return "<div style='text-align:center;color:#fff'>" + task.text + '(' + task.duration + '天)' + 289 "</div>"; 290 } 291 292 293 // gantt.templates.scale_cell_class = function(date) { 294 // /*if(date.getDay()== 0 || date.getDay()== 6){ 295 // return "weekend"; 296 // }*/ 297 // return 'weekend' 298 // } 299 300 301 //任務欄周末亮色 302 /*gantt.templates.task_cell_class = function(item,date){ 303 if(date.getDay()== 0 || date.getDay()== 6){ 304 return "weekend"; 305 } 306 };*/ 307 308 309 //任務條上的文字大小 以及取消border自帶樣式 310 gantt.templates.task_class = function(start, end, item) { 311 return item.$level == 0 ? 'firstLevelTask' : 'secondLevelTask' 312 } 313 314 gantt.config.layout = { 315 css: "gantt_container", 316 cols: [{ 317 width: 280, 318 min_width: 280, 319 rows: [{ 320 view: "grid", 321 scrollX: "gridScroll", 322 scrollable: true, 323 scrollY: "scrollVer" 324 }, 325 { 326 view: "scrollbar", 327 id: "gridScroll", 328 group: "horizontal" 329 } 330 ] 331 }, 332 { 333 resizer: true, 334 width: 1 335 }, 336 { 337 rows: [{ 338 view: "timeline", 339 scrollX: "scrollHor", 340 scrollY: "scrollVer" 341 }, 342 { 343 view: "scrollbar", 344 id: "scrollHor", 345 group: "horizontal" 346 } 347 ] 348 }, 349 { 350 view: "scrollbar", 351 id: "scrollVer" 352 } 353 ] 354 }; 355 356 //時間軸圖表中,任務條形圖的高度 357 // gantt.config.task_height = 28 358 //時間軸圖表中,甘特圖的高度 359 // gantt.config.row_height = 36 360 //時間軸圖表中,如果不設置,只有行邊框,區分上下的任務,設置之后帶有列的邊框,整個時間軸變成格子狀。 361 gantt.config.show_task_cells = true 362 //當task的長度改變時,自動調整圖表坐標軸區間用於適配task的長度 363 gantt.config.fit_tasks = true 364 gantt.config.min_column_width = 50; 365 gantt.config.auto_types = true; 366 gantt.config.xml_date = "%Y-%m-%d"; 367 gantt.config.scale_unit = "month"; 368 gantt.config.step = 1; 369 gantt.config.date_scale = "%Y年%M"; 370 gantt.config.start_on_monday = true; 371 gantt.config.scale_height = 90; 372 gantt.config.autoscroll = true; 373 gantt.config.calendar_property = "start_date"; 374 gantt.config.calendar_property = "end_date"; 375 gantt.config.readonly = true; 376 gantt.i18n.setLocale('cn'); 377 378 // 初始化 379 gantt.init(this.$refs.gantt) 380 // 數據解析 381 gantt.parse(this.tasks) 382 } 383 } 384 </script> 385 <style lang="scss"> 386 .firstLevelTask { 387 border: none; 388 389 .gantt_task_content { 390 font-size: 13px; 391 } 392 } 393 394 .secondLevelTask { 395 border: none; 396 } 397 398 .thirdLevelTask { 399 border: 2px solid #da645d; 400 color: #da645d; 401 background: #da645d; 402 } 403 404 .milestone-default { 405 border: none; 406 background: rgba(0, 0, 0, 0.45); 407 } 408 409 .milestone-unfinished { 410 border: none; 411 background: #5692f0; 412 } 413 414 .milestone-finished { 415 border: none; 416 background: #84bd54; 417 } 418 419 .milestone-canceled { 420 border: none; 421 background: #da645d; 422 } 423 424 html, 425 body { 426 margin: 0px; 427 padding: 0px; 428 height: 100%; 429 overflow: hidden; 430 } 431 432 .container { 433 height: 100%; 434 width: 100%; 435 position: relative; 436 .gantt_grid_head_cell { 437 padding-left: 20px; 438 text-align: left !important; 439 font-size: 14px; 440 color: #333; 441 } 442 443 .select-wrap { 444 position: absolute; 445 top: 25px; 446 z-index: 99; 447 width: 90px; 448 left: 180px; 449 450 .el-input__inner { 451 border: none; 452 } 453 } 454 455 .left-container { 456 height: 100%; 457 } 458 459 .parent { 460 .gantt_tree_icon { 461 &.gantt_folder_open { 462 background-image: url(assets/gantt-icon.svg) !important; 463 } 464 &.gantt_folder_closed{ 465 background-image: url(assets/gantt-icon-up.svg) !important; 466 } 467 } 468 } 469 470 .green, 471 .yellow, 472 .pink, 473 .popular { 474 .gantt_tree_icon.gantt_file { 475 background: none; 476 position: relative; 477 478 &::before { 479 content: ""; 480 width: 10px; 481 height: 10px; 482 border-radius: 50%; 483 position: absolute; 484 left: 50%; 485 top: 50%; 486 transform: translate(-50%, -50%); 487 } 488 } 489 } 490 491 .green { 492 .gantt_tree_icon.gantt_file { 493 &::before { 494 background: #84bd54; 495 } 496 } 497 } 498 499 .yellow { 500 .gantt_tree_icon.gantt_file { 501 &::before { 502 background: #fcca02; 503 } 504 } 505 } 506 507 .pink { 508 .gantt_tree_icon.gantt_file { 509 &::before { 510 background: #da645d; 511 } 512 } 513 } 514 515 .popular { 516 .gantt_tree_icon.gantt_file { 517 &::before { 518 background: #d1a6ff; 519 } 520 } 521 } 522 523 } 524 525 .left-container { 526 height: 100%; 527 } 528 529 .gantt_task_content { 530 text-align: left; 531 padding-left: 10px; 532 } 533 </style>