diff --git a/API_USAGE.md b/API_USAGE.md deleted file mode 100644 index d61741a..0000000 --- a/API_USAGE.md +++ /dev/null @@ -1,996 +0,0 @@ -# Gantt Chart API 使用指南 - -## 📖 概述 - -Jordium Gantt Vue3 组件提供了灵活的API接口,允许开发者自定义TaskBar双击事件的处理逻辑,可以完全替换默认的编辑行为。组件采用Vue3 + TypeScript构建,样式延续Element Plus设计风格但不依赖Element Plus组件库。 - -## 🚀 API 接口 - -### GanttChart Props - -| 属性名 | 类型 | 默认值 | 描述 | -|--------|------|--------|------| -| `tasks` | `Task[]` | `[]` | 任务数据数组,支持嵌套结构(树形数据) | -| `onTaskDoubleClick` | `(task: Task) => void` | `undefined` | 自定义双击事件处理器,当TaskBar被双击时触发 | -| `editComponent` | `any` | `undefined` | 自定义编辑组件(预留接口,暂未实现) | -| `useDefaultDrawer` | `boolean` | `true` | 是否使用默认的TaskDrawer抽屉组件进行编辑 | -| `onTaskDelete` | `(task: Task) => void` | `undefined` | 自定义删除事件处理器,当删除按钮被点击时触发 | -| `showToolbar` | `boolean` | `true` | 是否显示工具栏 | -| `toolbarConfig` | `ToolbarConfig` | `{}` | 工具栏配置选项 | -| `onAddTask` | `() => void` | `undefined` | 新增任务按钮点击事件处理器 | -| `onExportCsv` | `() => void \| boolean` | `undefined` | 导出CSV按钮点击事件处理器,返回false使用默认实现 | -| `onExportPdf` | `() => void` | `undefined` | 导出PDF按钮点击事件处理器 | -| `onLanguageChange` | `(lang: 'zh' \| 'en') => void` | `undefined` | 语言切换事件处理器 | -| `onThemeChange` | `(isDark: boolean) => void` | `undefined` | 主题切换事件处理器 | -| `onFullscreenChange` | `(isFullscreen: boolean) => void` | `undefined` | 全屏切换事件处理器 | - -### ToolbarConfig 接口定义 - -```typescript -interface ToolbarConfig { - showAddTask?: boolean // 是否显示新增任务按钮,默认true - showExportCsv?: boolean // 是否显示导出CSV按钮,默认true - showExportPdf?: boolean // 是否显示导出PDF按钮,默认true - showLanguage?: boolean // 是否显示语言切换按钮,默认true - showTheme?: boolean // 是否显示主题切换按钮,默认true - showFullscreen?: boolean // 是否显示全屏切换按钮,默认true -} -``` - -### API 工作机制 - -1. **双击优先级**: 当提供 `onTaskDoubleClick` 时,将优先调用自定义处理器,默认的TaskDrawer不会打开 -2. **双击一致性**: TaskList中任务行的双击与Timeline中TaskBar的双击具有完全相同的效果和优先级 -3. **删除优先级**: 当提供 `onTaskDelete` 时,将优先调用自定义删除处理器,否则使用默认删除行为 -4. **默认行为**: 当未提供 `onTaskDoubleClick` 且 `useDefaultDrawer` 为 `true` 时,双击TaskBar或TaskRow会打开内置的TaskDrawer -5. **完全自定义**: 设置 `useDefaultDrawer: false` 可以完全禁用默认抽屉,只使用自定义处理器 -6. **数据更新机制**: - - TaskDrawer更新任务 → Timeline本地更新 → 发送task-updated事件 → TaskList更新数据源 - - TaskList数据更新 → 发送tasks-changed事件 → Timeline重新渲染TaskBar - - TaskBar位置变化 → 自动重新报告位置 → 依赖关系线自动重新计算 -7. **CSV导出机制**: - - 当提供 `onExportCsv` 时,优先调用自定义处理器 - - 如果自定义处理器返回 `false`,则使用内置的默认导出功能 - - 默认导出支持UTF-8编码、多语言头部、安全字符转义等特性 - - 导出内容包含所有任务字段,递归处理子任务 -8. **工具栏功能**: - - 新增任务按钮:独立的主要操作按钮,具有醒目的主色调样式 - - 导出按钮组:CSV和PDF导出按钮采用button group样式,左右相连,统一的视觉效果 - - 设置按钮:语言、主题、全屏等图标按钮,位于右侧,支持响应式布局 - - 国际化支持:内置中英文切换,所有按钮文本和提示自动适配 - -### Task 接口定义 - -```typescript -interface Task { - id: number // 任务唯一标识 - name: string // 任务名称 - predecessor?: string // 前置任务ID - assignee?: string // 负责人 - startDate?: string // 开始日期 (YYYY-MM-DD格式) - endDate?: string // 结束日期 (YYYY-MM-DD格式) - progress?: number // 完成进度 (0-100) - estimatedHours?: number // 预估工时 - actualHours?: number // 实际工时 - children?: Task[] // 子任务数组(支持嵌套结构) - collapsed?: boolean // 是否折叠子任务 - isParent?: boolean // 是否为父级任务 - type?: string // 任务类型 (task/story/bug/milestone) - description?: string // 任务描述 -} -``` - -## 💡 使用示例 - -### 1. 默认模式(使用内置TaskDrawer) - -```vue - - - -``` - -### 2. 自定义双击处理器 - -```vue - - - -``` - -### 3. TaskList与TaskBar双击一致性 - -```vue - - - -``` - -### 4. CSV导出功能使用 - -CSV导出功能支持自定义处理器和默认实现,具有完整的多语言和UTF-8编码支持。详细文档请参考 [CSV_EXPORT.md](./CSV_EXPORT.md) - -#### 基本使用 - -```vue - - - -``` - -### 5. 工具栏配置与使用 - -工具栏采用了全新的设计,新增任务按钮独立显示,导出CSV和PDF按钮采用button group样式相连,右侧为设置类按钮。 - -```vue - - - -``` - -#### 工具栏样式特点 - -- **Button Group设计**: 导出CSV和PDF按钮采用连接式设计,视觉上更加统一 -- **主操作突出**: 新增任务按钮使用主色调,突出重要操作 -- **图标化右侧**: 设置类按钮采用纯图标设计,节省空间 -- **响应式布局**: 在小屏幕设备上自动调整按钮尺寸和间距 -- **国际化支持**: 所有按钮文本和提示自动适配当前语言 - -#### 选择性显示工具栏项 - -```vue - - - -``` - -#### 完全隐藏工具栏 - -```vue - -``` - -> **说明**: TaskList中任务行的双击与Timeline中TaskBar的双击具有完全相同的API行为和优先级机制,确保用户体验的一致性。 - -### 4. 条件性使用不同处理器 - -```vue - - - -``` - -### 4. 自定义删除处理器 - -```vue - - - -``` - -### 5. 完整的自定义处理器组合 - -```vue - - - -``` - -## 🏗️ 组件架构 - -### 组件层次结构 -``` -GanttChart (入口组件,API配置) -├── TaskList (左侧任务列表) -├── Timeline (右侧时间轴区域) - ├── TaskBar (任务条,支持双击事件) - ├── Milestone (里程碑) - └── TaskDrawer (默认编辑抽屉) -``` - -### API 数据流 -1. `GanttChart` 接收API配置props -2. 透传给 `Timeline` 组件 -3. `Timeline` 将API配置传递给 `TaskBar` -4. `TaskBar` 双击时调用API处理器或触发默认行为 - -### 内置功能 -- ✅ 拖拽调整任务时间 -- ✅ 调整任务条长度 -- ✅ 任务进度显示 -- ✅ 前置任务依赖关系 -- ✅ 父子任务层级结构 -- ✅ 时间轴缩放和导航 -- ✅ 自定义双击事件API - -## 🔧 技术特性 - -- **Framework**: Vue 3 + TypeScript + Vite -- **样式**: 延续Element Plus设计风格,无外部依赖 -- **响应式**: 完全使用Vue 3 Composition API -- **类型安全**: 完整的TypeScript接口定义 -- **可扩展**: 灵活的API设计,支持自定义扩展 - -## 🛠️ 高级用法 - -### 完整示例:集成自定义编辑功能 - -```vue - - - - - -``` - -## ⚡ 性能优化建议 - -1. **事件处理器缓存**: 使用 `computed` 或 `useMemo` 缓存事件处理器,避免不必要的重新渲染 - -2. **异步处理**: 对于复杂的双击处理逻辑,建议使用异步函数 - -```vue - -``` - -3. **防抖处理**: 对于可能触发频繁操作的场景,考虑添加防抖 - -```vue - -``` - -## 🔧 故障排除 - -### 常见问题 - -1. **Q: 双击事件不触发?** - - A: 检查是否正确传递了 `onTaskDoubleClick` 属性,并确保 `useDefaultDrawer` 设置正确 - -2. **Q: 同时使用自定义处理器和默认Drawer?** - - A: 当 `onTaskDoubleClick` 存在时,会优先调用自定义处理器,默认Drawer不会打开 - -3. **Q: 如何在自定义处理器中获取更多任务信息?** - - A: 可以通过全局状态管理或者父组件传递更多上下文信息 - -4. **Q: 自定义处理器中的异步操作报错?** - - A: 确保在异步操作中添加try-catch错误处理,避免未捕获的异常 - -### 调试技巧 - -```vue - -``` - -## ⚡ 性能优化 - -### 1. 事件处理器缓存 -```vue - - - -``` - -### 2. 异步处理优化 -```vue - -``` - -### 3. 防抖处理 -```vue - -``` - -## 🎯 最佳实践 - -### 1. 代码组织 -- **保持处理器简洁**: 双击处理器应该保持轻量,复杂逻辑建议抽取到单独的函数中 -- **类型安全**: 充分利用TypeScript的类型检查,确保Task接口一致性 -- **职责分离**: 将业务逻辑与UI逻辑分离 - -### 2. 用户体验 -- **即时反馈**: 为用户操作提供即时的视觉反馈 -- **错误处理**: 始终为异步操作添加错误处理和用户友好的错误提示 -- **加载状态**: 为长时间运行的操作提供适当的加载提示 -- **可访问性**: 确保自定义交互也支持键盘导航和屏幕阅读器 - -### 3. 性能考虑 -- **避免内存泄漏**: 及时清理事件监听器和定时器 -- **合理使用响应式**: 不要过度使用reactive,对于简单数据使用ref -- **组件懒加载**: 对于复杂的自定义组件考虑懒加载 - -### 4. 兼容性设计 -```vue - -``` - -## 🚀 扩展开发 - -### 自定义事件系统 -```vue - -``` - -### 插件化架构 -```typescript -// 定义插件接口 -interface GanttPlugin { - name: string - onTaskDoubleClick?: (task: Task) => void - onTaskCreate?: (task: Task) => void - onTaskUpdate?: (task: Task) => void -} - -// 使用插件 -const ganttPlugins: GanttPlugin[] = [ - { - name: 'analytics', - onTaskDoubleClick: (task) => { - // 分析统计逻辑 - } - } -] -``` - -## 📝 更新日志 - -- **v0.2.0-beta**: 工具栏集成与Button Group优化 - - ✅ 新增GanttToolbar工具栏组件,支持完整的功能配置 - - ✅ 导出CSV和PDF按钮采用Button Group样式,视觉统一 - - ✅ 新增任务按钮独立显示,采用主色调突出重要操作 - - ✅ 右侧设置按钮采用图标化设计,支持语言、主题、全屏切换 - - ✅ 工具栏支持国际化,内置中英文切换 - - ✅ 响应式设计,在小屏幕设备上自动调整布局 - - ✅ 支持选择性显示工具栏项,完全可配置 - -- **v1.0.0**: 初始API发布 - - ✅ 支持自定义双击处理器 (`onTaskDoubleClick`) - - ✅ 支持禁用默认编辑抽屉 (`useDefaultDrawer`) - - ✅ 完整的TypeScript类型支持 - - ✅ Element Plus风格设计,无外部依赖 - -- **规划中**: - - 🔄 自定义编辑组件支持 (`editComponent`) - - 🔄 更多事件API(创建、删除、拖拽等) - - 🔄 主题定制API - - 🔄 插件系统 - -## 🤝 贡献指南 - -欢迎提交Issue和Pull Request来改进这个组件! - -### 开发环境设置 -```bash -# 克隆项目 -git clone - -# 安装依赖 -npm install - -# 启动开发服务器 -npm run dev - -# 构建项目 -npm run build -``` - -### 代码规范 -- 使用TypeScript进行类型安全开发 -- 遵循Vue 3 Composition API最佳实践 -- 保持代码简洁和可读性 -- 添加适当的注释和文档 - ---- - -🎉 **通过这些API,您可以完全自定义TaskBar的交互行为,打造符合您项目需求的甘特图体验!** - -> 如有问题或建议,欢迎提交Issue或联系开发团队。 diff --git a/README.en.md b/README.en.md deleted file mode 100644 index fe901bc..0000000 --- a/README.en.md +++ /dev/null @@ -1,88 +0,0 @@ -# Jordium Gantt Vue3 - -> Modern, open-source, and high-performance Gantt chart component library for Vue3 + TypeScript - ---- - -## 🚀 Product Positioning - -Jordium Gantt Vue3 is a high-performance Gantt chart component library for modern web applications, focusing on excellent interaction, flexible extension, and open-source compliance. Ideal for project management, progress visualization, R&D collaboration, and more. - -- **Tech Stack**: Vue 3 + TypeScript -- **Design Language**: Element Plus style (no dependency) -- **Use Cases**: Enterprise project management, scheduling, resource allocation, etc. - -## ✨ Key Features - -- **Modern UI/UX**: Minimalist, clean, responsive, supports light/dark themes, auto system adaptation -- **High Performance**: Virtual scrolling, auto-expanding timeline, smooth with large datasets -- **Task Management**: Multi-level tasks, milestones, drag & resize, progress, dependencies -- **Enhanced Interaction**: Today locator, fullscreen, version history, toolbar, quick actions -- **Internationalization**: Built-in Chinese/English, easy to extend to more languages -- **Flexible Extension**: Custom editors, event handling, data sources, export, etc. -- **Open Source Compliance**: MIT License, suitable for secondary development and commercial use - -## 🖌️ Design Philosophy - -- **Ultimate Experience**: Interaction details match top-tier products, smooth animation, positioning, and theme switching -- **No External Dependencies**: Only Vue3/TS required, Element Plus style but zero dependency -- **Easy to Use & Extend**: Friendly API, supports slots, events, and deep prop customization -- **Ready to Use**: Comprehensive documentation, easy integration - -## 📦 Installation & Usage - -```bash -npm install jordium-gantt-vue3 -``` - -### Basic Usage - -```vue - - - -``` - -### Advanced Usage - -- Custom task editor (disable default drawer, handle double-click events) -- Custom toolbar buttons, export, theme switching -- Listen to task/milestone changes, drag, dependency events -- Support for external data sources, async loading - -See [API_USAGE.md](./API_USAGE.md) for details. - -## 🎨 Theming & Adaptation - -- Supports light/dark themes, auto system detection -- Theme variables customizable for easy brand adaptation -- Fully responsive, works on desktop and mobile - -## 📝 Version History - -See [version-history.json](./demo/version-history.json) for details. - -- **0.9.x-ALPHA**: Productization refactor, full interaction & visual upgrade, MIT License -- **0.2 Beta**: API improvements, tasks/milestones/dependencies/toolbar/i18n/theme -- **0.1 Beta**: Basic features, Element Plus style - -## 📚 API Documentation - -See [API_USAGE.md](./API_USAGE.md) for full API reference. - -## 🤝 Contribution & Community - -Issues, PRs, suggestions, and stars are welcome! For custom development, enterprise support, or bug reports, contact [jordium.com](https://jordium.com). - -## 📄 License - -MIT License © Jordium - -# README.en.md - -This file has been deprecated. Please refer to `README.md` for the latest documentation. diff --git a/README.md b/README.md index 259477d..7cb2af7 100644 --- a/README.md +++ b/README.md @@ -26,10 +26,1019 @@ import { GanttChart } from 'jordium-gantt-vue3' - GanttToolbar - GanttConfirmDialog -## 贡献与开发 -- 统一样式请参考 `src/styles/app.css` -- 组件开发请遵循解耦、命名规范 -- 详细API见 API_USAGE.md - ## License MIT +# Gantt Chart API 使用指南 + +## 📖 概述 + +Jordium Gantt Vue3 组件提供了灵活的API接口,允许开发者自定义TaskBar双击事件的处理逻辑,可以完全替换默认的编辑行为。组件采用Vue3 + TypeScript构建,样式延续Element Plus设计风格但不依赖Element Plus组件库。 + +## 🚀 API 接口 + +### GanttChart Props + +| 属性名 | 类型 | 默认值 | 描述 | +|--------|------|--------|------| +| `tasks` | `Task[]` | `[]` | 任务数据数组,支持嵌套结构(树形数据) | +| `onTaskDoubleClick` | `(task: Task) => void` | `undefined` | 自定义双击事件处理器,当TaskBar被双击时触发 | +| `editComponent` | `any` | `undefined` | 自定义编辑组件(预留接口,暂未实现) | +| `useDefaultDrawer` | `boolean` | `true` | 是否使用默认的TaskDrawer抽屉组件进行编辑 | +| `onTaskDelete` | `(task: Task) => void` | `undefined` | 自定义删除事件处理器,当删除按钮被点击时触发 | +| `showToolbar` | `boolean` | `true` | 是否显示工具栏 | +| `toolbarConfig` | `ToolbarConfig` | `{}` | 工具栏配置选项 | +| `onAddTask` | `() => void` | `undefined` | 新增任务按钮点击事件处理器 | +| `onExportCsv` | `() => void \| boolean` | `undefined` | 导出CSV按钮点击事件处理器,返回false使用默认实现 | +| `onExportPdf` | `() => void` | `undefined` | 导出PDF按钮点击事件处理器 | +| `onLanguageChange` | `(lang: 'zh' \| 'en') => void` | `undefined` | 语言切换事件处理器 | +| `onThemeChange` | `(isDark: boolean) => void` | `undefined` | 主题切换事件处理器 | +| `onFullscreenChange` | `(isFullscreen: boolean) => void` | `undefined` | 全屏切换事件处理器 | + +### ToolbarConfig 接口定义 + +```typescript +interface ToolbarConfig { + showAddTask?: boolean // 是否显示新增任务按钮,默认true + showExportCsv?: boolean // 是否显示导出CSV按钮,默认true + showExportPdf?: boolean // 是否显示导出PDF按钮,默认true + showLanguage?: boolean // 是否显示语言切换按钮,默认true + showTheme?: boolean // 是否显示主题切换按钮,默认true + showFullscreen?: boolean // 是否显示全屏切换按钮,默认true +} +``` + +### API 工作机制 + +1. **双击优先级**: 当提供 `onTaskDoubleClick` 时,将优先调用自定义处理器,默认的TaskDrawer不会打开 +2. **双击一致性**: TaskList中任务行的双击与Timeline中TaskBar的双击具有完全相同的效果和优先级 +3. **删除优先级**: 当提供 `onTaskDelete` 时,将优先调用自定义删除处理器,否则使用默认删除行为 +4. **默认行为**: 当未提供 `onTaskDoubleClick` 且 `useDefaultDrawer` 为 `true` 时,双击TaskBar或TaskRow会打开内置的TaskDrawer +5. **完全自定义**: 设置 `useDefaultDrawer: false` 可以完全禁用默认抽屉,只使用自定义处理器 +6. **数据更新机制**: + - TaskDrawer更新任务 → Timeline本地更新 → 发送task-updated事件 → TaskList更新数据源 + - TaskList数据更新 → 发送tasks-changed事件 → Timeline重新渲染TaskBar + - TaskBar位置变化 → 自动重新报告位置 → 依赖关系线自动重新计算 +7. **CSV导出机制**: + - 当提供 `onExportCsv` 时,优先调用自定义处理器 + - 如果自定义处理器返回 `false`,则使用内置的默认导出功能 + - 默认导出支持UTF-8编码、多语言头部、安全字符转义等特性 + - 导出内容包含所有任务字段,递归处理子任务 +8. **工具栏功能**: + - 新增任务按钮:独立的主要操作按钮,具有醒目的主色调样式 + - 导出按钮组:CSV和PDF导出按钮采用button group样式,左右相连,统一的视觉效果 + - 设置按钮:语言、主题、全屏等图标按钮,位于右侧,支持响应式布局 + - 国际化支持:内置中英文切换,所有按钮文本和提示自动适配 + +### Task 接口定义 + +```typescript +interface Task { + id: number // 任务唯一标识 + name: string // 任务名称 + predecessor?: string // 前置任务ID + assignee?: string // 负责人 + startDate?: string // 开始日期 (YYYY-MM-DD格式) + endDate?: string // 结束日期 (YYYY-MM-DD格式) + progress?: number // 完成进度 (0-100) + estimatedHours?: number // 预估工时 + actualHours?: number // 实际工时 + children?: Task[] // 子任务数组(支持嵌套结构) + collapsed?: boolean // 是否折叠子任务 + isParent?: boolean // 是否为父级任务 + type?: string // 任务类型 (task/story/bug/milestone) + description?: string // 任务描述 +} +``` + +## 💡 使用示例 + +### 1. 默认模式(使用内置TaskDrawer) + +```vue + + + +``` + +### 2. 自定义双击处理器 + +```vue + + + +``` + +### 3. TaskList与TaskBar双击一致性 + +```vue + + + +``` + +### 4. CSV导出功能使用 + +CSV导出功能支持自定义处理器和默认实现,具有完整的多语言和UTF-8编码支持。详细文档请参考 [CSV_EXPORT.md](./CSV_EXPORT.md) + +#### 基本使用 + +```vue + + + +``` + +### 5. 工具栏配置与使用 + +工具栏采用了全新的设计,新增任务按钮独立显示,导出CSV和PDF按钮采用button group样式相连,右侧为设置类按钮。 + +```vue + + + +``` + +#### 工具栏样式特点 + +- **Button Group设计**: 导出CSV和PDF按钮采用连接式设计,视觉上更加统一 +- **主操作突出**: 新增任务按钮使用主色调,突出重要操作 +- **图标化右侧**: 设置类按钮采用纯图标设计,节省空间 +- **响应式布局**: 在小屏幕设备上自动调整按钮尺寸和间距 +- **国际化支持**: 所有按钮文本和提示自动适配当前语言 + +#### 选择性显示工具栏项 + +```vue + + + +``` + +#### 完全隐藏工具栏 + +```vue + +``` + +> **说明**: TaskList中任务行的双击与Timeline中TaskBar的双击具有完全相同的API行为和优先级机制,确保用户体验的一致性。 + +### 4. 条件性使用不同处理器 + +```vue + + + +``` + +### 4. 自定义删除处理器 + +```vue + + + +``` + +### 5. 完整的自定义处理器组合 + +```vue + + + +``` + +## 🏗️ 组件架构 + +### 组件层次结构 +``` +GanttChart (入口组件,API配置) +├── TaskList (左侧任务列表) +├── Timeline (右侧时间轴区域) + ├── TaskBar (任务条,支持双击事件) + ├── Milestone (里程碑) + └── TaskDrawer (默认编辑抽屉) +``` + +### API 数据流 +1. `GanttChart` 接收API配置props +2. 透传给 `Timeline` 组件 +3. `Timeline` 将API配置传递给 `TaskBar` +4. `TaskBar` 双击时调用API处理器或触发默认行为 + +### 内置功能 +- ✅ 拖拽调整任务时间 +- ✅ 调整任务条长度 +- ✅ 任务进度显示 +- ✅ 前置任务依赖关系 +- ✅ 父子任务层级结构 +- ✅ 时间轴缩放和导航 +- ✅ 自定义双击事件API + +## 🔧 技术特性 + +- **Framework**: Vue 3 + TypeScript + Vite +- **样式**: 延续Element Plus设计风格,无外部依赖 +- **响应式**: 完全使用Vue 3 Composition API +- **类型安全**: 完整的TypeScript接口定义 +- **可扩展**: 灵活的API设计,支持自定义扩展 + +## 🛠️ 高级用法 + +### 完整示例:集成自定义编辑功能 + +```vue + + + + + +``` + +## ⚡ 性能优化建议 + +1. **事件处理器缓存**: 使用 `computed` 或 `useMemo` 缓存事件处理器,避免不必要的重新渲染 + +2. **异步处理**: 对于复杂的双击处理逻辑,建议使用异步函数 + +```vue + +``` + +3. **防抖处理**: 对于可能触发频繁操作的场景,考虑添加防抖 + +```vue + +``` + +## 🔧 故障排除 + +### 常见问题 + +1. **Q: 双击事件不触发?** + - A: 检查是否正确传递了 `onTaskDoubleClick` 属性,并确保 `useDefaultDrawer` 设置正确 + +2. **Q: 同时使用自定义处理器和默认Drawer?** + - A: 当 `onTaskDoubleClick` 存在时,会优先调用自定义处理器,默认Drawer不会打开 + +3. **Q: 如何在自定义处理器中获取更多任务信息?** + - A: 可以通过全局状态管理或者父组件传递更多上下文信息 + +4. **Q: 自定义处理器中的异步操作报错?** + - A: 确保在异步操作中添加try-catch错误处理,避免未捕获的异常 + +### 调试技巧 + +```vue + +``` + +## ⚡ 性能优化 + +### 1. 事件处理器缓存 +```vue + + + +``` + +### 2. 异步处理优化 +```vue + +``` + +### 3. 防抖处理 +```vue + +``` + +## 🎯 最佳实践 + +### 1. 代码组织 +- **保持处理器简洁**: 双击处理器应该保持轻量,复杂逻辑建议抽取到单独的函数中 +- **类型安全**: 充分利用TypeScript的类型检查,确保Task接口一致性 +- **职责分离**: 将业务逻辑与UI逻辑分离 + +### 2. 用户体验 +- **即时反馈**: 为用户操作提供即时的视觉反馈 +- **错误处理**: 始终为异步操作添加错误处理和用户友好的错误提示 +- **加载状态**: 为长时间运行的操作提供适当的加载提示 +- **可访问性**: 确保自定义交互也支持键盘导航和屏幕阅读器 + +### 3. 性能考虑 +- **避免内存泄漏**: 及时清理事件监听器和定时器 +- **合理使用响应式**: 不要过度使用reactive,对于简单数据使用ref +- **组件懒加载**: 对于复杂的自定义组件考虑懒加载 + +### 4. 兼容性设计 +```vue + +``` + +## 🚀 扩展开发 + +### 自定义事件系统 +```vue + +``` + +### 插件化架构 +```typescript +// 定义插件接口 +interface GanttPlugin { + name: string + onTaskDoubleClick?: (task: Task) => void + onTaskCreate?: (task: Task) => void + onTaskUpdate?: (task: Task) => void +} + +// 使用插件 +const ganttPlugins: GanttPlugin[] = [ + { + name: 'analytics', + onTaskDoubleClick: (task) => { + // 分析统计逻辑 + } + } +] +``` + +## 🤝 贡献指南 + +欢迎提交Issue和Pull Request来改进这个组件! + +### 开发环境设置 +```bash +# 克隆项目 +git clone + +# 安装依赖 +npm install + +# 启动开发服务器 +npm run dev + +# 构建项目 +npm run build +``` + +### 代码规范 +- 使用TypeScript进行类型安全开发 +- 遵循Vue 3 Composition API最佳实践 +- 保持代码简洁和可读性 +- 添加适当的注释和文档 + +--- + +🎉 **通过这些API,您可以完全自定义TaskBar的交互行为,打造符合您项目需求的甘特图体验!** + +> 如有问题或建议,欢迎提交Issue或联系开发团队。 + +## GanttChart 属性(Props) + +| 属性名 | 说明 | 类型 | 默认值 | +|----------------------|------------------------------|-------------------------------------------|---------------| +| tasks | 任务数据 | `Task[]` | `[]` | +| milestones | 里程碑数据 | `Milestone[]` | `[]` | +| toolbarConfig | 工具栏配置 | `ToolbarConfig` | `{}` | +| showToolbar | 是否显示工具栏 | `boolean` | `true` | +| editComponent | 自定义编辑组件 | `Component` | `undefined` | +| useDefaultDrawer | 是否使用默认TaskDrawer | `boolean` | `true` | +| localeMessages | 自定义多语言配置 | `object` | `undefined` | +| isDark | 主题模式(受控) | `boolean` | `false` | +| onLanguageChange | 语言切换回调 | `(lang: 'zh-CN' | 'en-US') => void` | `undefined` | +| onThemeChange | 主题切换回调 | `(isDark: boolean) => void` | `undefined` | +| onFullscreenChange | 全屏/退出全屏回调 | `(isFullscreen: boolean) => void` | `undefined` | +| onTaskDoubleClick | 任务双击回调 | `(task: Task) => void` | `undefined` | +| onTaskDelete | 任务删除回调 | `(task: Task) => void` | `undefined` | +| onTaskUpdate | 任务更新回调 | `(task: Task) => void` | `undefined` | +| onTaskAdd | 任务添加回调 | `(task: Task) => void` | `undefined` | +| onTaskbarDragEnd | 任务条拖拽结束 | `(task: Task) => void` | `undefined` | +| onTaskbarResizeEnd | 任务条拉伸结束 | `(task: Task) => void` | `undefined` | +| onMilestoneSave | 里程碑保存回调 | `(milestone: Milestone) => void` | `undefined` | +| onMilestoneDelete | 里程碑删除回调 | `(milestoneId: number) => void` | `undefined` | +| onMilestoneIconChange| 里程碑图标变更回调 | `(milestoneId: number, icon: string) => void` | `undefined` | +| onMilestoneDragEnd | 里程碑拖拽结束 | `(milestone: Milestone) => void` | `undefined` | +| onAddTask | 工具栏“新增任务”回调 | `() => void` | `undefined` | +| onAddMilestone | 工具栏“新增里程碑”回调 | `() => void` | `undefined` | +| onExportCsv | 导出CSV回调 | `() => boolean | void` | `undefined` | +| onExportPdf | 导出PDF回调 | `() => void` | `undefined` | +| onTodayLocate | 定位到今天回调 | `() => void` | `undefined` | + +| 事件名 | 说明 | 回调参数类型 | +|----------------------|------------------------------|-----------------------| +| taskbar-drag-end | 任务条拖拽结束 | `task: Task` | +| taskbar-resize-end | 任务条拉伸结束 | `task: Task` | +| milestone-drag-end | 里程碑拖拽结束 | `milestone: Milestone`| +| milestone-double-click| 里程碑双击 | `milestone: Milestone`| +| ...其它见源码 | | | + +> 详细类型定义请参考源码 `src/models/classes/Task.ts`、`src/models/classes/Milestone.ts`、`src/models/configs/ToolbarConfig.ts`。 diff --git a/demo/App.vue b/demo/App.vue index 5dee1bf..45a6232 100644 --- a/demo/App.vue +++ b/demo/App.vue @@ -8,24 +8,10 @@ import packageInfo from '../package.json' // 导入主题变量 import '../src/styles/theme-variables.css' import VersionHistoryDrawer from './VersionHistoryDrawer.vue' +import { useMessage } from '../src/composables/useMessage' +import type { Task } from '../src/models/Task' -interface Task { - id: number - name: string - predecessor?: string - assignee?: string - startDate?: string - endDate?: string - progress?: number - estimatedHours?: number - actualHours?: number - children?: Task[] - collapsed?: boolean - isParent?: boolean - type?: string - description?: string - parentId?: number // 上级任务ID -} +const { showMessage } = useMessage() const tasks = ref([]) const milestones = ref([]) @@ -56,9 +42,7 @@ const toolbarConfig = { // 自定义CSV导出处理器(可选) const handleCustomCsvExport = () => { - console.log('自定义CSV导出被调用') - console.log('当前任务数据:', tasks.value) - console.log('当前里程碑数据:', milestones.value) + showMessage('自定义CSV导出被调用', 'info', { closable: true }) // 这里可以实现自定义的CSV导出逻辑 // 例如:添加额外的数据处理、格式化、或发送到服务器等 @@ -86,11 +70,11 @@ const handleAddMilestone = () => { } const handleLanguageChange = (lang: 'zh' | 'en') => { - console.log('语言切换到:', lang) + showMessage(`语言切换到:${lang}`, 'info', { closable: true }) } const handleThemeChange = (isDark: boolean) => { - console.log('主题切换到:', isDark ? '暗黑模式' : '明亮模式') + showMessage(`主题切换到:${isDark ? '暗黑模式' : '明亮模式'}`, 'info', { closable: true }) } // 里程碑保存处理器示例 @@ -116,13 +100,11 @@ const handleMilestoneSave = (milestone: Task) => { // 里程碑删除处理器 const handleMilestoneDelete = async (milestoneId: number) => { - console.log('删除里程碑,ID:', milestoneId) - // 从里程碑数据中删除 const milestoneIndex = milestones.value.findIndex(m => m.id === milestoneId) if (milestoneIndex !== -1) { milestones.value.splice(milestoneIndex, 1) - console.log('里程碑删除成功') + showMessage('里程碑删除成功', 'success', { closable: false }) // 等待DOM更新完成 await nextTick() @@ -148,10 +130,6 @@ const handleMilestoneDelete = async (milestoneId: number) => { // 任务更新处理器 const handleTaskUpdate = (updatedTask: Task) => { - console.log('Demo App 接收到任务更新:', updatedTask) - console.log('updatedTask.type:', updatedTask.type) - console.log('updatedTask.parentId:', updatedTask.parentId) - // 先找到原任务,检查parentId是否改变了 const findOriginalTask = (taskArray: Task[]): Task | null => { for (const task of taskArray) { @@ -168,7 +146,7 @@ const handleTaskUpdate = (updatedTask: Task) => { const originalTask = findOriginalTask(tasks.value) if (!originalTask) { - console.log('未找到要更新的任务,ID:', updatedTask.id) + showMessage(`未找到要更新的任务,ID: ${updatedTask.id}`, 'warning', { closable: true }) return } @@ -177,13 +155,10 @@ const handleTaskUpdate = (updatedTask: Task) => { if (parentIdChanged) { // parentId改变了,需要移除任务并重新添加到新位置 - console.log('任务父级关系改变,执行移动操作') - const removeTaskFromArray = (taskArray: Task[]): Task | null => { for (let i = 0; i < taskArray.length; i++) { if (taskArray[i].id === updatedTask.id) { const removedTask = taskArray.splice(i, 1)[0] - console.log('从数组中移除任务:', removedTask) return removedTask } @@ -218,7 +193,6 @@ const handleTaskUpdate = (updatedTask: Task) => { if (!task.children) task.children = [] task.children.push(taskToAdd) task.isParent = true - console.log('已将任务添加到新父任务的children中,父任务ID:', taskToAdd.parentId) return true } if (task.children && task.children.length > 0) { @@ -229,17 +203,16 @@ const handleTaskUpdate = (updatedTask: Task) => { } if (!addToParentChildren(tasks.value)) { - console.warn('未找到新父任务,ID:', taskToAdd.parentId, ',将作为顶级任务添加') + showMessage(`未找到新父任务,ID: ${taskToAdd.parentId},将作为顶级任务添加`, 'warning', { + closable: true, + }) tasks.value.push(taskToAdd) } } else { tasks.value.push(taskToAdd) - console.log('已将任务添加为顶级任务') } } else { // parentId没有改变,只是就地更新任务数据 - console.log('任务父级关系未变,执行就地更新') - const updateTaskInPlace = (taskArray: Task[]): boolean => { for (let i = 0; i < taskArray.length; i++) { if (taskArray[i].id === updatedTask.id) { @@ -251,7 +224,6 @@ const handleTaskUpdate = (updatedTask: Task) => { updatedTask.type === 'story' || (taskArray[i].children && taskArray[i].children.length > 0), } - console.log('已就地更新任务数据:', taskArray[i]) return true } @@ -265,18 +237,15 @@ const handleTaskUpdate = (updatedTask: Task) => { } if (!updateTaskInPlace(tasks.value)) { - console.log('就地更新失败,未找到任务,ID:', updatedTask.id) + showMessage(`就地更新失败,未找到任务,ID: ${updatedTask.id}`, 'warning', { closable: true }) } } - console.log('任务更新完成,当前任务数据:', tasks.value) + showMessage('任务更新完成', 'success', { closable: false }) } // 任务添加处理器 const handleTaskAdd = (newTask: Task) => { - console.log('Demo App 接收到新增任务:', newTask) - console.log('newTask.type:', newTask.type) - // 为新任务生成ID(如果没有的话) if (!newTask.id) { // 找到当前最大的ID,然后+1 @@ -304,7 +273,6 @@ const handleTaskAdd = (newTask: Task) => { } // 将新任务添加到父任务的children中 task.children.push({ ...newTask }) - console.log('已将子任务添加到父任务的children中,父任务ID:', newTask.parentId) return true } // 递归查找父任务(支持多层嵌套) @@ -331,15 +299,13 @@ const handleTaskAdd = (newTask: Task) => { // 任务删除处理器 const handleTaskDelete = (taskToDelete: Task) => { - console.log('删除任务:', taskToDelete) - // 递归查找和删除任务(支持嵌套结构) const deleteTaskFromArray = (taskArray: Task[]): boolean => { for (let i = 0; i < taskArray.length; i++) { if (taskArray[i].id === taskToDelete.id) { // 找到任务,删除它 taskArray.splice(i, 1) - console.log('已删除任务') + showMessage('已删除任务', 'success', { closable: false }) return true } @@ -354,20 +320,17 @@ const handleTaskDelete = (taskToDelete: Task) => { } if (!deleteTaskFromArray(tasks.value)) { - console.log('未找到要删除的任务,ID:', taskToDelete.id) + showMessage(`未找到要删除的任务,ID: ${taskToDelete.id}`, 'warning', { closable: true }) } } // 里程碑图标变更处理器 const handleMilestoneIconChange = (milestoneId: number, icon: string) => { - console.log('里程碑图标变更:', { milestoneId, icon }) - const milestoneIndex = milestones.value.findIndex(m => m.id === milestoneId) if (milestoneIndex !== -1) { milestones.value[milestoneIndex].icon = icon - console.log('已更新里程碑图标') } else { - console.log('未找到要更新图标的里程碑,ID:', milestoneId) + showMessage(`未找到要更新图标的里程碑,ID: ${milestoneId}`, 'warning', { closable: true }) } } @@ -411,14 +374,45 @@ const handleGiteeDocsClick = (event: Event) => { window.open('https://gitee.com/jordium-gantt/jordium-gantt-vue3#readme', '_blank') } +// 任务拖拽/拉伸/里程碑拖拽监听 +function handleTaskbarDragOrResizeEnd(newTask) { + const oldTask = findTaskDeep(tasks.value, newTask.id) + if (!oldTask) return + showMessage( + `任务【${newTask.name}】\n` + + `开始: ${oldTask.startDate} → ${newTask.startDate}\n` + + `结束: ${oldTask.endDate} → ${newTask.endDate}`, + 'info', + { closable: true }, + ) +} +function handleMilestoneDragEnd(newMilestone) { + const oldMilestone = findTaskDeep(milestones.value, newMilestone.id) + if (!oldMilestone) return + showMessage( + `里程碑【${newMilestone.name}】\n` + + `开始: ${oldMilestone.endDate} → ${newMilestone.startDate}`, + 'info', + { closable: true }, + ) +} + onMounted(() => { tasks.value = demoData.tasks as Task[] milestones.value = demoData.milestones as Task[] - - // 调试信息:打印原始任务数据 - console.log('原始任务数据:', tasks.value) - console.log('里程碑数据:', milestones.value) }) + +// 递归查找任务/里程碑,因为原始结构一致 +function findTaskDeep(taskArray: Task[], id: number): Task | null { + for (const task of taskArray) { + if (task.id === id) return task + if (task.children && task.children.length > 0) { + const found = findTaskDeep(task.children, id) + if (found) return found + } + } + return null +}