forked from github/dataease
Merge pull request #12797 from dataease/pr@dev-v2@feat_online_map_custom_style
feat(图表): 在线地图支持自定义风格 #10408
This commit is contained in:
commit
060ca6ad33
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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: '仅编辑时生效',
|
||||
|
@ -205,6 +205,10 @@ declare interface ChartBasicStyle {
|
||||
* 地图主题风格
|
||||
*/
|
||||
mapStyle: string
|
||||
/**
|
||||
* 自定义地图风格url
|
||||
*/
|
||||
mapStyleUrl: string
|
||||
heatMapType?: string
|
||||
heatMapIntensity?: number
|
||||
heatMapRadius?: number
|
||||
|
@ -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 }) {
|
||||
|
@ -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') }}
|
||||
|
@ -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 = {
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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],
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -30,7 +30,7 @@ public interface SysParameterApi {
|
||||
|
||||
@Operation(summary = "查询在线地图")
|
||||
@GetMapping("/queryOnlineMap")
|
||||
String queryOnlineMap();
|
||||
OnlineMapEditor queryOnlineMap();
|
||||
|
||||
@Operation(summary = "查询基础设置(非xpack)")
|
||||
@GetMapping("basic/query")
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user