feat(图表-词云图): 支持自定义数据区间

This commit is contained in:
jianneng-fit2cloud 2024-08-15 16:24:23 +08:00
parent ee48b0236d
commit b3fd99036c
8 changed files with 245 additions and 61 deletions

View File

@ -705,6 +705,28 @@ declare interface ChartMiscAttr {
}
}
}
/**
* 词云图轴值配置
*/
wordCloudAxisValueRange: {
/**
* 自动轴值
*/
auto: boolean
/**
* 最小值
*/
min: number
/**
* 最大值
*/
max: number
/**
* 轴值字段
*/
fieldId: string
}
}
/**
* 动态极值配置

View File

@ -318,29 +318,32 @@ export function recursionTransObj(template, infoObj, scale, terminal) {
for (const templateKey in template) {
// 如果是数组 进行赋值计算
if (template[templateKey] instanceof Array) {
template[templateKey].forEach(templateProp => {
if (
infoObj[templateKey] &&
(infoObj[templateKey][templateProp] || infoObj[templateKey].length)
) {
// 移动端特殊属性值设置
if (terminal === 'mobile' && mobileSpecialProps[templateProp] !== undefined) {
infoObj[templateKey][templateProp] = mobileSpecialProps[templateProp]
} else {
// 数组依次设置
if (infoObj[templateKey] instanceof Array) {
infoObj[templateKey].forEach(v => {
v[templateProp] = getScaleValue(v[templateProp], scale)
})
// 词云图的大小区间不需要缩放
template[templateKey]
.filter(field => field !== 'wordSizeRange')
.forEach(templateProp => {
if (
infoObj[templateKey] &&
(infoObj[templateKey][templateProp] || infoObj[templateKey].length)
) {
// 移动端特殊属性值设置
if (terminal === 'mobile' && mobileSpecialProps[templateProp] !== undefined) {
infoObj[templateKey][templateProp] = mobileSpecialProps[templateProp]
} else {
infoObj[templateKey][templateProp] = getScaleValue(
infoObj[templateKey][templateProp],
scale
)
// 数组依次设置
if (infoObj[templateKey] instanceof Array) {
infoObj[templateKey].forEach(v => {
v[templateProp] = getScaleValue(v[templateProp], scale)
})
} else {
infoObj[templateKey][templateProp] = getScaleValue(
infoObj[templateKey][templateProp],
scale
)
}
}
}
}
})
})
} else if (typeof template[templateKey] === 'string') {
// 一级字段为字符串直接赋值
infoObj[templateKey] = getScaleValue(infoObj[templateKey], scale)

View File

@ -5,6 +5,7 @@ import { DEFAULT_MISC } from '@/views/chart/components/editor/util/chart'
import { ElMessage, ElRow } from 'element-plus-secondary'
import { fieldType } from '@/utils/attr'
import { cloneDeep, defaultsDeep } from 'lodash-es'
import { useEmitt } from '@/hooks/web/useEmitt'
const { t } = useI18n()
@ -18,6 +19,10 @@ const props = withDefaults(
{ themes: 'dark' }
)
useEmitt({
name: 'word-cloud-default-data-range',
callback: args => wordCloudDefaultDataRange(args)
})
const emit = defineEmits(['onMiscChange'])
watch(
@ -175,7 +180,11 @@ const isValidField = field => {
}
const showProperty = prop => props.propertyInner?.includes(prop)
const wordCloudDefaultDataRange = args => {
state.miscForm.wordCloudAxisValueRange.max = args.data.max
state.miscForm.wordCloudAxisValueRange.min = args.data.min
state.miscForm.wordCloudAxisValueRange.fieldId = props.chart.yAxis?.[0]?.id
}
onMounted(() => {
initField()
init()
@ -561,35 +570,109 @@ onMounted(() => {
<!--liquid-end-->
<!-- word-cloud start -->
<el-form-item
v-show="showProperty('wordSizeRange')"
class="form-item"
:label="t('chart.word_size_range')"
:class="'form-item-' + themes"
>
<el-slider
v-model="state.miscForm.wordSizeRange"
range
size="small"
:effect="themes"
:min="1"
:max="100"
@change="changeMisc('wordSizeRange')"
/>
</el-form-item>
<el-form-item
v-show="showProperty('wordSpacing')"
class="form-item form-item-slider"
:label="t('chart.word_spacing')"
:class="'form-item-' + themes"
>
<el-slider
v-model="state.miscForm.wordSpacing"
:min="0"
:max="20"
@change="changeMisc('wordSpacing')"
/>
</el-form-item>
<template v-if="showProperty('wordCloudAxisValueRange')">
<div style="display: flex; flex-direction: row; justify-content: space-between">
<label class="custom-form-item-label" :class="'custom-form-item-label--' + themes">
{{ t('chart.axis_value') }}
<el-tooltip class="item" :effect="toolTip" placement="top">
<template #content><span v-html="t('chart.axis_tip')"></span></template>
<span style="vertical-align: middle">
<el-icon style="cursor: pointer">
<Icon name="icon_info_outlined" />
</el-icon>
</span>
</el-tooltip>
</label>
<el-form-item class="form-item" :class="'form-item-' + themes">
<el-checkbox
size="small"
:effect="props.themes"
v-model="state.miscForm.wordCloudAxisValueRange.auto"
@change="changeMisc()"
>
{{ t('chart.axis_auto') }}
</el-checkbox>
</el-form-item>
</div>
<template
v-if="
showProperty('wordCloudAxisValueRange') && !state.miscForm.wordCloudAxisValueRange.auto
"
>
<el-row :gutter="8">
<el-col :span="12">
<el-form-item
class="form-item"
:class="'form-item-' + themes"
:label="t('chart.axis_value_max')"
>
<el-input-number
controls-position="right"
:effect="props.themes"
v-model="state.miscForm.wordCloudAxisValueRange.max"
@change="changeMisc()"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item
class="form-item"
:class="'form-item-' + themes"
:label="t('chart.axis_value_min')"
>
<el-input-number
:effect="props.themes"
controls-position="right"
v-model="state.miscForm.wordCloudAxisValueRange.min"
@change="changeMisc()"
/>
</el-form-item>
</el-col>
</el-row>
</template>
</template>
<div class="alpha-setting" v-if="showProperty('wordSizeRange')">
<label class="alpha-label" :class="{ dark: 'dark' === themes }">
{{ t('chart.word_size_range') }}
</label>
<el-row style="flex: 1" :gutter="8">
<el-col :span="24">
<el-form-item class="form-item alpha-slider" :class="'form-item-' + themes">
<el-slider
v-model="state.miscForm.wordSizeRange"
range
:effect="themes"
:min="1"
:max="100"
@change="changeMisc()"
/>
</el-form-item>
</el-col>
</el-row>
</div>
<div class="alpha-setting" v-if="showProperty('wordSpacing')">
<label class="alpha-label" :class="{ dark: 'dark' === themes }">
{{ t('chart.word_spacing') }}
</label>
<el-row style="flex: 1" :gutter="8">
<el-col :span="24">
<el-form-item
v-show="showProperty('wordSpacing')"
class="form-item alpha-slider"
:class="'form-item-' + themes"
>
<el-slider
v-model="state.miscForm.wordSpacing"
:min="0"
:max="20"
@change="changeMisc()"
/>
</el-form-item>
</el-col>
</el-row>
</div>
<!-- word-cloud end -->
</el-form>
</template>
@ -628,4 +711,34 @@ onMounted(() => {
box-shadow: 0 0 0 1px rgb(245, 74, 69) inset !important;
}
}
.alpha-setting {
display: flex;
width: 100%;
.alpha-slider {
padding: 0 8px;
:deep(.ed-slider__button-wrapper) {
--ed-slider-button-wrapper-size: 36px;
--ed-slider-button-size: 16px;
}
}
.alpha-label {
padding-right: 8px;
font-size: 12px;
font-style: normal;
font-weight: 400;
height: 32px;
line-height: 32px;
display: inline-flex;
align-items: flex-start;
min-width: 56px;
&.dark {
color: #a6a6a6;
}
}
}
</style>

View File

@ -298,6 +298,12 @@ export const DEFAULT_MISC: ChartMiscAttr = {
speed: 0.01
}
}
},
wordCloudAxisValueRange: {
auto: true,
min: 0,
max: 0,
fieldId: undefined
}
}

View File

@ -8,9 +8,9 @@ import {
flow,
getDynamicColorScale,
getGeoJsonFile,
setMapChartDefaultMaxAndMinValueByData,
hexColorToRGBA,
parseJson
parseJson,
getMaxAndMinValueByData
} from '@/views/chart/components/js/util'
import {
handleGeoJson,
@ -81,7 +81,7 @@ export class Map extends L7PlotChartView<ChoroplethOptions, Choropleth> {
if (!misc.mapAutoLegend && legend.show) {
let minValue = misc.mapLegendMin
let maxValue = misc.mapLegendMax
setMapChartDefaultMaxAndMinValueByData(sourceData, maxValue, minValue, (max, min) => {
getMaxAndMinValueByData(sourceData, 'value', maxValue, minValue, (max, min) => {
maxValue = max
minValue = min
action({
@ -202,7 +202,7 @@ export class Map extends L7PlotChartView<ChoroplethOptions, Choropleth> {
let maxValue = misc.mapLegendMax
if (legend.show) {
let mapLegendNumber = misc.mapLegendNumber
setMapChartDefaultMaxAndMinValueByData(sourceData, maxValue, minValue, (max, min) => {
getMaxAndMinValueByData(sourceData, 'value', maxValue, minValue, (max, min) => {
maxValue = max
minValue = min
mapLegendNumber = 9

View File

@ -3,7 +3,12 @@ import {
G2PlotDrawOptions
} from '@/views/chart/components/js/panel/types/impl/g2plot'
import type { WordCloud as G2WordCloud, WordCloudOptions } from '@antv/g2plot/esm/plots/word-cloud'
import { flow, parseJson } from '@/views/chart/components/js/util'
import {
filterChartDataByRange,
flow,
getMaxAndMinValueByData,
parseJson
} from '@/views/chart/components/js/util'
import { getPadding } from '@/views/chart/components/js/panel/common/common_antv'
import { valueFormatter } from '@/views/chart/components/js/formatter'
import { useI18n } from '@/hooks/web/useI18n'
@ -42,7 +47,7 @@ export class WordCloud extends G2PlotChartView<WordCloudOptions, G2WordCloud> {
'letterSpace',
'fontShadow'
],
'misc-selector': ['wordSizeRange', 'wordSpacing'],
'misc-selector': ['wordSizeRange', 'wordSpacing', 'wordCloudAxisValueRange'],
'tooltip-selector': ['color', 'fontSize', 'backgroundColor', 'seriesTooltipFormatter', 'show']
}
axis: AxisType[] = ['xAxis', 'yAxis', 'filter']
@ -58,12 +63,35 @@ export class WordCloud extends G2PlotChartView<WordCloudOptions, G2WordCloud> {
limit: 1
}
}
setDataRange = (action, maxValue, minValue) => {
action({
from: 'word-cloud',
data: {
max: maxValue,
min: minValue
}
})
}
async drawChart(drawOptions: G2PlotDrawOptions<G2WordCloud>): Promise<G2WordCloud> {
const { chart, container, action } = drawOptions
if (chart?.data) {
// data
const data = chart.data.data
let data = chart.data.data
const { misc } = parseJson(chart.customAttr)
let minValue = 0
let maxValue = 0
if (
!misc.wordCloudAxisValueRange?.auto &&
misc.wordCloudAxisValueRange?.fieldId === chart.yAxis[0].id
) {
minValue = misc.wordCloudAxisValueRange.min
maxValue = misc.wordCloudAxisValueRange.max
}
getMaxAndMinValueByData(data ?? [], 'value', maxValue, minValue, (max, min) => {
maxValue = max
minValue = min
})
data = filterChartDataByRange(data ?? [], maxValue, minValue)
// options
const initOptions: WordCloudOptions = {
data: data,
@ -84,6 +112,12 @@ export class WordCloud extends G2PlotChartView<WordCloudOptions, G2WordCloud> {
const options = this.setupOptions(chart, initOptions)
const { WordCloud: G2WordCloud } = await import('@antv/g2plot/esm/plots/word-cloud')
const newChart = new G2WordCloud(container, options)
newChart.on('click', () => {
this.setDataRange(action, maxValue, minValue)
})
newChart.on('afterrender', () => {
this.setDataRange(action, maxValue, minValue)
})
newChart.on('point:click', param => {
action({ x: param.x, y: param.y, data: { data: param.data.data.datum } })
})

View File

@ -609,24 +609,26 @@ export const filterChartDataByRange = (data: any[], maxValue: number, minValue:
}
/**
* 获取地图默认最大最小值根据数据
* 获取数据最大最小值
* @param data
* @param field 值字段
* @param maxValue
* @param minValue
* @param callback
*/
export const setMapChartDefaultMaxAndMinValueByData = (
export const getMaxAndMinValueByData = (
data: any[],
field: string,
maxValue: number,
minValue: number,
callback: (max: number, min: number) => void
) => {
if (minValue === 0 && maxValue === 0) {
const maxResult = data.reduce((max, current) => {
return current.value > max ? current.value : max
return current[field] > max ? current[field] : max
}, Number.MIN_SAFE_INTEGER)
const minResult = data.reduce((min, current) => {
return current.value < min ? current.value : min
return current[field] < min ? current[field] : min
}, Number.MAX_SAFE_INTEGER)
callback(maxResult, minResult)
}

View File

@ -313,6 +313,10 @@ const action = param => {
emitter.emit('map-default-range', param)
return
}
if (param.from === 'word-cloud') {
emitter.emit('word-cloud-default-data-range', param)
return
}
state.pointParam = param.data
//
pointClickTrans()