gantt-planing-vue
v0.10.0
Published
Vue 3 Gantt chart & 5-in-1 project-planning diagram component (Gantt, Time-Scaled network, ADM, PERT/PDM, Linear time-location) — task tree, CPM critical path, virtual scroll, CAD viewport
Maintainers
Readme
gantt-planing-vue
现代化 Vue 3 工程项目进度管理与 5 合 1 图表引擎组件库(甘特图 + 时标网络图 + 双代号 ADM + 单代号 PERT/PDM 前导图 + 形象进度时距图),原生支持 CAD 级平移缩放视口与关键路径法(CPM)算法。
🌟 核心特性
1. 5 合 1 图表引擎(统一底层数据驱动)
- 甘特图(Gantt Chart):
- 无限级任务树(WBS、工期、进度、起止日期),大数据高性能手动虚拟滚动(Phantom 容器 + translateY 视口裁剪)。
- 任务条交互:整体平移改期、右侧手柄拉伸工期,松手自动补齐到落点所在刻度格;进度以实心条按
progress绘制。 - 任务形态:普通任务、
project摘要条(两端竖线封口)、milestone里程碑(菱形标记)。 - 依赖连线:拖动任务条上的锚点直接拉线连接紧后任务,正交折线 + 箭头自动避让。
- 刻度模式:年 / 季 / 月 / 周 / 日 / 12 小时 / 6 小时 / 小时,共 8 档(
ScaleMode),支持国际化(locale)。 - 左侧表格由业务方自建:组件只负责时间轴 + 横道 + 连线,选中态通过
activeTaskId受控回写(v-model:active-task-id)。 - 快捷键:
Tab/Shift + Tab(升降级)、Delete/Backspace、Ctrl|Cmd + D(删除)、Ctrl|Cmd + X / C / V(剪切/复制/粘贴)、Esc(取消选中)。
- 时标网络图(Time-Scaled Network Diagram):水平实箭线表示工作持续时间,工程标准**自由时差波形线(FF)**自动生成,紧后工作折线/垂直逻辑转移线。
- 双代号网络图(ADM):节点为事件编号圆圈 $(i) \to (j)$(满足拓扑序 $i < j$),箭线上标工作名、下标持续时间,逻辑断路与虚工作自动生成虚箭线。
- 单代号 / PERT 前导图(PDM):工程级标准六参数活动卡片(ES / DU / EF / 工作名 / LS / TF / LF),基于拓扑分层的 Sugiyama 正交避让连线。
- 形象进度图(时距图 / Time-Location Diagram):横轴时间推进 × 纵轴里程桩号,支持构造物符号、路基填挖、停工区间、共线段、里程桩号与高程曲线,Canvas 光栅化 + 2 倍图导出 PNG/PDF。
- 视图旋转:工具栏内置旋转按钮,点一下转 90°(0° / 90° / 180° / 270°),与图片预览的「点一下转一下」一致。缩放、平移、点击拾取与导出的 PNG/PDF 全部跟着转,是真旋转而非只把画面转过去。详见 旋转视图。
2. 关键路径法(CPM)引擎
- 内置标准 CPM 前向正推(Forward Pass,求 $ES, EF$)与逆向逆推(Backward Pass,求 $LS, LF$)。
- 自动计算总时差 $TF$ 与自由时差 $FF$,识别关键工作($TF = 0$)并以红色标注关键线路。
- 图表工具栏提供「关键路径」开关:开启后淡化非关键工作,突出关键线路。
3. CAD 级交互视口(useCadViewport)
- 无级缩放:滚轮以鼠标光标为锚点缩放(10% ~ 450%)。
- 画布平移:鼠标左键 / 中键拖拽无限平移。
- CAD 控制台:放大、缩小、
1:1还原、一键全图自适应居中(Fit to Screen)。 - 点阵背景底纹与图例系统。
4. 主题与尺寸可运行时调整
所有外观项都通过 CSS 自定义属性下发(样式表内只写 var(...)),因此改动 props 即时换肤,无需重新编译;变量只挂在组件根元素上,不会污染宿主应用全局。
📦 安装
npm install gantt-planing-vue
# 或者
pnpm add gantt-planing-vue
# 或者
yarn add gantt-planing-vueVue 3.4+ 为 peerDependency,本库零运行时依赖。
🚀 快速上手
1. 全局注册插件
// main.ts
import { createApp } from 'vue'
import App from './App.vue'
import GanttPlaningVue from 'gantt-planing-vue'
import 'gantt-planing-vue/style.css'
const app = createApp(App)
app.use(GanttPlaningVue)
app.mount('#app')2. 在组件中使用
组件不内置左侧表格。数据由 taskData 传入(task-data),变更通过 update:tasks 事件回传——注意入参与出参的名字不对称,所以请写成显式的 :task-data + @update:tasks,而不要写 v-model:tasks(后者会把 tasks 当作未知属性透传到根元素上)。
<script setup lang="ts">
import { ref } from 'vue'
import { GanttChart, ScaleMode, ViewMode, type Task, type TaskId } from 'gantt-planing-vue'
const tasks = ref<Task[]>([
{
id: 1,
parent_id: 0,
type: 'task',
name: '基础工程',
duration: 5,
progress: 40,
project: 'ProjectSample',
start: new Date(2026, 8, 1),
end: new Date(2026, 8, 5, 23, 59, 59, 999),
children: [],
},
{
id: 2,
parent_id: 0,
type: 'task',
name: '主体结构施工',
duration: 10,
progress: 10,
project: 'ProjectSample',
dependencies: ['1'], // 紧前任务为基础工程
start: new Date(2026, 8, 6),
end: new Date(2026, 8, 15, 23, 59, 59, 999),
children: [],
},
])
const scaleMode = ref<ScaleMode>(ScaleMode.Day)
const viewMode = ref<ViewMode>(ViewMode.GanttViewer)
/** 左侧表格自建,这里只把选中行受控回写,键盘快捷键才有操作目标 */
const activeTaskId = ref<TaskId>(0)
</script>
<template>
<div style="width: 100vw; height: 100vh;">
<!-- 切换模式:GanttViewer | TimeViewer | AdmViewer | PdmViewer | LinearViewer -->
<GanttChart
:task-data="tasks"
:scale-mode="scaleMode"
:view-mode="viewMode"
v-model:active-task-id="activeTaskId"
:column-width="38"
@update:tasks="tasks = $event"
/>
</div>
</template>任务条拖拽改期后会派发
date-change与update:tasks,两者都可用于同步后端。
3. 里程碑与摘要任务
// 里程碑:渲染为菱形标记(居中于所在刻度格)
{ id: 3, type: 'milestone', name: '主体封顶', duration: 1, progress: 100, /* ... */ }
// 摘要 / 项目:渲染为两端带竖线的粗横杠,通常用其子任务的起止范围
{ id: 4, type: 'project', name: '一期工程', duration: 60, progress: 55, /* ... */ }🎨 外观与主题 Props
数值型尺寸项直接生效;颜色项留空即使用内置配色。
| 分类 | Props |
| --- | --- |
| 尺寸 | headerHeight、columnWidth、rowHeight、ganttHeight(不传则撑满父容器) |
| 条形态 | barCornerRadius、handleWidth、barFill(条高占行高百分比,默认 64) |
| 字体 | fontFamily、fontSize(不传则继承宿主应用) |
| 普通任务 | barBackgroundColor、barBackgroundSelectedColor、barProgressColor、barProgressSelectedColor |
| 摘要任务 | projectBackgroundColor、projectBackgroundSelectedColor、projectProgressColor、projectProgressSelectedColor |
| 里程碑 | milestoneBackgroundColor、milestoneBackgroundSelectedColor |
| 其他 | arrowColor、arrowIndent、todayColor |
需要更细的换肤(如摘要条端封颜色),可直接覆盖根元素上的 CSS 变量:
--gantt-selected-outline、--gantt-project-cap-color、--gantt-actual-bg、--gantt-bar-height等。
🔄 旋转视图
形象进度图的工具栏内置一组旋转按钮:⟲ 逆时针 90°、⟳ 顺时针 90°、角度按钮复位。旋转角只有四档(Rotation = 0 | 90 | 180 | 270),图纸连同坐标文字一起侧倒,与图片预览的「点一下转一下」是同一个观感。
旋转不是只把画面转过去。 图纸是 Canvas 绘制的,视口里每一处交互都锚定在「屏幕 x = 图纸 x」上,所以组件内部把屏幕坐标系统一逆变换回图纸坐标系,因此旋转 90° 后:
- 滚轮缩放:锚点仍然落在光标下(不会跑到对角去);
- 拖拽平移:鼠标沿屏幕上图纸的方向拖动即可,手感自洽;
- 点击拾取:点中的仍是光标正下方那条线,
select-task不会错位; - 导出 PNG / PDF:按当前旋转角转置,所见即所得(转着导出,图也是转的)。
旋转后组件只重新居中、不动缩放级别 —— 缩放是你此刻的观看意图,不该被旋转重置。
一个必须知道的副作用:旋转 90° / 270° 后,容器宽 = 图纸的时间轴、容器高 = 里程轴。也就是说容器又矮又宽时旋转反而只能看到很短的一段里程;又高又窄时时间轴能看很长,但一次只能看到很少的里程。所以旋转在这类工程图里主要是展示 / 投屏 / 打印用途,并不是提升信息密度的手段。
只作用于形象进度图:标准甘特图、时标网络图 / ADM / PERT 的图形形状本身就是横长的,旋转没有意义,这些视图不提供旋转按钮。
需要把旋转挂到自己的工具栏时,设 showRotateControls: false,改用组件实例方法:
<LinearProgressViewer ref="viewerRef" :show-rotate-controls="false" :tasks="tasks" />viewerRef.value.rotate(1) // 顺时针 90°
viewerRef.value.rotate(-1) // 逆时针 90°
viewerRef.value.setRotation(180) // 直接指定角度
viewerRef.value.resetRotation() // 复位为 0°
viewerRef.value.rotation // 当前角度🛠️ 单独使用 CAD 网络图查看器
<script setup lang="ts">
import { ref } from 'vue'
import { DiagramViewer, type Task } from 'gantt-planing-vue'
const tasks = ref<Task[]>([/* 任务数据 */])
const mode = ref<'time' | 'adm' | 'pert'>('pert')
</script>
<template>
<div style="width: 1000px; height: 600px;">
<DiagramViewer
:tasks="tasks"
:mode="mode"
:show-toolbar="true"
:show-legend="true"
@select-task="(t) => console.log('选中', t)"
/>
</div>
</template>🧮 独立调用 CPM 关键路径算法
import { calculateCpm } from 'gantt-planing-vue'
const result = calculateCpm(tasks)
console.log('总工期:', result.totalDuration)
console.log('关键线路任务 ID 列表:', result.criticalPath)
// 每个活动的六参数:
result.activityList.forEach(act => {
console.log(`${act.name}: ES=${act.es}, EF=${act.ef}, LS=${act.ls}, LF=${act.lf}, 总时差TF=${act.tf}, 自由时差FF=${act.ff}, 关键工作=${act.isCritical}`)
})💻 本地运行 Demo
# 进入项目根目录
cd gantt-planing-vue
# 安装依赖
npm install
# 启动 Vite 开发服务器预览 Demo
npm run dev
# 运行类型检查
npm run typecheck
# 构建生产产物
npm run build📚 文档
| 文档 | 内容 |
| --- | --- |
| docs/USAGE.md | 完整使用手册:Props / 事件 / 插槽速查、TaskId 与 idMode、刻度与国际化、快捷键与只读模式、按需导入与类型清单 |
| CHANGELOG.md | 版本变更记录(含破坏性变更逐条说明) |
| src/components/diagram/README.md | 网络图 / 形象进度图模块的内部结构与算法入口 |
📄 开源许可证
MIT License © 2026
