Настройка оси диаграммы Google Gantt [Метки и дата / время]

Я пытаюсь работать с диаграммами Ганта, и в целом все работает довольно хорошо. Однако некоторые требования к настройке заставили меня почесать голову.

Я хочу, чтобы метки задач имели имена задач внутри или рядом с панелью в зависимости от ширины и положения панели. Необходимо удалить имена задач из левой части оси Y

Кроме того, как только диаграмма станет больше, временной диапазон (ось X) не будет виден, поскольку он расположен внизу, поэтому было бы здорово, если бы его можно было разместить сверху.

Есть ли способ добиться этой настройки?

Пожалуйста, проверьте фрагменты кода, упомянутые ниже для справки.

Диаграмма Ганта

import { Component, OnInit } from '@angular/core';
import { nightcycleDependencies } from './nightcycleData';
declare var google: any;
declare var $ :any;

@Component({
  selector: 'app-chart',
  templateUrl: './chart.component.html',
  styleUrls: ['./chart.component.css']
})
export class ChartComponent implements OnInit {

  public allTasks: any[] = [];
  public nightcycleDependencies = nightcycleDependencies;
  constructor() {
  }

  ngOnInit() {
    this.renderChartForAll();
  }

  // Render Chart for Summarized View
  renderChartForAll() {
    google.charts.setOnLoadCallback(() => this.formatDataForAll());
  }

  // Data format for Summarized View
  formatDataForAll() {
    this.allTasks = [];
    var resource = 'Job Cycles';
    const today = new Date();
    const start_day = today.getDate();
    const end_day = today.getDate();
    const month = today.getMonth() + 1;
    const year = today.getFullYear();

    nightcycleDependencies.forEach((dependency) => {

      var finalStart = 9999999999999;
      var finalEnd = 0;
      var taskId = dependency.source.split(" ").join('');
      var taskName = dependency.source;
      var dependencies = dependency.parents ? dependency.parents.toString().split(" ").join("") : "null";

      dependency.ids.forEach((job) => {
        // Date handling
        var startTime = new Date(year + '-' + month + '-' + start_day + ' ' + "12:00").getTime()
        var startDate = new Date(year + '-' + month + '-' + start_day + ' ' + job.start).getTime()
        var endDate = new Date(year + '-' + month + '-' + end_day + ' ' + job.end).getTime()
        if (startTime > startDate) {
          startDate = new Date(year + '-' + month + '-' + (start_day + 1) + ' ' + job.end).getTime()
          endDate = new Date(year + '-' + month + '-' + (end_day + 1) + ' ' + job.end).getTime()
        } else if (endDate < startDate) {
          endDate = new Date(year + '-' + month + '-' + (end_day + 1) + ' ' + job.end).getTime()
        }
        if (finalStart > startDate) {
          finalStart = startDate
        }
        if (finalEnd < endDate) {
          finalEnd = endDate
        }
      });
      var duration = finalEnd - finalStart
      this.allTasks.push([taskId, taskName, taskName, new Date(finalStart), new Date(finalEnd), duration, 100, dependencies])
    });
    this.drawChart(this.allTasks, '');
  }

  // Render Chart for Selected Cycle
  getCycleBasedData(source) {
    google.charts.setOnLoadCallback(() => this.formatCycleBasedData(source));
  }

  // Data format for Selected Cycle
  formatCycleBasedData(source) {
    this.allTasks = [];
    var resource = source;
    var dependencies = [];
    var chartSource = [];
    var jobsFromOtherSources = [];

    // Set The source to loop over
    nightcycleDependencies.forEach(dependency => {
      if (dependency.source === source) {
        chartSource = dependency.ids;
      }
    });
    chartSource.forEach((dependency) => {
      var taskId = dependency.id.split(" ").join('');
      var taskName = dependency.id;
      dependencies = [];

      // Date handling
      var date = this.formatDate(dependency.start, dependency.end);

      // Find out dependencies
      dependency.parent.forEach((parent) => {
        dependencies.push(parent.id.split(' ').join(''))
      })
      var finalDependencies = dependencies.toString();

      // Get all Parent jobs and push them to task list
      jobsFromOtherSources = this.getAllParentJobs(taskName, resource);
      jobsFromOtherSources.forEach((otherJob) => {
        var jobId = otherJob.id.split(" ").join('');
        var jobName = otherJob.id;
        var jobSource = otherJob.source;
        var jobDate = this.formatDate(otherJob.start, otherJob.end);
        this.allTasks.push([jobId, jobName, jobSource, new Date(jobDate[0]), new Date(jobDate[0] + 800000), (jobDate[0] + 800000 - jobDate[0]), 100, null])
      })

      // Get all Children jobs
      // this.getAllChildJobs(taskName, resource);

      // Push to the task list
      this.allTasks.push([taskId, taskName, resource, new Date(date[0]), new Date(date[1]), (date[1] - date[0]), 100, finalDependencies])
    });
    this.drawChart(this.allTasks, source);
  }

