Skip to content

Commit

Permalink
feat: support hour view #375 #298 #INFR-4223 (#443)
Browse files Browse the repository at this point in the history
  • Loading branch information
walkerkay authored Mar 8, 2024
1 parent 70a821a commit fad93c1
Show file tree
Hide file tree
Showing 31 changed files with 513 additions and 207 deletions.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ speed-measure-plugin*.json

# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
Expand Down
25 changes: 25 additions & 0 deletions docs/guides/basic/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,31 @@ export class AppGanttExampleComponent {
}
```

## 视图配置

内置视图有一套默认的配置,如默认配置不满足需求时,可传入指定的 viewOptions 来进行自定义配置

```
<ngx-gantt #gantt [items]="items" [viewOptions]="viewOptions">
...
</ngx-gantt>
```

```javascript
class GanttViewOptions {
start?: GanttDate; // 视图开始时间
end?: GanttDate; // 视图结束时间
min?: GanttDate; // 视图最小时间
max?: GanttDate; // 视图最大时间
cellWidth?: number; // 视图最小单元宽度(小时视图,最小单元就是每小时的宽度,日视图,最新单元就是每日显示的宽度)
addAmount?: number; // 横向滚动加载时,每次加载的量
addUnit?: GanttDateUtil; // 横向滚动加载时,每次加载的量的单位
dateFormat?: GanttDateFormat; // 设置视图日期格式,可用于多语言
datePrecisionUnit?: 'day' | 'hour' | 'minute'; // 日期精度单位,小时视图默认精度为分钟,其他视图默认精度为天
dragPreviewDateFormat?: string; // 拖拽预览日期格式设置
}
```

## 如何设置分组

分组模式下我们还需要传入一个 `groups` 的数组,并且保证我们传入的 `items` 数据中设置了每个数据项的 `group_id`
Expand Down
2 changes: 1 addition & 1 deletion docs/guides/intro/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ order: 1

# 特性

- 5 种视图(日、周、月、季、年)
- 6 种内置视图(时、日、周、月、季、年),并支持自定义视图
- 任务分组展示
- 树形结构数据展示并支持异步加载
- 任务前后置依赖关联及展示
Expand Down
4 changes: 2 additions & 2 deletions example/src/app/gantt-custom-view/custom-day-view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ export class GanttViewCustom extends GanttView {
super(start, end, Object.assign({}, viewOptions, options));
}

startOf(date: GanttDate) {
viewStartOf(date: GanttDate) {
return date.startOfWeek({ weekStartsOn: 1 });
}

endOf(date: GanttDate) {
viewEndOf(date: GanttDate) {
return date.endOfWeek({ weekStartsOn: 1 });
}

Expand Down
13 changes: 4 additions & 9 deletions example/src/app/gantt/gantt.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -53,22 +53,17 @@
(dragStarted)="onDragStarted($event)"
(dragEnded)="onDragEnded($event)"
>
<ngx-gantt-column name="Id" [class.start-time]="true" [width]="120">
<ng-template #cell let-item="item">
{{ item.id }}
</ng-template>
</ngx-gantt-column>
<ngx-gantt-column name="标题" width="160px" [showExpandIcon]="true">
<ng-template #cell let-item="item"> {{ item.title }} </ng-template>
</ngx-gantt-column>
<ngx-gantt-column name="开始时间">
<ngx-gantt-column name="开始时间" width="200px">
<ng-template #cell let-item="item">
{{ item.start * 1000 | date : 'yyyy-MM-dd' }}
{{ item.start * 1000 | date : 'yyyy-MM-dd HH:mm' }}
</ng-template>
</ngx-gantt-column>
<ngx-gantt-column name="截止时间">
<ngx-gantt-column name="截止时间" width="200px">
<ng-template #cell let-item="item">
{{ item.end * 1000 | date : 'yyyy-MM-dd' }}
{{ item.end * 1000 | date : 'yyyy-MM-dd HH:mm' }}
</ng-template>
</ngx-gantt-column>
</ngx-gantt-table>
Expand Down
30 changes: 18 additions & 12 deletions example/src/app/gantt/gantt.component.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
import { Component, OnInit, HostBinding, ViewChild, AfterViewInit } from '@angular/core';
import { AfterViewInit, Component, HostBinding, OnInit, ViewChild } from '@angular/core';
import {
GanttBarClickEvent,
GanttViewType,
GanttBaselineItem,
GanttDragEvent,
GanttItem,
GanttLineClickEvent,
GanttLinkDragEvent,
GanttItem,
GanttPrintService,
NgxGanttComponent,
GanttSelectedEvent,
GanttBaselineItem,
GanttView,
GanttToolbarOptions,
GanttTableDragEnterPredicateContext,
GanttTableDragDroppedEvent,
GanttTableDragEndedEvent,
GanttTableDragEnterPredicateContext,
GanttTableDragStartedEvent,
GanttTableDragEndedEvent
GanttToolbarOptions,
GanttView,
GanttViewType,
NgxGanttComponent
} from 'ngx-gantt';
import { ThyNotifyService } from 'ngx-tethys/notify';
import { finalize, of } from 'rxjs';
import { delay } from 'rxjs/operators';
import { ThyNotifyService } from 'ngx-tethys/notify';
import { randomItems, random } from '../helper';
import { random, randomItems } from '../helper';

@Component({
selector: 'app-gantt-example',
Expand All @@ -30,6 +30,10 @@ import { randomItems, random } from '../helper';
})
export class AppGanttExampleComponent implements OnInit, AfterViewInit {
views = [
{
name: '小时',
value: GanttViewType.hour
},
{
name: '日',
value: GanttViewType.day
Expand Down Expand Up @@ -63,7 +67,7 @@ export class AppGanttExampleComponent implements OnInit, AfterViewInit {
loading = false;

items: GanttItem[] = [
{ id: '000000', title: 'Task 0', start: 1627729997, end: 1628421197 },
{ id: '000000', title: 'Task 0', start: 1627729997, end: 1627769997 },
// { id: '000001', title: 'Task 1', start: 1617361997, end: 1625483597, links: ['000003', '000004', '000000'], },
{ id: '000001', title: 'Task 1', start: 1617361997, end: 1625483597, links: ['000003', '000004', '0000029'] },
{ id: '000002', title: 'Task 2', start: 1610536397, end: 1610622797, progress: 0.5 },
Expand Down Expand Up @@ -153,6 +157,8 @@ export class AppGanttExampleComponent implements OnInit, AfterViewInit {
}

selectedChange(event: GanttSelectedEvent) {
event.current && this.ganttComponent.scrollToDate(event.current?.start);

this.thyNotify.info(
'Event: selectedChange',
`当前选中的 item 的 id 为 ${(event.selectedValue as GanttItem[]).map((item) => item.id).join('、')}`
Expand Down
1 change: 1 addition & 0 deletions packages/gantt/src/class/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export class GanttBarClickEvent<T = unknown> {

export class GanttSelectedEvent<T = unknown> {
event: Event;
current?: GanttItem<T>;
selectedValue: GanttItem<T> | GanttItem<T>[];
}

Expand Down
36 changes: 17 additions & 19 deletions packages/gantt/src/class/item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import { GanttDate } from '../utils/date';
import { BehaviorSubject } from 'rxjs';
import { GanttLink, GanttLinkType } from './link';
import { GanttViewType } from './view-type';
import { GanttView } from '../views/view';

const DEFAULT_FILL_INCREMENT_WIDTH = 120;

export interface GanttItemRefs {
width: number;
Expand Down Expand Up @@ -38,8 +41,8 @@ export interface GanttItem<T = unknown> {
export class GanttItemInternal {
id: string;
title: string;
start: GanttDate;
end: GanttDate;
start: GanttDate | null;
end: GanttDate | null;
links: GanttLink[];
color?: string;
barStyle?: Partial<CSSStyleDeclaration>;
Expand All @@ -53,17 +56,16 @@ export class GanttItemInternal {
children: GanttItemInternal[];
type?: GanttItemType;
progress?: number;
fillDays?: number;
viewType?: GanttViewType;
level?: number;
level: number;

get refs() {
return this.refs$.getValue();
}

refs$ = new BehaviorSubject<{ width: number; x: number; y: number }>(null);
refs$ = new BehaviorSubject<{ width: number; x: number; y: number }>(null as any);

constructor(item: GanttItem, level?: number, options?: { fillDays: number }) {
constructor(item: GanttItem, level: number, private view?: GanttView) {
this.origin = item;
this.id = this.origin.id;
this.links = (this.origin.links || []).map((link) => {
Expand All @@ -86,25 +88,21 @@ export class GanttItemInternal {
this.start = item.start ? new GanttDate(item.start) : null;
this.end = item.end ? new GanttDate(item.end) : null;
this.level = level;
// 默认填充 30 天
this.fillDays = options?.fillDays || 30;
this.children = (item.children || []).map((subItem) => {
return new GanttItemInternal(subItem, level + 1, { fillDays: this.fillDays });
return new GanttItemInternal(subItem, level + 1, view);
});
this.type = this.origin.type || GanttItemType.bar;
this.progress = this.origin.progress;
// fill days when start or end is null
this.fillItemStartOrEnd(item);
this.fillDateWhenStartOrEndIsNil(item);
}

fillItemStartOrEnd(item: GanttItem) {
if (this.fillDays > 0) {
const fillDays = this.fillDays - 1;
private fillDateWhenStartOrEndIsNil(item: GanttItem) {
if (this.view) {
if (item.start && !item.end) {
this.end = new GanttDate(item.start).addDays(fillDays).endOfDay();
this.end = this.view.getDateByXPoint(this.view.getXPointByDate(new GanttDate(item.start)) + DEFAULT_FILL_INCREMENT_WIDTH);
}
if (!item.start && item.end) {
this.start = new GanttDate(item.end).addDays(-fillDays).startOfDay();
this.start = this.view.getDateByXPoint(this.view.getXPointByDate(new GanttDate(item.end)) - DEFAULT_FILL_INCREMENT_WIDTH);
}
}
}
Expand All @@ -114,8 +112,8 @@ export class GanttItemInternal {
}

updateDate(start: GanttDate, end: GanttDate) {
this.start = start.startOfDay();
this.end = end.endOfDay();
this.start = start;
this.end = end;
this.origin.start = this.start.getUnixTime();
this.origin.end = this.end.getUnixTime();
}
Expand All @@ -127,7 +125,7 @@ export class GanttItemInternal {
addChildren(items: GanttItem[]) {
this.origin.children = items;
this.children = (items || []).map((subItem) => {
return new GanttItemInternal(subItem, this.level + 1, { fillDays: this.fillDays });
return new GanttItemInternal(subItem, this.level + 1, this.view);
});
}

Expand Down
52 changes: 37 additions & 15 deletions packages/gantt/src/class/test/item.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@ import { GanttLinkType } from 'ngx-gantt';
import { GanttDate } from '../../utils/date';
import { GanttItem, GanttItemInternal } from '../item';

class FakeView {
getDateByXPoint() {
return new GanttDate();
}

getXPointByDate() {
return 0;
}
}

describe('GanttItemInternal', () => {
let ganttItemInternal: GanttItemInternal;
let ganttItem: GanttItem;
Expand All @@ -24,28 +34,40 @@ describe('GanttItemInternal', () => {
}
]
};
ganttItemInternal = new GanttItemInternal(ganttItem);
ganttItemInternal = new GanttItemInternal(ganttItem, 0, new FakeView() as any);
});

it(`should has correct children`, () => {
expect(ganttItemInternal.children.length).toBe(1);
});

it(`should has correct end`, () => {
expect(ganttItemInternal.end.getUnixTime()).toBe(new GanttDate('2020-06-19 23:59:59').getUnixTime());
it(`should fill date when start or end date is nil`, () => {
const view = new FakeView();
const date = new GanttDate('2020-06-01 00:00:00');
spyOn(view, 'getDateByXPoint').and.returnValue(date);

ganttItemInternal = new GanttItemInternal(ganttItem, 0, { fillDays: 1 });
expect(ganttItemInternal.end.getUnixTime()).toBe(new GanttDate('2020-05-21 23:59:59').getUnixTime());
});
let ganttItemInternal = new GanttItemInternal(
{
...ganttItem,
start: null,
end: new GanttDate('2020-06-19 00:00:00').getUnixTime()
},
0,
view as any
);

it(`should has correct start`, () => {
ganttItem.start = null;
ganttItem.end = new GanttDate('2020-05-21 12:34:35').getUnixTime();
ganttItemInternal = new GanttItemInternal(ganttItem);
expect(ganttItemInternal.start.getUnixTime()).toBe(new GanttDate('2020-04-22 00:00:00').getUnixTime());
expect(ganttItemInternal.start.getUnixTime()).toBe(date.getUnixTime());

ganttItemInternal = new GanttItemInternal(ganttItem, 0, { fillDays: 1 });
expect(ganttItemInternal.start.getUnixTime()).toBe(new GanttDate('2020-05-21 00:00:00').getUnixTime());
ganttItemInternal = new GanttItemInternal(
{
...ganttItem,
start: new GanttDate('2020-05-19 00:00:00').getUnixTime(),
end: null
},
0,
view as any
);
expect(ganttItemInternal.end.getUnixTime()).toBe(date.getUnixTime());
});

it(`should update refs`, () => {
Expand All @@ -58,8 +80,8 @@ describe('GanttItemInternal', () => {
const start = new GanttDate('2020-04-21 12:34:35');
const end = new GanttDate('2020-09-21 12:34:35');
ganttItemInternal.updateDate(start, end);
expect(ganttItemInternal.start.getUnixTime()).toBe(start.startOfDay().getUnixTime());
expect(ganttItemInternal.end.getUnixTime()).toBe(end.endOfDay().getUnixTime());
expect(ganttItemInternal.start.getUnixTime()).toBe(start.getUnixTime());
expect(ganttItemInternal.end.getUnixTime()).toBe(end.getUnixTime());
});

it(`should add children`, () => {
Expand Down
3 changes: 2 additions & 1 deletion packages/gantt/src/class/view-type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ export enum GanttViewType {
quarter = 'quarter',
month = 'month',
year = 'year',
week = 'week'
week = 'week',
hour = 'hour'
}

export const ganttViews = [
Expand Down
Loading

0 comments on commit fad93c1

Please sign in to comment.