mirror of
https://github.com/dataease/dataease.git
synced 2026-05-21 04:08:10 +08:00
feat(图表): 堆叠条形图/柱状图可以设置图例排序 #16424
This commit is contained in:
@@ -103,6 +103,18 @@ const changeMisc = prop => {
|
||||
}
|
||||
|
||||
const legendSort = ref()
|
||||
const sortAxis = computed(() => {
|
||||
if (props.chart.type === 'line') {
|
||||
return 'xAxisExt'
|
||||
}
|
||||
return 'extStack'
|
||||
})
|
||||
const legendSortDisabled = computed(() => {
|
||||
if (props.chart?.type === 'line') {
|
||||
return !props.chart.xAxisExt?.length
|
||||
}
|
||||
return !props.chart?.extStack?.length
|
||||
})
|
||||
const init = () => {
|
||||
legendSort.value?.blur()
|
||||
const chart = JSON.parse(JSON.stringify(props.chart))
|
||||
@@ -234,7 +246,11 @@ const getMapCustomRange = index => {
|
||||
const customSort = []
|
||||
const changeLegendSort = sort => {
|
||||
if (sort === 'custom') {
|
||||
state.customSortField = cloneDeep(props.chart.xAxisExt?.[0])
|
||||
if (props.chart.type === 'line') {
|
||||
state.customSortField = cloneDeep(props.chart.xAxisExt?.[0])
|
||||
} else {
|
||||
state.customSortField = cloneDeep(props.chart.extStack?.[0])
|
||||
}
|
||||
if (!state.customSortField) {
|
||||
return
|
||||
}
|
||||
@@ -718,7 +734,7 @@ onMounted(() => {
|
||||
v-model="state.legendForm.sort"
|
||||
size="small"
|
||||
:effect="themes"
|
||||
:disabled="!chart.xAxisExt?.length"
|
||||
:disabled="legendSortDisabled"
|
||||
ref="legendSort"
|
||||
@change="changeLegendSort"
|
||||
>
|
||||
@@ -744,7 +760,7 @@ onMounted(() => {
|
||||
class="dialog-css custom_sort_dialog"
|
||||
>
|
||||
<custom-sort-edit
|
||||
field-type="xAxisExt"
|
||||
:field-type="sortAxis"
|
||||
:chart="chart"
|
||||
:field="state.customSortField"
|
||||
:origin-sort-list="state.legendForm.customSort"
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import type { Column, ColumnOptions } from '@antv/g2plot/esm/plots/column'
|
||||
import { cloneDeep, each, groupBy, isEmpty } from 'lodash-es'
|
||||
import { cloneDeep, defaults, each, groupBy, isEmpty } from 'lodash-es'
|
||||
import {
|
||||
G2PlotChartView,
|
||||
G2PlotDrawOptions
|
||||
} from '@/views/chart/components/js/panel/types/impl/g2plot'
|
||||
import {
|
||||
convertToAlphaColor,
|
||||
flow,
|
||||
hexColorToRGBA,
|
||||
isAlphaColor,
|
||||
parseJson,
|
||||
setUpGroupSeriesColor,
|
||||
setUpStackSeriesColor
|
||||
@@ -28,7 +30,11 @@ import {
|
||||
TOOLTIP_TPL
|
||||
} from '@/views/chart/components/js/panel/common/common_antv'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { DEFAULT_BASIC_STYLE, DEFAULT_LABEL } from '@/views/chart/components/editor/util/chart'
|
||||
import {
|
||||
DEFAULT_BASIC_STYLE,
|
||||
DEFAULT_LABEL,
|
||||
DEFAULT_LEGEND_STYLE
|
||||
} from '@/views/chart/components/editor/util/chart'
|
||||
import { clearExtremum, extremumEvt } from '@/views/chart/components/js/extremumUitl'
|
||||
import { Group } from '@antv/g-canvas'
|
||||
|
||||
@@ -267,7 +273,7 @@ export class Bar extends G2PlotChartView<ColumnOptions, Column> {
|
||||
* 堆叠柱状图
|
||||
*/
|
||||
export class StackBar extends Bar {
|
||||
properties = BAR_EDITOR_PROPERTY.filter(ele => ele !== 'threshold')
|
||||
properties: EditorProperty[] = BAR_EDITOR_PROPERTY.filter(ele => ele !== 'threshold')
|
||||
propertyInner = {
|
||||
...this['propertyInner'],
|
||||
'label-selector': [
|
||||
@@ -286,7 +292,8 @@ export class StackBar extends Bar {
|
||||
'tooltipFormatter',
|
||||
'show',
|
||||
'carousel'
|
||||
]
|
||||
],
|
||||
'legend-selector': [...BAR_EDITOR_PROPERTY_INNER['legend-selector'], 'legendSort']
|
||||
}
|
||||
protected configLabel(chart: Chart, options: ColumnOptions): ColumnOptions {
|
||||
let label = getLabel(chart)
|
||||
@@ -393,6 +400,113 @@ export class StackBar extends Bar {
|
||||
return options
|
||||
}
|
||||
|
||||
protected configSortedLegend(chart: Chart, options: ColumnOptions): ColumnOptions {
|
||||
const optionTmp = super.configLegend(chart, options)
|
||||
if (!optionTmp.legend) {
|
||||
return optionTmp
|
||||
}
|
||||
const extStack = chart.extStack[0]
|
||||
if (extStack?.customSort?.length > 0) {
|
||||
// 图例自定义排序
|
||||
const sort = extStack.customSort ?? []
|
||||
if (sort?.length) {
|
||||
// 用值域限定排序,有可能出现新数据但是未出现在图表上,所以这边要遍历一下子维度,加到后面,让新数据显示出来
|
||||
const data = optionTmp.data
|
||||
const cats =
|
||||
data?.reduce((p, n) => {
|
||||
const cat = n['category']
|
||||
if (cat && !p.includes(cat)) {
|
||||
p.push(cat)
|
||||
}
|
||||
return p
|
||||
}, []) || []
|
||||
const values = sort.reduce((p, n) => {
|
||||
if (cats.includes(n)) {
|
||||
const index = cats.indexOf(n)
|
||||
if (index !== -1) {
|
||||
cats.splice(index, 1)
|
||||
}
|
||||
p.push(n)
|
||||
}
|
||||
return p
|
||||
}, [])
|
||||
cats.length > 0 && values.push(...cats)
|
||||
optionTmp.meta = {
|
||||
...optionTmp.meta,
|
||||
category: {
|
||||
type: 'cat',
|
||||
values
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const customStyle = parseJson(chart.customStyle)
|
||||
let size
|
||||
if (customStyle && customStyle.legend) {
|
||||
size = defaults(JSON.parse(JSON.stringify(customStyle.legend)), DEFAULT_LEGEND_STYLE).size
|
||||
} else {
|
||||
size = DEFAULT_LEGEND_STYLE.size
|
||||
}
|
||||
|
||||
optionTmp.legend.marker.style = style => {
|
||||
return {
|
||||
r: size,
|
||||
fill: style.fill
|
||||
}
|
||||
}
|
||||
const { sort, customSort, icon } = customStyle.legend
|
||||
if (sort && sort !== 'none' && chart.extStack.length) {
|
||||
const customAttr = parseJson(chart.customAttr)
|
||||
const { basicStyle } = customAttr
|
||||
const seriesMap =
|
||||
basicStyle.seriesColor?.reduce((p, n) => {
|
||||
p[n.id] = n
|
||||
return p
|
||||
}, {}) || {}
|
||||
const dupCheck = new Set()
|
||||
const colors = optionTmp.color ?? optionTmp.theme.styleSheet.paletteQualitative10
|
||||
const items = optionTmp.data?.reduce((arr, item) => {
|
||||
if (!dupCheck.has(item.category)) {
|
||||
const fill = seriesMap[item.category]?.color ?? colors[dupCheck.size % colors.length]
|
||||
dupCheck.add(item.category)
|
||||
arr.push({
|
||||
name: item.category,
|
||||
value: item.category,
|
||||
marker: {
|
||||
symbol: icon,
|
||||
style: {
|
||||
r: size,
|
||||
fill: isAlphaColor(fill) ? fill : convertToAlphaColor(fill, basicStyle.alpha)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
return arr
|
||||
}, [])
|
||||
if (sort !== 'custom') {
|
||||
items.sort((a, b) => {
|
||||
return sort !== 'desc' ? a.name.localeCompare(b.name) : b.name.localeCompare(a.name)
|
||||
})
|
||||
} else {
|
||||
const tmp = []
|
||||
;(customSort || []).forEach(item => {
|
||||
const index = items.findIndex(i => i.name === item)
|
||||
if (index !== -1) {
|
||||
tmp.push(items[index])
|
||||
items.splice(index, 1)
|
||||
}
|
||||
})
|
||||
items.unshift(...tmp)
|
||||
}
|
||||
optionTmp.legend.items = items
|
||||
if (extStack?.customSort?.length > 0) {
|
||||
delete optionTmp.meta?.category.values
|
||||
}
|
||||
}
|
||||
return optionTmp
|
||||
}
|
||||
|
||||
public setupSeriesColor(chart: ChartObj, data?: any[]): ChartBasicStyle['seriesColor'] {
|
||||
return setUpStackSeriesColor(chart, data)
|
||||
}
|
||||
@@ -406,7 +520,7 @@ export class StackBar extends Bar {
|
||||
this.configBasicStyle,
|
||||
this.configLabel,
|
||||
this.configTooltip,
|
||||
this.configLegend,
|
||||
this.configSortedLegend,
|
||||
this.configXAxis,
|
||||
this.configYAxis,
|
||||
this.configSlider,
|
||||
@@ -437,7 +551,8 @@ export class GroupBar extends StackBar {
|
||||
properties = BAR_EDITOR_PROPERTY
|
||||
propertyInner = {
|
||||
...this['propertyInner'],
|
||||
'label-selector': [...BAR_EDITOR_PROPERTY_INNER['label-selector'], 'vPosition', 'showExtremum']
|
||||
'label-selector': [...BAR_EDITOR_PROPERTY_INNER['label-selector'], 'vPosition', 'showExtremum'],
|
||||
'legend-selector': BAR_EDITOR_PROPERTY_INNER['legend-selector']
|
||||
}
|
||||
axisConfig = {
|
||||
...this['axisConfig'],
|
||||
@@ -589,7 +704,8 @@ export class GroupBar extends StackBar {
|
||||
export class GroupStackBar extends StackBar {
|
||||
propertyInner = {
|
||||
...this['propertyInner'],
|
||||
'label-selector': [...BAR_EDITOR_PROPERTY_INNER['label-selector'], 'vPosition']
|
||||
'label-selector': [...BAR_EDITOR_PROPERTY_INNER['label-selector'], 'vPosition'],
|
||||
'legend-selector': BAR_EDITOR_PROPERTY_INNER['legend-selector']
|
||||
}
|
||||
protected configTheme(chart: Chart, options: ColumnOptions): ColumnOptions {
|
||||
const baseOptions = super.configTheme(chart, options)
|
||||
|
||||
@@ -12,10 +12,12 @@ import {
|
||||
setGradientColor,
|
||||
TOOLTIP_TPL
|
||||
} from '@/views/chart/components/js/panel/common/common_antv'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { cloneDeep, defaults } from 'lodash-es'
|
||||
import {
|
||||
convertToAlphaColor,
|
||||
flow,
|
||||
hexColorToRGBA,
|
||||
isAlphaColor,
|
||||
parseJson,
|
||||
setUpStackSeriesColor
|
||||
} from '@/views/chart/components/js/util'
|
||||
@@ -27,7 +29,11 @@ import {
|
||||
} from '@/views/chart/components/js/panel/charts/bar/common'
|
||||
import type { Datum } from '@antv/g2plot/esm/types/common'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { DEFAULT_BASIC_STYLE, DEFAULT_LABEL } from '@/views/chart/components/editor/util/chart'
|
||||
import {
|
||||
DEFAULT_BASIC_STYLE,
|
||||
DEFAULT_LABEL,
|
||||
DEFAULT_LEGEND_STYLE
|
||||
} from '@/views/chart/components/editor/util/chart'
|
||||
import { Group } from '@antv/g-canvas'
|
||||
|
||||
const { t } = useI18n()
|
||||
@@ -311,7 +317,8 @@ export class HorizontalStackBar extends HorizontalBar {
|
||||
propertyInner = {
|
||||
...this['propertyInner'],
|
||||
'label-selector': ['color', 'fontSize', 'hPosition', 'labelFormatter'],
|
||||
'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'tooltipFormatter', 'show']
|
||||
'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'tooltipFormatter', 'show'],
|
||||
'legend-selector': [...BAR_EDITOR_PROPERTY_INNER['legend-selector'], 'legendSort']
|
||||
}
|
||||
protected configLabel(chart: Chart, options: BarOptions): BarOptions {
|
||||
const baseOptions = super.configLabel(chart, options)
|
||||
@@ -406,6 +413,113 @@ export class HorizontalStackBar extends HorizontalBar {
|
||||
return options
|
||||
}
|
||||
|
||||
protected configLegend(chart: Chart, options: BarOptions): BarOptions {
|
||||
const optionTmp = super.configLegend(chart, options)
|
||||
if (!optionTmp.legend) {
|
||||
return optionTmp
|
||||
}
|
||||
const extStack = chart.extStack[0]
|
||||
if (extStack?.customSort?.length > 0) {
|
||||
// 图例自定义排序
|
||||
const sort = extStack.customSort ?? []
|
||||
if (sort?.length) {
|
||||
// 用值域限定排序,有可能出现新数据但是未出现在图表上,所以这边要遍历一下子维度,加到后面,让新数据显示出来
|
||||
const data = optionTmp.data
|
||||
const cats =
|
||||
data?.reduce((p, n) => {
|
||||
const cat = n['category']
|
||||
if (cat && !p.includes(cat)) {
|
||||
p.push(cat)
|
||||
}
|
||||
return p
|
||||
}, []) || []
|
||||
const values = sort.reduce((p, n) => {
|
||||
if (cats.includes(n)) {
|
||||
const index = cats.indexOf(n)
|
||||
if (index !== -1) {
|
||||
cats.splice(index, 1)
|
||||
}
|
||||
p.push(n)
|
||||
}
|
||||
return p
|
||||
}, [])
|
||||
cats.length > 0 && values.push(...cats)
|
||||
optionTmp.meta = {
|
||||
...optionTmp.meta,
|
||||
category: {
|
||||
type: 'cat',
|
||||
values
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const customStyle = parseJson(chart.customStyle)
|
||||
let size
|
||||
if (customStyle && customStyle.legend) {
|
||||
size = defaults(JSON.parse(JSON.stringify(customStyle.legend)), DEFAULT_LEGEND_STYLE).size
|
||||
} else {
|
||||
size = DEFAULT_LEGEND_STYLE.size
|
||||
}
|
||||
|
||||
optionTmp.legend.marker.style = style => {
|
||||
return {
|
||||
r: size,
|
||||
fill: style.fill
|
||||
}
|
||||
}
|
||||
const { sort, customSort, icon } = customStyle.legend
|
||||
if (sort && sort !== 'none' && chart.extStack.length) {
|
||||
const customAttr = parseJson(chart.customAttr)
|
||||
const { basicStyle } = customAttr
|
||||
const seriesMap =
|
||||
basicStyle.seriesColor?.reduce((p, n) => {
|
||||
p[n.id] = n
|
||||
return p
|
||||
}, {}) || {}
|
||||
const dupCheck = new Set()
|
||||
const colors = optionTmp.color ?? optionTmp.theme.styleSheet.paletteQualitative10
|
||||
const items = optionTmp.data?.reduce((arr, item) => {
|
||||
if (!dupCheck.has(item.category)) {
|
||||
const fill = seriesMap[item.category]?.color ?? colors[dupCheck.size % colors.length]
|
||||
dupCheck.add(item.category)
|
||||
arr.push({
|
||||
name: item.category,
|
||||
value: item.category,
|
||||
marker: {
|
||||
symbol: icon,
|
||||
style: {
|
||||
r: size,
|
||||
fill: isAlphaColor(fill) ? fill : convertToAlphaColor(fill, basicStyle.alpha)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
return arr
|
||||
}, [])
|
||||
if (sort !== 'custom') {
|
||||
items.sort((a, b) => {
|
||||
return sort !== 'desc' ? a.name.localeCompare(b.name) : b.name.localeCompare(a.name)
|
||||
})
|
||||
} else {
|
||||
const tmp = []
|
||||
;(customSort || []).forEach(item => {
|
||||
const index = items.findIndex(i => i.name === item)
|
||||
if (index !== -1) {
|
||||
tmp.push(items[index])
|
||||
items.splice(index, 1)
|
||||
}
|
||||
})
|
||||
items.unshift(...tmp)
|
||||
}
|
||||
optionTmp.legend.items = items
|
||||
if (extStack?.customSort?.length > 0) {
|
||||
delete optionTmp.meta?.category.values
|
||||
}
|
||||
}
|
||||
return optionTmp
|
||||
}
|
||||
|
||||
protected setupOptions(chart: Chart, options: BarOptions): BarOptions {
|
||||
return flow(
|
||||
this.configTheme,
|
||||
|
||||
Reference in New Issue
Block a user