Explorar el Código

Merge remote-tracking branch 'origin/master'

liu.shiyi hace 1 año
padre
commit
26bf5d5c2b
Se han modificado 20 ficheros con 1914 adiciones y 1 borrados
  1. 117 0
      DataRoom/dataroom-core/src/main/java/com/gccloud/dataroom/core/module/map/controller/DataRoomMapController.java
  2. 26 0
      DataRoom/dataroom-core/src/main/java/com/gccloud/dataroom/core/module/map/dao/DataRoomMapDao.java
  3. 38 0
      DataRoom/dataroom-core/src/main/java/com/gccloud/dataroom/core/module/map/dto/DataRoomMapDTO.java
  4. 23 0
      DataRoom/dataroom-core/src/main/java/com/gccloud/dataroom/core/module/map/dto/DataRoomMapRepeatDTO.java
  5. 27 0
      DataRoom/dataroom-core/src/main/java/com/gccloud/dataroom/core/module/map/dto/MapSearchDTO.java
  6. 46 0
      DataRoom/dataroom-core/src/main/java/com/gccloud/dataroom/core/module/map/entity/DataRoomMapEntity.java
  7. 100 0
      DataRoom/dataroom-core/src/main/java/com/gccloud/dataroom/core/module/map/service/IDataRoomMapService.java
  8. 270 0
      DataRoom/dataroom-core/src/main/java/com/gccloud/dataroom/core/module/map/service/impl/DataRoomMapServiceImpl.java
  9. 40 0
      DataRoom/dataroom-core/src/main/java/com/gccloud/dataroom/core/module/map/vo/DataRoomMapVO.java
  10. 20 0
      DataRoom/dataroom-core/src/main/java/com/gccloud/dataroom/core/module/map/vo/MapChildVO.java
  11. 55 0
      DataRoom/dataroom-core/src/main/resources/mapper/DataRoomMapDao.xml
  12. 23 1
      DataRoom/doc/update.sql
  13. 6 0
      data-room-ui/packages/Layout/BigScreenHomeLayout/index.vue
  14. 7 0
      data-room-ui/packages/MapDataManagement/index.js
  15. 338 0
      data-room-ui/packages/MapDataManagement/src/AddForm.vue
  16. 273 0
      data-room-ui/packages/MapDataManagement/src/EditForm.vue
  17. 426 0
      data-room-ui/packages/MapDataManagement/src/index.vue
  18. 1 0
      data-room-ui/packages/assets/style/bsTheme.scss
  19. 71 0
      data-room-ui/packages/js/utils/mapDataService.js
  20. 7 0
      data-room-ui/packages/js/utils/registerConfig.js

+ 117 - 0
DataRoom/dataroom-core/src/main/java/com/gccloud/dataroom/core/module/map/controller/DataRoomMapController.java

@@ -0,0 +1,117 @@
+package com.gccloud.dataroom.core.module.map.controller;
+
+import com.gccloud.common.utils.BeanConvertUtils;
+import com.gccloud.common.vo.R;
+import com.gccloud.dataroom.core.module.map.dto.DataRoomMapRepeatDTO;
+import com.gccloud.dataroom.core.module.map.vo.DataRoomMapVO;
+import com.gccloud.dataroom.core.module.map.vo.MapChildVO;
+import com.gccloud.dataroom.core.module.map.dto.DataRoomMapDTO;
+import com.gccloud.dataroom.core.module.map.dto.MapSearchDTO;
+import com.gccloud.dataroom.core.module.map.entity.DataRoomMapEntity;
+import com.gccloud.dataroom.core.module.map.service.IDataRoomMapService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiSort;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+/**
+ * 地图数据管理
+ * @author hongyang
+ * @version 1.0
+ * @date 2023/8/31 16:52
+ */
+@Slf4j
+@RestController
+@RequestMapping("/bigScreen/map")
+@Api(tags = "地图数据管理")
+@ApiSort(value = 110)
+public class DataRoomMapController {
+
+    @Resource
+    private IDataRoomMapService dataRoomMapService;
+
+
+    @GetMapping("/list")
+    @ApiOperation(value = "列表", position = 10, notes = "地图数据列表查询", produces = MediaType.APPLICATION_JSON_VALUE)
+    public R<List<DataRoomMapVO>> list(MapSearchDTO searchDTO) {
+        List<DataRoomMapVO> list = dataRoomMapService.getList(searchDTO);
+        return R.success(list);
+    }
+
+
+    @PostMapping("/add")
+    @ApiOperation(value = "添加", position = 20, notes = "添加地图数据", produces = MediaType.APPLICATION_JSON_VALUE)
+    public R<String> add(@RequestBody DataRoomMapDTO mapDTO) {
+        String id = dataRoomMapService.add(mapDTO);
+        return R.success(id);
+    }
+
+    @PostMapping("/update")
+    @ApiOperation(value = "修改", position = 30, notes = "修改地图数据", produces = MediaType.APPLICATION_JSON_VALUE)
+    public R<Void> update(@RequestBody DataRoomMapDTO mapDTO) {
+        dataRoomMapService.update(mapDTO);
+        return R.success();
+    }
+
+    @PostMapping("/delete/{id}")
+    @ApiOperation(value = "删除", position = 40, notes = "删除地图数据", produces = MediaType.APPLICATION_JSON_VALUE)
+    public R<Void> delete(@PathVariable String id) {
+        dataRoomMapService.delete(id);
+        return R.success();
+    }
+
+    @PostMapping("/cascadingDelete/{id}")
+    @ApiOperation(value = "级联删除", position = 50, notes = "级联删除地图数据", produces = MediaType.APPLICATION_JSON_VALUE)
+    public R<Void> cascadingDelete(@PathVariable String id) {
+        dataRoomMapService.cascadingDelete(id);
+        return R.success();
+    }
+
+    @GetMapping("/getMapChildFromGeoJson/{code}")
+    @ApiOperation(value = "根据父编码解析父级json中的子级", position = 60, notes = "根据父编码解析父级json中的子级", produces = MediaType.APPLICATION_JSON_VALUE)
+    public R<List<MapChildVO>> getMapChildFromGeoJson(@PathVariable String code) {
+        List<MapChildVO> list = dataRoomMapService.getChildFromGeo(code);
+        return R.success(list);
+    }
+
+
+    @GetMapping("/info/{id}")
+    @ApiOperation(value = "详情", position = 70, notes = "地图数据详情", produces = MediaType.APPLICATION_JSON_VALUE)
+    public R<DataRoomMapVO> info(@PathVariable String id) {
+        DataRoomMapEntity info = dataRoomMapService.info(id);
+        DataRoomMapVO vo = BeanConvertUtils.convert(info, DataRoomMapVO.class);
+        return R.success(vo);
+    }
+
+
+    @GetMapping("/data/{id}")
+    @ApiOperation(value = "数据", position = 80, notes = "地图数据数据", produces = MediaType.APPLICATION_JSON_VALUE)
+    public R<String> data(@PathVariable String id) {
+        DataRoomMapEntity info = dataRoomMapService.info(id);
+        return R.success(info.getGeoJson());
+    }
+
+
+    @PostMapping("/upload")
+    @ApiOperation(value = "上传", position = 90, notes = "上传地图数据", produces = MediaType.APPLICATION_JSON_VALUE)
+    public R<Void> upload(@RequestBody DataRoomMapDTO mapDTO) {
+        dataRoomMapService.uploadGeoJson(mapDTO.getId(), mapDTO.getGeoJson());
+        return R.success();
+    }
+
+
+    @PostMapping("/repeat")
+    @ApiOperation(value = "重复", position = 100, notes = "地图数据重复校验", produces = MediaType.APPLICATION_JSON_VALUE)
+    public R<Boolean> repeat(@RequestBody DataRoomMapRepeatDTO mapDTO) {
+        Boolean repeat = dataRoomMapService.repeatCheck(mapDTO);
+        return R.success(repeat);
+    }
+
+
+
+}

+ 26 - 0
DataRoom/dataroom-core/src/main/java/com/gccloud/dataroom/core/module/map/dao/DataRoomMapDao.java

