Răsfoiți Sursa

feat:合并代码

liu.tao3 1 an în urmă
părinte
comite
c9e22a1d95
46 a modificat fișierele cu 1792 adăugiri și 511 ștergeri
  1. 5 0
      DataRoom/dataroom-core/src/main/java/com/gccloud/dataroom/core/constant/PageDesignConstant.java
  2. 15 0
      DataRoom/dataroom-core/src/main/java/com/gccloud/dataroom/core/module/chart/components/ScreenMapChart.java
  3. 29 0
      DataRoom/dataroom-core/src/main/java/com/gccloud/dataroom/core/module/chart/components/ScreenTimePickerChart.java
  4. 14 1
      DataRoom/dataroom-core/src/main/java/com/gccloud/dataroom/core/module/chart/controller/ChartDataController.java
  5. 14 1
      DataRoom/dataroom-core/src/main/java/com/gccloud/dataroom/core/module/manage/service/impl/DataRoomPagePreviewServiceImpl.java
  6. 49 1
      DataRoom/dataroom-core/src/main/java/com/gccloud/dataroom/core/module/map/service/impl/DataRoomMapServiceImpl.java
  7. 8 1
      DataRoom/dataroom-server/src/main/resources/init-h2.sql
  8. 161 105
      data-room-ui/packages/BasicComponents/FlyMap/index.vue
  9. 105 45
      data-room-ui/packages/BasicComponents/FlyMap/setting.vue
  10. 5 1
      data-room-ui/packages/BasicComponents/FlyMap/settingConfig.js
  11. 187 128
      data-room-ui/packages/BasicComponents/Map/index.vue
  12. 113 39
      data-room-ui/packages/BasicComponents/Map/setting.vue
  13. 8 1
      data-room-ui/packages/BasicComponents/Map/settingConfig.js
  14. 3 0
      data-room-ui/packages/BasicComponents/ScreenScrollBoard/index.vue
  15. 2 1
      data-room-ui/packages/BasicComponents/ScreenScrollBoard/settingConfig.js
  16. 3 0
      data-room-ui/packages/BasicComponents/ScreenScrollRanking/index.vue
  17. 2 1
      data-room-ui/packages/BasicComponents/ScreenScrollRanking/settingConfig.js
  18. 89 36
      data-room-ui/packages/BasicComponents/Select/index.vue
  19. 3 40
      data-room-ui/packages/BasicComponents/Select/setting.vue
  20. 2 7
      data-room-ui/packages/BasicComponents/Select/settingConfig.js
  21. 3 0
      data-room-ui/packages/BasicComponents/Tables/index.vue
  22. 3 1
      data-room-ui/packages/BasicComponents/Tables/settingConfig.js
  23. 10 0
      data-room-ui/packages/BigScreenDesign/LeftPanel.vue
  24. 1 0
      data-room-ui/packages/BigScreenDesign/OverallSetting/index.vue
  25. 1 1
      data-room-ui/packages/BigScreenDesign/RightSetting/DataSetting.vue
  26. 2 2
      data-room-ui/packages/BigScreenDesign/RightSetting/TextGradient/index.vue
  27. 15 10
      data-room-ui/packages/BigScreenDesign/RightSetting/index.vue
  28. 4 4
      data-room-ui/packages/BorderSelect/index.vue
  29. 15 15
      data-room-ui/packages/ComponentList/EditForm.vue
  30. 277 0
      data-room-ui/packages/Echarts/3D图/3D固定柱状图.js
  31. 55 0
      data-room-ui/packages/Echarts/echartList.js
  32. 4 0
      data-room-ui/packages/Echarts/echartListSort.js
  33. BIN
      data-room-ui/packages/Echarts/images/3D固定柱状图.png
  34. 349 0
      data-room-ui/packages/EchartsRender/index.vue
  35. 23 0
      data-room-ui/packages/EchartsRender/settingConfig.js
  36. 2 1
      data-room-ui/packages/G2Plots/plotList.js
  37. 6 6
      data-room-ui/packages/Layout/BigScreenHomeLayout/index.vue
  38. 23 20
      data-room-ui/packages/MapDataManagement/src/AddForm.vue
  39. 59 19
      data-room-ui/packages/MapDataManagement/src/EditForm.vue
  40. 37 16
      data-room-ui/packages/MapDataManagement/src/index.vue
  41. 13 1
      data-room-ui/packages/PlotRender/index.vue
  42. 10 6
      data-room-ui/packages/Render/RenderCard.vue
  43. 44 0
      data-room-ui/packages/assets/style/bsTheme.scss
  44. 9 0
      data-room-ui/packages/js/mixins/commonMixins.js
  45. 9 0
      data-room-ui/packages/js/store/mutations.js
  46. 1 1
      data-room-ui/packages/js/utils/mapDataService.js

+ 5 - 0
DataRoom/dataroom-core/src/main/java/com/gccloud/dataroom/core/constant/PageDesignConstant.java

@@ -162,6 +162,11 @@ public interface PageDesignConstant {
              */
             String SELECT = "select";
 
+            /**
+             * 时间选择
+             */
+            String TIME_PICKER = "timePicker";
+
 
         }
     }

+ 15 - 0
DataRoom/dataroom-core/src/main/java/com/gccloud/dataroom/core/module/chart/components/ScreenMapChart.java

