forked from github/dataease
parent
ae492053ad
commit
93308d84df
@ -660,6 +660,7 @@ export default {
|
||||
horizontal: '水平',
|
||||
vertical: '垂直',
|
||||
legend: '图例',
|
||||
legend_num: '图例数',
|
||||
shape: '形状',
|
||||
polygon: '多边形',
|
||||
circle: '圆形',
|
||||
|
@ -612,6 +612,22 @@ declare interface ChartMiscAttr {
|
||||
* 词云图文字间距
|
||||
*/
|
||||
wordSpacing: number
|
||||
/**
|
||||
* 自动图例
|
||||
*/
|
||||
mapAutoLegend: boolean
|
||||
/**
|
||||
* 图例最大值
|
||||
*/
|
||||
mapLegendMax: number
|
||||
/**
|
||||
* 图例最小值
|
||||
*/
|
||||
mapLegendMin: number
|
||||
/**
|
||||
* 显示图例个数
|
||||
*/
|
||||
mapLegendNumber: number
|
||||
}
|
||||
/**
|
||||
* 动态极值配置
|
||||
|
@ -260,6 +260,7 @@ watch(
|
||||
:themes="themes"
|
||||
:chart="chart"
|
||||
@onLegendChange="onLegendChange"
|
||||
@onMiscChange="onMiscChange"
|
||||
/>
|
||||
</collapse-switch-item>
|
||||
<el-collapse-item
|
||||
|
@ -1,8 +1,14 @@
|
||||
<script lang="tsx" setup>
|
||||
import { computed, onMounted, reactive, watch } from 'vue'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { COLOR_PANEL, DEFAULT_LEGEND_STYLE } from '@/views/chart/components/editor/util/chart'
|
||||
import { ElSpace } from 'element-plus-secondary'
|
||||
import {
|
||||
COLOR_PANEL,
|
||||
DEFAULT_LEGEND_STYLE,
|
||||
DEFAULT_MISC
|
||||
} from '@/views/chart/components/editor/util/chart'
|
||||
import { ElCol, ElRow, ElSpace } from 'element-plus-secondary'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { useEmitt } from '@/hooks/web/useEmitt'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
@ -14,8 +20,11 @@ const props = withDefaults(
|
||||
}>(),
|
||||
{ themes: 'dark' }
|
||||
)
|
||||
|
||||
const emit = defineEmits(['onLegendChange'])
|
||||
useEmitt({
|
||||
name: 'map-default-range',
|
||||
callback: args => mapDefaultRange(args)
|
||||
})
|
||||
const emit = defineEmits(['onLegendChange', 'onMiscChange'])
|
||||
const toolTip = computed(() => {
|
||||
return props.themes === 'dark' ? 'ndark' : 'dark'
|
||||
})
|
||||
@ -36,7 +45,15 @@ const iconSymbolOptions = [
|
||||
]
|
||||
|
||||
const state = reactive({
|
||||
legendForm: JSON.parse(JSON.stringify(DEFAULT_LEGEND_STYLE))
|
||||
legendForm: {
|
||||
...JSON.parse(JSON.stringify(DEFAULT_LEGEND_STYLE)),
|
||||
miscForm: JSON.parse(JSON.stringify(DEFAULT_MISC)) as ChartMiscAttr
|
||||
}
|
||||
})
|
||||
|
||||
const chartType = computed(() => {
|
||||
const chart = JSON.parse(JSON.stringify(props.chart))
|
||||
return chart?.type
|
||||
})
|
||||
|
||||
const fontSizeList = computed(() => {
|
||||
@ -54,6 +71,10 @@ const changeLegendStyle = prop => {
|
||||
emit('onLegendChange', state.legendForm, prop)
|
||||
}
|
||||
|
||||
const changeMisc = prop => {
|
||||
emit('onMiscChange', { data: state.legendForm.miscForm, requestData: true }, prop)
|
||||
}
|
||||
|
||||
const init = () => {
|
||||
const chart = JSON.parse(JSON.stringify(props.chart))
|
||||
if (chart.customStyle) {
|
||||
@ -63,13 +84,21 @@ const init = () => {
|
||||
} else {
|
||||
customStyle = JSON.parse(chart.customStyle)
|
||||
}
|
||||
const miscStyle = cloneDeep(props.chart.customAttr.misc)
|
||||
if (customStyle.legend) {
|
||||
state.legendForm = customStyle.legend
|
||||
state.legendForm.miscForm = miscStyle
|
||||
}
|
||||
}
|
||||
}
|
||||
const showProperty = prop => props.propertyInner?.includes(prop)
|
||||
|
||||
const mapDefaultRange = args => {
|
||||
if (args.from === 'map') {
|
||||
state.legendForm.miscForm.mapLegendMax = args.data.max
|
||||
state.legendForm.miscForm.mapLegendMin = args.data.min
|
||||
state.legendForm.miscForm.mapLegendNumber = args.data.legendNumber
|
||||
}
|
||||
}
|
||||
onMounted(() => {
|
||||
init()
|
||||
})
|
||||
@ -145,6 +174,82 @@ onMounted(() => {
|
||||
</el-tooltip>
|
||||
</el-form-item>
|
||||
</el-space>
|
||||
<el-space>
|
||||
<div v-if="chartType === 'map'">
|
||||
<el-row>
|
||||
<el-col>
|
||||
<el-form-item
|
||||
class="form-item"
|
||||
:class="'form-item-' + themes"
|
||||
:label="t('chart.legend')"
|
||||
>
|
||||
<el-checkbox
|
||||
size="small"
|
||||
:effect="themes"
|
||||
v-model="state.legendForm.miscForm.mapAutoLegend"
|
||||
@change="changeMisc('mapAutoLegend')"
|
||||
>
|
||||
{{ t('chart.margin_model_auto') }}
|
||||
</el-checkbox>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div v-if="!state.legendForm.miscForm.mapAutoLegend">
|
||||
<el-row :gutter="8">
|
||||
<el-col :span="12">
|
||||
<el-form-item
|
||||
:label="t('chart.max')"
|
||||
class="form-item"
|
||||
:class="'form-item-' + themes"
|
||||
>
|
||||
<el-input-number
|
||||
:effect="themes"
|
||||
v-model="state.legendForm.miscForm.mapLegendMax"
|
||||
size="small"
|
||||
controls-position="right"
|
||||
@change="changeMisc('mapLegendMax')"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item
|
||||
:label="t('chart.min')"
|
||||
class="form-item"
|
||||
:class="'form-item-' + themes"
|
||||
>
|
||||
<el-input-number
|
||||
:effect="themes"
|
||||
v-model="state.legendForm.miscForm.mapLegendMin"
|
||||
size="small"
|
||||
controls-position="right"
|
||||
@change="changeMisc('mapLegendMin')"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col>
|
||||
<el-form-item
|
||||
class="form-item"
|
||||
:class="'form-item-' + themes"
|
||||
:label="t('chart.legend_num')"
|
||||
>
|
||||
<el-input-number
|
||||
:effect="themes"
|
||||
v-model="state.legendForm.miscForm.mapLegendNumber"
|
||||
size="small"
|
||||
:min="1"
|
||||
:max="9"
|
||||
controls-position="right"
|
||||
@change="changeMisc('mapLegendNumber')"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
</el-space>
|
||||
|
||||
<el-form-item
|
||||
:label="t('chart.orient')"
|
||||
class="form-item"
|
||||
|
@ -262,7 +262,11 @@ export const DEFAULT_MISC: ChartMiscAttr = {
|
||||
mapLineSourceColor: '#146C94',
|
||||
mapLineTargetColor: '#576CBC',
|
||||
wordSizeRange: [8, 32],
|
||||
wordSpacing: 6
|
||||
wordSpacing: 6,
|
||||
mapAutoLegend: true,
|
||||
mapLegendMax: 0,
|
||||
mapLegendMin: 0,
|
||||
mapLegendNumber: 9
|
||||
}
|
||||
|
||||
export const DEFAULT_MARK = {
|
||||
|
@ -3,7 +3,15 @@ import {
|
||||
L7PlotDrawOptions
|
||||
} from '@/views/chart/components/js/panel/types/impl/l7plot'
|
||||
import { Choropleth, ChoroplethOptions } from '@antv/l7plot/dist/esm/plots/choropleth'
|
||||
import { flow, getGeoJsonFile, hexColorToRGBA, parseJson } from '@/views/chart/components/js/util'
|
||||
import {
|
||||
filterChartDataByRange,
|
||||
flow,
|
||||
getDynamicColorScale,
|
||||
getGeoJsonFile,
|
||||
setMapChartDefaultMaxAndMinValueByData,
|
||||
hexColorToRGBA,
|
||||
parseJson
|
||||
} from '@/views/chart/components/js/util'
|
||||
import { handleGeoJson } from '@/views/chart/components/js/panel/common/common_antv'
|
||||
import { FeatureCollection } from '@antv/l7plot/dist/esm/plots/choropleth/types'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
@ -50,6 +58,30 @@ export class Map extends L7PlotChartView<ChoroplethOptions, Choropleth> {
|
||||
if (!areaId) {
|
||||
return
|
||||
}
|
||||
const sourceData = JSON.parse(JSON.stringify(chart.data?.data || []))
|
||||
let data = []
|
||||
const { misc } = parseJson(chart.customAttr)
|
||||
const { legend } = parseJson(chart.customStyle)
|
||||
// 自定义图例
|
||||
if (!misc.mapAutoLegend && legend.show) {
|
||||
let minValue = misc.mapLegendMin
|
||||
let maxValue = misc.mapLegendMax
|
||||
setMapChartDefaultMaxAndMinValueByData(sourceData, maxValue, minValue, (max, min) => {
|
||||
maxValue = max
|
||||
minValue = min
|
||||
action({
|
||||
from: 'map',
|
||||
data: {
|
||||
max: maxValue,
|
||||
min: minValue,
|
||||
legendNumber: 9
|
||||
}
|
||||
})
|
||||
})
|
||||
data = filterChartDataByRange(sourceData, maxValue, minValue)
|
||||
} else {
|
||||
data = sourceData
|
||||
}
|
||||
const geoJson = cloneDeep(await getGeoJsonFile(areaId))
|
||||
let options: ChoroplethOptions = {
|
||||
preserveDrawingBuffer: true,
|
||||
@ -61,7 +93,7 @@ export class Map extends L7PlotChartView<ChoroplethOptions, Choropleth> {
|
||||
type: 'geojson'
|
||||
},
|
||||
source: {
|
||||
data: chart.data?.data || [],
|
||||
data: data,
|
||||
joinBy: {
|
||||
sourceField: 'name',
|
||||
geoField: 'name',
|
||||
@ -125,7 +157,7 @@ export class Map extends L7PlotChartView<ChoroplethOptions, Choropleth> {
|
||||
): ChoroplethOptions {
|
||||
const { areaId }: L7PlotDrawOptions<any> = context.drawOption
|
||||
const geoJson: FeatureCollection = context.geoJson
|
||||
const { basicStyle, label } = parseJson(chart.customAttr)
|
||||
const { basicStyle, label, misc } = parseJson(chart.customAttr)
|
||||
const senior = parseJson(chart.senior)
|
||||
const curAreaNameMapping = senior.areaMapping?.[areaId]
|
||||
handleGeoJson(geoJson, curAreaNameMapping)
|
||||
@ -141,7 +173,32 @@ export class Map extends L7PlotChartView<ChoroplethOptions, Choropleth> {
|
||||
options.label && (options.label.field = 'name')
|
||||
return options
|
||||
}
|
||||
const data = chart.data.data
|
||||
const sourceData = JSON.parse(JSON.stringify(chart.data.data))
|
||||
const colors = basicStyle.colors.map(item => hexColorToRGBA(item, basicStyle.alpha))
|
||||
const { legend } = parseJson(chart.customStyle)
|
||||
let data = []
|
||||
let colorScale = []
|
||||
if (legend.show) {
|
||||
let minValue = misc.mapLegendMin
|
||||
let maxValue = misc.mapLegendMax
|
||||
let mapLegendNumber = misc.mapLegendNumber
|
||||
setMapChartDefaultMaxAndMinValueByData(sourceData, maxValue, minValue, (max, min) => {
|
||||
maxValue = max
|
||||
minValue = min
|
||||
mapLegendNumber = 9
|
||||
})
|
||||
// 非自动,过滤数据
|
||||
if (!misc.mapAutoLegend) {
|
||||
data = filterChartDataByRange(sourceData, maxValue, minValue)
|
||||
} else {
|
||||
mapLegendNumber = 9
|
||||
}
|
||||
// 定义最大值、最小值、区间数量和对应的颜色
|
||||
colorScale = getDynamicColorScale(minValue, maxValue, mapLegendNumber, colors)
|
||||
} else {
|
||||
data = sourceData
|
||||
colorScale = colors
|
||||
}
|
||||
const areaMap = data.reduce((obj, value) => {
|
||||
obj[value['field']] = value.value
|
||||
return obj
|
||||
@ -164,12 +221,11 @@ export class Map extends L7PlotChartView<ChoroplethOptions, Choropleth> {
|
||||
item.properties['_DE_LABEL_'] = content.join('\n\n')
|
||||
}
|
||||
})
|
||||
let colors = basicStyle.colors.map(item => hexColorToRGBA(item, basicStyle.alpha))
|
||||
if (validArea < colors.length) {
|
||||
colors = colors.slice(0, validArea)
|
||||
if (validArea < colorScale.length && !misc.mapAutoLegend) {
|
||||
colorScale = colorScale.map(item => (item.color ? item.color : item)).slice(0, validArea)
|
||||
}
|
||||
if (colors.length) {
|
||||
options.color['value'] = colors
|
||||
if (colorScale.length) {
|
||||
options.color['value'] = colorScale.map(item => (item.color ? item.color : item))
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
@ -527,3 +527,68 @@ export const copyString = (content: string, notify = false) => {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算动态区间和颜色
|
||||
* @param minValue
|
||||
* @param maxValue
|
||||
* @param intervals
|
||||
* @param colors
|
||||
*/
|
||||
export const getDynamicColorScale = (
|
||||
minValue: number,
|
||||
maxValue: number,
|
||||
intervals: number,
|
||||
colors: string[]
|
||||
) => {
|
||||
const step = (maxValue - minValue) / intervals
|
||||
|
||||
const colorScale = []
|
||||
for (let i = 0; i < intervals; i++) {
|
||||
colorScale.push({
|
||||
value: [minValue + i * step, minValue + (i + 1) * step],
|
||||
color: colors[i],
|
||||
label: `${(minValue + i * step).toFixed(2)} - ${(minValue + (i + 1) * step).toFixed(2)}`
|
||||
})
|
||||
}
|
||||
|
||||
return colorScale
|
||||
}
|
||||
/**
|
||||
* 过滤掉不在区间的数据
|
||||
* @param data
|
||||
* @param maxValue
|
||||
* @param minValue
|
||||
*/
|
||||
export const filterChartDataByRange = (data: any[], maxValue: number, minValue: number) => {
|
||||
return data.filter(
|
||||
item =>
|
||||
item.value === null ||
|
||||
item.value === undefined ||
|
||||
(item.value >= minValue && item.value <= maxValue)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取地图默认最大最小值根据数据
|
||||
* @param data
|
||||
* @param maxValue
|
||||
* @param minValue
|
||||
* @param callback
|
||||
*/
|
||||
export const setMapChartDefaultMaxAndMinValueByData = (
|
||||
data: any[],
|
||||
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
|
||||
}, Number.MIN_SAFE_INTEGER)
|
||||
const minResult = data.reduce((min, current) => {
|
||||
return current.value < min ? current.value : min
|
||||
}, Number.MAX_SAFE_INTEGER)
|
||||
callback(maxResult, minResult)
|
||||
}
|
||||
}
|
||||
|
@ -287,6 +287,10 @@ const pointClickTrans = () => {
|
||||
}
|
||||
|
||||
const action = param => {
|
||||
if (param.from === 'map') {
|
||||
emitter.emit('map-default-range', param)
|
||||
return
|
||||
}
|
||||
state.pointParam = param.data
|
||||
// 点击
|
||||
pointClickTrans()
|
||||
|
Loading…
Reference in New Issue
Block a user