feat(图表): 地图增加缩放按钮配配置项,适应浅色和深色模式。

This commit is contained in:
wisonic-s 2024-04-17 12:24:47 +08:00
parent 5c7e0511aa
commit 3c67956186
10 changed files with 167 additions and 72 deletions

View File

@ -449,7 +449,9 @@ export default {
latitude: '纬度', latitude: '纬度',
gradient: '渐变', gradient: '渐变',
layer_controller: '指标切换', layer_controller: '指标切换',
suspension: '悬浮', show_zoom: '显示缩放按钮',
button_color: '按钮颜色',
button_background_color: '按钮背景色',
chart_background: '组件背景', chart_background: '组件背景',
date_format: '请选择日期解析格式', date_format: '请选择日期解析格式',
solid_color: '纯色', solid_color: '纯色',

View File

@ -161,9 +161,10 @@ declare interface ChartBasicStyle {
*/ */
areaBorderColor: string areaBorderColor: string
/** /**
* @deprecated
* 悬浮工具栏 * 悬浮工具栏
*/ */
suspension: boolean suspension?: boolean
/** /**
* 地图底色 * 地图底色
*/ */
@ -192,6 +193,18 @@ declare interface ChartBasicStyle {
* 环形图/玫瑰图外径占比 * 环形图/玫瑰图外径占比
*/ */
radius: number radius: number
/**
* 是否显示地图缩放按钮
*/
showZoom: boolean
/**
* 地图缩放按钮颜色
*/
zoomButtonColor: string
/**
* 地图缩放按钮背景颜色
*/
zoomBackground: string
} }
/** /**
* 表头属性 * 表头属性

View File

@ -1,6 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, nextTick, onMounted, PropType, reactive, ref, watch } from 'vue' import { computed, nextTick, onMounted, PropType, reactive, ref, watch } from 'vue'
import { useI18n } from '@/hooks/web/useI18n'
import { getGeoJsonFile, parseJson } from '../../../js/util' import { getGeoJsonFile, parseJson } from '../../../js/util'
import { forEach, debounce } from 'lodash-es' import { forEach, debounce } from 'lodash-es'
import { EmptyBackground } from '@/components/empty-background' import { EmptyBackground } from '@/components/empty-background'
@ -118,14 +117,6 @@ const updateAreaData = debounce(() => {
const onMapMappingChange = () => { const onMapMappingChange = () => {
emit('onMapMappingChange', state.mappingForm) emit('onMapMappingChange', state.mappingForm)
} }
const mergeCellMethod = ({ columnIndex }) => {
if (columnIndex === 1) {
return [1, 2]
}
if (columnIndex === 2) {
return [0, 0]
}
}
onMounted(() => { onMounted(() => {
init() init()
}) })
@ -141,7 +132,6 @@ onMounted(() => {
:cell-class-name="'area-map-table-cell-' + themes" :cell-class-name="'area-map-table-cell-' + themes"
:row-class-name="'area-map-table-row-' + themes" :row-class-name="'area-map-table-row-' + themes"
:data="areaData" :data="areaData"
:span-method="mergeCellMethod"
> >
<el-table-column label="图形" prop="originName" width="80" show-overflow-tooltip /> <el-table-column label="图形" prop="originName" width="80" show-overflow-tooltip />
<el-table-column width="144"> <el-table-column width="144">

View File

@ -49,6 +49,7 @@ const changeBasicStyle = (prop?: string, requestData = false) => {
} }
const init = () => { const init = () => {
const basicStyle = cloneDeep(props.chart.customAttr.basicStyle) const basicStyle = cloneDeep(props.chart.customAttr.basicStyle)
configCompat(basicStyle)
state.basicStyleForm = defaultsDeep(basicStyle, cloneDeep(DEFAULT_BASIC_STYLE)) as ChartBasicStyle state.basicStyleForm = defaultsDeep(basicStyle, cloneDeep(DEFAULT_BASIC_STYLE)) as ChartBasicStyle
if (!state.customColor) { if (!state.customColor) {
state.customColor = state.basicStyleForm.colors[0] state.customColor = state.basicStyleForm.colors[0]
@ -56,6 +57,12 @@ const init = () => {
} }
initTableColumnWidth() initTableColumnWidth()
} }
const configCompat = (basicStyle: ChartBasicStyle) => {
//
if (basicStyle.suspension === false && basicStyle.showZoom === undefined) {
basicStyle.showZoom = false
}
}
const COLUMN_WIDTH_TYPE = ['table-info', 'table-normal'] const COLUMN_WIDTH_TYPE = ['table-info', 'table-normal']
const initTableColumnWidth = () => { const initTableColumnWidth = () => {
if (!COLUMN_WIDTH_TYPE.includes(props.chart.type)) { if (!COLUMN_WIDTH_TYPE.includes(props.chart.type)) {
@ -230,21 +237,51 @@ onMounted(() => {
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
<el-form-item <el-form-item class="form-item" :class="'form-item-' + themes" v-if="showProperty('zoom')">
class="form-item"
:class="'form-item-' + themes"
v-if="showProperty('suspension')"
>
<el-checkbox <el-checkbox
size="small" size="small"
:effect="themes" :effect="themes"
v-model="state.basicStyleForm.suspension" v-model="state.basicStyleForm.showZoom"
:predefine="predefineColors" :predefine="predefineColors"
@change="changeBasicStyle('suspension')" @change="changeBasicStyle('zoomShow')"
> >
{{ t('chart.suspension') }} {{ t('chart.show_zoom') }}
</el-checkbox> </el-checkbox>
</el-form-item> </el-form-item>
<div v-if="showProperty('zoom') && state.basicStyleForm.showZoom">
<el-form-item
class="form-item"
:class="'form-item-' + themes"
:label="t('chart.button_color')"
>
<el-color-picker
is-custom
class="color-picker-style"
v-model="state.basicStyleForm.zoomButtonColor"
:persistent="false"
:effect="themes"
:trigger-width="108"
:predefine="predefineColors"
@change="changeBasicStyle('zoomButtonColor')"
/>
</el-form-item>
<el-form-item
class="form-item"
:class="'form-item-' + themes"
:label="t('chart.button_background_color')"
>
<el-color-picker
is-custom
class="color-picker-style"
v-model="state.basicStyleForm.zoomBackground"
:persistent="false"
:effect="themes"
:trigger-width="108"
:predefine="predefineColors"
@change="changeBasicStyle('zoomBackground')"
/>
</el-form-item>
</div>
<!--map end--> <!--map end-->

View File

@ -24,7 +24,9 @@ export const DEFAULT_COLOR_CASE: DeepPartial<ChartAttr> = {
areaBorderColor: '#303133', areaBorderColor: '#303133',
gaugeStyle: 'default', gaugeStyle: 'default',
tableBorderColor: '#E6E7E4', tableBorderColor: '#E6E7E4',
tableScrollBarColor: 'rgba(0, 0, 0, 0.15)' tableScrollBarColor: 'rgba(0, 0, 0, 0.15)',
zoomButtonColor: '#aaa',
zoomBackground: '#fff'
}, },
misc: { misc: {
mapLineGradient: false, mapLineGradient: false,
@ -65,7 +67,9 @@ export const DEFAULT_COLOR_CASE_LIGHT: DeepPartial<ChartAttr> = {
areaBorderColor: '#303133', areaBorderColor: '#303133',
gaugeStyle: 'default', gaugeStyle: 'default',
tableBorderColor: '#E6E7E4', tableBorderColor: '#E6E7E4',
tableScrollBarColor: 'rgba(0, 0, 0, 0.15)' tableScrollBarColor: 'rgba(0, 0, 0, 0.15)',
zoomButtonColor: '#aaa',
zoomBackground: '#fff'
}, },
misc: { misc: {
mapLineGradient: false, mapLineGradient: false,
@ -106,7 +110,9 @@ export const DEFAULT_COLOR_CASE_DARK: DeepPartial<ChartAttr> = {
areaBorderColor: '#EBEEF5', areaBorderColor: '#EBEEF5',
gaugeStyle: 'default', gaugeStyle: 'default',
tableBorderColor: '#CCCCCC', tableBorderColor: '#CCCCCC',
tableScrollBarColor: 'rgba(255, 255, 255, 0.5)' tableScrollBarColor: 'rgba(255, 255, 255, 0.5)',
zoomButtonColor: '#aaa',
zoomBackground: '#fff'
}, },
misc: { misc: {
mapLineGradient: false, mapLineGradient: false,
@ -244,9 +250,6 @@ export const DEFAULT_MISC: ChartMiscAttr = {
mapLineSourceColor: '#146C94', mapLineSourceColor: '#146C94',
mapLineTargetColor: '#576CBC' mapLineTargetColor: '#576CBC'
} }
export const DEFAULT_SUSPENSION = {
show: true
}
export const DEFAULT_MARK = { export const DEFAULT_MARK = {
fieldId: '', fieldId: '',
@ -1364,14 +1367,16 @@ export const DEFAULT_BASIC_STYLE: ChartBasicStyle = {
radarShape: 'polygon', radarShape: 'polygon',
mapStyle: 'normal', mapStyle: 'normal',
areaBorderColor: '#EBEEF5', areaBorderColor: '#EBEEF5',
suspension: true,
areaBaseColor: '#ffffff', areaBaseColor: '#ffffff',
mapSymbolOpacity: 0.7, mapSymbolOpacity: 0.7,
mapSymbolStrokeWidth: 2, mapSymbolStrokeWidth: 2,
mapSymbol: 'circle', mapSymbol: 'circle',
mapSymbolSize: 20, mapSymbolSize: 20,
radius: 80, radius: 80,
innerRadius: 60 innerRadius: 60,
showZoom: true,
zoomButtonColor: '#aaa',
zoomBackground: '#fff'
} }
export const BASE_VIEW_CONFIG = { export const BASE_VIEW_CONFIG = {

View File

@ -98,7 +98,7 @@ export class BubbleMap extends L7PlotChartView<ChoroplethOptions, Choropleth> {
options = this.setupOptions(chart, options, drawOption, geoJson) options = this.setupOptions(chart, options, drawOption, geoJson)
const view = new Choropleth(container, options) const view = new Choropleth(container, options)
const dotLayer = this.getDotLayer(chart, geoJson, drawOption) const dotLayer = this.getDotLayer(chart, geoJson, drawOption)
this.configZoomButton(view) this.configZoomButton(chart, view)
view.once('loaded', () => { view.once('loaded', () => {
view.addLayer(dotLayer) view.addLayer(dotLayer)
view.on('fillAreaLayer:click', (ev: MapMouseEvent) => { view.on('fillAreaLayer:click', (ev: MapMouseEvent) => {
@ -178,13 +178,6 @@ export class BubbleMap extends L7PlotChartView<ChoroplethOptions, Choropleth> {
const curAreaNameMapping = senior.areaMapping?.[areaId] const curAreaNameMapping = senior.areaMapping?.[areaId]
handleGeoJson(geoJson, curAreaNameMapping) handleGeoJson(geoJson, curAreaNameMapping)
options.color = hexColorToRGBA(basicStyle.areaBaseColor, basicStyle.alpha) options.color = hexColorToRGBA(basicStyle.areaBaseColor, basicStyle.alpha)
const suspension = basicStyle.suspension
if (!suspension) {
options = {
...options,
zoom: false
}
}
if (!chart.data?.data?.length || !geoJson?.features?.length) { if (!chart.data?.data?.length || !geoJson?.features?.length) {
options.label && (options.label.field = 'name') options.label && (options.label.field = 'name')
return options return options
@ -221,8 +214,7 @@ export class BubbleMap extends L7PlotChartView<ChoroplethOptions, Choropleth> {
this.configLabel, this.configLabel,
this.configStyle, this.configStyle,
this.configTooltip, this.configTooltip,
this.configBasicStyle, this.configBasicStyle
this.configLegend
)(chart, options, extra) )(chart, options, extra)
} }
} }

View File

@ -12,7 +12,7 @@ export const MAP_EDITOR_PROPERTY: EditorProperty[] = [
export const MAP_EDITOR_PROPERTY_INNER: EditorPropertyInner = { export const MAP_EDITOR_PROPERTY_INNER: EditorPropertyInner = {
'background-overall-component': ['all'], 'background-overall-component': ['all'],
'basic-style-selector': ['colors', 'alpha', 'areaBorderColor', 'suspension'], 'basic-style-selector': ['colors', 'alpha', 'areaBorderColor', 'zoom'],
'title-selector': [ 'title-selector': [
'title', 'title',
'fontSize', 'fontSize',

View File

@ -22,8 +22,11 @@ const { t } = useI18n()
* 地图 * 地图
*/ */
export class Map extends L7PlotChartView<ChoroplethOptions, Choropleth> { export class Map extends L7PlotChartView<ChoroplethOptions, Choropleth> {
properties = MAP_EDITOR_PROPERTY properties: EditorProperty[] = [...MAP_EDITOR_PROPERTY, 'legend-selector']
propertyInner = MAP_EDITOR_PROPERTY_INNER propertyInner: EditorPropertyInner = {
...MAP_EDITOR_PROPERTY_INNER,
'legend-selector': ['icon', 'fontSize', 'color']
}
axis = MAP_AXIS_TYPE axis = MAP_AXIS_TYPE
axisConfig: AxisConfig = { axisConfig: AxisConfig = {
xAxis: { xAxis: {
@ -90,15 +93,12 @@ export class Map extends L7PlotChartView<ChoroplethOptions, Choropleth> {
active: { stroke: 'green', lineWidth: 1 } active: { stroke: 'green', lineWidth: 1 }
}, },
tooltip: {}, tooltip: {},
legend: {
position: 'bottomleft'
},
// 禁用线上地图数据 // 禁用线上地图数据
customFetchGeoData: () => null customFetchGeoData: () => null
} }
options = this.setupOptions(chart, options, drawOption, geoJson) options = this.setupOptions(chart, options, drawOption, geoJson)
const view = new Choropleth(container, options) const view = new Choropleth(container, options)
this.configZoomButton(view) this.configZoomButton(chart, view)
view.once('loaded', () => { view.once('loaded', () => {
view.on('fillAreaLayer:click', (ev: MapMouseEvent) => { view.on('fillAreaLayer:click', (ev: MapMouseEvent) => {
const data = ev.feature.properties const data = ev.feature.properties
@ -135,14 +135,6 @@ export class Map extends L7PlotChartView<ChoroplethOptions, Choropleth> {
unknown: basicStyle.areaBaseColor unknown: basicStyle.areaBaseColor
} }
} }
const suspension = basicStyle.suspension
if (!suspension) {
options = {
...options,
legend: false,
zoom: false
}
}
if (!chart.data?.data?.length || !geoJson?.features?.length) { if (!chart.data?.data?.length || !geoJson?.features?.length) {
options.label && (options.label.field = 'name') options.label && (options.label.field = 'name')
return options return options

View File

@ -900,9 +900,32 @@ export function getTooltipSeriesTotalMap(data: any[]): Record<string, number> {
}) })
return result return result
} }
const LEGEND_SHAPE_STYLE_MAP = {
export function configL7Legend(): LegendOptions { circle: {
borderRadius: '50%'
},
square: {},
triangle: {
borderLeft: '5px solid transparent',
borderRight: '5px solid transparent',
borderBottom: '10px solid var(--bgColor)',
background: 'unset'
},
diamond: {
transform: 'rotate(45deg)'
}
}
export function configL7Legend(chart: Chart): LegendOptions | false {
const { basicStyle } = parseJson(chart.customAttr)
if (basicStyle.suspension === false && basicStyle.showZoom === undefined) {
return false
}
const { legend } = parseJson(chart.customStyle)
if (!legend.show) {
return false
}
return { return {
position: 'bottomleft',
customContent: (_: string, items: CategoryLegendListItem[]) => { customContent: (_: string, items: CategoryLegendListItem[]) => {
const showItems = items?.length > 30 ? items.slice(0, 30) : items const showItems = items?.length > 30 ? items.slice(0, 30) : items
if (showItems?.length) { if (showItems?.length) {
@ -925,11 +948,22 @@ export function configL7Legend(): LegendOptions {
const domStr = substitute(ITEM_TPL, substituteObj) const domStr = substitute(ITEM_TPL, substituteObj)
const itemDom = createDom(domStr) const itemDom = createDom(domStr)
// legend 形状用的
itemDom.style.setProperty('--bgColor', item.color)
listDom.appendChild(itemDom) listDom.appendChild(itemDom)
}) })
return listDom return listDom
} }
return '' return ''
},
domStyles: {
'l7plot-legend__category-value': {
fontSize: legend.fontSize + 'px',
color: legend.color
},
'l7plot-legend__category-marker': {
...LEGEND_SHAPE_STYLE_MAP[legend.icon]
}
} }
} }
} }
@ -944,12 +978,18 @@ class CustomZoom extends Zoom {
this.zoomIn this.zoomIn
) )
const resetBtnIconText = createL7Icon('l7-icon-round') const resetBtnIconText = createL7Icon('l7-icon-round')
this['createButton'](resetBtnIconText, 'Reset', 'l7-button-control', container, () => { this['zoomResetButton'] = this['createButton'](
this.mapsService.setZoomAndCenter( resetBtnIconText,
this.controlOption['initZoom'], 'Reset',
this.controlOption['center'] 'l7-button-control',
) container,
}) () => {
this.mapsService.setZoomAndCenter(
this.controlOption['initZoom'],
this.controlOption['center']
)
}
)
if (this.controlOption.showZoom) { if (this.controlOption.showZoom) {
this['zoomNumDiv'] = this['createButton']( this['zoomNumDiv'] = this['createButton'](
'0', '0',
@ -965,19 +1005,44 @@ class CustomZoom extends Zoom {
container, container,
this.zoomOut this.zoomOut
) )
const { buttonColor, buttonBackground } = this.controlOption as any
if (buttonColor) {
const elements = [
this.controlOption.zoomInText,
this.controlOption.zoomOutText,
resetBtnIconText
] as HTMLElement[]
setStyle(elements, 'fill', buttonColor)
}
const elements = [this['zoomResetButton'], this['zoomInButton'], this['zoomOutButton']]
if (buttonBackground) {
setStyle(elements, 'background', buttonBackground)
}
setStyle(elements, 'border-bottom', 'none')
this['updateDisabled']() this['updateDisabled']()
} }
} }
export function configL7Zoom(plot: L7Plot<PlotOptions>) { export function configL7Zoom(chart: Chart, plot: L7Plot<PlotOptions>) {
const options = plot.options const { basicStyle } = parseJson(chart.customAttr)
if (options.zoom === false) { if (
(basicStyle.suspension === false && basicStyle.showZoom === undefined) ||
basicStyle.showZoom === false
) {
return return
} }
plot.once('loaded', () => { plot.once('loaded', () => {
const zoomOptions = { const zoomOptions = {
initZoom: plot.scene.getZoom(), initZoom: plot.scene.getZoom(),
center: plot.scene.getCenter() center: plot.scene.getCenter(),
buttonColor: basicStyle.zoomButtonColor,
buttonBackground: basicStyle.zoomBackground
} as any } as any
plot.scene.addControl(new CustomZoom(zoomOptions)) plot.scene.addControl(new CustomZoom(zoomOptions))
}) })
} }
function setStyle(elements: HTMLElement[], styleProp: string, value) {
elements.forEach(e => {
e.style[styleProp] = value
})
}

View File

@ -15,7 +15,6 @@ import {
ChartLibraryType ChartLibraryType
} from '@/views/chart/components/js/panel/types' } from '@/views/chart/components/js/panel/types'
import { cloneDeep, defaultsDeep } from 'lodash-es' import { cloneDeep, defaultsDeep } from 'lodash-es'
import { ChoroplethOptions } from '@antv/l7plot/dist/esm/plots/choropleth'
import { parseJson } from '@/views/chart/components/js/util' import { parseJson } from '@/views/chart/components/js/util'
export interface L7PlotDrawOptions<P> extends AntVDrawOptions<P> { export interface L7PlotDrawOptions<P> extends AntVDrawOptions<P> {
@ -47,12 +46,12 @@ export abstract class L7PlotChartView<
defaultsDeep(options.tooltip, tooltip) defaultsDeep(options.tooltip, tooltip)
return options return options
} }
protected configLegend(_: Chart, options: ChoroplethOptions) { protected configLegend(chart: Chart, options: O): O {
const legend = configL7Legend() const legend = configL7Legend(chart)
defaultsDeep(options.legend, legend) defaultsDeep(options, { legend })
return options return options
} }
protected configEmptyDataStrategy(chart: Chart, options: ChoroplethOptions): ChoroplethOptions { protected configEmptyDataStrategy(chart: Chart, options: O): O {
const { functionCfg } = parseJson(chart.senior) const { functionCfg } = parseJson(chart.senior)
const emptyDataStrategy = functionCfg.emptyDataStrategy const emptyDataStrategy = functionCfg.emptyDataStrategy
if (!emptyDataStrategy || emptyDataStrategy === 'breakLine') { if (!emptyDataStrategy || emptyDataStrategy === 'breakLine') {
@ -75,8 +74,8 @@ export abstract class L7PlotChartView<
return options return options
} }
protected configZoomButton(plot: P) { protected configZoomButton(chart: Chart, plot: P) {
configL7Zoom(plot) configL7Zoom(chart, plot)
} }
protected constructor(name: string, defaultData?: any[]) { protected constructor(name: string, defaultData?: any[]) {
super(ChartLibraryType.L7_PLOT, name) super(ChartLibraryType.L7_PLOT, name)