@@ -23,6 +23,9 @@ public class ScreenMapChart extends Chart {
     @Data
     public static class Customize {
 
+        @ApiModelProperty(notes = "地图id")
+        private String mapId;
+
         @ApiModelProperty(notes = "是否显示文字")
         private Boolean mapName;
 
@@ -89,6 +92,18 @@ public class ScreenMapChart extends Chart {
         @ApiModelProperty(notes = "是否开启下钻")
         private Boolean down;
 
+        @ApiModelProperty(notes = "允许下钻的层级")
+        private Integer downLevel;
+
+        @ApiModelProperty(notes = "地图比例")
+        private Integer zoom;
+
+        @ApiModelProperty(notes = "中心点x轴位置")
+        private Integer center1;
+
+        @ApiModelProperty(notes = "中心点y轴位置")
+        private Integer center2;
+
     }
 
 

+ 29 - 0
DataRoom/dataroom-core/src/main/java/com/gccloud/dataroom/core/module/chart/components/ScreenTimePickerChart.java

@@ -0,0 +1,29 @@
+package com.gccloud.dataroom.core.module.chart.components;
+
+import com.gccloud.dataroom.core.constant.PageDesignConstant;
+import com.gccloud.dataroom.core.module.chart.bean.Chart;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 时间选择
+ * @author hongyang
+ * @version 1.0
+ * @date 2023/09/14 16:44
+ */
+@Data
+public class ScreenTimePickerChart extends Chart {
+    
+    @ApiModelProperty(notes = "组件类型")
+    private String type = PageDesignConstant.BigScreen.Type.TIME_PICKER;
+
+    @ApiModelProperty(notes = "个性化")
+    private Customize customize = new Customize();
+
+    @Data
+    public static class Customize {
+
+    }
+
+
+}

+ 14 - 1
DataRoom/dataroom-core/src/main/java/com/gccloud/dataroom/core/module/chart/controller/ChartDataController.java

@@ -89,7 +89,20 @@ public class ChartDataController {
                 if (chart instanceof ScreenFlyMapChart) {
                     ScreenFlyMapChart screenFlyMapChart = (ScreenFlyMapChart) chart;
                     ScreenFlyMapChart.Customize customize = screenFlyMapChart.getCustomize();
-                    type += "-" + customize.getLevel();
+                    // 兼容旧版地图等级
+                    switch (customize.getLevel()) {
+                        case "0":
+                            type += "-world";
+                            break;
+                        case "1":
+                            type += "-country";
+                            break;
+                        case "2":
+                            type += "-province";
+                            break;
+                        default:
+                            type += "-" + customize.getLevel();
+                    }
                 }
                 chartDataVO = ChartMockData.getMockData(type);
             }

+ 14 - 1
DataRoom/dataroom-core/src/main/java/com/gccloud/dataroom/core/module/manage/service/impl/DataRoomPagePreviewServiceImpl.java

@@ -24,7 +24,20 @@ public class DataRoomPagePreviewServiceImpl extends ServiceImpl<DataRoomPagePrev
 
     @Override
     public String add(DataRoomPageDTO bigScreenPageDTO) {
-        String code = CodeGenerateUtils.generate(PREVIEW_KEY);
+        String originalCode = bigScreenPageDTO.getCode();
+        String code = PREVIEW_KEY + "_" + originalCode;
+        LambdaQueryWrapper<PagePreviewEntity> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(PagePreviewEntity::getCode, code);
+        List<PagePreviewEntity> list = this.list(queryWrapper);
+        if (list != null && !list.isEmpty()) {
+            // 有则直接更新
+            PagePreviewEntity pagePreviewEntity = list.get(0);
+            pagePreviewEntity.setConfig(bigScreenPageDTO);
+            pagePreviewEntity.setCreateDate(new Date());
+            this.updateById(pagePreviewEntity);
+            return code;
+        }
+        // 没有则新增
         bigScreenPageDTO.setCode(code);
         PagePreviewEntity pagePreviewEntity = BeanConvertUtils.convert(bigScreenPageDTO, PagePreviewEntity.class);
         pagePreviewEntity.setCreateDate(new Date());

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

@@ -160,8 +160,10 @@ public class DataRoomMapServiceImpl extends ServiceImpl<DataRoomMapDao, DataRoom
         Integer uploadedGeoJson = old.getUploadedGeoJson();
         LambdaUpdateWrapper<DataRoomMapEntity> updateWrapper = new LambdaUpdateWrapper<>();
         updateWrapper.eq(DataRoomMapEntity::getId, mapDTO.getId());
-        // 只允许修改名称和是否开启下钻
+        // 修改名称
         updateWrapper.set(DataRoomMapEntity::getName, mapDTO.getName());
+        // 修改地图编码
+        updateWrapper.set(!old.getMapCode().equals(mapDTO.getMapCode()), DataRoomMapEntity::getMapCode, mapDTO.getMapCode());
         // 如果之前没有上传过geoJson,现在上传了,那么允许更新geoJson
         if (!uploadedGeoJson.equals(YES) && StringUtils.isNotBlank(mapDTO.getGeoJson())) {
             if (mapDTO.getAutoParseNextLevel().equals(YES)) {
@@ -172,6 +174,10 @@ public class DataRoomMapServiceImpl extends ServiceImpl<DataRoomMapDao, DataRoom
             updateWrapper.set(DataRoomMapEntity::getUploadedGeoJson, YES);
         }
         this.update(updateWrapper);
+        // 更新父级的geoJson
+        if (!old.getMapCode().equals(mapDTO.getMapCode())) {
+            this.updateParentJson(old.getParentId(), old.getMapCode(), mapDTO.getMapCode());
+        }
     }
 
     /**
@@ -205,6 +211,45 @@ public class DataRoomMapServiceImpl extends ServiceImpl<DataRoomMapDao, DataRoom
         }
     }
 
+    /**
+     * 更新父级地图的geoJson
+     * @param parentId
+     * @param oldCode
+     * @param newMapCode
+     */
+    private void updateParentJson(String parentId, String oldCode, String newMapCode) {
+        if (StringUtils.isBlank(parentId) || parentId.equals(SUPER_PARENT_ID)) {
+            return;
+        }
+        DataRoomMapEntity parent = this.getById(parentId);
+        String geoJson = parent.getGeoJson();
+        if (StringUtils.isBlank(geoJson)) {
+            return;
+        }
+        JSONObject jsonObject = new JSONObject(geoJson);
+        JSONArray features = jsonObject.getJSONArray("features");
+        if (features == null || features.length() == 0) {
+            return;
+        }
+        for (int i = 0; i < features.length(); i++) {
+            JSONObject feature = features.getJSONObject(i);
+            JSONObject properties = feature.getJSONObject("properties");
+            if (properties == null) {
+                continue;
+            }
+            String name = properties.getString("name");
+            if (oldCode.equals(name)) {
+                properties.put("name", newMapCode);
+                break;
+            }
+        }
+        LambdaUpdateWrapper<DataRoomMapEntity> updateWrapper = new LambdaUpdateWrapper<>();
+        updateWrapper.eq(DataRoomMapEntity::getId, parentId);
+        updateWrapper.set(DataRoomMapEntity::getGeoJson, jsonObject.toString());
+        this.update(updateWrapper);
+    }
+
+
     @Override
     public void delete(String id) {
         if (StringUtils.isBlank(id)) {
@@ -324,6 +369,9 @@ public class DataRoomMapServiceImpl extends ServiceImpl<DataRoomMapDao, DataRoom
         for (int i = 0; i < features.length(); i++) {
             JSONObject feature = features.getJSONObject(i);
             JSONObject properties = feature.getJSONObject("properties");
+            if (!properties.has("name")) {
+                continue;
+            }
             String name = properties.getString("name");
             MapChildVO childVO = new MapChildVO();
             childVO.setName(name);

+ 8 - 1
DataRoom/dataroom-server/src/main/resources/init-h2.sql

@@ -179,4 +179,11 @@ CREATE TABLE IF NOT EXISTS big_screen_map (
     create_by BIGINT DEFAULT 2 COMMENT '创建人',
     update_by BIGINT DEFAULT 2 COMMENT '更新人',
     del_flag TINYINT NOT NULL DEFAULT '0' COMMENT '删除标识'
-)
+);
+
+CREATE TABLE IF NOT EXISTS big_screen_page_preview (
+    id bigint(32) NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT '主键',
+    code varchar(255) NOT NULL DEFAULT '' COMMENT '页面编码,页面唯一标识符',
+    config clob COMMENT '页面配置',
+    create_date timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间'
+);

+ 161 - 105
data-room-ui/packages/BasicComponents/FlyMap/index.vue

@@ -1,9 +1,9 @@
 <template>
   <div
-    style="width: 100%; height: 100%"
     class="bs-design-wrap bs-bar"
+    style="width: 100%; height: 100%"
   >
-  <el-button class="button" v-if="this.level=='province'&&config.customize.down" @click="jumpTo(config)" type='text' > 返回上一级</el-button>
+    <el-button v-if="currentDeep > 0" class="button" type='text' @click="jumpTo(config)"> 返回上一级</el-button>
     <div
       :id="`chart${config.code}`"
       style="width: 100%; height: 100%"
@@ -17,6 +17,7 @@ import {nameMap} from './json/mapData.js'
 import commonMixins from 'data-room-ui/js/mixins/commonMixins.js'
 import paramsMixins from 'data-room-ui/js/mixins/paramsMixins'
 import linkageMixins from 'data-room-ui/js/mixins/linkageMixins'
+
 export default {
   name: 'MapCharts',
   mixins: [paramsMixins, commonMixins, linkageMixins],
@@ -30,22 +31,24 @@ export default {
       default: () => ({})
     }
   },
-  data () {
+  data() {
     return {
       charts: null,
       hasData: false,
-      level:'',
-      option:{}
+      level: '',
+      option: {},
+      mapList: [],
+      currentDeep: 0,
     }
   },
   computed: {
-    Data () {
+    Data() {
       return JSON.parse(JSON.stringify(this.config))
     }
   },
   watch: {
     Data: {
-      handler (newVal, oldVal) {
+      handler(newVal, oldVal) {
         if (newVal.w !== oldVal.w || newVal.h !== oldVal.h) {
           this.$nextTick(() => {
             this.charts.resize()
@@ -55,14 +58,14 @@ export default {
       deep: true
     }
   },
-  mounted () {
+  mounted() {
     this.chartInit()
   },
-  beforeDestroy () {
+  beforeDestroy() {
     this.charts?.clear()
   },
   methods: {
-    chartInit () {
+    chartInit() {
       const config = this.config
       // key和code相等,说明是一进来刷新,调用list接口
       if (this.config.code === this.config.key || this.isPreview) {
@@ -71,7 +74,8 @@ export default {
           // 改变样式
           // config = this.changeStyle(res)
           this.newChart(config)
-        }).catch(() => {})
+        }).catch(() => {
+        })
       } else {
         // 否则说明是更新,这里的更新只指更新数据(改变样式时是直接调取changeStyle方法),因为更新数据会改变key,调用chart接口
         this.changeData(config).then((res) => {
@@ -80,51 +84,74 @@ export default {
         })
       }
     },
-    dataFormatting (config, data) {
+    dataFormatting(config, data) {
       config.option = {
         ...config.option,
         data: data?.data
       }
       return config
     },
-    async jumpTo(config){
-      this.level='country'
-      const mapUrl =`${window.BS_CONFIG?.httpConfigs?.baseURL}/static/chinaMap/country/中华人民共和国.json`
-      const map = await this.$dataRoomAxios.get(decodeURI(mapUrl), {}, true)
-      this.option.geo.map = '中华人民共和国';
-      this.changeData({...config,customize:{...config.customize,level:'country',scope:'中国'}})
-      echarts.registerMap('中华人民共和国', map);
+    async jumpTo(config) {
+      this.currentDeep--
+      let map = this.mapList[this.currentDeep]
+      // 移除mapList中的最后一个元素
+      this.mapList.pop()
+      let mapData = JSON.parse(map.geoJson)
+      this.option.geo.map = map.name;
+      this.changeData({...config, customize: {...config.customize, level: map.level, scope: map.name}})
+      echarts.registerMap(map.name, mapData);
       this.charts.setOption(this.option, true);
     },
-    async newChart (config) {
+    async newChart(config) {
       this.charts = echarts.init(
         document.getElementById(`chart${this.config.code}`)
       )
-      this.level=config.customize.level
+      this.level = config.customize.level
       const lines_coord = []
-      let fromCoord=[]
-      let coord=[]
-      const mapUrl =config.customize.level==='world'?`${window.BS_CONFIG?.httpConfigs?.baseURL}/static/worldMap/world.json`:`${window.BS_CONFIG?.httpConfigs?.baseURL}/static/chinaMap/${config.customize.level}/${config.customize.dataMap}`
-      this.$dataRoomAxios.get(decodeURI(mapUrl), {}, true).then(res=>{
-        this.config.option.data.forEach(val => {
-          lines_coord.push({value:val.value,msg:{...val}, coords:[[val.lat1,val.lng1],[val.lat2,val.lng2]]})
-          if(val.type==='move_in'){
-            coord.push({name:val.from,value:[val.lat1,val.lng1,val.value],msg:{...val}})
-            fromCoord.push({name:val.to,value:[val.lat2,val.lng2,val.value],msg:{...val}})
-          }else{
-            coord.push({name:val.to,value:[val.lat2,val.lng2,val.value],msg:{...val}})
-            fromCoord.push({name:val.from,value:[val.lat1,val.lng1,val.value],msg:{...val}})
-          }
-        })
-        echarts.registerMap(config.customize.scope, res)
+      let fromCoord = []
+      let coord = []
+
+      let hasMapId = !!config.customize.mapId
+
+      // 根据mapId获取地图数据
+      let mapInfoUrl = `${window.BS_CONFIG?.httpConfigs?.baseURL}/bigScreen/map/info/${config.customize.mapId}`
+      // 如果设置了地图id,就用地图id获取地图数据,否则用默认的世界地图
+      if (!hasMapId) {
+        mapInfoUrl = `${window.BS_CONFIG?.httpConfigs?.baseURL}/static/worldMap/world.json`
+      }
+      this.$dataRoomAxios.get(mapInfoUrl, {}, true).then(res => {
+        if (this.config.option.data) {
+          this.config.option.data.forEach(val => {
+            lines_coord.push({value: val.value, msg: {...val}, coords: [[val.lat1, val.lng1], [val.lat2, val.lng2]]})
+            if (val.type === 'move_in') {
+              coord.push({name: val.from, value: [val.lat1, val.lng1, val.value], msg: {...val}})
+              fromCoord.push({name: val.to, value: [val.lat2, val.lng2, val.value], msg: {...val}})
+            } else {
+              coord.push({name: val.to, value: [val.lat2, val.lng2, val.value], msg: {...val}})
+              fromCoord.push({name: val.from, value: [val.lat1, val.lng1, val.value], msg: {...val}})
+            }
+          })
+        }
+        let mapData = hasMapId ? JSON.parse(res.data.geoJson) : res
+        if (hasMapId && res.data.uploadedGeoJson !== 1) {
+          // 没有上传过geoJson
+          this.$message({
+            message: '请先上传地图数据',
+            type: 'warning'
+          })
+          return
+        }
+
+        this.mapList.push(res.data)
+        echarts.registerMap(config.customize.scope, mapData)
         this.option = {
-          nameMap:config.customize.level=='world'?nameMap:'',
+          nameMap: config.customize.level == '0' ? nameMap : '',
           // graphic: [
           // ],
           geo: {
             map: config.customize.scope,
             zlevel: 10,
-            show:true,
+            show: true,
             layoutCenter: ['50%', '50%'],
             roam: true,
             layoutSize: "100%",
@@ -166,74 +193,81 @@ export default {
             backgroundColor: config.customize.tooltipBackgroundColor,
             borderColor: config.customize.borderColor,
             show: true,
-             textStyle: {
+            textStyle: {
               color: config.customize.fontColor,
             },
           },
           series: [
             {
-                type:'effectScatter',
-                coordinateSystem: 'geo',
-                zlevel: 15,
-                symbolSize:8,
-                rippleEffect: {
-                    period: 4, brushType: 'stroke', scale: 4
-                },
-                 tooltip: {
-                    trigger: 'item',
-                    formatter(params) {
-                    const a= eval(config.customize.scatterFormatter)
-                      return a
-                    },
-                },
-                itemStyle:{
-                    color:config.customize.scatterColor,
-                    opacity:1
+              type: 'effectScatter',
+              coordinateSystem: 'geo',
+              zlevel: 15,
+              symbolSize: 8,
+              rippleEffect: {
+                period: 4, brushType: 'stroke', scale: 4
+              },
+              tooltip: {
+                trigger: 'item',
+                formatter(params) {
+                  const a = eval(config.customize.scatterFormatter)
+                  return a
                 },
-                data:coord
+              },
+              itemStyle: {
+                color: config.customize.scatterColor,
+                opacity: 1
+              },
+              data: coord
             },
             {
-                type:'effectScatter',
-                coordinateSystem: 'geo',
-                zlevel: 15,
-                symbolSize:12,
-                tooltip: {
-                  trigger: 'item',
-                  formatter(params) {
-                   const a= eval(config.customize.scatterFormatter)
-                    return a
-                  },
-                },
-                rippleEffect: {
-                    period: 6, brushType: 'stroke', scale: 8
+              type: 'effectScatter',
+              coordinateSystem: 'geo',
+              zlevel: 15,
+              symbolSize: 12,
+              tooltip: {
+                trigger: 'item',
+                formatter(params) {
+                  const a = eval(config.customize.scatterFormatter)
+                  return a
                 },
+              },
+              rippleEffect: {
+                period: 6, brushType: 'stroke', scale: 8
+              },
 
-                itemStyle:{
-                    color:config.customize.scatterCenterColor,
-                    opacity:1
-                },
-                data:fromCoord
+              itemStyle: {
+                color: config.customize.scatterCenterColor,
+                opacity: 1
+              },
+              data: fromCoord
             },
             {
-                type:'lines',
-                coordinateSystem:'geo',
-                zlevel: 15,
-                tooltip: {
-                  trigger: 'item',
-                  formatter(params) {
-                   const a= eval(config.customize.lineFormatter)
-                    return a
-                  },
-                },
-                effect: {
-                    show: true, period: 5, trailLength: 0, symbol: config.customize.symbol,  color:config.customize.symbolColor,symbolSize: config.customize.symbolSize,
+              type: 'lines',
+              coordinateSystem: 'geo',
+              zlevel: 15,
+              tooltip: {
+                trigger: 'item',
+                formatter(params) {
+                  const a = eval(config.customize.lineFormatter)
+                  return a
                 },
-                lineStyle: {
-                  normal: {color: function(value){
-                      return '#ffffff'
-                  },width: 2, opacity: 0.6, curveness: 0.2 }
-                },
-                data:lines_coord
+              },
+              effect: {
+                show: true,
+                period: 5,
+                trailLength: 0,
+                symbol: config.customize.symbol,
+                color: config.customize.symbolColor,
+                symbolSize: config.customize.symbolSize,
+              },
+              lineStyle: {
+                normal: {
+                  color: function (value) {
+                    return '#ffffff'
+                  }, width: 2, opacity: 0.6, curveness: 0.2
+                }
+              },
+              data: lines_coord
             }
 
           ]
@@ -243,24 +277,42 @@ export default {
             show: false,
             min: config.customize.range[0],
             max: config.customize.range[1],
-            seriesIndex: [0,2],
+            seriesIndex: [0, 2],
             inRange: {
               color: config.customize.rangeColor
             }
           }
         }
         this.charts.setOption(this.option)
-         this.charts.on('click',  async(params)=> {
-          if(params.name=='') return
-          if(config.customize.down===false||this.level==='province') return
-          this.level='province'
-          const mapUrl =`${window.BS_CONFIG?.httpConfigs?.baseURL}/static/chinaMap/province/${params.name}.json`
-          const map = await this.$dataRoomAxios.get(decodeURI(mapUrl), {}, true)
-          this.changeData({...config,customize:{...config.customize,level:'province',scope:params.name}})
+        // 点击下钻
+        this.charts.on('click', async (params) => {
+          if (params.name == '') return
+          if (!config.customize.down) {
+            this.$message({
+              message: '该地图未开启下钻',
+              type: 'warning'
+            })
+            return
+          }
+          // 到达允许下钻的层数,则不再下钻
+          if (this.currentDeep >= config.customize.downLevel) return
+          const mapUrl = `${window.BS_CONFIG?.httpConfigs?.baseURL}/bigScreen/map/data/${this.mapList[this.currentDeep].id}/${params.name}`
+          const map = await this.$dataRoomAxios.get(decodeURI(mapUrl), {}, false)
+          // 地图不可用
+          if (map.available !== 1) {
+            this.$message({
+              message: '未找到该地图配置',
+              type: 'warning'
+            })
+            return
+          }
+          this.currentDeep++
+          this.mapList.push(map)
+          this.changeData({...config, customize: {...config.customize, scope: params.name}})
           this.option.geo.map = params.name
-          echarts.registerMap(params.name, map);
+          echarts.registerMap(params.name, JSON.parse(map.geoJson));
           this.charts.setOption(this.option, true);
-          });
+        });
       })
     }
   }
@@ -269,17 +321,21 @@ export default {
 
 <style lang="scss" scoped>
 @import '../../assets/style/echartStyle';
+
 .light-theme {
   background-color: #ffffff;
   color: #000000;
 }
+
 .auto-theme {
   background-color: rgba(0, 0, 0, 0);
 }
-.bs-design-wrap{
+
+.bs-design-wrap {
   padding: 0 16px;
   position: relative;
-  .button{
+
+  .button {
     position: absolute;
     z-index: 999;
   }

+ 105 - 45
data-room-ui/packages/BasicComponents/FlyMap/setting.vue

@@ -55,40 +55,56 @@
             @change="changeLevel()"
           >
            <el-option
-              label="世界"
-              value="world"
-            />
-            <el-option
-              label="国家"
-              value="country"
-            />
-            <el-option
-              label="省份"
-              value="province"
+              v-for="level in levelList"
+              :key="level.value"
+              :label="level.label"
+              :value="level.value"
             />
           </el-select>
         </el-form-item>
+
+
         <el-form-item
-          v-if="config.customize.level == 'province'"
-          label="地图显示区域"
+          label="地图"
           label-width="100px"
         >
-          <el-select
-            v-model="config.customize.dataMap"
-            popper-class="bs-el-select"
-            class="bs-el-select"
-            @change="changeMap"
-          >
-            <el-option
-              v-for="map in mapList"
-              :key="map.name"
-              :label="map.name"
-              :value="map.url"
-            />
-          </el-select>
+          <el-cascader
+            ref="cascade"
+            v-model="config.customize.mapId"
+            popper-class="bs-el-cascader"
+            :options="mapTree"
+            :props="{ value: 'id', label: 'name', children: 'children', emitPath: false }"
+            @change="mapSelect">
+
+            <template slot-scope="{ node, data }">
+              <span style="float: left">{{ data.name }}</span>
+              <span v-if="data.disabled" style="float: right; color: #8492a6; font-size: 13px"> 未配置 </span>
+            </template>
+          </el-cascader>
+
         </el-form-item>
+
+
+<!--        <el-form-item-->
+<!--          v-if="config.customize.level == 'province'"-->
+<!--          label="地图显示区域"-->
+<!--          label-width="100px"-->
+<!--        >-->
+<!--          <el-select-->
+<!--            v-model="config.customize.dataMap"-->
+<!--            popper-class="bs-el-select"-->
+<!--            class="bs-el-select"-->
+<!--            @change="changeMap"-->
+<!--          >-->
+<!--            <el-option-->
+<!--              v-for="map in mapList"-->
+<!--              :key="map.name"-->
+<!--              :label="map.name"-->
+<!--              :value="map.url"-->
+<!--            />-->
+<!--          </el-select>-->
+<!--        </el-form-item>-->
         <el-form-item
-          v-if="config.customize.level == 'country'"
           label="是否开启下钻"
           label-width="100px"
         >
@@ -98,6 +114,24 @@
             active-color="#007aff"
           />
         </el-form-item>
+        <el-form-item
+          v-if="config.customize.down"
+          label="允许下钻层级"
+          label-width="100px"
+        >
+          <el-select
+            v-model="config.customize.downLevel"
+            popper-class="bs-el-select"
+            class="bs-el-select">
+            <el-option
+              v-for="level in downLevelList"
+              :key="level.value"
+              :label="level.label"
+              :value="level.value"
+            />
+          </el-select>
+        </el-form-item>
+
         <!-- <el-form-item
           v-if="config.customize.down"
           label="头部字体颜色"
@@ -323,7 +357,9 @@ export default {
   props: {},
   data () {
     return {
+      mapTree: [],
       mapList: [],
+      currentMap: {},
       predefineThemeColors: [
         '#007aff',
         '#1aa97b',
@@ -352,7 +388,27 @@ export default {
           name:'无',
           value:'none'
         }
-      ]
+      ],
+      levelList: [
+        {value: '0', label: '世界'},
+        {value: '1', label: '国家'},
+        {value: '2', label: '省份'},
+        {value: '3', label: '城市'},
+        {value: '4', label: '区县'}
+      ],
+      // 旧版本地图等级,该数据用于兼容旧版本
+      oldLevelMap: {
+        'world' : '0',
+        'country' : '1',
+        'province' : '2',
+      },
+      downLevelList: [
+        {value: 1, label: '下钻一层'},
+        {value: 2, label: '下钻两层'},
+        {value: 3, label: '下钻三层'},
+        {value: 4, label: '下钻四层'},
+        {value: 5, label: '下钻五层'}
+      ],
     }
   },
   computed: {
@@ -366,37 +422,40 @@ export default {
     }
   },
   watch: {
-    'config.customize.level': {
-      handler (val) {
-        this.getMapList()
-      }
-    }
+    // 'config.customize.level': {
+    //   handler (val) {
+    //     this.getMapList()
+    //   }
+    // }
   },
   mounted () {
-    this.getMapList()
+    // this.getMapList()
+    this.getMapTree()
   },
   methods: {
     getMapList () {
       this.$dataRoomAxios.get(`${window.BS_CONFIG?.httpConfigs?.baseURL}/bigScreen/design/map/list/${this.config.customize.level}`).then((res) => {
         this.mapList = res
       })
+    },
+    getMapTree() {
+      const levelConst = ['0', '1', '2', '3', '4']
+      if (!levelConst.includes(this.config.customize.level)) {
+        this.config.customize.level = this.oldLevelMap[this.config.customize.level] || '0'
+      }
+      this.$dataRoomAxios.get(`${window.BS_CONFIG?.httpConfigs?.baseURL}/bigScreen/map/tree/${this.config.customize.level}`).then((res) => {
+        this.mapTree = res
+      })
+    },
+    mapSelect (mapId) {
+      let mapData = this.$refs['cascade'].getCheckedNodes()[0].data
+      this.currentMap = mapData
     },
      changeMap(val){
       this.config.customize.scope=val.slice(0,-5)
     },
     changeLevel () {
-      if (this.config.customize.level === 'country') {
-        this.config.customize.dataMap = '中华人民共和国.json'
-        this.config.customize.scope='中国'
-      } else if (this.config.customize.level === 'province') {
-        this.getMapList()
-        this.config.customize.dataMap = '安徽省.json'
-        this.config.customize.scope='安徽省'
-        this.config.customize.down=false
-      }else{
-        this.config.customize.scope='世界'
-        this.config.customize.down=false
-      }
+      this.getMapTree()
     },
     delColor () {
       this.colors = []
@@ -431,4 +490,5 @@ export default {
 .lc-field-body {
   padding: 12px 16px;
 }
+
 </style>

+ 5 - 1
data-room-ui/packages/BasicComponents/FlyMap/settingConfig.js

@@ -41,6 +41,8 @@ const customConfig = {
     contribution: false
   },
   customize: {
+    // 地图id
+    mapId: '',
     // 是否显示文字
     mapName: false,
     // 悬浮框背景色
@@ -62,6 +64,8 @@ const customConfig = {
     fontSize:'30',
     // 是否开启下钻
     down: false,
+    // 允许下钻的层级
+    downLevel: 1,
     // 轨迹图像
     symbol: 'arrow',
     // 轨迹颜色
@@ -69,7 +73,7 @@ const customConfig = {
     // 轨迹大小
     symbolSize:8,
     // 地图级别
-    level: 'country',
+    level: 1,
     // 范围
     scope: '中国',
     // 地图区域颜色

+ 187 - 128
data-room-ui/packages/BasicComponents/Map/index.vue

@@ -1,9 +1,9 @@
 <template>
   <div
-    style="width: 100%; height: 100%"
     class="bs-design-wrap bs-bar"
+    style="width: 100%; height: 100%"
   >
-  <el-button class="button" v-if="this.level=='province'&&config.customize.down" @click="jumpTo(config)" type='text' > 返回上一级</el-button>
+    <el-button v-if="currentDeep > 0" class="button" type='text' @click="jumpTo(config)"> 返回上一级</el-button>
     <div
       :id="`chart${config.code}`"
       style="width: 100%; height: 100%"
@@ -16,6 +16,7 @@ import * as echarts from 'echarts'
 import commonMixins from 'data-room-ui/js/mixins/commonMixins.js'
 import paramsMixins from 'data-room-ui/js/mixins/paramsMixins'
 import linkageMixins from 'data-room-ui/js/mixins/linkageMixins'
+
 export default {
   name: 'MapCharts',
   mixins: [paramsMixins, commonMixins, linkageMixins],
@@ -29,22 +30,24 @@ export default {
       default: () => ({})
     }
   },
-  data () {
+  data() {
     return {
+      currentDeep: 0,
+      mapList: [],
       charts: null,
       hasData: false,
-      level:'',
-      option:{}
+      level: '',
+      option: {}
     }
   },
   computed: {
-    Data () {
+    Data() {
       return JSON.parse(JSON.stringify(this.config))
     }
   },
   watch: {
     Data: {
-      handler (newVal, oldVal) {
+      handler(newVal, oldVal) {
         if (newVal.w !== oldVal.w || newVal.h !== oldVal.h) {
           this.$nextTick(() => {
             this.charts.resize()
@@ -54,14 +57,14 @@ export default {
       deep: true
     }
   },
-  mounted () {
+  mounted() {
     this.chartInit()
   },
-  beforeDestroy () {
+  beforeDestroy() {
     this.charts?.clear()
   },
   methods: {
-    chartInit () {
+    chartInit() {
       const config = this.config
       // key和code相等,说明是一进来刷新,调用list接口
       if (this.config.code === this.config.key || this.isPreview) {
@@ -70,7 +73,8 @@ export default {
           // 改变样式
           // config = this.changeStyle(res)
           this.newChart(config)
-        }).catch(() => {})
+        }).catch(() => {
+        })
       } else {
         // 否则说明是更新,这里的更新只指更新数据(改变样式时是直接调取changeStyle方法),因为更新数据会改变key,调用chart接口
         this.changeData(config).then((res) => {
@@ -79,10 +83,13 @@ export default {
         })
       }
     },
-    dataFormatting (config, data) {
+    dataFormatting(config, data) {
       const dataList = []
       data?.data?.forEach(item => {
-        dataList.push({ name: item[config.customize.name], value: [item[config.customize.xaxis], item[config.customize.yaxis], item[config.customize.value]] })
+        dataList.push({
+          name: item[config.customize.name],
+          value: [item[config.customize.xaxis], item[config.customize.yaxis], item[config.customize.value]]
+        })
       })
       config.option = {
         ...config.option,
@@ -90,32 +97,36 @@ export default {
       }
       return config
     },
-    async jumpTo(config){
-      this.level='country'
-      const mapUrl =`${window.BS_CONFIG?.httpConfigs?.baseURL}/static/chinaMap/country/中华人民共和国.json`
-      const map = await this.$dataRoomAxios.get(decodeURI(mapUrl), {}, true)
-      this.option.geo.map = '中华人民共和国';
-      this.changeData({...config,customize:{...config.customize,level:'country',scope:'中国'}})
-      echarts.registerMap('中华人民共和国', map);
+    async jumpTo(config) {
+      this.currentDeep--
+      let map = this.mapList[this.currentDeep]
+      // 移除mapList中的最后一个元素
+      this.mapList.pop()
+      let mapData = JSON.parse(map.geoJson)
+      this.option.geo.map = map.name;
+      this.changeData({...config, customize: {...config.customize, level: map.level, scope: map.name}})
+      echarts.registerMap(map.name, mapData);
       this.charts.setOption(this.option, true);
     },
-    async newChart (config) {
+    async newChart(config) {
+      let center1 = config.customize.center1 ? config.customize.center1 + '%' : '50%'
+      let center2 = config.customize.center2 ? config.customize.center2 + '%' : '50%'
       this.charts = echarts.init(
         document.getElementById(`chart${this.config.code}`)
       )
       this.option = {
         // 背景颜色
         backgroundColor: config.customize.backgroundColor,
-        graphic: [
-          ],
+        graphic: [],
         geo: {
           map: config.customize.scope,
-          zlevel: 10,
-          show:true,
-          layoutCenter: ['50%', '50%'],
+          zlevel: 9,
+          show: true,
+          // 地图中心点位置
+          layoutCenter: [center1, center2],
           roam: true,
           layoutSize: "100%",
-          zoom: 1,
+          zoom: config.customize.zoom || 1,
           label: {
             // 通常状态下的样式
             normal: {
@@ -170,97 +181,97 @@ export default {
         },
         series: config.customize.scatter
           ? [
-              // {
-              //   type: 'effectScatter',
-              //   coordinateSystem: 'geo',
-              //   effectType: 'ripple',
-              //   showEffectOn: 'render',
-              //   rippleEffect: {
-              //     period: 10,
-              //     scale: 10,
-              //     brushType: 'fill'
-              //   },
+            // {
+            //   type: 'effectScatter',
+            //   coordinateSystem: 'geo',
+            //   effectType: 'ripple',
+            //   showEffectOn: 'render',
+            //   rippleEffect: {
+            //     period: 10,
+            //     scale: 10,
+            //     brushType: 'fill'
+            //   },
 
-              //   hoverAnimation: true,
-              //   itemStyle: {
-              //     normal: {
-              //       color: 'rgba(255, 235, 59, .7)',
-              //       shadowBlur: 10,
-              //       shadowColor: '#333'
-              //     }
-              //   },
-              //   tooltip: {
-              //     formatter(params) {
-              //       return `<p style="text-align:center;line-height: 30px;height:30px;font-size: 14px;border-bottom: 1px solid #7A8698;">${
-              //         params.name
-              //       }</p>
-              //   <div style="line-height:22px;margin-top:5px">GDP<span style="margin-left:12px;color:#fff;float:right">${
-              //     params.data?.value[2] || '--'
-              //   }</span></div>`
-              //     },
-              //     show: true
-              //   },
-              //   zlevel: 1,
-              //   data: [
-              //     { name: '西藏自治区', value: [91.23, 29.5, 1] },
-              //     { name: '黑龙江省', value: [128.03, 47.01, 1007] },
-              //     { name: '北京市', value: [116.4551, 40.2539, 5007] }
-              //   ]
-              // }
-              {
-                type: 'scatter',
-                coordinateSystem: 'geo',
-                symbol: 'pin',
-                legendHoverLink: true,
-                symbolSize: [60, 60],
-                showEffectOn: 'render',
-                rippleEffect: {
-                  brushType: 'stroke'
-                },
-                hoverAnimation: true,
-                zlevel: 1,
-                // 这里渲染标志里的内容以及样式
-                label: {
-                  show: true,
-                  formatter (value) {
-                    return value.data.value[2]
-                  },
-                  color: config.customize.scatterColor
+            //   hoverAnimation: true,
+            //   itemStyle: {
+            //     normal: {
+            //       color: 'rgba(255, 235, 59, .7)',
+            //       shadowBlur: 10,
+            //       shadowColor: '#333'
+            //     }
+            //   },
+            //   tooltip: {
+            //     formatter(params) {
+            //       return `<p style="text-align:center;line-height: 30px;height:30px;font-size: 14px;border-bottom: 1px solid #7A8698;">${
+            //         params.name
+            //       }</p>
+            //   <div style="line-height:22px;margin-top:5px">GDP<span style="margin-left:12px;color:#fff;float:right">${
+            //     params.data?.value[2] || '--'
+            //   }</span></div>`
+            //     },
+            //     show: true
+            //   },
+            //   zlevel: 1,
+            //   data: [
+            //     { name: '西藏自治区', value: [91.23, 29.5, 1] },
+            //     { name: '黑龙江省', value: [128.03, 47.01, 1007] },
+            //     { name: '北京市', value: [116.4551, 40.2539, 5007] }
+            //   ]
+            // }
+            {
+              type: 'scatter',
+              coordinateSystem: 'geo',
+              symbol: 'pin',
+              legendHoverLink: true,
+              symbolSize: [60, 60],
+              showEffectOn: 'render',
+              rippleEffect: {
+                brushType: 'stroke'
+              },
+              hoverAnimation: true,
+              zlevel: 11,
+              // 这里渲染标志里的内容以及样式
+              label: {
+                show: true,
+                formatter(value) {
+                  return value.data.value[2]
                 },
-                // 标志的样式
-                itemStyle: {
-                  normal: {
-                    color: config.customize.scatterBackgroundColor,
-                    shadowBlur: 2,
-                    shadowColor: 'D8BC37'
-                  }
-                },
-                data: config.option?.data
-              }
-            ]
+                color: config.customize.scatterColor
+              },
+              // 标志的样式
+              itemStyle: {
+                normal: {
+                  color: config.customize.scatterBackgroundColor,
+                  shadowBlur: 2,
+                  shadowColor: 'D8BC37'
+                }
+              },
+              data: config.option?.data
+            }
+          ]
           : [
-              {
-                type: 'map',
-                map: config.customize.scope,
-                geoIndex: 0,
-                roam: false,
-                zoom: 1.5,
-                center: [105, 36],
-                showLegendSymbol: false, // 存在legend时显示
-                data: config.option?.data,
-                tooltip: {
-                  formatter (params) {
-                    return `<p style="text-align:center;line-height: 30px;height:30px;font-size: 14px;border-bottom: 1px solid #7A8698;">${
-                      params.name
-                    }</p>
+            {
+              type: 'map',
+              map: config.customize.scope,
+              geoIndex: 0,
+              roam: false,
+              zoom: 1.5,
+              center: [105, 36],
+              showLegendSymbol: false, // 存在legend时显示
+              data: config.option?.data,
+              tooltip: {
+                formatter(params) {
+                  return `<p style="text-align:center;line-height: 30px;height:30px;font-size: 14px;border-bottom: 1px solid #7A8698;">${
+                    params.name
+                  }</p>
                 <div style="line-height:22px;margin-top:5px">GDP<span style="margin-left:12px;color:#fff;float:right">${
-                  params.data?.value[2] || '--'
-                }</span></div>`
-                  },
-                  show: true
-                }
+                    params.data?.value[2] || '--'
+                  }</span></div>`
+                },
+                show: true
               }
-            ]
+            }
+          ]
       }
       if (config.customize.visual) {
         this.option.visualMap = {
@@ -273,21 +284,65 @@ export default {
           }
         }
       }
-      const mapUrl = `${window.BS_CONFIG?.httpConfigs?.baseURL}/static/chinaMap/${config.customize.level}/${config.customize.dataMap}`
-      const map = await this.$dataRoomAxios.get(decodeURI(mapUrl), {}, true)
+      let hasMapId = !!config.customize.mapId
+      // 根据mapId获取地图数据
+      let mapInfoUrl = `${window.BS_CONFIG?.httpConfigs?.baseURL}/bigScreen/map/info/${config.customize.mapId}`
+      // 如果设置了地图id,就用地图id获取地图数据,否则用默认的世界地图
+      if (!hasMapId) {
+        mapInfoUrl = `${window.BS_CONFIG?.httpConfigs?.baseURL}/static/chinaMap/country/中华人民共和国.json`
+      }
+      const mapResp = await this.$dataRoomAxios.get(decodeURI(mapInfoUrl), {}, true)
+      const map = hasMapId ? JSON.parse(mapResp.data.geoJson) : mapResp
+      if (hasMapId && mapResp.data.uploadedGeoJson !== 1) {
+        // 没有上传过geoJson
+        this.$message({
+          message: '请先上传地图数据',
+          type: 'warning'
+        })
+        return
+      }
+      this.mapList.push(mapResp.data)
       echarts.registerMap(config.customize.scope, map)
       this.charts.setOption(this.option)
-      this.charts.on('click',  async(params)=> {
-          if(params.name=='') return
-          if(config.customize.down===false||this.level==='province') return
-          this.level='province'
-          const mapUrl =`${window.BS_CONFIG?.httpConfigs?.baseURL}/static/chinaMap/province/${params.name}.json`
-          const map = await this.$dataRoomAxios.get(decodeURI(mapUrl), {}, true)
-          this.changeData({...config,customize:{...config.customize,level:'province',scope:params.name}})
-          this.option.geo.map = params.name
-          echarts.registerMap(params.name, map);
-          this.charts.setOption(this.option, true);
-          });
+      // 点击下钻
+      this.charts.on('click', async (params) => {
+        if (params.name == '') return
+        if (!config.customize.down) {
+          this.$message({
+            message: '该地图未开启下钻',
+            type: 'warning'
+          })
+          return
+        }
+        // 到达允许下钻的层数,则不再下钻
+        if (this.currentDeep >= config.customize.downLevel) return
+        const downMapUrl = `${window.BS_CONFIG?.httpConfigs?.baseURL}/bigScreen/map/data/${this.mapList[this.currentDeep].id}/${params.name}`
+        const downMap = await this.$dataRoomAxios.get(decodeURI(downMapUrl), {}, false)
+        // 地图不可用
+        if (downMap.available !== 1) {
+          this.$message({
+            message: '未找到该地图配置',
+            type: 'warning'
+          })
+          return
+        }
+        let geoJsonObj
+        try {
+          geoJsonObj = JSON.parse(downMap.geoJson)
+        } catch (error) {
+          this.$message({
+            message: params.name + '地图数据格式错误',
+            type: 'warning'
+          })
+          return
+        }
+        this.currentDeep++
+        this.mapList.push(downMap)
+        this.changeData({...config, customize: {...config.customize, scope: params.name}})
+        this.option.geo.map = params.name
+        echarts.registerMap(params.name, geoJsonObj);
+        this.charts.setOption(this.option, true);
+      });
     }
   }
 }
@@ -295,17 +350,21 @@ export default {
 
 <style lang="scss" scoped>
 @import '../../assets/style/echartStyle';
+
 .light-theme {
   background-color: #ffffff;
   color: #000000;
 }
+
 .auto-theme {
   background-color: rgba(0, 0, 0, 0);
 }
-.bs-design-wrap{
+
+.bs-design-wrap {
   position: relative;
   padding: 0 16px;
-  .button{
+
+  .button {
     position: absolute;
     z-index: 999;
   }

+ 113 - 39
data-room-ui/packages/BasicComponents/Map/setting.vue

@@ -55,36 +55,32 @@
             @change="changeLevel()"
           >
             <el-option
-              label="国家"
-              value="country"
-            />
-            <el-option
-              label="省份"
-              value="province"
+              v-for="level in levelList"
+              :key="level.value"
+              :label="level.label"
+              :value="level.value"
             />
           </el-select>
         </el-form-item>
         <el-form-item
-          v-if="config.customize.level == 'province'"
-          label="地图显示区域"
+          label="地图"
           label-width="100px"
         >
-          <el-select
-            v-model="config.customize.dataMap"
-            popper-class="bs-el-select"
-            class="bs-el-select"
-            @change="changeMap()"
-          >
-            <el-option
-              v-for="map in mapList"
-              :key="map.name"
-              :label="map.name"
-              :value="map.url"
-            />
-          </el-select>
+          <el-cascader
+            ref="cascade"
+            v-model="config.customize.mapId"
+            popper-class="bs-el-cascader"
+            :options="mapTree"
+            :props="{ value: 'id', label: 'name', children: 'children', emitPath: false }"
+            @change="mapSelect">
+            <template slot-scope="{ node, data }">
+              <span style="float: left">{{ data.name }}</span>
+              <span v-if="data.disabled" style="float: right; color: #8492a6; font-size: 13px"> 未配置 </span>
+            </template>
+          </el-cascader>
+
         </el-form-item>
         <el-form-item
-          v-if="config.customize.level == 'country'"
           label="是否开启下钻"
           label-width="100px"
         >
@@ -94,6 +90,59 @@
             active-color="#007aff"
           />
         </el-form-item>
+        <el-form-item
+          v-if="config.customize.down"
+          label="允许下钻层级"
+          label-width="100px"
+        >
+          <el-select
+            v-model="config.customize.downLevel"
+            popper-class="bs-el-select"
+            class="bs-el-select">
+            <el-option
+              v-for="level in downLevelList"
+              :key="level.value"
+              :label="level.label"
+              :value="level.value"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item
+          label="比例"
+          label-width="100px"
+        >
+          <el-slider
+            v-model="config.customize.zoom"
+            class="bs-el-slider-dark"
+            :step="0.1"
+            :min="0.1"
+            :max="5"
+          ></el-slider>
+        </el-form-item>
+        <el-form-item
+          label="中心点横向偏移"
+          label-width="100px"
+        >
+          <el-slider
+            class="bs-el-slider-dark"
+            v-model="config.customize.center1"
+            :step="1"
+            :min="1"
+            :max="100"
+          ></el-slider>
+        </el-form-item>
+        <el-form-item
+          label="中心点纵向偏移"
+          label-width="100px"
+        >
+          <el-slider
+            class="bs-el-slider-dark"
+            v-model="config.customize.center2"
+            :step="1"
+            :min="1"
+            :max="100"
+          ></el-slider>
+        </el-form-item>
         <el-form-item
           v-if="config.customize.down"
           label="头部字体颜色"
@@ -295,7 +344,29 @@ export default {
         '#2B74CF',
         '#00BC9D',
         '#ED7D32'
-      ]
+      ],
+      mapTree: [],
+      currentMap: {},
+      levelList: [
+        {value: '0', label: '世界'},
+        {value: '1', label: '国家'},
+        {value: '2', label: '省份'},
+        {value: '3', label: '城市'},
+        {value: '4', label: '区县'}
+      ],
+      // 旧版本地图等级,该数据用于兼容旧版本
+      oldLevelMap: {
+        'world' : '0',
+        'country' : '1',
+        'province' : '2',
+      },
+      downLevelList: [
+        {value: 1, label: '下钻一层'},
+        {value: 2, label: '下钻两层'},
+        {value: 3, label: '下钻三层'},
+        {value: 4, label: '下钻四层'},
+        {value: 5, label: '下钻五层'}
+      ],
     }
   },
   computed: {
@@ -310,30 +381,27 @@ export default {
   },
   watch: {},
   mounted () {
-    this.getMapList()
+    this.getMapTree()
   },
   methods: {
-    getMapList () {
-      this.$dataRoomAxios.get(`${window.BS_CONFIG?.httpConfigs?.baseURL}/bigScreen/design/map/list/${this.config.customize.level}`).then((res) => {
-        this.mapList = res
+    getMapTree() {
+      const levelConst = ['0', '1', '2', '3', '4']
+      if (!levelConst.includes(this.config.customize.level)) {
+        this.config.customize.level = this.oldLevelMap[this.config.customize.level] || '0'
+      }
+      this.$dataRoomAxios.get(`${window.BS_CONFIG?.httpConfigs?.baseURL}/bigScreen/map/tree/${this.config.customize.level}`).then((res) => {
+        this.mapTree = res
       })
     },
+    mapSelect (mapId) {
+      let mapData = this.$refs['cascade'].getCheckedNodes()[0].data
+      this.currentMap = mapData
+    },
     changeMap(val){
       this.config.customize.scope=val.slice(0,-5)
     },
     changeLevel () {
-      this.getMapList()
-      if (this.config.customize.level === 'country') {
-        this.config.customize.dataMap = '中华人民共和国.json'
-        this.config.customize.scope='中国'
-      } else if (this.config.customize.level === 'province') {
-        this.config.customize.dataMap = '安徽省.json'
-        this.config.customize.scope='安徽省'
-        this.config.customize.down=false
-      }else{
-        this.config.customize.scope='世界'
-        this.config.customize.down=false
-      }
+      this.getMapTree()
     },
     delColor () {
       this.colors = []
@@ -368,4 +436,10 @@ export default {
 .lc-field-body {
   padding: 12px 16px;
 }
+/deep/.bs-el-slider-dark {
+
+  .el-slider__runway {
+    background-color: var(--bs-el-background-1) !important;
+  }
+}
 </style>

+ 8 - 1
data-room-ui/packages/BasicComponents/Map/settingConfig.js

@@ -41,6 +41,11 @@ const customConfig = {
     contribution: false
   },
   customize: {
+    mapId: '',
+    // 缩放尺寸
+    zoom: 1,
+    center1: 50,
+    center2: 50,
     // 是否显示文字
     mapName: true,
     // 地图背景色
@@ -61,8 +66,10 @@ const customConfig = {
     fontSize: '30',
     // 是否开启下钻
     down: false,
+    // 允许下钻的层级
+    downLevel: 1,
     // 地图级别
-    level: 'country',
+    level: 1,
     // 范围
     scope: '中国',
     // 地图区域颜色

+ 3 - 0
data-room-ui/packages/BasicComponents/ScreenScrollBoard/index.vue

@@ -1,5 +1,8 @@
 <template>
   <div
+    v-loading="config.loading"
+    element-loading-text="数据加载中"
+    :element-loading-background="loadingBackground"
     style="width: 100%;height: 100%"
     class="bs-design-wrap"
   >

+ 2 - 1
data-room-ui/packages/BasicComponents/ScreenScrollBoard/settingConfig.js

@@ -32,7 +32,8 @@ const customConfig = {
   type: 'screenScrollBoard',
   root: {
     version: '2023071001',
-    contribution: false
+    contribution: false,
+    loading: false
   },
   customize: {
     rowNum: 5,

+ 3 - 0
data-room-ui/packages/BasicComponents/ScreenScrollRanking/index.vue

@@ -1,5 +1,8 @@
 <template>
   <div
+    v-loading="config.loading"
+    element-loading-text="数据加载中"
+    :element-loading-background="loadingBackground"
     style="width: 100%;height: 100%"
     class="bs-design-wrap"
   >

+ 2 - 1
data-room-ui/packages/BasicComponents/ScreenScrollRanking/settingConfig.js

@@ -26,7 +26,8 @@ const customConfig = {
   type: 'screenScrollRanking',
   root: {
     version: '2023071001',
-    contribution: false
+    contribution: false,
+    loading: false
   },
   customize: {
     rowNum: 5,

+ 89 - 36
data-room-ui/packages/BasicComponents/Select/index.vue

@@ -1,17 +1,18 @@
 <template>
   <el-select
     :key="config.code"
-    v-model="selectValue"
-    :popper-class="'basic-component-select selct-popper-' + config.code"
-    :class="['basic-component-select', `selct-${config.code}`]"
-    :placeholder="`请选择${placeholder}`"
-    filterable
+    v-model="value"
+    :popper-class="'basic-component-select select-popper-' + config.code"
+    :class="['basic-component-select', `select-${config.code}`]"
+    :placeholder="`请选择${placeholder || newPlaceholder}`"
     clearable
+    :filterable="filterable"
     @visible-change="visibleChange"
     @change="selectChange"
+    @mouseenter.native="mouseenter"
   >
     <el-option
-      v-for="(option, key) in config.option.data"
+      v-for="(option, key) in optionData"
       :key="key"
       :label="option[config.dataSource.dimensionField]"
       :value="option[config.dataSource.metricField]"
@@ -24,6 +25,8 @@ import { EventBus } from 'data-room-ui/js/utils/eventBus'
 import commonMixins from 'data-room-ui/js/mixins/commonMixins'
 import linkageMixins from 'data-room-ui/js/mixins/linkageMixins'
 import { getDataSetDetails } from 'data-room-ui/js/api/bigScreenApi'
+import { settingToTheme } from 'data-room-ui/js/utils/themeFormatting'
+import cloneDeep from 'lodash/cloneDeep'
 window.dataSetFields = []
 export default {
   name: 'BasicComponentSelect',
@@ -38,22 +41,52 @@ export default {
   },
   data () {
     return {
-      selectValue: '',
-      innerConfig: {}
+      value: '',
+      innerConfig: {},
+      optionData: [],
+      newPlaceholder: '',
+      filterable: false
     }
   },
   computed: {
-    placeholder () {
-      return window.dataSetFields.find(field => field.value === this.config.dataSource.dimensionField)?.label || ''
+    isPreview () {
+      return (this.$route.path === window?.BS_CONFIG?.routers?.previewUrl) || (this.$route.path === '/big-screen/preview')
+    },
+    placeholder: {
+      get () {
+        return window.dataSetFields.find(field => field.value === this.config.dataSource.dimensionField)?.label || ''
+      },
+      set (val) {
+        this.newPlaceholder = val
+      }
     }
   },
   watch: { },
-  created () { },
+  created () {
+  },
   mounted () {
+    window.dataSetFields = []
     this.changeStyle(this.config)
     EventBus.$on('changeBusinessKey', () => {
       window.dataSetFields = []
     })
+    if (this.isPreview) {
+      this.filterable = true
+      document.querySelector(`.select-${this.config.code}`).style.pointerEvents = 'all'
+      if (this.config.dataSource.businessKey && window.dataSetFields.length === 0) {
+        getDataSetDetails(this.config.dataSource.businessKey).then(res => {
+          window.dataSetFields = res.fields.map(field => {
+            return {
+              label: field.comment || field.fieldDesc,
+              value: field.name || field.fieldName
+            }
+          })
+          this.placeholder = window.dataSetFields.find(field => field.value === this.config.dataSource.dimensionField)?.label || ''
+        })
+      }
+    } else {
+      document.querySelector(`.select-${this.config.code}`).style.pointerEvents = 'none'
+    }
   },
   beforeDestroy () {
     EventBus.$off('changeBusinessKey')
@@ -73,6 +106,7 @@ export default {
           }
         }
         config.option.data = data
+        this.optionData = data
         config.customize.title = config.option.data[config.dataSource.dimensionField] || config.customize.title
         if (window.dataSetFields.length === 0) {
           getDataSetDetails(this.config.dataSource.businessKey).then(res => {
@@ -82,47 +116,42 @@ export default {
                 value: field.name || field.fieldName
               }
             })
+            this.placeholder = window.dataSetFields.find(field => field.value === this.config.dataSource.dimensionField)?.label || ''
           })
         }
         // 语音播报
       } else {
         // 数据返回失败则赋前端的模拟数据
         config.option.data = []
+        this.optionData = []
       }
       return config
     },
     changeStyle (config) {
+      config = { ...this.config, ...config }
+      // 样式改变时更新主题配置
+      config.theme = settingToTheme(cloneDeep(config), this.customTheme)
+      this.changeChartConfig(config)
       this.innerConfig = config
       // 选择器元素
-      const selectInputEl = document.querySelector(`.selct-${config.code} .el-input__inner`)
+      const selectInputEl = document.querySelector(`.select-${config.code} .el-input__inner`)
       // 背景颜色
       selectInputEl.style.backgroundColor = config.customize.backgroundColor
       // 字体大小
       selectInputEl.style.fontSize = config.customize.fontSize + 'px'
       // 字体颜色
       selectInputEl.style.color = config.customize.fontColor
+      // 下拉图标
+      const selectDropdownIcon = document.querySelector(`.select-${config.code} .el-icon-arrow-up`)
+      selectDropdownIcon.style.fontSize = config.customize.fontSize + 'px'
       // 选择器下拉框元素
-      const selectDropdownEl = document.querySelector(`.selct-${config.code} .el-select-dropdown`)
+      const selectDropdownEl = document.querySelector(`.select-${config.code} .el-select-dropdown`)
       // 箭头背景颜色和下拉框背景颜色一致
       if (selectDropdownEl) {
         // 下拉框无边框
         selectDropdownEl.style.border = 'none'
         // 背景颜色
         selectDropdownEl.style.backgroundColor = config.customize.dropDownBackgroundColor
-        // 下拉项hover颜色
-        const selectDropdownItemEl = document.querySelectorAll(`.selct-${this.innerConfig.code} .el-select-dropdown__item`)
-        selectDropdownItemEl.forEach(item => {
-          // 下拉项字体颜色
-          item.style.color = this.innerConfig.customize.dropDownFontColor
-          item.addEventListener('mouseenter', () => {
-            item.style.color = this.innerConfig.customize.dropDownHoverFontColor
-            item.style.backgroundColor = this.innerConfig.customize.dropDownHoverBackgroundColor
-          })
-          item.addEventListener('mouseleave', () => {
-            item.style.color = this.innerConfig.customize.dropDownFontColor
-            item.style.backgroundColor = this.innerConfig.customize.dropDownBackgroundColor
-          })
-        })
       }
     },
     // 组件联动
@@ -132,22 +161,23 @@ export default {
     visibleChange (val) {
       if (val) {
         // 修改下拉框背景颜色,让下拉框背景颜色和箭头背景颜色一致
-        const selectDropdownEl = document.querySelector(`.selct-popper-${this.innerConfig.code}`)
+        const selectDropdownEl = document.querySelector(`.select-popper-${this.innerConfig.code}`)
         selectDropdownEl.style.color = this.innerConfig.customize.dropDownBackgroundColor
         // 空状态
-        const selectDropdownEmptyEl = document.querySelector(`.selct-popper-${this.innerConfig.code} .el-select-dropdown__empty`)
+        const selectDropdownEmptyEl = document.querySelector(`.select-popper-${this.innerConfig.code} .el-select-dropdown__empty`)
         if (selectDropdownEmptyEl) {
           selectDropdownEmptyEl.style.backgroundColor = this.innerConfig.customize.dropDownBackgroundColor
         }
-        // 激活项
-        const selectDropdownItemSelectedEl = document.querySelectorAll(`.selct-popper-${this.innerConfig.code} .el-select-dropdown__item.selected`)
-        selectDropdownItemSelectedEl.forEach(item => {
-          item.style.color = this.innerConfig.customize.activeFontColor
-          item.style.backgroundColor = this.innerConfig.customize.activeBackgroundColor
+        // 下拉项hover颜色
+        const selectDropdownItemEl = document.querySelectorAll(`.select-popper-${this.innerConfig.code} .el-select-dropdown__item`)
+        // 给--dropDownHoverFontColor 和 --dropDownHoverBackgroundColor 赋值
+        selectDropdownItemEl.forEach(item => {
+          item.style.setProperty('--dropDownHoverFontColor', this.innerConfig.customize.dropDownHoverFontColor)
+          item.style.setProperty('--dropDownHoverBackgroundColor', this.innerConfig.customize.dropDownHoverBackgroundColor)
         })
       }
       // 不是激活项的还是使用背景颜色
-      const selectDropdownItemEl = document.querySelectorAll(`.selct-popper-${this.innerConfig.code} .el-select-dropdown__item`)
+      const selectDropdownItemEl = document.querySelectorAll(`.select-popper-${this.innerConfig.code} .el-select-dropdown__item`)
       selectDropdownItemEl.forEach(item => {
         // 检查是否是激活项,不是则使用背景颜色
         if (!item.classList.contains('selected')) {
@@ -155,6 +185,18 @@ export default {
           item.style.backgroundColor = this.innerConfig.customize.dropDownBackgroundColor
         }
       })
+    },
+    // 鼠标进入
+    mouseenter () {
+      if (this.value) {
+        setTimeout(() => {
+        // 清空图标
+          const selectDropdownCloseIcon = document.querySelector(`.select-${this.innerConfig.code} .el-icon-circle-close`)
+          if (selectDropdownCloseIcon) {
+            selectDropdownCloseIcon.style.fontSize = this.innerConfig.customize.fontSize + 'px'
+          }
+        }, 30)
+      }
     }
   }
 
@@ -183,15 +225,26 @@ export default {
 .basic-component-select {
   width: 100%;
   height: 100%;
+
   ::v-deep .el-input {
     height: 100% !important;
-
+    .el-select__caret{
+      width: 100%;
+      height: 100%;
+      display: flex;
+      align-items: center;
+    }
     //  选择器输入框样式
     .el-input__inner {
       height: 100% !important;
       border-color: var(--bs-el-border) !important;
     }
   }
+  .el-select-dropdown__item.hover,
+  .el-select-dropdown__item:hover {
+    color: var(--dropDownHoverFontColor) !important;
+    background-color: var(--dropDownHoverBackgroundColor) !important;
+  }
 
   .el-tag.el-tag--info {
     color: var(--bs-el-text) !important;

+ 3 - 40
data-room-ui/packages/BasicComponents/Select/setting.vue

@@ -19,7 +19,9 @@
           <div class="lc-field-body">
             <PosWhSetting :config="config" />
           </div>
-          <SettingTitle v-if="config.border">边框</SettingTitle>
+          <SettingTitle v-if="config.border">
+            边框
+          </SettingTitle>
           <div class="lc-field-body">
             <BorderSetting
               v-if="config.border"
@@ -52,31 +54,6 @@
                 :predefine="predefineThemeColors"
               />
             </el-form-item>
-            <!-- 下拉框是否需要边框 -->
-            <!-- <el-form-item label="下拉框边框">
-              <el-switch v-model="config.customize.dropDownBorder" />
-            </el-form-item> -->
-            <!-- 下拉框如果有边框,再选择边框颜色 -->
-            <!-- <el-form-item
-              v-if="config.customize.dropDownBorder"
-              label="下拉框边框颜色"
-            >
-              <ColorPicker
-                v-model="config.customize.dropDownBorderColor"
-                :predefine="predefineThemeColors"
-              />
-            </el-form-item> -->
-            <!-- hover 颜色 -->
-            <!-- 下拉框字体大小 -->
-            <!-- <el-form-item label="下拉框字体大小">
-              <el-input-number
-                v-model="config.customize.dropDownFontSize"
-                class="bs-el-input-number"
-                :min="12"
-                :max="100"
-              />
-            </el-form-item> -->
-            <!-- 下拉框字体颜色 -->
           </div>
           <SettingTitle>下拉项</SettingTitle>
           <!-- 选择器下拉框背景颜色 -->
@@ -107,20 +84,6 @@
                 :predefine="predefineThemeColors"
               />
             </el-form-item>
-            <!-- 激活项背景颜色 -->
-            <el-form-item label="激活项背景颜色">
-              <ColorPicker
-                v-model="config.customize.activeBackgroundColor"
-                :predefine="predefineThemeColors"
-              />
-            </el-form-item>
-            <!-- 激活项文字颜色 -->
-            <el-form-item label="激活项文字颜色">
-              <ColorPicker
-                v-model="config.customize.activeFontColor"
-                :predefine="predefineThemeColors"
-              />
-            </el-form-item>
           </div>
         </el-form>
       </div>

+ 2 - 7
data-room-ui/packages/BasicComponents/Select/settingConfig.js

@@ -25,9 +25,8 @@ export const settingConfig = {
 const customConfig = {
   type: 'select',
   // 名称
-  title: '下拉框',
   root: {
-    version: '2023071001'
+    version: '2023091402'
   },
   // 自定义属性
   customize: {
@@ -44,11 +43,7 @@ const customConfig = {
     // 下拉项hover背景颜色
     dropDownHoverBackgroundColor: '#6A7E9D',
     // 下拉项hover字体颜色
-    dropDownHoverFontColor: '#FFFFFF',
-    // 激活项背景颜色
-    activeBackgroundColor: '#6A7E9D',
-    // 激活项字体颜色
-    activeFontColor: '#FFFFFF'
+    dropDownHoverFontColor: '#FFFFFF'
   }
 }
 export const dataConfig = {

+ 3 - 0
data-room-ui/packages/BasicComponents/Tables/index.vue

@@ -1,5 +1,8 @@
 <template>
   <div
+    v-loading="config.loading"
+    element-loading-text="数据加载中"
+    :element-loading-background="loadingBackground"
     style="width: 100%;height: 100%"
     class="bs-design-wrap "
   >

+ 3 - 1
data-room-ui/packages/BasicComponents/Tables/settingConfig.js

@@ -34,8 +34,10 @@ const customConfig = {
   type: 'tables',
   root: {
     version: '2023071001',
-    contribution: false
+    contribution: false,
+    loading: false
   },
+
   customize: {
     theme: 'dark', // 'light'、'dark'
     // 表格头部背景颜色

+ 10 - 0
data-room-ui/packages/BigScreenDesign/LeftPanel.vue

@@ -149,6 +149,7 @@
 import cloneDeep from 'lodash/cloneDeep'
 import basicComponents from 'data-room-ui/js/config/basicComponentsConfig'
 import g2PlotComponents, { getCustomPlots } from '../G2Plots/plotList'
+import echartsComponents from '../Echarts/echartList'
 import borderComponents from 'data-room-ui/js/config/borderComponentsConfig'
 import decorationComponents from 'data-room-ui/js/config/decorationComponentsConfig'
 import LayerList from './LayerList/index.vue'
@@ -172,6 +173,7 @@ export default {
   },
   data () {
     return {
+      echartsComponents,
       g2PlotComponents,
       activeName: 'chart', // 设置左侧tab栏的默认值
       fold: false, // 控制左侧菜单栏伸缩
@@ -191,6 +193,13 @@ export default {
           icon: 'icon-jichushuju',
           components: this.g2PlotComponents
         },
+        {
+          id: 7,
+          name: 'echart',
+          title: '3D',
+          icon: 'icon-jichushuju',
+          components: this.echartsComponents
+        },
         {
           id: 3,
           name: 'dataV',
@@ -243,6 +252,7 @@ export default {
     this.initList()
     this.g2PlotComponents = [...this.g2PlotComponents, ...getCustomPlots()]
     this.menuList[1].components = this.g2PlotComponents
+    this.menuList[2].components = this.echartsComponents
   },
   mounted () {
     this.nodeDrag()

+ 1 - 0
data-room-ui/packages/BigScreenDesign/OverallSetting/index.vue

@@ -677,6 +677,7 @@ export default {
 //修改notify的样式
 .ds-el-notify {
   background-color: var(--bs-el-background-1)!important;
+  border: var(--bs-el-border)!important;
   .el-notification__title{
     color: #fff!important;
   }

+ 1 - 1
data-room-ui/packages/BigScreenDesign/RightSetting/DataSetting.vue

@@ -75,7 +75,7 @@
             </div>
           </div>
           <!--  基础组件数据配置  -->
-          <template v-if="!['customComponent', 'remoteComponent'].includes(config.type)">
+          <template v-if="!['customComponent', 'remoteComponent','echartsComponent'].includes(config.type)">
             <!--维度多选-->
             <el-form-item
               v-if="config.option.displayOption.dimensionField.enable"

+ 2 - 2
data-room-ui/packages/BigScreenDesign/RightSetting/TextGradient/index.vue

@@ -92,9 +92,9 @@ export default {
     },
     colorChange (val) {
       if (!this.startColor && this.endColor) {
-        this.colorsValue = `${this.position} ,#ffffff,${this.endColor}`
+        this.colorsValue = `${this.position} ,${this.endColor},${this.endColor}`
       } else if (this.startColor && !this.endColor) {
-        this.colorsValue = `${this.position} ,${this.startColor} ,#ffffff`
+        this.colorsValue = `${this.position} ,${this.startColor} ,${this.startColor}`
       } else if (!this.startColor && !this.endColor) {
         this.colorsValue = `${this.position} ,#ffffff ,#ffffff`
       } else {

+ 15 - 10
data-room-ui/packages/BigScreenDesign/RightSetting/index.vue

@@ -77,7 +77,8 @@ export default {
     CustomComponent,
     Svgs,
     // 远程组件的样式配置也和g2Plot的样式配置一样,采用属性配置, 故使用一个组件
-    RemoteComponent: CustomComponent
+    RemoteComponent: CustomComponent,
+    EchartsComponent: CustomComponent
   },
   data () {
     return {
@@ -99,7 +100,8 @@ export default {
         dataSource: cloneDeep(this.config.dataSource),
         linkage: cloneDeep(this.config?.linkage),
         dataHandler: this.config?.dataHandler,
-        dataSourceSetting: cloneDeep(this.config?.setting?.filter(item => item.tabName === 'data')) || []
+        dataSourceSetting: cloneDeep(this.config?.setting?.filter(item => item.tabName === 'data')) || [],
+        code: this.config?.code
       }
     },
     configStyle () {
@@ -131,6 +133,7 @@ export default {
     // 更新数据源部分,需要调用接口
     configDataSource: {
       handler (val, oldValue) {
+        console.log('oldValue', oldValue)
         this.handleConfigChange(val, oldValue, 'configDataSource')
       },
       deep: true
@@ -150,17 +153,19 @@ export default {
       }, delay)
     },
     handleConfigChange (val, oldValue, type) {
-      if (!isEqual(val, oldValue)) {
-        if (type === 'configStyle') {
-          if (this.config.type === 'iframeChart') {
-            this.debounce(500, { ...val, type: this.config.type, code: this.config.code, parentCode: this.config?.parentCode })
+      if (val.code === oldValue.code) {
+        if (!isEqual(val, oldValue)) {
+          if (type === 'configStyle') {
+            if (this.config.type === 'iframeChart') {
+              this.debounce(500, { ...val, type: this.config.type, code: this.config.code, parentCode: this.config?.parentCode })
+            } else {
+              this.$emit('updateSetting', { ...val, type: this.config.type, code: this.config.code, theme: this.config.theme, parentCode: this.config?.parentCode })
+            }
           } else {
-            this.$emit('updateSetting', { ...val, type: this.config.type, code: this.config.code, theme: this.config.theme, parentCode: this.config?.parentCode })
+            this.$emit('updateDataSetting', this.config)
           }
-        } else {
-          this.$emit('updateDataSetting', this.config)
+          this.saveTimeLine(`更新${val?.title ?? this.config.title}组件属性`)
         }
-        this.saveTimeLine(`更新${val?.title ?? this.config.title}组件属性`)
       }
     },
     close () {

+ 4 - 4
data-room-ui/packages/BorderSelect/index.vue

@@ -92,7 +92,7 @@ import borderComponents from 'data-room-ui/BorderComponents/bordersList'
 export default {
   name: 'ComponentDialog',
   mixins: [pageMixins],
-   model: {
+  model: {
     prop: 'type',
     event: 'select'
   },
@@ -113,13 +113,13 @@ export default {
       code: '',
       focus: -1,
       searchKey: '',
-      remoteComponentlist: [],
+      remoteComponentlist: []
     }
   },
   computed: {
     remoteComponentsGridComputed () {
       return this.remoteComponentlist.length > 3
-    },
+    }
   },
   watch: {
   },
@@ -143,7 +143,7 @@ export default {
       this.dialogVisible = false
       if (isEmpty(this.focus)) return
       this.$emit('select', this.focus.title)
-    },
+    }
 
   }
 }

+ 15 - 15
data-room-ui/packages/ComponentList/EditForm.vue

@@ -29,9 +29,9 @@
             class="bs-el-input"
           />
         </el-form-item>
-         <el-form-item
-          label="组件类型"
+        <el-form-item
           v-if="type === 'bizComponent'"
+          label="组件类型"
         >
           <el-select
             v-model="bizType"
@@ -145,20 +145,20 @@ export default {
   },
   data () {
     return {
-      bizType:'native',
+      bizType: 'native',
       resolutionRatioValue: '',
       resolutionRatio: {},
-      BizList:[
+      BizList: [
         {
-          label:'echarts组件',
-          value:'echarts'
+          label: 'echarts组件',
+          value: 'echarts'
         }, {
-          label:'g2Plot组件',
-          value:'g2plot'
+          label: 'g2Plot组件',
+          value: 'g2plot'
         }, {
-          label:'基础组件',
-          value:'native'
-        },
+          label: '基础组件',
+          value: 'native'
+        }
       ],
       resolutionRatioOptions: [
         {
@@ -252,7 +252,7 @@ export default {
     closeAddDialog () {
       this.formVisible = false
       this.$refs.dataForm.resetFields()
-      this.bizType='native'
+      this.bizType = 'native'
     },
     // 初始化
     init (nodeData, parentCode) {
@@ -455,13 +455,13 @@ export default {
     // 跳转设计态
     toDesign (form) {
       const path = this.type === 'component' ? (window.BS_CONFIG?.routers?.designUrl || '/big-screen/design') : 'big-screen-biz-component-design'
-      const { href: bigScreenHref } =this.type=='bizComponent'? this.$router.resolve({
+      const { href: bigScreenHref } = this.type == 'bizComponent' ? this.$router.resolve({
         path,
         query: {
           code: form.code,
-          type:this.bizType
+          type: this.bizType
         }
-      }):this.$router.resolve({
+      }) : this.$router.resolve({
         path,
         query: {
           code: form.code

+ 277 - 0
data-room-ui/packages/Echarts/3D图/3D固定柱状图.js

@@ -0,0 +1,277 @@
+/*
+ * @description: 配置,参考https://g2plot.antv.antgroup.com/examples
+ * @Date: 2023-03-27 14:38:23
+ * @Author: xing.heng
+ */
+import * as echarts from 'echarts'
+// 配置版本号
+const version = '2023071001'
+// 分类
+const category = 'Column'
+// 标题
+const title = '3D固定柱状图'
+// 类别, new Line()
+const chartType = 'Column'
+// 用于标识,唯一,和文件夹名称一致
+const name = 'FenZuZhuZhuangTu'
+
+// 右侧配置项
+const setting = [
+  {
+    label: '维度',
+    type: 'select', // 设置组件类型
+    field: 'xField', // 字段
+    optionField: 'xField', // 对应options中的字段
+    // 是否多选
+    multiple: false,
+    value: '',
+    tabName: 'data'
+  },
+  {
+    label: '指标',
+    type: 'select', // 设置组件类型
+    field: 'yField', // 字段
+    optionField: 'yField', // 对应options中的字段
+    // 是否多选
+    multiple: false,
+    value: '',
+    tabName: 'data'
+  },
+  {
+    label: '分组字段',
+    type: 'select', // 设置组件类型
+    field: 'seriesField', // 字段
+    optionField: 'seriesField', // 对应options中的字段
+    // 是否多选
+    multiple: false,
+    value: '',
+    tabName: 'data'
+  }
+]
+
+// 配置处理脚本
+const optionHandler = ''
+
+// 数据处理脚本
+const dataHandler = ''
+
+// 图表配置 new Line('domName', option)
+
+const xData = ['本年话务总量', '本年人工话务量', '每万客户呼入量', '本年话务总量', '本年人工话务量', '每万客户呼入量']
+const yData = [300, 1230, 425, 300, 1230, 425]
+const maxData = [1500, 1500, 1500, 1500, 1500, 1500]
+const option = {
+  animation: false,
+  tooltip: {
+    show: true
+  },
+  grid: {
+    left: '15%',
+    right: '5%',
+    bottom: '15%',
+    z: 100,
+    containLabel: false,
+    show: false
+  },
+  graphic: {
+    type: 'group',
+    bottom: '8%',
+    left: '10%',
+    z: 100,
+    children: [
+      {
+        type: 'rect',
+        left: 0,
+        bottom: 0,
+        shape: {
+          width: 400,
+          height: 10
+        },
+        style: {
+          fill: '#3f4867'
+        }
+      },
+      {
+        type: 'polygon',
+        left: 0,
+        bottom: 10,
+        shape: {
+          // 左上、右上、右下、左下
+          points: [[40, -50], [360, -50], [400, 0], [0, 0]]
+        },
+        style: {
+          fill: '#303256'
+        }
+      }
+    ]
+  },
+  xAxis: [
+    {
+      type: 'category',
+      data: xData,
+      // 坐标轴刻度设置:x轴数据展示
+      axisTick: {
+        show: true,
+        alignWithLabel: true
+      },
+      // 横坐标颜色
+      nameTextStyle: {
+        color: '#82b0ec'
+      },
+      // 是否显示坐标轴的轴线
+      axisLine: {
+        show: false
+      },
+      // 坐标轴刻度标签
+      axisLabel: {
+        textStyle: {
+          fontSize: 10,
+          color: 'rgb(40, 129, 170)'
+        },
+        margin: 30
+      }
+    },
+    {
+      type: 'category',
+      axisLine: {
+        show: false
+      },
+      axisTick: {
+        show: false
+      },
+      axisLabel: {
+        show: false
+      },
+      splitArea: {
+        show: false
+      },
+      splitLine: {
+        show: false
+      },
+      data: xData
+    }
+  ],
+  yAxis: [
+    {
+      show: true, // y轴文本标签显示
+      type: 'value',
+      axisLabel: {
+        textStyle: {
+          color: 'rgb(40, 129, 170)'
+        }
+      },
+      // 分隔线
+      splitLine: {
+        show: false // yAxis.show配置为true时,该配置才有效
+      },
+      // y轴轴线是否显示
+      axisLine: {
+        show: true
+      }
+    }
+  ],
+  series: [
+    {
+      type: 'pictorialBar', // 象形柱图
+      symbol: 'diamond',
+      symbolOffset: [0, '-50%'], // 上部菱形
+      symbolSize: [30, 15],
+      // symbolOffset: [0, -6], // 上部椭圆
+      symbolPosition: 'end',
+      z: 12,
+      label: {
+        normal: {
+          show: true,
+          position: 'top',
+          fontSize: 15,
+          fontWeight: 'bold',
+          color: '#27a7ce'
+        }
+      },
+      color: '#2DB1EF',
+      data: yData
+    },
+    {
+      type: 'pictorialBar',
+      symbol: 'diamond',
+      symbolSize: [30, 15],
+      symbolOffset: ['0%', '50%'], // 下部菱形
+      // symbolOffset: [0, 7], // 下部椭圆
+      z: 12,
+      color: '#187dcb',
+      data: yData
+    },
+    {
+      type: 'bar',
+      barWidth: 30,
+      z: 10,
+      itemStyle: {
+        normal: {
+          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+            {
+              offset: 0,
+              color: '#115ba6'
+            },
+            {
+              offset: 1,
+              color: '#1db0dd'
+            }
+          ]),
+          opacity: 0.8,
+          shadowColor: 'rgba(0, 0, 0, 0.5)', // 阴影颜色
+          shadowBlur: 0 // 阴影模糊值
+        }
+      },
+      data: yData
+    },
+    {
+      type: 'bar',
+      barWidth: 30,
+      barGap: '-100%',
+      itemStyle: {
+        normal: {
+          color: '#041133',
+          opacity: 0.8,
+          shadowColor: 'rgba(0, 0, 0, 0.5)', // 阴影颜色
+          shadowBlur: 0 // 阴影模糊值
+        }
+      },
+      label: {
+        show: false
+      },
+      data: maxData
+    },
+    {
+      type: 'pictorialBar', // 象形柱图
+      symbol: 'diamond',
+      symbolOffset: [0, '-50%'], // 上部菱形
+      symbolSize: [30, 15],
+      // symbolOffset: [0, -6], // 上部椭圆
+      symbolPosition: 'end',
+      z: 12,
+      label: {
+        normal: {
+          show: false,
+          position: 'top',
+          fontSize: 15,
+          fontWeight: 'bold',
+          color: '#27a7ce'
+        }
+      },
+      color: '#142f5a',
+      data: maxData
+
+    }
+  ]
+}
+export default {
+  category,
+  version,
+  title,
+  chartType,
+  name,
+  option,
+  setting,
+  optionHandler,
+  dataHandler
+}

+ 55 - 0
data-room-ui/packages/Echarts/echartList.js

@@ -0,0 +1,55 @@
+/*
+ * @description: webpack读取当前文件夹下的所有 图表的js文件配置, 生成g2Plot配置列表
+ * @Date: 2023-03-28 10:40:22
+ * @Author: xing.heng
+ */
+import { dataConfig, settingConfig } from '../PlotRender/settingConfig'
+import cloneDeep from 'lodash/cloneDeep'
+import sortList from './echartListSort'
+// 遍历 当前文件夹下的所有文件,找到中文.js文件,然后导出
+const files = require.context('./', true, /[\u4e00-\u9fa5]+.js$/)
+const echartsList = getEchartsList(files)
+// 获取plot配置
+function getEchartsList (files) {
+  const configMapList = {}
+  files.keys().forEach((key) => {
+    // ./折线图/基础折线图.js
+    // 取到 "基础折线图"
+    const configName = key.split('/')[2].replace('.js', '')
+    configMapList[configName] = files(key).default
+  })
+  const echartsList = []
+  for (const configMapKey in configMapList) {
+    const index = sortList.findIndex((item) => item === configMapKey)
+    const config = configMapList[configMapKey]
+
+    echartsList[index] = {
+      version: config.version,
+      category: configMapKey,
+      name: config.name,
+      title: config.title,
+      border: { type: '', titleHeight: 60, fontSize: 30, color: ['#5B8FF9', '#61DDAA', '#5D7092', '#F6BD16', '#6F5EF9'] },
+      icon: null,
+      img: require(`../Echarts/images/${config.title}.png`),
+      className:
+        'com.gccloud.dataroom.core.module.chart.components.CustomComponentChart',
+      w: config?.option?.width || 450,
+      h: config?.option?.height || 320,
+      x: 0,
+      y: 0,
+      type: 'echartsComponent',
+      chartType: config.chartType,
+      loading: false,
+      option: {
+        ...config.option,
+        ...cloneDeep(settingConfig)
+      },
+      setting: config.setting, // 右侧面板自定义配置
+      dataHandler: config.dataHandler, // 数据自定义处理js脚本
+      optionHandler: config.optionHandler, // 配置自定义处理js脚本
+      ...cloneDeep(dataConfig)
+    }
+  }
+  return echartsList
+}
+export default echartsList

+ 4 - 0
data-room-ui/packages/Echarts/echartListSort.js

@@ -0,0 +1,4 @@
+// 左侧plot组件的排序
+export default [
+  '3D固定柱状图'
+]

BIN
data-room-ui/packages/Echarts/images/3D固定柱状图.png


+ 349 - 0
data-room-ui/packages/EchartsRender/index.vue

@@ -0,0 +1,349 @@
+<template>
+  <div
+    v-loading="config.loading"
+    element-loading-text="图表加载中"
+    :element-loading-background="loadingBackground"
+    style="width: 100%;height: 100%"
+    class="bs-design-wrap bs-custom-component"
+    :class="{'light-theme':customTheme === 'light','auto-theme':customTheme !=='light'}"
+  >
+    <div
+      :id="chatId"
+      style="width: 100%;height: 100%"
+    />
+    <!--    <span style="color:#ffffff">{{config.option.data}}</span>-->
+  </div>
+</template>
+<script>
+import 'insert-css'
+import cloneDeep from 'lodash/cloneDeep'
+import linkageMixins from 'data-room-ui/js/mixins/linkageMixins'
+import commonMixins from 'data-room-ui/js/mixins/commonMixins'
+import { mapState, mapMutations } from 'vuex'
+import plotList, { getCustomPlots } from '../G2Plots/plotList'
+import { settingToTheme } from 'data-room-ui/js/utils/themeFormatting'
+import _ from 'lodash'
+import * as echarts from 'echarts'
+
+export default {
+  name: 'PlotCustomComponent',
+  mixins: [commonMixins, linkageMixins],
+  props: {
+    config: {
+      type: Object,
+      default: () => ({})
+    }
+  },
+  data () {
+    return {
+      chart: null,
+      hasData: false,
+      plotList
+    }
+  },
+  computed: {
+    ...mapState('bigScreen', {
+      pageInfo: state => state.pageInfo,
+      customTheme: state => state.pageInfo.pageConfig.customTheme,
+      activeCode: state => state.activeCode
+    }),
+    chatId () {
+      let prefix = 'chart_'
+      if (this.$route.path === window?.BS_CONFIG?.routers?.previewUrl) {
+        prefix = 'preview_chart_'
+      }
+
+      if (this.$route.path === window?.BS_CONFIG?.routers?.designUrl) {
+        prefix = 'design_chart_'
+      }
+
+      if (this.$route.path === window?.BS_CONFIG?.routers?.pageListUrl) {
+        prefix = 'management_chart_'
+      }
+      return prefix + this.config.code
+    }
+  },
+  created () {
+    this.plotList = [...this.plotList, ...getCustomPlots()]
+  },
+  watch: {
+    // 监听主题变化手动触发组件配置更新
+    'config.option.theme': {
+      handler (val) {
+        if (val) {
+          this.changeStyle(this.config, true)
+        }
+      }
+    }
+  },
+  mounted () {
+  },
+  beforeDestroy () {
+    if (this.chart) {
+      this.chart.destroy()
+    }
+  },
+  methods: {
+    ...mapMutations('bigScreen', ['changeChartConfig', 'changeActiveItemConfig', 'changeChartLoading']),
+    chartInit () {
+      let config = this.config
+      // key和code相等,说明是一进来刷新,调用list接口
+      if (this.config.code === this.config.key || this.isPreview) {
+        // 改变样式
+        config = this.changeStyle(config)
+        // 改变数据
+        config.loading = true
+        this.changeChartLoading(config)
+        this.changeDataByCode(config).then((res) => {
+          // 初始化图表
+          config.loading = false
+          this.changeChartLoading(config)
+          this.newChart(res)
+        }).catch(() => {
+        })
+      } else {
+        config.loading = true
+        this.changeChartLoading(config)
+        // 否则说明是更新,这里的更新只指更新数据(改变样式时是直接调取changeStyle方法),因为更新数据会改变key,调用chart接口
+        this.changeData(config).then((res) => {
+          config.loading = false
+          this.changeChartLoading(config)
+          // 初始化图表
+          this.newChart(res)
+        })
+      }
+    },
+    /**
+     * 构造chart
+     */
+    newChart (config) {
+      const chartDom = document.getElementById(this.chatId)
+      const myChart = echarts.init(chartDom)
+      config.option && myChart.setOption(config.option)
+    },
+    /**
+     * 注册事件
+     */
+    registerEvent () {
+      // 图表添加事件进行数据联动
+      let formData = {}
+      // eslint-disable-next-line no-unused-vars
+      this.chart.on('tooltip:change', (...args) => {
+        formData = {}
+        formData = cloneDeep(args[0].data.items[0].data)
+      })
+      // eslint-disable-next-line no-unused-vars
+      this.chart.on('plot:click', (...args) => {
+        this.linkage(formData)
+      })
+    },
+    // 将config.setting的配置转化为option里的配置,这里之所以将转化的方法提出来,是因为在改变维度指标和样式的时候都需要转化
+    transformSettingToOption (config, type) {
+      let option = null
+      config.setting.forEach(set => {
+        if (set.optionField) {
+          const optionField = set.optionField.split('.')
+          option = config.option
+          optionField.forEach((field, index) => {
+            if (index === optionField.length - 1) {
+              // 数据配置时,必须有值才更新
+              if ((set.tabName === type && type === 'data' && set.value) || (set.tabName === type && type === 'custom')) {
+                option[field] = set.value
+              }
+            } else {
+              option = option[field]
+            }
+          })
+        }
+      })
+      config.option = { ...config.option, ...option }
+      return config
+    },
+    dataFormatting (config, data) {
+      // 数据返回成功则赋值
+      if (data.success) {
+        data = data.data
+        config = this.transformSettingToOption(config, 'data')
+        // 获取到后端返回的数据,有则赋值
+        const option = config.option
+        const setting = config.setting
+        if (config.dataHandler) {
+          try {
+            // 此处函数处理data
+            eval(config.dataHandler)
+          } catch (e) {
+            console.error(e)
+          }
+        }
+        config.option = this.echartsOptionFormatting(config, data)
+      } else {
+        // 数据返回失败则赋前端的模拟数据
+        // config.option.data = this.plotList?.find(plot => plot.name === config.name)?.option?.data || config?.option?.data
+      }
+      return config
+    },
+    // 格式化echarts的配置
+    echartsOptionFormatting (config, data) {
+      const option = config.option
+      // 分组字段
+      const xField = config.setting.find(item => item.optionField === 'xField')?.value
+      const yField = config.setting.find(item => item.optionField === 'yField')?.value
+      const xData = [...new Set(data.map(item => item[xField]))]
+      const yData = data.map(item => item[yField])
+      const maxY = Math.max(...yData)
+      // 生成阴影柱子的值
+      const shadowData = Array.from({ length: xField.length }, () => maxY)
+      option.xAxis = option.xAxis.map(item => {
+        return {
+          ...item,
+          data: xData
+        }
+      })
+      const hasSeries = config.setting.find(item => item.optionField === 'seriesField' && item.value !== '')
+      // 判断是否存在分组字段
+      if (hasSeries) {
+        const seriesField = config.setting.find(item => item.optionField === 'seriesField')?.value
+        const seriesFieldList = [...new Set(data.map(item => item[seriesField]))]
+        option.series = []
+        for (const seriesFieldItem of seriesFieldList) {
+          const seriesData = (data.filter(item => item[seriesField] === seriesFieldItem))?.map(item => item[yField])
+          const seriesItem = [
+            {
+              name: seriesFieldItem + '柱子顶部',
+              type: 'pictorialBar',
+              tooltip: { show: false },
+              symbol: 'diamond',
+              symbolSize: [30, 10],
+              symbolOffset: ['-60%', -5],
+              symbolPosition: 'end',
+              z: 15,
+              zlevel: 2,
+              color: 'rgba(2, 175, 249,1)',
+              data: seriesData
+            },
+            {
+              name: seriesFieldItem,
+              type: 'bar',
+              barGap: '20%',
+              barWidth: 30,
+              itemStyle: {
+                normal: {
+                  color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+                    {
+                      offset: 0,
+                      color: '#115ba6'
+                    },
+                    {
+                      offset: 1,
+                      color: '#1db0dd'
+                    }
+                  ]),
+                  opacity: 0.8,
+                  shadowColor: 'rgba(0, 0, 0, 0.5)', // 阴影颜色
+                  shadowBlur: 0 // 阴影模糊值
+                }
+              },
+              label: {
+                show: false
+
+              },
+              zlevel: 2,
+              z: 12,
+              data: seriesData
+            },
+            {
+              name: seriesFieldItem + '柱子底部',
+              type: 'pictorialBar',
+              tooltip: { show: false },
+              symbol: 'diamond',
+              symbolSize: [30, 10],
+              symbolOffset: ['-60%', 5],
+              zlevel: 2,
+              z: 15,
+              color: 'rgb(2, 192, 255)',
+              data: seriesData
+            },
+            {
+              name: seriesFieldItem + '背景柱子',
+              type: 'bar',
+              tooltip: { show: false },
+              xAxisIndex: 1,
+              barGap: '20%',
+              data: shadowData,
+              zlevel: 1,
+              barWidth: 30,
+              itemStyle: {
+                normal: {
+                  color: 'rgba(9, 44, 76,.8)'
+                }
+              }
+            },
+            {
+              name: seriesFieldItem + '背景柱子顶部',
+              type: 'pictorialBar',
+              tooltip: { show: false },
+              symbol: 'diamond',
+              symbolSize: [30, 10],
+              symbolOffset: ['-60%', -5],
+              symbolPosition: 'end',
+              z: 15,
+              color: 'rgb(15, 69, 133)',
+              zlevel: 1,
+              data: shadowData
+            }
+          ]
+          option.series.push(...seriesItem)
+        }
+      } else {
+        option.series = option.series.map(item => {
+          return {
+            ...item,
+            data: yData
+          }
+        })
+      }
+      return option
+    },
+    // 组件的样式改变,返回改变后的config
+    changeStyle (config, isUpdateTheme) {
+      config = { ...this.config, ...config }
+      config = this.transformSettingToOption(config, 'custom')
+      // 这里定义了option和setting是为了保证在执行eval时,optionHandler、dataHandler里面可能会用到,
+      const option = config.option
+      const setting = config.setting
+      if (this.config.optionHandler) {
+        try {
+          // 此处函数处理config
+          eval(this.config.optionHandler)
+        } catch (e) {
+          console.error(e)
+        }
+      }
+      // 只有样式改变时更新主题配置,切换主题时不需要保存
+      if (!isUpdateTheme) {
+        config.theme = settingToTheme(_.cloneDeep(config), this.customTheme)
+      }
+      this.changeChartConfig(config)
+      if (config.code === this.activeCode) {
+        this.changeActiveItemConfig(config)
+      }
+      if (this.chart) {
+        this.chart.update(config.option)
+      }
+      return config
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+@import '../assets/style/echartStyle';
+.light-theme{
+  background-color: #FFFFFF;
+  color: #000000;
+}
+.auto-theme{
+  background-color: transparent;
+}
+
+</style>

+ 23 - 0
data-room-ui/packages/EchartsRender/settingConfig.js

@@ -0,0 +1,23 @@
+import { commonConfig, displayOption } from 'data-room-ui/js/config'
+export const settingConfig = {
+  displayOption: { ...displayOption }
+}
+const customConfig = {
+  root: {
+    contribution: false
+  },
+  customize: {
+    theme: 'dark', // 'light'、'dark'
+    colorScheme: [],
+    legend: null,
+    legendColor: '', // 图例颜色
+    xaxisColor: '', // x轴标签颜色
+    xaxisFontSize: 14, // x轴标签字体大小
+    yaxisColor: '', // y轴标签颜色
+    yaxisFontSize: 14, // y轴标签字体大小
+    labelColor: ''// 标签颜色
+  }
+}
+export const dataConfig = {
+  ...commonConfig(customConfig)
+}

+ 2 - 1
data-room-ui/packages/G2Plots/plotList.js

@@ -43,6 +43,7 @@ function getPlotList (files) {
       y: 0,
       type: 'customComponent',
       chartType: config.chartType,
+      loading: false,
       option: {
         ...config.option,
         ...cloneDeep(settingConfig)
@@ -88,5 +89,5 @@ export function getCustomPlots () {
   return list
 }
 
-const plots = [...plotList, ...customPlots, mapData,FlyMapData]
+const plots = [...plotList, ...customPlots, mapData, FlyMapData]
 export default plots

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

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

+ 23 - 20
data-room-ui/packages/MapDataManagement/src/AddForm.vue

@@ -37,20 +37,35 @@
           />
         </el-form-item>
         <el-form-item
-          label="地图编码"
+          label="地图标识"
           prop="mapCode"
         >
+          <template slot="label">
+            <span>地图标识</span>
+            <el-tooltip
+              v-if="mapForm.parentId !== '0'"
+              class="item"
+              effect="light"
+              content="子级的地图标识解析自上级地图的子区块数据"
+              placement="top"
+            >
+              <i
+                class="el-icon-warning-outline"
+                style="color: #E3C98C;margin-left: 6px;font-size:14px"
+              />
+            </el-tooltip>
+          </template>
           <el-input
             v-if="mapForm.parentId === '0'"
             v-model="mapForm.mapCode"
             class="bs-el-input"
-            placeholder="请输入地图编码"
+            placeholder="请输入地图标识"
           />
           <el-select
             v-else
             v-model="mapForm.mapCode"
             class="bs-el-select"
-            placeholder="请选择地图编码"
+            placeholder="请选择地图标识"
             popper-class="bs-el-select"
           >
             <el-option
@@ -84,17 +99,6 @@
             />
           </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上传"
         >
@@ -165,8 +169,8 @@ export default {
   },
   computed: {
     autoParseNextLevelShow () {
-      // geoJson 不为空,且支持下钻
-      return !this.isEmpty(this.mapForm.geoJson) && this.mapForm.enableDown === 1
+      // geoJson 不为空
+      return !this.isEmpty(this.mapForm.geoJson)
     }
   },
   data () {
@@ -174,13 +178,14 @@ export default {
       if (this.mapForm.parentId !== '0') {
         // 不需要校验
         callback()
+        return
       }
       repeatCheck({
         parentId: this.mapForm.parentId,
         mapCode: value
       }).then(res => {
         if (res) {
-          callback(new Error('地图编码已存在'))
+          callback(new Error('地图标识已存在'))
         } else {
           callback()
         }
@@ -197,14 +202,13 @@ export default {
         mapCode: '',
         name: '',
         level: 0,
-        enableDown: 0,
         geoJson: {},
         uploadedGeoJson: 0,
         autoParseNextLevel: 0
       },
       rules: {
         mapCode: [
-          { required: true, message: '请选择地图编码', trigger: 'blur' },
+          { required: true, message: '请选择地图标识', trigger: 'blur' },
           { validator: validateCode, trigger: 'blur' }
         ],
         name: [
@@ -231,7 +235,6 @@ export default {
         mapCode: `map-${new Date().getTime()}`,
         name: '',
         level: 0,
-        enableDown: 0,
         geoJson: {},
         uploadedGeoJson: 0,
         autoParseNextLevel: 0

+ 59 - 19
data-room-ui/packages/MapDataManagement/src/EditForm.vue

@@ -37,20 +37,49 @@
           />
         </el-form-item>
         <el-form-item
-          label="地图编码"
+          label="地图标识"
           prop="mapCode"
         >
+          <template slot="label">
+            <span>地图标识</span>
+            <el-tooltip
+              v-if="mapForm.parentId !== '0'"
+              class="item"
+              effect="light"
+              content="地图标识取自上级地图JSON数据中的properties.name值,用于在地图中显示地区名称"
+              placement="top"
+            >
+              <i
+                class="el-icon-warning-outline"
+                style="color: #E3C98C;margin-left: 6px;font-size:14px"
+              />
+            </el-tooltip>
+          </template>
           <el-input
             v-model="mapForm.mapCode"
             class="bs-el-input"
-            disabled
-            placeholder="请输入地图编码"
+            placeholder="请输入地图标识"
           />
         </el-form-item>
         <el-form-item
           label="地图级别"
           prop="level"
         >
+          <template slot="label">
+            <span>地图级别</span>
+            <el-tooltip
+              v-if="mapForm.parentId !== '0'"
+              class="item"
+              effect="light"
+              content="子级地图的级别根据上级地图自动递增,不可修改"
+              placement="top"
+            >
+              <i
+                class="el-icon-warning-outline"
+                style="color: #E3C98C;margin-left: 6px;font-size:14px"
+              />
+            </el-tooltip>
+          </template>
           <el-select
             v-model="mapForm.level"
             disabled
@@ -66,17 +95,6 @@
             />
           </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"
         >
@@ -140,7 +158,7 @@
 <script>
 import _ from 'lodash'
 import vueJsonViewer from 'vue-json-viewer'
-import {mapUpdate} from 'data-room-ui/js/utils/mapDataService'
+import {mapUpdate, getMapChildFromGeoJson} from 'data-room-ui/js/utils/mapDataService'
 
 export default {
   name: "EditForm",
@@ -149,11 +167,33 @@ export default {
   },
   computed: {
     autoParseNextLevelShow() {
-      // geoJson 不为空,且支持下钻,且未上传过(说明是刚上传的)
-      return !this.isWhitespace(this.mapForm.geoJson) && this.mapForm.enableDown === 1 && this.mapForm.uploadedGeoJson === 0
+      // geoJson 不为空,且未上传过(说明是刚上传的)
+      return !this.isWhitespace(this.mapForm.geoJson) && this.mapForm.uploadedGeoJson === 0
     }
   },
   data() {
+    const validateCode = (rule, value, callback) => {
+      console.log(this.mapForm.parentId)
+      if (this.mapForm.parentId === '0' || this.mapForm.parentId === 0) {
+        // 不需要校验
+        callback()
+        return
+      }
+      getMapChildFromGeoJson(this.mapForm.parentId).then(children => {
+        let repeat = false
+        children.forEach(child => {
+          if (child.exist && child.name === value) {
+            repeat = true
+          }
+        })
+        if (repeat) {
+          callback(new Error('地图标识已存在'))
+        } else {
+          callback()
+        }
+      })
+
+    }
     return {
       mapFormVisible: false,
       geoJsonVisible: false,
@@ -165,14 +205,14 @@ export default {
         mapCode: '',
         name: '',
         level: 0,
-        enableDown: 0,
         geoJson: '',
         uploadedGeoJson: 0,
         autoParseNextLevel: 0
       },
       rules: {
         mapCode: [
-          {required: true, message: '请输入地图编码', trigger: 'blur'}
+          {required: true, message: '请输入地图标识', trigger: 'blur'},
+          { validator: validateCode, trigger: 'blur' }
         ],
         name: [
           {required: true, message: '请输入地图名称', trigger: 'blur'}

+ 37 - 16
data-room-ui/packages/MapDataManagement/src/index.vue

@@ -11,7 +11,7 @@
             class="bs-el-input"
             clearable
             maxlength="200"
-            placeholder="请输入地图名称或编码"
+            placeholder="请输入地图名称或标识"
           />
         </el-form-item>
         <el-form-item class="filter-item">
@@ -73,7 +73,7 @@
           />
           <el-table-column
             align="center"
-            label="编码"
+            label="标识"
             prop="mapCode"
             show-overflow-tooltip
           />
@@ -91,17 +91,6 @@
               <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"
@@ -230,7 +219,6 @@ export default {
       searchForm: {
         searchKey: '',
         level: null,
-        enableDown: null,
         uploadedGeoJson: null,
         parentId: '0'
       },
@@ -256,12 +244,14 @@ export default {
           value: 4
         }
       ],
-      mapList: []
+      mapList: [],
+      // 提示过了
+      tipped: false
+
     }
   },
   mounted() {
     this.init()
-
   },
   methods: {
     init() {
@@ -270,6 +260,9 @@ export default {
       mapList(this.searchForm).then(res => {
         this.mapList = res
         this.searchLoading = false
+        if (!this.tipped && this.mapList.length === 0) {
+          this.tip()
+        }
       }).catch(err => {
         this.searchLoading = false
       })
@@ -322,6 +315,18 @@ export default {
       }).catch(() => {
       })
     },
+    tip() {
+      // 超链接跳转至
+      const htmlStr = `<span>大屏设计器提供了全国省市区县的地图数据,<a href="https://www.yuque.com/chuinixiongkou/bigscreen/kdrm8g3c8zfgaaq6#xjE8w" style="color: #00a0e9"  target="_blank">点击查看</a></span>`
+      this.$notify({
+        title: '推荐',
+        dangerouslyUseHTMLString: true,
+        message: htmlStr,
+        customClass: 'ds-el-notify',
+        type: 'warning',
+        duration: 5000
+      })
+    },
     deleteMapCascade(map) {
       this.$confirm('该地图存在子级,是否直接删除该地图以及其所有子级?', '提示', {
         confirmButtonText: '确定',
@@ -425,3 +430,19 @@ export default {
   height: 150px;
 }
 </style>
+<style lang="scss">
+//修改notify的样式
+.ds-el-notify {
+  background-color: var(--bs-el-background-1)!important;
+  border: var(--bs-el-border)!important;
+  .el-notification__title{
+    color: #fff!important;
+  }
+  .el-notification__content{
+    color: #fff!important;
+  }
+  .el-notification__closeBtn{
+    color: #fff!important;
+  }
+}
+</style>

+ 13 - 1
data-room-ui/packages/PlotRender/index.vue

@@ -1,5 +1,8 @@
 <template>
   <div
+    v-loading="config.loading"
+    element-loading-text="图表加载中"
+    :element-loading-background="loadingBackground"
     style="width: 100%;height: 100%"
     class="bs-design-wrap bs-custom-component"
     :class="{'light-theme':customTheme === 'light','auto-theme':customTheme !=='light'}"
@@ -8,6 +11,7 @@
       :id="chatId"
       style="width: 100%;height: 100%"
     />
+    <!--    <span style="color:#ffffff">{{config.option.data}}</span>-->
   </div>
 </template>
 <script>
@@ -80,7 +84,7 @@ export default {
     }
   },
   methods: {
-    ...mapMutations('bigScreen', ['changeChartConfig', 'changeActiveItemConfig']),
+    ...mapMutations('bigScreen', ['changeChartConfig', 'changeActiveItemConfig', 'changeChartLoading']),
     chartInit () {
       let config = this.config
       // key和code相等,说明是一进来刷新,调用list接口
@@ -88,14 +92,22 @@ export default {
         // 改变样式
         config = this.changeStyle(config)
         // 改变数据
+        config.loading = true
+        this.changeChartLoading(config)
         this.changeDataByCode(config).then((res) => {
           // 初始化图表
+          config.loading = false
+          this.changeChartLoading(config)
           this.newChart(res)
         }).catch(() => {
         })
       } else {
+        config.loading = true
+        this.changeChartLoading(config)
         // 否则说明是更新,这里的更新只指更新数据(改变样式时是直接调取changeStyle方法),因为更新数据会改变key,调用chart接口
         this.changeData(config).then((res) => {
+          config.loading = false
+          this.changeChartLoading(config)
           // 初始化图表
           this.newChart(res)
         })

+ 10 - 6
data-room-ui/packages/Render/RenderCard.vue

@@ -7,15 +7,18 @@
 <template>
   <div class="content">
     <component
-      v-if="config.border&&config.border.type"
       :is="resolveComponentType(config.border.type)"
+      v-if="config.border&&config.border.type"
       :id="`border${config.code}`"
       :ref="`border${config.code}`"
       :key="`border${config.key}`"
       :config="config"
     />
-    <div class="render-item-wrap" :style="`height:calc(100% - ${(config.border&&config.border.type)?config.border.titleHeight:0}px)`">
-       <component
+    <div
+      class="render-item-wrap"
+      :style="`height:calc(100% - ${(config.border&&config.border.type)?config.border.titleHeight:0}px)`"
+    >
+      <component
         :is="resolveComponentType(config.type)"
         :id="`${config.code}`"
         :ref="config.code"
@@ -25,7 +28,6 @@
       />
     </div>
   </div>
-
 </template>
 <script>
 // import commonMixins from 'data-room-ui/js/mixins/commonMixins'
@@ -34,6 +36,7 @@ import { resolveComponentType } from 'data-room-ui/js/utils'
 import pcComponent from 'data-room-ui/js/utils/componentImport'
 import { dataInit, destroyedEvent } from 'data-room-ui/js/utils/eventBus'
 import CustomComponent from '../PlotRender/index.vue'
+import EchartsComponent from '../EchartsRender/index.vue'
 import Svgs from '../Svgs/index.vue'
 import RemoteComponent from 'data-room-ui/RemoteComponents/index.vue'
 import cloneDeep from 'lodash/cloneDeep'
@@ -50,7 +53,8 @@ export default {
     ...components,
     CustomComponent,
     Svgs,
-    RemoteComponent
+    RemoteComponent,
+    EchartsComponent
   },
   props: {
     // 卡片的属性
@@ -65,7 +69,7 @@ export default {
   },
   data () {
     return {
-      height:0
+      height: 0
     }
   },
   computed: {

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

@@ -157,6 +157,50 @@
   }
 }
 
+// cascader
+.bs-el-cascader {
+  border-color: var(--bs-el-border) !important;
+  background-color: var(--bs-el-background-1) !important;
+  background: var(--bs-el-background-1) !important;
+
+  .el-cascader-menu {
+    // 选项的未选中时颜色
+    color: var(--bs-el-text) !important;
+    border: var(--bs-el-border) !important;
+  }
+
+  .el-cascader-node.hover,
+  .el-cascader-node:hover {
+    // hover状态的选项颜色和背景色
+    color: var(--bs-el-color-primary) !important;
+    background-color: var(--bs-el-background-3) !important;
+  }
+
+  .in-active-path {
+    // 选中项的路径项的文本颜色和背景颜色
+    color: var(--bs-el-color-primary) !important;
+    background-color: var(--bs-el-background-3) !important;
+  }
+
+  .popper__arrow {
+    bottom: 0 !important;
+    border-top-color: var(--bs-el-background-1) !important;
+    border-bottom-color: var(--bs-el-background-1) !important;
+
+    &:after {
+      bottom: 0 !important;
+      border-top-color: var(--bs-el-background-1) !important;
+      border-bottom-color: var(--bs-el-background-1) !important;
+    }
+  }
+
+  // 弹出层,有滚动条下最后一项显示不全
+  .el-cascader-menu__wrap {
+    margin-bottom: 0px !important;
+  }
+
+}
+
 // select
 .bs-el-select {
   border-color: var(--bs-el-border) !important;

+ 9 - 0
data-room-ui/packages/js/mixins/commonMixins.js

@@ -25,6 +25,10 @@ export default {
       customTheme: state => state.bigScreen.pageInfo.pageConfig.customTheme,
       activeCode: state => state.bigScreen.activeCode
     }),
+    // 组件数据加载时的背景颜色
+    loadingBackground () {
+      return this.customTheme === 'light' ? '#ffffff' : '#151A26'
+    },
     isPreview () {
       return (this.$route.path === window?.BS_CONFIG?.routers?.previewUrl) || (this.$route.path === '/big-screen/preview')
     }
@@ -70,6 +74,7 @@ export default {
         size = config.option.pagination.pageSize
       }
       return new Promise((resolve, reject) => {
+        config.loading = true
         getChatInfo({
           // innerChartCode: this.pageCode ? config.code : undefined,
           chartCode: config.code,
@@ -78,6 +83,7 @@ export default {
           size: size,
           type: config.type
         }).then(async (res) => {
+          config.loading = false
           let _res = cloneDeep(res)
           // 如果是http数据集的前端代理,则需要调封装的axios请求
           if (res.executionByFrontend) {
@@ -139,7 +145,9 @@ export default {
         filterList: filterList || this.filterList
       }
       return new Promise((resolve, reject) => {
+        config.loading = true
         getUpdateChartInfo(params).then(async (res) => {
+          config.loading = false
           let _res = cloneDeep(res)
           // 如果是http数据集的前端代理,则需要调封装的axios请求
           if (res.executionByFrontend) {
@@ -177,6 +185,7 @@ export default {
         }).catch(err => {
           console.info(err)
         }).finally(() => {
+          config.loading = false
           resolve(config)
         })
       })

+ 9 - 0
data-room-ui/packages/js/store/mutations.js

@@ -84,6 +84,15 @@ export default {
     // 改变loading状态
     state.pageLoading = booleanValue
   },
+  // 改变当前组件的加载状态
+  changeChartLoading (state, itemConfig) {
+    // 改变loading状态
+    state.pageInfo.chartList.forEach((chart, index) => {
+      if (chart.code === itemConfig.code) {
+        chart.loading = itemConfig.loading
+      }
+    })
+  },
   // 改变当前组件配置
   changeChartConfig (state, itemConfig) {
     // 如果存在parentCode的组件,则是tab中的组件

+ 1 - 1
data-room-ui/packages/js/utils/mapDataService.js

@@ -37,7 +37,7 @@ const mapDelete = (id = '-1') => Vue.prototype.$dataRoomAxios.post(`/bigScreen/m
  * 级联删除地图
  * @param id
  */
-const mapCascadeDelete = (id = '-1') => Vue.prototype.$dataRoomAxios.post(`/bigScreen/map/cascadeDelete/${id}`)
+const mapCascadeDelete = (id = '-1') => Vue.prototype.$dataRoomAxios.post(`/bigScreen/map/cascadingDelete/${id}`)
 
 /**
  * 根据父编码解析父级json中的子级