gantt-planing-vue
| Install | |
|---|---|
npm install gantt-planing-vue |
|
| Latest Version: | 0.12.0 |
| Vue: | 3 |
| License: | MIT |
| Last Updated: | Oct 14, 2026 |
| Links: | npm |
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%);
Ctrl / ⌘ + 滚轮(触控板捏合)同样走缩放。 - 画布平移:鼠标左键 / 中键拖拽无限平移。
- CAD 控制台:放大、缩小、
1:1还原、一键全图自适应居中(Fit to Screen)。 - 点阵背景底纹与图例系统。
4. 主题与尺寸可运行时调整
所有外观项都通过 CSS 自定义属性下发(样式表内只写 var(...)),因此改动 props 即时换肤,无需重新编译;变量只挂在组件根元素上,不会污染宿主应用全局。
📦 安装
npm install gantt-planing-vue
# 或者
pnpm add gantt-planing-vue
# 或者
yarn add gantt-planing-vue
Vue 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 // 当前角度
🖥️ 大屏 / 全屏展示
形象进度图的图纸是「固定 2400 宽 + 里程越长越高」的竖版图(100km 的公路约 2400 × 3790),塞进扁宽的容器里做整图自适应,只能缩成一张贴图、四周全是留白。要缓解它有两个旋钮:
chartAspect:目标宽高比(宽 / 高)。大屏底部的扁横条给 3 ~ 4,里程纵轴会按它压缩、图纸从竖版变横向,自适应后留白大幅减少。只压缩不拉伸,跨度短的项目不受影响。canvasWidth:图纸的逻辑宽度,想更宽可以直接加大(默认 2400)。showHeader: false:关掉左上角的工程概况悬浮条。它与图纸自绘的标题带内容重复(项目名 / 标段 / 里程 / 统计日期),位置又正好压在标题上方。
fitViewport/viewportPadding已在 0.12.0 移除。 它们靠「按容器宽高比现算图纸高度」来铺满,代价是图纸几何变成了容器尺寸的函数:容器一有风吹草动(面板动画、滚动条出现/消失、切 Tab 重建),用户刚拖出来、刚缩好的视口就被弹回自适应 —— 表现就是「工具栏百分比在变,画面却纹丝不动」。 现在图纸几何只由tasks/config/chartAspect/canvasWidth决定,与容器尺寸彻底解耦:容器尺寸变化只重绘、绝不重排视口。chartAspect是静态值,与容器尺寸无关,因此没有这个问题。为什么图纸宽度不跟着容器走:图纸左侧那排表头(图例 / 工程名称 / 纵断面 / 专业 / 里程桩号)是固定宽度、不随缩放变化的。若让图纸尺寸等于容器尺寸做 1:1 摆放,这排表头会按原尺寸吃掉容器宽度 —— 1092px 宽的面板里主图区只剩 526px,反而比「整体缩到 0.45 倍」的 835px 更窄。保持图纸固定宽、只对齐宽高比,表头跟着一起缩小,主图区更大。
配合浏览器全屏:全屏元素会脱离祖先的 CSS transform(大屏自适应常给根节点挂 scale(0.6)),因此把图表所在的那层容器 requestFullscreen() 后,画布拿到的是真实的 1:1 视口尺寸、字线清晰;图表 DOM 不搬家,缩放与选中态都不会丢。
await sheetRef.value.requestFullscreen() // 退出用 document.exitFullscreen()
🛠️ 单独使用 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
Related Packages
Version History
| Version | Released | License |
|---|---|---|
| 0.12.0 | MIT | |
| 0.11.1 | MIT | |
| 0.11.0 | MIT | |
| 0.10.4 | MIT | |
| 0.10.3 | MIT | |
| 0.10.2 | MIT | |
| 0.10.1 | MIT | |
| 0.10.0 | MIT | |
| 0.9.0 | MIT | |
| 0.8.0 | MIT | |
| 0.7.0 | MIT | |
| 0.6.0 | MIT | |
| 0.5.0 | MIT | |
| 0.3.0 | MIT | |
| 0.2.0 | MIT | |
| 0.1.0 | MIT |