v1.6.0 - 提供Task列表插槽功能
This commit is contained in:
18
CHANGELOG.md
18
CHANGELOG.md
@@ -5,6 +5,24 @@ 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.6.0] - 2025-12-14
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- 🎉 新增:Task Table列声明式定义方式, 使用组件TaskListColumn
|
||||||
|
- 🎉 新增:Task Table #header和#default Slots
|
||||||
|
- 🎉 新增:Task List行样式设置属性 - task-list-row-class-name和task-list-row-style
|
||||||
|
- 🎉 新增:为兼容旧版本TaskListColumnConfig,提供task-list-column-render-mode属性开关,可以通过设置选择Task表格列定义方式
|
||||||
|
- 🎉 Added: Declarative definition method for Task Table columns using the TaskListColumn component
|
||||||
|
- 🎉 Added: Task Table #header and #default Slots
|
||||||
|
- 🎉 Added: Task List row style setting properties - task-list-row-class-name and task-list-row-style
|
||||||
|
- 🎉 Added: A switch for task-list-column-render-mode attribute to choose Task Table column definition method for compatibility with older versions of TaskListColumnConfig
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- 🔧 修复:仅删除Story层级后,子Task升级后关系线丢失问题
|
||||||
|
- 🔧 修复:修复已知问题
|
||||||
|
- 🔧 Fixed: Issue of missing relationship lines after upgrading child Tasks when only deleting Story
|
||||||
|
- 🔧 Fixed: Fixed issues
|
||||||
|
|
||||||
## [1.5.0] - 2025-12-13
|
## [1.5.0] - 2025-12-13
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
322
README-EN.md
322
README-EN.md
@@ -193,6 +193,39 @@ npm run dev
|
|||||||
| `enableTaskRowMove` | `boolean` | `false` | Whether to allow dragging and dropping TaskRow |
|
| `enableTaskRowMove` | `boolean` | `false` | Whether to allow dragging and dropping TaskRow |
|
||||||
| `assigneeOptions` | `Array<{ key?: string \| number; value: string \| number; label: string }>` | `[]` | Assignee dropdown options in task edit drawer |
|
| `assigneeOptions` | `Array<{ key?: string \| number; value: string \| number; label: string }>` | `[]` | Assignee dropdown options in task edit drawer |
|
||||||
|
|
||||||
|
#### TaskListColumn Component Props
|
||||||
|
|
||||||
|
The `TaskListColumn` component is used to define task list columns in declarative mode (`taskListColumnRenderMode="declarative"`). Similar to Element Plus's `el-table-column` component.
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
| ---------- | ------------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||||
|
| `prop` | `string` | - | Column property name, used to access task object fields. Examples: `'name'`, `'assignee'`, `'progress'`, etc. |
|
||||||
|
| `label` | `string` | - | Column header display text |
|
||||||
|
| `width` | `number \| string` | - | Column width. Number represents pixels (e.g., `200`), string supports percentage (e.g., `'20%'`) |
|
||||||
|
| `align` | `'left' \| 'center' \| 'right'` | `'left'` | Column content alignment |
|
||||||
|
| `cssClass` | `string` | - | Custom CSS class name for column styling |
|
||||||
|
|
||||||
|
**Usage Example**:
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<GanttChart
|
||||||
|
:tasks="tasks"
|
||||||
|
task-list-column-render-mode="declarative"
|
||||||
|
>
|
||||||
|
<TaskListColumn prop="name" label="Task Name" width="300" />
|
||||||
|
<TaskListColumn prop="assignee" label="Assignee" width="150" align="center" />
|
||||||
|
<TaskListColumn prop="progress" label="Progress" width="100" align="center" />
|
||||||
|
<TaskListColumn prop="startDate" label="Start Date" width="140" />
|
||||||
|
<TaskListColumn prop="endDate" label="End Date" width="140" />
|
||||||
|
</GanttChart>
|
||||||
|
```
|
||||||
|
|
||||||
|
> **💡 Tips**:
|
||||||
|
> - The `TaskListColumn` component itself does not render any content, it only declares column configuration
|
||||||
|
> - Must be used inside the `GanttChart` component with `task-list-column-render-mode="declarative"` set
|
||||||
|
> - Column display order is determined by the declaration order of `TaskListColumn` components
|
||||||
|
> - For detailed column content customization and slot usage, see [Slots](#slots) section
|
||||||
|
|
||||||
#### Configuration Object Props
|
#### Configuration Object Props
|
||||||
|
|
||||||
For complete configuration object documentation, see [⚙️ Configuration & Customization](#⚙️-configuration--customization) section.
|
For complete configuration object documentation, see [⚙️ Configuration & Customization](#⚙️-configuration--customization) section.
|
||||||
@@ -450,6 +483,9 @@ Tasks are the core elements of the Gantt chart. The component provides complete
|
|||||||
| `autoSortByStartDate` | `boolean` | `false` | Whether to automatically sort tasks by start date |
|
| `autoSortByStartDate` | `boolean` | `false` | Whether to automatically sort tasks by start date |
|
||||||
| `enableTaskRowMove` | `boolean` | `false` | Whether to alloww dragging and dropping TaskRow |
|
| `enableTaskRowMove` | `boolean` | `false` | Whether to alloww dragging and dropping TaskRow |
|
||||||
| `assigneeOptions` | `Array<{ key?: string \| number; value: string \| number; label: string }>` | `[]` | Assignee dropdown options in task edit drawer |
|
| `assigneeOptions` | `Array<{ key?: string \| number; value: string \| number; label: string }>` | `[]` | Assignee dropdown options in task edit drawer |
|
||||||
|
| `taskListColumnRenderMode` | `'default' \| 'declarative'` | `'default'` | Task list column render mode. `'default'`: Use TaskListColumnConfig configuration (compatibility mode, will be gradually deprecated); `'declarative'`: Use TaskListColumn component for declarative column definition (recommended). See [TaskListColumn Declarative Column Definition](#tasklistcolumn-declarative-column-definition) |
|
||||||
|
| `taskListRowClassName` | `string \| ((task: Task) => string)` | `undefined` | Custom CSS class name for task rows. Can be a string or a function that returns a string. **Note**: Row height is managed internally by the component, custom height styles will not take effect |
|
||||||
|
| `taskListRowStyle` | `CSSProperties \| ((task: Task) => CSSProperties)` | `undefined` | Custom inline styles for task rows. Can be a style object or a function that returns a style object. **Note**: Row height and width are managed internally by the component, custom width/height styles will not take effect |
|
||||||
|
|
||||||
**Configuration Notes**:
|
**Configuration Notes**:
|
||||||
|
|
||||||
@@ -2221,6 +2257,292 @@ const props = defineProps<Props>()
|
|||||||
> - TaskRow and TaskBar have different available space, need to adapt layout
|
> - TaskRow and TaskBar have different available space, need to adapt layout
|
||||||
> - Avoid using overly complex components in slot content, may affect performance
|
> - Avoid using overly complex components in slot content, may affect performance
|
||||||
|
|
||||||
|
##### TaskListColumn Slots
|
||||||
|
|
||||||
|
The `TaskListColumn` component provides two slots for customizing task list column headers and cell content. Must be used in declarative mode (`taskListColumnRenderMode="declarative"`).
|
||||||
|
|
||||||
|
**Slot List:**
|
||||||
|
|
||||||
|
| Slot Name | Parameters | Description |
|
||||||
|
| --------- | --------------------------------- | ----------------------------------------------------------------------------------------------------- |
|
||||||
|
| `header` | - | Custom column header content. If not provided, the `label` prop will be displayed |
|
||||||
|
| `default` | `scope: { row: Task, $index: number }` | Custom column cell content. Access current task object via `scope.row`, access task index via `scope.$index`. If not provided, the value of `prop` field is shown |
|
||||||
|
|
||||||
|
**Usage Example:**
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<GanttChart
|
||||||
|
:tasks="tasks"
|
||||||
|
task-list-column-render-mode="declarative"
|
||||||
|
>
|
||||||
|
<!-- Basic column definition without slots -->
|
||||||
|
<TaskListColumn prop="name" label="Task Name" width="300" />
|
||||||
|
|
||||||
|
<!-- Custom column header using header slot -->
|
||||||
|
<TaskListColumn prop="assignee" width="150" align="center">
|
||||||
|
<template #header>
|
||||||
|
<div style="display: flex; align-items: center; gap: 4px; color: #409eff;">
|
||||||
|
<span>👤</span>
|
||||||
|
<span>Assignee</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</TaskListColumn>
|
||||||
|
|
||||||
|
<!-- Custom cell content using default slot -->
|
||||||
|
<TaskListColumn prop="progress" label="Progress" width="150" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<div style="display: flex; align-items: center; gap: 8px;">
|
||||||
|
<div
|
||||||
|
style="
|
||||||
|
flex: 1;
|
||||||
|
height: 8px;
|
||||||
|
background: #f0f0f0;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
:style="{
|
||||||
|
width: `${scope.row.progress || 0}%`,
|
||||||
|
height: '100%',
|
||||||
|
background: scope.row.progress >= 100 ? '#67c23a' : scope.row.progress >= 50 ? '#409eff' : '#e6a23c',
|
||||||
|
transition: 'width 0.3s'
|
||||||
|
}"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
<span style="min-width: 40px; text-align: right; font-size: 12px;">
|
||||||
|
{{ scope.row.progress || 0 }}%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</TaskListColumn>
|
||||||
|
|
||||||
|
<!-- Using both header and default slots -->
|
||||||
|
<TaskListColumn prop="startDate" width="160">
|
||||||
|
<template #header>
|
||||||
|
<div style="color: #67c23a; font-weight: bold;">📅 Start Date</div>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<div style="display: flex; align-items: center; gap: 4px;">
|
||||||
|
<span style="color: #909399; font-size: 12px;">🕐</span>
|
||||||
|
<span>{{ scope.row.startDate || '-' }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</TaskListColumn>
|
||||||
|
|
||||||
|
<!-- Dynamic styling based on data -->
|
||||||
|
<TaskListColumn prop="status" label="Status" width="120" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<span
|
||||||
|
:style="{
|
||||||
|
padding: '2px 8px',
|
||||||
|
borderRadius: '4px',
|
||||||
|
fontSize: '12px',
|
||||||
|
color: 'white',
|
||||||
|
background: scope.row.progress >= 100 ? '#67c23a' :
|
||||||
|
scope.row.progress > 0 ? '#409eff' : '#909399'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
{{ scope.row.progress >= 100 ? 'Completed' : scope.row.progress > 0 ? 'In Progress' : 'Not Started' }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</TaskListColumn>
|
||||||
|
</GanttChart>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { GanttChart, TaskListColumn } from 'jordium-gantt-vue3'
|
||||||
|
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
|
||||||
|
import type { Task } from 'jordium-gantt-vue3'
|
||||||
|
|
||||||
|
const tasks = ref<Task[]>([
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: 'Project Planning',
|
||||||
|
assignee: 'John',
|
||||||
|
startDate: '2025-01-01',
|
||||||
|
endDate: '2025-01-10',
|
||||||
|
progress: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: 'Requirements Analysis',
|
||||||
|
assignee: 'Jane',
|
||||||
|
startDate: '2025-01-11',
|
||||||
|
endDate: '2025-01-20',
|
||||||
|
progress: 60,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
name: 'Development',
|
||||||
|
assignee: 'Bob',
|
||||||
|
startDate: '2025-01-21',
|
||||||
|
endDate: '2025-02-10',
|
||||||
|
progress: 0,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
> **💡 Usage Scenarios**:
|
||||||
|
>
|
||||||
|
> - Custom column header icons, styles, or sorting buttons
|
||||||
|
> - Display visual components like progress bars, status badges
|
||||||
|
> - Dynamically adjust styles and content based on task data
|
||||||
|
> - Add action buttons (edit, delete, etc.)
|
||||||
|
> - Integrate third-party UI components (ratings, tag selectors, etc.)
|
||||||
|
|
||||||
|
> **⚠️ Notes**:
|
||||||
|
>
|
||||||
|
> - Slots must be used in `task-list-column-render-mode="declarative"` mode
|
||||||
|
> - The `default` slot receives a `scope` parameter, access current task object via `scope.row` (Task type), access index via `scope.$index`
|
||||||
|
> - Recommended to use `scope.row` instead of destructuring `{ row }` for code clarity and consistency
|
||||||
|
> - `scope.$index` is the index of the current task in the visible list (not global index)
|
||||||
|
> - Slot content is rendered in each task row, avoid overly complex components for performance
|
||||||
|
> - When column width is fixed, slot content should consider overflow handling (text ellipsis, auto-wrap, etc.)
|
||||||
|
|
||||||
|
##### TaskListColumnConfig Column Slots (Deprecated Soon)
|
||||||
|
|
||||||
|
> **⚠️ Important Notice**: This slot approach based on `TaskListColumnConfig` configuration is **deprecated and will be removed soon**. Strongly recommend using the declarative approach with [TaskListColumn Slots](#tasklistcolumn-slots) above for better type hints and code maintainability.
|
||||||
|
|
||||||
|
In default mode (`taskListColumnRenderMode="default"`), you can customize columns defined in `TaskListColumnConfig` via slots. Slot names are based on the `key` property in column configuration.
|
||||||
|
|
||||||
|
**Slot List:**
|
||||||
|
|
||||||
|
| Slot Pattern | Parameters | Description |
|
||||||
|
| --------------- | ---------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
|
||||||
|
| `header-{key}` | - | Custom header content for specified column. `{key}` is the `key` value defined in `TaskListColumnConfig` |
|
||||||
|
| `column-{key}` | `{ task: Task, column: TaskListColumnConfig, value: any }` | Custom cell content for specified column. `task` is current task object, `column` is column config, `value` is the column's value |
|
||||||
|
|
||||||
|
**Usage Example:**
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<GanttChart
|
||||||
|
:tasks="tasks"
|
||||||
|
:task-list-config="taskListConfig"
|
||||||
|
task-list-column-render-mode="default"
|
||||||
|
>
|
||||||
|
<!-- Custom 'name' column header -->
|
||||||
|
<template #header-name>
|
||||||
|
<div style="display: flex; align-items: center; gap: 6px;">
|
||||||
|
<img src="/avatar.png" width="32" height="32" style="border-radius: 50%;" />
|
||||||
|
<strong style="font-size: 14px;">Task Name</strong>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- Custom 'name' column cell content -->
|
||||||
|
<template #column-name="{ task, column, value }">
|
||||||
|
<div style="display: flex; align-items: center; gap: 6px;">
|
||||||
|
<img src="/user-avatar.png" width="20" height="20" style="border-radius: 50%;" />
|
||||||
|
<span v-html="value"></span>
|
||||||
|
<span
|
||||||
|
v-if="task.priority"
|
||||||
|
style="
|
||||||
|
background: linear-gradient(135deg, #ff6b6b 0%, #ee5a6f 100%);
|
||||||
|
color: white;
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 10px;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
P-{{ task.priority }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- Custom other columns, e.g., custom field 'custom' -->
|
||||||
|
<template #column-custom="{ task, column, value }">
|
||||||
|
<div style="display: flex; align-items: center; gap: 4px;">
|
||||||
|
<span
|
||||||
|
v-if="typeof value === 'number'"
|
||||||
|
style="
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
color: white;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
💰 {{ value.toLocaleString() }}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
v-else-if="typeof value === 'string'"
|
||||||
|
style="
|
||||||
|
background: #e8f5e9;
|
||||||
|
color: #2e7d32;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
border: 1px solid #81c784;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
📝 {{ value }}
|
||||||
|
</span>
|
||||||
|
<span v-else style="color: #999;">-</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</GanttChart>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { GanttChart } from 'jordium-gantt-vue3'
|
||||||
|
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
|
||||||
|
import type { Task, TaskListConfig, TaskListColumnConfig } from 'jordium-gantt-vue3'
|
||||||
|
|
||||||
|
const tasks = ref<Task[]>([
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: 'Project Planning',
|
||||||
|
startDate: '2025-01-01',
|
||||||
|
endDate: '2025-01-10',
|
||||||
|
progress: 100,
|
||||||
|
priority: 1,
|
||||||
|
custom: 50000, // Custom field
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: 'Requirements Analysis',
|
||||||
|
startDate: '2025-01-11',
|
||||||
|
endDate: '2025-01-20',
|
||||||
|
progress: 60,
|
||||||
|
custom: 'Important', // Custom field
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
// Define column configuration
|
||||||
|
const taskListConfig = ref<TaskListConfig>({
|
||||||
|
columns: [
|
||||||
|
{ key: 'taskName', label: 'Task Name', visible: true, width: 300 },
|
||||||
|
{ key: 'assignee', label: 'Assignee', visible: true, width: 150 },
|
||||||
|
{ key: 'progress', label: 'Progress', visible: true, width: 100 },
|
||||||
|
{ key: 'custom', label: 'Custom', visible: true, width: 150 }, // Custom column
|
||||||
|
],
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
> **💡 Usage Notes**:
|
||||||
|
>
|
||||||
|
> - Slot name format: `header-{key}` and `column-{key}`, where `{key}` corresponds to `TaskListColumnConfig.key`
|
||||||
|
> - `column-{key}` slot receives three parameters:
|
||||||
|
> - `task`: Current task object (Task type)
|
||||||
|
> - `column`: Current column configuration object (TaskListColumnConfig type)
|
||||||
|
> - `value`: Current cell value (automatically obtained from `task[column.key]`)
|
||||||
|
> - You can mix slots and default rendering, only define slots for columns that need customization
|
||||||
|
|
||||||
|
> **⚠️ Migration Recommendation**:
|
||||||
|
>
|
||||||
|
> - **Strongly recommend migrating to declarative mode**: Use `task-list-column-render-mode="declarative"` with `TaskListColumn` component
|
||||||
|
> - Declarative mode provides better type hints, code organization, and maintainability
|
||||||
|
> - This configuration-based slot approach will be removed in future versions, please migrate soon
|
||||||
|
> - For migration examples, see [TaskListColumn Slots](#tasklistcolumn-slots) section
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## ❓ FAQ
|
## ❓ FAQ
|
||||||
|
|||||||
324
README.md
324
README.md
@@ -191,6 +191,39 @@ npm run dev
|
|||||||
| `enableTaskRowMove` | `boolean` | `false` | 是否允许拖拽和摆放TaskRow |
|
| `enableTaskRowMove` | `boolean` | `false` | 是否允许拖拽和摆放TaskRow |
|
||||||
| `assigneeOptions` | `Array<{ key?: string \| number; value: string \| number; label: string }>` | `[]` | 任务编辑抽屉中负责人下拉菜单的选项列表 |
|
| `assigneeOptions` | `Array<{ key?: string \| number; value: string \| number; label: string }>` | `[]` | 任务编辑抽屉中负责人下拉菜单的选项列表 |
|
||||||
|
|
||||||
|
#### TaskListColumn 属性
|
||||||
|
|
||||||
|
`TaskListColumn` 组件用于在声明式模式(`taskListColumnRenderMode="declarative"`)下定义任务列表的列。类似于 Element Plus 的 `el-table-column` 组件。
|
||||||
|
|
||||||
|
| 属性名 | 类型 | 默认值 | 说明 |
|
||||||
|
| ---------- | ------------------------------ | -------- | ---------------------------------------------------------------------------------------------------------- |
|
||||||
|
| `prop` | `string` | - | 列的属性名,用于访问任务对象的字段。例如:`'name'`、`'assignee'`、`'progress'` 等 |
|
||||||
|
| `label` | `string` | - | 列的显示标题文本 |
|
||||||
|
| `width` | `number \| string` | - | 列宽度。数字表示像素值(如 `200`),字符串支持百分比(如 `'20%'`) |
|
||||||
|
| `align` | `'left' \| 'center' \| 'right'` | `'left'` | 列内容对齐方式 |
|
||||||
|
| `cssClass` | `string` | - | 自定义 CSS 类名,用于列样式定制 |
|
||||||
|
|
||||||
|
**使用示例**:
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<GanttChart
|
||||||
|
:tasks="tasks"
|
||||||
|
task-list-column-render-mode="declarative"
|
||||||
|
>
|
||||||
|
<TaskListColumn prop="name" label="任务名称" width="300" />
|
||||||
|
<TaskListColumn prop="assignee" label="负责人" width="150" align="center" />
|
||||||
|
<TaskListColumn prop="progress" label="进度" width="100" align="center" />
|
||||||
|
<TaskListColumn prop="startDate" label="开始日期" width="140" />
|
||||||
|
<TaskListColumn prop="endDate" label="结束日期" width="140" />
|
||||||
|
</GanttChart>
|
||||||
|
```
|
||||||
|
|
||||||
|
> **💡 提示**:
|
||||||
|
> - `TaskListColumn` 组件本身不渲染任何内容,仅用于声明列配置
|
||||||
|
> - 必须在 `GanttChart` 组件内部使用,且设置 `task-list-column-render-mode="declarative"`
|
||||||
|
> - 列的显示顺序由 `TaskListColumn` 组件的声明顺序决定
|
||||||
|
> - 关于列内容自定义和插槽的详细使用方法,请参考 [插槽 (Slots)](#插槽-slots) 章节
|
||||||
|
|
||||||
#### 配置对象属性
|
#### 配置对象属性
|
||||||
|
|
||||||
完整的配置对象说明请参考 [⚙️ 配置与扩展](#⚙️-配置与扩展) 章节。
|
完整的配置对象说明请参考 [⚙️ 配置与扩展](#⚙️-配置与扩展) 章节。
|
||||||
@@ -447,7 +480,10 @@ const handleMilestoneSaved = milestone => {
|
|||||||
| `taskListConfig` | `TaskListConfig` | `undefined` | 任务列表配置,详见 [TaskListConfig 配置](#tasklistconfig-配置) |
|
| `taskListConfig` | `TaskListConfig` | `undefined` | 任务列表配置,详见 [TaskListConfig 配置](#tasklistconfig-配置) |
|
||||||
| `autoSortByStartDate` | `boolean` | `false` | 是否根据开始时间自动排序任务 |
|
| `autoSortByStartDate` | `boolean` | `false` | 是否根据开始时间自动排序任务 |
|
||||||
| `enableTaskRowMove` | `boolean` | `false` | 是否允许拖拽和摆放TaskRow |
|
| `enableTaskRowMove` | `boolean` | `false` | 是否允许拖拽和摆放TaskRow |
|
||||||
| `assigneeOptions` | `Array<{ key?: string \| number; value: string \| number; label: string }>` | `[]` | 任务编辑抽屉中负责人下拉菜单的选项列表 |
|
| `assigneeOptions` | `Array<{ key?: string \| number; value: string \| number; label: string }>` | `[]` | 任务编辑抽屉中负责人下拉菜单的选项列表 |
|
||||||
|
| `taskListColumnRenderMode` | `'default' \| 'declarative'` | `'default'` | 任务列表列渲染模式。`'default'`:使用 TaskListColumnConfig 配置(兼容模式,将逐渐废弃);`'declarative'`:使用 TaskListColumn 组件声明式定义列(推荐)。详见 [TaskListColumn 声明式列定义](#tasklistcolumn-声明式列定义) |
|
||||||
|
| `taskListRowClassName` | `string \| ((task: Task) => string)` | `undefined` | 自定义任务行的 CSS 类名。可以是字符串或返回字符串的函数。**注意**:行的高度由组件内部统一管理,自定义高度样式不会生效 |
|
||||||
|
| `taskListRowStyle` | `CSSProperties \| ((task: Task) => CSSProperties)` | `undefined` | 自定义任务行的内联样式。可以是样式对象或返回样式对象的函数。**注意**:行的高度和宽度由组件内部统一管理,自定义宽高样式不会生效 |
|
||||||
|
|
||||||
**配置说明**:
|
**配置说明**:
|
||||||
|
|
||||||
@@ -2212,6 +2248,292 @@ const props = defineProps<Props>()
|
|||||||
> - TaskRow 和 TaskBar 的可用空间不同,需要适配布局
|
> - TaskRow 和 TaskBar 的可用空间不同,需要适配布局
|
||||||
> - 避免在插槽内容中使用过于复杂的组件,可能影响性能
|
> - 避免在插槽内容中使用过于复杂的组件,可能影响性能
|
||||||
|
|
||||||
|
##### TaskListColumn 插槽
|
||||||
|
|
||||||
|
`TaskListColumn` 组件提供了两个插槽,用于自定义任务列表的列头和单元格内容。必须在声明式模式(`taskListColumnRenderMode="declarative"`)下使用。
|
||||||
|
|
||||||
|
**插槽列表:**
|
||||||
|
|
||||||
|
| 插槽名 | 参数 | 说明 |
|
||||||
|
| --------- | --------------------------------- | ---------------------------------------------------------------------------------------------- |
|
||||||
|
| `header` | - | 自定义列表头内容。如果未提供此插槽,将显示 `label` 属性 |
|
||||||
|
| `default` | `scope: { row: Task, $index: number }` | 自定义列单元格内容。通过 `scope.row` 访问当前任务对象,通过 `scope.$index` 访问任务索引。如果未提供此插槽,将显示 `prop` 对应的字段值 |
|
||||||
|
|
||||||
|
**使用示例:**
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<GanttChart
|
||||||
|
:tasks="tasks"
|
||||||
|
task-list-column-render-mode="declarative"
|
||||||
|
>
|
||||||
|
<!-- 基础列定义,不使用插槽 -->
|
||||||
|
<TaskListColumn prop="name" label="任务名称" width="300" />
|
||||||
|
|
||||||
|
<!-- 使用 header 插槽自定义列表头 -->
|
||||||
|
<TaskListColumn prop="assignee" width="150" align="center">
|
||||||
|
<template #header>
|
||||||
|
<div style="display: flex; align-items: center; gap: 4px; color: #409eff;">
|
||||||
|
<span>👤</span>
|
||||||
|
<span>负责人</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</TaskListColumn>
|
||||||
|
|
||||||
|
<!-- 使用 default 插槽自定义单元格内容 -->
|
||||||
|
<TaskListColumn prop="progress" label="进度" width="150" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<div style="display: flex; align-items: center; gap: 8px;">
|
||||||
|
<div
|
||||||
|
style="
|
||||||
|
flex: 1;
|
||||||
|
height: 8px;
|
||||||
|
background: #f0f0f0;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
:style="{
|
||||||
|
width: `${scope.row.progress || 0}%`,
|
||||||
|
height: '100%',
|
||||||
|
background: scope.row.progress >= 100 ? '#67c23a' : scope.row.progress >= 50 ? '#409eff' : '#e6a23c',
|
||||||
|
transition: 'width 0.3s'
|
||||||
|
}"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
<span style="min-width: 40px; text-align: right; font-size: 12px;">
|
||||||
|
{{ scope.row.progress || 0 }}%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</TaskListColumn>
|
||||||
|
|
||||||
|
<!-- 同时使用 header 和 default 插槽 -->
|
||||||
|
<TaskListColumn prop="startDate" width="160">
|
||||||
|
<template #header>
|
||||||
|
<div style="color: #67c23a; font-weight: bold;">📅 开始日期</div>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<div style="display: flex; align-items: center; gap: 4px;">
|
||||||
|
<span style="color: #909399; font-size: 12px;">🕐</span>
|
||||||
|
<span>{{ scope.row.startDate || '-' }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</TaskListColumn>
|
||||||
|
|
||||||
|
<!-- 根据数据动态渲染样式 -->
|
||||||
|
<TaskListColumn prop="status" label="状态" width="120" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<span
|
||||||
|
:style="{
|
||||||
|
padding: '2px 8px',
|
||||||
|
borderRadius: '4px',
|
||||||
|
fontSize: '12px',
|
||||||
|
color: 'white',
|
||||||
|
background: scope.row.progress >= 100 ? '#67c23a' :
|
||||||
|
scope.row.progress > 0 ? '#409eff' : '#909399'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
{{ scope.row.progress >= 100 ? '已完成' : scope.row.progress > 0 ? '进行中' : '未开始' }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</TaskListColumn>
|
||||||
|
</GanttChart>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { GanttChart, TaskListColumn } from 'jordium-gantt-vue3'
|
||||||
|
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
|
||||||
|
import type { Task } from 'jordium-gantt-vue3'
|
||||||
|
|
||||||
|
const tasks = ref<Task[]>([
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: '项目规划',
|
||||||
|
assignee: '张三',
|
||||||
|
startDate: '2025-01-01',
|
||||||
|
endDate: '2025-01-10',
|
||||||
|
progress: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: '需求分析',
|
||||||
|
assignee: '李四',
|
||||||
|
startDate: '2025-01-11',
|
||||||
|
endDate: '2025-01-20',
|
||||||
|
progress: 60,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
name: '开发实施',
|
||||||
|
assignee: '王五',
|
||||||
|
startDate: '2025-01-21',
|
||||||
|
endDate: '2025-02-10',
|
||||||
|
progress: 0,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
> **💡 使用场景**:
|
||||||
|
>
|
||||||
|
> - 自定义列表头图标、样式或排序按钮
|
||||||
|
> - 显示进度条、状态标签等可视化组件
|
||||||
|
> - 根据任务数据动态调整样式和内容
|
||||||
|
> - 添加操作按钮(编辑、删除等)
|
||||||
|
> - 集成第三方UI组件(如评分、标签选择器等)
|
||||||
|
|
||||||
|
> **⚠️ 注意事项**:
|
||||||
|
>
|
||||||
|
> - 插槽必须在 `task-list-column-render-mode="declarative"` 模式下使用
|
||||||
|
> - `default` 插槽接收 `scope` 参数,通过 `scope.row` 访问当前任务对象(Task类型),通过 `scope.$index` 访问索引
|
||||||
|
> - 推荐使用 `scope.row` 而不是解构 `{ row }`,以保持代码的清晰性和一致性
|
||||||
|
> - `scope.$index` 为当前任务在可见列表中的索引(非全局索引)
|
||||||
|
> - 插槽内容会在每个任务行中渲染,避免过于复杂的组件以保证性能
|
||||||
|
> - 列宽固定后,插槽内容应考虑溢出处理(如文本省略、自动换行等)
|
||||||
|
|
||||||
|
##### TaskListColumnConfig 列插槽(即将废弃)
|
||||||
|
|
||||||
|
> **⚠️ 重要提示**:此插槽方式基于 `TaskListColumnConfig` 配置,**即将废弃**。强烈建议使用上述 [TaskListColumn 插槽](#tasklistcolumn-插槽) 的声明式方式,以获得更好的类型提示和代码可维护性。
|
||||||
|
|
||||||
|
在默认模式(`taskListColumnRenderMode="default"`)下,可以通过插槽自定义 `TaskListColumnConfig` 中定义的列。插槽名称基于列配置中的 `key` 属性。
|
||||||
|
|
||||||
|
**插槽列表:**
|
||||||
|
|
||||||
|
| 插槽名模式 | 参数 | 说明 |
|
||||||
|
| --------------- | ---------------------------------------------- | ---------------------------------------------------------------------------------------------- |
|
||||||
|
| `header-{key}` | - | 自定义指定列的表头内容。`{key}` 为 `TaskListColumnConfig` 中定义的 `key` 值 |
|
||||||
|
| `column-{key}` | `{ task: Task, column: TaskListColumnConfig, value: any }` | 自定义指定列的单元格内容。`task` 为当前任务对象,`column` 为列配置,`value` 为该列的值 |
|
||||||
|
|
||||||
|
**使用示例:**
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<GanttChart
|
||||||
|
:tasks="tasks"
|
||||||
|
:task-list-config="taskListConfig"
|
||||||
|
task-list-column-render-mode="default"
|
||||||
|
>
|
||||||
|
<!-- 自定义 'name' 列的表头 -->
|
||||||
|
<template #header-name>
|
||||||
|
<div style="display: flex; align-items: center; gap: 6px;">
|
||||||
|
<img src="/avatar.png" width="32" height="32" style="border-radius: 50%;" />
|
||||||
|
<strong style="font-size: 14px;">任务名称</strong>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 自定义 'name' 列的单元格内容 -->
|
||||||
|
<template #column-name="{ task, column, value }">
|
||||||
|
<div style="display: flex; align-items: center; gap: 6px;">
|
||||||
|
<img src="/user-avatar.png" width="20" height="20" style="border-radius: 50%;" />
|
||||||
|
<span v-html="value"></span>
|
||||||
|
<span
|
||||||
|
v-if="task.priority"
|
||||||
|
style="
|
||||||
|
background: linear-gradient(135deg, #ff6b6b 0%, #ee5a6f 100%);
|
||||||
|
color: white;
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 10px;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
P-{{ task.priority }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 自定义其他列,例如自定义字段 'custom' -->
|
||||||
|
<template #column-custom="{ task, column, value }">
|
||||||
|
<div style="display: flex; align-items: center; gap: 4px;">
|
||||||
|
<span
|
||||||
|
v-if="typeof value === 'number'"
|
||||||
|
style="
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
color: white;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
💰 {{ value.toLocaleString() }}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
v-else-if="typeof value === 'string'"
|
||||||
|
style="
|
||||||
|
background: #e8f5e9;
|
||||||
|
color: #2e7d32;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
border: 1px solid #81c784;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
📝 {{ value }}
|
||||||
|
</span>
|
||||||
|
<span v-else style="color: #999;">-</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</GanttChart>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { GanttChart } from 'jordium-gantt-vue3'
|
||||||
|
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
|
||||||
|
import type { Task, TaskListConfig, TaskListColumnConfig } from 'jordium-gantt-vue3'
|
||||||
|
|
||||||
|
const tasks = ref<Task[]>([
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: '项目规划',
|
||||||
|
startDate: '2025-01-01',
|
||||||
|
endDate: '2025-01-10',
|
||||||
|
progress: 100,
|
||||||
|
priority: 1,
|
||||||
|
custom: 50000, // 自定义字段
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: '需求分析',
|
||||||
|
startDate: '2025-01-11',
|
||||||
|
endDate: '2025-01-20',
|
||||||
|
progress: 60,
|
||||||
|
custom: '重要', // 自定义字段
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
// 定义列配置
|
||||||
|
const taskListConfig = ref<TaskListConfig>({
|
||||||
|
columns: [
|
||||||
|
{ key: 'taskName', label: '任务名称', visible: true, width: 300 },
|
||||||
|
{ key: 'assignee', label: '负责人', visible: true, width: 150 },
|
||||||
|
{ key: 'progress', label: '进度', visible: true, width: 100 },
|
||||||
|
{ key: 'custom', label: '自定义', visible: true, width: 150 }, // 自定义列
|
||||||
|
],
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
> **💡 使用说明**:
|
||||||
|
>
|
||||||
|
> - 插槽名称格式:`header-{key}` 和 `column-{key}`,其中 `{key}` 对应 `TaskListColumnConfig.key`
|
||||||
|
> - `column-{key}` 插槽接收三个参数:
|
||||||
|
> - `task`:当前任务对象(Task类型)
|
||||||
|
> - `column`:当前列配置对象(TaskListColumnConfig类型)
|
||||||
|
> - `value`:当前单元格的值(自动从 `task[column.key]` 获取)
|
||||||
|
> - 可以混合使用插槽和默认渲染,只为需要自定义的列定义插槽
|
||||||
|
|
||||||
|
> **⚠️ 迁移建议**:
|
||||||
|
>
|
||||||
|
> - **强烈建议迁移到声明式模式**:使用 `task-list-column-render-mode="declarative"` 和 `TaskListColumn` 组件
|
||||||
|
> - 声明式模式提供更好的类型提示、代码组织和可维护性
|
||||||
|
> - 此配置式插槽方式将在未来版本中移除,请尽快迁移
|
||||||
|
> - 迁移示例请参考 [TaskListColumn 插槽](#tasklistcolumn-插槽) 章节
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## ❓ 常见问题
|
## ❓ 常见问题
|
||||||
|
|||||||
87
demo/App.vue
87
demo/App.vue
@@ -142,6 +142,9 @@ const toolbarConfig = {
|
|||||||
showExpandCollapse: true, // 显示全部展开/折叠按钮
|
showExpandCollapse: true, // 显示全部展开/折叠按钮
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TaskList列渲染模式配置
|
||||||
|
const taskListColumnRenderMode = ref<'default' | 'declarative'>('declarative')
|
||||||
|
|
||||||
// TaskList列配置
|
// TaskList列配置
|
||||||
const availableColumns = ref<TaskListColumnConfig[]>([
|
const availableColumns = ref<TaskListColumnConfig[]>([
|
||||||
{ key: 'predecessor', label: '前置任务', visible: true },
|
{ key: 'predecessor', label: '前置任务', visible: true },
|
||||||
@@ -518,6 +521,7 @@ const handleTaskAddEvent = (e: { task: Task }) => {
|
|||||||
// 任务删除事件处理器
|
// 任务删除事件处理器
|
||||||
const handleTaskDeleteEvent = (e: { task: Task; deleteChildren?: boolean }) => {
|
const handleTaskDeleteEvent = (e: { task: Task; deleteChildren?: boolean }) => {
|
||||||
const task = e.task
|
const task = e.task
|
||||||
|
const deleteChildren = e.deleteChildren
|
||||||
|
|
||||||
showMessage(`Demo 任务[${task.name}] 已删除`, 'success')
|
showMessage(`Demo 任务[${task.name}] 已删除`, 'success')
|
||||||
|
|
||||||
@@ -525,8 +529,10 @@ const handleTaskDeleteEvent = (e: { task: Task; deleteChildren?: boolean }) => {
|
|||||||
// 这里只需要显示消息,无需再次执行删除逻辑
|
// 这里只需要显示消息,无需再次执行删除逻辑
|
||||||
// 如果需要在删除后清理其他数据(如 predecessor),可以在这里处理
|
// 如果需要在删除后清理其他数据(如 predecessor),可以在这里处理
|
||||||
|
|
||||||
// 收集被删除任务的所有ID(包括子任务)
|
// 收集被删除任务的ID
|
||||||
const deletedTaskIds = collectAllTaskIds(task)
|
// 如果 deleteChildren 为 false,只删除父任务本身,子任务会被提升,所以不应该收集子任务ID
|
||||||
|
// 如果 deleteChildren 为 true,删除父任务及所有子任务,需要收集所有ID
|
||||||
|
const deletedTaskIds = deleteChildren ? collectAllTaskIds(task) : [task.id]
|
||||||
// 清理predecessor依赖关系
|
// 清理predecessor依赖关系
|
||||||
cleanupPredecessorReferences(deletedTaskIds)
|
cleanupPredecessorReferences(deletedTaskIds)
|
||||||
}
|
}
|
||||||
@@ -749,13 +755,24 @@ const handleTaskRowMoved = async (payload: {
|
|||||||
}}</span>
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="docs-links">
|
<div class="docs-links">
|
||||||
|
<a href="https://www.npmjs.com/package/jordium-gantt-vue3">
|
||||||
|
<img src="https://img.shields.io/npm/v/jordium-gantt-vue3?style=flat-square" alt="npm version">
|
||||||
|
</a>
|
||||||
|
<a href="https://www.npmjs.com/package/jordium-gantt-vue3">
|
||||||
|
<img src="https://img.shields.io/npm/dt/jordium-gantt-vue3?style=flat-square" alt="npm total">
|
||||||
|
</a>
|
||||||
|
<span class="docs-divider"></span>
|
||||||
<a href="#github-docs" class="doc-link github-link" @click="handleGithubDocsClick">
|
<a href="#github-docs" class="doc-link github-link" @click="handleGithubDocsClick">
|
||||||
<img class="doc-icon" src="./public/github.svg" alt="GitHub" />
|
<img class="doc-icon" src="./public/github.svg" alt="GitHub" />
|
||||||
</a>
|
</a>
|
||||||
|
<a href='https://github.com/nelson820125/jordium-gantt-vue3/stargazers'><img src='https://img.shields.io/github/stars/nelson820125/jordium-gantt-vue3?style=social' alt='star'></img></a>
|
||||||
|
<a href='https://github.com/nelson820125/jordium-gantt-vue3/network/members'><img src='https://img.shields.io/github/forks/nelson820125/jordium-gantt-vue3?style=social' alt='fork'></img></a>
|
||||||
<span class="docs-divider"></span>
|
<span class="docs-divider"></span>
|
||||||
<a href="#gitee-docs" class="doc-link gitee-link" @click="handleGiteeDocsClick">
|
<a href="#gitee-docs" class="doc-link gitee-link" @click="handleGiteeDocsClick">
|
||||||
<img class="doc-icon" src="./public/gitee.svg" alt="Gitee" />
|
<img class="doc-icon" src="./public/gitee.svg" alt="Gitee" />
|
||||||
</a>
|
</a>
|
||||||
|
<a href='https://gitee.com/jordium/jordium-gantt-vue3/stargazers'><img src='https://gitee.com/jordium/jordium-gantt-vue3/badge/star.svg?theme=dark' alt='star'></img></a>
|
||||||
|
<a href='https://gitee.com/jordium/jordium-gantt-vue3/members'><img src='https://gitee.com/jordium/jordium-gantt-vue3/badge/fork.svg?theme=dark' alt='fork'></img></a>
|
||||||
</div>
|
</div>
|
||||||
</h1>
|
</h1>
|
||||||
<VersionHistoryDrawer :visible="showVersionDrawer" @close="showVersionDrawer = false" />
|
<VersionHistoryDrawer :visible="showVersionDrawer" @close="showVersionDrawer = false" />
|
||||||
@@ -998,6 +1015,25 @@ const handleTaskRowMoved = async (payload: {
|
|||||||
|
|
||||||
<!-- 列配置 -->
|
<!-- 列配置 -->
|
||||||
<div class="subsection">
|
<div class="subsection">
|
||||||
|
<!-- 渲染模式选择 -->
|
||||||
|
<div class="render-mode-group" style="margin-bottom: 16px;">
|
||||||
|
<h5 class="subsection-title">{{ t.taskListConfig.columns.renderMode }}</h5>
|
||||||
|
<div
|
||||||
|
class="width-unit-toggle"
|
||||||
|
style="margin-bottom: 12px; display: flex; align-items: center; gap: 16px"
|
||||||
|
>
|
||||||
|
<label class="taskbar-control">
|
||||||
|
<input v-model="taskListColumnRenderMode" type="radio" value="default" />
|
||||||
|
<span class="taskbar-label">
|
||||||
|
{{ t.taskListConfig.columns.renderModeDefault }}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<label class="taskbar-control">
|
||||||
|
<input v-model="taskListColumnRenderMode" type="radio" value="declarative" />
|
||||||
|
<span class="taskbar-label">{{ t.taskListConfig.columns.renderModeDeclarative }}</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<h5 class="subsection-title">{{ t.taskListConfig.columns.title }}</h5>
|
<h5 class="subsection-title">{{ t.taskListConfig.columns.title }}</h5>
|
||||||
<div class="column-controls">
|
<div class="column-controls">
|
||||||
<label v-for="column in availableColumns" :key="column.key" class="column-control">
|
<label v-for="column in availableColumns" :key="column.key" class="column-control">
|
||||||
@@ -1184,7 +1220,7 @@ const handleTaskRowMoved = async (payload: {
|
|||||||
: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="declarative"
|
:task-list-column-render-mode="taskListColumnRenderMode"
|
||||||
@milestone-saved="handleMilestoneSaved"
|
@milestone-saved="handleMilestoneSaved"
|
||||||
@milestone-deleted="handleMilestoneDeleted"
|
@milestone-deleted="handleMilestoneDeleted"
|
||||||
@milestone-icon-changed="handleMilestoneIconChanged"
|
@milestone-icon-changed="handleMilestoneIconChanged"
|
||||||
@@ -1281,32 +1317,6 @@ const handleTaskRowMoved = async (payload: {
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 列级 Slot 示例:自定义 'assignee' 列的渲染(覆盖内置渲染) -->
|
|
||||||
<!-- 取消下面的注释可以看到效果 -->
|
|
||||||
<!--
|
|
||||||
<template #column-assignee="{ task }">
|
|
||||||
<div style="display: flex; align-items: center; gap: 6px;">
|
|
||||||
<div
|
|
||||||
style="
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
color: white;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: bold;
|
|
||||||
"
|
|
||||||
>
|
|
||||||
{{ task.assignee ? task.assignee.charAt(0) : '?' }}
|
|
||||||
</div>
|
|
||||||
<span style="font-size: 13px;">{{ task.assignee || '未分配' }}</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!-- 使用 TaskListColumn 组件自定义列 声明式模式 -->
|
<!-- 使用 TaskListColumn 组件自定义列 声明式模式 -->
|
||||||
<TaskListColumn prop="name" :label="t.taskName" width="300" align="center">
|
<TaskListColumn prop="name" :label="t.taskName" width="300" align="center">
|
||||||
<template #header>
|
<template #header>
|
||||||
@@ -1344,7 +1354,19 @@ const handleTaskRowMoved = async (payload: {
|
|||||||
</GanttChart>
|
</GanttChart>
|
||||||
</div>
|
</div>
|
||||||
<div class="license-info">
|
<div class="license-info">
|
||||||
<p>MIT License @JORDIUM.COM</p>
|
<span>MIT License @ 2025 JORDIUM.COM</span>
|
||||||
|
<a href="https://opensource.org/licenses/MIT">
|
||||||
|
<img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT License">
|
||||||
|
</a>
|
||||||
|
<a href="https://vuejs.org/">
|
||||||
|
<img src="https://img.shields.io/badge/Vue.js->=3.5.13-4FC08D?style=flat-square&logo=vue.js&logoColor=white" alt="Vue.js">
|
||||||
|
</a>
|
||||||
|
<a href="https://www.typescriptlang.org/">
|
||||||
|
<img src="https://img.shields.io/badge/TypeScript->=5.8.3-3178C6?style=flat-square&logo=typescript&logoColor=white" alt="TypeScript">
|
||||||
|
</a>
|
||||||
|
<a href="https://nodejs.org/">
|
||||||
|
<img src="https://img.shields.io/badge/Node.js->=16.0.0-339933?style=flat-square&logo=node.js&logoColor=white" alt="Nodejs">
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- MilestoneDialog用于新建/编辑里程碑 -->
|
<!-- MilestoneDialog用于新建/编辑里程碑 -->
|
||||||
@@ -2037,6 +2059,11 @@ const handleTaskRowMoved = async (payload: {
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
letter-spacing: 0.5px;
|
letter-spacing: 0.5px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 60px;
|
||||||
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 全局暗色主题支持 */
|
/* 全局暗色主题支持 */
|
||||||
|
|||||||
@@ -1,347 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { ref } from 'vue'
|
|
||||||
import { GanttChart } from '../src/index'
|
|
||||||
import type { Task } from '../src/models/classes/Task'
|
|
||||||
|
|
||||||
// 示例任务数据
|
|
||||||
const tasks = ref<Task[]>([
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
name: '项目规划',
|
|
||||||
type: 'story',
|
|
||||||
startDate: '2024-01-01',
|
|
||||||
endDate: '2024-01-15',
|
|
||||||
progress: 100,
|
|
||||||
assignee: '张三',
|
|
||||||
collapsed: false,
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
id: 11,
|
|
||||||
name: '需求分析',
|
|
||||||
type: 'task',
|
|
||||||
startDate: '2024-01-01',
|
|
||||||
endDate: '2024-01-05',
|
|
||||||
progress: 100,
|
|
||||||
assignee: '李四',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 12,
|
|
||||||
name: '技术方案设计',
|
|
||||||
type: 'task',
|
|
||||||
startDate: '2024-01-06',
|
|
||||||
endDate: '2024-01-15',
|
|
||||||
progress: 100,
|
|
||||||
assignee: '王五',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
name: '开发阶段',
|
|
||||||
type: 'story',
|
|
||||||
startDate: '2024-01-16',
|
|
||||||
endDate: '2024-02-28',
|
|
||||||
progress: 65,
|
|
||||||
assignee: '张三',
|
|
||||||
collapsed: false,
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
id: 21,
|
|
||||||
name: '前端开发',
|
|
||||||
type: 'task',
|
|
||||||
startDate: '2024-01-16',
|
|
||||||
endDate: '2024-02-15',
|
|
||||||
progress: 80,
|
|
||||||
assignee: '赵六',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 22,
|
|
||||||
name: '后端开发',
|
|
||||||
type: 'task',
|
|
||||||
startDate: '2024-01-16',
|
|
||||||
endDate: '2024-02-20',
|
|
||||||
progress: 70,
|
|
||||||
assignee: '孙七',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 23,
|
|
||||||
name: '数据库设计',
|
|
||||||
type: 'task',
|
|
||||||
startDate: '2024-01-20',
|
|
||||||
endDate: '2024-02-10',
|
|
||||||
progress: 50,
|
|
||||||
assignee: '周八',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
name: '测试上线',
|
|
||||||
type: 'story',
|
|
||||||
startDate: '2024-03-01',
|
|
||||||
endDate: '2024-03-15',
|
|
||||||
progress: 30,
|
|
||||||
assignee: '张三',
|
|
||||||
collapsed: false,
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
id: 31,
|
|
||||||
name: '功能测试',
|
|
||||||
type: 'task',
|
|
||||||
startDate: '2024-03-01',
|
|
||||||
endDate: '2024-03-08',
|
|
||||||
progress: 40,
|
|
||||||
assignee: '吴九',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 32,
|
|
||||||
name: '性能优化',
|
|
||||||
type: 'task',
|
|
||||||
startDate: '2024-03-05',
|
|
||||||
endDate: '2024-03-12',
|
|
||||||
progress: 20,
|
|
||||||
assignee: '郑十',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 33,
|
|
||||||
name: '生产部署',
|
|
||||||
type: 'task',
|
|
||||||
startDate: '2024-03-13',
|
|
||||||
endDate: '2024-03-15',
|
|
||||||
progress: 0,
|
|
||||||
assignee: '钱十一',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
const milestones = ref<Task[]>([])
|
|
||||||
|
|
||||||
// Helper function for progress bar background
|
|
||||||
const getProgressColor = (progress: number) => {
|
|
||||||
if (progress >= 100) return '#67c23a'
|
|
||||||
if (progress >= 50) return '#409eff'
|
|
||||||
return '#e6a23c'
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="declarative-demo">
|
|
||||||
<h1>声明式列定义演示 (Declarative Columns Demo)</h1>
|
|
||||||
|
|
||||||
<div class="demo-section">
|
|
||||||
<h2>示例 1: 基础声明式列</h2>
|
|
||||||
<GanttChart
|
|
||||||
:tasks="tasks"
|
|
||||||
:milestones="milestones"
|
|
||||||
task-list-column-render-mode="declarative"
|
|
||||||
:show-toolbar="false"
|
|
||||||
style="height: 400px"
|
|
||||||
>
|
|
||||||
<TaskListColumn prop="assignee" label="负责人" width="120" align="center" />
|
|
||||||
<TaskListColumn prop="name" label="任务名称" width="300" />
|
|
||||||
|
|
||||||
<TaskListColumn prop="startDate" label="开始日期" width="140" />
|
|
||||||
<TaskListColumn prop="endDate" label="结束日期" width="140" />
|
|
||||||
<TaskListColumn prop="progress" label="进度" width="100" align="center" />
|
|
||||||
</GanttChart>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="demo-section">
|
|
||||||
<h2>示例 2: 使用默认 Slot 自定义列内容</h2>
|
|
||||||
<GanttChart
|
|
||||||
:tasks="tasks"
|
|
||||||
:milestones="milestones"
|
|
||||||
task-list-column-render-mode="declarative"
|
|
||||||
:show-toolbar="false"
|
|
||||||
style="height: 400px"
|
|
||||||
>
|
|
||||||
<TaskListColumn prop="name" label="任务名称" width="300" />
|
|
||||||
|
|
||||||
<!-- 自定义负责人列显示 -->
|
|
||||||
<TaskListColumn prop="assignee" label="负责人" width="150" align="center">
|
|
||||||
<template #default="scope">
|
|
||||||
<div class="assignee-cell">
|
|
||||||
<div class="avatar">
|
|
||||||
{{ scope.row.assignee ? scope.row.assignee.charAt(0).toUpperCase() : '?' }}
|
|
||||||
</div>
|
|
||||||
<span>{{ scope.row.assignee || '未分配' }}</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</TaskListColumn>
|
|
||||||
|
|
||||||
<!-- 自定义进度列显示 -->
|
|
||||||
<TaskListColumn prop="progress" label="进度" width="150" align="center">
|
|
||||||
<template #default="scope">
|
|
||||||
<div class="progress-cell">
|
|
||||||
<div class="progress-bar-container">
|
|
||||||
<div
|
|
||||||
class="progress-bar"
|
|
||||||
:style="{
|
|
||||||
width: `${scope.row.progress || 0}%`,
|
|
||||||
background: getProgressColor(scope.row.progress || 0),
|
|
||||||
}"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
<span class="progress-text">{{ scope.row.progress || 0 }}%</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</TaskListColumn>
|
|
||||||
|
|
||||||
<TaskListColumn prop="startDate" label="开始" width="120" />
|
|
||||||
<TaskListColumn prop="endDate" label="结束" width="120" />
|
|
||||||
</GanttChart>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="demo-section">
|
|
||||||
<h2>示例 3: 自定义表头</h2>
|
|
||||||
<GanttChart
|
|
||||||
:tasks="tasks"
|
|
||||||
:milestones="milestones"
|
|
||||||
task-list-column-render-mode="declarative"
|
|
||||||
:show-toolbar="false"
|
|
||||||
style="height: 400px"
|
|
||||||
>
|
|
||||||
<TaskListColumn prop="name" width="300">
|
|
||||||
<template #header>
|
|
||||||
<div class="header-with-icon">
|
|
||||||
<svg
|
|
||||||
width="16"
|
|
||||||
height="16"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
stroke-width="2"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-5l-2-2H5a2 2 0 00-2 2z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<span class="header-title">任务信息</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</TaskListColumn>
|
|
||||||
|
|
||||||
<TaskListColumn prop="assignee" width="150">
|
|
||||||
<template #header>
|
|
||||||
<div class="header-success">👤 负责人</div>
|
|
||||||
</template>
|
|
||||||
</TaskListColumn>
|
|
||||||
|
|
||||||
<TaskListColumn prop="progress" width="150">
|
|
||||||
<template #header>
|
|
||||||
<div class="header-warning">📊 完成度</div>
|
|
||||||
</template>
|
|
||||||
<template #default="scope">
|
|
||||||
<span :class="scope.row.progress >= 100 ? 'text-success' : 'text-warning'">
|
|
||||||
{{ scope.row.progress || 0 }}%
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
</TaskListColumn>
|
|
||||||
|
|
||||||
<TaskListColumn prop="startDate" label="开始日期" width="140" />
|
|
||||||
<TaskListColumn prop="endDate" label="结束日期" width="140" />
|
|
||||||
</GanttChart>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.declarative-demo {
|
|
||||||
padding: 20px;
|
|
||||||
background: #f5f5f5;
|
|
||||||
min-height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.demo-section {
|
|
||||||
background: white;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 20px;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.demo-section h2 {
|
|
||||||
color: #409eff;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.assignee-cell {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar {
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: #409eff;
|
|
||||||
color: white;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress-cell {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress-bar-container {
|
|
||||||
flex: 1;
|
|
||||||
height: 8px;
|
|
||||||
background: #f0f0f0;
|
|
||||||
border-radius: 4px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress-bar {
|
|
||||||
height: 100%;
|
|
||||||
transition: width 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress-text {
|
|
||||||
min-width: 40px;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-with-icon {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 4px;
|
|
||||||
color: #409eff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-title {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-success {
|
|
||||||
color: #67c23a;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-warning {
|
|
||||||
color: #e6a23c;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-success {
|
|
||||||
color: #67c23a;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-warning {
|
|
||||||
color: #e6a23c;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
@@ -378,5 +378,25 @@
|
|||||||
"Fixed: Disabled Task drag-and-drop event trigger on double-click",
|
"Fixed: Disabled Task drag-and-drop event trigger on double-click",
|
||||||
"<span style=\"font-weight: bold; color: #f00;\">Special thanks to @qiuchengw for his professional contribution</span>"
|
"<span style=\"font-weight: bold; color: #f00;\">Special thanks to @qiuchengw for his professional contribution</span>"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.6.0",
|
||||||
|
"date": "2025-12-14",
|
||||||
|
"notes": [
|
||||||
|
"🎉 新增:Task Table列声明式定义方式, 使用组件TaskListColumn",
|
||||||
|
"🎉 新增:Task Table #header和#default Slots",
|
||||||
|
"🎉 新增:Task List行样式设置属性 - task-list-row-class-name和task-list-row-style",
|
||||||
|
"🎉 新增:为兼容旧版本TaskListColumnConfig,提供task-list-column-render-mode属性开关,可以通过设置选择Task表格列定义方式",
|
||||||
|
"🔧 修复:仅删除Story层级后,子Task升级后关系线丢失问题",
|
||||||
|
"🔧 修复:修复已知问题",
|
||||||
|
"<span style=\"font-weight: bold; color: #f00;\">特别感谢 osc_85545913@gitee的使用及反馈</span>",
|
||||||
|
"🎉 Added: Declarative definition method for Task Table columns using the TaskListColumn component",
|
||||||
|
"🎉 Added: Task Table #header and #default Slots",
|
||||||
|
"🎉 Added: Task List row style setting properties - task-list-row-class-name and task-list-row-style",
|
||||||
|
"🎉 Added: A switch for task-list-column-render-mode attribute to choose Task Table column definition method for compatibility with older versions of TaskListColumnConfig",
|
||||||
|
"🔧 Fixed: Issue of missing relationship lines after upgrading child Tasks when only deleting Story",
|
||||||
|
"🔧 Fixed: Fixed issues",
|
||||||
|
"<span style=\"font-weight: bold; color: #f00;\">Special thanks to osc_85545913@gitee for their use and feedback</span>"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "jordium-gantt-vue3",
|
"name": "jordium-gantt-vue3",
|
||||||
"version": "1.5.0",
|
"version": "1.6.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",
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ const __dirname = path.dirname(__filename)
|
|||||||
|
|
||||||
// 构建后生成CSS导出文件
|
// 构建后生成CSS导出文件
|
||||||
function generateCssExport() {
|
function generateCssExport() {
|
||||||
const distDir = path.resolve(__dirname, '../npm-package/dist')
|
const distDir = path.resolve(__dirname, '../../npm-package/dist')
|
||||||
const assetsDir = path.join(distDir, 'assets')
|
const assetsDir = path.join(distDir, 'assets')
|
||||||
const expectedCssFile = path.join(assetsDir, 'jordium-gantt-vue3.css')
|
const expectedCssFile = path.join(assetsDir, 'jordium-gantt-vue3.css')
|
||||||
const outputFile = path.join(distDir, 'jordium-gantt-vue3-styles.js')
|
const outputFile = path.join(distDir, 'jordium-gantt-vue3-styles.js')
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ const onNo = () => emit('no')
|
|||||||
<template v-else>
|
<template v-else>
|
||||||
<button type="button" class="gantt-btn gantt-btn-default" @click="onCancel">
|
<button type="button" class="gantt-btn gantt-btn-default" @click="onCancel">
|
||||||
{{ props.cancelText }}
|
{{ props.cancelText }}
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="gantt-btn gantt-btn-danger" @click="onConfirm">
|
<button type="button" class="gantt-btn gantt-btn-danger" @click="onConfirm">
|
||||||
{{ props.confirmText }}
|
{{ props.confirmText }}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1003,6 +1003,7 @@ onUnmounted(() => {
|
|||||||
.dialog-actions {
|
.dialog-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
gap: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dialog-actions .btn + .btn {
|
.dialog-actions .btn + .btn {
|
||||||
|
|||||||
@@ -4,12 +4,11 @@ import type { StyleValue, Slots } from 'vue'
|
|||||||
import TaskRow from './TaskRow.vue'
|
import TaskRow from './TaskRow.vue'
|
||||||
import { useI18n } from '../composables/useI18n'
|
import { useI18n } from '../composables/useI18n'
|
||||||
import type { Task } from '../models/classes/Task'
|
import type { Task } from '../models/classes/Task'
|
||||||
import type { TaskListConfig } from '../models/configs/TaskListConfig'
|
import type { TaskListConfig, TaskListColumnConfig } from '../models/configs/TaskListConfig'
|
||||||
import { DEFAULT_TASK_LIST_COLUMNS } from '../models/configs/TaskListConfig'
|
import { DEFAULT_TASK_LIST_COLUMNS } from '../models/configs/TaskListConfig'
|
||||||
import { useTaskRowDrag } from '../composables/useTaskRowDrag'
|
import { useTaskRowDrag } from '../composables/useTaskRowDrag'
|
||||||
import { moveTask } from '../utils/taskTreeUtils'
|
import { moveTask } from '../utils/taskTreeUtils'
|
||||||
import { useTaskListColumns } from '../composables/useTaskListColumns'
|
import { useTaskListColumns } from '../composables/useTaskListColumns'
|
||||||
import type { DeclarativeColumnConfig } from '../composables/useTaskListColumns'
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
tasks?: Task[]
|
tasks?: Task[]
|
||||||
@@ -57,7 +56,7 @@ const columnSlots = inject<Slots>('gantt-column-slots', {})
|
|||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
// 使用声明式列管理 composable
|
// 使用声明式列管理 composable
|
||||||
const { declarativeColumns, finalColumns, getColumnWidthStyle: getDeclarativeColumnWidth } =
|
const { declarativeColumns, getColumnWidthStyle: getDeclarativeColumnWidth } =
|
||||||
useTaskListColumns(
|
useTaskListColumns(
|
||||||
props.taskListColumnRenderMode || 'default',
|
props.taskListColumnRenderMode || 'default',
|
||||||
slots,
|
slots,
|
||||||
@@ -125,8 +124,14 @@ const getColumnWidthStyle = (column: { width?: number | string }) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算可见的列配置(已被 columnsToUse 替代,保留以兼容)
|
// 计算可见的列配置(仅用于默认模式)
|
||||||
const visibleColumns = computed(() => columnsToUse.value)
|
const visibleColumns = computed(() => {
|
||||||
|
if (props.taskListColumnRenderMode === 'declarative') {
|
||||||
|
return [] as TaskListColumnConfig[] // 声明式模式不需要这个
|
||||||
|
}
|
||||||
|
const columns = props.taskListConfig?.columns || DEFAULT_TASK_LIST_COLUMNS
|
||||||
|
return columns.filter(col => col.visible !== false)
|
||||||
|
})
|
||||||
|
|
||||||
// 使用 props.tasks 的引用,不创建副本
|
// 使用 props.tasks 的引用,不创建副本
|
||||||
// 这样可以直接修改对象,触发响应式更新,不需要外部监听事件
|
// 这样可以直接修改对象,触发响应式更新,不需要外部监听事件
|
||||||
@@ -636,16 +641,16 @@ onUnmounted(() => {
|
|||||||
:class="column.cssClass"
|
:class="column.cssClass"
|
||||||
:style="{
|
:style="{
|
||||||
...getColumnWidthStyle(column),
|
...getColumnWidthStyle(column),
|
||||||
justifyContent: column.align === 'center' ? 'center' : column.align === 'right' ? 'flex-end' : 'flex-start',
|
justifyContent: (column as any).align === 'center' ? 'center' : (column as any).align === 'right' ? 'flex-end' : 'flex-start',
|
||||||
textAlign: column.align || 'left'
|
textAlign: (column as any).align || 'left'
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<!-- 使用 header slot 或显示 label -->
|
<!-- 使用 header slot 或显示 label -->
|
||||||
<template v-if="column.headerSlot">
|
<template v-if="(column as any).headerSlot">
|
||||||
<component :is="column.headerSlot" />
|
<component :is="(column as any).headerSlot" />
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
{{ column.label }}
|
{{ (column as any).label }}
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -665,17 +670,17 @@ onUnmounted(() => {
|
|||||||
<!-- 可配置的其他列 -->
|
<!-- 可配置的其他列 -->
|
||||||
<div
|
<div
|
||||||
v-for="column in visibleColumns"
|
v-for="column in visibleColumns"
|
||||||
:key="column.key"
|
:key="(column as TaskListColumnConfig).key"
|
||||||
class="col"
|
class="col"
|
||||||
:class="column.cssClass || `col-${column.key}`"
|
:class="(column as TaskListColumnConfig).cssClass || `col-${(column as TaskListColumnConfig).key}`"
|
||||||
:style="getColumnWidthStyle(column)"
|
:style="getColumnWidthStyle(column)"
|
||||||
>
|
>
|
||||||
<!-- 检查是否有对应的 header-{key} slot -->
|
<!-- 检查是否有对应的 header-{key} slot -->
|
||||||
<template v-if="columnSlots[`header-${column.key}`]">
|
<template v-if="columnSlots[`header-${(column as TaskListColumnConfig).key}`]">
|
||||||
<component :is="columnSlots[`header-${column.key}`]" />
|
<component :is="columnSlots[`header-${(column as TaskListColumnConfig).key}`]" />
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
{{ (t as any)[column.key] || column.label }}
|
{{ (t as any)[(column as TaskListColumnConfig).key] || (column as TaskListColumnConfig).label }}
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -692,7 +697,7 @@ onUnmounted(() => {
|
|||||||
:is-hovered="hoveredTaskId === task.id"
|
:is-hovered="hoveredTaskId === task.id"
|
||||||
:hovered-task-id="hoveredTaskId"
|
:hovered-task-id="hoveredTaskId"
|
||||||
:on-hover="handleTaskRowHover"
|
:on-hover="handleTaskRowHover"
|
||||||
:columns="visibleColumns"
|
:columns="taskListColumnRenderMode === 'declarative' ? [] : visibleColumns"
|
||||||
:declarative-columns="taskListColumnRenderMode === 'declarative' ? columnsToUse : undefined"
|
:declarative-columns="taskListColumnRenderMode === 'declarative' ? columnsToUse : undefined"
|
||||||
:render-mode="taskListColumnRenderMode"
|
:render-mode="taskListColumnRenderMode"
|
||||||
:get-column-width-style="getColumnWidthStyle"
|
:get-column-width-style="getColumnWidthStyle"
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ interface Props {
|
|||||||
cssClass?: string
|
cssClass?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
withDefaults(defineProps<Props>(), {
|
||||||
align: 'left',
|
align: 'left',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -133,10 +133,10 @@ const renderDeclarativeColumn = (
|
|||||||
class: ['task-name-text', { 'parent-task': isParentTask.value }],
|
class: ['task-name-text', { 'parent-task': isParentTask.value }],
|
||||||
title: columnValue,
|
title: columnValue,
|
||||||
},
|
},
|
||||||
column.defaultSlot({
|
[column.defaultSlot({
|
||||||
row: props.task,
|
row: props.task,
|
||||||
$index: index,
|
$index: index,
|
||||||
}),
|
})],
|
||||||
),
|
),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
@@ -549,10 +549,10 @@ onUnmounted(() => {
|
|||||||
:key="index"
|
:key="index"
|
||||||
class="col"
|
class="col"
|
||||||
:class="[column.cssClass, { 'col-name': isFirstColumn(index) }]"
|
:class="[column.cssClass, { 'col-name': isFirstColumn(index) }]"
|
||||||
:style="{
|
:style="[
|
||||||
...(getColumnWidthStyle ? getColumnWidthStyle(column) : {}),
|
getColumnWidthStyle ? getColumnWidthStyle(column) : {},
|
||||||
...getDeclarativeColumnAlign(column),
|
getDeclarativeColumnAlign(column),
|
||||||
}"
|
]"
|
||||||
>
|
>
|
||||||
<!-- 第一列:保留 collapse-btn, milestone-spacer, leaf-spacer -->
|
<!-- 第一列:保留 collapse-btn, milestone-spacer, leaf-spacer -->
|
||||||
<div v-if="isFirstColumn(index)"
|
<div v-if="isFirstColumn(index)"
|
||||||
@@ -630,16 +630,16 @@ onUnmounted(() => {
|
|||||||
value: props.task.name,
|
value: props.task.name,
|
||||||
isParentTask,
|
isParentTask,
|
||||||
hasChildren,
|
hasChildren,
|
||||||
isStoryTask: isStoryTask.value,
|
isStoryTask: isStoryTask,
|
||||||
isMilestoneGroup: isMilestoneGroup.value,
|
isMilestoneGroup: isMilestoneGroup,
|
||||||
isMilestoneTask: isMilestoneTask.value,
|
isMilestoneTask: isMilestoneTask,
|
||||||
showTaskIcon: props.showTaskIcon,
|
showTaskIcon: props.showTaskIcon,
|
||||||
formattedTimer: formattedTimer.value,
|
formattedTimer: formattedTimer,
|
||||||
isOvertime: isOvertime(),
|
isOvertime: isOvertime(),
|
||||||
overdueDays: overdueDays(),
|
overdueDays: overdueDays(),
|
||||||
overtimeText: overtimeText.value,
|
overtimeText: overtimeText,
|
||||||
overdueText: overdueText.value,
|
overdueText: overdueText,
|
||||||
daysText: daysText.value,
|
daysText: daysText,
|
||||||
})"
|
})"
|
||||||
v-if="hasColumnSlot('name')"
|
v-if="hasColumnSlot('name')"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -209,7 +209,10 @@ const messages = {
|
|||||||
taskListConfig: {
|
taskListConfig: {
|
||||||
title: 'TaskList 配置',
|
title: 'TaskList 配置',
|
||||||
columns: {
|
columns: {
|
||||||
title: '列显示',
|
title: '列显示(默认渲染模式下生效)',
|
||||||
|
renderMode: '渲染模式',
|
||||||
|
renderModeDefault: '默认(通过 TaskListColumnConfig 设置)',
|
||||||
|
renderModeDeclarative: '声明式(使用 TaskListColumn 设置可见列)',
|
||||||
},
|
},
|
||||||
width: {
|
width: {
|
||||||
title: '宽度设置',
|
title: '宽度设置',
|
||||||
@@ -474,7 +477,10 @@ const messages = {
|
|||||||
taskListConfig: {
|
taskListConfig: {
|
||||||
title: 'TaskList Configuration',
|
title: 'TaskList Configuration',
|
||||||
columns: {
|
columns: {
|
||||||
title: 'Columns',
|
title: 'Columns(默effective in default render mode)',
|
||||||
|
renderMode: 'Render Mode',
|
||||||
|
renderModeDefault: 'Default (via TaskListColumnConfig)',
|
||||||
|
renderModeDeclarative: 'Declarative (via TaskListColumn)',
|
||||||
},
|
},
|
||||||
width: {
|
width: {
|
||||||
title: 'Width Settings',
|
title: 'Width Settings',
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ const generateCssExportPlugin = () => {
|
|||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [vue(), generateCssExportPlugin()],
|
plugins: [vue(), generateCssExportPlugin()],
|
||||||
build: {
|
build: {
|
||||||
outDir: './npm-package/dist',
|
outDir: '../npm-package/dist',
|
||||||
emptyOutDir: true,
|
emptyOutDir: true,
|
||||||
lib: {
|
lib: {
|
||||||
entry: './src/index.ts',
|
entry: './src/index.ts',
|
||||||
|
|||||||
Reference in New Issue
Block a user