jordium-gantt-vue3-lian
v1.0.1
Published
<p align="center"> <a href="https://www.npmjs.com/package/jordium-gantt-vue3"> <img src="https://img.shields.io/npm/v/jordium-gantt-vue3?style=flat-square" alt="npm version"> </a> <a href="https://www.npmjs.com/package/jordium-gantt-vue3"> <
Readme
jordium-gantt-vue3
✨ 简介
jordium-gantt-vue3 是一个现代化的 Vue3 甘特图组件,内置资源视图与资源规划能力。在统一界面中同时管理任务、时间线以及资源分配,适用于项目计划与资源调度场景。
核心特性
- 📊资源计划视图 - Vue 3 生态唯一支持资源视图的甘特图,可按资源(人员/设备)展示任务分配与工时占用
- 📊 功能完整 - 任务管理、里程碑、依赖关系、进度追踪
- 🎨 主题系统 - 内置亮色/暗色主题,支持自定义样式
- 🖱️ 交互流畅 - 拖拽调整、缩放、双击编辑、右键菜单
- 🌍 国际化 - 内置中英文,可扩展其他语言
- ⚡ 高性能 - 虚拟滚动、懒加载,轻松处理大量数据
- 💎 类型安全 - 完整 TypeScript 支持
效果预览
亮色主题
暗色主题
📦 安装
使用你喜欢的包管理器安装:
# npm
npm install jordium-gantt-vue3
# yarn
yarn add jordium-gantt-vue3
# pnpm
pnpm add jordium-gantt-vue3🚀 快速开始
组件引入
在组件中引入 GanttChart 组件和样式:
<script setup lang="ts">
import { GanttChart } from 'jordium-gantt-vue3'
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
</script>提示: 样式文件只需在项目中引入一次即可,建议在
main.ts或根组件中引入。
第一个示例
创建你的第一个甘特图:
<template>
<div style="height: 600px;">
<GanttChart :tasks="tasks" :milestones="milestones" />
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { GanttChart } from 'jordium-gantt-vue3'
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
const tasks = ref([
{
id: 1,
name: '项目启动',
startDate: '2025-01-01',
endDate: '2025-01-10',
progress: 100,
},
{
id: 2,
name: '需求分析',
startDate: '2025-01-11',
endDate: '2025-01-20',
progress: 80,
predecessor: [1],
},
{
id: 3,
name: '系统设计',
startDate: '2025-01-21',
endDate: '2025-02-05',
progress: 50,
predecessor: [2],
},
])
const milestones = ref([
{
id: 101,
name: '项目立项',
startDate: '2025-01-01',
type: 'milestone',
},
])
</script>🎯 立即体验 Github在线Demo → 推荐使用 DOVE VPN,快速、稳定。 (注意:请合法使用 VPN 资源)
🌞 NPM包使用示例
请参考项目下的npm-demo,这是一个独立的项目,可以使用IDE单独浏览和启动,运行前请安装element plus以及jordium-gantt-vue3插件包
# npm
npm install element-plus
npm install jordium-gantt-vue3
npm run dev组件指南
GanttChart 组件
GanttChart 是组件库的核心入口,提供了完整的甘特图功能。
基础属性
| 属性名 | 类型 | 默认值 | 说明 |
| --------------------------- | ----------------------------------------------------------------------------------------- | ------- | -------------------------------------------------------------- |
| tasks | Task[] | [] | 任务数据数组 |
| milestones | Task[] | [] | 里程碑数据数组(注意:类型为 Task[],需设置 type='milestone') |
| resources |
Resource[] | [] | 资源数据数组(资源计划视图使用) |
| viewMode |
'task' \| 'resource' | 'task' | 视图模式:'task' 任务计划视图 | 'resource' 资源计划视图 |
| showToolbar | boolean | true | 是否显示工具栏 |
| useDefaultDrawer | boolean | true | 是否使用内置任务编辑抽屉(TaskDrawer) |
| useDefaultMilestoneDialog | boolean | true | 是否使用内置里程碑编辑对话框(MilestoneDialog) |
| autoSortByStartDate | boolean | false | 是否根据开始时间自动排序任务 |
| allowDragAndResize | boolean | true | 是否允许拖拽和调整任务/里程碑大小 |
| enableTaskRowMove | boolean | false | 是否允许拖拽和摆放TaskRow |
| enableTaskListContextMenu | boolean | true | 是否启用 TaskList(TaskRow)右键菜单功能。为 true 时:未声明 task-list-context-menu 插槽则使用内置菜单,声明了插槽则使用自定义菜单;为 false 时右键菜单完全禁用 |
| enableTaskBarContextMenu | boolean | true | 是否启用 TaskBar 右键菜单功能。为 true 时:未声明 task-bar-context-menu 插槽则使用内置菜单,声明了插槽则使用自定义菜单;为 false 时右键菜单完全禁用 |
| assigneeOptions | Array<{ key?: string \| number; value: string \| number; label: string }> | [] | 任务编辑抽屉中负责人下拉菜单的选项列表 |
| locale |
'zh-CN' \| 'en-US' | 'zh-CN' | 语言设置(响应式)。设置后组件内部语言将跟随变化 |
| theme |
'light' \| 'dark' | 'light' | 主题模式(响应式)。设置后组件主题将跟随变化 |
| timeScale |
'hour' \| 'day' \| 'week' \| 'month' \| 'quarter' \| 'year' | 'week' | 时间刻度(响应式)。设置后时间线刻度将跟随变化 |
| fullscreen |
boolean | false | 全屏状态控制(响应式)。设置后组件全屏状态将跟随变化 |
| expandAll |
boolean | true | 展开/收起所有任务(响应式)。设置后所有任务的展开状态将跟随变化 |
| enableLinkAnchor |
boolean | true | 是否开启Taskbar的关系线锚点,默认值: true |
| pendingTaskBackgroundColor |
string | '#409eff' | 待处理任务的TaskBar背景色。支持十六进制颜色值(如 '#409eff')。优先级:高于系统默认,低于 Task 对象的 barColor 属性 |
| delayTaskBackgroundColor |
string | '#f56c6c' | 已逾期任务的TaskBar背景色。支持十六进制颜色值(如 '#f56c6c')。优先级:高于系统默认,低于 Task 对象的 barColor 属性 |
| completeTaskBackgroundColor |
string | '#909399' | 已完成任务的TaskBar背景色。支持十六进制颜色值(如 '#909399')。优先级:高于系统默认,低于 Task 对象的 barColor 属性 |
| ongoingTaskBackgroundColor |
string | '#e6a23c' | 进行中任务的TaskBar背景色。支持十六进制颜色值(如 '#e6a23c')。优先级:高于系统默认,低于 Task 对象的 barColor 属性 |
| showActualTaskbar |
boolean | false | 是否显示实际TaskBar(在计划TaskBar下方显示实际执行进度) |
| enableTaskbarTooltip |
boolean | true | 是否启用TaskBar悬停提示框(鼠标悬停显示任务详情) |
| enableMilestoneTooltip |
boolean | true | 是否启用里程碑悬停提示框(鼠标悬停显示里程碑名称和日期) |
| showConflicts |
boolean | true | 是否显示资源冲突可视化层(资源视图下显示斜纹背景标识超载区域) |
| showTaskbarTab |
boolean | true | 是否显示TaskBar上的资源Tab标签(资源视图下TaskBar的资源分配标签) |
| enableTaskListCollapsible |
boolean | true | 是否允许折叠/展开 TaskList 面板。false 时强制隐藏 TaskList、SplitterBar 及折叠按钮,Timeline 独占全宽 |
| taskListVisible |
boolean | true | 控制 TaskList 的显隐状态(响应式)。仅在 enableTaskListCollapsible=true 时有效 |
| enableTaskDrawerAutoClose |
boolean | true | 是否允许 TaskDrawer 自动关闭(外总点击或按 Esc 时自动关闭)。设为 false 时禁用自动关闭,仅可通过内部按钮手动关闭 |
TaskListColumn 属性
TaskListColumn 组件用于在声明式模式(taskListColumnRenderMode="declarative")下定义任务列表的列。类似于 Element Plus 的 el-table-column 组件。
| 属性名 | 类型 | 默认值 | 说明 |
| ---------- | ------------------------------ | -------- | ---------------------------------------------------------------------------------------------------------- |
| prop | string | - | 列的属性名,用于访问任务对象的字段。例如:'name'、'assignee'、'progress' 等 |
| label | string | - | 列的显示标题文本 |
| width | number \| string | - | 列宽度。数字表示像素值(如 200),字符串支持百分比(如 '20%') |
| align | 'left' \| 'center' \| 'right' | 'left' | 列内容对齐方式 |
| cssClass | string | - | 自定义 CSS 类名,用于列样式定制 |
使用示例:
<GanttChart
:tasks="tasks"
task-list-column-render-mode="declarative"
>
<TaskListColumn prop="name" label="任务名称" width="300" />
<TaskListColumn prop="assignee" label="负责人" width="150" align="center" />
<TaskListColumn prop="progress" label="进度" width="100" align="center" />
<TaskListColumn prop="startDate" label="开始日期" width="140" />
<TaskListColumn prop="endDate" label="结束日期" width="140" />
</GanttChart>💡 提示:
TaskListColumn组件本身不渲染任何内容,仅用于声明列配置- 必须在
GanttChart组件内部使用,且设置task-list-column-render-mode="declarative"- 列的显示顺序由
TaskListColumn组件的声明顺序决定- 关于列内容自定义和插槽的详细使用方法,请参考 插槽 (Slots) 章节
TaskListContextMenu 属性
TaskListContextMenu 组件用于声明式定义 TaskList(TaskRow)的右键菜单。当 enableTaskListContextMenu 为 true 时生效。
| 属性名 | 类型 | 默认值 | 说明 |
| ---------- | ---------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------- |
| taskType | string \| string[] | undefined | 指定哪些任务类型显示此右键菜单。不设置时遵循现有逻辑(所有任务都显示),设置后仅对指定类型任务显示。支持单个类型(如 'task')或多个类型(如 ['task', 'milestone']) |
使用示例:
<GanttChart
:tasks="tasks"
:enable-task-list-context-menu="true"
>
<!-- 默认行为:所有任务都显示此右键菜单 -->
<TaskListContextMenu>
<template #default="scope">
<div class="custom-menu">
<div class="menu-item" @click="editTask(scope.row)">编辑</div>
<div class="menu-item" @click="deleteTask(scope.row)">删除</div>
</div>
</template>
</TaskListContextMenu>
<!-- 仅对 type='task' 的任务显示此菜单 -->
<TaskListContextMenu task-type="task">
<template #default="scope">
<div class="custom-menu">
<div class="menu-item">任务专属菜单</div>
</div>
</template>
</TaskListContextMenu>
<!-- 对多种类型显示菜单 -->
<TaskListContextMenu :task-type="['task', 'milestone']">
<template #default="scope">
<div class="custom-menu">
<div class="menu-item">任务和里程碑菜单</div>
</div>
</template>
</TaskListContextMenu>
</GanttChart>💡 提示:
TaskListContextMenu组件本身不渲染任何内容,仅用于声明菜单配置- 必须在
GanttChart组件内部使用,且设置enable-task-list-context-menu="true"- 菜单定位和显示状态由内部自动管理,用户只需关心菜单内容的 HTML 结构
- 菜单会在点击外部或滚动时自动关闭
- 关于插槽的详细使用方法,请参考 插槽 (Slots) 章节
TaskBarContextMenu 属性
TaskBarContextMenu 组件用于声明式定义 TaskBar(时间线任务条)的右键菜单。当 enableTaskBarContextMenu 为 true 时生效。
| 属性名 | 类型 | 默认值 | 说明 |
| ---------- | ---------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------- |
| taskType | string \| string[] | undefined | 指定哪些任务类型显示此右键菜单。不设置时遵循现有逻辑(所有任务都显示),设置后仅对指定类型任务显示。支持单个类型(如 'task')或多个类型(如 ['task', 'milestone']) |
使用示例:
<GanttChart
:tasks="tasks"
:enable-task-bar-context-menu="true"
>
<!-- 默认行为:所有任务都显示此右键菜单 -->
<TaskBarContextMenu>
<template #default="scope">
<div class="custom-menu">
<div class="menu-item" @click="extendTask(scope.row)">延长任务</div>
<div class="menu-item" @click="moveTask(scope.row)">移动任务</div>
</div>
</template>
</TaskBarContextMenu>
<!-- 仅对 type='task' 的任务显示此菜单 -->
<TaskBarContextMenu task-type="task">
<template #default="scope">
<div class="custom-menu">
<div class="menu-item">任务条专属菜单</div>
</div>
</template>
</TaskBarContextMenu>
<!-- 对多种类型显示菜单 -->
<TaskBarContextMenu :task-type="['task', 'story']">
<template #default="scope">
<div class="custom-menu">
<div class="menu-item">任务和故事菜单</div>
</div>
</template>
</TaskBarContextMenu>
</GanttChart>💡 提示:
TaskBarContextMenu组件本身不渲染任何内容,仅用于声明菜单配置- 必须在
GanttChart组件内部使用,且设置enable-task-bar-context-menu="true"- 菜单定位和显示状态由内部自动管理,用户只需关心菜单内容的 HTML 结构
- 菜单会在点击外部或滚动时自动关闭
- 关于插槽的详细使用方法,请参考 插槽 (Slots) 章节
配置对象属性
完整的配置对象说明请参考 ⚙️ 配置与扩展 章节。
| 属性名 | 类型 | 默认值 | 说明 |
| ---------------- | ---------------------------- | ----------------------------------------------------------------------- | ---------------- |
| toolbarConfig | ToolbarConfig | {} | 工具栏配置 |
| taskListConfig | TaskListConfig | undefined | 任务列表配置 |
| resourceListConfig |
ResourceListConfig | undefined | 资源列表配置 |
| taskBarConfig | TaskBarConfig | undefined | 任务条样式配置 |
| localeMessages | Partial<Messages['zh-CN']> | undefined | 自定义多语言配置 |
| workingHours | WorkingHours | { morning: { start: 8, end: 11 }, afternoon: { start: 13, end: 17 } } | 工作时间配置 |
回调函数属性
| 属性名 | 类型 | 说明 |
| -------------------- | ------------------------------------ | -------------------------------------------------------- |
| onTodayLocate | () => void | 工具栏"今天"按钮点击回调 |
| onExportCsv | () => boolean \| void | 工具栏"导出CSV"按钮点击回调,返回 false 可阻止默认导出 |
| onExportPdf | () => void | 工具栏"导出PDF"按钮点击回调 |
| onLanguageChange | (lang: 'zh-CN' \| 'en-US') => void | 语言切换回调 |
| onThemeChange | (isDark: boolean) => void | 主题切换回调 |
| onFullscreenChange | (isFullscreen: boolean) => void | 全屏切换回调 |
| onExpandAll | () => void | 工具栏"全部展开"按钮点击回调 |
| onCollapseAll | () => void | 工具栏"全部折叠"按钮点击回调 |
组件事件(Events)
完整的事件说明请分别参考:
事件列表总览:
| 事件名 | 参数 | 说明 |
| ------------------------ | --------------------------------- | -------------------------- |
| add-task | - | 点击工具栏"添加任务"按钮 |
| task-click | (task: Task, event: MouseEvent) | 点击任务 |
| task-double-click | (task: Task) | 双击任务 |
| task-added | { task: Task } | 任务添加后触发 |
| task-updated | { task: Task } | 任务更新后触发 |
| task-deleted | { task: Task } | 任务删除后触发 |
| taskbar-drag-end | (task: Task) | 拖拽任务结束 |
| taskbar-resize-end | (task: Task) | 调整任务大小结束 |
| predecessor-added | { targetTask, newTask } | 添加前置任务 |
| successor-added | { targetTask, newTask } | 添加后置任务 |
| timer-started | (task: Task) | 任务计时器启动 |
| timer-stopped | (task: Task) | 任务计时器停止 |
| add-milestone | - | 点击工具栏"添加里程碑"按钮 |
| milestone-saved | (milestone: Task) | 里程碑保存 |
| milestone-deleted | { milestoneId: number } | 里程碑删除 |
| milestone-icon-changed | { milestoneId, icon } | 里程碑图标变更 |
| milestone-drag-end | (milestone: Task) | 拖拽里程碑结束 |
| task-row-moved | payload: { draggedTask: Task, targetTask: Task, position: 'after' \| 'child', oldParent: Task \| null, newParent: Task \| null } | 拖拽TaskRow结束(可选) |
| taskbar-resource-change |
payload: { task: Task, oldResourceId: string \| number, newResourceId: string \| number } | 任务跨资源移动事件(资源视图下拖拽任务到另一资源行) |
示例1:最简单的甘特图
<template>
<div style="height: 600px;">
<GanttChart :tasks="tasks" :assignee-options="assigneeOptions" />
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { GanttChart } from 'jordium-gantt-vue3'
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
const tasks = ref([
{
id: 1,
name: '任务1',
startDate: '2025-01-01',
endDate: '2025-01-10',
progress: 100,
},
])
const assigneeOptions = ref([
{ value: 'zhangsan', label: '张三' },
{ value: 'lisi', label: '李四' },
{ value: 'wangwu', label: '王五' },
])
</script>示例2:带里程碑的甘特图
<template>
<div style="height: 600px;">
<GanttChart :tasks="tasks" :milestones="milestones" :assignee-options="assigneeOptions" />
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { GanttChart } from 'jordium-gantt-vue3'
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
const tasks = ref([
{
id: 1,
name: '项目启动',
startDate: '2025-01-01',
endDate: '2025-01-10',
progress: 100,
},
])
const milestones = ref([
{
id: 101,
name: '项目立项',
startDate: '2025-01-01',
type: 'milestone',
icon: 'diamond',
},
])
const assigneeOptions = ref([
{ value: 'zhangsan', label: '张三' },
{ value: 'lisi', label: '李四' },
{ value: 'wangwu', label: '王五' },
])
</script>示例3:隐藏工具栏,自定义控制按钮绑定事件
<template>
<div>
<!-- 自定义控制栏 -->
<div class="custom-toolbar">
<button @click="addTask">新增任务</button>
<button @click="addMilestone">新增里程碑</button>
</div>
<!-- 甘特图组件,隐藏内置工具栏 -->
<div style="height: 600px;">
<GanttChart
:tasks="tasks"
:milestones="milestones"
:show-toolbar="false"
:assignee-options="assigneeOptions"
@task-added="handleTaskAdded"
@milestone-saved="handleMilestoneSaved"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { GanttChart } from 'jordium-gantt-vue3'
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
const tasks = ref([])
const milestones = ref([])
const assigneeOptions = ref([
{ value: 'zhangsan', label: '张三' },
{ value: 'lisi', label: '李四' },
{ value: 'wangwu', label: '王五' },
])
const addTask = () => {
const newTask = {
id: Date.now(),
name: '新任务',
startDate: new Date().toISOString().split('T')[0],
endDate: new Date().toISOString().split('T')[0],
progress: 0,
}
tasks.value.push(newTask)
}
const addMilestone = () => {
const newMilestone = {
id: Date.now(),
name: '新里程碑',
startDate: new Date().toISOString().split('T')[0],
type: 'milestone',
}
milestones.value.push(newMilestone)
}
const handleTaskAdded = e => {
console.log('任务已添加:', e.task)
}
const handleMilestoneSaved = milestone => {
console.log('里程碑已保存:', milestone)
}
</script>示例4:外部组件控制状态(TimeScale、Fullscreen、Expand/Collapse、Locale、Theme)
通过响应式Props绑定来控制组件状态,组件状态会自动跟随Props变化。
<template>
<div>
<!-- 外部控制面板 -->
<div class="control-panel">
<button @click="propsFullscreen = !propsFullscreen">切换全屏</button>
<button @click="propsExpandAll = !propsExpandAll">展开/收起所有</button>
<button @click="propsLocale = 'zh-CN'">中文</button>
<button @click="propsLocale = 'en-US'">English</button>
<button @click="propsTimeScale = 'day'">日视图</button>
<button @click="propsTimeScale = 'week'">周视图</button>
<button @click="propsTimeScale = 'month'">月视图</button>
<button @click="propsTheme = 'light'">亮色主题</button>
<button @click="propsTheme = 'dark'">暗色主题</button>
</div>
<!-- 甘特图组件 -->
<div style="height: 600px;">
<GanttChart
:tasks="tasks"
:milestones="milestones"
:locale="propsLocale"
:theme="propsTheme"
:time-scale="propsTimeScale"
:fullscreen="propsFullscreen"
:expand-all="propsExpandAll"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { GanttChart } from 'jordium-gantt-vue3'
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
const tasks = ref([
{ id: 1, name: '任务1', startDate: '2025-01-01', endDate: '2025-01-10', progress: 50 },
{ id: 2, name: '任务2', startDate: '2025-01-05', endDate: '2025-01-15', progress: 30 },
])
const milestones = ref([])
// Props控制变量
const propsLocale = ref<'zh-CN' | 'en-US'>('zh-CN')
const propsTheme = ref<'light' | 'dark'>('light')
const propsTimeScale = ref<'hour' | 'day' | 'week' | 'month' | 'quarter' | 'year'>('week')
const propsFullscreen = ref(false)
const propsExpandAll = ref(false)
</script>任务管理
任务是甘特图的核心元素,组件提供了完整的任务 CRUD 操作支持,包括添加、编辑、删除任务,以及丰富的交互事件。
Task 数据结构
| 字段名 | 类型 | 必填 | 默认值 | 说明 |
| ------------------ | ---------- | ---- | ----------- | ------------------------------------------------------------------------------------------------------------------------------- |
| id | number | ✅ | - | 任务唯一标识符 |
| name | string | ✅ | - | 任务名称 |
| startDate | string | - | - | 开始日期,格式:'YYYY-MM-DD' 或 'YYYY-MM-DD HH:mm' |
| endDate | string | - | - | 结束日期,格式:'YYYY-MM-DD' 或 'YYYY-MM-DD HH:mm' |
| progress | number | - | 0 | 任务进度,范围 0-100 |
| predecessor | number[] | - | - | 前置任务 ID 数组,标准格式:[1, 2, 3]兼容格式:也支持字符串 '1,2,3' 或字符串数组 ['1', '2', '3'],组件会自动解析 |
| assignee | string | string[] | - | - | 任务负责人,用作负责人下拉菜单的值绑定。支持单个负责人(字符串)或多个负责人(字符串数组) |
|
assigneeName | string | string[] | - | - | 任务负责人姓名,自动从绑定的数据集
assigneeOptions中获取Label作为显示,如果需要自定义,可以在GanttChart回调事件task-added中自定义信息。支持单个姓名(字符串)或多个姓名(字符串数组) |
| avatar | string | string[] | - | - | 任务负责人头像 URL。支持单个头像(字符串)或多个头像(字符串数组) |
|
estimatedHours | number | - | - | 预估工时(小时) |
| actualHours | number | - | - | 实际工时(小时) |
| parentId | number | - | - | 父任务 ID,用于任务分组 |
| children | Task[] | - | - | 子任务数组 |
| collapsed | boolean | - | false | 子任务是否折叠 |
| isParent | boolean | - | - | 是否为父任务 |
| type | string | - | - | 任务类型,'milestone' 表示里程碑,'milestone-group' 表示里程碑分组 |
| description | string | - | - | 任务描述 |
| icon | string | - | 'diamond' | 任务图标(用于里程碑),可选值:'diamond', 'flag', 'star', 'rocket' 等 |
| level | number | - | 0 | 任务层级(自动计算) |
| isTimerRunning | boolean | - | false | 计时器是否运行中 |
| timerStartTime | number | - | - | 计时开始时间(时间戳) |
| timerEndTime | number | - | - | 计时结束时间(时间戳) |
| timerStartDesc | string | - | - | 计时开始时填写的描述 |
| timerElapsedTime | number | - | 0 | 已计时的时长(毫秒) |
| isEditable | boolean | - | true | 单个任务是否可编辑(可拖拽、拉伸),优先级高于全局 allowDragAndResize |
| [key: string] | unknown | - | - | 支持自定义属性扩展,可添加任意额外字段 |
自定义属性扩展:Task 接口支持添加任意自定义字段,例如:
priority、tags、status、department等业务相关字段。前置任务字段说明:
- 标准格式(推荐):
predecessor: [1, 2, 3]- number 数组- 兼容格式1:
predecessor: '1,2,3'- 逗号分隔的字符串- 兼容格式2:
predecessor: ['1', '2', '3']- 字符串数组- 组件内部会自动将所有格式统一解析为 number 数组
- 无前置任务:使用空数组
[]、空字符串''或不设置该字段
任务相关属性
| 属性名 | 类型 | 默认值 | 说明 |
| --------------------- | ---------------- | ----------- | -------------------------------------------------------------- |
| tasks | Task[] | [] | 任务数据数组 |
| useDefaultDrawer | boolean | true | 是否使用内置的任务编辑抽屉(TaskDrawer) |
| taskBarConfig | TaskBarConfig | {} | 任务条样式配置,详见 TaskBarConfig 配置 |
| taskListConfig | TaskListConfig | undefined | 任务列表配置,详见 TaskListConfig 配置 |
| autoSortByStartDate | boolean | false | 是否根据开始时间自动排序任务 |
| enableTaskRowMove | boolean | false | 是否允许拖拽和摆放TaskRow |
| assigneeOptions | Array<{ key?: string \| number; value: string \| number; label: string }> | [] | 任务编辑抽屉中负责人下拉菜单的选项列表 |
| taskListColumnRenderMode | 'default' \| 'declarative' | 'default' | 任务列表列渲染模式。'default':使用 TaskListColumnConfig 配置(兼容模式,将逐渐废弃);'declarative':使用 TaskListColumn 组件声明式定义列(推荐)。详见 TaskListColumn 声明式列定义 |
| taskListRowClassName | string \| ((task: Task) => string) | undefined | 自定义任务行的 CSS 类名。可以是字符串或返回字符串的函数。注意:行的高度由组件内部统一管理,自定义高度样式不会生效 |
| taskListRowStyle | CSSProperties \| ((task: Task) => CSSProperties) | undefined | 自定义任务行的内联样式。可以是样式对象或返回样式对象的函数。注意:行的高度和宽度由组件内部统一管理,自定义宽高样式不会生效 |
配置说明:
- 默认模式:
useDefaultDrawer=true(默认),双击任务自动打开内置 TaskDrawer - 自定义编辑器:
useDefaultDrawer=false禁用内置抽屉,监听@task-double-click事件打开自定义编辑器 - 只读模式:
useDefaultDrawer=false且不监听@task-double-click事件,用户双击任务无反应
任务事件
💡 事件驱动架构:组件采用纯事件驱动设计,所有用户操作(添加、编辑、删除、拖拽等)都会触发对应事件,方便外部监听和处理。
| 事件名 | 参数 | 触发时机 | 说明 |
| -------------------- | ----------------------------------------- | -------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| add-task | - | 点击工具栏"添加任务"按钮时 | 可用于自定义新增任务逻辑。如 useDefaultDrawer=true,组件会自动打开内置 TaskDrawer |
| task-click | (task: Task, event: MouseEvent) => void | 点击任务条时 | 单击任务触发 |
| task-double-click | (task: Task) => void | 双击任务条时 | 双击任务时始终触发。useDefaultDrawer=true 时组件会额外打开内置编辑器,false 时不打开。事件触发与属性值无关 |
| task-added | { task: Task } | 任务添加后 | 通过内置 TaskDrawer 添加任务后触发。注意:组件已自动更新 tasks 数据,外部只需监听此事件做额外处理(如调用 API 保存) |
| task-updated | { task: Task } | 任务更新后 | 通过内置 TaskDrawer 或拖拽更新任务后触发。注意:组件已自动更新 tasks 数据,外部只需监听此事件做额外处理 |
| task-deleted | { task: Task } | 任务删除后 | 通过内置 TaskDrawer 删除任务后触发。注意:组件已自动更新 tasks 数据,外部只需监听此事件做额外处理 |
| taskbar-drag-end | (task: Task) => void | 拖拽任务条结束时 | 任务位置变化,startDate 和 endDate 已更新。注意:组件已自动更新 tasks 数据 |
| taskbar-resize-end | (task: Task) => void | 调整任务条大小结束时 | 任务时长变化,endDate 已更新。注意:组件已自动更新 tasks 数据 |
| predecessor-added | { targetTask: Task, newTask: Task } | 通过右键菜单添加前置任务后 | targetTask 是被添加前置任务的任务,newTask 是新创建的前置任务 |
| successor-added | { targetTask: Task, newTask: Task } | 通过右键菜单添加后置任务后 | targetTask 是原任务,newTask 是新创建的后置任务(其 predecessor 已包含 targetTask.id) |
| timer-started | (task: Task) => void | 任务计时器启动时 | 开始记录任务工时 |
| timer-stopped | (task: Task) => void | 任务计时器停止时 | 停止记录任务工时 |
| task-row-moved | payload: { draggedTask: Task, targetTask: Task, position: 'after' \| 'child', oldParent: Task \| null, newParent: Task \| null } | 拖拽TaskRow结束(可选) | 组件已自动完成数据移动和TaskList/Timeline同步。监听此事件为完全可选,仅用于显示提示、调用API保存等。position: 'after'=同级放置,'child'=作为子任务 |
数据同步说明:
- ✅ 组件内部自动更新:所有任务的增删改操作,组件都会自动更新
props.tasks数据 - ✅ 事件仅做通知:外部监听事件主要用于:显示提示消息、调用后端 API、更新其他相关数据等
- ❌ 避免重复操作:不要在事件处理器中再次修改
tasks数据,否则会导致重复更新
示例1:基础任务操作
<template>
<div style="height: 600px;">
<GanttChart
:tasks="tasks"
:assignee-options="assigneeOptions"
@add-task="handleAddTask"
@task-added="handleTaskAdded"
@task-updated="handleTaskUpdated"
@task-deleted="handleTaskDeleted"
@task-click="handleTaskClick"
@taskbar-drag-end="handleTaskDragEnd"
/>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { GanttChart } from 'jordium-gantt-vue3'
import type { Task } from 'jordium-gantt-vue3'
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
const tasks = ref<Task[]>([
{
id: 1,
name: '项目规划',
startDate: '2025-01-01',
endDate: '2025-01-10',
progress: 100,
assignee: '张三',
estimatedHours: 40,
},
{
id: 2,
name: '需求分析',
startDate: '2025-01-11',
endDate: '2025-01-20',
progress: 60,
assignee: '李四',
predecessor: [1], // 依赖任务1
},
])
const assigneeOptions = ref([
{ value: 'zhangsan', label: '张三' },
{ value: 'lisi', label: '李四' },
{ value: 'wangwu', label: '王五' },
])
// 工具栏"添加任务"按钮点击事件
const handleAddTask = () => {
console.log('准备新增任务...')
// 组件会自动打开 TaskDrawer(如果 useDefaultDrawer=true)
// 也可以在这里执行自定义逻辑,如显示提示消息
}
// 任务添加事件(通过内置抽屉添加)
const handleTaskAdded = (e: { task: Task }) => {
console.log('新增任务:', e.task)
// 注意:组件已自动将任务添加到 tasks 数组
// 这里只需调用后端 API 保存即可
// await api.createTask(e.task)
}
// 任务更新事件(通过内置抽屉或拖拽更新)
const handleTaskUpdated = (e: { task: Task }) => {
console.log('更新任务:', e.task)
// 注意:组件已自动更新 tasks 数组中的任务数据
// 这里只需调用后端 API 更新即可
// await api.updateTask(e.task.id, e.task)
}
// 任务删除事件
const handleTaskDeleted = (e: { task: Task }) => {
console.log('删除任务:', e.task)
// 注意:组件已自动从 tasks 数组中移除任务
// 这里只需调用后端 API 删除即可
// await api.deleteTask(e.task.id)
}
// 点击任务事件
const handleTaskClick = (task: Task) => {
console.log('点击任务:', task.name)
}
// 拖拽任务结束事件
const handleTaskDragEnd = (task: Task) => {
console.log('任务拖拽完成,新日期:', task.startDate, '至', task.endDate)
// 可以在这里调用后端 API 保存新的日期
}
</script>示例2:任务依赖关系(前置任务/后置任务)
任务可以通过 predecessor 字段配置前置任务,组件会自动绘制依赖关系连线:
<template>
<GanttChart
:tasks="tasks"
:assignee-options="assigneeOptions"
@predecessor-added="handlePredecessorAdded"
@successor-added="handleSuccessorAdded"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { GanttChart } from 'jordium-gantt-vue3'
import type { Task } from 'jordium-gantt-vue3'
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
const tasks = ref<Task[]>([
{
id: 1,
name: '需求分析',
startDate: '2025-01-01',
endDate: '2025-01-10',
progress: 100,
predecessor: [], // 无前置任务
},
{
id: 2,
name: '系统设计',
startDate: '2025-01-11',
endDate: '2025-01-20',
progress: 80,
predecessor: [1], // 依赖任务1(需求分析)
},
{
id: 3,
name: '数据库设计',
startDate: '2025-01-11',
endDate: '2025-01-18',
progress: 90,
predecessor: [1], // 依赖任务1
},
{
id: 4,
name: '前端开发',
startDate: '2025-01-21',
endDate: '2025-02-10',
progress: 60,
predecessor: [2], // 依赖任务2(系统设计)
},
{
id: 5,
name: '后端开发',
startDate: '2025-01-19',
endDate: '2025-02-08',
progress: 70,
predecessor: [2, 3], // 同时依赖任务2和3
},
{
id: 6,
name: '集成测试',
startDate: '2025-02-11',
endDate: '2025-02-20',
progress: 30,
predecessor: [4, 5], // 依赖前端和后端开发完成
},
])
const assigneeOptions = ref([
{ value: 'zhangsan', label: '张三' },
{ value: 'lisi', label: '李四' },
{ value: 'wangwu', label: '王五' },
])
// 通过右键菜单添加前置任务时触发
const handlePredecessorAdded = (event: { targetTask: Task; newTask: Task }) => {
console.log(`任务 [${event.targetTask.name}] 添加了前置任务 [${event.newTask.name}]`)
// 组件会自动更新 targetTask 的 predecessor 数组(追加新任务 ID)
// 这里可以调用后端 API 保存依赖关系
// await api.addTaskDependency(event.targetTask.id, event.newTask.id)
}
// 通过右键菜单添加后置任务时触发
const handleSuccessorAdded = (event: { targetTask: Task; newTask: Task }) => {
console.log(`任务 [${event.targetTask.name}] 添加了后置任务 [${event.newTask.name}]`)
// 组件会自动更新 newTask 的 predecessor 数组(将 targetTask.id 添加进去)
// 这里可以调用后端 API 保存依赖关系
// await api.addTaskDependency(event.newTask.id, event.targetTask.id)
}
</script>依赖关系说明:
predecessor字段支持多种格式:- 标准格式(推荐):
[1, 2, 3]- number 数组 - 兼容格式1:
'1,2,3'- 逗号分隔的字符串 - 兼容格式2:
['1', '2', '3']- 字符串数组 - 组件会自动解析所有格式
- 标准格式(推荐):
- 前置任务:必须先完成的任务(例如:设计完成后才能开发)
- 后置任务:依赖当前任务的任务(当前任务是其他任务的前置任务)
- 组件会自动绘制依赖关系连线,从前置任务指向依赖它的任务
- 可以通过内置右键菜单添加/删除前置任务和后置任务
- 内置菜单删除任务时,组件会自动清理相关的依赖关系引用
- 无前置任务:使用空数组
[]、空字符串''或不设置predecessor字段
示例3:隐藏工具栏,使用自定义按钮触发事件
适用于需要完全自定义控制栏的场景:
<template>
<div>
<!-- 自定义控制栏 -->
<div class="custom-toolbar">
<button @click="triggerAddTask">新增任务</button>
<button @click="triggerAddMilestone">新增里程碑</button>
<!-- 其他自定义按钮... -->
</div>
<!-- 甘特图组件,隐藏内置工具栏 -->
<GanttChart
:tasks="tasks"
:milestones="milestones"
:show-toolbar="false"
:use-default-drawer="true"
:use-default-milestone-dialog="true"
:assignee-options="assigneeOptions"
@add-task="handleAddTask"
@add-milestone="handleAddMilestone"
@task-added="handleTaskAdded"
/>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { GanttChart } from 'jordium-gantt-vue3'
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
const tasks = ref([])
const milestones = ref([])
const assigneeOptions = ref([
{ value: 'zhangsan', label: '张三' },
{ value: 'lisi', label: '李四' },
{ value: 'wangwu', label: '王五' },
])
// 自定义按钮触发事件(组件会响应并打开内置编辑器)
const triggerAddTask = () => {
// 直接触发组件的 add-task 事件
// 由于 useDefaultDrawer=true,组件会自动打开 TaskDrawer
}
const triggerAddMilestone = () => {
// 直接触发组件的 add-milestone 事件
// 由于 useDefaultMilestoneDialog=true,组件会自动打开 MilestoneDialog
}
// 监听事件处理逻辑
const handleAddTask = () => {
console.log('准备新增任务(由自定义按钮触发)')
}
const handleAddMilestone = () => {
console.log('准备新增里程碑(由自定义按钮触发)')
}
const handleTaskAdded = e => {
console.log('任务已添加:', e.task)
// 调用 API 保存...
}
</script>💡 灵活性设计:
- 显示工具栏 + 默认编辑器:最简单的开箱即用方式
- 隐藏工具栏 + 自定义按钮 + 默认编辑器:自定义控制栏样式,保留默认编辑功能
- 隐藏工具栏 + 自定义按钮 + 自定义编辑器:完全自定义所有交互逻辑
示例4:任务行拖拽排序
允许用户通过拖拽 TaskRow 来调整任务的层级关系和前后顺序:
<template>
<div style="height: 600px;">
<GanttChart
:tasks="tasks"
:enable-task-row-move="true"
:assignee-options="assigneeOptions"
@task-row-moved="handleTaskRowMoved"
/>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { GanttChart } from 'jordium-gantt-vue3'
import type { Task } from 'jordium-gantt-vue3'
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
const tasks = ref<Task[]>([
{
id: 1,
name: '项目规划',
startDate: '2025-01-01',
endDate: '2025-01-10',
progress: 100,
},
{
id: 2,
name: '需求分析',
startDate: '2025-01-11',
endDate: '2025-01-20',
progress: 60,
parentId: 1,
},
{
id: 3,
name: '系统设计',
startDate: '2025-01-21',
endDate: '2025-01-30',
progress: 40,
},
])
const assigneeOptions = ref([
{ value: 'zhangsan', label: '张三' },
{ value: 'lisi', label: '李四' },
{ value: 'wangwu', label: '王五' },
])
// 任务行拖拽完成事件(可选)
const handleTaskRowMoved = async (payload: {
draggedTask: Task
targetTask: Task
position: 'after' | 'child'
oldParent: Task | null
newParent: Task | null
}) => {
const { draggedTask, targetTask, position, oldParent, newParent } = payload
// 组件已自动完成任务移动、parentId更新和TaskList/Timeline同步
// 监听此事件为完全可选,仅用于:
// 1. 显示自定义提示消息
const oldParentName = oldParent?.name || '根目录'
const newParentName = newParent?.name || '根目录'
const positionText = position === 'after' ? '在目标任务之后' : '作为目标任务的子任务'
showMessage(`任务 [${draggedTask.name}] 已从 [${oldParentName}] 移动到 [${newParentName}] (${positionText})`, 'success')
// 2. 调用后端 API 保存新的任务层级关系
try {
await api.updateTaskHierarchy({
taskId: draggedTask.id,
targetTaskId: targetTask.id,
position: position,
oldParentId: oldParent?.id,
newParentId: newParent?.id,
})
} catch (error) {
console.error('保存任务层级失败:', error)
showMessage('保存失败,请刷新页面', 'error')
}
// 3. 触发其他业务逻辑(如更新关联数据、记录操作日志等)
// ...
}
</script>拖拽排序说明:
- 启用拖拽:设置
enable-task-row-move="true"启用任务行拖拽功能(默认为false) - 拖拽算法(组件内部自动执行):
- 算法1(放置在后面):当目标任务没有子任务时,被拖拽的任务会放置在目标任务之后(同级),
position='after' - 算法2(作为子任务):当目标任务有子任务时,被拖拽的任务会成为目标任务的第一个子任务,
position='child'
- 算法1(放置在后面):当目标任务没有子任务时,被拖拽的任务会放置在目标任务之后(同级),
- 视觉反馈:
- 拖拽时会显示半透明的跟随元素
- 悬停在有效目标任务上时显示蓝色边框提示
- 无子任务的任务显示蓝色底部边框
- 有子任务的任务显示蓝色四周边框
- 自动同步:组件内部通过对象引用直接修改
props.tasks,自动完成任务移动、parentId更新、children数组调整以及 TaskList/Timeline 同步 - 事件监听(可选):
task-row-moved事件为完全可选,仅用于显示提示、调用API保存、记录日志等额外处理- 无需手动更新
tasks.value,组件已自动完成数据同步
- 事件参数:
draggedTask: 被拖拽的任务targetTask: 目标任务position: 放置位置('after' 或 'child')oldParent: 原父任务(null 表示根目录)newParent: 新父任务(null 表示根目录)
- 限制条件:
- 不能拖拽到自己身上
- 不能拖拽到自己的子任务上(避免循环引用)
- 里程碑和里程碑分组不能被拖拽
资源管理 
资源管理用于管理项目中的人力或设备资源,支持资源视图下的任务分配、资源负载分析、冲突检测等功能。通过 viewMode="resource" 属性切换到资源计划视图。
核心特性:
- 📊 资源视图:按资源维度展示任务分配情况
- 🎯 负载分析:实时显示资源占用率和超载状态
- ⚠️ 冲突检测:自动检测资源时间冲突(如 A任务:40% + B任务:40% + C任务:30% = 110%超载)
- 🎨 可视化:斜纹背景标识冲突区域,资源Tab显示占用比例
- 🔄 跨资源移动:支持拖拽任务到不同资源行进行重新分配
视图限制:
- ❌ 任务关系线禁用:资源视图下不显示任务之间的前后置关系线,因为资源视图关注资源分配而非任务依赖关系
- ❌ 不支持实际TaskBar:
showActualTaskbar属性在资源视图下无效,不会显示实际执行进度条
Resource 数据结构
| 字段名 | 类型 | 必填 | 默认值 | 说明 |
| --------------- | ------------------- | ---- | ------ | ----------------------------------------------------------------------------------------------- |
| id | string \| number | ✅ | - | 资源唯一标识符 |
| name | string | ✅ | - | 资源名称(如人名、设备名) |
| type | string | - | - | 资源类型(如 'developer', 'designer', 'device' 等) |
| avatar | string | - | - | 资源头像 URL |
| description | string | - | - | 资源描述 |
| department | string | - | - | 所属部门 |
| skills | string[] | - | - | 技能标签数组(如 ['Vue', 'React', 'TypeScript']) |
| capacity | number | - | - | 资源容量/利用率(0-100),可用于表示资源的整体负载水平 |
| color | string | - | - | 自定义资源行左边框颜色(如 '#ff5733'),若不设置则使用默认颜色方案 |
| tasks | Task[] | - | [] | 分配给该资源的任务数组,每个任务需包含 resources 字段标注资源占用比例 |
| [key: string] | unknown | - | - | 支持自定义属性扩展,可添加任意额外字段 |
自定义属性扩展:Resource 接口支持添加任意自定义字段,例如:
phone、location、workHours等业务相关字段。任务资源关联说明:
- 每个 Resource 包含一个
tasks数组,存储分配给该资源的任务- 每个 Task 应包含
resources字段,标注该任务使用了哪些资源及占用比例- 资源占用比例格式:
task.resources = [{ id: 'resource1', capacity: 60 }, { id: 'resource2', capacity: 40 }]capacity范围:20-100,表示该任务占用该资源的百分比- 冲突检测:当同一资源在同一时间段的多个任务
capacity总和 > 100% 时,会显示冲突警告
Resource 数据示例:
import type { Resource, Task } from 'jordium-gantt-vue3'
const resources: Resource[] = [
{
id: 'dev-001',
name: '张三',
type: 'developer',
avatar: '/avatars/zhangsan.jpg',
department: '研发部',
skills: ['Vue', 'TypeScript', 'Node.js'],
capacity: 85, // 整体负载水平
color: '#409eff',
tasks: [
{
id: 1,
name: '前端开发',
startDate: '2026-02-01',
endDate: '2026-02-10',
progress: 50,
resources: [
{ id: 'dev-001', capacity: 60 }, // 该任务占用张三60%的时间
{ id: 'dev-002', capacity: 40 } // 同时占用李四40%的时间
]
},
{
id: 2,
name: '代码审查',
startDate: '2026-02-05',
endDate: '2026-02-08',
progress: 0,
resources: [
{ id: 'dev-001', capacity: 40 } // 该任务占用张三40%的时间
]
}
// 注意:如果两个任务时间重叠,张三在2月5-8日的总占用率为100%(60%+40%),临界值
]
},
{
id: 'dev-002',
name: '李四',
type: 'developer',
avatar: '/avatars/lisi.jpg',
department: '研发部',
skills: ['React', 'TypeScript'],
tasks: []
}
]资源冲突检测示例:
// 场景:张三在同一时间段被分配了3个任务
const resource = {
id: 'dev-001',
name: '张三',
tasks: [
{
id: 1,
name: '任务A',
startDate: '2026-02-10',
endDate: '2026-02-15',
resources: [{ id: 'dev-001', capacity: 40 }] // 占用40%
},
{
id: 2,
name: '任务B',
startDate: '2026-02-10',
endDate: '2026-02-20',
resources: [{ id: 'dev-001', capacity: 40 }] // 占用40%
},
{
id: 3,
name: '任务C',
startDate: '2026-02-12',
endDate: '2026-02-18',
resources: [{ id: 'dev-001', capacity: 30 }] // 占用30%
}
]
}
// 冲突分析:
// - 2月10-11日:A(40%) + B(40%) = 80%,未超载
// - 2月12-15日:A(40%) + B(40%) + C(30%) = 110%,超载!显示冲突警告
// - 2月16-18日:B(40%) + C(30%) = 70%,未超载
// - 2月19-20日:B(40%),未超载资源相关属性
| 属性名 | 类型 | 默认值 | 说明 |
| --------------------- | --------------------- | ------------ | --------------------------------------------------------------------------- |
| resources | Resource[] | [] | 资源数据数组 |
| viewMode | 'task' \| 'resource' | 'task' | 视图模式:'task' 任务计划视图,'resource' 资源计划视图 |
| resourceListConfig | ResourceListConfig | undefined | 资源列表配置,类似 TaskListConfig,用于配置资源列表的列定义、宽度等 |
| showConflicts | boolean | true | 是否显示资源冲突可视化层(资源视图下显示斜纹背景标识超载区域) |
| showTaskbarTab | boolean | true | 是否显示TaskBar上的资源Tab标签(资源视图下TaskBar上的资源占用比例标签) |
资源事件
| 事件名 | 参数 | 触发时机 | 说明 |
| -------------------------- | ---------------------------------------------------------------------------------------- | ---------------------------- | ---------------------------------------------------------------------------- |
| taskbar-resource-change | payload: { task: Task, oldResourceId: string \| number, newResourceId: string \| number } | 任务跨资源拖拽结束时 | 资源视图下拖拽任务到另一资源行时触发,组件已自动更新任务的 resources 字段 |
数据同步说明:
- ✅ 组件内部自动更新:资源相关操作(如任务跨资源移动)组件会自动更新
props.resources和任务的resources字段 - ✅ 事件仅做通知:外部监听事件主要用于:显示提示消息、调用后端 API、更新其他相关数据等
- ❌ 避免重复操作:不要在事件处理器中再次修改数据,否则会导致重复更新
示例:资源视图基础用法
<template>
<div style="height: 600px;">
<GanttChart
:resources="resources"
view-mode="resource"
:show-conflicts="true"
:show-taskbar-tab="true"
@taskbar-resource-change="handleTaskbarResourceChange"
/>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { GanttChart } from 'jordium-gantt-vue3'
import type { Resource } from 'jordium-gantt-vue3'
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
const resources = ref<Resource[]>([
{
id: 'dev-001',
name: '张三',
type: 'developer',
department: '研发部',
tasks: [
{
id: 1,
name: '前端开发',
startDate: '2026-02-01',
endDate: '2026-02-10',
progress: 50,
resources: [{ id: 'dev-001', capacity: 60 }]
}
]
}
])
const handleTaskbarResourceChange = (payload: any) => {
console.log('任务资源变更:', payload)
// 调用后端API保存资源分配变更
// api.updateTaskResource(payload.task.id, payload.newResourceId)
}
</script>里程碑管理
里程碑用于标记项目中的重要时间节点,如项目启动、阶段完成、产品发布等。组件提供了灵活的里程碑编辑配置,默认使用内置的 MilestoneDialog,也支持完全自定义编辑行为。
注意: 里程碑与任务是独立的数据集合,不存在直接关联关系。里程碑通过
milestones属性独立管理。
Milestone 数据结构
| 字段名 | 类型 | 必填 | 默认值 | 说明 |
| ------------- | -------- | ---- | ------------- | ---------------------------------------------------------- |
| id | number | ✅ | - | 里程碑唯一标识符 |
| name | string | ✅ | - | 里程碑名称 |
| startDate | string | ✅ | - | 里程碑日期,格式:'YYYY-MM-DD' 或 'YYYY-MM-DD HH:mm' |
| endDate | string | - | - | 结束日期(通常里程碑不需要,自动设置为与 startDate 相同) |
| assignee | string | - | - | 负责人 |
| type | string | ✅ | 'milestone' | 类型标识,必须设为 'milestone' |
| icon | string | - | 'diamond' | 里程碑图标,可选值:'diamond', 'flag', 'star', 'rocket' 等 |
| description | string | - | - | 里程碑描述 |
注意:
milestones属性的类型为Task[],需要确保每个里程碑对象的type字段设置为'milestone'。
里程碑相关属性
| 属性名 | 类型 | 默认值 | 说明 |
| --------------------------- | --------- | ------ | -------------------------------------------------------- |
| milestones | Task[] | [] | 里程碑数据数组(类型为 Task[],需确保 type='milestone') |
| useDefaultMilestoneDialog | boolean | true | 是否使用内置的里程碑编辑对话框(MilestoneDialog) |
配置说明:
- 默认模式:
useDefaultMilestoneDialog=true(默认),双击里程碑自动打开内置 MilestoneDialog - 禁用编辑器:
useDefaultMilestoneDialog=false,双击里程碑无反应(组件不打开任何编辑器) - 自定义编辑器:可以监听
onMilestoneDoubleClick回调或相关事件,实现自定义编辑逻辑
💡 里程碑与任务的区别:
- 里程碑数据通过
milestones属性独立管理,与tasks分开- 里程碑对象的
type字段必须设置为'milestone'- 里程碑不支持子任务、依赖关系等复杂结构
- 里程碑主要用于标记关键时间节点
里程碑回调函数(向后兼容)
⚠️ 已废弃:请使用新的事件驱动 API(见下方"里程碑事件"章节)
里程碑事件
💡 事件驱动架构:里程碑管理采用事件驱动设计,推荐使用事件 API 替代回调函数。
| 事件名 | 参数 | 触发时机 | 说明 |
| ------------------------ | --------------------------------------- | ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| add-milestone | - | 点击工具栏"添加里程碑"按钮时 | 可用于自定义新增里程碑逻辑。如 useDefaultMilestoneDialog=true,组件会自动打开内置 MilestoneDialog |
| milestone-saved | (milestone: Task) => void | 里程碑保存后(新增或编辑) | 通过内置 MilestoneDialog 保存里程碑后触发。注意:组件已自动更新 milestones 数据,外部只需监听此事件做额外处理(如调用 API 保存) |
| milestone-deleted | { milestoneId: number } | 里程碑删除后 | 通过内置 MilestoneDialog 删除里程碑后触发。注意:组件已自动更新 milestones 数据,外部只需监听此事件做额外处理 |
| milestone-icon-changed | { milestoneId: number, icon: string } | 里程碑图标变更后 | 通过内置 MilestoneDialog 修改图标后触发 |
| milestone-drag-end | (milestone: Task) => void | 拖拽里程碑结束时 | 里程碑日期已更新。注意:组件已自动更新 milestones 数据 |
数据同步说明:
- ✅ 组件内部自动更新:所有里程碑的增删改操作,组件都会自动更新
props.milestones数据 - ✅ 事件仅做通知:外部监听事件主要用于:显示提示消息、调用后端 API、更新其他相关数据等
- ❌ 避免重复操作:不要在事件处理器中再次修改
milestones数据,否则会导致重复更新
示例1:使用事件驱动 API(推荐)
使用新的事件 API,组件会自动管理数据,更加简洁:
<template>
<div style="height: 600px;">
<GanttChart
:milestones="milestones"
@add-milestone="handleAddMilestone"
@milestone-saved="handleMilestoneSaved"
@milestone-deleted="handleMilestoneDeleted"
@milestone-icon-changed="handleMilestoneIconChanged"
@milestone-drag-end="handleMilestoneDrag"
/>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { GanttChart } from 'jordium-gantt-vue3'
import type { Task } from 'jordium-gantt-vue3'
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
const milestones = ref<Task[]>([
{
id: 101,
name: '项目启动',
startDate: '2025-01-01',
type: 'milestone',
icon: 'diamond',
assignee: '项目经理',
description: '项目正式启动',
},
{
id: 102,
name: '需求评审',
startDate: '2025-01-15',
type: 'milestone',
icon: 'flag',
},
])
// 工具栏"添加里程碑"按钮点击事件
const handleAddMilestone = () => {
console.log('准备新增里程碑...')
// 组件会自动打开 MilestoneDialog(如果 useDefaultMilestoneDialog=true)
}
// 里程碑保存事件(添加或编辑)
const handleMilestoneSaved = (milestone: Task) => {
console.log('里程碑已保存:', milestone)
// 注意:组件已自动更新 milestones 数组
// 这里只需调用后端 API 保存即可
// await api.saveMilestone(milestone)
}
// 里程碑删除事件
const handleMilestoneDeleted = (e: { milestoneId: number }) => {
console.log('里程碑已删除, ID:', e.milestoneId)
// 注意:组件已自动从 milestones 数组中移除
// 这里只需调用后端 API 删除即可
// await api.deleteMilestone(e.milestoneId)
}
// 里程碑图标变更事件
const handleMilestoneIconChanged = (e: { milestoneId: number; icon: string }) => {
console.log('里程碑图标已变更:', e.milestoneId, '->', e.icon)
// 组件已自动更新图标,这里可以调用 API 保存
// await api.updateMilestoneIcon(e.milestoneId, e.icon)
}
// 拖拽里程碑结束事件
const handleMilestoneDrag = (milestone: Task) => {
console.log('里程碑拖拽完成,新日期:', milestone.startDate)
// 组件已自动更新日期,这里可以调用 API 保存
// await api.updateMilestoneDate(milestone.id, milestone.startDate)
}
</script>示例2:使用自定义里程碑编辑对话框
如果需要完全自定义里程碑编辑界面,可以禁用内置对话框并使用自己的组件:
<template>
<div style="height: 600px;">
<GanttChart
:milestones="milestones"
:use-default-milestone-dialog="false"
@add-milestone="handleAddMilestone"
@milestone-saved="handleMilestoneSaved"
@milestone-deleted="handleMilestoneDeleted"
@milestone-drag-end="handleMilestoneDrag"
/>
<!-- 自定义里程碑编辑对话框 -->
<CustomMilestoneDialog
v-model:visible="customDialogVisible"
:milestone="editingMilestone"
:is-new="isNewMilestone"
@save="handleCustomDialogSave"
@delete="handleCustomDialogDelete"
/>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { GanttChart } from 'jordium-gantt-vue3'
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
import CustomMilestoneDialog from './CustomMilestoneDialog.vue'
import type { Task } from 'jordium-gantt-vue3'
const milestones = ref<Task[]>([
{
id: 101,
name: '项目启动',
startDate: '2025-01-01',
type: 'milestone',
icon: 'diamond',
assignee: '项目经理',
description: '项目正式启动',
},
])
const customDialogVisible = ref(false)
const editingMilestone = ref<Task | null>(null)
const isNewMilestone = ref(false)
// 点击工具栏"添加里程碑"按钮
const handleAddMilestone = () => {
editingMilestone.value = null
isNewMilestone.value = true
customDialogVisible.value = true
}
// 双击里程碑时打开自定义对话框
// 注意:需要监听 Timeline 组件的里程碑双击事件
// 或者通过外部按钮/列表项触发编辑
const openEditDialog = (milestone: Task) => {
editingMilestone.value = { ...milestone }
isNewMilestone.value = false
customDialogVisible.value = true
}
// 自定义对话框保存事件
const handleCustomDialogSave = (milestone: Task) => {
if (isNewMilestone.value) {
// 新增里程碑
const newMilestone = {
...milestone,
id: Date.now(), // 生成新 ID
type: 'milestone',
}
milestones.value.push(newMilestone)
// 调用后端 API 保存
// await api.createMilestone(newMilestone)
} else {
// 更新现有里程碑
const index = milestones.value.findIndex(m => m.id === milestone.id)
if (index !== -1) {
milestones.value[index] = { ...milestone }
}
// 调用后端 API 更新
// await api.updateMilestone(milestone)
}
customDialogVisible.value = false
}
// 自定义对话框删除事件
const handleCustomDialogDelete = (milestoneId: number) => {
const index = milestones.value.findIndex(m => m.id === milestoneId)
if (index !== -1) {
milestones.value.splice(index, 1)
}
// 调用后端 API 删除
// await api.deleteMilestone(milestoneId)
customDialogVisible.value = false
}
// 以下事件处理器仍然有效(用于拖拽等操作)
const handleMilestoneSaved = (milestone: Task) => {
console.log('里程碑已保存(通过其他方式):', milestone)
}
const handleMilestoneDeleted = (e: { milestoneId: number }) => {
console.log('里程碑已删除(通过其他方式):', e.milestoneId)
}
const handleMilestoneDrag = (milestone: Task) => {
console.log('里程碑拖拽完成:', milestone.startDate)
// 调用 API 更新日期
// await api.updateMilestoneDate(milestone.id, milestone.startDate)
}
</script>自定义对话框组件示例 (CustomMilestoneDialog.vue - 使用 Element Plus):
注意:以下示例使用 Element Plus UI 框架。你也可以使用其他 UI 框架(如 Ant Design Vue、Naive UI 等)或原生 HTML 实现。
<template>
<el-dialog
v-model="dialogVisible"
:title="isNew ? '新增里程碑' : '编辑里程碑'"
width="500px"
@close="handleClose"
>
<el-form :model="form" label-width="100px">
<el-form-item label="里程碑名称">
<el-input v-model="form.name" placeholder="请输入里程碑名称" />
</el-form-item>
<el-form-item label="日期">
<el-date-picker
v-model="form.startDate"
type="date"
placeholder="选择日期"
value-format="YYYY-MM-DD"
/>
</el-form-item>
<el-form-item label="负责人">
<el-input v-model="form.assignee" placeholder="请输入负责人" />
</el-form-item>
<el-form-item label="图标">
<el-select v-model="form.icon" placeholder="选择图标">
<el-option label="钻石" value="diamond" />
<el-option label="旗帜" value="flag" />
<el-option label="星星" value="star" />
<el-option label="火箭" value="rocket" />
</el-select>
</el-form-item>
<el-form-item label="描述">
<el-input v-model="form.description" type="textarea" :rows="3" placeholder="请输入描述" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button v-if="!isNew" type="danger" @click="handleDelete"> 删除 </el-button>
<el-button @click="handleClose">取消</el-button>
<el-button type="primary" @click="handleSave">保存</el-button>
</div>
</template>
</el-dialog>
</template>
<script 