WeKan 甘特图(Gantt)全面指南:内置周视图、Frappe 与 dhtmlx 双引擎的配置与使用
2026/9/13 10:33:11 网站建设 项目流程

WeKan 甘特图(Gantt)全面指南:内置周视图、Frappe 与 dhtmlx 双引擎的配置与使用

【免费下载链接】wekanThe Open Source kanban, built with Meteor. GitHub issues/PRs are only for FLOSS Developers, not for support, support is at https://wekan.fi/commercial-support/ . PR source translation to imports/i18n/data/en.i18n.json, other translations at https://app.transifex.com/wekan/wekan项目地址: https://gitcode.com/GitHub_Trending/we/wekan

WeKan 是采用 Meteor 构建的开源看板应用,其在 2025-12-22 引入的全新 Gantt 功能,为看板卡片的时间管理提供了三种可视化方案:内置表格型周视图、基于 frappe-gantt 的条形图视图,以及基于 dhtmlx-gantt 的经典「网格+时间轴」视图。阅读本文后,你将掌握如何在"All Boards"页面进入 Gantt 视图、理解四种卡片日期(Received/Start/Due/End)在三种视图中的呈现与编辑方式,并能依据写入权限判断拖拽改期是否可用,同时了解其背后的源码实现原理。

一、Gantt 在 WeKan 中的定位与入口

Gantt 视图与 Swimlanes(泳道)、Lists(列表)、Calendar(日历)等视图并列,统一挂在看板顶部的Board View下拉菜单中。入口路径为:

Board View 菜单(顶部工具栏)→ Gantt / Gantt (Frappe) / Gantt (dhtmlx)

┌─ Board View ▾ ──────────────────────┐ │ ... │ │ ▸ Gantt <- here │ │ Gantt (Frappe) │ │ Gantt (dhtmlx) │ │ ... │ └───────────────────────────────────────┘

在 boardHeader.jade 中可以看到,当boardView切换为board-view-gantt时,菜单项会渲染为fa-bar-chart图标,标识 Gantt 视图处于激活状态。三种视图共享同一份数据源——当前打开看板内所有卡片的四个日期字段。

二、内置表格型 Gantt:以周为单位的时间网格

内置 Gantt 是一张以「任务 × 周」为坐标的表格视图:

┌ Gantt ──────────────────────────────────────────────┐ │ Task | wk1 | wk2 | wk3 | wk4 | ... │ │ Card A | ▬▬▬▬▬▬ 🔵🟢🟡🔴 (date icons) │ │ Card B | ▬▬▬▬▬▬▬ │ └─────────────────────────────────────────────────────────┘

周列的生成逻辑

视图的纵轴不是固定日历,而是只在当前看板存在卡片日期的周才生成列。在 gantt.js 中,getISOWeekInfo()依据 ISO 8601 标准计算某个日期所属的年-周编号(YYYY-Www),weeks()helper 遍历当前看板全部卡片,收集四个日期字段(receivedAt/startAt/dueAt/endAt)涉及的所有周,并按时间排序输出为列。因此一个没有任何日期卡片的看板,Gantt 表格会是空的。

四种日期在图中的呈现

表格按天细分到一周七天(weekDayshelper),每一天对应一个单元格。单元格通过cellContentClasscellContent两个 helper 决定样式与图标(源码见 gantt.js):

卡片日期字段CSS 类名Font Awesome 图标语义
receivedAt(收到日期)ganttview-receivedfa-inbox任务被接收/创建
startAt(开始日期)ganttview-startfa-rocket任务启动
dueAt(截止日期)ganttview-duefa-clock-o任务截止
endAt(结束日期)ganttview-endfa-flag-checkered任务实际完成

同时cellClasses会叠加ganttview-today(今天)与ganttview-weekend(周末)条件类,让当日与周末在视觉上可区分;只有命中四种日期的单元格才会附加js-gantt-date-icon,从而获得可点击编辑的能力。

点击交互:开卡与改期

  • 点击任务名称click .js-gantt-card-title事件把行上的data-card-id写入模板的selectedCardId响应式变量,随即通过 ganttCard.jade 渲染出完整的cardDetails组件(即卡片详情弹层),无需跳转页面即可查看并编辑卡片。
  • 点击日期图标onRendered中注册的委托事件监听.js-gantt-date-icon,读取单元格的data-date-type(去掉ganttview-前缀后)映射到对应的弹窗:received → editCardReceivedDatestart → editCardStartDatedue → editCardDueDateend → editCardEndDate,然后调用Popup.open(popupName)打开日期编辑弹窗。这一设计决定了内置视图是「点击日期图标」式的编辑器,不支持拖拽改期

