From 236216f65f38f332b93b38c740bd6e0c68ef294a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9F=A9=E5=B3=A510322371?= Date: Thu, 25 Jan 2024 20:39:02 +0800 Subject: [PATCH 1/8] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E6=B0=94=E6=B3=A1?= =?UTF-8?q?=E5=9B=BE=E7=9B=B8=E5=85=B3=E7=B1=BB=E5=9E=8B=E4=BB=A5=E5=8F=8A?= =?UTF-8?q?=E5=90=8E=E7=BB=AD=E8=AE=BE=E7=BD=AE=EF=BC=8C=E5=85=B3=E8=81=94?= =?UTF-8?q?=E4=BA=86@4550?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../demo/pc/graph/bubble/demo.component.ts | 23 ++++++ src/jigsaw/common/core/data/graph-data.ts | 18 ++--- .../common/core/data/modeled-graph-data.ts | 76 ++++++++++++++----- 3 files changed, 88 insertions(+), 29 deletions(-) diff --git a/src/app/for-internal/demo/pc/graph/bubble/demo.component.ts b/src/app/for-internal/demo/pc/graph/bubble/demo.component.ts index 8914300471..bbb30e2181 100644 --- a/src/app/for-internal/demo/pc/graph/bubble/demo.component.ts +++ b/src/app/for-internal/demo/pc/graph/bubble/demo.component.ts @@ -292,10 +292,33 @@ export class BubbleGraphComponent { this.staticBubbleData = new BubbleChartGraphData(); this.staticBubbleData.title = '位置固定气泡图'; this.staticBubbleData.data = this.staticData; + this.staticBubbleData.emphasisConfig = { + label: { + show: false + }, + itemStyle: { + borderWidth: 1, + borderColor: 'black', + shadowBlur: 5, + shadowColor: "red", + shadowOffsetX: 0, + shadowOffsetY: 5 + }, + } this.dynamicBubbleData = new BubbleChartGraphData(); this.dynamicBubbleData.title = "引力布局气泡图"; this.dynamicBubbleData.data = this.dynamicData; + this.dynamicBubbleData.emphasisConfig = { + itemStyle: { + borderWidth: 1, + borderColor: 'red', + shadowBlur: 5, + shadowColor: "pink", + shadowOffsetX: 0, + shadowOffsetY: 5 + }, + } } staticBubbleData: BubbleChartGraphData; diff --git a/src/jigsaw/common/core/data/graph-data.ts b/src/jigsaw/common/core/data/graph-data.ts index c407a1ba7e..754794dc42 100644 --- a/src/jigsaw/common/core/data/graph-data.ts +++ b/src/jigsaw/common/core/data/graph-data.ts @@ -1744,6 +1744,8 @@ export class BubbleChartGraphData extends AbstractNormalGraphData { public layout: string = 'force'; + public emphasisConfig: any; + protected createChartOptions(): EchartOptions { if (!this.data || !this.data.length) return; let maxValue = 1; @@ -1755,7 +1757,7 @@ export class BubbleChartGraphData extends AbstractNormalGraphData { const sizeOffset = this.minSymbolSize - sizeScale * minValue; // 斥力 为了防止重叠,斥力最好大于 symbolSize - const repulsion = this.symbolSize * 1.5; + const repulsion = this.symbolSize * 3; // 获取要渲染的数据 const data = this.data[0].map((item) => { @@ -1772,6 +1774,7 @@ export class BubbleChartGraphData extends AbstractNormalGraphData { label: item.labelConfig || {}, symbolSize: size, itemStyle: item.itemStyle || {}, + emphasis: item.emphasisConfig || this.emphasisConfig }; if (!item.x && !item.y) { return itemData; @@ -1780,6 +1783,8 @@ export class BubbleChartGraphData extends AbstractNormalGraphData { return {...itemData, x: item.x, y: item.y}; }); + const emphasisConfig = this.emphasisConfig || {}; + return { xAxis: { show: false, @@ -1793,7 +1798,6 @@ export class BubbleChartGraphData extends AbstractNormalGraphData { type: "graph", // 关系图 layout: this.layout, draggable: true, // 启用节点拖拽 - roam: true, // 启用鼠标缩放和平移漫游 force: { // 值越大则斥力越大 每个元素间隔越大 repulsion, @@ -1805,13 +1809,7 @@ export class BubbleChartGraphData extends AbstractNormalGraphData { coolDown: 10 }, // 高亮状态的图形样式 - emphasis: { - scale: 2, - // 失去焦点是模糊 - blur: 10, - // 强调状态下标签颜色 - label: '' - }, + emphasis: emphasisConfig, // 设置 label label: { show: true, @@ -1831,7 +1829,7 @@ export class BubbleChartGraphData extends AbstractNormalGraphData { fontWeight: 500, color: "#FFF", }, - }, + } }, itemStyle: { borderWidth: 1, diff --git a/src/jigsaw/common/core/data/modeled-graph-data.ts b/src/jigsaw/common/core/data/modeled-graph-data.ts index 234d9a12b8..fc91032451 100644 --- a/src/jigsaw/common/core/data/modeled-graph-data.ts +++ b/src/jigsaw/common/core/data/modeled-graph-data.ts @@ -1201,6 +1201,56 @@ export class ModeledFunnelGraphData extends AbstractModeledGraphData { } } +// ------------------------------------------------------------------------------------------------ +// 气泡图相关数据对象 +export type ColorStop = { + offset: number, + color: string +} + +export type GradientColor = { + type: string, + x: number, + y: number, + r: number, + colorStops: ColorStop[], + global: boolean +} + +export type BubbleItemStyle = { + borderWidth?: number, + borderType?: string, + borderColor?: string, + shadowBlur?: number, + shadowColor?: string, + shadowOffsetX?: number, + shadowOffsetY?: number, + color?: string | GradientColor +} + +export type BubbleLabelConfig = { + color: string, + fontSize: number, + fontWeight: string, + fontStyle: string, + formatter?: string, + position?: string, + show: boolean +} + +export type BubbleSeries = { + label: string, + value: number, + itemStyle: BubbleItemStyle, + labelConfig: BubbleLabelConfig, + x: number, + y: number +} + +export type EmphasisConfig = { + itemStyle: BubbleItemStyle, + label?: BubbleLabelConfig +} // 气泡图 export class ModeledBubbleGraphData extends AbstractModeledGraphData { constructor(data: GraphDataMatrix = [], header: GraphDataHeader = [], field: GraphDataField = []) { @@ -1217,6 +1267,7 @@ export class ModeledBubbleGraphData extends AbstractModeledGraphData { public maxSymbolSize: number; public layout: string = 'force'; public series: DimKpiBase[]; + public emphasisConfig: EmphasisConfig; private _options: EchartOptions; @@ -1235,35 +1286,21 @@ export class ModeledBubbleGraphData extends AbstractModeledGraphData { options.xAxis = this.xAxis; options.yAxis = this.yAxis; const data = this._handleData(); + const emphasisConfig = this.emphasisConfig || {}; // 斥力 为了防止重叠,斥力最好大于 maxSymbolSize - const repulsion = this.maxSymbolSize * 1.5; + const repulsion = this.maxSymbolSize * 3; options.series = [ { data, type: this.type, layout: this.layout, draggable: true, - roam: true, force: { repulsion, }, - emphasis: { - scale: 2, - }, - label: { - show: true, - position: 'inside', - formatter: '{b}\n{c}', - fontSize: 14, - fontWeight: 'bold', - color: 'black', - }, - itemStyle: { - borderWidth: 1, - color: "green", - }, - }, + emphasis: emphasisConfig + } ] CommonUtils.extendObject(options, this.template.optionPatch); return options; @@ -1291,7 +1328,8 @@ export class ModeledBubbleGraphData extends AbstractModeledGraphData { value: item["value"], label: item["labelConfig"] || {}, symbolSize: size, - itemStyle: item["itemStyle"] || {} + itemStyle: item["itemStyle"] || {}, + emphasis: item["emphasisConfig"] || this.emphasisConfig }; if (!item["x"] && !item["y"]) { return itemData; From a3c17b7f9d56219ca965a002c60ff3582690fe4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E6=97=AD10045812?= Date: Fri, 26 Jan 2024 15:02:24 +0800 Subject: [PATCH 2/8] ... --- src/jigsaw/common/core/data/graph-data.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/jigsaw/common/core/data/graph-data.ts b/src/jigsaw/common/core/data/graph-data.ts index 754794dc42..f6dda454db 100644 --- a/src/jigsaw/common/core/data/graph-data.ts +++ b/src/jigsaw/common/core/data/graph-data.ts @@ -5,6 +5,7 @@ import {Type} from "@angular/core"; declare const echarts: any; import {JigsawThemeService} from "../theming/theme"; +import {EmphasisConfig} from "./modeled-graph-data"; export type GraphMatrixRow = (string | number)[]; export type GraphDataHeader = string[]; @@ -1732,6 +1733,7 @@ export class FunnelPlotGraphData extends AbstractNormalGraphData { return opt; } } + /** * 气泡图 * */ @@ -1744,7 +1746,7 @@ export class BubbleChartGraphData extends AbstractNormalGraphData { public layout: string = 'force'; - public emphasisConfig: any; + public emphasisConfig: EmphasisConfig; protected createChartOptions(): EchartOptions { if (!this.data || !this.data.length) return; From 1a8389c4baa1b28ad4772ce713e54139bf90f7b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E6=97=AD10045812?= Date: Fri, 26 Jan 2024 15:17:17 +0800 Subject: [PATCH 3/8] =?UTF-8?q?=E5=9B=BE=E5=BD=A2=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E6=8B=86=E5=88=86=E4=B8=BA=E5=A4=9A=E4=B8=AA=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/data/{ => graph-data}/graph-data.ts | 412 +---------------- .../core/data/graph-data/pie-doughnut.ts | 416 ++++++++++++++++++ .../common/core/data/modeled-graph-data.ts | 2 +- src/jigsaw/mobile_public_api.ts | 7 +- src/jigsaw/pc-components/graph/graph.ts | 2 +- src/jigsaw/public_api.ts | 7 +- 6 files changed, 435 insertions(+), 411 deletions(-) rename src/jigsaw/common/core/data/{ => graph-data}/graph-data.ts (77%) create mode 100644 src/jigsaw/common/core/data/graph-data/pie-doughnut.ts diff --git a/src/jigsaw/common/core/data/graph-data.ts b/src/jigsaw/common/core/data/graph-data/graph-data.ts similarity index 77% rename from src/jigsaw/common/core/data/graph-data.ts rename to src/jigsaw/common/core/data/graph-data/graph-data.ts index f6dda454db..317daa422e 100644 --- a/src/jigsaw/common/core/data/graph-data.ts +++ b/src/jigsaw/common/core/data/graph-data/graph-data.ts @@ -1,11 +1,11 @@ -import {EchartLegend, EchartOptions, EchartTitle, EchartTooltip} from "./echart-types"; -import {TableDataBase} from "./table-data"; -import {CommonUtils} from "../utils/common-utils"; +import {EchartLegend, EchartOptions, EchartTitle, EchartTooltip} from "../echart-types"; +import {TableDataBase} from "../table-data"; +import {CommonUtils} from "../../utils/common-utils"; import {Type} from "@angular/core"; +import {EmphasisConfig} from "../modeled-graph-data"; +import {DoughnutGraphData, DoughnutRateGraphData, DoughnutScoreGraphData, PieGraphData, PieGraphDataByRow} from "./pie-doughnut"; declare const echarts: any; -import {JigsawThemeService} from "../theming/theme"; -import {EmphasisConfig} from "./modeled-graph-data"; export type GraphMatrixRow = (string | number)[]; export type GraphDataHeader = string[]; @@ -345,408 +345,6 @@ export class OutlineMapData extends AbstractNormalGraphData { } } -/** - * 饼图 - */ -export class PieGraphData extends AbstractNormalGraphData { - protected createSeries() { - return this.data[0].map((v, i) => { - return {value: v, name: this.header[i]} - }); - } - - protected optionsTemplate: EchartOptions = { - tooltip: { - trigger: 'item', - formatter: "{a}
{b} : {c} ({d}%)" - }, - legend: { - orient: 'vertical', - left: 'left', - data: [] - }, - series: [ - { - data: [], - type: 'pie', - radius: '55%', - center: ['50%', '60%'], - itemStyle: { - emphasis: { - shadowBlur: 10, - shadowOffsetX: 0, - shadowColor: 'rgba(0, 0, 0, 0.5)' - } - } - } - ] - }; - - protected createChartOptions(): EchartOptions { - if (!this.data || !this.data.length) return; - const opt: EchartOptions = {...this.optionsTemplate}; - this._extendOption(opt); - opt.legend.data = this.header; - opt.series[0].data = this.createSeries(); - return opt; - } -} - -export class PieGraphDataByRow extends PieGraphData { - protected createSeries() { - return this.data.map((row, i) => { - return {value: row[0], name: this.rowDescriptor[i]} - }); - } - - protected createChartOptions(): EchartOptions { - if (!this.data || !this.data.length) return; - const opt: EchartOptions = {...this.optionsTemplate}; - this._extendOption(opt); - opt.legend.data = this.rowDescriptor; - opt.series[0].data = this.createSeries(); - return opt; - } -} - -/** - * 环形对比图 - */ -export class DoughnutGraphData extends AbstractNormalGraphData { - private _calcSeries(): any[] { - return this.data.map((row: any, i) => ({value: row[0], name: this.rowDescriptor[i]})); - } - - protected optionsTemplate: EchartOptions = { - title: { - text: '', - x: 'center', - y: 'center', - itemGap: 20, - textStyle: { - fontSize: 12, - } - }, - series: [ - { - - type: 'pie', - radius: ['45', '57'], - avoidLabelOverlap: false, - hoverAnimation: false, - itemStyle: {}, - label: { - normal: { - show: true, - } - }, - labelLine: { - normal: { - show: true - } - }, - data: [] - } - ] - }; - - protected createChartOptions(): any { - if (!(this.data instanceof Array) || !this.data.length) return; - const labelFormatter = { - normal: { - label: { - formatter: function (params) { - return params.name + '\n' + params.value + '%' - }, - textStyle: { - baseline: 'top' - } - } - }, - }; - const opt = {...this.optionsTemplate}; - this._extendOption(opt); - opt.series[0].itemStyle = labelFormatter; - opt.series[0].data = this._calcSeries(); - return opt; - } -} - -/** - * 环形比例图 - */ -export class DoughnutRateGraphData extends AbstractNormalGraphData { - protected optionsTemplate: EchartOptions = { - series: [ - { - type: 'pie', - radius: ['31', '34'], - itemStyle: {}, - minAngle: 0, - data: [ - { - name: '', - value: null, - itemStyle: {} - }, - { - name: '', - value: null, - itemStyle: {} - } - ] - }, - //点击 - { - name: '点击环', - clickable: true, - type: 'pie', - hoverAnimation: false, - radius: ['0', '31'], - minAngle: 0, - data: [{ - name: "", - value: 100, - itemStyle: {} - }] - } - ] - }; - - protected createChartOptions(): any { - if (!(this.data instanceof Array) || !this.data.length || !(this.data[0] instanceof Array)) return; - - //不同区域得分颜色 - const color = Number(this.data[0][0]) >= 95 ? '#98e2a6' : (Number(this.data[0][0]) >= 90 && Number(this.data[0][0]) < 95) ? '#9ad0e2' : (Number(this.data[0][0]) >= 80 && Number(this.data[0][0]) < 90) ? '#f7e685' : (Number(this.data[0][0]) >= 70 && Number(this.data[0][0]) < 80) ? '#f6c88a' : Number(this.data[0][0]) < 70 ? '#ff8e74' : '#dedede'; - - const labelTop = { - itemStyle: { - color: color - }, - label: { - show: true, - position: 'top', - formatter: '{b}', - textStyle: { - baseline: 'top', - fontFamily: '微软雅黑', - fontSize: 14 - } - }, - labelLine: { - show: false - }, - emphasis: { - color: color - }, - value: this.data[0][0] - }; - - //中间显示 - const labelFormatter = { - label: { - formatter: function (params) { - return 100 - params.value + '%' - }, - textStyle: { - baseline: 'bottom', - color: JigsawThemeService.majorStyle == 'dark' ? '#fff' : '#333', - fontFamily: 'Arial' - } - }, - labelLine: { - show: false - }, - emphasis: { - color: 'rgba(0,0,0,0)' - } - }; - - const labelBottom = { - itemStyle: { - color: '#DEDEDE', - }, - label: { - show: true, - position: 'center', - textStyle: { - fontSize: 24 - } - }, - labelLine: { - show: false - }, - emphasis: { - color: '#DEDEDE' - }, - value: 100 - Number(this.data[0][0]) - }; - - //饼图的点击label - const placeHolderStyle = { - itemStyle: { - color: 'rgba(0,0,0,0)' - }, - label: { - show: false - }, - labelLine: { - show: false - }, - emphasis: { - color: 'rgba(0,0,0,0)' - } - }; - - const opt = {...this.optionsTemplate}; - this._extendOption(opt); - CommonUtils.extendObject(opt.series, [{...labelFormatter, data:[labelTop, labelBottom]}, placeHolderStyle]); - return opt; - } -} - -/** - * 环形得分图 - */ -export class DoughnutScoreGraphData extends AbstractNormalGraphData { - protected optionsTemplate: EchartOptions = { - series: [ - { - type: 'pie', - radius: ['45', '50'], - itemStyle: {}, - minAngle: 0, - data: [ - { - name: null, - value: 100, - itemStyle: {} - }, - { - name: null, - value: 0, - itemStyle: {} - } - ] - }, - //点击 - { - name: '点击环', - clickable: false, - type: 'pie', - hoverAnimation: false, - radius: ['0', '45'], - minAngle: 0, - data: [{ - name: "", - value: 100, - itemStyle: {} - }] - } - ] - }; - - protected createChartOptions(): any { - if (!(this.data instanceof Array) || !this.data.length || !(this.data[0] instanceof Array)) return; - - // innerColor 内圆颜色 - // outerColor 外环颜色 - const innerColor = Number(this.data[0][0]) >= 95 ? 'rgb(152,226,166)' : (Number(this.data[0][0]) >= 90 && Number(this.data[0][0]) < 95) ? 'rgb(154,208,226)' : (Number(this.data[0][0]) >= 80 && Number(this.data[0][0]) < 90) ? 'rgb(247,230,133)' : (Number(this.data[0][0]) >= 70 && Number(this.data[0][0]) < 80) ? 'rgb(246,200,138)' : (Number(this.data[0][0]) < 70) ? 'rgb(255,142,116)' : '#dedede'; - const outerColor = Number(this.data[0][0]) >= 95 ? 'rgba(152,226,166,.2)' : (Number(this.data[0][0]) >= 90 && Number(this.data[0][0]) < 95) ? 'rgba(154,208,226,.2)' : (Number(this.data[0][0]) >= 80 && Number(this.data[0][0]) < 90) ? 'rgba(247,230,133,.2)' : (Number(this.data[0][0]) >= 70 && Number(this.data[0][0]) < 80) ? 'rgba(246,200,138,.2)' : (Number(this.data[0][0]) < 70) ? 'rgba(255,142,116,.2)' : '#dedede'; - const labelTop = { - name: Number(this.data[0][0]) == 0 ? '0' : Number(this.data[0][0]), - itemStyle: { - color: outerColor, - }, - label: { - show: true, - position: 'center', - formatter: '{b}', - textStyle: { - baseline: 'top', - fontFamily: 'arial', - fontSize: 30, - color: '#fff', - fontWeight: 'bold' - }, - padding: [0, 0, 20, 0] - }, - labelLine: { - show: false - }, - emphasis: { - color: outerColor - } - }; - - //中间显示 - const labelFormatter = { - label: { - position: 'center', - textStyle: { - baseline: 'bottom', - color: '#333', - fontFamily: 'Arial' - } - }, - labelLine: { - show: false - }, - emphasis: { - color: outerColor - } - }; - - const labelBottom = { - name: this.rowDescriptor[0], - label: { - show: true, - position: 'center', - textStyle: { - fontSize: 14, - color: '#fff', - fontFamily: '微软雅黑' - }, - padding: [40, 0, 0, 0] - }, - labelLine: { - show: false - } - }; - - //饼图的点击label - const placeHolderStyle = { - name: this.rowDescriptor[0], - itemStyle: { - color: innerColor - }, - label: { - show: false - }, - labelLine: { - show: false - }, - emphasis: { - color: innerColor, - labelLine: { - show: false - } - } - }; - - const opt = {...this.optionsTemplate}; - this._extendOption(opt); - CommonUtils.extendObject(opt.series, [ - { - ...labelFormatter, - data: [labelTop, labelBottom] - }, - placeHolderStyle - ]); - return opt; - } -} - /** * 折线图 */ diff --git a/src/jigsaw/common/core/data/graph-data/pie-doughnut.ts b/src/jigsaw/common/core/data/graph-data/pie-doughnut.ts new file mode 100644 index 0000000000..c009e1c30c --- /dev/null +++ b/src/jigsaw/common/core/data/graph-data/pie-doughnut.ts @@ -0,0 +1,416 @@ +import {EchartOptions} from "../echart-types"; +import {CommonUtils} from "../../utils/common-utils"; +import {JigsawThemeService} from "../../theming/theme"; +import {AbstractNormalGraphData} from "./graph-data"; + +/** + * 饼图 + */ +export class PieGraphData extends AbstractNormalGraphData { + protected createSeries() { + return this.data[0].map((v, i) => { + return {value: v, name: this.header[i]} + }); + } + + protected optionsTemplate: EchartOptions = { + tooltip: { + trigger: 'item', + formatter: "{a}
{b} : {c} ({d}%)" + }, + legend: { + orient: 'vertical', + left: 'left', + data: [] + }, + series: [ + { + data: [], + type: 'pie', + radius: '55%', + center: ['50%', '60%'], + itemStyle: { + emphasis: { + shadowBlur: 10, + shadowOffsetX: 0, + shadowColor: 'rgba(0, 0, 0, 0.5)' + } + } + } + ] + }; + + protected createChartOptions(): EchartOptions { + if (!this.data || !this.data.length) { + return; + } + const opt: EchartOptions = {...this.optionsTemplate}; + this._extendOption(opt); + opt.legend.data = this.header; + opt.series[0].data = this.createSeries(); + return opt; + } +} + +export class PieGraphDataByRow extends PieGraphData { + protected createSeries() { + return this.data.map((row, i) => { + return {value: row[0], name: this.rowDescriptor[i]} + }); + } + + protected createChartOptions(): EchartOptions { + if (!this.data || !this.data.length) { + return; + } + const opt: EchartOptions = {...this.optionsTemplate}; + this._extendOption(opt); + opt.legend.data = this.rowDescriptor; + opt.series[0].data = this.createSeries(); + return opt; + } +} + +/** + * 环形对比图 + */ +export class DoughnutGraphData extends AbstractNormalGraphData { + private _calcSeries(): any[] { + return this.data.map((row: any, i) => ({value: row[0], name: this.rowDescriptor[i]})); + } + + protected optionsTemplate: EchartOptions = { + title: { + text: '', + x: 'center', + y: 'center', + itemGap: 20, + textStyle: { + fontSize: 12, + } + }, + series: [ + { + + type: 'pie', + radius: ['45', '57'], + avoidLabelOverlap: false, + hoverAnimation: false, + itemStyle: {}, + label: { + normal: { + show: true, + } + }, + labelLine: { + normal: { + show: true + } + }, + data: [] + } + ] + }; + + protected createChartOptions(): any { + if (!(this.data instanceof Array) || !this.data.length) { + return; + } + const labelFormatter = { + normal: { + label: { + formatter: function (params) { + return params.name + '\n' + params.value + '%' + }, + textStyle: { + baseline: 'top' + } + } + }, + }; + const opt = {...this.optionsTemplate}; + this._extendOption(opt); + opt.series[0].itemStyle = labelFormatter; + opt.series[0].data = this._calcSeries(); + return opt; + } +} + +/** + * 环形比例图 + */ +export class DoughnutRateGraphData extends AbstractNormalGraphData { + protected optionsTemplate: EchartOptions = { + series: [ + { + type: 'pie', + radius: ['31', '34'], + itemStyle: {}, + minAngle: 0, + data: [ + { + name: '', + value: null, + itemStyle: {} + }, + { + name: '', + value: null, + itemStyle: {} + } + ] + }, + //点击 + { + name: '点击环', + clickable: true, + type: 'pie', + hoverAnimation: false, + radius: ['0', '31'], + minAngle: 0, + data: [{ + name: "", + value: 100, + itemStyle: {} + }] + } + ] + }; + + protected createChartOptions(): any { + if (!(this.data instanceof Array) || !this.data.length || !(this.data[0] instanceof Array)) { + return; + } + + //不同区域得分颜色 + const color = Number(this.data[0][0]) >= 95 ? '#98e2a6' : (Number(this.data[0][0]) >= 90 && Number(this.data[0][0]) < 95) ? '#9ad0e2' : (Number(this.data[0][0]) >= 80 && Number(this.data[0][0]) < 90) ? '#f7e685' : (Number(this.data[0][0]) >= 70 && Number(this.data[0][0]) < 80) ? '#f6c88a' : Number(this.data[0][0]) < 70 ? '#ff8e74' : '#dedede'; + + const labelTop = { + itemStyle: { + color: color + }, + label: { + show: true, + position: 'top', + formatter: '{b}', + textStyle: { + baseline: 'top', + fontFamily: '微软雅黑', + fontSize: 14 + } + }, + labelLine: { + show: false + }, + emphasis: { + color: color + }, + value: this.data[0][0] + }; + + //中间显示 + const labelFormatter = { + label: { + formatter: function (params) { + return 100 - params.value + '%' + }, + textStyle: { + baseline: 'bottom', + color: JigsawThemeService.majorStyle == 'dark' ? '#fff' : '#333', + fontFamily: 'Arial' + } + }, + labelLine: { + show: false + }, + emphasis: { + color: 'rgba(0,0,0,0)' + } + }; + + const labelBottom = { + itemStyle: { + color: '#DEDEDE', + }, + label: { + show: true, + position: 'center', + textStyle: { + fontSize: 24 + } + }, + labelLine: { + show: false + }, + emphasis: { + color: '#DEDEDE' + }, + value: 100 - Number(this.data[0][0]) + }; + + //饼图的点击label + const placeHolderStyle = { + itemStyle: { + color: 'rgba(0,0,0,0)' + }, + label: { + show: false + }, + labelLine: { + show: false + }, + emphasis: { + color: 'rgba(0,0,0,0)' + } + }; + + const opt = {...this.optionsTemplate}; + this._extendOption(opt); + CommonUtils.extendObject(opt.series, [{...labelFormatter, data: [labelTop, labelBottom]}, placeHolderStyle]); + return opt; + } +} + +/** + * 环形得分图 + */ +export class DoughnutScoreGraphData extends AbstractNormalGraphData { + protected optionsTemplate: EchartOptions = { + series: [ + { + type: 'pie', + radius: ['45', '50'], + itemStyle: {}, + minAngle: 0, + data: [ + { + name: null, + value: 100, + itemStyle: {} + }, + { + name: null, + value: 0, + itemStyle: {} + } + ] + }, + //点击 + { + name: '点击环', + clickable: false, + type: 'pie', + hoverAnimation: false, + radius: ['0', '45'], + minAngle: 0, + data: [{ + name: "", + value: 100, + itemStyle: {} + }] + } + ] + }; + + protected createChartOptions(): any { + if (!(this.data instanceof Array) || !this.data.length || !(this.data[0] instanceof Array)) { + return; + } + + // innerColor 内圆颜色 + // outerColor 外环颜色 + const innerColor = Number(this.data[0][0]) >= 95 ? 'rgb(152,226,166)' : (Number(this.data[0][0]) >= 90 && Number(this.data[0][0]) < 95) ? 'rgb(154,208,226)' : (Number(this.data[0][0]) >= 80 && Number(this.data[0][0]) < 90) ? 'rgb(247,230,133)' : (Number(this.data[0][0]) >= 70 && Number(this.data[0][0]) < 80) ? 'rgb(246,200,138)' : (Number(this.data[0][0]) < 70) ? 'rgb(255,142,116)' : '#dedede'; + const outerColor = Number(this.data[0][0]) >= 95 ? 'rgba(152,226,166,.2)' : (Number(this.data[0][0]) >= 90 && Number(this.data[0][0]) < 95) ? 'rgba(154,208,226,.2)' : (Number(this.data[0][0]) >= 80 && Number(this.data[0][0]) < 90) ? 'rgba(247,230,133,.2)' : (Number(this.data[0][0]) >= 70 && Number(this.data[0][0]) < 80) ? 'rgba(246,200,138,.2)' : (Number(this.data[0][0]) < 70) ? 'rgba(255,142,116,.2)' : '#dedede'; + const labelTop = { + name: Number(this.data[0][0]) == 0 ? '0' : Number(this.data[0][0]), + itemStyle: { + color: outerColor, + }, + label: { + show: true, + position: 'center', + formatter: '{b}', + textStyle: { + baseline: 'top', + fontFamily: 'arial', + fontSize: 30, + color: '#fff', + fontWeight: 'bold' + }, + padding: [0, 0, 20, 0] + }, + labelLine: { + show: false + }, + emphasis: { + color: outerColor + } + }; + + //中间显示 + const labelFormatter = { + label: { + position: 'center', + textStyle: { + baseline: 'bottom', + color: '#333', + fontFamily: 'Arial' + } + }, + labelLine: { + show: false + }, + emphasis: { + color: outerColor + } + }; + + const labelBottom = { + name: this.rowDescriptor[0], + label: { + show: true, + position: 'center', + textStyle: { + fontSize: 14, + color: '#fff', + fontFamily: '微软雅黑' + }, + padding: [40, 0, 0, 0] + }, + labelLine: { + show: false + } + }; + + //饼图的点击label + const placeHolderStyle = { + name: this.rowDescriptor[0], + itemStyle: { + color: innerColor + }, + label: { + show: false + }, + labelLine: { + show: false + }, + emphasis: { + color: innerColor, + labelLine: { + show: false + } + } + }; + + const opt = {...this.optionsTemplate}; + this._extendOption(opt); + CommonUtils.extendObject(opt.series, [ + { + ...labelFormatter, + data: [labelTop, labelBottom] + }, + placeHolderStyle + ]); + return opt; + } +} diff --git a/src/jigsaw/common/core/data/modeled-graph-data.ts b/src/jigsaw/common/core/data/modeled-graph-data.ts index fc91032451..5d07fec403 100644 --- a/src/jigsaw/common/core/data/modeled-graph-data.ts +++ b/src/jigsaw/common/core/data/modeled-graph-data.ts @@ -8,7 +8,7 @@ import { EchartXAxis, EchartYAxis } from "./echart-types"; -import {GraphDataField, GraphDataHeader, GraphDataMatrix} from "./graph-data"; +import {GraphDataField, GraphDataHeader, GraphDataMatrix} from "./graph-data/graph-data"; import {aggregate, AggregateAlgorithm, distinct, flat, group, Grouped} from "../utils/data-collection-utils"; import {CommonUtils} from "../utils/common-utils"; import {getColumn} from "./unified-paging/paging"; diff --git a/src/jigsaw/mobile_public_api.ts b/src/jigsaw/mobile_public_api.ts index c51a03094e..412f0de9e7 100644 --- a/src/jigsaw/mobile_public_api.ts +++ b/src/jigsaw/mobile_public_api.ts @@ -12,7 +12,7 @@ export * from "./common/core/data/component-data"; export * from "./common/core/data/echart-types"; export * from "./common/core/data/modeled-graph-data"; export * from "./common/core/data/general-collection"; -export * from "./common/core/data/graph-data"; +export * from "./common/core/data/graph-data/graph-data"; export * from "./common/core/data/table-data"; export * from "./common/core/data/tree-data"; export * from "./common/core/data/unified-paging/exports.min"; @@ -54,4 +54,9 @@ export * from "./mobile-components/switch/index"; export * from "./mobile-components/tabs/index"; export * from "./mobile-components/tag/tag"; export * from "./mobile-components/header/header"; +export {DoughnutScoreGraphData} from "./common/core/data/graph-data/pie-doughnut"; +export {DoughnutRateGraphData} from "./common/core/data/graph-data/pie-doughnut"; +export {DoughnutGraphData} from "./common/core/data/graph-data/pie-doughnut"; +export {PieGraphDataByRow} from "./common/core/data/graph-data/pie-doughnut"; +export {PieGraphData} from "./common/core/data/graph-data/pie-doughnut"; diff --git a/src/jigsaw/pc-components/graph/graph.ts b/src/jigsaw/pc-components/graph/graph.ts index 5f82493bf0..4e9366c919 100644 --- a/src/jigsaw/pc-components/graph/graph.ts +++ b/src/jigsaw/pc-components/graph/graph.ts @@ -18,7 +18,7 @@ import { import {debounceTime} from 'rxjs/operators'; import {Subscription} from 'rxjs'; -import {AbstractGraphData} from "../../common/core/data/graph-data"; +import {AbstractGraphData} from "../../common/core/data/graph-data/graph-data"; import {CallbackRemoval, CommonUtils} from "../../common/core/utils/common-utils"; import {AbstractJigsawComponent, WingsTheme} from "../../common/common"; import {EchartOptions} from "../../common/core/data/echart-types"; diff --git a/src/jigsaw/public_api.ts b/src/jigsaw/public_api.ts index 735cc4e6d8..86cc2ffabc 100644 --- a/src/jigsaw/public_api.ts +++ b/src/jigsaw/public_api.ts @@ -13,7 +13,7 @@ export * from "./common/core/data/component-data"; export * from "./common/core/data/echart-types"; export * from "./common/core/data/modeled-graph-data"; export * from "./common/core/data/general-collection"; -export * from "./common/core/data/graph-data"; +export * from "./common/core/data/graph-data/graph-data"; export * from "./common/core/data/table-data"; export * from "./common/core/data/tree-data"; export * from "./common/core/data/unified-paging/exports.min"; @@ -102,3 +102,8 @@ export * from "./pc-components/form-display/form-display" /* fallback components */ export * from "./pc-components/fallback/upload/index"; +export {DoughnutScoreGraphData} from "./common/core/data/graph-data/pie-doughnut"; +export {DoughnutRateGraphData} from "./common/core/data/graph-data/pie-doughnut"; +export {DoughnutGraphData} from "./common/core/data/graph-data/pie-doughnut"; +export {PieGraphDataByRow} from "./common/core/data/graph-data/pie-doughnut"; +export {PieGraphData} from "./common/core/data/graph-data/pie-doughnut"; From 741c3bcdc39eafa716e7bb7a3f1212c92279bb5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E6=97=AD10045812?= Date: Fri, 26 Jan 2024 16:23:26 +0800 Subject: [PATCH 4/8] =?UTF-8?q?=E5=B0=86graph-data=E6=8B=86=E5=88=86?= =?UTF-8?q?=E6=88=90=E5=A4=9A=E4=B8=AA=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../demo/pc/graph/bubble/demo.component.ts | 3 - .../common/core/data/graph-data/graph-data.ts | 1186 ++------------- .../core/data/graph-data/modeled-bubble.ts | 93 ++ .../core/data/graph-data/modeled-funnel.ts | 150 ++ .../core/data/graph-data/modeled-gauge.ts | 117 ++ .../core/data/graph-data/modeled-map.ts | 119 ++ .../modeled-pie.spec.ts} | 7 +- .../core/data/graph-data/modeled-pie.ts | 104 ++ .../core/data/graph-data/modeled-radar.ts | 124 ++ .../modeled-rectangular.spec.ts} | 7 +- .../data/graph-data/modeled-rectangular.ts | 290 ++++ .../core/data/graph-data/modeled-scatter.ts | 129 ++ .../common/core/data/graph-data/modeled.ts | 178 +++ .../core/data/graph-data/normal-bubble.ts | 149 ++ .../data/graph-data/normal-funnel-plot.ts | 85 ++ .../data/graph-data/normal-line-bar-area.ts | 545 +++++++ .../core/data/graph-data/normal-others.ts | 281 ++++ ...pie-doughnut.ts => normal-pie-doughnut.ts} | 0 .../common/core/data/modeled-graph-data.ts | 1341 ----------------- src/jigsaw/mobile_public_api.ts | 58 +- src/jigsaw/public_api.ts | 58 +- 21 files changed, 2569 insertions(+), 2455 deletions(-) create mode 100644 src/jigsaw/common/core/data/graph-data/modeled-bubble.ts create mode 100644 src/jigsaw/common/core/data/graph-data/modeled-funnel.ts create mode 100644 src/jigsaw/common/core/data/graph-data/modeled-gauge.ts create mode 100644 src/jigsaw/common/core/data/graph-data/modeled-map.ts rename src/jigsaw/common/core/data/{modeled-pie-graph-data.spec.ts => graph-data/modeled-pie.spec.ts} (95%) create mode 100644 src/jigsaw/common/core/data/graph-data/modeled-pie.ts create mode 100644 src/jigsaw/common/core/data/graph-data/modeled-radar.ts rename src/jigsaw/common/core/data/{modeled-rectangular-graph-data.spec.ts => graph-data/modeled-rectangular.spec.ts} (96%) create mode 100644 src/jigsaw/common/core/data/graph-data/modeled-rectangular.ts create mode 100644 src/jigsaw/common/core/data/graph-data/modeled-scatter.ts create mode 100644 src/jigsaw/common/core/data/graph-data/modeled.ts create mode 100644 src/jigsaw/common/core/data/graph-data/normal-bubble.ts create mode 100644 src/jigsaw/common/core/data/graph-data/normal-funnel-plot.ts create mode 100644 src/jigsaw/common/core/data/graph-data/normal-line-bar-area.ts create mode 100644 src/jigsaw/common/core/data/graph-data/normal-others.ts rename src/jigsaw/common/core/data/graph-data/{pie-doughnut.ts => normal-pie-doughnut.ts} (100%) delete mode 100644 src/jigsaw/common/core/data/modeled-graph-data.ts diff --git a/src/app/for-internal/demo/pc/graph/bubble/demo.component.ts b/src/app/for-internal/demo/pc/graph/bubble/demo.component.ts index bbb30e2181..621a0e2b93 100644 --- a/src/app/for-internal/demo/pc/graph/bubble/demo.component.ts +++ b/src/app/for-internal/demo/pc/graph/bubble/demo.component.ts @@ -293,9 +293,6 @@ export class BubbleGraphComponent { this.staticBubbleData.title = '位置固定气泡图'; this.staticBubbleData.data = this.staticData; this.staticBubbleData.emphasisConfig = { - label: { - show: false - }, itemStyle: { borderWidth: 1, borderColor: 'black', diff --git a/src/jigsaw/common/core/data/graph-data/graph-data.ts b/src/jigsaw/common/core/data/graph-data/graph-data.ts index 317daa422e..25361eb222 100644 --- a/src/jigsaw/common/core/data/graph-data/graph-data.ts +++ b/src/jigsaw/common/core/data/graph-data/graph-data.ts @@ -1,11 +1,6 @@ import {EchartLegend, EchartOptions, EchartTitle, EchartTooltip} from "../echart-types"; import {TableDataBase} from "../table-data"; import {CommonUtils} from "../../utils/common-utils"; -import {Type} from "@angular/core"; -import {EmphasisConfig} from "../modeled-graph-data"; -import {DoughnutGraphData, DoughnutRateGraphData, DoughnutScoreGraphData, PieGraphData, PieGraphDataByRow} from "./pie-doughnut"; - -declare const echarts: any; export type GraphMatrixRow = (string | number)[]; export type GraphDataHeader = string[]; @@ -13,10 +8,6 @@ export type GraphDataField = string[]; export type GraphDataRowDescriptor = string[]; export type GraphDataMatrix = GraphMatrixRow[]; -export abstract class AbstractGraphStyle { - protected abstract createChartOptions(): EchartOptions; -} - /** * 这是所有的图形数据的基类,正如类名所提示的,这个类是抽象的。 * @@ -181,7 +172,7 @@ export abstract class AbstractGraphData extends TableDataBase { } /** - * 这是一个通用的图形数据,提供给它一个`EchartOptions`,它就可以渲染出对应的图了。 + * 这是一个通用的图形数据,提供给它一个`EchartsOptions`,它就可以渲染出对应的图了。 * * Jigsaw有计划对常用的图形做封装,包括使用接口和样式,尽情期待。 * @@ -205,98 +196,98 @@ export class GraphData extends AbstractGraphData { * @param rawTableData * */ - public static of(rawTableData: any): AbstractGraphData { - if (!super.isGraphData(rawTableData) || !rawTableData.type) { - return null; - } - let GraphDataType: Type; - switch (rawTableData.type) { - case 'OutlineMapData': - GraphDataType = OutlineMapData; - break; - case 'PieGraphData': - GraphDataType = PieGraphData; - break; - case 'PieGraphDataByRow': - GraphDataType = PieGraphDataByRow; - break; - case 'DoughnutGraphData': - GraphDataType = DoughnutGraphData; - break; - case 'DoughnutRateGraphData': - GraphDataType = DoughnutRateGraphData; - break; - case 'DoughnutScoreGraphData': - GraphDataType = DoughnutScoreGraphData; - break; - case 'LineGraphData': - GraphDataType = LineGraphData; - break; - case 'LineGraphDataByRow': - GraphDataType = LineGraphDataByRow; - break; - case 'LineBarGraphData': - GraphDataType = LineBarGraphData; - break; - case 'BarGraphData': - GraphDataType = BarGraphData; - break; - case 'BarGraphDataByRow': - GraphDataType = BarGraphDataByRow; - break; - case 'StripGraphData': - GraphDataType = StripGraphData; - break; - case 'StripSequenceGraphData': - GraphDataType = StripSequenceGraphData; - break; - case 'StripColorGraphData': - GraphDataType = StripColorGraphData; - break; - case 'StackedAreaGraphData': - GraphDataType = StackedAreaGraphData; - break; - case 'GaugeGraphData': - GraphDataType = GaugeGraphData; - break; - case 'ScatterGraphData': - GraphDataType = ScatterGraphData; - break; - case 'RadarGraphData': - GraphDataType = RadarGraphData; - break; - case 'KLineGraphData': - GraphDataType = KLineGraphData; - break; - case 'BoxPlotGraphData': - GraphDataType = BoxPlotGraphData; - break; - case 'HeatGraphData': - GraphDataType = HeatGraphData; - break; - case 'RelationalGraphData': - GraphDataType = RelationalGraphData; - break; - case 'FunnelPlotGraphData': - GraphDataType = FunnelPlotGraphData; - break; - } - return GraphDataType ? this._createGraphData(GraphDataType, rawTableData) : null; - } - - private static _createGraphData(ClassName: Type, rawData): T { - const gd = new ClassName(); - if (rawData.hasOwnProperty('header')) { - gd.header = rawData.header; - } - if (rawData.hasOwnProperty('data')) { - gd.data = rawData.data; - } - if (rawData.hasOwnProperty('rowDescriptor')) { - gd.rowDescriptor = rawData.rowDescriptor; - } - return gd; - } + // public static of(rawTableData: any): AbstractGraphData { + // if (!super.isGraphData(rawTableData) || !rawTableData.type) { + // return null; + // } + // let GraphDataType: Type; + // switch (rawTableData.type) { + // case 'OutlineMapData': + // GraphDataType = OutlineMapData; + // break; + // case 'PieGraphData': + // GraphDataType = PieGraphData; + // break; + // case 'PieGraphDataByRow': + // GraphDataType = PieGraphDataByRow; + // break; + // case 'DoughnutGraphData': + // GraphDataType = DoughnutGraphData; + // break; + // case 'DoughnutRateGraphData': + // GraphDataType = DoughnutRateGraphData; + // break; + // case 'DoughnutScoreGraphData': + // GraphDataType = DoughnutScoreGraphData; + // break; + // case 'LineGraphData': + // GraphDataType = LineGraphData; + // break; + // case 'LineGraphDataByRow': + // GraphDataType = LineGraphDataByRow; + // break; + // case 'LineBarGraphData': + // GraphDataType = LineBarGraphData; + // break; + // case 'BarGraphData': + // GraphDataType = BarGraphData; + // break; + // case 'BarGraphDataByRow': + // GraphDataType = BarGraphDataByRow; + // break; + // case 'StripGraphData': + // GraphDataType = StripGraphData; + // break; + // case 'StripSequenceGraphData': + // GraphDataType = StripSequenceGraphData; + // break; + // case 'StripColorGraphData': + // GraphDataType = StripColorGraphData; + // break; + // case 'StackedAreaGraphData': + // GraphDataType = StackedAreaGraphData; + // break; + // case 'GaugeGraphData': + // GraphDataType = GaugeGraphData; + // break; + // case 'ScatterGraphData': + // GraphDataType = ScatterGraphData; + // break; + // case 'RadarGraphData': + // GraphDataType = RadarGraphData; + // break; + // case 'KLineGraphData': + // GraphDataType = KLineGraphData; + // break; + // case 'BoxPlotGraphData': + // GraphDataType = BoxPlotGraphData; + // break; + // case 'HeatGraphData': + // GraphDataType = HeatGraphData; + // break; + // case 'RelationalGraphData': + // GraphDataType = RelationalGraphData; + // break; + // case 'FunnelPlotGraphData': + // GraphDataType = FunnelPlotGraphData; + // break; + // } + // return GraphDataType ? this._createGraphData(GraphDataType, rawTableData) : null; + // } + + // private static _createGraphData(ClassName: Type, rawData): T { + // const gd = new ClassName(); + // if (rawData.hasOwnProperty('header')) { + // gd.header = rawData.header; + // } + // if (rawData.hasOwnProperty('data')) { + // gd.data = rawData.data; + // } + // if (rawData.hasOwnProperty('rowDescriptor')) { + // gd.rowDescriptor = rawData.rowDescriptor; + // } + // return gd; + // } } export abstract class AbstractNormalGraphData extends AbstractGraphData { @@ -345,535 +336,6 @@ export class OutlineMapData extends AbstractNormalGraphData { } } -/** - * 折线图 - */ -export class LineGraphData extends AbstractNormalGraphData { - - protected defaultType = 'line'; - - protected createSeries() { - return this.data.map((row, index) => { - return {name: this.header[index], type: this.defaultType, data: this.data.map(row => row[index])} - }); - } - - protected optionsTemplate: EchartOptions = { - tooltip: { - trigger: 'axis', - axisPointer: { - type: 'cross', - label: { - backgroundColor: '#6a7985' - } - } - }, - legend: { - left: 'center', - data: [] - }, - toolbox: { - feature: { - saveAsImage: {} - } - }, - grid: { - left: '3%', - right: '4%', - bottom: '3%', - containLabel: true - }, - xAxis: [ - { - type: 'category', - boundaryGap: false, - data: [] - } - ], - yAxis: [ - { - type: 'value' - } - ], - series: [] - }; - - protected createChartOptions(): EchartOptions { - if (!this.data || !this.data.length) return; - const opt: EchartOptions = {...this.optionsTemplate}; - this._extendOption(opt); - opt.legend.data = this.header; - opt.xAxis[0].data = this.rowDescriptor; - opt.series = this.createSeries(); - return opt; - } -} - -/** - * 加这个类是为了保持向下兼容 - * @internal - */ -export class LineBarGraphData extends LineGraphData { -} - -export class LineGraphDataByRow extends LineGraphData { - protected createSeries() { - return this.data.map((row, index) => { - return {name: this.rowDescriptor[index], type: this.defaultType, data: row} - }); - } - - protected createChartOptions(): EchartOptions { - if (!this.data || !this.data.length) return; - const opt: EchartOptions = {...this.optionsTemplate}; - this._extendOption(opt); - opt.legend.data = this.rowDescriptor; - opt.xAxis[0].data = this.header; - opt.series = this.createSeries(); - return opt; - } -} - -/** - * 柱状图 - */ -export class BarGraphData extends LineGraphData { - protected defaultType = 'bar'; -} - -/** - * 柱状图(按行) - */ -export class BarGraphDataByRow extends LineGraphDataByRow { - protected defaultType = 'bar'; -} - -/** - * 条形图 - */ -export class StripGraphData extends AbstractNormalGraphData { - - protected getBrowserInfo() { - //只做了谷歌和火狐的兼容性 - let agent = navigator.userAgent.toLowerCase(); - if (agent.indexOf("firefox") > 0) { - return -10; - } else { - return -15; - } - } - - protected getGridRight() { - let gridRight = "" + this.data[0][0]; - return gridRight.length * 8 - } - - protected optionsTemplate: EchartOptions = { - grid: { - left: 100, - top: 60 - }, - tooltip: { - trigger: 'item', - axisPointer: {type: ''}, - formatter: function (params) { - return params[1].name + '
' - + params[1].seriesName + ' : ' + params[1].value - } - }, - xAxis: [ - { - type: 'value', - splitNumber: 4, - splitLine: {show: false}, - position: 'bottom', - max: "dataMax", - axisLabel: { - textStyle: { - color: '#bbbbbb' - } - }, - axisTick: {//坐标轴刻度相关设置 - show: true, - inside: false, - color: '#ddd', - length: 3,//刻度长短设置 - lineStyle: { - color: '#ddd', - } - }, - axisLine: { - show: true, - lineStyle: { - color: '#ddd', - width: 1 - } - } - } - ], - yAxis: [ - { - - splitLine: {show: false}, - data: [], - boundaryGap: [0.01, 0.01], - axisLabel: { - textStyle: { - color: '#666' - } - }, - axisLine: { - show: true, - lineStyle: { - color: '#ddd', - width: 1 - } - }, - axisTick: {//坐标轴刻度相关设置 - show: true, - length: 3,//刻度长短设置 - lineStyle: { - color: '#54acd5', - } - } - } - ] - }; - - protected createChartOptions(): any { - if (!this.data || !this.data.length) return; - const options = {...this.optionsTemplate}; - this._extendOption(options); - options.grid.right = this.getGridRight(); - options.yAxis[0].data = this.header; - options.series = [ - { - type: 'bar', - barGap: '-100%', - silent: true, - itemStyle: { - normal: { - barBorderColor: '#54acd5', - opacity: 0.2, - color: '#54acd5', - barBorderRadius: 5 - } - }, - barWidth: 10, - data: this.data[0] - }, - { - animation: true, - type: 'bar', - label: { - normal: { - show: true, - position: ['100%', this.getBrowserInfo()], - textStyle: { - fontSize: 12, - color: "#54acd5" - } - } - }, - barGap: '-100%', - itemStyle: { - normal: { - barBorderColor: '#54acd5', - color: '#54acd5', - barBorderRadius: 5 - } - }, - barWidth: 10, - tooltip: { - trigger: 'axis', - }, - data: this.data[1] - } - ]; - return options; - } -} - -/** - * 条形时序图 - */ -export class StripSequenceGraphData extends StripGraphData { - protected createChartOptions(): any { - if (!this.data || !this.data.length) return; - const options = {...this.optionsTemplate}; - this._extendOption(options); - options.grid.right = this.getGridRight(); - options.yAxis[0].type = 'category'; - options.yAxis[0].data = this.header; - options.series = [ - { - type: 'bar', - barGap: '-100%', - silent: true, - itemStyle: { - normal: { - barBorderColor: '#54acd5', - opacity: 0.2, - color: '#54acd5', - barBorderRadius: 5 - } - }, - barWidth: 10, - data: this.data[0] - }, - { - type: 'bar', - stack: '总量', - barGap: '-100%', - silent: true, - itemStyle: { - normal: { - barBorderColor: 'rgba(0,0,0,0)', - color: 'rgba(0,0,0,0)', - barBorderRadius: 5, - textStyle: { - align: 'right' - } - } - }, - barWidth: 10, - data: this.data[1] - }, - { - type: 'bar', - stack: '总量', - barGap: '-100%', - silent: true, - animation: true, - label: { - normal: { - show: true, - position: ['100%', this.getBrowserInfo()], - textStyle: { - color: "#54acd5" - } - } - }, - itemStyle: { - normal: { - barBorderColor: '#54acd5', - color: '#54acd5', - barBorderRadius: 5 - } - }, - barWidth: 10, - data: this.data[2] - } - ]; - return options; - } -} - -/** - * 条形色值图 - */ -export class StripColorGraphData extends AbstractNormalGraphData { - protected optionsTemplate: EchartOptions = { - title: { - text: '', - left: "center", - top: 20, - textStyle: { - color: '#434343', - fontSize: 12 - } - }, - calculable: true, - grid: { - left: 90, - right: 60, - top: 60 - }, - xAxis: [ - { - type: 'value', - splitNumber: 4, - axisLine: { - show: false - }, - splitLine: {//出网格线 - show: false - }, - axisLabel: { - show: false - } - } - ], - yAxis: [ - { - splitLine: { - show: false - }, - boundaryGap: true, - type: 'category', - scale: false, - axisLabel: { - textStyle: { - color: '#434343'//刻度标签样式 - } - - }, - axisLine: { - show: false - }, - axisTick: {//坐标轴刻度相关设置 - show: false - }, - data: [] - } - ], - series: [ - { - name: 'bar', - type: 'bar', - stack: "总量", - silent: true, - animation: false,//关闭动漫 - barWidth: '10px', - data: [] - - }, - { - name: 'bar6', - type: 'bar', - stack: "总量", - silent: true, - animation: false,//关闭动漫 - label: {//图形数据显示位置 - normal: { - show: true, position: ['100%', -5], - textStyle: { - color: "#585858" - }, - formatter: function (params) { - return " " + (100 - params.value) - } - }, - }, - itemStyle: {//图形边框设置,如边框大小,圆角,填充着色 - normal: { - color: "#dedede" - } - }, - barWidth: '10px',//条形宽度 - data: [] - } - - ] - }; - - protected createSeries(): any[][] { - return [ - this.data[0].map(v => { - return { - value: v, - itemStyle: { - normal: { - color: v > 95 ? "#98e2a6" : v > 90 ? "#9ad0e2" : v > 80 ? "#f7e685" : v > 70 ? "#f6c88a" : "#ff8e74" - } - } - } - }), - this.data[0].map(v => 100 - v) - ] - } - - protected createChartOptions(): EchartOptions { - if (!this.data || !this.data.length) return; - const opt = {...this.optionsTemplate}; - this._extendOption(opt); - opt.yAxis[0].data = this.header; - [opt.series[0].data, opt.series[1].data] = this.createSeries(); - return opt; - } -} - -/** - * 堆叠区域图 - */ -export class StackedAreaGraphData extends AbstractGraphData { - protected createChartOptions(): any { - if (!this.data || !this.data.length) return; - - return { - xAxis: [ - { - type: 'category', - boundaryGap: false, - alignWithLabel: true, - axisLabel: { // 类轴刻度间隔 - interval: 4 - }, - axisTick: { //坐标轴刻度相关设置 - show: true, - inside: true, - interval: 0, - length: 5,//刻度长短设置 - lineStyle: { - color: '#bbb', - } - }, - splitLine: {//网格线相关设置 - interval: 0,//类目轴为true且为这个为0时才会显示 - lineStyle: { - color: "#eee" - } - }, - data: this.header - } - ], - yAxis: [ - { - type: 'value', - max: 3, - axisTick: { - show: false, - }, - axisLine: {//轴线设置 - lineStyle: { - color: '#ccc' - } - }, - min: 0, - axisLabel: { - formatter: function (params) { - return params.toFixed(1) == "0.0" ? "" : params.toFixed(1) + "%" - } - } - } - ], - series: [ - { - type: 'line', - connectNulls: true, - smooth: true, - symbolSize: [5, 5], - showAllSymbol: true, - areaStyle: { - normal: { - color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ - { - offset: 0, color: '#ff7c24' // 0% 处的颜色 - }, - { - offset: 1, color: '#fff' // 100% 处的颜色 - } - ], false) - } - }, - data: this.data[0], - } - ] - }; - - } -} - /** * 仪表盘 */ @@ -978,465 +440,3 @@ export class RadarGraphData extends AbstractNormalGraphData { return opt; } } - -/** - * K线图 - */ -export class KLineGraphData extends AbstractNormalGraphData { - protected createSeries(): any[] { - return [{ - name: 'k data', - type: 'k', - showAllSymbol: true, - animation: true, - smooth: false, - symbolSize: [5, 5], - hoverAnimation: false, - data: this.data - }]; - } - - public sampleColors = ["#54acd5", "#f99660", "#a4bf6a", "#ec6d6d", "#f7b913", "#8ac9b6", "#bea5c8", "#01c5c2", "#a17660"]; - public vmaxColors = ['#41addc', '#bea5c8', '#85c56c', '#f99660', '#ffc20e', '#ec6d6d', '#8ac9b6', '#585eaa', '#b22c46', '#96582a']; - - protected optionsTemplate = { - color: this.vmaxColors, - tooltip: { - trigger: 'axis', - position: function (point) {// 固定在顶部 - return [point[0] + 10, point[1]]; - } - }, - grid: { - left: 45, - right: 45, - top: 60 - }, - calculable: true, - /*当某天无数据不补零,同时不连线的效果实现*/ - /* - *以x轴为类目轴为例:如下 - *坐标轴 刻度标签 设显示间隔:xAxis.axisLabel.interval根据具体情况设置标签显示间隔; - *坐标轴 刻度 的显示间隔:xAxis.axisTick.interval设置全部显示为0; - *标志图形全部显示:series.showAllSymbol设置为true, - * */ - xAxis: [ - { - type: 'category', - boundaryGap: false, - axisLabel: {//标签设置 - interval: 4 - }, - splitLine: {//设置网格 - interval: 0 - }, - scale: true, - data: [] - } - ], - yAxis: [ - { - type: 'value', - splitNumber: 10,//不是类轴才会生效,设置网格多少 - axisLabel: {//标签设置 - interval: 4 - } - } - ], - series: [] - }; - - protected createChartOptions(): EchartOptions { - if (!this.data || !this.data.length) return; - const opt = {...this.optionsTemplate}; - this._extendOption(opt); - opt.xAxis[0].data = this.header; - opt.series = this.createSeries(); - return opt; - } -} - -/** - * 箱线图 - */ -export class BoxPlotGraphData extends AbstractGraphData { - public title: string; - - protected createChartOptions(): any { - if (!this.data || !this.data.length) return; - - return { - title: [ - { - text: this.title, - left: 'center', - }, - { - text: 'upper: Q3 + 1.5 * IRQ \nlower: Q1 - 1.5 * IRQ', - borderColor: '#999', - borderWidth: 1, - textStyle: { - fontSize: 14 - }, - left: '10%', - top: '90%' - } - ], - tooltip: { - trigger: 'item', - axisPointer: { - type: 'shadow' - } - }, - grid: { - left: '10%', - right: '10%', - bottom: '15%' - }, - xAxis: { - type: 'category', - data: this.data.map((row, i) => i), - boundaryGap: true, - nameGap: 30, - splitArea: { - show: false - }, - axisLabel: { - formatter: 'expr {value}' - }, - splitLine: { - show: false - } - }, - yAxis: { - type: 'value', - name: 'km/s minus 299,000', - splitArea: { - show: true - } - }, - series: [ - { - name: 'boxplot', - type: 'boxplot', - data: this.data, - tooltip: { - formatter: function (param) { - return [ - 'Experiment ' + param.name + ': ', - 'upper: ' + param.data[5], - 'Q3: ' + param.data[4], - 'median: ' + param.data[3], - 'Q1: ' + param.data[2], - 'lower: ' + param.data[1] - ].join('
') - } - } - }, - /*{ - name: 'outlier', - type: 'scatter', - data: data.outliers - }*/ - ] - }; - } -} - -/** - * 热力图 - */ -export class HeatGraphData extends AbstractNormalGraphData { - - protected createChartOptions(): any { - if (!this.data || !this.data.length) return; - return { - tooltip: { - position: 'top' - }, - animation: false, - grid: { - height: '50%', - y: '10%' - }, - xAxis: { - type: 'category', - data: this.data[this.data.length - 2], - splitArea: { - show: true - } - }, - yAxis: { - type: 'category', - data: this.data[this.data.length - 1], - splitArea: { - show: true - } - }, - visualMap: { - min: 0, - max: 10, - calculable: true, - orient: 'horizontal', - left: 'center', - bottom: '15%' - }, - series: [{ - name: 'Punch Card', - type: 'heatmap', - data: this.data.slice(0, this.data.length - 2), - label: { - normal: { - show: true - } - }, - itemStyle: { - emphasis: { - shadowBlur: 10, - shadowColor: 'rgba(0, 0, 0, 0.5)' - } - } - }] - }; - } -} - -/** - * 关系图 - */ -export class RelationalGraphData extends AbstractGraphData { - public data: any[]; - - public title: string; - - protected createChartOptions(): any { - if (!this.data || !this.data.length) return; - return { - title: { - text: this.title - }, - tooltip: {}, - xAxis: { - type: 'category', - boundaryGap: false, - data: this.data[this.data.length - 2] - }, - yAxis: { - type: 'value' - }, - series: [ - { - type: 'graph', - layout: 'none', - coordinateSystem: 'cartesian2d', - symbolSize: 40, - label: { - normal: { - show: true - } - }, - edgeSymbol: ['circle', 'arrow'], - edgeSymbolSize: [4, 10], - data: this.data[0], - links: this.data[this.data.length - 1], - lineStyle: { - normal: { - color: '#2f4554' - } - } - } - ] - }; - } -} - -/** - * 漏斗图 - */ -export class FunnelPlotGraphData extends AbstractNormalGraphData { - protected optionsTemplate = { - title: { - text: '', - left: 'left' - }, - tooltip: { - trigger: 'item', - formatter: "{a}
{b} : {c}%" - }, - toolbox: { - feature: { - dataView: {readOnly: false}, - restore: {}, - saveAsImage: {} - } - }, - legend: { - left: 'center', - data: [] - }, - calculable: true, - series: [ - { - name: '漏斗图', - type: 'funnel', - left: '10%', - top: 60, - //x2: 80, - bottom: 60, - width: '80%', - // height: {totalHeight} - y - y2, - min: 0, - max: 100, - minSize: '0%', - maxSize: '100%', - sort: 'descending', - gap: 2, - label: { - normal: { - show: true, - position: 'inside' - }, - emphasis: { - textStyle: { - fontSize: 20 - } - } - }, - labelLine: { - normal: { - length: 10, - lineStyle: { - width: 1, - type: 'solid' - } - } - }, - itemStyle: { - normal: { - borderColor: '#fff', - borderWidth: 1 - } - }, - data: [] - } - ] - }; - - protected createChartOptions(): any { - if (!this.data || !this.data.length) return; - const opt = {...this.optionsTemplate}; - this._extendOption(opt); - opt.legend.data = this.rowDescriptor.reverse(); - opt.series[0].data = this.data.map((row, i) => ({value: row[0], name: this.rowDescriptor[i]})); - return opt; - } -} - -/** - * 气泡图 - * */ -export class BubbleChartGraphData extends AbstractNormalGraphData { - // 气泡图形大小最小值 - public minSymbolSize: number = 130; - - // 最大气泡大小 - public symbolSize: number = 150; - - public layout: string = 'force'; - - public emphasisConfig: EmphasisConfig; - - protected createChartOptions(): EchartOptions { - if (!this.data || !this.data.length) return; - let maxValue = 1; - const valueList = this.data[0].map(item => item.value); - maxValue = Math.max(maxValue, ...valueList); - const minValue = Math.min(maxValue, ...valueList); - - const sizeScale = (this.symbolSize - this.minSymbolSize) / (maxValue - minValue); - const sizeOffset = this.minSymbolSize - sizeScale * minValue; - - // 斥力 为了防止重叠,斥力最好大于 symbolSize - const repulsion = this.symbolSize * 3; - - // 获取要渲染的数据 - const data = this.data[0].map((item) => { - // 根据与最大值的比例和最大气泡大小,算出每个元素的大小 - let size: number; - if (maxValue == minValue) { - size = this.symbolSize; - } else { - size = Math.max(sizeScale*item.value + sizeOffset, this.minSymbolSize); - } - const itemData = { - name: item.label, - value: item.value, - label: item.labelConfig || {}, - symbolSize: size, - itemStyle: item.itemStyle || {}, - emphasis: item.emphasisConfig || this.emphasisConfig - }; - if (!item.x && !item.y) { - return itemData; - } - this.layout = "none"; - return {...itemData, x: item.x, y: item.y}; - }); - - const emphasisConfig = this.emphasisConfig || {}; - - return { - xAxis: { - show: false, - }, - yAxis: { - show: false, - }, - series: [ - { - data, - type: "graph", // 关系图 - layout: this.layout, - draggable: true, // 启用节点拖拽 - force: { - // 值越大则斥力越大 每个元素间隔越大 - repulsion, - // 是否开启布局动画 - layoutAnimation: true, - // 元素之间的引力,越大引力越强默认是1 - gravity: 0.1, - // 即布局动画执行的时间。数值越大,动画执行的时间越长 - coolDown: 10 - }, - // 高亮状态的图形样式 - emphasis: emphasisConfig, - // 设置 label - label: { - show: true, - position: "inside", - formatter: [`{title|{b}}`, `{num|{c}}`].join("\n"), - rich: { - title: { - align: "center", - fontSize: 13, - lineHeight: 18, - color: "#FFF", - }, - num: { - align: "center", - fontSize: "15", - lineHeight: 21, - fontWeight: 500, - color: "#FFF", - }, - } - }, - itemStyle: { - borderWidth: 1, - color: "green", - }, - }, - ], - }; - } -} diff --git a/src/jigsaw/common/core/data/graph-data/modeled-bubble.ts b/src/jigsaw/common/core/data/graph-data/modeled-bubble.ts new file mode 100644 index 0000000000..d0674a6af3 --- /dev/null +++ b/src/jigsaw/common/core/data/graph-data/modeled-bubble.ts @@ -0,0 +1,93 @@ +import {GraphDataField, GraphDataHeader, GraphDataMatrix} from "./graph-data"; +import {EchartOptions, EchartXAxis, EchartYAxis} from "../echart-types"; +import {CommonUtils} from "../../utils/common-utils"; +import {AbstractModeledGraphData, CustomModeledGraphTemplate, DimKpiBase, GraphType} from "./modeled"; +import {EmphasisConfig} from "./normal-bubble"; + +export class ModeledBubbleGraphData extends AbstractModeledGraphData { + constructor(data: GraphDataMatrix = [], header: GraphDataHeader = [], field: GraphDataField = []) { + super(data, header, field); + } + + // 此气泡图使用的是echarts的关系图,type类型为graph + public type: GraphType = 'graph'; + public template: CustomModeledGraphTemplate = new CustomModeledGraphTemplate(); + + public xAxis: EchartXAxis = {}; + public yAxis: EchartYAxis = {}; + public minSymbolSize: number; + public maxSymbolSize: number; + public layout: string = 'force'; + public series: DimKpiBase[]; + public emphasisConfig: EmphasisConfig; + + private _options: EchartOptions; + + get options(): EchartOptions { + if (!this._options) { + this._options = this.createChartOptions(); + } + return this._options; + } + + protected createChartOptions(): EchartOptions { + if (!this.minSymbolSize || !this.maxSymbolSize) { + return undefined; + } + const options = this.template.getInstance(); + options.xAxis = this.xAxis; + options.yAxis = this.yAxis; + const data = this._handleData(); + const emphasisConfig = this.emphasisConfig || {}; + + // 斥力 为了防止重叠,斥力最好大于 maxSymbolSize + const repulsion = this.maxSymbolSize * 3; + options.series = [ + { + data, + type: this.type, + layout: this.layout, + draggable: true, + force: { + repulsion, + }, + emphasis: emphasisConfig + } + ] + CommonUtils.extendObject(options, this.template.optionPatch); + return options; + } + + private _handleData() { + if (!this.data || !this.data.length) { + return; + } + let maxValue = 1; + const valueList = this.data.map(item => item["value"]); + maxValue = Math.max(maxValue, ...valueList); + const minValue = Math.min(maxValue, ...valueList); + + const sizeScale = (this.maxSymbolSize - this.minSymbolSize) / (maxValue - minValue); + const sizeOffset = this.minSymbolSize - sizeScale * minValue; + + // 获取要渲染的数据 + return this.data.map((item) => { + // 根据与最大值的比例和最大气泡大小,算出每个元素的大小 + let size = Math.max(sizeScale * Number(item["value"]) + sizeOffset, this.minSymbolSize); + + const itemData = { + name: item["label"], + value: item["value"], + label: item["labelConfig"] || {}, + symbolSize: size, + itemStyle: item["itemStyle"] || {}, + emphasis: item["emphasisConfig"] || this.emphasisConfig + }; + if (!item["x"] && !item["y"]) { + return itemData; + } + this.layout = "none"; + return {...itemData, x: item["x"], y: item["y"]}; + }); + } +} diff --git a/src/jigsaw/common/core/data/graph-data/modeled-funnel.ts b/src/jigsaw/common/core/data/graph-data/modeled-funnel.ts new file mode 100644 index 0000000000..1ae3b06109 --- /dev/null +++ b/src/jigsaw/common/core/data/graph-data/modeled-funnel.ts @@ -0,0 +1,150 @@ +import {GraphDataField, GraphDataHeader, GraphDataMatrix} from "./graph-data"; +import {EchartLegend, EchartOptions, EchartSeriesItem} from "../echart-types"; +import {CommonUtils} from "../../utils/common-utils"; +import {JigsawThemeService} from "../../theming/theme"; +import {AbstractModeledGraphData, CustomModeledGraphTemplate, Dimension, GraphType, Indicator, SeriesBase} from "./modeled"; + +export type FunnelItemStyle = { + borderColor?: string; + borderWidth?: number; + borderType?: string; + shadowBlur?: number; + shadowColor?: string; + shadowOffsetX?: number; + shadowOffsetY?: number; + opacity?: number; +} +export type FunnelLabel = { + show?: boolean; + position?: string; + color?: string; + fontStyle?: string; + fontWeight?: string; + fontSize?: number; +} +export type FunnelLabelLine = { + show?: boolean; + lineStyle?: FunnelLineStyle; +} +export type FunnelLineStyle = { + color?: string; + width?: number; + type?: string; +} + +export class FunnelSeries extends SeriesBase { + public sort?: 'ascending' | 'descending' | 'none'; + public gap?: number; + public orient?: 'vertical' | 'horizontal'; + public funnelAlign?: 'left' | 'right' | 'center'; + public label?: FunnelLabel; + public itemStyle?: FunnelItemStyle; + public labelLine?: FunnelLabelLine; + public width?: string; + public height?: string; + public min?: number; + public max?: number; + public top?: string; + public left?: string; + public colorConfig?: string; + public color?: string[]; +} + +export class ModeledFunnelGraphData extends AbstractModeledGraphData { + constructor(data: GraphDataMatrix = [], header: GraphDataHeader = [], field: GraphDataField = []) { + super(data, header, field); + } + + public type: GraphType = 'funnel'; + public template: CustomModeledGraphTemplate = new CustomModeledGraphTemplate(); + public series: FunnelSeries[]; + public legendSource: 'dim' | 'kpi'; + + private _options: EchartOptions; + + get options(): EchartOptions { + if (!this._options) { + this._options = this.createChartOptions(); + } + return this._options; + } + + private _mergeLegend(legendObject: EchartLegend, candidates: (Indicator | Dimension)[]): void { + if (!legendObject) { + return; + } + const names = candidates.map(can => can.name); + legendObject.data.push(...names.filter(legend => legendObject.data.indexOf(legend) == -1)); + } + + protected createChartOptions(): EchartOptions { + if (!this.series || !this.header.length || !this.data.length) { + return undefined; + } + + const options = this.template.getInstance(); + if (options.legend) { + options.legend.data = []; + } + + CommonUtils.extendObject(options, this.template.optionPatch); + options.series = this.series + .filter(seriesData => { + if (!seriesData.dimensionField) { + return false; + } + if (!seriesData.indicators || seriesData.indicators.length == 0) { + return false; + } + return seriesData.usingAllDimensions || (seriesData.dimensions && seriesData.dimensions.length > 0); + }) + .map((seriesData, idx) => { + const dimensions = this.getRealDimensions(seriesData.dimensionField, seriesData.dimensions, seriesData.usingAllDimensions); + const dimIndex = this.getIndex(seriesData.dimensionField); + seriesData.indicators.forEach(kpi => kpi.index = this.getIndex(kpi.field)); + seriesData.indicators.forEach(kpi => kpi.name = kpi.name ? kpi.name : this.header[kpi.index]); + + const seriesItem = CommonUtils.extendObjects({type: 'funnel'}, this.template.seriesItem); + const legendSource = this.legendSource ? this.legendSource : dimensions.length > 1 ? 'dim' : 'kpi'; + if (dimensions.length == 0) { + console.warn('No valid dimension found, this graph will not be rendered!'); + } else if (legendSource == 'dim') { + // 多维度 + this._mergeLegend(options.legend, dimensions); + let records; + if (seriesData.usingAllDimensions) { + records = this.data; + } else { + records = this.data.filter(row => dimensions.find(d => d.name == row[dimIndex])); + } + const kpiIndex = seriesData.indicators[0].index; + records = records.map(row => [row[dimIndex], row[kpiIndex]]); + const indicator: Indicator = CommonUtils.deepCopy(seriesData.indicators[0]); + indicator.index = 1; + seriesItem.data = this.pruneData(records, 0, dimensions, [indicator]) + .map(row => ({name: row[0], value: row[1]})); + } else { + // 多指标 + this._mergeLegend(options.legend, seriesData.indicators); + const dim = dimensions[0].name; + const records = this.data.filter(row => row[dimIndex] == dim); + const pruned = this.pruneData(records, dimIndex, dimensions, seriesData.indicators)[0]; + seriesItem.data = seriesData.indicators.map(i => ({name: i.name, value: pruned[i.index]})); + } + + if (seriesData.colorConfig) { + seriesData.color = JigsawThemeService.getGraphTheme().chartColorConfigs[seriesData.colorConfig]; + } + SeriesBase.extend(seriesItem, seriesData, idx); + return seriesItem; + }); + + + return options; + } + + public refresh(): void { + this._options = undefined; + super.refresh(); + } +} diff --git a/src/jigsaw/common/core/data/graph-data/modeled-gauge.ts b/src/jigsaw/common/core/data/graph-data/modeled-gauge.ts new file mode 100644 index 0000000000..2b06906471 --- /dev/null +++ b/src/jigsaw/common/core/data/graph-data/modeled-gauge.ts @@ -0,0 +1,117 @@ +import {GraphDataField, GraphDataHeader, GraphDataMatrix} from "./graph-data"; +import {EchartOptions, EchartSeriesItem, EchartTitle} from "../echart-types"; +import {CommonUtils} from "../../utils/common-utils"; +import {aggregate} from "../../utils/data-collection-utils"; +import {AbstractModeledGraphData, CustomModeledGraphTemplate, Dimension, GraphType, Indicator, SeriesBase} from "./modeled"; + +export class GaugeSeries extends SeriesBase { + public dimensions: Dimension[] = [new Dimension('')]; + public usingAllDimensions: boolean = false; + + public center?: number[] = [50, 50]; + public radius?: number = 90; + public startAngle?: number = 225; + public endAngle?: number = -45; + public min?: number = 0; + public max?: number = 100; + public detail?: any = {formatter: '{value}%'}; + + public splitNumber?: number; + public axisLine?: any; + public axisTick?: any; + public axisLabel?: any; + public splitLine?: any; + public pointer?: any; + public itemStyle?: any; + public title?: any; + public autoAxisLineColor?: boolean; + public slope?: number; +} + +export class ModeledGaugeGraphData extends AbstractModeledGraphData { + constructor(data: GraphDataMatrix = [], header: GraphDataHeader = [], field: GraphDataField = []) { + super(data, header, field); + } + + public type: GraphType = 'gauge'; + public template: CustomModeledGraphTemplate = new CustomModeledGraphTemplate(); + public series: GaugeSeries[]; + public title: EchartTitle; + private _options: EchartOptions; + + get options(): EchartOptions { + if (!this._options) { + this._options = this.createChartOptions(); + } + return this._options; + } + + protected createChartOptions(): EchartOptions { + if (!this.series || !this.field.length || !this.header.length || !this.data.length) { + return undefined; + } + const options = this.template.getInstance(); + options.series = this.series + .filter(seriesData => { + if (!seriesData.dimensionField) { + return false; + } + if (!seriesData.indicators || seriesData.indicators.length == 0) { + return false; + } + return seriesData.usingAllDimensions || (seriesData.dimensions && seriesData.dimensions.length > 0); + }) + .map((seriesData, idx) => { + const dimensions = this.getRealDimensions(seriesData.dimensionField, seriesData.dimensions, seriesData.usingAllDimensions); + const dimIndex = this.getIndex(seriesData.dimensionField); + seriesData.indicators.forEach(kpi => kpi.index = this.getIndex(kpi.field)); + seriesData.indicators.forEach(kpi => kpi.name = kpi.name ? kpi.name : this.header[kpi.index]); + + const seriesItem = CommonUtils.extendObjects({type: 'gauge'}, this.template.seriesItem, seriesData); + + if (dimensions.length > 1) { + // 多维度 + let records; + if (seriesData.usingAllDimensions) { + records = this.data; + } else { + records = this.data.filter(row => dimensions.find(d => d.name == row[dimIndex])); + } + const kpiIndex = seriesData.indicators[0].index; + records = records.map(row => [row[dimIndex], row[kpiIndex]]); + const indicator: Indicator = CommonUtils.deepCopy(seriesData.indicators[0]); + indicator.index = 1; + seriesItem.data = this.aggregateData(records, [indicator]) + .map(row => ({name: indicator.name, value: row[1]})); + } else { + // 多指标 + const dim = dimensions[0].name; + const records = this.data.filter(row => row[dimIndex] == dim); + const pruned = this.aggregateData(records, seriesData.indicators)[0]; + seriesItem.data = seriesData.indicators.map(i => ({name: i.name, value: pruned[i.index]})); + } + + SeriesBase.extend(seriesItem, seriesData, idx); + seriesItem.radius = seriesData.radius ? seriesData.radius + '%' : seriesData.radius; + seriesItem.center = seriesData.center ? seriesData.center.map(r => r + '%') : seriesData.center; + + if (seriesItem.autoAxisLineColor && seriesItem.axisLine?.lineStyle?.color?.[0]?.length && seriesItem.data?.[0]) { + seriesItem.axisLine.lineStyle.color[0][0] = Number(seriesItem.data[0].value) / (Number(seriesItem.max) || 100); + } + + return seriesItem; + }); + CommonUtils.extendObject(options, this.template.optionPatch); + return options; + } + + protected aggregateData(records: (string | number)[][], indicators: Indicator[]): string[][] { + const aggregateBy = indicators.map(kpi => ({index: kpi.index, algorithm: kpi.aggregateBy})); + return [aggregate(records, aggregateBy)]; + } + + public refresh(): void { + this._options = undefined; + super.refresh(); + } +} diff --git a/src/jigsaw/common/core/data/graph-data/modeled-map.ts b/src/jigsaw/common/core/data/graph-data/modeled-map.ts new file mode 100644 index 0000000000..8067905a2c --- /dev/null +++ b/src/jigsaw/common/core/data/graph-data/modeled-map.ts @@ -0,0 +1,119 @@ +import {GraphDataField, GraphDataHeader, GraphDataMatrix} from "./graph-data"; +import {EchartOptions, EchartSeriesItem} from "../echart-types"; +import {CommonUtils} from "../../utils/common-utils"; +import {JigsawThemeService} from "../../theming/theme"; +import {AbstractModeledGraphData, CustomModeledGraphTemplate, GraphType, Indicator, SeriesBase} from "./modeled"; + +export class MapSeries extends SeriesBase { + public mapType?: string = ''; + public label?: string; + public itemStyle?: MapItemStyle; + public animation?: string; + public roam?: boolean; +} + +export type MapVisualMap = { + position?: string; + type?: string; + startText?: string; + endText?: string; + text?: string[]; + min?: number; + max?: number; + colorConfig?: string; + color?: string[]; + textStyle?: MapVisualMapTextStyle; + more?: any; +} +export type MapVisualMapTextStyle = { + color?: string; + fontStyle?: string; + fontWeight?: string; + fontSize?: number; +} +export type MapItemStyle = { + areaColor?: string; + borderColor?: string; + borderWidth?: number; + borderType?: string; + shadowBlur?: number; + shadowColor?: string; + shadowOffsetX?: number; + shadowOffsetY?: number; + opacity?: number; +} + +export class ModeledMapGraphData extends AbstractModeledGraphData { + constructor(data: GraphDataMatrix = [], header: GraphDataHeader = [], field: GraphDataField = []) { + super(data, header, field); + } + + public type: GraphType = 'map'; + public template: CustomModeledGraphTemplate = new CustomModeledGraphTemplate(); + public series: MapSeries[]; + public visualMap: MapVisualMap; + private _options: EchartOptions; + + get options(): EchartOptions { + if (!this._options) { + this._options = this.createChartOptions(); + } + return this._options; + } + + protected createChartOptions(): EchartOptions { + if (!this.series) { + return undefined; + } + const options = this.template.getInstance(); + + options.series = this.series + .filter(seriesData => { + if (!seriesData.dimensionField) { + return false; + } + if (!seriesData.indicators || seriesData.indicators.length == 0) { + return false; + } + return seriesData.usingAllDimensions || (seriesData.dimensions && seriesData.dimensions.length > 0); + }) + .map((seriesData, idx) => { + const dimensions = this.getRealDimensions(seriesData.dimensionField, seriesData.dimensions, seriesData.usingAllDimensions); + const dimIndex = this.getIndex(seriesData.dimensionField); + seriesData.indicators.forEach(kpi => kpi.index = this.getIndex(kpi.field)); + seriesData.indicators.forEach(kpi => kpi.name = kpi.name ? kpi.name : this.header[kpi.index]); + + const seriesItem = CommonUtils.extendObjects({type: 'map'}, this.template.seriesItem); + if (!dimensions.length) { + return {}; + } + + // 多维度单指标 + let records; + if (seriesData.usingAllDimensions) { + records = this.data; + } else { + records = this.data.filter(row => dimensions.find(d => d.name == row[dimIndex])); + } + const kpiIndex = seriesData.indicators[0].index; + records = records.map(row => [row[dimIndex], row[kpiIndex]]); + const indicator: Indicator = CommonUtils.deepCopy(seriesData.indicators[0]); + indicator.index = 1; + seriesItem.data = this.pruneData(records, 0, dimensions, [indicator]) + .map(row => ({name: row[0], value: row[1]})); + + SeriesBase.extend(seriesItem, seriesData, idx); + return seriesItem; + }); + if (options.visualMap?.colorConfig) { + options.visualMap.color = JigsawThemeService.getGraphTheme().chartColorConfigs[options.visualMap.colorConfig]; + } + CommonUtils.extendObject(options, this.template.optionPatch); + return options; + } + + public refresh(): void { + this._options = undefined; + super.refresh(); + } +} diff --git a/src/jigsaw/common/core/data/modeled-pie-graph-data.spec.ts b/src/jigsaw/common/core/data/graph-data/modeled-pie.spec.ts similarity index 95% rename from src/jigsaw/common/core/data/modeled-pie-graph-data.spec.ts rename to src/jigsaw/common/core/data/graph-data/modeled-pie.spec.ts index ca0886c200..c75d3cff65 100644 --- a/src/jigsaw/common/core/data/modeled-pie-graph-data.spec.ts +++ b/src/jigsaw/common/core/data/graph-data/modeled-pie.spec.ts @@ -2,9 +2,10 @@ import { AbstractModeledGraphData, CustomModeledGraphTemplate, Dimension, Indicator, - ModeledPieGraphData, -} from "./modeled-graph-data"; -import {EchartOptions} from "./echart-types"; + +} from "./modeled"; +import {EchartOptions} from "../echart-types"; +import {ModeledPieGraphData} from "./modeled-pie"; class TestGraphData extends AbstractModeledGraphData { diff --git a/src/jigsaw/common/core/data/graph-data/modeled-pie.ts b/src/jigsaw/common/core/data/graph-data/modeled-pie.ts new file mode 100644 index 0000000000..e66c9cd4b4 --- /dev/null +++ b/src/jigsaw/common/core/data/graph-data/modeled-pie.ts @@ -0,0 +1,104 @@ +import {GraphDataField, GraphDataHeader, GraphDataMatrix} from "./graph-data"; +import {EchartLegend, EchartOptions, EchartSeriesItem} from "../echart-types"; +import {CommonUtils} from "../../utils/common-utils"; +import {AbstractModeledGraphData, CustomModeledGraphTemplate, Dimension, GraphType, Indicator, SeriesBase} from "./modeled"; + +export class PieSeries extends SeriesBase { + public radius: number[]; + public center: number[]; + public roseType?: boolean | 'radius' | 'area'; + public label?: any; +} + +export class ModeledPieGraphData extends AbstractModeledGraphData { + constructor(data: GraphDataMatrix = [], header: GraphDataHeader = [], field: GraphDataField = []) { + super(data, header, field); + } + + public type: GraphType = 'pie'; + public template: CustomModeledGraphTemplate = new CustomModeledGraphTemplate(); + public series: PieSeries[]; + private _options: EchartOptions; + public legendSource: 'dim' | 'kpi'; + + get options(): EchartOptions { + if (!this._options) { + this._options = this.createChartOptions(); + } + return this._options; + } + + protected createChartOptions(): EchartOptions { + if (!this.series) { + return undefined; + } + const options = this.template.getInstance(); + if (options.legend) { + options.legend.data = []; + } + options.series = this.series + .filter(seriesData => { + if (!seriesData.dimensionField) { + return false; + } + if (!seriesData.indicators || seriesData.indicators.length == 0) { + return false; + } + return seriesData.usingAllDimensions || (seriesData.dimensions && seriesData.dimensions.length > 0); + }) + .map((seriesData, idx) => { + const dimensions = this.getRealDimensions(seriesData.dimensionField, seriesData.dimensions, seriesData.usingAllDimensions); + const dimIndex = this.getIndex(seriesData.dimensionField); + seriesData.indicators.forEach(kpi => kpi.index = this.getIndex(kpi.field)); + seriesData.indicators.forEach(kpi => kpi.name = kpi.name ? kpi.name : this.header[kpi.index]); + + const seriesItem = CommonUtils.extendObjects({type: 'pie'}, this.template.seriesItem); + const legendSource = this.legendSource ? this.legendSource : dimensions.length > 1 ? 'dim' : 'kpi'; + if (dimensions.length == 0) { + console.warn('No valid dimension found, this graph will not be rendered!'); + } else if (legendSource == 'dim') { + // 多维度 + this._mergeLegend(options.legend, dimensions); + let records; + if (seriesData.usingAllDimensions) { + records = this.data; + } else { + records = this.data.filter(row => dimensions.find(d => d.name == row[dimIndex])); + } + const kpiIndex = seriesData.indicators[0].index; + records = records.map(row => [row[dimIndex], row[kpiIndex]]); + const indicator: Indicator = CommonUtils.deepCopy(seriesData.indicators[0]); + indicator.index = 1; + seriesItem.data = this.pruneData(records, 0, dimensions, [indicator]) + .map(row => ({name: row[0], value: row[1]})); + } else { + // 多指标 + this._mergeLegend(options.legend, seriesData.indicators); + const dim = dimensions[0].name; + const records = this.data.filter(row => row[dimIndex] == dim); + const pruned = this.pruneData(records, dimIndex, dimensions, seriesData.indicators)[0]; + seriesItem.data = seriesData.indicators.map(i => ({name: i.name, value: pruned[i.index]})); + } + + SeriesBase.extend(seriesItem, seriesData, idx); + seriesItem.radius = seriesData.radius ? seriesItem.radius.map(r => r + '%') : seriesData.radius; + seriesItem.center = seriesItem.center ? seriesItem.center.map(r => r + '%') : seriesItem.center; + return seriesItem; + }); + CommonUtils.extendObject(options, this.template.optionPatch); + return options; + } + + private _mergeLegend(legendObject: EchartLegend, candidates: (Indicator | Dimension)[]): void { + if (!legendObject) { + return; + } + const names = candidates.map(can => can.name); + legendObject.data.push(...names.filter(legend => legendObject.data.indexOf(legend) == -1)); + } + + public refresh(): void { + this._options = undefined; + super.refresh(); + } +} diff --git a/src/jigsaw/common/core/data/graph-data/modeled-radar.ts b/src/jigsaw/common/core/data/graph-data/modeled-radar.ts new file mode 100644 index 0000000000..8e1ec43ab4 --- /dev/null +++ b/src/jigsaw/common/core/data/graph-data/modeled-radar.ts @@ -0,0 +1,124 @@ +import {EchartOptions, EchartSeriesItem} from "../echart-types"; +import {CommonUtils} from "../../utils/common-utils"; +import {GraphDataField, GraphDataHeader, GraphDataMatrix} from "./graph-data"; +import {AbstractModeledGraphData, CustomModeledGraphTemplate, Dimension, GraphType, Indicator} from "./modeled"; + +export class RadarSeries { + public name: string; + public type: string = 'radar'; + public data: any[]; + public areaStyle: any; + + constructor(name?: string) { + this.name = name; + } +} + +export class RadarItem { + public indicator: RadarIndicator[] = []; + public radius: any; + public center: any[]; +} + +export class RadarIndicator extends Indicator { + public max?: number; + public min?: number; + public color?: string; + + public static extend(indicatorItem: EchartSeriesItem, indicator: RadarIndicator) { + const indicatorBak = CommonUtils.deepCopy(indicator); + indicatorItem.min = indicator.min ? indicator.min : 0; + delete indicatorBak.min; + delete indicatorBak.field; + delete indicatorBak.defaultValue; + delete indicatorBak.aggregateBy; + Object.assign(indicatorItem, indicatorBak); + } +} + +export class RadarDimension extends Dimension { + public area: boolean; // 是否填充 + + public static extend(seriesItem: EchartSeriesItem, dimension: RadarDimension) { + const dimensionBak = CommonUtils.deepCopy(dimension); + seriesItem.areaStyle = dimension.area ? {} : null; + delete dimensionBak.area; + Object.assign(seriesItem, dimensionBak); + } +} + +export class ModeledRadarGraphData extends AbstractModeledGraphData { + public type: GraphType = 'radar'; + public template: CustomModeledGraphTemplate = new CustomModeledGraphTemplate(); + + public dimensionField: string; + public usingAllDimensions: boolean = true; + public dimensions: RadarDimension[] = []; + public indicators: RadarIndicator[] = []; + public isDefaultFillBackground: boolean; + + constructor(data: GraphDataMatrix = [], header: GraphDataHeader = [], field: GraphDataField = []) { + super(data, header, field); + } + + private _options: EchartOptions; + + get options(): EchartOptions { + if (!this._options) { + this._options = this.createChartOptions(); + } + return this._options; + } + + protected createChartOptions(): EchartOptions { + if (!this.dimensionField) { + return undefined; + } + if (!this.indicators || this.indicators.length == 0) { + return undefined; + } + if (!this.usingAllDimensions && (!this.dimensions || this.dimensions.length == 0)) { + return undefined; + } + const dimIndex = this.getIndex(this.dimensionField); + if (dimIndex == -1) { + return undefined; + } + + const options = this.template.getInstance(); + + this.indicators.forEach(kpi => kpi.index = this.getIndex(kpi.field)); + const dimensions = this.getRealDimensions(this.dimensionField, this.dimensions, this.usingAllDimensions); + if (options.legend) { + options.legend.data = dimensions.map(d => d.name); + } + let indicator = this.indicators.map((indicator: RadarIndicator) => { + const indicatorItem = {} + RadarIndicator.extend(indicatorItem, indicator); + return indicatorItem; + }); + options.radar = CommonUtils.extendObject(options.radar, {indicator: indicator}); + + let series = CommonUtils.extendObjects({type: 'radar'}, this.template.seriesItem); + series.data = dimensions.map((dimension: RadarDimension) => { + const fromData = this.dimensions.every(dim => dim.name != dimension.name); + // 数据里过来的维度值自动加上默认填充背景 + dimension.area = fromData ? this.isDefaultFillBackground : dimension.area; + const records = this.data.filter(row => row[dimIndex] == dimension.name); + const pruned = this.pruneData(records, dimIndex, [dimension], this.indicators)[0]; + const seriesItem = { + value: this.indicators.map(i => pruned[i.index]) + } + RadarDimension.extend(seriesItem, dimension); + return seriesItem; + }); + options.series = [series]; + CommonUtils.extendObject(options, this.template.optionPatch); + return options; + } + + public refresh(): void { + this._options = undefined; + super.refresh(); + } +} diff --git a/src/jigsaw/common/core/data/modeled-rectangular-graph-data.spec.ts b/src/jigsaw/common/core/data/graph-data/modeled-rectangular.spec.ts similarity index 96% rename from src/jigsaw/common/core/data/modeled-rectangular-graph-data.spec.ts rename to src/jigsaw/common/core/data/graph-data/modeled-rectangular.spec.ts index 53fbe2ff2a..f9b415953d 100644 --- a/src/jigsaw/common/core/data/modeled-rectangular-graph-data.spec.ts +++ b/src/jigsaw/common/core/data/graph-data/modeled-rectangular.spec.ts @@ -1,6 +1,7 @@ -import {CustomModeledGraphTemplate, Dimension, Indicator, ModeledRectangularGraphData} from "./modeled-graph-data"; -import {EchartOptions} from "./echart-types"; -import {Grouped} from "../utils/data-collection-utils"; +import {CustomModeledGraphTemplate, Dimension, Indicator} from "./modeled"; +import {EchartOptions} from "../echart-types"; +import {Grouped} from "../../utils/data-collection-utils"; +import {ModeledRectangularGraphData} from "./modeled-rectangular"; class ModeledRectangularGraphDataSpec extends ModeledRectangularGraphData { public getRealDimensions(dimField: string, dimensions: Dimension[], usingAllDimensions: boolean): Dimension[] { diff --git a/src/jigsaw/common/core/data/graph-data/modeled-rectangular.ts b/src/jigsaw/common/core/data/graph-data/modeled-rectangular.ts new file mode 100644 index 0000000000..8b00b65aed --- /dev/null +++ b/src/jigsaw/common/core/data/graph-data/modeled-rectangular.ts @@ -0,0 +1,290 @@ +import {EchartOptions, EchartSeriesItem, EchartXAxis, EchartYAxis} from "../echart-types"; +import {GraphDataField, GraphDataHeader, GraphDataMatrix} from "./graph-data"; +import {CommonUtils} from "../../utils/common-utils"; +import {aggregate, distinct, flat, group, Grouped} from "../../utils/data-collection-utils"; +import {getColumn} from "../unified-paging/paging"; +import {AbstractModeledGraphData, CustomModeledGraphTemplate, Dimension, DimKpiBase, GraphType, Indicator} from "./modeled"; + +export type RectangularSeriesType = 'bar' | 'line' | 'area'; +// ------------------------------------------------------------------------------------------------ +// 直角系图相关数据对象 +export class ModeledRectangularGraphData extends AbstractModeledGraphData { + public type: GraphType = 'rectangular'; + public defaultSeriesType: RectangularSeriesType = 'bar'; + public template: CustomModeledGraphTemplate = new CustomModeledGraphTemplate(); + + public xAxis: { field?: string, style?: EchartXAxis } = {}; + public yAxis1: EchartYAxis = {position: 'left'}; + public yAxis2: EchartYAxis = {position: 'right'}; + public dimensionField: string; + public dimensions: Dimension[] = []; + public usingAllDimensions: boolean = true; + public indicators: Indicator[] = []; + public series: DimKpiBase[]; + public legendSource: 'dim' | 'kpi'; + + constructor(data: GraphDataMatrix = [], header: GraphDataHeader = [], field: GraphDataField = []) { + super(data, header, field); + } + + private _options: EchartOptions; + + get options(): EchartOptions { + if (!this._options) { + this._options = this.createChartOptions(); + } + return this._options; + } + + protected createChartOptions(): EchartOptions { + if (!this.xAxis || !this.xAxis.field) { + return undefined; + } + if (!this.indicators || this.indicators.length == 0) { + return undefined; + } + + if (this.legendSource == 'dim') { + // 维度值作为图例却没有配置维度就返回 + if (!this.dimensionField) { + return undefined; + } + if (!this.usingAllDimensions && (!this.dimensions || this.dimensions.length == 0)) { + return undefined; + } + } + + this.indicators.forEach(kpi => kpi.index = this.getIndex(kpi.field)); + const dimensions = this.getRealDimensions(this.dimensionField, this.dimensions, this.usingAllDimensions); + let options = this.legendSource == 'dim' ? this.createMultiDimensionOptions(dimensions) : + this.createMultiKPIOptions(dimensions); + CommonUtils.extendObject(options, this.template.optionPatch); + return options; + } + + /** + * 单指标多维度 + */ + protected createMultiDimensionOptions(dimensions: Dimension[]): EchartOptions { + if (dimensions.length == 0) { + return undefined; + } + const xAxisIndex = this.getIndex(this.xAxis.field); + if (xAxisIndex == -1) { + return undefined; + } + const xAxisItems = distinct(getColumn(this.data, xAxisIndex)); + if (!xAxisItems || xAxisItems.length == 0) { + return undefined + } + + const options: EchartOptions = this.template.getInstance(); + options.xAxis = CommonUtils.extendObjects(options.xAxis, this.xAxis.style, {data: xAxisItems}); + if (options.yAxis instanceof Array) { + options.yAxis = [ + CommonUtils.extendObjects(options.yAxis[0], this.yAxis1), + CommonUtils.extendObjects(options.yAxis[1], this.yAxis2) + ]; + } else { + options.yAxis = CommonUtils.extendObjects(options.yAxis, this.yAxis1); + } + + if (options.legend) { + options.legend.data = dimensions.map(d => d.name); + } + + const dimIndex = this.getIndex(this.dimensionField); + const pruned = this.pruneAllData(xAxisIndex, dimIndex, dimensions); + const groupedByDim = group(flat(pruned), dimIndex); + options.series = groupedByDim._$groupItems + .map(dimName => ({data: getColumn(groupedByDim[dimName], this.indicators[0].index), name: dimName})) + .map((seriesData: EchartSeriesItem, index) => { + CommonUtils.extendObjects(seriesData, this.template.seriesItem); + const dim = this.dimensions.find(dim => dim.name == seriesData.name); + if (dim) { + Dimension.extend(seriesData, dim); + this._processSeriesData(index, seriesData, dim, options); + this._autoRange(seriesData, options); + } else { + // 数据自带的维度值 + this._setSeriesType(seriesData); + } + return seriesData; + }); + + return options; + } + + private _setSeriesType(seriesData: EchartSeriesItem) { + if (this.defaultSeriesType == 'area') { + seriesData.type = 'line'; + seriesData.areaStyle = {}; + return; + } + seriesData.type = this.defaultSeriesType; + } + + private _correctDoubleYAxis(dimOrKpi: Dimension | Indicator, options: EchartOptions) { + if (dimOrKpi.yAxisIndex == 1 && !(options.yAxis instanceof Array)) { + options.yAxis = [ + options.yAxis, + CommonUtils.extendObjects({}, this.yAxis2 ? this.yAxis2 : { + type: 'value', + axisLabel: { + formatter: '{value}' + } + }) + ] + } + } + + private _autoRange(seriesData: EchartSeriesItem, options: EchartOptions) { + const yAxisItem = options.yAxis instanceof Array ? options.yAxis[seriesData.yAxisIndex || 0] : options.yAxis; + if (!yAxisItem || !yAxisItem.autoRange) { + return; + } + ModeledRectangularGraphData.autoRange(seriesData.data, yAxisItem); + } + + /** + * 自动计算y坐标轴的最大最小值,并按照差值的10%来放大范围,这样可以避免在指标值差异很小时时,图形看起来像是一个直线的问题 + * @param data + * @param yAxisItem + */ + public static autoRange(data: number[], yAxisItem: EchartYAxis): void { + // 避免js直接加减后浮点数变成一长串的问题 + function _getRangeNum(num: number, delta: number): number { + const pointLength = String(num).split('.')[1]?.length || 1; + return Number((num + delta).toFixed(pointLength)); + } + + function _isNumber(num: any): boolean { + return CommonUtils.isDefined(num) && num !== '' && !isNaN(num) + } + + if (!data || isNaN(data[0]) || !yAxisItem) { + // 允许出现''或null的数据 + return; + } + let min = Math.min(...data), max = Math.max(...data); + if (min == max) { + return; + } + const range = (max - min) * 0.1; + min = _getRangeNum(min, -range); + max = _getRangeNum(max, range); + if (_isNumber(yAxisItem.min)) { + min = Math.min(min, yAxisItem.min); + } + if (_isNumber(yAxisItem.max)) { + max = Math.max(max, yAxisItem.max); + } + Object.assign(yAxisItem, {min, max}); + } + + /** + * 确保给定的数据中,每一个给定的维度,都有且只有一个记录,缺少的记录用默认值补齐,多出的记录删除, 重复的记录用聚集算法聚集, + * 并且要保证每组中的维度顺序一致。 + * + * @param xAxisIndex + * @param dimIndex + * @param dimensions + */ + protected pruneAllData(xAxisIndex: number, dimIndex: number, dimensions: Dimension[]): Grouped { + const groups = group(this.data, xAxisIndex); + for (let xAxisItem in groups) { + const records: string[][] = groups[xAxisItem]; + if (records === groups._$groupItems) { + continue; + } + const pruned = this.pruneData(records, dimIndex, dimensions, this.indicators); + pruned.forEach(row => row[xAxisIndex] = xAxisItem); + groups[xAxisItem] = pruned; + } + return groups; + } + + protected createMultiKPIOptions(dims: Dimension[]): EchartOptions { + const xAxisIndex = this.getIndex(this.xAxis.field); + if (xAxisIndex == -1) { + return undefined; + } + const dimIndex = this.getIndex(this.dimensionField); + const xAxisGroups = group(this.data, xAxisIndex); + if (!xAxisGroups) { + return undefined; + } + const pruned: string[][] = []; + for (let xAxisItem in xAxisGroups) { + const records: string[][] = xAxisGroups[xAxisItem]; + if (records === xAxisGroups._$groupItems) { + continue; + } + let filteredWithDim = dimIndex != xAxisIndex && dimIndex != -1 && dims && dims.length; + const prunedRecords = filteredWithDim ? records.filter(r => !!dims.find(dim => dim.name == r[dimIndex])) : records; + if (prunedRecords.length == 1) { + pruned.push(prunedRecords[0]); + } else if (prunedRecords.length > 1) { + const by = this.indicators.map(kpi => ({index: kpi.index, algorithm: kpi.aggregateBy})); + pruned.push(aggregate(prunedRecords, by)); + } else { + const row = []; + row[xAxisIndex] = xAxisItem; + if (filteredWithDim) { + row[dimIndex] = dims[0].name; + } + this.indicators.forEach(i => row[i.index] = i.defaultValue); + pruned.push(row); + } + } + + const options: EchartOptions = this.template.getInstance(); + options.xAxis = CommonUtils.extendObjects(options.xAxis, this.xAxis.style, {data: xAxisGroups._$groupItems}); + if (options.yAxis instanceof Array) { + options.yAxis = [ + CommonUtils.extendObjects(options.yAxis[0], this.yAxis1), + CommonUtils.extendObjects(options.yAxis[1], this.yAxis2) + ]; + } else { + options.yAxis = CommonUtils.extendObjects(options.yAxis, this.yAxis1); + } + + if (options.legend) { + options.legend.data = this.indicators.map(kpi => this.header[kpi.index]); + } + options.series = this.indicators + .map(kpi => ({name: this.header[kpi.index], field: this.field[kpi.index], data: getColumn(pruned, kpi.index)})) + .map((seriesData, index) => { + // 先取模板中的配置 + CommonUtils.extendObjects(seriesData, this.template.seriesItem); + // 再取用户配置 + const indicator = this.indicators.find(indicator => indicator.field == seriesData.field); + if (indicator) { + Indicator.extend(seriesData, indicator); + this._processSeriesData(index, seriesData, indicator, options); + } + return seriesData; + }); + return options; + } + + private _processSeriesData(index: number, seriesData: EchartSeriesItem, kpiOrDim: Indicator | Dimension, options: EchartOptions) { + if (this.series && this.series.length > 0) { + if (this.series[index]) { + delete this.series[index].name + Indicator.extend(seriesData, this.series[index]); + } else { + this.series[index] = JSON.parse(JSON.stringify(this.series[0])); + Indicator.extend(seriesData, this.series[index]); + this._setSeriesType(seriesData); + } + } + // 指标或维度里面设置了双坐标,而模板是单坐标的,需要转为双坐标,不然会报错 + this._correctDoubleYAxis(kpiOrDim, options); + } + + public refresh(): void { + this._options = undefined; + super.refresh(); + } +} diff --git a/src/jigsaw/common/core/data/graph-data/modeled-scatter.ts b/src/jigsaw/common/core/data/graph-data/modeled-scatter.ts new file mode 100644 index 0000000000..3bcb21afb1 --- /dev/null +++ b/src/jigsaw/common/core/data/graph-data/modeled-scatter.ts @@ -0,0 +1,129 @@ +// ------------------------------------------------------------------------------------------------ +// 散点图相关数据对象 +import {EchartLegend, EchartOptions, EchartSeriesItem, EchartXAxis, EchartYAxis} from "../echart-types"; +import {GraphDataField, GraphDataHeader, GraphDataMatrix} from "./graph-data"; +import {CommonUtils} from "../../utils/common-utils"; +import {AbstractModeledGraphData, CustomModeledGraphTemplate, Dimension, DimKpiBase, GraphType, Indicator} from "./modeled"; + +export class ScatterDimension extends Dimension { + public itemStyle?: any = {}; + public symbol?: string = ""; + + public static extend(seriesItem: EchartSeriesItem, dimension: ScatterDimension) { + const dimensionBak = CommonUtils.deepCopy(dimension); + delete dimensionBak.name; + Object.assign(seriesItem, dimensionBak); + } +} + +export const availableScatterSymbols: ('circle' | 'rect' | 'roundRect' | 'triangle' | 'diamond' | 'pin')[] = + ['circle', 'roundRect', 'triangle', 'diamond', 'pin', 'rect']; + +export class ModeledScatterGraphData extends AbstractModeledGraphData { + public type: GraphType = 'scatter'; + public template: CustomModeledGraphTemplate = new CustomModeledGraphTemplate(); + + public xAxis: EchartXAxis = {}; + public yAxis: EchartYAxis = {}; + public xAxisKpiField: { name: string, field: string }; + public yAxisKpiField: { name: string, field: string }; + public dimensionField: string; + public dimensions: ScatterDimension[] = []; + public usingAllDimensions: boolean = true; + public useDefaultBubble: boolean; + public useDefaultSingleColor: boolean; + public series: DimKpiBase[]; + + constructor(data: GraphDataMatrix = [], header: GraphDataHeader = [], field: GraphDataField = []) { + super(data, header, field); + } + + private _options: EchartOptions; + + get options(): EchartOptions { + if (!this._options) { + this._options = this.createChartOptions(); + } + return this._options; + } + + protected createChartOptions(): EchartOptions { + if (!this.xAxisKpiField || !this.yAxisKpiField) { + return undefined; + } + + let [xAxisKpiIndex, yAxisKpiIndex] = [this.getIndex(this.xAxisKpiField.field), this.getIndex(this.yAxisKpiField.field)]; + + if (xAxisKpiIndex == -1 || yAxisKpiIndex == -1) { + return undefined; + } + + const options = this.template.getInstance(); + + if (options.legend) { + options.legend.data = []; + } + + options.xAxis = CommonUtils.extendObject(options.xAxis, this.xAxis); + options.yAxis = CommonUtils.extendObject(options.yAxis, this.yAxis); + + const dimensions = this.getRealDimensions(this.dimensionField, this.dimensions, this.usingAllDimensions, ScatterDimension); + const dimIndex = this.getIndex(this.dimensionField); + this._mergeLegend(options.legend, dimensions); + + options.series = dimensions.map((dim, idx) => { + const fromData = this.dimensions.every(d => d.name != dim.name); + // 数据里过来的维度值自动加上默认散点样式 + if (fromData && this.useDefaultBubble) { + dim.itemStyle = dim.itemStyle || {}; + Object.assign(dim.itemStyle, {opacity: 0.3, borderWidth: 2}); + } + if (fromData && this.useDefaultSingleColor) { + dim.symbol = dim.symbol || ''; + Object.assign(dim.itemStyle, {color: '#3B69FF'}); + // 获取下一个要使用的 symbol 类型 + const nextSymbolIndex = idx % availableScatterSymbols.length; + // 更新 dim 对象中的 symbol 属性 + Object.assign(dim, {symbol: availableScatterSymbols[nextSymbolIndex]}); + } + if (this.series && this.series.length > 0) { + if (this.series[idx]) { + Object.assign(dim, this.series[idx]); + } else { + this.series[idx] = JSON.parse(JSON.stringify(this.series[0])); + Object.assign(dim, this.series[idx]); + if (this.useDefaultBubble) { + dim.itemStyle = {opacity: 0.3, borderWidth: 2}; + } + if (this.useDefaultSingleColor) { + dim.itemStyle = {color: '#3B69FF'}; + const nextSymbolIndex = idx % availableScatterSymbols.length; + Object.assign(dim, {symbol: availableScatterSymbols[nextSymbolIndex]}); + } + } + } + const seriesItem = CommonUtils.extendObjects({type: 'scatter'}, this.template.seriesItem); + seriesItem.data = this.data.filter(row => row[dimIndex] == dim.name) + .map(row => [row[xAxisKpiIndex], row[yAxisKpiIndex]]); + seriesItem.name = dim.name ? dim.name : 'series' + idx; + ScatterDimension.extend(seriesItem, dim); + return seriesItem; + }); + CommonUtils.extendObject(options, this.template.optionPatch); + return options; + } + + private _mergeLegend(legendObject: EchartLegend, candidates: (Indicator | Dimension)[]): void { + if (!legendObject) { + return; + } + const names = candidates.map(can => can.name); + legendObject.data.push(...names.filter(legend => legendObject.data.indexOf(legend) == -1)); + } + + + public refresh(): void { + this._options = undefined; + super.refresh(); + } +} diff --git a/src/jigsaw/common/core/data/graph-data/modeled.ts b/src/jigsaw/common/core/data/graph-data/modeled.ts new file mode 100644 index 0000000000..d0aa9b6963 --- /dev/null +++ b/src/jigsaw/common/core/data/graph-data/modeled.ts @@ -0,0 +1,178 @@ +import {Type} from "@angular/core"; +import {TableDataBase} from "../table-data"; +import {EchartOptions, EchartSeriesItem} from "../echart-types"; +import {GraphDataField, GraphDataHeader, GraphDataMatrix} from "./graph-data"; +import {aggregate, AggregateAlgorithm, distinct, group} from "../../utils/data-collection-utils"; +import {CommonUtils} from "../../utils/common-utils"; +import {getColumn} from "../unified-paging/paging"; + +export type GraphType = 'rectangular' | 'pie' | 'gauge' | 'radar' | 'scatter' | 'map' | 'funnel' | 'graph'; + +export abstract class AbstractModeledGraphData extends TableDataBase { + protected abstract createChartOptions(): EchartOptions; + + /** + * 图形的数据,二维数组。 + */ + public data: GraphDataMatrix; + /** + * 图形的列字段描述。 + */ + public header: GraphDataHeader; + /** + * 图形的列字段。 + */ + public field: GraphDataField; + /** + * 一个适合输入给 echarts 的参数,由本类的子类自动构建出来 + */ + public options: EchartOptions; + /** + * 图形个关键配置项的模板 + */ + public template: CustomModeledGraphTemplate; + + public type: GraphType; + + protected constructor(data: GraphDataMatrix = [], header: GraphDataHeader = [], field: GraphDataField = []) { + super(data, field, header); + this.data = data; + this.field = field; + this.header = header; + } + + public getIndex(field: string): number { + if (CommonUtils.isUndefined(field)) { + return -1; + } + let idx = this.field.indexOf(field); + return idx == -1 ? this.header.indexOf(field) : idx; + } + + protected getRealDimensions(dimField: string, dimensions: Dimension[], usingAllDimensions: boolean, DimType: Type = Dimension): Dimension[] { + const dims = []; + const dimIndex = this.getIndex(dimField); + if (dimIndex == -1) { + return dims; + } + if (usingAllDimensions) { + const series = distinct(getColumn(this.data, dimIndex)); + if (series) { + dims.push(...series.map(s => { + const d = dimensions.find(d => d.name === s); + return d ? d : new DimType(s); + })); + } + } else { + dims.push(...dimensions); + } + return dims; + } + + protected pruneData(records: (string | number)[][], dimIndex: number, dimensions: Dimension[], indicators: Indicator[]): string[][] { + const aggregateBy = indicators.map(kpi => ({index: kpi.index, algorithm: kpi.aggregateBy})); + const dimGroups = group(records, dimIndex); + const pruned: string[][] = []; + dimensions.forEach(dim => { + const dimRecords = dimGroups[dim.name]; + if (dimRecords) { + pruned.push(dimRecords.length > 1 ? aggregate(dimRecords, aggregateBy) : dimRecords[0]); + } else { + const row = []; + row[dimIndex] = dim.name; + indicators.forEach(i => row[i.index] = i.defaultValue); + pruned.push(row); + } + }); + return pruned; + } +} + +export class CustomModeledGraphTemplate { + public option: EchartOptions; + public optionPatch?: EchartOptions; + public seriesItem?: EchartSeriesItem; + + public getInstance():EchartOptions { + return CommonUtils.extendObjects({}, this.option) + } +} + +export class DimKpiBase { + public name?: string; + public stack?: string; + public color?: string; + public shade?: 'bar' | 'line' | 'area' = 'bar'; + public barWidth?: any; + public yAxisIndex?: 0 | 1 = 0; + public more?: any; + + public static extend(seriesItem: EchartSeriesItem, dimKpi: DimKpiBase) { + const dimKpiBak = CommonUtils.deepCopy(dimKpi); + if (dimKpiBak.shade == 'area') { + // 面积图 + seriesItem['type'] = 'line'; + seriesItem['areaStyle'] = {}; + } else { + seriesItem['type'] = dimKpiBak.shade; + } + delete dimKpiBak.shade; + this._deleteOtherProp(dimKpiBak); + Object.assign(seriesItem, dimKpiBak); + } + + protected static _deleteOtherProp(dimKpi: T) { + } +} + +export class Dimension extends DimKpiBase{ + constructor(name?: string) { + super(); + this.name = name; + } +} + +export class Indicator extends DimKpiBase { + public field?: string; + public index?: number = -1; + public defaultValue?: number = 0; + public aggregateBy?: AggregateAlgorithm = 'sum'; + + constructor(field?: string) { + super(); + this.field = field; + } + + protected static _deleteOtherProp(dimKpi: Indicator) { + delete dimKpi.field; + delete dimKpi.index; + delete dimKpi.defaultValue; + delete dimKpi.aggregateBy; + } +} + +export class SeriesBase { + public dimensionField: string; + public dimensions: Dimension[] = []; + public usingAllDimensions: boolean = true; + public indicators: Indicator[] = []; + public name?: string; + public more?: any; + public data?: any; + + constructor(name?: string) { + this.name = name; + } + + public static extend(seriesItem: EchartSeriesItem, seriesData: SeriesBase, index: number) { + const seriesDataBak = CommonUtils.deepCopy(seriesData); + delete seriesDataBak.dimensionField; + delete seriesDataBak.dimensions; + delete seriesDataBak.usingAllDimensions; + delete seriesDataBak.indicators; + delete seriesDataBak.more; + delete seriesDataBak.data; + Object.assign(seriesItem, seriesDataBak); + seriesItem.name = seriesItem.name ? seriesItem.name : 'series' + index; + } +} diff --git a/src/jigsaw/common/core/data/graph-data/normal-bubble.ts b/src/jigsaw/common/core/data/graph-data/normal-bubble.ts new file mode 100644 index 0000000000..ab70aeb563 --- /dev/null +++ b/src/jigsaw/common/core/data/graph-data/normal-bubble.ts @@ -0,0 +1,149 @@ +import {EchartOptions} from "../echart-types"; +import {AbstractNormalGraphData} from "./graph-data"; + +export type ColorStop = { + offset: number, + color: string +} +export type GradientColor = { + type: string, + x: number, + y: number, + r: number, + colorStops: ColorStop[], + global: boolean +} +export type BubbleItemStyle = { + borderWidth?: number, + borderType?: string, + borderColor?: string, + shadowBlur?: number, + shadowColor?: string, + shadowOffsetX?: number, + shadowOffsetY?: number, + color?: string | GradientColor +} +export type BubbleLabelConfig = { + color: string, + fontSize: number, + fontWeight: string, + fontStyle: string, + formatter?: string, + position?: string, + show: boolean +} +export type EmphasisConfig = { + itemStyle: BubbleItemStyle, + label?: BubbleLabelConfig +} + +/** + * 气泡图 + * */ +export class BubbleChartGraphData extends AbstractNormalGraphData { + // 气泡图形大小最小值 + public minSymbolSize: number = 130; + + // 最大气泡大小 + public symbolSize: number = 150; + + public layout: string = 'force'; + + public emphasisConfig: EmphasisConfig; + + protected createChartOptions(): EchartOptions { + if (!this.data || !this.data.length) { + return; + } + let maxValue = 1; + const valueList = this.data[0].map(item => item.value); + maxValue = Math.max(maxValue, ...valueList); + const minValue = Math.min(maxValue, ...valueList); + + const sizeScale = (this.symbolSize - this.minSymbolSize) / (maxValue - minValue); + const sizeOffset = this.minSymbolSize - sizeScale * minValue; + + // 斥力 为了防止重叠,斥力最好大于 symbolSize + const repulsion = this.symbolSize * 3; + + // 获取要渲染的数据 + const data = this.data[0].map((item) => { + // 根据与最大值的比例和最大气泡大小,算出每个元素的大小 + let size: number; + if (maxValue == minValue) { + size = this.symbolSize; + } else { + size = Math.max(sizeScale * item.value + sizeOffset, this.minSymbolSize); + } + const itemData = { + name: item.label, + value: item.value, + label: item.labelConfig || {}, + symbolSize: size, + itemStyle: item.itemStyle || {}, + emphasis: item.emphasisConfig || this.emphasisConfig + }; + if (!item.x && !item.y) { + return itemData; + } + this.layout = "none"; + return {...itemData, x: item.x, y: item.y}; + }); + + const emphasisConfig = this.emphasisConfig || {}; + + return { + xAxis: { + show: false, + }, + yAxis: { + show: false, + }, + series: [ + { + data, + type: "graph", // 关系图 + layout: this.layout, + draggable: true, // 启用节点拖拽 + force: { + // 值越大则斥力越大 每个元素间隔越大 + repulsion, + // 是否开启布局动画 + layoutAnimation: true, + // 元素之间的引力,越大引力越强默认是1 + gravity: 0.1, + // 即布局动画执行的时间。数值越大,动画执行的时间越长 + coolDown: 10 + }, + // 高亮状态的图形样式 + emphasis: emphasisConfig, + // 设置 label + label: { + show: true, + position: "inside", + formatter: [`{title|{b}}`, `{num|{c}}`].join("\n"), + rich: { + title: { + align: "center", + fontSize: 13, + lineHeight: 18, + color: "#FFF", + }, + num: { + align: "center", + fontSize: "15", + lineHeight: 21, + fontWeight: 500, + color: "#FFF", + }, + } + }, + itemStyle: { + borderWidth: 1, + color: "green", + }, + }, + ], + }; + } +} diff --git a/src/jigsaw/common/core/data/graph-data/normal-funnel-plot.ts b/src/jigsaw/common/core/data/graph-data/normal-funnel-plot.ts new file mode 100644 index 0000000000..7bfa05ac44 --- /dev/null +++ b/src/jigsaw/common/core/data/graph-data/normal-funnel-plot.ts @@ -0,0 +1,85 @@ +import {AbstractNormalGraphData} from "./graph-data"; + +/** + * 漏斗图 + */ +export class FunnelPlotGraphData extends AbstractNormalGraphData { + protected optionsTemplate = { + title: { + text: '', + left: 'left' + }, + tooltip: { + trigger: 'item', + formatter: "{a}
{b} : {c}%" + }, + toolbox: { + feature: { + dataView: {readOnly: false}, + restore: {}, + saveAsImage: {} + } + }, + legend: { + left: 'center', + data: [] + }, + calculable: true, + series: [ + { + name: '漏斗图', + type: 'funnel', + left: '10%', + top: 60, + //x2: 80, + bottom: 60, + width: '80%', + // height: {totalHeight} - y - y2, + min: 0, + max: 100, + minSize: '0%', + maxSize: '100%', + sort: 'descending', + gap: 2, + label: { + normal: { + show: true, + position: 'inside' + }, + emphasis: { + textStyle: { + fontSize: 20 + } + } + }, + labelLine: { + normal: { + length: 10, + lineStyle: { + width: 1, + type: 'solid' + } + } + }, + itemStyle: { + normal: { + borderColor: '#fff', + borderWidth: 1 + } + }, + data: [] + } + ] + }; + + protected createChartOptions(): any { + if (!this.data || !this.data.length) { + return; + } + const opt = {...this.optionsTemplate}; + this._extendOption(opt); + opt.legend.data = this.rowDescriptor.reverse(); + opt.series[0].data = this.data.map((row, i) => ({value: row[0], name: this.rowDescriptor[i]})); + return opt; + } +} diff --git a/src/jigsaw/common/core/data/graph-data/normal-line-bar-area.ts b/src/jigsaw/common/core/data/graph-data/normal-line-bar-area.ts new file mode 100644 index 0000000000..319e8f5ab8 --- /dev/null +++ b/src/jigsaw/common/core/data/graph-data/normal-line-bar-area.ts @@ -0,0 +1,545 @@ +import {EchartOptions} from "../echart-types"; +import {AbstractGraphData, AbstractNormalGraphData} from "./graph-data"; + +declare const echarts: any; + +/** + * 折线图 + */ +export class LineGraphData extends AbstractNormalGraphData { + + protected defaultType = 'line'; + + protected createSeries() { + return this.data.map((row, index) => { + return {name: this.header[index], type: this.defaultType, data: this.data.map(row => row[index])} + }); + } + + protected optionsTemplate: EchartOptions = { + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'cross', + label: { + backgroundColor: '#6a7985' + } + } + }, + legend: { + left: 'center', + data: [] + }, + toolbox: { + feature: { + saveAsImage: {} + } + }, + grid: { + left: '3%', + right: '4%', + bottom: '3%', + containLabel: true + }, + xAxis: [ + { + type: 'category', + boundaryGap: false, + data: [] + } + ], + yAxis: [ + { + type: 'value' + } + ], + series: [] + }; + + protected createChartOptions(): EchartOptions { + if (!this.data || !this.data.length) { + return; + } + const opt: EchartOptions = {...this.optionsTemplate}; + this._extendOption(opt); + opt.legend.data = this.header; + opt.xAxis[0].data = this.rowDescriptor; + opt.series = this.createSeries(); + return opt; + } +} + +/** + * 加这个类是为了保持向下兼容 + * @internal + */ +export class LineBarGraphData extends LineGraphData { +} + +export class LineGraphDataByRow extends LineGraphData { + protected createSeries() { + return this.data.map((row, index) => { + return {name: this.rowDescriptor[index], type: this.defaultType, data: row} + }); + } + + protected createChartOptions(): EchartOptions { + if (!this.data || !this.data.length) { + return; + } + const opt: EchartOptions = {...this.optionsTemplate}; + this._extendOption(opt); + opt.legend.data = this.rowDescriptor; + opt.xAxis[0].data = this.header; + opt.series = this.createSeries(); + return opt; + } +} + +/** + * 柱状图 + */ +export class BarGraphData extends LineGraphData { + protected defaultType = 'bar'; +} + +/** + * 柱状图(按行) + */ +export class BarGraphDataByRow extends LineGraphDataByRow { + protected defaultType = 'bar'; +} + +/** + * 条形图 + */ +export class StripGraphData extends AbstractNormalGraphData { + + protected getBrowserInfo() { + //只做了谷歌和火狐的兼容性 + let agent = navigator.userAgent.toLowerCase(); + if (agent.indexOf("firefox") > 0) { + return -10; + } else { + return -15; + } + } + + protected getGridRight() { + let gridRight = "" + this.data[0][0]; + return gridRight.length * 8 + } + + protected optionsTemplate: EchartOptions = { + grid: { + left: 100, + top: 60 + }, + tooltip: { + trigger: 'item', + axisPointer: {type: ''}, + formatter: function (params) { + return params[1].name + '
' + + params[1].seriesName + ' : ' + params[1].value + } + }, + xAxis: [ + { + type: 'value', + splitNumber: 4, + splitLine: {show: false}, + position: 'bottom', + max: "dataMax", + axisLabel: { + textStyle: { + color: '#bbbbbb' + } + }, + axisTick: {//坐标轴刻度相关设置 + show: true, + inside: false, + color: '#ddd', + length: 3,//刻度长短设置 + lineStyle: { + color: '#ddd', + } + }, + axisLine: { + show: true, + lineStyle: { + color: '#ddd', + width: 1 + } + } + } + ], + yAxis: [ + { + + splitLine: {show: false}, + data: [], + boundaryGap: [0.01, 0.01], + axisLabel: { + textStyle: { + color: '#666' + } + }, + axisLine: { + show: true, + lineStyle: { + color: '#ddd', + width: 1 + } + }, + axisTick: {//坐标轴刻度相关设置 + show: true, + length: 3,//刻度长短设置 + lineStyle: { + color: '#54acd5', + } + } + } + ] + }; + + protected createChartOptions(): any { + if (!this.data || !this.data.length) { + return; + } + const options = {...this.optionsTemplate}; + this._extendOption(options); + options.grid.right = this.getGridRight(); + options.yAxis[0].data = this.header; + options.series = [ + { + type: 'bar', + barGap: '-100%', + silent: true, + itemStyle: { + normal: { + barBorderColor: '#54acd5', + opacity: 0.2, + color: '#54acd5', + barBorderRadius: 5 + } + }, + barWidth: 10, + data: this.data[0] + }, + { + animation: true, + type: 'bar', + label: { + normal: { + show: true, + position: ['100%', this.getBrowserInfo()], + textStyle: { + fontSize: 12, + color: "#54acd5" + } + } + }, + barGap: '-100%', + itemStyle: { + normal: { + barBorderColor: '#54acd5', + color: '#54acd5', + barBorderRadius: 5 + } + }, + barWidth: 10, + tooltip: { + trigger: 'axis', + }, + data: this.data[1] + } + ]; + return options; + } +} + +/** + * 条形时序图 + */ +export class StripSequenceGraphData extends StripGraphData { + protected createChartOptions(): any { + if (!this.data || !this.data.length) { + return; + } + const options = {...this.optionsTemplate}; + this._extendOption(options); + options.grid.right = this.getGridRight(); + options.yAxis[0].type = 'category'; + options.yAxis[0].data = this.header; + options.series = [ + { + type: 'bar', + barGap: '-100%', + silent: true, + itemStyle: { + normal: { + barBorderColor: '#54acd5', + opacity: 0.2, + color: '#54acd5', + barBorderRadius: 5 + } + }, + barWidth: 10, + data: this.data[0] + }, + { + type: 'bar', + stack: '总量', + barGap: '-100%', + silent: true, + itemStyle: { + normal: { + barBorderColor: 'rgba(0,0,0,0)', + color: 'rgba(0,0,0,0)', + barBorderRadius: 5, + textStyle: { + align: 'right' + } + } + }, + barWidth: 10, + data: this.data[1] + }, + { + type: 'bar', + stack: '总量', + barGap: '-100%', + silent: true, + animation: true, + label: { + normal: { + show: true, + position: ['100%', this.getBrowserInfo()], + textStyle: { + color: "#54acd5" + } + } + }, + itemStyle: { + normal: { + barBorderColor: '#54acd5', + color: '#54acd5', + barBorderRadius: 5 + } + }, + barWidth: 10, + data: this.data[2] + } + ]; + return options; + } +} + +/** + * 条形色值图 + */ +export class StripColorGraphData extends AbstractNormalGraphData { + protected optionsTemplate: EchartOptions = { + title: { + text: '', + left: "center", + top: 20, + textStyle: { + color: '#434343', + fontSize: 12 + } + }, + calculable: true, + grid: { + left: 90, + right: 60, + top: 60 + }, + xAxis: [ + { + type: 'value', + splitNumber: 4, + axisLine: { + show: false + }, + splitLine: {//出网格线 + show: false + }, + axisLabel: { + show: false + } + } + ], + yAxis: [ + { + splitLine: { + show: false + }, + boundaryGap: true, + type: 'category', + scale: false, + axisLabel: { + textStyle: { + color: '#434343'//刻度标签样式 + } + + }, + axisLine: { + show: false + }, + axisTick: {//坐标轴刻度相关设置 + show: false + }, + data: [] + } + ], + series: [ + { + name: 'bar', + type: 'bar', + stack: "总量", + silent: true, + animation: false,//关闭动漫 + barWidth: '10px', + data: [] + + }, + { + name: 'bar6', + type: 'bar', + stack: "总量", + silent: true, + animation: false,//关闭动漫 + label: {//图形数据显示位置 + normal: { + show: true, position: ['100%', -5], + textStyle: { + color: "#585858" + }, + formatter: function (params) { + return " " + (100 - params.value) + } + }, + }, + itemStyle: {//图形边框设置,如边框大小,圆角,填充着色 + normal: { + color: "#dedede" + } + }, + barWidth: '10px',//条形宽度 + data: [] + } + + ] + }; + + protected createSeries(): any[][] { + return [ + this.data[0].map(v => { + return { + value: v, + itemStyle: { + normal: { + color: v > 95 ? "#98e2a6" : v > 90 ? "#9ad0e2" : v > 80 ? "#f7e685" : v > 70 ? "#f6c88a" : "#ff8e74" + } + } + } + }), + this.data[0].map(v => 100 - v) + ] + } + + protected createChartOptions(): EchartOptions { + if (!this.data || !this.data.length) { + return; + } + const opt = {...this.optionsTemplate}; + this._extendOption(opt); + opt.yAxis[0].data = this.header; + [opt.series[0].data, opt.series[1].data] = this.createSeries(); + return opt; + } +} + +/** + * 堆叠区域图 + */ +export class StackedAreaGraphData extends AbstractGraphData { + protected createChartOptions(): any { + if (!this.data || !this.data.length) { + return; + } + + return { + xAxis: [ + { + type: 'category', + boundaryGap: false, + alignWithLabel: true, + axisLabel: { // 类轴刻度间隔 + interval: 4 + }, + axisTick: { //坐标轴刻度相关设置 + show: true, + inside: true, + interval: 0, + length: 5,//刻度长短设置 + lineStyle: { + color: '#bbb', + } + }, + splitLine: {//网格线相关设置 + interval: 0,//类目轴为true且为这个为0时才会显示 + lineStyle: { + color: "#eee" + } + }, + data: this.header + } + ], + yAxis: [ + { + type: 'value', + max: 3, + axisTick: { + show: false, + }, + axisLine: {//轴线设置 + lineStyle: { + color: '#ccc' + } + }, + min: 0, + axisLabel: { + formatter: function (params) { + return params.toFixed(1) == "0.0" ? "" : params.toFixed(1) + "%" + } + } + } + ], + series: [ + { + type: 'line', + connectNulls: true, + smooth: true, + symbolSize: [5, 5], + showAllSymbol: true, + areaStyle: { + normal: { + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { + offset: 0, color: '#ff7c24' // 0% 处的颜色 + }, + { + offset: 1, color: '#fff' // 100% 处的颜色 + } + ], false) + } + }, + data: this.data[0], + } + ] + }; + + } +} diff --git a/src/jigsaw/common/core/data/graph-data/normal-others.ts b/src/jigsaw/common/core/data/graph-data/normal-others.ts new file mode 100644 index 0000000000..c9c49f5727 --- /dev/null +++ b/src/jigsaw/common/core/data/graph-data/normal-others.ts @@ -0,0 +1,281 @@ +import {EchartOptions} from "../echart-types"; +import {AbstractGraphData, AbstractNormalGraphData} from "./graph-data"; + +/** + * K线图 + */ +export class KLineGraphData extends AbstractNormalGraphData { + protected createSeries(): any[] { + return [{ + name: 'k data', + type: 'k', + showAllSymbol: true, + animation: true, + smooth: false, + symbolSize: [5, 5], + hoverAnimation: false, + data: this.data + }]; + } + + public sampleColors = ["#54acd5", "#f99660", "#a4bf6a", "#ec6d6d", "#f7b913", "#8ac9b6", "#bea5c8", "#01c5c2", "#a17660"]; + public vmaxColors = ['#41addc', '#bea5c8', '#85c56c', '#f99660', '#ffc20e', '#ec6d6d', '#8ac9b6', '#585eaa', '#b22c46', '#96582a']; + + protected optionsTemplate = { + color: this.vmaxColors, + tooltip: { + trigger: 'axis', + position: function (point) {// 固定在顶部 + return [point[0] + 10, point[1]]; + } + }, + grid: { + left: 45, + right: 45, + top: 60 + }, + calculable: true, + /*当某天无数据不补零,同时不连线的效果实现*/ + /* + *以x轴为类目轴为例:如下 + *坐标轴 刻度标签 设显示间隔:xAxis.axisLabel.interval根据具体情况设置标签显示间隔; + *坐标轴 刻度 的显示间隔:xAxis.axisTick.interval设置全部显示为0; + *标志图形全部显示:series.showAllSymbol设置为true, + * */ + xAxis: [ + { + type: 'category', + boundaryGap: false, + axisLabel: {//标签设置 + interval: 4 + }, + splitLine: {//设置网格 + interval: 0 + }, + scale: true, + data: [] + } + ], + yAxis: [ + { + type: 'value', + splitNumber: 10,//不是类轴才会生效,设置网格多少 + axisLabel: {//标签设置 + interval: 4 + } + } + ], + series: [] + }; + + protected createChartOptions(): EchartOptions { + if (!this.data || !this.data.length) { + return; + } + const opt = {...this.optionsTemplate}; + this._extendOption(opt); + opt.xAxis[0].data = this.header; + opt.series = this.createSeries(); + return opt; + } +} + +/** + * 箱线图 + */ +export class BoxPlotGraphData extends AbstractGraphData { + public title: string; + + protected createChartOptions(): any { + if (!this.data || !this.data.length) { + return; + } + + return { + title: [ + { + text: this.title, + left: 'center', + }, + { + text: 'upper: Q3 + 1.5 * IRQ \nlower: Q1 - 1.5 * IRQ', + borderColor: '#999', + borderWidth: 1, + textStyle: { + fontSize: 14 + }, + left: '10%', + top: '90%' + } + ], + tooltip: { + trigger: 'item', + axisPointer: { + type: 'shadow' + } + }, + grid: { + left: '10%', + right: '10%', + bottom: '15%' + }, + xAxis: { + type: 'category', + data: this.data.map((row, i) => i), + boundaryGap: true, + nameGap: 30, + splitArea: { + show: false + }, + axisLabel: { + formatter: 'expr {value}' + }, + splitLine: { + show: false + } + }, + yAxis: { + type: 'value', + name: 'km/s minus 299,000', + splitArea: { + show: true + } + }, + series: [ + { + name: 'boxplot', + type: 'boxplot', + data: this.data, + tooltip: { + formatter: function (param) { + return [ + 'Experiment ' + param.name + ': ', + 'upper: ' + param.data[5], + 'Q3: ' + param.data[4], + 'median: ' + param.data[3], + 'Q1: ' + param.data[2], + 'lower: ' + param.data[1] + ].join('
') + } + } + }, + /*{ + name: 'outlier', + type: 'scatter', + data: data.outliers + }*/ + ] + }; + } +} + +/** + * 热力图 + */ +export class HeatGraphData extends AbstractNormalGraphData { + + protected createChartOptions(): any { + if (!this.data || !this.data.length) { + return; + } + return { + tooltip: { + position: 'top' + }, + animation: false, + grid: { + height: '50%', + y: '10%' + }, + xAxis: { + type: 'category', + data: this.data[this.data.length - 2], + splitArea: { + show: true + } + }, + yAxis: { + type: 'category', + data: this.data[this.data.length - 1], + splitArea: { + show: true + } + }, + visualMap: { + min: 0, + max: 10, + calculable: true, + orient: 'horizontal', + left: 'center', + bottom: '15%' + }, + series: [{ + name: 'Punch Card', + type: 'heatmap', + data: this.data.slice(0, this.data.length - 2), + label: { + normal: { + show: true + } + }, + itemStyle: { + emphasis: { + shadowBlur: 10, + shadowColor: 'rgba(0, 0, 0, 0.5)' + } + } + }] + }; + } +} + +/** + * 关系图 + */ +export class RelationalGraphData extends AbstractGraphData { + public data: any[]; + + public title: string; + + protected createChartOptions(): any { + if (!this.data || !this.data.length) { + return; + } + return { + title: { + text: this.title + }, + tooltip: {}, + xAxis: { + type: 'category', + boundaryGap: false, + data: this.data[this.data.length - 2] + }, + yAxis: { + type: 'value' + }, + series: [ + { + type: 'graph', + layout: 'none', + coordinateSystem: 'cartesian2d', + symbolSize: 40, + label: { + normal: { + show: true + } + }, + edgeSymbol: ['circle', 'arrow'], + edgeSymbolSize: [4, 10], + data: this.data[0], + links: this.data[this.data.length - 1], + lineStyle: { + normal: { + color: '#2f4554' + } + } + } + ] + }; + } +} diff --git a/src/jigsaw/common/core/data/graph-data/pie-doughnut.ts b/src/jigsaw/common/core/data/graph-data/normal-pie-doughnut.ts similarity index 100% rename from src/jigsaw/common/core/data/graph-data/pie-doughnut.ts rename to src/jigsaw/common/core/data/graph-data/normal-pie-doughnut.ts diff --git a/src/jigsaw/common/core/data/modeled-graph-data.ts b/src/jigsaw/common/core/data/modeled-graph-data.ts deleted file mode 100644 index 5d07fec403..0000000000 --- a/src/jigsaw/common/core/data/modeled-graph-data.ts +++ /dev/null @@ -1,1341 +0,0 @@ -import { Type } from "@angular/core"; -import {TableDataBase} from "./table-data"; -import { - EchartLegend, - EchartOptions, - EchartSeriesItem, - EchartTitle, - EchartXAxis, - EchartYAxis -} from "./echart-types"; -import {GraphDataField, GraphDataHeader, GraphDataMatrix} from "./graph-data/graph-data"; -import {aggregate, AggregateAlgorithm, distinct, flat, group, Grouped} from "../utils/data-collection-utils"; -import {CommonUtils} from "../utils/common-utils"; -import {getColumn} from "./unified-paging/paging"; -import { JigsawThemeService } from "../theming/theme"; - -export type GraphType = 'rectangular' | 'pie' | 'gauge' | 'radar' | 'scatter' | 'map' | 'funnel' | 'graph'; - -export abstract class AbstractModeledGraphData extends TableDataBase { - protected abstract createChartOptions(): EchartOptions; - - /** - * 图形的数据,二维数组。 - */ - public data: GraphDataMatrix; - /** - * 图形的列字段描述。 - */ - public header: GraphDataHeader; - /** - * 图形的列字段。 - */ - public field: GraphDataField; - /** - * 一个适合输入给 echarts 的参数,由本类的子类自动构建出来 - */ - public options: EchartOptions; - /** - * 图形个关键配置项的模板 - */ - public template: CustomModeledGraphTemplate; - - public type: GraphType; - - protected constructor(data: GraphDataMatrix = [], header: GraphDataHeader = [], field: GraphDataField = []) { - super(data, field, header); - this.data = data; - this.field = field; - this.header = header; - } - - public getIndex(field: string): number { - if (CommonUtils.isUndefined(field)) { - return -1; - } - let idx = this.field.indexOf(field); - return idx == -1 ? this.header.indexOf(field) : idx; - } - - protected getRealDimensions(dimField: string, dimensions: Dimension[], usingAllDimensions: boolean, DimType: Type = Dimension): Dimension[] { - const dims = []; - const dimIndex = this.getIndex(dimField); - if (dimIndex == -1) { - return dims; - } - if (usingAllDimensions) { - const series = distinct(getColumn(this.data, dimIndex)); - if (series) { - dims.push(...series.map(s => { - const d = dimensions.find(d => d.name === s); - return d ? d : new DimType(s); - })); - } - } else { - dims.push(...dimensions); - } - return dims; - } - - protected pruneData(records: (string | number)[][], dimIndex: number, dimensions: Dimension[], indicators: Indicator[]): string[][] { - const aggregateBy = indicators.map(kpi => ({index: kpi.index, algorithm: kpi.aggregateBy})); - const dimGroups = group(records, dimIndex); - const pruned: string[][] = []; - dimensions.forEach(dim => { - const dimRecords = dimGroups[dim.name]; - if (dimRecords) { - pruned.push(dimRecords.length > 1 ? aggregate(dimRecords, aggregateBy) : dimRecords[0]); - } else { - const row = []; - row[dimIndex] = dim.name; - indicators.forEach(i => row[i.index] = i.defaultValue); - pruned.push(row); - } - }); - return pruned; - } -} - -export class CustomModeledGraphTemplate { - public option: EchartOptions; - public optionPatch?: EchartOptions; - public seriesItem?: EchartSeriesItem; - - public getInstance():EchartOptions { - return CommonUtils.extendObjects({}, this.option) - } -} - -class DimKpiBase { - public name?: string; - public stack?: string; - public color?: string; - public shade?: 'bar' | 'line' | 'area' = 'bar'; - public barWidth?: any; - public yAxisIndex?: 0 | 1 = 0; - public more?: any; - - public static extend(seriesItem: EchartSeriesItem, dimKpi: DimKpiBase) { - const dimKpiBak = CommonUtils.deepCopy(dimKpi); - if (dimKpiBak.shade == 'area') { - // 面积图 - seriesItem['type'] = 'line'; - seriesItem['areaStyle'] = {}; - } else { - seriesItem['type'] = dimKpiBak.shade; - } - delete dimKpiBak.shade; - this._deleteOtherProp(dimKpiBak); - Object.assign(seriesItem, dimKpiBak); - } - - protected static _deleteOtherProp(dimKpi: T) { - } -} - -export class Dimension extends DimKpiBase{ - constructor(name?: string) { - super(); - this.name = name; - } -} - -export class Indicator extends DimKpiBase { - public field?: string; - public index?: number = -1; - public defaultValue?: number = 0; - public aggregateBy?: AggregateAlgorithm = 'sum'; - - constructor(field?: string) { - super(); - this.field = field; - } - - protected static _deleteOtherProp(dimKpi: Indicator) { - delete dimKpi.field; - delete dimKpi.index; - delete dimKpi.defaultValue; - delete dimKpi.aggregateBy; - } -} - -export class SeriesBase { - public dimensionField: string; - public dimensions: Dimension[] = []; - public usingAllDimensions: boolean = true; - public indicators: Indicator[] = []; - public name?: string; - public more?: any; - public data?: any; - - constructor(name?: string) { - this.name = name; - } - - public static extend(seriesItem: EchartSeriesItem, seriesData: SeriesBase, index: number) { - const seriesDataBak = CommonUtils.deepCopy(seriesData); - delete seriesDataBak.dimensionField; - delete seriesDataBak.dimensions; - delete seriesDataBak.usingAllDimensions; - delete seriesDataBak.indicators; - delete seriesDataBak.more; - delete seriesDataBak.data; - Object.assign(seriesItem, seriesDataBak); - seriesItem.name = seriesItem.name ? seriesItem.name : 'series' + index; - } -} - -export type RectangularSeriesType = 'bar' | 'line' | 'area'; -// ------------------------------------------------------------------------------------------------ -// 直角系图相关数据对象 -export class ModeledRectangularGraphData extends AbstractModeledGraphData { - public type: GraphType = 'rectangular'; - public defaultSeriesType: RectangularSeriesType = 'bar'; - public template: CustomModeledGraphTemplate = new CustomModeledGraphTemplate(); - - public xAxis: { field?: string, style?: EchartXAxis } = {}; - public yAxis1: EchartYAxis = {position: 'left'}; - public yAxis2: EchartYAxis = {position: 'right'}; - public dimensionField: string; - public dimensions: Dimension[] = []; - public usingAllDimensions: boolean = true; - public indicators: Indicator[] = []; - public series: DimKpiBase[]; - public legendSource: 'dim' | 'kpi'; - - constructor(data: GraphDataMatrix = [], header: GraphDataHeader = [], field: GraphDataField = []) { - super(data, header, field); - } - - private _options: EchartOptions; - - get options(): EchartOptions { - if (!this._options) { - this._options = this.createChartOptions(); - } - return this._options; - } - - protected createChartOptions(): EchartOptions { - if (!this.xAxis || !this.xAxis.field) { - return undefined; - } - if (!this.indicators || this.indicators.length == 0) { - return undefined; - } - - if (this.legendSource == 'dim') { - // 维度值作为图例却没有配置维度就返回 - if (!this.dimensionField) { - return undefined; - } - if (!this.usingAllDimensions && (!this.dimensions || this.dimensions.length == 0)) { - return undefined; - } - } - - this.indicators.forEach(kpi => kpi.index = this.getIndex(kpi.field)); - const dimensions = this.getRealDimensions(this.dimensionField, this.dimensions, this.usingAllDimensions); - let options = this.legendSource == 'dim' ? this.createMultiDimensionOptions(dimensions) : - this.createMultiKPIOptions(dimensions); - CommonUtils.extendObject(options, this.template.optionPatch); - return options; - } - - /** - * 单指标多维度 - */ - protected createMultiDimensionOptions(dimensions: Dimension[]): EchartOptions { - if (dimensions.length == 0) { - return undefined; - } - const xAxisIndex = this.getIndex(this.xAxis.field); - if (xAxisIndex == -1) { - return undefined; - } - const xAxisItems = distinct(getColumn(this.data, xAxisIndex)); - if (!xAxisItems || xAxisItems.length == 0) { - return undefined - } - - const options: EchartOptions = this.template.getInstance(); - options.xAxis = CommonUtils.extendObjects(options.xAxis, this.xAxis.style, {data: xAxisItems}); - if(options.yAxis instanceof Array) { - options.yAxis = [ - CommonUtils.extendObjects(options.yAxis[0], this.yAxis1), - CommonUtils.extendObjects(options.yAxis[1], this.yAxis2) - ]; - } else { - options.yAxis = CommonUtils.extendObjects(options.yAxis, this.yAxis1); - } - - if (options.legend) { - options.legend.data = dimensions.map(d => d.name); - } - - const dimIndex = this.getIndex(this.dimensionField); - const pruned = this.pruneAllData(xAxisIndex, dimIndex, dimensions); - const groupedByDim = group(flat(pruned), dimIndex); - options.series = groupedByDim._$groupItems - .map(dimName => ({data: getColumn(groupedByDim[dimName], this.indicators[0].index), name: dimName})) - .map((seriesData: EchartSeriesItem, index) => { - CommonUtils.extendObjects(seriesData, this.template.seriesItem); - const dim = this.dimensions.find(dim => dim.name == seriesData.name); - if (dim) { - Dimension.extend(seriesData, dim); - this._processSeriesData(index, seriesData, dim, options); - this._autoRange(seriesData, options); - } else { - // 数据自带的维度值 - this._setSeriesType(seriesData); - } - return seriesData; - }); - - return options; - } - - private _setSeriesType(seriesData: EchartSeriesItem) { - if (this.defaultSeriesType == 'area') { - seriesData.type = 'line'; - seriesData.areaStyle = {}; - return; - } - seriesData.type = this.defaultSeriesType; - } - - private _correctDoubleYAxis(dimOrKpi: Dimension | Indicator, options: EchartOptions) { - if(dimOrKpi.yAxisIndex == 1 && !(options.yAxis instanceof Array)) { - options.yAxis = [ - options.yAxis, - CommonUtils.extendObjects({}, this.yAxis2 ? this.yAxis2 : { - type: 'value', - axisLabel: { - formatter: '{value}' - } - }) - ] - } - } - - private _autoRange(seriesData: EchartSeriesItem, options: EchartOptions) { - const yAxisItem = options.yAxis instanceof Array ? options.yAxis[seriesData.yAxisIndex || 0] : options.yAxis; - if (!yAxisItem || !yAxisItem.autoRange) { - return; - } - ModeledRectangularGraphData.autoRange(seriesData.data, yAxisItem); - } - - /** - * 自动计算y坐标轴的最大最小值,并按照差值的10%来放大范围,这样可以避免在指标值差异很小时时,图形看起来像是一个直线的问题 - * @param data - * @param yAxisItem - */ - public static autoRange(data: number[], yAxisItem: EchartYAxis): void { - // 避免js直接加减后浮点数变成一长串的问题 - function _getRangeNum(num: number, delta: number): number { - const pointLength = String(num).split('.')[1]?.length || 1; - return Number((num + delta).toFixed(pointLength)); - } - - function _isNumber(num: any): boolean { - return CommonUtils.isDefined(num) && num !== '' && !isNaN(num) - } - - if (!data || isNaN(data[0]) || !yAxisItem) { - // 允许出现''或null的数据 - return; - } - let min = Math.min(...data), max = Math.max(...data); - if (min == max) { - return; - } - const range = (max - min) * 0.1; - min = _getRangeNum(min, -range); - max = _getRangeNum(max, range); - if (_isNumber(yAxisItem.min)) { - min = Math.min(min, yAxisItem.min); - } - if (_isNumber(yAxisItem.max)) { - max = Math.max(max, yAxisItem.max); - } - Object.assign(yAxisItem, {min, max}); - } - - /** - * 确保给定的数据中,每一个给定的维度,都有且只有一个记录,缺少的记录用默认值补齐,多出的记录删除, 重复的记录用聚集算法聚集, - * 并且要保证每组中的维度顺序一致。 - * - * @param xAxisIndex - * @param dimIndex - * @param dimensions - */ - protected pruneAllData(xAxisIndex: number, dimIndex: number, dimensions: Dimension[]): Grouped { - const groups = group(this.data, xAxisIndex); - for (let xAxisItem in groups) { - const records: string[][] = groups[xAxisItem]; - if (records === groups._$groupItems) { - continue; - } - const pruned = this.pruneData(records, dimIndex, dimensions, this.indicators); - pruned.forEach(row => row[xAxisIndex] = xAxisItem); - groups[xAxisItem] = pruned; - } - return groups; - } - - protected createMultiKPIOptions(dims: Dimension[]): EchartOptions { - const xAxisIndex = this.getIndex(this.xAxis.field); - if (xAxisIndex == -1) { - return undefined; - } - const dimIndex = this.getIndex(this.dimensionField); - const xAxisGroups = group(this.data, xAxisIndex); - if(!xAxisGroups) { - return undefined; - } - const pruned: string[][] = []; - for (let xAxisItem in xAxisGroups) { - const records: string[][] = xAxisGroups[xAxisItem]; - if (records === xAxisGroups._$groupItems) { - continue; - } - let filteredWithDim = dimIndex != xAxisIndex && dimIndex != -1 && dims && dims.length; - const prunedRecords = filteredWithDim ? records.filter(r => !!dims.find(dim => dim.name == r[dimIndex])) : records; - if (prunedRecords.length == 1) { - pruned.push(prunedRecords[0]); - } else if (prunedRecords.length > 1) { - const by = this.indicators.map(kpi => ({index: kpi.index, algorithm: kpi.aggregateBy})); - pruned.push(aggregate(prunedRecords, by)); - } else { - const row = []; - row[xAxisIndex] = xAxisItem; - if(filteredWithDim) { - row[dimIndex] = dims[0].name; - } - this.indicators.forEach(i => row[i.index] = i.defaultValue); - pruned.push(row); - } - } - - const options: EchartOptions = this.template.getInstance(); - options.xAxis = CommonUtils.extendObjects(options.xAxis, this.xAxis.style, {data: xAxisGroups._$groupItems}); - if(options.yAxis instanceof Array) { - options.yAxis = [ - CommonUtils.extendObjects(options.yAxis[0], this.yAxis1), - CommonUtils.extendObjects(options.yAxis[1], this.yAxis2) - ]; - } else { - options.yAxis = CommonUtils.extendObjects(options.yAxis, this.yAxis1); - } - - if (options.legend) { - options.legend.data = this.indicators.map(kpi => this.header[kpi.index]); - } - options.series = this.indicators - .map(kpi => ({name: this.header[kpi.index], field: this.field[kpi.index], data: getColumn(pruned, kpi.index)})) - .map((seriesData,index) => { - // 先取模板中的配置 - CommonUtils.extendObjects(seriesData, this.template.seriesItem); - // 再取用户配置 - const indicator = this.indicators.find(indicator => indicator.field == seriesData.field); - if (indicator) { - Indicator.extend(seriesData, indicator); - this._processSeriesData(index, seriesData, indicator, options); - } - return seriesData; - }); - return options; - } - - private _processSeriesData(index: number, seriesData: EchartSeriesItem, kpiOrDim: Indicator | Dimension, options: EchartOptions) { - if (this.series && this.series.length > 0) { - if (this.series[index]) { - delete this.series[index].name - Indicator.extend(seriesData, this.series[index]); - } else { - this.series[index] = JSON.parse(JSON.stringify(this.series[0])); - Indicator.extend(seriesData, this.series[index]); - this._setSeriesType(seriesData); - } - } - // 指标或维度里面设置了双坐标,而模板是单坐标的,需要转为双坐标,不然会报错 - this._correctDoubleYAxis(kpiOrDim, options); - } - - public refresh(): void { - this._options = undefined; - super.refresh(); - } -} - -// ------------------------------------------------------------------------------------------------ -// 饼图相关数据对象 - -export class PieSeries extends SeriesBase { - public radius: number[]; - public center: number[]; - public roseType?: boolean | 'radius' | 'area'; - public label?: any; -} - -export class ModeledPieGraphData extends AbstractModeledGraphData { - constructor(data: GraphDataMatrix = [], header: GraphDataHeader = [], field: GraphDataField = []) { - super(data, header, field); - } - - public type: GraphType = 'pie'; - public template: CustomModeledGraphTemplate = new CustomModeledGraphTemplate(); - public series: PieSeries[]; - private _options: EchartOptions; - public legendSource: 'dim' | 'kpi'; - - get options(): EchartOptions { - if (!this._options) { - this._options = this.createChartOptions(); - } - return this._options; - } - - protected createChartOptions(): EchartOptions { - if (!this.series) { - return undefined; - } - const options = this.template.getInstance(); - if (options.legend) { - options.legend.data = []; - } - options.series = this.series - .filter(seriesData => { - if (!seriesData.dimensionField) { - return false; - } - if (!seriesData.indicators || seriesData.indicators.length == 0) { - return false; - } - return seriesData.usingAllDimensions || (seriesData.dimensions && seriesData.dimensions.length > 0); - }) - .map((seriesData, idx) => { - const dimensions = this.getRealDimensions(seriesData.dimensionField, seriesData.dimensions, seriesData.usingAllDimensions); - const dimIndex = this.getIndex(seriesData.dimensionField); - seriesData.indicators.forEach(kpi => kpi.index = this.getIndex(kpi.field)); - seriesData.indicators.forEach(kpi => kpi.name = kpi.name ? kpi.name : this.header[kpi.index]); - - const seriesItem = CommonUtils.extendObjects({type: 'pie'}, this.template.seriesItem); - const legendSource = this.legendSource ? this.legendSource : dimensions.length > 1 ? 'dim' : 'kpi'; - if (dimensions.length == 0) { - console.warn('No valid dimension found, this graph will not be rendered!'); - } else if (legendSource == 'dim') { - // 多维度 - this._mergeLegend(options.legend, dimensions); - let records; - if (seriesData.usingAllDimensions) { - records = this.data; - } else { - records = this.data.filter(row => dimensions.find(d => d.name == row[dimIndex])); - } - const kpiIndex = seriesData.indicators[0].index; - records = records.map(row => [row[dimIndex], row[kpiIndex]]); - const indicator: Indicator = CommonUtils.deepCopy(seriesData.indicators[0]); - indicator.index = 1; - seriesItem.data = this.pruneData(records, 0, dimensions, [indicator]) - .map(row => ({name: row[0], value: row[1]})); - } else { - // 多指标 - this._mergeLegend(options.legend, seriesData.indicators); - const dim = dimensions[0].name; - const records = this.data.filter(row => row[dimIndex] == dim); - const pruned = this.pruneData(records, dimIndex, dimensions, seriesData.indicators)[0]; - seriesItem.data = seriesData.indicators.map(i => ({name: i.name, value: pruned[i.index]})); - } - - SeriesBase.extend(seriesItem, seriesData, idx); - seriesItem.radius = seriesData.radius ? seriesItem.radius.map(r => r + '%') : seriesData.radius; - seriesItem.center = seriesItem.center ? seriesItem.center.map(r => r + '%') : seriesItem.center; - return seriesItem; - }); - CommonUtils.extendObject(options, this.template.optionPatch); - return options; - } - - private _mergeLegend(legendObject: EchartLegend, candidates: (Indicator | Dimension)[]): void { - if (!legendObject) { - return; - } - const names = candidates.map(can => can.name); - legendObject.data.push(...names.filter(legend => legendObject.data.indexOf(legend) == -1)); - } - - public refresh(): void { - this._options = undefined; - super.refresh(); - } -} - - -// ------------------------------------------------------------------------------------------------ -// 仪表盘相关数据对象 - -export class GaugeSeries extends SeriesBase { - public dimensions: Dimension[] = [new Dimension('')]; - public usingAllDimensions: boolean = false; - - public center?: number[] = [50, 50]; - public radius?: number = 90; - public startAngle?: number = 225; - public endAngle?: number = -45; - public min?: number = 0; - public max?: number = 100; - public detail?: any = {formatter: '{value}%'}; - - public splitNumber?: number; - public axisLine?: any; - public axisTick?: any; - public axisLabel?: any; - public splitLine?: any; - public pointer?: any; - public itemStyle?: any; - public title?: any; - public autoAxisLineColor?: boolean; - public slope?: number; -} - -export class ModeledGaugeGraphData extends AbstractModeledGraphData { - constructor(data: GraphDataMatrix = [], header: GraphDataHeader = [], field: GraphDataField = []) { - super(data, header, field); - } - - public type: GraphType = 'gauge'; - public template: CustomModeledGraphTemplate = new CustomModeledGraphTemplate(); - public series: GaugeSeries[]; - public title: EchartTitle; - private _options: EchartOptions; - - get options(): EchartOptions { - if (!this._options) { - this._options = this.createChartOptions(); - } - return this._options; - } - - protected createChartOptions(): EchartOptions { - if (!this.series || !this.field.length || !this.header.length || !this.data.length) { - return undefined; - } - const options = this.template.getInstance(); - options.series = this.series - .filter(seriesData => { - if (!seriesData.dimensionField) { - return false; - } - if (!seriesData.indicators || seriesData.indicators.length == 0) { - return false; - } - return seriesData.usingAllDimensions || (seriesData.dimensions && seriesData.dimensions.length > 0); - }) - .map((seriesData, idx) => { - const dimensions = this.getRealDimensions(seriesData.dimensionField, seriesData.dimensions, seriesData.usingAllDimensions); - const dimIndex = this.getIndex(seriesData.dimensionField); - seriesData.indicators.forEach(kpi => kpi.index = this.getIndex(kpi.field)); - seriesData.indicators.forEach(kpi => kpi.name = kpi.name ? kpi.name : this.header[kpi.index]); - - const seriesItem = CommonUtils.extendObjects({type: 'gauge'}, this.template.seriesItem, seriesData); - - if (dimensions.length > 1) { - // 多维度 - let records; - if (seriesData.usingAllDimensions) { - records = this.data; - } else { - records = this.data.filter(row => dimensions.find(d => d.name == row[dimIndex])); - } - const kpiIndex = seriesData.indicators[0].index; - records = records.map(row => [row[dimIndex], row[kpiIndex]]); - const indicator: Indicator = CommonUtils.deepCopy(seriesData.indicators[0]); - indicator.index = 1; - seriesItem.data = this.aggregateData(records, [indicator]) - .map(row => ({name: indicator.name, value: row[1]})); - } else { - // 多指标 - const dim = dimensions[0].name; - const records = this.data.filter(row => row[dimIndex] == dim); - const pruned = this.aggregateData(records, seriesData.indicators)[0]; - seriesItem.data = seriesData.indicators.map(i => ({name: i.name, value: pruned[i.index]})); - } - - SeriesBase.extend(seriesItem, seriesData, idx); - seriesItem.radius = seriesData.radius ? seriesData.radius + '%' : seriesData.radius; - seriesItem.center = seriesData.center ? seriesData.center.map(r => r + '%') : seriesData.center; - - if (seriesItem.autoAxisLineColor && seriesItem.axisLine?.lineStyle?.color?.[0]?.length && seriesItem.data?.[0]) { - seriesItem.axisLine.lineStyle.color[0][0] = Number(seriesItem.data[0].value) / (Number(seriesItem.max) || 100); - } - - return seriesItem; - }); - CommonUtils.extendObject(options, this.template.optionPatch); - return options; - } - - protected aggregateData(records: (string|number)[][], indicators: Indicator[]): string[][] { - const aggregateBy = indicators.map(kpi => ({index: kpi.index, algorithm: kpi.aggregateBy})); - return [aggregate(records, aggregateBy)]; - } - - public refresh(): void { - this._options = undefined; - super.refresh(); - } -} - -// ------------------------------------------------------------------------------------------------ -// 雷达图相关数据对象 - -export class RadarSeries { - public name: string; - public type: string = 'radar'; - public data: any[]; - public areaStyle: any; - - constructor(name?: string) { - this.name = name; - } -} - -export class RadarItem { - public indicator: RadarIndicator[] = []; - public radius: any; - public center: any[]; -} - -export class RadarIndicator extends Indicator { - public max?: number; - public min?: number; - public color?: string; - public static extend(indicatorItem: EchartSeriesItem, indicator: RadarIndicator) { - const indicatorBak = CommonUtils.deepCopy(indicator); - indicatorItem.min = indicator.min ? indicator.min : 0; - delete indicatorBak.min; - delete indicatorBak.field; - delete indicatorBak.defaultValue; - delete indicatorBak.aggregateBy; - Object.assign(indicatorItem, indicatorBak); - } -} - -export class RadarDimension extends Dimension { - public area: boolean; // 是否填充 - - public static extend(seriesItem: EchartSeriesItem, dimension: RadarDimension) { - const dimensionBak = CommonUtils.deepCopy(dimension); - seriesItem.areaStyle = dimension.area ? {} : null; - delete dimensionBak.area; - Object.assign(seriesItem, dimensionBak); - } -} - -export class ModeledRadarGraphData extends AbstractModeledGraphData { - public type: GraphType = 'radar'; - public template: CustomModeledGraphTemplate = new CustomModeledGraphTemplate(); - - public dimensionField: string; - public usingAllDimensions: boolean = true; - public dimensions: RadarDimension[] = []; - public indicators: RadarIndicator[] = []; - public isDefaultFillBackground: boolean; - - constructor(data: GraphDataMatrix = [], header: GraphDataHeader = [], field: GraphDataField = []) { - super(data, header, field); - } - - private _options: EchartOptions; - - get options(): EchartOptions { - if (!this._options) { - this._options = this.createChartOptions(); - } - return this._options; - } - - protected createChartOptions(): EchartOptions { - if (!this.dimensionField) { - return undefined; - } - if (!this.indicators || this.indicators.length == 0) { - return undefined; - } - if (!this.usingAllDimensions && (!this.dimensions || this.dimensions.length == 0)) { - return undefined; - } - const dimIndex = this.getIndex(this.dimensionField); - if (dimIndex == -1) { - return undefined; - } - - const options = this.template.getInstance(); - - this.indicators.forEach(kpi => kpi.index = this.getIndex(kpi.field)); - const dimensions = this.getRealDimensions(this.dimensionField, this.dimensions, this.usingAllDimensions); - if (options.legend) { - options.legend.data = dimensions.map(d => d.name); - } - let indicator = this.indicators.map((indicator: RadarIndicator) => { - const indicatorItem = {} - RadarIndicator.extend(indicatorItem, indicator); - return indicatorItem; - }); - options.radar = CommonUtils.extendObject(options.radar, {indicator: indicator}); - - let series = CommonUtils.extendObjects({type: 'radar'}, this.template.seriesItem); - series.data = dimensions.map((dimension: RadarDimension) => { - const fromData = this.dimensions.every(dim => dim.name != dimension.name); - // 数据里过来的维度值自动加上默认填充背景 - dimension.area = fromData ? this.isDefaultFillBackground : dimension.area; - const records = this.data.filter(row => row[dimIndex] == dimension.name); - const pruned = this.pruneData(records, dimIndex, [dimension], this.indicators)[0]; - const seriesItem = { - value: this.indicators.map(i => pruned[i.index]) - } - RadarDimension.extend(seriesItem, dimension); - return seriesItem; - }); - options.series = [series]; - CommonUtils.extendObject(options, this.template.optionPatch); - return options; - } - - public refresh(): void { - this._options = undefined; - super.refresh(); - } -} - - -// ------------------------------------------------------------------------------------------------ -// 散点图相关数据对象 -export class ScatterDimension extends Dimension { - public itemStyle?: any = {}; - public symbol?: string = ""; - public static extend(seriesItem: EchartSeriesItem, dimension: ScatterDimension) { - const dimensionBak = CommonUtils.deepCopy(dimension); - delete dimensionBak.name; - Object.assign(seriesItem, dimensionBak); - } -} - -export const availableScatterSymbols: ('circle' | 'rect' | 'roundRect' | 'triangle' | 'diamond' | 'pin')[] = - ['circle', 'roundRect', 'triangle', 'diamond', 'pin', 'rect']; - -export class ModeledScatterGraphData extends AbstractModeledGraphData { - public type: GraphType = 'scatter'; - public template: CustomModeledGraphTemplate = new CustomModeledGraphTemplate(); - - public xAxis: EchartXAxis = {}; - public yAxis: EchartYAxis = {}; - public xAxisKpiField: {name: string, field: string}; - public yAxisKpiField: {name: string, field: string}; - public dimensionField: string; - public dimensions: ScatterDimension[] = []; - public usingAllDimensions: boolean = true; - public useDefaultBubble: boolean; - public useDefaultSingleColor: boolean; - public series: DimKpiBase[]; - - constructor(data: GraphDataMatrix = [], header: GraphDataHeader = [], field: GraphDataField = []) { - super(data, header, field); - } - - private _options: EchartOptions; - - get options(): EchartOptions { - if (!this._options) { - this._options = this.createChartOptions(); - } - return this._options; - } - - protected createChartOptions(): EchartOptions { - if (!this.xAxisKpiField || !this.yAxisKpiField) { - return undefined; - } - - let [xAxisKpiIndex, yAxisKpiIndex] = [this.getIndex(this.xAxisKpiField.field), this.getIndex(this.yAxisKpiField.field)]; - - if(xAxisKpiIndex == -1 || yAxisKpiIndex == -1) { - return undefined; - } - - const options = this.template.getInstance(); - - if (options.legend) { - options.legend.data = []; - } - - options.xAxis = CommonUtils.extendObject(options.xAxis, this.xAxis); - options.yAxis = CommonUtils.extendObject(options.yAxis, this.yAxis); - - const dimensions = this.getRealDimensions(this.dimensionField, this.dimensions, this.usingAllDimensions, ScatterDimension); - const dimIndex = this.getIndex(this.dimensionField); - this._mergeLegend(options.legend, dimensions); - - options.series = dimensions.map((dim, idx) => { - const fromData = this.dimensions.every(d => d.name != dim.name); - // 数据里过来的维度值自动加上默认散点样式 - if (fromData && this.useDefaultBubble) { - dim.itemStyle = dim.itemStyle || {}; - Object.assign(dim.itemStyle, {opacity: 0.3, borderWidth: 2}); - } - if (fromData && this.useDefaultSingleColor) { - dim.symbol = dim.symbol || ''; - Object.assign(dim.itemStyle, {color: '#3B69FF'}); - // 获取下一个要使用的 symbol 类型 - const nextSymbolIndex = idx % availableScatterSymbols.length; - // 更新 dim 对象中的 symbol 属性 - Object.assign(dim, {symbol: availableScatterSymbols[nextSymbolIndex]}); - } - if (this.series && this.series.length > 0) { - if (this.series[idx]) { - Object.assign(dim, this.series[idx]); - } else { - this.series[idx] = JSON.parse(JSON.stringify(this.series[0])); - Object.assign(dim, this.series[idx]); - if (this.useDefaultBubble) { - dim.itemStyle = {opacity: 0.3, borderWidth: 2}; - } - if (this.useDefaultSingleColor) { - dim.itemStyle = {color: '#3B69FF'}; - const nextSymbolIndex = idx % availableScatterSymbols.length; - Object.assign(dim, {symbol: availableScatterSymbols[nextSymbolIndex]}); - } - } - } - const seriesItem = CommonUtils.extendObjects({type: 'scatter'}, this.template.seriesItem); - seriesItem.data = this.data.filter(row => row[dimIndex] == dim.name) - .map(row => [row[xAxisKpiIndex], row[yAxisKpiIndex]]); - seriesItem.name = dim.name ? dim.name : 'series' + idx; - ScatterDimension.extend(seriesItem, dim); - return seriesItem; - }); - CommonUtils.extendObject(options, this.template.optionPatch); - return options; - } - - private _mergeLegend(legendObject: EchartLegend, candidates: (Indicator | Dimension)[]): void { - if (!legendObject) { - return; - } - const names = candidates.map(can => can.name); - legendObject.data.push(...names.filter(legend => legendObject.data.indexOf(legend) == -1)); - } - - - public refresh(): void { - this._options = undefined; - super.refresh(); - } -} - -// ------------------------------------------------------------------------------------------------ -// 轮廓图相关数据对象 -export class MapSeries extends SeriesBase { - public mapType?: string = ''; - public label?: string; - public itemStyle?: MapItemStyle; - public animation?: string; - public roam?: boolean; -} - -export type MapVisualMap = { - position?: string; - type?: string; - startText?: string; - endText?: string; - text?: string[]; - min?: number; - max?: number; - colorConfig?: string; - color?: string[]; - textStyle?: MapVisualMapTextStyle; - more?: any; -} - -export type MapVisualMapTextStyle = { - color?: string; - fontStyle?: string; - fontWeight?: string; - fontSize?: number; -} - -export type MapItemStyle = { - areaColor?: string; - borderColor?: string; - borderWidth?: number; - borderType?: string; - shadowBlur?: number; - shadowColor?: string; - shadowOffsetX?: number; - shadowOffsetY?: number; - opacity?: number; -} - -export class ModeledMapGraphData extends AbstractModeledGraphData { - constructor(data: GraphDataMatrix = [], header: GraphDataHeader = [], field: GraphDataField = []) { - super(data, header, field); - } - - public type: GraphType = 'map'; - public template: CustomModeledGraphTemplate = new CustomModeledGraphTemplate(); - public series: MapSeries[]; - public visualMap: MapVisualMap; - private _options: EchartOptions; - - get options(): EchartOptions { - if (!this._options) { - this._options = this.createChartOptions(); - } - return this._options; - } - - protected createChartOptions(): EchartOptions { - if (!this.series) { - return undefined; - } - const options = this.template.getInstance(); - - options.series = this.series - .filter(seriesData => { - if (!seriesData.dimensionField) { - return false; - } - if (!seriesData.indicators || seriesData.indicators.length == 0) { - return false; - } - return seriesData.usingAllDimensions || (seriesData.dimensions && seriesData.dimensions.length > 0); - }) - .map((seriesData, idx) => { - const dimensions = this.getRealDimensions(seriesData.dimensionField, seriesData.dimensions, seriesData.usingAllDimensions); - const dimIndex = this.getIndex(seriesData.dimensionField); - seriesData.indicators.forEach(kpi => kpi.index = this.getIndex(kpi.field)); - seriesData.indicators.forEach(kpi => kpi.name = kpi.name ? kpi.name : this.header[kpi.index]); - - const seriesItem = CommonUtils.extendObjects({type: 'map'}, this.template.seriesItem); - if(!dimensions.length) return {}; - - // 多维度单指标 - let records; - if (seriesData.usingAllDimensions) { - records = this.data; - } else { - records = this.data.filter(row => dimensions.find(d => d.name == row[dimIndex])); - } - const kpiIndex = seriesData.indicators[0].index; - records = records.map(row => [row[dimIndex], row[kpiIndex]]); - const indicator: Indicator = CommonUtils.deepCopy(seriesData.indicators[0]); - indicator.index = 1; - seriesItem.data = this.pruneData(records, 0, dimensions, [indicator]) - .map(row => ({name: row[0], value: row[1]})); - - SeriesBase.extend(seriesItem, seriesData, idx); - return seriesItem; - }); - if (options.visualMap?.colorConfig) { - options.visualMap.color = JigsawThemeService.getGraphTheme().chartColorConfigs[options.visualMap.colorConfig]; - } - CommonUtils.extendObject(options, this.template.optionPatch); - return options; - } - - public refresh(): void { - this._options = undefined; - super.refresh(); - } -} - -// ------------------------------------------------------------------------------------------------ -// 漏斗图相关数据对象 -export type FunnelItemStyle = { - borderColor?: string; - borderWidth?: number; - borderType?: string; - shadowBlur?: number; - shadowColor?: string; - shadowOffsetX?: number; - shadowOffsetY?: number; - opacity?: number; -} - -export type FunnelLabel = { - show?: boolean; - position?: string; - color?: string; - fontStyle?: string; - fontWeight?: string; - fontSize?: number; -} - -export type FunnelLabelLine = { - show?: boolean; - lineStyle?: FunnelLineStyle; -} - -export type FunnelLineStyle = { - color?: string; - width?: number; - type?: string; -} - -export class FunnelSeries extends SeriesBase { - public sort?: 'ascending' | 'descending' | 'none'; - public gap?: number; - public orient?: 'vertical' | 'horizontal'; - public funnelAlign?: 'left' | 'right' | 'center'; - public label?: FunnelLabel; - public itemStyle?: FunnelItemStyle; - public labelLine?: FunnelLabelLine; - public width?: string; - public height?: string; - public min?: number; - public max?: number; - public top?: string; - public left?: string; - public colorConfig?: string; - public color?: string[]; -} - -export class ModeledFunnelGraphData extends AbstractModeledGraphData { - constructor(data: GraphDataMatrix = [], header: GraphDataHeader = [], field: GraphDataField = []) { - super(data, header, field); - } - - public type: GraphType = 'funnel'; - public template: CustomModeledGraphTemplate = new CustomModeledGraphTemplate(); - public series: FunnelSeries[]; - public legendSource: 'dim' | 'kpi'; - - private _options: EchartOptions; - - get options(): EchartOptions { - if (!this._options) { - this._options = this.createChartOptions(); - } - return this._options; - } - private _mergeLegend(legendObject: EchartLegend, candidates: (Indicator | Dimension)[]): void { - if (!legendObject) { - return; - } - const names = candidates.map(can => can.name); - legendObject.data.push(...names.filter(legend => legendObject.data.indexOf(legend) == -1)); - } - - protected createChartOptions(): EchartOptions { - if (!this.series || !this.header.length || !this.data.length) { - return undefined; - } - - const options = this.template.getInstance(); - if (options.legend) { - options.legend.data = []; - } - - CommonUtils.extendObject(options, this.template.optionPatch); - options.series = this.series - .filter(seriesData => { - if (!seriesData.dimensionField) { - return false; - } - if (!seriesData.indicators || seriesData.indicators.length == 0) { - return false; - } - return seriesData.usingAllDimensions || (seriesData.dimensions && seriesData.dimensions.length > 0); - }) - .map((seriesData, idx) => { - const dimensions = this.getRealDimensions(seriesData.dimensionField, seriesData.dimensions, seriesData.usingAllDimensions); - const dimIndex = this.getIndex(seriesData.dimensionField); - seriesData.indicators.forEach(kpi => kpi.index = this.getIndex(kpi.field)); - seriesData.indicators.forEach(kpi => kpi.name = kpi.name ? kpi.name : this.header[kpi.index]); - - const seriesItem = CommonUtils.extendObjects({ type: 'funnel' }, this.template.seriesItem); - const legendSource = this.legendSource ? this.legendSource : dimensions.length > 1 ? 'dim' : 'kpi'; - if (dimensions.length == 0) { - console.warn('No valid dimension found, this graph will not be rendered!'); - } else if (legendSource == 'dim') { - // 多维度 - this._mergeLegend(options.legend, dimensions); - let records; - if (seriesData.usingAllDimensions) { - records = this.data; - } else { - records = this.data.filter(row => dimensions.find(d => d.name == row[dimIndex])); - } - const kpiIndex = seriesData.indicators[0].index; - records = records.map(row => [row[dimIndex], row[kpiIndex]]); - const indicator: Indicator = CommonUtils.deepCopy(seriesData.indicators[0]); - indicator.index = 1; - seriesItem.data = this.pruneData(records, 0, dimensions, [indicator]) - .map(row => ({ name: row[0], value: row[1] })); - } else { - // 多指标 - this._mergeLegend(options.legend, seriesData.indicators); - const dim = dimensions[0].name; - const records = this.data.filter(row => row[dimIndex] == dim); - const pruned = this.pruneData(records, dimIndex, dimensions, seriesData.indicators)[0]; - seriesItem.data = seriesData.indicators.map(i => ({ name: i.name, value: pruned[i.index] })); - } - - if (seriesData.colorConfig) { - seriesData.color = JigsawThemeService.getGraphTheme().chartColorConfigs[seriesData.colorConfig]; - } - SeriesBase.extend(seriesItem, seriesData, idx); - return seriesItem; - }); - - - - return options; - } - - public refresh(): void { - this._options = undefined; - super.refresh(); - } -} - -// ------------------------------------------------------------------------------------------------ -// 气泡图相关数据对象 -export type ColorStop = { - offset: number, - color: string -} - -export type GradientColor = { - type: string, - x: number, - y: number, - r: number, - colorStops: ColorStop[], - global: boolean -} - -export type BubbleItemStyle = { - borderWidth?: number, - borderType?: string, - borderColor?: string, - shadowBlur?: number, - shadowColor?: string, - shadowOffsetX?: number, - shadowOffsetY?: number, - color?: string | GradientColor -} - -export type BubbleLabelConfig = { - color: string, - fontSize: number, - fontWeight: string, - fontStyle: string, - formatter?: string, - position?: string, - show: boolean -} - -export type BubbleSeries = { - label: string, - value: number, - itemStyle: BubbleItemStyle, - labelConfig: BubbleLabelConfig, - x: number, - y: number -} - -export type EmphasisConfig = { - itemStyle: BubbleItemStyle, - label?: BubbleLabelConfig -} -// 气泡图 -export class ModeledBubbleGraphData extends AbstractModeledGraphData { - constructor(data: GraphDataMatrix = [], header: GraphDataHeader = [], field: GraphDataField = []) { - super(data, header, field); - } - - // 此气泡图使用的是echarts的关系图,type类型为graph - public type: GraphType = 'graph'; - public template: CustomModeledGraphTemplate = new CustomModeledGraphTemplate(); - - public xAxis: EchartXAxis = {}; - public yAxis: EchartYAxis = {}; - public minSymbolSize: number; - public maxSymbolSize: number; - public layout: string = 'force'; - public series: DimKpiBase[]; - public emphasisConfig: EmphasisConfig; - - private _options: EchartOptions; - - get options(): EchartOptions { - if (!this._options) { - this._options = this.createChartOptions(); - } - return this._options; - } - - protected createChartOptions(): EchartOptions { - if (!this.minSymbolSize || !this.maxSymbolSize) { - return undefined; - } - const options = this.template.getInstance(); - options.xAxis = this.xAxis; - options.yAxis = this.yAxis; - const data = this._handleData(); - const emphasisConfig = this.emphasisConfig || {}; - - // 斥力 为了防止重叠,斥力最好大于 maxSymbolSize - const repulsion = this.maxSymbolSize * 3; - options.series = [ - { - data, - type: this.type, - layout: this.layout, - draggable: true, - force: { - repulsion, - }, - emphasis: emphasisConfig - } - ] - CommonUtils.extendObject(options, this.template.optionPatch); - return options; - } - - private _handleData() { - if (!this.data || !this.data.length) { - return; - } - let maxValue = 1; - const valueList = this.data.map(item => item["value"]); - maxValue = Math.max(maxValue, ...valueList); - const minValue = Math.min(maxValue, ...valueList); - - const sizeScale = (this.maxSymbolSize - this.minSymbolSize) / (maxValue - minValue); - const sizeOffset = this.minSymbolSize - sizeScale * minValue; - - // 获取要渲染的数据 - return this.data.map((item) => { - // 根据与最大值的比例和最大气泡大小,算出每个元素的大小 - let size = Math.max(sizeScale * Number(item["value"]) + sizeOffset, this.minSymbolSize); - - const itemData = { - name: item["label"], - value: item["value"], - label: item["labelConfig"] || {}, - symbolSize: size, - itemStyle: item["itemStyle"] || {}, - emphasis: item["emphasisConfig"] || this.emphasisConfig - }; - if (!item["x"] && !item["y"]) { - return itemData; - } - this.layout = "none"; - return {...itemData, x: item["x"], y: item["y"]}; - }); - } -} diff --git a/src/jigsaw/mobile_public_api.ts b/src/jigsaw/mobile_public_api.ts index 412f0de9e7..48730eb695 100644 --- a/src/jigsaw/mobile_public_api.ts +++ b/src/jigsaw/mobile_public_api.ts @@ -10,7 +10,7 @@ export * from "./common/components/scrollbar/index"; export * from "./common/core/data/array-collection"; export * from "./common/core/data/component-data"; export * from "./common/core/data/echart-types"; -export * from "./common/core/data/modeled-graph-data"; +export * from "./common/core/data/graph-data/modeled"; export * from "./common/core/data/general-collection"; export * from "./common/core/data/graph-data/graph-data"; export * from "./common/core/data/table-data"; @@ -54,9 +54,55 @@ export * from "./mobile-components/switch/index"; export * from "./mobile-components/tabs/index"; export * from "./mobile-components/tag/tag"; export * from "./mobile-components/header/header"; -export {DoughnutScoreGraphData} from "./common/core/data/graph-data/pie-doughnut"; -export {DoughnutRateGraphData} from "./common/core/data/graph-data/pie-doughnut"; -export {DoughnutGraphData} from "./common/core/data/graph-data/pie-doughnut"; -export {PieGraphDataByRow} from "./common/core/data/graph-data/pie-doughnut"; -export {PieGraphData} from "./common/core/data/graph-data/pie-doughnut"; +export {DoughnutScoreGraphData} from "./common/core/data/graph-data/normal-pie-doughnut"; +export {DoughnutRateGraphData} from "./common/core/data/graph-data/normal-pie-doughnut"; +export {DoughnutGraphData} from "./common/core/data/graph-data/normal-pie-doughnut"; +export {PieGraphDataByRow} from "./common/core/data/graph-data/normal-pie-doughnut"; +export {PieGraphData} from "./common/core/data/graph-data/normal-pie-doughnut"; +export {StackedAreaGraphData} from "./common/core/data/graph-data/normal-line-bar-area"; +export {StripColorGraphData} from "./common/core/data/graph-data/normal-line-bar-area"; +export {StripSequenceGraphData} from "./common/core/data/graph-data/normal-line-bar-area"; +export {StripGraphData} from "./common/core/data/graph-data/normal-line-bar-area"; +export {BarGraphDataByRow} from "./common/core/data/graph-data/normal-line-bar-area"; +export {BarGraphData} from "./common/core/data/graph-data/normal-line-bar-area"; +export {LineGraphDataByRow} from "./common/core/data/graph-data/normal-line-bar-area"; +export {LineBarGraphData} from "./common/core/data/graph-data/normal-line-bar-area"; +export {LineGraphData} from "./common/core/data/graph-data/normal-line-bar-area"; +export {RelationalGraphData} from "./common/core/data/graph-data/normal-others"; +export {HeatGraphData} from "./common/core/data/graph-data/normal-others"; +export {BoxPlotGraphData} from "./common/core/data/graph-data/normal-others"; +export {KLineGraphData} from "./common/core/data/graph-data/normal-others"; +export {ModeledRectangularGraphData} from "./common/core/data/graph-data/modeled-rectangular"; +export {RectangularSeriesType} from "./common/core/data/graph-data/modeled-rectangular"; +export {ModeledPieGraphData} from "./common/core/data/graph-data/modeled-pie"; +export {PieSeries} from "./common/core/data/graph-data/modeled-pie"; +export {ModeledGaugeGraphData} from "./common/core/data/graph-data/modeled-gauge"; +export {GaugeSeries} from "./common/core/data/graph-data/modeled-gauge"; +export {ModeledRadarGraphData} from "./common/core/data/graph-data/modeled-radar"; +export {RadarDimension} from "./common/core/data/graph-data/modeled-radar"; +export {RadarIndicator} from "./common/core/data/graph-data/modeled-radar"; +export {RadarItem} from "./common/core/data/graph-data/modeled-radar"; +export {RadarSeries} from "./common/core/data/graph-data/modeled-radar"; +export {ModeledScatterGraphData} from "./common/core/data/graph-data/modeled-scatter"; +export {availableScatterSymbols} from "./common/core/data/graph-data/modeled-scatter"; +export {ScatterDimension} from "./common/core/data/graph-data/modeled-scatter"; +export {ModeledMapGraphData} from "./common/core/data/graph-data/modeled-map"; +export {MapItemStyle} from "./common/core/data/graph-data/modeled-map"; +export {MapVisualMapTextStyle} from "./common/core/data/graph-data/modeled-map"; +export {MapVisualMap} from "./common/core/data/graph-data/modeled-map"; +export {MapSeries} from "./common/core/data/graph-data/modeled-map"; +export {ModeledFunnelGraphData} from "./common/core/data/graph-data/modeled-funnel"; +export {FunnelSeries} from "./common/core/data/graph-data/modeled-funnel"; +export {FunnelLineStyle} from "./common/core/data/graph-data/modeled-funnel"; +export {FunnelLabelLine} from "./common/core/data/graph-data/modeled-funnel"; +export {FunnelLabel} from "./common/core/data/graph-data/modeled-funnel"; +export {FunnelItemStyle} from "./common/core/data/graph-data/modeled-funnel"; +export {ModeledBubbleGraphData} from "./common/core/data/graph-data/modeled-bubble"; +export {FunnelPlotGraphData} from "./common/core/data/graph-data/normal-funnel-plot"; +export {BubbleChartGraphData} from "./common/core/data/graph-data/normal-bubble"; +export {EmphasisConfig} from "./common/core/data/graph-data/normal-bubble"; +export {BubbleLabelConfig} from "./common/core/data/graph-data/normal-bubble"; +export {BubbleItemStyle} from "./common/core/data/graph-data/normal-bubble"; +export {GradientColor} from "./common/core/data/graph-data/normal-bubble"; +export {ColorStop} from "./common/core/data/graph-data/normal-bubble"; diff --git a/src/jigsaw/public_api.ts b/src/jigsaw/public_api.ts index 86cc2ffabc..428312f5f5 100644 --- a/src/jigsaw/public_api.ts +++ b/src/jigsaw/public_api.ts @@ -11,7 +11,7 @@ export * from "./common/components/scrollbar/index"; export * from "./common/core/data/array-collection"; export * from "./common/core/data/component-data"; export * from "./common/core/data/echart-types"; -export * from "./common/core/data/modeled-graph-data"; +export * from "./common/core/data/graph-data/modeled"; export * from "./common/core/data/general-collection"; export * from "./common/core/data/graph-data/graph-data"; export * from "./common/core/data/table-data"; @@ -102,8 +102,54 @@ export * from "./pc-components/form-display/form-display" /* fallback components */ export * from "./pc-components/fallback/upload/index"; -export {DoughnutScoreGraphData} from "./common/core/data/graph-data/pie-doughnut"; -export {DoughnutRateGraphData} from "./common/core/data/graph-data/pie-doughnut"; -export {DoughnutGraphData} from "./common/core/data/graph-data/pie-doughnut"; -export {PieGraphDataByRow} from "./common/core/data/graph-data/pie-doughnut"; -export {PieGraphData} from "./common/core/data/graph-data/pie-doughnut"; +export {DoughnutScoreGraphData} from "./common/core/data/graph-data/normal-pie-doughnut"; +export {DoughnutRateGraphData} from "./common/core/data/graph-data/normal-pie-doughnut"; +export {DoughnutGraphData} from "./common/core/data/graph-data/normal-pie-doughnut"; +export {PieGraphDataByRow} from "./common/core/data/graph-data/normal-pie-doughnut"; +export {PieGraphData} from "./common/core/data/graph-data/normal-pie-doughnut"; +export {StackedAreaGraphData} from "./common/core/data/graph-data/normal-line-bar-area"; +export {StripColorGraphData} from "./common/core/data/graph-data/normal-line-bar-area"; +export {StripSequenceGraphData} from "./common/core/data/graph-data/normal-line-bar-area"; +export {StripGraphData} from "./common/core/data/graph-data/normal-line-bar-area"; +export {BarGraphDataByRow} from "./common/core/data/graph-data/normal-line-bar-area"; +export {BarGraphData} from "./common/core/data/graph-data/normal-line-bar-area"; +export {LineGraphDataByRow} from "./common/core/data/graph-data/normal-line-bar-area"; +export {LineBarGraphData} from "./common/core/data/graph-data/normal-line-bar-area"; +export {LineGraphData} from "./common/core/data/graph-data/normal-line-bar-area"; +export {RelationalGraphData} from "./common/core/data/graph-data/normal-others"; +export {HeatGraphData} from "./common/core/data/graph-data/normal-others"; +export {BoxPlotGraphData} from "./common/core/data/graph-data/normal-others"; +export {KLineGraphData} from "./common/core/data/graph-data/normal-others"; +export {ModeledRectangularGraphData} from "./common/core/data/graph-data/modeled-rectangular"; +export {RectangularSeriesType} from "./common/core/data/graph-data/modeled-rectangular"; +export {ModeledPieGraphData} from "./common/core/data/graph-data/modeled-pie"; +export {PieSeries} from "./common/core/data/graph-data/modeled-pie"; +export {ModeledGaugeGraphData} from "./common/core/data/graph-data/modeled-gauge"; +export {GaugeSeries} from "./common/core/data/graph-data/modeled-gauge"; +export {ModeledRadarGraphData} from "./common/core/data/graph-data/modeled-radar"; +export {RadarDimension} from "./common/core/data/graph-data/modeled-radar"; +export {RadarIndicator} from "./common/core/data/graph-data/modeled-radar"; +export {RadarItem} from "./common/core/data/graph-data/modeled-radar"; +export {RadarSeries} from "./common/core/data/graph-data/modeled-radar"; +export {ModeledScatterGraphData} from "./common/core/data/graph-data/modeled-scatter"; +export {availableScatterSymbols} from "./common/core/data/graph-data/modeled-scatter"; +export {ScatterDimension} from "./common/core/data/graph-data/modeled-scatter"; +export {ModeledMapGraphData} from "./common/core/data/graph-data/modeled-map"; +export {MapItemStyle} from "./common/core/data/graph-data/modeled-map"; +export {MapVisualMapTextStyle} from "./common/core/data/graph-data/modeled-map"; +export {MapVisualMap} from "./common/core/data/graph-data/modeled-map"; +export {MapSeries} from "./common/core/data/graph-data/modeled-map"; +export {ModeledFunnelGraphData} from "./common/core/data/graph-data/modeled-funnel"; +export {FunnelSeries} from "./common/core/data/graph-data/modeled-funnel"; +export {FunnelLineStyle} from "./common/core/data/graph-data/modeled-funnel"; +export {FunnelLabelLine} from "./common/core/data/graph-data/modeled-funnel"; +export {FunnelLabel} from "./common/core/data/graph-data/modeled-funnel"; +export {FunnelItemStyle} from "./common/core/data/graph-data/modeled-funnel"; +export {ModeledBubbleGraphData} from "./common/core/data/graph-data/modeled-bubble"; +export {FunnelPlotGraphData} from "./common/core/data/graph-data/normal-funnel-plot"; +export {BubbleChartGraphData} from "./common/core/data/graph-data/normal-bubble"; +export {EmphasisConfig} from "./common/core/data/graph-data/normal-bubble"; +export {BubbleLabelConfig} from "./common/core/data/graph-data/normal-bubble"; +export {BubbleItemStyle} from "./common/core/data/graph-data/normal-bubble"; +export {GradientColor} from "./common/core/data/graph-data/normal-bubble"; +export {ColorStop} from "./common/core/data/graph-data/normal-bubble"; From 7e4d46cd9307b5f92b8fea36b526347beebe09cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E6=97=AD10045812?= Date: Fri, 26 Jan 2024 16:31:17 +0800 Subject: [PATCH 5/8] ... --- src/jigsaw/mobile-components/graph/graph.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jigsaw/mobile-components/graph/graph.ts b/src/jigsaw/mobile-components/graph/graph.ts index 2c0d71f0da..f20519e3ce 100644 --- a/src/jigsaw/mobile-components/graph/graph.ts +++ b/src/jigsaw/mobile-components/graph/graph.ts @@ -2,7 +2,7 @@ * Created by 10177553 on 2017/3/23. */ import {AfterViewInit, Component, ElementRef, EventEmitter, Input, NgZone, OnDestroy, OnInit, Output, Renderer2,} from "@angular/core"; -import {AbstractGraphData} from "../../common/core/data/graph-data"; +import {AbstractGraphData} from "../../common/core/data/graph-data/graph-data"; import {CallbackRemoval, CommonUtils} from "../../common/core/utils/common-utils"; import {AbstractJigsawComponent} from "../../common/common"; From 2212cea3e59c3d2032a34d96cd9182da47cbb038 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9F=A9=E5=B3=A510322371?= Date: Wed, 21 Feb 2024 17:47:19 +0800 Subject: [PATCH 6/8] ... --- src/jigsaw/common/core/data/graph-data/normal-bubble.ts | 8 ++++++++ src/jigsaw/mobile_public_api.ts | 2 +- .../auto-display/renderer/auto-display-renderer.ts | 4 ++-- src/jigsaw/public_api.ts | 1 + 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/jigsaw/common/core/data/graph-data/normal-bubble.ts b/src/jigsaw/common/core/data/graph-data/normal-bubble.ts index ab70aeb563..8f766dcca0 100644 --- a/src/jigsaw/common/core/data/graph-data/normal-bubble.ts +++ b/src/jigsaw/common/core/data/graph-data/normal-bubble.ts @@ -32,6 +32,14 @@ export type BubbleLabelConfig = { position?: string, show: boolean } +export type BubbleSeries = { + label: string, + value: number, + itemStyle: BubbleItemStyle, + labelConfig: BubbleLabelConfig, + x: number, + y: number +} export type EmphasisConfig = { itemStyle: BubbleItemStyle, label?: BubbleLabelConfig diff --git a/src/jigsaw/mobile_public_api.ts b/src/jigsaw/mobile_public_api.ts index 48730eb695..60aff89727 100644 --- a/src/jigsaw/mobile_public_api.ts +++ b/src/jigsaw/mobile_public_api.ts @@ -102,7 +102,7 @@ export {FunnelPlotGraphData} from "./common/core/data/graph-data/normal-funnel-p export {BubbleChartGraphData} from "./common/core/data/graph-data/normal-bubble"; export {EmphasisConfig} from "./common/core/data/graph-data/normal-bubble"; export {BubbleLabelConfig} from "./common/core/data/graph-data/normal-bubble"; +export {BubbleSeries} from "./common/core/data/graph-data/normal-bubble"; export {BubbleItemStyle} from "./common/core/data/graph-data/normal-bubble"; export {GradientColor} from "./common/core/data/graph-data/normal-bubble"; export {ColorStop} from "./common/core/data/graph-data/normal-bubble"; - diff --git a/src/jigsaw/pc-components/auto-display/renderer/auto-display-renderer.ts b/src/jigsaw/pc-components/auto-display/renderer/auto-display-renderer.ts index 8a14913869..c5c55c92de 100644 --- a/src/jigsaw/pc-components/auto-display/renderer/auto-display-renderer.ts +++ b/src/jigsaw/pc-components/auto-display/renderer/auto-display-renderer.ts @@ -11,8 +11,8 @@ import { import { CommonModule } from "@angular/common"; import { JigsawTrustedHtmlModule } from "../../../common/directive/trusted-html/trusted-html"; import { JigsawTableModule } from "../../table/table"; -import { JigsawGraphModule } from "../../graph/index"; -import { AbstractGraphData, GraphData } from "../../../common/core/data/graph-data"; +import { JigsawGraphModule } from "../../graph"; +import { AbstractGraphData, GraphData} from "../../../common/core/data/graph-data/graph-data"; import { TableData } from "../../../common/core/data/table-data"; import { CommonUtils } from "../../../common/core/utils/common-utils"; diff --git a/src/jigsaw/public_api.ts b/src/jigsaw/public_api.ts index 233bd98f17..2baaeb4334 100644 --- a/src/jigsaw/public_api.ts +++ b/src/jigsaw/public_api.ts @@ -153,6 +153,7 @@ export {FunnelPlotGraphData} from "./common/core/data/graph-data/normal-funnel-p export {BubbleChartGraphData} from "./common/core/data/graph-data/normal-bubble"; export {EmphasisConfig} from "./common/core/data/graph-data/normal-bubble"; export {BubbleLabelConfig} from "./common/core/data/graph-data/normal-bubble"; +export {BubbleSeries} from "./common/core/data/graph-data/normal-bubble"; export {BubbleItemStyle} from "./common/core/data/graph-data/normal-bubble"; export {GradientColor} from "./common/core/data/graph-data/normal-bubble"; export {ColorStop} from "./common/core/data/graph-data/normal-bubble"; From 01f5ec93001600eea5539ee0fd0f9ece29fc0014 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9F=A9=E5=B3=A510322371?= Date: Wed, 21 Feb 2024 18:00:50 +0800 Subject: [PATCH 7/8] .. --- .../auto-display/renderer/auto-display-renderer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jigsaw/pc-components/auto-display/renderer/auto-display-renderer.ts b/src/jigsaw/pc-components/auto-display/renderer/auto-display-renderer.ts index c5c55c92de..5eece7aa9f 100644 --- a/src/jigsaw/pc-components/auto-display/renderer/auto-display-renderer.ts +++ b/src/jigsaw/pc-components/auto-display/renderer/auto-display-renderer.ts @@ -11,7 +11,7 @@ import { import { CommonModule } from "@angular/common"; import { JigsawTrustedHtmlModule } from "../../../common/directive/trusted-html/trusted-html"; import { JigsawTableModule } from "../../table/table"; -import { JigsawGraphModule } from "../../graph"; +import { JigsawGraphModule } from "../../graph/index"; import { AbstractGraphData, GraphData} from "../../../common/core/data/graph-data/graph-data"; import { TableData } from "../../../common/core/data/table-data"; import { CommonUtils } from "../../../common/core/utils/common-utils"; From c5cae3d734dfc3bd26edd84607041b24d37b9122 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9F=A9=E5=B3=A510322371?= Date: Thu, 22 Feb 2024 09:36:38 +0800 Subject: [PATCH 8/8] ... --- build/scripts/check-non-i18n-terms.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/build/scripts/check-non-i18n-terms.js b/build/scripts/check-non-i18n-terms.js index d6cdc3f5b6..f71943ab5e 100644 --- a/build/scripts/check-non-i18n-terms.js +++ b/build/scripts/check-non-i18n-terms.js @@ -2,7 +2,11 @@ const fs = require("fs"); const glob = require('glob').sync; const excludesFiles = [ - 'common/core/data/graph-data.ts', 'common/core/theming/echarts-theme.ts' + 'common/core/data/graph-data/graph-data.ts', + 'common/core/data/graph-data/normal-funnel-plot.ts', + 'common/core/data/graph-data/normal-line-bar-area.ts', + 'common/core/data/graph-data/normal-pie-doughnut.ts', + 'common/core/theming/echarts-theme.ts' ]; process.chdir(`${__dirname}/../../src/jigsaw/`);