jordium-gantt-vue3 1.4.3 → 1.4.4
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 +2176 -2079
- package/dist/assets/jordium-gantt-vue3.css +1 -1
- package/dist/jordium-gantt-vue3-styles.js +1 -1
- package/dist/jordium-gantt-vue3.cjs.js +58 -58
- package/dist/jordium-gantt-vue3.es.js +9515 -9388
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,2079 +1,2176 @@
|
|
|
1
|
-
# <img src="public/assets/jordium-gantt-vue3-logo.svg" alt="jordium-gantt-vue3 logo" width="32" style="vertical-align:middle;margin-right:8px;" /> jordium-gantt-vue3
|
|
2
|
-
|
|
3
|
-
<p align="center">
|
|
4
|
-
<a href="https://www.npmjs.com/package/jordium-gantt-vue3">
|
|
5
|
-
<img src="https://img.shields.io/npm/v/jordium-gantt-vue3?style=flat-square" alt="npm version">
|
|
6
|
-
</a>
|
|
7
|
-
<a href="https://www.npmjs.com/package/jordium-gantt-vue3">
|
|
8
|
-
<img src="https://img.shields.io/npm/dt/jordium-gantt-vue3?style=flat-square" alt="npm total">
|
|
9
|
-
</a>
|
|
10
|
-
<a href="https://opensource.org/licenses/MIT">
|
|
11
|
-
<img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT License">
|
|
12
|
-
</a>
|
|
13
|
-
<a href="https://vuejs.org/">
|
|
14
|
-
<img src="https://img.shields.io/badge/Vue.js->=3.5.13-4FC08D?style=flat-square&logo=vue.js&logoColor=white" alt="Vue.js">
|
|
15
|
-
</a>
|
|
16
|
-
<a href="https://www.typescriptlang.org/">
|
|
17
|
-
<img src="https://img.shields.io/badge/TypeScript->=5.8.3-3178C6?style=flat-square&logo=typescript&logoColor=white" alt="TypeScript">
|
|
18
|
-
</a>
|
|
19
|
-
<a href="https://nodejs.org/">
|
|
20
|
-
<img src="https://img.shields.io/badge/Node.js->=16.0.0-339933?style=flat-square&logo=node.js&logoColor=white" alt="Nodejs">
|
|
21
|
-
</a>
|
|
22
|
-
</p>
|
|
23
|
-
|
|
24
|
-
<p align="center">
|
|
25
|
-
<a href="./README.md">中文</a> |
|
|
26
|
-
<a href="./README-EN.md">English</a>
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
</
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
-
|
|
55
|
-
-
|
|
56
|
-
-
|
|
57
|
-
-
|
|
58
|
-
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
npm
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
yarn
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
pnpm
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
import 'jordium-gantt-vue3
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
</
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
import {
|
|
118
|
-
import 'jordium-gantt-vue3
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
npm
|
|
167
|
-
npm install
|
|
168
|
-
npm
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
|
183
|
-
|
|
|
184
|
-
| `
|
|
185
|
-
| `
|
|
186
|
-
| `
|
|
187
|
-
| `
|
|
188
|
-
| `
|
|
189
|
-
| `
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
|
198
|
-
|
|
|
199
|
-
| `
|
|
200
|
-
| `
|
|
201
|
-
| `
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
|
208
|
-
|
|
|
209
|
-
| `
|
|
210
|
-
| `
|
|
211
|
-
| `
|
|
212
|
-
| `
|
|
213
|
-
| `
|
|
214
|
-
| `
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
|
228
|
-
|
|
|
229
|
-
| `task
|
|
230
|
-
| `task-
|
|
231
|
-
| `task-
|
|
232
|
-
| `task-
|
|
233
|
-
| `
|
|
234
|
-
| `
|
|
235
|
-
| `
|
|
236
|
-
| `
|
|
237
|
-
| `
|
|
238
|
-
| `
|
|
239
|
-
| `
|
|
240
|
-
| `
|
|
241
|
-
| `milestone
|
|
242
|
-
| `milestone-
|
|
243
|
-
| `milestone-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
const
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
|
382
|
-
|
|
|
383
|
-
| `
|
|
384
|
-
| `
|
|
385
|
-
| `
|
|
386
|
-
| `
|
|
387
|
-
| `
|
|
388
|
-
| `
|
|
389
|
-
| `
|
|
390
|
-
| `
|
|
391
|
-
| `
|
|
392
|
-
| `
|
|
393
|
-
| `
|
|
394
|
-
| `
|
|
395
|
-
| `
|
|
396
|
-
| `
|
|
397
|
-
| `
|
|
398
|
-
| `
|
|
399
|
-
| `
|
|
400
|
-
| `
|
|
401
|
-
| `
|
|
402
|
-
| `
|
|
403
|
-
| `
|
|
404
|
-
| `
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
>
|
|
410
|
-
>
|
|
411
|
-
>
|
|
412
|
-
>
|
|
413
|
-
> -
|
|
414
|
-
> -
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
|
422
|
-
|
|
|
423
|
-
| `
|
|
424
|
-
| `
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
|
441
|
-
|
|
|
442
|
-
| `
|
|
443
|
-
| `task-
|
|
444
|
-
| `
|
|
445
|
-
| `
|
|
446
|
-
| `
|
|
447
|
-
| `
|
|
448
|
-
| `
|
|
449
|
-
| `
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
-
|
|
454
|
-
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
@
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
//
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
//
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
//
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
//
|
|
538
|
-
const
|
|
539
|
-
console.log('
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
//
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
-
|
|
641
|
-
-
|
|
642
|
-
-
|
|
643
|
-
-
|
|
644
|
-
-
|
|
645
|
-
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
const
|
|
690
|
-
// 直接触发组件的 add-
|
|
691
|
-
// 由于
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
>
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
-
|
|
747
|
-
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
//
|
|
920
|
-
const
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
}
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
>
|
|
1117
|
-
>
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
}
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
TimelineScale
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
```
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
import {
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
<script setup lang="ts">
|
|
1410
|
-
import
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
</
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
}
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
{
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
```
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
.
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
>
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
>
|
|
1989
|
-
>
|
|
1990
|
-
>
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
</
|
|
1
|
+
# <img src="public/assets/jordium-gantt-vue3-logo.svg" alt="jordium-gantt-vue3 logo" width="32" style="vertical-align:middle;margin-right:8px;" /> jordium-gantt-vue3
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<a href="https://www.npmjs.com/package/jordium-gantt-vue3">
|
|
5
|
+
<img src="https://img.shields.io/npm/v/jordium-gantt-vue3?style=flat-square" alt="npm version">
|
|
6
|
+
</a>
|
|
7
|
+
<a href="https://www.npmjs.com/package/jordium-gantt-vue3">
|
|
8
|
+
<img src="https://img.shields.io/npm/dt/jordium-gantt-vue3?style=flat-square" alt="npm total">
|
|
9
|
+
</a>
|
|
10
|
+
<a href="https://opensource.org/licenses/MIT">
|
|
11
|
+
<img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT License">
|
|
12
|
+
</a>
|
|
13
|
+
<a href="https://vuejs.org/">
|
|
14
|
+
<img src="https://img.shields.io/badge/Vue.js->=3.5.13-4FC08D?style=flat-square&logo=vue.js&logoColor=white" alt="Vue.js">
|
|
15
|
+
</a>
|
|
16
|
+
<a href="https://www.typescriptlang.org/">
|
|
17
|
+
<img src="https://img.shields.io/badge/TypeScript->=5.8.3-3178C6?style=flat-square&logo=typescript&logoColor=white" alt="TypeScript">
|
|
18
|
+
</a>
|
|
19
|
+
<a href="https://nodejs.org/">
|
|
20
|
+
<img src="https://img.shields.io/badge/Node.js->=16.0.0-339933?style=flat-square&logo=node.js&logoColor=white" alt="Nodejs">
|
|
21
|
+
</a>
|
|
22
|
+
</p>
|
|
23
|
+
|
|
24
|
+
<p align="center">
|
|
25
|
+
<a href="./README.md">中文</a> |
|
|
26
|
+
<a href="./README-EN.md">English</a>
|
|
27
|
+
<a href="./CHANGELOG.md">更新日志</a>
|
|
28
|
+
</p>
|
|
29
|
+
|
|
30
|
+
<p align="center">现代化的 Vue 3 甘特图组件库,为项目管理和任务调度提供完整解决方案</p>
|
|
31
|
+
|
|
32
|
+
<p align="center">
|
|
33
|
+
<a href="https://nelson820125.github.io/jordium-gantt-vue3/">
|
|
34
|
+
<strong>📱 在线演示</strong>
|
|
35
|
+
</a>
|
|
36
|
+
|
|
|
37
|
+
<a href="https://github.com/nelson820125/jordium-gantt-vue3">
|
|
38
|
+
<strong>📦 GitHub</strong>
|
|
39
|
+
</a>
|
|
40
|
+
|
|
|
41
|
+
<a href="https://www.npmjs.com/package/jordium-gantt-vue3">
|
|
42
|
+
<strong>📚 npm</strong>
|
|
43
|
+
</a>
|
|
44
|
+
</p>
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## ✨ 简介
|
|
49
|
+
|
|
50
|
+
jordium-gantt-vue3 是一个基于 Vue 3 和 TypeScript 开发的现代化甘特图组件,专为项目管理和任务调度场景设计。它提供了丰富的交互功能、灵活的配置选项和优雅的视觉效果。
|
|
51
|
+
|
|
52
|
+
### 核心特性
|
|
53
|
+
|
|
54
|
+
- 📊 **功能完整** - 任务管理、里程碑、依赖关系、进度追踪
|
|
55
|
+
- 🎨 **主题系统** - 内置亮色/暗色主题,支持自定义样式
|
|
56
|
+
- 🖱️ **交互流畅** - 拖拽调整、缩放、双击编辑、右键菜单
|
|
57
|
+
- 🌍 **国际化** - 内置中英文,可扩展其他语言
|
|
58
|
+
- ⚡ **高性能** - 虚拟滚动、懒加载,轻松处理大量数据
|
|
59
|
+
- 💎 **类型安全** - 完整 TypeScript 支持
|
|
60
|
+
|
|
61
|
+
### 效果预览
|
|
62
|
+
|
|
63
|
+
#### 亮色主题
|
|
64
|
+
|
|
65
|
+
<img src="design/screenshots/light-theme.png" alt="亮色主题" width="100%">
|
|
66
|
+
|
|
67
|
+
#### 暗色主题
|
|
68
|
+
|
|
69
|
+
<img src="design/screenshots/dark-theme.png" alt="暗色主题" width="100%">
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## 📦 安装
|
|
74
|
+
|
|
75
|
+
使用你喜欢的包管理器安装:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# npm
|
|
79
|
+
npm install jordium-gantt-vue3
|
|
80
|
+
|
|
81
|
+
# yarn
|
|
82
|
+
yarn add jordium-gantt-vue3
|
|
83
|
+
|
|
84
|
+
# pnpm
|
|
85
|
+
pnpm add jordium-gantt-vue3
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## 🚀 快速开始
|
|
91
|
+
|
|
92
|
+
### 组件引入
|
|
93
|
+
|
|
94
|
+
在组件中引入 `GanttChart` 组件和样式:
|
|
95
|
+
|
|
96
|
+
```vue
|
|
97
|
+
<script setup lang="ts">
|
|
98
|
+
import { GanttChart } from 'jordium-gantt-vue3'
|
|
99
|
+
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
|
|
100
|
+
</script>
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
> **提示**: 样式文件只需在项目中引入一次即可,建议在 `main.ts` 或根组件中引入。
|
|
104
|
+
|
|
105
|
+
### 第一个示例
|
|
106
|
+
|
|
107
|
+
创建你的第一个甘特图:
|
|
108
|
+
|
|
109
|
+
```vue
|
|
110
|
+
<template>
|
|
111
|
+
<div style="height: 600px;">
|
|
112
|
+
<GanttChart :tasks="tasks" :milestones="milestones" />
|
|
113
|
+
</div>
|
|
114
|
+
</template>
|
|
115
|
+
|
|
116
|
+
<script setup lang="ts">
|
|
117
|
+
import { ref } from 'vue'
|
|
118
|
+
import { GanttChart } from 'jordium-gantt-vue3'
|
|
119
|
+
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
|
|
120
|
+
|
|
121
|
+
const tasks = ref([
|
|
122
|
+
{
|
|
123
|
+
id: 1,
|
|
124
|
+
name: '项目启动',
|
|
125
|
+
startDate: '2025-01-01',
|
|
126
|
+
endDate: '2025-01-10',
|
|
127
|
+
progress: 100,
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
id: 2,
|
|
131
|
+
name: '需求分析',
|
|
132
|
+
startDate: '2025-01-11',
|
|
133
|
+
endDate: '2025-01-20',
|
|
134
|
+
progress: 80,
|
|
135
|
+
predecessor: [1],
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
id: 3,
|
|
139
|
+
name: '系统设计',
|
|
140
|
+
startDate: '2025-01-21',
|
|
141
|
+
endDate: '2025-02-05',
|
|
142
|
+
progress: 50,
|
|
143
|
+
predecessor: [2],
|
|
144
|
+
},
|
|
145
|
+
])
|
|
146
|
+
|
|
147
|
+
const milestones = ref([
|
|
148
|
+
{
|
|
149
|
+
id: 101,
|
|
150
|
+
name: '项目立项',
|
|
151
|
+
date: '2025-01-01',
|
|
152
|
+
type: 'milestone',
|
|
153
|
+
},
|
|
154
|
+
])
|
|
155
|
+
</script>
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
🎯 **[立即体验 Github在线Demo →](https://nelson820125.github.io/jordium-gantt-vue3/)**
|
|
159
|
+
<span><strong>推荐使用 <a href="https://dovee.cc/a.php?anaxjgyz1ozZq2B">DOVE</a> VPN,快速、稳定。</strong></span> <span style="color:red;">(注意:请合法使用 VPN 资源)</span>
|
|
160
|
+
|
|
161
|
+
## 🌞 NPM包使用示例
|
|
162
|
+
|
|
163
|
+
请参考项目下的npm-demo,这是一个独立的项目,可以使用IDE单独浏览和启动,运行前请安装element plus以及jordium-gantt-vue3插件包
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
# npm
|
|
167
|
+
npm install element-plus
|
|
168
|
+
npm install jordium-gantt-vue3
|
|
169
|
+
npm run dev
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## 组件指南
|
|
175
|
+
|
|
176
|
+
### GanttChart 组件
|
|
177
|
+
|
|
178
|
+
`GanttChart` 是组件库的核心入口,提供了完整的甘特图功能。
|
|
179
|
+
|
|
180
|
+
#### 基础属性
|
|
181
|
+
|
|
182
|
+
| 属性名 | 类型 | 默认值 | 说明 |
|
|
183
|
+
| --------------------------- | --------- | ------- | -------------------------------------------------------------- |
|
|
184
|
+
| `tasks` | `Task[]` | `[]` | 任务数据数组 |
|
|
185
|
+
| `milestones` | `Task[]` | `[]` | 里程碑数据数组(注意:类型为 Task[],需设置 type='milestone') |
|
|
186
|
+
| `showToolbar` | `boolean` | `true` | 是否显示工具栏 |
|
|
187
|
+
| `useDefaultDrawer` | `boolean` | `true` | 是否使用内置任务编辑抽屉(TaskDrawer) |
|
|
188
|
+
| `useDefaultMilestoneDialog` | `boolean` | `true` | 是否使用内置里程碑编辑对话框(MilestoneDialog) |
|
|
189
|
+
| `autoSortByStartDate` | `boolean` | `false` | 是否根据开始时间自动排序任务 |
|
|
190
|
+
| `allowDragAndResize` | `boolean` | `true` | 是否允许拖拽和调整任务/里程碑大小 |
|
|
191
|
+
| `enableTaskRowMove` | `boolean` | `false` | 是否允许拖拽和摆放TaskRow |
|
|
192
|
+
|
|
193
|
+
#### 配置对象属性
|
|
194
|
+
|
|
195
|
+
完整的配置对象说明请参考 [⚙️ 配置与扩展](#⚙️-配置与扩展) 章节。
|
|
196
|
+
|
|
197
|
+
| 属性名 | 类型 | 默认值 | 说明 |
|
|
198
|
+
| ---------------- | ---------------------------- | ----------------------------------------------------------------------- | ---------------- |
|
|
199
|
+
| `toolbarConfig` | `ToolbarConfig` | `{}` | 工具栏配置 |
|
|
200
|
+
| `taskListConfig` | `TaskListConfig` | `undefined` | 任务列表配置 |
|
|
201
|
+
| `taskBarConfig` | `TaskBarConfig` | `undefined` | 任务条样式配置 |
|
|
202
|
+
| `localeMessages` | `Partial<Messages['zh-CN']>` | `undefined` | 自定义多语言配置 |
|
|
203
|
+
| `workingHours` | `WorkingHours` | `{ morning: { start: 8, end: 11 }, afternoon: { start: 13, end: 17 } }` | 工作时间配置 |
|
|
204
|
+
|
|
205
|
+
#### 回调函数属性
|
|
206
|
+
|
|
207
|
+
| 属性名 | 类型 | 说明 |
|
|
208
|
+
| -------------------- | ------------------------------------ | -------------------------------------------------------- |
|
|
209
|
+
| `onTodayLocate` | `() => void` | 工具栏"今天"按钮点击回调 |
|
|
210
|
+
| `onExportCsv` | `() => boolean \| void` | 工具栏"导出CSV"按钮点击回调,返回 `false` 可阻止默认导出 |
|
|
211
|
+
| `onExportPdf` | `() => void` | 工具栏"导出PDF"按钮点击回调 |
|
|
212
|
+
| `onLanguageChange` | `(lang: 'zh-CN' \| 'en-US') => void` | 语言切换回调 |
|
|
213
|
+
| `onThemeChange` | `(isDark: boolean) => void` | 主题切换回调 |
|
|
214
|
+
| `onFullscreenChange` | `(isFullscreen: boolean) => void` | 全屏切换回调 |
|
|
215
|
+
| `onExpandAll` | `() => void` | 工具栏"全部展开"按钮点击回调 |
|
|
216
|
+
| `onCollapseAll` | `() => void` | 工具栏"全部折叠"按钮点击回调 |
|
|
217
|
+
|
|
218
|
+
#### 组件事件(Events)
|
|
219
|
+
|
|
220
|
+
完整的事件说明请分别参考:
|
|
221
|
+
|
|
222
|
+
- **任务相关事件**:参见下方 [任务管理](#任务管理) 章节
|
|
223
|
+
- **里程碑相关事件**:参见下方 [里程碑管理](#里程碑管理) 章节
|
|
224
|
+
|
|
225
|
+
**事件列表总览:**
|
|
226
|
+
|
|
227
|
+
| 事件名 | 参数 | 说明 |
|
|
228
|
+
| ------------------------ | --------------------------------- | -------------------------- |
|
|
229
|
+
| `add-task` | - | 点击工具栏"添加任务"按钮 |
|
|
230
|
+
| `task-click` | `(task: Task, event: MouseEvent)` | 点击任务 |
|
|
231
|
+
| `task-double-click` | `(task: Task)` | 双击任务 |
|
|
232
|
+
| `task-added` | `{ task: Task }` | 任务添加后触发 |
|
|
233
|
+
| `task-updated` | `{ task: Task }` | 任务更新后触发 |
|
|
234
|
+
| `task-deleted` | `{ task: Task }` | 任务删除后触发 |
|
|
235
|
+
| `taskbar-drag-end` | `(task: Task)` | 拖拽任务结束 |
|
|
236
|
+
| `taskbar-resize-end` | `(task: Task)` | 调整任务大小结束 |
|
|
237
|
+
| `predecessor-added` | `{ targetTask, newTask }` | 添加前置任务 |
|
|
238
|
+
| `successor-added` | `{ targetTask, newTask }` | 添加后置任务 |
|
|
239
|
+
| `timer-started` | `(task: Task)` | 任务计时器启动 |
|
|
240
|
+
| `timer-stopped` | `(task: Task)` | 任务计时器停止 |
|
|
241
|
+
| `add-milestone` | - | 点击工具栏"添加里程碑"按钮 |
|
|
242
|
+
| `milestone-saved` | `(milestone: Task)` | 里程碑保存 |
|
|
243
|
+
| `milestone-deleted` | `{ milestoneId: number }` | 里程碑删除 |
|
|
244
|
+
| `milestone-icon-changed` | `{ milestoneId, icon }` | 里程碑图标变更 |
|
|
245
|
+
| `milestone-drag-end` | `(milestone: Task)` | 拖拽里程碑结束 |
|
|
246
|
+
| `task-row-moved` | `payload: { draggedTask: Task, targetTask: Task, position: 'after' 或 'child'}` | 拖拽TaskRow结束 |
|
|
247
|
+
|
|
248
|
+
#### 示例1:最简单的甘特图
|
|
249
|
+
|
|
250
|
+
```vue
|
|
251
|
+
<template>
|
|
252
|
+
<div style="height: 600px;">
|
|
253
|
+
<GanttChart :tasks="tasks" />
|
|
254
|
+
</div>
|
|
255
|
+
</template>
|
|
256
|
+
|
|
257
|
+
<script setup lang="ts">
|
|
258
|
+
import { ref } from 'vue'
|
|
259
|
+
import { GanttChart } from 'jordium-gantt-vue3'
|
|
260
|
+
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
|
|
261
|
+
|
|
262
|
+
const tasks = ref([
|
|
263
|
+
{
|
|
264
|
+
id: 1,
|
|
265
|
+
name: '任务1',
|
|
266
|
+
startDate: '2025-01-01',
|
|
267
|
+
endDate: '2025-01-10',
|
|
268
|
+
progress: 100,
|
|
269
|
+
},
|
|
270
|
+
])
|
|
271
|
+
</script>
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
#### 示例2:带里程碑的甘特图
|
|
275
|
+
|
|
276
|
+
```vue
|
|
277
|
+
<template>
|
|
278
|
+
<div style="height: 600px;">
|
|
279
|
+
<GanttChart :tasks="tasks" :milestones="milestones" />
|
|
280
|
+
</div>
|
|
281
|
+
</template>
|
|
282
|
+
|
|
283
|
+
<script setup lang="ts">
|
|
284
|
+
import { ref } from 'vue'
|
|
285
|
+
import { GanttChart } from 'jordium-gantt-vue3'
|
|
286
|
+
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
|
|
287
|
+
|
|
288
|
+
const tasks = ref([
|
|
289
|
+
{
|
|
290
|
+
id: 1,
|
|
291
|
+
name: '项目启动',
|
|
292
|
+
startDate: '2025-01-01',
|
|
293
|
+
endDate: '2025-01-10',
|
|
294
|
+
progress: 100,
|
|
295
|
+
},
|
|
296
|
+
])
|
|
297
|
+
|
|
298
|
+
const milestones = ref([
|
|
299
|
+
{
|
|
300
|
+
id: 101,
|
|
301
|
+
name: '项目立项',
|
|
302
|
+
startDate: '2025-01-01',
|
|
303
|
+
type: 'milestone',
|
|
304
|
+
icon: 'diamond',
|
|
305
|
+
},
|
|
306
|
+
])
|
|
307
|
+
</script>
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
#### 示例3:隐藏工具栏,自定义控制按钮绑定事件
|
|
311
|
+
|
|
312
|
+
```vue
|
|
313
|
+
<template>
|
|
314
|
+
<div>
|
|
315
|
+
<!-- 自定义控制栏 -->
|
|
316
|
+
<div class="custom-toolbar">
|
|
317
|
+
<button @click="addTask">新增任务</button>
|
|
318
|
+
<button @click="addMilestone">新增里程碑</button>
|
|
319
|
+
</div>
|
|
320
|
+
|
|
321
|
+
<!-- 甘特图组件,隐藏内置工具栏 -->
|
|
322
|
+
<div style="height: 600px;">
|
|
323
|
+
<GanttChart
|
|
324
|
+
:tasks="tasks"
|
|
325
|
+
:milestones="milestones"
|
|
326
|
+
:show-toolbar="false"
|
|
327
|
+
@task-added="handleTaskAdded"
|
|
328
|
+
@milestone-saved="handleMilestoneSaved"
|
|
329
|
+
/>
|
|
330
|
+
</div>
|
|
331
|
+
</div>
|
|
332
|
+
</template>
|
|
333
|
+
|
|
334
|
+
<script setup lang="ts">
|
|
335
|
+
import { ref } from 'vue'
|
|
336
|
+
import { GanttChart } from 'jordium-gantt-vue3'
|
|
337
|
+
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
|
|
338
|
+
|
|
339
|
+
const tasks = ref([])
|
|
340
|
+
const milestones = ref([])
|
|
341
|
+
|
|
342
|
+
const addTask = () => {
|
|
343
|
+
const newTask = {
|
|
344
|
+
id: Date.now(),
|
|
345
|
+
name: '新任务',
|
|
346
|
+
startDate: new Date().toISOString().split('T')[0],
|
|
347
|
+
endDate: new Date().toISOString().split('T')[0],
|
|
348
|
+
progress: 0,
|
|
349
|
+
}
|
|
350
|
+
tasks.value.push(newTask)
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const addMilestone = () => {
|
|
354
|
+
const newMilestone = {
|
|
355
|
+
id: Date.now(),
|
|
356
|
+
name: '新里程碑',
|
|
357
|
+
startDate: new Date().toISOString().split('T')[0],
|
|
358
|
+
type: 'milestone',
|
|
359
|
+
}
|
|
360
|
+
milestones.value.push(newMilestone)
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const handleTaskAdded = e => {
|
|
364
|
+
console.log('任务已添加:', e.task)
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const handleMilestoneSaved = milestone => {
|
|
368
|
+
console.log('里程碑已保存:', milestone)
|
|
369
|
+
}
|
|
370
|
+
</script>
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
---
|
|
374
|
+
|
|
375
|
+
### 任务管理
|
|
376
|
+
|
|
377
|
+
任务是甘特图的核心元素,组件提供了完整的任务 CRUD 操作支持,包括添加、编辑、删除任务,以及丰富的交互事件。
|
|
378
|
+
|
|
379
|
+
#### Task 数据结构
|
|
380
|
+
|
|
381
|
+
| 字段名 | 类型 | 必填 | 默认值 | 说明 |
|
|
382
|
+
| ------------------ | ---------- | ---- | ----------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
|
383
|
+
| `id` | `number` | ✅ | - | 任务唯一标识符 |
|
|
384
|
+
| `name` | `string` | ✅ | - | 任务名称 |
|
|
385
|
+
| `startDate` | `string` | - | - | 开始日期,格式:'YYYY-MM-DD' 或 'YYYY-MM-DD HH:mm' |
|
|
386
|
+
| `endDate` | `string` | - | - | 结束日期,格式:'YYYY-MM-DD' 或 'YYYY-MM-DD HH:mm' |
|
|
387
|
+
| `progress` | `number` | - | `0` | 任务进度,范围 0-100 |
|
|
388
|
+
| `predecessor` | `number[]` | - | - | 前置任务 ID 数组,标准格式:`[1, 2, 3]`<br/>**兼容格式**:也支持字符串 `'1,2,3'` 或字符串数组 `['1', '2', '3']`,组件会自动解析 |
|
|
389
|
+
| `assignee` | `string` | - | - | 任务负责人 |
|
|
390
|
+
| `avatar` | `string` | - | - | 任务负责人头像 URL |
|
|
391
|
+
| `estimatedHours` | `number` | - | - | 预估工时(小时) |
|
|
392
|
+
| `actualHours` | `number` | - | - | 实际工时(小时) |
|
|
393
|
+
| `parentId` | `number` | - | - | 父任务 ID,用于任务分组 |
|
|
394
|
+
| `children` | `Task[]` | - | - | 子任务数组 |
|
|
395
|
+
| `collapsed` | `boolean` | - | `false` | 子任务是否折叠 |
|
|
396
|
+
| `isParent` | `boolean` | - | - | 是否为父任务 |
|
|
397
|
+
| `type` | `string` | - | - | 任务类型,'milestone' 表示里程碑,'milestone-group' 表示里程碑分组 |
|
|
398
|
+
| `description` | `string` | - | - | 任务描述 |
|
|
399
|
+
| `icon` | `string` | - | `'diamond'` | 任务图标(用于里程碑),可选值:'diamond', 'flag', 'star', 'rocket' 等 |
|
|
400
|
+
| `level` | `number` | - | `0` | 任务层级(自动计算) |
|
|
401
|
+
| `isTimerRunning` | `boolean` | - | `false` | 计时器是否运行中 |
|
|
402
|
+
| `timerStartTime` | `number` | - | - | 计时开始时间(时间戳) |
|
|
403
|
+
| `timerEndTime` | `number` | - | - | 计时结束时间(时间戳) |
|
|
404
|
+
| `timerStartDesc` | `string` | - | - | 计时开始时填写的描述 |
|
|
405
|
+
| `timerElapsedTime` | `number` | - | `0` | 已计时的时长(毫秒) |
|
|
406
|
+
| `isEditable` | `boolean` | - | `true` | 单个任务是否可编辑(可拖拽、拉伸),优先级高于全局 `allowDragAndResize` |
|
|
407
|
+
| `[key: string]` | `unknown` | - | - | 支持自定义属性扩展,可添加任意额外字段 |
|
|
408
|
+
|
|
409
|
+
> **自定义属性扩展**:Task 接口支持添加任意自定义字段,例如:`priority`、`tags`、`status`、`department` 等业务相关字段。
|
|
410
|
+
>
|
|
411
|
+
> **前置任务字段说明**:
|
|
412
|
+
>
|
|
413
|
+
> - **标准格式**(推荐):`predecessor: [1, 2, 3]` - number 数组
|
|
414
|
+
> - **兼容格式1**:`predecessor: '1,2,3'` - 逗号分隔的字符串
|
|
415
|
+
> - **兼容格式2**:`predecessor: ['1', '2', '3']` - 字符串数组
|
|
416
|
+
> - 组件内部会自动将所有格式统一解析为 number 数组
|
|
417
|
+
> - 无前置任务:使用空数组 `[]`、空字符串 `''` 或不设置该字段
|
|
418
|
+
|
|
419
|
+
#### 任务相关属性
|
|
420
|
+
|
|
421
|
+
| 属性名 | 类型 | 默认值 | 说明 |
|
|
422
|
+
| --------------------- | ---------------- | ----------- | -------------------------------------------------------------- |
|
|
423
|
+
| `tasks` | `Task[]` | `[]` | 任务数据数组 |
|
|
424
|
+
| `useDefaultDrawer` | `boolean` | `true` | 是否使用内置的任务编辑抽屉(TaskDrawer) |
|
|
425
|
+
| `taskBarConfig` | `TaskBarConfig` | `{}` | 任务条样式配置,详见 [TaskBarConfig 配置](#taskbarconfig-配置) |
|
|
426
|
+
| `taskListConfig` | `TaskListConfig` | `undefined` | 任务列表配置,详见 [TaskListConfig 配置](#tasklistconfig-配置) |
|
|
427
|
+
| `autoSortByStartDate` | `boolean` | `false` | 是否根据开始时间自动排序任务 |
|
|
428
|
+
| `enableTaskRowMove` | `boolean` | `false` | 是否允许拖拽和摆放TaskRow
|
|
429
|
+
|
|
430
|
+
**配置说明**:
|
|
431
|
+
|
|
432
|
+
- **默认模式**:`useDefaultDrawer=true`(默认),双击任务自动打开内置 TaskDrawer
|
|
433
|
+
- **自定义编辑器**:`useDefaultDrawer=false` 禁用内置抽屉,监听 `@task-double-click` 事件打开自定义编辑器
|
|
434
|
+
- **只读模式**:`useDefaultDrawer=false` 且不监听 `@task-double-click` 事件,用户双击任务无反应
|
|
435
|
+
|
|
436
|
+
#### 任务事件
|
|
437
|
+
|
|
438
|
+
> **💡 事件驱动架构**:组件采用纯事件驱动设计,所有用户操作(添加、编辑、删除、拖拽等)都会触发对应事件,方便外部监听和处理。
|
|
439
|
+
|
|
440
|
+
| 事件名 | 参数 | 触发时机 | 说明 |
|
|
441
|
+
| -------------------- | ----------------------------------------- | -------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
|
|
442
|
+
| `add-task` | - | 点击工具栏"添加任务"按钮时 | 可用于自定义新增任务逻辑。如 `useDefaultDrawer=true`,组件会自动打开内置 TaskDrawer |
|
|
443
|
+
| `task-click` | `(task: Task, event: MouseEvent) => void` | 点击任务条时 | 单击任务触发 |
|
|
444
|
+
| `task-double-click` | `(task: Task) => void` | 双击任务条时 | 双击任务时**始终触发**。`useDefaultDrawer=true` 时组件会额外打开内置编辑器,`false` 时不打开。事件触发与属性值无关 |
|
|
445
|
+
| `task-added` | `{ task: Task }` | 任务添加后 | 通过内置 TaskDrawer 添加任务后触发。**注意**:组件已自动更新 `tasks` 数据,外部只需监听此事件做额外处理(如调用 API 保存) |
|
|
446
|
+
| `task-updated` | `{ task: Task }` | 任务更新后 | 通过内置 TaskDrawer 或拖拽更新任务后触发。**注意**:组件已自动更新 `tasks` 数据,外部只需监听此事件做额外处理 |
|
|
447
|
+
| `task-deleted` | `{ task: Task }` | 任务删除后 | 通过内置 TaskDrawer 删除任务后触发。**注意**:组件已自动更新 `tasks` 数据,外部只需监听此事件做额外处理 |
|
|
448
|
+
| `taskbar-drag-end` | `(task: Task) => void` | 拖拽任务条结束时 | 任务位置变化,startDate 和 endDate 已更新。**注意**:组件已自动更新 `tasks` 数据 |
|
|
449
|
+
| `taskbar-resize-end` | `(task: Task) => void` | 调整任务条大小结束时 | 任务时长变化,endDate 已更新。**注意**:组件已自动更新 `tasks` 数据 |
|
|
450
|
+
| `predecessor-added` | `{ targetTask: Task, newTask: Task }` | 通过右键菜单添加前置任务后 | `targetTask` 是被添加前置任务的任务,`newTask` 是新创建的前置任务 |
|
|
451
|
+
| `successor-added` | `{ targetTask: Task, newTask: Task }` | 通过右键菜单添加后置任务后 | `targetTask` 是原任务,`newTask` 是新创建的后置任务(其 predecessor 已包含 targetTask.id) |
|
|
452
|
+
| `timer-started` | `(task: Task) => void` | 任务计时器启动时 | 开始记录任务工时 |
|
|
453
|
+
| `timer-stopped` | `(task: Task) => void` | 任务计时器停止时 | 停止记录任务工时 |
|
|
454
|
+
| `task-row-moved` | `payload: { draggedTask: Task, targetTask: Task, position: 'after' 或 'child'}` | 拖拽TaskRow结束 | TaskList中的TasRow按住拖拽,完成前后排放位置放置后
|
|
455
|
+
|
|
456
|
+
**数据同步说明**:
|
|
457
|
+
|
|
458
|
+
- ✅ **组件内部自动更新**:所有任务的增删改操作,组件都会自动更新 `props.tasks` 数据
|
|
459
|
+
- ✅ **事件仅做通知**:外部监听事件主要用于:显示提示消息、调用后端 API、更新其他相关数据等
|
|
460
|
+
- ❌ **避免重复操作**:不要在事件处理器中再次修改 `tasks` 数据,否则会导致重复更新
|
|
461
|
+
|
|
462
|
+
#### 示例1:基础任务操作
|
|
463
|
+
|
|
464
|
+
```vue
|
|
465
|
+
<template>
|
|
466
|
+
<div style="height: 600px;">
|
|
467
|
+
<GanttChart
|
|
468
|
+
:tasks="tasks"
|
|
469
|
+
@add-task="handleAddTask"
|
|
470
|
+
@task-added="handleTaskAdded"
|
|
471
|
+
@task-updated="handleTaskUpdated"
|
|
472
|
+
@task-deleted="handleTaskDeleted"
|
|
473
|
+
@task-click="handleTaskClick"
|
|
474
|
+
@taskbar-drag-end="handleTaskDragEnd"
|
|
475
|
+
/>
|
|
476
|
+
</div>
|
|
477
|
+
</template>
|
|
478
|
+
|
|
479
|
+
<script setup lang="ts">
|
|
480
|
+
import { ref } from 'vue'
|
|
481
|
+
import { GanttChart } from 'jordium-gantt-vue3'
|
|
482
|
+
import type { Task } from 'jordium-gantt-vue3'
|
|
483
|
+
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
|
|
484
|
+
|
|
485
|
+
const tasks = ref<Task[]>([
|
|
486
|
+
{
|
|
487
|
+
id: 1,
|
|
488
|
+
name: '项目规划',
|
|
489
|
+
startDate: '2025-01-01',
|
|
490
|
+
endDate: '2025-01-10',
|
|
491
|
+
progress: 100,
|
|
492
|
+
assignee: '张三',
|
|
493
|
+
estimatedHours: 40,
|
|
494
|
+
},
|
|
495
|
+
{
|
|
496
|
+
id: 2,
|
|
497
|
+
name: '需求分析',
|
|
498
|
+
startDate: '2025-01-11',
|
|
499
|
+
endDate: '2025-01-20',
|
|
500
|
+
progress: 60,
|
|
501
|
+
assignee: '李四',
|
|
502
|
+
predecessor: [1], // 依赖任务1
|
|
503
|
+
},
|
|
504
|
+
])
|
|
505
|
+
|
|
506
|
+
// 工具栏"添加任务"按钮点击事件
|
|
507
|
+
const handleAddTask = () => {
|
|
508
|
+
console.log('准备新增任务...')
|
|
509
|
+
// 组件会自动打开 TaskDrawer(如果 useDefaultDrawer=true)
|
|
510
|
+
// 也可以在这里执行自定义逻辑,如显示提示消息
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// 任务添加事件(通过内置抽屉添加)
|
|
514
|
+
const handleTaskAdded = (e: { task: Task }) => {
|
|
515
|
+
console.log('新增任务:', e.task)
|
|
516
|
+
// 注意:组件已自动将任务添加到 tasks 数组
|
|
517
|
+
// 这里只需调用后端 API 保存即可
|
|
518
|
+
// await api.createTask(e.task)
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// 任务更新事件(通过内置抽屉或拖拽更新)
|
|
522
|
+
const handleTaskUpdated = (e: { task: Task }) => {
|
|
523
|
+
console.log('更新任务:', e.task)
|
|
524
|
+
// 注意:组件已自动更新 tasks 数组中的任务数据
|
|
525
|
+
// 这里只需调用后端 API 更新即可
|
|
526
|
+
// await api.updateTask(e.task.id, e.task)
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// 任务删除事件
|
|
530
|
+
const handleTaskDeleted = (e: { task: Task }) => {
|
|
531
|
+
console.log('删除任务:', e.task)
|
|
532
|
+
// 注意:组件已自动从 tasks 数组中移除任务
|
|
533
|
+
// 这里只需调用后端 API 删除即可
|
|
534
|
+
// await api.deleteTask(e.task.id)
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
// 点击任务事件
|
|
538
|
+
const handleTaskClick = (task: Task) => {
|
|
539
|
+
console.log('点击任务:', task.name)
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// 拖拽任务结束事件
|
|
543
|
+
const handleTaskDragEnd = (task: Task) => {
|
|
544
|
+
console.log('任务拖拽完成,新日期:', task.startDate, '至', task.endDate)
|
|
545
|
+
// 可以在这里调用后端 API 保存新的日期
|
|
546
|
+
}
|
|
547
|
+
</script>
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
#### 示例2:任务依赖关系(前置任务/后置任务)
|
|
551
|
+
|
|
552
|
+
任务可以通过 `predecessor` 字段配置前置任务,组件会自动绘制依赖关系连线:
|
|
553
|
+
|
|
554
|
+
```vue
|
|
555
|
+
<template>
|
|
556
|
+
<GanttChart
|
|
557
|
+
:tasks="tasks"
|
|
558
|
+
@predecessor-added="handlePredecessorAdded"
|
|
559
|
+
@successor-added="handleSuccessorAdded"
|
|
560
|
+
/>
|
|
561
|
+
</template>
|
|
562
|
+
|
|
563
|
+
<script setup lang="ts">
|
|
564
|
+
import { ref } from 'vue'
|
|
565
|
+
import { GanttChart } from 'jordium-gantt-vue3'
|
|
566
|
+
import type { Task } from 'jordium-gantt-vue3'
|
|
567
|
+
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
|
|
568
|
+
|
|
569
|
+
const tasks = ref<Task[]>([
|
|
570
|
+
{
|
|
571
|
+
id: 1,
|
|
572
|
+
name: '需求分析',
|
|
573
|
+
startDate: '2025-01-01',
|
|
574
|
+
endDate: '2025-01-10',
|
|
575
|
+
progress: 100,
|
|
576
|
+
predecessor: [], // 无前置任务
|
|
577
|
+
},
|
|
578
|
+
{
|
|
579
|
+
id: 2,
|
|
580
|
+
name: '系统设计',
|
|
581
|
+
startDate: '2025-01-11',
|
|
582
|
+
endDate: '2025-01-20',
|
|
583
|
+
progress: 80,
|
|
584
|
+
predecessor: [1], // 依赖任务1(需求分析)
|
|
585
|
+
},
|
|
586
|
+
{
|
|
587
|
+
id: 3,
|
|
588
|
+
name: '数据库设计',
|
|
589
|
+
startDate: '2025-01-11',
|
|
590
|
+
endDate: '2025-01-18',
|
|
591
|
+
progress: 90,
|
|
592
|
+
predecessor: [1], // 依赖任务1
|
|
593
|
+
},
|
|
594
|
+
{
|
|
595
|
+
id: 4,
|
|
596
|
+
name: '前端开发',
|
|
597
|
+
startDate: '2025-01-21',
|
|
598
|
+
endDate: '2025-02-10',
|
|
599
|
+
progress: 60,
|
|
600
|
+
predecessor: [2], // 依赖任务2(系统设计)
|
|
601
|
+
},
|
|
602
|
+
{
|
|
603
|
+
id: 5,
|
|
604
|
+
name: '后端开发',
|
|
605
|
+
startDate: '2025-01-19',
|
|
606
|
+
endDate: '2025-02-08',
|
|
607
|
+
progress: 70,
|
|
608
|
+
predecessor: [2, 3], // 同时依赖任务2和3
|
|
609
|
+
},
|
|
610
|
+
{
|
|
611
|
+
id: 6,
|
|
612
|
+
name: '集成测试',
|
|
613
|
+
startDate: '2025-02-11',
|
|
614
|
+
endDate: '2025-02-20',
|
|
615
|
+
progress: 30,
|
|
616
|
+
predecessor: [4, 5], // 依赖前端和后端开发完成
|
|
617
|
+
},
|
|
618
|
+
])
|
|
619
|
+
|
|
620
|
+
// 通过右键菜单添加前置任务时触发
|
|
621
|
+
const handlePredecessorAdded = (event: { targetTask: Task; newTask: Task }) => {
|
|
622
|
+
console.log(`任务 [${event.targetTask.name}] 添加了前置任务 [${event.newTask.name}]`)
|
|
623
|
+
// 组件会自动更新 targetTask 的 predecessor 数组(追加新任务 ID)
|
|
624
|
+
// 这里可以调用后端 API 保存依赖关系
|
|
625
|
+
// await api.addTaskDependency(event.targetTask.id, event.newTask.id)
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
// 通过右键菜单添加后置任务时触发
|
|
629
|
+
const handleSuccessorAdded = (event: { targetTask: Task; newTask: Task }) => {
|
|
630
|
+
console.log(`任务 [${event.targetTask.name}] 添加了后置任务 [${event.newTask.name}]`)
|
|
631
|
+
// 组件会自动更新 newTask 的 predecessor 数组(将 targetTask.id 添加进去)
|
|
632
|
+
// 这里可以调用后端 API 保存依赖关系
|
|
633
|
+
// await api.addTaskDependency(event.newTask.id, event.targetTask.id)
|
|
634
|
+
}
|
|
635
|
+
</script>
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
**依赖关系说明**:
|
|
639
|
+
|
|
640
|
+
- **`predecessor` 字段支持多种格式**:
|
|
641
|
+
- 标准格式(推荐):`[1, 2, 3]` - number 数组
|
|
642
|
+
- 兼容格式1:`'1,2,3'` - 逗号分隔的字符串
|
|
643
|
+
- 兼容格式2:`['1', '2', '3']` - 字符串数组
|
|
644
|
+
- 组件会自动解析所有格式
|
|
645
|
+
- 前置任务:必须先完成的任务(例如:设计完成后才能开发)
|
|
646
|
+
- 后置任务:依赖当前任务的任务(当前任务是其他任务的前置任务)
|
|
647
|
+
- 组件会自动绘制依赖关系连线,从前置任务指向依赖它的任务
|
|
648
|
+
- 可以通过内置右键菜单添加/删除前置任务和后置任务
|
|
649
|
+
- 内置菜单删除任务时,组件会自动清理相关的依赖关系引用
|
|
650
|
+
- 无前置任务:使用空数组 `[]`、空字符串 `''` 或不设置 `predecessor` 字段
|
|
651
|
+
|
|
652
|
+
#### 示例3:隐藏工具栏,使用自定义按钮触发事件
|
|
653
|
+
|
|
654
|
+
适用于需要完全自定义控制栏的场景:
|
|
655
|
+
|
|
656
|
+
```vue
|
|
657
|
+
<template>
|
|
658
|
+
<div>
|
|
659
|
+
<!-- 自定义控制栏 -->
|
|
660
|
+
<div class="custom-toolbar">
|
|
661
|
+
<button @click="triggerAddTask">新增任务</button>
|
|
662
|
+
<button @click="triggerAddMilestone">新增里程碑</button>
|
|
663
|
+
<!-- 其他自定义按钮... -->
|
|
664
|
+
</div>
|
|
665
|
+
|
|
666
|
+
<!-- 甘特图组件,隐藏内置工具栏 -->
|
|
667
|
+
<GanttChart
|
|
668
|
+
:tasks="tasks"
|
|
669
|
+
:milestones="milestones"
|
|
670
|
+
:show-toolbar="false"
|
|
671
|
+
:use-default-drawer="true"
|
|
672
|
+
:use-default-milestone-dialog="true"
|
|
673
|
+
@add-task="handleAddTask"
|
|
674
|
+
@add-milestone="handleAddMilestone"
|
|
675
|
+
@task-added="handleTaskAdded"
|
|
676
|
+
/>
|
|
677
|
+
</div>
|
|
678
|
+
</template>
|
|
679
|
+
|
|
680
|
+
<script setup lang="ts">
|
|
681
|
+
import { ref } from 'vue'
|
|
682
|
+
import { GanttChart } from 'jordium-gantt-vue3'
|
|
683
|
+
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
|
|
684
|
+
|
|
685
|
+
const tasks = ref([])
|
|
686
|
+
const milestones = ref([])
|
|
687
|
+
|
|
688
|
+
// 自定义按钮触发事件(组件会响应并打开内置编辑器)
|
|
689
|
+
const triggerAddTask = () => {
|
|
690
|
+
// 直接触发组件的 add-task 事件
|
|
691
|
+
// 由于 useDefaultDrawer=true,组件会自动打开 TaskDrawer
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
const triggerAddMilestone = () => {
|
|
695
|
+
// 直接触发组件的 add-milestone 事件
|
|
696
|
+
// 由于 useDefaultMilestoneDialog=true,组件会自动打开 MilestoneDialog
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
// 监听事件处理逻辑
|
|
700
|
+
const handleAddTask = () => {
|
|
701
|
+
console.log('准备新增任务(由自定义按钮触发)')
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
const handleAddMilestone = () => {
|
|
705
|
+
console.log('准备新增里程碑(由自定义按钮触发)')
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
const handleTaskAdded = e => {
|
|
709
|
+
console.log('任务已添加:', e.task)
|
|
710
|
+
// 调用 API 保存...
|
|
711
|
+
}
|
|
712
|
+
</script>
|
|
713
|
+
```
|
|
714
|
+
|
|
715
|
+
> **💡 灵活性设计**:
|
|
716
|
+
>
|
|
717
|
+
> - 显示工具栏 + 默认编辑器:最简单的开箱即用方式
|
|
718
|
+
> - 隐藏工具栏 + 自定义按钮 + 默认编辑器:自定义控制栏样式,保留默认编辑功能
|
|
719
|
+
> - 隐藏工具栏 + 自定义按钮 + 自定义编辑器:完全自定义所有交互逻辑
|
|
720
|
+
|
|
721
|
+
#### 示例4:任务行拖拽排序
|
|
722
|
+
|
|
723
|
+
允许用户通过拖拽 TaskRow 来调整任务的层级关系和前后顺序:
|
|
724
|
+
|
|
725
|
+
```vue
|
|
726
|
+
<template>
|
|
727
|
+
<div style="height: 600px;">
|
|
728
|
+
<GanttChart
|
|
729
|
+
:tasks="tasks"
|
|
730
|
+
:enable-task-row-move="true"
|
|
731
|
+
@task-row-moved="handleTaskRowMoved"
|
|
732
|
+
/>
|
|
733
|
+
</div>
|
|
734
|
+
</template>
|
|
735
|
+
|
|
736
|
+
<script setup lang="ts">
|
|
737
|
+
import { ref } from 'vue'
|
|
738
|
+
import { GanttChart } from 'jordium-gantt-vue3'
|
|
739
|
+
import type { Task } from 'jordium-gantt-vue3'
|
|
740
|
+
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
|
|
741
|
+
|
|
742
|
+
const tasks = ref<Task[]>([
|
|
743
|
+
{
|
|
744
|
+
id: 1,
|
|
745
|
+
name: '项目规划',
|
|
746
|
+
startDate: '2025-01-01',
|
|
747
|
+
endDate: '2025-01-10',
|
|
748
|
+
progress: 100,
|
|
749
|
+
},
|
|
750
|
+
{
|
|
751
|
+
id: 2,
|
|
752
|
+
name: '需求分析',
|
|
753
|
+
startDate: '2025-01-11',
|
|
754
|
+
endDate: '2025-01-20',
|
|
755
|
+
progress: 60,
|
|
756
|
+
parentId: 1,
|
|
757
|
+
},
|
|
758
|
+
{
|
|
759
|
+
id: 3,
|
|
760
|
+
name: '系统设计',
|
|
761
|
+
startDate: '2025-01-21',
|
|
762
|
+
endDate: '2025-01-30',
|
|
763
|
+
progress: 40,
|
|
764
|
+
},
|
|
765
|
+
])
|
|
766
|
+
|
|
767
|
+
// 任务行拖拽完成事件
|
|
768
|
+
const handleTaskRowMoved = (payload: {
|
|
769
|
+
draggedTask: Task
|
|
770
|
+
targetTask: Task
|
|
771
|
+
position: 'after' | 'child'
|
|
772
|
+
}) => {
|
|
773
|
+
const { draggedTask, targetTask, position } = payload
|
|
774
|
+
|
|
775
|
+
console.log(`任务 [${draggedTask.name}] 被拖拽到任务 [${targetTask.name}] ${position === 'after' ? '之后' : '下方作为子任务'}`)
|
|
776
|
+
|
|
777
|
+
// 组件已自动更新任务的层级关系和位置
|
|
778
|
+
// position === 'after': 任务被放置在目标任务之后(同级)
|
|
779
|
+
// position === 'child': 任务被放置为目标任务的子任务(第一个子任务位置)
|
|
780
|
+
|
|
781
|
+
// 这里可以:
|
|
782
|
+
// 1. 显示确认对话框,让用户确认是否移动
|
|
783
|
+
// 2. 调用后端 API 保存新的任务层级关系
|
|
784
|
+
// 3. 更新相关的依赖关系
|
|
785
|
+
|
|
786
|
+
// 示例:调用后端 API
|
|
787
|
+
// await api.updateTaskHierarchy({
|
|
788
|
+
// taskId: draggedTask.id,
|
|
789
|
+
// targetTaskId: targetTask.id,
|
|
790
|
+
// position: position
|
|
791
|
+
// })
|
|
792
|
+
}
|
|
793
|
+
</script>
|
|
794
|
+
```
|
|
795
|
+
|
|
796
|
+
**拖拽排序说明**:
|
|
797
|
+
|
|
798
|
+
- **启用拖拽**:设置 `enable-task-row-move="true"` 启用任务行拖拽功能(默认为 `false`)
|
|
799
|
+
- **拖拽算法**:
|
|
800
|
+
- **算法1(放置在后面)**:当目标任务没有子任务时,被拖拽的任务会放置在目标任务之后(同级),`position='after'`
|
|
801
|
+
- **算法2(作为子任务)**:当目标任务有子任务时,被拖拽的任务会成为目标任务的第一个子任务,`position='child'`
|
|
802
|
+
- **视觉反馈**:
|
|
803
|
+
- 拖拽时会显示半透明的跟随元素
|
|
804
|
+
- 悬停在有效目标任务上时显示蓝色边框提示
|
|
805
|
+
- 无子任务的任务显示蓝色底部边框
|
|
806
|
+
- 有子任务的任务显示蓝色四周边框
|
|
807
|
+
- **自动更新**:组件会自动更新任务的 `parentId` 字段和 `children` 数组,外部只需监听事件做额外处理(如保存到后端)
|
|
808
|
+
- **限制条件**:
|
|
809
|
+
- 不能拖拽到自己身上
|
|
810
|
+
- 不能拖拽到自己的子任务上(避免循环引用)
|
|
811
|
+
- 里程碑和里程碑分组不能被拖拽
|
|
812
|
+
|
|
813
|
+
### 里程碑管理
|
|
814
|
+
|
|
815
|
+
里程碑用于标记项目中的重要时间节点,如项目启动、阶段完成、产品发布等。组件提供了灵活的里程碑编辑配置,默认使用内置的 MilestoneDialog,也支持完全自定义编辑行为。
|
|
816
|
+
|
|
817
|
+
> **注意**: 里程碑与任务是独立的数据集合,不存在直接关联关系。里程碑通过 `milestones` 属性独立管理。
|
|
818
|
+
|
|
819
|
+
#### Milestone 数据结构
|
|
820
|
+
|
|
821
|
+
| 字段名 | 类型 | 必填 | 默认值 | 说明 |
|
|
822
|
+
| ------------- | -------- | ---- | ------------- | ---------------------------------------------------------- |
|
|
823
|
+
| `id` | `number` | ✅ | - | 里程碑唯一标识符 |
|
|
824
|
+
| `name` | `string` | ✅ | - | 里程碑名称 |
|
|
825
|
+
| `startDate` | `string` | ✅ | - | 里程碑日期,格式:'YYYY-MM-DD' 或 'YYYY-MM-DD HH:mm' |
|
|
826
|
+
| `endDate` | `string` | - | - | 结束日期(通常里程碑不需要,自动设置为与 startDate 相同) |
|
|
827
|
+
| `assignee` | `string` | - | - | 负责人 |
|
|
828
|
+
| `type` | `string` | ✅ | `'milestone'` | 类型标识,必须设为 'milestone' |
|
|
829
|
+
| `icon` | `string` | - | `'diamond'` | 里程碑图标,可选值:'diamond', 'flag', 'star', 'rocket' 等 |
|
|
830
|
+
| `description` | `string` | - | - | 里程碑描述 |
|
|
831
|
+
|
|
832
|
+
> **注意**:`milestones` 属性的类型为 `Task[]`,需要确保每个里程碑对象的 `type` 字段设置为 `'milestone'`。
|
|
833
|
+
|
|
834
|
+
#### 里程碑相关属性
|
|
835
|
+
|
|
836
|
+
| 属性名 | 类型 | 默认值 | 说明 |
|
|
837
|
+
| --------------------------- | --------- | ------ | -------------------------------------------------------- |
|
|
838
|
+
| `milestones` | `Task[]` | `[]` | 里程碑数据数组(类型为 Task[],需确保 type='milestone') |
|
|
839
|
+
| `useDefaultMilestoneDialog` | `boolean` | `true` | 是否使用内置的里程碑编辑对话框(MilestoneDialog) |
|
|
840
|
+
|
|
841
|
+
**配置说明**:
|
|
842
|
+
|
|
843
|
+
- **默认模式**:`useDefaultMilestoneDialog=true`(默认),双击里程碑自动打开内置 MilestoneDialog
|
|
844
|
+
- **禁用编辑器**:`useDefaultMilestoneDialog=false`,双击里程碑无反应(组件不打开任何编辑器)
|
|
845
|
+
- **自定义编辑器**:可以监听 `onMilestoneDoubleClick` 回调或相关事件,实现自定义编辑逻辑
|
|
846
|
+
|
|
847
|
+
> **💡 里程碑与任务的区别**:
|
|
848
|
+
>
|
|
849
|
+
> - 里程碑数据通过 `milestones` 属性独立管理,与 `tasks` 分开
|
|
850
|
+
> - 里程碑对象的 `type` 字段必须设置为 `'milestone'`
|
|
851
|
+
> - 里程碑不支持子任务、依赖关系等复杂结构
|
|
852
|
+
> - 里程碑主要用于标记关键时间节点
|
|
853
|
+
|
|
854
|
+
#### 里程碑回调函数(向后兼容)
|
|
855
|
+
|
|
856
|
+
> **⚠️ 已废弃**:请使用新的事件驱动 API(见下方"里程碑事件"章节)
|
|
857
|
+
|
|
858
|
+
#### 里程碑事件
|
|
859
|
+
|
|
860
|
+
> **💡 事件驱动架构**:里程碑管理采用事件驱动设计,推荐使用事件 API 替代回调函数。
|
|
861
|
+
|
|
862
|
+
| 事件名 | 参数 | 触发时机 | 说明 |
|
|
863
|
+
| ------------------------ | --------------------------------------- | ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
|
|
864
|
+
| `add-milestone` | - | 点击工具栏"添加里程碑"按钮时 | 可用于自定义新增里程碑逻辑。如 `useDefaultMilestoneDialog=true`,组件会自动打开内置 MilestoneDialog |
|
|
865
|
+
| `milestone-saved` | `(milestone: Task) => void` | 里程碑保存后(新增或编辑) | 通过内置 MilestoneDialog 保存里程碑后触发。**注意**:组件已自动更新 `milestones` 数据,外部只需监听此事件做额外处理(如调用 API 保存) |
|
|
866
|
+
| `milestone-deleted` | `{ milestoneId: number }` | 里程碑删除后 | 通过内置 MilestoneDialog 删除里程碑后触发。**注意**:组件已自动更新 `milestones` 数据,外部只需监听此事件做额外处理 |
|
|
867
|
+
| `milestone-icon-changed` | `{ milestoneId: number, icon: string }` | 里程碑图标变更后 | 通过内置 MilestoneDialog 修改图标后触发 |
|
|
868
|
+
| `milestone-drag-end` | `(milestone: Task) => void` | 拖拽里程碑结束时 | 里程碑日期已更新。**注意**:组件已自动更新 `milestones` 数据 |
|
|
869
|
+
|
|
870
|
+
**数据同步说明**:
|
|
871
|
+
|
|
872
|
+
- ✅ **组件内部自动更新**:所有里程碑的增删改操作,组件都会自动更新 `props.milestones` 数据
|
|
873
|
+
- ✅ **事件仅做通知**:外部监听事件主要用于:显示提示消息、调用后端 API、更新其他相关数据等
|
|
874
|
+
- ❌ **避免重复操作**:不要在事件处理器中再次修改 `milestones` 数据,否则会导致重复更新
|
|
875
|
+
|
|
876
|
+
#### 示例1:使用事件驱动 API(推荐)
|
|
877
|
+
|
|
878
|
+
使用新的事件 API,组件会自动管理数据,更加简洁:
|
|
879
|
+
|
|
880
|
+
```vue
|
|
881
|
+
<template>
|
|
882
|
+
<div style="height: 600px;">
|
|
883
|
+
<GanttChart
|
|
884
|
+
:milestones="milestones"
|
|
885
|
+
@add-milestone="handleAddMilestone"
|
|
886
|
+
@milestone-saved="handleMilestoneSaved"
|
|
887
|
+
@milestone-deleted="handleMilestoneDeleted"
|
|
888
|
+
@milestone-icon-changed="handleMilestoneIconChanged"
|
|
889
|
+
@milestone-drag-end="handleMilestoneDrag"
|
|
890
|
+
/>
|
|
891
|
+
</div>
|
|
892
|
+
</template>
|
|
893
|
+
|
|
894
|
+
<script setup lang="ts">
|
|
895
|
+
import { ref } from 'vue'
|
|
896
|
+
import { GanttChart } from 'jordium-gantt-vue3'
|
|
897
|
+
import type { Task } from 'jordium-gantt-vue3'
|
|
898
|
+
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
|
|
899
|
+
|
|
900
|
+
const milestones = ref<Task[]>([
|
|
901
|
+
{
|
|
902
|
+
id: 101,
|
|
903
|
+
name: '项目启动',
|
|
904
|
+
startDate: '2025-01-01',
|
|
905
|
+
type: 'milestone',
|
|
906
|
+
icon: 'diamond',
|
|
907
|
+
assignee: '项目经理',
|
|
908
|
+
description: '项目正式启动',
|
|
909
|
+
},
|
|
910
|
+
{
|
|
911
|
+
id: 102,
|
|
912
|
+
name: '需求评审',
|
|
913
|
+
startDate: '2025-01-15',
|
|
914
|
+
type: 'milestone',
|
|
915
|
+
icon: 'flag',
|
|
916
|
+
},
|
|
917
|
+
])
|
|
918
|
+
|
|
919
|
+
// 工具栏"添加里程碑"按钮点击事件
|
|
920
|
+
const handleAddMilestone = () => {
|
|
921
|
+
console.log('准备新增里程碑...')
|
|
922
|
+
// 组件会自动打开 MilestoneDialog(如果 useDefaultMilestoneDialog=true)
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
// 里程碑保存事件(添加或编辑)
|
|
926
|
+
const handleMilestoneSaved = (milestone: Task) => {
|
|
927
|
+
console.log('里程碑已保存:', milestone)
|
|
928
|
+
// 注意:组件已自动更新 milestones 数组
|
|
929
|
+
// 这里只需调用后端 API 保存即可
|
|
930
|
+
// await api.saveMilestone(milestone)
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
// 里程碑删除事件
|
|
934
|
+
const handleMilestoneDeleted = (e: { milestoneId: number }) => {
|
|
935
|
+
console.log('里程碑已删除, ID:', e.milestoneId)
|
|
936
|
+
// 注意:组件已自动从 milestones 数组中移除
|
|
937
|
+
// 这里只需调用后端 API 删除即可
|
|
938
|
+
// await api.deleteMilestone(e.milestoneId)
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
// 里程碑图标变更事件
|
|
942
|
+
const handleMilestoneIconChanged = (e: { milestoneId: number; icon: string }) => {
|
|
943
|
+
console.log('里程碑图标已变更:', e.milestoneId, '->', e.icon)
|
|
944
|
+
// 组件已自动更新图标,这里可以调用 API 保存
|
|
945
|
+
// await api.updateMilestoneIcon(e.milestoneId, e.icon)
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
// 拖拽里程碑结束事件
|
|
949
|
+
const handleMilestoneDrag = (milestone: Task) => {
|
|
950
|
+
console.log('里程碑拖拽完成,新日期:', milestone.startDate)
|
|
951
|
+
// 组件已自动更新日期,这里可以调用 API 保存
|
|
952
|
+
// await api.updateMilestoneDate(milestone.id, milestone.startDate)
|
|
953
|
+
}
|
|
954
|
+
</script>
|
|
955
|
+
```
|
|
956
|
+
|
|
957
|
+
#### 示例2:使用自定义里程碑编辑对话框
|
|
958
|
+
|
|
959
|
+
如果需要完全自定义里程碑编辑界面,可以禁用内置对话框并使用自己的组件:
|
|
960
|
+
|
|
961
|
+
```vue
|
|
962
|
+
<template>
|
|
963
|
+
<div style="height: 600px;">
|
|
964
|
+
<GanttChart
|
|
965
|
+
:milestones="milestones"
|
|
966
|
+
:use-default-milestone-dialog="false"
|
|
967
|
+
@add-milestone="handleAddMilestone"
|
|
968
|
+
@milestone-saved="handleMilestoneSaved"
|
|
969
|
+
@milestone-deleted="handleMilestoneDeleted"
|
|
970
|
+
@milestone-drag-end="handleMilestoneDrag"
|
|
971
|
+
/>
|
|
972
|
+
|
|
973
|
+
<!-- 自定义里程碑编辑对话框 -->
|
|
974
|
+
<CustomMilestoneDialog
|
|
975
|
+
v-model:visible="customDialogVisible"
|
|
976
|
+
:milestone="editingMilestone"
|
|
977
|
+
:is-new="isNewMilestone"
|
|
978
|
+
@save="handleCustomDialogSave"
|
|
979
|
+
@delete="handleCustomDialogDelete"
|
|
980
|
+
/>
|
|
981
|
+
</div>
|
|
982
|
+
</template>
|
|
983
|
+
|
|
984
|
+
<script setup lang="ts">
|
|
985
|
+
import { ref } from 'vue'
|
|
986
|
+
import { GanttChart } from 'jordium-gantt-vue3'
|
|
987
|
+
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
|
|
988
|
+
import CustomMilestoneDialog from './CustomMilestoneDialog.vue'
|
|
989
|
+
import type { Task } from 'jordium-gantt-vue3'
|
|
990
|
+
|
|
991
|
+
const milestones = ref<Task[]>([
|
|
992
|
+
{
|
|
993
|
+
id: 101,
|
|
994
|
+
name: '项目启动',
|
|
995
|
+
startDate: '2025-01-01',
|
|
996
|
+
type: 'milestone',
|
|
997
|
+
icon: 'diamond',
|
|
998
|
+
assignee: '项目经理',
|
|
999
|
+
description: '项目正式启动',
|
|
1000
|
+
},
|
|
1001
|
+
])
|
|
1002
|
+
|
|
1003
|
+
const customDialogVisible = ref(false)
|
|
1004
|
+
const editingMilestone = ref<Task | null>(null)
|
|
1005
|
+
const isNewMilestone = ref(false)
|
|
1006
|
+
|
|
1007
|
+
// 点击工具栏"添加里程碑"按钮
|
|
1008
|
+
const handleAddMilestone = () => {
|
|
1009
|
+
editingMilestone.value = null
|
|
1010
|
+
isNewMilestone.value = true
|
|
1011
|
+
customDialogVisible.value = true
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
// 双击里程碑时打开自定义对话框
|
|
1015
|
+
// 注意:需要监听 Timeline 组件的里程碑双击事件
|
|
1016
|
+
// 或者通过外部按钮/列表项触发编辑
|
|
1017
|
+
const openEditDialog = (milestone: Task) => {
|
|
1018
|
+
editingMilestone.value = { ...milestone }
|
|
1019
|
+
isNewMilestone.value = false
|
|
1020
|
+
customDialogVisible.value = true
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
// 自定义对话框保存事件
|
|
1024
|
+
const handleCustomDialogSave = (milestone: Task) => {
|
|
1025
|
+
if (isNewMilestone.value) {
|
|
1026
|
+
// 新增里程碑
|
|
1027
|
+
const newMilestone = {
|
|
1028
|
+
...milestone,
|
|
1029
|
+
id: Date.now(), // 生成新 ID
|
|
1030
|
+
type: 'milestone',
|
|
1031
|
+
}
|
|
1032
|
+
milestones.value.push(newMilestone)
|
|
1033
|
+
|
|
1034
|
+
// 调用后端 API 保存
|
|
1035
|
+
// await api.createMilestone(newMilestone)
|
|
1036
|
+
} else {
|
|
1037
|
+
// 更新现有里程碑
|
|
1038
|
+
const index = milestones.value.findIndex(m => m.id === milestone.id)
|
|
1039
|
+
if (index !== -1) {
|
|
1040
|
+
milestones.value[index] = { ...milestone }
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
// 调用后端 API 更新
|
|
1044
|
+
// await api.updateMilestone(milestone)
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
customDialogVisible.value = false
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
// 自定义对话框删除事件
|
|
1051
|
+
const handleCustomDialogDelete = (milestoneId: number) => {
|
|
1052
|
+
const index = milestones.value.findIndex(m => m.id === milestoneId)
|
|
1053
|
+
if (index !== -1) {
|
|
1054
|
+
milestones.value.splice(index, 1)
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
// 调用后端 API 删除
|
|
1058
|
+
// await api.deleteMilestone(milestoneId)
|
|
1059
|
+
|
|
1060
|
+
customDialogVisible.value = false
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
// 以下事件处理器仍然有效(用于拖拽等操作)
|
|
1064
|
+
const handleMilestoneSaved = (milestone: Task) => {
|
|
1065
|
+
console.log('里程碑已保存(通过其他方式):', milestone)
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
const handleMilestoneDeleted = (e: { milestoneId: number }) => {
|
|
1069
|
+
console.log('里程碑已删除(通过其他方式):', e.milestoneId)
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
const handleMilestoneDrag = (milestone: Task) => {
|
|
1073
|
+
console.log('里程碑拖拽完成:', milestone.startDate)
|
|
1074
|
+
// 调用 API 更新日期
|
|
1075
|
+
// await api.updateMilestoneDate(milestone.id, milestone.startDate)
|
|
1076
|
+
}
|
|
1077
|
+
</script>
|
|
1078
|
+
```
|
|
1079
|
+
|
|
1080
|
+
**自定义对话框组件示例** (`CustomMilestoneDialog.vue` - 使用 Element Plus):
|
|
1081
|
+
|
|
1082
|
+
> **注意**:以下示例使用 Element Plus UI 框架。你也可以使用其他 UI 框架(如 Ant Design Vue、Naive UI 等)或原生 HTML 实现。
|
|
1083
|
+
|
|
1084
|
+
```vue
|
|
1085
|
+
<template>
|
|
1086
|
+
<el-dialog
|
|
1087
|
+
v-model="dialogVisible"
|
|
1088
|
+
:title="isNew ? '新增里程碑' : '编辑里程碑'"
|
|
1089
|
+
width="500px"
|
|
1090
|
+
@close="handleClose"
|
|
1091
|
+
>
|
|
1092
|
+
<el-form :model="form" label-width="100px">
|
|
1093
|
+
<el-form-item label="里程碑名称">
|
|
1094
|
+
<el-input v-model="form.name" placeholder="请输入里程碑名称" />
|
|
1095
|
+
</el-form-item>
|
|
1096
|
+
|
|
1097
|
+
<el-form-item label="日期">
|
|
1098
|
+
<el-date-picker
|
|
1099
|
+
v-model="form.startDate"
|
|
1100
|
+
type="date"
|
|
1101
|
+
placeholder="选择日期"
|
|
1102
|
+
value-format="YYYY-MM-DD"
|
|
1103
|
+
/>
|
|
1104
|
+
</el-form-item>
|
|
1105
|
+
|
|
1106
|
+
<el-form-item label="负责人">
|
|
1107
|
+
<el-input v-model="form.assignee" placeholder="请输入负责人" />
|
|
1108
|
+
</el-form-item>
|
|
1109
|
+
|
|
1110
|
+
<el-form-item label="图标">
|
|
1111
|
+
<el-select v-model="form.icon" placeholder="选择图标">
|
|
1112
|
+
<el-option label="钻石" value="diamond" />
|
|
1113
|
+
<el-option label="旗帜" value="flag" />
|
|
1114
|
+
<el-option label="星星" value="star" />
|
|
1115
|
+
<el-option label="火箭" value="rocket" />
|
|
1116
|
+
</el-select>
|
|
1117
|
+
</el-form-item>
|
|
1118
|
+
|
|
1119
|
+
<el-form-item label="描述">
|
|
1120
|
+
<el-input v-model="form.description" type="textarea" :rows="3" placeholder="请输入描述" />
|
|
1121
|
+
</el-form-item>
|
|
1122
|
+
</el-form>
|
|
1123
|
+
|
|
1124
|
+
<template #footer>
|
|
1125
|
+
<div class="dialog-footer">
|
|
1126
|
+
<el-button v-if="!isNew" type="danger" @click="handleDelete"> 删除 </el-button>
|
|
1127
|
+
<el-button @click="handleClose">取消</el-button>
|
|
1128
|
+
<el-button type="primary" @click="handleSave">保存</el-button>
|
|
1129
|
+
</div>
|
|
1130
|
+
</template>
|
|
1131
|
+
</el-dialog>
|
|
1132
|
+
</template>
|
|
1133
|
+
|
|
1134
|
+
<script setup lang="ts">
|
|
1135
|
+
import { ref, watch } from 'vue'
|
|
1136
|
+
import type { Task } from 'jordium-gantt-vue3'
|
|
1137
|
+
|
|
1138
|
+
interface Props {
|
|
1139
|
+
visible: boolean
|
|
1140
|
+
milestone: Task | null
|
|
1141
|
+
isNew: boolean
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
const props = defineProps<Props>()
|
|
1145
|
+
const emit = defineEmits<{
|
|
1146
|
+
'update:visible': [value: boolean]
|
|
1147
|
+
save: [milestone: Task]
|
|
1148
|
+
delete: [milestoneId: number]
|
|
1149
|
+
}>()
|
|
1150
|
+
|
|
1151
|
+
const dialogVisible = ref(false)
|
|
1152
|
+
const form = ref({
|
|
1153
|
+
id: 0,
|
|
1154
|
+
name: '',
|
|
1155
|
+
startDate: '',
|
|
1156
|
+
assignee: '',
|
|
1157
|
+
icon: 'diamond',
|
|
1158
|
+
description: '',
|
|
1159
|
+
type: 'milestone',
|
|
1160
|
+
})
|
|
1161
|
+
|
|
1162
|
+
watch(
|
|
1163
|
+
() => props.visible,
|
|
1164
|
+
val => {
|
|
1165
|
+
dialogVisible.value = val
|
|
1166
|
+
if (val) {
|
|
1167
|
+
if (props.milestone) {
|
|
1168
|
+
// 编辑模式,填充数据
|
|
1169
|
+
form.value = { ...props.milestone }
|
|
1170
|
+
} else {
|
|
1171
|
+
// 新增模式,重置表单
|
|
1172
|
+
form.value = {
|
|
1173
|
+
id: 0,
|
|
1174
|
+
name: '',
|
|
1175
|
+
startDate: new Date().toISOString().split('T')[0],
|
|
1176
|
+
assignee: '',
|
|
1177
|
+
icon: 'diamond',
|
|
1178
|
+
description: '',
|
|
1179
|
+
type: 'milestone',
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
)
|
|
1185
|
+
|
|
1186
|
+
watch(dialogVisible, val => {
|
|
1187
|
+
emit('update:visible', val)
|
|
1188
|
+
})
|
|
1189
|
+
|
|
1190
|
+
const handleClose = () => {
|
|
1191
|
+
dialogVisible.value = false
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
const handleSave = () => {
|
|
1195
|
+
if (!form.value.name || !form.value.startDate) {
|
|
1196
|
+
alert('请填写必填项')
|
|
1197
|
+
return
|
|
1198
|
+
}
|
|
1199
|
+
emit('save', { ...form.value })
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
const handleDelete = () => {
|
|
1203
|
+
if (confirm('确定要删除这个里程碑吗?')) {
|
|
1204
|
+
emit('delete', form.value.id)
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
</script>
|
|
1208
|
+
```
|
|
1209
|
+
|
|
1210
|
+
> **💡 自定义对话框说明**:
|
|
1211
|
+
>
|
|
1212
|
+
> - 设置 `use-default-milestone-dialog="false"` 禁用内置对话框
|
|
1213
|
+
> - 监听 `@add-milestone` 事件打开自定义对话框
|
|
1214
|
+
> - 需要手动管理 `milestones` 数组的增删改
|
|
1215
|
+
> - 仍然可以监听其他事件(如 `@milestone-drag-end`)处理拖拽等操作
|
|
1216
|
+
> - 适合需要复杂表单验证、特殊 UI 设计或额外字段的场景
|
|
1217
|
+
|
|
1218
|
+
---
|
|
1219
|
+
|
|
1220
|
+
## ⚙️ 配置与扩展
|
|
1221
|
+
|
|
1222
|
+
本章节详细介绍 GanttChart 组件的配置选项和扩展能力,包括组件配置、主题与国际化、自定义扩展三个部分。
|
|
1223
|
+
|
|
1224
|
+
### 组件配置
|
|
1225
|
+
|
|
1226
|
+
#### ToolbarConfig(工具栏配置)
|
|
1227
|
+
|
|
1228
|
+
自定义工具栏显示的功能按钮和时间刻度选项。
|
|
1229
|
+
|
|
1230
|
+
**类型定义:**
|
|
1231
|
+
|
|
1232
|
+
| 字段名 | 类型 | 默认值 | 说明 |
|
|
1233
|
+
| --------------------- | ----------------- | ----------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
|
|
1234
|
+
| `showAddTask` | `boolean` | `true` | 显示"添加任务"按钮 |
|
|
1235
|
+
| `showAddMilestone` | `boolean` | `true` | 显示"添加里程碑"按钮 |
|
|
1236
|
+
| `showTodayLocate` | `boolean` | `true` | 显示"定位到今天"按钮 |
|
|
1237
|
+
| `showExportCsv` | `boolean` | `true` | 显示"导出 CSV"按钮 |
|
|
1238
|
+
| `showExportPdf` | `boolean` | `true` | 显示"导出 PDF"按钮 |
|
|
1239
|
+
| `showLanguage` | `boolean` | `true` | 显示"语言切换"按钮(中/英文) |
|
|
1240
|
+
| `showTheme` | `boolean` | `true` | 显示"主题切换"按钮(亮色/暗色) |
|
|
1241
|
+
| `showFullscreen` | `boolean` | `true` | 显示"全屏"按钮 |
|
|
1242
|
+
| `showTimeScale` | `boolean` | `true` | 显示时间刻度按钮组(控制整组按钮的显隐) |
|
|
1243
|
+
| `timeScaleDimensions` | `TimelineScale[]` | `['hour', 'day', 'week', 'month', 'quarter', 'year']` | 设置时间刻度按钮组要显示的维度,可选值:`'hour'`、`'day'`、`'week'`、`'month'`、`'quarter'`、`'year'` |
|
|
1244
|
+
| `defaultTimeScale` | `TimelineScale` | `'week'` | 默认选中的时间刻度 |
|
|
1245
|
+
| `showExpandCollapse` | `boolean` | `true` | 显示"全部展开/折叠"按钮(用于父子任务树形结构) |
|
|
1246
|
+
|
|
1247
|
+
**TimelineScale 类型说明:**
|
|
1248
|
+
|
|
1249
|
+
```typescript
|
|
1250
|
+
type TimelineScale = 'hour' | 'day' | 'week' | 'month' | 'quarter' | 'year'
|
|
1251
|
+
|
|
1252
|
+
// 也可以使用常量形式
|
|
1253
|
+
import { TimelineScale } from 'jordium-gantt-vue3'
|
|
1254
|
+
|
|
1255
|
+
TimelineScale.HOUR // 'hour' - 小时视图
|
|
1256
|
+
TimelineScale.DAY // 'day' - 日视图
|
|
1257
|
+
TimelineScale.WEEK // 'week' - 周视图
|
|
1258
|
+
TimelineScale.MONTH // 'month' - 月视图
|
|
1259
|
+
TimelineScale.QUARTER // 'quarter' - 季度视图
|
|
1260
|
+
TimelineScale.YEAR // 'year' - 年视图
|
|
1261
|
+
```
|
|
1262
|
+
|
|
1263
|
+
**示例1:完整配置(显示所有功能)**
|
|
1264
|
+
|
|
1265
|
+
```vue
|
|
1266
|
+
<template>
|
|
1267
|
+
<GanttChart :tasks="tasks" :toolbar-config="toolbarConfig" />
|
|
1268
|
+
</template>
|
|
1269
|
+
|
|
1270
|
+
<script setup lang="ts">
|
|
1271
|
+
import { GanttChart } from 'jordium-gantt-vue3'
|
|
1272
|
+
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
|
|
1273
|
+
import type { ToolbarConfig } from 'jordium-gantt-vue3'
|
|
1274
|
+
|
|
1275
|
+
const toolbarConfig: ToolbarConfig = {
|
|
1276
|
+
showAddTask: true, // 显示添加任务按钮
|
|
1277
|
+
showAddMilestone: true, // 显示添加里程碑按钮
|
|
1278
|
+
showTodayLocate: true, // 显示定位到今天按钮
|
|
1279
|
+
showExportCsv: true, // 显示导出CSV按钮
|
|
1280
|
+
showExportPdf: true, // 显示导出PDF按钮
|
|
1281
|
+
showLanguage: true, // 显示语言切换按钮
|
|
1282
|
+
showTheme: true, // 显示主题切换按钮
|
|
1283
|
+
showFullscreen: true, // 显示全屏按钮
|
|
1284
|
+
showTimeScale: true, // 显示时间刻度按钮组
|
|
1285
|
+
timeScaleDimensions: [
|
|
1286
|
+
// 显示所有时间刻度维度
|
|
1287
|
+
'hour',
|
|
1288
|
+
'day',
|
|
1289
|
+
'week',
|
|
1290
|
+
'month',
|
|
1291
|
+
'quarter',
|
|
1292
|
+
'year',
|
|
1293
|
+
],
|
|
1294
|
+
defaultTimeScale: 'week', // 默认选中周视图
|
|
1295
|
+
showExpandCollapse: true, // 显示展开/折叠按钮
|
|
1296
|
+
}
|
|
1297
|
+
</script>
|
|
1298
|
+
```
|
|
1299
|
+
|
|
1300
|
+
**示例2:精简配置(只显示常用功能)**
|
|
1301
|
+
|
|
1302
|
+
```vue
|
|
1303
|
+
<script setup lang="ts">
|
|
1304
|
+
import type { ToolbarConfig } from 'jordium-gantt-vue3'
|
|
1305
|
+
|
|
1306
|
+
const toolbarConfig: ToolbarConfig = {
|
|
1307
|
+
showAddTask: true, // 保留添加任务
|
|
1308
|
+
showAddMilestone: true, // 保留添加里程碑
|
|
1309
|
+
showTodayLocate: true, // 保留定位今天
|
|
1310
|
+
showExportCsv: false, // 隐藏导出CSV
|
|
1311
|
+
showExportPdf: false, // 隐藏导出PDF
|
|
1312
|
+
showLanguage: false, // 隐藏语言切换(固定使用一种语言)
|
|
1313
|
+
showTheme: true, // 保留主题切换
|
|
1314
|
+
showFullscreen: true, // 保留全屏
|
|
1315
|
+
showTimeScale: true, // 显示时间刻度
|
|
1316
|
+
timeScaleDimensions: [
|
|
1317
|
+
// 只显示日/周/月三种刻度
|
|
1318
|
+
'day',
|
|
1319
|
+
'week',
|
|
1320
|
+
'month',
|
|
1321
|
+
],
|
|
1322
|
+
defaultTimeScale: 'week', // 默认周视图
|
|
1323
|
+
showExpandCollapse: true, // 保留展开/折叠
|
|
1324
|
+
}
|
|
1325
|
+
</script>
|
|
1326
|
+
```
|
|
1327
|
+
|
|
1328
|
+
**示例3:使用 TimelineScale 常量**
|
|
1329
|
+
|
|
1330
|
+
```vue
|
|
1331
|
+
<script setup lang="ts">
|
|
1332
|
+
import { TimelineScale } from 'jordium-gantt-vue3'
|
|
1333
|
+
import type { ToolbarConfig } from 'jordium-gantt-vue3'
|
|
1334
|
+
|
|
1335
|
+
const toolbarConfig: ToolbarConfig = {
|
|
1336
|
+
showTimeScale: true,
|
|
1337
|
+
timeScaleDimensions: [
|
|
1338
|
+
TimelineScale.DAY,
|
|
1339
|
+
TimelineScale.WEEK,
|
|
1340
|
+
TimelineScale.MONTH,
|
|
1341
|
+
TimelineScale.QUARTER,
|
|
1342
|
+
],
|
|
1343
|
+
defaultTimeScale: TimelineScale.MONTH, // 默认月视图
|
|
1344
|
+
}
|
|
1345
|
+
</script>
|
|
1346
|
+
```
|
|
1347
|
+
|
|
1348
|
+
**示例4:极简配置(适合嵌入式使用)**
|
|
1349
|
+
|
|
1350
|
+
```vue
|
|
1351
|
+
<script setup lang="ts">
|
|
1352
|
+
import type { ToolbarConfig } from 'jordium-gantt-vue3'
|
|
1353
|
+
|
|
1354
|
+
const toolbarConfig: ToolbarConfig = {
|
|
1355
|
+
showAddTask: false, // 隐藏所有编辑按钮
|
|
1356
|
+
showAddMilestone: false,
|
|
1357
|
+
showTodayLocate: true, // 只保留导航功能
|
|
1358
|
+
showExportCsv: false,
|
|
1359
|
+
showExportPdf: false,
|
|
1360
|
+
showLanguage: false,
|
|
1361
|
+
showTheme: false,
|
|
1362
|
+
showFullscreen: false,
|
|
1363
|
+
showTimeScale: true, // 保留时间刻度切换
|
|
1364
|
+
timeScaleDimensions: ['week', 'month'],
|
|
1365
|
+
defaultTimeScale: 'month',
|
|
1366
|
+
showExpandCollapse: false, // 隐藏展开/折叠
|
|
1367
|
+
}
|
|
1368
|
+
</script>
|
|
1369
|
+
```
|
|
1370
|
+
|
|
1371
|
+
> **💡 配置建议**:
|
|
1372
|
+
>
|
|
1373
|
+
> - **默认配置**:不传 `toolbar-config` 时,所有按钮默认显示
|
|
1374
|
+
> - **按需显示**:根据业务需求隐藏不需要的功能按钮
|
|
1375
|
+
> - **时间刻度**:`timeScaleDimensions` 控制显示哪些时间维度,建议选择 2-4 个常用维度
|
|
1376
|
+
> - **响应式布局**:工具栏会自动适配容器宽度,按钮过多时会折叠到更多菜单中
|
|
1377
|
+
|
|
1378
|
+
#### TaskListConfig(任务列表配置)
|
|
1379
|
+
|
|
1380
|
+
自定义任务列表的显示列、宽度限制等。任务列表位于甘特图左侧,显示任务的详细信息。
|
|
1381
|
+
|
|
1382
|
+
**类型定义:**
|
|
1383
|
+
|
|
1384
|
+
| 字段名 | 类型 | 默认值 | 说明 |
|
|
1385
|
+
| ---------------- | ------------------------ | ------- | ------------------------------------------------------------------------------ |
|
|
1386
|
+
| `columns` | `TaskListColumnConfig[]` | 默认8列 | 任务列表的列配置数组,定义显示哪些列及其属性 |
|
|
1387
|
+
| `showAllColumns` | `boolean` | `true` | 是否显示所有列。`true` 时忽略 `columns` 中的 `visible` 设置 |
|
|
1388
|
+
| `defaultWidth` | `number \| string` | `320` | 默认展开宽度。支持像素数字(如 `320`)或百分比字符串(如 `'30%'`) |
|
|
1389
|
+
| `minWidth` | `number \| string` | `280` | 最小宽度。支持像素数字(如 `280`)或百分比字符串(如 `'20%'`)。不能小于 280px |
|
|
1390
|
+
| `maxWidth` | `number \| string` | `1160` | 最大宽度。支持像素数字(如 `1160`)或百分比字符串(如 `'80%'`) |
|
|
1391
|
+
|
|
1392
|
+
**TaskListColumnConfig 类型定义:**
|
|
1393
|
+
|
|
1394
|
+
| 字段名 | 类型 | 必填 | 说明 |
|
|
1395
|
+
| ---------- | --------- | ---- | ---------------------------------------------------------------- |
|
|
1396
|
+
| `key` | `string` | ✅ | 列的唯一标识符,用于访问 Task 对象中的字段,也用于国际化 |
|
|
1397
|
+
| `label` | `string` | - | 列的显示标签(表头文字) |
|
|
1398
|
+
| `cssClass` | `string` | - | 自定义 CSS 类名 |
|
|
1399
|
+
| `width` | `number` | - | 列宽度(单位:像素) |
|
|
1400
|
+
| `visible` | `boolean` | - | 是否显示该列,默认 `true`。当 `showAllColumns=true` 时此设置无效 |
|
|
1401
|
+
|
|
1402
|
+
**示例1:基础配置(调整宽度)**
|
|
1403
|
+
|
|
1404
|
+
```vue
|
|
1405
|
+
<template>
|
|
1406
|
+
<GanttChart :tasks="tasks" :task-list-config="taskListConfig" />
|
|
1407
|
+
</template>
|
|
1408
|
+
|
|
1409
|
+
<script setup lang="ts">
|
|
1410
|
+
import { GanttChart } from 'jordium-gantt-vue3'
|
|
1411
|
+
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
|
|
1412
|
+
import type { TaskListConfig } from 'jordium-gantt-vue3'
|
|
1413
|
+
|
|
1414
|
+
const taskListConfig: TaskListConfig = {
|
|
1415
|
+
defaultWidth: 450, // 默认宽度450px(比默认值320px更宽)
|
|
1416
|
+
minWidth: 300, // 最小宽度300px
|
|
1417
|
+
maxWidth: 1200, // 最大宽度1200px
|
|
1418
|
+
}
|
|
1419
|
+
</script>
|
|
1420
|
+
```
|
|
1421
|
+
|
|
1422
|
+
**示例2:使用百分比宽度**
|
|
1423
|
+
|
|
1424
|
+
```vue
|
|
1425
|
+
<template>
|
|
1426
|
+
<GanttChart :tasks="tasks" :task-list-config="taskListConfig" />
|
|
1427
|
+
</template>
|
|
1428
|
+
|
|
1429
|
+
<script setup lang="ts">
|
|
1430
|
+
import { GanttChart } from 'jordium-gantt-vue3'
|
|
1431
|
+
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
|
|
1432
|
+
import type { TaskListConfig } from 'jordium-gantt-vue3'
|
|
1433
|
+
|
|
1434
|
+
const taskListConfig: TaskListConfig = {
|
|
1435
|
+
defaultWidth: '25%', // 默认占容器宽度的25%
|
|
1436
|
+
minWidth: '15%', // 最小占15%
|
|
1437
|
+
maxWidth: '60%', // 最大占60%
|
|
1438
|
+
}
|
|
1439
|
+
</script>
|
|
1440
|
+
```
|
|
1441
|
+
|
|
1442
|
+
**示例3:自定义显示列(标准配置)**
|
|
1443
|
+
|
|
1444
|
+
根据业务需求,可以自定义要显示的列、列宽度和显示顺序。建议先定义列配置数组,再赋值给 `columns` 属性。
|
|
1445
|
+
|
|
1446
|
+
```vue
|
|
1447
|
+
<template>
|
|
1448
|
+
<GanttChart :tasks="tasks" :task-list-config="taskListConfig" />
|
|
1449
|
+
</template>
|
|
1450
|
+
|
|
1451
|
+
<script setup lang="ts">
|
|
1452
|
+
import { GanttChart } from 'jordium-gantt-vue3'
|
|
1453
|
+
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
|
|
1454
|
+
import type { TaskListConfig, TaskListColumnConfig } from 'jordium-gantt-vue3'
|
|
1455
|
+
|
|
1456
|
+
// 定义要显示的列配置
|
|
1457
|
+
const columns: TaskListColumnConfig[] = [
|
|
1458
|
+
{ key: 'predecessor', label: '前置任务', visible: true },
|
|
1459
|
+
{ key: 'assignee', label: '负责人', visible: true },
|
|
1460
|
+
{ key: 'startDate', label: '开始日期', visible: true },
|
|
1461
|
+
{ key: 'endDate', label: '结束日期', visible: true },
|
|
1462
|
+
{ key: 'estimatedHours', label: '预估工时', visible: true },
|
|
1463
|
+
{ key: 'actualHours', label: '实际工时', visible: true },
|
|
1464
|
+
{ key: 'progress', label: '进度', visible: true },
|
|
1465
|
+
]
|
|
1466
|
+
|
|
1467
|
+
const taskListConfig: TaskListConfig = {
|
|
1468
|
+
columns,
|
|
1469
|
+
defaultWidth: 450,
|
|
1470
|
+
minWidth: 300,
|
|
1471
|
+
maxWidth: 1200,
|
|
1472
|
+
}
|
|
1473
|
+
</script>
|
|
1474
|
+
```
|
|
1475
|
+
|
|
1476
|
+
**示例4:精简列配置**
|
|
1477
|
+
|
|
1478
|
+
只显示核心信息列,适合空间有限或需要简洁展示的场景。
|
|
1479
|
+
|
|
1480
|
+
```vue
|
|
1481
|
+
<script setup lang="ts">
|
|
1482
|
+
import type { TaskListConfig, TaskListColumnConfig } from 'jordium-gantt-vue3'
|
|
1483
|
+
|
|
1484
|
+
// 定义精简列配置
|
|
1485
|
+
const columns: TaskListColumnConfig[] = [
|
|
1486
|
+
{ key: 'name', label: '任务', visible: true },
|
|
1487
|
+
{ key: 'assignee', label: '负责人', width: 80, visible: true },
|
|
1488
|
+
{ key: 'progress', label: '进度', width: 60, visible: true },
|
|
1489
|
+
]
|
|
1490
|
+
|
|
1491
|
+
const taskListConfig: TaskListConfig = {
|
|
1492
|
+
columns,
|
|
1493
|
+
defaultWidth: 350,
|
|
1494
|
+
minWidth: 280,
|
|
1495
|
+
maxWidth: 500,
|
|
1496
|
+
showAllColumns: false, // 只显示 visible=true 的列
|
|
1497
|
+
}
|
|
1498
|
+
</script>
|
|
1499
|
+
```
|
|
1500
|
+
|
|
1501
|
+
**示例5:自定义业务列**
|
|
1502
|
+
|
|
1503
|
+
添加业务相关的自定义列,需要确保 Task 对象中包含对应字段。
|
|
1504
|
+
|
|
1505
|
+
```vue
|
|
1506
|
+
<script setup lang="ts">
|
|
1507
|
+
import type { TaskListConfig, TaskListColumnConfig } from 'jordium-gantt-vue3'
|
|
1508
|
+
|
|
1509
|
+
// 定义包含自定义列的配置
|
|
1510
|
+
const columns: TaskListColumnConfig[] = [
|
|
1511
|
+
{ key: 'name', label: '任务名称', visible: true },
|
|
1512
|
+
{ key: 'priority', label: '优先级', width: 80, visible: true }, // 自定义列
|
|
1513
|
+
{ key: 'department', label: '部门', width: 100, visible: true }, // 自定义列
|
|
1514
|
+
{ key: 'status', label: '状态', width: 80, visible: true }, // 自定义列
|
|
1515
|
+
{ key: 'assignee', label: '负责人', visible: true },
|
|
1516
|
+
{ key: 'startDate', label: '开始日期', visible: true },
|
|
1517
|
+
{ key: 'endDate', label: '结束日期', visible: true },
|
|
1518
|
+
{ key: 'progress', label: '进度', visible: true },
|
|
1519
|
+
]
|
|
1520
|
+
|
|
1521
|
+
const taskListConfig: TaskListConfig = {
|
|
1522
|
+
columns,
|
|
1523
|
+
}
|
|
1524
|
+
</script>
|
|
1525
|
+
```
|
|
1526
|
+
|
|
1527
|
+
**示例6:动态列配置**
|
|
1528
|
+
|
|
1529
|
+
配合 `ref` 和 `computed` 实现列的动态显示/隐藏和宽度调整。
|
|
1530
|
+
|
|
1531
|
+
```vue
|
|
1532
|
+
<template>
|
|
1533
|
+
<GanttChart :tasks="tasks" :task-list-config="taskListConfig" />
|
|
1534
|
+
</template>
|
|
1535
|
+
|
|
1536
|
+
<script setup lang="ts">
|
|
1537
|
+
import { ref, computed } from 'vue'
|
|
1538
|
+
import { GanttChart } from 'jordium-gantt-vue3'
|
|
1539
|
+
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
|
|
1540
|
+
import type { TaskListConfig, TaskListColumnConfig } from 'jordium-gantt-vue3'
|
|
1541
|
+
|
|
1542
|
+
// 定义可动态配置的列
|
|
1543
|
+
const availableColumns = ref<TaskListColumnConfig[]>([
|
|
1544
|
+
{ key: 'predecessor', label: '前置任务', visible: true },
|
|
1545
|
+
{ key: 'assignee', label: '负责人', visible: true },
|
|
1546
|
+
{ key: 'startDate', label: '开始日期', visible: true },
|
|
1547
|
+
{ key: 'endDate', label: '结束日期', visible: true },
|
|
1548
|
+
{ key: 'estimatedHours', label: '预估工时', visible: true },
|
|
1549
|
+
{ key: 'actualHours', label: '实际工时', visible: true },
|
|
1550
|
+
{ key: 'progress', label: '进度', visible: true },
|
|
1551
|
+
{ key: 'custom', label: '自定义列', visible: true, width: 120 },
|
|
1552
|
+
])
|
|
1553
|
+
|
|
1554
|
+
// 定义宽度配置
|
|
1555
|
+
const taskListWidth = ref({
|
|
1556
|
+
defaultWidth: 450,
|
|
1557
|
+
minWidth: 300,
|
|
1558
|
+
maxWidth: 1200,
|
|
1559
|
+
})
|
|
1560
|
+
|
|
1561
|
+
// 使用计算属性动态生成配置
|
|
1562
|
+
const taskListConfig = computed<TaskListConfig>(() => ({
|
|
1563
|
+
columns: availableColumns.value,
|
|
1564
|
+
defaultWidth: taskListWidth.value.defaultWidth,
|
|
1565
|
+
minWidth: taskListWidth.value.minWidth,
|
|
1566
|
+
maxWidth: taskListWidth.value.maxWidth,
|
|
1567
|
+
}))
|
|
1568
|
+
</script>
|
|
1569
|
+
```
|
|
1570
|
+
|
|
1571
|
+
> **💡 配置说明**:
|
|
1572
|
+
>
|
|
1573
|
+
> - **默认行为**:不传 `task-list-config` 时,显示所有 8 个默认列,宽度为 320px
|
|
1574
|
+
> - **宽度单位**:支持像素(`number`)和百分比(`string`,如 `'30%'`)两种方式
|
|
1575
|
+
> - **百分比计算**:基于甘特图容器的总宽度,响应式调整
|
|
1576
|
+
> - **列顺序**:`columns` 数组的顺序决定列的显示顺序
|
|
1577
|
+
> - **列配置规范**:建议先定义 `TaskListColumnConfig[]` 类型的列数组,再赋值给 `columns` 属性
|
|
1578
|
+
> - **自定义列支持**:Task 接口通过 `[key: string]: unknown` 索引签名支持任意自定义字段,组件会通过 `task[column.key]` 动态读取列值,无需修改 Task 接口即可添加自定义列
|
|
1579
|
+
> - **动态配置**:配合 `ref` 和 `computed` 可实现列的动态显示/隐藏和宽度调整
|
|
1580
|
+
> - **最小宽度限制**:`minWidth` 不能小于 280px,这是保证基本可用性的最小值
|
|
1581
|
+
|
|
1582
|
+
#### TaskBarConfig(任务条配置)
|
|
1583
|
+
|
|
1584
|
+
控制任务条的显示内容和交互行为。
|
|
1585
|
+
|
|
1586
|
+
**配置字段:**
|
|
1587
|
+
|
|
1588
|
+
| 字段名 | 类型 | 默认值 | 说明 |
|
|
1589
|
+
| ------------------- | --------- | ------- | ------------------------------- |
|
|
1590
|
+
| `showAvatar` | `boolean` | `true` | 是否展示头像 |
|
|
1591
|
+
| `showTitle` | `boolean` | `true` | 是否展示标题文字 |
|
|
1592
|
+
| `showProgress` | `boolean` | `true` | 是否展示进度文字 |
|
|
1593
|
+
| `dragThreshold` | `number` | `5` | 拖拽触发阈值(像素) |
|
|
1594
|
+
| `resizeHandleWidth` | `number` | `5` | 拉伸手柄宽度(像素),最大 15px |
|
|
1595
|
+
| `enableDragDelay` | `boolean` | `false` | 是否启用拖拽延迟(防止误触) |
|
|
1596
|
+
| `dragDelayTime` | `number` | `150` | 拖拽延迟时间(毫秒) |
|
|
1597
|
+
|
|
1598
|
+
> **💡 编辑权限控制**:
|
|
1599
|
+
>
|
|
1600
|
+
> - **全局控制**:使用 `<GanttChart :allow-drag-and-resize="false" />` 禁用所有任务的拖拽/拉伸
|
|
1601
|
+
> - **单个任务控制**:设置任务对象的 `isEditable: false` 属性单独控制某个任务
|
|
1602
|
+
|
|
1603
|
+
**示例1:完整配置**
|
|
1604
|
+
|
|
1605
|
+
```vue
|
|
1606
|
+
<template>
|
|
1607
|
+
<GanttChart :tasks="tasks" :task-bar-config="taskBarConfig" />
|
|
1608
|
+
</template>
|
|
1609
|
+
|
|
1610
|
+
<script setup lang="ts">
|
|
1611
|
+
import { GanttChart } from 'jordium-gantt-vue3'
|
|
1612
|
+
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
|
|
1613
|
+
import type { TaskBarConfig } from 'jordium-gantt-vue3'
|
|
1614
|
+
|
|
1615
|
+
const taskBarConfig: TaskBarConfig = {
|
|
1616
|
+
showAvatar: true,
|
|
1617
|
+
showTitle: true,
|
|
1618
|
+
showProgress: true,
|
|
1619
|
+
dragThreshold: 8,
|
|
1620
|
+
resizeHandleWidth: 8,
|
|
1621
|
+
enableDragDelay: true,
|
|
1622
|
+
dragDelayTime: 200,
|
|
1623
|
+
}
|
|
1624
|
+
</script>
|
|
1625
|
+
```
|
|
1626
|
+
|
|
1627
|
+
**示例2:全局只读模式**
|
|
1628
|
+
|
|
1629
|
+
禁用所有任务的编辑操作。
|
|
1630
|
+
|
|
1631
|
+
```vue
|
|
1632
|
+
<template>
|
|
1633
|
+
<GanttChart :tasks="tasks" :allow-drag-and-resize="false" />
|
|
1634
|
+
</template>
|
|
1635
|
+
```
|
|
1636
|
+
|
|
1637
|
+
**示例3:单个任务只读**
|
|
1638
|
+
|
|
1639
|
+
仅某些任务不可编辑,其他任务正常。
|
|
1640
|
+
|
|
1641
|
+
```vue
|
|
1642
|
+
<script setup lang="ts">
|
|
1643
|
+
import type { Task } from 'jordium-gantt-vue3'
|
|
1644
|
+
|
|
1645
|
+
const tasks: Task[] = [
|
|
1646
|
+
{
|
|
1647
|
+
id: 1,
|
|
1648
|
+
name: '可编辑任务',
|
|
1649
|
+
startDate: '2025-01-01',
|
|
1650
|
+
endDate: '2025-01-10',
|
|
1651
|
+
// isEditable 默认为 true
|
|
1652
|
+
},
|
|
1653
|
+
{
|
|
1654
|
+
id: 2,
|
|
1655
|
+
name: '只读任务(已锁定)',
|
|
1656
|
+
startDate: '2025-01-05',
|
|
1657
|
+
endDate: '2025-01-15',
|
|
1658
|
+
isEditable: false, // 此任务不可拖拽/拉伸
|
|
1659
|
+
},
|
|
1660
|
+
]
|
|
1661
|
+
</script>
|
|
1662
|
+
```
|
|
1663
|
+
|
|
1664
|
+
**示例4:精简显示**
|
|
1665
|
+
|
|
1666
|
+
仅显示任务条,隐藏头像、标题和进度文字。
|
|
1667
|
+
|
|
1668
|
+
```vue
|
|
1669
|
+
<script setup lang="ts">
|
|
1670
|
+
import type { TaskBarConfig } from 'jordium-gantt-vue3'
|
|
1671
|
+
|
|
1672
|
+
const taskBarConfig: TaskBarConfig = {
|
|
1673
|
+
showAvatar: false,
|
|
1674
|
+
showTitle: false,
|
|
1675
|
+
showProgress: false,
|
|
1676
|
+
}
|
|
1677
|
+
</script>
|
|
1678
|
+
```
|
|
1679
|
+
|
|
1680
|
+
**示例5:防误触配置**
|
|
1681
|
+
|
|
1682
|
+
移动端或触摸屏场景下,增加拖拽阈值和延迟时间。
|
|
1683
|
+
|
|
1684
|
+
```vue
|
|
1685
|
+
<script setup lang="ts">
|
|
1686
|
+
import { computed, ref } from 'vue'
|
|
1687
|
+
import type { TaskBarConfig } from 'jordium-gantt-vue3'
|
|
1688
|
+
|
|
1689
|
+
const isTouchDevice = ref('ontouchstart' in window)
|
|
1690
|
+
|
|
1691
|
+
const taskBarConfig = computed<TaskBarConfig>(() => ({
|
|
1692
|
+
dragThreshold: isTouchDevice.value ? 10 : 5,
|
|
1693
|
+
resizeHandleWidth: isTouchDevice.value ? 12 : 5,
|
|
1694
|
+
enableDragDelay: isTouchDevice.value,
|
|
1695
|
+
dragDelayTime: isTouchDevice.value ? 300 : 150,
|
|
1696
|
+
}))
|
|
1697
|
+
</script>
|
|
1698
|
+
```
|
|
1699
|
+
|
|
1700
|
+
#### Timeline 容器自动填充配置
|
|
1701
|
+
|
|
1702
|
+
组件内置了智能的时间线范围计算逻辑,确保无论任务数据量多少、任务持续时间长短,时间线始终能够填充满容器宽度,提供最佳的视觉体验。
|
|
1703
|
+
|
|
1704
|
+
**核心设计思路:**
|
|
1705
|
+
|
|
1706
|
+
1. **基础缓冲机制**:在任务的实际时间范围基础上,根据不同视图添加固定的缓冲区
|
|
1707
|
+
|
|
1708
|
+
- 小时视图:任务范围前后各 ±1 天
|
|
1709
|
+
- 日视图:任务范围前后各 ±30 天
|
|
1710
|
+
- 周视图:任务范围前后各 ±8 周(约2个月)
|
|
1711
|
+
- 月视图:任务范围前后各 ±1 年
|
|
1712
|
+
- 季度视图:任务范围前后各 ±1 年
|
|
1713
|
+
- 年视图:任务范围前后各 ±1 年
|
|
1714
|
+
|
|
1715
|
+
2. **容器宽度适配**:基础缓冲后,如果计算出的时间线宽度小于容器宽度,会自动扩展范围
|
|
1716
|
+
|
|
1717
|
+
- 计算容器需要的时间单位数(天/周/月/季度/年)
|
|
1718
|
+
- 在基础范围两侧**对称扩展**,确保时间线填充满容器
|
|
1719
|
+
|
|
1720
|
+
3. **空数据处理**:当没有任务数据时,根据容器宽度和时间刻度计算合理的时间范围
|
|
1721
|
+
|
|
1722
|
+
- 以当前日期为中心
|
|
1723
|
+
- 根据容器宽度动态计算需要显示的时间跨度
|
|
1724
|
+
- 确保最小显示范围(如日视图至少60天,周视图至少20周等)
|
|
1725
|
+
|
|
1726
|
+
4. **视图切换独立计算**:每次切换时间刻度时,都会独立重新计算该视图的最佳时间范围
|
|
1727
|
+
- 避免不同视图共享缓存导致的范围不合理
|
|
1728
|
+
- 每个视图都能获得最优的显示效果
|
|
1729
|
+
|
|
1730
|
+
**各视图计算模式对照表:**
|
|
1731
|
+
|
|
1732
|
+
| 视图 | 单位宽度 | 基础缓冲 | 空数据最小范围 | 容器自动填充? |
|
|
1733
|
+
| -------- | -------------------- | -------- | -------------- | -------------- |
|
|
1734
|
+
| 小时视图 | 30px/时 | ±1天 | 3天 | ✅ |
|
|
1735
|
+
| 日视图 | 30px/天 | ±30天 | 60天 | ✅ |
|
|
1736
|
+
| 周视图 | 60px/周 | ±2月 | 20周 | ✅ |
|
|
1737
|
+
| 月视图 | 60px/月 | ±1年 | 3年 | ✅ |
|
|
1738
|
+
| 季度视图 | 60px/季度 (240px/年) | ±1年 | 5年 | ✅ |
|
|
1739
|
+
| 年视图 | 360px/年 | ±1年 | 5年 | ✅ |
|
|
1740
|
+
|
|
1741
|
+
**实际应用场景:**
|
|
1742
|
+
|
|
1743
|
+
- **短期任务**(如1周项目, 分辨率1080):
|
|
1744
|
+
|
|
1745
|
+
- 不会导致时间线过窄,自动扩展到填充满容器
|
|
1746
|
+
- 日视图:1周(7天×30px=210px) → 扩展至 ≥1200px(约40天)
|
|
1747
|
+
- 周视图:1周(60px) → 扩展至 ≥1200px(约20周)
|
|
1748
|
+
|
|
1749
|
+
- **长期项目**(如2年项目):
|
|
1750
|
+
|
|
1751
|
+
- 添加固定缓冲后,自动适配容器
|
|
1752
|
+
- 月视图:24个月 + 缓冲 → 如需要则扩展至容器宽度
|
|
1753
|
+
- 季度视图:8个季度 + 缓冲 → 如需要则扩展至容器宽度
|
|
1754
|
+
|
|
1755
|
+
- **空白看板**(无任务数据):
|
|
1756
|
+
- 日视图:以今天为中心,显示至少60天
|
|
1757
|
+
- 周视图:以今天为中心,显示至少20周
|
|
1758
|
+
- 月视图:显示至少3年
|
|
1759
|
+
- 季度/年视图:显示至少5年
|
|
1760
|
+
|
|
1761
|
+
> **💡 自动化优势**:
|
|
1762
|
+
>
|
|
1763
|
+
> - 无需手动设置 `startDate` 和 `endDate`,组件会自动计算最优范围
|
|
1764
|
+
> - 响应式容器宽度变化,时间线自动重新计算
|
|
1765
|
+
> - 不同视图独立优化,切换视图时自动调整到最佳显示效果
|
|
1766
|
+
> - 避免出现时间线过窄或留白过多的问题
|
|
1767
|
+
> - 适用不同分辨率展示
|
|
1768
|
+
|
|
1769
|
+
### 主题与国际化
|
|
1770
|
+
|
|
1771
|
+
#### 主题切换
|
|
1772
|
+
|
|
1773
|
+
组件内置亮色和暗色两种主题,可通过工具栏按钮切换,也可监听切换事件:
|
|
1774
|
+
|
|
1775
|
+
```vue
|
|
1776
|
+
<template>
|
|
1777
|
+
<GanttChart :tasks="tasks" :on-theme-change="handleThemeChange" />
|
|
1778
|
+
</template>
|
|
1779
|
+
|
|
1780
|
+
<script setup lang="ts">
|
|
1781
|
+
const handleThemeChange = (isDark: boolean) => {
|
|
1782
|
+
console.log('主题切换为:', isDark ? '暗色' : '亮色')
|
|
1783
|
+
// 可在此保存用户偏好设置到 localStorage
|
|
1784
|
+
localStorage.setItem('gantt-theme', isDark ? 'dark' : 'light')
|
|
1785
|
+
}
|
|
1786
|
+
</script>
|
|
1787
|
+
```
|
|
1788
|
+
|
|
1789
|
+
#### 自定义主题变量
|
|
1790
|
+
|
|
1791
|
+
通过覆盖 CSS 变量实现主题自定义:
|
|
1792
|
+
|
|
1793
|
+
```css
|
|
1794
|
+
/* 自定义亮色主题 */
|
|
1795
|
+
:root {
|
|
1796
|
+
/* 主色调 */
|
|
1797
|
+
--gantt-primary-color: #409eff;
|
|
1798
|
+
--gantt-success-color: #67c23a;
|
|
1799
|
+
--gantt-warning-color: #e6a23c;
|
|
1800
|
+
--gantt-danger-color: #f56c6c;
|
|
1801
|
+
|
|
1802
|
+
/* 背景色 */
|
|
1803
|
+
--gantt-bg-primary: #ffffff;
|
|
1804
|
+
--gantt-bg-secondary: #f5f7fa;
|
|
1805
|
+
--gantt-bg-hover: #ecf5ff;
|
|
1806
|
+
|
|
1807
|
+
/* 文字颜色 */
|
|
1808
|
+
--gantt-text-primary: #303133;
|
|
1809
|
+
--gantt-text-secondary: #606266;
|
|
1810
|
+
--gantt-text-placeholder: #c0c4cc;
|
|
1811
|
+
|
|
1812
|
+
/* 边框颜色 */
|
|
1813
|
+
--gantt-border-color: #dcdfe6;
|
|
1814
|
+
--gantt-border-color-light: #e4e7ed;
|
|
1815
|
+
|
|
1816
|
+
/* 任务条颜色 */
|
|
1817
|
+
--gantt-task-bg: #409eff;
|
|
1818
|
+
--gantt-task-border: #66b1ff;
|
|
1819
|
+
--gantt-task-text: #ffffff;
|
|
1820
|
+
}
|
|
1821
|
+
|
|
1822
|
+
/* 自定义暗色主题 */
|
|
1823
|
+
.dark {
|
|
1824
|
+
--gantt-bg-primary: #1a1a1a;
|
|
1825
|
+
--gantt-bg-secondary: #2c2c2c;
|
|
1826
|
+
--gantt-bg-hover: #3a3a3a;
|
|
1827
|
+
|
|
1828
|
+
--gantt-text-primary: #e5e5e5;
|
|
1829
|
+
--gantt-text-secondary: #b0b0b0;
|
|
1830
|
+
|
|
1831
|
+
--gantt-border-color: #3a3a3a;
|
|
1832
|
+
--gantt-border-color-light: #4a4a4a;
|
|
1833
|
+
|
|
1834
|
+
--gantt-task-bg: #409eff;
|
|
1835
|
+
--gantt-task-border: #66b1ff;
|
|
1836
|
+
--gantt-task-text: #ffffff;
|
|
1837
|
+
}
|
|
1838
|
+
```
|
|
1839
|
+
|
|
1840
|
+
#### 语言切换
|
|
1841
|
+
|
|
1842
|
+
组件内置中文(zh-CN)和英文(en-US)两种语言,可通过工具栏按钮切换:
|
|
1843
|
+
|
|
1844
|
+
```vue
|
|
1845
|
+
<template>
|
|
1846
|
+
<GanttChart :tasks="tasks" :on-language-change="handleLanguageChange" />
|
|
1847
|
+
</template>
|
|
1848
|
+
|
|
1849
|
+
<script setup lang="ts">
|
|
1850
|
+
const handleLanguageChange = (lang: 'zh-CN' | 'en-US') => {
|
|
1851
|
+
console.log('语言切换为:', lang)
|
|
1852
|
+
// 可在此保存用户偏好设置到 localStorage
|
|
1853
|
+
localStorage.setItem('gantt-language', lang)
|
|
1854
|
+
}
|
|
1855
|
+
</script>
|
|
1856
|
+
```
|
|
1857
|
+
|
|
1858
|
+
#### 自定义翻译
|
|
1859
|
+
|
|
1860
|
+
通过 `localeMessages` 属性覆盖或扩展默认翻译:
|
|
1861
|
+
|
|
1862
|
+
```vue
|
|
1863
|
+
<template>
|
|
1864
|
+
<GanttChart :tasks="tasks" :locale-messages="customMessages" />
|
|
1865
|
+
</template>
|
|
1866
|
+
|
|
1867
|
+
<script setup lang="ts">
|
|
1868
|
+
const customMessages = {
|
|
1869
|
+
"zh-CN": {
|
|
1870
|
+
// 任务列表相关
|
|
1871
|
+
name: '任务名称(自定义)',
|
|
1872
|
+
startDate: '开始日期',
|
|
1873
|
+
endDate: '结束日期',
|
|
1874
|
+
duration: '工期',
|
|
1875
|
+
progress: '完成度',
|
|
1876
|
+
predecessor: '前置任务',
|
|
1877
|
+
assignee: '负责人',
|
|
1878
|
+
estimatedHours: '预估工时',
|
|
1879
|
+
actualHours: '实际工时'
|
|
1880
|
+
|
|
1881
|
+
// 工具栏相关
|
|
1882
|
+
addTask: '新建任务',
|
|
1883
|
+
addMilestone: '新建里程碑',
|
|
1884
|
+
today: '今天',
|
|
1885
|
+
exportCsv: '导出 CSV',
|
|
1886
|
+
exportPdf: '导出 PDF',
|
|
1887
|
+
fullscreen: '全屏',
|
|
1888
|
+
exitFullscreen: '退出全屏',
|
|
1889
|
+
language: '语言',
|
|
1890
|
+
theme: '主题',
|
|
1891
|
+
expandAll: '全部展开',
|
|
1892
|
+
collapseAll: '全部折叠'
|
|
1893
|
+
|
|
1894
|
+
// 内置任务编辑器相关
|
|
1895
|
+
title: '任务详情',
|
|
1896
|
+
titleEdit: '编辑任务',
|
|
1897
|
+
titleNew: '新建任务',
|
|
1898
|
+
name: '任务名称',
|
|
1899
|
+
startDate: '开始日期',
|
|
1900
|
+
endDate: '结束日期',
|
|
1901
|
+
assignee: '负责人',
|
|
1902
|
+
predecessor: '前置任务',
|
|
1903
|
+
description: '描述',
|
|
1904
|
+
estimatedHours: '预估工时',
|
|
1905
|
+
actualHours: '实际工时',
|
|
1906
|
+
progress: '进度',
|
|
1907
|
+
save: '保存',
|
|
1908
|
+
cancel: '取消',
|
|
1909
|
+
delete: '删除'
|
|
1910
|
+
|
|
1911
|
+
// 其他文本
|
|
1912
|
+
days: '天',
|
|
1913
|
+
hours: '小时',
|
|
1914
|
+
overtime: '超时',
|
|
1915
|
+
overdue: '逾期',
|
|
1916
|
+
// ... 更多自定义翻译
|
|
1917
|
+
},
|
|
1918
|
+
"en-US": {......}
|
|
1919
|
+
}
|
|
1920
|
+
</script>
|
|
1921
|
+
```
|
|
1922
|
+
|
|
1923
|
+
> **💡 提示**:
|
|
1924
|
+
>
|
|
1925
|
+
> - `localeMessages` 采用**深度合并**策略,只需传递需要覆盖的字段即可
|
|
1926
|
+
> - 支持嵌套对象,如 `taskList.name`、`toolbar.addTask` 等
|
|
1927
|
+
> - 完整的翻译键请参考组件内置的 `messages['zh-CN']` 对象
|
|
1928
|
+
|
|
1929
|
+
### 自定义扩展
|
|
1930
|
+
|
|
1931
|
+
#### 插槽 (Slots)
|
|
1932
|
+
|
|
1933
|
+
组件提供了插槽支持,允许自定义任务内容的渲染。
|
|
1934
|
+
|
|
1935
|
+
##### `custom-task-content` 插槽
|
|
1936
|
+
|
|
1937
|
+
用于自定义任务在任务列表(TaskRow)和时间轴(TaskBar)中的显示内容。
|
|
1938
|
+
|
|
1939
|
+
**插槽参数:**
|
|
1940
|
+
|
|
1941
|
+
| 参数名 | 类型 | 来源 | 说明 |
|
|
1942
|
+
| ------ | ---------------------------- | ---- | ---------------- |
|
|
1943
|
+
| `type` | `'task-row'` \| `'task-bar'` | 通用 | 插槽调用位置标识 |
|
|
1944
|
+
| `task` | `Task` | 通用 | 当前任务对象 |
|
|
1945
|
+
|
|
1946
|
+
**TaskRow 特有参数(当 `type === 'task-row'` 时):**
|
|
1947
|
+
|
|
1948
|
+
| 参数名 | 类型 | 说明 |
|
|
1949
|
+
| ---------------- | -------------------------------- | ---------------- |
|
|
1950
|
+
| `isRowContent` | `boolean` | 标识为行内容 |
|
|
1951
|
+
| `level` | `number` | 任务层级 |
|
|
1952
|
+
| `indent` | `string` | 缩进样式 |
|
|
1953
|
+
| `isHovered` | `boolean` | 是否悬停 |
|
|
1954
|
+
| `hoveredTaskId` | `number \| null` | 当前悬停任务ID |
|
|
1955
|
+
| `isParent` | `boolean` | 是否为父任务 |
|
|
1956
|
+
| `hasChildren` | `boolean` | 是否有子任务 |
|
|
1957
|
+
| `collapsed` | `boolean` | 是否折叠 |
|
|
1958
|
+
| `formattedTimer` | `string` | 格式化的计时文本 |
|
|
1959
|
+
| `timerRunning` | `boolean` | 计时器是否运行 |
|
|
1960
|
+
| `timerElapsed` | `number` | 已计时时长 |
|
|
1961
|
+
| `isOvertime` | `number \| boolean \| undefined` | 是否超时 |
|
|
1962
|
+
| `overdueDays` | `number` | 逾期天数 |
|
|
1963
|
+
| `overtimeText` | `string` | 超时文本 |
|
|
1964
|
+
| `overdueText` | `string` | 逾期文本 |
|
|
1965
|
+
| `daysText` | `string` | 天数文本 |
|
|
1966
|
+
| `progressClass` | `string` | 进度CSS类名 |
|
|
1967
|
+
|
|
1968
|
+
**TaskBar 特有参数(当 `type === 'task-bar'` 时):**
|
|
1969
|
+
|
|
1970
|
+
| 参数名 | 类型 | 说明 |
|
|
1971
|
+
| ------------------ | --------------- | ---------------------------------------------------------------------------------- |
|
|
1972
|
+
| `status` | `object` | 任务状态对象,包含 `type`, `color`, `bgColor`, `borderColor` |
|
|
1973
|
+
| `statusType` | `string` | 状态类型:`'completed'`, `'delayed'`, `'in-progress'`, `'not-started'`, `'parent'` |
|
|
1974
|
+
| `isParent` | `boolean` | 是否为父任务 |
|
|
1975
|
+
| `progress` | `number` | 任务进度(0-100) |
|
|
1976
|
+
| `currentTimeScale` | `TimelineScale` | 当前时间刻度 |
|
|
1977
|
+
| `rowHeight` | `number` | 行高(像素) |
|
|
1978
|
+
| `dayWidth` | `number` | 每天宽度(像素) |
|
|
1979
|
+
|
|
1980
|
+
**使用示例:**
|
|
1981
|
+
|
|
1982
|
+
```vue
|
|
1983
|
+
<template>
|
|
1984
|
+
<GanttChart :tasks="tasks">
|
|
1985
|
+
<template #custom-task-content="slotProps">
|
|
1986
|
+
<!-- 根据类型渲染不同内容 -->
|
|
1987
|
+
<CustomTaskContent :task="slotProps.task" :type="slotProps.type" :status="slotProps.status" />
|
|
1988
|
+
</template>
|
|
1989
|
+
</GanttChart>
|
|
1990
|
+
</template>
|
|
1991
|
+
|
|
1992
|
+
<script setup lang="ts">
|
|
1993
|
+
import { ref } from 'vue'
|
|
1994
|
+
import { GanttChart } from 'jordium-gantt-vue3'
|
|
1995
|
+
import 'jordium-gantt-vue3/dist/assets/jordium-gantt-vue3.css'
|
|
1996
|
+
import type { Task } from 'jordium-gantt-vue3'
|
|
1997
|
+
import CustomTaskContent from './CustomTaskContent.vue'
|
|
1998
|
+
|
|
1999
|
+
const tasks = ref<Task[]>([
|
|
2000
|
+
{
|
|
2001
|
+
id: 1,
|
|
2002
|
+
name: '<strong>重要任务</strong>',
|
|
2003
|
+
startDate: '2025-01-01',
|
|
2004
|
+
endDate: '2025-01-10',
|
|
2005
|
+
progress: 50,
|
|
2006
|
+
},
|
|
2007
|
+
])
|
|
2008
|
+
</script>
|
|
2009
|
+
```
|
|
2010
|
+
|
|
2011
|
+
**自定义内容组件示例:**
|
|
2012
|
+
|
|
2013
|
+
```vue
|
|
2014
|
+
<!-- CustomTaskContent.vue -->
|
|
2015
|
+
<script setup lang="ts">
|
|
2016
|
+
import type { Task } from 'jordium-gantt-vue3'
|
|
2017
|
+
|
|
2018
|
+
interface Props {
|
|
2019
|
+
task: Task
|
|
2020
|
+
type: 'task-row' | 'task-bar'
|
|
2021
|
+
status?: {
|
|
2022
|
+
type: string
|
|
2023
|
+
color: string
|
|
2024
|
+
bgColor: string
|
|
2025
|
+
borderColor: string
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
|
|
2029
|
+
const props = defineProps<Props>()
|
|
2030
|
+
</script>
|
|
2031
|
+
|
|
2032
|
+
<template>
|
|
2033
|
+
<div class="custom-task-content">
|
|
2034
|
+
<!-- TaskRow 中的渲染 -->
|
|
2035
|
+
<div v-if="type === 'task-row'" class="task-row-content">
|
|
2036
|
+
<span v-html="task.name" />
|
|
2037
|
+
</div>
|
|
2038
|
+
|
|
2039
|
+
<!-- TaskBar 中的渲染 -->
|
|
2040
|
+
<div v-else-if="type === 'task-bar'" class="task-bar-content">
|
|
2041
|
+
<div class="task-icon" :style="{ color: status?.color }">📌</div>
|
|
2042
|
+
<span class="task-title" v-html="task.name" />
|
|
2043
|
+
</div>
|
|
2044
|
+
</div>
|
|
2045
|
+
</template>
|
|
2046
|
+
|
|
2047
|
+
<style scoped>
|
|
2048
|
+
.custom-task-content {
|
|
2049
|
+
width: 100%;
|
|
2050
|
+
height: 100%;
|
|
2051
|
+
}
|
|
2052
|
+
|
|
2053
|
+
.task-row-content {
|
|
2054
|
+
padding: 0 4px;
|
|
2055
|
+
overflow: hidden;
|
|
2056
|
+
text-overflow: ellipsis;
|
|
2057
|
+
white-space: nowrap;
|
|
2058
|
+
}
|
|
2059
|
+
|
|
2060
|
+
.task-bar-content {
|
|
2061
|
+
display: flex;
|
|
2062
|
+
align-items: center;
|
|
2063
|
+
gap: 4px;
|
|
2064
|
+
padding: 0 8px;
|
|
2065
|
+
overflow: hidden;
|
|
2066
|
+
}
|
|
2067
|
+
|
|
2068
|
+
.task-icon {
|
|
2069
|
+
flex-shrink: 0;
|
|
2070
|
+
font-size: 14px;
|
|
2071
|
+
}
|
|
2072
|
+
|
|
2073
|
+
.task-title {
|
|
2074
|
+
flex: 1;
|
|
2075
|
+
overflow: hidden;
|
|
2076
|
+
text-overflow: ellipsis;
|
|
2077
|
+
white-space: nowrap;
|
|
2078
|
+
}
|
|
2079
|
+
</style>
|
|
2080
|
+
```
|
|
2081
|
+
|
|
2082
|
+
> **💡 使用场景**:
|
|
2083
|
+
>
|
|
2084
|
+
> - 支持 HTML 格式的任务名称
|
|
2085
|
+
> - 添加自定义图标、标签或徽章
|
|
2086
|
+
> - 根据任务状态显示不同样式
|
|
2087
|
+
> - 集成第三方富文本渲染
|
|
2088
|
+
> - 显示额外的业务信息
|
|
2089
|
+
|
|
2090
|
+
> **⚠️ 注意事项**:
|
|
2091
|
+
>
|
|
2092
|
+
> - 插槽内容会同时在 TaskRow 和 TaskBar 中渲染
|
|
2093
|
+
> - 需要根据 `type` 参数区分渲染位置
|
|
2094
|
+
> - TaskRow 和 TaskBar 的可用空间不同,需要适配布局
|
|
2095
|
+
> - 避免在插槽内容中使用过于复杂的组件,可能影响性能
|
|
2096
|
+
|
|
2097
|
+
---
|
|
2098
|
+
|
|
2099
|
+
## ❓ 常见问题
|
|
2100
|
+
|
|
2101
|
+
### 如何集成到现有项目?
|
|
2102
|
+
|
|
2103
|
+
1. 安装依赖
|
|
2104
|
+
2. 引入组件和样式
|
|
2105
|
+
3. 传入数据
|
|
2106
|
+
4. 监听事件处理业务逻辑
|
|
2107
|
+
|
|
2108
|
+
详见 [快速开始](#-快速开始) 章节。
|
|
2109
|
+
|
|
2110
|
+
### 支持哪些浏览器?
|
|
2111
|
+
|
|
2112
|
+
- Chrome >= 90
|
|
2113
|
+
- Firefox >= 88
|
|
2114
|
+
- Safari >= 14
|
|
2115
|
+
- Edge >= 90
|
|
2116
|
+
|
|
2117
|
+
---
|
|
2118
|
+
|
|
2119
|
+
## 📁 项目结构
|
|
2120
|
+
|
|
2121
|
+
```
|
|
2122
|
+
jordium-gantt-vue3/
|
|
2123
|
+
├── src/ # 源代码
|
|
2124
|
+
│ ├── components/ # Vue 组件
|
|
2125
|
+
│ │ ├── GanttChart.vue # 甘特图主组件
|
|
2126
|
+
│ │ ├── TaskList.vue # 任务列表
|
|
2127
|
+
│ │ ├── Timeline.vue # 时间轴
|
|
2128
|
+
│ │ └── ...
|
|
2129
|
+
│ ├── models/ # 数据模型
|
|
2130
|
+
│ │ ├── classes/ # 类定义
|
|
2131
|
+
│ │ ├── configs/ # 配置接口
|
|
2132
|
+
│ │ └── types/ # 类型定义
|
|
2133
|
+
│ ├── composables/ # 组合式函数
|
|
2134
|
+
│ ├── styles/ # 样式文件
|
|
2135
|
+
│ └── utils/ # 工具函数
|
|
2136
|
+
├── demo/ # 示例代码
|
|
2137
|
+
├── docs/ # 文档
|
|
2138
|
+
├── public/ # 公共资源
|
|
2139
|
+
└── package.json # 项目配置
|
|
2140
|
+
```
|
|
2141
|
+
|
|
2142
|
+
---
|
|
2143
|
+
|
|
2144
|
+
## 🔗 相关链接
|
|
2145
|
+
|
|
2146
|
+
- **在线演示**: [https://nelson820125.github.io/jordium-gantt-vue3/](https://nelson820125.github.io/jordium-gantt-vue3/)
|
|
2147
|
+
- **GitHub**: [https://github.com/nelson820125/jordium-gantt-vue3](https://github.com/nelson820125/jordium-gantt-vue3)
|
|
2148
|
+
- **npm**: [https://www.npmjs.com/package/jordium-gantt-vue3](https://www.npmjs.com/package/jordium-gantt-vue3)
|
|
2149
|
+
- **更新日志**: [CHANGELOG.md](./CHANGELOG.md)
|
|
2150
|
+
- **贡献指南**: [CONTRIBUTING.md](./CONTRIBUTING.md)
|
|
2151
|
+
|
|
2152
|
+
---
|
|
2153
|
+
|
|
2154
|
+
## 🤝 贡献
|
|
2155
|
+
|
|
2156
|
+
欢迎提交 Issue 和 Pull Request!
|
|
2157
|
+
|
|
2158
|
+
详细的贡献指南请查看 [CONTRIBUTING.md](./CONTRIBUTING.md)。
|
|
2159
|
+
|
|
2160
|
+
### 贡献者
|
|
2161
|
+
|
|
2162
|
+
感谢所有为本项目做出贡献的开发者!
|
|
2163
|
+
|
|
2164
|
+
查看完整的 [贡献者名单](./CONTRIBUTORS.md)。
|
|
2165
|
+
|
|
2166
|
+
---
|
|
2167
|
+
|
|
2168
|
+
## 📄 开源协议
|
|
2169
|
+
|
|
2170
|
+
[MIT License](./LICENSE) © 2025 JORDIUM.COM
|
|
2171
|
+
|
|
2172
|
+
---
|
|
2173
|
+
|
|
2174
|
+
<p align="center">
|
|
2175
|
+
<sub>如果这个项目对你有帮助,请给一个 ⭐️ 支持一下!</sub>
|
|
2176
|
+
</p>
|