mirror of
https://github.com/goharbor/harbor.git
synced 2025-02-15 03:11:50 +01:00
[Cherry-pick]Switch to a new chart library (#19263)
Switch to a new chart library Signed-off-by: AllForNothing <sshijun@vmware.com>
This commit is contained in:
parent
44f5702e87
commit
0f9839bf6e
34
src/portal/package-lock.json
generated
34
src/portal/package-lock.json
generated
@ -25,7 +25,7 @@
|
|||||||
"@ngx-translate/core": "15.0.0",
|
"@ngx-translate/core": "15.0.0",
|
||||||
"@ngx-translate/http-loader": "8.0.0",
|
"@ngx-translate/http-loader": "8.0.0",
|
||||||
"cron-validator": "^1.3.1",
|
"cron-validator": "^1.3.1",
|
||||||
"highcharts": "^11.1.0",
|
"echarts": "^5.4.3",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"ngx-clipboard": "^15.1.0",
|
"ngx-clipboard": "^15.1.0",
|
||||||
"ngx-cookie": "^6.0.1",
|
"ngx-cookie": "^6.0.1",
|
||||||
@ -8126,6 +8126,20 @@
|
|||||||
"safer-buffer": "^2.1.0"
|
"safer-buffer": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/echarts": {
|
||||||
|
"version": "5.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/echarts/-/echarts-5.4.3.tgz",
|
||||||
|
"integrity": "sha512-mYKxLxhzy6zyTi/FaEbJMOZU1ULGEQHaeIeuMR5L+JnJTpz+YR03mnnpBhbR4+UYJAgiXgpyTVLffPAjOTLkZA==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "2.3.0",
|
||||||
|
"zrender": "5.4.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/echarts/node_modules/tslib": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
|
||||||
|
},
|
||||||
"node_modules/ee-first": {
|
"node_modules/ee-first": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||||
@ -10030,11 +10044,6 @@
|
|||||||
"integrity": "sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==",
|
"integrity": "sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
"node_modules/highcharts": {
|
|
||||||
"version": "11.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/highcharts/-/highcharts-11.1.0.tgz",
|
|
||||||
"integrity": "sha512-vhmqq6/frteWMx0GKYWwEFL25g4OYc7+m+9KQJb/notXbNtIb8KVy+ijOF7XAFqF165cq0pdLIePAmyFY5ph3g=="
|
|
||||||
},
|
|
||||||
"node_modules/hosted-git-info": {
|
"node_modules/hosted-git-info": {
|
||||||
"version": "6.1.1",
|
"version": "6.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.1.tgz",
|
||||||
@ -19341,6 +19350,19 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": "^2.3.0"
|
"tslib": "^2.3.0"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"node_modules/zrender": {
|
||||||
|
"version": "5.4.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/zrender/-/zrender-5.4.4.tgz",
|
||||||
|
"integrity": "sha512-0VxCNJ7AGOMCWeHVyTrGzUgrK4asT4ml9PEkeGirAkKNYXYzoPJCLvmyfdoOXcjTHPs10OZVMfD1Rwg16AZyYw==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "2.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/zrender/node_modules/tslib": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@
|
|||||||
"@ngx-translate/core": "15.0.0",
|
"@ngx-translate/core": "15.0.0",
|
||||||
"@ngx-translate/http-loader": "8.0.0",
|
"@ngx-translate/http-loader": "8.0.0",
|
||||||
"cron-validator": "^1.3.1",
|
"cron-validator": "^1.3.1",
|
||||||
"highcharts": "^11.1.0",
|
"echarts": "^5.4.3",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"ngx-clipboard": "^15.1.0",
|
"ngx-clipboard": "^15.1.0",
|
||||||
"ngx-cookie": "^6.0.1",
|
"ngx-cookie": "^6.0.1",
|
||||||
|
@ -8,7 +8,7 @@ import {
|
|||||||
ViewChild,
|
ViewChild,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { DangerousArtifact } from '../../../../../../../ng-swagger-gen/models/dangerous-artifact';
|
import { DangerousArtifact } from '../../../../../../../ng-swagger-gen/models/dangerous-artifact';
|
||||||
import * as Highcharts from 'highcharts';
|
import * as echarts from 'echarts/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-single-bar',
|
selector: 'app-single-bar',
|
||||||
@ -30,61 +30,71 @@ export class SingleBarComponent implements OnChanges {
|
|||||||
}
|
}
|
||||||
|
|
||||||
initChart() {
|
initChart() {
|
||||||
(Highcharts as any).chart(this.container.nativeElement, {
|
const chart = echarts.init(this.container.nativeElement);
|
||||||
credits: {
|
chart.setOption({
|
||||||
enabled: false,
|
color: ['red', '#e64524', 'orange'],
|
||||||
},
|
|
||||||
chart: {
|
|
||||||
backgroundColor: 'transparent',
|
|
||||||
type: 'bar',
|
|
||||||
},
|
|
||||||
title: {
|
title: {
|
||||||
text: '',
|
text: '',
|
||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
pointFormat: '{series.data.name}{point.y}',
|
formatter: '{b}: {c}',
|
||||||
style: {
|
textStyle: {
|
||||||
fontSize: 12,
|
fontSize: '12px',
|
||||||
whiteSpace: 'nowrap',
|
whiteSpace: 'nowrap',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plotOptions: {
|
legend: {
|
||||||
pie: {
|
show: false,
|
||||||
startAngle: -90,
|
|
||||||
endAngle: 90,
|
|
||||||
dataLabels: {
|
|
||||||
enabled: true,
|
|
||||||
distance: -8,
|
|
||||||
style: {
|
|
||||||
fontSize: '8px',
|
|
||||||
fontWeight: 1,
|
|
||||||
},
|
|
||||||
pointFormat: '{point.y}',
|
|
||||||
},
|
|
||||||
size: 50,
|
|
||||||
borderWidth: 0,
|
|
||||||
borderRadius: 2,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
|
radius: '65%',
|
||||||
name: 'Severity',
|
name: 'Severity',
|
||||||
|
// adjust the start angle
|
||||||
|
startAngle: 180,
|
||||||
|
itemStyle: {
|
||||||
|
borderRadius: 2,
|
||||||
|
borderWidth: 1,
|
||||||
|
},
|
||||||
|
labelLine: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
position: 'inside',
|
||||||
|
formatter: '{c}',
|
||||||
|
fontSize: '9px',
|
||||||
|
},
|
||||||
data: [
|
data: [
|
||||||
{
|
{
|
||||||
name: 'Critical',
|
name: 'Critical',
|
||||||
y: this.dangerousArtifact?.critical_cnt || 0,
|
value: this.dangerousArtifact?.critical_cnt || 0,
|
||||||
color: 'red',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'High',
|
name: 'High',
|
||||||
y: this.dangerousArtifact?.high_cnt || 0,
|
value: this.dangerousArtifact?.high_cnt || 0,
|
||||||
color: '#e64524',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Medium',
|
name: 'Medium',
|
||||||
y: this.dangerousArtifact?.medium_cnt || 0,
|
value: this.dangerousArtifact?.medium_cnt || 0,
|
||||||
color: 'orange',
|
},
|
||||||
|
{
|
||||||
|
// make a record to fill the bottom 50%
|
||||||
|
value:
|
||||||
|
this.dangerousArtifact?.critical_cnt +
|
||||||
|
this.dangerousArtifact?.high_cnt +
|
||||||
|
this.dangerousArtifact?.medium_cnt || 0,
|
||||||
|
itemStyle: {
|
||||||
|
// stop the chart from rendering this piece
|
||||||
|
color: 'none',
|
||||||
|
decal: {
|
||||||
|
symbol: 'none',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -96,7 +96,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="clr-row" [hidden]="!securitySummary?.total_vuls">
|
<div class="clr-row" [hidden]="!securitySummary?.total_vuls">
|
||||||
<div class="placeholder">
|
<div class="placeholder">
|
||||||
<div class="pie-chart" id="pie-chart"></div>
|
<div #pieChart class="pie-chart" id="pie-chart"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
import {
|
import {
|
||||||
Component,
|
Component,
|
||||||
|
ElementRef,
|
||||||
EventEmitter,
|
EventEmitter,
|
||||||
|
HostListener,
|
||||||
OnDestroy,
|
OnDestroy,
|
||||||
OnInit,
|
OnInit,
|
||||||
Output,
|
Output,
|
||||||
|
ViewChild,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { SecurityhubService } from '../../../../../../../ng-swagger-gen/services/securityhub.service';
|
import { SecurityhubService } from '../../../../../../../ng-swagger-gen/services/securityhub.service';
|
||||||
import { SecuritySummary } from '../../../../../../../ng-swagger-gen/models/security-summary';
|
import { SecuritySummary } from '../../../../../../../ng-swagger-gen/models/security-summary';
|
||||||
import { MessageHandlerService } from '../../../../../shared/services/message-handler.service';
|
import { MessageHandlerService } from '../../../../../shared/services/message-handler.service';
|
||||||
import * as Highcharts from 'highcharts';
|
|
||||||
import highchartsAccessibility from 'highcharts/modules/accessibility';
|
|
||||||
import { getDigestLink, severityText, VUL_ID } from '../security-hub.interface';
|
import { getDigestLink, severityText, VUL_ID } from '../security-hub.interface';
|
||||||
import { HAS_STYLE_MODE, StyleMode } from '../../../../../services/theme';
|
import { HAS_STYLE_MODE, StyleMode } from '../../../../../services/theme';
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
@ -19,7 +20,7 @@ import {
|
|||||||
} from '../../../../../services/event-service/event.service';
|
} from '../../../../../services/event-service/event.service';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { DangerousArtifact } from '../../../../../../../ng-swagger-gen/models/dangerous-artifact';
|
import { DangerousArtifact } from '../../../../../../../ng-swagger-gen/models/dangerous-artifact';
|
||||||
highchartsAccessibility(Highcharts);
|
import * as echarts from 'echarts/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-vulnerability-summary',
|
selector: 'app-vulnerability-summary',
|
||||||
@ -36,6 +37,9 @@ export class VulnerabilitySummaryComponent implements OnInit, OnDestroy {
|
|||||||
readonly severityText = severityText;
|
readonly severityText = severityText;
|
||||||
readonly getDigestLink = getDigestLink;
|
readonly getDigestLink = getDigestLink;
|
||||||
harborEventSub: Subscription;
|
harborEventSub: Subscription;
|
||||||
|
chart: any;
|
||||||
|
@ViewChild('pieChart', { static: true })
|
||||||
|
pieChartEle: ElementRef;
|
||||||
constructor(
|
constructor(
|
||||||
private securityHubService: SecurityhubService,
|
private securityHubService: SecurityhubService,
|
||||||
private messageHandler: MessageHandlerService,
|
private messageHandler: MessageHandlerService,
|
||||||
@ -44,6 +48,7 @@ export class VulnerabilitySummaryComponent implements OnInit, OnDestroy {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
this.chart = echarts.init(this.pieChartEle.nativeElement);
|
||||||
this.getSummary();
|
this.getSummary();
|
||||||
if (!this.harborEventSub) {
|
if (!this.harborEventSub) {
|
||||||
this.harborEventSub = this.event.subscribe(
|
this.harborEventSub = this.event.subscribe(
|
||||||
@ -91,79 +96,74 @@ export class VulnerabilitySummaryComponent implements OnInit, OnDestroy {
|
|||||||
'UNKNOWN',
|
'UNKNOWN',
|
||||||
];
|
];
|
||||||
this.translate.get([severity, c, h, m, l, n, u]).subscribe(res => {
|
this.translate.get([severity, c, h, m, l, n, u]).subscribe(res => {
|
||||||
Highcharts.chart('pie-chart', {
|
this.chart.setOption({
|
||||||
credits: {
|
color: ['red', '#e64524', 'orange', '#007CBB', 'grey', 'green'],
|
||||||
enabled: false,
|
|
||||||
},
|
|
||||||
chart: {
|
|
||||||
backgroundColor: 'transparent',
|
|
||||||
plotBackgroundColor: null,
|
|
||||||
plotBorderWidth: null,
|
|
||||||
plotShadow: false,
|
|
||||||
type: 'pie',
|
|
||||||
},
|
|
||||||
title: {
|
title: {
|
||||||
text: '',
|
text: '',
|
||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
pointFormat: '<b>{point.percentage:.1f}%</b>',
|
formatter: '<b>{b}: {d}%</b>',
|
||||||
},
|
|
||||||
plotOptions: {
|
|
||||||
pie: {
|
|
||||||
dataLabels: {
|
|
||||||
enabled: false,
|
|
||||||
},
|
|
||||||
showInLegend: true,
|
|
||||||
borderWidth: 0,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
align: 'left',
|
align: 'left',
|
||||||
floating: true,
|
left: 5,
|
||||||
symbolRadius: 2,
|
bottom: 5,
|
||||||
itemStyle: {
|
formatter: '{a|{name}}',
|
||||||
fontSize: '12px',
|
textStyle: {
|
||||||
fontWeight: '100',
|
|
||||||
color: this.getColorByTheme(),
|
color: this.getColorByTheme(),
|
||||||
|
width: 50,
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
rich: {
|
||||||
|
a: {
|
||||||
|
fontWeight: '100',
|
||||||
|
fontSize: '12px',
|
||||||
|
verticalAlign: 'bottom',
|
||||||
|
height: 12,
|
||||||
|
lineHeight: 15,
|
||||||
},
|
},
|
||||||
width: '60%',
|
},
|
||||||
|
},
|
||||||
|
itemWidth: 12,
|
||||||
|
itemHeight: 12,
|
||||||
|
width: '50%',
|
||||||
},
|
},
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
innerSize: '60%',
|
itemStyle: {
|
||||||
|
borderRadius: 3,
|
||||||
|
borderWidth: 1,
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
radius: ['50%', '80%'],
|
||||||
name: res[severity],
|
name: res[severity],
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
center: ['80%', '50%'],
|
center: ['68%', '50%'],
|
||||||
data: [
|
data: [
|
||||||
{
|
{
|
||||||
name: res[c],
|
name: res[c],
|
||||||
y: summary?.critical_cnt || 0,
|
value: summary?.critical_cnt || 0,
|
||||||
color: 'red',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: res[h],
|
name: res[h],
|
||||||
y: summary?.high_cnt || 0,
|
value: summary?.high_cnt || 0,
|
||||||
color: '#e64524',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: res[m],
|
name: res[m],
|
||||||
y: summary?.medium_cnt || 0,
|
value: summary?.medium_cnt || 0,
|
||||||
color: 'orange',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: res[l],
|
name: res[l],
|
||||||
y: summary?.low_cnt || 0,
|
value: summary?.low_cnt || 0,
|
||||||
color: '#007CBB',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: res[u],
|
name: res[u],
|
||||||
y: summary?.unknown_cnt || 0,
|
value: summary?.unknown_cnt || 0,
|
||||||
color: 'grey',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: res[n],
|
name: res[n],
|
||||||
y: summary?.none_cnt || 0,
|
value: summary?.none_cnt || 0,
|
||||||
color: 'green',
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -185,4 +185,9 @@ export class VulnerabilitySummaryComponent implements OnInit, OnDestroy {
|
|||||||
? '#000'
|
? '#000'
|
||||||
: '#fff';
|
: '#fff';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@HostListener('window:resize', ['$event'])
|
||||||
|
onResize(event: any) {
|
||||||
|
this.chart.resize();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,9 +76,35 @@ import { HarborDatetimePipe } from './pipes/harbor-datetime.pipe';
|
|||||||
import { RemainingTimeComponent } from './components/remaining-time/remaining-time.component';
|
import { RemainingTimeComponent } from './components/remaining-time/remaining-time.component';
|
||||||
import { LabelSelectorComponent } from './components/label-selector/label-selector.component';
|
import { LabelSelectorComponent } from './components/label-selector/label-selector.component';
|
||||||
import { ScrollSectionDirective } from './directives/scroll/scroll-section.directive';
|
import { ScrollSectionDirective } from './directives/scroll/scroll-section.directive';
|
||||||
import { ScrollManagerService } from './directives/scroll/scroll-manager.service';
|
|
||||||
import { ScrollAnchorDirective } from './directives/scroll/scroll-anchor.directive';
|
import { ScrollAnchorDirective } from './directives/scroll/scroll-anchor.directive';
|
||||||
import { AppLevelAlertsComponent } from './components/app-level-alerts/app-level-alerts.component';
|
import { AppLevelAlertsComponent } from './components/app-level-alerts/app-level-alerts.component';
|
||||||
|
// import echarts
|
||||||
|
import * as echarts from 'echarts/core';
|
||||||
|
import { PieChart } from 'echarts/charts';
|
||||||
|
import {
|
||||||
|
TitleComponent,
|
||||||
|
TooltipComponent,
|
||||||
|
GridComponent,
|
||||||
|
DatasetComponent,
|
||||||
|
TransformComponent,
|
||||||
|
LegendComponent,
|
||||||
|
} from 'echarts/components';
|
||||||
|
import { LabelLayout, UniversalTransition } from 'echarts/features';
|
||||||
|
import { CanvasRenderer } from 'echarts/renderers';
|
||||||
|
|
||||||
|
// register necessary components
|
||||||
|
echarts.use([
|
||||||
|
TitleComponent,
|
||||||
|
TooltipComponent,
|
||||||
|
GridComponent,
|
||||||
|
DatasetComponent,
|
||||||
|
TransformComponent,
|
||||||
|
PieChart,
|
||||||
|
LabelLayout,
|
||||||
|
UniversalTransition,
|
||||||
|
CanvasRenderer,
|
||||||
|
LegendComponent,
|
||||||
|
]);
|
||||||
|
|
||||||
// ClarityIcons is publicly accessible from the browser's window object.
|
// ClarityIcons is publicly accessible from the browser's window object.
|
||||||
declare const ClarityIcons: ClarityIconsApi;
|
declare const ClarityIcons: ClarityIconsApi;
|
||||||
|
Loading…
Reference in New Issue
Block a user