高级用法
本节介绍 GanttFlow 的高级功能,帮助你构建更复杂的甘特图应用。
拖拽交互
GanttFlow 提供流畅的拖拽交互,包括任务拖拽、尺寸调整和进度拖拽。
启用拖拽
tsx
<EnhancedGanttChart
tasks={tasks}
enableDrag={true} // 启用任务拖拽
enableResize={true} // 启用尺寸调整
enableProgressDrag={true} // 启用进度拖拽
snapToGuides={true} // 磁吸辅助线
/>拖拽事件
tsx
<EnhancedGanttChart
tasks={tasks}
onTaskDragStart={(task) => {
console.log("开始拖拽:", task.name)
}}
onTaskDrag={(task, e, newStart, newEnd) => {
console.log(`拖拽中: ${task.name} -> ${newStart} ~ ${newEnd}`)
}}
onTaskDragEnd={(task, newStart, newEnd) => {
console.log("拖拽完成:", task.name, newStart, newEnd)
// 更新任务数据
updateTask(task.id, { start: newStart, end: newEnd })
}}
onTaskResize={(task, e, newStart, newEnd) => {
console.log("调整大小:", task.name, newStart, newEnd)
}}
/>磁吸辅助线
拖拽时显示时间对齐辅助线:
tsx
<EnhancedGanttChart
tasks={tasks}
snapToGuides={true} // 启用磁吸
guideColor="#4f46e5" // 辅助线颜色
showSnapGuides={true} // 显示对齐辅助线
snapThreshold={5} // 吸附阈值(像素)
/>拖拽限制
可以限制任务只能在特定范围内拖动:
tsx
<EnhancedGanttChart
tasks={tasks}
draggable={(task) => {
// 只允许未锁定的任务拖动
return !task.locked
}}
minDragDistance={10} // 最小拖拽距离(防止误触)
/>依赖关系
任务之间可以建立多种依赖关系。
定义依赖
tsx
const dependencies = [
{ fromId: "1", toId: "2", type: "finish_to_start" },
{ fromId: "2", toId: "3", type: "finish_to_start" },
]
<EnhancedGanttChart
tasks={tasks}
dependencies={dependencies}
/>依赖类型
| 类型 | 说明 | 图示 |
|---|---|---|
finish_to_start | FS: A 结束后,B 开始 | A ████→ ████ B |
start_to_start | SS: A 开始后,B 开始 | A ████ ⟷ B |
finish_to_finish | FF: A 结束后,B 结束 | A ████ ←▸ ████ B |
start_to_finish | SF: A 开始后,B 结束 | A ████ ⟵ B |
关键路径
关键路径高亮显示:
tsx
<EnhancedGanttChart
tasks={tasks}
dependencies={dependencies}
enableCriticalPath={true}
criticalPathColor="#ef4444"
/>循环依赖检测
自动检测并阻止循环依赖:
tsx
// 如果添加会导致循环的依赖,会抛出错误
const dependencies = [
{ fromId: "1", toId: "2", type: "finish_to_start" },
{ fromId: "2", toId: "3", type: "finish_to_start" },
{ fromId: "3", toId: "1", type: "finish_to_start" }, // 循环!
]
// Error: 检测到循环依赖: 任务1 → 任务2 → 任务3 → 任务1依赖事件
tsx
<EnhancedGanttChart
tasks={tasks}
dependencies={dependencies}
onDependencyAdd={(dependency) => {
console.log("添加依赖:", dependency)
}}
onDependencyDelete={(dependency) => {
console.log("删除依赖:", dependency)
}}
/>虚拟滚动
当任务数量超过 100 个时,强烈建议启用虚拟滚动。
启用虚拟滚动
tsx
<EnhancedGanttChart
tasks={largeTaskList}
virtualScrolling={true}
visibleTaskCount={50} // 可见区域任务数
bufferSize={10} // 上下缓冲区大小
/>配置参数
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
virtualScrolling | boolean | false | 是否启用虚拟滚动 |
visibleTaskCount | number | 50 | 可见区域渲染的任务数 |
bufferSize | number | 10 | 上下缓冲区渲染的任务数 |
性能对比
| 任务数量 | 无虚拟滚动 | 启用虚拟滚动 |
|---|---|---|
| 50 | ✅ 流畅 | ✅ 流畅 |
| 200 | ⚠️ 卡顿 | ✅ 流畅 |
| 500 | ❌ 卡顿 | ✅ 流畅 |
| 1000+ | ❌ 无法使用 | ✅ 流畅 |
自动启用
tsx
const shouldEnableVirtualScroll = tasks.length > 100
<EnhancedGanttChart
tasks={tasks}
virtualScrolling={shouldEnableVirtualScroll}
/>导出功能
GanttFlow 支持导出为 PNG、PDF 和 Excel 格式。
导出 PNG
tsx
// 通过 ref 调用导出方法
const handleExportPNG = async () => {
const dataUrl = await ganttRef.current?.exportToPNG({
backgroundColor: "#ffffff",
scale: 2, // 分辨率缩放
quality: 1.0
})
// 下载
const link = document.createElement("a")
link.download = "gantt.png"
link.href = dataUrl
link.click()
}
<EnhancedGanttChart ref={ganttRef} tasks={tasks} />
<button onClick={handleExportPNG}>导出 PNG</button>导出 PDF
tsx
const handleExportPDF = async () => {
await ganttRef.current?.exportToPDF({
filename: "project-gantt.pdf",
orientation: "landscape", // 或 "portrait"
margin: 20,
header: "项目甘特图",
footer: "Generated by GanttFlow"
})
}导出 Excel
tsx
const handleExportExcel = async () => {
await ganttRef.current?.exportToExcel({
filename: "tasks.xlsx",
includeDependencies: true, // 包含依赖关系
dateFormat: "YYYY-MM-DD"
})
}导出配置
typescript
interface ExportOptions {
// PNG/PDF 通用
backgroundColor?: string
padding?: number
// PNG 专用
scale?: number // 1-3,分辨率缩放
quality?: number // 0-1,图片质量
// PDF 专用
orientation?: "portrait" | "landscape"
filename?: string
// Excel 专用
includeDependencies?: boolean
dateFormat?: string
}滚动控制
滚动到指定日期
tsx
ganttRef.current?.scrollToDate("2024-01-15")
ganttRef.current?.scrollToTask("task-1")自适应屏幕
tsx
ganttRef.current?.fitToScreen()
// 自动调整视图以显示所有任务任务操作
动态更新任务
tsx
// 添加任务
const addTask = (newTask) => {
setTasks([...tasks, newTask])
}
// 更新任务
const updateTask = (taskId, updates) => {
setTasks(tasks.map(t =>
t.id === taskId ? { ...t, ...updates } : t
))
}
// 删除任务
const deleteTask = (taskId) => {
setTasks(tasks.filter(t => t.id !== taskId))
}任务选择
tsx
// 选中任务
ganttRef.current?.selectTask("task-1")
// 获取选中任务
const selected = ganttRef.current?.getSelectedTask()
// 清除选中
ganttRef.current?.clearSelection()
// 多选(按住 Ctrl/Cmd)
ganttRef.current?.selectTask("task-2", { multi: true })完整示例
tsx
import React, { useRef, useState } from "react"
import { EnhancedGanttChart } from "@agions/gantt-flow"
import "@agions/gantt-flow/style"
const tasks = [
{ id: "1", name: "需求分析", start: "2024-01-01", end: "2024-01-05", progress: 100 },
{ id: "2", name: "系统设计", start: "2024-01-06", end: "2024-01-10", progress: 80 },
{ id: "3", name: "后端开发", start: "2024-01-11", end: "2024-01-20", progress: 50 },
{ id: "4", name: "前端开发", start: "2024-01-11", end: "2024-01-20", progress: 30 },
{ id: "5", name: "测试验收", start: "2024-01-21", end: "2024-01-25", progress: 0 },
]
const dependencies = [
{ fromId: "1", toId: "2", type: "finish_to_start" },
{ fromId: "2", toId: "3", type: "finish_to_start" },
{ fromId: "2", toId: "4", type: "finish_to_start" },
{ fromId: "3", toId: "5", type: "finish_to_start" },
{ fromId: "4", toId: "5", type: "finish_to_start" },
]
function AdvancedGantt() {
const ganttRef = useRef(null)
const [viewMode, setViewMode] = useState("week")
return (
<div>
{/* 工具栏 */}
<div style={{ marginBottom: 16 }}>
<button onClick={() => setViewMode("day")}>日</button>
<button onClick={() => setViewMode("week")}>周</button>
<button onClick={() => setViewMode("month")}>月</button>
<button onClick={() => ganttRef.current?.fitToScreen()}>适应屏幕</button>
<button onClick={() => ganttRef.current?.exportToPNG()}>导出 PNG</button>
</div>
{/* 甘特图 */}
<div style={{ height: "600px" }}>
<EnhancedGanttChart
ref={ganttRef}
tasks={tasks}
dependencies={dependencies}
viewMode={viewMode}
enableCriticalPath={true}
virtualScrolling={true}
onTaskClick={(task) => console.log("点击:", task.name)}
onTaskDragEnd={(task, newStart, newEnd) => {
console.log("拖拽完成:", task.id, newStart, newEnd)
}}
/>
</div>
</div>
)
}