import { ChangeDetectorRef, 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'; import { CurrencyPipe } from '@angular/common'; import { LOGS } from '_mock'; import { G2CustomComponent } from '@delon/chart/custom'; import { G2BarData } from '@delon/chart/bar'; import { GeometryLabelCfg } from '@antv/g2/lib/interface'; @Component({ selector: 'app-datatable-dataindex', templateUrl: './dataindex.component.html', styleUrls: ['./dataindex.component.less'], providers: [CurrencyPipe] }) export class DatatableDataindexComponent implements OnInit { @ViewChild('g2custom', { static: false }) g2custom!: G2CustomComponent; @ViewChild('RegionalPerforman', { static: false }) RegionalPerforman!: G2CustomComponent; @ViewChild('BillDirectProportion', { static: false }) BillDirectProportion!: G2CustomComponent; @ViewChild('SaleProportion', { static: false }) SaleProportion!: G2CustomComponent; salesData: any[] = []; totalAdvanceDeposit: { totalAmount: string; list: G2MiniAreaData[] } = { totalAmount: '0', list: this.genData() }; totalPerformanceVolume: { totalAmount: string; list: G2MiniAreaData[] } = { totalAmount: '0', list: this.genData() }; totalFreight: { totalAmount: string; list: G2MiniAreaData[] } = { totalAmount: '0', list: this.genData() }; totalSurcharge: { totalAmount: string; list: G2MiniAreaData[] } = { totalAmount: '0', list: this.genData() }; billTypeDatas: any = [ { item: '货源单', count: 0, percent: 0 }, { item: '合同单', count: 0, percent: 0 } ]; regionalPerformanceCompletion: DataPerformanceTrendVO[] = []; constructor(private service: DataService, private currency: CurrencyPipe) {} ngOnInit(): void { this.initMiniAreaData(); this.initOthersData(); } private initMiniAreaData() { // 客户预存款总额 this.service.request(this.service.$api_total_advance_deposit).subscribe((res: DataTotalVO) => { if (res) { this.totalAdvanceDeposit = this.formatMiniAreaData(res); } }); // 业绩量总额 this.service.request(this.service.$api_total_performance_volume).subscribe((res: DataTotalVO) => { if (res) { this.totalPerformanceVolume = this.formatMiniAreaData(res); } }); // 司机应付总额 this.service.request(this.service.$api_total_freight).subscribe((res: DataTotalVO) => { if (res) { this.totalFreight = this.formatMiniAreaData(res); } }); // 业绩量总额 this.service.request(this.service.$api_total_surcharge).subscribe((res: DataTotalVO) => { if (res) { this.totalSurcharge = this.formatMiniAreaData(res); } }); } private initOthersData() { // 订单类型比例 this.service.request(this.service.$api_getBillTypeProportion).subscribe(res => { if (res) { this.billTypeDatas = this.formatCoordinateData(res); this.initBillChart(this.g2custom['el'].nativeElement as any); } }); // 大区业绩完成情况 this.service.request(this.service.$api_getBillAmount).subscribe((res: DataPerformanceTrendVO[]) => { if (res) { this.regionalPerformanceCompletion = res.map(item => ({ ...item, time: new Date(item.time)?.getTime() })); this.initRegionalPerformanceChart(this.RegionalPerforman['el'].nativeElement as any, this.regionalPerformanceCompletion); } }); // 订单类型比例 this.service.request(this.service.$api_getWayBillDirectProportion).subscribe(res => { if (res) { const billTypeDatas = this.formatCoordinateData(res.map((item: any) => ({ ...item, billType: item.wayBillType }))); this.initBillChart(this.BillDirectProportion['el'].nativeElement as any, billTypeDatas); } }); // 统计订单结算金额-趋势 this.service.request(this.service.$api_get_bill_payment_amount).subscribe(res => { if (res) { this.salesData = this.formatBarData(res); this.initBiaxialChart(this.SaleProportion['el'].nativeElement as any, this.salesData); } }); } handleClick(data: G2MiniAreaClickItem): void { this.service.msgSrv.info(`${data.item.x} - ${data.item.y}`); } /** * 构建订单类型秘鲁图 * @param el */ private initBillChart(el: HTMLElement, datas?: any[]): void { const data = datas || this.billTypeDatas; 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: any, index: any) => { 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') }; } } }); innerView .annotation() .text({ position: ['50%', '50%'], content: data[0].item, style: { fontSize: 20, fill: '#8c8c8c', textAlign: 'center' }, offsetY: -20 }) .text({ position: ['50%', '50%'], content: data[0].count, style: { fontSize: 28, fill: '#8c8c8c', textAlign: 'center' }, offsetY: 20 }); innerView.render(true); // 移除图例点击过滤交互 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' }, offsetY: 20 }); innerView.render(true); lastItem = data.item; } } // 清空 annotation function clearAnnotation() { innerView.annotation().clear(true); innerView.render(true); lastItem = null; } } /** * 构建大区业绩完成情况柱折双轴图 * @param el * @param data */ private initRegionalPerformanceChart(el: HTMLElement, data: DataPerformanceTrendVO[]) { const chart = new Chart({ container: el, autoFit: true, height: 400 }); chart.data(data); chart.scale({ time: { alias: '日期', type: 'time' }, quantity: { alias: '金额', min: 0, sync: true, // 将 pv 字段数值同 time 字段数值进行同步 nice: true } }); chart.axis('quantity', { title: {} }); chart.tooltip({ showCrosshairs: true, shared: true }); // 声明需要进行自定义图例字段: 'item' chart.legend({ offsetY: 10, position: 'bottom', // 配置图例显示位置 custom: true, // 关键字段,告诉 G2,要使用自定义的图例 items: [ { name: '订单金额 (元)', // 对应 itemName value: '' // 对应 itemValue } ], itemValue: { style: { fill: '#999' } // 配置 itemValue 样式 } }); chart.line().position('time*quantity').color('#4FAAEB'); chart.render(); } /** * 构建业绩完成情况柱折双轴图 * @param el * @param data */ private initBiaxialChart(el: HTMLElement, data: any[]) { const chart = new Chart({ container: el, autoFit: true, height: 400 }); chart.data(data); // 设置坐标轴 chart.scale({ pre: { alias: '同期业绩完成率', min: 0, max: 1, formatter: val => val * 100 + '%' }, pre2: { alias: '业绩完成率', min: 0, max: 1, formatter: val => val * 100 + '%' }, quantity: { alias: '业绩量 (万)', min: 0, max: 1000000 } }); // 设置 chart.legend({ custom: true, items: [ { value: 'quantity', name: '业绩量 (万)', marker: { symbol: 'hyphen', style: { stroke: '#3182bd', r: 15, lineWidth: 5 } } }, { value: 'pre2', name: '业绩完成率', marker: { symbol: 'hyphen', style: { stroke: '#fdae6b', r: 15, lineWidth: 5 } } }, { value: 'pre', name: '同期业绩完成率', marker: { symbol: 'hyphen', style: { stroke: '#ff4d4f', r: 15, lineWidth: 5 } } } ] }); chart.axis('pre', { grid: null, title: null, label: { formatter: val => +val * 100 + '%' } }); chart.axis('pre2', false); chart.tooltip({ shared: true }); chart.interval().position('time*quantity').label('quantity').color('#3182bd'); chart .line() .position('time*pre') .label('pre', val => ({ content: (val * 100).toFixed(0) + '%' })) .color('#ff4d4f') .size(3); chart.point().position('time*pre').color('#ff4d4f').size(3).shape('circle'); chart .line() .position('time*pre2') .label('pre2', val => ({ content: (val * 100).toFixed(0) + '%' })) .color('#fdae6b') .size(3); chart.point().position('time*pre2').color('#fdae6b').size(3).shape('circle'); chart.interaction('active-region'); chart.removeInteraction('legend-filter'); // 自定义图例,移除默认的分类图例筛选交互 chart.render(); } private formatMiniAreaData(data: DataTotalVO) { return { totalAmount: `${this.currency.transform(data.totalAmount || 0)}万`, list: data.list?.map(item => ({ x: item.time, y: item.quantity })) }; } /** * 初始化饼图数据格式 * @param data * @returns */ private formatCoordinateData(data: DataBillTypeProportion[]): any[] { const total = data.map(item => item.quantity).reduce((pre, next) => pre + next); const rs: any[] = []; data.forEach(item => { rs.push({ item: (BILLTYPE as any)[item.billType], count: item.quantity, percent: Number((item.quantity / total).toFixed(2)) }); }); return rs; } private formatBarData(data: DataPerformanceTrendVO[]): any[] { return data.map(item => ({ time: item.time, quantity: item.quantity, color: undefined, pre: Math.floor(Math.random() * 100) / 100, pre2: Math.floor(Math.random() * 100) / 100 })); } 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() * 1) }); } return res; } } interface DataTotalVO { totalAmount: number; list: DataPerformanceTrendVO[]; } interface DataPerformanceTrendVO { quantity: number; time: number; } interface DataBillTypeProportion { billType: BILLTYPE; quantity: number; } enum BILLTYPE { 'HY' = '货源单', 'HT' = '合同单', 'ZF' = '直付', 'CDZ' = '车队长' }