forked from github/dataease
feat: 自定义地图设置
This commit is contained in:
parent
831a92b91c
commit
60b0f322e7
@ -21,12 +21,16 @@ public class DeMvcConfig implements WebMvcConfigurer {
|
|||||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||||
String workDir = FILE_PROTOCOL + ensureSuffix(WORK_DIR, FILE_SEPARATOR);
|
String workDir = FILE_PROTOCOL + ensureSuffix(WORK_DIR, FILE_SEPARATOR);
|
||||||
String uploadUrlPattern = ensureBoth(URL_SEPARATOR + UPLOAD_URL_PREFIX, AuthConstant.DE_API_PREFIX, URL_SEPARATOR) + "**";
|
String uploadUrlPattern = ensureBoth(URL_SEPARATOR + UPLOAD_URL_PREFIX, AuthConstant.DE_API_PREFIX, URL_SEPARATOR) + "**";
|
||||||
registry.addResourceHandler(uploadUrlPattern)
|
registry.addResourceHandler(uploadUrlPattern).addResourceLocations(workDir);
|
||||||
.addResourceLocations(workDir);
|
|
||||||
// map
|
// map
|
||||||
String mapDir = FILE_PROTOCOL + ensureSuffix(MAP_DIR, FILE_SEPARATOR);
|
String mapDir = FILE_PROTOCOL + ensureSuffix(MAP_DIR, FILE_SEPARATOR);
|
||||||
String mapUrlPattern = ensureBoth(MAP_URL, AuthConstant.DE_API_PREFIX, URL_SEPARATOR) + "**";
|
String mapUrlPattern = ensureBoth(MAP_URL, AuthConstant.DE_API_PREFIX, URL_SEPARATOR) + "**";
|
||||||
registry.addResourceHandler(mapUrlPattern)
|
registry.addResourceHandler(mapUrlPattern).addResourceLocations(mapDir);
|
||||||
.addResourceLocations(mapDir);
|
|
||||||
|
String geoDir = FILE_PROTOCOL + ensureSuffix(CUSTOM_MAP_DIR, FILE_SEPARATOR);
|
||||||
|
String geoUrlPattern = ensureBoth(GEO_URL, AuthConstant.DE_API_PREFIX, URL_SEPARATOR) + "**";
|
||||||
|
registry.addResourceHandler(geoUrlPattern).addResourceLocations(geoDir);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,13 @@
|
|||||||
|
package io.dataease.map.bo;
|
||||||
|
|
||||||
|
import io.dataease.map.dao.auto.entity.Area;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@Data
|
||||||
|
public class AreaBO extends Area implements Serializable {
|
||||||
|
private boolean custom = false;
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package io.dataease.map.dao.ext.entity;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class CoreAreaCustom implements Serializable {
|
||||||
|
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
private String pid;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package io.dataease.map.dao.ext.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import io.dataease.map.dao.ext.entity.CoreAreaCustom;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface CoreAreaCustomMapper extends BaseMapper<CoreAreaCustom> {
|
||||||
|
}
|
@ -1,17 +1,34 @@
|
|||||||
package io.dataease.map.manage;
|
package io.dataease.map.manage;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollectionUtil;
|
import io.dataease.api.map.dto.GeometryNodeCreator;
|
||||||
import io.dataease.api.map.vo.AreaNode;
|
import io.dataease.api.map.vo.AreaNode;
|
||||||
|
import io.dataease.constant.StaticResourceConstants;
|
||||||
|
import io.dataease.exception.DEException;
|
||||||
|
import io.dataease.map.bo.AreaBO;
|
||||||
import io.dataease.map.dao.auto.entity.Area;
|
import io.dataease.map.dao.auto.entity.Area;
|
||||||
import io.dataease.map.dao.auto.mapper.AreaMapper;
|
import io.dataease.map.dao.auto.mapper.AreaMapper;
|
||||||
|
import io.dataease.map.dao.ext.entity.CoreAreaCustom;
|
||||||
|
import io.dataease.map.dao.ext.mapper.CoreAreaCustomMapper;
|
||||||
import io.dataease.utils.BeanUtils;
|
import io.dataease.utils.BeanUtils;
|
||||||
|
import io.dataease.utils.CommonBeanFactory;
|
||||||
|
import io.dataease.utils.LogUtil;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
|
import org.apache.commons.collections4.CollectionUtils;
|
||||||
|
import org.apache.commons.lang3.ObjectUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.cache.annotation.CacheEvict;
|
||||||
import org.springframework.cache.annotation.Cacheable;
|
import org.springframework.cache.annotation.Cacheable;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static io.dataease.constant.CacheConstant.CommonCacheConstant.WORLD_MAP_CACHE;
|
import static io.dataease.constant.CacheConstant.CommonCacheConstant.WORLD_MAP_CACHE;
|
||||||
|
|
||||||
@ -19,6 +36,8 @@ import static io.dataease.constant.CacheConstant.CommonCacheConstant.WORLD_MAP_C
|
|||||||
public class MapManage {
|
public class MapManage {
|
||||||
private final static AreaNode WORLD;
|
private final static AreaNode WORLD;
|
||||||
|
|
||||||
|
private static final String GEO_PREFIX = "geo_";
|
||||||
|
|
||||||
static {
|
static {
|
||||||
WORLD = AreaNode.builder()
|
WORLD = AreaNode.builder()
|
||||||
.id("000")
|
.id("000")
|
||||||
@ -30,13 +49,34 @@ public class MapManage {
|
|||||||
@Resource
|
@Resource
|
||||||
private AreaMapper areaMapper;
|
private AreaMapper areaMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private CoreAreaCustomMapper coreAreaCustomMapper;
|
||||||
|
|
||||||
|
public List<Area> defaultArea() {
|
||||||
|
return areaMapper.selectList(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private MapManage proxy() {
|
||||||
|
return CommonBeanFactory.getBean(MapManage.class);
|
||||||
|
}
|
||||||
|
|
||||||
@Cacheable(value = WORLD_MAP_CACHE, key = "'world_map'")
|
@Cacheable(value = WORLD_MAP_CACHE, key = "'world_map'")
|
||||||
public AreaNode getWorldTree() {
|
public AreaNode getWorldTree() {
|
||||||
List<Area> areas = areaMapper.selectList(null);
|
List<Area> areas = proxy().defaultArea();
|
||||||
|
List<AreaBO> areaBOS = areas.stream().map(item -> BeanUtils.copyBean(new AreaBO(), item)).collect(Collectors.toList());
|
||||||
|
List<CoreAreaCustom> coreAreaCustoms = coreAreaCustomMapper.selectList(null);
|
||||||
|
if (CollectionUtils.isNotEmpty(coreAreaCustoms)) {
|
||||||
|
List<AreaBO> customBoList = coreAreaCustoms.stream().map(item -> {
|
||||||
|
AreaBO areaBO = BeanUtils.copyBean(new AreaBO(), item);
|
||||||
|
areaBO.setCustom(true);
|
||||||
|
return areaBO;
|
||||||
|
}).toList();
|
||||||
|
areaBOS.addAll(customBoList);
|
||||||
|
}
|
||||||
WORLD.setChildren(new ArrayList<>());
|
WORLD.setChildren(new ArrayList<>());
|
||||||
var areaNodeMap = new HashMap<String, AreaNode>();
|
var areaNodeMap = new HashMap<String, AreaNode>();
|
||||||
areaNodeMap.put(WORLD.getId(), WORLD);
|
areaNodeMap.put(WORLD.getId(), WORLD);
|
||||||
areas.forEach(area -> {
|
areaBOS.forEach(area -> {
|
||||||
var node = areaNodeMap.get(area.getId());
|
var node = areaNodeMap.get(area.getId());
|
||||||
if (node == null) {
|
if (node == null) {
|
||||||
node = AreaNode.builder().build();
|
node = AreaNode.builder().build();
|
||||||
@ -64,5 +104,80 @@ public class MapManage {
|
|||||||
return WORLD;
|
return WORLD;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@CacheEvict(cacheNames = WORLD_MAP_CACHE, key = "'world_map'")
|
||||||
|
@Transactional
|
||||||
|
public void saveMapGeo(GeometryNodeCreator request, MultipartFile file) {
|
||||||
|
List<Area> areas = proxy().defaultArea();
|
||||||
|
String code = getBusiGeoCode(request.getCode());
|
||||||
|
|
||||||
|
AtomicReference<String> atomicReference = new AtomicReference<>();
|
||||||
|
if (areas.stream().anyMatch(area -> {
|
||||||
|
boolean exist = area.getId().equals(code);
|
||||||
|
if (exist) {
|
||||||
|
atomicReference.set(area.getName());
|
||||||
|
}
|
||||||
|
return exist;
|
||||||
|
})) {
|
||||||
|
DEException.throwException(String.format("Area code [%s] is already exists for [%s]", code, atomicReference.get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
CoreAreaCustom originData = null;
|
||||||
|
if (ObjectUtils.isNotEmpty(originData = coreAreaCustomMapper.selectById(getDaoGeoCode(code)))) {
|
||||||
|
DEException.throwException(String.format("Area code [%s] is already exists for [%s]", code, originData.getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
CoreAreaCustom coreAreaCustom = new CoreAreaCustom();
|
||||||
|
coreAreaCustom.setId(getDaoGeoCode(code));
|
||||||
|
coreAreaCustom.setPid(request.getPid());
|
||||||
|
coreAreaCustom.setName(request.getName());
|
||||||
|
coreAreaCustomMapper.insert(coreAreaCustom);
|
||||||
|
|
||||||
|
File geoFile = buildGeoFile(code);
|
||||||
|
try {
|
||||||
|
file.transferTo(geoFile);
|
||||||
|
} catch (IOException e) {
|
||||||
|
LogUtil.error(e.getMessage());
|
||||||
|
DEException.throwException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@CacheEvict(cacheNames = WORLD_MAP_CACHE, key = "'world_map'")
|
||||||
|
@Transactional
|
||||||
|
public void deleteGeo(String code) {
|
||||||
|
if (!StringUtils.startsWith(code, GEO_PREFIX)) {
|
||||||
|
DEException.throwException("内置Geometry,禁止删除");
|
||||||
|
}
|
||||||
|
coreAreaCustomMapper.deleteById(code);
|
||||||
|
File file = buildGeoFile(code);
|
||||||
|
if (file.exists()) {
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getDaoGeoCode(String code) {
|
||||||
|
return StringUtils.startsWith(code, GEO_PREFIX) ? code : (GEO_PREFIX + code);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getBusiGeoCode(String code) {
|
||||||
|
return StringUtils.startsWith(code, GEO_PREFIX) ? code.substring(GEO_PREFIX.length()) : code;
|
||||||
|
}
|
||||||
|
|
||||||
|
private File buildGeoFile(String code) {
|
||||||
|
String id = getBusiGeoCode(code);
|
||||||
|
String customMapDir = StaticResourceConstants.CUSTOM_MAP_DIR;
|
||||||
|
String countryCode = countryCode(id);
|
||||||
|
String fileDirPath = customMapDir + "/" + countryCode + "/";
|
||||||
|
File dir = new File(fileDirPath);
|
||||||
|
if (!dir.exists()) {
|
||||||
|
dir.mkdirs();
|
||||||
|
}
|
||||||
|
String filePath = fileDirPath + id + ".json";
|
||||||
|
return new File(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String countryCode(String code) {
|
||||||
|
return code.substring(0, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
package io.dataease.map.server;
|
||||||
|
|
||||||
|
import io.dataease.api.map.GeoApi;
|
||||||
|
import io.dataease.api.map.dto.GeometryNodeCreator;
|
||||||
|
import io.dataease.map.manage.MapManage;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/geometry")
|
||||||
|
public class GeoServer implements GeoApi {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private MapManage mapManage;
|
||||||
|
@Override
|
||||||
|
public void saveMapGeo(GeometryNodeCreator request, MultipartFile file) {
|
||||||
|
mapManage.saveMapGeo(request, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteGeo(String id) {
|
||||||
|
mapManage.deleteGeo(id);
|
||||||
|
}
|
||||||
|
}
|
@ -26,12 +26,25 @@ VALUES (20, 15, 2, 'template-setting', 'system/template-setting', 4, 'icon_templ
|
|||||||
COMMIT;
|
COMMIT;
|
||||||
|
|
||||||
DROP TABLE IF EXISTS `visualization_template_extend_data`;
|
DROP TABLE IF EXISTS `visualization_template_extend_data`;
|
||||||
CREATE TABLE `visualization_template_extend_data` (
|
CREATE TABLE `visualization_template_extend_data`
|
||||||
`id` bigint NOT NULL,
|
(
|
||||||
`dv_id` bigint DEFAULT NULL,
|
`id` bigint NOT NULL,
|
||||||
`view_id` bigint DEFAULT NULL,
|
`dv_id` bigint DEFAULT NULL,
|
||||||
`view_details` longtext,
|
`view_id` bigint DEFAULT NULL,
|
||||||
`copy_from` varchar(255) DEFAULT NULL,
|
`view_details` longtext,
|
||||||
`copy_id` varchar(255) DEFAULT NULL,
|
`copy_from` varchar(255) DEFAULT NULL,
|
||||||
PRIMARY KEY (`id`)
|
`copy_id` varchar(255) DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Table structure for core_area_custom
|
||||||
|
-- ----------------------------
|
||||||
|
DROP TABLE IF EXISTS `core_area_custom`;
|
||||||
|
CREATE TABLE `core_area_custom`
|
||||||
|
(
|
||||||
|
`id` varchar(255) NOT NULL,
|
||||||
|
`name` varchar(255) NOT NULL,
|
||||||
|
`pid` varchar(255) NOT NULL,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
);
|
);
|
||||||
|
@ -5,9 +5,21 @@ export const getWorldTree = (): Promise<IResponse<AreaNode>> => {
|
|||||||
return request.get({ url: '/map/worldTree' })
|
return request.get({ url: '/map/worldTree' })
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getGeoJson = (
|
export const getGeoJson = (areaId: string): Promise<IResponse<FeatureCollection>> => {
|
||||||
country: string,
|
let prefix = '/map'
|
||||||
areaId: string
|
let areaCode = areaId
|
||||||
): Promise<IResponse<FeatureCollection>> => {
|
if (isCustomGeo(areaId)) {
|
||||||
return request.get({ url: `/map/${country}/${areaId}.json` })
|
prefix = '/geo'
|
||||||
|
areaCode = getBusiGeoCode(areaId)
|
||||||
|
}
|
||||||
|
const realCountry = areaCode.substring(0, 3)
|
||||||
|
const url = `${prefix}/${realCountry}/${areaCode}.json`
|
||||||
|
return request.get({ url })
|
||||||
|
}
|
||||||
|
|
||||||
|
const isCustomGeo = (id: string) => {
|
||||||
|
return id.startsWith('geo_')
|
||||||
|
}
|
||||||
|
const getBusiGeoCode = (id: string) => {
|
||||||
|
return id.substring(4)
|
||||||
}
|
}
|
||||||
|
@ -130,7 +130,7 @@ service.interceptors.response.use(
|
|||||||
return response
|
return response
|
||||||
} else if (response.data.code === result_code || response.data.code === 50002) {
|
} else if (response.data.code === result_code || response.data.code === 50002) {
|
||||||
return response.data
|
return response.data
|
||||||
} else if (response.config.url.match(/^\/map\/\d{3}\/\d+\.json$/)) {
|
} else if (response.config.url.match(/^\/map|geo\/\d{3}\/\d+\.json$/)) {
|
||||||
// TODO 处理静态文件
|
// TODO 处理静态文件
|
||||||
return response
|
return response
|
||||||
} else {
|
} else {
|
||||||
|
@ -421,10 +421,8 @@ export const getGeoJsonFile = async (areaId: string): Promise<FeatureCollection>
|
|||||||
const mapStore = useMapStoreWithOut()
|
const mapStore = useMapStoreWithOut()
|
||||||
let geoJson = mapStore.mapCache[areaId]
|
let geoJson = mapStore.mapCache[areaId]
|
||||||
if (!geoJson) {
|
if (!geoJson) {
|
||||||
const country = areaId.slice(0, 3)
|
const res = await getGeoJson(areaId)
|
||||||
geoJson = await getGeoJson(country, areaId).then(result => {
|
geoJson = res.data
|
||||||
return result.data
|
|
||||||
})
|
|
||||||
mapStore.setMap({ id: areaId, geoJson })
|
mapStore.setMap({ id: areaId, geoJson })
|
||||||
}
|
}
|
||||||
return toRaw(geoJson)
|
return toRaw(geoJson)
|
||||||
|
@ -43,6 +43,19 @@
|
|||||||
:title="data.name"
|
:title="data.name"
|
||||||
v-html="data.colorName && keyword ? data.colorName : data.name"
|
v-html="data.colorName && keyword ? data.colorName : data.name"
|
||||||
/>
|
/>
|
||||||
|
<span class="geo-operate-container">
|
||||||
|
<el-tooltip
|
||||||
|
v-if="data.custom"
|
||||||
|
class="box-item"
|
||||||
|
effect="dark"
|
||||||
|
:content="t('common.delete')"
|
||||||
|
placement="top"
|
||||||
|
>
|
||||||
|
<el-icon @click.stop="delHandler(data)" class="hover-icon">
|
||||||
|
<Icon name="icon_delete-trash_outlined"></Icon>
|
||||||
|
</el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</el-tree>
|
</el-tree>
|
||||||
@ -83,7 +96,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-main>
|
</el-main>
|
||||||
</el-container>
|
</el-container>
|
||||||
<geometry-edit ref="editor" :tree-data="treeData" @saved="loadTreeData" />
|
<geometry-edit ref="editor" @saved="loadTreeData(false)" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
@ -95,6 +108,10 @@ import { getGeoJsonFile } from '@/views/chart/components/js/util'
|
|||||||
import { cloneDeep } from 'lodash-es'
|
import { cloneDeep } from 'lodash-es'
|
||||||
import { setColorName } from '@/utils/utils'
|
import { setColorName } from '@/utils/utils'
|
||||||
import GeometryEdit from './GeometryEdit.vue'
|
import GeometryEdit from './GeometryEdit.vue'
|
||||||
|
import { useCache } from '@/hooks/web/useCache'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus-secondary'
|
||||||
|
import request from '@/config/axios'
|
||||||
|
const { wsCache } = useCache()
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const keyword = ref('')
|
const keyword = ref('')
|
||||||
const treeData = ref([])
|
const treeData = ref([])
|
||||||
@ -104,7 +121,7 @@ interface Tree {
|
|||||||
children?: Tree[]
|
children?: Tree[]
|
||||||
}
|
}
|
||||||
const areaTreeRef = ref(null)
|
const areaTreeRef = ref(null)
|
||||||
|
const loading = ref(false)
|
||||||
const selectedData = ref(null)
|
const selectedData = ref(null)
|
||||||
|
|
||||||
const handleNodeClick = async (data: Tree) => {
|
const handleNodeClick = async (data: Tree) => {
|
||||||
@ -119,6 +136,29 @@ const handleNodeClick = async (data: Tree) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const delHandler = data => {
|
||||||
|
ElMessageBox.confirm('确定删除此节点吗', {
|
||||||
|
confirmButtonType: 'danger',
|
||||||
|
type: 'warning',
|
||||||
|
confirmButtonText: t('common.delete'),
|
||||||
|
cancelButtonText: t('dataset.cancel'),
|
||||||
|
autofocus: false,
|
||||||
|
showClose: false
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
const url = '/geometry/delete/' + data.id
|
||||||
|
request.post({ url }).then(() => {
|
||||||
|
if (selectedData.value?.id === data.id) {
|
||||||
|
selectedData.value = null
|
||||||
|
}
|
||||||
|
ElMessage.success(t('common.delete_success'))
|
||||||
|
loadTreeData(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
loading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
const filterResource = val => {
|
const filterResource = val => {
|
||||||
areaTreeRef.value?.filter(val)
|
areaTreeRef.value?.filter(val)
|
||||||
}
|
}
|
||||||
@ -128,11 +168,18 @@ const filterResourceNode = (value: string, data) => {
|
|||||||
return data.name.toLocaleLowerCase().includes(value.toLocaleLowerCase())
|
return data.name.toLocaleLowerCase().includes(value.toLocaleLowerCase())
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadTreeData = () => {
|
const loadTreeData = (cache?: boolean) => {
|
||||||
|
const key = 'de-area-tree'
|
||||||
|
const result = wsCache.get(key)
|
||||||
|
if (result && cache) {
|
||||||
|
treeData.value = result
|
||||||
|
return
|
||||||
|
}
|
||||||
getWorldTree()
|
getWorldTree()
|
||||||
.then(res => {
|
.then(res => {
|
||||||
const root = res.data
|
const root = res.data
|
||||||
treeData.value = [root]
|
treeData.value = [root]
|
||||||
|
wsCache.set(key, treeData.value)
|
||||||
})
|
})
|
||||||
.catch(e => {
|
.catch(e => {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
@ -143,7 +190,7 @@ const add = (pid?: string) => {
|
|||||||
editor?.value.edit(pid)
|
editor?.value.edit(pid)
|
||||||
}
|
}
|
||||||
|
|
||||||
loadTreeData()
|
loadTreeData(true)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
@ -165,7 +212,6 @@ loadTreeData()
|
|||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
}
|
}
|
||||||
.add-icon-span {
|
.add-icon-span {
|
||||||
// display: none;
|
|
||||||
color: #3370ff;
|
color: #3370ff;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
width: 20px;
|
width: 20px;
|
||||||
@ -257,5 +303,16 @@ loadTreeData()
|
|||||||
box-sizing: content-box;
|
box-sizing: content-box;
|
||||||
padding-right: 4px;
|
padding-right: 4px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.geo-operate-container {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.geo-operate-container {
|
||||||
|
display: contents;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, reactive, PropType } from 'vue'
|
import { ref, reactive } from 'vue'
|
||||||
import { ElMessage, ElLoading } from 'element-plus-secondary'
|
import { ElMessage, ElLoading } from 'element-plus-secondary'
|
||||||
import { useI18n } from '@/hooks/web/useI18n'
|
import { useI18n } from '@/hooks/web/useI18n'
|
||||||
import type {
|
import type {
|
||||||
@ -10,16 +10,12 @@ import type {
|
|||||||
} from 'element-plus-secondary'
|
} from 'element-plus-secondary'
|
||||||
import request from '@/config/axios'
|
import request from '@/config/axios'
|
||||||
import { GeometryFrom } from './interface'
|
import { GeometryFrom } from './interface'
|
||||||
|
import { useCache } from '@/hooks/web/useCache'
|
||||||
|
const { wsCache } = useCache()
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const dialogVisible = ref(false)
|
const dialogVisible = ref(false)
|
||||||
const loadingInstance = ref(null)
|
const loadingInstance = ref(null)
|
||||||
const geoForm = ref<FormInstance>()
|
const geoForm = ref<FormInstance>()
|
||||||
const props = defineProps({
|
|
||||||
treeData: {
|
|
||||||
type: Array as PropType<unknown[]>,
|
|
||||||
default: () => []
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const geoFile = ref()
|
const geoFile = ref()
|
||||||
const fileName = ref()
|
const fileName = ref()
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
@ -27,7 +23,8 @@ const state = reactive({
|
|||||||
pid: null,
|
pid: null,
|
||||||
code: null,
|
code: null,
|
||||||
name: null
|
name: null
|
||||||
})
|
}),
|
||||||
|
treeData: []
|
||||||
})
|
})
|
||||||
const treeProps = {
|
const treeProps = {
|
||||||
value: 'id',
|
value: 'id',
|
||||||
@ -60,6 +57,8 @@ const rule = reactive<FormRules>({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const edit = (pid?: string) => {
|
const edit = (pid?: string) => {
|
||||||
|
const key = 'de-area-tree'
|
||||||
|
state.treeData = wsCache.get(key)
|
||||||
state.form.pid = pid
|
state.form.pid = pid
|
||||||
state.form.code = null
|
state.form.code = null
|
||||||
state.form.name = null
|
state.form.name = null
|
||||||
@ -75,9 +74,10 @@ const submitForm = async (formEl: FormInstance | undefined) => {
|
|||||||
await formEl.validate((valid, fields) => {
|
await formEl.validate((valid, fields) => {
|
||||||
if (valid) {
|
if (valid) {
|
||||||
const param = { ...state.form }
|
const param = { ...state.form }
|
||||||
|
const formData = buildFormData(geoFile.value, param)
|
||||||
showLoading()
|
showLoading()
|
||||||
request
|
request
|
||||||
.post({ url: '/sysParameter/map/save', data: param })
|
.post({ url: '/geometry/save', data: formData, headersType: 'multipart/form-data;' })
|
||||||
.then(res => {
|
.then(res => {
|
||||||
if (!res.msg) {
|
if (!res.msg) {
|
||||||
ElMessage.success(t('common.save_success'))
|
ElMessage.success(t('common.save_success'))
|
||||||
@ -136,6 +136,14 @@ const uploadValidate = file => {
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
const buildFormData = (file, param) => {
|
||||||
|
const formData = new FormData()
|
||||||
|
if (file) {
|
||||||
|
formData.append('file', file)
|
||||||
|
}
|
||||||
|
formData.append('request', new Blob([JSON.stringify(param)], { type: 'application/json' }))
|
||||||
|
return formData
|
||||||
|
}
|
||||||
defineExpose({
|
defineExpose({
|
||||||
edit
|
edit
|
||||||
})
|
})
|
||||||
@ -163,7 +171,7 @@ defineExpose({
|
|||||||
node-key="id"
|
node-key="id"
|
||||||
v-model="state.form.pid"
|
v-model="state.form.pid"
|
||||||
:props="treeProps"
|
:props="treeProps"
|
||||||
:data="props.treeData"
|
:data="state.treeData"
|
||||||
check-strictly
|
check-strictly
|
||||||
:render-after-expand="false"
|
:render-after-expand="false"
|
||||||
:placeholder="t('common.please_select')"
|
:placeholder="t('common.please_select')"
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
package io.dataease.api.map;
|
||||||
|
|
||||||
|
import io.dataease.api.map.dto.GeometryNodeCreator;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestPart;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
@Tag(name = "地理信息")
|
||||||
|
public interface GeoApi {
|
||||||
|
|
||||||
|
@PostMapping(value = "/save", consumes = {"multipart/form-data"})
|
||||||
|
void saveMapGeo(@RequestPart("request") GeometryNodeCreator request, @RequestPart(value = "file") MultipartFile file);
|
||||||
|
|
||||||
|
@PostMapping("/delete/{id}")
|
||||||
|
void deleteGeo(@PathVariable("id") String id);
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package io.dataease.api.map.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class GeometryNodeCreator implements Serializable {
|
||||||
|
|
||||||
|
private String code;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
private String pid;
|
||||||
|
}
|
@ -18,6 +18,7 @@ public class AreaNode implements Serializable {
|
|||||||
private String level;
|
private String level;
|
||||||
private String name;
|
private String name;
|
||||||
private String pid;
|
private String pid;
|
||||||
|
private boolean custom = false;
|
||||||
/**
|
/**
|
||||||
* 国家代码
|
* 国家代码
|
||||||
*/
|
*/
|
||||||
|
@ -16,8 +16,10 @@ public class StaticResourceConstants {
|
|||||||
public static String WORK_DIR = ensureSuffix(USER_HOME, FILE_SEPARATOR) + "static-resource" + FILE_SEPARATOR;
|
public static String WORK_DIR = ensureSuffix(USER_HOME, FILE_SEPARATOR) + "static-resource" + FILE_SEPARATOR;
|
||||||
|
|
||||||
public static String MAP_DIR = ensureSuffix(USER_HOME, FILE_SEPARATOR) + "map";
|
public static String MAP_DIR = ensureSuffix(USER_HOME, FILE_SEPARATOR) + "map";
|
||||||
|
public static String CUSTOM_MAP_DIR = ensureSuffix(USER_HOME, FILE_SEPARATOR) + "geo";
|
||||||
|
|
||||||
public static String MAP_URL = "/map";
|
public static String MAP_URL = "/map";
|
||||||
|
public static String GEO_URL = "/geo";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Upload prefix.
|
* Upload prefix.
|
||||||
|
@ -36,7 +36,7 @@ public class WhitelistUtils {
|
|||||||
|| StringUtils.startsWithAny(requestURI, "/static-resource/")
|
|| StringUtils.startsWithAny(requestURI, "/static-resource/")
|
||||||
|| StringUtils.startsWithAny(requestURI, "/share/proxyInfo")
|
|| StringUtils.startsWithAny(requestURI, "/share/proxyInfo")
|
||||||
|| StringUtils.startsWithAny(requestURI, "/xpackComponent/content/")
|
|| StringUtils.startsWithAny(requestURI, "/xpackComponent/content/")
|
||||||
|| StringUtils.startsWithAny(requestURI, "/platform/")
|
|| StringUtils.startsWithAny(requestURI, "/geo/")
|
||||||
|| StringUtils.startsWithAny(requestURI, "/map/");
|
|| StringUtils.startsWithAny(requestURI, "/map/");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user