gantt-planing-vue

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
2,604
Install
npm install gantt-planing-vue
Latest Version:0.12.0
Vue:3
License:MIT
Last Updated:Oct 14, 2026
Links: npm
Maintainer: r2g4ai

gantt-planing-vue

现代化 Vue 3 工程项目进度管理与 5 合 1 图表引擎组件库(甘特图 + 时标网络图 + 双代号 ADM + 单代号 PERT/PDM 前导图 + 形象进度时距图),原生支持 CAD 级平移缩放视口与关键路径法(CPM)算法。

Vue 3 TypeScript Vite License: MIT


🌟 核心特性

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

vue-apexgantt

Vue 3 wrapper for ApexGantt - Interactive Gantt chart library

46,524 0
@dizzy_yakov/vue-gantt

Headless, composable Gantt chart components for Vue 3

2,053 1
@fluxgantt/vue

FluxGantt Vue 3 wrapper — component + useFluxGantt composable.

797 0

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