feat(图表): 符号地图支持条件样式

#13785
This commit is contained in:
ulleo
2024-12-12 15:49:48 +08:00
committed by dataeaseShu
parent f7bde3adeb
commit 4bfcf011e2
6 changed files with 314 additions and 27 deletions

View File

@@ -1,3 +1,5 @@
import { SymbolicMap } from '@/views/chart/components/js/panel/charts/map/symbolic-map'
/**
* 高级设置
*/
@@ -151,6 +153,8 @@ declare interface ChartThreshold {
* 折线阈值
*/
lineThreshold: TableThreshold[]
symbolicBubbleThreshold: TableThreshold[]
}
declare interface TableThreshold {
/**

View File

@@ -790,6 +790,138 @@ init()
</el-col>
<!--折线-->
<el-col v-show="showProperty('lineThreshold')">
<el-col>
<div class="inner-container">
<span class="label" :class="'label-' + props.themes">{{
$t('visualization.condition_style_set')
}}</span>
<span class="right-btns">
<span
class="set-text-info"
:class="{ 'set-text-info-dark': themes === 'dark' }"
v-if="state.thresholdForm?.tableThreshold?.length > 0"
>
$t('visualization.already_setting')
</span>
<el-button
:title="t('chart.edit')"
:class="'label-' + props.themes"
:style="{ width: '24px', marginLeft: '6px' }"
:disabled="!state.thresholdForm.enable"
class="circle-button"
text
size="small"
@click="editLineThreshold"
>
<template #icon>
<el-icon size="14px">
<Icon name="icon_edit_outlined"><icon_edit_outlined class="svg-icon" /></Icon>
</el-icon>
</template>
</el-button>
</span>
</div>
<div
class="threshold-container"
:class="{ 'threshold-container-dark': themes === 'dark' }"
v-if="state.thresholdForm.lineThreshold?.length > 0"
>
<el-row
v-for="(fieldItem, fieldIndex) in state.thresholdForm.lineThreshold"
:key="fieldIndex"
style="flex-direction: column"
>
<div class="field-style" :class="{ 'field-style-dark': themes === 'dark' }">
<el-icon>
<Icon :className="`field-icon-${fieldType[fieldItem.field.deType]}`"
><component
class="svg-icon"
:class="`field-icon-${fieldType[fieldItem.field.deType]}`"
:is="iconFieldMap[fieldType[fieldItem.field.deType]]"
></component
></Icon>
</el-icon>
<span :title="fieldItem.field.name" class="field-text">{{
fieldItem.field.name
}}</span>
</div>
<div v-for="(item, index) in fieldItem.conditions" :key="index" class="line-style">
<div style="flex: 1">
<span v-if="item.term === 'eq'" :title="t('chart.filter_eq')">
{{ t('chart.filter_eq') }}</span
>
<span v-else-if="item.term === 'not_eq'" :title="t('chart.filter_not_eq')">
{{ t('chart.filter_not_eq') }}</span
>
<span v-if="item.term === 'lt'" :title="t('chart.filter_lt')">
{{ t('chart.filter_lt') }}
</span>
<span v-else-if="item.term === 'gt'" :title="t('chart.filter_gt')">
{{ t('chart.filter_gt') }}
</span>
<span v-else-if="item.term === 'le'" :title="t('chart.filter_le')">
{{ t('chart.filter_le') }}
</span>
<span v-else-if="item.term === 'ge'" :title="t('chart.filter_ge')">
{{ t('chart.filter_ge') }}
</span>
<span v-else-if="item.term === 'between'" :title="t('chart.filter_between')">
{{ t('chart.filter_between') }}
</span>
<span v-else-if="item.term === 'default'" title="默认"> 默认 </span>
</div>
<div v-if="item.type !== 'dynamic'" style="flex: 1; margin: 0 8px">
<span style="margin: 0 8px">
{{ t('chart.fix') }}
</span>
</div>
<div v-else style="flex: 1; margin: 0 8px">
<span style="margin: 0 8px">
{{ t('chart.dynamic') }}
</span>
</div>
<div v-if="item.type !== 'dynamic'" style="flex: 1; margin: 0 8px">
<span
v-if="
!item.term.includes('null') &&
!item.term.includes('default') &&
!item.term.includes('empty') &&
item.term !== 'between'
"
:title="item.value + ''"
>{{ item.value }}</span
>
<span
v-else-if="
!item.term.includes('null') &&
!item.term.includes('empty') &&
item.term === 'between'
"
:title="item.min + ' ≤= ' + t('chart.drag_block_label_value') + ' ≤ ' + item.max"
>
{{ item.min }}&nbsp;≤{{ t('chart.drag_block_label_value') }}≤&nbsp;{{ item.max }}
</span>
<span v-else>&nbsp;</span>
</div>
<template v-if="chart.type !== 'picture-group'">
<div
:title="t('chart.color')"
:style="{
backgroundColor: item.color
}"
class="color-div"
:class="{ 'color-div-dark': themes === 'dark' }"
></div>
</template>
</div>
</el-row>
</div>
</el-col>
</el-col>
<!-- symbolic map -->
<el-col v-show="showProperty('symbolicBubbleThreshold')">
<el-col>
<div class="inner-container">
<span class="label" :class="'label-' + props.themes">{{

View File

@@ -2,7 +2,7 @@
import icon_info_filled from '@/assets/svg/icon_info_filled.svg'
import icon_deleteTrash_outlined from '@/assets/svg/icon_delete-trash_outlined.svg'
import icon_add_outlined from '@/assets/svg/icon_add_outlined.svg'
import { PropType, reactive } from 'vue'
import { computed, PropType, reactive } from 'vue'
import { useI18n } from '@/hooks/web/useI18n'
import { COLOR_PANEL } from '../../../util/chart'
import { fieldType } from '@/utils/attr'
@@ -34,30 +34,84 @@ const thresholdCondition = {
max: '1',
type: 'fixed'
}
const valueOptions = [
{
label: '',
options: [
const valueOptions = computed(() => {
if (props.chart.type === 'symbolic-map') {
return [
{
value: 'lt',
label: t('chart.filter_lt')
label: '',
options: [
{
value: 'eq',
label: t('chart.filter_eq')
},
{
value: 'not_eq',
label: t('chart.filter_not_eq')
}
]
},
{
value: 'gt',
label: t('chart.filter_gt')
}
]
},
{
label: '',
options: [
label: '',
options: [
{
value: 'lt',
label: t('chart.filter_lt')
},
{
value: 'gt',
label: t('chart.filter_gt')
}
]
},
{
value: 'between',
label: t('chart.filter_between')
label: '',
options: [
{
value: 'le',
label: t('chart.filter_le')
},
{
value: 'ge',
label: t('chart.filter_ge')
}
]
},
{
label: '',
options: [
{
value: 'between',
label: t('chart.filter_between')
}
]
}
]
}
]
return [
{
label: '',
options: [
{
value: 'lt',
label: t('chart.filter_lt')
},
{
value: 'gt',
label: t('chart.filter_gt')
}
]
},
{
label: '',
options: [
{
value: 'between',
label: t('chart.filter_between')
}
]
}
]
})
const predefineColors = COLOR_PANEL
const state = reactive({
@@ -76,21 +130,32 @@ const init = () => {
}
const initOptions = (item, fieldObj) => {
if (fieldObj) {
item.options = JSON.parse(JSON.stringify(valueOptions))
item.options = JSON.parse(JSON.stringify(valueOptions.value))
item.conditions &&
item.conditions.forEach(ele => {
ele.term = ''
})
}
}
const isSymbolicMap = computed(() => {
return props.chart.type === 'symbolic-map'
})
const initFields = () => {
let fields = []
const yAxis = JSON.parse(JSON.stringify(props.chart.yAxis))
fields = [...yAxis]
if (isSymbolicMap.value) {
const extBubble = JSON.parse(JSON.stringify(props.chart.extBubble))
fields = [...extBubble]
} else {
const yAxis = JSON.parse(JSON.stringify(props.chart.yAxis))
fields = [...yAxis]
}
state.fields.splice(0, state.fields.length, ...fields)
// 字段不存在时
let change = false
state.thresholdArr.forEach(item => {
item.options = JSON.parse(JSON.stringify(valueOptions.value))
const fieldItemObj = state.fields.filter(ele => ele.id === item.fieldId)
if (fieldItemObj.length === 0) {
change = true
@@ -327,7 +392,10 @@ init()
</el-col>
<el-col :span="3">
<el-form-item class="form-item" :label="t('chart.textColor')">
<el-form-item
class="form-item"
:label="isSymbolicMap ? t('chart.color') : t('chart.textColor')"
>
<el-color-picker
is-custom
size="large"

View File

@@ -6,7 +6,12 @@ import {
L7Wrapper
} from '@/views/chart/components/js/panel/types/impl/l7'
import { MAP_EDITOR_PROPERTY_INNER } from '@/views/chart/components/js/panel/charts/map/common'
import { hexColorToRGBA, parseJson, svgStrToUrl } from '@/views/chart/components/js/util'
import {
getColorFormAlphaColor,
hexColorToRGBA,
parseJson,
svgStrToUrl
} from '@/views/chart/components/js/util'
import { deepCopy } from '@/utils/utils'
import { GaodeMap } from '@antv/l7-maps'
import { Scene } from '@antv/l7-scene'
@@ -15,6 +20,7 @@ import { LayerPopup } from '@antv/l7'
import { mapRendered, mapRendering } from '@/views/chart/components/js/panel/common/common_antv'
import { configCarouselTooltip } from '@/views/chart/components/js/panel/charts/map/tooltip-carousel'
import { DEFAULT_BASIC_STYLE } from '@/views/chart/components/editor/util/chart'
import { filter } from 'lodash-es'
const { t } = useI18n()
/**
@@ -28,7 +34,8 @@ export class SymbolicMap extends L7ChartView<Scene, L7Config> {
'symbolic-style-selector',
'title-selector',
'label-selector',
'tooltip-selector'
'tooltip-selector',
'threshold'
]
propertyInner: EditorPropertyInner = {
...MAP_EDITOR_PROPERTY_INNER,
@@ -52,7 +59,8 @@ export class SymbolicMap extends L7ChartView<Scene, L7Config> {
'show',
'backgroundColor',
'carousel'
]
],
threshold: ['lineThreshold']
}
axis: AxisType[] = ['xAxis', 'xAxisExt', 'extBubble', 'filter', 'extLabel', 'extTooltip']
axisConfig: AxisConfig = {
@@ -228,8 +236,18 @@ export class SymbolicMap extends L7ChartView<Scene, L7Config> {
// 存储已分配的颜色
const colorAssignments = new Map()
const sizeKey = extBubble.length > 0 ? extBubble[0].dataeaseName : ''
//todo 条件颜色
const { threshold } = parseJson(chart.senior)
let conditions = []
if (threshold.enable) {
conditions = threshold.lineThreshold ?? []
}
const extBubbleIds = chart.extBubble.map(i => i.id)
conditions = filter(conditions, c => extBubbleIds.includes(c.fieldId))
const data = chart.data?.tableRow
? chart.data.tableRow.map(item => {
? chart.data.tableRow.map((item, index) => {
// 颜色标识
const identifier = item[xAxisExt[0]?.dataeaseName]
// 检查该标识是否已有颜色分配,如果没有则分配
@@ -239,6 +257,57 @@ export class SymbolicMap extends L7ChartView<Scene, L7Config> {
// 记录分配的颜色
colorAssignments.set(identifier, color)
}
if (conditions.length > 0) {
for (let i = 0; i < conditions.length; i++) {
const c = conditions[i]
const value = item[c.field.dataeaseName]
for (const t of c.conditions) {
const v = t.value
//保存一下颜色到map
const _color = getColorFormAlphaColor(t.color)
if (t.term === 'between') {
const start = parseFloat(t.min)
const end = parseFloat(t.max)
if (start <= value && value <= end) {
color = t.color
colorsWithAlpha[index] = hexColorToRGBA(_color, alpha)
}
} else if ('lt' === t.term) {
if (value < v) {
color = t.color
colorsWithAlpha[index] = hexColorToRGBA(_color, alpha)
}
} else if ('le' === t.term) {
if (value <= v) {
color = t.color
colorsWithAlpha[index] = hexColorToRGBA(_color, alpha)
}
} else if ('gt' === t.term) {
if (value > v) {
color = t.color
colorsWithAlpha[index] = hexColorToRGBA(_color, alpha)
}
} else if ('ge' === t.term) {
if (value >= v) {
color = t.color
colorsWithAlpha[index] = hexColorToRGBA(_color, alpha)
}
} else if ('eq' === t.term) {
if (value === v) {
color = t.color
colorsWithAlpha[index] = hexColorToRGBA(_color, alpha)
}
} else if ('not_eq' === t.term) {
if (value !== v) {
color = t.color
colorsWithAlpha[index] = hexColorToRGBA(_color, alpha)
}
}
}
}
}
return {
...item,
color,

View File

@@ -1373,7 +1373,8 @@ export const TOOLTIP_TPL =
export function getConditions(chart: Chart) {
const { threshold } = parseJson(chart.senior)
const annotations = []
if (!threshold.enable || chart.type === 'area-stack') return annotations
if (!threshold.enable || chart.type === 'area-stack' || chart.type === 'symbolic-map')
return annotations
const conditions = threshold.lineThreshold ?? []
const yAxisIds = chart.yAxis.map(i => i.id)
for (const field of conditions) {

View File

@@ -999,6 +999,19 @@ export function isAlphaColor(color: string): boolean {
return false
}
export function getColorFormAlphaColor(color: string): string {
if (isAlphaColor(color)) {
if (color.startsWith('#')) {
return color.slice(0, 7)
}
if (color.startsWith('rgb') || color.startsWith('RGB')) {
const list = color.split(',')
return list[0] + ',' + list[1] + ',' + list[2] + ')'
}
}
return color
}
export function isTransparent(color: string): boolean {
if (!color?.trim()) {
return true