vue前端项目实现 dhtmlxgantt 甘特图
最近项目中刚好使用了甘特图,第一次使用,在此记录一下,有需要的同学可以借鉴一下。
首先来个效果图吧,哈哈哈~~~
因为这里用到了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>