三、内置 Gantt 的使用步骤

  1. 打开一个看板,确保其中至少一张卡片设置了 Received/Start/Due/End 中的任一日期;
  2. 点击顶部工具栏Board ViewGantt
  3. 点击任务名称打开对应卡片;
  4. 点击行上四个日期图标(Received/Start/Due/End)中的任意一个,直接在图表中修改该日期。

四、两套第三方 Gantt 引擎:Frappe 与 dhtmlx

除内置表格视图外,同一个Board View菜单还提供基于第三方库的两个渲染器,对应模板分别为 frappeGantt.jade 与 dhtmlxGantt.jade。

三者读取的卡片数据完全相同(同一看板卡片的四个日期),区别在于渲染库、交互手感,以及是否支持拖拽条形图改期

┌─ Board View ▾ ──────────────────────┐ │ ▸ Gantt │ │ ▸ Gantt (Frappe) <- here │ │ ▸ Gantt (dhtmlx) <- or here │ └───────────────────────────────────────┘

两个引擎都由 client/features/gantt.js 统一导入,属于按需挂载的 Board View 组件。

4.1 Gantt (Frappe)

Frappe 引擎基于 frappe-gantt。

核心机制:

  • 懒加载:库通过动态import('frappe-gantt')加载(loadGanttLib()),只有用户真正打开该视图时代码才进入浏览器,不会拖慢首屏。由于 frappe-gantt 的package.jsonexports 映射只暴露了"style"条件、不接受./dist/frappe-gantt.css子路径,其 CSS 被原样复制为 frappeGanttLib.css 静态加载。
  • 条形图生成cardsToTasks()把每张卡片转换为一个任务条:以startAt(缺失时回退到receivedAt)作为条形起点,以dueAt(缺失时回退到endAt)作为终点;若终点早于或等于起点,则自动补足为起点次日(addDays(start, 1))。_startField/_endField记录了条形两端实际代表的字段,供拖拽回写时精确定位。
  • 拖拽改期与权限readonly_dates: !Utils.currentUserCan('write', board)——只有看板写入权限的用户才能拖动条形的左右边缘改期;无写权限时视图仍可读但不可拖。这一权限口径与 WeKan 其余模块的Utils.canModifyCard一致。
  • 进度与逾期:卡片已有endAtprogress为 100,否则为 0(进度条不可拖,readonly_progress: true);逾期任务(有dueAt、无endAt且已过截止日)会附加gantt-task-overdue样式类。
  • 视图模式:默认以周(WEEK)展示,且开启view_mode_select: true提供 Day/Week/Month/Year 切换下拉框;Frappe 内置的 7 种VIEW_MODE会被复制并逐一替换为 WeKan 的翻译文案(translatedViewModes()),其头部硬编码的 "Today" 按钮与 "Mode" 占位符则由MutationObservertranslateFrappeChrome())在每次重建时重新翻译。
  • 弹出信息:点击条形的弹出面板中按行展示全部四个日期(popupDetailsHtml()),因此即使某卡片通过回退字段仅用两个日期绘制条形,另外两个日期依然可见。
  • 点击条形开卡on_click回调通过FlowRouter.go('card', ...)跳转到对应卡片页。

4.2 Gantt (dhtmlx)

dhtmlx 引擎基于 dhtmlx-gantt Community Edition,采用经典的「左侧任务网格 + 右侧时间轴」布局,交互手感与 Frappe 明显不同。

核心机制:

  • 单例特性:dhtmlx-gantt 暴露的是一个共享的 JavaScript单例对象gantt),而非可按容器实例化的类。因此每次重新渲染及模板销毁时都必须先gantt.destructor()销毁(见onDestroyed),再在下次打开时重建,否则第二次打开会复用第一次遗留的实例状态——这是与 Frappe 引擎最本质的工程差异。
  • 只读与权限gantt.config.readonly = !Utils.currentUserCan('write', board),同样以看板写权限控制拖拽。
  • 任务数据cardsToTasks()按 dhtmlx 的任务结构输出{id, text, start_date, duration, progress}duration以整天计(Math.max(1, Math.round((end - start) / 86400000)));同样以 Start→Due(或 Received/End 回退)确定条形区间,并用_startField/_endField记录边缘对应字段。逾期任务条形渲染为#e74c3c红色。
  • 拖拽回写:监听onAfterTaskDrag事件(而非更通用的onAfterTaskUpdate,避免在响应式重渲染触发的gantt.parse()时误写回),仅在真实拖拽/缩放时按_startField/_endField调用card.setReceived/card.setStart/card.setEnd/card.setDue之一写回数据库。
  • 交互收敛:双击弹出的编辑灯箱(lightbox)被onBeforeLightbox => false禁用——卡片标题等字段的编辑统一在卡片详情页完成,图表内仅保留「点击开卡、悬停看详情、拖拽改期」三种交互。
  • 本地化gantt.i18n.setLocale(dhtmlxLocaleFor(gantt, TAPi18n.getLanguage()))在存在内置语言包时使用 dhtmlx 自带 locale,否则用 Intl 动态生成日期名称;同时date_format设为'%Y-%m-%d %H:%i'。对于非公历日历系统(如希伯来历、伊斯兰历),还会整体替换时间刻度与日期模板为formatDateForDisplay的显示格式(见dateDisplayPreferences().calendarSystem !== 'gregorian'分支)。
  • 悬停提示tooltip_text模板把全部四个日期逐行展示在悬停提示框中。

