Prechádzať zdrojové kódy

Merge branch 'master' of http://47.103.92.60:3003/skyversation/qp_onemap_ui.git

wdq 4 týždňov pred
rodič
commit
94acb283c1

+ 47 - 68
package-lock.json

@@ -9,15 +9,16 @@
       "version": "0.1.0",
       "dependencies": {
         "@element-plus/icons-vue": "^2.0.10",
+        "@vue-office/excel": "^1.7.14",
         "axios": "^0.27.2",
         "echarts": "^5.6.0",
         "element-plus": "^2.2.15",
         "moment": "^2.30.1",
         "vue": "^3.2.13",
+        "vue-demi": "^0.14.10",
         "vue-json-editor": "^1.4.3",
         "vue-json-viewer": "^3.0.4",
         "vue-router": "^4.0.3",
-        "vue3-lottie": "^3.3.1",
         "vuex": "^4.0.0"
       },
       "devDependencies": {
@@ -557,6 +558,23 @@
         "@types/node": "*"
       }
     },
+    "node_modules/@vue-office/excel": {
+      "version": "1.7.14",
+      "resolved": "https://registry.npmjs.org/@vue-office/excel/-/excel-1.7.14.tgz",
+      "integrity": "sha512-pVUgt+emDQUnW7q22CfnQ+jl43mM/7IFwYzOg7lwOwPEbiVB4K4qEQf+y/bc4xGXz75w1/e3Kz3G6wAafmFBFg==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "peerDependencies": {
+        "@vue/composition-api": "^1.7.1",
+        "vue": "^2.0.0 || >=3.0.0",
+        "vue-demi": "^0.14.6"
+      },
+      "peerDependenciesMeta": {
+        "@vue/composition-api": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/@vue/cli-overlay": {
       "version": "5.0.9",
       "dev": true,
@@ -945,30 +963,6 @@
         "url": "https://github.com/sponsors/antfu"
       }
     },
-    "node_modules/@vueuse/core/node_modules/vue-demi": {
-      "version": "0.14.10",
-      "hasInstallScript": true,
-      "license": "MIT",
-      "bin": {
-        "vue-demi-fix": "bin/vue-demi-fix.js",
-        "vue-demi-switch": "bin/vue-demi-switch.js"
-      },
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/antfu"
-      },
-      "peerDependencies": {
-        "@vue/composition-api": "^1.0.0-rc.1",
-        "vue": "^3.0.0-0 || ^2.6.0"
-      },
-      "peerDependenciesMeta": {
-        "@vue/composition-api": {
-          "optional": true
-        }
-      }
-    },
     "node_modules/@vueuse/metadata": {
       "version": "9.13.0",
       "license": "MIT",
@@ -986,30 +980,6 @@
         "url": "https://github.com/sponsors/antfu"
       }
     },
-    "node_modules/@vueuse/shared/node_modules/vue-demi": {
-      "version": "0.14.10",
-      "hasInstallScript": true,
-      "license": "MIT",
-      "bin": {
-        "vue-demi-fix": "bin/vue-demi-fix.js",
-        "vue-demi-switch": "bin/vue-demi-switch.js"
-      },
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/antfu"
-      },
-      "peerDependencies": {
-        "@vue/composition-api": "^1.0.0-rc.1",
-        "vue": "^3.0.0-0 || ^2.6.0"
-      },
-      "peerDependenciesMeta": {
-        "@vue/composition-api": {
-          "optional": true
-        }
-      }
-    },
     "node_modules/@webassemblyjs/ast": {
       "version": "1.14.1",
       "dev": true,
@@ -3064,6 +3034,7 @@
     },
     "node_modules/fast-deep-equal": {
       "version": "3.1.3",
+      "dev": true,
       "license": "MIT"
     },
     "node_modules/fast-glob": {
@@ -4079,6 +4050,7 @@
     },
     "node_modules/klona": {
       "version": "2.0.6",
+      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">= 8"
@@ -4367,10 +4339,6 @@
         "node": ">=4"
       }
     },
-    "node_modules/lottie-web": {
-      "version": "5.12.2",
-      "license": "MIT"
-    },
     "node_modules/lower-case": {
       "version": "2.0.2",
       "dev": true,
@@ -7043,6 +7011,32 @@
         }
       }
     },