@@ -0,0 +1,26 @@
+package com.gccloud.dataroom.core.module.map.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.gccloud.dataroom.core.module.map.dto.MapSearchDTO;
+import com.gccloud.dataroom.core.module.map.entity.DataRoomMapEntity;
+import com.gccloud.dataroom.core.module.map.vo.DataRoomMapVO;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * @author hongyang
+ * @version 1.0
+ * @date 2023/8/31 13:52
+ */
+@Mapper
+public interface DataRoomMapDao extends BaseMapper<DataRoomMapEntity> {
+
+    /**
+     * 获取地图列表
+     * @return
+     */
+    List<DataRoomMapVO> getList(@Param("searchDTO") MapSearchDTO searchDTO);
+
+}

+ 38 - 0
DataRoom/dataroom-core/src/main/java/com/gccloud/dataroom/core/module/map/dto/DataRoomMapDTO.java

@@ -0,0 +1,38 @@
+package com.gccloud.dataroom.core.module.map.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * @author hongyang
+ * @version 1.0
+ * @date 2023/8/31 13:44
+ */
+@Data
+public class DataRoomMapDTO {
+
+    @ApiModelProperty(notes = "主键")
+    private String id;
+
+    @ApiModelProperty(notes = "父级编码")
+    private String parentCode;
+
+    @ApiModelProperty(notes = "地图编码")
+    private String mapCode;
+
+    @ApiModelProperty(notes = "地图名称")
+    private String name;
+
+    @ApiModelProperty(notes = "地图级别 0-世界 1-国家 2-省 3-市 4-区县")
+    private Integer level;
+
+    @ApiModelProperty(notes = "是否支持下钻")
+    private Integer enableDown;
+
+    @ApiModelProperty(notes = "geo地图数据json")
+    private String geoJson;
+
+    @ApiModelProperty(notes = "是否自动解析下一级,是的话根据geoJson自动解析下一级的基础信息(不包含geoJson)")
+    private Integer autoParseNextLevel;
+
+}

+ 23 - 0
DataRoom/dataroom-core/src/main/java/com/gccloud/dataroom/core/module/map/dto/DataRoomMapRepeatDTO.java

@@ -0,0 +1,23 @@
+package com.gccloud.dataroom.core.module.map.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * @author hongyang
+ * @version 1.0
+ * @date 2023/9/7 16:14
+ */
+@Data
+public class DataRoomMapRepeatDTO {
+
+    @ApiModelProperty(notes = "主键")
+    private String id;
+
+    @ApiModelProperty(notes = "父级编码")
+    private String parentCode;
+
+    @ApiModelProperty(notes = "地图编码")
+    private String mapCode;
+
+}

+ 27 - 0
DataRoom/dataroom-core/src/main/java/com/gccloud/dataroom/core/module/map/dto/MapSearchDTO.java

@@ -0,0 +1,27 @@
+package com.gccloud.dataroom.core.module.map.dto;
+
+import com.gccloud.common.dto.SearchDTO;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * @author hongyang
+ * @version 1.0
+ * @date 2023/8/31 14:07
+ */
+@Data
+public class MapSearchDTO extends SearchDTO {
+
+    @ApiModelProperty(notes = "父级编码")
+    private String parentCode;
+
+    @ApiModelProperty(notes = "层级")
+    private Integer level;
+
+    @ApiModelProperty(notes = "是否已上传")
+    private Integer uploadedGeoJson;
+
+    @ApiModelProperty(notes = "是否支持下钻")
+    private Integer enableDown;
+
+}

+ 46 - 0
DataRoom/dataroom-core/src/main/java/com/gccloud/dataroom/core/module/map/entity/DataRoomMapEntity.java

@@ -0,0 +1,46 @@
+package com.gccloud.dataroom.core.module.map.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.gccloud.common.entity.SuperEntity;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.ToString;
+import lombok.experimental.Accessors;
+
+/**
+ * geo地图信息
+ * @author hongyang
+ * @version 1.0
+ * @date 2023/8/31 13:36
+ */
+@Data
+@Accessors(chain = true)
+@TableName("big_screen_map")
+@ApiModel
+@ToString(callSuper = true)
+public class DataRoomMapEntity extends SuperEntity {
+
+    @ApiModelProperty(notes = "父级编码")
+    private String parentCode;
+
+    @ApiModelProperty(notes = "地图编码")
+    private String mapCode;
+
+    @ApiModelProperty(notes = "地图名称")
+    private String name;
+
+    @ApiModelProperty(notes = "地图级别 0-世界 1-国家 2-省 3-市 4-区县")
+    private Integer level;
+
+    @ApiModelProperty(notes = "是否支持下钻 0-否 1-是")
+    private Integer enableDown;
+
+    @ApiModelProperty(notes = "geo地图数据json")
+    private String geoJson;
+
+    @ApiModelProperty(notes = "是否已上传geoJson 0-否 1-是")
+    private Integer uploadedGeoJson;
+
+
+}

+ 100 - 0
DataRoom/dataroom-core/src/main/java/com/gccloud/dataroom/core/module/map/service/IDataRoomMapService.java

@@ -0,0 +1,100 @@
+package com.gccloud.dataroom.core.module.map.service;
+
+import com.gccloud.common.service.ISuperService;
+import com.gccloud.dataroom.core.module.map.dto.DataRoomMapRepeatDTO;
+import com.gccloud.dataroom.core.module.map.vo.DataRoomMapVO;
+import com.gccloud.dataroom.core.module.map.vo.MapChildVO;
+import com.gccloud.dataroom.core.module.map.dto.DataRoomMapDTO;
+import com.gccloud.dataroom.core.module.map.dto.MapSearchDTO;
+import com.gccloud.dataroom.core.module.map.entity.DataRoomMapEntity;
+
+import java.util.List;
+
+/**
+ * @author hongyang
+ * @version 1.0
+ * @date 2023/8/31 13:53
+ */
+public interface IDataRoomMapService extends ISuperService<DataRoomMapEntity> {
+
+    /**
+     * 顶级父级编码
+     */
+    String SUPER_PARENT_CODE = "0";
+
+
+    /**
+     * 获取地图列表
+     * @param searchDTO
+     * @return
+     */
+    List<DataRoomMapVO> getList(MapSearchDTO searchDTO);
+
+
+    /**
+     * 添加地图
+     * @param mapDTO
+     * @return
+     */
+    String add(DataRoomMapDTO mapDTO);
+
+
+    /**
+     * 修改地图
+     * @param mapDTO
+     */
+    void update(DataRoomMapDTO mapDTO);
+
+
+    /**
+     * 删除地图
+     * @param id
+     */
+    void delete(String id);
+
+    /**
+     * 级联删除地图,删除地图及其子地图...
+     * @param id
+     */
+    void cascadingDelete(String id);
+
+
+    /**
+     * 获取地图
+     * @param id
+     * @return
+     */
+    DataRoomMapEntity info(String id);
+
+
+    /**
+     * 获取地图
+     * @param mapCode
+     * @return
+     */
+    DataRoomMapEntity infoByMapCode(String mapCode);
+
+
+    /**
+     * 根据编码获取地图信息,取到geoJson,根据geoJson解析下一级的基础数据
+     * @param code
+     * @return
+     */
+    List<MapChildVO> getChildFromGeo(String code);
+
+
+    /**
+     * 上传geoJson
+     * @param id
+     * @param geoJson
+     */
+    void uploadGeoJson(String id, String geoJson);
+
+    /**
+     * 编码重复校验
+     * @param mapDTO
+     * @return
+     */
+    boolean repeatCheck(DataRoomMapRepeatDTO mapDTO);
+
+}

+ 270 - 0
DataRoom/dataroom-core/src/main/java/com/gccloud/dataroom/core/module/map/service/impl/DataRoomMapServiceImpl.java

