feat(图表): 词云图支持配置字号大小区间和文字间距

This commit is contained in:
wisonic-s 2024-04-18 12:05:35 +08:00
parent b862aa52d4
commit f5b89fd339
7 changed files with 371 additions and 590 deletions

View File

@ -1107,7 +1107,9 @@ export default {
add_condition: '添加条件',
chart_quadrant: '象限图',
quadrant: '象限',
font_size: '字号'
font_size: '字号',
word_size_range: '字号区间',
word_spacing: '文字间隔'
},
dataset: {
scope_edit: '仅编辑时生效',

View File

@ -542,6 +542,14 @@ declare interface ChartMiscAttr {
* 指标/文本卡垂直位置
*/
vPosition: 'top' | 'center' | 'bottom'
/**
* 词云图字体大小区间
*/
wordSizeRange: [number, number]
/**
* 词云图文字间距
*/
wordSpacing: number
}
/**
* 动态极值配置

View File

@ -121,7 +121,9 @@ export const customAttrTrans = {
'valueFontSize',
'spaceSplit', // 间隔
'scatterSymbolSize', // 气泡大小散点图
'radarSize' // 雷达占比
'radarSize', // 雷达占比
'wordSizeRange',
'wordSpacing'
],
label: ['fontSize'],
tooltip: ['fontSize'],
@ -278,6 +280,13 @@ export const mobileSpecialProps = {
}
export function getScaleValue(propValue, scale) {
if (propValue instanceof Array) {
propValue.forEach((v, i) => {
const val = Math.round(v * scale)
propValue[i] = val > 1 ? val : 1
})
return propValue
}
const propValueTemp = Math.round(propValue * scale)
return propValueTemp > 1 ? propValueTemp : 1
}

View File

@ -1,19 +1,16 @@
<script lang="ts" setup>
import { computed, onMounted, reactive, watch } from 'vue'
import { useI18n } from '@/hooks/web/useI18n'
import {
CHART_FONT_FAMILY,
DEFAULT_MISC,
CHART_FONT_LETTER_SPACE
} from '@/views/chart/components/editor/util/chart'
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'
const { t } = useI18n()
const props = withDefaults(
defineProps<{
chart: any
chart: ChartObj
themes?: EditorTheme
quotaFields: Array<any>
propertyInner?: Array<string>
@ -66,10 +63,6 @@ const liquidShapeOptions = [
{ name: t('chart.liquid_shape_rect'), value: 'rect' }
]
const fontFamily = CHART_FONT_FAMILY
const fontLetterSpace = CHART_FONT_LETTER_SPACE
const changeMisc = (prop = '', refresh = false) => {
if (state.miscForm.gaugeMax <= state.miscForm.gaugeMin) {
ElMessage.error(t('chart.max_more_than_mix'))
@ -77,30 +70,9 @@ const changeMisc = (prop = '', refresh = false) => {
emit('onMiscChange', { data: state.miscForm, requestData: refresh }, prop)
}
const fontSizeList = computed(() => {
const arr = []
for (let i = 10; i <= 60; i = i + 2) {
arr.push({
name: i + '',
value: i
})
}
return arr
})
const init = () => {
const chart = JSON.parse(JSON.stringify(props.chart))
if (chart.customAttr) {
let customAttr = null
if (Object.prototype.toString.call(chart.customAttr) === '[object Object]') {
customAttr = JSON.parse(JSON.stringify(chart.customAttr))
} else {
customAttr = JSON.parse(chart.customAttr)
}
if (customAttr.misc) {
state.miscForm = customAttr.misc
}
}
const misc = cloneDeep(props.chart.customAttr.misc)
state.miscForm = defaultsDeep(misc, cloneDeep(DEFAULT_MISC)) as ChartMiscAttr
}
const initField = () => {
@ -202,6 +174,8 @@ const isValidField = field => {
return field.id !== '-1' && state.quotaData.findIndex(ele => ele.id === field.id) !== -1
}
const showProperty = prop => props.propertyInner?.includes(prop)
onMounted(() => {
initField()
init()
@ -210,8 +184,8 @@ onMounted(() => {
<template>
<el-form :model="state.miscForm">
<el-row :gutter="8" v-if="!props.chart.type.includes('liquid')">
<el-col :span="12">
<el-row :gutter="8">
<el-col :span="12" v-show="showProperty('gaugeStartAngle')">
<el-form-item
:label="t('chart.start_angle')"
class="form-item"
@ -228,7 +202,7 @@ onMounted(() => {
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-col :span="12" v-show="showProperty('gaugeEndAngle')">
<el-form-item
:label="t('chart.end_angle')"
class="form-item"
@ -248,10 +222,10 @@ onMounted(() => {
</el-row>
<!--gauge-begin-->
<template v-if="props.chart.type.includes('gauge')">
<el-form-item
:label="t('chart.min')"
v-show="showProperty('gaugeMinType')"
class="form-item margin-bottom-8"
:label="t('chart.min')"
:class="'form-item-' + themes"
>
<el-radio-group
@ -265,7 +239,7 @@ onMounted(() => {
</el-radio-group>
</el-form-item>
<el-form-item
v-if="state.miscForm.gaugeMinType === 'fix'"
v-if="showProperty('gaugeMin') && state.miscForm.gaugeMinType === 'fix'"
class="form-item"
:class="'form-item-' + themes"
>
@ -277,7 +251,10 @@ onMounted(() => {
@change="changeMisc('gaugeMin')"
/>
</el-form-item>
<el-row :gutter="8" v-if="state.miscForm.gaugeMinType === 'dynamic'">
<el-row
:gutter="8"
v-if="showProperty('gaugeMinField') && state.miscForm.gaugeMinType === 'dynamic'"
>
<el-col :span="12">
<el-form-item class="form-item" :class="'form-item-' + themes">
<el-select
@ -342,8 +319,9 @@ onMounted(() => {
</el-row>
<el-form-item
:label="t('chart.max')"
v-show="showProperty('gaugeMaxType')"
class="form-item margin-bottom-8"
:label="t('chart.max')"
:class="'form-item-' + themes"
>
<el-radio-group
@ -356,7 +334,7 @@ onMounted(() => {
</el-radio-group>
</el-form-item>
<el-form-item
v-if="state.miscForm.gaugeMaxType === 'fix'"
v-if="showProperty('gaugeMax') && state.miscForm.gaugeMaxType === 'fix'"
class="form-item"
:class="'form-item-' + themes"
>
@ -368,7 +346,10 @@ onMounted(() => {
@change="changeMisc('gaugeMax')"
/>
</el-form-item>
<el-row :gutter="8" v-if="state.miscForm.gaugeMaxType === 'dynamic'">
<el-row
:gutter="8"
v-if="showProperty('gaugeMaxField') && state.miscForm.gaugeMaxType === 'dynamic'"
>
<el-col :span="12">
<el-form-item class="form-item" :class="'form-item-' + themes">
<el-select
@ -431,16 +412,15 @@ onMounted(() => {
</el-form-item>
</el-col>
</el-row>
</template>
<!--gauge-end-->
<!--liquid-begin-->
<template v-if="props.chart.type.includes('liquid')">
<el-row :gutter="8">
<el-col :span="12">
<el-form-item
:label="t('chart.liquid_shape')"
v-show="showProperty('liquidShape')"
class="form-item"
:label="t('chart.liquid_shape')"
:class="'form-item-' + themes"
>
<el-select
@ -460,8 +440,9 @@ onMounted(() => {
</el-col>
<el-col :span="12">
<el-form-item
:label="t('chart.radar_size')"
v-show="showProperty('liquidSize')"
class="form-item"
:label="t('chart.radar_size')"
:class="'form-item-' + themes"
>
<el-input-number
@ -478,8 +459,9 @@ onMounted(() => {
</el-row>
<el-form-item
:label="t('chart.liquid_max')"
v-show="showProperty('liquidMaxType')"
class="form-item margin-bottom-8"
:label="t('chart.liquid_max')"
:class="'form-item-' + themes"
>
<el-radio-group
@ -496,7 +478,7 @@ onMounted(() => {
</el-form-item>
<el-form-item
v-if="state.miscForm.liquidMaxType === 'fix'"
v-if="showProperty('liquidMaxType') && state.miscForm.liquidMaxType === 'fix'"
class="form-item"
:class="'form-item-' + themes"
>
@ -510,7 +492,10 @@ onMounted(() => {
/>
</el-form-item>
<el-row :gutter="8" v-if="state.miscForm.liquidMaxType === 'dynamic'">
<el-row
:gutter="8"
v-if="showProperty('liquidMaxField') && state.miscForm.liquidMaxType === 'dynamic'"
>
<el-col :span="12">
<el-form-item class="form-item" :class="'form-item-' + themes">
<el-select
@ -573,268 +558,39 @@ onMounted(() => {
</el-form-item>
</el-col>
</el-row>
</template>
<!--liquid-end-->
<!--text&label-start-->
<template v-if="props.chart.type.includes('indicator') || props.chart.type.includes('label')">
<!-- word-cloud start -->
<el-form-item
:label="t('chart.quota_font_size')"
v-show="showProperty('wordSizeRange')"
class="form-item"
:label="t('chart.word_size_range')"
:class="'form-item-' + themes"
>
<el-select
<el-slider
v-model="state.miscForm.wordSizeRange"
range
size="small"
:effect="themes"
v-model="state.miscForm.quotaFontSize"
:placeholder="t('chart.quota_font_size')"
@change="changeMisc('quotaFontSize')"
>
<el-option
v-for="option in fontSizeList"
:key="option.value"
:label="option.name"
:value="option.value"
:min="1"
:max="100"
@change="changeMisc('wordSizeRange')"
/>
</el-select>
</el-form-item>
<el-form-item
:label="t('chart.quota_font_family')"
class="form-item"
v-show="showProperty('wordSpacing')"
class="form-item form-item-slider"
:label="t('chart.word_spacing')"
:class="'form-item-' + themes"
>
<el-select
:effect="themes"
v-model="state.miscForm.quotaFontFamily"
:placeholder="t('chart.quota_font_family')"
@change="changeMisc('quotaFontFamily')"
>
<el-option
v-for="option in fontFamily"
:key="option.value"
:label="option.name"
:value="option.value"
/>
</el-select>
</el-form-item>
<el-form-item
:label="t('chart.quota_text_style')"
class="form-item"
:class="'form-item-' + themes"
>
<el-checkbox
size="small"
:effect="themes"
v-model="state.miscForm.quotaFontIsItalic"
@change="changeMisc('quotaFontIsItalic')"
>
{{ t('chart.italic') }}
</el-checkbox>
<el-checkbox
size="small"
:effect="themes"
v-model="state.miscForm.quotaFontIsBolder"
@change="changeMisc('quotaFontIsBolder')"
>
{{ t('chart.bolder') }}
</el-checkbox>
</el-form-item>
<el-form-item
:label="t('chart.quota_letter_space')"
class="form-item"
:class="'form-item-' + themes"
>
<el-select
:effect="themes"
v-model="state.miscForm.quotaLetterSpace"
:placeholder="t('chart.quota_letter_space')"
@change="changeMisc('quotaLetterSpace')"
>
<el-option
v-for="option in fontLetterSpace"
:key="option.value"
:label="option.name"
:value="option.value"
/>
</el-select>
</el-form-item>
<el-form-item
:label="t('chart.font_shadow')"
class="form-item"
:class="'form-item-' + themes"
>
<el-checkbox
size="small"
:effect="themes"
v-model="state.miscForm.quotaFontShadow"
@change="changeMisc('quotaFontShadow')"
>{{ t('chart.font_shadow') }}</el-checkbox
>
</el-form-item>
<el-divider />
<el-form-item
:label="t('chart.dimension_show')"
class="form-item"
:class="'form-item-' + themes"
>
<el-checkbox
size="small"
:effect="themes"
v-model="state.miscForm.dimensionShow"
@change="changeMisc('dimensionShow')"
>
{{ t('chart.show') }}
</el-checkbox>
</el-form-item>
<template v-if="state.miscForm.dimensionShow">
<el-form-item
:label="t('chart.dimension_font_size')"
class="form-item"
:class="'form-item-' + themes"
>
<el-select
:effect="themes"
v-model="state.miscForm.dimensionFontSize"
:placeholder="t('chart.dimension_font_size')"
@change="changeMisc('dimensionFontSize')"
>
<el-option
v-for="option in fontSizeList"
:key="option.value"
:label="option.name"
:value="option.value"
/>
</el-select>
</el-form-item>
<el-form-item
:label="t('chart.dimension_font_family')"
class="form-item"
:class="'form-item-' + themes"
>
<el-select
:effect="themes"
v-model="state.miscForm.dimensionFontFamily"
:placeholder="t('chart.dimension_font_family')"
@change="changeMisc('dimensionFontFamily')"
>
<el-option
v-for="option in fontFamily"
:key="option.value"
:label="option.name"
:value="option.value"
/>
</el-select>
</el-form-item>
<el-form-item
:label="t('chart.dimension_text_style')"
class="form-item"
:class="'form-item-' + themes"
>
<el-checkbox
size="small"
:effect="themes"
v-model="state.miscForm.dimensionFontIsItalic"
@change="changeMisc('dimensionFontIsItalic')"
>
{{ t('chart.italic') }}
</el-checkbox>
<el-checkbox
size="small"
:effect="themes"
v-model="state.miscForm.dimensionFontIsBolder"
@change="changeMisc('dimensionFontIsBolder')"
>
{{ t('chart.bolder') }}
</el-checkbox>
</el-form-item>
<el-form-item
:label="t('chart.dimension_letter_space')"
class="form-item"
:class="'form-item-' + themes"
>
<el-select
:effect="themes"
v-model="state.miscForm.dimensionLetterSpace"
:placeholder="t('chart.dimension_letter_space')"
@change="changeMisc('dimensionLetterSpace')"
>
<el-option
v-for="option in fontLetterSpace"
:key="option.value"
:label="option.name"
:value="option.value"
/>
</el-select>
</el-form-item>
<el-form-item
:label="t('chart.font_shadow')"
class="form-item"
:class="'form-item-' + themes"
>
<el-checkbox
size="small"
:effect="themes"
v-model="state.miscForm.dimensionFontShadow"
@change="changeMisc('dimensionFontShadow')"
>
{{ t('chart.font_shadow') }}
</el-checkbox>
</el-form-item>
<el-divider />
<el-form-item
:label="t('chart.space_split')"
class="form-item"
:class="'form-item-' + themes"
>
<el-input-number
:effect="themes"
v-model="state.miscForm.spaceSplit"
<el-slider
v-model="state.miscForm.wordSpacing"
:min="0"
size="small"
controls-position="right"
@change="changeMisc('spaceSplit')"
:max="20"
@change="changeMisc('wordSpacing')"
/>
</el-form-item>
<el-form-item
:label="t('chart.h_position')"
class="form-item"
:class="'form-item-' + themes"
>
<el-select
:effect="themes"
v-model="state.miscForm.hPosition"
:placeholder="t('chart.h_position')"
@change="changeMisc('hPosition')"
>
<el-option value="start" :label="t('chart.p_left')">{{ t('chart.p_left') }}</el-option>
<el-option value="center" :label="t('chart.p_center')">{{
t('chart.p_center')
}}</el-option>
<el-option value="end" :label="t('chart.p_right')">{{ t('chart.p_right') }}</el-option>
</el-select>
</el-form-item>
<el-form-item
:label="t('chart.v_position')"
class="form-item"
:class="'form-item-' + themes"
>
<el-select
:effect="themes"
v-model="state.miscForm.vPosition"
:placeholder="t('chart.v_position')"
@change="changeMisc('vPosition')"
>
<el-option value="start" :label="t('chart.p_top')">{{ t('chart.p_top') }}</el-option>
<el-option value="center" :label="t('chart.p_center')">{{
t('chart.p_center')
}}</el-option>
<el-option value="end" :label="t('chart.p_bottom')">{{
t('chart.p_bottom')
}}</el-option>
</el-select>
</el-form-item>
</template>
</template>
<!--text&label-end-->
<!-- word-cloud end -->
</el-form>
</template>

View File

@ -248,7 +248,9 @@ export const DEFAULT_MISC: ChartMiscAttr = {
mapLineAnimateDuration: 3,
mapLineGradient: false,
mapLineSourceColor: '#146C94',
mapLineTargetColor: '#576CBC'
mapLineTargetColor: '#576CBC',
wordSizeRange: [8, 32],
wordSpacing: 6
}
export const DEFAULT_MARK = {

View File

@ -25,7 +25,7 @@ export class Liquid extends G2PlotChartView<LiquidOptions, G2Liquid> {
'background-overall-component': ['all'],
'basic-style-selector': ['colors', 'alpha'],
'label-selector': ['fontSize', 'color', 'labelFormatter'],
'misc-selector': ['liquidShape', 'liquidMaxType', 'liquidMaxField'],
'misc-selector': ['liquidShape', 'liquidSize', 'liquidMaxType', 'liquidMaxField'],
'title-selector': [
'title',
'fontSize',

View File

@ -8,6 +8,7 @@ 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'
import { isEmpty } from 'lodash-es'
import { DEFAULT_MISC } from '@/views/chart/components/editor/util/chart'
const { t } = useI18n()
const DEFAULT_DATA = []
@ -20,6 +21,7 @@ export class WordCloud extends G2PlotChartView<WordCloudOptions, G2WordCloud> {
'background-overall-component',
'title-selector',
'tooltip-selector',
'misc-selector',
'jump-set',
'linkage'
]
@ -38,6 +40,7 @@ export class WordCloud extends G2PlotChartView<WordCloudOptions, G2WordCloud> {
'letterSpace',
'fontShadow'
],
'misc-selector': ['wordSizeRange', 'wordSpacing'],
'tooltip-selector': ['color', 'fontSize', 'backgroundColor', 'seriesTooltipFormatter']
}
axis: AxisType[] = ['xAxis', 'yAxis', 'filter']
@ -58,6 +61,7 @@ export class WordCloud extends G2PlotChartView<WordCloudOptions, G2WordCloud> {
if (chart?.data) {
// data
const data = chart.data.data
const { misc } = parseJson(chart.customAttr)
// options
const initOptions: WordCloudOptions = {
data: data,
@ -66,9 +70,9 @@ export class WordCloud extends G2PlotChartView<WordCloudOptions, G2WordCloud> {
colorField: 'field',
wordStyle: {
fontFamily: 'Verdana',
fontSize: [8, 32],
fontSize: (misc.wordSizeRange ?? DEFAULT_MISC.wordSizeRange) as [number, number],
rotation: [0, 0],
padding: 6
padding: misc.wordSpacing ?? DEFAULT_MISC.wordSpacing
},
random: () => 0.5,
appendPadding: getPadding(chart),