Настройка оси диаграммы 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>");
}
});
}
С небольшой настройкой в соответствии с вашими потребностями, она должна работать нормально.