@@ -0,0 +1,270 @@
+package com.gccloud.dataroom.core.module.map.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.gccloud.common.exception.GlobalException;
+import com.gccloud.common.utils.BeanConvertUtils;
+import com.gccloud.common.utils.JSON;
+import com.gccloud.dataroom.core.module.map.dto.DataRoomMapRepeatDTO;
+import com.gccloud.dataroom.core.module.map.vo.DataRoomMapVO;
+import com.gccloud.dataroom.core.module.map.vo.MapChildVO;
+import com.gccloud.dataroom.core.module.map.dao.DataRoomMapDao;
+import com.gccloud.dataroom.core.module.map.dto.DataRoomMapDTO;
+import com.gccloud.dataroom.core.module.map.dto.MapSearchDTO;
+import com.gccloud.dataroom.core.module.map.entity.DataRoomMapEntity;
+import com.gccloud.dataroom.core.module.map.service.IDataRoomMapService;
+import com.google.common.collect.Lists;
+import org.apache.commons.lang3.StringUtils;
+import org.json.JSONArray;
+import org.json.JSONObject;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @author hongyang
+ * @version 1.0
+ * @date 2023/8/31 14:08
+ */
+@Service
+public class DataRoomMapServiceImpl extends ServiceImpl<DataRoomMapDao, DataRoomMapEntity> implements IDataRoomMapService {
+
+    public static final int YES = 1;
+    public static final int NO = 0;
+
+    @Override
+    public List<DataRoomMapVO> getList(MapSearchDTO searchDTO) {
+        List<DataRoomMapVO> list = this.baseMapper.getList(searchDTO);
+        return list;
+    }
+
+    @Override
+    public String add(DataRoomMapDTO mapDTO) {
+        if (StringUtils.isBlank(mapDTO.getParentCode())) {
+            mapDTO.setParentCode(SUPER_PARENT_CODE);
+        }
+        if (StringUtils.isBlank(mapDTO.getMapCode())) {
+            throw new GlobalException("地图编码不能为空");
+        }
+        DataRoomMapEntity mapEntity = BeanConvertUtils.convert(mapDTO, DataRoomMapEntity.class);
+        if (StringUtils.isNotBlank(mapEntity.getGeoJson())) {
+            mapEntity.setUploadedGeoJson(YES);
+        } else {
+            mapEntity.setUploadedGeoJson(NO);
+        }
+        this.save(mapEntity);
+        if (mapDTO.getAutoParseNextLevel().equals(YES) && mapEntity.getUploadedGeoJson().equals(YES)) {
+            // 自动从geoJson中解析下一级的基础数据,保存到数据库,将geoJson上传状态置为false
+            this.parseNextLevelAndSave(mapEntity, mapEntity.getGeoJson());
+        }
+        return mapEntity.getId();
+    }
+
+    @Override
+    public void update(DataRoomMapDTO mapDTO) {
+        if (StringUtils.isBlank(mapDTO.getId())) {
+            throw new GlobalException("地图id不能为空");
+        }
+        DataRoomMapEntity old = this.getById(mapDTO.getId());
+        if (old == null) {
+            throw new GlobalException("地图不存在");
+        }
+        Integer uploadedGeoJson = old.getUploadedGeoJson();
+        LambdaUpdateWrapper<DataRoomMapEntity> updateWrapper = new LambdaUpdateWrapper<>();
+        updateWrapper.eq(DataRoomMapEntity::getId, mapDTO.getId());
+        // 只允许修改名称和是否开启下钻
+        updateWrapper.set(DataRoomMapEntity::getName, mapDTO.getName());
+        updateWrapper.set(DataRoomMapEntity::getEnableDown, mapDTO.getEnableDown());
+        // 如果之前没有上传过geoJson,现在上传了,那么允许更新geoJson
+        if (!uploadedGeoJson.equals(YES) && StringUtils.isNotBlank(mapDTO.getGeoJson())) {
+            if (mapDTO.getAutoParseNextLevel().equals(YES)) {
+                // 自动从geoJson中解析下一级的基础数据,保存到数据库
+                this.parseNextLevelAndSave(old, mapDTO.getGeoJson());
+            }
+            updateWrapper.set(DataRoomMapEntity::getGeoJson, mapDTO.getGeoJson());
+            updateWrapper.set(DataRoomMapEntity::getUploadedGeoJson, YES);
+        }
+        this.update(updateWrapper);
+    }
+
+    /**
+     * 从geoJson中解析下一级的基础数据,保存到数据库
+     * @param old
+     * @param geoJson
+     */
+    private void parseNextLevelAndSave(DataRoomMapEntity old, String geoJson) {
+        JSONObject jsonObject = new JSONObject(geoJson);
+        JSONArray features = jsonObject.getJSONArray("features");
+        if (features == null || features.length() == 0) {
+            throw new GlobalException("GeoJson格式不正确,自动解析失败");
+        }
+        List<DataRoomMapEntity> mapEntityList = Lists.newArrayList();
+        for (int i = 0; i < features.length(); i++) {
+            JSONObject feature = features.getJSONObject(i);
+            JSONObject properties = feature.getJSONObject("properties");
+            if (properties == null) {
+                continue;
+            }
+            DataRoomMapEntity childMapEntity = new DataRoomMapEntity();
+            childMapEntity.setParentCode(old.getMapCode());
+            childMapEntity.setLevel(old.getLevel() + 1);
+            childMapEntity.setMapCode(properties.getString("name"));
+            childMapEntity.setName(properties.getString("name"));
+            childMapEntity.setEnableDown(NO);
+            childMapEntity.setUploadedGeoJson(NO);
+            mapEntityList.add(childMapEntity);
+        }
+        if (mapEntityList.size() > 0) {
+            this.saveBatch(mapEntityList);
+        }
+    }
+
+    @Override
+    public void delete(String id) {
+        if (StringUtils.isBlank(id)) {
+            return;
+        }
+        DataRoomMapEntity mapEntity = this.getById(id);
+        if (mapEntity == null) {
+            return;
+        }
+        LambdaQueryWrapper<DataRoomMapEntity> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(DataRoomMapEntity::getParentCode, mapEntity.getMapCode());
+        List<DataRoomMapEntity> list = this.list(queryWrapper);
+        if (list != null && list.size() > 0) {
+            throw new GlobalException("该地图下存在子地图,不能删除");
+        }
+        this.removeById(id);
+    }
+
+
+    /**
+     * 级联删除
+     * @param id
+     */
+    @Override
+    public void cascadingDelete(String id) {
+        if (StringUtils.isBlank(id)) {
+            return;
+        }
+        DataRoomMapEntity mapEntity = this.getById(id);
+        if (mapEntity == null) {
+            return;
+        }
+        LambdaQueryWrapper<DataRoomMapEntity> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(DataRoomMapEntity::getParentCode, mapEntity.getMapCode());
+        List<DataRoomMapEntity> list = this.list(queryWrapper);
+        if (list != null && list.size() > 0) {
+            for (DataRoomMapEntity entity : list) {
+                this.cascadingDelete(entity.getId());
+            }
+        }
+        this.removeById(id);
+    }
+
+    @Override
+    public DataRoomMapEntity info(String id) {
+        DataRoomMapEntity mapEntity = getById(id);
+        if (mapEntity == null) {
+            throw new GlobalException("地图不存在");
+        }
+        return mapEntity;
+    }
+
+    @Override
+    public DataRoomMapEntity infoByMapCode(String mapCode) {
+        LambdaQueryWrapper<DataRoomMapEntity> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(DataRoomMapEntity::getMapCode, mapCode);
+        List<DataRoomMapEntity> list = this.list(queryWrapper);
+        if (list == null || list.size() == 0) {
+            throw new GlobalException("地图不存在");
+        }
+        if (list.size() > 1) {
+            throw new GlobalException("地图编码重复");
+        }
+        return list.get(0);
+    }
+
+
+    /**
+     * 根据编码获取地图信息,取到geoJson,根据geoJson解析下一级的基础数据
+     * @param code
+     * @return
+     */
+    @Override
+    public List<MapChildVO> getChildFromGeo(String code) {
+        DataRoomMapEntity mapEntity = this.infoByMapCode(code);
+        if (mapEntity.getUploadedGeoJson().equals(NO)) {
+            return Lists.newArrayList();
+        }
+        String geoJson = mapEntity.getGeoJson();
+        if (StringUtils.isBlank(geoJson)) {
+            return Lists.newArrayList();
+        }
+        JSONObject geoObj = JSON.parseObject(geoJson);
+        JSONArray features = geoObj.getJSONArray("features");
+        if (features == null || features.length() == 0) {
+            return Lists.newArrayList();
+        }
+        // 查询当前地图下的所有子地图
+        LambdaQueryWrapper<DataRoomMapEntity> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.select(DataRoomMapEntity::getMapCode);
+        queryWrapper.eq(DataRoomMapEntity::getParentCode, code);
+        List<DataRoomMapEntity> list = this.list(queryWrapper);
+        List<String> mapCodeList = list.stream().map(DataRoomMapEntity::getMapCode).collect(Collectors.toList());
+        // 解析geoJson,获取下一级的基础数据
+        List<MapChildVO> childList = Lists.newArrayList();
+        for (int i = 0; i < features.length(); i++) {
+            JSONObject feature = features.getJSONObject(i);
+            JSONObject properties = feature.getJSONObject("properties");
+            String name = properties.getString("name");
+            MapChildVO childVO = new MapChildVO();
+            childVO.setName(name);
+            childVO.setExist(mapCodeList.contains(name));
+            childList.add(childVO);
+        }
+        return childList;
+    }
+
+
+    @Override
+    public void uploadGeoJson(String id, String geoJson) {
+        if (StringUtils.isBlank(id)) {
+            throw new GlobalException("地图id不能为空");
+        }
+        if (StringUtils.isBlank(geoJson)) {
+            throw new GlobalException("geoJson不能为空");
+        }
+        DataRoomMapEntity mapEntity = this.getById(id);
+        if (mapEntity == null) {
+            throw new GlobalException("地图不存在");
+        }
+        if (mapEntity.getUploadedGeoJson().equals(YES)) {
+            throw new GlobalException("该地图已上传过geoJson,不能重复上传");
+        }
+        mapEntity.setGeoJson(geoJson);
+        mapEntity.setUploadedGeoJson(YES);
+        this.updateById(mapEntity);
+    }
+
+    @Override
+    public boolean repeatCheck(DataRoomMapRepeatDTO mapDTO) {
+        if (StringUtils.isBlank(mapDTO.getMapCode())) {
+            throw new GlobalException("地图编码不能为空");
+        }
+        if (StringUtils.isBlank(mapDTO.getParentCode())) {
+            throw new GlobalException("上级地图编码不能为空");
+        }
+        // TODO 这里存在问题,因为编码只在某个地图下的某个层级下是唯一的,所以如果碰巧有两个地图下的某个层级下的编码相同,就会出现问题
+        LambdaQueryWrapper<DataRoomMapEntity> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(DataRoomMapEntity::getMapCode, mapDTO.getMapCode());
+        queryWrapper.eq(DataRoomMapEntity::getParentCode, mapDTO.getParentCode());
+        if (StringUtils.isNotBlank(mapDTO.getId())) {
+            queryWrapper.ne(DataRoomMapEntity::getId, mapDTO.getId());
+        }
+        List<DataRoomMapEntity> list = this.list(queryWrapper);
+        return list != null && list.size() > 0;
+    }
+}

