From 978105867579c9833f2fb5c916d6123e822be568 Mon Sep 17 00:00:00 2001 From: wisonic-s Date: Wed, 19 Apr 2023 10:16:58 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E8=A7=86=E5=9B=BE-=E5=AF=B9=E7=A7=B0?= =?UTF-8?q?=E6=9D=A1=E5=BD=A2=E5=9B=BE):=20=E6=96=B0=E5=A2=9E=E5=AF=B9?= =?UTF-8?q?=E7=A7=B0=E6=9D=A1=E5=BD=A2=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://github.com/dataease/dataease/issues/4727 --- .../dto/chart/AxisChartDataAntVDTO.java | 1 + .../service/chart/ChartViewService.java | 4 +- .../service/chart/util/ChartDataBuild.java | 59 ++++++++ .../canvas/components/editor/SettingMenu.vue | 10 +- frontend/src/icons/svg/bidirectional-bar.svg | 1 + frontend/src/lang/en.js | 1 + frontend/src/lang/tw.js | 1 + frontend/src/lang/zh.js | 3 +- .../src/views/chart/chart/bar/bar_antv.js | 109 +++++++++++++- .../views/chart/chart/common/common_antv.js | 37 ++++- frontend/src/views/chart/chart/util.js | 94 ++++++++++++- .../chart/components/ChartComponentG2.vue | 4 +- .../componentStyle/XAxisSelectorAntV.vue | 10 +- .../componentStyle/YAxisSelectorAntV.vue | 10 +- .../chart/components/senior/FunctionCfg.vue | 133 ++++++++++-------- frontend/src/views/chart/view/ChartEdit.vue | 60 ++++++-- frontend/src/views/chart/view/ChartStyle.vue | 19 ++- 17 files changed, 465 insertions(+), 91 deletions(-) create mode 100644 frontend/src/icons/svg/bidirectional-bar.svg diff --git a/backend/src/main/java/io/dataease/dto/chart/AxisChartDataAntVDTO.java b/backend/src/main/java/io/dataease/dto/chart/AxisChartDataAntVDTO.java index 17ea712504..4ab8ccda0b 100644 --- a/backend/src/main/java/io/dataease/dto/chart/AxisChartDataAntVDTO.java +++ b/backend/src/main/java/io/dataease/dto/chart/AxisChartDataAntVDTO.java @@ -19,4 +19,5 @@ public class AxisChartDataAntVDTO { private String category; private BigDecimal popSize; private String group; + private BigDecimal extValue; } diff --git a/backend/src/main/java/io/dataease/service/chart/ChartViewService.java b/backend/src/main/java/io/dataease/service/chart/ChartViewService.java index 9bc5029653..bc5239baa8 100644 --- a/backend/src/main/java/io/dataease/service/chart/ChartViewService.java +++ b/backend/src/main/java/io/dataease/service/chart/ChartViewService.java @@ -611,7 +611,7 @@ public class ChartViewService { xAxis.addAll(xAxisExt); } List yAxis = gson.fromJson(view.getYAxis(), tokenType); - if (StringUtils.equalsIgnoreCase(view.getType(), "chart-mix")) { + if (StringUtils.equalsAnyIgnoreCase(view.getType(), "chart-mix","bidirectional-bar")) { List yAxisExt = gson.fromJson(view.getYAxisExt(), tokenType); yAxis.addAll(yAxisExt); } @@ -1337,6 +1337,8 @@ public class ChartViewService { mapChart = ChartDataBuild.transMixChartDataAntV(xAxis, yAxis, view, data, isDrill); } else if (StringUtils.containsIgnoreCase(view.getType(), "label")) { mapChart = ChartDataBuild.transLabelChartData(xAxis, yAxis, view, data, isDrill); + } else if (StringUtils.containsIgnoreCase(view.getType(), "bidirectional-bar")) { + mapChart = ChartDataBuild.transBidirectionalBarData(xAxis, yAxis, view, data, isDrill); } else { mapChart = ChartDataBuild.transChartDataAntV(xAxis, yAxis, view, data, isDrill); } diff --git a/backend/src/main/java/io/dataease/service/chart/util/ChartDataBuild.java b/backend/src/main/java/io/dataease/service/chart/util/ChartDataBuild.java index f2c03fcbdb..88646f5326 100644 --- a/backend/src/main/java/io/dataease/service/chart/util/ChartDataBuild.java +++ b/backend/src/main/java/io/dataease/service/chart/util/ChartDataBuild.java @@ -1199,4 +1199,63 @@ public class ChartDataBuild { return map; } } + + public static Map transBidirectionalBarData(List xAxis, List yAxis, ChartViewDTO view, List data, boolean isDrill) { + Map map = new HashMap<>(); + + List 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"); + } + } + } + + AxisChartDataAntVDTO axisChartDataDTO = new AxisChartDataAntVDTO(); + axisChartDataDTO.setField(a.toString()); + axisChartDataDTO.setName(a.toString()); + + List 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); + for (int i = xAxis.size(); i < xAxis.size() + yAxis.size(); i++) { + List quotaList = new ArrayList<>(); + int j = i - xAxis.size(); + ChartQuotaDTO chartQuotaDTO = new ChartQuotaDTO(); + chartQuotaDTO.setId(yAxis.get(j).getId()); + quotaList.add(chartQuotaDTO); + axisChartDataDTO.setQuotaList(quotaList); + } + if (yAxis.size() == 2){ + try { + axisChartDataDTO.setValue(StringUtils.isEmpty(row[xAxis.size()]) ? null : new BigDecimal(row[xAxis.size()])); + } catch (Exception e) { + axisChartDataDTO.setValue(new BigDecimal(0)); + } + try { + axisChartDataDTO.setExtValue(StringUtils.isEmpty(row[xAxis.size() + yAxis.size() - 1]) ? null : new BigDecimal(row[xAxis.size() + yAxis.size() - 1])); + } catch (Exception e) { + axisChartDataDTO.setExtValue(new BigDecimal(0)); + } + dataList.add(axisChartDataDTO); + } + } + map.put("data", dataList); + return map; + } } diff --git a/frontend/src/components/canvas/components/editor/SettingMenu.vue b/frontend/src/components/canvas/components/editor/SettingMenu.vue index 6b1380bf0b..c8d946f59a 100644 --- a/frontend/src/components/canvas/components/editor/SettingMenu.vue +++ b/frontend/src/components/canvas/components/editor/SettingMenu.vue @@ -192,7 +192,8 @@ export default { 'gauge', 'text', 'label', - 'word-cloud' + 'word-cloud', + 'flow-map' ], linkageExcludeViewType: [ 'richTextView', @@ -200,7 +201,8 @@ export default { 'gauge', 'text', 'label', - 'word-cloud' + 'word-cloud', + 'flow-map' ], copyData: null, hyperlinksSetVisible: false, @@ -218,12 +220,12 @@ export default { linkJumpSetShow() { return this.curComponent.type === 'view' && !this.jumpExcludeViewType.includes(this.curComponent.propValue.innerType) && - !(this.curComponent.propValue.innerType && this.curComponent.propValue.innerType.includes('table') && this.curComponent.propValue.render === 'echarts') + !(this.curComponent.propValue.innerType?.includes('table') && this.curComponent.propValue.render === 'echarts') }, linkageSettingShow() { return this.curComponent.type === 'view' && !this.linkageExcludeViewType.includes(this.curComponent.propValue.innerType) && - !(this.curComponent.propValue.innerType && this.curComponent.propValue.innerType.includes('table') && this.curComponent.propValue.render === 'echarts') + !(this.curComponent.propValue.innerType?.includes('table') && this.curComponent.propValue.render === 'echarts') }, panelInfo() { return this.$store.state.panel.panelInfo diff --git a/frontend/src/icons/svg/bidirectional-bar.svg b/frontend/src/icons/svg/bidirectional-bar.svg new file mode 100644 index 0000000000..a357716cbd --- /dev/null +++ b/frontend/src/icons/svg/bidirectional-bar.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/lang/en.js b/frontend/src/lang/en.js index 69354b8cd6..36d2dd7baa 100644 --- a/frontend/src/lang/en.js +++ b/frontend/src/lang/en.js @@ -1169,6 +1169,7 @@ export default { chart_bar_horizontal: 'Horizontal Bar', chart_bar_stack_horizontal: 'Stack Horizontal Bar', chart_percentage_bar_stack_horizontal: 'Horizontal Percentage Stack Bar', + chart_bidirectional_bar: 'Bidirectional Bar', chart_line: 'Base Line', chart_line_stack: 'Stack Line', chart_pie: 'Pie', diff --git a/frontend/src/lang/tw.js b/frontend/src/lang/tw.js index eebb8c4b66..1f3bce50bb 100644 --- a/frontend/src/lang/tw.js +++ b/frontend/src/lang/tw.js @@ -1168,6 +1168,7 @@ export default { chart_bar_horizontal: '橫嚮柱狀圖', chart_bar_stack_horizontal: '橫嚮堆疊柱狀圖', chart_percentage_bar_stack_horizontal: '橫嚮百分比柱狀圖', + chart_bidirectional_bar: '對稱柱狀圖', chart_line: '基礎摺線圖', chart_line_stack: '堆疊摺線圖', chart_pie: '餅圖', diff --git a/frontend/src/lang/zh.js b/frontend/src/lang/zh.js index 18f7b604de..bf71845835 100644 --- a/frontend/src/lang/zh.js +++ b/frontend/src/lang/zh.js @@ -1,5 +1,3 @@ -import { $confirm } from '@/utils/message' - export default { fu: { search_bar: { @@ -1169,6 +1167,7 @@ export default { chart_bar_horizontal: '横向柱状图', chart_bar_stack_horizontal: '横向堆叠柱状图', chart_percentage_bar_stack_horizontal: '横向百分比柱状图', + chart_bidirectional_bar: '对称柱状图', chart_line: '基础折线图', chart_line_stack: '堆叠折线图', chart_pie: '饼图', diff --git a/frontend/src/views/chart/chart/bar/bar_antv.js b/frontend/src/views/chart/chart/bar/bar_antv.js index 3cb4b0d193..ddd0e33509 100644 --- a/frontend/src/views/chart/chart/bar/bar_antv.js +++ b/frontend/src/views/chart/chart/bar/bar_antv.js @@ -1,4 +1,4 @@ -import { Column, Bar } from '@antv/g2plot' +import { Column, Bar, BidirectionalBar } from '@antv/g2plot' import { getTheme, getLabel, @@ -240,3 +240,110 @@ export function hBaseBarOptionAntV(plot, container, chart, action, isGroup, isSt return plot } + +export function baseBidirectionalBarOptionAntV(plot, container, chart, action, isGroup, isStack) { + // theme + const theme = getTheme(chart) + // attr + const label = getLabel(chart) + const tooltip = getTooltip(chart) + // style + const legend = getLegend(chart) + const xAxis = getXAxis(chart) + const yAxis = getYAxis(chart) + // data + const data = _.cloneDeep(chart.data.data) + // options + const options = { + theme: theme, + data: data, + xField: 'field', + yField: ['value', 'extValue'], + appendPadding: getPadding(chart), + label: label, + tooltip: tooltip, + legend: legend, + xAxis: xAxis, + yAxis: { + value: yAxis, + extValue: yAxis + }, + interactions: [ + { + type: 'legend-active', cfg: { + start: [{ trigger: 'legend-item:mouseenter', action: ['element-active:reset'] }], + end: [{ trigger: 'legend-item:mouseleave', action: ['element-active:reset'] }] + } + }, + { + type: 'legend-filter', cfg: { + start: [{ trigger: 'legend-item:click', action: ['list-unchecked:toggle', 'data-filter:filter', 'element-active:reset', 'element-highlight:reset'] }] + } + }, + { + type: 'tooltip', cfg: { + start: [{ trigger: 'interval:mousemove', action: 'tooltip:show' }], + end: [{ trigger: 'interval:mouseleave', action: 'tooltip:hide' }] + } + }, + { + type: 'active-region', cfg: { + start: [{ trigger: 'interval:mousemove', action: 'active-region:show' }], + end: [{ trigger: 'interval:mouseleave', action: 'active-region:hide' }] + } + } + ] + } + // size + let customAttr = {} + if (chart.customAttr) { + customAttr = JSON.parse(chart.customAttr) + if (customAttr.size) { + const s = JSON.parse(JSON.stringify(customAttr.size)) + if (s.barDefault) { + delete options.marginRatio + } else { + options.marginRatio = s.barGap + } + } + } + // group + if (isGroup) { + options.isGroup = true + } else { + delete options.isGroup + } + // stack + if (isStack) { + options.isStack = true + } else { + delete options.isStack + } + options.isPercent = chart.type.includes('percentage') + // custom color + options.color = antVCustomColor(chart) + if (customAttr.color.gradient) { + options.color = options.color.map((ele, index) => { + return setGradientColor(ele, customAttr.color.gradient, 180 - index * 180) + }) + } + // 处理空值 + if (chart.senior) { + let emptyDataStrategy = JSON.parse(chart.senior)?.functionCfg?.emptyDataStrategy + if (!emptyDataStrategy) { + emptyDataStrategy = 'breakLine' + } + handleEmptyDataStrategy(emptyDataStrategy, chart, data, options) + } + + // 开始渲染 + if (plot) { + plot.destroy() + } + plot = new BidirectionalBar(container, options) + + plot.off('interval:click') + plot.on('interval:click', action) + + return plot +} diff --git a/frontend/src/views/chart/chart/common/common_antv.js b/frontend/src/views/chart/chart/common/common_antv.js index 949381b507..e0c1c8da8e 100644 --- a/frontend/src/views/chart/chart/common/common_antv.js +++ b/frontend/src/views/chart/chart/common/common_antv.js @@ -200,6 +200,17 @@ export function getLabel(chart) { f.formatterCfg.thousandSeparator = false } res = valueFormatter(param.value, f.formatterCfg) + } else if (chart.type === 'bidirectional-bar') { + let yaxis = yAxis[0] + if (param['series-field-key'] === 'extValue') { + yaxis = JSON.parse(chart.yaxisExt)[0] + } + const value = param[param['series-field-key']] + if (yaxis.formatterCfg) { + res = valueFormatter(value, yaxis.formatterCfg) + } else { + res = valueFormatter(value, formatterItem) + } } else if (equalsAny(chart.type, 'bar-group')) { const f = yAxis[0] if (f.formatterCfg) { @@ -344,6 +355,17 @@ export function getTooltip(chart) { res = valueFormatter(param.value, formatterItem) } } + } else if (chart.type === 'bidirectional-bar') { + let yaxis = yAxis[0] + if (param['series-field-key'] === 'extValue') { + yaxis = JSON.parse(chart.yaxisExt)[0] + } + obj = { name: yaxis.name, value: param[param['series-field-key']] } + if (yaxis.formatterCfg) { + res = valueFormatter(obj.value, yaxis.formatterCfg) + } else { + res = valueFormatter(obj.value, formatterItem) + } } else if (chart.type.includes('treemap')) { obj = { name: param.name, value: param.value } for (let i = 0; i < yAxis.length; i++) { @@ -506,7 +528,20 @@ export function getLegend(chart) { marker: { symbol: legendSymbol }, - radio: false // 柱状图图例的聚焦功能,默认先关掉 + radio: false, // 柱状图图例的聚焦功能,默认先关掉 + itemName: { + formatter: (text, item, index) => { + if (chart.type !== 'bidirectional-bar') { + return text + } + const yaxis = JSON.parse(chart.yaxis)[0] + const yaxisExt = JSON.parse(chart.yaxisExt)[0] + if (index === 0) { + return yaxis.name + } + return yaxisExt.name + } + } } } else { legend = false diff --git a/frontend/src/views/chart/chart/util.js b/frontend/src/views/chart/chart/util.js index 6217ad80c3..08324858de 100644 --- a/frontend/src/views/chart/chart/util.js +++ b/frontend/src/views/chart/chart/util.js @@ -1546,6 +1546,81 @@ export const TYPE_CONFIGS = [ ] } }, + { + render: 'antv', + category: 'chart.chart_type_compare', + value: 'bidirectional-bar', + title: 'chart.chart_bidirectional_bar', + icon: 'bidirectional-bar', + properties: [ + 'color-selector', + 'label-selector-ant-v', + 'tooltip-selector-ant-v', + 'x-axis-selector-ant-v', + 'y-axis-selector-ant-v', + 'title-selector-ant-v', + 'legend-selector-ant-v' + ], + propertyInner: { + 'color-selector': [ + 'value', + 'colorPanel', + 'customColor', + 'gradient', + 'alpha' + ], + 'label-selector-ant-v': [ + 'show', + 'fontSize', + 'color', + 'position-h' + ], + 'tooltip-selector-ant-v': [ + 'show', + 'textStyle' + ], + 'x-axis-selector-ant-v': [ + 'show', + 'position', + 'name', + 'nameTextStyle', + 'splitLine', + 'axisForm', + 'axisLabel' + ], + 'y-axis-selector-ant-v': [ + 'show', + 'position', + 'name', + 'nameTextStyle', + 'axisValue', + 'splitLine', + 'axisForm', + 'axisLabel' + ], + 'title-selector-ant-v': [ + 'show', + 'title', + 'fontSize', + 'color', + 'hPosition', + 'isItalic', + 'isBolder', + 'remarkShow', + 'fontFamily', + 'letterSpace', + 'fontShadow' + ], + 'legend-selector-ant-v': [ + 'show', + 'icon', + 'orient', + 'textStyle', + 'hPosition', + 'vPosition' + ] + } + }, { render: 'antv', @@ -1856,7 +1931,7 @@ export const TYPE_CONFIGS = [ 'mapLineAnimate', 'mapLineAnimateDuration', 'mapLineAnimateInterval', - 'mapLineAnimateTrailLength', + 'mapLineAnimateTrailLength' ], 'title-selector-ant-v': [ 'show', @@ -3349,6 +3424,23 @@ export function getColors(chart, colors, reset) { }) } } + } else if (chart.type === 'bidirectional-bar') { + const yaxis = JSON.parse(chart.yaxis)[0] + const yaxisExt = JSON.parse(chart.yaxisExt)[0] + if (yaxis) { + seriesColors.push({ + name: yaxis.name, + color: colors[0], + isCustom: false + }) + } + if (yaxisExt) { + seriesColors.push({ + name: yaxisExt.name, + color: colors[1], + isCustom: false + }) + } } else if (includesAny(chart.type, 'bar', 'scatter', 'radar', 'area') && !chart.type.includes('group')) { if (Object.prototype.toString.call(chart.yaxis) === '[object Array]') { series = JSON.parse(JSON.stringify(chart.yaxis)) diff --git a/frontend/src/views/chart/components/ChartComponentG2.vue b/frontend/src/views/chart/components/ChartComponentG2.vue index 1e8242ef9c..3ef1b888db 100644 --- a/frontend/src/views/chart/components/ChartComponentG2.vue +++ b/frontend/src/views/chart/components/ChartComponentG2.vue @@ -43,7 +43,7 @@ import { baseLiquid } from '@/views/chart/chart/liquid/liquid' import { uuid } from 'vue-uuid' import ViewTrackBar from '@/components/canvas/components/editor/ViewTrackBar' import { getRemark, hexColorToRGBA } from '@/views/chart/chart/util' -import { baseBarOptionAntV, hBaseBarOptionAntV } from '@/views/chart/chart/bar/bar_antv' +import { baseBarOptionAntV, hBaseBarOptionAntV, baseBidirectionalBarOptionAntV } from '@/views/chart/chart/bar/bar_antv' import { baseAreaOptionAntV, baseLineOptionAntV } from '@/views/chart/chart/line/line_antv' import { basePieOptionAntV, basePieRoseOptionAntV } from '@/views/chart/chart/pie/pie_antv' import { baseScatterOptionAntV } from '@/views/chart/chart/scatter/scatter_antv' @@ -277,6 +277,8 @@ export default { this.myChart = baseMixOptionAntV(this.myChart, this.chartId, chart, this.antVAction) } else if (chart.type === 'flow-map') { this.myChart = baseFlowMapOption(this.myChart, this.chartId, chart, this.antVAction) + } else if (chart.type === 'bidirectional-bar') { + this.myChart = baseBidirectionalBarOptionAntV(this.myChart, this.chartId, chart, this.antVAction) } else { if (this.myChart) { this.antVRenderStatus = false diff --git a/frontend/src/views/chart/components/componentStyle/XAxisSelectorAntV.vue b/frontend/src/views/chart/components/componentStyle/XAxisSelectorAntV.vue index 076fdab3ee..7bd74c37d5 100644 --- a/frontend/src/views/chart/components/componentStyle/XAxisSelectorAntV.vue +++ b/frontend/src/views/chart/components/componentStyle/XAxisSelectorAntV.vue @@ -28,8 +28,14 @@ size="mini" @change="changeXAxisStyle('position')" > - {{ $t('chart.text_pos_top') }} - {{ $t('chart.text_pos_bottom') }} +
+ {{ $t('chart.text_pos_top') }} + {{ $t('chart.text_pos_bottom') }} +
+
+ {{ $t('chart.text_pos_left') }} + {{ $t('chart.text_pos_center') }} +
- {{ $t('chart.text_pos_left') }} - {{ $t('chart.text_pos_right') }} +
+ {{ $t('chart.text_pos_left') }} + {{ $t('chart.text_pos_right') }} +
+
+ {{ $t('chart.text_pos_top') }} + {{ $t('chart.text_pos_bottom') }} +
+
+ + {{ $t('chart.show') }} + + + + + + + + + + + + + +
- {{ $t('chart.show') }} - - - - - - - - - - - - - - @@ -88,6 +90,7 @@