v1.8.0 - Add actual Taskbar component

This commit is contained in:
LINING-PC\lining
2026-01-17 18:03:19 +08:00
parent 9fc10b2851
commit 90f0f5bc60
23 changed files with 1445 additions and 127 deletions

View File

@@ -5,6 +5,30 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.8.0] - 2026-01-17
### Added
- 🎉 新增GanttChart允许展示实际Taskbar
- 🎉 新增TaskDrawer增加实际开始日期和实际结束日期的属性
- 🎉 新增TaskDrawer增加当前Task状态待处理、进行中、已完成、已逾期
- 🎉 新增GanttChart增加Taskbar不同状态下背景色预设属性pendingTaskBackgroundColor、delayTaskBackgroundColo- pleteTaskBackgroundColor、ongoingTaskBackgroundColor
- 🎉 新增Task对象增加barColor属性允许自定义对应Taskbar的背景色优先级高于GanttChart预设背景色
- 🎉 新增Taskbar增加气泡提示框
- 🎉 新增GanttChart增加showActualTaskbar属性用于控制是否展示实际Taskbar默认值为false需要时手动打开
- 🎉 新增GanttChart增加enableTaskbarTooltip属性用于控制是否展示Taskbar的气泡提示框默认值为true需要时手动关闭
- 🎉 Added: GanttChart allows displaying actual Taskbar
- 🎉 Added: TaskDrawer adds properties for actual start date and actual end date
- 🎉 Added: TaskDrawer adds current Task status: Pending, Ongoing, Completed, Overdue
- 🎉 Added: GanttChart adds preset background color properties for Taskbar in different statuses: pendingTaskBackgroundColor, delayTaskBackgroundColor, completeTaskBackgroundColor, ongoingTaskBackgroundColor
- 🎉 Added: Task object adds barColor property to allow customization of the corresponding Taskbar background color, with higher priority than GanttChart preset background colors
- 🎉 Added: Taskbar adds tooltip
- 🎉 Added: GanttChart adds showActualTaskbar property to control whether to show actual Taskbar
- 🎉 Added: GanttChart adds enableTaskbarTooltip property to control whether to show Taskbar tooltip
### Enhancement
- 🎉 优化Task对象assignee以及avatar属性允许接收数组标记多为负责人
- Optimized: The assignee and avatar properties of the Task object now accept arrays to mark multiple assignees
## [1.7.2] - 2026-01-15
### Added

View File

