Implementing Interactive Project Timelines with dhtmlxGantt
Install the library via npm:
npm install dhtmlx-gantt
Import the Gantt component and its stylesheet:
import gantt from 'dhtmlx-gantt';
import 'dhtmlx-gantt/codebase/dhtmlxgantt.css';
Create a container element in your template:
<div ref="chartContainer" class="gantt-chart" />
Define your dataset sturcture:
data() {
return {
chartData: {
data: [
{
id: "task-001",
content: "Foundation Work",
commence: "2023-06-01",
conclude: "2023-06-30",
span: 30,
status: 0.5
}
]
},
viewMode: 'week',
scaleTypes: [
{ label: 'Weekly', value: 'week' },
{ label: 'Monthly', value: 'month' }
]
};
}
Initialize the chart with in the mounted lifecycle:
mounted() {
this.renderChart();
},
methods: {
renderChart() {
gantt.clearAll();
// Disable interactions
gantt.config.drag_move = false;
gantt.config.drag_resize = false;
gantt.config.drag_progress = false;
gantt.config.order_branch = false;
gantt.config.show_links = false;
// Visual settings
gantt.config.show_progress = true;
gantt.config.min_grid_column_width = 130;
gantt.config.duration_unit = 'day';
gantt.config.duration_step = 1;
gantt.config.scale_height = 85;
gantt.config.xml_date = '%Y-%m-%d';
// Set locale
gantt.i18n.setLocale('cn');
// Configure grid columns
gantt.config.columns = [
{
name: 'content',
label: 'Work Item',
tree: true,
width: '*',
align: 'left',
template: (task) => `<div class="task-title" title="${task.content}">${task.content}</div>`
},
{
name: 'commence',
label: 'Start Date',
width: 100,
align: 'center'
},
{
name: 'conclude',
label: 'Due Date',
width: 100,
align: 'center'
},
{
name: 'span',
label: 'Duration',
width: 80,
align: 'center',
template: (task) => `${task.span}d`
}
];
// Configure time scales
const scaleConfig = {
week: [
{ unit: 'year', step: 1, format: '%Y' },
{ unit: 'week', step: 1, format: (date) => this.weekFormatter(date) }
],
month: [
{ unit: 'year', step: 1, format: '%Y' },
{ unit: 'month', step: 1, format: '%M' }
]
};
gantt.config.scales = scaleConfig[this.viewMode];
// Add current date marker
gantt.plugins({ marker: true });
gantt.addMarker({
start_date: new Date(),
text: 'Today'
});
// Suppress default lightbox
gantt.attachEvent('onBeforeLightbox', () => false);
// Handle double-click events
gantt.attachEvent('onTaskDblClick', (id) => {
this.openTaskDetails(id);
return false;
});
// Mount and load data
gantt.init(this.$refs.chartContainer);
gantt.parse(this.chartData);
},
weekFormatter(date) {
const endOfWeek = gantt.date.add(date, 6, 'day');
const formatDay = gantt.date.date_to_str('%d');
const formatMonth = gantt.date.date_to_str('%m');
return `${formatMonth(endOfWeek)}/${formatDay(endOfWeek)}`;
},
openTaskDetails(id) {
console.log('Selected task:', id);
}
}
Apply custom styling:
.gantt-chart {
height: calc(100vh - 220px);
::v-deep .task-title {
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 100%;
}
}