+ 40 - 0
DataRoom/dataroom-core/src/main/java/com/gccloud/dataroom/core/module/map/vo/DataRoomMapVO.java

@@ -0,0 +1,40 @@
+package com.gccloud.dataroom.core.module.map.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * @author hongyang
+ * @version 1.0
+ * @date 2023/9/4 17:16
+ */
+@Data
+public class DataRoomMapVO {
+
+    @ApiModelProperty(notes = "主键")
+    private String id;
+
+    @ApiModelProperty(notes = "父级编码")
+    private String parentCode;
+
+    @ApiModelProperty(notes = "地图编码")
+    private String mapCode;
+
+    @ApiModelProperty(notes = "地图名称")
+    private String name;
+
+    @ApiModelProperty(notes = "地图级别 0-世界 1-国家 2-省 3-市 4-区县")
+    private Integer level;
+
+    @ApiModelProperty(notes = "是否支持下钻 0-否 1-是")
+    private Integer enableDown;
+
+    @ApiModelProperty(notes = "geo地图数据json")
+    private String geoJson;
+
+    @ApiModelProperty(notes = "是否已上传geoJson 0-否 1-是")
+    private Integer uploadedGeoJson;
+
+    @ApiModelProperty(notes = "是否有子节点")
+    private Boolean hasChildren;
+}

+ 20 - 0
DataRoom/dataroom-core/src/main/java/com/gccloud/dataroom/core/module/map/vo/MapChildVO.java

@@ -0,0 +1,20 @@
+package com.gccloud.dataroom.core.module.map.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * @author hongyang
+ * @version 1.0
+ * @date 2023/8/31 16:13
+ */
+@Data
+public class MapChildVO {
+
+    @ApiModelProperty(notes = "地图名称")
+    private String name;
+
+    @ApiModelProperty(notes = "是否已存在")
+    private Boolean exist;
+
+}

+ 55 - 0
DataRoom/dataroom-core/src/main/resources/mapper/DataRoomMapDao.xml

