Разработка Gantt-диаграмм для управления проектами на сайте
Gantt-диаграмма отображает задачи проекта на временно́й шкале: начало/конец, зависимости между задачами, прогресс выполнения, перегруженность ресурсов.
Библиотеки
| Библиотека | Особенности |
|---|---|
| DHTMLX Gantt | Коммерческая, feature-rich, drag-drop |
| Bryntum Gantt | Коммерческая, React-компоненты |
| frappe-gantt | Open-source, простая |
| @dhtmlx/gantt | npm пакет DHTMLX |
| gantt-task-react | Open-source React |
gantt-task-react (open-source)
npm install gantt-task-react
import { Gantt, Task, ViewMode } from 'gantt-task-react';
import 'gantt-task-react/dist/index.css';
interface ProjectTask {
id: string;
name: string;
start: Date;
end: Date;
progress: number;
type: 'task' | 'milestone' | 'project';
dependencies?: string[];
assignee?: string;
}
function ProjectGantt({ tasks, onTaskChange }: {
tasks: ProjectTask[];
onTaskChange: (task: Task) => void;
}) {
const [view, setView] = useState<ViewMode>(ViewMode.Week);
const ganttTasks: Task[] = tasks.map(t => ({
id: t.id,
name: t.name,
start: t.start,
end: t.end,
progress: t.progress,
type: t.type,
dependencies: t.dependencies ?? [],
styles: {
progressColor: t.progress >= 100 ? '#22c55e' : '#3b82f6',
progressSelectedColor: '#1d4ed8'
}
}));
return (
<div>
<div className="gantt-toolbar">
<button onClick={() => setView(ViewMode.Day)}>День</button>
<button onClick={() => setView(ViewMode.Week)}>Неделя</button>
<button onClick={() => setView(ViewMode.Month)}>Месяц</button>
</div>
<Gantt
tasks={ganttTasks}
viewMode={view}
onDateChange={onTaskChange}
onProgressChange={onTaskChange}
locale="ru"
listCellWidth="200px"
ganttHeight={400}
todayColor="rgba(59, 130, 246, 0.1)"
TooltipContent={({ task }) => (
<div className="gantt-tooltip">
<strong>{task.name}</strong>
<p>Начало: {format(task.start, 'dd.MM.yyyy')}</p>
<p>Конец: {format(task.end, 'dd.MM.yyyy')}</p>
<p>Прогресс: {task.progress}%</p>
</div>
)}
/>
</div>
);
}
Кастомный Gantt на SVG + D3
Для специфических требований (особый стиль, нестандартные ячейки):
import { scaleTime } from 'd3-scale';
import { timeDay, timeWeek } from 'd3-time';
function CustomGantt({ tasks, startDate, endDate, width = 900 }) {
const timelineWidth = width - 200; // 200px на названия задач
const rowHeight = 40;
const height = tasks.length * rowHeight + 60;
const xScale = scaleTime()
.domain([startDate, endDate])
.range([0, timelineWidth]);
return (
<svg width={width} height={height}>
{/* Временны́е метки */}
{timeWeek.range(startDate, endDate).map(week => (
<g key={week.toISOString()}>
<line
x1={xScale(week) + 200} y1={0}
x2={xScale(week) + 200} y2={height}
stroke="#e5e7eb" strokeWidth={1}
/>
<text
x={xScale(week) + 204}
y={15}
fontSize={11}
fill="#6b7280"
>
{format(week, 'dd MMM')}
</text>
</g>
))}
{/* Задачи */}
{tasks.map((task, i) => {
const x = xScale(task.start) + 200;
const width = xScale(task.end) - xScale(task.start);
const y = i * rowHeight + 25;
return (
<g key={task.id}>
{/* Название */}
<text x={4} y={y + 14} fontSize={13} fill="#374151"
style={{ cursor: 'pointer' }}>
{task.name.length > 22 ? task.name.slice(0, 22) + '…' : task.name}
</text>
{/* Бар задачи */}
<rect x={x} y={y} width={width} height={24}
rx={4} fill="#93c5fd" />
{/* Прогресс */}
<rect x={x} y={y} width={width * (task.progress / 100)} height={24}
rx={4} fill="#3b82f6" />
{/* Процент */}
{width > 40 && (
<text x={x + width / 2} y={y + 16}
textAnchor="middle" fontSize={11} fill="white">
{task.progress}%
</text>
)}
</g>
);
})}
</svg>
);
}
Drag-and-drop для изменения дат
При использовании DHTMLX Gantt drag-drop встроен. Для кастомного SVG — d3-drag или @dnd-kit.
Сроки
gantt-task-react с кастомным tooltip — 3–5 дней. Кастомный SVG Gantt с drag-drop и зависимостями — 2–3 недели.