  // Function to handle date
  formatDate(start, end) {
    const today = new Date();
    var startTime = 0;
    var startDate = 0;
    var endDate = 0;
    const start_day = today.getDate();
    const end_day = today.getDate();
    const month = today.getMonth() + 1;
    const year = today.getFullYear();

    startTime = new Date(year + '-' + month + '-' + start_day + ' ' + "12:00").getTime()
    startDate = new Date(year + '-' + month + '-' + start_day + ' ' + start).getTime()
    endDate = new Date(year + '-' + month + '-' + end_day + ' ' + end).getTime()
    if (startTime > startDate) {
      startDate = new Date(year + '-' + month + '-' + (start_day + 1) + ' ' + start).getTime()
      endDate = new Date(year + '-' + month + '-' + (end_day + 1) + ' ' + end).getTime()
    } else if (endDate < startDate) {
      endDate = new Date(year + '-' + month + '-' + (end_day + 1) + ' ' + end).getTime()
    }
    return [startDate, endDate]
  }

  // Get all Parent Jobs
  getAllParentJobs(name, source) {
    var job = {};
    var jobSource = '';
    var jobsFromOtherSources = [];
    nightcycleDependencies.forEach((dependency) => {
      if (dependency.source != source) {
        jobSource = dependency.source;
        dependency.ids.forEach((job) => {
          job = job;
          job.source = jobSource
          job.children.forEach((child) => {
            if (child.id === name) {
              jobsFromOtherSources.push(job);
            }
          });
        });
      }
    });
    return jobsFromOtherSources;
  }

  // Get all children jobs
  // getAllChildJobs(name, source) {

  // }

  // Chart configuration
  drawChart(allTasks, source) {
    var nightcycleData = new google.visualization.DataTable();
    nightcycleData.addColumn('string', 'Task ID');
    nightcycleData.addColumn('string', 'Task Name');
    nightcycleData.addColumn('string', 'Resource');
    nightcycleData.addColumn('date', 'Start');
    nightcycleData.addColumn('date', 'End');
    nightcycleData.addColumn('number', 'Duration');
    nightcycleData.addColumn('number', 'Percent Complete');
    nightcycleData.addColumn('string', 'Dependencies');

    nightcycleData.addRows(this.allTasks);
    var trackHeight = 50;
    var options = {
      height: nightcycleData.getNumberOfRows() * trackHeight + 200,
      gantt: {
        labelStyle: {
          fontName: ["RobotoCondensedRegular"],
          fontSize: 15
        },
        textPosition: 'none',
        innerGridTrack: { fill: '#fff3e0' },
        innerGridDarkTrack: { fill: '#ffcc80' },
        trackHeight: trackHeight
      }
    };

    var chart = new google.visualization.Gantt(source ? document.getElementById(source.split(' ').join('')) : document.getElementById('chart_div'));
    chart.draw(nightcycleData, options);
    // Event listener which will fire on bar selection
    google.visualization.events.addListener(chart, 'select', () => this.getSelectedBarInfo(nightcycleData, chart));

  }

  // Change chart tab programmatically
  getSelectedBarInfo(nightcycleData, chart) {
    var selection = chart.getSelection();
    if (selection.length) {
      var cycle = nightcycleData.getValue(selection[0].row, 2);
      this.getCycleBasedData(cycle);
      var cycleId = cycle.split(" ").join("");
      document.querySelector('.tab-pane.fade.active.show').classList.remove('active', 'show');
      document.querySelector('.nav-link.active').classList.remove('active');
      document.getElementById(cycleId).classList.add('active', 'show');
      document.getElementById('id'+cycleId).classList.add('active', 'show');
    }
  }

