mirror of
https://github.com/dataease/dataease.git
synced 2026-06-16 11:21:44 +08:00
feat(图表): 支持象限图
This commit is contained in:
@@ -23,9 +23,9 @@ public class QuadrantHandler extends YoyChartHandler {
|
||||
yAxis.addAll(view.getExtBubble());
|
||||
yAxis.addAll(view.getExtTooltip());
|
||||
result.getAxisMap().put(ChartAxis.yAxis, yAxis);
|
||||
result.getAxisMap().put(ChartAxis.yAxisExt, view.getYAxisExt());
|
||||
result.getAxisMap().put(ChartAxis.extBubble, view.getExtBubble());
|
||||
result.getAxisMap().put(ChartAxis.extTooltip, view.getExtTooltip());
|
||||
result.getAxisMap().put(ChartAxis.yAxisExt, view.getYAxisExt());
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,8 @@ public class QuadrantHandler extends YoyChartHandler {
|
||||
boolean isDrill = filterResult.getFilterList().stream().anyMatch(ele -> ele.getFilterType() == 1);
|
||||
var xAxis = formatResult.getAxisMap().get(ChartAxis.xAxis);
|
||||
var yAxis = formatResult.getAxisMap().get(ChartAxis.yAxis);
|
||||
Map<String, Object> result = ChartDataBuild.transMixChartDataAntV(xAxis, xAxis, new ArrayList<>(), yAxis, view, data, isDrill);
|
||||
var extBubble = formatResult.getAxisMap().get(ChartAxis.extBubble);
|
||||
Map<String, Object> result = ChartDataBuild.transQuadrantData(xAxis, yAxis, view, data, extBubble, isDrill);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -443,6 +443,86 @@ public class ChartDataBuild {
|
||||
return map;
|
||||
}
|
||||
|
||||
public static Map<String, Object> transQuadrantData(List<ChartViewFieldDTO> xAxis, List<ChartViewFieldDTO> yAxis, ChartViewDTO view, List<String[]> data, List<ChartViewFieldDTO> extBubble, boolean isDrill) {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
|
||||
List<AxisChartDataAntVDTO> dataList = new ArrayList<>();
|
||||
for (int i1 = 0; i1 < data.size(); i1++) {
|
||||
String[] row = data.get(i1);
|
||||
|
||||
StringBuilder a = new StringBuilder();
|
||||
if (isDrill) {
|
||||
a.append(row[xAxis.size() - 1]);
|
||||
} else {
|
||||
for (int i = 0; i < xAxis.size(); i++) {
|
||||
if (i == xAxis.size() - 1) {
|
||||
a.append(row[i]);
|
||||
} else {
|
||||
a.append(row[i]).append("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// yAxis最后的数据对应extLabel和extTooltip,将他们从yAxis中去掉,同时转换成动态值
|
||||
int size = xAxis.size() + yAxis.size() + extBubble.size();
|
||||
int extSize = view.getExtLabel().size() + view.getExtTooltip().size();
|
||||
|
||||
AxisChartDataAntVDTO axisChartDataDTO = new AxisChartDataAntVDTO();
|
||||
axisChartDataDTO.setField(a.toString());
|
||||
axisChartDataDTO.setName(a.toString());
|
||||
List<ChartDimensionDTO> dimensionList = new ArrayList<>();
|
||||
for (int j = 0; j < xAxis.size(); j++) {
|
||||
ChartDimensionDTO chartDimensionDTO = new ChartDimensionDTO();
|
||||
chartDimensionDTO.setId(xAxis.get(j).getId());
|
||||
chartDimensionDTO.setValue(row[j]);
|
||||
dimensionList.add(chartDimensionDTO);
|
||||
}
|
||||
axisChartDataDTO.setDimensionList(dimensionList);
|
||||
|
||||
List<ChartQuotaDTO> quotaList = new ArrayList<>();
|
||||
axisChartDataDTO.setQuotaList(quotaList);
|
||||
// 横轴
|
||||
var hIndex = xAxis.size();
|
||||
var hAxis = yAxis.getFirst();
|
||||
ChartQuotaDTO hQuota = new ChartQuotaDTO();
|
||||
hQuota.setId(hAxis.getId());
|
||||
quotaList.add(hQuota);
|
||||
try {
|
||||
axisChartDataDTO.setValue(StringUtils.isEmpty(row[hIndex]) ? null : new BigDecimal(row[hIndex]));
|
||||
} catch (Exception e) {
|
||||
axisChartDataDTO.setValue(new BigDecimal(0));
|
||||
}
|
||||
// 纵轴
|
||||
var vIndex = xAxis.size() + 1;
|
||||
var vAxis = yAxis.get(1);
|
||||
ChartQuotaDTO vQuota = new ChartQuotaDTO();
|
||||
vQuota.setId(vAxis.getId());
|
||||
quotaList.add(vQuota);
|
||||
try {
|
||||
axisChartDataDTO.setExtValue(StringUtils.isEmpty(row[vIndex]) ? null : new BigDecimal(row[vIndex]));
|
||||
} catch (Exception e) {
|
||||
axisChartDataDTO.setExtValue(new BigDecimal(0));
|
||||
}
|
||||
// 气泡大小
|
||||
if (ObjectUtils.isNotEmpty(extBubble)) {
|
||||
var popIndex = vIndex + 1;
|
||||
var popAxis = extBubble.getFirst();
|
||||
try {
|
||||
axisChartDataDTO.setPopSize(StringUtils.isEmpty(row[popIndex]) ? null : new BigDecimal(row[popIndex]));
|
||||
ChartQuotaDTO bubbleQuotaDTO = new ChartQuotaDTO();
|
||||
bubbleQuotaDTO.setId(popAxis.getId());
|
||||
quotaList.add(bubbleQuotaDTO);
|
||||
} catch (Exception e) {
|
||||
axisChartDataDTO.setPopSize(new BigDecimal(0));
|
||||
}
|
||||
}
|
||||
buildDynamicValue(view, axisChartDataDTO, row, size, extSize);
|
||||
dataList.add(axisChartDataDTO);
|
||||
}
|
||||
map.put("data", dataList);
|
||||
return map;
|
||||
}
|
||||
|
||||
// antv radar
|
||||
public static Map<String, Object> transRadarChartDataAntV(List<ChartViewFieldDTO> xAxis, List<ChartViewFieldDTO> yAxis, ChartViewDTO view, List<String[]> data, boolean isDrill) {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
|
||||
@@ -31,13 +31,13 @@ const isDashboard = dvMainStore.dvInfo.type === 'dashboard'
|
||||
for (let i = 0; i < 4; i++) {
|
||||
regionStyle.push({
|
||||
fill: isDashboard ? '#ffffff' : 'rgb(2,4,8,1,1)',
|
||||
fillOpacity: '1.0'
|
||||
fillOpacity: 1.0
|
||||
})
|
||||
labels.push({
|
||||
content: '',
|
||||
style: {
|
||||
fill: isDashboard ? 'rgb(2,4,8,1,1)' : '#ffffff',
|
||||
fillOpacity: '0.5',
|
||||
fillOpacity: 0.5,
|
||||
fontSize: 14
|
||||
}
|
||||
})
|
||||
@@ -84,8 +84,8 @@ const fillOpacityList = computed(() => {
|
||||
for (let i = 0; i <= 1; i = i + 0.1) {
|
||||
let c = i.toFixed(1)
|
||||
arr.push({
|
||||
name: c + '',
|
||||
value: c
|
||||
name: c,
|
||||
value: i
|
||||
})
|
||||
}
|
||||
return arr
|
||||
|
||||
@@ -0,0 +1,696 @@
|
||||
import { G2ChartView, G2DrawOptions } from '../../../types/impl/g2'
|
||||
import {
|
||||
flow,
|
||||
hexColorToRGBA,
|
||||
parseJson,
|
||||
setUpSingleDimensionSeriesColor
|
||||
} from '@/views/chart/components/js/util'
|
||||
import { TOOLTIP_ITEM_TPL, TOOLTIP_TITLE_TPL } from '../../../common/common_antv'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { defaultsDeep, isEmpty } from 'lodash-es'
|
||||
import { ChartEvent, Chart as G2Chart, G2Spec } from '@antv/g2'
|
||||
import { valueFormatter } from '../../../../formatter'
|
||||
|
||||
const { t } = useI18n()
|
||||
/**
|
||||
* 象限图
|
||||
*/
|
||||
export class Quadrant extends G2ChartView {
|
||||
properties: EditorProperty[] = [
|
||||
'background-overall-component',
|
||||
'border-style',
|
||||
'basic-style-selector',
|
||||
'x-axis-selector',
|
||||
'y-axis-selector',
|
||||
'title-selector',
|
||||
'label-selector',
|
||||
'tooltip-selector',
|
||||
'legend-selector',
|
||||
'jump-set',
|
||||
'linkage',
|
||||
'quadrant-selector'
|
||||
]
|
||||
propertyInner: EditorPropertyInner = {
|
||||
'basic-style-selector': [
|
||||
'colors',
|
||||
'alpha',
|
||||
'scatterSymbol',
|
||||
'scatterSymbolSize',
|
||||
'seriesColor'
|
||||
],
|
||||
'label-selector': ['fontSize', 'color'],
|
||||
'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'seriesTooltipFormatter', 'show'],
|
||||
'x-axis-selector': [
|
||||
'position',
|
||||
'name',
|
||||
'color',
|
||||
'fontSize',
|
||||
'axisLine',
|
||||
'axisValue',
|
||||
'splitLine',
|
||||
'axisForm',
|
||||
'axisLabel',
|
||||
'axisLabelFormatter'
|
||||
],
|
||||
'y-axis-selector': [
|
||||
'position',
|
||||
'name',
|
||||
'color',
|
||||
'fontSize',
|
||||
'axisValue',
|
||||
'axisLine',
|
||||
'splitLine',
|
||||
'axisForm',
|
||||
'axisLabel',
|
||||
'axisLabelFormatter'
|
||||
],
|
||||
'title-selector': [
|
||||
'title',
|
||||
'fontSize',
|
||||
'color',
|
||||
'hPosition',
|
||||
'isItalic',
|
||||
'isBolder',
|
||||
'remarkShow',
|
||||
'fontFamily',
|
||||
'letterSpace',
|
||||
'fontShadow'
|
||||
],
|
||||
'legend-selector': ['icon', 'orient', 'color', 'fontSize', 'hPosition', 'vPosition'],
|
||||
'quadrant-selector': ['regionStyle', 'label', 'lineStyle']
|
||||
}
|
||||
axis: AxisType[] = [
|
||||
'xAxis',
|
||||
'yAxis',
|
||||
'yAxisExt',
|
||||
'extBubble',
|
||||
'filter',
|
||||
'drill',
|
||||
'extLabel',
|
||||
'extTooltip'
|
||||
]
|
||||
axisConfig: AxisConfig = {
|
||||
extBubble: {
|
||||
name: `${t('chart.bubble_size')} / ${t('chart.quota')}`,
|
||||
type: 'q',
|
||||
limit: 1,
|
||||
allowEmpty: true
|
||||
},
|
||||
xAxis: {
|
||||
name: `${t('chart.form_type')} / ${t('chart.dimension')}`,
|
||||
type: 'd',
|
||||
limit: 1
|
||||
},
|
||||
yAxis: {
|
||||
name: `${t('chart.x_axis')} / ${t('chart.quota')}`,
|
||||
type: 'q',
|
||||
limit: 1
|
||||
},
|
||||
yAxisExt: {
|
||||
name: `${t('chart.y_axis')} / ${t('chart.quota')}`,
|
||||
type: 'q',
|
||||
limit: 1
|
||||
}
|
||||
}
|
||||
|
||||
async drawChart(drawOptions: G2DrawOptions<G2Chart>): Promise<G2Chart> {
|
||||
const { chart, container, action, quadrantDefaultBaseline } = drawOptions
|
||||
if (!chart.data?.data) {
|
||||
return
|
||||
}
|
||||
// data
|
||||
const data = chart.data.data
|
||||
// x轴基准线 默认值
|
||||
const xValues = data.map(item => item.value)
|
||||
const xBaseline = (Math.max(...xValues) + Math.min(...xValues)) / 2
|
||||
// y轴基准线 默认值
|
||||
const yValues = data.map(item => item.extValue)
|
||||
const yBaseline = (Math.max(...yValues) + Math.min(...yValues)) / 2
|
||||
const defaultBaselineQuadrant = {
|
||||
...chart.customAttr['quadrant']
|
||||
}
|
||||
// 新建图表
|
||||
if (defaultBaselineQuadrant.xBaseline === undefined) {
|
||||
// 默认基准线值
|
||||
defaultBaselineQuadrant.xBaseline = xBaseline
|
||||
defaultBaselineQuadrant.yBaseline = yBaseline
|
||||
}
|
||||
const baseOptions: G2Spec = {
|
||||
type: 'view',
|
||||
autoFit: true,
|
||||
data: {
|
||||
value: data
|
||||
},
|
||||
children: [
|
||||
{ type: 'lineX', data: [xBaseline] },
|
||||
{ type: 'lineY', data: [yBaseline] },
|
||||
{
|
||||
type: 'point',
|
||||
encode: { x: 'value', y: 'extValue', color: 'field' },
|
||||
legend: { size: false }
|
||||
},
|
||||
{ type: 'range', data: [], encode: { x: 'x', y: 'y' }, zIndex: -1 }
|
||||
]
|
||||
}
|
||||
chart.container = container
|
||||
const options: G2Spec = this.setupOptions(chart, baseOptions, {})
|
||||
const newChart = new G2Chart({ container })
|
||||
newChart.options(options)
|
||||
newChart.on(`point:${ChartEvent.CLICK}`, action)
|
||||
newChart.on(`plot:${ChartEvent.CLICK}`, () => quadrantDefaultBaseline(defaultBaselineQuadrant))
|
||||
newChart.once(ChartEvent.AFTER_RENDER, () => quadrantDefaultBaseline(defaultBaselineQuadrant))
|
||||
newChart.once(ChartEvent.AFTER_RENDER, () => {
|
||||
const rangeMark = newChart.getNodeByType('range')
|
||||
const xScale = newChart.getScaleByChannel('x')
|
||||
const [xMin, xMax] = xScale.getOptions().domain
|
||||
const yScale = newChart.getScaleByChannel('y')
|
||||
const [yMin, yMax] = yScale.getOptions().domain
|
||||
rangeMark.data([
|
||||
{ x: [xBaseline, xMax], y: [yBaseline, yMax], region: 0 },
|
||||
{ x: [xMin, xBaseline], y: [yBaseline, yMax], region: 1 },
|
||||
{ x: [xMin, xBaseline], y: [yMin, yBaseline], region: 2 },
|
||||
{ x: [xBaseline, xMax], y: [yMin, yBaseline], region: 3 }
|
||||
])
|
||||
newChart.render()
|
||||
})
|
||||
|
||||
return newChart
|
||||
}
|
||||
|
||||
protected configTheme(chart: Chart, options: G2Spec): G2Spec {
|
||||
const customAttr = parseJson(chart.customAttr)
|
||||
const colors: string[] = []
|
||||
if (customAttr.basicStyle) {
|
||||
const basicStyle = customAttr.basicStyle
|
||||
basicStyle.colors.forEach(ele => {
|
||||
colors.push(hexColorToRGBA(ele, basicStyle.alpha))
|
||||
})
|
||||
}
|
||||
const customStyle = parseJson(chart.customStyle)
|
||||
let bgColor
|
||||
if (customStyle.background) {
|
||||
bgColor = hexColorToRGBA(customStyle.background.color, customStyle.background.alpha)
|
||||
}
|
||||
const theme = {
|
||||
color: colors[0],
|
||||
category10: colors,
|
||||
category20: colors,
|
||||
view: {
|
||||
viewFill: bgColor
|
||||
}
|
||||
}
|
||||
return { ...options, theme }
|
||||
}
|
||||
|
||||
protected configColor(chart: Chart, options: G2Spec): G2Spec {
|
||||
const basicStyle = parseJson(chart.customAttr).basicStyle
|
||||
const { seriesColor } = basicStyle
|
||||
if (seriesColor?.length) {
|
||||
const pointMark = options.children[2]
|
||||
const colorRelations = []
|
||||
seriesColor.forEach(item => {
|
||||
colorRelations.push([item.name, hexColorToRGBA(item.color, basicStyle.alpha)])
|
||||
})
|
||||
if (colorRelations.length) {
|
||||
defaultsDeep(pointMark, {
|
||||
scale: {
|
||||
color: {
|
||||
relations: colorRelations
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
protected configBasicStyle(chart: Chart, options: G2Spec): G2Spec {
|
||||
const customAttr = parseJson(chart.customAttr)
|
||||
const basicStyle = customAttr.basicStyle
|
||||
const sizeOptions = {
|
||||
scale: {
|
||||
color: {
|
||||
range: options.theme.category10
|
||||
}
|
||||
},
|
||||
encode: {
|
||||
shape: {
|
||||
type: 'constant',
|
||||
value: basicStyle.scatterSymbol
|
||||
}
|
||||
}
|
||||
}
|
||||
const pointMark = options.children[2]
|
||||
if (chart.extBubble?.length) {
|
||||
const tmpOptions = {
|
||||
encode: {
|
||||
size: 'popSize'
|
||||
},
|
||||
scale: {
|
||||
size: {
|
||||
range: [5, 30]
|
||||
}
|
||||
}
|
||||
}
|
||||
defaultsDeep(pointMark, sizeOptions, tmpOptions)
|
||||
return options
|
||||
}
|
||||
const tmp = {
|
||||
encode: {
|
||||
size: {
|
||||
type: 'constant',
|
||||
value: basicStyle.scatterSymbolSize
|
||||
}
|
||||
}
|
||||
}
|
||||
defaultsDeep(pointMark, sizeOptions, tmp)
|
||||
return options
|
||||
}
|
||||
|
||||
protected configXAxis(chart: Chart, options: G2Spec): G2Spec {
|
||||
const { xAxis } = parseJson(chart.customStyle)
|
||||
if (!xAxis.show) {
|
||||
const axisHide = {
|
||||
axis: {
|
||||
x: false
|
||||
}
|
||||
}
|
||||
return defaultsDeep(options, axisHide)
|
||||
}
|
||||
let lineLineDash = undefined
|
||||
if (xAxis.axisLine.lineStyle.style === 'dashed') {
|
||||
lineLineDash = [10, 8]
|
||||
}
|
||||
if (xAxis.axisLine.lineStyle.style === 'dotted') {
|
||||
lineLineDash = [1, 2]
|
||||
}
|
||||
let gridLineDash = undefined
|
||||
if (xAxis.splitLine.lineStyle.style === 'dashed') {
|
||||
gridLineDash = [10, 8]
|
||||
}
|
||||
if (xAxis.splitLine.lineStyle.style === 'dotted') {
|
||||
gridLineDash = [1, 2]
|
||||
}
|
||||
const axisStyle = {
|
||||
axis: {
|
||||
x: {
|
||||
position: xAxis.position,
|
||||
title: xAxis.nameShow === false ? false : xAxis.name,
|
||||
titleFontSize: xAxis.fontSize,
|
||||
titleFill: xAxis.color,
|
||||
line: xAxis.axisLine.show,
|
||||
lineStroke: xAxis.axisLine.lineStyle.color,
|
||||
lineStrokeOpacity: 1,
|
||||
lineLineWidth: xAxis.axisLine.lineStyle.width,
|
||||
lineLineDash,
|
||||
label: xAxis.axisLabel.show,
|
||||
labelFill: xAxis.axisLabel.color,
|
||||
labelFillOpacity: 1,
|
||||
labelFontSize: xAxis.axisLabel.fontSize,
|
||||
grid: xAxis.splitLine.show,
|
||||
gridStroke: xAxis.splitLine.lineStyle.color,
|
||||
gridStrokeOpacity: 1,
|
||||
gridLineWidth: xAxis.splitLine.lineStyle.width,
|
||||
gridLineDash,
|
||||
transform: xAxis.axisLabel.rotate
|
||||
? [
|
||||
{
|
||||
type: 'rotate',
|
||||
optionalAngles: [xAxis.axisLabel.rotate],
|
||||
recoverWhenFailed: false
|
||||
}
|
||||
]
|
||||
: []
|
||||
}
|
||||
}
|
||||
}
|
||||
const pointMatk = options.children[2]
|
||||
if (!xAxis.axisValue.auto) {
|
||||
const scaleOpt = {
|
||||
scale: {
|
||||
x: {
|
||||
domainMin: xAxis.axisValue.min,
|
||||
domainMax: xAxis.axisValue.max,
|
||||
tickCount: xAxis.axisValue.splitCount,
|
||||
tickMethod: (min, max, count) => {
|
||||
const step = (max - min) / count
|
||||
const ticks = []
|
||||
for (let i = 0; i <= count; i++) {
|
||||
ticks.push(min + step * i)
|
||||
}
|
||||
return ticks
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
defaultsDeep(pointMatk, scaleOpt)
|
||||
}
|
||||
return defaultsDeep(options, axisStyle)
|
||||
}
|
||||
|
||||
protected configYAxis(chart: Chart, options: G2Spec): G2Spec {
|
||||
const { yAxis } = parseJson(chart.customStyle)
|
||||
if (!yAxis.show) {
|
||||
const axisHide = {
|
||||
axis: {
|
||||
y: false
|
||||
}
|
||||
}
|
||||
return defaultsDeep(options, axisHide)
|
||||
}
|
||||
let lineLineDash = undefined
|
||||
if (yAxis.axisLine.lineStyle.style === 'dashed') {
|
||||
lineLineDash = [10, 8]
|
||||
}
|
||||
if (yAxis.axisLine.lineStyle.style === 'dotted') {
|
||||
lineLineDash = [1, 2]
|
||||
}
|
||||
let gridLineDash = [0, 0]
|
||||
if (yAxis.splitLine.lineStyle.style === 'dashed') {
|
||||
gridLineDash = [10, 8]
|
||||
}
|
||||
if (yAxis.splitLine.lineStyle.style === 'dotted') {
|
||||
gridLineDash = [1, 2]
|
||||
}
|
||||
const axisOption = {
|
||||
axis: {
|
||||
y: {
|
||||
position: yAxis.position,
|
||||
title: yAxis.nameShow === false ? false : yAxis.name,
|
||||
titleFontSize: yAxis.fontSize,
|
||||
titleFill: yAxis.color,
|
||||
line: yAxis.axisLine.show,
|
||||
lineStroke: yAxis.axisLine.lineStyle.color,
|
||||
lineStrokeOpacity: 1,
|
||||
lineLineWidth: yAxis.axisLine.lineStyle.width,
|
||||
lineLineDash,
|
||||
label: yAxis.axisLabel.show,
|
||||
labelFill: yAxis.axisLabel.color,
|
||||
labelFillOpacity: 1,
|
||||
labelFontSize: yAxis.axisLabel.fontSize,
|
||||
grid: yAxis.splitLine.show,
|
||||
gridStroke: yAxis.splitLine.lineStyle.color,
|
||||
gridStrokeOpacity: 1,
|
||||
gridLineWidth: yAxis.splitLine.lineStyle.width,
|
||||
gridLineDash,
|
||||
transform: yAxis.axisLabel.rotate
|
||||
? [
|
||||
{
|
||||
type: 'rotate',
|
||||
optionalAngles: [yAxis.axisLabel.rotate],
|
||||
recoverWhenFailed: false
|
||||
}
|
||||
]
|
||||
: [],
|
||||
labelFormatter: d => {
|
||||
return valueFormatter(d, yAxis.axisLabelFormatter)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!yAxis.axisValue.auto) {
|
||||
const pointMatk = options.children[2]
|
||||
const scaleOpt = {
|
||||
scale: {
|
||||
y: {
|
||||
domainMin: yAxis.axisValue.min,
|
||||
domainMax: yAxis.axisValue.max,
|
||||
tickCount: yAxis.axisValue.splitCount,
|
||||
tickMethod: (min, max, count) => {
|
||||
const step = (max - min) / count
|
||||
const ticks = []
|
||||
for (let i = 0; i <= count; i++) {
|
||||
ticks.push(min + step * i)
|
||||
}
|
||||
return ticks
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
defaultsDeep(pointMatk, scaleOpt)
|
||||
}
|
||||
return defaultsDeep(options, axisOption)
|
||||
}
|
||||
|
||||
protected configQuadrant(chart: Chart, options: G2Spec): G2Spec {
|
||||
const { quadrant } = parseJson(chart.customAttr)
|
||||
const [lineX, lineY, , range] = options.children
|
||||
const linetStyle = {
|
||||
style: {
|
||||
stroke: quadrant.lineStyle.stroke,
|
||||
lineWidth: quadrant.lineStyle.lineWidth,
|
||||
opacity: quadrant.lineStyle.opacity
|
||||
}
|
||||
}
|
||||
defaultsDeep(lineX, linetStyle)
|
||||
defaultsDeep(lineY, linetStyle)
|
||||
const rangeOpt = {
|
||||
style: {
|
||||
fill: d => {
|
||||
return quadrant.regionStyle[d.region].fill
|
||||
},
|
||||
fillOpacity: d => {
|
||||
return quadrant.regionStyle[d.region].fillOpacity
|
||||
}
|
||||
},
|
||||
labels: [
|
||||
{
|
||||
text: d => {
|
||||
return quadrant.labels[d.region].content
|
||||
},
|
||||
style: {
|
||||
fill: d => {
|
||||
return quadrant.labels[d.region].style.fill
|
||||
},
|
||||
fontSize: d => {
|
||||
return quadrant.labels[d.region].style.fontSize
|
||||
},
|
||||
fillOpacity: d => {
|
||||
return quadrant.labels[d.region].style.fillOpacity
|
||||
},
|
||||
position: d => {
|
||||
return {
|
||||
0: 'bottom-right',
|
||||
1: 'bottom-left',
|
||||
2: 'top-left',
|
||||
3: 'top-right'
|
||||
}[d.region]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
defaultsDeep(range, rangeOpt)
|
||||
return options
|
||||
}
|
||||
|
||||
protected configLegend(chart: Chart, options: G2Spec): G2Spec {
|
||||
const { legend } = parseJson(chart.customStyle)
|
||||
if (!legend.show) {
|
||||
return { ...options, legend: false }
|
||||
}
|
||||
const baseLegend = this.getLegend(chart)
|
||||
const tmpLegend = {
|
||||
legend: {
|
||||
color: {
|
||||
...baseLegend,
|
||||
itemMarkerSize: legend.size,
|
||||
itemMarker: legend.icon
|
||||
}
|
||||
}
|
||||
}
|
||||
const pointMark = options.children[2]
|
||||
defaultsDeep(pointMark, tmpLegend)
|
||||
return options
|
||||
}
|
||||
|
||||
protected configLabel(chart: Chart, options: G2Spec): G2Spec {
|
||||
const { label } = parseJson(chart.customAttr)
|
||||
if (!label.show) {
|
||||
return options
|
||||
}
|
||||
const pointMark = options.children[2]
|
||||
const labelStyle = {
|
||||
labels: [
|
||||
{
|
||||
text: 'field',
|
||||
position: 'inside',
|
||||
style: {
|
||||
fill: label.color,
|
||||
fontSize: label.fontSize,
|
||||
fillOpacity: 1
|
||||
},
|
||||
transform: label.fullDisplay ? [] : [{ type: 'overlapHide' }, { type: 'exceedAdjust' }]
|
||||
}
|
||||
]
|
||||
}
|
||||
defaultsDeep(pointMark, labelStyle)
|
||||
return options
|
||||
}
|
||||
|
||||
protected configTooltip(chart: Chart, options: G2Spec): G2Spec {
|
||||
const customAttr: DeepPartial<ChartAttr> = parseJson(chart.customAttr)
|
||||
const tooltipAttr = customAttr.tooltip
|
||||
if (!tooltipAttr.show) {
|
||||
return { ...options, tooltip: false }
|
||||
}
|
||||
const formatterMap = tooltipAttr.seriesTooltipFormatter
|
||||
?.filter(i => i.show)
|
||||
.reduce((pre, next) => {
|
||||
pre[next.seriesId] = next
|
||||
return pre
|
||||
}, {}) as Record<string, SeriesFormatter>
|
||||
let g2TooltipWrapper = document.getElementById('G2-TOOLTIP-WRAPPER')
|
||||
if (!g2TooltipWrapper) {
|
||||
g2TooltipWrapper = document.createElement('div')
|
||||
g2TooltipWrapper.id = 'G2-TOOLTIP-WRAPPER'
|
||||
g2TooltipWrapper.style.position = 'absolute'
|
||||
g2TooltipWrapper.style.pointerEvents = 'none'
|
||||
g2TooltipWrapper.style.zIndex = '9999'
|
||||
document.body.appendChild(g2TooltipWrapper)
|
||||
}
|
||||
const { yAxis, extBubble } = chart
|
||||
const tooltipOptions: G2Spec = {
|
||||
tooltip: d => d,
|
||||
interaction: {
|
||||
tooltip: {
|
||||
mount: g2TooltipWrapper,
|
||||
css: {
|
||||
'.g2-tooltip': {
|
||||
background: tooltipAttr.backgroundColor
|
||||
},
|
||||
'.g2-tooltip-title': {
|
||||
color: tooltipAttr.color,
|
||||
'font-size': `${tooltipAttr.fontSize}px`
|
||||
},
|
||||
'.g2-tooltip-list-item-name-label': {
|
||||
color: tooltipAttr.color,
|
||||
'font-size': `${tooltipAttr.fontSize}px`
|
||||
},
|
||||
'.g2-tooltip-list-item-value': {
|
||||
color: tooltipAttr.color,
|
||||
'font-size': `${tooltipAttr.fontSize}px`
|
||||
}
|
||||
},
|
||||
render: (_, { items }) => {
|
||||
const head = items[0]
|
||||
const title = head.field
|
||||
const titleHtml = TOOLTIP_TITLE_TPL.replace('{title}', title)
|
||||
const validField = []
|
||||
if (tooltipAttr.seriesTooltipFormatter?.length) {
|
||||
let seriesId = `${head.quotaList[0].id}-yAxis`
|
||||
let formatter = formatterMap[seriesId]
|
||||
if (formatter) {
|
||||
validField.push('value')
|
||||
}
|
||||
seriesId = `${head.quotaList[1].id}-yAxisExt`
|
||||
formatter = formatterMap[seriesId]
|
||||
if (formatter) {
|
||||
validField.push('extValue')
|
||||
}
|
||||
if (extBubble?.length) {
|
||||
const extBubbleId = `${head.quotaList[2].id}-extBubble`
|
||||
formatter = formatterMap[extBubbleId]
|
||||
if (formatter) {
|
||||
validField.push('popSize')
|
||||
}
|
||||
}
|
||||
} else {
|
||||
validField.push('value', 'extValue')
|
||||
if (extBubble?.length) {
|
||||
validField.push('popSize')
|
||||
}
|
||||
}
|
||||
const result = []
|
||||
validField.forEach(field => {
|
||||
let seriesId = `${head.quotaList[0].id}-yAxis`
|
||||
if (field === 'extValue') {
|
||||
seriesId = `${head.quotaList[1].id}-yAxisExt`
|
||||
}
|
||||
if (field === 'popSize') {
|
||||
seriesId = `${head.quotaList[2].id}-extBubble`
|
||||
}
|
||||
const formatter = formatterMap[seriesId] ?? yAxis[0]
|
||||
const value = valueFormatter(head[field], formatter.formatterCfg)
|
||||
const name = isEmpty(formatter.chartShowName)
|
||||
? formatter.name
|
||||
: formatter.chartShowName
|
||||
result.push({ color: head.color, name, value })
|
||||
})
|
||||
head.dynamicTooltipValue?.forEach(item => {
|
||||
const formatter = formatterMap[item.fieldId]
|
||||
if (formatter) {
|
||||
const value = valueFormatter(parseFloat(item.value), formatter.formatterCfg)
|
||||
const name = isEmpty(formatter.chartShowName)
|
||||
? formatter.name
|
||||
: formatter.chartShowName
|
||||
result.push({ color: 'grey', name, value })
|
||||
}
|
||||
})
|
||||
const itemsHtml = result
|
||||
.map(item => {
|
||||
const marker = item.color
|
||||
const label = item.name
|
||||
const value = item.value
|
||||
return TOOLTIP_ITEM_TPL.replace('{marker}', marker)
|
||||
.replace('{label}', label)
|
||||
.replace('{value}', value)
|
||||
})
|
||||
.join('')
|
||||
const listHtml = `<ul class="g2-tooltip-list" style="margin: 0px; list-style-type: none; padding: 0px;">${itemsHtml}</ul>`
|
||||
return `${titleHtml}${listHtml}`
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const pointMark = options.children[2]
|
||||
defaultsDeep(pointMark, tooltipOptions)
|
||||
return options
|
||||
}
|
||||
|
||||
setupDefaultOptions(chart: ChartObj): ChartObj {
|
||||
chart.customStyle.yAxis.splitLine = {
|
||||
...chart.customStyle.yAxis.splitLine,
|
||||
show: false
|
||||
}
|
||||
chart.customStyle.yAxisExt.splitLine = {
|
||||
...chart.customStyle.yAxisExt.splitLine,
|
||||
show: false
|
||||
}
|
||||
chart.customStyle.yAxis.axisLine = {
|
||||
...chart.customStyle.yAxis.axisLine,
|
||||
show: true
|
||||
}
|
||||
chart.customStyle.yAxisExt.axisLine = {
|
||||
...chart.customStyle.yAxisExt.axisLine,
|
||||
show: true
|
||||
}
|
||||
return chart
|
||||
}
|
||||
|
||||
public setupSeriesColor(chart: ChartObj, data?: any[]): ChartBasicStyle['seriesColor'] {
|
||||
const { xAxis, yAxis, yAxisExt } = chart
|
||||
if (!(xAxis?.length && yAxis?.length && yAxisExt?.length)) {
|
||||
return []
|
||||
}
|
||||
return setUpSingleDimensionSeriesColor(chart, data)
|
||||
}
|
||||
|
||||
protected setupOptions(chart: Chart, options: G2Spec, context: Record<string, any>): G2Spec {
|
||||
return flow(
|
||||
this.configTheme,
|
||||
this.configColor,
|
||||
this.configBasicStyle,
|
||||
this.configQuadrant,
|
||||
this.configXAxis,
|
||||
this.configYAxis,
|
||||
this.configLegend,
|
||||
this.configLabel,
|
||||
this.configTooltip
|
||||
)(chart, options, context, this)
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super('quadrant', [])
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ public class AxisChartDataAntVDTO {
|
||||
private String name;
|
||||
private String category;
|
||||
private BigDecimal popSize;
|
||||
private BigDecimal extValue;
|
||||
private String group;
|
||||
private List<DynamicValueDTO> dynamicLabelValue;
|
||||
private List<DynamicValueDTO> dynamicTooltipValue;
|
||||
|
||||
Reference in New Issue
Block a user