Merge pull request #12797 from dataease/pr@dev-v2@feat_online_map_custom_style

feat(图表): 在线地图支持自定义风格 #10408
This commit is contained in:
wisonic-s 2024-10-21 15:06:43 +08:00 committed by GitHub
commit 060ca6ad33
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 148 additions and 67 deletions

View File

@ -1,6 +1,7 @@
package io.dataease.system.manage;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import io.dataease.api.system.request.OnlineMapEditor;
import io.dataease.api.system.vo.SettingItemVO;
import io.dataease.datasource.server.DatasourceServer;
import io.dataease.license.config.XpackInteract;
@ -13,11 +14,13 @@ import io.dataease.utils.IDUtils;
import io.dataease.utils.SystemSettingUtils;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import java.beans.PropertyDescriptor;
import java.util.*;
import java.util.stream.Collectors;
@ -30,7 +33,7 @@ public class SysParameterManage {
@Value("${dataease.demo-tips-content:#{null}}")
private String demoTipsContent;
private static final String mapKey = "map.key";
private static final String MAP_KEY_PREFIX = "map.";
@Resource
private CoreSysSettingMapper coreSysSettingMapper;
@ -50,26 +53,39 @@ public class SysParameterManage {
return null;
}
public String queryOnlineMap() {
return singleVal(mapKey);
public OnlineMapEditor queryOnlineMap() {
var editor = new OnlineMapEditor();
List<String> fields = BeanUtils.getFieldNames(OnlineMapEditor.class);
Map<String, String> mapVal = groupVal(MAP_KEY_PREFIX);
fields.forEach(field -> {
String val = mapVal.get(MAP_KEY_PREFIX + field);
if (StringUtils.isNotBlank(val)) {
BeanUtils.setFieldValueByName(editor, field, val, String.class);
}
});
return editor;
}
public void saveOnlineMap(String val) {
QueryWrapper<CoreSysSetting> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("pkey", mapKey);
CoreSysSetting sysSetting = coreSysSettingMapper.selectOne(queryWrapper);
if (ObjectUtils.isEmpty(sysSetting)) {
sysSetting = new CoreSysSetting();
sysSetting.setId(IDUtils.snowID());
sysSetting.setPkey(mapKey);
public void saveOnlineMap(OnlineMapEditor editor) {
List<String> fieldNames = BeanUtils.getFieldNames(OnlineMapEditor.class);
fieldNames.forEach(field -> {
QueryWrapper<CoreSysSetting> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("pkey", MAP_KEY_PREFIX + field);
CoreSysSetting sysSetting = coreSysSettingMapper.selectOne(queryWrapper);
var val = (String) BeanUtils.getFieldValueByName(field, editor);
if (ObjectUtils.isEmpty(sysSetting)) {
sysSetting = new CoreSysSetting();
sysSetting.setId(IDUtils.snowID());
sysSetting.setPkey(MAP_KEY_PREFIX + field);
sysSetting.setPval(val == null ? "" : val);
sysSetting.setType("text");
sysSetting.setSort(1);
coreSysSettingMapper.insert(sysSetting);
return;
}
sysSetting.setPval(val);
sysSetting.setType("text");
sysSetting.setSort(1);
coreSysSettingMapper.insert(sysSetting);
}
sysSetting.setPval(val);
coreSysSettingMapper.updateById(sysSetting);
coreSysSettingMapper.updateById(sysSetting);
});
}
@ -81,7 +97,7 @@ public class SysParameterManage {
if (!CollectionUtils.isEmpty(sysSettings)) {
return sysSettings.stream().collect(Collectors.toMap(CoreSysSetting::getPkey, CoreSysSetting::getPval));
}
return null;
return new HashMap<>();
}
public List<CoreSysSetting> groupList(String groupKey) {

View File

@ -27,13 +27,12 @@ public class SysParameterServer implements SysParameterApi {
@Override
public void saveOnlineMap(OnlineMapEditor editor) {
sysParameterManage.saveOnlineMap(editor.getKey());
sysParameterManage.saveOnlineMap(editor);
}
@Override
public String queryOnlineMap() {
String key = sysParameterManage.queryOnlineMap();
return StringUtils.isNotBlank(key) ? key : "";
public OnlineMapEditor queryOnlineMap() {
return sysParameterManage.queryOnlineMap();
}
@Override

View File

@ -1451,20 +1451,17 @@ export default {
start_point: '起点经纬度',
end_point: '终点经纬度',
line: '线条',
map_style: '风格',
map_style: '地图风格',
map_style_url: '地图风格 URL',
map_pitch: '倾角',
map_rotation: '旋转',
map_style_normal: '标准',
map_style_light: '明亮',
map_style_dark: '暗黑',
map_style_whitesmoke: '远山黛',
map_style_fresh: '草色青',
map_style_grey: '雅士灰',
map_style_graffiti: '涂鸦',
map_style_macaron: '马卡龙',
map_style_blue: '靛青蓝',
map_style_darkblue: '极夜蓝',
map_style_wine: '酱籽',
map_line_type: '类型',
type: '类型',
map_line_width: '线条宽度',
@ -1542,7 +1539,8 @@ export default {
map_symbol_hexagram: '菱形',
tip: '提示',
hide: '隐藏',
show_label: '显示标签'
show_label: '显示标签',
security_code: '安全密钥'
},
dataset: {
scope_edit: '仅编辑时生效',

View File

@ -205,6 +205,10 @@ declare interface ChartBasicStyle {
* 地图主题风格
*/
mapStyle: string
/**
* 自定义地图风格url
*/
mapStyleUrl: string
heatMapType?: string
heatMapIntensity?: number
heatMapRadius?: number

View File

@ -3,12 +3,18 @@ import { store } from '@/store'
import { FeatureCollection } from '@antv/l7plot/dist/esm/plots/choropleth/types'
interface MapStore {
mapCache: Record<string, FeatureCollection>
mapKey: string
mapKey: {
key: string
securityCode: string
}
}
export const useMapStore = defineStore('map', {
state: (): MapStore => ({
mapCache: {},
mapKey: ''
mapKey: {
key: '',
securityCode: ''
}
}),
actions: {
setMap({ id, geoJson }) {

View File

@ -213,13 +213,10 @@ const mapStyleOptions = [
{ name: t('chart.map_style_darkblue'), value: 'darkblue' },
{ name: t('chart.map_style_light'), value: 'light' },
{ name: t('chart.map_style_dark'), value: 'dark' },
{ name: t('chart.map_style_whitesmoke'), value: 'whitesmoke' },
{ name: t('chart.map_style_fresh'), value: 'fresh' },
{ name: t('chart.map_style_grey'), value: 'grey' },
{ name: t('chart.map_style_graffiti'), value: 'graffiti' },
{ name: t('chart.map_style_macaron'), value: 'macaron' },
{ name: t('chart.map_style_blue'), value: 'blue' },
{ name: t('chart.map_style_wine'), value: 'wine' }
{ name: t('commons.custom'), value: 'custom' }
]
const heatMapTypeOptions = [
{ name: t('chart.heatmap_classics'), value: 'heatmap' },
@ -374,7 +371,7 @@ onMounted(() => {
<el-row style="flex: 1">
<el-col>
<el-form-item
:label="t('chart.chart_map') + t('chart.map_style')"
:label="t('chart.map_style')"
class="form-item"
:class="'form-item-' + themes"
>
@ -393,6 +390,21 @@ onMounted(() => {
</el-form-item>
</el-col>
</el-row>
<el-row style="flex: 1" v-if="state.basicStyleForm.mapStyle === 'custom'">
<el-col>
<el-form-item
:label="t('chart.map_style_url')"
class="form-item"
:class="'form-item-' + themes"
>
<el-input
:effect="themes"
v-model="state.basicStyleForm.mapStyleUrl"
@change="changeBasicStyle('mapStyleUrl')"
/>
</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') }}

View File

@ -1586,7 +1586,8 @@ export const DEFAULT_BASIC_STYLE: ChartBasicStyle = {
layout: 'horizontal',
mapSymbolSizeMin: 4,
mapSymbolSizeMax: 30,
showLabel: true
showLabel: true,
mapStyleUrl: ''
}
export const BASE_VIEW_CONFIG = {

View File

@ -74,14 +74,17 @@ export class FlowMap extends L7ChartView<Scene, L7Config> {
const xAxisExt = deepCopy(chart.xAxisExt)
const { basicStyle, misc } = deepCopy(parseJson(chart.customAttr))
const mapStyle = `amap://styles/${basicStyle.mapStyle ? basicStyle.mapStyle : 'normal'}`
const key = await this.getMapKey()
let mapStyle = basicStyle.mapStyleUrl
if (basicStyle.mapStyle !== 'custom') {
mapStyle = `amap://styles/${basicStyle.mapStyle ? basicStyle.mapStyle : 'normal'}`
}
const mapKey = await this.getMapKey()
// 底层
const scene = new Scene({
id: container,
logoVisible: false,
map: new GaodeMap({
token: key ?? undefined,
token: mapKey?.key ?? undefined,
style: mapStyle,
pitch: misc.mapPitch,
zoom: 2.5,

View File

@ -56,14 +56,17 @@ export class HeatMap extends L7ChartView<Scene, L7Config> {
basicStyle = parseJson(chart.customAttr).basicStyle
miscStyle = parseJson(chart.customAttr).misc
}
const mapStyle = `amap://styles/${basicStyle.mapStyle ? basicStyle.mapStyle : 'normal'}`
const key = await this.getMapKey()
let mapStyle = basicStyle.mapStyleUrl
if (basicStyle.mapStyle !== 'custom') {
mapStyle = `amap://styles/${basicStyle.mapStyle ? basicStyle.mapStyle : 'normal'}`
}
const mapKey = await this.getMapKey()
// 底层
const scene = new Scene({
id: container,
logoVisible: false,
map: new GaodeMap({
token: key ?? undefined,
token: mapKey?.key ?? undefined,
style: mapStyle,
pitch: miscStyle.mapPitch,
zoom: 2.5,

View File

@ -85,14 +85,17 @@ export class SymbolicMap extends L7ChartView<Scene, L7Config> {
miscStyle = parseJson(chart.customAttr).misc
}
const mapStyle = `amap://styles/${basicStyle.mapStyle ? basicStyle.mapStyle : 'normal'}`
const key = await this.getMapKey()
let mapStyle = basicStyle.mapStyleUrl
if (basicStyle.mapStyle !== 'custom') {
mapStyle = `amap://styles/${basicStyle.mapStyle ? basicStyle.mapStyle : 'normal'}`
}
const mapKey = await this.getMapKey()
// 底层
const scene = new Scene({
id: container,
logoVisible: false,
map: new GaodeMap({
token: key ?? undefined,
token: mapKey?.key ?? undefined,
style: mapStyle,
pitch: miscStyle.mapPitch,
center: [104.434765, 38.256735],

View File

@ -114,9 +114,14 @@ export abstract class L7ChartView<
}
protected getMapKey = async () => {
if (!mapStore.mapKey) {
if (!mapStore.mapKey.key) {
await queryMapKeyApi().then(res => mapStore.setKey(res.data))
}
if (mapStore.mapKey.securityCode) {
window._AMapSecurityConfig = {
securityJsCode: mapStore.mapKey.securityCode
}
}
return mapStore.mapKey
}

View File

@ -4,19 +4,33 @@
<div class="geo-title">
<span>{{ t('online_map.onlinemap') }}</span>
</div>
<div class="online-form-item">
<div class="map-item">
<div class="map-item-label">
<span class="form-label">Key</span>
<el-row>
<el-col>
<div class="online-form-item">
<div class="map-item">
<div class="map-item-label">
<span class="form-label">Key</span>
</div>
</div>
<div class="map-item">
<el-input v-model="mapEditor.key" />
</div>
<div class="map-item">
<div class="map-item-label">
<span class="form-label">{{ t('chart.security_code') }}</span>
</div>
</div>
<div class="map-item">
<el-input v-model="mapEditor.securityCode" />
</div>
</div>
</div>
<div class="map-item">
<el-input v-model="key" />
</div>
</div>
<el-button type="primary" :disabled="!key" @click="saveHandler">{{
t('commons.save')
}}</el-button>
</el-col>
</el-row>
<el-row>
<el-button type="primary" :disabled="!mapEditor.key" @click="saveHandler">
{{ t('commons.save') }}
</el-button>
</el-row>
</el-aside>
<el-main>
<div v-show="mapLoaded" v-if="!mapReloading" class="de-map-container" :id="domId" />
@ -30,23 +44,26 @@
</template>
<script lang="ts" setup>
import { nextTick, onMounted, ref } from 'vue'
import { nextTick, onMounted, reactive, ref } from 'vue'
import { useI18n } from '@/hooks/web/useI18n'
import { queryMapKeyApi, saveMapKeyApi } from '@/api/setting/sysParameter'
import { ElMessage } from 'element-plus-secondary'
import EmptyBackground from '@/components/empty-background/src/EmptyBackground.vue'
const { t } = useI18n()
const key = ref('')
const mapEditor = reactive({
key: '',
securityCode: ''
})
const mapInstance = ref(null)
const mapReloading = ref(false)
const domId = ref('de-map-container')
const mapLoaded = ref(false)
const loadMap = () => {
if (!key.value) {
if (!mapEditor.key) {
return
}
const mykey = key.value
const mykey = mapEditor.key
const url = `https://webapi.amap.com/maps?v=2.0&key=${mykey}`
loadScript(url)
@ -84,7 +101,7 @@ const createMapInstance = () => {
mapLoaded.value = true
}
const saveHandler = () => {
saveMapKeyApi({ key: key.value })
saveMapKeyApi(mapEditor)
.then(() => {
ElMessage.success(t('commons.save_success'))
initLoad()
@ -96,7 +113,8 @@ const saveHandler = () => {
const initLoad = () => {
queryMapKeyApi()
.then(res => {
key.value = res.data
mapEditor.key = res.data.key
mapEditor.securityCode = res.data.securityCode
loadMap()
})
.catch(e => {
@ -154,7 +172,6 @@ onMounted(() => {
}
}
.online-form-item {
height: 64px;
margin-bottom: 16px;
.map-item {
height: 32px;

View File

@ -30,7 +30,7 @@ public interface SysParameterApi {
@Operation(summary = "查询在线地图")
@GetMapping("/queryOnlineMap")
String queryOnlineMap();
OnlineMapEditor queryOnlineMap();
@Operation(summary = "查询基础设置(非xpack)")
@GetMapping("basic/query")

View File

@ -10,4 +10,6 @@ import java.io.Serializable;
public class OnlineMapEditor implements Serializable {
@Schema(description = "在线地图key", requiredMode = Schema.RequiredMode.REQUIRED)
private String key;
@Schema(description = "在线地图安全密钥", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String securityCode;
}

View File

@ -2,7 +2,10 @@ package io.dataease.utils;
import org.apache.commons.lang3.StringUtils;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
public class BeanUtils {
@ -65,4 +68,13 @@ public class BeanUtils {
return null;
}
}
public static List<String> getFieldNames(Class<?> clazz) {
List<String> fieldNames = new ArrayList<>();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
fieldNames.add(field.getName());
}
return fieldNames;
}
}