import type { FunnelOptions, Funnel as G2Funnel } from '@antv/g2plot/esm/plots/funnel'
|
import { G2PlotChartView, G2PlotDrawOptions } from '../../types/impl/g2plot'
|
import { flow, parseJson, setUpSingleDimensionSeriesColor } from '@/views/chart/components/js/util'
|
import { configPlotTooltipEvent, getPadding } from '../../common/common_antv'
|
import { useI18n } from '@/hooks/web/useI18n'
|
import { Datum } from '@antv/g2plot/esm/types/common'
|
import { valueFormatter } from '@/views/chart/components/js/formatter'
|
|
const { t } = useI18n()
|
|
/**
|
* 漏斗图
|
*/
|
export class Funnel extends G2PlotChartView<FunnelOptions, G2Funnel> {
|
properties: EditorProperty[] = [
|
'background-overall-component',
|
'border-style',
|
'basic-style-selector',
|
'label-selector',
|
'tooltip-selector',
|
'title-selector',
|
'legend-selector',
|
'jump-set',
|
'linkage'
|
]
|
propertyInner: EditorPropertyInner = {
|
'background-overall-component': ['all'],
|
'border-style': ['all'],
|
'basic-style-selector': ['colors', 'alpha', 'seriesColor'],
|
'label-selector': ['fontSize', 'color', 'hPosition', 'showQuota', 'conversionTag'],
|
'tooltip-selector': ['color', 'fontSize', 'backgroundColor', 'seriesTooltipFormatter', 'show'],
|
'title-selector': [
|
'show',
|
'title',
|
'fontSize',
|
'color',
|
'hPosition',
|
'isItalic',
|
'isBolder',
|
'remarkShow',
|
'fontFamily',
|
'letterSpace',
|
'fontShadow'
|
],
|
'legend-selector': ['icon', 'orient', 'color', 'fontSize', 'hPosition', 'vPosition']
|
}
|
axis: AxisType[] = ['xAxis', 'yAxis', 'filter', 'drill', 'extLabel', 'extTooltip']
|
axisConfig: AxisConfig = {
|
xAxis: {
|
name: `${t('chart.drag_block_funnel_split')} / ${t('chart.dimension')}`,
|
type: 'd'
|
},
|
yAxis: {
|
name: `${t('chart.drag_block_funnel_width')} / ${t('chart.quota')}`,
|
type: 'q',
|
limit: 1
|
}
|
}
|
|
async drawChart(drawOptions: G2PlotDrawOptions<G2Funnel>): Promise<G2Funnel> {
|
const { chart, container, action } = drawOptions
|
if (!chart.data?.data) {
|
return
|
}
|
const data = chart.data.data
|
const baseOptions: FunnelOptions = {
|
data,
|
xField: 'field',
|
yField: 'value',
|
appendPadding: getPadding(chart),
|
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' }]
|
}
|
}
|
],
|
meta: {
|
field: {
|
type: 'cat'
|
}
|
}
|
}
|
const options = this.setupOptions(chart, baseOptions)
|
const { Funnel: G2Funnel } = await import('@antv/g2plot/esm/plots/funnel')
|
const newChart = new G2Funnel(container, options)
|
newChart.on('interval:click', action)
|
configPlotTooltipEvent(chart, newChart)
|
return newChart
|
}
|
|
protected configLabel(chart: Chart, options: FunnelOptions): FunnelOptions {
|
let label
|
let conversionTag
|
let customAttr: DeepPartial<ChartAttr>
|
if (chart.customAttr) {
|
customAttr = parseJson(chart.customAttr)
|
const showQuota = customAttr.label.showQuota
|
const l = customAttr.label
|
if (customAttr.label?.show) {
|
// label
|
if (showQuota) {
|
const layout = []
|
if (!l.fullDisplay) {
|
layout.push(...[{ type: 'hide-overlap' }, { type: 'limit-in-plot' }])
|
}
|
label = {
|
position: l.position,
|
layout,
|
style: {
|
fill: l.color,
|
fontSize: l.fontSize
|
},
|
formatter: function (param: Datum) {
|
return valueFormatter(param.value, l.quotaLabelFormatter)
|
}
|
}
|
const position = label.position
|
if (position === 'right') {
|
label.offsetX = -40
|
}
|
}
|
// 转化率
|
const conversionTagAtt = parseJson(chart.customAttr).label.conversionTag
|
if (conversionTagAtt && conversionTagAtt.show) {
|
conversionTag = {
|
style: {
|
fill: l.color,
|
fontSize: l.fontSize
|
},
|
formatter: datum => {
|
if (!datum['$$conversion$$'][0]) {
|
return `${conversionTagAtt.text ?? ''} -`
|
}
|
const rate = (
|
(datum['$$conversion$$'][1] / datum['$$conversion$$'][0]) *
|
100
|
).toFixed(conversionTagAtt.precision)
|
return `${conversionTagAtt.text ?? ''} ${rate}%`
|
}
|
}
|
}
|
}
|
return {
|
...options,
|
label,
|
conversionTag,
|
maxSize: conversionTag ? 0.8 : 1
|
}
|
}
|
return options
|
}
|
|
public setupSeriesColor(chart: ChartObj, data?: any[]): ChartBasicStyle['seriesColor'] {
|
return setUpSingleDimensionSeriesColor(chart, data)
|
}
|
protected setupOptions(chart: Chart, options: FunnelOptions): FunnelOptions {
|
return flow(
|
this.configTheme,
|
this.configSingleDimensionColor,
|
this.configLabel,
|
this.configMultiSeriesTooltip,
|
this.configLegend
|
)(chart, options)
|
}
|
setupDefaultOptions(chart: ChartObj): ChartObj {
|
const { customAttr, customStyle } = chart
|
const { label } = customAttr
|
if (!['left', 'middle', 'right'].includes(label.position)) {
|
label.position = 'middle'
|
}
|
customAttr.label = {
|
...label,
|
show: true,
|
showQuota: true,
|
conversionTag: {
|
show: false,
|
precision: 2,
|
text: t('chart.conversion_rate')
|
}
|
}
|
const { legend } = customStyle
|
legend.show = false
|
return chart
|
}
|
|
constructor() {
|
super('funnel', [])
|
}
|
}
|