feat(图表): 新增流向地图

This commit is contained in:
jianneng-fit2cloud 2024-05-22 20:31:45 +08:00
parent af9629747e
commit 8cc1aab59f
17 changed files with 725 additions and 40 deletions

View File

@ -0,0 +1,11 @@
<svg width="80" height="56" viewBox="0 0 80 56" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M41 2.00391V53.0016V54.0016H38V2.00391H41Z" fill="#434343"/>
<path d="M4.5 50C4.22386 50 4 49.7761 4 49.5L4 42.5C4 42.2239 4.22386 42 4.5 42H35.5C35.7761 42 36 42.2239 36 42.5V49.5C36 49.7761 35.7761 50 35.5 50H4.5Z" fill="#00D6B9"/>
<path d="M12.5 38C12.2239 38 12 37.7761 12 37.5V30.5C12 30.2239 12.2239 30 12.5 30H35.5C35.7761 30 36 30.2239 36 30.5V37.5C36 37.7761 35.7761 38 35.5 38H12.5Z" fill="#00D6B9"/>
<path d="M43.5 14C43.2239 14 43 13.7761 43 13.5V6.5C43 6.22386 43.2239 6 43.5 6L71.5 6C71.7761 6 72 6.22386 72 6.5V13.5C72 13.7761 71.7761 14 71.5 14L43.5 14Z" fill="#3370FF"/>
<path d="M43.5 26C43.2239 26 43 25.7761 43 25.5V18.5C43 18.2239 43.2239 18 43.5 18H62.5C62.7761 18 63 18.2239 63 18.5V25.5C63 25.7761 62.7761 26 62.5 26H43.5Z" fill="#3370FF"/>
<path d="M43.5 38C43.2239 38 43 37.7761 43 37.5V30.5C43 30.2239 43.2239 30 43.5 30H57.5C57.7761 30 58 30.2239 58 30.5V37.5C58 37.7761 57.7761 38 57.5 38H43.5Z" fill="#3370FF"/>
<path d="M43.5 50C43.2239 50 43 49.7761 43 49.5V42.5C43 42.2239 43.2239 42 43.5 42H75.5C75.7761 42 76 42.2239 76 42.5V49.5C76 49.7761 75.7761 50 75.5 50H43.5Z" fill="#3370FF"/>
<path d="M16.5 26C16.2239 26 16 25.7761 16 25.5V18.5C16 18.2239 16.2239 18 16.5 18H35.5C35.7761 18 36 18.2239 36 18.5V25.5C36 25.7761 35.7761 26 35.5 26H16.5Z" fill="#00D6B9"/>
<path d="M8.5 14C8.22386 14 8 13.7761 8 13.5V6.5C8 6.22386 8.22386 6 8.5 6L35.5 6C35.7761 6 36 6.22386 36 6.5V13.5C36 13.7761 35.7761 14 35.5 14L8.5 14Z" fill="#00D6B9"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,11 @@
<svg width="80" height="56" viewBox="0 0 80 56" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M41 2.00391V53.0016V54.0016H38V2.00391H41Z" fill="#DEE0E3"/>
<path d="M4.5 50C4.22386 50 4 49.7761 4 49.5L4 42.5C4 42.2239 4.22386 42 4.5 42H35.5C35.7761 42 36 42.2239 36 42.5V49.5C36 49.7761 35.7761 50 35.5 50H4.5Z" fill="#00D6B9"/>
<path d="M12.5 38C12.2239 38 12 37.7761 12 37.5V30.5C12 30.2239 12.2239 30 12.5 30H35.5C35.7761 30 36 30.2239 36 30.5V37.5C36 37.7761 35.7761 38 35.5 38H12.5Z" fill="#00D6B9"/>
<path d="M43.5 14C43.2239 14 43 13.7761 43 13.5V6.5C43 6.22386 43.2239 6 43.5 6L71.5 6C71.7761 6 72 6.22386 72 6.5V13.5C72 13.7761 71.7761 14 71.5 14L43.5 14Z" fill="#3370FF"/>
<path d="M43.5 26C43.2239 26 43 25.7761 43 25.5V18.5C43 18.2239 43.2239 18 43.5 18H62.5C62.7761 18 63 18.2239 63 18.5V25.5C63 25.7761 62.7761 26 62.5 26H43.5Z" fill="#3370FF"/>
<path d="M43.5 38C43.2239 38 43 37.7761 43 37.5V30.5C43 30.2239 43.2239 30 43.5 30H57.5C57.7761 30 58 30.2239 58 30.5V37.5C58 37.7761 57.7761 38 57.5 38H43.5Z" fill="#3370FF"/>
<path d="M43.5 50C43.2239 50 43 49.7761 43 49.5V42.5C43 42.2239 43.2239 42 43.5 42H75.5C75.7761 42 76 42.2239 76 42.5V49.5C76 49.7761 75.7761 50 75.5 50H43.5Z" fill="#3370FF"/>
<path d="M16.5 26C16.2239 26 16 25.7761 16 25.5V18.5C16 18.2239 16.2239 18 16.5 18H35.5C35.7761 18 36 18.2239 36 18.5V25.5C36 25.7761 35.7761 26 35.5 26H16.5Z" fill="#00D6B9"/>
<path d="M8.5 14C8.22386 14 8 13.7761 8 13.5V6.5C8 6.22386 8.22386 6 8.5 6L35.5 6C35.7761 6 36 6.22386 36 6.5V13.5C36 13.7761 35.7761 14 35.5 14L8.5 14Z" fill="#00D6B9"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,11 @@
<svg width="80" height="56" viewBox="0 0 80 56" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7 2.00391V53.0016V54.0016H4V2.00391H7Z" fill="#434343"/>
<path d="M9.5 14C9.22386 14 9 13.7761 9 13.5V6.5C9 6.22386 9.22386 6 9.5 6L71.5 6C71.7761 6 72 6.22386 72 6.5V13.5C72 13.7761 71.7761 14 71.5 14L9.5 14Z" fill="#434343"/>
<path d="M9.5 26C9.22386 26 9 25.7761 9 25.5V18.5C9 18.2239 9.22386 18 9.5 18H71.5C71.7761 18 72 18.2239 72 18.5V25.5C72 25.7761 71.7761 26 71.5 26L9.5 26Z" fill="#434343"/>
<path d="M9.5 38C9.22386 38 9 37.7761 9 37.5V30.5C9 30.2239 9.22386 30 9.5 30H71.5C71.7761 30 72 30.2239 72 30.5V37.5C72 37.7761 71.7761 38 71.5 38H9.5Z" fill="#434343"/>
<path d="M9.5 50C9.22386 50 9 49.7761 9 49.5V42.5C9 42.2239 9.22386 42 9.5 42H71.5C71.7761 42 72 42.2239 72 42.5V49.5C72 49.7761 71.7761 50 71.5 50H9.5Z" fill="#434343"/>
<path d="M9.5 14C9.22386 14 9 13.7761 9 13.5V6.5C9 6.22386 9.22386 6 9.5 6L59.5 6C59.7761 6 60 6.22386 60 6.5V13.5C60 13.7761 59.7761 14 59.5 14L9.5 14Z" fill="#3370FF"/>
<path d="M9.5 26C9.22386 26 9 25.7761 9 25.5V18.5C9 18.2239 9.22386 18 9.5 18H46.5C46.7761 18 47 18.2239 47 18.5V25.5C47 25.7761 46.7761 26 46.5 26H9.5Z" fill="#3370FF"/>
<path d="M9.5 38C9.22386 38 9 37.7761 9 37.5V30.5C9 30.2239 9.22386 30 9.5 30H37.5C37.7761 30 38 30.2239 38 30.5V37.5C38 37.7761 37.7761 38 37.5 38H9.5Z" fill="#3370FF"/>
<path d="M9.5 50C9.22386 50 9 49.7761 9 49.5V42.5C9 42.2239 9.22386 42 9.5 42H21.5C21.7761 42 22 42.2239 22 42.5V49.5C22 49.7761 21.7761 50 21.5 50H9.5Z" fill="#3370FF"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,11 @@
<svg width="80" height="56" viewBox="0 0 80 56" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7 2.00391V53.0016V54.0016H4V2.00391H7Z" fill="#DEE0E3"/>
<path d="M9.5 14C9.22386 14 9 13.7761 9 13.5V6.5C9 6.22386 9.22386 6 9.5 6L71.5 6C71.7761 6 72 6.22386 72 6.5V13.5C72 13.7761 71.7761 14 71.5 14L9.5 14Z" fill="#DEE0E3"/>
<path d="M9.5 26C9.22386 26 9 25.7761 9 25.5V18.5C9 18.2239 9.22386 18 9.5 18H71.5C71.7761 18 72 18.2239 72 18.5V25.5C72 25.7761 71.7761 26 71.5 26L9.5 26Z" fill="#DEE0E3"/>
<path d="M9.5 38C9.22386 38 9 37.7761 9 37.5V30.5C9 30.2239 9.22386 30 9.5 30H71.5C71.7761 30 72 30.2239 72 30.5V37.5C72 37.7761 71.7761 38 71.5 38H9.5Z" fill="#DEE0E3"/>
<path d="M9.5 50C9.22386 50 9 49.7761 9 49.5V42.5C9 42.2239 9.22386 42 9.5 42H71.5C71.7761 42 72 42.2239 72 42.5V49.5C72 49.7761 71.7761 50 71.5 50H9.5Z" fill="#DEE0E3"/>
<path d="M9.5 14C9.22386 14 9 13.7761 9 13.5V6.5C9 6.22386 9.22386 6 9.5 6L59.5 6C59.7761 6 60 6.22386 60 6.5V13.5C60 13.7761 59.7761 14 59.5 14L9.5 14Z" fill="#3370FF"/>
<path d="M9.5 26C9.22386 26 9 25.7761 9 25.5V18.5C9 18.2239 9.22386 18 9.5 18H46.5C46.7761 18 47 18.2239 47 18.5V25.5C47 25.7761 46.7761 26 46.5 26H9.5Z" fill="#3370FF"/>
<path d="M9.5 38C9.22386 38 9 37.7761 9 37.5V30.5C9 30.2239 9.22386 30 9.5 30H37.5C37.7761 30 38 30.2239 38 30.5V37.5C38 37.7761 37.7761 38 37.5 38H9.5Z" fill="#3370FF"/>
<path d="M9.5 50C9.22386 50 9 49.7761 9 49.5V42.5C9 42.2239 9.22386 42 9.5 42H21.5C21.7761 42 22 42.2239 22 42.5V49.5C22 49.7761 21.7761 50 21.5 50H9.5Z" fill="#3370FF"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 739 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 742 KiB