@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="com.gccloud.dataroom.core.module.map.dao.DataRoomMapDao">
+
+    <!--  查询地图数据  -->
+    <select id="getList" resultType="com.gccloud.dataroom.core.module.map.vo.DataRoomMapVO">
+        SELECT id,
+               parent_code,
+               map_code,
+               name,
+               level,
+               enable_down,
+               geo_json,
+               uploaded_geo_json,
+               create_date,
+               update_date,
+               create_by,
+               update_by,
+               del_flag,
+                map_code as curentMapcode,
+               CASE WHEN EXISTS(SELECT 1
+                                FROM big_screen_map
+                                WHERE parent_code = curentMapcode
+                                  AND del_flag = 0)
+                   THEN 1
+               ELSE 0 END AS has_children
+        FROM big_screen_map
+        WHERE del_flag = 0
+        <if test="searchDTO != null">
+            <if test="searchDTO.parentCode != null and searchDTO.parentCode != ''">
+                AND parent_code = #{searchDTO.parentCode}
+            </if>
+            <if test="searchDTO.searchKey != null and searchDTO.searchKey != ''">
+                AND (
+                    name like concat('%', #{searchDTO.searchKey}, '%')
+                    OR map_code like concat('%', #{searchDTO.searchKey}, '%')
+                    )
+            </if>
+            <if test="searchDTO.enableDown != null">
+                AND enable_down = #{searchDTO.enableDown}
+            </if>
+            <if test="searchDTO.level != null">
+                AND level = #{searchDTO.level}
+            </if>
+            <if test="searchDTO.uploadedGeoJson != null">
+                AND uploaded_geo_json = #{searchDTO.uploadedGeoJson}
+            </if>
+
+        </if>
+
+    </select>
+
+
+</mapper>

+ 23 - 1
DataRoom/doc/update.sql

@@ -103,4 +103,26 @@ CREATE TABLE `ds_dataset_label` (
 ) ENGINE=InnoDB   DEFAULT CHARSET=utf8 COLLATE=utf8_general_mysql500_ci COMMENT='数据集与标签关联表';
 
 # 20230710 数据源新增字段
-ALTER TABLE `ds_datasource` ADD COLUMN `table_name` varchar(255) DEFAULT NULL COMMENT '表名' AFTER `module_code`;
+ALTER TABLE `ds_datasource` ADD COLUMN `table_name` varchar(255) DEFAULT NULL COMMENT '表名' AFTER `module_code`;
+
+
+# 20230907 新增地图数据维护表
+DROP TABLE IF EXISTS `big_screen_map`;
+CREATE TABLE `big_screen_map`
+(
+    `id`                bigint(32) NOT NULL AUTO_INCREMENT COMMENT '主键',
+    `parent_code`       varchar(255)        DEFAULT NULL COMMENT '父级地图编码',
+    `map_code`          varchar(255)        DEFAULT NULL COMMENT '地图编码',
+    `name`              varchar(255)        DEFAULT NULL COMMENT '地图名称',
+    `geo_json`          longtext            DEFAULT NULL COMMENT '地图geoJson',
+    `level`             tinyint(2) NOT NULL DEFAULT 0 COMMENT '地图级别 0-世界 1-国家 2-省 3-市 4-区县',
+    `enable_down`       tinyint(2) NOT NULL DEFAULT 0 COMMENT '是否支持下钻 0-否 1-是',
+    `uploaded_geo_json` tinyint(2) NOT NULL DEFAULT 0 COMMENT '是否已上传geoJson 0-否 1-是',
+    `update_date`       timestamp  NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
+    `create_date`       timestamp  NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    `create_by`         bigint(64) null     default 2 comment '创建人',
+    `update_by`         bigint(64) null     default 2 comment '更新人',
+    `del_flag`          tinyint(2) NOT NULL DEFAULT '0' COMMENT '删除标识',
+    PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB
+  DEFAULT CHARSET = utf8 COMMENT ='地图数据维护表';

+ 6 - 0
data-room-ui/packages/Layout/BigScreenHomeLayout/index.vue

@@ -113,6 +113,12 @@ export default {
           name: '数据集管理',
           path: window?.BS_CONFIG?.routers?.dataSetUrl || '/big-screen-dataSet',
           icon: 'icon-data'
+        },
+        {
+          id: 5,
+          name: '地图数据管理',
+          path: '/big-screen-map-data',
+          icon: 'icon-data'
         }
       ]
     }

+ 7 - 0
data-room-ui/packages/MapDataManagement/index.js

@@ -0,0 +1,7 @@
+import MapDataManagement from './src/index.vue'
+
+MapDataManagement.install = function (Vue) {
+  Vue.component(MapDataManagement.name, MapDataManagement)
+}
+
+export default MapDataManagement

+ 338 - 0
data-room-ui/packages/MapDataManagement/src/AddForm.vue

@@ -0,0 +1,338 @@
+<template>
+  <div>
+    <el-dialog
+      :append-to-body="true"
+      :before-close="handleClose"
+      :close-on-click-modal="false"
+      :title="title"
+      :visible.sync="mapFormVisible"
+      class="bs-dialog-wrap bs-el-dialog"
+      width="700px"
+    >
+      <el-form
+        ref="mapForm"
+        :model="mapForm"
+        :rules="rules"
+        class="bs-el-form"
+        label-width="120px"
+      >
+        <el-form-item
+          label="上级地图"
+          prop="parentCode"
+        >
+          <el-input
+            v-model="parentName"
+            class="bs-el-input"
+            disabled
+          />
+        </el-form-item>
+        <el-form-item
+          label="地图名称"
+          prop="name"
+        >
+          <el-input
+            v-model="mapForm.name"
+            class="bs-el-input"
+            placeholder="请输入"
+          />
+        </el-form-item>
+        <el-form-item
+          label="地图编码"
+          prop="mapCode"
+        >
+          <el-input
+            v-if="mapForm.parentCode === '0'"
+            v-model="mapForm.mapCode"
+            class="bs-el-input"
+            placeholder="请输入地图编码"
+          />
+          <el-select
+            v-else
+            v-model="mapForm.mapCode"
+            class="bs-el-select"
+            placeholder="请选择地图编码"
+            popper-class="bs-el-select"
+          >
+            <el-option
+              v-for="mapCode in mapCodeList"
+              :key="mapCode.name"
+              :disabled="mapCode.exist"
+              :label="mapCode.name"
+              :value="mapCode.name"
+            >
+              <span style="float: left">{{ mapCode.name }}</span>
+              <span style="float: right; color: #8492a6; font-size: 13px">{{ mapCode.exist ? '已存在' : '' }}</span>
+            </el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item
+          label="地图级别"
+          prop="level"
+        >
+          <el-select
+            v-model="mapForm.level"
+            :disabled="mapForm.parentCode !== '0'"
+            class="bs-el-select"
+            placeholder="请选择地图级别"
+            popper-class="bs-el-select"
+          >
+            <el-option
+              v-for="level in levelList"
+              :key="level.value"
+              :label="level.label"
+              :value="level.value"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item
+          label="开启下钻"
+          prop="enableDown"
+        >
+          <el-switch
+            v-model="mapForm.enableDown"
+            :active-value="1"
+            :inactive-value="0"
+            class="bs-el-switch"
+          />
+        </el-form-item>
+        <el-form-item
+          label="geoJson上传"
+        >
+          <vue-json-viewer
+            v-model="mapForm.geoJson"
+            theme="dark"
+            :show-btns="false"
+            mode="code"
+          />
+          <el-button
+            class="bs-el-button-default"
+            @click="upload"
+          >
+            <i class="el-icon-upload2" />
+            上传
+          </el-button>
+        </el-form-item>
+        <el-form-item
+          v-if="autoParseNextLevelShow"
+          label="自动解析下一级"
+          prop="autoParseNextLevel"
+        >
+          <el-switch
+            v-model="mapForm.autoParseNextLevel"
+            :active-value="1"
+            :inactive-value="0"
+            class="bs-el-switch"
+          />
+        </el-form-item>
+
+      </el-form>
+      <span
+        slot="footer"
+        class="dialog-footer"
+      >
+      <el-button
+        class="bs-el-button-default"
+        @click="handleClose"
+      >
+        取消
+      </el-button>
+      <el-button
+        type="primary"
+        @click="submitForm"
+      >
+        确定
+      </el-button>
+    </span>
+    </el-dialog>
+    <input
+      ref="geoJsonFile"
+      accept=".json"
+      name="file"
+      style="display: none"
+      type="file"
+      @change="handleBatchUpload"
+    >
+  </div>
+</template>
+
+<script>
+import vueJsonViewer from 'vue-json-viewer'
+import {getMapChildFromGeoJson, mapAdd, repeatCheck} from 'data-room-ui/js/utils/mapDataService'
+
+export default {
+  name: "AddForm",
+  components: {
+    vueJsonViewer
+  },
+  computed: {
+    autoParseNextLevelShow() {
+      // geoJson 不为空,且支持下钻
+      return !this.isEmpty(this.mapForm.geoJson) && this.mapForm.enableDown === 1
+    }
+  },
+  data() {
+    const validateCode = (rule, value, callback) => {
+      console.log(value)
+      if (this.mapForm.parentCode !== '0') {
+        // 不需要校验
+        callback()
+      }
+      repeatCheck({
+        parentCode: this.mapForm.parentCode,
+        mapCode: value
+      }).then(res => {
+        if (res) {
+          callback(new Error('地图编码已存在'))
+        } else {
+          callback()
+        }
+      })
+
+    }
+    return {
+      mapFormVisible: false,
+      geoJsonVisible: false,
+      uploadLoading: false,
+      title: '新增地图数据',
+      parentName: '顶级',
+      mapForm: {
+        parentId: '0',
+        parentCode: '0',
+        mapCode: '',
+        name: '',
+        level: 0,
+        enableDown: 0,
+        geoJson: {},
+        uploadedGeoJson: 0,
+        autoParseNextLevel: 0
+      },
+      rules: {
+        mapCode: [
+          {required: true, message: '请选择地图编码', trigger: 'blur'},
+          {validator: validateCode, trigger: 'blur'}
+        ],
+        name: [
+          {required: true, message: '请输入地图名称', trigger: 'blur'}
+        ],
+        level: [
+          {required: true, message: '请选择地图级别', trigger: 'change'}
+        ]
+      },
+      levelList: [
+        {value: 0, label: '世界'},
+        {value: 1, label: '国家'},
+        {value: 2, label: '省份'},
+        {value: 3, label: '城市'},
+        {value: 4, label: '区县'}
+      ],
+      mapCodeList: []
+    }
+  },
+  methods: {
+    init(parentMap) {
+      this.mapForm = {
+        parentId: '0',
+        parentCode: '0',
+        mapCode: `map-${new Date().getTime()}`,
+        name: '',
+        level: 0,
+        enableDown: 0,
+        geoJson: {},
+        uploadedGeoJson: 0,
+        autoParseNextLevel: 0
+      }
+      this.parentName = '顶级'
+      if (parentMap) {
+        this.mapForm.parentId = parentMap.id
+        this.mapForm.parentCode = parentMap.mapCode
+        this.parentName = parentMap.name
+        this.mapForm.level = parentMap.level + 1
+        this.mapForm.mapCode = ''
+        this.getMapCodeList()
+      }
+    },
+    handleClose() {
+      this.mapFormVisible = false
+    },
+    submitForm() {
+      this.$refs.mapForm.validate(valid => {
+        if (!valid) {
+          return false
+        }
+        let geoJson
+        // 如果geoJson是空的,包括空字符串,纯空格、空对象,空数组,都不允许提交
+        if (this.isEmpty(this.mapForm.geoJson)) {
+          geoJson = ''
+        } else {
+          geoJson = JSON.stringify(this.mapForm.geoJson)
+        }
+        mapAdd({
+          ...this.mapForm,
+          geoJson: geoJson
+        }).then(res => {
+          this.mapFormVisible = false
+          this.$emit('refresh')
+        })
+
+      })
+    },
+    isEmpty(obj) {
+      if (typeof obj === 'object') {
+        return Object.keys(obj).length === 0 && obj.constructor === Object;
+      }
+      if (typeof obj === 'string') {
+        return /^\s*$/.test(obj);
+      }
+      return Array.isArray(obj) && obj.length === 0;
+    },
+    getMapCodeList() {
+      this.mapCodeList = []
+      if (this.mapForm.parentCode === '0') {
+        this.mapCodeList = [{
+          name: `map-${new Date().getTime()}`,
+          exist: false
+        }]
+      } else {
+        getMapChildFromGeoJson(this.mapForm.parentCode).then(res => {
+          this.mapCodeList = res
+        })
+      }
+    },
+    upload() {
+      this.$refs.geoJsonFile.click()
+    },
+    handleBatchUpload(source) {
+      this.uploadLoading = true
+      const file = source.target.files
+      const reader = new FileReader() // 新建一个FileReader
+      reader.readAsText(file[0], 'UTF-8') // 读取文件
+
+      reader.onload = (event) => {
+        let jsonStr = event.target.result
+        // 读取文件内容
+        try {
+          this.mapForm.geoJson = JSON.parse(jsonStr)
+        } catch (e) {
+          this.uploadLoading = false
+          this.$message.error('JSON文件格式错误')
+          return false
+        }
+        this.uploadLoading = false
+        // input通过onchange事件来触发js代码的,由于两次文件是重复的,所以这个时候onchange事件是没有触发到的,所以需要手动清空input的值
+        source.target.value = ''
+      }
+    },
+
+  }
+}
+</script>
+
+
+<style lang="scss" scoped>
+@import '../../assets/style/bsTheme.scss';
+
+.jv-container.dark {
+  color: aliceblue;
+  background: #161A26;
+}
+</style>

+ 273 - 0
data-room-ui/packages/MapDataManagement/src/EditForm.vue

@@ -0,0 +1,273 @@
+<template>
+  <div>
+    <el-dialog
+      :append-to-body="true"
+      :before-close="handleClose"
+      :close-on-click-modal="false"
+      :title="title"
+      :visible.sync="mapFormVisible"
+      class="bs-dialog-wrap bs-el-dialog"
+      width="700px"
+    >
+      <el-form
+        ref="mapForm"
+        :model="mapForm"
+        :rules="rules"
+        class="bs-el-form"
+        label-width="120px"
+      >
+        <el-form-item
+          label="上级地图"
+          prop="parentCode"
+        >
+          <el-input
+            v-model="parentName"
+            class="bs-el-input"
+            disabled
+          />
+        </el-form-item>
+        <el-form-item
+          label="地图名称"
+          prop="name"
+        >
+          <el-input
+            v-model="mapForm.name"
+            class="bs-el-input"
+            placeholder="请输入"
+          />
+        </el-form-item>
+        <el-form-item
+          label="地图编码"
+          prop="mapCode"
+        >
+          <el-input
+            v-model="mapForm.mapCode"
+            class="bs-el-input"
+            disabled
+            placeholder="请输入地图编码"
+          />
+        </el-form-item>
+        <el-form-item
+          label="地图级别"
+          prop="level"
+        >
+          <el-select
+            v-model="mapForm.level"
+            disabled
+            class="bs-el-select"
+            placeholder="请选择地图级别"
+            popper-class="bs-el-select"
+          >
+            <el-option
+              v-for="level in levelList"
+              :key="level.value"
+              :label="level.label"
+              :value="level.value"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item
+          label="开启下钻"
+          prop="enableDown"
+        >
+          <el-switch
+            v-model="mapForm.enableDown"
+            :active-value="1"
+            :inactive-value="0"
+            class="bs-el-switch"
+          />
+        </el-form-item>
+        <el-form-item
+          label="geoJson"
+        >
+          <vue-json-viewer
+            v-model="mapForm.geoJson"
+            theme="dark"
+            :show-btns="false"
+            mode="code"
+          />
+          <el-button
+            v-if="mapForm.uploadedGeoJson !== 1"
+            class="bs-el-button-default"
+            @click="upload"
+          >
+            <i class="el-icon-upload2" />
+            上传
+          </el-button>
+        </el-form-item>
+        <el-form-item
+          v-if="autoParseNextLevelShow"
+          label="自动解析下一级"
+          prop="autoParseNextLevel"
+        >
+          <el-switch
+            v-model="mapForm.autoParseNextLevel"
+            :active-value="1"
+            :inactive-value="0"
+            class="bs-el-switch"
+          />
+        </el-form-item>
+      </el-form>
+      <span
+        slot="footer"
+        class="dialog-footer"
+      >
+      <el-button
+        class="bs-el-button-default"
+        @click="handleClose"
+      >
+        取消
+      </el-button>
+      <el-button
+        type="primary"
+        @click="submitForm"
+      >
+        确定
+      </el-button>
+    </span>
+    </el-dialog>
+    <input
+      ref="geoJsonFile"
+      accept=".json"
+      name="file"
+      style="display: none"
+      type="file"
+      @change="handleBatchUpload"
+    >
+  </div>
+</template>
+
+<script>
+import _ from 'lodash'
+import vueJsonViewer from 'vue-json-viewer'
+import {mapUpdate} from 'data-room-ui/js/utils/mapDataService'
+
+export default {
+  name: "EditForm",
+  components: {
+    vueJsonViewer
+  },
+  computed: {
+    autoParseNextLevelShow() {
+      // geoJson 不为空,且支持下钻,且未上传过(说明是刚上传的)
+      return !this.isWhitespace(this.mapForm.geoJson) && this.mapForm.enableDown === 1 && this.mapForm.uploadedGeoJson === 0
+    }
+  },
+  data() {
+    return {
+      mapFormVisible: false,
+      geoJsonVisible: false,
+      uploadLoading: false,
+      title: '编辑地图数据',
+      parentName: '顶级',
+      mapForm: {
+        parentId: '0',
+        parentCode: '0',
+        mapCode: '',
+        name: '',
+        level: 0,
+        enableDown: 0,
+        geoJson: '',
+        uploadedGeoJson: 0,
+        autoParseNextLevel: 0
+      },
+      rules: {
+        mapCode: [
+          {required: true, message: '请输入地图编码', trigger: 'blur'}
+        ],
+        name: [
+          {required: true, message: '请输入地图名称', trigger: 'blur'}
+        ],
+        level: [
+          {required: true, message: '请选择地图级别', trigger: 'change'}
+        ],
+        geoJson: [
+          {required: true, message: '请上传地图数据', trigger: 'change'}
+        ]
+      },
+      levelList: [
+        {value: 0, label: '世界'},
+        {value: 1, label: '国家'},
+        {value: 2, label: '省份'},
+        {value: 3, label: '城市'},
+        {value: 4, label: '区县'}
+      ],
+      mapCodeList: []
+    }
+  },
+  methods: {
+    init(map) {
+      this.mapForm = _.cloneDeep(map)
+      if (!this.isWhitespace(this.mapForm.geoJson)) {
+        this.mapForm.geoJson = JSON.parse(this.mapForm.geoJson)
+      }
+    },
+    handleClose() {
+      this.mapFormVisible = false
+    },
+    submitForm() {
+      this.$refs.mapForm.validate(valid => {
+        if (!valid) {
+          return false
+        }
+        let geoJson
+        // 如果geoJson是空的,包括空字符串,纯空格、空对象,空数组,都不允许提交
+        if (this.isWhitespace(this.mapForm.geoJson) || this.mapForm.geoJson === '{}' || this.mapForm.geoJson === '[]') {
+          geoJson = ''
+        } else {
+          geoJson = JSON.stringify(this.mapForm.geoJson)
+        }
+        mapUpdate({
+          ...this.mapForm,
+          geoJson: geoJson
+        }).then(res => {
+          this.mapFormVisible = false
+          this.$emit('refresh')
+        })
+
+      })
+    },
+    isWhitespace(str) {
+      // 如果是null、undefined,返回true
+      if (str == null) {
+        return true
+      }
+      return /^\s*$/.test(str);
+    },
+    upload() {
+      this.$refs.geoJsonFile.click()
+    },
+    handleBatchUpload(source) {
+      this.uploadLoading = true
+      const file = source.target.files
+      const reader = new FileReader() // 新建一个FileReader
+      reader.readAsText(file[0], 'UTF-8') // 读取文件
+
+      reader.onload = (event) => {
+        let jsonStr = event.target.result
+        // 读取文件内容
+        try {
+          this.mapForm.geoJson = JSON.parse(jsonStr)
+        } catch (e) {
+          this.uploadLoading = false
+          this.$message.error('JSON文件格式错误')
+          return false
+        }
+        this.uploadLoading = false
+        // input通过onchange事件来触发js代码的,由于两次文件是重复的,所以这个时候onchange事件是没有触发到的,所以需要手动清空input的值
+        source.target.value = ''
+      }
+    },
+  }
+}
+</script>
+
+
+<style lang="scss" scoped>
+@import '../../assets/style/bsTheme.scss';
+
+.jv-container.dark {
+  color: aliceblue;
+  background: #161A26;
+}
+</style>

+ 426 - 0
data-room-ui/packages/MapDataManagement/src/index.vue

@@ -0,0 +1,426 @@
+<template>
+  <div class="bs-container">
+    <div class="inner-container">
+      <el-form
+        :inline="true"
+        class="filter-container"
+      >
+        <el-form-item class="filter-input filter-item">
+          <el-input
+            v-model="searchForm.searchKey"
+            class="bs-el-input"
+            clearable
+            maxlength="200"
+            placeholder="请输入地图名称或编码"
+          />
+        </el-form-item>
+        <el-form-item class="filter-item">
+          <el-select
+            v-model="searchForm.level"
+            class="bs-el-select"
+            clearable
+            placeholder="请选择地图级别"
+            popper-class="bs-el-select"
+            @change="getDataList"
+          >
+            <el-option
+              v-for="level in levelList"
+              :key="level.value"
+              :label="level.label"
+              :value="level.value"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item class="filter-item">
+          <el-button
+            :loading="searchLoading"
+            icon="el-icon-search"
+            type="primary"
+            @click="getDataList"
+          >
+            查询
+          </el-button>
+        </el-form-item>
+        <el-form-item class="filter-item">
+          <el-button
+            class="bs-el-button-default"
+            @click="addMap"
+          >
+            新增
+          </el-button>
+        </el-form-item>
+      </el-form>
+      <div class="bs-table-box">
+        <el-table
+          v-loading="searchLoading"
+          ref="table"
+          v-table
+          :data="mapList"
+          :element-loading-text="loadingText"
+          :load="load"
+          :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
+          class="bs-el-table bs-scrollbar"
+          height="0"
+          lazy
+          row-key="id"
+        >
+          <el-empty slot="empty"/>
+          <el-table-column
+            align="left"
+            label="名称"
+            prop="name"
+            show-overflow-tooltip
+          />
+          <el-table-column
+            align="center"
+            label="编码"
+            prop="mapCode"
+            show-overflow-tooltip
+          />
+          <el-table-column
+            align="center"
+            label="级别"
+            prop="level"
+            show-overflow-tooltip
+          >
+            <template slot-scope="scope">
+              <span v-if="scope.row.level === 0">世界</span>
+              <span v-else-if="scope.row.level === 1">国家</span>
+              <span v-else-if="scope.row.level === 2">省份</span>
+              <span v-else-if="scope.row.level === 3">城市</span>
+              <span v-else-if="scope.row.level === 4">区县</span>
+            </template>
+          </el-table-column>
+          <el-table-column
+            align="center"
+            label="开启下钻"
+            prop="enableDown"
+            show-overflow-tooltip
+          >
+            <template slot-scope="scope">
+              <span v-if="scope.row.enableDown === 1">是</span>
+              <span v-else>否</span>
+            </template>
+          </el-table-column>
+          <el-table-column
+            align="center"
+            label="已上传配置JSON"
+            prop="uploadedGeoJson"
+            show-overflow-tooltip
+          >
+            <template slot-scope="scope">
+              <span v-if="scope.row.uploadedGeoJson === 1">是</span>
+              <span v-else>否</span>
+            </template>
+          </el-table-column>
+          <el-table-column
+            align="center"
+            label="操作"
+            width="200"
+          >
+            <template slot-scope="scope">
+              <el-button
+                class="bs-el-button-default"
+                @click="editMap(scope.row)"
+              >
+                编辑
+              </el-button>
+              <el-button
+                class="bs-el-button-default"
+                @click="deleteMap(scope.row)"
+              >
+                删除
+              </el-button>
+              <el-button
+                v-if="scope.row.uploadedGeoJson === 1"
+                class="bs-el-button-default"
+                @click="addChild(scope.row)"
+              >
+                添加下级
+              </el-button>
+              <el-button
+                v-if="scope.row.uploadedGeoJson === 0"
+                class="bs-el-button-default"
+                @click="uploadGeoJson(scope.row)"
+              >
+                上传配置
+              </el-button>
+            </template>
+          </el-table-column>
+
+        </el-table>
+      </div>
+    </div>
+    <add-form
+      ref="addForm"
+      @refresh="getDataList"
+    />
+    <edit-form
+      ref="editForm"
+      @refresh="getDataList"
+    />
+    <el-dialog
+      :close-on-click-modal="false"
+      :visible.sync="geoJsonVisible"
+      append-to-body
+      class="bs-dialog-wrap bs-el-dialog"
+      height="1000px"
+      title="geoJson数据"
+      width="1000px"
+    >
+      <vue-json-viewer
+        v-model="currentMapGeoJSon"
+        theme="dark"
+        :show-btns="false"
+        mode="code"
+      />
+      <el-button
+        class="bs-el-button-default"
+        @click="upload()"
+      >
+        <i class="el-icon-upload2"></i>
+        上传
+      </el-button>
+      <span
+        slot="footer"
+        class="dialog-footer"
+      >
+        <el-button
+          class="bs-el-button-default"
+          @click="submitUpload"
+        >提交</el-button>
+        </span>
+    </el-dialog>
+    <input
+      ref="geoJsonFileUpload"
+      accept=".json"
+      name="file"
+      style="display: none"
+      type="file"
+      @change="handleBatchUpload"
+    >
+  </div>
+</template>
+
+<script>
+import table from 'data-room-ui/js/utils/table.js'
+import {mapList, mapDelete, uploadGeoJson, mapCascadeDelete} from 'data-room-ui/js/utils/mapDataService'
+import AddForm from "./AddForm"
+import EditForm from "./EditForm"
+import vueJsonViewer from 'vue-json-viewer'
+
+export default {
+  name: "MapManagement",
+  directives: {
+    table // 注册自定义指令
+  },
+  components: {
+    AddForm,
+    EditForm,
+    vueJsonViewer
+  },
+  data() {
+    return {
+      currentMap: {}, // 当前操作的地图
+      currentMapGeoJSon: {}, // 当前操作的地图geoJson
+      loadingText: '',
+      searchLoading: false,
+      geoJsonVisible: false,
+      lazyResolveIds: [],
+      searchForm: {
+        searchKey: '',
+        level: null,
+        enableDown: null,
+        uploadedGeoJson: null,
+        parentCode: '0'
+      },
+      levelList: [
+        {
+          label: '世界',
+          value: 0
+        },
+        {
+          label: '国家',
+          value: 1
+        },
+        {
+          label: '省份',
+          value: 2
+        },
+        {
+          label: '城市',
+          value: 3
+        },
+        {
+          label: '区县',
+          value: 4
+        }
+      ],
+      mapList: []
+    }
+  },
+  mounted() {
+    this.init()
+
+  },
+  methods: {
+    init() {
+      this.searchLoading = true
+      this.loadingText = '正在加载地图数据...'
+      mapList(this.searchForm).then(res => {
+        this.mapList = res
+        this.searchLoading = false
+      }).catch(err => {
+        this.searchLoading = false
+      })
+    },
+    getDataList() {
+      this.searchLoading = true
+      this.loadingText = '正在加载地图数据...'
+      mapList(this.searchForm).then(res => {
+        this.mapList = res
+        this.searchLoading = false
+      }).catch(err => {
+        this.searchLoading = false
+      })
+      // 清除展开状态
+      for (let i = 0; i < this.lazyResolveIds.length; i++) {
+        this.$refs.table.store.states.treeData[this.lazyResolveIds[i]].loaded = false;
+        this.$refs.table.store.states.treeData[this.lazyResolveIds[i]].expanded = false
+      }
+    },
+    addMap() {
+      this.$refs.addForm.mapFormVisible = true
+      this.$refs.addForm.init()
+    },
+    load(data, treeNode, resolve) {
+      this.lazyResolveIds.push(data.id)
+      mapList({
+        parentCode: data.mapCode
+      }).then(res => {
+        resolve(res)
+      }).catch(err => {
+        resolve([])
+      })
+    },
+    deleteMap(map) {
+      this.$confirm('确定删除该地图?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+        customClass: 'bs-el-message-box'
+      }).then(async () => {
+        mapDelete(map.id).then(() => {
+          this.$message({
+            type: 'success',
+            message: '删除成功'
+          })
+          this.getDataList()
+        }).catch(() => {
+          this.deleteMapCascade(map)
+        })
+      }).catch(() => {
+      })
+    },
+    deleteMapCascade(map) {
+      this.$confirm('该地图存在子级,是否直接删除该地图以及其所有子级?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+        customClass: 'bs-el-message-box'
+      }).then(async () => {
+        mapCascadeDelete(map.id).then(() => {
+          this.$message({
+            type: 'success',
+            message: '删除成功'
+          })
+          this.getDataList()
+        }).catch(() => {
+        })
+      }).catch(() => {
+      })
+    },
+    addChild(map) {
+      this.$refs.addForm.mapFormVisible = true
+      this.$refs.addForm.init(map)
+    },
+    editMap(map) {
+      this.$refs.editForm.mapFormVisible = true
+      this.$refs.editForm.init(map)
+    },
+    uploadGeoJson(map) {
+      this.currentMap = map
+      this.currentMapGeoJSon = {}
+      this.geoJsonVisible = true
+    },
+    upload() {
+      this.$refs.geoJsonFileUpload.click()
+    },
+    handleBatchUpload(source) {
+      this.uploadLoading = true
+      const file = source.target.files
+      const reader = new FileReader() // 新建一个FileReader
+      reader.readAsText(file[0], 'UTF-8') // 读取文件
+
+      reader.onload = (event) => {
+        let jsonStr = event.target.result
+        // 读取文件内容
+        try {
+          this.currentMapGeoJSon = JSON.parse(jsonStr)
+        } catch (e) {
+          this.uploadLoading = false
+          this.$message.error('JSON文件格式错误')
+          return false
+        }
+        this.uploadLoading = false
+        // input通过onchange事件来触发js代码的,由于两次文件是重复的,所以这个时候onchange事件是没有触发到的,所以需要手动清空input的值
+        source.target.value = ''
+      }
+    },
+    submitUpload() {
+      // 先检查JSON是否合法
+      if (typeof this.currentMapGeoJSon === 'string') {
+        this.$message.error('JSON文件格式错误')
+        return false
+      }
+      if (this.currentMapGeoJSon === {}) {
+        this.$message.error('JSON数据不能为空')
+        return false
+      }
+      // 调接口保存
+      uploadGeoJson({
+        id: this.currentMap.id,
+        geoJson: JSON.stringify(this.currentMapGeoJSon)
+      }).then(res => {
+        this.$message({
+          type: 'success',
+          message: '上传成功'
+        })
+        this.geoJsonVisible = false
+        this.getDataList()
+      }).catch(err => {
+        this.$message({
+          type: 'error',
+          message: '上传失败'
+        })
+      })
+      this.geoJsonVisible = false
+    },
+    isWhitespace(str) {
+      // 如果是null、undefined,返回true
+      if (str == null) {
+        return true
+      }
+      return /^\s*$/.test(str);
+    },
+  },
+}
+</script>
+
+<style lang="scss" scoped>
+@import '../../assets/style/bsTheme.scss';
+.jv-container.dark {
+  color: aliceblue;
+  background: #161A26;
+}
+</style>