+    "node_modules/vue-demi": {
+      "version": "0.14.10",
+      "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
+      "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "bin": {
+        "vue-demi-fix": "bin/vue-demi-fix.js",
+        "vue-demi-switch": "bin/vue-demi-switch.js"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      },
+      "peerDependencies": {
+        "@vue/composition-api": "^1.0.0-rc.1",
+        "vue": "^3.0.0-0 || ^2.6.0"
+      },
+      "peerDependenciesMeta": {
+        "@vue/composition-api": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/vue-hot-reload-api": {
       "version": "2.3.4",
       "dev": true,
@@ -7163,21 +7157,6 @@
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/vue3-lottie": {
-      "version": "3.3.1",
-      "license": "MIT",
-      "dependencies": {
-        "fast-deep-equal": "^3.1.3",
-        "klona": "^2.0.6",
-        "lottie-web": "5.12.2"
-      },
-      "engines": {
-        "node": ">=12"
-      },
-      "peerDependencies": {
-        "vue": "^3.2"
-      }
-    },
     "node_modules/vuex": {
       "version": "4.1.0",
       "license": "MIT",

+ 2 - 1
package.json

@@ -8,15 +8,16 @@
   },
   "dependencies": {
     "@element-plus/icons-vue": "^2.0.10",
+    "@vue-office/excel": "^1.7.14",
     "axios": "^0.27.2",
     "echarts": "^5.6.0",
     "element-plus": "^2.2.15",
     "moment": "^2.30.1",
     "vue": "^3.2.13",
+    "vue-demi": "^0.14.10",
     "vue-json-editor": "^1.4.3",
     "vue-json-viewer": "^3.0.4",
     "vue-router": "^4.0.3",
-    "vue3-lottie": "^3.3.1",
     "vuex": "^4.0.0"
   },
   "devDependencies": {

+ 312 - 0
src/api/count.js

@@ -0,0 +1,312 @@
+import {
+    postform,get
+} from '../utils/request'
+
+const dmsPath = systemConfig.dmsDataProxy 
+
+const oauthPath = systemConfig.oauthServiceUrl
+
+const multiSearch = dmsPath + "/content/multipleFormsOfJointInvestigation"
+
+const oauthCountUser = oauthPath + "/user/countUser"
+
+const preCountDmsId = 1652
+
+
+export function countUnitUse(start, end) {
+    let data = {
+        "columnId": preCountDmsId,
+        "conditionsList": JSON.stringify([
+            ...notNullAndEmptyCheckers("precount", "c_path_comment"),
+            ...notNullAndEmptyCheckers("precount", "c_application"),
+            ...notNullAndEmptyCheckers("precount", "c_unit"),
+            ...timeCheckers(start, end)
+        ]),
+        "autoSelectItem": false,
+        "page": 1,
+        "pageSize": 2000,
+        "columnAlias": "precount",
+        "selectItem": JSON.stringify([
+            {
+                "table": "precount",
+                "column": "c_unit",
+                "alias": "name"
+            },
+            {
+                "table": "precount",
+                "column": "c_count",
+                "function": "sum",
+                "alias": "count"
+            }
+        ]),
+        "groupBy": JSON.stringify([
+            {
+                "table": "precount",
+                "column": "c_unit"
+            }
+        ])
+    }
+    return resolveDmsMultiTableResult(postform(multiSearch, data));
+}
+
+export function countAppUseByUnitName(start, end, unitName) {
+    let data = {
+        "columnId": preCountDmsId,
+        "conditionsList": JSON.stringify([
+            ...notNullAndEmptyCheckers("precount", "c_path_comment"),
+            ...notNullAndEmptyCheckers("precount", "c_application"),
+            ...timeCheckers(start, end),
+            {
+                "columnId": "precount",
+                "columnName": "c_unit",
+                "condition": "=",
+                "value": unitName
+            },
+        ]),
+        "autoSelectItem": false,
+        "page": 1,
+        "pageSize": 2000,
+        "columnAlias": "precount",
+        "selectItem": JSON.stringify([
+            {
+                "table": "precount",
+                "column": "c_application",
+                "alias": "name"
+            },
+            {
+                "table": "precount",
+                "column": "c_count",
+                "function": "sum",
+                "alias": "count"
+            }
+        ]),
+        "groupBy": JSON.stringify([
+            {
+                "table": "precount",
+                "column": "c_application"
+            }
+        ])
+    }
+
+    return resolveDmsMultiTableResult(postform(multiSearch, data));
+}
+
+export function countServiceUseByApp(start, end, app) {
+    let data = {
+        "columnId": preCountDmsId,
+        "conditionsList": JSON.stringify([
+            ...notNullAndEmptyCheckers("precount", "c_path_comment"),
+            ...timeCheckers(start, end),
+            {
+                "columnId": "precount",
+                "columnName": "c_application",
+                "condition": "=",
+                "value": app
+            },
+        ]),
+        "autoSelectItem": false,
+        "page": 1,
+        "pageSize": 2000,
+        "columnAlias": "precount",
+        "selectItem": JSON.stringify([
+            {
+                "table": "precount",
+                "column": "c_path_comment",
+                "alias": "name"
+            },
+            {
+                "table": "precount",
+                "column": "c_count",
+                "function": "sum",
+                "alias": "count"
+            }
+        ]),
+        "groupBy": JSON.stringify([
+            {
+                "table": "precount",
+                "column": "c_path_comment"
+            }
+        ])
+    }
+
+    return resolveDmsMultiTableResult(postform(multiSearch, data));
+}
+
+export function topService(num) {
+    let data = {
+        "columnId": preCountDmsId,
+        "autoSelectItem": false,
+        "page": 1,
+        "pageSize": num,
+        "columnAlias": "precount",
+        "orderBy": "_select_list,count,desc",
+        "conditionsList": JSON.stringify([
+            ...notNullAndEmptyCheckers("precount", "c_path_comment")
+        ]),
+        "selectItem": JSON.stringify([
+            {
+                "table": "precount",
+                "column": "c_path_comment",
+                "alias": "name"
+            },
+            {
+                "table": "precount",
+                "column": "c_count",
+                "function": "sum",
+                "alias": "count"
+            }
+        ]),
+        "groupBy": JSON.stringify([
+            {
+                "table": "precount",
+                "column": "c_path_comment"
+            }
+        ])
+    }
+    return resolveDmsMultiTableResult(postform(multiSearch, data));
+}
+
+export function topUnit(num) {
+    let data = {
+        "columnId": preCountDmsId,
+        "autoSelectItem": false,
+        "page": 1,
+        "pageSize": num,
+        "columnAlias": "precount",
+        "orderBy": "_select_list,count,desc",
+        "conditionsList": JSON.stringify([
+            ...notNullAndEmptyCheckers("precount", "c_unit")
+        ]),
+        "selectItem": JSON.stringify([
+            {
+                "table": "precount",
+                "column": "c_unit",
+                "alias": "name"
+            },
+            {
+                "table": "precount",
+                "column": "c_count",
+                "function": "sum",
+                "alias": "count"
+            }
+        ]),
+        "groupBy": JSON.stringify([
+            {
+                "table": "precount",
+                "column": "c_unit"
+            }
+        ])
+    }
+    return resolveDmsMultiTableResult(postform(multiSearch, data));
+}
+
+export function totalCount(start, end) {
+    let data = {
+        "columnId": preCountDmsId,
+        "autoSelectItem": false,
+        "page": 1,
+        "pageSize": 1,
+        "columnAlias": "precount",
+        "selectItem": JSON.stringify([
+            {
+                "table": "precount",
+                "column": "c_count",
+                "function": "sum",
+                "alias": "count"
+            }
+        ]),
+        "conditionsList": JSON.stringify([
+            ...timeCheckers(start, end),
+        ]),
+    }
+
+    return resolveDmsMultiTableResult(postform(multiSearch, data));
+}
+export function totalCountGroupByTime(start, end) {
+    let data = {
+        "columnId": preCountDmsId,
+        "autoSelectItem": false,
+        "page": 1,
+        "pageSize": 10000,
+        "columnAlias": "precount",
+        "selectItem": JSON.stringify([
+            {
+                "table": "precount",
+                "column": "c_count",
+                "function": "sum",
+                "alias": "count"
+            }, {
+                "table": "precount",
+                "column": "c_date",
+                "alias":"time"
+            }
+        ]),
+        "groupBy": JSON.stringify([
+            {
+                "table": "precount",
+                "column": "c_date"
+            }
+        ]),
+        "conditionsList": JSON.stringify([
+            ...timeCheckers(start, end),
+        ]),
+    }
+
+    return resolveDmsMultiTableResult(postform(multiSearch, data));
+}
+export function countUserData(){
+        return resolveoauthResult(get(oauthCountUser))
+}
+function timeCheckers(start, end) {
+    let output = []
+    if (start != null) {
+        output.push({
+            "columnId": "precount",
+            "columnName": "c_date",
+            "condition": ">=",
+            "value": start.toLocaleString('zh-CN')
+
+        })
+    }
+    if (end != null) {
+        output.push(
+            {
+                "columnId": "precount",
+                "columnName": "c_date",
+                "condition": "<",
+                "value": end.toLocaleString('zh-CN')
+            })
+    }
+    return output
+}
+function notNullAndEmptyCheckers(targetTable, target) {
+    return [{
+        "columnId": targetTable,
+        "columnName": target,
+        "condition": "is not null"
+    },
+    {
+        "columnId": targetTable,
+        "columnName": target,
+        "condition": "!=",
+        "value": ""
+    }]
+}
+async function resolveDmsMultiTableResult(result) {
+    result = await result;
+    if (result.code == 200) {
+        return result.content.data
+    } else if (result.code == 202) {
+        return [];
+    } else {
+        return null;
+    }
+}
+async function resolveoauthResult(result) {
+    result = await result;
+    if (result.code == 200) {
+        return result.content
+    } else {
+        return null;
+    }
+}

+ 84 - 0
src/api/rwgl.js

@@ -0,0 +1,84 @@
+import {
+    postform
+} from '../utils/request'
+import content from './content'
+
+
+const dmsPath = systemConfig.dmsDataProxy
+const multiSearch = dmsPath + "/content/multipleFormsOfJointInvestigation"
+
+const taskDmsId = 1662
+
+export function getTaskStatus() {
+    return resolveResult(content.getCategoryDetail({
+        type: 0,
+        cName: "task_status",
+    }))
+}
+
+export function getTasks(page, pageSize, name, status) {
+    let data = {
+        "columnId": taskDmsId,
+        "autoSelectItem": true,
+        "page": page,
+        "pageSize": pageSize,
+        "columnAlias": "main",
+        "orderBy":"main,c_start_time,desc",
+        "conditionsList": JSON.stringify([
+            ...getNameChecker(name),
+            ...getStatusChecker(status)
+        ]),
+    }
+
+    return resolveDmsMultiTableResult(postform(multiSearch, data));
+}
+
+function getNameChecker(name) {
+    if (name == null||name=="") {
+        return []
+    }
+    return [{
+        "columnId": "main",
+        "columnName": "c_name",
+        "condition": "like",
+        "value": `%${name}%`
+    }]
+}
+function getStatusChecker(status) {
+    
+    if (status == null || status.length == 0) {
+        return []
+    }
+    let output = ["or"]
+    for (let i = 0; i < status.length; i++) {
+        const e = status[i];
+        
+        output.push({
+            "columnId": "main",
+            "columnName": "c_state",
+            "condition": "=",
+            "value": `${e}`
+        })
+    }
+    return [output]
+}
+
+async function resolveResult(result) {
+    result = await result;
+    if (result.code == 200) {
+        return result.content
+    } else {
+        return null;
+    }
+}
+
+async function resolveDmsMultiTableResult(result) {
+    result = await result;
+    if (result.code == 200) {
+        return result.content
+    } else if (result.code == 202) {
+        return [];
+    } else {
+        return null;
+    }
+}

+ 2 - 2
src/components/AppVue/Header.vue

@@ -19,7 +19,7 @@ export default {
       menuList: [
         { index: 1, label: "首页", isActive: true },
         { index: 2, label: "时空数据管理", isActive: false },
-        { index: 3, label: "二维GIS引擎", isActive: false },
+        // { index: 3, label: "二维GIS引擎", isActive: false },
         { index: 4, label: "时空门户", isActive: false },
         { index: 5, label: "微功能", isActive: false },
         { index: 6, label: "应用管理", isActive: false },
@@ -47,7 +47,7 @@ export default {
           break;
         case 3:
           // this.$router.push("/application");
-          this.$router.push("/skmh/scene");
+          // this.$router.push("/skmh/scene");
           break;
         case 4:
           // this.$router.push("/function");

+ 389 - 0
src/components/filePreview/viewPreview.vue

@@ -0,0 +1,389 @@
+<template>
+    <div class="container">
+        <div class="map-container" v-if="isShow == 1">
+            <div id="skysceneryContainer"></div>
+            <div class="infoDialog" v-show="infoDialogShow">
+                <div class="close" @click="closeWin">×</div>
+                <div class="content" v-if="nowPoint != null">
+                    <el-scrollbar>
+                        <div class="item" v-for="info in nowPointInfo" :key="info">{{info.key}}: {{ info.value }}</div>
+                    </el-scrollbar>
+                </div>
+            </div>
+        </div>
+        <div class="page-content" v-if="isShow == 2">
+            <vue-office-excel
+                :src="excelUrl"
+                @rendered="onRendered"
+                @error="onError"
+                />
+        </div>
+         <div class="page-content" v-if="isShow == 3">
+            <div style="height: 100%; display: flex; align-items: center; justify-content: center;">
+                <el-empty class="custom-empty" description="不支持的文件类型" />
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+import { ElMessage } from 'element-plus'
+import VueOfficeExcel from '@vue-office/excel'
+import '@vue-office/excel/lib/index.css'
+import { toRaw } from "vue";
+export default {
+    name: "",
+    components: {
+       VueOfficeExcel
+    },
+    data() {
+        return {
+            dataJson:[],
+            pointImg:require('@static/images/point.png'),
+            geometryArr:[],
+            mapHandle:null,
+            infoDialogShow:false,
+            nowPoint: null,
+            nowPointInfo:null,
+            excelUrl:'',
+            loading:false,
+            error:'',
+            isShow:1,
+        };
+    },
+    created() {
+        
+    },
+    mounted() {
+        const queryParams = new URLSearchParams(window.location.search);
+        let url = queryParams.get('url');
+        console.log(queryParams.get('url')); // 输出: value1
+        if(url){
+            let arr = url.split('.');
+            let type = arr[arr.length-1];
+            if(type == 'json' || type == 'geojson'){
+                let param = {url:url}
+                this.isShow = 1
+                window.SkySceneryConfig = {};
+                window.loadScripts([systemConfig.scriptMain]).then(() => {
+                    this.creatMap();
+                    setTimeout(() => {
+                        this.getVectorData(param);
+                    }, 1000)
+                    
+                });
+            }else if(type == 'xlsx' || type == 'xls'){
+                this.isShow = 2
+                this.excelUrl = url
+                this.loadExcel();
+            }else{
+                this.isShow = 3
+                // ElMessage.error('不支持的文件类型')
+            }
+        }
+    },
+   
+    methods: {
+        onRendered() {
+            // ElMessage.success('Excel文件渲染完成')
+        },
+        loadExcel() {
+            if (!this.excelUrl) {
+                // this.error = '请输入Excel文件在线地址'
+                return
+            }
+            this.loading = true
+            this.error = ''
+            // 加载Excel文件,实际加载由VueOfficeExcel组件处理
+            setTimeout(() => {
+                this.loading = false
+            }, 1000)
+        },
+        onError(err) {
+            ElMessage.error('Excel文件加载失败:', err)
+            // this.error = 'Excel文件加载失败,请检查文件地址是否正确'
+            this.loading = false
+        },
+         creatMap() {
+            window.viewer = new SkyScenery.Viewer("skysceneryContainer", {
+                animation: false, //是否创建动画小器件,左下角仪表
+                baseLayerPicker: false, //是否显示图层选择器
+                imageryProvider: new SkyScenery.SingleTileImageryProvider({
+                url: (function createColorCanvas(color) {
+                    var width = 1,
+                    height = 1;
+                    var canvas = document.createElement("canvas");
+                    canvas.width = width;
+                    canvas.height = height;
+                    var ctx = canvas.getContext("2d");
+                    ctx.fillStyle = color;
+                    ctx.fillRect(0, 0, width, height);
+                    return canvas.toDataURL();
+                })("#ffffff00"),
+                rectangle: SkyScenery.Rectangle.fromDegrees(-180.0, -90.0, 180.0, 90.0),
+                }),
+                fullscreenButton: false, //是否显示全屏按钮
+                geocoder: false, //是否显示geocoder小器件,右上角查询按钮
+                homeButton: false, //是否显示Home按钮
+                infoBox: false, //是否显示信息框
+                sceneModePicker: false, //是否显示3D/2D选择器
+                selectionIndicator: false, //是否显示选取指示器组件
+                timeline: false, //是否显示时间轴
+                navigationHelpButton: false, //是否显示右上角的帮助按钮
+                scene3DOnly: true, //如果设置为true,则所有几何图形以3D模式绘制以节约GPU资源
+                shouldAnimate: false, //是否自动播放
+                // 性能优化配置
+                requestRenderMode: true,
+                maximumRenderTimeChange: Infinity,
+                preserveDrawingBuffer: false,
+                useBrowserRecommendedResolution: true,
+            });
+
+            // 添加地图服务
+            viewer.imageryLayers.addImageryProvider(
+                new SkyScenery.ArcGisMapServerImageryProvider({
+                url:
+                    "https://szlszxdt.qpservice.org.cn/internal_map/?servertype=shmap_blue_web&proxyToken=" +
+                    SkySceneryConfig.token,
+                enablePickFeatures: false, // 禁用要素拾取功能以提高性能
+                })
+            );
+
+            // 定位
+            viewer.camera.setView({
+                destination: SkyScenery.Cartesian3.fromDegrees(121.1158994,31.15205574, 30000.0), // 设置位置
+                orientation: {
+                    heading: SkyScenery.Math.toRadians(0.0), // 方向
+                    pitch: SkyScenery.Math.toRadians(-90.0), // 倾斜角度
+                    roll: 0,
+                },
+            });
+            
+        },
+        closeWin() {
+            this.infoDialogShow = false;
+            this.nowPoint = null;
+            this.nowPointInfo = null;
+        },
+        dwanMap(){
+            let that = this;
+            if(that.geometryArr.length>0){
+                that.geometryArr.map(function (info) {
+                    viewer.entities.remove(info)
+                })
+            }
+            that.geometryArr = that.dataJson.features.map(function (info) {
+                if(info.geometry.type == "Point"){
+                    return that.addPoint(info)
+                }else if(info.geometry.type == "LineString"){
+                    return that.addLine(info)
+                }else{
+                    return that.addPolygon(info)
+                }
+                
+            })
+            that.pointTCHandle();
+        },
+        addPolygon(param){
+            let arr = [];
+            param.geometry.coordinates.forEach(element => {
+                element.forEach(e => {
+                    arr.push(e[0]);
+                    arr.push(e[1]);
+                })
+            });
+            return viewer.entities.add(new SkyScenery.Entity({
+                name: " polygon",
+                polygon: {
+                    hierarchy: {
+                        positions: SkyScenery.Cartesian3.fromDegreesArray(arr)
+                    },
+                    heightReference: SkyScenery.HeightReference.CLAMP_TO_GROUND,
+                    material: SkyScenery.Color.CYAN.withAlpha(0.5)
+                },
+                info: {
+                    coor: [arr[0], arr[1]],
+                    properties: param.properties
+                },
+            }));
+        },
+        addLine(param){
+            return viewer.entities.add({
+                name: "line",
+                polyline: {
+                    //经纬度数组转世界坐标,带高度的话是fromDegreesArrayHeights
+                    positions: SkyScenery.Cartesian3.fromDegreesArray(param.geometry.coordinates),
+                    width: 2,
+                    material: SkyScenery.Color.CYAN,
+                    info: {
+                        properties: param.properties
+                    },
+                }
+            });
+        },
+        addPoint(param){
+            let that = this;
+            return viewer.entities.add(new SkyScenery.Entity({
+                name:"point",
+                position: SkyScenery.Cartesian3.fromDegrees(param.geometry.coordinates[0], param.geometry.coordinates[1]),
+                type: "point",
+                info: {
+                  coor: [param.geometry.coordinates[0], param.geometry.coordinates[1]],
+                  properties: param.properties
+                },
+                billboard: {
+                  image: that.pointImg,
+                  disableDepthTestDistance: Number.POSITIVE_INFINITY,
+                  scale: 0.3,
+                  horizontalOrigin: SkyScenery.HorizontalOrigin.CENTER,
+                  verticalOrigin: SkyScenery.VerticalOrigin.BOTTOM,
+                }
+              }));
+        },
+        // 点击事件绑定
+        pointTCHandle() {
+            let that = this;
+            if (!this.mapHandle) {
+                this.mapHandle = new SkyScenery.ScreenSpaceEventHandler(viewer.canvas, this);
+                this.mapHandle.setInputAction(function (movement) {
+                    that.infoDialogShow = false
+                    const pickedObject = viewer.scene.pick(movement.position);
+
+                    let cartesian = viewer.camera.pickEllipsoid(
+                        movement.position,
+                        viewer.scene.globe.ellipsoid
+                    );
+                    // 空间坐标转世界坐标(弧度)
+                    let cartographic = SkyScenery.Cartographic.fromCartesian(cartesian);
+                    // 弧度转为角度(经纬度)
+                    let lon = SkyScenery.Math.toDegrees(cartographic.longitude); // 经度值
+                    let lat = SkyScenery.Math.toDegrees(cartographic.latitude); // 纬度值
+                    let center = [lon,lat]
+                    if (SkyScenery.defined(pickedObject) && SkyScenery.defined(pickedObject.id)) {
+                        const entity = pickedObject.id;
+                        that.infoDialogShow = true
+                        that.nowPoint = entity.info;
+                        that.nowPointInfo =  Object.keys(that.nowPoint.properties).map(key => ({ key, value: that.nowPoint.properties[key] }));
+                        that.nowPoint["type"] = "info";
+                        // let xy = that.lonlatConvertToScreenXY(entity.info.coor)
+                        let xy = that.lonlatConvertToScreenXY(center)
+                        document.querySelector(".infoDialog").style.top = (xy.y - 230) + "px";
+                        document.querySelector(".infoDialog").style.left = (xy.x - 100) + "px";
+                    } else {
+                        // console.log('未拾取到实体');
+                    }
+                }, SkyScenery.ScreenSpaceEventType.LEFT_CLICK);
+                viewer.scene.postRender.addEventListener(that.updatePosition, this);
+            } else {
+                toRaw(this.mapHandle).destroy();
+                this.mapHandle = null
+                that.nowPointInfo = null
+                viewer.scene.postRender.removeEventListener(that.updatePosition, this);
+            }
+        },
+        // 经纬度转屏幕坐标
+        lonlatConvertToScreenXY(lonlat) {
+            // 定义经纬度
+            var longitude = SkyScenery.Math.toRadians(lonlat[0]); // 例如:东经116.391度
+            var latitude = SkyScenery.Math.toRadians(lonlat[1]); // 例如:北纬39.907度
+            var height = 0; // 高度,通常在地表为0
+            // 将经纬度转换为笛卡尔坐标
+            // var cartographic = SkyScenery.Cartographic.fromDegrees(longitude, latitude, height);
+            var cartesian = SkyScenery.Cartesian3.fromRadians(longitude, latitude, height, viewer.scene.globe.ellipsoid);
+            // 将笛卡尔坐标转换为窗口坐标
+            var canvasCoordinates = viewer.scene.cartesianToCanvasCoordinates(cartesian);
+            return canvasCoordinates
+        },
+        // 位置更新
+        updatePosition() {
+            try {
+                if (this.nowPoint != null) {
+                    const obj = toRaw(this.nowPoint);
+                    let xy = this.lonlatConvertToScreenXY(obj.coor)
+                    if (!xy) {
+                        document.querySelector(".infoDialog").style.top = "-9999px";
+                        document.querySelector(".infoDialog").style.left = "-9999px";
+                    } else {
+                        document.querySelector(".infoDialog").style.top = (xy.y - 230) + "px";
+                        document.querySelector(".infoDialog").style.left = (xy.x - 100) + "px";
+                    }
+                }
+            } catch (error) {
+                // debugger
+            }
+        },
+        
+        getVectorData(param){
+            let that = this;
+            fetch(param.url, {
+                method: 'GET',
+            }).then(response => response.json()) // 假设服务器返回 JSON 数据
+                .then(data => {
+                    console.log('[ eee ] >')
+                    that.dataJson = data;
+                    that.closeWin()
+                    that.dwanMap()
+                })
+        },
+    },
+    beforeDestroy() {
+        viewer = undefined
+    }
+};
+</script>
+<style lang="less" scoped>
+.container {
+    width: 100%;
+    height: 100%;
+    padding: 0px;
+    margin: 0 auto;
+    overflow: hidden;
+}
+.map-container {
+    width: 100%;
+    height: 100%;
+}
+.page-content {
+    width: 100%;
+    height: 100%;
+    background-color: #fff;
+    border-radius: 4px;
+    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+    overflow: hidden;
+  }
+.infoDialog {
+    position: absolute;
+    top: 0px;
+    left: 0px;
+    max-width: 500px;
+    height: 200px;
+    // background: #01346f99;
+    background: #ffffff;
+    border-radius: 10px;
+
+    .close {
+      font-size: 24px;
+      position: absolute;
+      top: 6px;
+      right: 8px;
+      line-height: 24px;
+      width: 24px;
+      height: 24px;
+      cursor: pointer;
+      // color: #ffffff;
+      color: #000000;
+    }
+
+    .content {
+      height: 160px;
+      // color: #ffffff;
+      color: #000000;
+      margin: 30px 0px 10px 20px;
+      overflow: auto;
+      .item {
+        line-height: 30px;
+        margin-right: 20px;
+      }
+    }
+  }
+</style>

+ 1 - 0
src/main.js

@@ -10,6 +10,7 @@ import ElementPlus from 'element-plus'
 import zhCn from 'element-plus/es/locale/lang/zh-cn'
 import 'element-plus/dist/index.css' // 引入默认主题样式
 import 'element-plus/theme-chalk/dark/css-vars.css' // 引入暗色主题样式
+
 initApp.use(ElementPlus, {
     locale: zhCn,
 })

+ 16 - 0
src/router/index.js

@@ -71,6 +71,14 @@ const routes = [
           return import('../views/yygl/appCenter.vue')
         }
       },
+      //任务管理
+      {
+        path: 'taskManger',
+        path: 'taskManger',
+        component: function () {
+          return import('../views/rwgl/Index.vue')
+        }
+      },
     ],
   },
   // {
@@ -134,6 +142,14 @@ const routes = [
     component: function () {
       return import('../views/wgn/Example.vue')
     },
+  },
+   // 文件预览功能
+  {
+    path: '/fileView',
+    name: 'fileView',
+    component: function () {
+      return import('../components/filePreview/viewPreview.vue')
+    },
   },
   // 应用管理
   // {

+ 406 - 186
src/views/HomePage.vue

@@ -22,8 +22,8 @@
           <div class="call-count-container">
             <div class="line"></div>
             <div>
-              <span class="text1">日调用量</span>
-              <span class="count">{{ todayCall }}</span>
+              <span class="text1">日调用量</span>
+              <span class="count">{{ lastDayCall }}</span>
               <span class="text2">次</span>
             </div>
             <div class="line"></div>
@@ -34,17 +34,17 @@
     <div class="darkblue-background">
       <div class="row">
         <div>
-          <div class="strong-data blue ">{{ serveNum }}个</div>
-          <div class="font middle">服务总数</div>
-        </div>
-        <div>
-          <div class="strong-data blue ">{{ userNum }}个</div>
-          <div class="font middle">授权用户数</div>
+          <div class="strong-data blue ">{{ unitNum }}个</div>
+          <div class="font middle">授权委办数</div>
         </div>
         <div>
           <div class="strong-data blue ">{{ appNum }}个</div>
           <div class="font middle">授权应用数</div>
         </div>
+        <div>
+          <div class="strong-data blue ">{{ userNum }}个</div>
+          <div class="font middle">授权用户数</div>
+        </div>
       </div>
       <div class="second-title ">
         服务调用趋势(近30天)
@@ -65,32 +65,43 @@
           </div>
           <div class="grid-2x2">
             <div class="lighter-container middle-container">
-              <div class="strong-data  blue">100</div>
-              <div class="title-sub ">text</div>
+              <div class="strong-data  blue">{{ weekCall }}</div>
+              <div class="title-sub ">近一周总调用</div>
             </div>
             <div class="lighter-container middle-container">
-              <div class="strong-data  blue">200</div>
-              <div class="title-sub ">text</div>
+              <div class="strong-data  blue">{{ monthCall }}</div>
+              <div class="title-sub ">近一月总调用</div>
             </div>
             <div class="lighter-container middle-container">
-              <div class="strong-data  blue">300</div>
-              <div class="title-sub ">text</div>
+              <div class="strong-data  blue">{{ seasonCall }}</div>
+              <div class="title-sub ">近三月总调用</div>
             </div>
             <div class="lighter-container middle-container">
-              <div class="strong-data  blue">400</div>
-              <div class="title-sub ">text</div>
+              <div class="strong-data  blue">{{ yearCall }}</div>
+              <div class="title-sub ">近一年总调用</div>
             </div>
           </div>
         </div>
         <div class="lighter-container" style="flex: 3;">
+          <div class="between-row">
+            <div class="third-title">
+              <el-icon color="#0071e3" size="30px">
+                <Histogram />
+              </el-icon>
+              <div>委办调用情况</div>
 
-          <div class="third-title ">
-            <el-icon color="#0071e3" size="30px">
-              <Histogram />
-            </el-icon>
-            <div>委办调用情况(近7天)</div>
+            </div>
+            <span>
+              <el-button v-show="this.chartMluLevel != 'unit'" text size="large" circle @click="chartMluBack()">
+                <el-icon color="#0071e3" size="30px">
+                  <Back />
+                </el-icon>
+              </el-button>
+              <el-date-picker v-model="chartMluTimeRange" type="daterange" range-separator="到" start-placeholder="开始日期"
+                end-placeholder="结束日期" @change="chartMluFocus()" />
+            </span>
           </div>
-          <div id="echart2"></div>
+          <div id="echartMultiLevelUse"></div>
 
         </div>
       </div>
@@ -155,15 +166,15 @@
 
       <div class="row" style="margin-top: 90px;">
         <div>
-          <div class="strong-data blue ">{{ serveNum }}个</div>
+          <div class="strong-data blue ">365个</div>
           <div class="font middle">地址</div>
         </div>
         <div>
-          <div class="strong-data blue ">{{ userNum }}个</div>
+          <div class="strong-data blue ">366个</div>
           <div class="font middle">应用场景</div>
         </div>
         <div>
-          <div class="strong-data blue ">{{ appNum }}个</div>
+          <div class="strong-data blue ">367个</div>
           <div class="font middle">服务人数</div>
         </div>
       </div>
@@ -221,78 +232,72 @@
       <div class="title-sub ">聚焦政务场景痛点,打造从数据治理到应用落地的全链路创新解决方案</div>
 
       <div class="row" style="margin-top: 36px;">
-        <div class="lightblue-container">
+        <div class="lightblue-container six-router">
           <div class="third-title">
             <el-icon color="white" size="45px">
-              <DataBoard />
+              <Coin />
             </el-icon>
             时空数据管理
           </div>
           <p class="font lightgrey">
-            空间数据查询是从空间数据集中检索特定信息的操作。它可在关系型数据库如MySQL和SQL
-            Server中进行,也能在NoSQL数据库如MongoDB里实现。通过特定函数和方法,能快速找到符合条件的数据,为地理信息等领域提供重要支持
+            覆盖数据质量检查、加工、管理、服务发布全流程,支持矢量/栅格数据标准化处理,实现多格式转换与瓦片定制,保障数据规范可用。
           </p>
         </div>
-        <div class="lightblue-container">
+        <div class="lightblue-container six-router">
           <div class="third-title">
             <el-icon color="white" size="45px">
-              <Coin />
+              <Orange />
             </el-icon>
-            时空数据管理
+            二三维一体化引擎
           </div>
           <p class="font lightgrey">
-            空间数据查询是从空间数据集中检索特定信息的操作。它可在关系型数据库如MySQL和SQL
-            Server中进行,也能在NoSQL数据库如MongoDB里实现。通过特定函数和方法,能快速找到符合条件的数据,为地理信息等领域提供重要支持
+            嵌入式SDK支撑地图交互、空间分析、数据可视化,涵盖热力图/聚合图/迁徙图等多维呈现,实现精准定位与场景切换。
           </p>
         </div>
-        <div class="lightblue-container">
+        <div class="lightblue-container six-router">
           <div class="third-title">
             <el-icon color="white" size="45px">
-              <Coin />
+              <Guide />
             </el-icon>
-            时空数据管理
+            时空门户
           </div>
           <p class="font lightgrey">
-            空间数据查询是从空间数据集中检索特定信息的操作。它可在关系型数据库如MySQL和SQL
-            Server中进行,也能在NoSQL数据库如MongoDB里实现。通过特定函数和方法,能快速找到符合条件的数据,为地理信息等领域提供重要支持
+            整合平台访问统计、功能演示、服务导航,提供视频教程与业务场景展示,快速上手平台核心能力,支撑政务场景应用。
           </p>
         </div>
       </div>
       <div class="row">
-        <div class="lightblue-container">
+        <div class="lightblue-container six-router">
           <div class="third-title">
             <el-icon color="white" size="45px">
-              <Coin />
+              <Tickets />
             </el-icon>
-            时空数据管理
+            微功能
           </div>
           <p class="font lightgrey">
-            空间数据查询是从空间数据集中检索特定信息的操作。它可在关系型数据库如MySQL和SQL
-            Server中进行,也能在NoSQL数据库如MongoDB里实现。通过特定函数和方法,能快速找到符合条件的数据,为地理信息等领域提供重要支持
+            支持拓扑计算、空间量算、几何运算与数据转换,批量处理海量时空数据,适配规划、管理多场景计算需求。
           </p>
         </div>
-        <div class="lightblue-container">
+        <div class="lightblue-container six-router">
           <div class="third-title">
             <el-icon color="white" size="45px">
-              <Coin />
+              <Open />
             </el-icon>
-            时空数据管理
+            应用管理
           </div>
           <p class="font lightgrey">
-            空间数据查询是从空间数据集中检索特定信息的操作。它可在关系型数据库如MySQL和SQL
-            Server中进行,也能在NoSQL数据库如MongoDB里实现。通过特定函数和方法,能快速找到符合条件的数据,为地理信息等领域提供重要支持
+            统一接入各类时空业务应用,管控访问权限与应用信息,实现“接入-访问-管理”全流程闭环,支撑区级特色应用建设。
           </p>
         </div>
-        <div class="lightblue-container">
+        <div class="lightblue-container six-router">
           <div class="third-title">
             <el-icon color="white" size="45px">
-              <Coin />
+              <Help />
             </el-icon>
-            时空数据管理
+            运行管理
           </div>
           <p class="font lightgrey">
-            空间数据查询是从空间数据集中检索特定信息的操作。它可在关系型数据库如MySQL和SQL
-            Server中进行,也能在NoSQL数据库如MongoDB里实现。通过特定函数和方法,能快速找到符合条件的数据,为地理信息等领域提供重要支持
+            实时监控平台运行数据,负责数据全生命周期管理与权限管控,建立四级管理架构,保障系统合规安全运行。
           </p>
         </div>
       </div>
@@ -350,72 +355,72 @@
             构建“检查-加工-发布”全流程数据治理体系,实现矢量/栅格数据标准化处理、多格式转换与瓦片定制,解决区级时空数据格式不统一、质量参差不齐的通电,为全场景应用构牢数据基础
           </p>
           <div class="row" style="margin-top: auto;">
-            <div class="font" style="margin-left: auto;">
+            <div class="font" style="margin-left: auto;" @click="goto('/sksjgl')">
               了解更多→
             </div>
           </div>
         </div>
         <div class="block-page-router">
           <div class="title">
-            时空数据治理标准化实践
+            二三维一体化引擎创新应用
           </div>
           <p class="font grey">
-            构建“检查-加工-发布”全流程数据治理体系,实现矢量/栅格数据标准化处理、多格式转换与瓦片定制,解决区级时空数据格式不统一、质量参差不齐的通电,为全场景应用构牢数据基础
+            自研嵌入式 GIS SDK,融合二维地图交互、三维场景渲染能力,创新落地热力图 / 聚合图 / 迁徙图等多维可视化形式,实现区级地理信息 “全景式” 呈现与精准定位。
           </p>
           <div class="row" style="margin-top: auto;">
-            <div class="font" style="margin-left: auto;">
+            <div class="font" style="margin-left: auto;"  @click="goto('/skmh/scene')">
               了解更多→
             </div>
           </div>
         </div>
         <div class="block-page-router">
           <div class="title">
-            时空数据治理标准化实践
+            政务时空门户一体化探索
           </div>
           <p class="font grey">
-            构建“检查-加工-发布”全流程数据治理体系,实现矢量/栅格数据标准化处理、多格式转换与瓦片定制,解决区级时空数据格式不统一、质量参差不齐的通电,为全场景应用构牢数据基础
+            整合平台访问统计、功能演示、视频教程等能力,打造适配青浦区政务场景的时空门户总入口,打通 “一网通办”“一网统管” 数据链路,降低政务人员使用门槛。
           </p>
           <div class="row" style="margin-top: auto;">
-            <div class="font" style="margin-left: auto;">
+            <div class="font" style="margin-left: auto;"  @click="goto('/skmh')">
               了解更多→
             </div>
           </div>
         </div>
         <div class="block-page-router">
           <div class="title">
-            时空数据治理标准化实践
+            空间计算微功能场景化落地
           </div>
           <p class="font grey">
-            构建“检查-加工-发布”全流程数据治理体系,实现矢量/栅格数据标准化处理、多格式转换与瓦片定制,解决区级时空数据格式不统一、质量参差不齐的通电,为全场景应用构牢数据基础
+            聚焦区级规划、管理等实际需求,落地拓扑计算、空间量算、几何运算等轻量化微功能,支持海量时空数据批量处理,让专业空间分析能力下沉至街镇级应用场景。
           </p>
           <div class="row" style="margin-top: auto;">
-            <div class="font" style="margin-left: auto;">
+            <div class="font" style="margin-left: auto;"  @click="goto('/wgn')">
               了解更多→
             </div>
           </div>
         </div>
         <div class="block-page-router">
           <div class="title">
-            时空数据治理标准化实践
+            政务应用标准化接入管理
           </div>
           <p class="font grey">
-            构建“检查-加工-发布”全流程数据治理体系,实现矢量/栅格数据标准化处理、多格式转换与瓦片定制,解决区级时空数据格式不统一、质量参差不齐的通电,为全场景应用构牢数据基础
+            建立区级时空业务应用 “接入 - 授权 - 管控” 全闭环体系,统一规范应用接入流程与权限管理,适配青浦区特色政务应用的快速接入与安全运行。
           </p>
           <div class="row" style="margin-top: auto;">
-            <div class="font" style="margin-left: auto;">
+            <div class="font" style="margin-left: auto;"  @click="goto('/yygl')">
               了解更多→
             </div>
           </div>
         </div>
         <div class="block-page-router">
           <div class="title">
-            时空数据治理标准化实践
+            四级架构运行管理体系构建
           </div>
           <p class="font grey">
-            构建“检查-加工-发布”全流程数据治理体系,实现矢量/栅格数据标准化处理、多格式转换与瓦片定制,解决区级时空数据格式不统一、质量参差不齐的通电,为全场景应用构牢数据基础
+            创新构建 “区 — 街镇 — 综合网格 — 村居” 四级运行管理架构,实现平台数据全生命周期管控、运行状态实时监控,保障区级 “一张图” 系统合规、安全、稳定运行。
           </p>
           <div class="row" style="margin-top: auto;">
-            <div class="font" style="margin-left: auto;">
+            <div class="font" style="margin-left: auto;"  @click="goto('/yxgl')">
               了解更多→
             </div>
           </div>
@@ -448,18 +453,26 @@
 </template>
 
 <script>
+import { countAppUseByUnitName, countServiceUseByApp, countUnitUse, countUserData, topService, topUnit, totalCount, totalCountGroupByTime } from "@/api/count";
 import * as echarts from "echarts";
+
+let chart1 = null;
+let chartMultiLevelUse = null;
+
 export default {
   name: "Home",
   data() {
     return {
-      todayCall: "857,605",
-      totalCall: [],
-
-      serveNum: 456,
-      userNum: 17,
-      appNum: 43,
-
+      lastDayCall: "0",
+      totalCall: ["0", "0", "0", ",", "0", "0", "0"],
+
+      serveNum: 0,
+      userNum: 0,
+      appNum: 0,
+      weekCall: 0,
+      monthCall: 0,
+      seasonCall: 0,
+      yearCall: 0,
       currentTab: "data-governance", // 初始选中项
       tabs: [
         { key: "data-governance", icon: "", title: "数据治理" },
@@ -467,55 +480,8 @@ export default {
         { key: "comprehensive-decision", icon: "", title: "综合决策" }
       ],
       resourceTop: [
-        {
-          name: "常规色地图",
-          value: 12311
-        }, {
-          name: "常规色地图",
-          value: 12311
-        }, {
-          name: "常规色地图",
-          value: 12311
-        }, {
-          name: "常规色地图",
-          value: 12311
-        }, {
-          name: "常规色地图",
-          value: 12311
-        }, {
-          name: "常规色地图",
-          value: 12311
-        }, {
-          name: "常规色地图",
-          value: 12311
-        }, {
-          name: "常规色地图",
-          value: 12311
-        }, {
-          name: "常规色地图",
-          value: 12311
-        }, {
-          name: "常规色地图",
-          value: 12311
-        },
       ],
       userTop: [
-        {
-          name: "区数据局",
-          value: 6014480
-        }, {
-          name: "区数据局",
-          value: 6014480
-        }, {
-          name: "区数据局",
-          value: 6014480
-        }, {
-          name: "区数据局",
-          value: 6014480
-        }, {
-          name: "区数据局",
-          value: 6014480
-        },
       ],
       useCase: {
         dataProcass: [{
@@ -586,19 +552,33 @@ export default {
         ],
       },
       activeUseCase: "dataProcass",
-      chart1: null,
-      chart2: null
+      chartMluLevel: "unit",
+      chartMluUnitName: null,
+      chartMluAppName: null,
+      chartMluTimeRange: null,
+
     };
   },
+  async beforeMount() {
+    await this.pullTotalCount()
+    await this.pullLastDayCount()
+  },
   mounted() {
-    this.handleTotalCallNumber(667507060);
     this.initChart1();
-    this.initChart2();
+    this.initchartMultiLevelUse();
+    const nowDate = new Date();
+    nowDate.setHours(0, 0, 0, 0);
+    this.chartMluTimeRange = [new Date(new Date(nowDate).setDate(nowDate.getDate() - 7)), new Date(nowDate)];
+    this.chartMluFocusRoot()
+    this.pullTopUnit()
+    this.pullTopService()
+    this.pullMultiTimeRangeCall()
+    this.pullUserCount()
   },
   methods: {
-    gotoDetail() {
+    goto(src) {
       this.$router.push({
-        path: "/sksjgl",
+        path: src,
       });
     },
     // 处理调用总数数字
@@ -615,7 +595,29 @@ export default {
         }
       }
     },
-    initChart1() {
+    async initChart1() {
+
+      const nowDate = new Date();
+      nowDate.setHours(0, 0, 0, 0);
+      let end = new Date(nowDate)
+      let start = new Date(nowDate.setDate(nowDate.getDate() - 30))
+      let data = await totalCountGroupByTime(start, end)
+      let dataMap = {}
+      for (let i = 0; i < data.length; i++) {
+        const e = data[i];
+        dataMap[e.time] = e.count
+      }
+
+      let times = []
+      let values = []
+
+      for (let i = 0; i < 30; i++) {
+        let date = new Date(start);
+        date.setDate(start.getDate() + i)
+        times.push(this.formatDateMMDD(date))
+        values.push(dataMap[date.getTime()] ?? 0)
+      }
+
       // 基于准备好的dom,初始化echarts实例
       var myChart = echarts.init(document.getElementById("echart1"));
 
@@ -624,32 +626,15 @@ export default {
           trigger: "axis", // 坐标轴触发提示
           axisPointer: { type: "shadow" },
         },
-        legend: {
-          data: ["tokyo", "london"],
-          textStyle: { color: "#fff" }, // 图例文字颜色
-        },
         xAxis: {
           type: "category",
-          data: [
-            "Jan",
-            "Feb",
-            "Mar",
-            "Apr",
-            "May",
-            "Jun",
-            "Jul",
-            "Aug",
-            "Sep",
-            "Oct",
-            "Nov",
-          ],
+          data: times,
           axisLine: { lineStyle: { color: "#fff" } }, // 坐标轴颜色
           axisTick: { show: false }, // 隐藏刻度
           splitLine: { show: false }, // 隐藏分割线
         },
         yAxis: {
           type: "value",
-          max: 30,
           axisLine: { lineStyle: { color: "#fff" } },
           splitLine: {
             lineStyle: {
@@ -660,38 +645,46 @@ export default {
         },
         series: [
           {
-            name: "tokyo",
+            name: "调用总数",
             type: "line",
-            data: [7, 7, 15, 19, 21, 22, 25, 26, 23, 19, 12], // 模拟数据
+            data: values, // 模拟数据
             lineStyle: { color: "#42a5f5" }, // 蓝色线条
             itemStyle: { color: "#42a5f5" }, // 节点颜色
             symbol: "circle", // 节点形状
             symbolSize: 6, // 节点大小
           },
-          {
-            name: "london",
-            type: "line",
-            data: [3, 3, 6, 12, 15, 17, 18, 17, 14, 10, 6], // 模拟数据
-            lineStyle: { color: "#4caf50" }, // 绿色线条
-            itemStyle: { color: "#4caf50" },
-            symbol: "circle",
-            symbolSize: 6,
-          },
         ],
       };
       // 绘制图表
       myChart.setOption(option);
-      this.chart1 = myChart
+      chart1 = myChart
     },
-    initChart2() {
-      var myChart = echarts.init(document.getElementById("echart2"));
+    initchartMultiLevelUse() {
+      var myChart = echarts.init(document.getElementById("echartMultiLevelUse"));
       let option = {
         tooltip: {
           trigger: 'axis',
-          axisPointer: {
-            type: 'shadow'
+          show: true,
+          formatter: '{b}: {c}',
+          backgroundColor: 'rgba(50, 50, 50, 0.7)',
+          borderColor: '#333',
+          textStyle: {
+            color: '#fff'
           }
         },
+        title: {
+          text: '所有委办',
+          left: 'center',
+          top: 20,
+          textStyle: {
+            color: '#fff',
+            fontSize: 18,
+            fontWeight: 'bold'
+          },
+        },
+        axisPointer: {
+          type: 'shadow'
+        },
         grid: {
           left: '3%',
           right: '4%',
@@ -701,7 +694,7 @@ export default {
         xAxis: [
           {
             type: 'value',
-            axisLine: { lineStyle: { color: "#fff" } },
+            axisLine: { lineStyle: { color: "#fff" } }
           }
         ],
         yAxis: [
@@ -709,29 +702,16 @@ export default {
             type: 'category',
             axisLine: { lineStyle: { color: "#fff" } },
             inverse: true,
-            data: [
-              "temperature", "humidity", "score", "age", "price",
-              "quantity", "speed", "distance", "weight", "height",
-              "population", "rating", "level", "voltage", "current",
-              "pressure", "frequency", "duration", "count", "percentage",
-              "altitude", "depth", "width", "length", "area",
-              "volume", "density", "intensity", "efficiency", "accuracy"
-            ],
+            data: [""]
           }
         ],
         series: [
           {
+            cursorSize: 20,
             name: 'Direct',
             type: 'bar',
             barWidth: '60%',
-            data: [
-              25.5, 65, 88.3, 28, 299.99,
-              150, 120.7, 350, 75.2, 180.5,
-              125, 4.7, 3, 220, 15.2,
-              101.3, 50, 3600, 42, 85.5,
-              1500, 300, 45, 120, 2500,
-              600, 1.2, 150, 92.8, 99.1
-            ]
+            data: [0]
           }
         ],
         dataZoom: [
@@ -748,10 +728,235 @@ export default {
       };
 
       myChart.setOption(option);
-      this.chart2 = myChart
+
+      myChart.on("click", this.chartMluClick)
+
+      chartMultiLevelUse = myChart
+    },
+    async chartMluFocus(level, farther) {
+      if (level == null) {
+        level = this.chartMluLevel
+      }
+      switch (level) {
+        case "unit":
+        default: {
+          await this.chartMluFocusRoot();
+          this.chartMluUpdateTitle("所有委办")
+          break;
+        }
+        case "app": {
+          let unit = farther ?? this.chartMluUnitName ?? ""
+          await this.chartMluFocusApp(unit);
+          this.chartMluUpdateTitle("委办:" + unit)
+          break;
+        }
+        case "service": {
+          let app = farther ?? this.chartMluAppName ?? ""
+          await this.chartMluFocusService(app);
+          this.chartMluUpdateTitle("应用:" + app)
+          break;
+        }
+      }
+
+    },
+    chartMluClick(event) {
+      switch (this.chartMluLevel) {
+        case "unit": {
+          this.chartMluLevel = "app";
+          this.chartMluUnitName = event.name
+          break;
+        }
+        case "app": {
+          this.chartMluLevel = "service";
+          this.chartMluAppName = event.name
+          break;
+        }
+        case "service":
+        default: {
+          // do nothing
+        }
+      }
+      this.chartMluFocus();
+    },
+    chartMluBack(level) {
+      //自动回退
+      if (level == null) {
+        switch (this.chartMluLevel) {
+          default:
+          case "unit": level = "unit"; break;
+          case "app": level = "unit"; break;
+          case "service": level = "app"; break;
+        }
+      }
+      this.chartMluLevel = level;
+      this.chartMluFocus()
+    },
+    async chartMluFocusRoot() {
+      const data = await countUnitUse(...this.chartMluTimeRange);
+      const names = [];
+      const values = [];
+      for (let index = 0; index < data.length; index++) {
+        const e = data[index];
+        names.push(e.name);
+        values.push(e.count);
+      }
+      this.chartMluUpdateData(names, values)
+    },
+    async chartMluFocusApp(father) {
+      const data = await countAppUseByUnitName(...this.chartMluTimeRange, father);
+      const names = [];
+      const values = [];
+      for (let index = 0; index < data.length; index++) {
+        const e = data[index];
+        names.push(e.name);
+        values.push(e.count);
+      }
+      this.chartMluUpdateData(names, values)
+    },
+    async chartMluFocusService(father) {
+      const data = await countServiceUseByApp(...this.chartMluTimeRange, father);
+      const names = [];
+      const values = [];
+      for (let index = 0; index < data.length; index++) {
+        const e = data[index];
+        names.push(e.name);
+        values.push(e.count);
+      }
+      this.chartMluUpdateData(names, values)
+    },
+    async pullTopUnit() {
+      let data = await topUnit(5)
+      for (let i = 0; i < data.length; i++) {
+        const e = data[i];
+        e.value = this.numberFormatter(e.count);
+      }
+      this.userTop = data
+    },
+    async pullTopService() {
+      let data = await topService(10)
+      for (let i = 0; i < data.length; i++) {
+        const e = data[i];
+
+        e.value = this.numberFormatter(e.count);
+      }
+      this.resourceTop = data
+    },
+    async pullTotalCount() {
+      this.handleTotalCallNumber((await totalCount())[0].count)
+    },
+    async pullLastDayCount() {
+      const nowDate = new Date();
+      nowDate.setHours(0, 0, 0, 0);
+      let end = new Date(nowDate)
+      let start = new Date(nowDate.setDate(nowDate.getDate() - 1))
+      let data = (await totalCount(start, end))[0]
+      this.lastDayCall = data == null ? 0 : data.count
+    },
+    async pullMultiTimeRangeCall() {
+      this.weekCall = await this.pullTotalCountByDays(7)
+      this.monthCall = await this.pullTotalCountByDays(30)
+      this.seasonCall = await this.pullTotalCountByDays(91)
+      this.yearCall = await this.pullTotalCountByDays(365)
+    },
+    async pullTotalCountByDays(days) {
+      const nowDate = new Date();
+      nowDate.setHours(0, 0, 0, 0);
+      let end = new Date(nowDate)
+      let start = new Date(nowDate.setDate(nowDate.getDate() - days))
+      let data = (await totalCount(start, end))[0]
+      return data == null ? 0 : data.count
+    },
+    async pullUserCount() {
+      let data = await countUserData();
+      this.appNum = data.application;
+      this.unitNum = data.unit;
+      this.userNum = data.username;
+
+
+    },
+    chartMluUpdateData(names, values) {
+      chartMultiLevelUse.setOption({
+        yAxis: [
+          {
+            data: names,
+          }
+        ],
+        series: [
+          {
+            data: values
+          }
+        ]
+      })
+      this.chartMluCheckChangeToLog(values)
+    },
+    chartMluCheckChangeToLog(values) {
+      let toLog = false;
+      values.sort((a, b) => a - b);
+      let min = values[0];
+      let max = values[values.length - 1]
+      if (max / min > 3000) {
+        toLog = true
+      }
+
+      if (toLog) {
+        chartMultiLevelUse.setOption(
+          {
+            xAxis: [{
+              type: 'log',
+              logBase: 10,
+              min: 1,
+
+              axisLine: { lineStyle: { color: "#fff" } },
+              axisLabel: {
+                formatter: this.numberFormatter
+              },
+            }]
+          }
+        )
+      } else {
+        chartMultiLevelUse.setOption(
+          {
+            xAxis: [{
+              type: 'value',
+              min: 0,
+
+              axisLine: { lineStyle: { color: "#fff" } },
+            }]
+          }
+        )
+      }
+    },
+    chartMluUpdateTitle(newTitle) {
+      chartMultiLevelUse.setOption({
+        title: {
+          text: newTitle,
+        }
+      })
+    },
+    numberFormatter(num, decimals = 1) {
+      if (num >= 1000000000000) {
+
+        return parseFloat((num / 1000000000000).toFixed(decimals)).toString() + '亿万';
+      }
+      if (num >= 100000000) {
+        return parseFloat((num / 100000000).toFixed(decimals)).toString() + '亿';
+      }
+      if (num >= 10000) {
+        return parseFloat((num / 10000).toFixed(decimals)).toString() + '万';
+      }
+      return num.toString();
+    },
+    formatDateMMDD(date) {
+      // 月份从0开始,所以要 +1
+      const month = String(date.getMonth() + 1).padStart(2, '0');
+      const day = String(date.getDate()).padStart(2, '0');
+
+      return `${month}/${day}`;
     }
+
+
   }
-};
+}
 </script>
 
 <style lang="less" scoped>
@@ -848,7 +1053,8 @@ export default {
 }
 
 .row,
-.warp-row {
+.warp-row,
+.between-row {
   display: flex !important;
   justify-content: space-around;
   padding: 0;
@@ -859,6 +1065,10 @@ export default {
   flex-wrap: wrap;
 }
 
+.between-row {
+  justify-content: space-between;
+}
+
 .col {
   display: flex !important;
   justify-content: space-around;
@@ -996,6 +1206,16 @@ export default {
   }
 }
 
+.six-router {
+  width: 30%;
+  height: 300px;
+  font-size: 28px;
+
+  .third-title {
+    font-size: 32px;
+  }
+}
+
 .circle-number {
   display: block;
   background-color: #eeeeee1d;
@@ -1033,7 +1253,7 @@ export default {
   height: 450px;
 }
 
-#echart2 {
+#echartMultiLevelUse {
   width: 100%;
   height: 450px;
 }

+ 1 - 1
src/views/example/Map.vue

@@ -60,7 +60,7 @@ export default {
             );
             // 定位
             viewer.camera.setView({
-                destination: SkyScenery.Cartesian3.fromDegrees(121.1, 31, 30000.0), // 设置位置
+                destination: SkyScenery.Cartesian3.fromDegrees(121.1158994,31.15205574, 30000.0), // 设置位置
                 orientation: {
                     heading: SkyScenery.Math.toRadians(0.0), // 方向
                     pitch: SkyScenery.Math.toRadians(-90.0), // 倾斜角度

+ 423 - 0
src/views/rwgl/Index.vue

@@ -0,0 +1,423 @@
+<template>
+    <div class="blue-background">
+        <div class="lighter-container">
+            <span>
+                <div>
+                    <div>状态:</div>
+                    <el-tag size="large" :effect="focusTaskStatus.includes('all') ? 'dark' : ''" type="primary"
+                        @click="changeTaskStatus()">
+                        全部
+                    </el-tag>
+                    <template v-for="status in taskStatus" :key="status.index">
+                        <el-tag size="large" :effect="focusTaskStatus.includes(status.index) ? 'dark' : ''" type="primary"
+                            @click="changeTaskStatus(status)">
+                            {{ status.name }}
+                        </el-tag>
+                    </template>
+                </div>
+            </span>
+            <div class="row">
+                <el-input class="searcher" v-model="searcher" placeholder="请输入任务名称相关关键字" />
+                <el-button type="primary" @click="pullTaskData(1)">搜索</el-button>
+                <el-button type="primary" @click="reset(),pullTaskData(1)">重置</el-button>
+            </div>
+        </div>
+        <div class="lighter-container">
+            查询到{{ taskNum }}条任务
+            <el-table table-layout="fixed" :data="taskData" class="table">
+                <el-table-column prop="main_c_name" label="名称" />
+                <el-table-column prop="main_c_comment" label="描述" />
+                <el-table-column prop="main_c_state" label="状态">
+                    <template #default="scope">
+                        <el-tag effect="dark" :type="statusStaticInfo[scope.row.main_c_state]?.tagType ?? ''"
+                            disable-transitions>{{
+                                getStatus(scope.row.main_c_state)?.name ?? '' }}</el-tag>
+                    </template>
+                </el-table-column>
+                <el-table-column prop="main_c_start_time" label="任务开始时间">
+                    <template #default="scope">
+                        {{ timeFormatter(scope.row.main_c_start_time) }}
+                    </template>
+                </el-table-column>
+                <el-table-column prop="main_c_end_time" label="任务结束时间">
+                    <template #default="scope">
+                        {{ timeFormatter(scope.row.main_c_end_time) }}
+                    </template>
+                </el-table-column>
+                <el-table-column prop="main_c_file_name" label="结果">
+                    <template #default="scope">
+                        <span class="link" v-if="scope.row.main_c_file != null && scope.row.main_c_file_name != null"
+                            @click="downloadWithBlob(scope.row.main_c_file, scope.row.main_c_file_name)">
+                            {{ scope.row.main_c_file_name }}
+                        </span>
+                    </template>
+                </el-table-column>
+                <el-table-column label="操作" width="360">
+                    <template #default="scope">
+                        <!-- <el-button  type="primary">
+                            查看详情
+                        </el-button> -->
+                        <template v-if="scope.row.main_c_file != null && scope.row.main_c_file_name != null">
+                            <el-button type="primary"
+                                @click="downloadWithBlob(scope.row.main_c_file, scope.row.main_c_file_name)">
+                                下载结果
+                            </el-button>
+                            <el-button type="primary" @click="preView(scope.row.main_c_file)">
+                                预览结果
+                            </el-button>
+                        </template>
+                    </template>
+
+                </el-table-column>
+            </el-table>
+            <div class="between-row">
+                <div><!--empty div--></div>
+                <el-pagination layout="prev, pager, next" :total="taskNum" @change="page=>pullTaskData(page)"/>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+import { getTasks, getTaskStatus } from '@/api/rwgl';
+
+export default {
+    data() {
+        return {
+            searcher: "",
+            taskStatus: [],
+            focusTaskStatus: ["all"],
+            taskData: [],
+            taskNum: 0,
+            statusStaticInfo: {
+                0: {
+                    tagType: "primary"
+                },
+                1: {
+                    tagType: "primary"
+                },
+                2: {
+                    tagType: "success"
+                },
+                3: {
+                    tagType: "danger"
+                },
+            },
+            page: 1
+        }
+    },
+    mounted() {
+        this.pullTaskStatus()
+        this.pullTaskData(1)
+    },
+    methods: {
+        async pullTaskStatus() {
+            this.taskStatus = (await getTaskStatus()).sort((a, b) => a.index - b.index)
+        },
+        changeTaskStatus(status) {
+            if (status == null) {
+                this.focusTaskStatus = ["all"]
+            } else {
+                let set = new Set(this.focusTaskStatus)
+                set.delete("all")
+                let index = status.index
+                if (this.focusTaskStatus.includes(index)) {
+                    set.delete(index)
+                } else {
+                    set.add(index)
+                }
+                this.focusTaskStatus = Array.from(set)
+            }
+            // if(this.taskStatus.length==this.focusTaskStatus.length){
+            //     this.focusTaskStatus = ["all"]
+            // }
+        },
+        getCheckedStatus() {
+            if (this.focusTaskStatus.includes("all")) {
+                return this.taskStatus.map(t => t.index)
+            } else {
+                return this.focusTaskStatus
+            }
+        },
+        async pullTaskData(page) {
+            if(page!=null)this.page = page
+            let res = await getTasks(this.page, 10, this.searcher, this.getCheckedStatus())
+            this.taskNum = res.count
+            this.taskData = res.data
+        },
+        getStatus(index) {
+            for (let i = 0; i < this.taskStatus.length; i++) {
+                const e = this.taskStatus[i];
+                if (e.index == index) {
+                    return e;
+                }
+            }
+        },
+        timeFormatter(time) {
+            let date = new Date(time)
+            return date.toLocaleString()
+        },
+        async downloadWithBlob(url, filename) {
+            try {
+                const response = await fetch(systemConfig.dmsDataProxy + url);
+                const blob = await response.blob();
+                const blobUrl = window.URL.createObjectURL(blob);
+
+                const link = document.createElement('a');
+                link.href = blobUrl;
+                link.download = filename || 'file';
+                link.click();
+
+                // 清理URL对象
+                window.URL.revokeObjectURL(blobUrl);
+            } catch (error) {
+                console.error('下载失败:', error);
+            }
+        },
+        reset(){
+            this.focusTaskStatus= ["all"]
+            this.searcher = ""
+        },
+        preView(url){
+            window.open("fileView?url="+systemConfig.dmsDataProxy+url,'_blank')
+        }
+    }
+}
+</script>
+
+<style>
+* {
+    --el-table-bg-color: #eeeeee0b;
+    /* 表格背景 */
+    --el-table-header-bg-color: #eeeeee0b;
+    /* 表头背景 */
+    --el-table-tr-bg-color: #eeeeee0b;
+    /* 行背景 */
+    --el-table-row-hover-bg-color: #eeeeee0b;
+    /* 行悬浮背景 */
+    --el-table-header-text-color: #ededed;
+    /* 表头文字颜色 */
+}
+
+.font,
+.title,
+.super-title,
+.title-sub,
+.second-title,
+.third-title,
+.strong-data,
+link {
+    color: #fff;
+}
+
+.icon {
+    color: #fff;
+    fill: currentColor;
+}
+
+.middle {
+    text-align: center;
+}
+
+.darkblue-background,
+.dark-background,
+.bluedark-background,
+.blue-background,
+.image-background {
+    padding-top: 40px;
+    padding-bottom: 40px;
+    margin-left: 0;
+    padding-left: 90px;
+    padding-right: 90px;
+    min-height: 600px;
+}
+
+.darkblue-background {
+    background: linear-gradient(to bottom,
+            #02060c 0%,
+            #0f3460 40%,
+            #0f3460 100%);
+}
+
+.dark-background {
+    background: #0a0a08
+}
+
+.blue-background,
+body {
+    background: #0f3460
+}
+
+.bluedark-background {
+    background: linear-gradient(to bottom,
+            #0f3460 0%,
+            #0f3460 60%,
+            #02060c 100%);
+}
+
+.image-background {
+    background-repeat: no-repeat;
+    background-position: center;
+    background-size: 100% auto;
+}
+
+.lighter-container {
+    background-color: #eeeeee0b;
+    padding: 10px;
+    margin: 15px;
+    vertical-align: middle;
+    border-radius: 3%;
+}
+
+.lightblue-container {
+    border-radius: 3%;
+    padding: 10px;
+    margin: 15px;
+    background: linear-gradient(to bottom,
+            #215476 0%,
+            #28638b 66%,
+            #337aac 100%);
+    font-size: 20px;
+
+    .third-title {
+        font-size: 28px;
+        padding: 0;
+    }
+}
+
+.middle-container {
+    display: flex !important;
+    justify-content: center;
+    flex-direction: column;
+    padding: 0;
+    align-items: stretch;
+}
+
+.row,
+.warp-row,
+.between-row {
+    display: flex !important;
+    justify-content: space-around;
+    padding: 0;
+    align-items: stretch;
+}
+
+.warp-row {
+    flex-wrap: wrap;
+}
+
+.between-row {
+    justify-content: space-between;
+}
+
+.col {
+    display: flex !important;
+    justify-content: space-around;
+    flex-direction: column;
+}
+
+.dense-col {
+    display: flex !important;
+    justify-content: center;
+    flex-direction: column;
+}
+
+.start-reverse-col {
+    display: flex !important;
+    justify-content: start;
+    flex-direction: column-reverse;
+}
+
+.dense-col>*,
+.start-reverse-col>* {
+    margin: 10px;
+}
+
+.grid-2x2 {
+    display: grid !important;
+    grid-template-columns: 1fr 1fr;
+    gap: 12px;
+    width: 100%;
+    height: 80%;
+}
+
+.strong-data {
+    font-size: 32px;
+    font-weight: bold;
+    text-align: center;
+}
+
+.title {
+    font-size: 36px;
+    margin-top: 10px;
+    text-align: center;
+    font-weight: bold;
+}
+
+.super-title {
+    font-size: 44px;
+    margin-top: 10px;
+    text-align: center;
+    font-weight: bold;
+
+}
+
+.title-sub {
+    font-size: 16px;
+    margin-top: 10px;
+    color: rgb(192, 192, 192);
+    text-align: center;
+}
+
+.second-title {
+    font-size: 20px;
+    margin-top: 10px;
+    padding-left: 10px;
+    border-left: 2px solid #3498db;
+    font-weight: bold;
+}
+
+.third-title {
+    font-size: 18px;
+    margin-top: 10px;
+    padding-left: 10px;
+    display: flex;
+    font-weight: bold;
+}
+
+.link {
+    font-weight: bold;
+    /* 字体加粗 */
+    text-decoration: underline;
+    /* 下划线 */
+    cursor: pointer;
+    /* 鼠标悬浮时变为手型(可点击形式) */
+}
+
+.searcher {
+    background-color: #334155;
+    margin-right: 15px;
+}
+
+.blue {
+    color: #3498db;
+}
+
+.grey {
+    color: rgb(192, 192, 192);
+}
+
+.lightgrey {
+    color: rgb(229, 229, 229);
+}
+
+#echart1 {
+    width: 100%;
+    height: 450px;
+}
+
+.container {
+    width: 1920px;
+    margin: 0 auto;
+}
+</style>

+ 1 - 1
src/views/yygl/manage/index.vue

@@ -45,7 +45,7 @@
       <!-- 搜索栏区域 -->
       <div class="search-section">
         <el-input 
-          placeholder="请输入应用名称模块相关关键字" 
+          placeholder="请输入应用名称关键字" 
           v-model="searchKeyword" 
           class="search-input" 
           clearable

+ 6 - 1
src/views/yygl/overview/index.vue

@@ -48,7 +48,7 @@
               <p class="app-description">{{ app.content }}</p>
               <div class="app-footer">
                 <span class="app-date">{{ app.createTime }}</span>
-                <el-button type="primary" size="small" class="visit-button">访问</el-button>
+                <el-button type="primary" size="small" class="visit-button" @click="handleVisit">访问</el-button>
               </div>
             </div>
           </div>
@@ -94,6 +94,11 @@ export default {
       this.getDmsTagSName();
       this.getDmsDataList();
     },
+    handleVisit() {
+      // http://localhost:2027/fileView?url=/proxy_dms/static/1_青浦大数据中心一张图功能表20240531.xlsx
+      // http://localhost:2027/fileView?url=/proxy_dms/static/test.geojson
+      window.open('fileView?url='+this.curUrl+'/static/1_青浦大数据中心一张图功能表20240531.xlsx', '_blank');
+    },
     getDmsTagSName(){
       let requestParams = {
         sName: 'tag',