v 1.4.2 - 徽章更新

This commit is contained in:
LINING-PC\lining
2025-11-03 14:49:10 +08:00
parent cf888f411d
commit 308a8d1421
2 changed files with 751 additions and 763 deletions
+120 -123
View File
@@ -2,7 +2,10 @@
<p align="center">
<a href="https://www.npmjs.com/package/jordium-gantt-vue3">
<img src="https://img.shields.io/npm/v/jordium-gantt-vue3.svg" alt="npm version">
<img src="https://img.shields.io/npm/v/jordium-gantt-vue3?style=flat-square" alt="npm version">
</a>
<a href="https://www.npmjs.com/package/jordium-gantt-vue3">
<img src="https://img.shields.io/npm/dt/jordium-gantt-vue3?style=flat-square" alt="npm total">
</a>
<a href="https://opensource.org/licenses/MIT">
<img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT License">
@@ -105,10 +108,7 @@ Create your first Gantt chart:
```vue
<template>
<div style="height: 600px;">
<GanttChart
:tasks="tasks"
:milestones="milestones"
/>
<GanttChart :tasks="tasks" :milestones="milestones" />
</div>
</template>
@@ -123,7 +123,7 @@ const tasks = ref([
name: 'Project Kickoff',
startDate: '2025-01-01',
endDate: '2025-01-10',
progress: 100
progress: 100,
},
{
id: 2,
@@ -131,7 +131,7 @@ const tasks = ref([
startDate: '2025-01-11',
endDate: '2025-01-20',
progress: 80,
predecessor: [1]
predecessor: [1],
},
{
id: 3,
@@ -139,8 +139,8 @@ const tasks = ref([
startDate: '2025-01-21',
endDate: '2025-02-05',
progress: 50,
predecessor: [2]
}
predecessor: [2],
},
])
const milestones = ref([
@@ -148,8 +148,8 @@ const milestones = ref([
id: 101,
name: 'Project Approval',
date: '2025-01-01',
type: 'milestone'
}
type: 'milestone',
},
])
</script>
```
@@ -158,6 +158,7 @@ const milestones = ref([
<span><strong>Recommended: <a href="https://dovee.cc/a.php?anaxjgyz1ozZq2B">DOVE</a> VPN for fast and stable access.</strong></span> <span style="color:red;">(Note: Please use VPN services legally)</span>
## 🌞 NPM Package Usage Example
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.
@@ -168,6 +169,7 @@ npm install element-plus
npm install jordium-gantt-vue3
npm run dev
```
---
## 📖 Component Guide
@@ -179,7 +181,7 @@ npm run dev
#### Basic Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| --------------------------- | --------- | ------- | ------------------------------------------------------------------------- |
| `tasks` | `Task[]` | `[]` | Array of task data |
| `milestones` | `Task[]` | `[]` | Array of milestone data (Note: Type is Task[], must set type='milestone') |
| `showToolbar` | `boolean` | `true` | Whether to show the toolbar |
@@ -193,7 +195,7 @@ npm run dev
For complete configuration object documentation, see [⚙️ Configuration & Customization](#⚙️-configuration--customization) section.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| ---------------- | ---------------------------- | ----------------------------------------------------------------------- | ---------------------------- |
| `toolbarConfig` | `ToolbarConfig` | `{}` | Toolbar configuration |
| `taskListConfig` | `TaskListConfig` | `undefined` | Task list configuration |
| `taskBarConfig` | `TaskBarConfig` | `undefined` | Task bar style configuration |
@@ -203,7 +205,7 @@ For complete configuration object documentation, see [⚙️ Configuration & Cus
#### Callback Props
| 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 |
@@ -216,13 +218,14 @@ For complete configuration object documentation, see [⚙️ Configuration & Cus
#### Component Events
For complete event documentation, see:
- **Task-related events**: See [Task Management](#task-management) section below
- **Milestone-related events**: See [Milestone Management](#milestone-management) section below
**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 |
@@ -261,8 +264,8 @@ const tasks = ref([
name: 'Task 1',
startDate: '2025-01-01',
endDate: '2025-01-10',
progress: 100
}
progress: 100,
},
])
</script>
```
@@ -272,10 +275,7 @@ const tasks = ref([
```vue
<template>
<div style="height: 600px;">
<GanttChart
:tasks="tasks"
:milestones="milestones"
/>
<GanttChart :tasks="tasks" :milestones="milestones" />
</div>
</template>
@@ -290,8 +290,8 @@ const tasks = ref([
name: 'Project Kickoff',
startDate: '2025-01-01',
endDate: '2025-01-10',
progress: 100
}
progress: 100,
},
])
const milestones = ref([
@@ -300,8 +300,8 @@ const milestones = ref([
name: 'Project Approval',
startDate: '2025-01-01',
type: 'milestone',
icon: 'diamond'
}
icon: 'diamond',
},
])
</script>
```
@@ -344,7 +344,7 @@ const addTask = () => {
name: 'New Task',
startDate: new Date().toISOString().split('T')[0],
endDate: new Date().toISOString().split('T')[0],
progress: 0
progress: 0,
}
tasks.value.push(newTask)
}
@@ -354,16 +354,16 @@ const addMilestone = () => {
id: Date.now(),
name: 'New Milestone',
startDate: new Date().toISOString().split('T')[0],
type: 'milestone'
type: 'milestone',
}
milestones.value.push(newMilestone)
}
const handleTaskAdded = (e) => {
const handleTaskAdded = e => {
console.log('Task added:', e.task)
}
const handleMilestoneSaved = (milestone) => {
const handleMilestoneSaved = milestone => {
console.log('Milestone saved:', milestone)
}
</script>
@@ -378,7 +378,7 @@ Tasks are the core elements of the Gantt chart. The component provides complete
#### Task Data Structure
| 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' |
@@ -408,6 +408,7 @@ Tasks are the core elements of the Gantt chart. The component provides complete
> **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
@@ -417,7 +418,7 @@ Tasks are the core elements of the Gantt chart. The component provides complete
#### Task-Related Props
| 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](#taskbarconfig-configuration) |
@@ -425,6 +426,7 @@ Tasks are the core elements of the Gantt chart. The component provides complete
| `autoSortByStartDate` | `boolean` | `false` | Whether to automatically sort tasks by start date |
**Configuration Notes**:
- **Default mode**: `useDefaultDrawer=true` (default), double-click task to auto-open built-in TaskDrawer
- **Custom editor**: `useDefaultDrawer=false` disables built-in drawer, listen to `@task-double-click` event to open custom editor
- **Read-only mode**: `useDefaultDrawer=false` and don't listen to `@task-double-click` event, user double-click task has no response
@@ -434,7 +436,7 @@ Tasks are the core elements of the Gantt chart. The component provides complete
> **💡 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 |
@@ -449,6 +451,7 @@ Tasks are the core elements of the Gantt chart. The component provides complete
| `timer-stopped` | `(task: Task) => void` | When task timer stops | Stop recording task hours |
**Data Synchronization Notes**:
-**Component auto-updates internally**: For all task CRUD operations, component will auto-update `props.tasks` data
-**Events are for notification only**: External event listeners are mainly for: showing messages, calling backend APIs, updating other related data, etc.
-**Avoid duplicate operations**: Don't modify `tasks` data again in event handlers, otherwise it will cause duplicate updates
@@ -494,7 +497,7 @@ const tasks = ref<Task[]>([
progress: 60,
assignee: 'Bob',
predecessor: [1], // Depends on task 1
}
},
])
// Toolbar "Add Task" button click event
@@ -567,7 +570,7 @@ const tasks = ref<Task[]>([
startDate: '2025-01-01',
endDate: '2025-01-10',
progress: 100,
predecessor: [] // No predecessors
predecessor: [], // No predecessors
},
{
id: 2,
@@ -575,7 +578,7 @@ const tasks = ref<Task[]>([
startDate: '2025-01-11',
endDate: '2025-01-20',
progress: 80,
predecessor: [1] // Depends on task 1 (Requirements Analysis)
predecessor: [1], // Depends on task 1 (Requirements Analysis)
},
{
id: 3,
@@ -583,7 +586,7 @@ const tasks = ref<Task[]>([
startDate: '2025-01-11',
endDate: '2025-01-18',
progress: 90,
predecessor: [1] // Depends on task 1
predecessor: [1], // Depends on task 1
},
{
id: 4,
@@ -591,7 +594,7 @@ const tasks = ref<Task[]>([
startDate: '2025-01-21',
endDate: '2025-02-10',
progress: 60,
predecessor: [2] // Depends on task 2 (System Design)
predecessor: [2], // Depends on task 2 (System Design)
},
{
id: 5,
@@ -599,7 +602,7 @@ const tasks = ref<Task[]>([
startDate: '2025-01-19',
endDate: '2025-02-08',
progress: 70,
predecessor: [2, 3] // Depends on both task 2 and 3
predecessor: [2, 3], // Depends on both task 2 and 3
},
{
id: 6,
@@ -607,8 +610,8 @@ const tasks = ref<Task[]>([
startDate: '2025-02-11',
endDate: '2025-02-20',
progress: 30,
predecessor: [4, 5] // Depends on frontend and backend development completion
}
predecessor: [4, 5], // Depends on frontend and backend development completion
},
])
// Triggered when adding predecessor via context menu
@@ -630,6 +633,7 @@ const handleSuccessorAdded = (event: { targetTask: Task; newTask: Task }) => {
```
**Dependency Relationship Notes**:
- **`predecessor` field supports multiple formats**:
- Standard format (recommended): `[1, 2, 3]` - number array
- Compatible format 1: `'1,2,3'` - comma-separated string
@@ -698,7 +702,7 @@ const handleAddMilestone = () => {
console.log('Preparing to add milestone (triggered by custom button)')
}
const handleTaskAdded = (e) => {
const handleTaskAdded = e => {
console.log('Task added:', e.task)
// Call API to save...
}
@@ -706,6 +710,7 @@ const handleTaskAdded = (e) => {
```
> **💡 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
@@ -721,7 +726,7 @@ Milestones are used to mark important time points in a project, such as project
#### Milestone Data Structure
| 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' |
@@ -736,16 +741,18 @@ Milestones are used to mark important time points in a project, such as project
#### Milestone-Related Props
| 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**:
- **Default mode**: `useDefaultMilestoneDialog=true` (default), double-click milestone to auto-open built-in MilestoneDialog
- **Disable editor**: `useDefaultMilestoneDialog=false`, double-click milestone has no response (component doesn't open any editor)
- **Custom editor**: Can listen to `onMilestoneDoubleClick` callback or related events to implement custom editing logic
> **💡 Differences Between Milestones and Tasks**:
>
> - Milestone data is managed independently via `milestones` prop, separate from `tasks`
> - Milestone object's `type` field must be set to `'milestone'`
> - Milestones don't support child tasks, dependency relationships, and other complex structures
@@ -755,13 +762,12 @@ Milestones are used to mark important time points in a project, such as project
> **⚠️ Deprecated**: Please use the new event-driven API (see "Milestone Events" section below)
#### Milestone Events
> **💡 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 |
@@ -769,6 +775,7 @@ Milestones are used to mark important time points in a project, such as project
| `milestone-drag-end` | `(milestone: Task) => void` | When milestone drag ends | Milestone date updated. **Note**: Component has auto-updated `milestones` data |
**Data Synchronization Notes**:
-**Component auto-updates internally**: For all milestone CRUD operations, component will auto-update `props.milestones` data
-**Events are for notification only**: External event listeners are mainly for: showing messages, calling backend APIs, updating other related data, etc.
-**Avoid duplicate operations**: Don't modify `milestones` data again in event handlers, otherwise it will cause duplicate updates
@@ -805,15 +812,15 @@ const milestones = ref<Task[]>([
type: 'milestone',
icon: 'diamond',
assignee: 'Project Manager',
description: 'Official project kickoff'
description: 'Official project kickoff',
},
{
id: 102,
name: 'Requirements Review',
startDate: '2025-01-15',
type: 'milestone',
icon: 'flag'
}
icon: 'flag',
},
])
// Toolbar "Add Milestone" button click event
@@ -896,8 +903,8 @@ const milestones = ref<Task[]>([
type: 'milestone',
icon: 'diamond',
assignee: 'Project Manager',
description: 'Official project kickoff'
}
description: 'Official project kickoff',
},
])
const customDialogVisible = ref(false)
@@ -927,7 +934,7 @@ const handleCustomDialogSave = (milestone: Task) => {
const newMilestone = {
...milestone,
id: Date.now(), // Generate new ID
type: 'milestone'
type: 'milestone',
}
milestones.value.push(newMilestone)
@@ -1028,9 +1035,7 @@ const handleMilestoneDrag = (milestone: Task) => {
<template #footer>
<div class="dialog-footer">
<el-button v-if="!isNew" type="danger" @click="handleDelete">
Delete
</el-button>
<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>
@@ -1063,10 +1068,12 @@ const form = ref({
assignee: '',
icon: 'diamond',
description: '',
type: 'milestone'
type: 'milestone',
})
watch(() => props.visible, (val) => {
watch(
() => props.visible,
val => {
dialogVisible.value = val
if (val) {
if (props.milestone) {
@@ -1081,13 +1088,14 @@ watch(() => props.visible, (val) => {
assignee: '',
icon: 'diamond',
description: '',
type: 'milestone'
type: 'milestone',
}
}
}
})
}
)
watch(dialogVisible, (val) => {
watch(dialogVisible, val => {
emit('update:visible', val)
})
@@ -1112,6 +1120,7 @@ const handleDelete = () => {
```
> **💡 Custom Dialog Notes**:
>
> - Set `use-default-milestone-dialog="false"` to disable built-in dialog
> - Listen to `@add-milestone` event to open custom dialog
> - Need to manually manage `milestones` array CRUD operations
@@ -1133,7 +1142,7 @@ 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 |
@@ -1167,10 +1176,7 @@ TimelineScale.YEAR // 'year' - Year view
```vue
<template>
<GanttChart
:tasks="tasks"
:toolbar-config="toolbarConfig"
/>
<GanttChart :tasks="tasks" :toolbar-config="toolbarConfig" />
</template>
<script setup lang="ts">
@@ -1188,11 +1194,17 @@ const toolbarConfig: ToolbarConfig = {
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'
timeScaleDimensions: [
// Show all time scale dimensions
'hour',
'day',
'week',
'month',
'quarter',
'year',
],
defaultTimeScale: 'week', // Default week view
showExpandCollapse: true // Show expand/collapse button
showExpandCollapse: true, // Show expand/collapse button
}
</script>
```
@@ -1213,11 +1225,14 @@ const toolbarConfig: ToolbarConfig = {
showTheme: true, // Keep theme switch
showFullscreen: true, // Keep fullscreen
showTimeScale: true, // Show time scale
timeScaleDimensions: [ // Only show day/week/month scales
'day', 'week', 'month'
timeScaleDimensions: [
// Only show day/week/month scales
'day',
'week',
'month',
],
defaultTimeScale: 'week', // Default week view
showExpandCollapse: true // Keep expand/collapse
showExpandCollapse: true, // Keep expand/collapse
}
</script>
```
@@ -1235,9 +1250,9 @@ const toolbarConfig: ToolbarConfig = {
TimelineScale.DAY,
TimelineScale.WEEK,
TimelineScale.MONTH,
TimelineScale.QUARTER
TimelineScale.QUARTER,
],
defaultTimeScale: TimelineScale.MONTH // Default month view
defaultTimeScale: TimelineScale.MONTH, // Default month view
}
</script>
```
@@ -1260,12 +1275,13 @@ const toolbarConfig: ToolbarConfig = {
showTimeScale: true, // Keep time scale switch
timeScaleDimensions: ['week', 'month'],
defaultTimeScale: 'month',
showExpandCollapse: false // Hide expand/collapse
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**`timeScaleDimensions` controls which time dimensions to display, recommend selecting 2-4 common dimensions
@@ -1278,7 +1294,7 @@ Customize task list display columns, width limits, etc. Task list is located on
**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%'`) |
@@ -1288,7 +1304,7 @@ Customize task list display columns, width limits, etc. Task list is located on
**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 |
@@ -1299,10 +1315,7 @@ Customize task list display columns, width limits, etc. Task list is located on
```vue
<template>
<GanttChart
:tasks="tasks"
:task-list-config="taskListConfig"
/>
<GanttChart :tasks="tasks" :task-list-config="taskListConfig" />
</template>
<script setup lang="ts">
@@ -1322,10 +1335,7 @@ const taskListConfig: TaskListConfig = {
```vue
<template>
<GanttChart
:tasks="tasks"
:task-list-config="taskListConfig"
/>
<GanttChart :tasks="tasks" :task-list-config="taskListConfig" />
</template>
<script setup lang="ts">
@@ -1347,10 +1357,7 @@ Based on business requirements, you can customize columns to display, column wid
```vue
<template>
<GanttChart
:tasks="tasks"
:task-list-config="taskListConfig"
/>
<GanttChart :tasks="tasks" :task-list-config="taskListConfig" />
</template>
<script setup lang="ts">
@@ -1435,10 +1442,7 @@ Combine `ref` and `computed` to achieve dynamic show/hide and width adjustment o
```vue
<template>
<GanttChart
:tasks="tasks"
:task-list-config="taskListConfig"
/>
<GanttChart :tasks="tasks" :task-list-config="taskListConfig" />
</template>
<script setup lang="ts">
@@ -1477,6 +1481,7 @@ const taskListConfig = computed<TaskListConfig>(() => ({
```
> **💡 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
@@ -1493,7 +1498,7 @@ 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 |
@@ -1503,6 +1508,7 @@ Controls task bar display content and interaction behavior。
| `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: false` property to control individual task
@@ -1510,10 +1516,7 @@ Controls task bar display content and interaction behavior。
```vue
<template>
<GanttChart
:tasks="tasks"
:task-bar-config="taskBarConfig"
/>
<GanttChart :tasks="tasks" :task-bar-config="taskBarConfig" />
</template>
<script setup lang="ts">
@@ -1539,10 +1542,7 @@ Disable edit operations for all tasks.
```vue
<template>
<GanttChart
:tasks="tasks"
:allow-drag-and-resize="false"
/>
<GanttChart :tasks="tasks" :allow-drag-and-resize="false" />
</template>
```
@@ -1608,6 +1608,7 @@ const taskBarConfig = computed<TaskBarConfig>(() => ({
}))
</script>
```
#### Timeline Container Auto-Fill Configuration
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.
@@ -1615,6 +1616,7 @@ The component has built-in intelligent timeline range calculation logic, ensurin
**Core Design Principles:**
1. **Base Buffer Mechanism**: Add fixed buffers based on the actual time range of tasks, varying by view type
- Hour view:±1 day task range
- Day view: ±30 days before/after task range
- Week view: ±8 weeks (approx. 2 months) before/after task range
@@ -1623,10 +1625,12 @@ The component has built-in intelligent timeline range calculation logic, ensurin
- Year view: ±1 year before/after task range
2. **Container Width Adaptation**: After base buffering, if calculated timeline width is less than container width, automatically extend the range
- Calculate time units (days/weeks/months/quarters/years) needed for container
- **Symmetrically extend** on both sides of base range to ensure timeline fills container
3. **Empty Data Handling**: When no task data exists, calculate reasonable time range based on container width and time scale
- Center on current date
- Dynamically calculate time span to display based on container width
- Ensure minimum display range (e.g., at least 60 days for day view, at least 20 weeks for week view)
@@ -1638,7 +1642,7 @@ The component has built-in intelligent timeline range calculation logic, ensurin
**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 | ✅ |
@@ -1649,11 +1653,13 @@ The component has built-in intelligent timeline range calculation logic, ensurin
**Practical Application Scenarios:**
- **Short-term Tasks** (e.g., 1-week project):
- Won't result in narrow timeline, automatically extends to fill container
- Day view: 1 week (7 days × 30px = 210px) → Extends to ≥1200px (approx. 40 days)
- Week view: 1 week (60px) → Extends to ≥1200px (approx. 20 weeks)
- **Long-term Projects** (e.g., 2-year project):
- After adding fixed buffer, automatically adapts to container
- Month view: 24 months + buffer → Extends to container width if needed
- Quarter view: 8 quarters + buffer → Extends to container width if needed
@@ -1665,6 +1671,7 @@ The component has built-in intelligent timeline range calculation logic, ensurin
- Quarter/Year view: Displays at least 5 years
> **💡 Automation Advantages**:
>
> - No need to manually set `startDate` and `endDate`, 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
@@ -1679,10 +1686,7 @@ Component has built-in light and dark themes, can switch via toolbar button, als
```vue
<template>
<GanttChart
:tasks="tasks"
:on-theme-change="handleThemeChange"
/>
<GanttChart :tasks="tasks" :on-theme-change="handleThemeChange" />
</template>
<script setup lang="ts">
@@ -1751,10 +1755,7 @@ Component has built-in Chinese (zh-CN) and English (en-US), can switch via toolb
```vue
<template>
<GanttChart
:tasks="tasks"
:on-language-change="handleLanguageChange"
/>
<GanttChart :tasks="tasks" :on-language-change="handleLanguageChange" />
</template>
<script setup lang="ts">
@@ -1772,10 +1773,7 @@ Override or extend default translations via `localeMessages` prop:
```vue
<template>
<GanttChart
:tasks="tasks"
:locale-messages="customMessages"
/>
<GanttChart :tasks="tasks" :locale-messages="customMessages" />
</template>
<script setup lang="ts">
@@ -1832,6 +1830,7 @@ const customMessages = {
```
> **💡 Tip**
>
> - `localeMessages` adopts **deep merge** strategy, only need to pass fields that need to be overridden
> - supports nested objects, like `taskList.name`, `toolbar.addTask`, etc.
> - For complete translation keys, please refer to built-in `messages['zh-CN']` object in component
@@ -1849,14 +1848,14 @@ Used to customize task display content in task list (TaskRow) and timeline (Task
**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 |
@@ -1878,7 +1877,7 @@ Used to customize task display content in task list (TaskRow) and timeline (Task
**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 |
@@ -1894,11 +1893,7 @@ Used to customize task display content in task list (TaskRow) and timeline (Task
<GanttChart :tasks="tasks">
<template #custom-task-content="slotProps">
<!-- Render different content based on type -->
<CustomTaskContent
:task="slotProps.task"
:type="slotProps.type"
:status="slotProps.status"
/>
<CustomTaskContent :task="slotProps.task" :type="slotProps.type" :status="slotProps.status" />
</template>
</GanttChart>
</template>
@@ -1916,8 +1911,8 @@ const tasks = ref<Task[]>([
name: '<strong>Important Task</strong>',
startDate: '2025-01-01',
endDate: '2025-01-10',
progress: 50
}
progress: 50,
},
])
</script>
```
@@ -1994,6 +1989,7 @@ const props = defineProps<Props>()
```
> **💡 Usage Scenarios**
>
> - Support HTML formatted task names
> - Add custom icons, tags or badges
> - Display different styles based on task status
@@ -2001,6 +1997,7 @@ const props = defineProps<Props>()
> - Display additional business information
> **⚠️ Notes**
>
> - Slot content will be rendered in both TaskRow and TaskBar
> - Need to distinguish rendering position based on `type` parameter
> - TaskRow and TaskBar have different available space, need to adapt layout
+120 -129
View File
@@ -2,7 +2,10 @@
<p align="center">
<a href="https://www.npmjs.com/package/jordium-gantt-vue3">
<img src="https://img.shields.io/npm/v/jordium-gantt-vue3.svg" alt="npm version">
<img src="https://img.shields.io/npm/v/jordium-gantt-vue3?style=flat-square" alt="npm version">
</a>
<a href="https://www.npmjs.com/package/jordium-gantt-vue3">
<img src="https://img.shields.io/npm/dt/jordium-gantt-vue3?style=flat-square" alt="npm total">
</a>
<a href="https://opensource.org/licenses/MIT">
<img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT License">
@@ -105,10 +108,7 @@ import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
```vue
<template>
<div style="height: 600px;">
<GanttChart
:tasks="tasks"
:milestones="milestones"
/>
<GanttChart :tasks="tasks" :milestones="milestones" />
</div>
</template>
@@ -123,7 +123,7 @@ const tasks = ref([
name: '项目启动',
startDate: '2025-01-01',
endDate: '2025-01-10',
progress: 100
progress: 100,
},
{
id: 2,
@@ -131,7 +131,7 @@ const tasks = ref([
startDate: '2025-01-11',
endDate: '2025-01-20',
progress: 80,
predecessor: [1]
predecessor: [1],
},
{
id: 3,
@@ -139,8 +139,8 @@ const tasks = ref([
startDate: '2025-01-21',
endDate: '2025-02-05',
progress: 50,
predecessor: [2]
}
predecessor: [2],
},
])
const milestones = ref([
@@ -148,8 +148,8 @@ const milestones = ref([
id: 101,
name: '项目立项',
date: '2025-01-01',
type: 'milestone'
}
type: 'milestone',
},
])
</script>
```
@@ -158,6 +158,7 @@ const milestones = ref([
<span><strong>推荐使用 <a href="https://dovee.cc/a.php?anaxjgyz1ozZq2B">DOVE</a> VPN,快速、稳定。</strong></span> <span style="color:red;">(注意:请合法使用 VPN 资源)</span>
## 🌞 NPM包使用示例
请参考项目下的npm-demo,这是一个独立的项目,可以使用IDE单独浏览和启动,运行前请安装element plus以及jordium-gantt-vue3插件包
```bash
@@ -166,6 +167,7 @@ npm install element-plus
npm install jordium-gantt-vue3
npm run dev
```
---
## 组件指南
@@ -177,7 +179,7 @@ npm run dev
#### 基础属性
| 属性名 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| --------------------------- | --------- | ------- | -------------------------------------------------------------- |
| `tasks` | `Task[]` | `[]` | 任务数据数组 |
| `milestones` | `Task[]` | `[]` | 里程碑数据数组(注意:类型为 Task[],需设置 type='milestone' |
| `showToolbar` | `boolean` | `true` | 是否显示工具栏 |
@@ -191,7 +193,7 @@ npm run dev
完整的配置对象说明请参考 [⚙️ 配置与扩展](#⚙️-配置与扩展) 章节。
| 属性名 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| ---------------- | ---------------------------- | ----------------------------------------------------------------------- | ---------------- |
| `toolbarConfig` | `ToolbarConfig` | `{}` | 工具栏配置 |
| `taskListConfig` | `TaskListConfig` | `undefined` | 任务列表配置 |
| `taskBarConfig` | `TaskBarConfig` | `undefined` | 任务条样式配置 |
@@ -201,7 +203,7 @@ npm run dev
#### 回调函数属性
| 属性名 | 类型 | 说明 |
|--------|------|------|
| -------------------- | ------------------------------------ | -------------------------------------------------------- |
| `onTodayLocate` | `() => void` | 工具栏"今天"按钮点击回调 |
| `onExportCsv` | `() => boolean \| void` | 工具栏"导出CSV"按钮点击回调,返回 `false` 可阻止默认导出 |
| `onExportPdf` | `() => void` | 工具栏"导出PDF"按钮点击回调 |
@@ -214,13 +216,14 @@ npm run dev
#### 组件事件(Events
完整的事件说明请分别参考:
- **任务相关事件**:参见下方 [任务管理](#任务管理) 章节
- **里程碑相关事件**:参见下方 [里程碑管理](#里程碑管理) 章节
**事件列表总览:**
| 事件名 | 参数 | 说明 |
|--------|------|------|
| ------------------------ | --------------------------------- | -------------------------- |
| `add-task` | - | 点击工具栏"添加任务"按钮 |
| `task-click` | `(task: Task, event: MouseEvent)` | 点击任务 |
| `task-double-click` | `(task: Task)` | 双击任务 |
@@ -259,8 +262,8 @@ const tasks = ref([
name: '任务1',
startDate: '2025-01-01',
endDate: '2025-01-10',
progress: 100
}
progress: 100,
},
])
</script>
```
@@ -270,10 +273,7 @@ const tasks = ref([
```vue
<template>
<div style="height: 600px;">
<GanttChart
:tasks="tasks"
:milestones="milestones"
/>
<GanttChart :tasks="tasks" :milestones="milestones" />
</div>
</template>
@@ -288,8 +288,8 @@ const tasks = ref([
name: '项目启动',
startDate: '2025-01-01',
endDate: '2025-01-10',
progress: 100
}
progress: 100,
},
])
const milestones = ref([
@@ -298,8 +298,8 @@ const milestones = ref([
name: '项目立项',
startDate: '2025-01-01',
type: 'milestone',
icon: 'diamond'
}
icon: 'diamond',
},
])
</script>
```
@@ -342,7 +342,7 @@ const addTask = () => {
name: '新任务',
startDate: new Date().toISOString().split('T')[0],
endDate: new Date().toISOString().split('T')[0],
progress: 0
progress: 0,
}
tasks.value.push(newTask)
}
@@ -352,16 +352,16 @@ const addMilestone = () => {
id: Date.now(),
name: '新里程碑',
startDate: new Date().toISOString().split('T')[0],
type: 'milestone'
type: 'milestone',
}
milestones.value.push(newMilestone)
}
const handleTaskAdded = (e) => {
const handleTaskAdded = e => {
console.log('任务已添加:', e.task)
}
const handleMilestoneSaved = (milestone) => {
const handleMilestoneSaved = milestone => {
console.log('里程碑已保存:', milestone)
}
</script>
@@ -376,7 +376,7 @@ const handleMilestoneSaved = (milestone) => {
#### Task 数据结构
| 字段名 | 类型 | 必填 | 默认值 | 说明 |
|--------|------|------|--------|------|
| ------------------ | ---------- | ---- | ----------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `id` | `number` | ✅ | - | 任务唯一标识符 |
| `name` | `string` | ✅ | - | 任务名称 |
| `startDate` | `string` | - | - | 开始日期,格式:'YYYY-MM-DD' 或 'YYYY-MM-DD HH:mm' |
@@ -406,6 +406,7 @@ const handleMilestoneSaved = (milestone) => {
> **自定义属性扩展**:Task 接口支持添加任意自定义字段,例如:`priority`、`tags`、`status`、`department` 等业务相关字段。
>
> **前置任务字段说明**
>
> - **标准格式**(推荐):`predecessor: [1, 2, 3]` - number 数组
> - **兼容格式1**`predecessor: '1,2,3'` - 逗号分隔的字符串
> - **兼容格式2**`predecessor: ['1', '2', '3']` - 字符串数组
@@ -415,7 +416,7 @@ const handleMilestoneSaved = (milestone) => {
#### 任务相关属性
| 属性名 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| --------------------- | ---------------- | ----------- | -------------------------------------------------------------- |
| `tasks` | `Task[]` | `[]` | 任务数据数组 |
| `useDefaultDrawer` | `boolean` | `true` | 是否使用内置的任务编辑抽屉(TaskDrawer) |
| `taskBarConfig` | `TaskBarConfig` | `{}` | 任务条样式配置,详见 [TaskBarConfig 配置](#taskbarconfig-配置) |
@@ -423,6 +424,7 @@ const handleMilestoneSaved = (milestone) => {
| `autoSortByStartDate` | `boolean` | `false` | 是否根据开始时间自动排序任务 |
**配置说明**
- **默认模式**`useDefaultDrawer=true`(默认),双击任务自动打开内置 TaskDrawer
- **自定义编辑器**`useDefaultDrawer=false` 禁用内置抽屉,监听 `@task-double-click` 事件打开自定义编辑器
- **只读模式**`useDefaultDrawer=false` 且不监听 `@task-double-click` 事件,用户双击任务无反应
@@ -432,7 +434,7 @@ const handleMilestoneSaved = (milestone) => {
> **💡 事件驱动架构**:组件采用纯事件驱动设计,所有用户操作(添加、编辑、删除、拖拽等)都会触发对应事件,方便外部监听和处理。
| 事件名 | 参数 | 触发时机 | 说明 |
|--------|------|---------|------|
| -------------------- | ----------------------------------------- | -------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `add-task` | - | 点击工具栏"添加任务"按钮时 | 可用于自定义新增任务逻辑。如 `useDefaultDrawer=true`,组件会自动打开内置 TaskDrawer |
| `task-click` | `(task: Task, event: MouseEvent) => void` | 点击任务条时 | 单击任务触发 |
| `task-double-click` | `(task: Task) => void` | 双击任务条时 | 双击任务时**始终触发**。`useDefaultDrawer=true` 时组件会额外打开内置编辑器,`false` 时不打开。事件触发与属性值无关 |
@@ -447,6 +449,7 @@ const handleMilestoneSaved = (milestone) => {
| `timer-stopped` | `(task: Task) => void` | 任务计时器停止时 | 停止记录任务工时 |
**数据同步说明**
-**组件内部自动更新**:所有任务的增删改操作,组件都会自动更新 `props.tasks` 数据
-**事件仅做通知**:外部监听事件主要用于:显示提示消息、调用后端 API、更新其他相关数据等
-**避免重复操作**:不要在事件处理器中再次修改 `tasks` 数据,否则会导致重复更新
@@ -492,7 +495,7 @@ const tasks = ref<Task[]>([
progress: 60,
assignee: '李四',
predecessor: [1], // 依赖任务1
}
},
])
// 工具栏"添加任务"按钮点击事件
@@ -565,7 +568,7 @@ const tasks = ref<Task[]>([
startDate: '2025-01-01',
endDate: '2025-01-10',
progress: 100,
predecessor: [] // 无前置任务
predecessor: [], // 无前置任务
},
{
id: 2,
@@ -573,7 +576,7 @@ const tasks = ref<Task[]>([
startDate: '2025-01-11',
endDate: '2025-01-20',
progress: 80,
predecessor: [1] // 依赖任务1(需求分析)
predecessor: [1], // 依赖任务1(需求分析)
},
{
id: 3,
@@ -581,7 +584,7 @@ const tasks = ref<Task[]>([
startDate: '2025-01-11',
endDate: '2025-01-18',
progress: 90,
predecessor: [1] // 依赖任务1
predecessor: [1], // 依赖任务1
},
{
id: 4,
@@ -589,7 +592,7 @@ const tasks = ref<Task[]>([
startDate: '2025-01-21',
endDate: '2025-02-10',
progress: 60,
predecessor: [2] // 依赖任务2(系统设计)
predecessor: [2], // 依赖任务2(系统设计)
},
{
id: 5,
@@ -597,7 +600,7 @@ const tasks = ref<Task[]>([
startDate: '2025-01-19',
endDate: '2025-02-08',
progress: 70,
predecessor: [2, 3] // 同时依赖任务2和3
predecessor: [2, 3], // 同时依赖任务2和3
},
{
id: 6,
@@ -605,8 +608,8 @@ const tasks = ref<Task[]>([
startDate: '2025-02-11',
endDate: '2025-02-20',
progress: 30,
predecessor: [4, 5] // 依赖前端和后端开发完成
}
predecessor: [4, 5], // 依赖前端和后端开发完成
},
])
// 通过右键菜单添加前置任务时触发
@@ -628,6 +631,7 @@ const handleSuccessorAdded = (event: { targetTask: Task; newTask: Task }) => {
```
**依赖关系说明**
- **`predecessor` 字段支持多种格式**
- 标准格式(推荐):`[1, 2, 3]` - number 数组
- 兼容格式1`'1,2,3'` - 逗号分隔的字符串
@@ -696,7 +700,7 @@ const handleAddMilestone = () => {
console.log('准备新增里程碑(由自定义按钮触发)')
}
const handleTaskAdded = (e) => {
const handleTaskAdded = e => {
console.log('任务已添加:', e.task)
// 调用 API 保存...
}
@@ -704,6 +708,7 @@ const handleTaskAdded = (e) => {
```
> **💡 灵活性设计**
>
> - 显示工具栏 + 默认编辑器:最简单的开箱即用方式
> - 隐藏工具栏 + 自定义按钮 + 默认编辑器:自定义控制栏样式,保留默认编辑功能
> - 隐藏工具栏 + 自定义按钮 + 自定义编辑器:完全自定义所有交互逻辑
@@ -717,7 +722,7 @@ const handleTaskAdded = (e) => {
#### Milestone 数据结构
| 字段名 | 类型 | 必填 | 默认值 | 说明 |
|--------|------|------|--------|------|
| ------------- | -------- | ---- | ------------- | ---------------------------------------------------------- |
| `id` | `number` | ✅ | - | 里程碑唯一标识符 |
| `name` | `string` | ✅ | - | 里程碑名称 |
| `startDate` | `string` | ✅ | - | 里程碑日期,格式:'YYYY-MM-DD' 或 'YYYY-MM-DD HH:mm' |
@@ -732,16 +737,18 @@ const handleTaskAdded = (e) => {
#### 里程碑相关属性
| 属性名 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| --------------------------- | --------- | ------ | -------------------------------------------------------- |
| `milestones` | `Task[]` | `[]` | 里程碑数据数组(类型为 Task[],需确保 type='milestone' |
| `useDefaultMilestoneDialog` | `boolean` | `true` | 是否使用内置的里程碑编辑对话框(MilestoneDialog |
**配置说明**
- **默认模式**`useDefaultMilestoneDialog=true`(默认),双击里程碑自动打开内置 MilestoneDialog
- **禁用编辑器**`useDefaultMilestoneDialog=false`,双击里程碑无反应(组件不打开任何编辑器)
- **自定义编辑器**:可以监听 `onMilestoneDoubleClick` 回调或相关事件,实现自定义编辑逻辑
> **💡 里程碑与任务的区别**
>
> - 里程碑数据通过 `milestones` 属性独立管理,与 `tasks` 分开
> - 里程碑对象的 `type` 字段必须设置为 `'milestone'`
> - 里程碑不支持子任务、依赖关系等复杂结构
@@ -751,13 +758,12 @@ const handleTaskAdded = (e) => {
> **⚠️ 已废弃**:请使用新的事件驱动 API(见下方"里程碑事件"章节)
#### 里程碑事件
> **💡 事件驱动架构**:里程碑管理采用事件驱动设计,推荐使用事件 API 替代回调函数。
| 事件名 | 参数 | 触发时机 | 说明 |
|--------|------|---------|------|
| ------------------------ | --------------------------------------- | ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `add-milestone` | - | 点击工具栏"添加里程碑"按钮时 | 可用于自定义新增里程碑逻辑。如 `useDefaultMilestoneDialog=true`,组件会自动打开内置 MilestoneDialog |
| `milestone-saved` | `(milestone: Task) => void` | 里程碑保存后(新增或编辑) | 通过内置 MilestoneDialog 保存里程碑后触发。**注意**:组件已自动更新 `milestones` 数据,外部只需监听此事件做额外处理(如调用 API 保存) |
| `milestone-deleted` | `{ milestoneId: number }` | 里程碑删除后 | 通过内置 MilestoneDialog 删除里程碑后触发。**注意**:组件已自动更新 `milestones` 数据,外部只需监听此事件做额外处理 |
@@ -765,6 +771,7 @@ const handleTaskAdded = (e) => {
| `milestone-drag-end` | `(milestone: Task) => void` | 拖拽里程碑结束时 | 里程碑日期已更新。**注意**:组件已自动更新 `milestones` 数据 |
**数据同步说明**
-**组件内部自动更新**:所有里程碑的增删改操作,组件都会自动更新 `props.milestones` 数据
-**事件仅做通知**:外部监听事件主要用于:显示提示消息、调用后端 API、更新其他相关数据等
-**避免重复操作**:不要在事件处理器中再次修改 `milestones` 数据,否则会导致重复更新
@@ -801,15 +808,15 @@ const milestones = ref<Task[]>([
type: 'milestone',
icon: 'diamond',
assignee: '项目经理',
description: '项目正式启动'
description: '项目正式启动',
},
{
id: 102,
name: '需求评审',
startDate: '2025-01-15',
type: 'milestone',
icon: 'flag'
}
icon: 'flag',
},
])
// 工具栏"添加里程碑"按钮点击事件
@@ -892,8 +899,8 @@ const milestones = ref<Task[]>([
type: 'milestone',
icon: 'diamond',
assignee: '项目经理',
description: '项目正式启动'
}
description: '项目正式启动',
},
])
const customDialogVisible = ref(false)
@@ -923,7 +930,7 @@ const handleCustomDialogSave = (milestone: Task) => {
const newMilestone = {
...milestone,
id: Date.now(), // 生成新 ID
type: 'milestone'
type: 'milestone',
}
milestones.value.push(newMilestone)
@@ -1013,20 +1020,13 @@ const handleMilestoneDrag = (milestone: Task) => {
</el-form-item>
<el-form-item label="描述">
<el-input
v-model="form.description"
type="textarea"
:rows="3"
placeholder="请输入描述"
/>
<el-input v-model="form.description" type="textarea" :rows="3" placeholder="请输入描述" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button v-if="!isNew" type="danger" @click="handleDelete">
删除
</el-button>
<el-button v-if="!isNew" type="danger" @click="handleDelete"> 删除 </el-button>
<el-button @click="handleClose">取消</el-button>
<el-button type="primary" @click="handleSave">保存</el-button>
</div>
@@ -1059,10 +1059,12 @@ const form = ref({
assignee: '',
icon: 'diamond',
description: '',
type: 'milestone'
type: 'milestone',
})
watch(() => props.visible, (val) => {
watch(
() => props.visible,
val => {
dialogVisible.value = val
if (val) {
if (props.milestone) {
@@ -1077,13 +1079,14 @@ watch(() => props.visible, (val) => {
assignee: '',
icon: 'diamond',
description: '',
type: 'milestone'
type: 'milestone',
}
}
}
})
}
)
watch(dialogVisible, (val) => {
watch(dialogVisible, val => {
emit('update:visible', val)
})
@@ -1108,6 +1111,7 @@ const handleDelete = () => {
```
> **💡 自定义对话框说明**
>
> - 设置 `use-default-milestone-dialog="false"` 禁用内置对话框
> - 监听 `@add-milestone` 事件打开自定义对话框
> - 需要手动管理 `milestones` 数组的增删改
@@ -1129,7 +1133,7 @@ const handleDelete = () => {
**类型定义:**
| 字段名 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| --------------------- | ----------------- | ----------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| `showAddTask` | `boolean` | `true` | 显示"添加任务"按钮 |
| `showAddMilestone` | `boolean` | `true` | 显示"添加里程碑"按钮 |
| `showTodayLocate` | `boolean` | `true` | 显示"定位到今天"按钮 |
@@ -1163,10 +1167,7 @@ TimelineScale.YEAR // 'year' - 年视图
```vue
<template>
<GanttChart
:tasks="tasks"
:toolbar-config="toolbarConfig"
/>
<GanttChart :tasks="tasks" :toolbar-config="toolbarConfig" />
</template>
<script setup lang="ts">
@@ -1184,11 +1185,17 @@ const toolbarConfig: ToolbarConfig = {
showTheme: true, // 显示主题切换按钮
showFullscreen: true, // 显示全屏按钮
showTimeScale: true, // 显示时间刻度按钮组
timeScaleDimensions: [ // 显示所有时间刻度维度
'hour', 'day', 'week', 'month', 'quarter', 'year'
timeScaleDimensions: [
// 显示所有时间刻度维度
'hour',
'day',
'week',
'month',
'quarter',
'year',
],
defaultTimeScale: 'week', // 默认选中周视图
showExpandCollapse: true // 显示展开/折叠按钮
showExpandCollapse: true, // 显示展开/折叠按钮
}
</script>
```
@@ -1209,11 +1216,14 @@ const toolbarConfig: ToolbarConfig = {
showTheme: true, // 保留主题切换
showFullscreen: true, // 保留全屏
showTimeScale: true, // 显示时间刻度
timeScaleDimensions: [ // 只显示日/周/月三种刻度
'day', 'week', 'month'
timeScaleDimensions: [
// 只显示日/周/月三种刻度
'day',
'week',
'month',
],
defaultTimeScale: 'week', // 默认周视图
showExpandCollapse: true // 保留展开/折叠
showExpandCollapse: true, // 保留展开/折叠
}
</script>
```
@@ -1231,9 +1241,9 @@ const toolbarConfig: ToolbarConfig = {
TimelineScale.DAY,
TimelineScale.WEEK,
TimelineScale.MONTH,
TimelineScale.QUARTER
TimelineScale.QUARTER,
],
defaultTimeScale: TimelineScale.MONTH // 默认月视图
defaultTimeScale: TimelineScale.MONTH, // 默认月视图
}
</script>
```
@@ -1256,12 +1266,13 @@ const toolbarConfig: ToolbarConfig = {
showTimeScale: true, // 保留时间刻度切换
timeScaleDimensions: ['week', 'month'],
defaultTimeScale: 'month',
showExpandCollapse: false // 隐藏展开/折叠
showExpandCollapse: false, // 隐藏展开/折叠
}
</script>
```
> **💡 配置建议**
>
> - **默认配置**:不传 `toolbar-config` 时,所有按钮默认显示
> - **按需显示**:根据业务需求隐藏不需要的功能按钮
> - **时间刻度**`timeScaleDimensions` 控制显示哪些时间维度,建议选择 2-4 个常用维度
@@ -1274,7 +1285,7 @@ const toolbarConfig: ToolbarConfig = {
**类型定义:**
| 字段名 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| ---------------- | ------------------------ | ------- | ------------------------------------------------------------------------------ |
| `columns` | `TaskListColumnConfig[]` | 默认8列 | 任务列表的列配置数组,定义显示哪些列及其属性 |
| `showAllColumns` | `boolean` | `true` | 是否显示所有列。`true` 时忽略 `columns` 中的 `visible` 设置 |
| `defaultWidth` | `number \| string` | `320` | 默认展开宽度。支持像素数字(如 `320`)或百分比字符串(如 `'30%'` |
@@ -1284,7 +1295,7 @@ const toolbarConfig: ToolbarConfig = {
**TaskListColumnConfig 类型定义:**
| 字段名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| ---------- | --------- | ---- | ---------------------------------------------------------------- |
| `key` | `string` | ✅ | 列的唯一标识符,用于访问 Task 对象中的字段,也用于国际化 |
| `label` | `string` | - | 列的显示标签(表头文字) |
| `cssClass` | `string` | - | 自定义 CSS 类名 |
@@ -1295,10 +1306,7 @@ const toolbarConfig: ToolbarConfig = {
```vue
<template>
<GanttChart
:tasks="tasks"
:task-list-config="taskListConfig"
/>
<GanttChart :tasks="tasks" :task-list-config="taskListConfig" />
</template>
<script setup lang="ts">
@@ -1318,10 +1326,7 @@ const taskListConfig: TaskListConfig = {
```vue
<template>
<GanttChart
:tasks="tasks"
:task-list-config="taskListConfig"
/>
<GanttChart :tasks="tasks" :task-list-config="taskListConfig" />
</template>
<script setup lang="ts">
@@ -1343,10 +1348,7 @@ const taskListConfig: TaskListConfig = {
```vue
<template>
<GanttChart
:tasks="tasks"
:task-list-config="taskListConfig"
/>
<GanttChart :tasks="tasks" :task-list-config="taskListConfig" />
</template>
<script setup lang="ts">
@@ -1431,10 +1433,7 @@ const taskListConfig: TaskListConfig = {
```vue
<template>
<GanttChart
:tasks="tasks"
:task-list-config="taskListConfig"
/>
<GanttChart :tasks="tasks" :task-list-config="taskListConfig" />
</template>
<script setup lang="ts">
@@ -1473,6 +1472,7 @@ const taskListConfig = computed<TaskListConfig>(() => ({
```
> **💡 配置说明**
>
> - **默认行为**:不传 `task-list-config` 时,显示所有 8 个默认列,宽度为 320px
> - **宽度单位**:支持像素(`number`)和百分比(`string`,如 `'30%'`)两种方式
> - **百分比计算**:基于甘特图容器的总宽度,响应式调整
@@ -1489,7 +1489,7 @@ const taskListConfig = computed<TaskListConfig>(() => ({
**配置字段:**
| 字段名 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| ------------------- | --------- | ------- | ------------------------------- |
| `showAvatar` | `boolean` | `true` | 是否展示头像 |
| `showTitle` | `boolean` | `true` | 是否展示标题文字 |
| `showProgress` | `boolean` | `true` | 是否展示进度文字 |
@@ -1499,6 +1499,7 @@ const taskListConfig = computed<TaskListConfig>(() => ({
| `dragDelayTime` | `number` | `150` | 拖拽延迟时间(毫秒) |
> **💡 编辑权限控制**
>
> - **全局控制**:使用 `<GanttChart :allow-drag-and-resize="false" />` 禁用所有任务的拖拽/拉伸
> - **单个任务控制**:设置任务对象的 `isEditable: false` 属性单独控制某个任务
@@ -1506,10 +1507,7 @@ const taskListConfig = computed<TaskListConfig>(() => ({
```vue
<template>
<GanttChart
:tasks="tasks"
:task-bar-config="taskBarConfig"
/>
<GanttChart :tasks="tasks" :task-bar-config="taskBarConfig" />
</template>
<script setup lang="ts">
@@ -1535,10 +1533,7 @@ const taskBarConfig: TaskBarConfig = {
```vue
<template>
<GanttChart
:tasks="tasks"
:allow-drag-and-resize="false"
/>
<GanttChart :tasks="tasks" :allow-drag-and-resize="false" />
</template>
```
@@ -1612,6 +1607,7 @@ const taskBarConfig = computed<TaskBarConfig>(() => ({
**核心设计思路:**
1. **基础缓冲机制**:在任务的实际时间范围基础上,根据不同视图添加固定的缓冲区
- 小时视图:任务范围前后各 ±1 天
- 日视图:任务范围前后各 ±30 天
- 周视图:任务范围前后各 ±8 周(约2个月)
@@ -1620,10 +1616,12 @@ const taskBarConfig = computed<TaskBarConfig>(() => ({
- 年视图:任务范围前后各 ±1 年
2. **容器宽度适配**:基础缓冲后,如果计算出的时间线宽度小于容器宽度,会自动扩展范围
- 计算容器需要的时间单位数(天/周/月/季度/年)
- 在基础范围两侧**对称扩展**,确保时间线填充满容器
3. **空数据处理**:当没有任务数据时,根据容器宽度和时间刻度计算合理的时间范围
- 以当前日期为中心
- 根据容器宽度动态计算需要显示的时间跨度
- 确保最小显示范围(如日视图至少60天,周视图至少20周等)
@@ -1635,7 +1633,7 @@ const taskBarConfig = computed<TaskBarConfig>(() => ({
**各视图计算模式对照表:**
| 视图 | 单位宽度 | 基础缓冲 | 空数据最小范围 | 容器自动填充? |
|------|----------|----------|----------------|----------|
| -------- | -------------------- | -------- | -------------- | -------------- |
| 小时视图 | 30px/时 | ±1天 | 3天 | ✅ |
| 日视图 | 30px/天 | ±30天 | 60天 | ✅ |
| 周视图 | 60px/周 | ±2月 | 20周 | ✅ |
@@ -1646,11 +1644,13 @@ const taskBarConfig = computed<TaskBarConfig>(() => ({
**实际应用场景:**
- **短期任务**(如1周项目, 分辨率1080):
- 不会导致时间线过窄,自动扩展到填充满容器
- 日视图:1周(7天×30px=210px) → 扩展至 ≥1200px(约40天)
- 周视图:1周(60px) → 扩展至 ≥1200px(约20周)
- **长期项目**(如2年项目):
- 添加固定缓冲后,自动适配容器
- 月视图:24个月 + 缓冲 → 如需要则扩展至容器宽度
- 季度视图:8个季度 + 缓冲 → 如需要则扩展至容器宽度
@@ -1662,6 +1662,7 @@ const taskBarConfig = computed<TaskBarConfig>(() => ({
- 季度/年视图:显示至少5年
> **💡 自动化优势**
>
> - 无需手动设置 `startDate` 和 `endDate`,组件会自动计算最优范围
> - 响应式容器宽度变化,时间线自动重新计算
> - 不同视图独立优化,切换视图时自动调整到最佳显示效果
@@ -1676,10 +1677,7 @@ const taskBarConfig = computed<TaskBarConfig>(() => ({
```vue
<template>
<GanttChart
:tasks="tasks"
:on-theme-change="handleThemeChange"
/>
<GanttChart :tasks="tasks" :on-theme-change="handleThemeChange" />
</template>
<script setup lang="ts">
@@ -1748,10 +1746,7 @@ const handleThemeChange = (isDark: boolean) => {
```vue
<template>
<GanttChart
:tasks="tasks"
:on-language-change="handleLanguageChange"
/>
<GanttChart :tasks="tasks" :on-language-change="handleLanguageChange" />
</template>
<script setup lang="ts">
@@ -1769,10 +1764,7 @@ const handleLanguageChange = (lang: 'zh-CN' | 'en-US') => {
```vue
<template>
<GanttChart
:tasks="tasks"
:locale-messages="customMessages"
/>
<GanttChart :tasks="tasks" :locale-messages="customMessages" />
</template>
<script setup lang="ts">
@@ -1829,6 +1821,7 @@ const customMessages = {
```
> **💡 提示**
>
> - `localeMessages` 采用**深度合并**策略,只需传递需要覆盖的字段即可
> - 支持嵌套对象,如 `taskList.name`、`toolbar.addTask` 等
> - 完整的翻译键请参考组件内置的 `messages['zh-CN']` 对象
@@ -1846,14 +1839,14 @@ const customMessages = {
**插槽参数:**
| 参数名 | 类型 | 来源 | 说明 |
|--------|------|------|------|
| ------ | ---------------------------- | ---- | ---------------- |
| `type` | `'task-row'` \| `'task-bar'` | 通用 | 插槽调用位置标识 |
| `task` | `Task` | 通用 | 当前任务对象 |
**TaskRow 特有参数(当 `type === 'task-row'` 时):**
| 参数名 | 类型 | 说明 |
|--------|------|------|
| ---------------- | -------------------------------- | ---------------- |
| `isRowContent` | `boolean` | 标识为行内容 |
| `level` | `number` | 任务层级 |
| `indent` | `string` | 缩进样式 |
@@ -1875,7 +1868,7 @@ const customMessages = {
**TaskBar 特有参数(当 `type === 'task-bar'` 时):**
| 参数名 | 类型 | 说明 |
|--------|------|------|
| ------------------ | --------------- | ---------------------------------------------------------------------------------- |
| `status` | `object` | 任务状态对象,包含 `type`, `color`, `bgColor`, `borderColor` |
| `statusType` | `string` | 状态类型:`'completed'`, `'delayed'`, `'in-progress'`, `'not-started'`, `'parent'` |
| `isParent` | `boolean` | 是否为父任务 |
@@ -1891,11 +1884,7 @@ const customMessages = {
<GanttChart :tasks="tasks">
<template #custom-task-content="slotProps">
<!-- 根据类型渲染不同内容 -->
<CustomTaskContent
:task="slotProps.task"
:type="slotProps.type"
:status="slotProps.status"
/>
<CustomTaskContent :task="slotProps.task" :type="slotProps.type" :status="slotProps.status" />
</template>
</GanttChart>
</template>
@@ -1913,8 +1902,8 @@ const tasks = ref<Task[]>([
name: '<strong>重要任务</strong>',
startDate: '2025-01-01',
endDate: '2025-01-10',
progress: 50
}
progress: 50,
},
])
</script>
```
@@ -1991,6 +1980,7 @@ const props = defineProps<Props>()
```
> **💡 使用场景**
>
> - 支持 HTML 格式的任务名称
> - 添加自定义图标、标签或徽章
> - 根据任务状态显示不同样式
@@ -1998,6 +1988,7 @@ const props = defineProps<Props>()
> - 显示额外的业务信息
> **⚠️ 注意事项**
>
> - 插槽内容会同时在 TaskRow 和 TaskBar 中渲染
> - 需要根据 `type` 参数区分渲染位置
> - TaskRow 和 TaskBar 的可用空间不同,需要适配布局