  //For Responsiveness on resizing
  onResize(event) {
    this.drawChart(this.allTasks, event.target.id);
  }
}
.btn-holders{
    padding:10px;
}
.chart-tabs{
    padding: 20px 0px;
}
.chart-btn{
    margin: 5px;
}
.chart-item{
    float:left;
    padding:20px 20px 20px 0px;
    list-style: none;
}
.chart-list{
    padding: 0px;
}
.chart-anchor{
    background-color: #c0b5d0;
    padding: 20px;
    border-radius: 10px;
    color: #0c5460;
}
.chart-anchor.active {
    background-color: dodgerblue;
    color:#fff;
}
.tab-content>.tab-pane {
  height: 1px;
  overflow: hidden;
  display: block;
 visibility: hidden;
}
.tab-content>.active {
  height: auto;
  overflow: hidden;
  visibility: visible;
}
<div class="container-fluid">
  <!-- Tabs -->
  <div class="chart-tabs">
    <ul class="nav nav-pills" role="tablist">
      <li class="nav-item">
        <a [attr.id]="'idall'" class="nav-link active" data-toggle="pill" href="#all" role="tab">All</a>
      </li>
      <li class="nav-item" *ngFor="let dependency of nightcycleDependencies" (click)="getCycleBasedData(dependency.source)">
        <a [attr.id]="'id'+dependency.source.split(' ').join('')" class="nav-link" data-toggle="pill" [attr.href]="'#'+dependency.source.split(' ').join('')" role="tab">{{dependency.source}}</a>
      </li>
    </ul>
  </div>
  <!-- Tab Content -->
  <div class="tab-content">
    <div id="all" class="tab-pane fade in active show" role="tabpanel">
      <div id="chart_div" style="width:100%;" (window:resize)="onResize($event)"></div>
    </div>
    <div id="DailyProcessing" style="width:100%;" class="tab-pane fade" role="tabpanel">
      <div (window:resize)="onResize($event)"></div>
    </div>
    <div id="TradeMgmtandProcessing" style="width:100%;" class="tab-pane fade" role="tabpanel">
      <div (window:resize)="onResize($event)"></div>
    </div>
    <div id="DailyAnalyticsCycle" style="width:100%;" class="tab-pane fade" role="tabpanel">
      <div (window:resize)="onResize($event)"></div>
    </div>
    <div id="EuropeCycle" style="width:100%;" class="tab-pane fade" role="tabpanel">
      <div (window:resize)="onResize($event)"></div>
    </div>
    <div id="USCycle" style="width:100%;" class="tab-pane fade" role="tabpanel">
      <div (window:resize)="onResize($event)"></div>
    </div>
  </div>
</div>

1 ответ

Решение

Неважно, я получил ответ с небольшой манипуляцией DOM:

Добавьте прослушиватель событий, который должен срабатывать после того, как график будет готов

google.visualization.events.addListener(chart, 'ready', () => this.addLabelText(chartId, this.allTasks));

Тело функции должно выглядеть примерно так:

addLabelText(chartId, allTasks) {
    var toContainer = $('#' + chartId + ' > div > div');
    $("#" + chartId + " g:eq(5) rect").each(function ($index) {

      if($(this).attr('width') < (allTasks[$index][1].length * 7)) {

        if((Number($(this).attr('x')) + Number($(this).attr('width')) + 100) > document.body.clientWidth) {
          toContainer.append("<div style='top:" + $(this).attr('y') + "px; left: " + (Number($(this).attr('x')) - (allTasks[$index][1].length * 7)) +
          "px; text-align: left;position:absolute;line-height:2; padding-left: 5px; color:#111; font-size:14px;'>" + allTasks[$index][1] + "</div>");
        } else {
          toContainer.append("<div style='top:" + $(this).attr('y') + "px; left: " + (Number($(this).attr('x')) + Number($(this).attr('width'))) +
          "px; text-align: left;position:absolute;line-height:2; padding-left: 5px; color:#111; font-size:14px;'>" + allTasks[$index][1] + "</div>");
        }

      } else {
        toContainer.append("<div style='top:" + $(this).attr('y') + "px; left: " + Number($(this).attr('x')) +
        "px; text-align: left;position:absolute;line-height:2; padding-left: 5px; color:#fff; font-size:14px;'>" + allTasks[$index][1] + "</div>");
      }
    });
  }

С небольшой настройкой в ​​соответствии с вашими потребностями, она должна работать нормально.

Другие вопросы по тегам