View File

@ -294,7 +294,7 @@ const active = computed(() => {
})
const boardMoveActive = computed(() => {
const CHARTS = ['map', 'bubble-map', 'table-info', 'table-normal', 'table-pivot']
const CHARTS = ['flow-map', 'map', 'bubble-map', 'table-info', 'table-normal', 'table-pivot']
return CHARTS.includes(element.value.innerType)
})

View File

@ -232,7 +232,7 @@ declare interface ChartBasicStyle {
/**
* 对称柱状图方向
*/
layout: 'horizontal' | 'vertical'
layout?: 'horizontal' | 'vertical'
}
/**
* 表头属性
@ -482,6 +482,10 @@ declare interface ChartMiscAttr {
* 地图线条宽度
*/
mapLineWidth: number
/**
* 流向地图动画
*/
mapLineAnimate?: boolean
/**
* 流向地图动画间隔
*/

View File

@ -221,6 +221,7 @@ watch(
:themes="themes"
:chart="chart"
@onBasicStyleChange="onBasicStyleChange"
@onMiscChange="onMiscChange"
/>
</el-collapse-item>
<collapse-switch-item

View File

@ -1,6 +1,10 @@
<script setup lang="ts">
import { onMounted, PropType, reactive, watch } from 'vue'
import { COLOR_PANEL, DEFAULT_BASIC_STYLE } from '@/views/chart/components/editor/util/chart'
import {
COLOR_PANEL,
DEFAULT_BASIC_STYLE,
DEFAULT_MISC
} from '@/views/chart/components/editor/util/chart'
import { useI18n } from '@/hooks/web/useI18n'
import CustomColorStyleSelect from '@/views/chart/components/editor/editor-style/components/CustomColorStyleSelect.vue'
import { cloneDeep, defaultsDeep } from 'lodash-es'
@ -28,6 +32,7 @@ const showProperty = prop => props.propertyInner?.includes(prop)
const predefineColors = COLOR_PANEL
const state = reactive({
basicStyleForm: JSON.parse(JSON.stringify(DEFAULT_BASIC_STYLE)) as ChartBasicStyle,
miscForm: JSON.parse(JSON.stringify(DEFAULT_MISC)) as ChartMiscAttr,
customColor: null,
colorIndex: 0,
fieldColumnWidth: {
@ -38,6 +43,7 @@ const state = reactive({
watch(
[
() => props.chart.customAttr.basicStyle,
() => props.chart.customAttr.misc,
() => props.chart.customAttr.tableHeader,
() => props.chart.xAxis,
() => props.chart.yAxis
@ -47,14 +53,19 @@ watch(
},
{ deep: true }
)
const emit = defineEmits(['onBasicStyleChange'])
const emit = defineEmits(['onBasicStyleChange', 'onMiscChange'])
const changeBasicStyle = (prop?: string, requestData = false) => {
emit('onBasicStyleChange', { data: state.basicStyleForm, requestData }, prop)
}
const changeMisc = prop => {
emit('onMiscChange', { data: state.miscForm, requestData: true }, prop)
}
const init = () => {
const basicStyle = cloneDeep(props.chart.customAttr.basicStyle)
const miscStyle = cloneDeep(props.chart.customAttr.misc)
configCompat(basicStyle)
state.basicStyleForm = defaultsDeep(basicStyle, cloneDeep(DEFAULT_BASIC_STYLE)) as ChartBasicStyle
state.miscForm = defaultsDeep(miscStyle, cloneDeep(DEFAULT_MISC)) as ChartMiscAttr
if (!state.customColor) {
state.customColor = state.basicStyleForm.colors[0]
state.colorIndex = 0
@ -177,6 +188,12 @@ const mapStyleOptions = [
{ name: t('chart.map_style_wine'), value: 'wine' }
]
const flowLineTypeOptions = [
{ name: t('chart.map_line_type_line'), value: 'line' },
{ name: t('chart.map_line_type_arc'), value: 'arc' },
{ name: t('chart.map_line_type_arc_3d'), value: 'arc3d' }
]
onMounted(() => {
init()
})
@ -268,7 +285,236 @@ onMounted(() => {
<el-radio :effect="themes" label="vertical">{{ t('chart.vertical') }}</el-radio>
</el-radio-group>
</el-form-item>
<!--flow map begin-->
<div class="map-setting" v-if="showProperty('mapStyle')">
<div class="map-style">
<el-row style="flex: 1">
<el-col>
<el-form-item
:label="t('chart.chart_map') + t('chart.map_style')"
class="form-item"
:class="'form-item-' + themes"
>
<el-select
:effect="themes"
v-model="state.basicStyleForm.mapStyle"
@change="changeBasicStyle('mapStyle')"
>
<el-option
v-for="item in mapStyleOptions"
:key="item.name"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<div class="alpha-setting">
<label class="alpha-label" :class="{ dark: 'dark' === themes }">
{{ t('chart.chart_map') + t('chart.map_pitch') }}
</label>
<el-row style="flex: 1" :gutter="8">
<el-col>
<el-form-item class="form-item alpha-slider" :class="'form-item-' + themes">
<el-slider
:effect="themes"
:min="0"
:max="90"
v-model="state.miscForm.mapPitch"
@change="changeMisc('mapPitch')"
/>
</el-form-item>
</el-col>
</el-row>
</div>
</div>
<div class="map-flow-style">
<el-row style="flex: 1">
<el-col>
<el-form-item
:label="t('chart.line') + t('chart.map_line_type')"
class="form-item"
:class="'form-item-' + themes"
>
<el-select
:effect="themes"
v-model="state.miscForm.mapLineType"
@change="changeMisc('mapLineType')"
>
<el-option
v-for="item in flowLineTypeOptions"
:key="item.name"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<div class="alpha-setting">
<label class="alpha-label" :class="{ dark: 'dark' === themes }">
{{ t('chart.map_line_width') }}
</label>
<el-row style="flex: 1">
<el-col>
<el-form-item class="form-item alpha-slider" :class="'form-item-' + themes">
<el-slider
:effect="themes"
:min="1"
:max="10"
v-model="state.miscForm.mapLineWidth"
@change="changeMisc('mapLineWidth')"
/>
</el-form-item>
</el-col>
</el-row>
</div>
<el-row style="flex: 1">
<el-col>
<el-form-item class="form-item" :class="'form-item-' + themes">
<el-checkbox
size="small"
:effect="themes"
v-model="state.miscForm.mapLineGradient"
:predefine="predefineColors"
@change="changeMisc('mapLineGradient')"
>
{{ t('chart.line') + t('chart.map_line_linear') }}
</el-checkbox>
</el-form-item>
</el-col>
</el-row>
<div v-if="state.miscForm.mapLineGradient">
<el-row style="flex: 1" :gutter="8">
<el-col :span="13">
<el-form-item
class="form-item"
:class="'form-item-' + themes"
:label="t('chart.map_line_color_source_color')"
>
<el-color-picker
is-custom
class="color-picker-style"
v-model="state.miscForm.mapLineSourceColor"
:persistent="false"
:effect="themes"
:trigger-width="108"
:predefine="predefineColors"
@change="changeMisc('mapLineSourceColor')"
/>
</el-form-item>
</el-col>
<el-col :span="13">
<el-form-item
class="form-item"
:class="'form-item-' + themes"
:label="t('chart.map_line_color_target_color')"
>
<el-color-picker
is-custom
class="color-picker-style"
v-model="state.miscForm.mapLineTargetColor"
:persistent="false"
:effect="themes"
:trigger-width="108"
:predefine="predefineColors"
@change="changeMisc('mapLineTargetColor')"
/>
</el-form-item>
</el-col>
</el-row>
</div>
<div v-if="!state.miscForm.mapLineGradient">
<el-row style="flex: 1" :gutter="8">
<el-col>
<el-form-item
class="form-item"
:class="'form-item-' + themes"
:label="t('chart.color')"
>
<el-color-picker
is-custom
class="color-picker-style"
v-model="state.miscForm.mapLineSourceColor"
:persistent="false"
:effect="themes"
:trigger-width="108"
:predefine="predefineColors"
@change="changeMisc('mapLineSourceColor')"
/>
</el-form-item>
</el-col>
</el-row>
</div>
<div class="alpha-setting">
<label class="alpha-label" :class="{ dark: 'dark' === themes }">
{{ t('chart.not_alpha') }}
</label>
<el-row style="flex: 1" :gutter="8">
<el-col :span="13">
<el-form-item class="form-item alpha-slider" :class="'form-item-' + themes">
<el-slider
:effect="themes"
v-model="state.basicStyleForm.alpha"
@change="changeBasicStyle('alpha')"
/>
</el-form-item>
</el-col>
<el-col :span="11" style="padding-top: 2px">
<el-form-item class="form-item" :class="'form-item-' + themes">
<el-input
type="number"
:effect="themes"
v-model="state.basicStyleForm.alpha"
:min="0"
:max="100"
class="basic-input-number"
:controls="false"
@change="changeBasicStyle('alpha')"
>
<template #suffix> % </template>
</el-input>
</el-form-item>
</el-col>
</el-row>
</div>
<el-row style="flex: 1">
<el-col>
<el-form-item class="form-item" :class="'form-item-' + themes">
<el-checkbox
size="small"
:effect="themes"
v-model="state.miscForm.mapLineAnimate"
:predefine="predefineColors"
@change="changeMisc('mapLineAnimate')"
>
{{ t('chart.line') + t('chart.map_line_animate') }}
</el-checkbox>
</el-form-item>
</el-col>
</el-row>
<div class="alpha-setting" v-if="state.miscForm.mapLineAnimate">
<label class="alpha-label" :class="{ dark: 'dark' === themes }">
{{ t('chart.map_line_animate_duration') }}
</label>
<el-row style="flex: 1" :gutter="8">
<el-col>
<el-form-item class="form-item alpha-slider" :class="'form-item-' + themes">
<el-slider
:effect="themes"
:min="0"
:max="20"
v-model="state.miscForm.mapLineAnimateDuration"
@change="changeMisc('mapLineAnimateDuration')"
/>
</el-form-item>
</el-col>
</el-row>
</div>
</div>
</div>
<!--flow map end-->
<!--map start-->
<el-row :gutter="8">
<el-col :span="12" v-if="showProperty('areaBorderColor')">
@ -658,27 +904,6 @@ onMounted(() => {
</el-radio-group>
</el-form-item>
<!--radar end-->
<!--flow map begin-->
<el-form-item
:label="t('chart.map_style')"
class="form-item"
:class="'form-item-' + themes"
v-if="showProperty('mapStyle')"
>
<el-select
:effect="themes"
v-model="state.basicStyleForm.mapStyle"
@change="changeBasicStyle('mapStyle')"
>
<el-option
v-for="item in mapStyleOptions"
:key="item.name"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<!--flow map end-->
<!--scatter start-->
<el-form-item
:label="t('chart.bubble_symbol')"

View File

@ -1223,14 +1223,14 @@ export const CHART_TYPE_CONFIGS = [
category: 'compare',
value: 'bidirectional-bar',
title: t('chart.chart_bidirectional_bar'),
icon: 'percentage-bar-stack-horizontal'
icon: 'bar-bidirectional'
},
{
render: 'antv',
category: 'compare',
value: 'progress-bar',
title: t('chart.chart_progress_bar'),
icon: 'percentage-bar-stack-horizontal'
icon: 'bar-progress'
}
]
},
@ -1308,6 +1308,13 @@ export const CHART_TYPE_CONFIGS = [
value: 'bubble-map',
title: t('chart.chart_bubble_map'),
icon: 'bubble-map'
},
{
render: 'antv',
category: 'map',
value: 'flow-map',
title: t('chart.chart_flow_map'),
icon: 'flow-map'
}
]
},

View File

@ -0,0 +1,138 @@
import { useI18n } from '@/hooks/web/useI18n'
import {
L7ChartView,
L7Config,
L7DrawConfig,
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 { flow, parseJson } from '@/views/chart/components/js/util'
import { deepCopy } from '@/utils/utils'
import { GaodeMap } from '@antv/l7-maps'
import { Scene } from '@antv/l7-scene'
import { LineLayer } from '@antv/l7-layers'
import { queryMapKeyApi } from '@/api/setting/sysParameter'
const { t } = useI18n()
/**
* 流向地图
*/
export class FlowMap extends L7ChartView<Scene, L7Config> {
properties: EditorProperty[] = [
'background-overall-component',
'basic-style-selector',
'title-selector'
]
propertyInner: EditorPropertyInner = {
...MAP_EDITOR_PROPERTY_INNER,
'basic-style-selector': ['mapStyle', 'zoom']
}
axis: AxisType[] = ['xAxis', 'xAxisExt', 'filter']
axisConfig: AxisConfig = {
xAxis: {
name: `起点经纬度 / ${t('chart.dimension')}`,
type: 'd',
limit: 2
},
xAxisExt: {
name: `终点经纬度 / ${t('chart.dimension')}`,
type: 'd',
limit: 2
}
}
constructor() {
super('flow-map', [])
}
async drawChart(drawOption: L7DrawConfig<L7Config>) {
const { chart, container } = drawOption
const xAxis = deepCopy(chart.xAxis)
const xAxisExt = deepCopy(chart.xAxisExt)
let basicStyle
let miscStyle
if (chart.customAttr) {
basicStyle = parseJson(chart.customAttr).basicStyle
miscStyle = parseJson(chart.customAttr).misc
}
const flowLineStyle = {
type: miscStyle.mapLineType,
size: miscStyle.mapLineWidth,
animate: miscStyle.mapLineAnimate,
animateDuration: miscStyle.mapLineAnimateDuration,
gradient: miscStyle.mapLineGradient,
sourceColor: miscStyle.mapLineSourceColor,
targetColor: miscStyle.mapLineTargetColor,
alpha: basicStyle.alpha
}
const mapStyle = `amap://styles/${basicStyle.mapStyle ? basicStyle.mapStyle : 'normal'}`
const key = await this.getMapKey()
// 底层
const scene = new Scene({
id: container,
logoVisible: false,
map: new GaodeMap({
token: key ?? undefined,
style: mapStyle,
pitch: miscStyle.mapPitch,
zoom: 2.5
})
})
if (xAxis?.length < 2 || xAxisExt?.length < 2) {
return new L7Wrapper(scene, undefined)
}
const config: L7Config = new LineLayer({
name: 'line',
blend: 'normal',
autoFit: true
})
.source(chart.data?.tableRow ? chart.data.tableRow : [], {
parser: {
type: 'json',
x: xAxis[0].dataeaseName,
y: xAxis[1].dataeaseName,
x1: xAxisExt[0].dataeaseName,
y1: xAxisExt[1].dataeaseName
}
})
.size(flowLineStyle.size)
.shape(flowLineStyle.type)
.animate({
enable: flowLineStyle.animate,
duration: flowLineStyle.animateDuration,
interval: 1,
trailLength: 1
})
if (flowLineStyle.gradient) {
config.style({
sourceColor: flowLineStyle.sourceColor,
targetColor: flowLineStyle.targetColor,
opacity: flowLineStyle.alpha / 100
})
} else {
config
.style({
opacity: flowLineStyle.alpha / 100
})
.color(flowLineStyle.sourceColor)
}
this.configZoomButton(chart, scene)
return new L7Wrapper(scene, config)
}
getMapKey = async () => {
const key = 'online-map-key'
if (!localStorage.getItem(key)) {
await queryMapKeyApi().then(res => localStorage.setItem(key, res.data))
}
return localStorage.getItem(key)
}
setupDefaultOptions(chart: ChartObj): ChartObj {
chart.customAttr.misc.mapLineAnimate = true
return chart
}
protected setupOptions(chart: Chart, config: L7Config): L7Config {
return flow(this.configEmptyDataStrategy)(chart, config)
}
}

View File

@ -26,6 +26,7 @@ import type { Plot as L7Plot, PlotOptions } from '@antv/l7plot/dist/esm'
import { Zoom } from '@antv/l7'
import { createL7Icon } from '@antv/l7-component/es/utils/icon'
import { DOM } from '@antv/l7-utils'
import { Scene } from '@antv/l7-scene'
export function getPadding(chart: Chart): number[] {
if (chart.drill) {
@ -1023,7 +1024,7 @@ class CustomZoom extends Zoom {
this['updateDisabled']()
}
}
export function configL7Zoom(chart: Chart, plot: L7Plot<PlotOptions>) {
export function configL7Zoom(chart: Chart, plot: L7Plot<PlotOptions> | Scene) {
const { basicStyle } = parseJson(chart.customAttr)
if (
(basicStyle.suspension === false && basicStyle.showZoom === undefined) ||
@ -1031,14 +1032,15 @@ export function configL7Zoom(chart: Chart, plot: L7Plot<PlotOptions>) {
) {
return
}
plot.once('loaded', () => {
const plotScene = plot instanceof Scene ? plot : plot.scene
plotScene.once('loaded', () => {
const zoomOptions = {
initZoom: plot.scene.getZoom(),
center: plot.scene.getCenter(),
initZoom: plotScene.getZoom(),
center: plotScene.getCenter(),
buttonColor: basicStyle.zoomButtonColor,
buttonBackground: basicStyle.zoomBackground
} as any
plot.scene.addControl(new CustomZoom(zoomOptions))
plotScene.addControl(new CustomZoom(zoomOptions))
})
}

View File

@ -0,0 +1,95 @@
import { Scene } from '@antv/l7-scene'
import {
AntVAbstractChartView,
AntVDrawOptions,
ChartLibraryType,
ChartWrapper
} from '@/views/chart/components/js/panel/types'
import { cloneDeep } from 'lodash-es'
import { parseJson } from '@/views/chart/components/js/util'
import { ILayer } from '@antv/l7plot'
import { configL7Zoom } from '@/views/chart/components/js/panel/common/common_antv'
export type L7DrawConfig<P> = AntVDrawOptions<P>
export interface L7Config extends ILayer {
handleConfig?: (arg0: Scene) => void
[key: string]: string | any
}
export class L7Wrapper<
O extends L7Config | Array<L7Config>,
S extends Scene
> extends ChartWrapper<S> {
private readonly config: O | Array<O>
private readonly scene: S | null = null
constructor(scene: S, l7config: O | Array<O> | undefined) {
super()
this.chartInstance = scene
this.config = l7config
this.scene = scene
}
destroy = () => {
if (!this.chartInstance) {
return
}
this.chartInstance?.destroy()
}
render = () => {
if (this.scene && this.config) {
this.scene.on('loaded', () => {
if (Array.isArray(this.config)) {
this.config?.forEach(p => {
this.handleConfig(p)
})
} else {
this.handleConfig(this.config)
}
})
}
}
handleConfig = (config: L7Config) => {
if (config.handleConfig) {
config.handleConfig?.(this.scene)
} else {
this.scene.addLayer(config)
}
}
}
export abstract class L7ChartView<
S extends Scene,
O extends L7Config
> extends AntVAbstractChartView {
public abstract drawChart(drawOption: L7DrawConfig<O>): L7Wrapper<O, S> | any
protected configEmptyDataStrategy(chart: Chart, options: O): O {
const { functionCfg } = parseJson(chart.senior)
const emptyDataStrategy = functionCfg.emptyDataStrategy
if (!emptyDataStrategy || emptyDataStrategy === 'breakLine') {
return options
}
const data = cloneDeep(options.sourceOption.data)
if (emptyDataStrategy === 'setZero') {
data.forEach(item => {
item.value === null && (item.value = 0)
})
}
if (emptyDataStrategy === 'ignoreData') {
for (let i = data.length - 1; i >= 0; i--) {
if (data[i].value === null) {
data.splice(i, 1)
}
}
}
options.sourceOption.data = data
return options
}
protected configZoomButton(chart: Chart, plot: S) {
configL7Zoom(chart, plot)
}
protected constructor(name: string, defaultData: any[]) {
super(ChartLibraryType.L7, name, defaultData)
}
protected abstract setupOptions(chart: Chart, options: O): O
}

View File

@ -11,6 +11,7 @@ export enum ChartRenderType {
export enum ChartLibraryType {
G2_PLOT = 'g2plot',
L7_PLOT = 'l7plot',
L7 = 'l7',
ECHARTS = 'echarts',
S2 = 's2',
RICH_TEXT = 'rich-text',

View File

@ -17,6 +17,7 @@ import { customAttrTrans, customStyleTrans, recursionTransObj } from '@/utils/ca
import { deepCopy } from '@/utils/utils'
import { trackBarStyleCheck } from '@/utils/canvasUtils'
import { useEmitt } from '@/hooks/web/useEmitt'
import { L7ChartView } from '@/views/chart/components/js/panel/types/impl/l7'
const dvMainStore = dvMainStoreWithOut()
const { nowPanelTrackInfo, nowPanelJumpInfo, mobileInPc, embeddedCallBack } =
@ -116,7 +117,7 @@ const calcData = async (view, callback) => {
callback?.()
})
} else {
if (['bubble-map', 'map'].includes(view.type)) {
if (['bubble-map', 'map', 'flow-map'].includes(view.type)) {
await renderChart(view, callback)
}
callback?.()
@ -141,6 +142,9 @@ const renderChart = async (view, callback?) => {
case ChartLibraryType.L7_PLOT:
await renderL7Plot(chart, chartView as L7PlotChartView<any, any>, callback)
break
case ChartLibraryType.L7:
await renderL7(chart, chartView as L7ChartView<any, any>, callback)
break
case ChartLibraryType.G2_PLOT:
renderG2Plot(chart, chartView as G2PlotChartView<any, any>)
callback?.()
@ -199,6 +203,23 @@ const renderL7Plot = async (chart: ChartObj, chartView: L7PlotChartView<any, any
}, 500)
}
let mapL7Timer: number
const renderL7 = async (chart: ChartObj, chartView: L7ChartView<any, any>, callback) => {
mapL7Timer && clearTimeout(mapL7Timer)
mapL7Timer = setTimeout(async () => {
myChart?.destroy()
myChart = await chartView.drawChart({
chartObj: myChart,
container: containerId,
chart: chart,
action
})
myChart?.render()
callback?.()
emit('resetLoading')
}, 500)
}
const pointClickTrans = () => {
if (embeddedCallBack.value === 'yes') {
trackClick('pointClick')
@ -333,7 +354,7 @@ defineExpose({
})
let resizeObserver
const TOLERANCE = 0.01
const RESIZE_MONITOR_CHARTS = ['map', 'bubble-map']
const RESIZE_MONITOR_CHARTS = ['map', 'bubble-map', 'flow-map']
onMounted(() => {
const containerDom = document.getElementById(containerId)
const { offsetWidth, offsetHeight } = containerDom

View File

@ -40,7 +40,6 @@ import { Base64 } from 'js-base64'
import DeRichTextView from '@/custom-component/rich-text/DeRichTextView.vue'
import ChartEmptyInfo from '@/views/chart/components/views/components/ChartEmptyInfo.vue'
import { snapshotStoreWithOut } from '@/store/modules/data-visualization/snapshot'
import { useAppStoreWithOut } from '@/store/modules/app'
const { wsCache } = useCache()
const chartComponent = ref<any>()
@ -105,7 +104,6 @@ const props = defineProps({
})
const dynamicAreaId = ref('')
const { view, showPosition, element, active, searchCount, scale } = toRefs(props)
const appStore = useAppStoreWithOut()
const titleShow = computed(
() =>
@ -242,7 +240,6 @@ watch([() => curComponent.value], () => {
})
}
})
const isDataEaseBi = computed(() => appStore.getIsDataEaseBi)
const chartExtRequest = shallowRef(null)
provide('chartExtRequest', chartExtRequest)
@ -568,7 +565,7 @@ const chartAreaShow = computed(() => {
return true
}
if (view.value.customAttr.map.id) {
const MAP_CHARTS = ['map', 'bubble-map']
const MAP_CHARTS = ['map', 'bubble-map', 'flow-map']
if (MAP_CHARTS.includes(view.value.type)) {
return true
}
@ -726,7 +723,9 @@ const titleIconStyle = computed(() => {
:view="view"
:show-position="showPosition"
:element="element"
v-else-if="showChartView(ChartLibraryType.G2_PLOT, ChartLibraryType.L7_PLOT)"
v-else-if="
showChartView(ChartLibraryType.G2_PLOT, ChartLibraryType.L7_PLOT, ChartLibraryType.L7)
"
ref="chartComponent"
@onChartClick="chartClick"
@onPointClick="onPointClick"