五、使用 Frappe / dhtmlx Gantt 的步骤

  1. 打开一个看板,确保卡片设置了 Start/Due(或 Received/End)日期;
  2. 点击顶部工具栏Board ViewGantt (Frappe)Gantt (dhtmlx)
  3. 点击卡片的条形图打开对应卡片;
  4. 拥有看板写入权限时,拖动条形图边缘即可直接修改该卡片日期,改动会写回条形边缘所代表的那个日期字段;
  5. 无写入权限时图表仍可浏览,只是不可拖拽。

六、前置条件与权限边界

  • 卡片至少需要设置 Received/Start/Due/End 中的一个日期,才会出现在三种 Gantt 视图之一;四个日期都没有的卡片会被跳过(cards.filter(c => c.receivedAt || c.startAt || c.dueAt || c.endAt),见 gantt.js)。
  • 拖拽改期(仅 Frappe/dhtmlx 两个引擎支持)要求看板写入权限;内置表格视图则始终通过点击日期图标弹窗改期。
  • 无论哪种视图,日期变更都会落回卡片的真实字段,与其他视图(如日历视图的拖拽编辑)共享同一套写回方法。

七、数据模型与写回原理

Gantt 依赖的四个日期字段定义于 models/cards.js(receivedAt等),对应的读写方法同样集中在该文件中:

  • setReceived(receivedAt)— models/cards.js
  • setStart(startAt)— models/cards.js
  • setDue(dueAt)— models/cards.js
  • setEnd(endAt)— models/cards.js

从源码看,这些方法都先通过getRealId()解析真实文档 ID(兼容「关联卡片/关联看板」的链接场景),再以Cards.updateAsyncBoards.updateAsync写回$set。Frappe 与 dhtmlx 引擎的拖拽回写、日历视图的拖拽编辑(见 boardBody.js 的eventDrop/eventResize)最终都汇入这四个方法,保证了不同视图间日期语义的一致性。此外,每次修改截止日期都会产生一条a-dueAt类型的活动记录,getDueDateChangeCount()(models/cards.js)据此统计截止日期被改动的次数,用于卡片详情中展示截止日期变更的责任归属。

八、旧版 GPL Gantt 的弃用与 UCS 平台

早先基于 GPLv2 的 WeKan Gantt(wekan-gantt-gpl)现已弃用,不再作为推荐方案使用;新的 MIT 协议 Gantt 功能从 2025-12-22 起随 MIT WeKan 主仓库发布。在 UCS(Univention Corporate Server)容器化部署场景下,原文档也标注了 Gantt 功能的对应章节入口,仓库中提供了一份 UCS 环境运行 WeKan 的演示视频(docs/Platforms/FOSS/Container/UCS/UCS-4.4-with-wekan-10.200.2.25.webm)可供参考。

九、源码索引

  • 入口装配:client/features/gantt.js
  • 内置表格视图模板:gantt.jade、样式 gantt.css
  • 卡片详情弹层:ganttCard.jade、ganttCard.js
  • Frappe 引擎:frappeGantt.js、frappeGantt.jade
  • dhtmlx 引擎:dhtmlxGantt.js、dhtmlxGantt.jade
  • 本地化映射:ganttLocale.js
  • 日期字段模型:models/cards.js

总结来说,WeKan 的三套 Gantt 视图共享同一数据模型,只是交互层次不同:内置表格视图适合快速浏览与逐项点选日期,Frappe 引擎适合轻量拖拽排期(零依赖、按需加载),dhtmlx 引擎则提供最接近桌面项目管理软件的「网格+时间轴」体验。理解四日期字段与写回方法,是进一步定制或二次开发这些视图的起点。

【免费下载链接】wekanThe Open Source kanban, built with Meteor. GitHub issues/PRs are only for FLOSS Developers, not for support, support is at https://wekan.fi/commercial-support/ . PR source translation to imports/i18n/data/en.i18n.json, other translations at https://app.transifex.com/wekan/wekan项目地址: https://gitcode.com/GitHub_Trending/we/wekan

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询