+ 1 - 0
data-room-ui/packages/assets/style/bsTheme.scss

@@ -610,6 +610,7 @@
   .el-switch__core {
     background: var(--bs-el-background-1);
     background-color: var(--bs-el-background-1);
+    border-color: var(--bs-el-border);
   }
 }
 

+ 71 - 0
data-room-ui/packages/js/utils/mapDataService.js

@@ -0,0 +1,71 @@
+/*!
+ * 地图数据管理
+ */
+import Vue from 'vue'
+
+/**
+ * 获取地图列表
+ * @param params
+ * @param flag
+ * @returns {*}
+ */
+const mapList = (params = {}, flag = false) => Vue.prototype.$dataRoomAxios.get('/bigScreen/map/list', params, flag)
+
+/**
+ * 新增地图
+ * @param params
+ * @param flag
+ * @returns {*}
+ */
+const mapAdd = (params = {}, flag = false) => Vue.prototype.$dataRoomAxios.post('/bigScreen/map/add', params, flag)
+
+/**
+ * 更新地图
+ * @param params
+ * @param flag
+ * @returns {*}
+ */
+const mapUpdate = (params = {}, flag = false) => Vue.prototype.$dataRoomAxios.post('/bigScreen/map/update', params, flag)
+
+/**
+ * 删除地图
+ * @param id
+ */
+const mapDelete = (id = '-1') => Vue.prototype.$dataRoomAxios.post(`/bigScreen/map/delete/${id}`)
+
+/**
+ * 级联删除地图
+ * @param id
+ */
+const mapCascadeDelete = (id = '-1') => Vue.prototype.$dataRoomAxios.post(`/bigScreen/map/cascadeDelete/${id}`)
+
+/**
+ * 根据父编码解析父级json中的子级
+ * @param code
+ */
+const getMapChildFromGeoJson = (code = '-1') => Vue.prototype.$dataRoomAxios.get(`/bigScreen/map/getMapChildFromGeoJson/${code}`)
+
+/**
+ * 上传地图json
+ * @param params
+ * @param flag
+ */
+const uploadGeoJson = (params = {}, flag = false) => Vue.prototype.$dataRoomAxios.post('/bigScreen/map/upload', params, flag)
+
+/**
+ * 编码重复校验
+ * @param params
+ * @param flag
+ */
+const repeatCheck = (params = {}, flag = false) => Vue.prototype.$dataRoomAxios.post('/bigScreen/map/repeat', params, flag)
+
+export {
+  mapList,
+  mapAdd,
+  mapUpdate,
+  mapDelete,
+  mapCascadeDelete,
+  getMapChildFromGeoJson,
+  uploadGeoJson,
+  repeatCheck
+}

+ 7 - 0
data-room-ui/packages/js/utils/registerConfig.js

@@ -72,6 +72,13 @@ function registerRouters (config, router) {
             title: '数据集管理'
           }
         },
+        {
+          path: '/big-screen-map-data',
+          component: () => import('data-room-ui/MapDataManagement'),
+          meta: {
+            title: '地图数据管理'
+          }
+        },
         {
           path: config?.routers?.SourceUrl || '/big-screen-source',
           component: () => import('data-room-ui/SourceManagement'),