v1.8.0 - Add actual Taskbar component
@@ -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/),
|
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).
|
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
|
## [1.7.2] - 2026-01-15
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -209,7 +209,13 @@ npm run dev
|
|||||||
| `timeScale`  | `'hour' \| 'day' \| 'week' \| 'month' \| 'quarter' \| 'year'` | `'week'` | Time scale (reactive). Timeline scale will follow changes |
|
| `timeScale`  | `'hour' \| 'day' \| 'week' \| 'month' \| 'quarter' \| 'year'` | `'week'` | Time scale (reactive). Timeline scale will follow changes |
|
||||||
| `fullscreen`  | `boolean` | `false` | Fullscreen state control (reactive). Component's fullscreen state will follow changes |
|
| `fullscreen`  | `boolean` | `false` | Fullscreen state control (reactive). Component's fullscreen state will follow changes |
|
||||||
| `expandAll`  | `boolean` | `true` | Expand/collapse all tasks (reactive). All tasks' expand state will follow changes |
|
| `expandAll`  | `boolean` | `true` | Expand/collapse all tasks (reactive). All tasks' expand state will follow changes |
|
||||||
| `enableLinkAnchor`  | `boolean` | `true` | Whether to enable Link Anchor,Default: true |
|
| `enableLinkAnchor`  | `boolean` | `true` | Whether to enable Link Anchor,Default: true |
|
||||||
|
| `pendingTaskBackgroundColor`  | `string` | `'#409eff'` | Background color for pending tasks' TaskBar. Supports hex color values (e.g., `'#409eff'`). **Priority**: Higher than system default, lower than Task object's `barColor` property |
|
||||||
|
| `delayTaskBackgroundColor`  | `string` | `'#f56c6c'` | Background color for overdue tasks' TaskBar. Supports hex color values (e.g., `'#f56c6c'`). **Priority**: Higher than system default, lower than Task object's `barColor` property |
|
||||||
|
| `completeTaskBackgroundColor`  | `string` | `'#909399'` | Background color for completed tasks' TaskBar. Supports hex color values (e.g., `'#909399'`). **Priority**: Higher than system default, lower than Task object's `barColor` property |
|
||||||
|
| `ongoingTaskBackgroundColor`  | `string` | `'#e6a23c'` | Background color for ongoing tasks' TaskBar. Supports hex color values (e.g., `'#e6a23c'`). **Priority**: Higher than system default, lower than Task object's `barColor` property |
|
||||||
|
| `showActualTaskbar`  | `boolean` | `false` | Whether to display actual TaskBar (shows actual execution progress below planned TaskBar) |
|
||||||
|
| `enableTaskbarTooltip`  | `boolean` | `true` | Whether to enable TaskBar hover tooltip (shows task details on mouse hover) |
|
||||||
|
|
||||||
#### TaskListColumn Component Props
|
#### 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' |
|
| `endDate` | `string` | - | - | End date, format: 'YYYY-MM-DD' or 'YYYY-MM-DD HH:mm' |
|
||||||
| `progress` | `number` | - | `0` | Task progress, range 0-100 |
|
| `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 |
|
| `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 |
|
| `assignee` | `string` \| `string[]`  | - | - | Task assignee, used as the value binding for the assignee dropdown menu. Supports single assignee (string) or multiple assignees (string array) |
|
||||||
| `assigneeName` | `string` | - | - | 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 |
|
| `assigneeName` | `string` \| `string[]`  | - | - | Task assignee name, automatically obtained from the label in the bound `assigneeOptions` dataset; for custom display, you can set it in the `task-added` callback event of GanttChart. Supports single name (string) or multiple names (string array) |
|
||||||
| `avatar` | `string` | - | - | Avatar URL of task assignee |
|
| `avatar` | `string` \| `string[]`  | - | - | Avatar URL of task assignee. Supports single avatar (string) or multiple avatars (string array) |
|
||||||
| `estimatedHours` | `number` | - | - | Estimated hours |
|
| `estimatedHours` | `number` | - | - | Estimated hours |
|
||||||
| `actualHours` | `number` | - | - | Actual hours |
|
| `actualHours` | `number` | - | - | Actual hours |
|
||||||
| `parentId` | `number` | - | - | Parent task ID, used for task grouping |
|
| `parentId` | `number` | - | - | Parent task ID, used for task grouping |
|
||||||
|
|||||||
@@ -207,7 +207,13 @@ npm run dev
|
|||||||
| `timeScale`  | `'hour' \| 'day' \| 'week' \| 'month' \| 'quarter' \| 'year'` | `'week'` | 时间刻度(响应式)。设置后时间线刻度将跟随变化 |
|
| `timeScale`  | `'hour' \| 'day' \| 'week' \| 'month' \| 'quarter' \| 'year'` | `'week'` | 时间刻度(响应式)。设置后时间线刻度将跟随变化 |
|
||||||
| `fullscreen`  | `boolean` | `false` | 全屏状态控制(响应式)。设置后组件全屏状态将跟随变化 |
|
| `fullscreen`  | `boolean` | `false` | 全屏状态控制(响应式)。设置后组件全屏状态将跟随变化 |
|
||||||
| `expandAll`  | `boolean` | `true` | 展开/收起所有任务(响应式)。设置后所有任务的展开状态将跟随变化 |
|
| `expandAll`  | `boolean` | `true` | 展开/收起所有任务(响应式)。设置后所有任务的展开状态将跟随变化 |
|
||||||
| `enableLinkAnchor`  | `boolean` | `true` | 是否开启Taskbar的关系线锚点,默认值: true |
|
| `enableLinkAnchor`  | `boolean` | `true` | 是否开启Taskbar的关系线锚点,默认值: true |
|
||||||
|
| `pendingTaskBackgroundColor`  | `string` | `'#409eff'` | 待处理任务的TaskBar背景色。支持十六进制颜色值(如 `'#409eff'`)。**优先级**:高于系统默认,低于 Task 对象的 `barColor` 属性 |
|
||||||
|
| `delayTaskBackgroundColor`  | `string` | `'#f56c6c'` | 已逾期任务的TaskBar背景色。支持十六进制颜色值(如 `'#f56c6c'`)。**优先级**:高于系统默认,低于 Task 对象的 `barColor` 属性 |
|
||||||
|
| `completeTaskBackgroundColor`  | `string` | `'#909399'` | 已完成任务的TaskBar背景色。支持十六进制颜色值(如 `'#909399'`)。**优先级**:高于系统默认,低于 Task 对象的 `barColor` 属性 |
|
||||||
|
| `ongoingTaskBackgroundColor`  | `string` | `'#e6a23c'` | 进行中任务的TaskBar背景色。支持十六进制颜色值(如 `'#e6a23c'`)。**优先级**:高于系统默认,低于 Task 对象的 `barColor` 属性 |
|
||||||
|
| `showActualTaskbar`  | `boolean` | `false` | 是否显示实际TaskBar(在计划TaskBar下方显示实际执行进度) |
|
||||||
|
| `enableTaskbarTooltip`  | `boolean` | `true` | 是否启用TaskBar悬停提示框(鼠标悬停显示任务详情) |
|
||||||
|
|
||||||
#### TaskListColumn 属性
|
#### TaskListColumn 属性
|
||||||
|
|
||||||
@@ -616,9 +622,9 @@ const propsExpandAll = ref(false)
|
|||||||
| `endDate` | `string` | - | - | 结束日期,格式:'YYYY-MM-DD' 或 'YYYY-MM-DD HH:mm' |
|
| `endDate` | `string` | - | - | 结束日期,格式:'YYYY-MM-DD' 或 'YYYY-MM-DD HH:mm' |
|
||||||
| `progress` | `number` | - | `0` | 任务进度,范围 0-100 |
|
| `progress` | `number` | - | `0` | 任务进度,范围 0-100 |
|
||||||
| `predecessor` | `number[]` | - | - | 前置任务 ID 数组,标准格式:`[1, 2, 3]`<br/>**兼容格式**:也支持字符串 `'1,2,3'` 或字符串数组 `['1', '2', '3']`,组件会自动解析 |
|
| `predecessor` | `number[]` | - | - | 前置任务 ID 数组,标准格式:`[1, 2, 3]`<br/>**兼容格式**:也支持字符串 `'1,2,3'` 或字符串数组 `['1', '2', '3']`,组件会自动解析 |
|
||||||
| `assignee` | `string` | - | - | 任务负责人,用作负责人下拉菜单的值绑定 |
|
| `assignee` | `string` \| `string[]`  | - | - | 任务负责人,用作负责人下拉菜单的值绑定。支持单个负责人(字符串)或多个负责人(字符串数组) |
|
||||||
| `assigneeName` | `string` | - | - | 任务负责人姓名,自动从绑定的数据集`assigneeOptions`中获取Label作为显示,如果需要自定义,可以在GanttChart回调事件`task-added`中自定义信息 |
|
| `assigneeName` | `string` \| `string[]`  | - | - | 任务负责人姓名,自动从绑定的数据集`assigneeOptions`中获取Label作为显示,如果需要自定义,可以在GanttChart回调事件`task-added`中自定义信息。支持单个姓名(字符串)或多个姓名(字符串数组) |
|
||||||
| `avatar` | `string` | - | - | 任务负责人头像 URL |
|
| `avatar` | `string` \| `string[]`  | - | - | 任务负责人头像 URL。支持单个头像(字符串)或多个头像(字符串数组) |
|
||||||
| `estimatedHours` | `number` | - | - | 预估工时(小时) |
|
| `estimatedHours` | `number` | - | - | 预估工时(小时) |
|
||||||
| `actualHours` | `number` | - | - | 实际工时(小时) |
|
| `actualHours` | `number` | - | - | 实际工时(小时) |
|
||||||
| `parentId` | `number` | - | - | 父任务 ID,用于任务分组 |
|
| `parentId` | `number` | - | - | 父任务 ID,用于任务分组 |
|
||||||
|
|||||||
@@ -25,6 +25,9 @@ const { locale: demoLocale, messages: demoMessages, setLocale: setDemoLocale, fo
|
|||||||
// Tool Settings 多语言
|
// Tool Settings 多语言
|
||||||
const ts = computed(() => demoMessages.value.toolSettings || {})
|
const ts = computed(() => demoMessages.value.toolSettings || {})
|
||||||
|
|
||||||
|
// TaskBar Config 多语言
|
||||||
|
const taskBarConfigMessages = computed(() => demoMessages.value.taskBarConfig || {})
|
||||||
|
|
||||||
// GanttChart ref
|
// GanttChart ref
|
||||||
const gantt = ref<InstanceType<typeof import('../src/components/GanttChart.vue').default> | null>(null)
|
const gantt = ref<InstanceType<typeof import('../src/components/GanttChart.vue').default> | null>(null)
|
||||||
|
|
||||||
@@ -242,6 +245,13 @@ const taskBarOptions = ref({
|
|||||||
dragDelayTime: 150, // 拖拽延迟时间(毫秒)
|
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>(() => ({
|
const taskBarConfig = computed<TaskBarConfig>(() => ({
|
||||||
showAvatar: taskBarOptions.value.showAvatar,
|
showAvatar: taskBarOptions.value.showAvatar,
|
||||||
showTitle: taskBarOptions.value.showTitle,
|
showTitle: taskBarOptions.value.showTitle,
|
||||||
@@ -1302,6 +1312,53 @@ const handleCustomMenuAction = (action: string, task: Task) => {
|
|||||||
<input v-model="taskBarOptions.showProgress" type="checkbox" />
|
<input v-model="taskBarOptions.showProgress" type="checkbox" />
|
||||||
<span class="taskbar-label">{{ t.taskBarConfig.display.showProgress }}</span>
|
<span class="taskbar-label">{{ t.taskBarConfig.display.showProgress }}</span>
|
||||||
</label>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1324,11 +1381,11 @@ const handleCustomMenuAction = (action: string, task: Task) => {
|
|||||||
<label class="taskbar-control">
|
<label class="taskbar-control">
|
||||||
<input v-model="enableTaskRowMove" type="checkbox" />
|
<input v-model="enableTaskRowMove" type="checkbox" />
|
||||||
<span class="taskbar-label">
|
<span class="taskbar-label">
|
||||||
启用TaskRow拖拽移动
|
{{ taskBarConfigMessages.enableTaskRowMove?.label }}
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<span class="control-hint">
|
<span class="control-hint">
|
||||||
允许通过拖拽TaskRow来调整任务的层级和顺序
|
{{ taskBarConfigMessages.enableTaskRowMove?.hint }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="control-row">
|
<div class="control-row">
|
||||||
@@ -1819,12 +1876,16 @@ const handleCustomMenuAction = (action: string, task: Task) => {
|
|||||||
:allow-drag-and-resize="allowDragAndResize"
|
:allow-drag-and-resize="allowDragAndResize"
|
||||||
:enable-task-row-move="enableTaskRowMove"
|
:enable-task-row-move="enableTaskRowMove"
|
||||||
:assignee-options="assigneeOptions"
|
:assignee-options="assigneeOptions"
|
||||||
:task-list-row-class-name="getTaskRowClassName"
|
|
||||||
:task-list-row-style="getTaskRowStyle"
|
|
||||||
:on-export-csv="handleCustomCsvExport"
|
:on-export-csv="handleCustomCsvExport"
|
||||||
:on-language-change="handleLanguageChange"
|
:on-language-change="handleLanguageChange"
|
||||||
:on-theme-change="handleThemeChange"
|
:on-theme-change="handleThemeChange"
|
||||||
:task-list-column-render-mode="taskListColumnRenderMode"
|
: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-saved="handleMilestoneSaved"
|
||||||
@milestone-deleted="handleMilestoneDeleted"
|
@milestone-deleted="handleMilestoneDeleted"
|
||||||
@milestone-icon-changed="handleMilestoneIconChanged"
|
@milestone-icon-changed="handleMilestoneIconChanged"
|
||||||
@@ -2528,6 +2589,62 @@ const handleCustomMenuAction = (action: string, task: Task) => {
|
|||||||
color: var(--gantt-primary-color, #409eff);
|
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 高级配置样式 */
|
||||||
.taskbar-advanced-controls {
|
.taskbar-advanced-controls {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -27,21 +27,25 @@
|
|||||||
"description": "评估ADX-2024在健康志愿者和患者中的安全性、耐受性和药代动力学特征",
|
"description": "评估ADX-2024在健康志愿者和患者中的安全性、耐受性和药代动力学特征",
|
||||||
"custom": "test11111",
|
"custom": "test11111",
|
||||||
"priority": "高",
|
"priority": "高",
|
||||||
|
"barColor": "#7B3FF2",
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"id": 1101,
|
"id": 1101,
|
||||||
"name": "试验方案设计与<span style='font-weight: bold;color:red;'>伦理审查</span>",
|
"name": "试验方案设计与<span style='font-weight: bold;color:red;'>伦理审查</span>",
|
||||||
"assignee": "方案设计师 李明",
|
"assignee": "方案设计师 李明",
|
||||||
"avatar": "50-1.jpg",
|
"avatar": ["50-1.jpg", "50-2.jpg"],
|
||||||
"startDate": "2025-01-01",
|
"startDate": "2025-01-01",
|
||||||
"endDate": "2025-02-28",
|
"endDate": "2025-02-28",
|
||||||
|
"actualStartDate": "2025-01-03",
|
||||||
|
"actualEndDate": "2025-02-25",
|
||||||
"progress": 100,
|
"progress": 100,
|
||||||
"estimatedHours": 464,
|
"estimatedHours": 464,
|
||||||
"actualHours": 480,
|
"actualHours": 480,
|
||||||
"type": "task",
|
"type": "task",
|
||||||
"parentId": 1100,
|
"parentId": 1100,
|
||||||
"description": "完成I期试验方案设计、伦理委员会审批及监管部门申报",
|
"description": "完成I期试验方案设计、伦理委员会审批及监管部门申报",
|
||||||
"custom": "test11111"
|
"custom": "test11111",
|
||||||
|
"barColor": "#2E7D6B"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 1102,
|
"id": 1102,
|
||||||
@@ -50,6 +54,8 @@
|
|||||||
"avatar": "50-2.jpg",
|
"avatar": "50-2.jpg",
|
||||||
"startDate": "2025-03-01",
|
"startDate": "2025-03-01",
|
||||||
"endDate": "2025-04-30",
|
"endDate": "2025-04-30",
|
||||||
|
"actualStartDate": "2025-03-05",
|
||||||
|
"actualEndDate": "2025-04-28",
|
||||||
"progress": 90,
|
"progress": 90,
|
||||||
"estimatedHours": 1392.5,
|
"estimatedHours": 1392.5,
|
||||||
"actualHours": 1250.25,
|
"actualHours": 1250.25,
|
||||||
@@ -57,15 +63,17 @@
|
|||||||
"predecessor": [1101],
|
"predecessor": [1101],
|
||||||
"parentId": 1100,
|
"parentId": 1100,
|
||||||
"description": "招募24名健康志愿者,完成医学筛选和入组评估",
|
"description": "招募24名健康志愿者,完成医学筛选和入组评估",
|
||||||
"custom": "test11111"
|
"custom": "test11111",
|
||||||
|
"barColor": "#FF4D4F"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 1103,
|
"id": 1103,
|
||||||
"name": "药物给药与安全性监测",
|
"name": "药物给药与安全性监测",
|
||||||
"assignee": "临床医生 Dr. Liu",
|
"assignee": "临床医生 Dr. Liu",
|
||||||
"avatar": "50-3.jpg",
|
"avatar": ["50-3.jpg", "50-2.jpg"],
|
||||||
"startDate": "2025-05-01",
|
"startDate": "2025-05-01",
|
||||||
"endDate": "2025-08-31",
|
"endDate": "2025-08-31",
|
||||||
|
"actualStartDate": "2025-05-10",
|
||||||
"progress": 40,
|
"progress": 40,
|
||||||
"estimatedHours": 3400,
|
"estimatedHours": 3400,
|
||||||
"actualHours": 1687,
|
"actualHours": 1687,
|
||||||
@@ -73,7 +81,8 @@
|
|||||||
"predecessor": [1102],
|
"predecessor": [1102],
|
||||||
"parentId": 1100,
|
"parentId": 1100,
|
||||||
"description": "按照剂量递增方案给药,密切监测不良反应和药代动力学参数",
|
"description": "按照剂量递增方案给药,密切监测不良反应和药代动力学参数",
|
||||||
"custom": "test11111"
|
"custom": "test11111",
|
||||||
|
"barColor": "#7B3FF2"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"collapsed": false
|
"collapsed": false
|
||||||
@@ -91,14 +100,16 @@
|
|||||||
"parentId": 1000,
|
"parentId": 1000,
|
||||||
"description": "在目标患者群体中评估ADX-2024的初步疗效和进一步的安全性",
|
"description": "在目标患者群体中评估ADX-2024的初步疗效和进一步的安全性",
|
||||||
"custom": "test11111",
|
"custom": "test11111",
|
||||||
|
"barColor": "#3F6DF6",
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"id": 1201,
|
"id": 1201,
|
||||||
"name": "多中心试验<span style='font-weight: bold; color: blue;'>启动</span>",
|
"name": "多中心试验<span style='font-weight: bold; color: blue;'>启动</span>",
|
||||||
"assignee": "项目经理 王芳",
|
"assignee": "项目经理 王芳",
|
||||||
"avatar": "50-10.jpg",
|
"avatar": ["50-10.jpg", "50-2.jpg"],
|
||||||
"startDate": "2025-09-01",
|
"startDate": "2025-09-01",
|
||||||
"endDate": "2025-11-30",
|
"endDate": "2025-11-30",
|
||||||
|
"actualStartDate": "2025-09-01",
|
||||||
"progress": 25,
|
"progress": 25,
|
||||||
"estimatedHours": 2160,
|
"estimatedHours": 2160,
|
||||||
"actualHours": 324,
|
"actualHours": 324,
|
||||||
@@ -106,13 +117,14 @@
|
|||||||
"predecessor": [1103],
|
"predecessor": [1103],
|
||||||
"parentId": 1200,
|
"parentId": 1200,
|
||||||
"description": "启动5个临床中心,完成研究者培训和质量体系建立",
|
"description": "启动5个临床中心,完成研究者培训和质量体系建立",
|
||||||
"custom": "test11111"
|
"custom": "test11111",
|
||||||
|
"barColor": "#3F6DF6"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 1202,
|
"id": 1202,
|
||||||
"name": "患者入组与随机化",
|
"name": "患者入组与随机化",
|
||||||
"assignee": "数据管理员 陈静",
|
"assignee": "数据管理员 陈静",
|
||||||
"avatar": "50-6.jpg",
|
"avatar": ["50-6.jpg", "50-10.jpg"],
|
||||||
"startDate": "2025-12-01",
|
"startDate": "2025-12-01",
|
||||||
"endDate": "2026-03-31",
|
"endDate": "2026-03-31",
|
||||||
"progress": 0,
|
"progress": 0,
|
||||||
@@ -138,7 +150,8 @@
|
|||||||
"predecessor": [1202],
|
"predecessor": [1202],
|
||||||
"parentId": 1200,
|
"parentId": 1200,
|
||||||
"description": "定期评估患者疗效指标,收集安全性数据,进行中期分析",
|
"description": "定期评估患者疗效指标,收集安全性数据,进行中期分析",
|
||||||
"custom": "test11111"
|
"custom": "test11111",
|
||||||
|
"barColor": "#FFA43A"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"collapsed": false
|
"collapsed": false
|
||||||
|
|||||||
@@ -111,5 +111,21 @@
|
|||||||
"false": "False"
|
"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"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,5 +111,21 @@
|
|||||||
"false": "假"
|
"false": "假"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"taskBarConfig": {
|
||||||
|
"display": {
|
||||||
|
"showActualTaskBar": "显示实际TaskBar"
|
||||||
|
},
|
||||||
|
"colorConfig": {
|
||||||
|
"title": "自定义任务状态背景色",
|
||||||
|
"pendingTask": "待处理任务",
|
||||||
|
"delayTask": "逾期任务",
|
||||||
|
"completeTask": "已完成任务",
|
||||||
|
"ongoingTask": "进行中任务"
|
||||||
|
},
|
||||||
|
"enableTaskRowMove": {
|
||||||
|
"label": "启用TaskRow拖拽移动",
|
||||||
|
"hint": "允许通过拖拽TaskRow来调整任务的层级和顺序"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,11 +27,114 @@ interface LocaleMessages {
|
|||||||
task: string
|
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> = {
|
const localeMessages: Record<LocaleKey, LocaleMessages> = {
|
||||||
'zh-CN': zhCN,
|
'zh-CN': zhCN as any,
|
||||||
'en-US': enUS,
|
'en-US': enUS as any,
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentLocale = ref<LocaleKey>('zh-CN')
|
const currentLocale = ref<LocaleKey>('zh-CN')
|
||||||
|
|||||||
@@ -466,5 +466,31 @@
|
|||||||
"🎉 Added: New property in GanttChart - enableLinkAnchor to control the relationship line anchor points of Taskbar",
|
"🎉 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>"
|
"<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>"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 151 KiB After Width: | Height: | Size: 261 KiB |
|
Before Width: | Height: | Size: 141 KiB After Width: | Height: | Size: 260 KiB |
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 203 KiB |
|
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 248 KiB |
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "jordium-gantt-vue3",
|
"name": "jordium-gantt-vue3",
|
||||||
"version": "1.7.2",
|
"version": "1.8.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "npm-package/dist/jordium-gantt-vue3.cjs.js",
|
"main": "npm-package/dist/jordium-gantt-vue3.cjs.js",
|
||||||
"module": "npm-package/dist/jordium-gantt-vue3.es.js",
|
"module": "npm-package/dist/jordium-gantt-vue3.es.js",
|
||||||
|
|||||||
@@ -59,6 +59,8 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
enableTaskListContextMenu: true,
|
enableTaskListContextMenu: true,
|
||||||
enableTaskBarContextMenu: true,
|
enableTaskBarContextMenu: true,
|
||||||
enableLinkAnchor: true,
|
enableLinkAnchor: true,
|
||||||
|
showActualTaskbar: false,
|
||||||
|
enableTaskBarTooltip: true,
|
||||||
fullscreen: false,
|
fullscreen: false,
|
||||||
expandAll: true,
|
expandAll: true,
|
||||||
locale: 'zh-CN',
|
locale: 'zh-CN',
|
||||||
@@ -201,6 +203,21 @@ interface Props {
|
|||||||
// 是否启用 LinkAnchor 连接触点功能(默认为 true)
|
// 是否启用 LinkAnchor 连接触点功能(默认为 true)
|
||||||
// 当设置为 false 时,TaskBar 上不显示前置/后置任务的连接触点
|
// 当设置为 false 时,TaskBar 上不显示前置/后置任务的连接触点
|
||||||
enableLinkAnchor?: boolean
|
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
|
fullscreen?: boolean
|
||||||
// 展开/收起所有任务(响应式)
|
// 展开/收起所有任务(响应式)
|
||||||
@@ -1201,6 +1218,8 @@ const milestonesForTimeline = computed((): Milestone[] => {
|
|||||||
startDate: task.startDate, // 此时已确保非空
|
startDate: task.startDate, // 此时已确保非空
|
||||||
endDate: task.endDate,
|
endDate: task.endDate,
|
||||||
assignee: task.assignee,
|
assignee: task.assignee,
|
||||||
|
assigneeName: task.assigneeName,
|
||||||
|
avatar: task.avatar,
|
||||||
type: task.type || 'milestone',
|
type: task.type || 'milestone',
|
||||||
icon: task.icon,
|
icon: task.icon,
|
||||||
description: task.description,
|
description: task.description,
|
||||||
@@ -2808,6 +2827,12 @@ defineExpose({
|
|||||||
:working-hours="props.workingHours"
|
:working-hours="props.workingHours"
|
||||||
:task-bar-config="props.taskBarConfig"
|
:task-bar-config="props.taskBarConfig"
|
||||||
:allow-drag-and-resize="props.allowDragAndResize"
|
: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-drawer="props.useDefaultDrawer"
|
||||||
:use-default-milestone-dialog="props.useDefaultMilestoneDialog"
|
:use-default-milestone-dialog="props.useDefaultMilestoneDialog"
|
||||||
:on-milestone-save="handleMilestoneSave"
|
:on-milestone-save="handleMilestoneSave"
|
||||||
@@ -2861,6 +2886,10 @@ defineExpose({
|
|||||||
:task="taskDrawerTask"
|
:task="taskDrawerTask"
|
||||||
:is-edit="taskDrawerEditMode"
|
:is-edit="taskDrawerEditMode"
|
||||||
:assignee-options="props.assigneeOptions"
|
: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"
|
@submit="handleTaskDrawerSubmit"
|
||||||
@close="taskDrawerVisible = false"
|
@close="taskDrawerVisible = false"
|
||||||
@start-timer="handleStartTimer"
|
@start-timer="handleStartTimer"
|
||||||
|
|||||||
@@ -509,7 +509,7 @@ defineExpose({
|
|||||||
top: `${offsetTop}px`,
|
top: `${offsetTop}px`,
|
||||||
width: `${width}px`,
|
width: `${width}px`,
|
||||||
height: `${height}px`,
|
height: `${height}px`,
|
||||||
zIndex: highlightedTaskId !== null ? 1001 : 25,
|
zIndex: highlightedTaskId !== null ? 2000 : 500,
|
||||||
pointerEvents: 'none',
|
pointerEvents: 'none',
|
||||||
}"
|
}"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ interface Props {
|
|||||||
isEdit?: boolean
|
isEdit?: boolean
|
||||||
onDelete?: (task: Task) => void
|
onDelete?: (task: Task) => void
|
||||||
assigneeOptions?: AssigneeOption[]
|
assigneeOptions?: AssigneeOption[]
|
||||||
|
pendingTaskBackgroundColor?: string
|
||||||
|
delayTaskBackgroundColor?: string
|
||||||
|
completeTaskBackgroundColor?: string
|
||||||
|
ongoingTaskBackgroundColor?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
@@ -35,6 +39,10 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
{ value: 'zhaoliu', label: '赵六' },
|
{ value: 'zhaoliu', label: '赵六' },
|
||||||
{ value: 'qianqi', label: '钱七' },
|
{ value: 'qianqi', label: '钱七' },
|
||||||
],
|
],
|
||||||
|
pendingTaskBackgroundColor: '#c0c4cc',
|
||||||
|
delayTaskBackgroundColor: '#f56c6c',
|
||||||
|
completeTaskBackgroundColor: '#67c23a',
|
||||||
|
ongoingTaskBackgroundColor: '#409eff',
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
@@ -146,6 +154,8 @@ const formData = reactive<Task>({
|
|||||||
assignee: '',
|
assignee: '',
|
||||||
startDate: '',
|
startDate: '',
|
||||||
endDate: '',
|
endDate: '',
|
||||||
|
actualStartDate: '',
|
||||||
|
actualEndDate: '',
|
||||||
predecessor: [],
|
predecessor: [],
|
||||||
estimatedHours: 0,
|
estimatedHours: 0,
|
||||||
actualHours: 0,
|
actualHours: 0,
|
||||||
@@ -334,6 +344,8 @@ const errors = reactive({
|
|||||||
type: '',
|
type: '',
|
||||||
startDate: '',
|
startDate: '',
|
||||||
endDate: '',
|
endDate: '',
|
||||||
|
actualStartDate: '',
|
||||||
|
actualEndDate: '',
|
||||||
})
|
})
|
||||||
|
|
||||||
// 监听 visible 属性变化
|
// 监听 visible 属性变化
|
||||||
@@ -397,6 +409,8 @@ const resetForm = () => {
|
|||||||
assignee: '',
|
assignee: '',
|
||||||
startDate: '',
|
startDate: '',
|
||||||
endDate: '',
|
endDate: '',
|
||||||
|
actualStartDate: '',
|
||||||
|
actualEndDate: '',
|
||||||
predecessor: [],
|
predecessor: [],
|
||||||
estimatedHours: 0,
|
estimatedHours: 0,
|
||||||
actualHours: 0,
|
actualHours: 0,
|
||||||
@@ -408,6 +422,9 @@ const resetForm = () => {
|
|||||||
timerEndTime: undefined,
|
timerEndTime: undefined,
|
||||||
timerElapsedTime: 0,
|
timerElapsedTime: 0,
|
||||||
children: undefined,
|
children: undefined,
|
||||||
|
bgColor: undefined,
|
||||||
|
avatar: undefined,
|
||||||
|
barColor: undefined,
|
||||||
})
|
})
|
||||||
|
|
||||||
// 清除错误信息
|
// 清除错误信息
|
||||||
@@ -434,14 +451,19 @@ const validateForm = (): boolean => {
|
|||||||
isValid = false
|
isValid = false
|
||||||
}
|
}
|
||||||
if (!formData.startDate) {
|
if (!formData.startDate) {
|
||||||
errors.startDate = t.value.startDateRequired
|
errors.startDate = t.value.plannedStartDateRequired
|
||||||
isValid = false
|
isValid = false
|
||||||
}
|
}
|
||||||
if (!formData.endDate) {
|
if (!formData.endDate) {
|
||||||
errors.endDate = t.value.endDateRequired
|
errors.endDate = t.value.plannedEndDateRequired
|
||||||
isValid = false
|
isValid = false
|
||||||
} else if (formData.startDate && new Date(formData.endDate) < new Date(formData.startDate)) {
|
} 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
|
isValid = false
|
||||||
}
|
}
|
||||||
return isValid
|
return isValid
|
||||||
@@ -654,6 +676,71 @@ const handleAssigneeChanged = (event: Event) => {
|
|||||||
formData.assigneeName = selected.label
|
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>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -763,6 +850,18 @@ const handleAssigneeChanged = (event: Event) => {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div style="flex: 1"></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">
|
<button class="drawer-close-btn" type="button" @click="handleClose">
|
||||||
<svg
|
<svg
|
||||||
class="close-icon"
|
class="close-icon"
|
||||||
@@ -844,14 +943,14 @@ const handleAssigneeChanged = (event: Event) => {
|
|||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label" for="task-start-date">
|
<label class="form-label" for="task-start-date">
|
||||||
{{ t.startDate }} <span class="required">*</span></label
|
{{ t.plannedStartDate }} <span class="required">*</span></label
|
||||||
>
|
>
|
||||||
<DatePicker
|
<DatePicker
|
||||||
id="task-start-date"
|
id="task-start-date"
|
||||||
v-model="formData.startDate"
|
v-model="formData.startDate"
|
||||||
:type="'datetime' as any"
|
:type="'datetime' as any"
|
||||||
value-format="YYYY-MM-DD HH:mm"
|
value-format="YYYY-MM-DD HH:mm"
|
||||||
:placeholder="t.startDateRequired"
|
:placeholder="t.plannedStartDateRequired"
|
||||||
:class="{ error: errors.startDate }"
|
:class="{ error: errors.startDate }"
|
||||||
/>
|
/>
|
||||||
<span v-if="errors.startDate" class="error-text">{{ errors.startDate }}</span>
|
<span v-if="errors.startDate" class="error-text">{{ errors.startDate }}</span>
|
||||||
@@ -859,20 +958,52 @@ const handleAssigneeChanged = (event: Event) => {
|
|||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label" for="task-end-date">
|
<label class="form-label" for="task-end-date">
|
||||||
{{ t.endDate }} <span class="required">*</span></label
|
{{ t.plannedEndDate }} <span class="required">*</span></label
|
||||||
>
|
>
|
||||||
<DatePicker
|
<DatePicker
|
||||||
id="task-end-date"
|
id="task-end-date"
|
||||||
v-model="formData.endDate"
|
v-model="formData.endDate"
|
||||||
:type="'datetime' as any"
|
:type="'datetime' as any"
|
||||||
value-format="YYYY-MM-DD HH:mm"
|
value-format="YYYY-MM-DD HH:mm"
|
||||||
:placeholder="t.endDateRequired"
|
:placeholder="t.plannedEndDateRequired"
|
||||||
:class="{ error: errors.endDate }"
|
:class="{ error: errors.endDate }"
|
||||||
/>
|
/>
|
||||||
<span v-if="errors.endDate" class="error-text">{{ errors.endDate }}</span>
|
<span v-if="errors.endDate" class="error-text">{{ errors.endDate }}</span>
|
||||||
</div>
|
</div>
|
||||||
</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
|
<MultiSelectPredecessor
|
||||||
v-model="formData.predecessor"
|
v-model="formData.predecessor"
|
||||||
:tasks="allTasks"
|
:tasks="allTasks"
|
||||||
@@ -1068,6 +1199,15 @@ const handleAssigneeChanged = (event: Event) => {
|
|||||||
color: var(--gantt-text-primary, #303133);
|
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 {
|
.drawer-close-btn {
|
||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
@@ -1075,6 +1215,7 @@ const handleAssigneeChanged = (event: Event) => {
|
|||||||
padding: 4px;
|
padding: 4px;
|
||||||
color: var(--gantt-text-muted, #909399);
|
color: var(--gantt-text-muted, #909399);
|
||||||
transition: color 0.2s;
|
transition: color 0.2s;
|
||||||
|
margin-left: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.drawer-close-btn:hover {
|
.drawer-close-btn:hover {
|
||||||
|
|||||||
@@ -226,6 +226,59 @@ const slotPayload = computed(() => ({
|
|||||||
daysText: daysText.value,
|
daysText: daysText.value,
|
||||||
progressClass: progressClass.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>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -243,7 +296,7 @@ const slotPayload = computed(() => ({
|
|||||||
'task-type-milestone': isMilestoneTask,
|
'task-type-milestone': isMilestoneTask,
|
||||||
[customRowClass]: customRowClass,
|
[customRowClass]: customRowClass,
|
||||||
}"
|
}"
|
||||||
:style="customRowStyle"
|
:style="[customRowStyle, customBorderStyle]"
|
||||||
@click="handleRowClick"
|
@click="handleRowClick"
|
||||||
@dblclick="handleTaskRowDoubleClick"
|
@dblclick="handleTaskRowDoubleClick"
|
||||||
@mousedown="handleMouseDown"
|
@mousedown="handleMouseDown"
|
||||||
@@ -373,10 +426,25 @@ const slotPayload = computed(() => ({
|
|||||||
<!-- 负责人列 -->
|
<!-- 负责人列 -->
|
||||||
<template v-else-if="column.key === 'assignee'">
|
<template v-else-if="column.key === 'assignee'">
|
||||||
<div class="assignee-info">
|
<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>
|
</div>
|
||||||
<span class="assignee-name">{{ props.task.assignee || '-' }}</span>
|
<!-- 名称显示,支持换行和超出显示... -->
|
||||||
|
<span class="assignee-name" :title="assigneeDisplayData.nameText">{{ assigneeDisplayData.nameText }}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -614,9 +682,29 @@ const slotPayload = computed(() => ({
|
|||||||
gap: 8px;
|
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 {
|
.avatar {
|
||||||
min-width: 25px;
|
min-width: 25px;
|
||||||
min-height: 25px;
|
min-height: 25px;
|
||||||
|
width: 25px;
|
||||||
|
height: 25px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: var(--gantt-primary);
|
background: var(--gantt-primary);
|
||||||
color: var(--gantt-text-white);
|
color: var(--gantt-text-white);
|
||||||
@@ -627,11 +715,31 @@ const slotPayload = computed(() => ({
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
border: 2px solid var(--gantt-border-medium);
|
border: 2px solid var(--gantt-border-medium);
|
||||||
box-sizing: border-box;
|
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 {
|
.assignee-name {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: var(--gantt-text-secondary);
|
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 {
|
.progress-value {
|
||||||
|
|||||||
@@ -37,6 +37,15 @@ interface Props {
|
|||||||
taskBarConfig?: TaskBarConfig
|
taskBarConfig?: TaskBarConfig
|
||||||
// 是否允许拖拽和拉伸(默认为 true)
|
// 是否允许拖拽和拉伸(默认为 true)
|
||||||
allowDragAndResize?: boolean
|
allowDragAndResize?: boolean
|
||||||
|
// 是否显示实际任务条(默认为 false)
|
||||||
|
showActualTaskbar?: boolean
|
||||||
|
// 是否启用 TaskBar 气泡提示框(默认为 true)
|
||||||
|
enableTaskBarTooltip?: boolean
|
||||||
|
// 自定义任务状态背景色
|
||||||
|
pendingTaskBackgroundColor?: string
|
||||||
|
delayTaskBackgroundColor?: string
|
||||||
|
completeTaskBackgroundColor?: string
|
||||||
|
ongoingTaskBackgroundColor?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
@@ -55,6 +64,12 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
}),
|
}),
|
||||||
taskBarConfig: undefined,
|
taskBarConfig: undefined,
|
||||||
allowDragAndResize: true,
|
allowDragAndResize: true,
|
||||||
|
showActualTaskbar: false,
|
||||||
|
enableTaskBarTooltip: true,
|
||||||
|
pendingTaskBackgroundColor: undefined,
|
||||||
|
delayTaskBackgroundColor: undefined,
|
||||||
|
completeTaskBackgroundColor: undefined,
|
||||||
|
ongoingTaskBackgroundColor: undefined,
|
||||||
})
|
})
|
||||||
|
|
||||||
// 定义emits
|
// 定义emits
|
||||||
@@ -1049,11 +1064,6 @@ const hasCircularDependency = (taskId: number, targetId: number): boolean => {
|
|||||||
|
|
||||||
// 创建连接
|
// 创建连接
|
||||||
const createLink = (sourceTask: Task, targetTask: Task, mode: 'predecessor' | 'successor') => {
|
const createLink = (sourceTask: Task, targetTask: Task, mode: 'predecessor' | 'successor') => {
|
||||||
console.log('----> hello:', {
|
|
||||||
mode,
|
|
||||||
sourceTask,
|
|
||||||
targetTask,
|
|
||||||
})
|
|
||||||
if (mode === 'predecessor') {
|
if (mode === 'predecessor') {
|
||||||
// mode='predecessor' 表示从左侧 anchor 拖出
|
// mode='predecessor' 表示从左侧 anchor 拖出
|
||||||
// 从左侧 anchor 拖到右侧:sourceTask 依赖 targetTask
|
// 从左侧 anchor 拖到右侧:sourceTask 依赖 targetTask
|
||||||
@@ -4478,6 +4488,12 @@ const handleAddSuccessor = (task: Task) => {
|
|||||||
:current-time-scale="currentTimeScale"
|
:current-time-scale="currentTimeScale"
|
||||||
:task-bar-config="props.taskBarConfig"
|
:task-bar-config="props.taskBarConfig"
|
||||||
:allow-drag-and-resize="props.allowDragAndResize && !isInHighlightMode"
|
: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-highlighted="highlightedTaskIds.has(task.id)"
|
||||||
:is-primary-highlight="highlightedTaskId === task.id"
|
:is-primary-highlight="highlightedTaskId === task.id"
|
||||||
:is-in-highlight-mode="isInHighlightMode"
|
:is-in-highlight-mode="isInHighlightMode"
|
||||||
|
|||||||
@@ -13,6 +13,10 @@ const messages = {
|
|||||||
assignee: '分配人',
|
assignee: '分配人',
|
||||||
startDate: '开始日期',
|
startDate: '开始日期',
|
||||||
endDate: '结束日期',
|
endDate: '结束日期',
|
||||||
|
plannedStartDate: '预计开始日期',
|
||||||
|
plannedEndDate: '预计结束日期',
|
||||||
|
actualStartDate: '实际开始日期',
|
||||||
|
actualEndDate: '实际结束日期',
|
||||||
estimatedHours: '预计工时(hr)',
|
estimatedHours: '预计工时(hr)',
|
||||||
actualHours: '实际工时(hr)',
|
actualHours: '实际工时(hr)',
|
||||||
progress: '进度',
|
progress: '进度',
|
||||||
@@ -26,6 +30,10 @@ const messages = {
|
|||||||
assignee: '分配人',
|
assignee: '分配人',
|
||||||
startDate: '开始日期',
|
startDate: '开始日期',
|
||||||
endDate: '结束日期',
|
endDate: '结束日期',
|
||||||
|
plannedStartDate: '预计开始日期',
|
||||||
|
plannedEndDate: '预计结束日期',
|
||||||
|
actualStartDate: '实际开始日期',
|
||||||
|
actualEndDate: '实际结束日期',
|
||||||
estimatedHours: '预计工时(hr)',
|
estimatedHours: '预计工时(hr)',
|
||||||
actualHours: '实际工时(hr)',
|
actualHours: '实际工时(hr)',
|
||||||
progress: '进度(%)',
|
progress: '进度(%)',
|
||||||
@@ -119,6 +127,15 @@ const messages = {
|
|||||||
startDateRequired: '开始日期不能为空',
|
startDateRequired: '开始日期不能为空',
|
||||||
endDateRequired: '结束日期不能为空',
|
endDateRequired: '结束日期不能为空',
|
||||||
endDateInvalid: '结束日期不能早于开始日期',
|
endDateInvalid: '结束日期不能早于开始日期',
|
||||||
|
plannedStartDateRequired: '预计开始日期不能为空',
|
||||||
|
plannedEndDateRequired: '预计结束日期不能为空',
|
||||||
|
plannedEndDateInvalid: '预计结束日期不能早于预计开始日期',
|
||||||
|
actualEndDateInvalid: '实际结束日期不能早于实际开始日期',
|
||||||
|
// 任务状态
|
||||||
|
statusPending: '待处理',
|
||||||
|
statusOngoing: '进行中',
|
||||||
|
statusDelayed: '已逾期',
|
||||||
|
statusCompleted: '已完成',
|
||||||
// 新建里程碑对话框
|
// 新建里程碑对话框
|
||||||
newMilestone: '新建里程碑',
|
newMilestone: '新建里程碑',
|
||||||
editMilestone: '编辑里程碑',
|
editMilestone: '编辑里程碑',
|
||||||
@@ -279,6 +296,10 @@ const messages = {
|
|||||||
assignee: 'Assignee',
|
assignee: 'Assignee',
|
||||||
startDate: 'Start Date',
|
startDate: 'Start Date',
|
||||||
endDate: 'End Date',
|
endDate: 'End Date',
|
||||||
|
plannedStartDate: 'Planned Start Date',
|
||||||
|
plannedEndDate: 'Planned End Date',
|
||||||
|
actualStartDate: 'Actual Start Date',
|
||||||
|
actualEndDate: 'Actual End Date',
|
||||||
estimatedHours: 'Est. Hours',
|
estimatedHours: 'Est. Hours',
|
||||||
actualHours: 'Act. Hours',
|
actualHours: 'Act. Hours',
|
||||||
progress: 'Progress',
|
progress: 'Progress',
|
||||||
@@ -291,6 +312,10 @@ const messages = {
|
|||||||
assignee: 'Assignee',
|
assignee: 'Assignee',
|
||||||
startDate: 'Start Date',
|
startDate: 'Start Date',
|
||||||
endDate: 'End Date',
|
endDate: 'End Date',
|
||||||
|
plannedStartDate: 'Planned Start Date',
|
||||||
|
plannedEndDate: 'Planned End Date',
|
||||||
|
actualStartDate: 'Actual Start Date',
|
||||||
|
actualEndDate: 'Actual End Date',
|
||||||
estimatedHours: 'Est. Hours',
|
estimatedHours: 'Est. Hours',
|
||||||
actualHours: 'Act. Hours',
|
actualHours: 'Act. Hours',
|
||||||
progress: 'Progress (%)',
|
progress: 'Progress (%)',
|
||||||
@@ -382,6 +407,15 @@ const messages = {
|
|||||||
startDateRequired: 'Start date is required',
|
startDateRequired: 'Start date is required',
|
||||||
endDateRequired: 'End date is required',
|
endDateRequired: 'End date is required',
|
||||||
endDateInvalid: 'End date cannot be earlier than start date',
|
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',
|
newMilestone: 'New Milestone',
|
||||||
editMilestone: 'Edit Milestone',
|
editMilestone: 'Edit Milestone',
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ export interface Milestone {
|
|||||||
name: string
|
name: string
|
||||||
startDate: string
|
startDate: string
|
||||||
endDate?: string
|
endDate?: string
|
||||||
assignee?: string
|
assignee?: string | string[] // 支持单个或多个负责人
|
||||||
|
assigneeName?: string | string[] // 支持单个或多个负责人姓名
|
||||||
|
avatar?: string | string[] // 支持单个或多个头像
|
||||||
type: string
|
type: string
|
||||||
icon?: string
|
icon?: string
|
||||||
description?: string
|
description?: string
|
||||||
|
|||||||
@@ -3,11 +3,13 @@ export interface Task {
|
|||||||
id: number
|
id: number
|
||||||
name: string
|
name: string
|
||||||
predecessor?: number[] // 前置任务ID数组
|
predecessor?: number[] // 前置任务ID数组
|
||||||
assignee?: string // 记录唯一键值,如用户ID或用户名
|
assignee?: string | string[] // 记录唯一键值,如用户ID或用户名,支持单个或多个
|
||||||
assigneeName?: string // 任务负责人名称
|
assigneeName?: string // 任务负责人名称
|
||||||
avatar?: string // 任务负责人头像URL
|
avatar?: string | string[] // 任务负责人头像URL,支持单个或多个头像数组
|
||||||
startDate?: string
|
startDate?: string
|
||||||
endDate?: string
|
endDate?: string
|
||||||
|
actualStartDate?: string // 实际开始日期
|
||||||
|
actualEndDate?: string // 实际结束日期
|
||||||
progress?: number
|
progress?: number
|
||||||
estimatedHours?: number
|
estimatedHours?: number
|
||||||
actualHours?: number
|
actualHours?: number
|
||||||
@@ -27,6 +29,8 @@ export interface Task {
|
|||||||
timerElapsedTime?: number
|
timerElapsedTime?: number
|
||||||
// 权限控制
|
// 权限控制
|
||||||
isEditable?: boolean // 是否可编辑(可拖拽、拉伸),默认为true
|
isEditable?: boolean // 是否可编辑(可拖拽、拉伸),默认为true
|
||||||
|
// 自定义样式
|
||||||
|
barColor?: string // 自定义TaskBar颜色,如 '#ff5733',若不设置则使用默认颜色方案
|
||||||
// 支持自定义属性 - 使用 unknown 允许任意类型
|
// 支持自定义属性 - 使用 unknown 允许任意类型
|
||||||
[key: string]: unknown
|
[key: string]: unknown
|
||||||
}
|
}
|
||||||
|
|||||||