@wimi/gantt 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,785 @@
1
+ # @wimi/gantt
2
+
3
+ 基于 SVG + DOM `foreignObject` 的通用排产甘特图组件,不绑定 Vue/React 等框架。组件只接收一个 DOM 容器和一份 `options`,通过 `setOptions` 增量更新。
4
+
5
+ ## 安装和引入
6
+
7
+ ```bash
8
+ npm install @wimi/gantt
9
+ ```
10
+
11
+ ```js
12
+ import { VanillaGantt } from '@wimi/gantt'
13
+ import '@wimi/gantt/dist/VanillaGantt.css'
14
+ ```
15
+
16
+ 浏览器直接引入 UMD:
17
+
18
+ ```html
19
+ <link rel="stylesheet" href="./dist/VanillaGantt.css" />
20
+ <script src="./dist/VanillaGantt.umd.js"></script>
21
+ <script>
22
+ const { VanillaGantt } = window.VanillaGantt
23
+ </script>
24
+ ```
25
+
26
+ ## 最小示例
27
+
28
+ ```html
29
+ <div id="gantt" style="height: 420px"></div>
30
+ ```
31
+
32
+ ```js
33
+ const gantt = new VanillaGantt(document.querySelector('#gantt'), {
34
+ minDate: '2026-03-30T00:00:00',
35
+ maxDate: '2026-03-31T00:00:00',
36
+ records: [
37
+ {
38
+ id: 'unit-1',
39
+ name: '机组 1',
40
+ tasks: [
41
+ {
42
+ id: 'task-1',
43
+ title: '计划工单',
44
+ subtitle: 'A001',
45
+ startDate: '2026-03-30T08:00:00',
46
+ endDate: '2026-03-30T14:00:00',
47
+ progress: 40
48
+ }
49
+ ]
50
+ }
51
+ ],
52
+ taskListTable: {
53
+ columns: [
54
+ { field: 'name', title: '资源', width: 160, tree: true }
55
+ ]
56
+ },
57
+ timelineHeader: {
58
+ scales: [
59
+ { unit: 'day', step: 1, rowHeight: 24 },
60
+ { unit: 'hour', step: 2, colWidth: 56, rowHeight: 24 }
61
+ ]
62
+ }
63
+ })
64
+ ```
65
+
66
+ ## 表格列和自定义单元格
67
+
68
+ 左侧表格通过 `taskListTable.columns` 配置,可多列显示,每列支持独立宽度、最小宽度、表头对齐、单元格对齐、列宽拖拽、表头模板和单元格模板。
69
+
70
+ ```js
71
+ const options = {
72
+ taskListTable: {
73
+ tableWidth: 'auto',
74
+ minTableWidth: 180,
75
+ maxTableWidth: 720,
76
+ columnResizable: true,
77
+ headerStyle: {
78
+ backgroundColor: '#eaf3f8',
79
+ color: '#2f4858',
80
+ fontWeight: '700'
81
+ },
82
+ columns: [
83
+ { field: 'name', title: '资源', width: 160, minWidth: 120, tree: true },
84
+ {
85
+ field: 'stage',
86
+ title: '工序',
87
+ width: 90,
88
+ renderHeader: () => '<strong>工序</strong>'
89
+ },
90
+ {
91
+ title: '任务数',
92
+ width: 72,
93
+ align: 'right',
94
+ headerAlign: 'right',
95
+ valueGetter: ({ row }) => (row.tasks || []).length
96
+ },
97
+ {
98
+ title: '状态',
99
+ width: 96,
100
+ headerStyle: { backgroundColor: '#dcecf4' },
101
+ valueGetter: ({ row }) => row.status || '',
102
+ renderCell: ({ value }) => `<span class="status-pill">${value || '-'}</span>`
103
+ }
104
+ ]
105
+ }
106
+ }
107
+ ```
108
+
109
+ 树节点默认使用第一列 `field: 'name'` 作为树列,也可以显式设置 `tree: true`。如果自定义树单元格,需要给展开按钮加 `data-vg-toggle`:
110
+
111
+ ```js
112
+ {
113
+ field: 'name',
114
+ title: '资源',
115
+ tree: true,
116
+ renderCell: ({ row, expanded }) => `
117
+ <div class="resource-cell" style="padding-left:${row.level * 16}px">
118
+ ${row.children ? `<button data-vg-toggle>${expanded ? '收起' : '展开'}</button>` : ''}
119
+ <span>${row.name}</span>
120
+ </div>
121
+ `
122
+ }
123
+ ```
124
+
125
+ ## 时间轴刻度
126
+
127
+ 时间轴由 `timelineHeader.scales` 决定,数组里每一项就是一行刻度。比如 15 分钟刻度只显示两行:天 + 15 分钟。
128
+
129
+ ```js
130
+ function formatMinute({ startDate }) {
131
+ const hour = String(startDate.getHours()).padStart(2, '0')
132
+ const minute = String(startDate.getMinutes()).padStart(2, '0')
133
+ return `${hour}:${minute}`
134
+ }
135
+
136
+ const options = {
137
+ timelineHeader: {
138
+ backgroundColor: '#f7fbfd',
139
+ style: {
140
+ backgroundColor: '#f7fbfd',
141
+ color: '#53666f'
142
+ },
143
+ scales: [
144
+ {
145
+ unit: 'day',
146
+ step: 1,
147
+ rowHeight: 24,
148
+ style: { backgroundColor: '#eaf3f8', fontWeight: '700' }
149
+ },
150
+ {
151
+ unit: 'minute',
152
+ step: 15,
153
+ colWidth: 48,
154
+ rowHeight: 24,
155
+ format: formatMinute
156
+ }
157
+ ]
158
+ }
159
+ }
160
+ ```
161
+
162
+ 可配置的 `unit`:`minute`、`hour`、`day`、`week`、`month`、`year`。
163
+
164
+ ```js
165
+ const scalePresets = {
166
+ hour: [
167
+ { unit: 'day', step: 1, rowHeight: 24 },
168
+ { unit: 'hour', step: 1, colWidth: 44, rowHeight: 24 }
169
+ ],
170
+ week: [
171
+ { unit: 'month', step: 1, rowHeight: 24 },
172
+ { unit: 'week', step: 1, colWidth: 180, rowHeight: 24 }
173
+ ],
174
+ month: [
175
+ { unit: 'year', step: 1, rowHeight: 24 },
176
+ { unit: 'month', step: 1, colWidth: 220, rowHeight: 24 }
177
+ ]
178
+ }
179
+
180
+ gantt.setOptions({
181
+ timelineHeader: {
182
+ scales: scalePresets.week
183
+ }
184
+ })
185
+ ```
186
+
187
+ 自定义刻度内容:
188
+
189
+ ```js
190
+ timelineHeader: {
191
+ customLayout: ({ unit, major }) => `
192
+ <div class="timeline-cell ${major ? 'is-major' : ''}">
193
+ ${unit.label}
194
+ </div>
195
+ `,
196
+ scales: [
197
+ { unit: 'day', step: 1 },
198
+ { unit: 'hour', step: 2, colWidth: 64 }
199
+ ]
200
+ }
201
+ ```
202
+
203
+ ## 多行任务和自定义 TaskBar
204
+
205
+ 一个资源行里可以放多条任务。通过 `taskBar.lanes` 定义泳道位置,任务记录上通过 `lane` 选择泳道。下面示例中第一行是计划任务,第二行是上料任务,第三行是下料任务。
206
+
207
+ ```js
208
+ const options = {
209
+ rowHeight: 92,
210
+ taskBar: {
211
+ startDateField: 'startDate',
212
+ endDateField: 'endDate',
213
+ progressField: 'progress',
214
+ laneField: 'lane',
215
+ statusField: 'status',
216
+ labelText: 'title',
217
+ subLabelText: 'subtitle',
218
+ lanes: [
219
+ { key: 'plan', offset: 8, height: 58 },
220
+ { key: 'load', offset: 70, height: 6 },
221
+ { key: 'unload', offset: 82, height: 6 }
222
+ ],
223
+ customLayout: ({ task, progress }) => {
224
+ if (task.logistics) {
225
+ return `<div class="logistics logistics-${task.status}"></div>`
226
+ }
227
+ return `
228
+ <div class="work-task work-task-${task.status || 'normal'}">
229
+ <strong>${task.title || ''}</strong>
230
+ <span>${task.subtitle || ''}</span>
231
+ ${progress !== undefined ? `<i style="width:${progress}%"></i>` : ''}
232
+ </div>
233
+ `
234
+ }
235
+ },
236
+ records: [
237
+ {
238
+ id: 'unit-1',
239
+ name: '热处理炉 1',
240
+ height: 92,
241
+ tasks: [
242
+ { id: 'wo-1', lane: 'plan', title: '计划工单', subtitle: 'A001', startDate: '2026-03-30T08:00', endDate: '2026-03-30T18:00', progress: 60 },
243
+ { id: 'wo-1-load', lane: 'load', title: '上料', startDate: '2026-03-30T06:30', endDate: '2026-03-30T08:00', status: 'load-running', logistics: true },
244
+ { id: 'wo-1-unload', lane: 'unload', title: '下料', startDate: '2026-03-30T18:00', endDate: '2026-03-30T19:00', status: 'unload-waiting', logistics: true }
245
+ ]
246
+ }
247
+ ]
248
+ }
249
+ ```
250
+
251
+ 自定义模板中如果存在按钮、下拉等控件,并且不希望触发任务拖拽,给元素加 `data-vg-no-drag`。
252
+
253
+ ```js
254
+ customLayout: ({ task }) => `
255
+ <div class="work-task">
256
+ <span>${task.title}</span>
257
+ <button data-vg-no-drag>操作</button>
258
+ </div>
259
+ `
260
+ ```
261
+
262
+ ## 任务交互、拖拽和 Tooltip
263
+
264
+ Tooltip 默认关闭。需要显示时配置 `taskBar.tooltip: true` 或传入对象。
265
+
266
+ ```js
267
+ const options = {
268
+ taskBar: {
269
+ draggable: ({ task }) => !task.locked && !task.logistics,
270
+ dragStep: 15 * 60 * 1000,
271
+ tooltip: {
272
+ visible: true,
273
+ offsetX: 12,
274
+ offsetY: 12,
275
+ customLayout: ({ task, startDate, endDate }) => `
276
+ <div class="task-tooltip">
277
+ <strong>${task.title}</strong>
278
+ <span>${startDate.toLocaleString()} - ${endDate.toLocaleString()}</span>
279
+ </div>
280
+ `
281
+ },
282
+ onClick: ({ task, row }) => {
283
+ console.log('click', task, row)
284
+ },
285
+ onContextMenu: ({ task, event }) => {
286
+ console.log('context menu', task, event.clientX, event.clientY)
287
+ },
288
+ onMouseEnter: ({ task }) => {
289
+ console.log('mouseenter', task.id)
290
+ },
291
+ onMouseLeave: ({ task }) => {
292
+ console.log('mouseleave', task.id)
293
+ },
294
+ onDragStart: ({ task }) => {
295
+ if (task.locked) return false
296
+ },
297
+ onDrag: ({ sourceTask, startDate, endDate }) => {
298
+ console.log('dragging', sourceTask.id, startDate, endDate)
299
+ },
300
+ onDragEnd: ({ sourceTask, startDate, endDate }) => {
301
+ console.log('drag end', sourceTask.id, startDate, endDate)
302
+ }
303
+ }
304
+ }
305
+ ```
306
+
307
+ 拖拽结束后组件会把源任务的开始和结束字段写回原始任务对象,字段名由 `startDateField`、`endDateField` 决定。
308
+
309
+ ## 树形数据和父节点任务条
310
+
311
+ `records` 支持 `children`。父节点未展开时,组件会基于所有子节点的主任务自动生成一条聚合任务条;父节点展开后,只显示子节点任务,父节点自己的聚合任务条不显示。
312
+
313
+ ```js
314
+ const options = {
315
+ records: [
316
+ {
317
+ id: 'heat',
318
+ name: '热处理',
319
+ expanded: false,
320
+ children: [
321
+ {
322
+ id: 'heat-1',
323
+ name: '热处理炉 1',
324
+ tasks: [
325
+ { id: 'wo-1', lane: 'plan', title: '工单 1', startDate: '2026-03-30T08:00', endDate: '2026-03-30T12:00' }
326
+ ]
327
+ },
328
+ {
329
+ id: 'heat-2',
330
+ name: '热处理炉 2',
331
+ tasks: [
332
+ { id: 'wo-2', lane: 'plan', title: '工单 2', startDate: '2026-03-30T13:00', endDate: '2026-03-30T18:00' }
333
+ ]
334
+ }
335
+ ]
336
+ }
337
+ ],
338
+ taskBar: {
339
+ projectStyle: {
340
+ barColor: '#dff4f1',
341
+ borderColor: '#8bc7c0',
342
+ cornerRadius: 2
343
+ }
344
+ }
345
+ }
346
+ ```
347
+
348
+ ## 背景区间、休息时间和标记线
349
+
350
+ `grid.backgroundRanges` 用于整张时间区域背景,比如休息、停机、冻结区;支持精确到分钟。`grid.rowBackgroundRanges` 用于某个资源行的局部背景。
351
+
352
+ ```js
353
+ const options = {
354
+ grid: {
355
+ backgroundColor: '#f7fbfb',
356
+ alternatingBackgroundColor: '#f8fbfb',
357
+ verticalLine: { lineColor: '#e8eeee' },
358
+ horizontalLine: { lineColor: '#edf1f2' },
359
+ backgroundRanges: [
360
+ {
361
+ id: 'rest-1',
362
+ startDate: '2026-03-30T12:00:00',
363
+ endDate: '2026-03-30T13:30:00',
364
+ fill: '#f2f3f3',
365
+ opacity: 1
366
+ },
367
+ {
368
+ id: 'stop-1',
369
+ startDate: '2026-03-30T19:00:00',
370
+ endDate: '2026-03-31T05:00:00',
371
+ fill: '#fdeeee',
372
+ opacity: 1
373
+ }
374
+ ],
375
+ rowBackgroundRanges: [
376
+ {
377
+ id: 'shift-unit-1',
378
+ recordKey: 'unit-1',
379
+ startDate: '2026-03-30T08:00:00',
380
+ endDate: '2026-03-30T20:00:00',
381
+ fill: '#ffe2a8',
382
+ opacity: 0.8,
383
+ offsetY: 8,
384
+ height: 32
385
+ }
386
+ ]
387
+ },
388
+ markLine: [
389
+ {
390
+ date: '2026-03-30T12:00:00',
391
+ content: '当前时间',
392
+ style: { lineColor: '#35cce0', lineDash: [4, 4] },
393
+ contentStyle: { color: '#1599aa' }
394
+ }
395
+ ]
396
+ }
397
+ ```
398
+
399
+ ## 依赖连接线
400
+
401
+ 通过 `dependency.links` 定义任务依赖。`from`、`to` 都可以是单个 id 或 id 数组。默认 `showLinks: false`,即不常驻显示连接线;点击某个任务后只显示它所在的一组连接关系。点击空白区域会取消连接状态。
402
+
403
+ ```js
404
+ const options = {
405
+ dependency: {
406
+ showLinks: false,
407
+ highlightConnected: true,
408
+ dimOpacity: 0.18,
409
+ linkLineStyle: {
410
+ lineColor: '#168dff',
411
+ lineWidth: 2
412
+ },
413
+ links: [
414
+ {
415
+ id: 'flow-1',
416
+ type: 'finish_to_start',
417
+ from: 'wo-1',
418
+ to: ['wo-2', 'wo-3'],
419
+ color: '#168dff'
420
+ },
421
+ {
422
+ id: 'flow-2',
423
+ type: 'start_to_start',
424
+ from: 'wo-4',
425
+ to: 'wo-5',
426
+ dashed: true,
427
+ linkLineStyle: { lineColor: '#40c51b' }
428
+ }
429
+ ]
430
+ }
431
+ }
432
+ ```
433
+
434
+ 支持的连接类型:
435
+
436
+ | type | 说明 |
437
+ | --- | --- |
438
+ | `finish_to_start` | 从前置任务结束连到后置任务开始 |
439
+ | `start_to_start` | 从前置任务开始连到后置任务开始 |
440
+ | `finish_to_finish` | 从前置任务结束连到后置任务结束 |
441
+ | `start_to_finish` | 从前置任务开始连到后置任务结束 |
442
+
443
+ ## 动态更新
444
+
445
+ ```js
446
+ gantt.setOptions({
447
+ minDate: '2026-03-30T00:00:00',
448
+ maxDate: '2026-04-02T00:00:00',
449
+ timelineHeader: {
450
+ scales: [
451
+ { unit: 'day', step: 1, rowHeight: 24 },
452
+ { unit: 'hour', step: 4, colWidth: 72, rowHeight: 24 }
453
+ ]
454
+ }
455
+ })
456
+ ```
457
+
458
+ 销毁:
459
+
460
+ ```js
461
+ gantt.destroy()
462
+ ```
463
+
464
+ ## 完整 options 说明
465
+
466
+ ### 顶层配置
467
+
468
+ | 字段 | 类型 | 默认值 | 说明 |
469
+ | --- | --- | --- | --- |
470
+ | `records` | `GanttRecord[]` | `[]` | 左侧表格和任务数据 |
471
+ | `recordKeyField` | `string` | `'id'` | 资源行唯一 key 字段 |
472
+ | `taskKeyField` | `string` | `'id'` | 任务唯一 key 字段 |
473
+ | `minDate` | `string \| number \| Date` | 自动取最小任务开始时间 | 时间轴开始时间 |
474
+ | `maxDate` | `string \| number \| Date` | 自动取最大任务结束时间 | 时间轴结束时间 |
475
+ | `rowHeight` | `number` | `78` | 默认行高 |
476
+ | `taskHeight` | `number` | `36` | 默认任务条高度 |
477
+ | `headerRowHeight` | `number` | `24` | 默认时间刻度行高 |
478
+ | `taskListTable` | `GanttTaskListTableOptions` | 见下方 | 左侧表格配置 |
479
+ | `timelineHeader` | `GanttTimelineHeaderOptions` | 见下方 | 顶部时间轴配置 |
480
+ | `taskBar` | `GanttTaskBarOptions` | 见下方 | 任务条配置 |
481
+ | `dependency` | `GanttDependencyOptions` | 见下方 | 依赖连接线配置 |
482
+ | `grid` | `GanttGridOptions` | 见下方 | 时间区网格和背景配置 |
483
+ | `markLine` | `GanttMarkLine \| GanttMarkLine[] \| null` | `null` | 标记线 |
484
+ | `onScroll` | `(payload) => void` | `null` | 滚动回调,返回 `scrollLeft`、`scrollTop` |
485
+
486
+ ### records
487
+
488
+ ```ts
489
+ interface GanttRecord {
490
+ id?: string | number
491
+ name?: string
492
+ type?: string
493
+ height?: number
494
+ load?: number
495
+ expanded?: boolean
496
+ children?: GanttRecord[]
497
+ tasks?: GanttTaskRecord[]
498
+ [key: string]: unknown
499
+ }
500
+ ```
501
+
502
+ `children` 表示树形子节点;`expanded` 控制初始展开状态;`height` 可单独覆盖当前行行高。
503
+
504
+ ### tasks
505
+
506
+ ```ts
507
+ interface GanttTaskRecord {
508
+ id?: string | number
509
+ title?: string
510
+ subtitle?: string
511
+ startDate?: string | number | Date
512
+ endDate?: string | number | Date
513
+ lane?: string
514
+ status?: string
515
+ height?: number
516
+ offsetY?: number
517
+ progress?: number
518
+ locked?: boolean
519
+ striped?: boolean
520
+ draggable?: boolean
521
+ [key: string]: unknown
522
+ }
523
+ ```
524
+
525
+ 任务字段名可以通过 `taskBar.startDateField`、`taskBar.endDateField`、`taskBar.progressField`、`taskBar.laneField`、`taskBar.statusField` 改掉。
526
+
527
+ ### taskListTable
528
+
529
+ | 字段 | 类型 | 默认值 | 说明 |
530
+ | --- | --- | --- | --- |
531
+ | `tableWidth` | `'auto' \| number` | `'auto'` | 左侧表格宽度,`auto` 时按列宽求和 |
532
+ | `minTableWidth` | `number` | `120` | 左侧表格整体最小宽度 |
533
+ | `maxTableWidth` | `number` | `640` | 左侧表格整体最大宽度 |
534
+ | `columnResizable` | `boolean` | `true` | 是否允许拖拽调整列宽 |
535
+ | `headerStyle` | `GanttTableHeaderStyle` | `null` | 表头整体样式 |
536
+ | `columns` | `GanttTableColumn[]` | `[{ field: 'name', title: '工位', width: 170, tree: true }]` | 表格列 |
537
+ | `renderHeader` | `renderer` | `null` | 整体表头自定义渲染 |
538
+ | `renderCell` | `renderer` | `null` | 全局单元格自定义渲染 |
539
+
540
+ `GanttTableColumn`:
541
+
542
+ | 字段 | 说明 |
543
+ | --- | --- |
544
+ | `field` | 从 row 上读取的字段名 |
545
+ | `title` | 表头文本 |
546
+ | `width` / `minWidth` / `maxWidth` | 列宽配置 |
547
+ | `resizable` | 当前列是否允许拖拽列宽 |
548
+ | `align` | 单元格水平对齐:`left`、`center`、`right`、`start`、`end` |
549
+ | `headerAlign` | 表头水平对齐 |
550
+ | `headerStyle` | 当前列表头样式,会覆盖 `taskListTable.headerStyle` |
551
+ | `tree` | 是否作为树列 |
552
+ | `className` | 追加到表头和单元格的 class |
553
+ | `valueGetter` | 自定义单元格值 |
554
+ | `renderHeader` | 当前列表头模板 |
555
+ | `renderCell` | 当前列单元格模板 |
556
+
557
+ ### timelineHeader
558
+
559
+ | 字段 | 类型 | 默认值 | 说明 |
560
+ | --- | --- | --- | --- |
561
+ | `backgroundColor` | `string` | `'#fff'` | 时间轴区域背景 |
562
+ | `colWidth` | `number` | `56` | 默认基础刻度列宽 |
563
+ | `style` | `GanttTimelineHeaderStyle` | `null` | 时间刻度默认样式 |
564
+ | `scales` | `GanttTimelineScale[]` | 天 + 2 小时 | 时间轴刻度行 |
565
+ | `customLayout` | `renderer` | `null` | 全局刻度自定义模板 |
566
+
567
+ `GanttTimelineScale`:
568
+
569
+ | 字段 | 说明 |
570
+ | --- | --- |
571
+ | `unit` | `minute`、`hour`、`day`、`week`、`month`、`year` |
572
+ | `step` | 刻度步长,例如 `minute + 15` 表示 15 分钟 |
573
+ | `colWidth` | 当前刻度作为基础刻度时的列宽 |
574
+ | `rowHeight` | 当前刻度行高 |
575
+ | `startOfWeek` | 周起始:`monday` 或 `sunday` |
576
+ | `visible` | 是否显示当前刻度行 |
577
+ | `style` | 当前刻度行样式 |
578
+ | `format` | 自定义刻度文案,参数为完整 `dateInfo` |
579
+ | `customLayout` | 当前刻度行自定义模板 |
580
+
581
+ `format` 参数:
582
+
583
+ ```ts
584
+ {
585
+ unit: 'minute' | 'hour' | 'day' | 'week' | 'month' | 'year'
586
+ step: number
587
+ scaleIndex: number
588
+ dateIndex: number
589
+ label: string
590
+ title: string
591
+ startDate: Date
592
+ endDate: Date
593
+ days: number
594
+ x: number
595
+ width: number
596
+ }
597
+ ```
598
+
599
+ ### taskBar
600
+
601
+ | 字段 | 默认值 | 说明 |
602
+ | --- | --- | --- |
603
+ | `tasksField` | `'tasks'` | row 上任务数组字段 |
604
+ | `startDateField` | `'startDate'` | 任务开始时间字段 |
605
+ | `endDateField` | `'endDate'` | 任务结束时间字段 |
606
+ | `progressField` | `'progress'` | 进度字段 |
607
+ | `laneField` | `'lane'` | 泳道字段 |
608
+ | `statusField` | `'status'` | 状态字段 |
609
+ | `labelText` | `'title'` | 默认任务标题字段或函数 |
610
+ | `subLabelText` | `'subtitle'` | 默认任务副标题字段或函数 |
611
+ | `barStyle` | `null` | 普通任务默认样式,支持函数 |
612
+ | `projectStyle` | `null` | 父节点聚合任务样式,支持函数 |
613
+ | `customLayout` | `null` | 任务条自定义模板 |
614
+ | `clip` | `true` | 是否裁剪超出时间范围的任务 |
615
+ | `draggable` | `true` | 是否允许拖拽,支持函数 |
616
+ | `dragStep` | `60000` | 拖拽吸附步长,单位毫秒 |
617
+ | `tooltip` | `false` | Tooltip 配置,默认关闭 |
618
+ | `lanes` | `plan/load/unload` | 行内多任务泳道 |
619
+ | `onClick` | `null` | 任务点击 |
620
+ | `onContextMenu` | `null` | 任务右键 |
621
+ | `onMouseEnter` | `null` | 鼠标移入 |
622
+ | `onMouseLeave` | `null` | 鼠标移出 |
623
+ | `onDragStart` | `null` | 拖拽开始,返回 `false` 可阻止拖拽 |
624
+ | `onDrag` | `null` | 拖拽中 |
625
+ | `onDragEnd` | `null` | 拖拽结束 |
626
+
627
+ `barStyle` / `projectStyle` 支持:
628
+
629
+ ```ts
630
+ {
631
+ barColor?: string
632
+ completedBarColor?: string
633
+ width?: number
634
+ cornerRadius?: number
635
+ borderWidth?: number
636
+ borderLineWidth?: number
637
+ borderColor?: string
638
+ minSize?: number
639
+ }
640
+ ```
641
+
642
+ ### tooltip
643
+
644
+ ```js
645
+ taskBar: {
646
+ tooltip: false
647
+ }
648
+ ```
649
+
650
+ ```js
651
+ taskBar: {
652
+ tooltip: true
653
+ }
654
+ ```
655
+
656
+ ```js
657
+ taskBar: {
658
+ tooltip: {
659
+ visible: true,
660
+ className: 'my-tooltip',
661
+ offsetX: 12,
662
+ offsetY: 12,
663
+ customLayout: ({ task, startDate, endDate }) => `
664
+ <div>${task.title} ${startDate.toLocaleString()} - ${endDate.toLocaleString()}</div>
665
+ `
666
+ }
667
+ }
668
+ ```
669
+
670
+ ### dependency
671
+
672
+ | 字段 | 默认值 | 说明 |
673
+ | --- | --- | --- |
674
+ | `links` | `[]` | 依赖连接线数组 |
675
+ | `linkLineStyle` | `null` | 全局连接线样式 |
676
+ | `showLinks` | `false` | 是否默认显示全部连接线 |
677
+ | `highlightConnected` | `false` | 显示连接线时是否弱化无关任务 |
678
+ | `dimOpacity` | `0.18` | 无关任务透明度 |
679
+ | `linkCreatable` | `undefined` | 预留字段 |
680
+ | `linkSelectable` | `undefined` | 预留字段 |
681
+ | `linkDeletable` | `undefined` | 预留字段 |
682
+
683
+ `links`:
684
+
685
+ ```ts
686
+ {
687
+ id?: string | number
688
+ type?: 'finish_to_start' | 'start_to_start' | 'finish_to_finish' | 'start_to_finish'
689
+ from: string | number | Array<string | number>
690
+ to: string | number | Array<string | number>
691
+ color?: string
692
+ dashed?: boolean
693
+ linkLineStyle?: {
694
+ lineColor?: string
695
+ lineWidth?: number
696
+ lineDash?: number[]
697
+ }
698
+ }
699
+ ```
700
+
701
+ ### grid
702
+
703
+ | 字段 | 默认值 | 说明 |
704
+ | --- | --- | --- |
705
+ | `backgroundColor` | `'#f7fbfb'` | 时间区底色 |
706
+ | `alternatingBackgroundColor` | `'#f8fbfb'` | 按第一层刻度隔列背景 |
707
+ | `verticalLine` | `{ lineColor: '#e8eeee' }` | 竖向网格线,支持函数 |
708
+ | `horizontalLine` | `{ lineColor: '#edf1f2' }` | 横向网格线,支持函数 |
709
+ | `backgroundRanges` | `[]` | 全局时间背景区间 |
710
+ | `rowBackgroundRanges` | `[]` | 指定行背景区间 |
711
+
712
+ `backgroundRanges`:
713
+
714
+ ```ts
715
+ {
716
+ id?: string | number
717
+ startDate: string | number | Date
718
+ endDate: string | number | Date
719
+ color?: string
720
+ fill?: string
721
+ opacity?: number
722
+ }
723
+ ```
724
+
725
+ `rowBackgroundRanges` 额外支持:
726
+
727
+ ```ts
728
+ {
729
+ recordKey: string | number
730
+ height?: number
731
+ offsetY?: number
732
+ }
733
+ ```
734
+
735
+ ### markLine
736
+
737
+ ```ts
738
+ {
739
+ date: string | number | Date
740
+ content?: string
741
+ contentStyle?: {
742
+ color?: string
743
+ fontSize?: number
744
+ fontWeight?: string
745
+ }
746
+ style?: {
747
+ lineColor?: string
748
+ lineWidth?: number
749
+ lineDash?: number[]
750
+ }
751
+ position?: 'left' | 'right' | 'middle' | 'date'
752
+ scrollToMarkLine?: boolean
753
+ }
754
+ ```
755
+
756
+ ### renderer 返回值
757
+
758
+ 所有 `customLayout`、`renderHeader`、`renderCell` 都支持返回:
759
+
760
+ ```ts
761
+ Node | string | { rootContainer?: Node } | null | undefined
762
+ ```
763
+
764
+ 推荐使用模板字符串。组件内部会把字符串放入 `<template>` 解析成 DOM。
765
+
766
+ ## 本项目内 demo
767
+
768
+ 当前项目只保留一个业务 demo:
769
+
770
+ ```text
771
+ src/views/WorkOrderStatusDemo.vue
772
+ ```
773
+
774
+ 路由:
775
+
776
+ ```text
777
+ #/work-order-status
778
+ ```
779
+
780
+ ## 构建
781
+
782
+ ```bash
783
+ npm --prefix packages/gantt run build
784
+ ```
785
+