fix bug
This commit is contained in:
		| @ -0,0 +1,69 @@ | ||||
| <!-- 页头 --> | ||||
| <page-header-wrapper [title]="'数据报表'"></page-header-wrapper> | ||||
| <div nz-row [nzGutter]="16"> | ||||
|   <div nz-col class="gutter-row" [nzSpan]="6"> | ||||
|     <g2-card [title]="'客户预存款总额'" [bordered]="true" [total]="'¥ 126,560.00'" [footer]="footer" contentHeight="46"> | ||||
|       <ng-template #footer> | ||||
|         <g2-mini-area line color="#cceafe" height="45" [data]="visitData" (clickItem)="handleClick($event)"> | ||||
|         </g2-mini-area> | ||||
|       </ng-template> | ||||
|     </g2-card> | ||||
|   </div> | ||||
|   <div nz-col class="gutter-row" [nzSpan]="6"> | ||||
|     <g2-card [title]="'业绩量总额'" [bordered]="true" [total]="'¥ 126,560.00'" [footer]="footer" contentHeight="46"> | ||||
|       <ng-template #footer> | ||||
|         <g2-mini-area line color="#cceafe" height="45" [data]="visitData" (clickItem)="handleClick($event)"> | ||||
|         </g2-mini-area> | ||||
|       </ng-template> | ||||
|     </g2-card> | ||||
|   </div> | ||||
|   <div nz-col class="gutter-row" [nzSpan]="6"> | ||||
|     <g2-card [title]="'司机应付总额'" [bordered]="true" [total]="'¥ 126,560.00'" [footer]="footer" contentHeight="46"> | ||||
|       <ng-template #footer> | ||||
|         <g2-mini-area line color="#cceafe" height="45" [data]="visitData" (clickItem)="handleClick($event)"> | ||||
|         </g2-mini-area> | ||||
|       </ng-template> | ||||
|     </g2-card> | ||||
|   </div> | ||||
|   <div nz-col class="gutter-row" [nzSpan]="6"> | ||||
|     <g2-card [title]="'附加费总额'" [bordered]="true" [total]="'¥ 126,560.00'" [footer]="footer" contentHeight="46"> | ||||
|       <ng-template #footer> | ||||
|         <g2-mini-area line color="#cceafe" height="45" [data]="visitData" (clickItem)="handleClick($event)"> | ||||
|         </g2-mini-area> | ||||
|       </ng-template> | ||||
|     </g2-card> | ||||
|   </div> | ||||
| </div> | ||||
| <div nz-row [nzGutter]="16"> | ||||
|   <div nz-col class="gutter-row" [nzSpan]="12"> | ||||
|     <nz-card [nzTitle]="'订单类型比例'"> | ||||
|       <g2-custom delay="100" (render)="render($event)"></g2-custom> | ||||
|     </nz-card> | ||||
|   </div> | ||||
|   <div nz-col class="gutter-row" [nzSpan]="12"> | ||||
|     <nz-card [nzTitle]="'大区业绩完成情况'"> | ||||
|       <g2-timeline | ||||
|     [data]="chartData" | ||||
|     [titleMap]="{ y1: '客流量', y2: '支付笔数' }" | ||||
|     [height]="200" | ||||
|     mask="MM月DD日" | ||||
|     [slider]="false" | ||||
|   ></g2-timeline> | ||||
|     </nz-card> | ||||
|   </div> | ||||
| </div> | ||||
| <div nz-row [nzGutter]="16"> | ||||
|   <div nz-col class="gutter-row" [nzSpan]="12"> | ||||
|     <nz-card [nzTitle]="'运单直付比例'"> | ||||
|       <g2-pie #pie title="销售额" subTitle="销售额" [total]="total" [valueFormat]="format" [data]="salesPieData" height="294" | ||||
|       (clickItem)="handleClick($event)" [lineWidth]="10"> | ||||
|       </g2-pie> | ||||
|     </nz-card> | ||||
|   </div> | ||||
|   <div nz-col class="gutter-row" [nzSpan]="12"> | ||||
|     <nz-card [nzTitle]="'业绩完成情况'"> | ||||
|       <g2-bar height="200" [title]="'销售额趋势'" [data]="salesData" (clickItem)="handleClick($event)"></g2-bar> | ||||
|  | ||||
|     </nz-card> | ||||
|   </div> | ||||
| </div> | ||||
| @ -0,0 +1,24 @@ | ||||
| import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; | ||||
| import { DatatableDataindexComponent } from './dataindex.component'; | ||||
|  | ||||
| describe('DatatableDataindexComponent', () => { | ||||
|   let component: DatatableDataindexComponent; | ||||
|   let fixture: ComponentFixture<DatatableDataindexComponent>; | ||||
|  | ||||
|   beforeEach(waitForAsync(() => { | ||||
|     TestBed.configureTestingModule({ | ||||
|       declarations: [ DatatableDataindexComponent ] | ||||
|     }) | ||||
|     .compileComponents(); | ||||
|   })); | ||||
|  | ||||
|   beforeEach(() => { | ||||
|     fixture = TestBed.createComponent(DatatableDataindexComponent); | ||||
|     component = fixture.componentInstance; | ||||
|     fixture.detectChanges(); | ||||
|   }); | ||||
|  | ||||
|   it('should create', () => { | ||||
|     expect(component).toBeTruthy(); | ||||
|   }); | ||||
| }); | ||||
| @ -0,0 +1,249 @@ | ||||
| import { Component, ElementRef, NgZone, OnInit, ViewChild } from '@angular/core'; | ||||
| import { ModalHelper, _HttpClient } from '@delon/theme'; | ||||
| import { G2MiniAreaClickItem, G2MiniAreaData } from '@delon/chart/mini-area'; | ||||
| import { G2PieClickItem, G2PieComponent, G2PieData } from '@delon/chart/pie'; | ||||
| import { format } from 'date-fns'; | ||||
| import { DataService } from '../../services/data.service'; | ||||
| import { Chart, registerShape, Util } from '@antv/g2'; | ||||
| import { G2TimelineClickItem, G2TimelineData } from '@delon/chart/timeline'; | ||||
|  | ||||
| @Component({ | ||||
|   selector: 'app-datatable-dataindex', | ||||
|   templateUrl: './dataindex.component.html', | ||||
|   styleUrls: ['./dataindex.component.less'] | ||||
| }) | ||||
| export class DatatableDataindexComponent implements OnInit { | ||||
|   @ViewChild('pie', { static: false }) pie!: G2PieComponent; | ||||
|   chartData: G2TimelineData[] = []; | ||||
|   visitData = this.genData(); | ||||
|   salesData = this.genData(); | ||||
|   salesPieData: G2PieData[] = []; | ||||
|   total = ''; | ||||
|  | ||||
|   constructor(private service: DataService, private ngZone: NgZone) { | ||||
|  | ||||
|   } | ||||
|  | ||||
|   ngOnInit(): void { | ||||
|     this.refreshPie(); | ||||
|     this.initLineData() | ||||
|   } | ||||
|   initLineData(){ | ||||
|     for (let i = 0; i < 20; i += 1) { | ||||
|       this.chartData.push({ | ||||
|         time: new Date().getTime() + 1000 * 60 * 60 * 24 * i, | ||||
|         y1: Math.floor(Math.random() * 100) + 1000, | ||||
|         y2: Math.floor(Math.random() * 100) + 10, | ||||
|       }); | ||||
|     } | ||||
|   } | ||||
|   private genData(): G2MiniAreaData[] { | ||||
|     const beginDay = new Date().getTime(); | ||||
|     const res: G2MiniAreaData[] = []; | ||||
|     for (let i = 0; i < 20; i += 1) { | ||||
|       res.push({ | ||||
|         x: format(new Date(beginDay + 1000 * 60 * 60 * 24 * i), 'yyyy-MM-dd'), | ||||
|         y: Math.floor(Math.random() * 100) + 10, | ||||
|       }); | ||||
|     } | ||||
|     return res; | ||||
|   } | ||||
|  | ||||
|   refresh(): void { | ||||
|     this.visitData = this.genData(); | ||||
|   } | ||||
|   refreshPie(): void { | ||||
|     const rv = (min: number = 0, max: number = 5000) => Math.floor(Math.random() * (max - min + 1) + min); | ||||
|     this.salesPieData = [ | ||||
|       { | ||||
|         x: '家用电器', | ||||
|         y: rv(), | ||||
|       }, | ||||
|       { | ||||
|         x: '食用酒水', | ||||
|         y: rv(), | ||||
|       }, | ||||
|       { | ||||
|         x: '个护健康', | ||||
|         y: rv(), | ||||
|       }, | ||||
|       { | ||||
|         x: '服饰箱包', | ||||
|         y: rv(), | ||||
|       }, | ||||
|       { | ||||
|         x: '母婴产品', | ||||
|         y: rv(), | ||||
|       }, | ||||
|     ]; | ||||
|     if (Math.random() > 0.5) { | ||||
|       this.salesPieData.push({ | ||||
|         x: '其他', | ||||
|         y: rv(), | ||||
|       }); | ||||
|     } | ||||
|     this.total = `¥ ${this.salesPieData.reduce((pre, now) => now.y + pre, 0).toFixed(2)}`; | ||||
|     if (this.pie) { | ||||
|       // 等待组件渲染 | ||||
|       setTimeout(() => { | ||||
|         console.log('a') | ||||
|         this.pie.changeData() | ||||
|       }); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   handleClick(data: G2MiniAreaClickItem): void { | ||||
|     this.service.msgSrv.info(`${data.item.x} - ${data.item.y}`); | ||||
|   } | ||||
|   format(val: number): string { | ||||
|     return `¥ ${val.toFixed(2)}`; | ||||
|   } | ||||
|   render(el: ElementRef<HTMLDivElement>): void { | ||||
|     this.ngZone.runOutsideAngular(() => this.init(el.nativeElement)); | ||||
|   } | ||||
|   private init(el: HTMLElement): void { | ||||
|     const data = [ | ||||
|       { item: '货源单', count: 40, percent: 0.4 }, | ||||
|       { item: '合同单', count: 21, percent: 0.21 }, | ||||
|       { item: '事例三', count: 17, percent: 0.17 }, | ||||
|       { item: '事例四', count: 13, percent: 0.13 }, | ||||
|       { item: '事例五', count: 9, percent: 0.09 }, | ||||
|     ]; | ||||
|     const chart = new Chart({ | ||||
|       container: el, | ||||
|       autoFit: true, | ||||
|       height: 400, | ||||
|     }); | ||||
|     // 新建一个 view 用来单独渲染Annotation | ||||
|     const innerView = chart.createView(); | ||||
|     chart.coordinate('theta', { | ||||
|       radius: 0.6, | ||||
|       innerRadius: 0.7, | ||||
|     }); | ||||
|  | ||||
|     chart.data(data); | ||||
|  | ||||
|     chart.scale('percent', { | ||||
|       formatter: val => { | ||||
|         val = val * 100 + '%'; | ||||
|         return val; | ||||
|       }, | ||||
|     }); | ||||
|  | ||||
|     chart.tooltip(false); | ||||
|  | ||||
|     // 声明需要进行自定义图例字段: 'item' | ||||
|     chart.legend('item', { | ||||
|       position: 'right',                                  // 配置图例显示位置 | ||||
|       custom: true,                                       // 关键字段,告诉 G2,要使用自定义的图例 | ||||
|       items: data.map((obj, index) => { | ||||
|         return { | ||||
|           name: obj.item,                                 // 对应 itemName | ||||
|           value: obj.percent,                             // 对应 itemValue | ||||
|           marker: { | ||||
|             symbol: 'square',                             // marker 的形状 | ||||
|             style: { | ||||
|               r: 5,                                       // marker 图形半径 | ||||
|               fill: chart.getTheme().colors10[index],     // marker 颜色,使用默认颜色,同图形对应 | ||||
|             }, | ||||
|           },                                              // marker 配置 | ||||
|         }; | ||||
|       }), | ||||
|       itemValue: { | ||||
|         style: { | ||||
|           fill: '#999', | ||||
|         },                                               // 配置 itemValue 样式 | ||||
|         formatter: (val: any) => `${val * 100}%`                // 格式化 itemValue 内容 | ||||
|       }, | ||||
|     }); | ||||
|  | ||||
|     chart | ||||
|       .interval() | ||||
|       .adjust('stack') | ||||
|       .position('percent') | ||||
|       .color('item') | ||||
|       .style({ | ||||
|         fillOpacity: 1, | ||||
|         stroke: 'white', | ||||
|         lineWidth: 8, | ||||
|       }) | ||||
|       .state({ | ||||
|         active: { | ||||
|           style: element => { | ||||
|             const shape = element.shape; | ||||
|             return { | ||||
|               lineWidth: 1, | ||||
|               stroke: 'white', | ||||
|               strokeOpacity: shape.attr('fillOpacity'), | ||||
|             }; | ||||
|           }, | ||||
|         }, | ||||
|       }); | ||||
|  | ||||
|     // 移除图例点击过滤交互 | ||||
|     chart.removeInteraction('legend-filter'); | ||||
|     chart.interaction('element-active'); | ||||
|  | ||||
|     chart.render(); | ||||
|  | ||||
|     // 监听 element 上状态的变化来动态更新 Annotation 信息 | ||||
|     chart.on('element:statechange', (ev: any) => { | ||||
|       const { state, stateStatus, element } = ev.gEvent.originalEvent; | ||||
|  | ||||
|       // 本示例只需要监听 active 的状态变化 | ||||
|       if (state === 'active') { | ||||
|         const data = element.getData(); | ||||
|         if (stateStatus) { | ||||
|           // 更新 Annotation | ||||
|           updateAnnotation(data); | ||||
|         } else { | ||||
|           // 隐藏 Annotation | ||||
|           clearAnnotation(); | ||||
|         } | ||||
|       } | ||||
|     }); | ||||
|  | ||||
|     // 绘制 annotation | ||||
|     let lastItem: any; | ||||
|     function updateAnnotation(data: any) { | ||||
|       if (data.item !== lastItem) { | ||||
|         innerView.annotation().clear(true); | ||||
|         innerView | ||||
|           .annotation() | ||||
|           .text({ | ||||
|             position: ['50%', '50%'], | ||||
|             content: data.item, | ||||
|             style: { | ||||
|               fontSize: 20, | ||||
|               fill: '#8c8c8c', | ||||
|               textAlign: 'center', | ||||
|             }, | ||||
|             offsetY: -20, | ||||
|           }) | ||||
|           .text({ | ||||
|             position: ['50%', '50%'], | ||||
|             content: data.count, | ||||
|             style: { | ||||
|               fontSize: 28, | ||||
|               fill: '#8c8c8c', | ||||
|               textAlign: 'center', | ||||
|             }, | ||||
|             offsetX: -10, | ||||
|             offsetY: 20, | ||||
|           }) | ||||
|         innerView.render(true); | ||||
|         lastItem = data.item; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     // 清空 annotation | ||||
|     function clearAnnotation() { | ||||
|       innerView.annotation().clear(true); | ||||
|       innerView.render(true); | ||||
|       lastItem = null; | ||||
|     } | ||||
|  | ||||
|   } | ||||
| } | ||||
|  | ||||
|  | ||||
| @ -0,0 +1,69 @@ | ||||
| <!-- 页头 --> | ||||
| <page-header-wrapper [title]="'数据报表'"></page-header-wrapper> | ||||
| <div nz-row [nzGutter]="16"> | ||||
|   <div nz-col class="gutter-row" [nzSpan]="6"> | ||||
|     <g2-card [title]="'客户预存款总额'" [bordered]="true" [total]="'¥ 126,560.00'" [footer]="footer" contentHeight="46"> | ||||
|       <ng-template #footer> | ||||
|         <g2-mini-area line color="#cceafe" height="45" [data]="visitData" (clickItem)="handleClick($event)"> | ||||
|         </g2-mini-area> | ||||
|       </ng-template> | ||||
|     </g2-card> | ||||
|   </div> | ||||
|   <div nz-col class="gutter-row" [nzSpan]="6"> | ||||
|     <g2-card [title]="'业绩量总额'" [bordered]="true" [total]="'¥ 126,560.00'" [footer]="footer" contentHeight="46"> | ||||
|       <ng-template #footer> | ||||
|         <g2-mini-area line color="#cceafe" height="45" [data]="visitData" (clickItem)="handleClick($event)"> | ||||
|         </g2-mini-area> | ||||
|       </ng-template> | ||||
|     </g2-card> | ||||
|   </div> | ||||
|   <div nz-col class="gutter-row" [nzSpan]="6"> | ||||
|     <g2-card [title]="'司机应付总额'" [bordered]="true" [total]="'¥ 126,560.00'" [footer]="footer" contentHeight="46"> | ||||
|       <ng-template #footer> | ||||
|         <g2-mini-area line color="#cceafe" height="45" [data]="visitData" (clickItem)="handleClick($event)"> | ||||
|         </g2-mini-area> | ||||
|       </ng-template> | ||||
|     </g2-card> | ||||
|   </div> | ||||
|   <div nz-col class="gutter-row" [nzSpan]="6"> | ||||
|     <g2-card [title]="'附加费总额'" [bordered]="true" [total]="'¥ 126,560.00'" [footer]="footer" contentHeight="46"> | ||||
|       <ng-template #footer> | ||||
|         <g2-mini-area line color="#cceafe" height="45" [data]="visitData" (clickItem)="handleClick($event)"> | ||||
|         </g2-mini-area> | ||||
|       </ng-template> | ||||
|     </g2-card> | ||||
|   </div> | ||||
| </div> | ||||
| <div nz-row [nzGutter]="16"> | ||||
|   <div nz-col class="gutter-row" [nzSpan]="12"> | ||||
|     <nz-card [nzTitle]="'订单类型比例'"> | ||||
|       <g2-custom delay="100" (render)="render($event)"></g2-custom> | ||||
|     </nz-card> | ||||
|   </div> | ||||
|   <div nz-col class="gutter-row" [nzSpan]="12"> | ||||
|     <nz-card [nzTitle]="'大区业绩完成情况'"> | ||||
|       <g2-timeline | ||||
|     [data]="chartData" | ||||
|     [titleMap]="{ y1: '客流量', y2: '支付笔数' }" | ||||
|     [height]="200" | ||||
|     mask="MM月DD日" | ||||
|     [slider]="false" | ||||
|   ></g2-timeline> | ||||
|     </nz-card> | ||||
|   </div> | ||||
| </div> | ||||
| <div nz-row [nzGutter]="16"> | ||||
|   <div nz-col class="gutter-row" [nzSpan]="12"> | ||||
|     <nz-card [nzTitle]="'运单直付比例'"> | ||||
|       <g2-pie #pie title="销售额" subTitle="销售额" [total]="total" [valueFormat]="format" [data]="salesPieData" height="294" | ||||
|       (clickItem)="handleClick($event)" [lineWidth]="10"> | ||||
|       </g2-pie> | ||||
|     </nz-card> | ||||
|   </div> | ||||
|   <div nz-col class="gutter-row" [nzSpan]="12"> | ||||
|     <nz-card [nzTitle]="'业绩完成情况'"> | ||||
|       <g2-bar height="200" [title]="'销售额趋势'" [data]="salesData" (clickItem)="handleClick($event)"></g2-bar> | ||||
|  | ||||
|     </nz-card> | ||||
|   </div> | ||||
| </div> | ||||
| @ -0,0 +1,24 @@ | ||||
| import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; | ||||
| import { DatatableDataindexComponent } from './dataindex.component'; | ||||
|  | ||||
| describe('DatatableDataindexComponent', () => { | ||||
|   let component: DatatableDataindexComponent; | ||||
|   let fixture: ComponentFixture<DatatableDataindexComponent>; | ||||
|  | ||||
|   beforeEach(waitForAsync(() => { | ||||
|     TestBed.configureTestingModule({ | ||||
|       declarations: [ DatatableDataindexComponent ] | ||||
|     }) | ||||
|     .compileComponents(); | ||||
|   })); | ||||
|  | ||||
|   beforeEach(() => { | ||||
|     fixture = TestBed.createComponent(DatatableDataindexComponent); | ||||
|     component = fixture.componentInstance; | ||||
|     fixture.detectChanges(); | ||||
|   }); | ||||
|  | ||||
|   it('should create', () => { | ||||
|     expect(component).toBeTruthy(); | ||||
|   }); | ||||
| }); | ||||
| @ -0,0 +1,249 @@ | ||||
| import { Component, ElementRef, NgZone, OnInit, ViewChild } from '@angular/core'; | ||||
| import { ModalHelper, _HttpClient } from '@delon/theme'; | ||||
| import { G2MiniAreaClickItem, G2MiniAreaData } from '@delon/chart/mini-area'; | ||||
| import { G2PieClickItem, G2PieComponent, G2PieData } from '@delon/chart/pie'; | ||||
| import { format } from 'date-fns'; | ||||
| import { DataindexService } from '../services/dataindex.service'; | ||||
| import { Chart, registerShape, Util } from '@antv/g2'; | ||||
| import { G2TimelineClickItem, G2TimelineData } from '@delon/chart/timeline'; | ||||
|  | ||||
| @Component({ | ||||
|   selector: 'app-datatable-dataindex', | ||||
|   templateUrl: './dataindex.component.html', | ||||
|   styleUrls: ['./dataindex.component.less'] | ||||
| }) | ||||
| export class DatatableDataindexComponent implements OnInit { | ||||
|   @ViewChild('pie', { static: false }) pie!: G2PieComponent; | ||||
|   chartData: G2TimelineData[] = []; | ||||
|   visitData = this.genData(); | ||||
|   salesData = this.genData(); | ||||
|   salesPieData: G2PieData[] = []; | ||||
|   total = ''; | ||||
|  | ||||
|   constructor(private service: DataindexService, private ngZone: NgZone) { | ||||
|  | ||||
|   } | ||||
|  | ||||
|   ngOnInit(): void { | ||||
|     this.refreshPie(); | ||||
|     this.initLineData() | ||||
|   } | ||||
|   initLineData(){ | ||||
|     for (let i = 0; i < 20; i += 1) { | ||||
|       this.chartData.push({ | ||||
|         time: new Date().getTime() + 1000 * 60 * 60 * 24 * i, | ||||
|         y1: Math.floor(Math.random() * 100) + 1000, | ||||
|         y2: Math.floor(Math.random() * 100) + 10, | ||||
|       }); | ||||
|     } | ||||
|   } | ||||
|   private genData(): G2MiniAreaData[] { | ||||
|     const beginDay = new Date().getTime(); | ||||
|     const res: G2MiniAreaData[] = []; | ||||
|     for (let i = 0; i < 20; i += 1) { | ||||
|       res.push({ | ||||
|         x: format(new Date(beginDay + 1000 * 60 * 60 * 24 * i), 'yyyy-MM-dd'), | ||||
|         y: Math.floor(Math.random() * 100) + 10, | ||||
|       }); | ||||
|     } | ||||
|     return res; | ||||
|   } | ||||
|  | ||||
|   refresh(): void { | ||||
|     this.visitData = this.genData(); | ||||
|   } | ||||
|   refreshPie(): void { | ||||
|     const rv = (min: number = 0, max: number = 5000) => Math.floor(Math.random() * (max - min + 1) + min); | ||||
|     this.salesPieData = [ | ||||
|       { | ||||
|         x: '家用电器', | ||||
|         y: rv(), | ||||
|       }, | ||||
|       { | ||||
|         x: '食用酒水', | ||||
|         y: rv(), | ||||
|       }, | ||||
|       { | ||||
|         x: '个护健康', | ||||
|         y: rv(), | ||||
|       }, | ||||
|       { | ||||
|         x: '服饰箱包', | ||||
|         y: rv(), | ||||
|       }, | ||||
|       { | ||||
|         x: '母婴产品', | ||||
|         y: rv(), | ||||
|       }, | ||||
|     ]; | ||||
|     if (Math.random() > 0.5) { | ||||
|       this.salesPieData.push({ | ||||
|         x: '其他', | ||||
|         y: rv(), | ||||
|       }); | ||||
|     } | ||||
|     this.total = `¥ ${this.salesPieData.reduce((pre, now) => now.y + pre, 0).toFixed(2)}`; | ||||
|     if (this.pie) { | ||||
|       // 等待组件渲染 | ||||
|       setTimeout(() => { | ||||
|         console.log('a') | ||||
|         this.pie.changeData() | ||||
|       }); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   handleClick(data: G2MiniAreaClickItem): void { | ||||
|     this.service.msgSrv.info(`${data.item.x} - ${data.item.y}`); | ||||
|   } | ||||
|   format(val: number): string { | ||||
|     return `¥ ${val.toFixed(2)}`; | ||||
|   } | ||||
|   render(el: ElementRef<HTMLDivElement>): void { | ||||
|     this.ngZone.runOutsideAngular(() => this.init(el.nativeElement)); | ||||
|   } | ||||
|   private init(el: HTMLElement): void { | ||||
|     const data = [ | ||||
|       { item: '货源单', count: 40, percent: 0.4 }, | ||||
|       { item: '合同单', count: 21, percent: 0.21 }, | ||||
|       { item: '事例三', count: 17, percent: 0.17 }, | ||||
|       { item: '事例四', count: 13, percent: 0.13 }, | ||||
|       { item: '事例五', count: 9, percent: 0.09 }, | ||||
|     ]; | ||||
|     const chart = new Chart({ | ||||
|       container: el, | ||||
|       autoFit: true, | ||||
|       height: 400, | ||||
|     }); | ||||
|     // 新建一个 view 用来单独渲染Annotation | ||||
|     const innerView = chart.createView(); | ||||
|     chart.coordinate('theta', { | ||||
|       radius: 0.6, | ||||
|       innerRadius: 0.7, | ||||
|     }); | ||||
|  | ||||
|     chart.data(data); | ||||
|  | ||||
|     chart.scale('percent', { | ||||
|       formatter: val => { | ||||
|         val = val * 100 + '%'; | ||||
|         return val; | ||||
|       }, | ||||
|     }); | ||||
|  | ||||
|     chart.tooltip(false); | ||||
|  | ||||
|     // 声明需要进行自定义图例字段: 'item' | ||||
|     chart.legend('item', { | ||||
|       position: 'right',                                  // 配置图例显示位置 | ||||
|       custom: true,                                       // 关键字段,告诉 G2,要使用自定义的图例 | ||||
|       items: data.map((obj, index) => { | ||||
|         return { | ||||
|           name: obj.item,                                 // 对应 itemName | ||||
|           value: obj.percent,                             // 对应 itemValue | ||||
|           marker: { | ||||
|             symbol: 'square',                             // marker 的形状 | ||||
|             style: { | ||||
|               r: 5,                                       // marker 图形半径 | ||||
|               fill: chart.getTheme().colors10[index],     // marker 颜色,使用默认颜色,同图形对应 | ||||
|             }, | ||||
|           },                                              // marker 配置 | ||||
|         }; | ||||
|       }), | ||||
|       itemValue: { | ||||
|         style: { | ||||
|           fill: '#999', | ||||
|         },                                               // 配置 itemValue 样式 | ||||
|         formatter: (val: any) => `${val * 100}%`                // 格式化 itemValue 内容 | ||||
|       }, | ||||
|     }); | ||||
|  | ||||
|     chart | ||||
|       .interval() | ||||
|       .adjust('stack') | ||||
|       .position('percent') | ||||
|       .color('item') | ||||
|       .style({ | ||||
|         fillOpacity: 1, | ||||
|         stroke: 'white', | ||||
|         lineWidth: 8, | ||||
|       }) | ||||
|       .state({ | ||||
|         active: { | ||||
|           style: element => { | ||||
|             const shape = element.shape; | ||||
|             return { | ||||
|               lineWidth: 1, | ||||
|               stroke: 'white', | ||||
|               strokeOpacity: shape.attr('fillOpacity'), | ||||
|             }; | ||||
|           }, | ||||
|         }, | ||||
|       }); | ||||
|  | ||||
|     // 移除图例点击过滤交互 | ||||
|     chart.removeInteraction('legend-filter'); | ||||
|     chart.interaction('element-active'); | ||||
|  | ||||
|     chart.render(); | ||||
|  | ||||
|     // 监听 element 上状态的变化来动态更新 Annotation 信息 | ||||
|     chart.on('element:statechange', (ev: any) => { | ||||
|       const { state, stateStatus, element } = ev.gEvent.originalEvent; | ||||
|  | ||||
|       // 本示例只需要监听 active 的状态变化 | ||||
|       if (state === 'active') { | ||||
|         const data = element.getData(); | ||||
|         if (stateStatus) { | ||||
|           // 更新 Annotation | ||||
|           updateAnnotation(data); | ||||
|         } else { | ||||
|           // 隐藏 Annotation | ||||
|           clearAnnotation(); | ||||
|         } | ||||
|       } | ||||
|     }); | ||||
|  | ||||
|     // 绘制 annotation | ||||
|     let lastItem: any; | ||||
|     function updateAnnotation(data: any) { | ||||
|       if (data.item !== lastItem) { | ||||
|         innerView.annotation().clear(true); | ||||
|         innerView | ||||
|           .annotation() | ||||
|           .text({ | ||||
|             position: ['50%', '50%'], | ||||
|             content: data.item, | ||||
|             style: { | ||||
|               fontSize: 20, | ||||
|               fill: '#8c8c8c', | ||||
|               textAlign: 'center', | ||||
|             }, | ||||
|             offsetY: -20, | ||||
|           }) | ||||
|           .text({ | ||||
|             position: ['50%', '50%'], | ||||
|             content: data.count, | ||||
|             style: { | ||||
|               fontSize: 28, | ||||
|               fill: '#8c8c8c', | ||||
|               textAlign: 'center', | ||||
|             }, | ||||
|             offsetX: -10, | ||||
|             offsetY: 20, | ||||
|           }) | ||||
|         innerView.render(true); | ||||
|         lastItem = data.item; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     // 清空 annotation | ||||
|     function clearAnnotation() { | ||||
|       innerView.annotation().clear(true); | ||||
|       innerView.render(true); | ||||
|       lastItem = null; | ||||
|     } | ||||
|  | ||||
|   } | ||||
| } | ||||
|  | ||||
|  | ||||
		Reference in New Issue
	
	Block a user