
Company News
Socket Named Top Sales Organization by RepVue
Socket won two 2026 Reppy Awards from RepVue, ranking in the top 5% of all sales orgs. AE Alexandra Lister shares what it's like to grow a sales career here.
jordium-gantt-vue3
Advanced tools
A Vue 3 Gantt chart component for project management, task scheduling, resource planning and timeline visualization.
ไธญๆ | English | Release Notes
A modern Vue 3 Gantt chart component library with built-in resource view and resource planning capabilities, providing complete solutions for project management, task scheduling, and resource allocation
๐ฑ Gitee Live Demo ย ย |ย ย ๐ฑ Github Live Demo ย ย |ย ย ๐ฆ GitHub ย ย |ย ย ๐ฆ Gitee ย ย |ย ย ๐ npm
jordium-gantt-vue3 is a modern Gantt chart component for Vue3 with built-in Resource View and Resource Planning capabilities. Manage tasks, timelines, and resource allocation in one unified interface, ideal for project scheduling and workforce planning.
Install using your preferred package manager:
# npm
npm install jordium-gantt-vue3
# yarn
yarn add jordium-gantt-vue3
# pnpm
pnpm add jordium-gantt-vue3
Import the GanttChart component and styles:
<script setup lang="ts">
import { GanttChart } from 'jordium-gantt-vue3'
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
</script>
Tip: The style file only needs to be imported once in your project. It's recommended to import it in
main.tsor the root component.
Create your first Gantt chart:
<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: 'Project Kickoff',
startDate: '2025-01-01',
endDate: '2025-01-10',
progress: 100,
},
{
id: 2,
name: 'Requirements Analysis',
startDate: '2025-01-11',
endDate: '2025-01-20',
progress: 80,
predecessor: [1],
},
{
id: 3,
name: 'System Design',
startDate: '2025-01-21',
endDate: '2025-02-05',
progress: 50,
predecessor: [2],
},
])
const milestones = ref([
{
id: 101,
name: 'Project Approval',
startDate: '2025-01-01',
type: 'milestone',
},
])
</script>
๐ฏ Try Live Demo on Github โ Recommended: DOVE VPN for fast and stable access. (Note: Please use VPN services legally)
Please refer to the npm-demo folder in the project. It is a standalone project that can be opened and run independently using your IDE. Before running, make sure to install the Element Plus library and the jordium-gantt-vue3 plugin package.
# npm
npm install element-plus
npm install jordium-gantt-vue3
npm run dev
GanttChart is the core entry point of the library, providing complete Gantt chart functionality.
| Prop | Type | Default | Description |
|---|---|---|---|
tasks | Task[] | [] | Array of task data |
milestones | Task[] | [] | Array of milestone data (Note: Type is Task[], must set type='milestone') |
resources | Resource[] | [] | Array of resource data (used in resource planning view) |
viewMode | 'task' | 'resource' | 'task' | View mode: 'task' for task planning view | 'resource' for resource planning view |
showToolbar | boolean | true | Whether to show the toolbar |
useDefaultDrawer | boolean | true | Whether to use the built-in task edit drawer (TaskDrawer) |
useDefaultMilestoneDialog | boolean | true | Whether to use the built-in milestone edit dialog (MilestoneDialog) |
autoSortByStartDate | boolean | false | Whether to automatically sort tasks by start date |
allowDragAndResize | boolean | true | Whether to allow dragging and resizing tasks/milestones |
enableTaskRowMove | boolean | false | Whether to allow dragging and dropping TaskRow |
enableTaskListContextMenu | boolean | true | Whether to enable TaskList (TaskRow) context menu. When true: uses built-in menu if task-list-context-menu slot is not declared, uses custom menu if slot is declared; when false: context menu is completely disabled |
enableTaskBarContextMenu | boolean | true | Whether to enable TaskBar context menu. When true: uses built-in menu if task-bar-context-menu slot is not declared, uses custom menu if slot is declared; when false: context menu is completely disabled |
assigneeOptions | Array<{ key?: string | number; value: string | number; label: string }> | [] | Assignee dropdown options in task edit drawer |
locale | 'zh-CN' | 'en-US' | 'zh-CN' | Language setting (reactive). Component's internal language will follow changes |
theme | 'light' | 'dark' | 'light' | Theme mode (reactive). Component's theme will follow changes |
timeScale | 'hour' | 'day' | 'week' | 'month' | 'quarter' | 'year' | 'week' | Time scale (reactive). Timeline scale will follow changes |
fullscreen | boolean | false | Fullscreen state control (reactive). Component's fullscreen state will follow changes |
expandAll | boolean | true | Expand/collapse all tasks (reactive). All tasks' expand state will follow changes |
enableLinkAnchor | boolean | true | Whether to enable Link Anchor๏ผDefault: true |
pendingTaskBackgroundColor | string | '#409eff' | Background color for pending tasks' TaskBar. Supports hex color values (e.g., '#409eff'). Priority: Higher than system default, lower than Task object's barColor property |
delayTaskBackgroundColor | string | '#f56c6c' | Background color for overdue tasks' TaskBar. Supports hex color values (e.g., '#f56c6c'). Priority: Higher than system default, lower than Task object's barColor property |
completeTaskBackgroundColor | string | '#909399' | Background color for completed tasks' TaskBar. Supports hex color values (e.g., '#909399'). Priority: Higher than system default, lower than Task object's barColor property |
ongoingTaskBackgroundColor | string | '#e6a23c' | Background color for ongoing tasks' TaskBar. Supports hex color values (e.g., '#e6a23c'). Priority: Higher than system default, lower than Task object's barColor property |
showActualTaskbar | boolean | false | Whether to display actual TaskBar (shows actual execution progress below planned TaskBar) |
enableTaskbarTooltip | boolean | true | Whether to enable TaskBar hover tooltip (shows task details on mouse hover) |
enableMilestoneTooltip | boolean | true | Whether to enable milestone hover tooltip (shows milestone name and date on mouse hover) |
showConflicts | boolean | true | Whether to display resource conflict visualization layer (shows diagonal stripe background for overload zones in resource view) |
showTaskbarTab | boolean | true | Whether to display resource tab on TaskBar (shows resource allocation label on TaskBar in resource view) |
enableTaskListCollapsible | boolean | true | Whether to allow collapsing/expanding the TaskList panel. When false: forcibly hides TaskList, SplitterBar and collapse button; Timeline takes full width |
taskListVisible | boolean | true | Controls TaskList visibility (reactive). Only effective when enableTaskListCollapsible=true |
enableTaskDrawerAutoClose | boolean | true | Whether to allow TaskDrawer to auto-close (closes on outside click or Esc key). Set to false to disable auto-close โ the drawer can only be closed via its internal close button |
The TaskListColumn component is used to define task list columns in declarative mode (taskListColumnRenderMode="declarative"). Similar to Element Plus's el-table-column component.
| Prop | Type | Default | Description |
|---|---|---|---|
prop | string | - | Column property name, used to access task object fields. Examples: 'name', 'assignee', 'progress', etc. |
label | string | - | Column header display text |
width | number | string | - | Column width. Number represents pixels (e.g., 200), string supports percentage (e.g., '20%') |
align | 'left' | 'center' | 'right' | 'left' | Column content alignment |
cssClass | string | - | Custom CSS class name for column styling |
Usage Example:
<GanttChart
:tasks="tasks"
task-list-column-render-mode="declarative"
>
<TaskListColumn prop="name" label="Task Name" width="300" />
<TaskListColumn prop="assignee" label="Assignee" width="150" align="center" />
<TaskListColumn prop="progress" label="Progress" width="100" align="center" />
<TaskListColumn prop="startDate" label="Start Date" width="140" />
<TaskListColumn prop="endDate" label="End Date" width="140" />
</GanttChart>
๐ก Tips:
- The
TaskListColumncomponent itself does not render any content, it only declares column configuration- Must be used inside the
GanttChartcomponent withtask-list-column-render-mode="declarative"set- Column display order is determined by the declaration order of
TaskListColumncomponents- For detailed column content customization and slot usage, see Slots section
The TaskListContextMenu component is used to declaratively define the context menu for TaskList (TaskRow). Takes effect when enableTaskListContextMenu is true.
| Prop | Type | Default | Description |
|---|---|---|---|
taskType | string | string[] | undefined | Specifies which task types should display this context menu. When not set, follows existing logic (all tasks show menu). When set, only specified types show menu. Supports single type (e.g., 'task') or multiple types (e.g., ['task', 'milestone']) |
Usage Examples:
<GanttChart
:tasks="tasks"
:enable-task-list-context-menu="true"
>
<!-- Default behavior: all tasks show this context menu -->
<TaskListContextMenu>
<template #default="scope">
<div class="custom-menu">
<div class="menu-item" @click="editTask(scope.row)">Edit</div>
<div class="menu-item" @click="deleteTask(scope.row)">Delete</div>
</div>
</template>
</TaskListContextMenu>
<!-- Only show for tasks with type='task' -->
<TaskListContextMenu task-type="task">
<template #default="scope">
<div class="custom-menu">
<div class="menu-item">Task Specific Menu</div>
</div>
</template>
</TaskListContextMenu>
<!-- Show for multiple types -->
<TaskListContextMenu :task-type="['task', 'milestone']">
<template #default="scope">
<div class="custom-menu">
<div class="menu-item">Task and Milestone Menu</div>
</div>
</template>
</TaskListContextMenu>
</GanttChart>
๐ก Tips:
- The
TaskListContextMenucomponent itself does not render any content, it only declares menu configuration- Must be used inside the
GanttChartcomponent withenable-task-list-context-menu="true"set- Menu positioning and visibility are automatically managed internally, users only need to focus on menu content HTML structure
- Menu automatically closes when clicking outside or scrolling
- For detailed slot usage, see Slots section
The TaskBarContextMenu component is used to declaratively define the context menu for TaskBar (timeline task bars). Takes effect when enableTaskBarContextMenu is true.
| Prop | Type | Default | Description |
|---|---|---|---|
taskType | string | string[] | undefined | Specifies which task types should display this context menu. When not set, follows existing logic (all tasks show menu). When set, only specified types show menu. Supports single type (e.g., 'task') or multiple types (e.g., ['task', 'milestone']) |
Usage Examples:
<GanttChart
:tasks="tasks"
:enable-task-bar-context-menu="true"
>
<!-- Default behavior: all tasks show this context menu -->
<TaskBarContextMenu>
<template #default="scope">
<div class="custom-menu">
<div class="menu-item" @click="extendTask(scope.row)">Extend Task</div>
<div class="menu-item" @click="moveTask(scope.row)">Move Task</div>
</div>
</template>
</TaskBarContextMenu>
<!-- Only show for tasks with type='task' -->
<TaskBarContextMenu task-type="task">
<template #default="scope">
<div class="custom-menu">
<div class="menu-item">Task Bar Specific Menu</div>
</div>
</template>
</TaskBarContextMenu>
<!-- Show for multiple types -->
<TaskBarContextMenu :task-type="['task', 'story']">
<template #default="scope">
<div class="custom-menu">
<div class="menu-item">Task and Story Menu</div>
</div>
</template>
</TaskBarContextMenu>
</GanttChart>
๐ก Tips:
- The
TaskBarContextMenucomponent itself does not render any content, it only declares menu configuration- Must be used inside the
GanttChartcomponent withenable-task-bar-context-menu="true"set- Menu positioning and visibility are automatically managed internally, users only need to focus on menu content HTML structure
- Menu automatically closes when clicking outside or scrolling
- For detailed slot usage, see Slots section
For complete configuration object documentation, see โ๏ธ Configuration & Customization section.
| Prop | Type | Default | Description |
|---|---|---|---|
toolbarConfig | ToolbarConfig | {} | Toolbar configuration |
taskListConfig | TaskListConfig | undefined | Task list configuration |
resourceListConfig | ResourceListConfig | undefined | Resource list configuration |
taskBarConfig | TaskBarConfig | undefined | Task bar style configuration |
localeMessages | Partial<Messages['zh-CN']> | undefined | Custom localization messages |
workingHours | WorkingHours | { morning: { start: 8, end: 11 }, afternoon: { start: 13, end: 17 } } | Working hours configuration |
scaleConfigs | { [scale: TimelineScale]?: ScaleConfigOption } | undefined | Custom display configuration per time scale (cell width, header formatters, buffers, etc.), see scaleConfigs (Timeline Scale Configuration) |
| Prop | Type | Description |
|---|---|---|
onTodayLocate | () => void | Toolbar "Today" button click callback |
onExportCsv | () => boolean | void | Toolbar "Export CSV" button click callback, return false to prevent default export |
onExportPdf | () => void | Toolbar "Export PDF" button click callback |
onLanguageChange | (lang: 'zh-CN' | 'en-US') => void | Language switch callback |
onThemeChange | (isDark: boolean) => void | Theme switch callback |
onFullscreenChange | (isFullscreen: boolean) => void | Fullscreen toggle callback |
onExpandAll | () => void | Toolbar "Expand All" button click callback |
onCollapseAll | () => void | Toolbar "Collapse All" button click callback |
For complete event documentation, see:
Event List Overview:
| Event Name | Parameters | Description |
|---|---|---|
add-task | - | Clicked toolbar "Add Task" button |
task-click | (task: Task, event: MouseEvent) | Clicked task |
task-double-click | (task: Task) | Double-clicked task |
task-added | { task: Task } | Triggered after task added |
task-updated | { task: Task } | Triggered after task updated |
task-deleted | { task: Task } | Triggered after task deleted |
taskbar-drag-end | (task: Task) | Task drag ended |
taskbar-resize-end | (task: Task) | Task resize ended |
predecessor-added | { targetTask, newTask } | Added predecessor task |
successor-added | { targetTask, newTask } | Added successor task |
timer-started | (task: Task) | Task timer started |
timer-stopped | (task: Task) | Task timer stopped |
add-milestone | - | Clicked toolbar "Add Milestone" button |
milestone-saved | (milestone: Task) | Milestone saved |
milestone-deleted | { milestoneId: number } | Milestone deleted |
milestone-icon-changed | { milestoneId, icon } | Milestone icon changed |
milestone-drag-end | (milestone: Task) | Milestone drag ended |
task-row-moved | payload: { draggedTask: Task, targetTask: Task, position: 'after' | 'child', oldParent: Task | null, newParent: Task | null } | TaskRow drag ended (optional) |
taskbar-resource-change | payload: { task: Task, oldResourceId: string | number, newResourceId: string | number } | Task moved across resources (dragging task to another resource row in resource view) |
<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: 'Task 1',
startDate: '2025-01-01',
endDate: '2025-01-10',
progress: 100,
},
])
const assigneeOptions = ref([
{ value: 'alice', label: 'Alice' },
{ value: 'bob', label: 'Bob' },
{ value: 'charlie', label: 'Charlie' },
])
</script>
<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: 'Project Kickoff',
startDate: '2025-01-01',
endDate: '2025-01-10',
progress: 100,
},
])
const milestones = ref([
{
id: 101,
name: 'Project Approval',
startDate: '2025-01-01',
type: 'milestone',
icon: 'diamond',
},
])
const assigneeOptions = ref([
{ value: 'alice', label: 'Alice' },
{ value: 'bob', label: 'Bob' },
{ value: 'charlie', label: 'Charlie' },
])
</script>
<template>
<div>
<!-- Custom toolbar -->
<div class="custom-toolbar">
<button @click="addTask">Add Task</button>
<button @click="addMilestone">Add Milestone</button>
</div>
<!-- Gantt chart component with hidden built-in toolbar -->
<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: 'alice', label: 'Alice' },
{ value: 'bob', label: 'Bob' },
{ value: 'charlie', label: 'Charlie' },
])
const addTask = () => {
const newTask = {
id: Date.now(),
name: 'New Task',
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: 'New Milestone',
startDate: new Date().toISOString().split('T')[0],
type: 'milestone',
}
milestones.value.push(newMilestone)
}
const handleTaskAdded = e => {
console.log('Task added:', e.task)
}
const handleMilestoneSaved = milestone => {
console.log('Milestone saved:', milestone)
}
</script>
Control component state through reactive Props binding. Component state will automatically follow Props changes.
<template>
<div>
<!-- External control panel -->
<div class="control-panel">
<button @click="propsFullscreen = !propsFullscreen">Toggle Fullscreen</button>
<button @click="propsExpandAll = !propsExpandAll">Expand/Collapse All</button>
<button @click="propsLocale = 'zh-CN'">ไธญๆ</button>
<button @click="propsLocale = 'en-US'">English</button>
<button @click="propsTimeScale = 'day'">Day View</button>
<button @click="propsTimeScale = 'week'">Week View</button>
<button @click="propsTimeScale = 'month'">Month View</button>
<button @click="propsTheme = 'light'">Light Theme</button>
<button @click="propsTheme = 'dark'">Dark Theme</button>
</div>
<!-- Gantt chart component -->
<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: 'Task 1', startDate: '2025-01-01', endDate: '2025-01-10', progress: 50 },
{ id: 2, name: 'Task 2', startDate: '2025-01-05', endDate: '2025-01-15', progress: 30 },
])
const milestones = ref([])
// Props control variables
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>
Tasks are the core elements of the Gantt chart. The component provides complete CRUD operation support for tasks, including adding, editing, deleting tasks, and rich interactive events.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
id | number | โ | - | Unique task identifier |
name | string | โ | - | Task name |
startDate | string | - | - | Start date, format: 'YYYY-MM-DD' or 'YYYY-MM-DD HH:mm' |
endDate | string | - | - | End date, format: 'YYYY-MM-DD' or 'YYYY-MM-DD HH:mm' |
progress | number | - | 0 | Task progress, range 0-100 |
predecessor | number[] | - | - | Array of predecessor task IDs, standard format: [1, 2, 3]Compatible formats: Also supports string '1,2,3' or string array ['1', '2', '3'], component will auto-parse |
assignee | string | string[] | - | - | Task assignee, used as the value binding for the assignee dropdown menu. Supports single assignee (string) or multiple assignees (string array) |
assigneeName | string | string[] | - | - | Task assignee name, automatically obtained from the label in the bound assigneeOptions dataset; for custom display, you can set it in the task-added callback event of GanttChart. Supports single name (string) or multiple names (string array) |
avatar | string | string[] | - | - | Avatar URL of task assignee. Supports single avatar (string) or multiple avatars (string array) |
estimatedHours | number | - | - | Estimated hours |
actualHours | number | - | - | Actual hours |
parentId | number | - | - | Parent task ID, used for task grouping |
children | Task[] | - | - | Array of child tasks |
collapsed | boolean | - | false | Whether child tasks are collapsed |
isParent | boolean | - | - | Whether this is a parent task |
type | string | - | - | Task type, 'milestone' for milestone, 'milestone-group' for milestone group |
description | string | - | - | Task description |
icon | string | - | 'diamond' | Task icon (for milestones), options: 'diamond', 'flag', 'star', 'rocket', etc. |
level | number | - | 0 | Task level (auto-calculated) |
isTimerRunning | boolean | - | false | Whether timer is running |
timerStartTime | number | - | - | Timer start time (timestamp) |
timerEndTime | number | - | - | Timer end time (timestamp) |
timerStartDesc | string | - | - | Description filled when timer starts |
timerElapsedTime | number | - | 0 | Elapsed time (milliseconds) |
isEditable | boolean | - | true | Whether individual task is editable (draggable, resizable), overrides global allowDragAndResize |
[key: string] | unknown | - | - | Supports custom property extensions, can add any additional fields |
Custom Property Extensions: The Task interface supports adding arbitrary custom fields, such as:
priority,tags,status,department, and other business-related fields.Predecessor Field Notes:
- Standard format (recommended):
predecessor: [1, 2, 3]- number array Compatible format 1:predecessor: '1,2,3'- comma-separated string- Compatible format 2:
predecessor: ['1', '2', '3']- string array- Component will automatically parse all formats into number array
- No predecessors: use empty array
[], empty string'', or don't set this field
| Prop | Type | Default | Description |
|---|---|---|---|
tasks | Task[] | [] | Array of task data |
useDefaultDrawer | boolean | true | Whether to use built-in task edit drawer (TaskDrawer) |
taskBarConfig | TaskBarConfig | {} | Task bar style configuration, see TaskBarConfig Configuration |
taskListConfig | TaskListConfig | undefined | Task list configuration, see TaskListConfig Configuration |
autoSortByStartDate | boolean | false | Whether to automatically sort tasks by start date |
enableTaskRowMove | boolean | false | Whether to alloww dragging and dropping TaskRow |
assigneeOptions | Array<{ key?: string | number; value: string | number; label: string }> | [] | Assignee dropdown options in task edit drawer |
taskListColumnRenderMode | 'default' | 'declarative' | 'default' | Task list column render mode. 'default': Use TaskListColumnConfig configuration (compatibility mode, will be gradually deprecated); 'declarative': Use TaskListColumn component for declarative column definition (recommended). See TaskListColumn Declarative Column Definition |
taskListRowClassName | string | ((task: Task) => string) | undefined | Custom CSS class name for task rows. Can be a string or a function that returns a string. Note: Row height is managed internally by the component, custom height styles will not take effect |
taskListRowStyle | CSSProperties | ((task: Task) => CSSProperties) | undefined | Custom inline styles for task rows. Can be a style object or a function that returns a style object. Note: Row height and width are managed internally by the component, custom width/height styles will not take effect |
Configuration Notes:
useDefaultDrawer=true (default), double-click task to auto-open built-in TaskDraweruseDefaultDrawer=false disables built-in drawer, listen to @task-double-click event to open custom editoruseDefaultDrawer=false and don't listen to @task-double-click event, user double-click task has no response๐ก Event-Driven Architecture: Component adopts pure event-driven design. All user operations (add, edit, delete, drag, etc.) will trigger corresponding events for easy external listening and handling.
| Event Name | Parameters | When Triggered | Description |
|---|---|---|---|
add-task | - | When clicking toolbar "Add Task" button | Can be used for custom add task logic. If useDefaultDrawer=true, component will auto-open built-in TaskDrawer |
task-click | (task: Task, event: MouseEvent) => void | When clicking task bar | Triggered on single-click task |
task-double-click | (task: Task) => void | When double-clicking task bar | Double-click task always triggers. When useDefaultDrawer=true, component will additionally open built-in editor; when false, won't open. Event triggering is independent of property value |
task-added | { task: Task } | After task added | Triggered after adding task via built-in TaskDrawer. Note: Component has auto-updated tasks data, external only needs to listen to this event for additional processing (like calling API to save) |
task-updated | { task: Task } | After task updated | Triggered after updating task via built-in TaskDrawer or drag. Note: Component has auto-updated tasks data, external only needs to listen to this event for additional processing |
task-deleted | { task: Task } | After task deleted | Triggered after deleting task via built-in TaskDrawer. Note: Component has auto-updated tasks data, external only needs to listen to this event for additional processing |
taskbar-drag-end | (task: Task) => void | When task bar drag ends | Task position changed, startDate and endDate updated. Note: Component has auto-updated tasks data |
taskbar-resize-end | (task: Task) => void | When task bar resize ends | Task duration changed, endDate updated. Note: Component has auto-updated tasks data |
predecessor-added | { targetTask: Task, newTask: Task } | After adding predecessor via context menu | targetTask is the task to which predecessor is added, newTask is the newly created predecessor task |
successor-added | { targetTask: Task, newTask: Task } | After adding successor via context menu | targetTask is the original task, newTask is the newly created successor task (its predecessor already contains targetTask.id) |
timer-started | (task: Task) => void | When task timer starts | Start recording task hours |
timer-stopped | (task: Task) => void | When task timer stops | Stop recording task hours |
task-row-moved | payload: { draggedTask: Task, targetTask: Task, position: 'after' | 'child', oldParent: Task | null, newParent: Task | null } | TaskRow drag ended (optional) | Component has automatically completed data movement and TaskList/Timeline sync via object reference mutation. Listening to this event is completely optional, only for showing messages, calling API, etc. position: 'after'=same level, 'child'=as child |
Data Synchronization Notes:
props.tasks datatasks data again in event handlers, otherwise it will cause duplicate updates<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: 'Project Planning',
startDate: '2025-01-01',
endDate: '2025-01-10',
progress: 100,
assignee: 'Alice',
estimatedHours: 40,
},
{
id: 2,
name: 'Requirements Analysis',
startDate: '2025-01-11',
endDate: '2025-01-20',
progress: 60,
assignee: 'Bob',
predecessor: [1], // Depends on task 1
},
])
const assigneeOptions = ref([
{ value: 'alice', label: 'Alice' },
{ value: 'bob', label: 'Bob' },
{ value: 'charlie', label: 'Charlie' },
])
// Toolbar "Add Task" button click event
const handleAddTask = () => {
console.log('Preparing to add task...')
// Component will auto-open TaskDrawer (if useDefaultDrawer=true)
// Can also execute custom logic here, like showing messages
}
// Task add event (added via built-in drawer)
const handleTaskAdded = (e: { task: Task }) => {
console.log('Task added:', e.task)
// Note: Component has auto-added task to tasks array
// Only need to call backend API to save here
// await api.createTask(e.task)
}
// Task update event (updated via built-in drawer or drag)
const handleTaskUpdated = (e: { task: Task }) => {
console.log('Task updated:', e.task)
// Note: Component has auto-updated task data in tasks array
// Only need to call backend API to update here
// await api.updateTask(e.task.id, e.task)
}
// Task delete event
const handleTaskDeleted = (e: { task: Task }) => {
console.log('Task deleted:', e.task)
// Note: Component has auto-removed task from tasks array
// Only need to call backend API to delete here
// await api.deleteTask(e.task.id)
}
// Task click event
const handleTaskClick = (task: Task) => {
console.log('Clicked task:', task.name)
}
// Task drag end event
const handleTaskDragEnd = (task: Task) => {
console.log('Task drag completed, new dates:', task.startDate, 'to', task.endDate)
// Can call backend API here to save new dates
}
</script>
Tasks can configure predecessors via the predecessor field, and the component will automatically draw dependency lines:
<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: 'Requirements Analysis',
startDate: '2025-01-01',
endDate: '2025-01-10',
progress: 100,
predecessor: [], // No predecessors
},
{
id: 2,
name: 'System Design',
startDate: '2025-01-11',
endDate: '2025-01-20',
progress: 80,
predecessor: [1], // Depends on task 1 (Requirements Analysis)
},
{
id: 3,
name: 'Database Design',
startDate: '2025-01-11',
endDate: '2025-01-18',
progress: 90,
predecessor: [1], // Depends on task 1
},
{
id: 4,
name: 'Frontend Development',
startDate: '2025-01-21',
endDate: '2025-02-10',
progress: 60,
predecessor: [2], // Depends on task 2 (System Design)
},
{
id: 5,
name: 'Backend Development',
startDate: '2025-01-19',
endDate: '2025-02-08',
progress: 70,
predecessor: [2, 3], // Depends on both task 2 and 3
},
{
id: 6,
name: 'Integration Testing',
startDate: '2025-02-11',
endDate: '2025-02-20',
progress: 30,
predecessor: [4, 5], // Depends on frontend and backend development completion
},
])
const assigneeOptions = ref([
{ value: 'alice', label: 'Alice' },
{ value: 'bob', label: 'Bob' },
{ value: 'charlie', label: 'Charlie' },
])
// Triggered when adding predecessor via context menu
const handlePredecessorAdded = (event: { targetTask: Task; newTask: Task }) => {
console.log(`Task [${event.targetTask.name}] added predecessor [${event.newTask.name}]`)
// Component will auto-update targetTask's predecessor array (append new task ID)
// Can call backend API here to save dependency relationship
// await api.addTaskDependency(event.targetTask.id, event.newTask.id)
}
// Triggered when adding successor via context menu
const handleSuccessorAdded = (event: { targetTask: Task; newTask: Task }) => {
console.log(`Task [${event.targetTask.name}] added successor [${event.newTask.name}]`)
// Component will auto-update newTask's predecessor array (add targetTask.id)
// Can call backend API here to save dependency relationship
// await api.addTaskDependency(event.newTask.id, event.targetTask.id)
}
</script>
Dependency Relationship Notes:
predecessor field supports multiple formats:
[1, 2, 3] - number array'1,2,3' - comma-separated string['1', '2', '3'] - string array[], empty string '', or don't set predecessor fieldSuitable for scenarios requiring complete custom control bar:
<template>
<div>
<!-- Custom control bar -->
<div class="custom-toolbar">
<button @click="triggerAddTask">Add Task</button>
<button @click="triggerAddMilestone">Add Milestone</button>
<!-- Other custom buttons... -->
</div>
<!-- Gantt chart component with hidden built-in toolbar -->
<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: 'alice', label: 'Alice' },
{ value: 'bob', label: 'Bob' },
{ value: 'charlie', label: 'Charlie' },
])
// Custom button triggers event (component will respond and open built-in editor)
const triggerAddTask = () => {
// Directly trigger component's add-task event
// Since useDefaultDrawer=true, component will auto-open TaskDrawer
}
const triggerAddMilestone = () => {
// Directly trigger component's add-milestone event
// Since useDefaultMilestoneDialog=true, component will auto-open MilestoneDialog
}
// Listen to event handling logic
const handleAddTask = () => {
console.log('Preparing to add task (triggered by custom button)')
}
const handleAddMilestone = () => {
console.log('Preparing to add milestone (triggered by custom button)')
}
const handleTaskAdded = e => {
console.log('Task added:', e.task)
// Call API to save...
}
</script>
๐ก Flexibility Design:
- Show toolbar + default editor: Simplest out-of-the-box approach
- Hide toolbar + custom buttons + default editor: Custom control bar style while keeping default edit functionality
- Hide toolbar + custom buttons + custom editor: Fully customize all interaction logic
Allow users to adjust task hierarchy and order by dragging 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: 'Project Planning',
startDate: '2025-01-01',
endDate: '2025-01-10',
progress: 100,
},
{
id: 2,
name: 'Requirements Analysis',
startDate: '2025-01-11',
endDate: '2025-01-20',
progress: 60,
parentId: 1,
},
{
id: 3,
name: 'System Design',
startDate: '2025-01-21',
endDate: '2025-01-30',
progress: 40,
},
])
const assigneeOptions = ref([
{ value: 'alice', label: 'Alice' },
{ value: 'bob', label: 'Bob' },
{ value: 'charlie', label: 'Charlie' },
])
// Task row drag completed event (optional)
const handleTaskRowMoved = async (payload: {
draggedTask: Task
targetTask: Task
position: 'after' | 'child'
oldParent: Task | null
newParent: Task | null
}) => {
const { draggedTask, targetTask, position, oldParent, newParent } = payload
// Component has automatically completed task move, parentId update and TaskList/Timeline sync
// Listening to this event is completely optional, only for:
// 1. Show custom notification message
const oldParentName = oldParent?.name || 'Root'
const newParentName = newParent?.name || 'Root'
const positionText = position === 'after' ? 'after target task' : 'as child of target task'
showMessage(`Task [${draggedTask.name}] moved from [${oldParentName}] to [${newParentName}] (${positionText})`, 'success')
// 2. Call backend API to save new task hierarchy
try {
await api.updateTaskHierarchy({
taskId: draggedTask.id,
targetTaskId: targetTask.id,
position: position,
oldParentId: oldParent?.id,
newParentId: newParent?.id,
})
} catch (error) {
console.error('Save task hierarchy failed:', error)
showMessage('Save failed, please refresh page', 'error')
}
// 3. Trigger other business logic (like updating related data, recording operation logs, etc.)
// ...
}
</script>
Drag and Drop Sorting Notes:
enable-task-row-move="true" to enable task row dragging (default is false)position='after'position='child'props.tasks via object reference, automatically completing task move, parentId update, children array adjustment, and TaskList/Timeline synchronizationtask-row-moved event is completely optional, only used for showing messages, calling API to save, recording logs, etc.tasks.value, component has automatically completed data synchronizationdraggedTask: The dragged tasktargetTask: The target taskposition: Drop position ('after' or 'child')oldParent: Original parent task (null means root)newParent: New parent task (null means root)Resource management is used to manage human resources or equipment in a project, supporting task allocation, resource load analysis, and conflict detection in resource view. Switch to resource planning view using the viewMode="resource" prop.
Core Features:
- ๐ Resource View: Display task allocation by resource dimension
- ๐ฏ Load Analysis: Real-time display of resource utilization and overload status
- โ ๏ธ Conflict Detection: Automatically detect resource time conflicts (e.g., Task A:40% + Task B:40% + Task C:30% = 110% overload)
- ๐จ Visualization: Diagonal stripe background marks conflict zones, resource tabs show utilization percentage
- ๐ Cross-Resource Move: Support dragging tasks to different resource rows for reallocation
View Limitations:
- โ Task Links Disabled: Resource view does not display predecessor/successor relationship lines between tasks, as resource view focuses on resource allocation rather than task dependencies
- โ No Actual TaskBar:
showActualTaskbarprop has no effect in resource view, actual execution progress bar will not be displayed
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
id | string | number | โ | - | Unique resource identifier |
name | string | โ | - | Resource name (e.g., person name, device name) |
type | string | - | - | Resource type (e.g., 'developer', 'designer', 'device') |
avatar | string | - | - | Resource avatar URL |
description | string | - | - | Resource description |
department | string | - | - | Department |
skills | string[] | - | - | Skill tags array (e.g., ['Vue', 'React', 'TypeScript']) |
capacity | number | - | - | Resource capacity/utilization (0-100), can represent overall load level |
color | string | - | - | Custom resource row left border color (e.g., '#ff5733'), uses default color scheme if not set |
tasks | Task[] | - | [] | Array of tasks assigned to this resource, each task needs resources field to mark resource utilization |
[key: string] | unknown | - | - | Support custom property extension, can add any additional fields |
Custom Property Extension: Resource interface supports adding any custom fields, e.g.,
phone,location,workHours, etc.Task-Resource Association:
- Each Resource contains a
tasksarray storing tasks assigned to that resource- Each Task should include a
resourcesfield marking which resources are used and their utilization percentage- Resource utilization format:
task.resources = [{ id: 'resource1', capacity: 60 }, { id: 'resource2', capacity: 40 }]capacityrange: 20-100, representing the percentage of resource used by that task- Conflict detection: When multiple tasks'
capacitysum > 100% for the same resource in the same time period, a conflict warning is displayed
Resource Data Example:
import type { Resource, Task } from 'jordium-gantt-vue3'
const resources: Resource[] = [
{
id: 'dev-001',
name: 'Zhang San',
type: 'developer',
avatar: '/avatars/zhangsan.jpg',
department: 'R&D',
skills: ['Vue', 'TypeScript', 'Node.js'],
capacity: 85, // Overall load level
color: '#409eff',
tasks: [
{
id: 1,
name: 'Frontend Development',
startDate: '2026-02-01',
endDate: '2026-02-10',
progress: 50,
resources: [
{ id: 'dev-001', capacity: 60 }, // This task uses 60% of Zhang San's time
{ id: 'dev-002', capacity: 40 } // Also uses 40% of Li Si's time
]
},
{
id: 2,
name: 'Code Review',
startDate: '2026-02-05',
endDate: '2026-02-08',
progress: 0,
resources: [
{ id: 'dev-001', capacity: 40 } // This task uses 40% of Zhang San's time
]
}
// Note: If two tasks overlap, Zhang San's total utilization on Feb 5-8 is 100% (60%+40%), at threshold
]
},
{
id: 'dev-002',
name: 'Li Si',
type: 'developer',
avatar: '/avatars/lisi.jpg',
department: 'R&D',
skills: ['React', 'TypeScript'],
tasks: []
}
]
Resource Conflict Detection Example:
// Scenario: Zhang San is assigned 3 tasks in the same time period
const resource = {
id: 'dev-001',
name: 'Zhang San',
tasks: [
{
id: 1,
name: 'Task A',
startDate: '2026-02-10',
endDate: '2026-02-15',
resources: [{ id: 'dev-001', capacity: 40 }] // Uses 40%
},
{
id: 2,
name: 'Task B',
startDate: '2026-02-10',
endDate: '2026-02-20',
resources: [{ id: 'dev-001', capacity: 40 }] // Uses 40%
},
{
id: 3,
name: 'Task C',
startDate: '2026-02-12',
endDate: '2026-02-18',
resources: [{ id: 'dev-001', capacity: 30 }] // Uses 30%
}
]
}
// Conflict Analysis:
// - Feb 10-11: A(40%) + B(40%) = 80%, not overloaded
// - Feb 12-15: A(40%) + B(40%) + C(30%) = 110%, overloaded! Shows conflict warning
// - Feb 16-18: B(40%) + C(30%) = 70%, not overloaded
// - Feb 19-20: B(40%), not overloaded
| Prop | Type | Default | Description |
|---|---|---|---|
resources | Resource[] | [] | Array of resource data |
viewMode | 'task' | 'resource' | 'task' | View mode: 'task' for task planning view, 'resource' for resource planning view |
resourceListConfig | ResourceListConfig | undefined | Resource list configuration, similar to TaskListConfig, for configuring resource list columns, width etc |
showConflicts | boolean | true | Whether to display resource conflict visualization layer (diagonal stripe background in resource view) |
showTaskbarTab | boolean | true | Whether to display resource tab on TaskBar (resource utilization label on TaskBar in resource view) |
| Event Name | Parameters | Trigger Timing | Description |
|---|---|---|---|
taskbar-resource-change | payload: { task: Task, oldResourceId: string | number, newResourceId: string | number } | When task drag across resources | Triggered when dragging task to another resource row in resource view, component auto-updates task's resources field |
Data Sync Notes:
props.resources and task's resources field<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: 'Zhang San',
type: 'developer',
department: 'R&D',
tasks: [
{
id: 1,
name: 'Frontend Development',
startDate: '2026-02-01',
endDate: '2026-02-10',
progress: 50,
resources: [{ id: 'dev-001', capacity: 60 }]
}
]
}
])
const handleTaskbarResourceChange = (payload: any) => {
console.log('Task resource changed:', payload)
// Call backend API to save resource allocation change
// api.updateTaskResource(payload.task.id, payload.newResourceId)
}
</script>
Milestones are used to mark important time points in a project, such as project kickoff, phase completion, product release, etc. The component provides flexible milestone editing configuration, using the built-in MilestoneDialog by default, and also supports fully custom editing behavior.
Note: Milestones and tasks are independent data collections with no direct association. Milestones are managed independently through the
milestonesprop.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
id | number | โ | - | Unique milestone identifier |
name | string | โ | - | Milestone name |
startDate | string | โ | - | Milestone date, format: 'YYYY-MM-DD' or 'YYYY-MM-DD HH:mm' |
endDate | string | - | - | End date (usually not needed for milestones, auto-set to same as startDate) |
assignee | string | - | - | Assignee |
type | string | โ | 'milestone' | Type identifier, must be set to 'milestone' |
icon | string | - | 'diamond' | Milestone icon, options: 'diamond', 'flag', 'star', 'rocket', etc. |
description | string | - | - | Milestone description |
Note: The
milestonesprop type isTask[], ensure each milestone object'stypefield is set to'milestone'.
| Prop | Type | Default | Description |
|---|---|---|---|
milestones | Task[] | [] | Array of milestone data (type is Task[], ensure type='milestone') |
useDefaultMilestoneDialog | boolean | true | Whether to use built-in milestone edit dialog (MilestoneDialog) |
Configuration Notes:
useDefaultMilestoneDialog=true (default), double-click milestone to auto-open built-in MilestoneDialoguseDefaultMilestoneDialog=false, double-click milestone has no response (component doesn't open any editor)onMilestoneDoubleClick callback or related events to implement custom editing logic๐ก Differences Between Milestones and Tasks:
- Milestone data is managed independently via
milestonesprop, separate fromtasks- Milestone object's
typefield must be set to'milestone'- Milestones don't support child tasks, dependency relationships, and other complex structures
- Milestones are mainly used to mark key time points
โ ๏ธ Deprecated: Please use the new event-driven API (see "Milestone Events" section below)
๐ก Event-Driven Architecture: Milestone management adopts event-driven design. Using event API is recommended over callback functions.
| Event Name | Parameters | When Triggered | Description |
|---|---|---|---|
add-milestone | - | When clicking toolbar "Add Milestone" button | Can be used for custom add milestone logic. If useDefaultMilestoneDialog=true, component will auto-open built-in MilestoneDialog |
milestone-saved | (milestone: Task) => void | After milestone saved (add or edit) | Triggered after saving milestone via built-in MilestoneDialog. Note: Component has auto-updated milestones data, external only needs to listen to this event for additional processing (like calling API to save) |
milestone-deleted | { milestoneId: number } | After milestone deleted | Triggered after deleting milestone via built-in MilestoneDialog. Note: Component has auto-updated milestones data, external only needs to listen to this event for additional processing |
milestone-icon-changed | { milestoneId: number, icon: string } | After milestone icon changed | Triggered after modifying icon via built-in MilestoneDialog |
milestone-drag-end | (milestone: Task) => void | When milestone drag ends | Milestone date updated. Note: Component has auto-updated milestones data |
Data Synchronization Notes:
props.milestones datamilestones data again in event handlers, otherwise it will cause duplicate updatesUsing the new event API, component auto-manages data, more concise:
<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: 'Project Kickoff',
startDate: '2025-01-01',
type: 'milestone',
icon: 'diamond',
assignee: 'Project Manager',
description: 'Official project kickoff',
},
{
id: 102,
name: 'Requirements Review',
startDate: '2025-01-15',
type: 'milestone',
icon: 'flag',
},
])
// Toolbar "Add Milestone" button click event
const handleAddMilestone = () => {
console.log('Preparing to add milestone...')
// Component will auto-open MilestoneDialog (if useDefaultMilestoneDialog=true)
}
// Milestone save event (add or edit)
const handleMilestoneSaved = (milestone: Task) => {
console.log('Milestone saved:', milestone)
// Note: Component has auto-updated milestones array
// Only need to call backend API to save here
// await api.saveMilestone(milestone)
}
// Milestone delete event
const handleMilestoneDeleted = (e: { milestoneId: number }) => {
console.log('Milestone deleted, ID:', e.milestoneId)
// Note: Component has auto-removed from milestones array
// Only need to call backend API to delete here
// await api.deleteMilestone(e.milestoneId)
}
// Milestone icon change event
const handleMilestoneIconChanged = (e: { milestoneId: number; icon: string }) => {
console.log('Milestone icon changed:', e.milestoneId, '->', e.icon)
// Component has auto-updated icon, can call API to save here
// await api.updateMilestoneIcon(e.milestoneId, e.icon)
}
// Milestone drag end event
const handleMilestoneDrag = (milestone: Task) => {
console.log('Milestone drag completed, new date:', milestone.startDate)
// Component has auto-updated date, can call API to save here
// await api.updateMilestoneDate(milestone.id, milestone.startDate)
}
</script>
If you need to fully customize the milestone editing interface, you can disable the built-in dialog and use your own component:
<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"
/>
<!-- Custom Milestone Edit Dialog -->
<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: 'Project Kickoff',
startDate: '2025-01-01',
type: 'milestone',
icon: 'diamond',
assignee: 'Project Manager',
description: 'Official project kickoff',
},
])
const customDialogVisible = ref(false)
const editingMilestone = ref<Task | null>(null)
const isNewMilestone = ref(false)
// Click toolbar "Add Milestone" button
const handleAddMilestone = () => {
editingMilestone.value = null
isNewMilestone.value = true
customDialogVisible.value = true
}
// Open custom dialog when double-clicking milestone
// Note: Need to listen to Timeline component milestone double-click event
// or trigger edit via external button/list item
const openEditDialog = (milestone: Task) => {
editingMilestone.value = { ...milestone }
isNewMilestone.value = false
customDialogVisible.value = true
}
// Custom dialog save event
const handleCustomDialogSave = (milestone: Task) => {
if (isNewMilestone.value) {
// Add milestone
const newMilestone = {
...milestone,
id: Date.now(), // Generate new ID
type: 'milestone',
}
milestones.value.push(newMilestone)
// Call backend API to save
// await api.createMilestone(newMilestone)
} else {
// Update existing milestone
const index = milestones.value.findIndex(m => m.id === milestone.id)
if (index !== -1) {
milestones.value[index] = { ...milestone }
}
// Call backend API to update
// await api.updateMilestone(milestone)
}
customDialogVisible.value = false
}
// Custom dialog delete event
const handleCustomDialogDelete = (milestoneId: number) => {
const index = milestones.value.findIndex(m => m.id === milestoneId)
if (index !== -1) {
milestones.value.splice(index, 1)
}
// Call backend API to delete
// await api.deleteMilestone(milestoneId)
customDialogVisible.value = false
}
// Following event handlers are still valid (for drag operations, etc.)
const handleMilestoneSaved = (milestone: Task) => {
console.log('Milestone saved (via other method):', milestone)
}
const handleMilestoneDeleted = (e: { milestoneId: number }) => {
console.log('Milestone deleted (via other method):', e.milestoneId)
}
const handleMilestoneDrag = (milestone: Task) => {
console.log('Milestone drag completed:', milestone.startDate)
// Call API to update date
// await api.updateMilestoneDate(milestone.id, milestone.startDate)
}
</script>
Custom Dialog Component Example (CustomMilestoneDialog.vue - Using Element Plus)๏ผ
Note: The following examplesUsing Element Plus UI framework. You can also use other UI frameworks (such as Ant Design Vue, Naive UI, etc.) or native HTML implementation.
<template>
<el-dialog
v-model="dialogVisible"
:title="isNew ? 'Add milestone' : 'Edit Milestone'"
width="500px"
@close="handleClose"
>
<el-form :model="form" label-width="100px">
<el-form-item label="Milestone Name">
<el-input v-model="form.name" placeholder="Please enter milestone name" />
</el-form-item>
<el-form-item label="Date">
<el-date-picker
v-model="form.startDate"
type="date"
placeholder="Select date"
value-format="YYYY-MM-DD"
/>
</el-form-item>
<el-form-item label="Assignee">
<el-input v-model="form.assignee" placeholder="Please enter assignee" />
</el-form-item>
<el-form-item label="Icon">
<el-select v-model="form.icon" placeholder="Select icon">
<el-option label="Diamond" value="diamond" />
<el-option label="Flag" value="flag" />
<el-option label="Star" value="star" />
<el-option label="Rocket" value="rocket" />
</el-select>
</el-form-item>
<el-form-item label="Description">
<el-input
v-model="form.description"
type="textarea"
:rows="3"
placeholder="Please enter description"
/>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button v-if="!isNew" type="danger" @click="handleDelete"> Delete </el-button>
<el-button @click="handleClose">Cancel</el-button>
<el-button type="primary" @click="handleSave">Save</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue'
import type { Task } from 'jordium-gantt-vue3'
interface Props {
visible: boolean
milestone: Task | null
isNew: boolean
}
const props = defineProps<Props>()
const emit = defineEmits<{
'update:visible': [value: boolean]
save: [milestone: Task]
delete: [milestoneId: number]
}>()
const dialogVisible = ref(false)
const form = ref({
id: 0,
name: '',
startDate: '',
assignee: '',
icon: 'diamond',
description: '',
type: 'milestone',
})
watch(
() => props.visible,
val => {
dialogVisible.value = val
if (val) {
if (props.milestone) {
// Edit mode, fill data
form.value = { ...props.milestone }
} else {
// Add mode, reset form
form.value = {
id: 0,
name: '',
startDate: new Date().toISOString().split('T')[0],
assignee: '',
icon: 'diamond',
description: '',
type: 'milestone',
}
}
}
}
)
watch(dialogVisible, val => {
emit('update:visible', val)
})
const handleClose = () => {
dialogVisible.value = false
}
const handleSave = () => {
if (!form.value.name || !form.value.startDate) {
alert('Please fill required fields')
return
}
emit('save', { ...form.value })
}
const handleDelete = () => {
if (confirm('Are you sure to delete this milestone?')) {
emit('delete', form.value.id)
}
}
</script>
๐ก Custom Dialog Notes:
- Set
use-default-milestone-dialog="false"to disable built-in dialog- Listen to
@add-milestoneevent to open custom dialog- Need to manually manage
milestonesarray CRUD operations- Can still listen to other events (like
@milestone-drag-end) to handle drag operations- Suitable for scenarios requiring complex form validation, special UI design, or additional fields
This section details the configuration options and extension capabilities of the GanttChart component, including Component Configuration, Theme & Internationalization, and Custom Extensions.
Task type (type field) is used to distinguish different types of tasks, and the component internally executes different logic based on the type.
| Type | Description | Default |
|---|---|---|
story | User Story | - |
task | Regular Task | โ |
bug | Bug/Issue | - |
Different task types have different functional characteristics in the component:
| Feature | story | task | bug |
|---|---|---|---|
| Can be parent task | โ | โ | โ |
| Can be predecessor | โ | โ | โ |
| Timer support | โ | โ | โ |
| Auto parent task | โ | โ | โ |
| Special delete hint | โ | โ | โ |
โ ๏ธ Important
- Task type values are used for internal component logic, do not modify these enum values arbitrarily
- When customizing TaskDrawer, you must maintain these three enum values:
story,task,bug- For additional business labels, use custom property fields such as:
customType,category,label, etc.
Example: Using Custom Labels
const tasks = ref([
{
id: 1,
name: 'Requirements Analysis',
type: 'task', // Keep built-in component type
customType: 'requirement', // Custom business type
category: 'analysis', // Custom category
startDate: '2025-01-01',
endDate: '2025-01-10',
},
])
Customize the toolbar functional buttons and time scale options.
Type Definition:
| Field | Type | Default | Description |
|---|---|---|---|
showAddTask | boolean | true | Show "Add Task" button |
showAddMilestone | boolean | true | Show "Add Milestone" button |
showTodayLocate | boolean | true | Show "Locate to Today" button |
showExportCsv | boolean | true | Show "Export CSV" button |
showExportPdf | boolean | true | Show "Export PDF" button |
showLanguage | boolean | true | Show "Language Switch" button (Chinese/English) |
showTheme | boolean | true | Show "Theme Switch" button (Light/Dark) |
showFullscreen | boolean | true | Show "Fullscreen" button |
showTimeScale | boolean | true | Show time scale button group (controls entire group visibility) |
timeScaleDimensions | TimelineScale[] | ['hour', 'day', 'week', 'month', 'quarter', 'year'] | Set time scale dimensions to display, options: 'hour', 'day', 'week', 'month', 'quarter', 'year' |
defaultTimeScale | TimelineScale | 'week' | Default selected time scale |
showExpandCollapse | boolean | true | Show "Expand All/Collapse All" button (for parent-child task tree structure) |
showViewMode | boolean | true | Show Task / Resource view mode toggle button group |
TimelineScale Type Description:
type TimelineScale = 'hour' | 'day' | 'week' | 'month' | 'quarter' | 'year'
// Can also use constant form
import { TimelineScale } from 'jordium-gantt-vue3'
TimelineScale.HOUR // 'hour' - Hour view
TimelineScale.DAY // 'day' - Day view
TimelineScale.WEEK // 'week' - Week view
TimelineScale.MONTH // 'month' - Month view
TimelineScale.QUARTER // 'quarter' - Quarter view
TimelineScale.YEAR // 'year' - Year view
Example 1: Complete Configuration (Show All Features)
<template>
<GanttChart :tasks="tasks" :toolbar-config="toolbarConfig" />
</template>
<script setup lang="ts">
import { GanttChart } from 'jordium-gantt-vue3'
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
import type { ToolbarConfig } from 'jordium-gantt-vue3'
const toolbarConfig: ToolbarConfig = {
showAddTask: true, // Show add task button
showAddMilestone: true, // Show add milestone button
showTodayLocate: true, // Show locate to today button
showExportCsv: true, // Show export CSV button
showExportPdf: true, // Show export PDF button
showLanguage: true, // Show language switch button
showTheme: true, // Show theme switch button
showFullscreen: true, // Show fullscreen button
showTimeScale: true, // Show time scale button group
timeScaleDimensions: [
// Show all time scale dimensions
'hour',
'day',
'week',
'month',
'quarter',
'year',
],
defaultTimeScale: 'week', // Default week view
showExpandCollapse: true, // Show expand/collapse button
}
</script>
Example 2: Simplified Configuration (Show Common Features Only)
<script setup lang="ts">
import type { ToolbarConfig } from 'jordium-gantt-vue3'
const toolbarConfig: ToolbarConfig = {
showAddTask: true, // Keep add task
showAddMilestone: true, // Keep add milestone
showTodayLocate: true, // Keep locate today
showExportCsv: false, // Hide export CSV
showExportPdf: false, // Hide export PDF
showLanguage: false, // Hide language switch (fixed to one language)
showTheme: true, // Keep theme switch
showFullscreen: true, // Keep fullscreen
showTimeScale: true, // Show time scale
timeScaleDimensions: [
// Only show day/week/month scales
'day',
'week',
'month',
],
defaultTimeScale: 'week', // Default week view
showExpandCollapse: true, // Keep expand/collapse
}
</script>
Example 3: Using TimelineScale Constants
<script setup lang="ts">
import { TimelineScale } from 'jordium-gantt-vue3'
import type { ToolbarConfig } from 'jordium-gantt-vue3'
const toolbarConfig: ToolbarConfig = {
showTimeScale: true,
timeScaleDimensions: [
TimelineScale.DAY,
TimelineScale.WEEK,
TimelineScale.MONTH,
TimelineScale.QUARTER,
],
defaultTimeScale: TimelineScale.MONTH, // Default month view
}
</script>
Example 4: Minimal Configuration (Suitable for Embedded Use)
<script setup lang="ts">
import type { ToolbarConfig } from 'jordium-gantt-vue3'
const toolbarConfig: ToolbarConfig = {
showAddTask: false, // Hide all edit buttons
showAddMilestone: false,
showTodayLocate: true, // Only keep navigation features
showExportCsv: false,
showExportPdf: false,
showLanguage: false,
showTheme: false,
showFullscreen: false,
showTimeScale: true, // Keep time scale switch
timeScaleDimensions: ['week', 'month'],
defaultTimeScale: 'month',
showExpandCollapse: false, // Hide expand/collapse
}
</script>
๐ก Configuration Recommendations๏ผ
- Default configuration๏ผWhen not passed, all buttons are shown by default
- Show as needed: Hide unnecessary feature buttons based on business requirements
- Time scale๏ผ
timeScaleDimensionscontrols which time dimensions to display, recommend selecting 2-4 common dimensions- Responsive layout๏ผtoolbar will automatically adapt to container width, excessive buttons will collapse into more menu
Customize task list display columns, width limits, etc. Task list is located on the left side of the Gantt chart, showing detailed task information.
Type Definition๏ผ
| Field | Type | Default | Description |
|---|---|---|---|
columns | TaskListColumnConfig[] | Default 8 columns | Task list column configuration array, defines which columns to display and their properties |
showAllColumns | boolean | true | Whether to show all columns. When true, ignores visible setting in columns |
defaultWidth | number | string | 320 | Default expanded width. Supports pixel number (like 320) or percentage string (like '30%') |
minWidth | number | string | 280 | Minimum width. Supports pixel number (like 280) or percentage string (like '20%'). Cannot be less than 280px |
maxWidth | number | string | 1160 | Maximum width. Supports pixel number (like 1160) or percentage string (like '80%') |
showTaskIcon | boolean | true | Whether to show Tasks' icons |
TaskListColumnConfig Type Definition๏ผ
| Field | Type | Required | Description |
|---|---|---|---|
key | string | โ | Unique column identifier, used to access fields in Task object and for internationalization |
label | string | - | Column display label (header text) |
cssClass | string | - | Custom CSS class name |
width | number | - | Column width (unit: pixels) |
visible | boolean | - | Whether to show this column, default true. This setting is invalid when showAllColumns=true |
Example1๏ผBasic Configuration (Adjust Width)
<template>
<GanttChart :tasks="tasks" :task-list-config="taskListConfig" />
</template>
<script setup lang="ts">
import { GanttChart } from 'jordium-gantt-vue3'
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
import type { TaskListConfig } from 'jordium-gantt-vue3'
const taskListConfig: TaskListConfig = {
defaultWidth: 450, // Default width 450px (wider than default 320px)
minWidth: 300, // Minimum width 300px
maxWidth: 1200, // Maximum width 1200px
}
</script>
Example2๏ผUsing Percentage Width
<template>
<GanttChart :tasks="tasks" :task-list-config="taskListConfig" />
</template>
<script setup lang="ts">
import { GanttChart } from 'jordium-gantt-vue3'
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
import type { TaskListConfig } from 'jordium-gantt-vue3'
const taskListConfig: TaskListConfig = {
defaultWidth: '25%', // Default 25% of container width
minWidth: '15%', // Minimum 15%
maxWidth: '60%', // Maximum 60%
}
</script>
Example3๏ผCustom Display Columns (Standard Configuration)
Based on business requirements, you can customize columns to display, column widths, and display order. Recommend defining column configuration array first, then assign to columns prop.
<template>
<GanttChart :tasks="tasks" :task-list-config="taskListConfig" />
</template>
<script setup lang="ts">
import { GanttChart } from 'jordium-gantt-vue3'
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
import type { TaskListConfig, TaskListColumnConfig } from 'jordium-gantt-vue3'
// Define column configuration to display
const columns: TaskListColumnConfig[] = [
{ key: 'predecessor', label: 'Predecessors', visible: true },
{ key: 'assignee', label: 'Assignee', visible: true },
{ key: 'startDate', label: 'Start Date', visible: true },
{ key: 'endDate', label: 'End Date', visible: true },
{ key: 'estimatedHours', label: 'Estimated Hours', visible: true },
{ key: 'actualHours', label: 'Actual Hours', visible: true },
{ key: 'progress', label: 'Progress', visible: true },
]
const taskListConfig: TaskListConfig = {
columns,
defaultWidth: 450,
minWidth: 300,
maxWidth: 1200,
}
</script>
Example4๏ผSimplified Column Configuration
Only show core information columns, suitable for scenarios with limited space or requiring concise display.
<script setup lang="ts">
import type { TaskListConfig, TaskListColumnConfig } from 'jordium-gantt-vue3'
// Define simplified Column Configuration
const columns: TaskListColumnConfig[] = [
{ key: 'name', label: 'Task', visible: true },
{ key: 'assignee', label: 'Assignee', width: 80, visible: true },
{ key: 'progress', label: 'Progress', width: 60, visible: true },
]
const taskListConfig: TaskListConfig = {
columns,
defaultWidth: 350,
minWidth: 280,
maxWidth: 500,
showAllColumns: false, // Only show columns with visible=true
}
</script>
Example5๏ผCustom Business Columns
Add business-related custom columns, ensure Task object contains corresponding fields.
<script setup lang="ts">
import type { TaskListConfig, TaskListColumnConfig } from 'jordium-gantt-vue3'
// Define configuration with custom columns
const columns: TaskListColumnConfig[] = [
{ key: 'name', label: 'Task Name', visible: true },
{ key: 'priority', label: 'Priority', width: 80, visible: true }, // Custom column
{ key: 'department', label: 'Department', width: 100, visible: true }, // Custom column
{ key: 'status', label: 'Status', width: 80, visible: true }, // Custom column
{ key: 'assignee', label: 'Assignee', visible: true },
{ key: 'startDate', label: 'Start Date', visible: true },
{ key: 'endDate', label: 'End Date', visible: true },
{ key: 'progress', label: 'Progress', visible: true },
]
const taskListConfig: TaskListConfig = {
columns,
}
</script>
Example6๏ผDynamic Column Configuration
Combine ref and computed to achieve dynamic show/hide and width adjustment of columns.
<template>
<GanttChart :tasks="tasks" :task-list-config="taskListConfig" />
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { GanttChart } from 'jordium-gantt-vue3'
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
import type { TaskListConfig, TaskListColumnConfig } from 'jordium-gantt-vue3'
// Define dynamically configurable columns
const availableColumns = ref<TaskListColumnConfig[]>([
{ key: 'predecessor', label: 'Predecessors', visible: true },
{ key: 'assignee', label: 'Assignee', visible: true },
{ key: 'startDate', label: 'Start Date', visible: true },
{ key: 'endDate', label: 'End Date', visible: true },
{ key: 'estimatedHours', label: 'Estimated Hours', visible: true },
{ key: 'actualHours', label: 'Actual Hours', visible: true },
{ key: 'progress', label: 'Progress', visible: true },
{ key: 'custom', label: 'Custom column', visible: true, width: 120 },
])
// Define width configuration
const taskListWidth = ref({
defaultWidth: 450,
minWidth: 300,
maxWidth: 1200,
})
// Use computed property to dynamically generate configuration
const taskListConfig = computed<TaskListConfig>(() => ({
columns: availableColumns.value,
defaultWidth: taskListWidth.value.defaultWidth,
minWidth: taskListWidth.value.minWidth,
maxWidth: taskListWidth.value.maxWidth,
}))
</script>
๐ก Configuration Notes๏ผ
- Default behavior๏ผWhen not passed, show all 8 default columns with width of 320px
- Width units๏ผSupports pixel (
number) and percentage (string, like'30%') methods- Percentage calculation๏ผBased on total width of Gantt chart container, responsive adjustment
- Column order:
columnsarray order determines column display order- Column configuration standards๏ผRecommend defining
TaskListColumnConfig[]type column array first, then assign tocolumnsprop- Custom column support๏ผTask interface supports arbitrary custom fields through
[key: string]: unknownindex signature, component will dynamically read column values throughtask[column.key], no need to modify Task interface to add custom columns- Dynamic configuration๏ผCombine
refandcomputedto achieve dynamic show/hide and width adjustment of columns- Minimum width limit:
minWidthcannot be less than 280px, this is the minimum value to ensure basic usability
Controls task bar display content and interaction behaviorใ
Configuration Fields๏ผ
| Field | Type | Default | Description |
|---|---|---|---|
showAvatar | boolean | true | Whether to show avatar |
showTitle | boolean | true | Whether to show title text |
showProgress | boolean | true | Whether to show progress text |
dragThreshold | number | 5 | Drag trigger threshold (pixels) |
resizeHandleWidth | number | 5 | Resize handle width (pixels), max 15px |
enableDragDelay | boolean | false | Whether to enable drag delay (prevent accidental trigger) |
dragDelayTime | number | 150 | Drag delay time (milliseconds) |
๐ก Edit Permission Control๏ผ
- Global control: Use
<GanttChart :allow-drag-and-resize="false" />to disable drag/resize for all tasks- Individual task control: Set task object
isEditable: falseproperty to control individual task
Example1๏ผComplete Configuration
<template>
<GanttChart :tasks="tasks" :task-bar-config="taskBarConfig" />
</template>
<script setup lang="ts">
import { GanttChart } from 'jordium-gantt-vue3'
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
import type { TaskBarConfig } from 'jordium-gantt-vue3'
const taskBarConfig: TaskBarConfig = {
showAvatar: true,
showTitle: true,
showProgress: true,
dragThreshold: 8,
resizeHandleWidth: 8,
enableDragDelay: true,
dragDelayTime: 200,
}
</script>
Example2๏ผGlobal Read-Only Mode
Disable edit operations for all tasks.
<template>
<GanttChart :tasks="tasks" :allow-drag-and-resize="false" />
</template>
Example3๏ผIndividual Task Read-Only
Only certain tasks are non-editable, other tasks are normal.
<script setup lang="ts">
import type { Task } from 'jordium-gantt-vue3'
const tasks: Task[] = [
{
id: 1,
name: 'Editable Task',
startDate: '2025-01-01',
endDate: '2025-01-10',
// isEditable default is true
},
{
id: 2,
name: 'Read-Only Task (Locked)',
startDate: '2025-01-05',
endDate: '2025-01-15',
isEditable: false, // This task cannot be dragged/resized
},
]
</script>
Example4๏ผSimplified Display
Only show task bar, hide avatar, title and progress text.
<script setup lang="ts">
import type { TaskBarConfig } from 'jordium-gantt-vue3'
const taskBarConfig: TaskBarConfig = {
showAvatar: false,
showTitle: false,
showProgress: false,
}
</script>
Example5๏ผAnti-Accidental Touch Configuration
In mobile or touch screen scenarios, increase drag threshold and delay time.
<script setup lang="ts">
import { computed, ref } from 'vue'
import type { TaskBarConfig } from 'jordium-gantt-vue3'
const isTouchDevice = ref('ontouchstart' in window)
const taskBarConfig = computed<TaskBarConfig>(() => ({
dragThreshold: isTouchDevice.value ? 10 : 5,
resizeHandleWidth: isTouchDevice.value ? 12 : 5,
enableDragDelay: isTouchDevice.value,
dragDelayTime: isTouchDevice.value ? 300 : 150,
}))
</script>
Customize the cell width, header formatter strings, and buffer sizes for each time scale. Only pass the scales you want to override โ unspecified scales continue using built-in defaults.
Configurable Fields (ScaleConfigOption):
| Field | Type | Description |
|---|---|---|
cellWidth | number | Cell width in px, clamped to each scale's built-in min/max |
formatter | { primary: string; secondary?: string } | Header formatter strings, see Token reference below |
preBuffer | number | Leading buffer size (in current scale's natural unit) |
sufBuffer | number | Trailing buffer size (in current scale's natural unit) |
Built-in cellWidth Constraints (values outside range are automatically clamped):
| Scale | Default (px) | Min (px) | Max (px) |
|---|---|---|---|
hour | 40 | 40 | 120 |
day | 30 | 30 | 120 |
week | 60 | 60 | 240 |
month | 60 | 60 | 180 |
quarter | 60 | 60 | 120 |
year | 180 | 180 | 720 |
Supported formatter Tokens:
| Token | Meaning | Example Output |
|---|---|---|
yyyy | 4-digit year | 2025 |
MM | Zero-padded month | 01 |
M | Non-padded month | 1 |
dd | Zero-padded day | 05 |
d | Non-padded day | 5 |
HH | Zero-padded hour | 09 |
mm | Zero-padded minute | 30 |
Q | Quarter number | 2 (rendered as "Q2" in en-US, "2ๅญฃๅบฆ" in zh-CN) |
W | Week number | 5 (rendered as "W5" in en-US, "5ๅจ" in zh-CN) |
Built-in Default Formatter Reference:
| Scale | primary | secondary |
|---|---|---|
hour | yyyyๅนดMMๆddๆฅ | HH:mm |
day | yyyyๅนดMMๆ | dd |
week | yyyyๅนดMMๆ | d |
month | yyyyๅนด | MMๆ |
quarter | yyyyๅนด | Qๅญฃๅบฆ |
year | yyyyๅนด | ไธๅๅนด|ไธๅๅนด |
Week scale note: The default
secondary: 'd'displays the date of the first day of the week (e.g.,5). Set it to'W'to show the week number instead (e.g., "W5" in en-US, "5ๅจ" in zh-CN).
Example 1: Override cell width for day and week views
<template>
<GanttChart :tasks="tasks" :scale-configs="scaleConfigs" />
</template>
<script setup lang="ts">
import { GanttChart } from 'jordium-gantt-vue3'
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
const scaleConfigs = {
day: { cellWidth: 60 }, // Widen day view cells to 60px (default 30px)
week: { cellWidth: 120 }, // Widen week view cells to 120px (default 60px)
}
</script>
Example 2: Custom formatter strings
<script setup lang="ts">
const scaleConfigs = {
// Day view: primary row shows year, secondary shows "MM/dd"
day: { formatter: { primary: 'yyyy', secondary: 'MM/dd' } },
// Week view: show week number instead of the first day's date
week: { formatter: { primary: 'yyyyๅนดMMๆ', secondary: 'W' } },
// Month view: combined "yyyy-MM" format on primary row
month: { formatter: { primary: 'yyyy-MM' } },
}
</script>
Example 3: Adjust leading/trailing buffer range
<script setup lang="ts">
const scaleConfigs = {
year: { preBuffer: 3, sufBuffer: 3 }, // 3-year buffer on each side (default 1 year)
month: { preBuffer: 6, sufBuffer: 6 }, // 6-month buffer on each side
}
</script>
๐ก Notes:
- Incremental override: Only specified scales and fields are overridden; all others keep their built-in defaults
- cellWidth clamping: Values outside each scale's min/max are automatically clamped
- formatter replacement: Passing
formatterreplaces the entire formatter object for that scale (no field-level merge)- Buffer units:
preBuffer/sufBufferuse the scale's natural unit (dayโ days,weekโ weeks,monthโ months, etc.)
The component has built-in intelligent timeline range calculation logic, ensuring that regardless of task data volume or task duration, the timeline always fills the container width, providing the best visual experience.
Core Design Principles:
Base Buffer Mechanism: Add fixed buffers based on the actual time range of tasks, varying by view type
Container Width Adaptation: After base buffering, if calculated timeline width is less than container width, automatically extend the range
Empty Data Handling: When no task data exists, calculate reasonable time range based on container width and time scale
Independent Calculation on View Switch: Each time scale switch triggers independent recalculation of optimal time range for that view
Calculation Pattern Reference Table:
| View | Unit Width | Base Buffer | Empty Data Min Range | Container Auto-Fill? |
|---|---|---|---|---|
| Hour View | 30px/hour | ยฑ1 day | 3 days | โ |
| Day View | 30px/day | ยฑ30 days | 60 days | โ |
| Week View | 60px/week | ยฑ2 months | 20 weeks | โ |
| Month View | 60px/month | ยฑ1 year | 3 years | โ |
| Quarter View | 60px/quarter (240px/year) | ยฑ1 year | 5 years | โ |
| Year View | 360px/year | ยฑ1 year | 5 years | โ |
Practical Application Scenarios:
Short-term Tasks (e.g., 1-week project):
Long-term Projects (e.g., 2-year project):
Empty Board (no task data):
๐ก Automation Advantages:
- No need to manually set
startDateandendDate, component automatically calculates optimal range- Responsive to container width changes, timeline automatically recalculates
- Different views independently optimized, auto-adjusts to best display effect when switching views
- Avoids issues with timeline being too narrow or having excessive whitespace
- Suitable for displaying at different resolutions
The GanttChart component exposes a series of methods through defineExpose, allowing parent components to directly call these methods via template references (ref) to control component behavior. This imperative control approach is suitable for scenarios requiring precise timing control.
| Method | Parameters | Return Value | Description |
|---|---|---|---|
setLocale | locale: 'zh-CN' | 'en-US' | void | Set component language |
currentLocale | - | 'zh-CN' | 'en-US' | Get current language setting |
setTheme | mode: 'light' | 'dark' | void | Set theme mode |
currentTheme | - | 'light' | 'dark' | Get current theme mode |
setTimeScale | scale: TimelineScale | void | Set time scale ('hour' | 'day' | 'week' | 'month' | 'quarter' | 'year') |
zoomIn | - | void | zoom in time scale๏ผ'year' -> 'quarter' -> 'month' -> 'week' -> 'day' -> 'hour'๏ผ |
zoomOut | - | void | zoom out time scale๏ผ'hour' -> 'day' -> 'week' -> 'month' -> 'quarter' -> 'year'๏ผ |
currentScale | - | TimelineScale | Get current time scale |
toggleFullscreen | - | void | Toggle fullscreen state |
enterFullscreen | - | void | Enter fullscreen mode |
exitFullscreen | - | void | Exit fullscreen mode |
isFullscreen | - | boolean | Get current fullscreen state |
toggleExpandAll | - | void | Toggle expand/collapse all tasks |
expandAll | - | void | Expand all tasks |
collapseAll | - | void | Collapse all tasks |
isExpandAll | - | boolean | Get current expand all state |
scrollToToday | - | void | Scroll to today's position |
scrollToTask | taskId: number | string | void | Scroll to specified task (task will auto-expand to visible state) |
scrollToDate | date: string | Date | void | Scroll to specified date position (format: 'YYYY-MM-DD' or Date object) |
getTaskListVisible | - | boolean | Get the current visibility state of TaskList |
setTaskListVisible | visible: boolean | void | Imperatively set TaskList visibility (only effective when enableTaskListCollapsible=true) |
toggleTaskList | - | void | Toggle TaskList expand/collapse state with animation |
Basic Usage: Imperative Control
<template>
<div>
<!-- External control buttons -->
<div class="control-panel">
<button @click="handleSetLocale('zh-CN')">ไธญๆ</button>
<button @click="handleSetLocale('en-US')">English</button>
<button @click="handleSetTheme('light')">Light Theme</button>
<button @click="handleSetTheme('dark')">Dark Theme</button>
<button @click="handleSetTimeScale('day')">Day View</button>
<button @click="handleSetTimeScale('week')">Week View</button>
<button @click="handleSetTimeScale('month')">Month View</button>
<button @click="ganttRef?.toggleFullscreen()">Toggle Fullscreen</button>
<button @click="ganttRef?.toggleExpandAll()">Expand/Collapse All</button>
<button @click="ganttRef?.scrollToToday()">Locate Today</button>
<button @click="handleScrollToTask">Scroll to Task 2</button>
<button @click="handleScrollToDate">Scroll to 2025-06-01</button>
</div>
<!-- Status display -->
<div class="status-panel">
<p>Current Language: {{ currentLang }}</p>
<p>Current Theme: {{ currentThemeMode }}</p>
<p>Current Scale: {{ currentTimeScale }}</p>
<p>Fullscreen: {{ isFullscreenMode ? 'Yes' : 'No' }}</p>
<p>Expand State: {{ isAllExpanded ? 'All Expanded' : 'Partially Collapsed' }}</p>
</div>
<!-- Gantt chart component -->
<div style="height: 600px;">
<GanttChart
ref="ganttRef"
:tasks="tasks"
:milestones="milestones"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { GanttChart } from 'jordium-gantt-vue3'
import type { TimelineScale } from 'jordium-gantt-vue3'
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
// Component reference
const ganttRef = ref<InstanceType<typeof GanttChart>>()
// State variables
const currentLang = ref<'zh-CN' | 'en-US'>('zh-CN')
const currentThemeMode = ref<'light' | 'dark'>('light')
const currentTimeScale = ref<TimelineScale>('week')
const isFullscreenMode = ref(false)
const isAllExpanded = ref(true)
// Task data
const tasks = ref([
{ id: 1, name: 'Task 1', startDate: '2025-01-01', endDate: '2025-01-10', progress: 50 },
{ id: 2, name: 'Task 2', startDate: '2025-01-05', endDate: '2025-01-15', progress: 30 },
])
const milestones = ref([])
// Language control
const handleSetLocale = (locale: 'zh-CN' | 'en-US') => {
ganttRef.value?.setLocale(locale)
currentLang.value = ganttRef.value?.currentLocale() || locale
}
// Theme control
const handleSetTheme = (mode: 'light' | 'dark') => {
ganttRef.value?.setTheme(mode)
currentThemeMode.value = ganttRef.value?.currentTheme() || mode
}
// Time scale control
const handleSetTimeScale = (scale: TimelineScale) => {
ganttRef.value?.setTimeScale(scale)
currentTimeScale.value = ganttRef.value?.currentScale() || scale
}
// Scroll to specified task
const handleScrollToTask = () => {
ganttRef.value?.scrollToTask(2)
}
// Scroll to specified date
const handleScrollToDate = () => {
ganttRef.value?.scrollToDate('2025-06-01')
}
</script>
<style scoped>
.control-panel {
display: flex;
gap: 10px;
margin-bottom: 20px;
flex-wrap: wrap;
}
.status-panel {
padding: 10px;
background-color: #f5f5f5;
border-radius: 4px;
margin-bottom: 20px;
}
.status-panel p {
margin: 5px 0;
}
</style>
Imperative vs Reactive
Getting State
currentLocale(), currentTheme())Error Handling
ref is mounted before calling: ganttRef.value?.methodName()onMounted lifecycleComplete examples can be found in:
npm-demo/src/components/GanttTest.vuenpm-webpack-demo/src/App.vueComponent has built-in light and dark themes, can switch via toolbar button, also can listen to switch events:
<template>
<GanttChart :tasks="tasks" :on-theme-change="handleThemeChange" />
</template>
<script setup lang="ts">
const handleThemeChange = (isDark: boolean) => {
console.log('Theme switched to:', isDark ? 'dark' : 'light')
// Can save user preference settings to localStorage here
localStorage.setItem('gantt-theme', isDark ? 'dark' : 'light')
}
</script>
Customize theme by overriding CSS variables:
/* Customize light theme */
:root {
/* Primary colors */
--gantt-primary-color: #409eff;
--gantt-success-color: #67c23a;
--gantt-warning-color: #e6a23c;
--gantt-danger-color: #f56c6c;
/* Background colors */
--gantt-bg-primary: #ffffff;
--gantt-bg-secondary: #f5f7fa;
--gantt-bg-hover: #ecf5ff;
/* Text colors */
--gantt-text-primary: #303133;
--gantt-text-secondary: #606266;
--gantt-text-placeholder: #c0c4cc;
/* Border colors */
--gantt-border-color: #dcdfe6;
--gantt-border-color-light: #e4e7ed;
/* Task bar colors */
--gantt-task-bg: #409eff;
--gantt-task-border: #66b1ff;
--gantt-task-text: #ffffff;
}
/* Customize dark theme */
.dark {
--gantt-bg-primary: #1a1a1a;
--gantt-bg-secondary: #2c2c2c;
--gantt-bg-hover: #3a3a3a;
--gantt-text-primary: #e5e5e5;
--gantt-text-secondary: #b0b0b0;
--gantt-border-color: #3a3a3a;
--gantt-border-color-light: #4a4a4a;
--gantt-task-bg: #409eff;
--gantt-task-border: #66b1ff;
--gantt-task-text: #ffffff;
}
Component has built-in Chinese (zh-CN) and English (en-US), can switch via toolbar button:
<template>
<GanttChart :tasks="tasks" :on-language-change="handleLanguageChange" />
</template>
<script setup lang="ts">
const handleLanguageChange = (lang: 'zh-CN' | 'en-US') => {
console.log('Language switched to:', lang)
// Can save user preference settings to localStorage here
localStorage.setItem('gantt-language', lang)
}
</script>
Pass custom multilingual text via the localeMessages prop, which will be automatically merged into the default translations:
<template>
<GanttChart :tasks="tasks" :locale-messages="customMessages" />
</template>
<script setup lang="ts">
const customMessages = {
"zh-CN": {
// Task list related
taskName: 'Task Name (Custom)',
startDate: 'Start Date',
endDate: 'End Date',
duration: 'Duration',
progress: 'Completion',
predecessor: 'Predecessors',
assignee: 'Assignee',
estimatedHours: 'Estimated Hours',
actualHours: 'Actual Hours',
// Custom fields (supports nested structure)
department: 'Department',
status: 'Status',
gantt: {
planEndDate: 'Plan End Date',
planStartDate: 'Plan Start Date'
}
},
"en-US": {
department: 'Department',
status: 'Status',
gantt: {
planEndDate: 'Plan End Date',
planStartDate: 'Plan Start Date'
}
}
}
</script>
๐ก Tip:
localeMessagesuses a deep merge strategy, only pass fields that need to be overridden or added- Supports nested object structures, such as
gantt.planEndDate- For complete built-in translation keys, please refer to
useI18n.tsin the component source code
The component exports the useI18n composable, which can be used in custom slots to access translation text. It supports two access methods:
Method 1: Reactive Access (t.field)
Access translation text directly through a reactive object, with concise syntax, suitable for use in templates:
<script setup>
import { GanttChart, TaskListColumn, useI18n } from 'jordium-gantt-vue3'
const { t } = useI18n()
const customMessages = {
'zh-CN': {
department: 'Department'
}
}
</script>
<template>
<GanttChart :tasks="tasks" :locale-messages="customMessages" />
<!-- Reactive access: directly through the t object -->
<TaskListColumn prop="startDate" label="Start Date" width="250">
<template #header>
<strong style="color: #1890ff;">{{ t.department }}</strong>
</template>
</TaskListColumn>
</template>
Method 2: Function Access (getTranslation())
Supports nested keys and default values, suitable for accessing deep structures or dynamic keys:
<script setup>
import { GanttChart, TaskListColumn, useI18n } from 'jordium-gantt-vue3'
const { getTranslation } = useI18n()
const customMessages = {
'en-US': {
gantt: {
planEndDate: 'Plan End Date'
}
}
}
</script>
<template>
<GanttChart :tasks="tasks" :locale-messages="customMessages" />
<!-- Function access: supports nested keys and default values -->
<TaskListColumn prop="endDate" :label="getTranslation('gantt.planEndDate')" width="250" />
</template>
Complete Example (with Language Switching):
<script setup>
import { ref } from 'vue'
import { GanttChart, TaskListColumn, useI18n } from 'jordium-gantt-vue3'
// Custom multilingual configuration
const customMessages = {
'zh-CN': {
department: '้จ้จ',
gantt: {
planEndDate: '่ฎกๅ็ปๆๆถ้ด'
}
},
'en-US': {
department: 'Department',
gantt: {
planEndDate: 'Plan End Date'
}
}
}
// Use useI18n to access translations
const { t, getTranslation, locale, setLocale } = useI18n()
const tasks = ref([...])
// Language switching
const switchLanguage = () => {
setLocale(locale.value === 'zh-CN' ? 'en-US' : 'zh-CN')
}
</script>
<template>
<button @click="switchLanguage">Switch Language</button>
<GanttChart :tasks="tasks" :locale-messages="customMessages" />
<!-- Reactive access: directly through the t object -->
<TaskListColumn prop="startDate" label="Start Date" width="250">
<template #header>
<strong style="color: #1890ff;">{{ t.department }}</strong>
</template>
</TaskListColumn>
<!-- Function access: supports nested keys and default values -->
<TaskListColumn prop="endDate" :label="getTranslation('gantt.planEndDate')" width="250" />
</template>
useI18n API Reference:
| Export | Type | Description |
|---|---|---|
t | Ref<object> | Reactive translation object, access via t.key or t.nested.key |
getTranslation(key, defaultValue?) | Function | Function-based access to translation text โข key: Translation key, supports nested paths (e.g., 'gantt.planEndDate')โข defaultValue: Optional, default value returned when translation is not foundโข Returns: Translation text, default value, or the key itself |
formatTranslation(key, params) | Function | Format translation text with parameters โข key: Translation keyโข params: Parameter object, e.g., { name: 'Task1' }โข Returns: Text with placeholders replaced (e.g., 'Task {name}' โ 'Task Task1') |
locale | Ref<string> | Current language ('zh-CN' or 'en-US') |
setLocale(locale) | Function | Switch language, automatically updates all components using useI18n |
formatYearMonth(year, month) | Function | Format year-month display โข Chinese: formatYearMonth(2024, 3) โ '2024ๅนด03ๆ'โข English: formatYearMonth(2024, 3) โ '2024/03' |
formatMonth(month) | Function | Format month display โข Chinese: formatMonth(3) โ '3ๆ'โข English: formatMonth(3) โ '03' |
Usage Examples:
<script setup>
import { useI18n } from 'jordium-gantt-vue3'
const { t, getTranslation, formatTranslation, formatYearMonth, formatMonth } = useI18n()
// 1. Basic access
const text1 = t.taskName // 'Task Name'
const text2 = getTranslation('gantt.planEndDate', 'Plan End') // 'Plan End Date' or default value
// 2. Translation with parameters
const message = formatTranslation('taskNotFound', { id: '123' }) // 'Task not found for update, ID: 123'
// 3. Date formatting
const yearMonth = formatYearMonth(2024, 3) // '2024ๅนด03ๆ' (zh-CN) or '2024/03' (en-US)
const month = formatMonth(3) // '3ๆ' (zh-CN) or '03' (en-US)
</script>
๐ก Usage Tips:
- Reactive access (
t.key): Concise syntax, suitable for direct use in templates- Function access (
getTranslation('nested.key', 'default')): Supports nested keys and default values, suitable for accessing deep structures- Pass custom translations via the
localeMessagesprop, then access viauseI18nto translate- Language switching is implemented via
setLocale(), all components will automatically respond to updates
Component provides slots support, allowing custom task content renderingใ
taskbar-tooltip Slot Fully replaces the built-in TaskBar hover tooltip content. When used, the built-in tooltip is no longer rendered โ the consumer has complete control.
Prerequisite: Requires
:enable-taskbar-tooltip="true"(enabled by default)
Slot scope parameters (TaskbarTooltipSlotScope):
The component provides data via
<slot :task="..." :task-status="..." />. The consumer can receive the wholescopeobject or destructure only what's needed โ all parameters are optional.taskis the raw task object from your:tasks/:resourcesdata, including any custom fields.
| Param | Type | Description |
|---|---|---|
task | Task | The currently hovered task object with complete task data |
taskStatus | { color: string; label: string } | Pre-computed task status (color + label) for quick display |
resourcePercent | number | null | Resource usage percentage (valid in resource view; null in task view) |
All three patterns are equivalent:
<!-- Pattern 1: whole scope object -->
<template #taskbar-tooltip="scope">
{{ scope.task.name }}
</template>
<!-- Pattern 2: destructure only what you need -->
<template #taskbar-tooltip="{ task }">
{{ task.name }}
</template>
<!-- Pattern 3: destructure all fields -->
<template #taskbar-tooltip="{ task, taskStatus, resourcePercent }">
...
</template>
Example โ minimal usage (task name only):
<GanttChart :tasks="tasks">
<template #taskbar-tooltip="{ task }">
<div class="my-simple-tooltip">{{ task.name }}</div>
</template>
</GanttChart>
Example โ using full scope:
<GanttChart :tasks="tasks">
<template #taskbar-tooltip="{ task, taskStatus, resourcePercent }">
<div class="my-tooltip">
<div class="tooltip-title" :style="{ borderColor: taskStatus.color }">
{{ task.name }}
</div>
<div>Status: {{ taskStatus.label }}</div>
<div>Start: {{ task.startDate }}</div>
<div>End: {{ task.endDate }}</div>
<div v-if="resourcePercent !== null">Resource: {{ resourcePercent }}%</div>
</div>
</template>
</GanttChart>
milestone-tooltip Slot Fully replaces the built-in milestone hover Tooltip content. When used, the built-in tooltip is no longer rendered โ the consumer has complete control.
Prerequisite: Requires
:enable-milestone-tooltip="true"(enabled by default)
Slot scope parameters (MilestoneTooltipSlotScope):
| Param | Type | Description |
|---|---|---|
milestone | Milestone | The currently hovered milestone object with complete milestone data |
Example:
<GanttChart :tasks="tasks">
<template #milestone-tooltip="{ milestone }">
<div class="my-milestone-tooltip">
<div class="title">{{ milestone.name }}</div>
<div>Target Date: {{ milestone.startDate }}</div>
</div>
</template>
</GanttChart>
custom-task-content SlotsUsed to customize task display content in task list (TaskRow) and timeline (TaskBar).
Slot Parameters๏ผ
| Parameter | Type | Source | Description |
|---|---|---|---|
type | 'task-row' | 'task-bar' | Common | Slot call position identifier |
task | Task | Common | Current task object |
TaskRow specific parameters (when type === 'task-row'):
| Parameter | Type | Description |
|---|---|---|
isRowContent | boolean | Identified as row content |
level | number | Task level |
indent | string | Indent style |
isHovered | boolean | Whether hovering |
hoveredTaskId | number | null | Current hovering task ID |
isParent | boolean | Whether parent task |
hasChildren | boolean | Whether has child tasks |
collapsed | boolean | Whether collapsed |
formattedTimer | string | Formatted timer text |
timerRunning | boolean | Whether timer is running |
timerElapsed | number | Elapsed time |
isOvertime | number | boolean | undefined | Whether overtime |
overdueDays | number | Overdue days |
overtimeText | string | Overtime text |
overdueText | string | Overdue text |
daysText | string | Days text |
progressClass | string | Progress CSS class name |
TaskBar specific parameters (when type === 'task-bar'):
| Parameter | Type | Description |
|---|---|---|
status | object | TaskStatus object, contains type, color, bgColor, borderColor |
statusType | string | StatusType๏ผ'completed', 'delayed', 'in-progress', 'not-started', 'parent' |
isParent | boolean | Whether parent task |
progress | number | TaskProgress๏ผ0-100๏ผ |
currentTimeScale | TimelineScale | Current time scale |
rowHeight | number | Row height (pixels) |
dayWidth | number | Width per day (pixels) |
Usage Example๏ผ
<template>
<GanttChart :tasks="tasks">
<template #custom-task-content="slotProps">
<!-- Render different content based on type -->
<CustomTaskContent :task="slotProps.task" :type="slotProps.type" :status="slotProps.status" />
</template>
</GanttChart>
</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 type { Task } from 'jordium-gantt-vue3'
import CustomTaskContent from './CustomTaskContent.vue'
const tasks = ref<Task[]>([
{
id: 1,
name: '<strong>Important Task</strong>',
startDate: '2025-01-01',
endDate: '2025-01-10',
progress: 50,
},
])
</script>
Custom Content Component Example๏ผ
<!-- CustomTaskContent.vue -->
<script setup lang="ts">
import type { Task } from 'jordium-gantt-vue3'
interface Props {
task: Task
type: 'task-row' | 'task-bar'
status?: {
type: string
color: string
bgColor: string
borderColor: string
}
}
const props = defineProps<Props>()
</script>
<template>
<div class="custom-task-content">
<!-- Rendering in TaskRow -->
<div v-if="type === 'task-row'" class="task-row-content">
<span v-html="task.name" />
</div>
<!-- Rendering in TaskBar -->
<div v-else-if="type === 'task-bar'" class="task-bar-content">
<div class="task-icon" :style="{ color: status?.color }">๐</div>
<span class="task-title" v-html="task.name" />
</div>
</div>
</template>
<style scoped>
.custom-task-content {
width: 100%;
height: 100%;
}
.task-row-content {
padding: 0 4px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.task-bar-content {
display: flex;
align-items: center;
gap: 4px;
padding: 0 8px;
overflow: hidden;
}
.task-icon {
flex-shrink: 0;
font-size: 14px;
}
.task-title {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
</style>
๐ก Usage Scenarios๏ผ
- Support HTML formatted task names
- Add custom icons, tags or badges
- Display different styles based on task status
- Integrate third-party rich text rendering
- Display additional business information
โ ๏ธ Notes๏ผ
- Slot content will be rendered in both TaskRow and TaskBar
- Need to distinguish rendering position based on
typeparameter- TaskRow and TaskBar have different available space, need to adapt layout
- Avoid using overly complex components in slot content, may affect performance
Used to customize the context menu content for TaskRow (task list row).
Menu Display Logic:
enableTaskListContextMenu=true and TaskListContextMenu component is not declared โ Uses built-in context menuenableTaskListContextMenu=true and TaskListContextMenu component is declared โ Uses custom context menuenableTaskListContextMenu=false โ Context menu is completely disabled (regardless of component declaration)Slot List:
| Slot Name | Parameters | Description |
|---|---|---|
default | scope: { row: Task, $index: number } | Custom Task List Context Menu content. Access current task object via scope.row, access task index via scope.$index. |
Usage Example:
<template>
<GanttChart
:tasks="tasks"
:enable-task-list-context-menu="true"
>
<TaskListContextMenu>
<template #default="scope">
<div class="custom-menu">
<div class="menu-item" @click="handleEdit(task); onClose()">
โ๏ธ Edit Task
</div>
<div class="menu-item" @click="handleDelete(task); onClose()">
๐๏ธ Delete Task
</div>
<div class="menu-divider"></div>
<div class="menu-item" @click="handleDuplicate(task); onClose()">
๐ Duplicate Task
</div>
</div>
</template>
</TaskListContextMenu>
</GanttChart>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { GanttChart, TaskListContextMenu } from 'jordium-gantt-vue3'
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
import type { Task } from 'jordium-gantt-vue3'
const tasks = ref<Task[]>([])
const handleEdit = (task: Task) => {
console.log('Edit task:', task)
}
const handleDelete = (task: Task) => {
console.log('Delete task:', task)
}
const handleDuplicate = (task: Task) => {
console.log('Duplicate task:', task)
}
</script>
<style scoped>
.custom-context-menu {
position: fixed;
background: white;
border: 1px solid #e0e0e0;
border-radius: 4px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
z-index: 9999;
min-width: 120px;
}
.menu-item {
padding: 8px 16px;
cursor: pointer;
transition: background-color 0.2s;
}
.menu-item:hover {
background-color: #f5f5f5;
}
.menu-divider {
height: 1px;
background-color: #e0e0e0;
margin: 4px 0;
}
</style>
Used to customize the context menu content for TaskBar (timeline task bar).
Menu Display Logic:
enableTaskBarContextMenu=true and TaskBarContextMenu component is not declared โ Uses built-in context menuenableTaskBarContextMenu=true and TaskBarContextMenu component is declared โ Uses custom context menuenableTaskBarContextMenu=false โ Context menu is completely disabled (regardless of component declaration)Slot List:
| Slot Name | Parameters | Description |
|---|---|---|
default | scope: { row: Task, $index: number } | Custom Task Bar Context Menu content. Access current task object via scope.row, access task index via scope.$index. |
Usage Example:
<template>
<GanttChart
:tasks="tasks"
:enable-task-bar-context-menu="true"
>
<TaskBarContextMenu>
<template #default="scope">
<div class="custom-context-menu">
<div class="menu-item" @click="handleViewDetails(scope.row)">
๐๏ธ View Details
</div>
<div class="menu-item" @click="handleAdjustTime(scope.row)">
โฐ Adjust Time
</div>
<div class="menu-divider"></div>
<div class="menu-item" @click="handleSetDependency(scope.row)">
๐ Set Dependency
</div>
</div>
</template>
</TaskBarContextMenu>
</GanttChart>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { GanttChart, TaskBarContextMenu } from 'jordium-gantt-vue3'
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
import type { Task } from 'jordium-gantt-vue3'
const tasks = ref<Task[]>([])
const handleViewDetails = (task: Task) => {
console.log('View details:', task)
}
const handleAdjustTime = (task: Task) => {
console.log('Adjust time:', task)
}
const handleSetDependency = (task: Task) => {
console.log('Set dependency:', task)
}
</script>
<style scoped>
.custom-context-menu {
position: fixed;
background: white;
border: 1px solid #e0e0e0;
border-radius: 4px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
z-index: 9999;
min-width: 120px;
}
.menu-item {
padding: 8px 16px;
cursor: pointer;
transition: background-color 0.2s;
}
.menu-item:hover {
background-color: #f5f5f5;
}
.menu-divider {
height: 1px;
background-color: #e0e0e0;
margin: 4px 0;
}
</style>
๐ก Usage Scenarios๏ผ
- Customize context menu styles and layouts
- Add business-specific operations
- Dynamically show menu items based on task status
- Add permission control logic
- Integrate third-party UI component library menus
โ ๏ธ Notes๏ผ
- When
enableTaskListContextMenu=falseorenableTaskBarContextMenu=false, the menu will not be displayed even if the component is declared- Context menus will automatically close when scrolling or clicking outside the menu
- Recommended to use
<Teleport to="body">to render the menu under body to avoid positioning and z-index issues- Remember to call
onClose()after menu item clicks to close the menu- TaskRow and TaskBar context menus are independent and can be customized separately
- By default, the system provides a built-in context menu with common operations
- Declarative components do not render any content, only used for passing configuration
The TaskListColumn component provides two slots for customizing task list column headers and cell content. Must be used in declarative mode (taskListColumnRenderMode="declarative").
Slot List:
| Slot Name | Parameters | Description |
|---|---|---|
header | - | Custom column header content. If not provided, the label prop will be displayed |
default | scope: { row: Task, $index: number } | Custom column cell content. Access current task object via scope.row, access task index via scope.$index. If not provided, the value of prop field is shown |
Usage Example:
<template>
<GanttChart
:tasks="tasks"
task-list-column-render-mode="declarative"
>
<!-- Basic column definition without slots -->
<TaskListColumn prop="name" label="Task Name" width="300" />
<!-- Custom column header using header slot -->
<TaskListColumn prop="assignee" width="150" align="center">
<template #header>
<div style="display: flex; align-items: center; gap: 4px; color: #409eff;">
<span>๐ค</span>
<span>Assignee</span>
</div>
</template>
</TaskListColumn>
<!-- Custom cell content using default slot -->
<TaskListColumn prop="progress" label="Progress" width="150" align="center">
<template #default="scope">
<div style="display: flex; align-items: center; gap: 8px;">
<div
style="
flex: 1;
height: 8px;
background: #f0f0f0;
border-radius: 4px;
overflow: hidden;
"
>
<div
:style="{
width: `${scope.row.progress || 0}%`,
height: '100%',
background: scope.row.progress >= 100 ? '#67c23a' : scope.row.progress >= 50 ? '#409eff' : '#e6a23c',
transition: 'width 0.3s'
}"
></div>
</div>
<span style="min-width: 40px; text-align: right; font-size: 12px;">
{{ scope.row.progress || 0 }}%
</span>
</div>
</template>
</TaskListColumn>
<!-- Using both header and default slots -->
<TaskListColumn prop="startDate" width="160">
<template #header>
<div style="color: #67c23a; font-weight: bold;">๐
Start Date</div>
</template>
<template #default="scope">
<div style="display: flex; align-items: center; gap: 4px;">
<span style="color: #909399; font-size: 12px;">๐</span>
<span>{{ scope.row.startDate || '-' }}</span>
</div>
</template>
</TaskListColumn>
<!-- Dynamic styling based on data -->
<TaskListColumn prop="status" label="Status" width="120" align="center">
<template #default="scope">
<span
:style="{
padding: '2px 8px',
borderRadius: '4px',
fontSize: '12px',
color: 'white',
background: scope.row.progress >= 100 ? '#67c23a' :
scope.row.progress > 0 ? '#409eff' : '#909399'
}"
>
{{ scope.row.progress >= 100 ? 'Completed' : scope.row.progress > 0 ? 'In Progress' : 'Not Started' }}
</span>
</template>
</TaskListColumn>
</GanttChart>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { GanttChart, TaskListColumn } from 'jordium-gantt-vue3'
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
import type { Task } from 'jordium-gantt-vue3'
const tasks = ref<Task[]>([
{
id: 1,
name: 'Project Planning',
assignee: 'John',
startDate: '2025-01-01',
endDate: '2025-01-10',
progress: 100,
},
{
id: 2,
name: 'Requirements Analysis',
assignee: 'Jane',
startDate: '2025-01-11',
endDate: '2025-01-20',
progress: 60,
},
{
id: 3,
name: 'Development',
assignee: 'Bob',
startDate: '2025-01-21',
endDate: '2025-02-10',
progress: 0,
},
])
</script>
๐ก Usage Scenarios:
- Custom column header icons, styles, or sorting buttons
- Display visual components like progress bars, status badges
- Dynamically adjust styles and content based on task data
- Add action buttons (edit, delete, etc.)
- Integrate third-party UI components (ratings, tag selectors, etc.)
โ ๏ธ Notes:
- Slots must be used in
task-list-column-render-mode="declarative"mode- The
defaultslot receives ascopeparameter, access current task object viascope.row(Task type), access index viascope.$index- Recommended to use
scope.rowinstead of destructuring{ row }for code clarity and consistencyscope.$indexis the index of the current task in the visible list (not global index)- Slot content is rendered in each task row, avoid overly complex components for performance
- When column width is fixed, slot content should consider overflow handling (text ellipsis, auto-wrap, etc.)
โ ๏ธ Important Notice: This slot approach based on
TaskListColumnConfigconfiguration is deprecated and will be removed soon. Strongly recommend using the declarative approach with TaskListColumn Slots above for better type hints and code maintainability.
In default mode (taskListColumnRenderMode="default"), you can customize columns defined in TaskListColumnConfig via slots. Slot names are based on the key property in column configuration.
Slot List:
| Slot Pattern | Parameters | Description |
|---|---|---|
header-{key} | - | Custom header content for specified column. {key} is the key value defined in TaskListColumnConfig |
column-{key} | { task: Task, column: TaskListColumnConfig, value: any } | Custom cell content for specified column. task is current task object, column is column config, value is the column's value |
Usage Example:
<template>
<GanttChart
:tasks="tasks"
:task-list-config="taskListConfig"
task-list-column-render-mode="default"
>
<!-- Custom 'name' column header -->
<template #header-name>
<div style="display: flex; align-items: center; gap: 6px;">
<img src="/avatar.png" width="32" height="32" style="border-radius: 50%;" />
<strong style="font-size: 14px;">Task Name</strong>
</div>
</template>
<!-- Custom 'name' column cell content -->
<template #column-name="{ task, column, value }">
<div style="display: flex; align-items: center; gap: 6px;">
<img src="/user-avatar.png" width="20" height="20" style="border-radius: 50%;" />
<span v-html="value"></span>
<span
v-if="task.priority"
style="
background: linear-gradient(135deg, #ff6b6b 0%, #ee5a6f 100%);
color: white;
padding: 2px 6px;
border-radius: 10px;
font-size: 11px;
font-weight: 600;
"
>
P-{{ task.priority }}
</span>
</div>
</template>
<!-- Custom other columns, e.g., custom field 'custom' -->
<template #column-custom="{ task, column, value }">
<div style="display: flex; align-items: center; gap: 4px;">
<span
v-if="typeof value === 'number'"
style="
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 2px 8px;
border-radius: 12px;
font-size: 12px;
font-weight: 600;
"
>
๐ฐ {{ value.toLocaleString() }}
</span>
<span
v-else-if="typeof value === 'string'"
style="
background: #e8f5e9;
color: #2e7d32;
padding: 2px 8px;
border-radius: 4px;
font-size: 12px;
border: 1px solid #81c784;
"
>
๐ {{ value }}
</span>
<span v-else style="color: #999;">-</span>
</div>
</template>
</GanttChart>
</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 type { Task, TaskListConfig, TaskListColumnConfig } from 'jordium-gantt-vue3'
const tasks = ref<Task[]>([
{
id: 1,
name: 'Project Planning',
startDate: '2025-01-01',
endDate: '2025-01-10',
progress: 100,
priority: 1,
custom: 50000, // Custom field
},
{
id: 2,
name: 'Requirements Analysis',
startDate: '2025-01-11',
endDate: '2025-01-20',
progress: 60,
custom: 'Important', // Custom field
},
])
// Define column configuration
const taskListConfig = ref<TaskListConfig>({
columns: [
{ key: 'taskName', label: 'Task Name', visible: true, width: 300 },
{ key: 'assignee', label: 'Assignee', visible: true, width: 150 },
{ key: 'progress', label: 'Progress', visible: true, width: 100 },
{ key: 'custom', label: 'Custom', visible: true, width: 150 }, // Custom column
],
})
</script>
๐ก Usage Notes:
- Slot name format:
header-{key}andcolumn-{key}, where{key}corresponds toTaskListColumnConfig.keycolumn-{key}slot receives three parameters:
task: Current task object (Task type)column: Current column configuration object (TaskListColumnConfig type)value: Current cell value (automatically obtained fromtask[column.key])- You can mix slots and default rendering, only define slots for columns that need customization
โ ๏ธ Migration Recommendation:
- Strongly recommend migrating to declarative mode: Use
task-list-column-render-mode="declarative"withTaskListColumncomponent- Declarative mode provides better type hints, code organization, and maintainability
- This configuration-based slot approach will be removed in future versions, please migrate soon
- For migration examples, see TaskListColumn Slots section
See Contributing Guide for details.
jordium-gantt-vue3/
โโโ src/ # Source code
โ โโโ components/ # Vue components
โ โ โโโ GanttChart.vue # Main Gantt chart component
โ โ โโโ GanttToolbar.vue # Toolbar component
โ โ โโโ Timeline.vue # Timeline component
โ โ โโโ TaskBar.vue # Task bar component
โ โ โโโ TaskRow.vue # Task row component
โ โ โโโ TaskDrawer.vue # Task editing drawer
โ โ โโโ MilestonePoint.vue # Milestone point component
โ โ โโโ MilestoneDialog.vue # Milestone editing dialog
โ โ โโโ TaskContextMenu.vue # Context menu component
โ โ โโโ TaskList/ # Task list module (modular refactoring)
โ โ โ โโโ TaskList.vue # Task list main component
โ โ โ โโโ TaskListColumn.vue # Declarative column component
โ โ โ โโโ index.ts # Module exports
โ โ โ โโโ composables/ # Task list related composables
โ โ โ โโโ useTaskListLayout.ts # Virtual scrolling and layout calculation
โ โ โ โโโ useTaskListColumns.ts # Column configuration management
โ โ โ โโโ useTaskListResize.ts # Container size management
โ โ โ โโโ useTaskListEventHandlers.ts # Event handling logic
โ โ โ โโโ useTaskParentCalculation.ts # Parent task data calculation
โ โ โโโ index.ts # Unified component exports
โ โโโ models/ # Data models
โ โ โโโ classes/ # Class definitions
โ โ โ โโโ Task.ts # Task class
โ โ โ โโโ Milestone.ts # Milestone class
โ โ โ โโโ Language.ts # Language configuration class
โ โ โโโ configs/ # Configuration interfaces
โ โ โ โโโ TaskListConfig.ts # Task list configuration
โ โ โ โโโ TaskBarConfig.ts # Task bar configuration
โ โ โ โโโ TimelineConfig.ts # Timeline configuration
โ โ โ โโโ ToolbarConfig.ts # Toolbar configuration
โ โ โโโ types/ # Type definitions
โ โ โโโ TimelineDataTypes.ts # Timeline data types
โ โ โโโ TimelineScale.ts # Timeline scale types
โ โ โโโ TimelineCompat.ts # Compatibility types
โ โโโ composables/ # Global composables
โ โ โโโ useI18n.ts # Internationalization
โ โ โโโ useMessage.ts # Message notifications
โ โ โโโ useTaskRowDrag.ts # Task row dragging
โ โ โโโ useTaskListColumns.ts # Column configuration (migrated to TaskList/composables)
โ โโโ styles/ # Style files
โ โ โโโ app.css # Main styles
โ โ โโโ list.css # List styles
โ โ โโโ theme-variables.css # Theme variables
โ โ โโโ index.ts # Style exports
โ โโโ utils/ # Utility functions
โ โ โโโ canvasUtils.ts # Canvas utilities
โ โ โโโ perfMonitor.ts # Performance monitoring
โ โ โโโ predecessorUtils.ts # Predecessor task utilities
โ โ โโโ taskTreeUtils.ts # Task tree utilities
โ โโโ index.ts # Main entry file
โโโ demo/ # Online demo code
โ โโโ App.vue # Demo application
โ โโโ data.json # Sample data
โ โโโ locales/ # Locale files
โ โโโ ...
โโโ docs/ # Documentation
โ โโโ TaskList-้ๆๆต่ฏๆธ
ๅ.md # Refactoring documentation
โโโ npm-demo/ # NPM package usage example
โโโ npm-webpack-demo/ # Webpack integration example
โโโ public/ # Public assets
โ โโโ assets/ # Static resources
โโโ package.json # Project configuration
The TaskList component has been deeply refactored with a modular design, improving code maintainability:
Refactoring Results:
Composables Responsibility Division:
useTaskListLayout - Virtual scrolling and layout calculationuseTaskListColumns - Column configuration management and style calculationuseTaskListResize - Container size monitoring and ResizeObserver managementuseTaskListEventHandlers - Global event handling and scroll synchronizationuseTaskParentCalculation - Parent task data calculation and task tree traversalAll theme colors, spacing, and z-index values are managed through CSS custom properties defined in src/styles/theme-variables.css. All components reference variables rather than hardcoded values โ changing a theme only requires modifying the variables for global effect.
Naming convention: --gantt-{category}-{variant}. Existing groups: bg (backgrounds), text (text colors), border (borders), z (z-index layers).
Following Element Plus design conventions, z-index is divided into viewport-level and component-local systems, with all hardcoded values replaced by semantic variables.
| Variable | Value | Scope | Stacking Context | Usage |
|---|---|---|---|---|
--gantt-z-overlay | 9999 | Viewport | viewport (position:fixed, escapes all SC) | position:fixed tooltips, context menus, fullscreen; must exceed host page modals |
--gantt-z-toggle | 1000 | Container | host/document initial SC | Task list toggle button, avoid being covered by sticky header |
--gantt-z-sticky | 30 | Container | host/document initial SC | Focus close button, year-view today-line and similar floating decorations |
--gantt-z-canvas-hl | 20 | Container | host/document initial SC | GanttLinks canvas highlighted state (must > --gantt-z-row:11) |
--gantt-z-canvas | 12 | Container | host/document initial SC | GanttLinks canvas normal state |
--gantt-z-row | 11 | Container | task-bar-container SC | .task-row / .resource-row (establishes local stacking context) |
--gantt-z-container | 10 | Container | host/document initial SC | .task-bar-container (establishes local stacking context) |
--gantt-z-bar-drag | 40 | Row-local | task-row SC | task-bar / milestone while dragging (highest interaction layer) |
--gantt-z-avatar | 35 | Row-local | task-row SC | Avatar stacking layer |
--gantt-z-actual-hl-pri | 32 | Row-local | task-row SC | actual-bar primary-highlight |
--gantt-z-bar-hl-pri | 30 | Row-local | task-row SC | task-bar primary-highlight |
--gantt-z-actual-hl | 28 | Row-local | task-row SC | actual-bar highlighted |
--gantt-z-bar-hl | 26 | Row-local | task-row SC | task-bar highlighted |
--gantt-z-milestone-sticky | 24 | Row-local | task-row SC | Milestone sticky mode (pinned to left edge) |
--gantt-z-conflict | 22 | Row-local | task-row SC | GanttConflicts canvas |
--gantt-z-milestone | 20 | Row-local | task-row SC | Milestone normal state |
--gantt-z-bar-bubble | 18 | Row-local | task-row SC | task-bar has-bubble indicator |
--gantt-z-bar-hover | 15 | Row-local | task-row SC | task-bar hover / has-actual:hover |
--gantt-z-bar-actual | 12 | Row-local | task-row SC | actual-bar (actual progress bar) |
--gantt-z-bar | 10 | Row-local | task-row SC | task-bar base layer |
Three-tier stacking context isolation model:
.gantt-roothas noposition/z-indexโ does not create a stacking context; the component participates in the host document's default stacking order..task-bar-container(position:absolute + z-index:10) creates SC โ.task-row(z-index:11) sorts only within this context..task-row(position:absolute + z-index:11) creates SC โ TaskBar / Milestone and all row-local elements sort only within the row, fully isolated from outside the component.Impact on host application:
position:fixedoverlays (--gantt-z-overlay: 9999, including tooltips, context menus, fullscreen) escape all stacking contexts and can cover host page elements with z-index < 9999. If the host modal is โฅ 9999, override the variable as needed::root { --gantt-z-overlay: 500; }.- All other positioned elements (z-index 10 ~ 1000) are constrained by the DOM layout box and rarely conflict with host content in practice. For complete isolation, add
isolation: isolateon the host's gantt container.
| Element | Governance | Reason |
|---|---|---|
position:fixed loading overlay | 10000 (kept hardcoded) | Dynamically created temporary DOM, extremely short lifecycle |
.task-bar.dimmed | 1 (kept hardcoded) | Intentionally lowest, not part of regular layer competition |
::before/::after inner elements | 1/-1 (kept hardcoded) | No external stacking context impact |
Welcome to submit Issues and Pull Requests!
For detailed contributing guide, please see CONTRIBUTING.mdใ
Thanks to all developers who contributed to this project๏ผ
View the complete Contributors listใ
For common issues and solutions, please refer to Troubleshooting-EN.md.
MIT License ยฉ 2025 JORDIUM.COM
Buy the author a coffee โ โ it keeps the motivation going and the project moving forward!
ย ย ย ย
WeChat Pay ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย Alipay
๐ A โญ Star on GitHub is also a wonderful way to show support โ it helps others discover this project!
If this project helps you, please give it a โญ๏ธ to support it!
FAQs
A Vue 3 Gantt chart component for project management, task scheduling, resource planning and timeline visualization.
The npm package jordium-gantt-vue3 receives a total of 618 weekly downloads. As such, jordium-gantt-vue3 popularity was classified as not popular.
We found that jordium-gantt-vue3 demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago.ย It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Company News
Socket won two 2026 Reppy Awards from RepVue, ranking in the top 5% of all sales orgs. AE Alexandra Lister shares what it's like to grow a sales career here.

Security News
NIST will stop enriching most CVEs under a new risk-based model, narrowing the NVD's scope as vulnerability submissions continue to surge.

Company News
/Security News
Socket is an initial recipient of OpenAI's Cybersecurity Grant Program, which commits $10M in API credits to defenders securing open source software.