@@ -209,7 +209,13 @@ npm run dev
| `timeScale` ![v1.7.1](https://img.shields.io/badge/v1.7.1-409EFF?style=flat-square&labelColor=ECF5FF) | `'hour' \| 'day' \| 'week' \| 'month' \| 'quarter' \| 'year'` | `'week'` | Time scale (reactive). Timeline scale will follow changes |
| `fullscreen` ![v1.7.1](https://img.shields.io/badge/v1.7.1-409EFF?style=flat-square&labelColor=ECF5FF) | `boolean` | `false` | Fullscreen state control (reactive). Component's fullscreen state will follow changes |
| `expandAll` ![v1.7.1](https://img.shields.io/badge/v1.7.1-409EFF?style=flat-square&labelColor=ECF5FF) | `boolean` | `true` | Expand/collapse all tasks (reactive). All tasks' expand state will follow changes |
| `enableLinkAnchor` ![v1.7.2](https://img.shields.io/badge/v1.7.2-409EFF?style=flat-square&labelColor=ECF5FF) | `boolean` | `true` | Whether to enable Link AnchorDefault: true |
| `enableLinkAnchor` ![v1.7.2](https://img.shields.io/badge/v1.7.2-409EFF?style=flat-square&labelColor=ECF5FF) | `boolean` | `true` | Whether to enable Link AnchorDefault: true |
| `pendingTaskBackgroundColor` ![v1.8.0](https://img.shields.io/badge/v1.8.0-409EFF?style=flat-square&labelColor=ECF5FF) | `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` ![v1.8.0](https://img.shields.io/badge/v1.8.0-409EFF?style=flat-square&labelColor=ECF5FF) | `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` ![v1.8.0](https://img.shields.io/badge/v1.8.0-409EFF?style=flat-square&labelColor=ECF5FF) | `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` ![v1.8.0](https://img.shields.io/badge/v1.8.0-409EFF?style=flat-square&labelColor=ECF5FF) | `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` ![v1.8.0](https://img.shields.io/badge/v1.8.0-409EFF?style=flat-square&labelColor=ECF5FF) | `boolean` | `false` | Whether to display actual TaskBar (shows actual execution progress below planned TaskBar) |
| `enableTaskbarTooltip` ![v1.8.0](https://img.shields.io/badge/v1.8.0-409EFF?style=flat-square&labelColor=ECF5FF) | `boolean` | `true` | Whether to enable TaskBar hover tooltip (shows task details on mouse hover) |
#### TaskListColumn Component Props
@@ -618,9 +624,9 @@ Tasks are the core elements of the Gantt chart. The component provides complete
| `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]`<br/>**Compatible formats**: Also supports string `'1,2,3'` or string array `['1', '2', '3']`, component will auto-parse |
| `assignee` | `string` | - | - | Task assignee, used as the value binding for the assignee dropdown menu |
| `assigneeName` | `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 |
| `avatar` | `string` | - | - | Avatar URL of task assignee |
| `assignee` | `string` \| `string[]` ![v1.8.0](https://img.shields.io/badge/v1.8.0-409EFF?style=flat-square&labelColor=ECF5FF) | - | - | Task assignee, used as the value binding for the assignee dropdown menu. Supports single assignee (string) or multiple assignees (string array) |
| `assigneeName` | `string` \| `string[]` ![v1.8.0](https://img.shields.io/badge/v1.8.0-409EFF?style=flat-square&labelColor=ECF5FF) | - | - | 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[]` ![v1.8.0](https://img.shields.io/badge/v1.8.0-409EFF?style=flat-square&labelColor=ECF5FF) | - | - | 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 |

View File

@@ -207,7 +207,13 @@ npm run dev
| `timeScale` ![v1.7.1](https://img.shields.io/badge/v1.7.1-409EFF?style=flat-square&labelColor=ECF5FF) | `'hour' \| 'day' \| 'week' \| 'month' \| 'quarter' \| 'year'` | `'week'` | 时间刻度(响应式)。设置后时间线刻度将跟随变化 |
| `fullscreen` ![v1.7.1](https://img.shields.io/badge/v1.7.1-409EFF?style=flat-square&labelColor=ECF5FF) | `boolean` | `false` | 全屏状态控制(响应式)。设置后组件全屏状态将跟随变化 |
| `expandAll` ![v1.7.1](https://img.shields.io/badge/v1.7.1-409EFF?style=flat-square&labelColor=ECF5FF) | `boolean` | `true` | 展开/收起所有任务(响应式)。设置后所有任务的展开状态将跟随变化 |
| `enableLinkAnchor` ![v1.7.2](https://img.shields.io/badge/v1.7.2-409EFF?style=flat-square&labelColor=ECF5FF) | `boolean` | `true` | 是否开启Taskbar的关系线锚点默认值: true |
| `enableLinkAnchor` ![v1.7.2](https://img.shields.io/badge/v1.7.2-409EFF?style=flat-square&labelColor=ECF5FF) | `boolean` | `true` | 是否开启Taskbar的关系线锚点默认值: true |
| `pendingTaskBackgroundColor` ![v1.8.0](https://img.shields.io/badge/v1.8.0-409EFF?style=flat-square&labelColor=ECF5FF) | `string` | `'#409eff'` | 待处理任务的TaskBar背景色。支持十六进制颜色值`'#409eff'`)。**优先级**:高于系统默认,低于 Task 对象的 `barColor` 属性 |
| `delayTaskBackgroundColor` ![v1.8.0](https://img.shields.io/badge/v1.8.0-409EFF?style=flat-square&labelColor=ECF5FF) | `string` | `'#f56c6c'` | 已逾期任务的TaskBar背景色。支持十六进制颜色值`'#f56c6c'`)。**优先级**:高于系统默认,低于 Task 对象的 `barColor` 属性 |
| `completeTaskBackgroundColor` ![v1.8.0](https://img.shields.io/badge/v1.8.0-409EFF?style=flat-square&labelColor=ECF5FF) | `string` | `'#909399'` | 已完成任务的TaskBar背景色。支持十六进制颜色值`'#909399'`)。**优先级**:高于系统默认,低于 Task 对象的 `barColor` 属性 |
| `ongoingTaskBackgroundColor` ![v1.8.0](https://img.shields.io/badge/v1.8.0-409EFF?style=flat-square&labelColor=ECF5FF) | `string` | `'#e6a23c'` | 进行中任务的TaskBar背景色。支持十六进制颜色值`'#e6a23c'`)。**优先级**:高于系统默认,低于 Task 对象的 `barColor` 属性 |
| `showActualTaskbar` ![v1.8.0](https://img.shields.io/badge/v1.8.0-409EFF?style=flat-square&labelColor=ECF5FF) | `boolean` | `false` | 是否显示实际TaskBar在计划TaskBar下方显示实际执行进度 |
| `enableTaskbarTooltip` ![v1.8.0](https://img.shields.io/badge/v1.8.0-409EFF?style=flat-square&labelColor=ECF5FF) | `boolean` | `true` | 是否启用TaskBar悬停提示框鼠标悬停显示任务详情 |
#### TaskListColumn 属性
@@ -616,9 +622,9 @@ const propsExpandAll = ref(false)
| `endDate` | `string` | - | - | 结束日期,格式:'YYYY-MM-DD' 或 'YYYY-MM-DD HH:mm' |
| `progress` | `number` | - | `0` | 任务进度,范围 0-100 |
| `predecessor` | `number[]` | - | - | 前置任务 ID 数组,标准格式:`[1, 2, 3]`<br/>**兼容格式**:也支持字符串 `'1,2,3'` 或字符串数组 `['1', '2', '3']`,组件会自动解析 |
| `assignee` | `string` | - | - | 任务负责人,用作负责人下拉菜单的值绑定 |
| `assigneeName` | `string` | - | - | 任务负责人姓名,自动从绑定的数据集`assigneeOptions`中获取Label作为显示如果需要自定义可以在GanttChart回调事件`task-added`中自定义信息 |
| `avatar` | `string` | - | - | 任务负责人头像 URL |
| `assignee` | `string` \| `string[]` ![v1.8.0](https://img.shields.io/badge/v1.8.0-409EFF?style=flat-square&labelColor=ECF5FF) | - | - | 任务负责人,用作负责人下拉菜单的值绑定。支持单个负责人(字符串)或多个负责人(字符串数组) |
| `assigneeName` | `string` \| `string[]` ![v1.8.0](https://img.shields.io/badge/v1.8.0-409EFF?style=flat-square&labelColor=ECF5FF) | - | - | 任务负责人姓名,自动从绑定的数据集`assigneeOptions`中获取Label作为显示如果需要自定义可以在GanttChart回调事件`task-added`中自定义信息。支持单个姓名(字符串)或多个姓名(字符串数组) |
| `avatar` | `string` \| `string[]` ![v1.8.0](https://img.shields.io/badge/v1.8.0-409EFF?style=flat-square&labelColor=ECF5FF) | - | - | 任务负责人头像 URL。支持单个头像(字符串)或多个头像(字符串数组) |
| `estimatedHours` | `number` | - | - | 预估工时(小时) |
| `actualHours` | `number` | - | - | 实际工时(小时) |
| `parentId` | `number` | - | - | 父任务 ID用于任务分组 |

View File

@@ -25,6 +25,9 @@ const { locale: demoLocale, messages: demoMessages, setLocale: setDemoLocale, fo
// Tool Settings 多语言
const ts = computed(() => demoMessages.value.toolSettings || {})
// TaskBar Config 多语言
const taskBarConfigMessages = computed(() => demoMessages.value.taskBarConfig || {})
// GanttChart ref
const gantt = ref<InstanceType<typeof import('../src/components/GanttChart.vue').default> | null>(null)
@@ -242,6 +245,13 @@ const taskBarOptions = ref({
dragDelayTime: 150, // 拖拽延迟时间(毫秒)
})
// 自定义任务状态背景色
const showActualTaskBar = ref(true)
const pendingTaskBackgroundColor = ref('#409eff')
const delayTaskBackgroundColor = ref('#f56c6c')
const completeTaskBackgroundColor = ref('#909399')
const ongoingTaskBackgroundColor = ref('#e6a23c')
const taskBarConfig = computed<TaskBarConfig>(() => ({
showAvatar: taskBarOptions.value.showAvatar,
showTitle: taskBarOptions.value.showTitle,
@@ -1302,6 +1312,53 @@ const handleCustomMenuAction = (action: string, task: Task) => {
<input v-model="taskBarOptions.showProgress" type="checkbox" />
<span class="taskbar-label">{{ t.taskBarConfig.display.showProgress }}</span>
</label>
<label class="taskbar-control">
<input v-model="showActualTaskBar" type="checkbox" />
<span class="taskbar-label">{{ taskBarConfigMessages.display?.showActualTaskBar }}</span>
</label>
</div>
<!-- 自定义任务状态背景色 -->
<div class="color-config-group">
<h6 class="color-config-title">{{ taskBarConfigMessages.colorConfig?.title }}</h6>
<div class="color-inputs-row">
<div class="color-input-item">
<label class="color-label">{{ taskBarConfigMessages.colorConfig?.pendingTask }}</label>
<input
v-model="pendingTaskBackgroundColor"
type="text"
class="color-input"
placeholder="#409eff"
/>
</div>
<div class="color-input-item">
<label class="color-label">{{ taskBarConfigMessages.colorConfig?.delayTask }}</label>
<input
v-model="delayTaskBackgroundColor"
type="text"
class="color-input"
placeholder="#f56c6c"
/>
</div>
<div class="color-input-item">
<label class="color-label">{{ taskBarConfigMessages.colorConfig?.completeTask }}</label>
<input
v-model="completeTaskBackgroundColor"
type="text"
class="color-input"
placeholder="#909399"
/>
</div>
<div class="color-input-item">
<label class="color-label">{{ taskBarConfigMessages.colorConfig?.ongoingTask }}</label>
<input
v-model="ongoingTaskBackgroundColor"
type="text"
class="color-input"
placeholder="#e6a23c"
/>
</div>
</div>
</div>
</div>
@@ -1324,11 +1381,11 @@ const handleCustomMenuAction = (action: string, task: Task) => {
<label class="taskbar-control">
<input v-model="enableTaskRowMove" type="checkbox" />
<span class="taskbar-label">
启用TaskRow拖拽移动
{{ taskBarConfigMessages.enableTaskRowMove?.label }}
</span>
</label>
<span class="control-hint">
允许通过拖拽TaskRow来调整任务的层级和顺序
{{ taskBarConfigMessages.enableTaskRowMove?.hint }}
</span>
</div>
<div class="control-row">
@@ -1819,12 +1876,16 @@ const handleCustomMenuAction = (action: string, task: Task) => {
:allow-drag-and-resize="allowDragAndResize"
:enable-task-row-move="enableTaskRowMove"
:assignee-options="assigneeOptions"
:task-list-row-class-name="getTaskRowClassName"
:task-list-row-style="getTaskRowStyle"
:on-export-csv="handleCustomCsvExport"
:on-language-change="handleLanguageChange"
:on-theme-change="handleThemeChange"
:task-list-column-render-mode="taskListColumnRenderMode"
:show-actual-taskbar="showActualTaskBar"
:enable-task-bar-tooltip="true"
:pending-task-background-color="pendingTaskBackgroundColor"
:delay-task-background-color="delayTaskBackgroundColor"
:complete-task-background-color="completeTaskBackgroundColor"
:ongoing-task-background-color="ongoingTaskBackgroundColor"
@milestone-saved="handleMilestoneSaved"
@milestone-deleted="handleMilestoneDeleted"
@milestone-icon-changed="handleMilestoneIconChanged"
@@ -2528,6 +2589,62 @@ const handleCustomMenuAction = (action: string, task: Task) => {
color: var(--gantt-primary-color, #409eff);
}
/* 自定义颜色配置组 */
.color-config-group {
margin-top: 16px;
padding: 12px;
background: var(--gantt-bg-tertiary, #fafafa);
border-radius: 8px;
border: 1px solid var(--gantt-border-color, #e4e7ed);
}
.color-config-title {
font-size: 13px;
font-weight: 600;
color: var(--gantt-text-secondary, #606266);
margin: 0 0 12px 0;
}
.color-inputs-row {
display: flex;
gap: 12px;
flex-wrap: nowrap;
}
.color-input-item {
display: flex;
flex-direction: column;
gap: 6px;
flex: 1;
min-width: 0;
}
.color-label {
font-size: 12px;
font-weight: 500;
color: var(--gantt-text-secondary, #606266);
}
.color-input {
padding: 6px 10px;
font-size: 13px;
border: 1px solid var(--gantt-border-color, #dcdfe6);
border-radius: 4px;
background: var(--gantt-bg-primary, #ffffff);
color: var(--gantt-text-primary, #333);
transition: all 0.2s ease;
}
.color-input:focus {
outline: none;
border-color: var(--gantt-primary-color, #409eff);
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
}
.color-input::placeholder {
color: var(--gantt-text-placeholder, #c0c4cc);
}
/* TaskBar 高级配置样式 */
.taskbar-advanced-controls {
display: flex;

View File

@@ -27,21 +27,25 @@
"description": "评估ADX-2024在健康志愿者和患者中的安全性、耐受性和药代动力学特征",
"custom": "test11111",
"priority": "高",
"barColor": "#7B3FF2",
"children": [
{
"id": 1101,
"name": "试验方案设计与<span style='font-weight: bold;color:red;'>伦理审查</span>",
"assignee": "方案设计师 李明",
"avatar": "50-1.jpg",
"avatar": ["50-1.jpg", "50-2.jpg"],
"startDate": "2025-01-01",
"endDate": "2025-02-28",
"actualStartDate": "2025-01-03",
"actualEndDate": "2025-02-25",
"progress": 100,
"estimatedHours": 464,
"actualHours": 480,
"type": "task",
"parentId": 1100,
"description": "完成I期试验方案设计、伦理委员会审批及监管部门申报",
"custom": "test11111"
"custom": "test11111",
"barColor": "#2E7D6B"
},
{
"id": 1102,
@@ -50,6 +54,8 @@
"avatar": "50-2.jpg",
"startDate": "2025-03-01",
"endDate": "2025-04-30",
"actualStartDate": "2025-03-05",
"actualEndDate": "2025-04-28",
"progress": 90,
"estimatedHours": 1392.5,
"actualHours": 1250.25,
@@ -57,15 +63,17 @@
"predecessor": [1101],
"parentId": 1100,
"description": "招募24名健康志愿者完成医学筛选和入组评估",
"custom": "test11111"
"custom": "test11111",
"barColor": "#FF4D4F"
},
{
"id": 1103,
"name": "药物给药与安全性监测",
"assignee": "临床医生 Dr. Liu",
"avatar": "50-3.jpg",
"avatar": ["50-3.jpg", "50-2.jpg"],
"startDate": "2025-05-01",
"endDate": "2025-08-31",
"actualStartDate": "2025-05-10",
"progress": 40,
"estimatedHours": 3400,
"actualHours": 1687,
@@ -73,7 +81,8 @@
"predecessor": [1102],
"parentId": 1100,
"description": "按照剂量递增方案给药,密切监测不良反应和药代动力学参数",
"custom": "test11111"
"custom": "test11111",
"barColor": "#7B3FF2"
}
],
"collapsed": false
@@ -91,14 +100,16 @@
"parentId": 1000,
"description": "在目标患者群体中评估ADX-2024的初步疗效和进一步的安全性",
"custom": "test11111",
"barColor": "#3F6DF6",
"children": [
{
"id": 1201,
"name": "多中心试验<span style='font-weight: bold; color: blue;'>启动</span>",
"assignee": "项目经理 王芳",
"avatar": "50-10.jpg",
"avatar": ["50-10.jpg", "50-2.jpg"],
"startDate": "2025-09-01",
"endDate": "2025-11-30",
"actualStartDate": "2025-09-01",
"progress": 25,
"estimatedHours": 2160,
"actualHours": 324,
@@ -106,13 +117,14 @@
"predecessor": [1103],
"parentId": 1200,
"description": "启动5个临床中心完成研究者培训和质量体系建立",
"custom": "test11111"
"custom": "test11111",
"barColor": "#3F6DF6"
},
{
"id": 1202,
"name": "患者入组与随机化",
"assignee": "数据管理员 陈静",
"avatar": "50-6.jpg",
"avatar": ["50-6.jpg", "50-10.jpg"],
"startDate": "2025-12-01",
"endDate": "2026-03-31",
"progress": 0,
@@ -138,7 +150,8 @@
"predecessor": [1202],
"parentId": 1200,
"description": "定期评估患者疗效指标,收集安全性数据,进行中期分析",
"custom": "test11111"
"custom": "test11111",
"barColor": "#FFA43A"
}
],
"collapsed": false

View File

@@ -111,5 +111,21 @@
"false": "False"
}
}
},
"taskBarConfig": {
"display": {
"showActualTaskBar": "Show Actual TaskBar"
},
"colorConfig": {
"title": "Custom Task Status Background Colors",
"pendingTask": "Pending Task",
"delayTask": "Overdue Task",
"completeTask": "Completed Task",
"ongoingTask": "Ongoing Task"
},
"enableTaskRowMove": {
"label": "Enable TaskRow Drag and Drop",
"hint": "Allow dragging TaskRow to adjust task hierarchy and order"
}
}
}

View File

@@ -111,5 +111,21 @@
"false": "假"
}
}
},
"taskBarConfig": {
"display": {
"showActualTaskBar": "显示实际TaskBar"
},
"colorConfig": {
"title": "自定义任务状态背景色",
"pendingTask": "待处理任务",
"delayTask": "逾期任务",
"completeTask": "已完成任务",
"ongoingTask": "进行中任务"
},
"enableTaskRowMove": {
"label": "启用TaskRow拖拽移动",
"hint": "允许通过拖拽TaskRow来调整任务的层级和顺序"
}
}
}

View File

@@ -27,11 +27,114 @@ interface LocaleMessages {
task: string
}
}
taskBarConfig: {
display: {
showActualTaskBar: string
}
colorConfig: {
title: string
pendingTask: string
delayTask: string
completeTask: string
ongoingTask: string
}
enableTaskRowMove: {
label: string
hint: string
}
}
toolSettings: {
title: string
currentStatus: {
title: string
fullscreen: string
expandAll: string
locale: string
timeScale: string
theme: string
controlMode: string
active: string
inactive: string
expanded: string
collapsed: string
}
controlMode: {
title: string
expose: string
props: string
exposeHint: string
propsHint: string
}
exposeMethods: {
sectionTitle: string
fullscreen: {
title: string
enter: string
exit: string
toggle: string
}
expand: {
title: string
all: string
none: string
toggle: string
}
timeScale: {
title: string
hour: string
day: string
week: string
month: string
quarter: string
year: string
zoomIn: string
zoomOut: string
}
locale: {
title: string
zhCN: string
enUS: string
}
theme: {
title: string
light: string
dark: string
}
navigation: {
title: string
today: string
taskIdPlaceholder: string
go: string
}
}
propsControl: {
sectionTitle: string
locale: {
title: string
}
theme: {
title: string
}
timeScale: {
title: string
}
fullscreen: {
title: string
true: string
false: string
}
expandAll: {
title: string
true: string
false: string
}
}
}
}
const localeMessages: Record<LocaleKey, LocaleMessages> = {
'zh-CN': zhCN,
'en-US': enUS,
'zh-CN': zhCN as any,
'en-US': enUS as any,
}
const currentLocale = ref<LocaleKey>('zh-CN')

View File

@@ -466,5 +466,31 @@
"🎉 Added: New property in GanttChart - enableLinkAnchor to control the relationship line anchor points of Taskbar",
"<span style=\"font-weight: bold; color: #f00;\">Special thanks to YQ6494@gitee for their use and suggestions</span>"
]
},
{
"version": "1.8.0",
"date": "2026-01-17",
"notes": [
"🎉 新增GanttChart允许展示实际Taskbar",
"🎉 新增TaskDrawer增加实际开始日期和实际结束日期的属性",
"🎉 新增TaskDrawer增加当前Task状态待处理、进行中、已完成、已逾期",
"🎉 新增GanttChart增加Taskbar不同状态下背景色预设属性pendingTaskBackgroundColor、delayTaskBackgroundColor、completeTaskBackgroundColor、ongoingTaskBackgroundColor",
"🎉 新增Task对象增加barColor属性允许自定义对应Taskbar的背景色优先级高于GanttChart预设背景色",
"🎉 新增Taskbar增加气泡提示框",
"🎉 新增GanttChart增加showActualTaskbar属性用于控制是否展示实际Taskbar默认值为false需要时手动打开",
"🎉 新增GanttChart增加enableTaskbarTooltip属性用于控制是否展示Taskbar的气泡提示框默认值为true需要时手动关闭",
"🎉 优化Task对象assignee以及avatar属性允许接收数组标记多为负责人",
"<span style=\"font-weight: bold; color: #f00;\">特别感谢 yunlbs/fhjfhj/jakepan@gitee的使用及建议</span>",
"🎉 Added: GanttChart allows displaying actual Taskbar",
"🎉 Added: TaskDrawer adds properties for actual start date and actual end date",
"🎉 Added: TaskDrawer adds current Task status: Pending, Ongoing, Completed, Overdue",
"🎉 Added: GanttChart adds preset background color properties for Taskbar in different statuses: pendingTaskBackgroundColor, delayTaskBackgroundColor, completeTaskBackgroundColor, ongoingTaskBackgroundColor",
"🎉 Added: Task object adds barColor property to allow customization of the corresponding Taskbar background color, with higher priority than GanttChart preset background colors",
"🎉 Added: Taskbar adds tooltip",
"🎉 Added: GanttChart adds showActualTaskbar property to control whether to show actual Taskbar",
"🎉 Added: GanttChart adds enableTaskbarTooltip property to control whether to show Taskbar tooltip",
"🎉 Optimized: The assignee and avatar properties of the Task object now accept arrays to mark multiple assignees",
"<span style=\"font-weight: bold; color: #f00;\">Special thanks to yunlbs/fhjfhj/jakepan@gitee for their use and suggestions</span>"
]
}
]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 151 KiB

After

Width:  |  Height:  |  Size: 261 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 141 KiB

After

Width:  |  Height:  |  Size: 260 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 203 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

After

Width:  |  Height:  |  Size: 248 KiB

View File

@@ -1,6 +1,6 @@
{
"name": "jordium-gantt-vue3",
"version": "1.7.2",
"version": "1.8.0",
"type": "module",
"main": "npm-package/dist/jordium-gantt-vue3.cjs.js",
"module": "npm-package/dist/jordium-gantt-vue3.es.js",

View File

@@ -59,6 +59,8 @@ const props = withDefaults(defineProps<Props>(), {
enableTaskListContextMenu: true,
enableTaskBarContextMenu: true,
enableLinkAnchor: true,
showActualTaskbar: false,
enableTaskBarTooltip: true,
fullscreen: false,
expandAll: true,
locale: 'zh-CN',
@@ -201,6 +203,21 @@ interface Props {
// 是否启用 LinkAnchor 连接触点功能(默认为 true
// 当设置为 false 时TaskBar 上不显示前置/后置任务的连接触点
enableLinkAnchor?: boolean
// 是否启用 TaskBar 气泡提示框(默认为 true
// 当设置为 false 时TaskBar 上不显示悬停气泡提示框
enableTaskBarTooltip?: boolean
// 是否显示实际任务条(默认为 false
// 当设置为 true 且任务存在 actualStartDate 时,会在计划任务条下方显示实际任务条
showActualTaskbar?: boolean
// 自定义任务状态背景色优先级高于默认配色低于Task.barColor
// 待处理任务背景色:任务未开始且未逾期时使用
pendingTaskBackgroundColor?: string
// 逾期任务背景色:当前日期晚于任务结束日期时使用
delayTaskBackgroundColor?: string
// 已完成任务背景色任务进度达到100%时使用
completeTaskBackgroundColor?: string
// 进行中任务背景色:任务已开始但未完成且未逾期时使用
ongoingTaskBackgroundColor?: string
// 全屏状态控制(响应式)
fullscreen?: boolean
// 展开/收起所有任务(响应式)
@@ -1201,6 +1218,8 @@ const milestonesForTimeline = computed((): Milestone[] => {
startDate: task.startDate, // 此时已确保非空
endDate: task.endDate,
assignee: task.assignee,
assigneeName: task.assigneeName,
avatar: task.avatar,
type: task.type || 'milestone',
icon: task.icon,
description: task.description,
@@ -2808,6 +2827,12 @@ defineExpose({
:working-hours="props.workingHours"
:task-bar-config="props.taskBarConfig"
:allow-drag-and-resize="props.allowDragAndResize"
:show-actual-taskbar="props.showActualTaskbar"
:enable-task-bar-tooltip="props.enableTaskBarTooltip"
:pending-task-background-color="props.pendingTaskBackgroundColor"
:delay-task-background-color="props.delayTaskBackgroundColor"
:complete-task-background-color="props.completeTaskBackgroundColor"
:ongoing-task-background-color="props.ongoingTaskBackgroundColor"
:use-default-drawer="props.useDefaultDrawer"
:use-default-milestone-dialog="props.useDefaultMilestoneDialog"
:on-milestone-save="handleMilestoneSave"
@@ -2861,6 +2886,10 @@ defineExpose({
:task="taskDrawerTask"
:is-edit="taskDrawerEditMode"
:assignee-options="props.assigneeOptions"
:pending-task-background-color="props.pendingTaskBackgroundColor"
:delay-task-background-color="props.delayTaskBackgroundColor"
:complete-task-background-color="props.completeTaskBackgroundColor"
:ongoing-task-background-color="props.ongoingTaskBackgroundColor"
@submit="handleTaskDrawerSubmit"
@close="taskDrawerVisible = false"
@start-timer="handleStartTimer"

View File

@@ -509,7 +509,7 @@ defineExpose({
top: `${offsetTop}px`,
width: `${width}px`,
height: `${height}px`,
zIndex: highlightedTaskId !== null ? 1001 : 25,
zIndex: highlightedTaskId !== null ? 2000 : 500,
pointerEvents: 'none',
}"
/>

File diff suppressed because it is too large Load Diff

View File

@@ -21,6 +21,10 @@ interface Props {
isEdit?: boolean
onDelete?: (task: Task) => void
assigneeOptions?: AssigneeOption[]
pendingTaskBackgroundColor?: string
delayTaskBackgroundColor?: string
completeTaskBackgroundColor?: string
ongoingTaskBackgroundColor?: string
}
const props = withDefaults(defineProps<Props>(), {
@@ -35,6 +39,10 @@ const props = withDefaults(defineProps<Props>(), {
{ value: 'zhaoliu', label: '赵六' },
{ value: 'qianqi', label: '钱七' },
],
pendingTaskBackgroundColor: '#c0c4cc',
delayTaskBackgroundColor: '#f56c6c',
completeTaskBackgroundColor: '#67c23a',
ongoingTaskBackgroundColor: '#409eff',
})
const emit = defineEmits<{
@@ -146,6 +154,8 @@ const formData = reactive<Task>({
assignee: '',
startDate: '',
endDate: '',
actualStartDate: '',
actualEndDate: '',
predecessor: [],
estimatedHours: 0,
actualHours: 0,
@@ -334,6 +344,8 @@ const errors = reactive({
type: '',
startDate: '',
endDate: '',
actualStartDate: '',
actualEndDate: '',
})
// 监听 visible 属性变化
@@ -397,6 +409,8 @@ const resetForm = () => {
assignee: '',
startDate: '',
endDate: '',
actualStartDate: '',
actualEndDate: '',
predecessor: [],
estimatedHours: 0,
actualHours: 0,
@@ -408,6 +422,9 @@ const resetForm = () => {
timerEndTime: undefined,
timerElapsedTime: 0,
children: undefined,
bgColor: undefined,
avatar: undefined,
barColor: undefined,
})
// 清除错误信息
@@ -434,14 +451,19 @@ const validateForm = (): boolean => {
isValid = false
}
if (!formData.startDate) {
errors.startDate = t.value.startDateRequired
errors.startDate = t.value.plannedStartDateRequired
isValid = false
}
if (!formData.endDate) {
errors.endDate = t.value.endDateRequired
errors.endDate = t.value.plannedEndDateRequired
isValid = false
} else if (formData.startDate && new Date(formData.endDate) < new Date(formData.startDate)) {
errors.endDate = t.value.endDateInvalid
errors.endDate = t.value.plannedEndDateInvalid
isValid = false
}
// 验证实际日期(可选字段)
if (formData.actualStartDate && formData.actualEndDate && new Date(formData.actualEndDate) < new Date(formData.actualStartDate)) {
errors.actualEndDate = t.value.actualEndDateInvalid
isValid = false
}
return isValid
@@ -654,6 +676,71 @@ const handleAssigneeChanged = (event: Event) => {
formData.assigneeName = selected.label
}
}
// 计算任务状态
const taskStatus = computed(() => {
if (!props.task) return { text: '', color: '', bgColor: '', borderColor: '' }
const task = props.task
const progress = task.progress || 0
const now = new Date()
const endDate = task.endDate ? new Date(task.endDate) : null
const startDate = task.startDate ? new Date(task.startDate) : null
const actualStartDate = task.actualStartDate
// 待处理没有实际开始日期并且进度为0
if (!actualStartDate && progress === 0) {
const color = props.pendingTaskBackgroundColor
return {
text: t.value.statusPending,
color: color,
bgColor: color + '20', // 添加20%透明度
borderColor: color,
}
}
// 已完成
if (progress >= 100) {
const color = props.completeTaskBackgroundColor
return {
text: t.value.statusCompleted,
color: color,
bgColor: color + '20',
borderColor: color,
}
}
// 已逾期:当前时间已超过结束日期,但未完成
if (endDate && now > endDate && progress < 100) {
const color = props.delayTaskBackgroundColor
return {
text: t.value.statusDelayed,
color: color,
bgColor: color + '20',
borderColor: color,
}
}
// 进行中:当前时间在开始日期之后,且未完成,且未逾期
if (startDate && now >= startDate && progress < 100) {
const color = props.ongoingTaskBackgroundColor
return {
text: t.value.statusOngoing,
color: color,
bgColor: color + '20',
borderColor: color,
}
}
// 默认待处理
const color = props.pendingTaskBackgroundColor
return {
text: t.value.statusPending,
color: color,
bgColor: color + '20',
borderColor: color,
}
})
</script>
<template>
@@ -763,6 +850,18 @@ const handleAssigneeChanged = (event: Event) => {
</span>
</div>
<div style="flex: 1"></div>
<!-- Status Badge -->
<div
v-if="isEdit && props.task"
class="status-badge"
:style="{
backgroundColor: taskStatus.bgColor,
color: taskStatus.color,
border: `1px solid ${taskStatus.borderColor}`,
}"
>
{{ taskStatus.text }}
</div>
<button class="drawer-close-btn" type="button" @click="handleClose">
<svg
class="close-icon"
@@ -844,14 +943,14 @@ const handleAssigneeChanged = (event: Event) => {
<div class="form-row">
<div class="form-group">
<label class="form-label" for="task-start-date">
{{ t.startDate }} <span class="required">*</span></label
{{ t.plannedStartDate }} <span class="required">*</span></label
>
<DatePicker
id="task-start-date"
v-model="formData.startDate"
:type="'datetime' as any"
value-format="YYYY-MM-DD HH:mm"
:placeholder="t.startDateRequired"
:placeholder="t.plannedStartDateRequired"
:class="{ error: errors.startDate }"
/>
<span v-if="errors.startDate" class="error-text">{{ errors.startDate }}</span>
@@ -859,20 +958,52 @@ const handleAssigneeChanged = (event: Event) => {
<div class="form-group">
<label class="form-label" for="task-end-date">
{{ t.endDate }} <span class="required">*</span></label
{{ t.plannedEndDate }} <span class="required">*</span></label
>
<DatePicker
id="task-end-date"
v-model="formData.endDate"
:type="'datetime' as any"
value-format="YYYY-MM-DD HH:mm"
:placeholder="t.endDateRequired"
:placeholder="t.plannedEndDateRequired"
:class="{ error: errors.endDate }"
/>
<span v-if="errors.endDate" class="error-text">{{ errors.endDate }}</span>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label class="form-label" for="task-actual-start-date">
{{ t.actualStartDate }}</label
>
<DatePicker
id="task-actual-start-date"
v-model="formData.actualStartDate"
:type="'datetime' as any"
value-format="YYYY-MM-DD HH:mm"
:placeholder="t.actualStartDate"
:class="{ error: errors.actualStartDate }"
/>
<span v-if="errors.actualStartDate" class="error-text">{{ errors.actualStartDate }}</span>
</div>
<div class="form-group">
<label class="form-label" for="task-actual-end-date">
{{ t.actualEndDate }}</label
>
<DatePicker
id="task-actual-end-date"
v-model="formData.actualEndDate"
:type="'datetime' as any"
value-format="YYYY-MM-DD HH:mm"
:placeholder="t.actualEndDate"
:class="{ error: errors.actualEndDate }"
/>
<span v-if="errors.actualEndDate" class="error-text">{{ errors.actualEndDate }}</span>
</div>
</div>
<MultiSelectPredecessor
v-model="formData.predecessor"
:tasks="allTasks"
@@ -1068,6 +1199,15 @@ const handleAssigneeChanged = (event: Event) => {
color: var(--gantt-text-primary, #303133);
}
.status-badge {
padding: 4px 12px;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
white-space: nowrap;
margin-left: 12px;
}
.drawer-close-btn {
background: none;
border: none;
@@ -1075,6 +1215,7 @@ const handleAssigneeChanged = (event: Event) => {
padding: 4px;
color: var(--gantt-text-muted, #909399);
transition: color 0.2s;
margin-left: 12px;
}
.drawer-close-btn:hover {

View File

@@ -226,6 +226,59 @@ const slotPayload = computed(() => ({
daysText: daysText.value,
progressClass: progressClass.value,
}))
// 计算左侧边框颜色 - 支持barColor自定义
const leftBorderColor = computed(() => {
// 如果设置了barColor优先使用
if (props.task.barColor) {
return props.task.barColor
}
// 否则返回null使用CSS默认样式
return null
})
// 计算自定义边框样式
const customBorderStyle = computed(() => {
if (leftBorderColor.value) {
return {
borderLeftColor: leftBorderColor.value
}
}
return {}
})
// 处理assignee列的显示数据
const assigneeDisplayData = computed(() => {
const task = props.task
const avatar = task.avatar
const assignee = task.assignee
// 处理avatar列表
let avatarList: string[] = []
if (avatar) {
avatarList = Array.isArray(avatar) ? avatar : [avatar]
}
// 处理assignee列表
let assigneeList: string[] = []
if (assignee) {
assigneeList = Array.isArray(assignee) ? assignee : [assignee]
}
// 如果没有avatar从assignee生成文字头像
const displayAvatars = avatarList.length > 0
? avatarList.map(url => ({ type: 'image', url }))
: assigneeList.map(name => ({ type: 'text', name }))
// 生成显示的名称文本(换行拼接)
const nameText = assigneeList.join('\n') || '-'
return {
avatars: displayAvatars,
nameText,
hasMultiple: displayAvatars.length > 1
}
})
</script>
<template>
@@ -243,7 +296,7 @@ const slotPayload = computed(() => ({
'task-type-milestone': isMilestoneTask,
[customRowClass]: customRowClass,
}"
:style="customRowStyle"
:style="[customRowStyle, customBorderStyle]"
@click="handleRowClick"
@dblclick="handleTaskRowDoubleClick"
@mousedown="handleMouseDown"
@@ -373,10 +426,25 @@ const slotPayload = computed(() => ({
<!-- 负责人列 -->
<template v-else-if="column.key === 'assignee'">
<div class="assignee-info">
<div class="avatar">
{{ props.task.assignee ? props.task.assignee.charAt(0) : '-' }}
<!-- 多头像容器 -->
<div class="assignee-avatars-container">
<div
v-for="(avatarItem, idx) in assigneeDisplayData.avatars"
:key="idx"
class="avatar"
:style="{
zIndex: idx + 1,
marginLeft: idx > 0 ? '-8px' : '0'
}"
>
<!-- 图片头像 -->
<img v-if="avatarItem.type === 'image'" :src="(avatarItem as any).url" :alt="`avatar-${idx}`" />
<!-- 文字头像 -->
<span v-else class="avatar-text">{{ (avatarItem as any).name.charAt(0).toUpperCase() }}</span>
</div>
</div>
<span class="assignee-name">{{ props.task.assignee || '-' }}</span>
<!-- 名称显示支持换行和超出显示... -->
<span class="assignee-name" :title="assigneeDisplayData.nameText">{{ assigneeDisplayData.nameText }}</span>
</div>
</template>
@@ -614,9 +682,29 @@ const slotPayload = computed(() => ({
gap: 8px;
}
/* 多头像容器 */
.assignee-avatars-container {
display: flex;
align-items: center;
position: relative;
}
.assignee-avatars-container .avatar {
position: relative;
transition: transform 0.2s ease;
cursor: pointer;
}
.assignee-avatars-container .avatar:hover {
transform: translateY(-2px) scale(1.1);
z-index: 999 !important;
}
.avatar {
min-width: 25px;
min-height: 25px;
width: 25px;
height: 25px;
border-radius: 50%;
background: var(--gantt-primary);
color: var(--gantt-text-white);
@@ -627,11 +715,31 @@ const slotPayload = computed(() => ({
font-weight: 500;
border: 2px solid var(--gantt-border-medium);
box-sizing: border-box;
flex-shrink: 0;
}
.avatar img {
width: 100%;
height: 100%;
border-radius: 50%;
object-fit: cover;
}
.avatar .avatar-text {
font-size: 11px;
font-weight: 600;
}
.assignee-name {
font-size: 14px;
color: var(--gantt-text-secondary);
white-space: pre-line;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
line-height: 1.4;
}
.progress-value {

View File

@@ -37,6 +37,15 @@ interface Props {
taskBarConfig?: TaskBarConfig
// 是否允许拖拽和拉伸(默认为 true
allowDragAndResize?: boolean
// 是否显示实际任务条(默认为 false
showActualTaskbar?: boolean
// 是否启用 TaskBar 气泡提示框(默认为 true
enableTaskBarTooltip?: boolean
// 自定义任务状态背景色
pendingTaskBackgroundColor?: string
delayTaskBackgroundColor?: string
completeTaskBackgroundColor?: string
ongoingTaskBackgroundColor?: string
}
const props = withDefaults(defineProps<Props>(), {
@@ -55,6 +64,12 @@ const props = withDefaults(defineProps<Props>(), {
}),
taskBarConfig: undefined,
allowDragAndResize: true,
showActualTaskbar: false,
enableTaskBarTooltip: true,
pendingTaskBackgroundColor: undefined,
delayTaskBackgroundColor: undefined,
completeTaskBackgroundColor: undefined,
ongoingTaskBackgroundColor: undefined,
})
// 定义emits
@@ -1049,11 +1064,6 @@ const hasCircularDependency = (taskId: number, targetId: number): boolean => {
// 创建连接
const createLink = (sourceTask: Task, targetTask: Task, mode: 'predecessor' | 'successor') => {
console.log('----> hello:', {
mode,
sourceTask,
targetTask,
})
if (mode === 'predecessor') {
// mode='predecessor' 表示从左侧 anchor 拖出
// 从左侧 anchor 拖到右侧sourceTask 依赖 targetTask
@@ -4478,6 +4488,12 @@ const handleAddSuccessor = (task: Task) => {
:current-time-scale="currentTimeScale"
:task-bar-config="props.taskBarConfig"
:allow-drag-and-resize="props.allowDragAndResize && !isInHighlightMode"
:show-actual-taskbar="props.showActualTaskbar"
:enable-task-bar-tooltip="props.enableTaskBarTooltip"
:pending-task-background-color="props.pendingTaskBackgroundColor"
:delay-task-background-color="props.delayTaskBackgroundColor"
:complete-task-background-color="props.completeTaskBackgroundColor"
:ongoing-task-background-color="props.ongoingTaskBackgroundColor"
:is-highlighted="highlightedTaskIds.has(task.id)"
:is-primary-highlight="highlightedTaskId === task.id"
:is-in-highlight-mode="isInHighlightMode"

View File

@@ -13,6 +13,10 @@ const messages = {
assignee: '分配人',
startDate: '开始日期',
endDate: '结束日期',
plannedStartDate: '预计开始日期',
plannedEndDate: '预计结束日期',
actualStartDate: '实际开始日期',
actualEndDate: '实际结束日期',
estimatedHours: '预计工时(hr)',
actualHours: '实际工时(hr)',
progress: '进度',
@@ -26,6 +30,10 @@ const messages = {
assignee: '分配人',
startDate: '开始日期',
endDate: '结束日期',
plannedStartDate: '预计开始日期',
plannedEndDate: '预计结束日期',
actualStartDate: '实际开始日期',
actualEndDate: '实际结束日期',
estimatedHours: '预计工时(hr)',
actualHours: '实际工时(hr)',
progress: '进度(%)',
@@ -119,6 +127,15 @@ const messages = {
startDateRequired: '开始日期不能为空',
endDateRequired: '结束日期不能为空',
endDateInvalid: '结束日期不能早于开始日期',
plannedStartDateRequired: '预计开始日期不能为空',
plannedEndDateRequired: '预计结束日期不能为空',
plannedEndDateInvalid: '预计结束日期不能早于预计开始日期',
actualEndDateInvalid: '实际结束日期不能早于实际开始日期',
// 任务状态
statusPending: '待处理',
statusOngoing: '进行中',
statusDelayed: '已逾期',
statusCompleted: '已完成',
// 新建里程碑对话框
newMilestone: '新建里程碑',
editMilestone: '编辑里程碑',
@@ -279,6 +296,10 @@ const messages = {
assignee: 'Assignee',
startDate: 'Start Date',
endDate: 'End Date',
plannedStartDate: 'Planned Start Date',
plannedEndDate: 'Planned End Date',
actualStartDate: 'Actual Start Date',
actualEndDate: 'Actual End Date',
estimatedHours: 'Est. Hours',
actualHours: 'Act. Hours',
progress: 'Progress',
@@ -291,6 +312,10 @@ const messages = {
assignee: 'Assignee',
startDate: 'Start Date',
endDate: 'End Date',
plannedStartDate: 'Planned Start Date',
plannedEndDate: 'Planned End Date',
actualStartDate: 'Actual Start Date',
actualEndDate: 'Actual End Date',
estimatedHours: 'Est. Hours',
actualHours: 'Act. Hours',
progress: 'Progress (%)',
@@ -382,6 +407,15 @@ const messages = {
startDateRequired: 'Start date is required',
endDateRequired: 'End date is required',
endDateInvalid: 'End date cannot be earlier than start date',
plannedStartDateRequired: 'Planned start date is required',
plannedEndDateRequired: 'Planned end date is required',
plannedEndDateInvalid: 'Planned end date cannot be earlier than planned start date',
actualEndDateInvalid: 'Actual end date cannot be earlier than actual start date',
// Task status
statusPending: 'Pending',
statusOngoing: 'Ongoing',
statusDelayed: 'Delayed',
statusCompleted: 'Completed',
// 新建里程碑对话框
newMilestone: 'New Milestone',
editMilestone: 'Edit Milestone',

View File

@@ -4,7 +4,9 @@ export interface Milestone {
name: string
startDate: string
endDate?: string
assignee?: string
assignee?: string | string[] // 支持单个或多个负责人
assigneeName?: string | string[] // 支持单个或多个负责人姓名
avatar?: string | string[] // 支持单个或多个头像
type: string
icon?: string
description?: string

View File

@@ -3,11 +3,13 @@ export interface Task {
id: number
name: string
predecessor?: number[] // 前置任务ID数组
assignee?: string // 记录唯一键值如用户ID或用户名
assignee?: string | string[] // 记录唯一键值如用户ID或用户名,支持单个或多个
assigneeName?: string // 任务负责人名称
avatar?: string // 任务负责人头像URL
avatar?: string | string[] // 任务负责人头像URL,支持单个或多个头像数组
startDate?: string
endDate?: string
actualStartDate?: string // 实际开始日期
actualEndDate?: string // 实际结束日期
progress?: number
estimatedHours?: number
actualHours?: number
@@ -27,6 +29,8 @@ export interface Task {
timerElapsedTime?: number
// 权限控制
isEditable?: boolean // 是否可编辑可拖拽、拉伸默认为true
// 自定义样式
barColor?: string // 自定义TaskBar颜色如 '#ff5733',若不设置则使用默认颜色方案
// 支持自定义属性 - 使用 unknown 允许任意类型
[key: string]: unknown
}