Răsfoiți Sursa

Merge branch 'master' into onemap_zmg

mork 1 lună în urmă
părinte
comite
8002ef197b

Fișier diff suprimat deoarece este prea mare
+ 1 - 238
package-lock.json


+ 2 - 1
package.json

@@ -12,6 +12,7 @@
     "echarts": "^5.6.0",
     "element-plus": "^2.2.15",
     "vue": "^3.2.13",
+    "vue-json-viewer": "^3.0.4",
     "vue-router": "^4.0.3",
     "vue3-lottie": "^3.3.1",
     "vuex": "^4.0.0"
@@ -30,4 +31,4 @@
     "not dead",
     "not ie 11"
   ]
-}
+}

+ 1 - 0
public/static/config/config.js

@@ -4,6 +4,7 @@ let systemConfig = {
         userName: "user002",
         password: "Yysz@1234002"
     },
+    baseServicerPath: "/oneMap",
     // oauth地址
     oauthServiceUrlOrigin: "http://121.43.55.7:10086/",
     // oauth地址

+ 146 - 21
public/static/css/reset.css

@@ -1,25 +1,150 @@
 /* reset */
-html,body,h1,h2,h3,h4,h5,h6,div,dl,dt,dd,ul,ol,li,p,blockquote,pre,hr,figure,table,caption,th,td,form,fieldset,legend,input,button,textarea,menu{margin:0;padding:0;}
-header,footer,section,article,aside,nav,hgroup,address,figure,figcaption,menu,details{display:block;}
-html,body{overflow: hidden;}
-table{border-collapse:collapse;border-spacing:0;}
-caption,th{text-align:left;font-weight:normal;}
-html,body,fieldset,img,iframe,abbr{border:0;}
-i,cite,em,var,address,dfn{font-style:normal;}
-[hidefocus],summary{outline:0;}
-li{list-style:none;}
-h1,h2,h3,h4,h5,h6,small{font-size:100%;}
-sup,sub{font-size:83%;}
-pre,code,kbd,samp{font-family:inherit;}
-q:before,q:after{content:none;}
-textarea{overflow:auto;resize:none;}
-label,summary{cursor:default;}
-a,button{cursor:pointer;}
-h1,h2,h3,h4,h5,h6,em,strong,b{font-weight:bold;}
-del,ins,u,s,a,a:hover{text-decoration:none;}
+html,
+body,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+div,
+dl,
+dt,
+dd,
+ul,
+ol,
+li,
+p,
+blockquote,
+pre,
+hr,
+figure,
+table,
+caption,
+th,
+td,
+form,
+fieldset,
+legend,
+input,
+button,
+textarea,
+menu {
+  margin: 0;
+  padding: 0;
+}
+header,
+footer,
+section,
+article,
+aside,
+nav,
+hgroup,
+address,
+figure,
+figcaption,
+menu,
+details {
+  display: block;
+}
+html,
+body {
+  overflow: hidden;
+}
+table {
+  border-collapse: collapse;
+  border-spacing: 0;
+}
+caption,
+th {
+  text-align: left;
+  font-weight: normal;
+}
+html,
+body,
+fieldset,
+img,
+iframe,
+abbr {
+  border: 0;
+}
+i,
+cite,
+em,
+var,
+address,
+dfn {
+  font-style: normal;
+}
+[hidefocus],
+summary {
+  outline: 0;
+}
+li {
+  list-style: none;
+}
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+small {
+  font-size: 100%;
+}
+sup,
+sub {
+  font-size: 83%;
+}
+pre,
+code,
+kbd,
+samp {
+  font-family: inherit;
+}
+q:before,
+q:after {
+  content: none;
+}
+textarea {
+  overflow: auto;
+  resize: none;
+}
+label,
+summary {
+  cursor: default;
+}
+a,
+button {
+  cursor: pointer;
+}
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+em,
+strong,
+b {
+  font-weight: bold;
+}
+del,
+ins,
+u,
+s,
+a,
+a:hover {
+  text-decoration: none;
+}
 /* body,textarea,input,button,select,keygen,legend{font:12px/1.14 arial,\5b8b\4f53;color:#000000;outline:0;} */
-body{background:#fff;}
-a,a:hover{color:#333;}
+body {
+  background: #fff;
+}
+a,
+a:hover {
+  color: #333;
+}
 
 /* 滚动条样式 */
 /* ::-webkit-scrollbar {
@@ -35,4 +160,4 @@ a,a:hover{color:#333;}
 ::-webkit-scrollbar-track {
     border-radius: 10px;
     background: #eeeeee;
-} */
+} */

BIN
public/static/images/wgn_title.png


+ 8 - 10
src/App.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="main">
-    <Header @scroll-update="scrollUpdate"></Header>
+    <Header></Header>
     <el-scrollbar ref="pagescrollbar" view-style="height:100%">
       <router-view />
       <Footer></Footer>
@@ -60,12 +60,12 @@ export default {
     });
   },
   methods: {
-    scrollUpdate() {
-      let that = this;
-      setTimeout(() => {
-        that.$refs.pagescrollbar.update();
-      }, 1000);
-    },
+    // scrollUpdate() {
+    //   let that = this;
+    //   setTimeout(() => {
+    //     that.$refs.pagescrollbar.update();
+    //   }, 1000);
+    // },
     getScale() {
       const ww = window.innerWidth / 1920
       // const wh = window.innerHeight / 945
@@ -154,8 +154,6 @@ html {
 }
 
 #app {
-  // width: 100vw;
-  // height: 100vw;
   width: 1920px;
   height: 100%;
   position: fixed;
@@ -173,7 +171,7 @@ html {
 
   .el-scrollbar {
     width: 100%;
-    // height: calc(100% - 90px);
+    height: calc(100% - 70px);
     box-sizing: border-box;
     overflow: hidden;
   }

+ 12 - 0
src/api/skszk.js

@@ -0,0 +1,12 @@
+import {
+    postBody
+} from '../utils/request'
+
+// 拓扑计算
+const topology = (url, params) => {
+    return postBody(systemConfig.baseServicerPath + url, params)
+}
+
+export default {
+    topology,
+}

+ 8 - 0
src/api/wgn.js

@@ -0,0 +1,8 @@
+import {
+    postform
+} from '../utils/request'
+
+// 拓扑计算
+export const getDmsData = (params) => {
+    return postform(systemConfig.dmsDataProxy + "/content/selectContentList", params)
+}

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

@@ -3,12 +3,8 @@
     <div class="logo">青浦一张图</div>
     <div class="menu">
       <ul class="menu_ul">
-        <li
-          v-for="(item,index) in menuList"
-          :key="index"
-          :class="{'active':item.isActive}"
-          @click="menuClick(item.index,$event)"
-        >{{ item.label }}</li>
+        <li v-for="(item, index) in menuList" :key="index" :class="{ 'active': item.isActive }"
+          @click="menuClick(item.index, $event)">{{ item.label }}</li>
       </ul>
     </div>
   </div>
@@ -21,18 +17,18 @@ export default {
     return {
       menuList: [
         { index: 1, label: "首页", isActive: true },
-        { index: 2, label: "数据中心", isActive: false },
-        { index: 3, label: "应用中心", isActive: false },
-        { index: 4, label: "微功能中心", isActive: false },
-        { index: 5, label: "开发中心", isActive: false },
-        { index: 6, label: "运管中心", isActive: false },
-        { index: 7, label: "综合展示", isActive: false }
+        { index: 2, label: "时空数据管理", isActive: false },
+        { index: 3, label: "二维GIS引擎", isActive: false },
+        { index: 4, label: "时空门户", isActive: false },
+        { index: 5, label: "微功能", isActive: false },
+        { index: 6, label: "应用管理", isActive: false },
+        { index: 7, label: "运行管理", isActive: false }
       ]
     };
   },
   methods: {
     menuClick(index) {
-      this.menuList.map(function(item) {
+      this.menuList.map(function (item) {
         if (item.index == index) {
           item.isActive = true;
         } else {
@@ -56,7 +52,7 @@ export default {
           this.$router.push("/wgn/sksj");
           break;
         case 5:
-          this.$router.push("/development");
+          this.$router.push("/wgn");
           break;
         case 6:
           this.$router.push("/oamc");
@@ -88,37 +84,41 @@ export default {
 <style lang="less" scoped>
 #header {
   width: 1920px;
-  height: 90px;
+  height: 70px;
   background: linear-gradient(180deg, #2c2f74, #494d98);
   color: #ffffff;
   margin: 0 auto;
+
   .logo {
     display: inline-block;
-    height: 90px;
+    height: 70px;
     width: 200px;
     text-align: center;
-    line-height: 90px;
+    line-height: 70px;
     font-size: 24px;
   }
+
   .menu {
     display: inline-block;
     float: right;
+
     .menu_ul {
       li {
         display: inline-block;
         height: 50px;
-        width: 150px;
+        width: fit-content;
         line-height: 50px;
         text-align: center;
         cursor: pointer;
-        margin-top: 30px;
-        margin-bottom: 10px;
-        font-size: 24px;
+        margin: 10px 20px;
+        font-size: 20px;
         font-weight: bold;
       }
+
       li:hover {
         color: #00bbff;
       }
+
       li.active {
         color: #00bbff;
       }

+ 1604 - 0
src/components/skszk/controlPanel.vue

@@ -0,0 +1,1604 @@
+<template>
+  <div id="controlPanelBox">
+    <div>
+      <div class="sceneNameBox">
+        <div class="">
+          场景名称:
+          <el-cascader
+            :disabled="$route.query.sceneId"
+            v-model="SceneValue"
+            placeholder="试试搜索:距离"
+            :options="SceneList"
+            :props="{ expandTrigger: 'hover' }"
+            @change="handleChange"
+            filterable
+          ></el-cascader>
+          <el-tooltip
+            v-if="SceneValue && SceneRule[SceneValue].functionalDefinition"
+            :content="SceneRule[SceneValue].functionalDefinition"
+            placement="bottom"
+            :popper-style="{ maxWidth: '300px' }"
+          >
+            <el-icon
+              style="margin-left: 1rem; width: 1rem; height: 1rem"
+              v-show="SceneValue && SceneRule[SceneValue].functionalDefinition"
+              ><QuestionFilled
+            /></el-icon>
+          </el-tooltip>
+        </div>
+        <div>
+          <el-button type="primary" @click="sendGeometriesToBackend"
+            >发送<i class="el-icon-s-promotion el-icon--right"></i
+          ></el-button>
+        </div>
+      </div>
+      <el-divider></el-divider>
+      <div
+        v-if="
+          SceneValue &&
+          SceneRule[SceneValue] &&
+          (SceneRule[SceneValue].elementTypes.includes('point') ||
+            SceneRule[SceneValue].elementTypes.includes('polyline') ||
+            SceneRule[SceneValue].elementTypes.includes('polygon'))
+        "
+      >
+        元素个数:{{
+          SceneValue && SceneRule[SceneValue] ? SceneRule[SceneValue].numberOf : ""
+        }}
+      </div>
+      <div>
+        参数类型:{{
+          SceneValue && SceneRule[SceneValue] ? SceneRule[SceneValue].elementTypes : ""
+        }}
+      </div>
+      <div>
+        接口地址:{{
+          SceneValue && SceneRule[SceneValue] ? SceneRule[SceneValue].apiUrl : ""
+        }}
+      </div>
+    </div>
+    <el-divider></el-divider>
+    <div>
+      <!-- 元素文本渲染和操作区域 -->
+      <el-tabs tab-position="left" style="height: calc(100vh - 250px)" class="demo-tabs">
+        <el-tab-pane label="入参">
+          <div
+            v-if="
+              SceneValue &&
+              SceneRule[SceneValue] &&
+              SceneRule[SceneValue].elementTypes.includes('file')
+            "
+          >
+            <el-upload
+              v-model:file-list="fileList"
+              class="upload-demo"
+              action=""
+              :limit="1"
+              :on-change="handleFileChange"
+              :on-exceed="handleExceed"
+              :on-remove="uploadRemove"
+              :auto-upload="false"
+            >
+              <el-button type="primary">上传文件</el-button>
+            </el-upload>
+          </div>
+          <div v-if="SceneValue && SceneRule[SceneValue]">
+            <div
+              v-for="item in SceneRule[SceneValue].elementTypes"
+              :key="item"
+              style="margin-top: 0.5rem"
+            >
+              <div
+                v-if="SceneValue && SceneRule[SceneValue] && SceneRule[SceneValue][item]"
+              >
+                {{ item }}:<el-select
+                  v-if="SceneRule[SceneValue][item]"
+                  v-model="params[item]"
+                  :placeholder="'请选择' + item"
+                  style="width: 240px"
+                >
+                  <el-option
+                    v-for="item in SceneRule[SceneValue][item]"
+                    :key="item.value"
+                    :label="item.label"
+                    :value="item.value"
+                  />
+                </el-select>
+              </div>
+              <div v-if="['lon', 'lat', 'filePath', 'EPSILON'].includes(item)">
+                {{ item }}:
+                <el-input
+                  v-model="params[item]"
+                  style="width: 240px"
+                  :placeholder="'请输入' + item"
+                />
+              </div>
+            </div>
+          </div>
+          <json-viewer
+            v-if="
+              SceneValue &&
+              SceneRule[SceneValue] &&
+              (SceneRule[SceneValue].elementTypes.includes('point') ||
+                SceneRule[SceneValue].elementTypes.includes('polyline') ||
+                SceneRule[SceneValue].elementTypes.includes('polygon'))
+            "
+            :value="jsonData"
+            :editable="true"
+            :preview-mode="false"
+            style="
+              pointer-events: auto;
+              max-height: calc(100vh - 250px) !important;
+              overflow-y: scroll !important;
+            "
+            copyable
+            @input="handleJsonInput"
+          />
+        </el-tab-pane>
+        <el-tab-pane label="返回">
+          <div
+            v-if="backData.message || backData.error"
+            :style="{
+              backgroundColor: backData.code === 200 ? '#67C23A' : '#F56C6C',
+              color: '#fff',
+              padding: '5px 10px',
+              fontSize: '14px',
+            }"
+          >
+            {{ backData.message || backData.error }}
+          </div>
+          <json-viewer
+            :value="backData.content"
+            :editable="true"
+            :preview-mode="false"
+            style="
+              pointer-events: auto;
+              max-height: calc(100vh - 250px) !important;
+              overflow-y: scroll !important;
+            "
+            copyable
+        /></el-tab-pane>
+      </el-tabs>
+    </div>
+    <!-- 绘制工具栏 -->
+    <div
+      class="toolbar"
+      v-if="
+        SceneValue &&
+        SceneRule[SceneValue] &&
+        (SceneRule[SceneValue].elementTypes.includes('point') ||
+          SceneRule[SceneValue].elementTypes.includes('polyline') ||
+          SceneRule[SceneValue].elementTypes.includes('polygon'))
+      "
+    >
+      <div
+        class="tool-item"
+        @click="activateDraw('point')"
+        :class="{ active: currentTool === 'point' }"
+      >
+        绘制点
+        <el-tooltip
+          content="绘制开关,蓝色状态代表开启,再次点击结束绘制,地图左键点击选点,"
+          placement="right"
+        >
+          <el-icon style="width: 1rem; height: 1rem"><QuestionFilled /></el-icon>
+        </el-tooltip>
+      </div>
+      <div
+        class="tool-item"
+        @click="activateDraw('polyline')"
+        :class="{ active: currentTool === 'polyline' }"
+      >
+        绘制线<el-tooltip
+          content="绘制线最少需要两个点,鼠标左键点击选点,点之间自动连线,鼠标右键结束绘制"
+          placement="right"
+        >
+          <el-icon style="width: 1rem; height: 1rem"><QuestionFilled /></el-icon>
+        </el-tooltip>
+      </div>
+      <div
+        class="tool-item"
+        @click="activateDraw('polygon')"
+        :class="{ active: currentTool === 'polygon' }"
+      >
+        绘制面<el-tooltip
+          content="绘制面最少需要三个点,鼠标左键点击选点,面自动必合,鼠标右键结束绘制"
+          placement="right"
+        >
+          <el-icon style="width: 1rem; height: 1rem"><QuestionFilled /></el-icon>
+        </el-tooltip>
+      </div>
+      <!-- <div class="tool-item" @click="startHoleDrawing" :class="{ active: isDrawingHole }">
+        绘制镂空
+      </div> -->
+      <div class="tool-item" @click="clearAll">清除所有</div>
+    </div>
+  </div>
+</template>
+
+<script>
+import skszk from "../../api/skszk";
+// 控制面板
+export default {
+  name: "ControlPanel",
+  // 2. 接收父组件传递的 props(单向数据流,子组件不能直接修改)
+  props: {
+    btnText: {
+      type: String, // 类型限制
+      default: "默认按钮", // 默认值
+      required: false, // 是否必传
+    },
+    showCount: {
+      type: Boolean,
+      default: false,
+    },
+  },
+  data() {
+    return {
+      currentFile: null, // 当前选中的文件
+      fileList: [],
+      // 其他参数
+      params: {
+        unit: "",
+        outFileType: "",
+        outPrj: "",
+        inPrj: "",
+        lon: "",
+        lat: "",
+      },
+      // 请求参数
+      jsonData: {},
+      // 返回参数
+      backData: {},
+      // 当前场景值
+      SceneValue: "",
+      // 场景列表
+      SceneList: [
+        {
+          value: "1.5.1",
+          label: "1.5.1拓扑计算",
+          children: [
+            {
+              value: "1.5.1.1",
+              label: "点线面拓扑关系",
+            },
+          ],
+        },
+        {
+          value: "1.5.2",
+          label: "1.5.2空间量算",
+          children: [
+            {
+              value: "1.5.2.1",
+              label: "两点欧氏距离",
+            },
+            {
+              value: "1.5.2.2",
+              label: "点到线的最短距离",
+            },
+            {
+              value: "1.5.2.3",
+              label: "点到面的最短距离",
+            },
+            {
+              value: "1.5.2.4",
+              label: "面到面的最短距离",
+            },
+            {
+              value: "1.5.2.5",
+              label: "折线距离(累加线段长度)",
+            },
+            {
+              value: "1.5.2.6",
+              label: "平面面积、不规则面面积",
+            },
+            {
+              value: "1.5.2.7",
+              label: "曲面面积,考虑地形起伏",
+            },
+            {
+              value: "1.5.2.8",
+              label: "单点高程查询",
+            },
+            {
+              value: "1.5.2.9",
+              label: "两点高程差",
+            },
+            {
+              value: "1.5.2.10",
+              label: "区域高程统计",
+            },
+            {
+              value: "1.5.4.5",
+              label: "缓冲区计算",
+            },
+          ],
+        },
+        {
+          value: "1.5.3",
+          label: "1.5.3几何运算",
+          children: [
+            {
+              value: "1.5.3.1",
+              label: "并集运算",
+            },
+            {
+              value: "1.5.3.2",
+              label: "交集运算",
+            },
+            {
+              value: "1.5.3.3",
+              label: "差集运算",
+            },
+            {
+              value: "1.5.3.4",
+              label: "几何参数计算",
+            },
+          ],
+        },
+        {
+          value: "1.5.4",
+          label: "1.5.4关系分析",
+          children: [
+            {
+              value: "1.5.4.1",
+              label: "邻接关系分析",
+            },
+            {
+              value: "1.5.4.2",
+              label: "包含关系分析",
+            },
+            {
+              value: "1.5.4.3",
+              label: "相交关系分析",
+            },
+            {
+              value: "1.5.4.4",
+              label: "相离关系分析",
+            },
+          ],
+        },
+        {
+          value: "1.5.5",
+          label: "1.5.5非空间数据转换",
+          children: [
+            {
+              value: "1.5.5.1",
+              label: "非空间数据转空间数据",
+            },
+          ],
+        },
+        {
+          value: "1.5.6",
+          label: "1.5.6坐标转换",
+          children: [
+            {
+              value: "1.5.6.1",
+              label: "单点的坐标转换接口",
+            },
+          ],
+        },
+        {
+          value: "1.5.7",
+          label: "1.5.7时空数据格式转换",
+          children: [
+            {
+              value: "1.5.7.1",
+              label: "文件格式转换",
+            },
+            {
+              value: "1.5.7.2",
+              label: "转换文件下载",
+            },
+          ],
+        },
+      ],
+      // 场景规则
+      SceneRule: {
+        "1.5.1.1": {
+          // 功能描述
+          functionalDefinition:
+            "拓扑计算功能聚焦于深度解析大批量数据的点、线、面之间的拓扑关系,能够精准判断点与线、面的位置关系,以及线、面之间的相交、平行、邻接、包含等复杂状态。借助四叉树、R 树等精心设计的数据结构,结合 Douglas - Peucker 等高效算法,确保在处理海量空间数据时,也能快速且准确地输出分析结果。",
+          // 元素类型
+          elementTypes: ["point", "polyline", "polygon"],
+          // 元素个数
+          numberOf: 2,
+          // 后台接口路径
+          apiUrl: "/topology/geoJsonToGeoJson",
+        },
+        "1.5.2.1": {
+          // 功能描述
+          functionalDefinition:
+            "计算两个点要素之间的最短空间距离,单位支持米、千米。操作逻辑为:用户通过接口输入两点经纬度坐标,或在系统内部地图上点击选择两点,系统基于坐标系参数计算欧氏距离。例如:计算青浦区某学校与社区卫生服务中心的直线距离,用于公共服务覆盖半径评估;计算两个监测点位的直线距离,用于监测设备信号覆盖范围判断。",
+          // 元素类型
+          elementTypes: ["point", "point", "unit"],
+          // 单位
+          unit: [
+            {
+              value: "METER",
+              label: "米",
+            },
+            {
+              value: "KILOMETER",
+              label: "千米",
+            },
+          ],
+          // 元素个数
+          numberOf: 2,
+          // 后台接口路径
+          apiUrl: "/spatialMeasure/pointToPoint",
+        },
+        "1.5.2.2": {
+          // 功能描述
+          functionalDefinition: "计算点和线要素之间的最短空间距离,单位支持米、千米",
+          // 元素类型
+          elementTypes: ["point", "polyline", "unit"],
+          // 单位
+          unit: [
+            {
+              value: "METER",
+              label: "米",
+            },
+            {
+              value: "KILOMETER",
+              label: "千米",
+            },
+          ],
+          // 元素个数
+          numberOf: 2,
+          // 后台接口路径
+          apiUrl: "/spatialMeasure/pointToLine",
+        },
+        "1.5.2.3": {
+          // 功能描述
+          functionalDefinition: "计算点和面要素之间的最短空间距离,单位支持米、千米",
+          // 元素类型
+          elementTypes: ["point", "polygon", "unit"],
+          // 单位
+          unit: [
+            {
+              value: "METER",
+              label: "米",
+            },
+            {
+              value: "KILOMETER",
+              label: "千米",
+            },
+          ],
+          // 元素个数
+          numberOf: 2,
+          // 后台接口路径
+          apiUrl: "/spatialMeasure/pointToPolygon",
+        },
+        "1.5.2.4": {
+          // 功能描述
+          functionalDefinition: "计算两个面要素之间的最短空间距离,单位支持米、千米",
+          // 元素类型
+          elementTypes: ["polygon", "polygon", "unit"],
+          // 单位
+          unit: [
+            {
+              value: "METER",
+              label: "米",
+            },
+            {
+              value: "KILOMETER",
+              label: "千米",
+            },
+          ],
+          // 元素个数
+          numberOf: 2,
+          // 后台接口路径
+          apiUrl: "/spatialMeasure/PolygonToPolygon",
+        },
+        "1.5.2.5": {
+          // 功能描述
+          functionalDefinition: "计算多条线段组成的折线总长度,单位支持米、千米",
+          // 元素类型
+          elementTypes: ["polyline", "unit"],
+          // 单位
+          unit: [
+            {
+              value: "METER",
+              label: "米",
+            },
+            {
+              value: "KILOMETER",
+              label: "千米",
+            },
+          ],
+          // 元素个数
+          numberOf: 1,
+          // 后台接口路径
+          apiUrl: "/spatialMeasure/calculatePolylineDistance",
+        },
+        "1.5.2.6": {
+          // 功能描述
+          functionalDefinition:
+            "计算面要素在二维平面上的投影面积,单位支持平方米、公顷、平方千米切换",
+          // 元素类型
+          elementTypes: ["polygon", "unit"],
+          // 单位
+          unit: [
+            {
+              value: "SQUARE_METER",
+              label: "平方米",
+            },
+            {
+              value: "HECTARE",
+              label: "公顷",
+            },
+            {
+              value: "SQUARE_KILOMETER",
+              label: "平方千米",
+            },
+          ],
+          // 元素个数
+          numberOf: 1,
+          // 后台接口路径
+          apiUrl: "/spatialMeasure/calculatePlaneAreaInGeometry",
+        },
+        "1.5.2.7": {
+          // 功能描述
+          functionalDefinition:
+            "结合 DEM 高程数据,计算面要素在三维地形上的实际曲面面积(考虑地形起伏),精度依赖 DEM 数据分辨率。,单位支持平方米、公顷、平方千米切换",
+          // 元素类型
+          elementTypes: ["polygon", "unit"],
+          // 单位
+          unit: [
+            {
+              value: "SQUARE_METER",
+              label: "平方米",
+            },
+            {
+              value: "HECTARE",
+              label: "公顷",
+            },
+            {
+              value: "SQUARE_KILOMETER",
+              label: "平方千米",
+            },
+          ],
+          // 元素个数
+          numberOf: 1,
+          // 后台接口路径
+          apiUrl: "/spatialMeasure/calculateSurfaceAreaInGeometry",
+        },
+        "1.5.2.8": {
+          // 功能描述
+          functionalDefinition:
+            "查询单个点要素的海拔高度(基于 CGCS2000 大地高程基准),单位为米",
+          // 元素类型
+          elementTypes: ["point"],
+          // 元素个数
+          numberOf: 1,
+          // 后台接口路径
+          apiUrl: "/spatialMeasure/getPointElevationInGeometry",
+        },
+        "1.5.2.9": {
+          // 功能描述
+          functionalDefinition:
+            "计算两个点要素之间的高程差值,支持正负值显示(正值表示前者高于后者,负值相反)",
+          // 元素类型
+          elementTypes: ["point", "point"],
+          // 元素个数
+          numberOf: 2,
+          // 后台接口路径
+          apiUrl: "/spatialMeasure/calculateElevationDiffInGeometry",
+        },
+        "1.5.2.10": {
+          // 功能描述
+          functionalDefinition:
+            "查询单个面要素的海拔高度(基于 CGCS2000 大地高程基准),单位为米",
+          // 元素类型
+          elementTypes: ["polygon"],
+          // 元素个数
+          numberOf: 1,
+          // 后台接口路径
+          apiUrl: "/spatialMeasure/calculateAreaElevationStatsInGeometry",
+        },
+        "1.5.3.1": {
+          // 功能描述
+          functionalDefinition:
+            "将两个或多个不重叠 / 部分重叠的面要素,合并为一个连续的新面要素(消除重叠部分,保留所有要素的覆盖范围)",
+          // 元素类型
+          elementTypes: ["polygon"],
+          // 元素个数
+          minNumberOf: 2,
+          // 后台接口路径
+          apiUrl: "/geometry/union",
+        },
+        "1.5.3.2": {
+          // 功能描述
+          functionalDefinition:
+            "提取两个或多个面要素之间的重叠区域,生成新的面要素(仅保留所有要素共同覆盖的范围)",
+          // 元素类型
+          elementTypes: ["polygon"],
+          // 元素个数
+          minNumberOf: 2,
+          // 后台接口路径
+          apiUrl: "/geometry/intersection",
+        },
+        "1.5.3.3": {
+          // 功能描述
+          functionalDefinition:
+            "以一个面要素(称为 “源面”)为基础,减去与另一个面要素(称为 “裁剪面”)的重叠区域,生成新的面要素(保留源面中未被裁剪面覆盖的部分)",
+          // 元素类型
+          elementTypes: ["polygon"],
+          // 元素个数
+          minNumberOf: 2,
+          // 后台接口路径
+          apiUrl: "/geometry/difference",
+        },
+        "1.5.3.4": {
+          // 功能描述
+          functionalDefinition:
+            "计算面要素的参数(面积、周长、中心坐标、最小外接矩形等)",
+          // 元素类型
+          elementTypes: ["point", "polyline", "polygon"],
+          // 元素个数
+          numberOf: 1,
+          // 后台接口路径
+          apiUrl: "/geometry/parameters",
+        },
+        "1.5.4.1": {
+          // 功能描述
+          functionalDefinition:
+            "判断两个面要素是否 “边界接触但不重叠”,且距离小于等于自定义阈值(默认 1 米,可通过接口参数调整),核心用于 “相邻区域联动” 场景",
+          // 元素类型
+          elementTypes: ["polygon", "polygon", "EPSILON"],
+          // 元素个数
+          numberOf: 2,
+          // 后台接口路径
+          apiUrl: "/relationAnalys/adjacent",
+        },
+        "1.5.4.2": {
+          // 功能描述
+          functionalDefinition:
+            "判断一个要素(含点、线、面)是否完全位于另一个面要素内部,且无任何部分超出,核心用于 “归属界定” 场景",
+          // 元素类型
+          elementTypes: ["point", "polyline", "polygon", "EPSILON"],
+          // 元素个数
+          numberOf: 2,
+          // 后台接口路径
+          apiUrl: "/relationAnalys/include",
+        },
+        "1.5.4.3": {
+          // 功能描述
+          functionalDefinition:
+            "判断两个要素(点与线、点与面、线与线、线与面、面与面)是否存在空间重叠(含部分重叠),核心用于 “交叉影响” 场景",
+          // 元素类型
+          elementTypes: ["point", "polyline", "polygon", "EPSILON"],
+          // 元素个数
+          numberOf: 2,
+          // 后台接口路径
+          apiUrl: "/relationAnalys/intersect",
+        },
+        "1.5.4.4": {
+          // 功能描述
+          functionalDefinition:
+            "判断两个要素(点、线、面)之间无任何空间接触(含边界接触),且最小距离大于自定义阈值(默认 10 米,可通过接口参数调整),核心用于 “无关联排除” 场景",
+          // 元素类型
+          elementTypes: ["point", "polyline", "polygon", "EPSILON"],
+          // 元素个数
+          numberOf: 2,
+          // 后台接口路径
+          apiUrl: "/relationAnalys/parting",
+        },
+        "1.5.4.5": {
+          // 功能描述
+          functionalDefinition:
+            "以一个核心要素(点、线、面)为中心,按自定义半径(如 500 米、1 千米)划定范围,查询范围内所有目标要素(如设施、地块、道路),核心用于 “资源覆盖范围” 场景。由于不是我负责,暂时为缓冲区计算功能。​",
+          // 元素类型
+          elementTypes: ["point", "polyline", "polygon", "EPSILON"],
+          // 元素个数
+          numberOf: 2,
+          // 后台接口路径
+          apiUrl: "/relationAnalys/neighbor",
+        },
+        "1.5.5.1": {
+          // 功能描述
+          functionalDefinition:
+            "非空间数据转空间数据功能支持常见格式的非空间数据转换,如 CSV、Excel 表格数据等。通过与地理编码服务紧密结合,关联空间参考信息(如地址、行政区域、关联 ID),转换为带坐标的空间要素(点、线、面),将表格中的地址、坐标等信息转换为具有空间位置的矢量数据的功能,并依据用户设定规则赋予属性信息。核心解决 “业务数据与地图引擎关联” 的问题,实现 “数据可视化” 与 “空间分析” 的联动。在智慧城市建设中,整合人口、环境、交通等多源非空间数据,实现数据融合分析,为城市精细化管理提供数据支持;在地理大数据分析中,拓展非空间数据的应用维度,挖掘数据潜在价值。",
+          // 元素类型
+          elementTypes: ["file"],
+          // 元素个数
+          numberOf: 0,
+          // 后台接口路径
+          apiUrl: "/nonStandardToStandard/importFile",
+        },
+        "1.5.6.1": {
+          // 功能描述
+          functionalDefinition:
+            "坐标转换是基于青浦区三维数字底板的坐标系统(SH2000投影坐标系),将空间要素的坐标在不同坐标系之间进行转换的功能,包括地理坐标系(如 WGS84、上海 2000 等)和投影坐标系(如高斯 - 克吕格投影、UTM 投影等)",
+          // 元素类型
+          elementTypes: ["lon", "lat", "inPrj", "outPrj"],
+          inPrj: [
+            {
+              value: "WGS84",
+              label: "WGS84",
+            },
+            {
+              value: "SH2000",
+              label: "SH2000",
+            },
+            {
+              value: "UTM",
+              label: "UTM",
+            },
+          ],
+          outPrj: [
+            {
+              value: "WGS84",
+              label: "WGS84",
+            },
+            {
+              value: "SH2000",
+              label: "SH2000",
+            },
+            {
+              value: "UTM",
+              label: "UTM",
+            },
+          ],
+          // 元素个数
+          numberOf: 0,
+          // 后台接口路径
+          apiUrl: "/coordinateSystemTransformation/pointTransformation",
+        },
+        "1.5.7.1": {
+          // 功能描述
+          functionalDefinition:
+            "时空数据格式转换是将青浦区三维数字底板中的时空数据(含空间坐标与时间属性的数据,如轨迹数据、时序监测数据),在不同标准数据格式之间进行转换的功能,如 Shapefile、GeoJSON、KML、NetCDF 等。核心支持 “矢量数据格式转换”“栅格数据格式转换”“时空轨迹数据格式转换” 三类转换,确保数据能在不同 GIS 软件、不同应用系统之间兼容流转,不同部门、不同系统之间顺利读取和处理时空数据;在大数据存储和管理时,根据不同应用场景和存储需求,选择合适的数据格式。提高数据存储和访问效率,满足数据共享、分析、展示的多场景需求。",
+          // 元素类型
+          elementTypes: ["file", "outFileType", "lonKey", "latKey"],
+          outFileType: [
+            {
+              value: "geoJson",
+              label: "geoJson",
+            },
+            {
+              value: "shp",
+              label: "shp",
+            },
+            {
+              value: "csv",
+              label: "csv",
+            },
+            {
+              value: "xlsx",
+              label: "xlsx",
+            },
+          ],
+          // 元素个数
+          numberOf: 0,
+          // 后台接口路径
+          apiUrl: "/spatiotemporal_data_format_conversion/fileFormatConversion",
+        },
+        "1.5.7.2": {
+          // 功能描述
+          functionalDefinition: "转换文件下载",
+          // 元素类型
+          elementTypes: ["filePath"],
+          // 元素个数
+          numberOf: 0,
+          // 后台接口路径
+          apiUrl: "/spatiotemporal_data_format_conversion/downloadFile",
+        },
+      },
+      currentTool: null,
+      drawingMode: null,
+      handler: null,
+      drawnEntities: [],
+      geometries: [], // 存储绘制的几何对象
+      // 绘制状态相关
+      currentEntity: null, // 当前正在绘制的实体
+      currentPositions: [], // 当前正在绘制的位置数组
+      isDrawingHole: false, // 是否正在绘制镂空
+      currentPolygonEntity: null, // 当前要添加镂空的多边形实体
+      currentPolygonGeometry: null, // 当前要添加镂空的多边形几何对象
+      tempEntity: null, // 临时预览实体
+    };
+  },
+  mounted() {
+    this.SceneValue = this.$route.query.sceneId ? this.$route.query.sceneId : "";
+  },
+
+  beforeUnmount() {
+    // 组件卸载前清理资源
+    this.deactivateDraw();
+    if (this.handler) {
+      this.handler.destroy();
+    }
+  },
+  methods: {
+    handleExceed(file) {
+      this.$message({
+        message: "最多只能上传一个文件",
+        type: "warning",
+      });
+    },
+    handleFileChange(file, fileList) {
+      this.currentFile = file; // 注意:Element UI Plus 无需 .raw
+    },
+    uploadRemove() {
+      this.currentFile = null;
+    },
+    handleJsonInput(value) {
+      this.jsonData = JSON.parse(value);
+    },
+    handleChange(emit) {
+      this.params.unit = "";
+      this.currentFile = null;
+      // 清除所有地图中的元素
+      this.clearAll();
+      this.jsonData = {};
+      this.backData = {};
+      this.SceneValue = emit[emit.length - 1];
+    },
+    // 初始化绘制处理器
+    initDrawHandler() {
+      // 创建绘制处理器
+      this.handler = new SkyScenery.ScreenSpaceEventHandler(viewer.canvas);
+    },
+
+    // 激活绘制工具
+    activateDraw(type) {
+      // 取消镂空绘制模式
+      this.isDrawingHole = false;
+      // 如果已经是当前激活的工具,则取消激活
+      if (this.currentTool === type) {
+        this.deactivateDraw();
+        return;
+      }
+      // 先取消之前的绘制模式
+      this.deactivateDraw();
+      // 设置当前工具
+      this.currentTool = type;
+      this.drawingMode = type;
+      // 重置当前绘制状态
+      this.resetDrawingState();
+      switch (type) {
+        case "point":
+          this.drawPoint();
+          break;
+        case "polyline":
+          this.drawPolyline();
+          break;
+        case "polygon":
+          this.drawPolygon();
+          break;
+      }
+    },
+
+    // 重置绘制状态
+    resetDrawingState() {
+      this.currentPositions = [];
+      this.currentEntity = null;
+      // 移除临时预览实体
+      if (this.tempEntity) {
+        viewer.entities.remove(this.tempEntity);
+        viewer.scene.requestRender();
+        this.tempEntity = null;
+      }
+    },
+
+    // 取消绘制模式
+    deactivateDraw() {
+      if (this.handler) {
+        // 移除所有事件监听器
+        this.handler.removeInputAction(SkyScenery.ScreenSpaceEventType.LEFT_CLICK);
+        this.handler.removeInputAction(SkyScenery.ScreenSpaceEventType.MOUSE_MOVE);
+        this.handler.removeInputAction(SkyScenery.ScreenSpaceEventType.RIGHT_CLICK);
+      }
+      // 重置状态
+      this.resetDrawingState();
+      this.currentTool = null;
+      this.drawingMode = null;
+      this.isDrawingHole = false;
+    },
+
+    // 绘制点
+    drawPoint() {
+      const that = this;
+      this.handler.setInputAction(function (event) {
+        const ray = viewer.camera.getPickRay(event.position);
+        const position = viewer.scene.globe.pick(ray, viewer.scene);
+        if (position) {
+          const cartographic = SkyScenery.Cartographic.fromCartesian(position);
+          const longitude = SkyScenery.Math.toDegrees(cartographic.longitude);
+          const latitude = SkyScenery.Math.toDegrees(cartographic.latitude);
+          // 创建点实体(实时渲染)
+          const entity = viewer.entities.add({
+            position: position,
+            point: {
+              show: true,
+              color: SkyScenery.Color.RED,
+              pixelSize: 10,
+              outlineColor: SkyScenery.Color.WHITE,
+              outlineWidth: 2,
+            },
+          });
+          viewer.scene.requestRender();
+          that.drawnEntities.push(entity);
+          // 转换为geometry格式并保存
+          const geometry = {
+            type: "Point",
+            coordinates: [longitude, latitude],
+          };
+          that.geometries.push(geometry);
+          that.changeGeometries();
+          console.log("绘制了点:", geometry);
+        }
+      }, SkyScenery.ScreenSpaceEventType.LEFT_CLICK);
+    },
+
+    // 绘制线
+    drawPolyline() {
+      const that = this;
+      // 鼠标移动时更新临时线(实时渲染预览)
+      this.handler.setInputAction(function (event) {
+        if (that.currentPositions.length > 0) {
+          const ray = viewer.camera.getPickRay(event.endPosition);
+          const endPosition = viewer.scene.globe.pick(ray, viewer.scene);
+
+          if (endPosition) {
+            const tempPositions = [...that.currentPositions, endPosition];
+
+            // 更新临时预览实体
+            if (!that.tempEntity) {
+              that.tempEntity = viewer.entities.add({
+                polyline: {
+                  show: true,
+                  positions: tempPositions,
+                  material: SkyScenery.Color.BLUE.withAlpha(0.5),
+                  width: 3,
+                },
+              });
+              viewer.scene.requestRender();
+            } else {
+              that.tempEntity.polyline.positions = tempPositions;
+            }
+          }
+        }
+      }, SkyScenery.ScreenSpaceEventType.MOUSE_MOVE);
+
+      // 左键点击添加点
+      this.handler.setInputAction(function (event) {
+        const ray = viewer.camera.getPickRay(event.position);
+        const position = viewer.scene.globe.pick(ray, viewer.scene);
+
+        if (position) {
+          that.currentPositions.push(position.clone());
+
+          // 如果是第一个点,不需要更新实体
+          if (that.currentPositions.length > 1) {
+            // 如果已有实体,更新它
+            if (that.currentEntity) {
+              viewer.entities.remove(that.currentEntity);
+            }
+
+            // 创建当前实体(实时渲染)
+            that.currentEntity = viewer.entities.add({
+              polyline: {
+                show: true,
+                positions: [...that.currentPositions],
+                material: SkyScenery.Color.BLUE,
+                width: 3,
+              },
+            });
+            viewer.scene.requestRender();
+          }
+        }
+      }, SkyScenery.ScreenSpaceEventType.LEFT_CLICK);
+
+      // 右键点击完成绘制
+      this.handler.setInputAction(function () {
+        if (that.currentPositions.length > 1) {
+          // 移除临时预览实体
+          if (that.tempEntity) {
+            viewer.entities.remove(that.tempEntity);
+            that.tempEntity = null;
+          }
+
+          // 否则创建线
+          // 确保当前实体存在
+          if (!that.currentEntity) {
+            that.currentEntity = viewer.entities.add({
+              polyline: {
+                show: true,
+                positions: [...that.currentPositions],
+                material: SkyScenery.Color.BLUE,
+                width: 3,
+              },
+            });
+          }
+
+          that.drawnEntities.push(that.currentEntity);
+          viewer.scene.requestRender();
+
+          // 转换为geometry格式并保存
+          const coordinates = [];
+          that.currentPositions.forEach((pos) => {
+            const cartographic = SkyScenery.Cartographic.fromCartesian(pos);
+            coordinates.push([
+              SkyScenery.Math.toDegrees(cartographic.longitude),
+              SkyScenery.Math.toDegrees(cartographic.latitude),
+            ]);
+          });
+
+          const geometry = {
+            type: "LineString",
+            coordinates: coordinates,
+          };
+          that.geometries.push(geometry);
+          that.changeGeometries();
+          console.log("绘制了线:", geometry);
+          // 取消当前绘制模式
+          that.deactivateDraw();
+        }
+      }, SkyScenery.ScreenSpaceEventType.RIGHT_CLICK);
+    },
+
+    // 绘制面
+    drawPolygon() {
+      const that = this;
+
+      // 鼠标移动时更新临时面(实时渲染预览)
+      this.handler.setInputAction(function (event) {
+        if (that.currentPositions.length > 0) {
+          const ray = viewer.camera.getPickRay(event.endPosition);
+          const endPosition = viewer.scene.globe.pick(ray, viewer.scene);
+
+          if (endPosition) {
+            // 为了预览效果,临时闭合多边形
+            const tempPositions = [
+              ...that.currentPositions,
+              endPosition,
+              that.currentPositions[0],
+            ];
+
+            // 更新临时预览实体
+            if (!that.tempEntity) {
+              that.tempEntity = viewer.entities.add({
+                polygon: {
+                  show: true,
+                  hierarchy: new SkyScenery.PolygonHierarchy(tempPositions),
+                  material: new SkyScenery.ColorMaterialProperty(
+                    new SkyScenery.Color(0, 0, 1, 0.2)
+                  ),
+                  outline: true,
+                  outlineColor: SkyScenery.Color.BLUE.withAlpha(0.5),
+                  outlineWidth: 2,
+                },
+              });
+              viewer.scene.requestRender();
+            } else {
+              that.tempEntity.polygon.hierarchy = new SkyScenery.PolygonHierarchy(
+                tempPositions
+              );
+            }
+          }
+        }
+      }, SkyScenery.ScreenSpaceEventType.MOUSE_MOVE);
+
+      // 左键点击添加点
+      this.handler.setInputAction(function (event) {
+        const ray = viewer.camera.getPickRay(event.position);
+        const position = viewer.scene.globe.pick(ray, viewer.scene);
+
+        if (position) {
+          // 检查是否点击了第一个点附近(自动闭合)
+          if (
+            that.currentPositions.length > 2 &&
+            that.isPositionNearFirst(position, that.currentPositions[0])
+          ) {
+            // 完成多边形绘制
+            that.finalizePolygonDrawing();
+            that.deactivateDraw();
+            return;
+          }
+
+          that.currentPositions.push(position.clone());
+
+          // 如果有足够的点,更新多边形实体(实时渲染)
+          if (that.currentPositions.length > 2) {
+            // 闭合多边形
+            const closedPositions = [...that.currentPositions, that.currentPositions[0]];
+            // 如果已有实体,更新它
+            if (that.currentEntity) {
+              viewer.entities.remove(that.currentEntity);
+            }
+
+            // 创建当前实体
+            that.currentEntity = viewer.entities.add({
+              polygon: {
+                show: true,
+                hierarchy: new SkyScenery.PolygonHierarchy(closedPositions),
+                material: new SkyScenery.ColorMaterialProperty(
+                  new SkyScenery.Color(0, 0, 1, 0.3)
+                ),
+                outline: true,
+                outlineColor: SkyScenery.Color.BLUE,
+                outlineWidth: 2,
+              },
+            });
+            viewer.scene.requestRender();
+          }
+        }
+      }, SkyScenery.ScreenSpaceEventType.LEFT_CLICK);
+
+      // 右键点击完成绘制
+      this.handler.setInputAction(function () {
+        if (that.currentPositions.length > 2) {
+          that.finalizePolygonDrawing();
+          // 取消当前绘制模式
+          that.deactivateDraw();
+        }
+      }, SkyScenery.ScreenSpaceEventType.RIGHT_CLICK);
+    },
+
+    // 完成多边形绘制
+    finalizePolygonDrawing() {
+      // 移除临时预览实体
+      if (this.tempEntity) {
+        viewer.entities.remove(this.tempEntity);
+        this.tempEntity = null;
+      }
+
+      // 确保多边形是闭合的
+      let closedPositions = [...this.currentPositions];
+      // 创建最终的多边形实体
+      if (this.currentEntity) {
+        viewer.entities.remove(this.currentEntity);
+      }
+
+      this.currentEntity = viewer.entities.add({
+        polygon: {
+          show: true,
+          hierarchy: new SkyScenery.PolygonHierarchy(closedPositions),
+          material: new SkyScenery.ColorMaterialProperty(
+            new SkyScenery.Color(0, 0, 1, 0.3)
+          ),
+          outline: true,
+          outlineColor: SkyScenery.Color.BLUE,
+          outlineWidth: 2,
+        },
+      });
+      viewer.scene.requestRender();
+
+      this.drawnEntities.push(this.currentEntity);
+
+      // 转换为geometry格式并保存(不包含重复的最后一个点)
+      const coordinates = [];
+      const mainRing = [];
+      for (let i = 0; i < closedPositions.length; i++) {
+        const cartographic = SkyScenery.Cartographic.fromCartesian(closedPositions[i]);
+        mainRing.push([
+          SkyScenery.Math.toDegrees(cartographic.longitude),
+          SkyScenery.Math.toDegrees(cartographic.latitude),
+        ]);
+      }
+      const cartographic = SkyScenery.Cartographic.fromCartesian(closedPositions[0]);
+      mainRing.push([
+        SkyScenery.Math.toDegrees(cartographic.longitude),
+        SkyScenery.Math.toDegrees(cartographic.latitude),
+      ]);
+      coordinates.push(mainRing);
+
+      const geometry = {
+        type: "Polygon",
+        coordinates: coordinates, // GeoJSON格式
+        holes: [], // 用于存储镂空区域
+      };
+
+      // 保存实体和几何对象的关联
+      this.currentEntity.geometryRef = geometry;
+      this.geometries.push(geometry);
+      this.changeGeometries();
+      console.log("绘制了面:", geometry);
+    },
+
+    // 开始绘制镂空
+    startHoleDrawing() {
+      // 如果已经在绘制镂空,则取消
+      if (this.isDrawingHole) {
+        this.isDrawingHole = false;
+        this.deactivateDraw();
+        return;
+      }
+
+      // 取消其他绘制模式
+      this.deactivateDraw();
+
+      // 设置为镂空绘制模式
+      this.isDrawingHole = true;
+
+      console.log("请点击一个多边形以添加镂空");
+
+      // 设置点击事件选择多边形
+      const that = this;
+      this.handler.setInputAction(function (event) {
+        // 拾取实体
+        const pickedObject = viewer.scene.pick(event.position);
+        if (pickedObject && pickedObject.id && pickedObject.id.polygon) {
+          const polygonEntity = pickedObject.id;
+
+          // 检查是否有对应的几何对象
+          if (polygonEntity.geometryRef && polygonEntity.geometryRef.type === "Polygon") {
+            that.currentPolygonEntity = polygonEntity;
+            that.currentPolygonGeometry = polygonEntity.geometryRef;
+
+            console.log("已选择多边形,现在绘制镂空区域");
+
+            // 开始绘制镂空区域(使用多边形绘制逻辑,但最后添加为镂空)
+            that.resetDrawingState();
+            that.drawHole();
+          } else {
+            console.log("选择的不是有效的多边形");
+          }
+        } else {
+          console.log("未选中任何多边形");
+        }
+      }, SkyScenery.ScreenSpaceEventType.LEFT_CLICK);
+    },
+
+    // 绘制镂空区域
+    drawHole() {
+      const that = this;
+
+      // 清除之前的事件
+      this.handler.removeInputAction(SkyScenery.ScreenSpaceEventType.LEFT_CLICK);
+
+      // 鼠标移动时更新临时镂空区域
+      this.handler.setInputAction(function (event) {
+        if (that.currentPositions.length > 0) {
+          const ray = viewer.camera.getPickRay(event.endPosition);
+          const endPosition = viewer.scene.globe.pick(ray, viewer.scene);
+
+          if (endPosition) {
+            // 为了预览效果,临时闭合多边形
+            const tempPositions = [
+              ...that.currentPositions,
+              endPosition,
+              that.currentPositions[0],
+            ];
+
+            // 更新临时预览实体
+            if (!that.tempEntity) {
+              that.tempEntity = viewer.entities.add({
+                polygon: {
+                  show: true,
+                  hierarchy: new SkyScenery.PolygonHierarchy(tempPositions),
+                  material: new SkyScenery.ColorMaterialProperty(
+                    new SkyScenery.Color(1, 0, 0, 0.3)
+                  ),
+                  outline: true,
+                  outlineColor: SkyScenery.Color.RED,
+                  outlineWidth: 2,
+                },
+              });
+              viewer.scene.requestRender();
+            } else {
+              that.tempEntity.polygon.hierarchy = new SkyScenery.PolygonHierarchy(
+                tempPositions
+              );
+            }
+          }
+        }
+      }, SkyScenery.ScreenSpaceEventType.MOUSE_MOVE);
+
+      // 左键点击添加点
+      this.handler.setInputAction(function (event) {
+        const ray = viewer.camera.getPickRay(event.position);
+        const position = viewer.scene.globe.pick(ray, viewer.scene);
+
+        if (position) {
+          // 检查是否点击了第一个点附近(自动闭合)
+          if (
+            that.currentPositions.length > 2 &&
+            that.isPositionNearFirst(position, that.currentPositions[0])
+          ) {
+            // 完成镂空绘制
+            that.finalizeHoleDrawing();
+            return;
+          }
+
+          that.currentPositions.push(position.clone());
+        }
+      }, SkyScenery.ScreenSpaceEventType.LEFT_CLICK);
+
+      // 右键点击完成绘制
+      this.handler.setInputAction(function () {
+        if (that.currentPositions.length > 2) {
+          that.finalizeHoleDrawing();
+        }
+      }, SkyScenery.ScreenSpaceEventType.RIGHT_CLICK);
+    },
+
+    // 完成镂空绘制
+    finalizeHoleDrawing() {
+      // 移除临时预览实体
+      if (this.tempEntity) {
+        viewer.entities.remove(this.tempEntity);
+        viewer.scene.requestRender();
+        this.tempEntity = null;
+      }
+
+      // 确保镂空区域是闭合的
+      let closedPositions = [...this.currentPositions];
+
+      // 转换为几何坐标
+      const holeCoordinates = [];
+      for (let i = 0; i < closedPositions.length; i++) {
+        const cartographic = SkyScenery.Cartographic.fromCartesian(closedPositions[i]);
+        holeCoordinates.push([
+          SkyScenery.Math.toDegrees(cartographic.longitude),
+          SkyScenery.Math.toDegrees(cartographic.latitude),
+        ]);
+      }
+
+      const cartographic = SkyScenery.Cartographic.fromCartesian(closedPositions[0]);
+      holeCoordinates.push([
+        SkyScenery.Math.toDegrees(cartographic.longitude),
+        SkyScenery.Math.toDegrees(cartographic.latitude),
+      ]);
+      // 添加到几何对象的镂空数组
+      if (!this.currentPolygonGeometry.holes) {
+        this.currentPolygonGeometry.holes = [];
+      }
+      this.currentPolygonGeometry.holes.push(holeCoordinates);
+
+      // 更新多边形的层级结构以包含镂空
+      const polygonHierarchy = this.buildPolygonHierarchyWithHoles(
+        this.currentPolygonGeometry.coordinates[0],
+        this.currentPolygonGeometry.holes
+      );
+
+      // 更新实体显示
+      this.currentPolygonEntity.polygon.hierarchy = polygonHierarchy;
+
+      console.log("添加了镂空区域:", holeCoordinates);
+      console.log("更新后的多边形几何:", this.currentPolygonGeometry);
+
+      // 重置状态
+      this.resetDrawingState();
+      this.isDrawingHole = false;
+      this.currentPolygonEntity = null;
+      this.currentPolygonGeometry = null;
+
+      // 取消绘制模式
+      this.deactivateDraw();
+    },
+
+    // 构建包含镂空的多边形层级结构
+    buildPolygonHierarchyWithHoles(outerRing, holes) {
+      // 将外部环转换为Cartesian3数组
+      const outerRingPositions = [];
+      outerRing.forEach((coord) => {
+        const cartesian = SkyScenery.Cartesian3.fromDegrees(coord[0], coord[1]);
+        outerRingPositions.push(cartesian);
+      });
+      // 闭合外部环
+      outerRingPositions.push(outerRingPositions[0]);
+
+      // 创建层级结构
+      const hierarchy = new SkyScenery.PolygonHierarchy(outerRingPositions);
+
+      // 添加镂空
+      if (holes && holes.length > 0) {
+        hierarchy.holes = holes.map((hole) => {
+          const holePositions = [];
+          hole.forEach((coord) => {
+            const cartesian = SkyScenery.Cartesian3.fromDegrees(coord[0], coord[1]);
+            holePositions.push(cartesian);
+          });
+          // 闭合镂空环
+          holePositions.push(holePositions[0]);
+          return new SkyScenery.PolygonHierarchy(holePositions);
+        });
+      }
+      return hierarchy;
+    },
+
+    // 检查点是否靠近第一个点
+    isPositionNearFirst(position, firstPosition, tolerance = 10) {
+      // 单位:米
+      const distance = SkyScenery.Cartesian3.distance(position, firstPosition);
+      return distance < tolerance;
+    },
+    changeGeometries() {
+      let requestData = {};
+      const geometriesToSend = this.geometries;
+      let FeatureCollectionFeatures = [];
+      geometriesToSend.forEach((item) => {
+        FeatureCollectionFeatures.push({
+          type: "Feature",
+          properties: {},
+          geometry: {
+            type: item.type,
+            coordinates: item.coordinates,
+          },
+        });
+      });
+      // 构造请求数据
+      requestData = {
+        type: "FeatureCollection",
+        features: FeatureCollectionFeatures,
+        timestamp: new Date().getTime(),
+      };
+      if (
+        this.SceneValue &&
+        this.SceneRule[this.SceneValue] &&
+        this.SceneRule[this.SceneValue].elementTypes &&
+        this.SceneRule[this.SceneValue].elementTypes.includes("unit") &&
+        this.params.unit
+      ) {
+        requestData.unit = this.params.unit;
+      }
+      if (
+        this.SceneValue &&
+        this.SceneRule[this.SceneValue] &&
+        this.SceneRule[this.SceneValue].elementTypes &&
+        this.SceneRule[this.SceneValue].elementTypes.includes("EPSILON") &&
+        this.params.EPSILON
+      ) {
+        requestData.EPSILON = this.params.EPSILON;
+      }
+      this.jsonData = requestData;
+      return requestData;
+    },
+    // 发送几何数据到后台接口
+    sendGeometriesToBackend() {
+      // 这里使用axios发送请求,需要确保项目中已安装并导入axios
+      let requestData;
+      let requestUrl = this.SceneRule[this.SceneValue].apiUrl;
+      if (
+        this.SceneRule[this.SceneValue].elementTypes.includes("point") ||
+        this.SceneRule[this.SceneValue].elementTypes.includes("polyline") ||
+        this.SceneRule[this.SceneValue].elementTypes.includes("polygon")
+      ) {
+        requestData = this.changeGeometries();
+      } else {
+        requestData = new FormData();
+        this.SceneRule[this.SceneValue].elementTypes.forEach((key) => {
+          if (key == "file") {
+            if (!this.currentFile) {
+              return this.$message({
+                message: "请选择文件",
+                type: "error",
+              });
+            }
+            requestData.append("file", this.currentFile.raw);
+          } else {
+            requestData.append(key, this.params[key]);
+          }
+        });
+      }
+      let that = this;
+      // 实际项目中使用以下代码发送请求
+      skszk
+        .topology(requestUrl, requestData)
+        .then((res) => {
+          if (requestUrl.indexOf("downloadFile") == -1) {
+            that.backData = res;
+            if (res.code && res.code == 200) {
+              that.$message({
+                message: res.message,
+                type: "success",
+              });
+            } else {
+              that.$message({
+                message: res.content,
+                type: "error",
+              });
+            }
+          } else {
+            const blob = res; // 响应体是 Blob 类型
+            if (!blob) {
+              that.$message.error("下载失败:文件流为空");
+              reject("文件流为空");
+              return;
+            }
+            let _downloadFile = that.params.filePath + "";
+            let fileName = _downloadFile.substring(
+              _downloadFile.indexOf("down_files") + 11
+            );
+            // D:\work\backCode\one_map_portal_server\down_files\0378b1c2e92a4c36ab447f552c0c7888.xlsx
+            // 替换原代码中创建 url 的逻辑,先加校验
+            console.log("blob 类型:", Object.prototype.toString.call(blob)); // 正常应输出 [object Blob]
+            console.log("blob 大小:", blob.size); // 正常应大于 0
+
+            if (!(blob instanceof Blob) || blob.size === 0) {
+              this.$message.error("文件流解析失败,无法生成下载链接");
+              return;
+            }
+            const url = window.URL.createObjectURL(blob); // 将 Blob 转为临时 URL
+            const link = document.createElement("a");
+            link.href = url;
+            link.download = fileName; // 设置下载文件名
+            link.style.display = "none";
+            document.body.appendChild(link);
+            link.click(); // 触发点击下载
+            document.body.removeChild(link);
+            window.URL.revokeObjectURL(url); // 销毁临时 URL,避免内存泄漏
+          }
+        })
+        .catch((error) => {
+          console.error("后台接口调用失败:", error);
+        });
+    },
+
+    // 清除所有绘制的元素
+    clearAll() {
+      // 移除所有实体
+      this.drawnEntities.forEach((entity) => {
+        viewer.entities.remove(entity);
+      });
+      viewer.scene.requestRender();
+      // 清空数组
+      this.drawnEntities = [];
+      this.geometries = [];
+      this.changeGeometries();
+      // 取消当前绘制模式
+      this.deactivateDraw();
+
+      console.log("已清除所有绘制的元素");
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+// 操作栏样式
+#controlPanelBox {
+  width: calc(30vw - 2rem);
+  min-width: 500px;
+  height: 100vh;
+  display: flex;
+  flex-direction: column;
+  padding: 1rem;
+  position: fixed;
+  top: 0;
+  right: 0;
+  background: #fff;
+}
+
+// 绘制按钮区域
+.toolbar {
+  position: absolute;
+  top: 10px;
+  left: -120px;
+  z-index: 1000;
+  background: rgba(255, 255, 255, 0.9);
+  border-radius: 8px;
+  padding: 3px 5px;
+  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
+  display: flex;
+  flex-direction: column;
+  gap: 8px;
+}
+
+.tool-item {
+  padding: 10px 20px;
+  cursor: pointer;
+  border-radius: 4px;
+  transition: all 0.3s ease;
+  user-select: none;
+  font-size: 14px;
+}
+
+.tool-item:hover {
+  background: #f0f0f0;
+}
+
+.tool-item.active {
+  background: #409eff;
+  color: white;
+}
+.sceneNameBox {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+}
+</style>

+ 114 - 0
src/components/yxgl/EchartsDome.vue

@@ -0,0 +1,114 @@
+<template>
+  <div class="echartsDome">
+    <div class="echartsDome_title">{{ title }}</div>
+    <div class="echartsDome_chart" ref="chartContainer"></div>
+  </div>
+</template>
+
+<script>
+import * as echarts from "echarts";
+export default {
+  name: "EchartsDome",
+  props: {
+    title: {
+      type: String,
+      default: "图表标题",
+    },
+  },
+  data() {
+    return {
+      chart: null,
+    };
+  },
+  mounted() {
+    this.initChart();
+  },
+  methods: {
+    initChart() {
+      this.chart = echarts.init(this.$refs.chartContainer);
+
+      let option = {
+        tooltip: {
+          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",
+          ],
+          axisLine: { lineStyle: { color: "#fff" } }, // 坐标轴颜色
+          axisTick: { show: false }, // 隐藏刻度
+          splitLine: { show: false }, // 隐藏分割线
+        },
+        yAxis: {
+          type: "value",
+          max: 30,
+          axisLine: { lineStyle: { color: "#fff" } },
+          splitLine: {
+            lineStyle: {
+              type: "dashed", // 虚线网格
+              color: "#fff",
+            },
+          },
+        },
+        series: [
+          {
+            name: "调用次数",
+            type: "line",
+            data: [7, 7, 15, 19, 21, 22, 25, 26, 23, 19, 12], // 模拟数据
+            lineStyle: { color: "#42a5f5" }, // 蓝色线条
+            itemStyle: { color: "#42a5f5" }, // 节点颜色
+            symbol: "circle", // 节点形状
+            symbolSize: 6, // 节点大小
+          },
+          {
+            name: "平均响应时间",
+            type: "line",
+            data: [3, 3, 6, 12, 15, 17, 18, 17, 14, 10, 6], // 模拟数据
+            lineStyle: { color: "#4caf50" }, // 绿色线条
+            itemStyle: { color: "#4caf50" },
+            symbol: "circle",
+            symbolSize: 6,
+          },
+        ],
+      };
+      // 绘制图表
+      this.chart.setOption(option);
+    },
+  },
+};
+</script>
+
+<style scoped>
+.echartsDome {
+  width: 100%;
+  height: 400px;
+  background-color: #222;
+  /* color: #fff; */
+  padding: 20px;
+  box-sizing: border-box;
+}
+.echartsDome_title {
+  font-size: 20px;
+  font-weight: bold;
+}
+.echartsDome_chart {
+  width: 100%;
+  height: 360px;
+}
+</style>

+ 93 - 0
src/components/yxgl/card.vue

@@ -0,0 +1,93 @@
+<template>
+  <div>
+    <div class="leftInfo">
+      <div class="leftInfo_title">{{ title }}</div>
+      <div class="leftInfo_value">{{ value }}</div>
+      <div class="leftInfo_growth" :style="{ color: growthColors[upStatus + 1] }">
+        <el-icon
+          ><Top v-if="upStatus == 1" /><SemiSelect v-if="upStatus == 0" /><Bottom
+            v-if="upStatus == -1"
+        /></el-icon>
+        {{ growth }}
+      </div>
+    </div>
+    <div class="icon" :style="{ background: iconColor + '32' }">
+      <el-icon><Flag :color="iconColor" /></el-icon>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: "card",
+  props: {
+    title: {
+      type: String,
+      default: "",
+    },
+    value: {
+      type: String,
+      default: "",
+    },
+    growth: {
+      type: String,
+      default: "",
+    },
+    iconColor: {
+      type: String,
+      default: "#CCCCCC",
+    },
+    upStatus: {
+      type: Number,
+      //   -1 下降
+      //   0 持平
+      //   1 上升
+      default: -1,
+    },
+  },
+  data() {
+    return {
+      // 0 下降
+      // 1 持平
+      // 2 上升
+      growthColors: ["#F56C6C", "#909399", "#67C23A"],
+    };
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.leftInfo {
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  align-items: flex-start;
+}
+.leftInfo_title {
+  font-size: 12px;
+  color: #909399;
+  line-height: 20px;
+}
+.leftInfo_value {
+  margin-top: 4px;
+  font-size: 24px;
+  font-weight: bold;
+  line-height: 33px;
+}
+.leftInfo_growth {
+  margin-top: 12px;
+  font-size: 12px;
+  line-height: 20px;
+  font-weight: bold;
+}
+.icon {
+  width: 40px;
+  height: 40px;
+  border-radius: 5px;
+  background: #ccc;
+  font-size: 20px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+</style>

+ 7 - 0
src/main.js

@@ -8,11 +8,18 @@ import store from './store'
 initApp.use(store);
 
 import ElementPlus from 'element-plus'
+import zhCn from 'element-plus/es/locale/lang/zh-cn'
+
+initApp.use(ElementPlus, {
+    locale: zhCn,
+})
 import 'element-plus/dist/index.css'
 initApp.use(ElementPlus)
 
 // 如果您正在使用CDN引入,请删除下面一行。
 import * as ElementPlusIconsVue from '@element-plus/icons-vue'
+import JsonViewer from 'vue-json-viewer'
+initApp.component('JsonViewer', JsonViewer)
 
 for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
     initApp.component(key, component)

+ 5 - 14
src/router/index.js

@@ -30,22 +30,13 @@ const routes = [
       }
     ], // 子路由配置结束
   },
-
   // 时空算子库
   {
     path: '/skszk',
     name: 'skszk',
     component: function () {
-      return import('../views/Skszk.vue')
-    },
-    children: [ // 子路由配置开始
-      {
-        path: 'example',
-        component: function () {
-          return import('../views/skszk/Example.vue')
-        },
-      }
-    ], // 子路由配置结束
+      return import('../views/skszk/Example.vue')
+    }
   },
   // 时空门户
   {
@@ -74,7 +65,7 @@ const routes = [
     path: '/wgn',
     name: 'wgn',
     component: function () {
-      return import('../views/Wgn.vue')
+      return import('../views/wgn/index.vue')
     },
     children: [ // 子路由配置开始
       {
@@ -132,9 +123,9 @@ const routes = [
     },
     children: [ // 子路由配置开始
       {
-        path: 'example',
+        path: 'StatisticalAnalysis',
         component: function () {
-          return import('../views/yxgl/Example.vue')
+          return import('../views/yxgl/StatisticalAnalysis.vue')
         },
       }
     ], // 子路由配置结束

+ 21 - 0
src/utils/request.js

@@ -143,6 +143,26 @@ function postform(url, data) {
     })
   })
 }
+function postBody(url, data) {
+  return new Promise((resolve, reject) => {
+    let params = {
+      method: 'POST',
+      url,
+      data: data,
+      headers: {
+        'Content-Type': 'application/json;'
+      }
+    };
+    if (url.indexOf('downloadFile') != -1) {
+      params.responseType = 'blob';
+    }
+    service(params).then(res => {
+      resolve(res.data);
+    }).catch(err => {
+      reject(err);
+    })
+  })
+}
 
 function put(url, data) {
   return new Promise((resolve, reject) => {
@@ -280,6 +300,7 @@ export {
   put,
   putform,
   postform,
+  postBody,
   delform,
   postFile,
   getFile,

+ 113 - 41
src/views/HomePage.vue

@@ -5,8 +5,8 @@
         <div class="part1-1-1">
           <div class="number-container">
             <span class="text">调用总数</span>
-            <span v-for="(num,index) in totalCall" :key="index">
-              <span v-if="num.indexOf(',')>-1">
+            <span v-for="(num, index) in totalCall" :key="index">
+              <span v-if="num.indexOf(',') > -1">
                 <span class="separator">,</span>
               </span>
               <span v-else>
@@ -51,29 +51,47 @@
       <div class="row">
         <div class="service">
           <h2 class="title yellow">空间数据查询</h2>
-          <p>空间数据查询是从空间数据集中检索特定信息的操作。它可在关系型数据库如MySQL和SQL Server中进行,也能在NoSQL数据库如MongoDB里实现。通过特定函数和方法,能快速找到符合条件的数据,为地理信息等领域提供重要支持。</p>
+          <p>
+            空间数据查询是从空间数据集中检索特定信息的操作。它可在关系型数据库如MySQL和SQL
+            Server中进行,也能在NoSQL数据库如MongoDB里实现。通过特定函数和方法,能快速找到符合条件的数据,为地理信息等领域提供重要支持。
+          </p>
         </div>
         <div class="service">
           <h2 class="title gray">数据分析</h2>
-          <p>空间数据查询是从空间数据集中检索特定信息的操作。它可在关系型数据库如MySQL和SQL Server中进行,也能在NoSQL数据库如MongoDB里实现。通过特定函数和方法,能快速找到符合条件的数据,为地理信息等领域提供重要支持。</p>
+          <p>
+            空间数据查询是从空间数据集中检索特定信息的操作。它可在关系型数据库如MySQL和SQL
+            Server中进行,也能在NoSQL数据库如MongoDB里实现。通过特定函数和方法,能快速找到符合条件的数据,为地理信息等领域提供重要支持。
+          </p>
         </div>
         <div class="service">
           <h2 class="title blue">可视化服务</h2>
-          <p>空间数据查询是从空间数据集中检索特定信息的操作。它可在关系型数据库如MySQL和SQL Server中进行,也能在NoSQL数据库如MongoDB里实现。通过特定函数和方法,能快速找到符合条件的数据,为地理信息等领域提供重要支持。</p>
+          <p>
+            空间数据查询是从空间数据集中检索特定信息的操作。它可在关系型数据库如MySQL和SQL
+            Server中进行,也能在NoSQL数据库如MongoDB里实现。通过特定函数和方法,能快速找到符合条件的数据,为地理信息等领域提供重要支持。
+          </p>
         </div>
       </div>
       <div class="row">
         <div class="service">
           <h2 class="title orange">空间数据查询</h2>
-          <p>空间数据查询是从空间数据集中检索特定信息的操作。它可在关系型数据库如MySQL和SQL Server中进行,也能在NoSQL数据库如MongoDB里实现。通过特定函数和方法,能快速找到符合条件的数据,为地理信息等领域提供重要支持。</p>
+          <p>
+            空间数据查询是从空间数据集中检索特定信息的操作。它可在关系型数据库如MySQL和SQL
+            Server中进行,也能在NoSQL数据库如MongoDB里实现。通过特定函数和方法,能快速找到符合条件的数据,为地理信息等领域提供重要支持。
+          </p>
         </div>
         <div class="service">
           <h2 class="title green">数据分析</h2>
-          <p>空间数据查询是从空间数据集中检索特定信息的操作。它可在关系型数据库如MySQL和SQL Server中进行,也能在NoSQL数据库如MongoDB里实现。通过特定函数和方法,能快速找到符合条件的数据,为地理信息等领域提供重要支持。</p>
+          <p>
+            空间数据查询是从空间数据集中检索特定信息的操作。它可在关系型数据库如MySQL和SQL
+            Server中进行,也能在NoSQL数据库如MongoDB里实现。通过特定函数和方法,能快速找到符合条件的数据,为地理信息等领域提供重要支持。
+          </p>
         </div>
         <div class="service">
           <h2 class="title yellow-green">可视化服务</h2>
-          <p>空间数据查询是从空间数据集中检索特定信息的操作。它可在关系型数据库如MySQL和SQL Server中进行,也能在NoSQL数据库如MongoDB里实现。通过特定函数和方法,能快速找到符合条件的数据,为地理信息等领域提供重要支持。</p>
+          <p>
+            空间数据查询是从空间数据集中检索特定信息的操作。它可在关系型数据库如MySQL和SQL
+            Server中进行,也能在NoSQL数据库如MongoDB里实现。通过特定函数和方法,能快速找到符合条件的数据,为地理信息等领域提供重要支持。
+          </p>
         </div>
       </div>
     </div>
@@ -84,7 +102,7 @@
           GIS 通用计算功能,实用便捷超乎想象!
           <br />测距、剖面分析及点线面绘制轻松实现,坐标转换精准高效,路径规划快速准确,地名搜索智能推荐
         </p>
-        <el-button type="primary" size="large">了解更多</el-button>
+        <el-button type="primary" size="large" @click="gotoDetail">了解更多</el-button>
       </div>
     </div>
     <div class="part4">
@@ -92,34 +110,52 @@
       <div class="row">
         <div class="service">
           <h2 class="title yellow">空间数据查询</h2>
-          <p>空间数据查询是从空间数据集中检索特定信息的操作。它可在关系型数据库如MySQL和SQL Server中进行,也能在NoSQL数据库如MongoDB里实现。通过特定函数和方法,能快速找到符合条件的数据,为地理信息等领域提供重要支持。</p>
+          <p>
+            空间数据查询是从空间数据集中检索特定信息的操作。它可在关系型数据库如MySQL和SQL
+            Server中进行,也能在NoSQL数据库如MongoDB里实现。通过特定函数和方法,能快速找到符合条件的数据,为地理信息等领域提供重要支持。
+          </p>
           <el-button size="large" link>了解详情→</el-button>
         </div>
         <div class="service">
           <h2 class="title gray">数据分析</h2>
-          <p>空间数据查询是从空间数据集中检索特定信息的操作。它可在关系型数据库如MySQL和SQL Server中进行,也能在NoSQL数据库如MongoDB里实现。通过特定函数和方法,能快速找到符合条件的数据,为地理信息等领域提供重要支持。</p>
+          <p>
+            空间数据查询是从空间数据集中检索特定信息的操作。它可在关系型数据库如MySQL和SQL
+            Server中进行,也能在NoSQL数据库如MongoDB里实现。通过特定函数和方法,能快速找到符合条件的数据,为地理信息等领域提供重要支持。
+          </p>
           <el-button size="large" link>了解详情→</el-button>
         </div>
         <div class="service">
           <h2 class="title blue">可视化服务</h2>
-          <p>空间数据查询是从空间数据集中检索特定信息的操作。它可在关系型数据库如MySQL和SQL Server中进行,也能在NoSQL数据库如MongoDB里实现。通过特定函数和方法,能快速找到符合条件的数据,为地理信息等领域提供重要支持。</p>
+          <p>
+            空间数据查询是从空间数据集中检索特定信息的操作。它可在关系型数据库如MySQL和SQL
+            Server中进行,也能在NoSQL数据库如MongoDB里实现。通过特定函数和方法,能快速找到符合条件的数据,为地理信息等领域提供重要支持。
+          </p>
           <el-button size="large" link>了解详情→</el-button>
         </div>
       </div>
       <div class="row">
         <div class="service">
           <h2 class="title orange">空间数据查询</h2>
-          <p>空间数据查询是从空间数据集中检索特定信息的操作。它可在关系型数据库如MySQL和SQL Server中进行,也能在NoSQL数据库如MongoDB里实现。通过特定函数和方法,能快速找到符合条件的数据,为地理信息等领域提供重要支持。</p>
+          <p>
+            空间数据查询是从空间数据集中检索特定信息的操作。它可在关系型数据库如MySQL和SQL
+            Server中进行,也能在NoSQL数据库如MongoDB里实现。通过特定函数和方法,能快速找到符合条件的数据,为地理信息等领域提供重要支持。
+          </p>
           <el-button size="large" link>了解详情→</el-button>
         </div>
         <div class="service">
           <h2 class="title green">数据分析</h2>
-          <p>空间数据查询是从空间数据集中检索特定信息的操作。它可在关系型数据库如MySQL和SQL Server中进行,也能在NoSQL数据库如MongoDB里实现。通过特定函数和方法,能快速找到符合条件的数据,为地理信息等领域提供重要支持。</p>
+          <p>
+            空间数据查询是从空间数据集中检索特定信息的操作。它可在关系型数据库如MySQL和SQL
+            Server中进行,也能在NoSQL数据库如MongoDB里实现。通过特定函数和方法,能快速找到符合条件的数据,为地理信息等领域提供重要支持。
+          </p>
           <el-button size="large" link>了解详情→</el-button>
         </div>
         <div class="service">
           <h2 class="title yellow-green">可视化服务</h2>
-          <p>空间数据查询是从空间数据集中检索特定信息的操作。它可在关系型数据库如MySQL和SQL Server中进行,也能在NoSQL数据库如MongoDB里实现。通过特定函数和方法,能快速找到符合条件的数据,为地理信息等领域提供重要支持。</p>
+          <p>
+            空间数据查询是从空间数据集中检索特定信息的操作。它可在关系型数据库如MySQL和SQL
+            Server中进行,也能在NoSQL数据库如MongoDB里实现。通过特定函数和方法,能快速找到符合条件的数据,为地理信息等领域提供重要支持。
+          </p>
           <el-button size="large" link>了解详情→</el-button>
         </div>
       </div>
@@ -130,20 +166,23 @@
         <div class="part5-1-1">
           <div>
-            <p>545</p>万次
+            <p>545</p>
+            万次
           </div>
           <div>地址</div>
         </div>
         <div class="part5-1-2">
           <div>
-            <p>319</p>个
+            <p>319</p>
+            个
           </div>
           <div>应用场景</div>
         </div>
         <div class="part5-1-3">
           <div>
-            <p>230</p>万条
+            <p>230</p>
+            万条
           </div>
           <div>服务人数</div>
         </div>
@@ -151,10 +190,10 @@
       <div class="part5-2">
         <div class="tab-container">
           <div
-            v-for="(item) in tabs"
+            v-for="item in tabs"
             :key="item.key"
             @click="currentTab = item.key"
-            :class="{'tab-item':true, 'tab-item-active': currentTab === item.key }"
+            :class="{ 'tab-item': true, 'tab-item-active': currentTab === item.key }"
           >
             <!-- <span class="tab-icon">{{ item.icon }}</span> -->
             <span class="tab-title">{{ item.title }}</span>
@@ -165,30 +204,58 @@
             <div class="content">
               <div class="tag">盈港街道、夏阳街道</div>
               <h2 class="title">上海市青浦区教育局·学区查询</h2>
-              <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean euismod bibendum laoreet. Proin gravida dolor sit amet lacus accumsan et viverra justo commodo. Proin sodales pulvinar sic tempor. Sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nam fermentum, nulla luctus pharetra vulputate, felis tellus mollis orci, sed rhoncus pronin sapien nunc accuan eget.</p>
+              <p>
+                Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean euismod
+                bibendum laoreet. Proin gravida dolor sit amet lacus accumsan et viverra
+                justo commodo. Proin sodales pulvinar sic tempor. Sociis natoque penatibus
+                et magnis dis parturient montes, nascetur ridiculus mus. Nam fermentum,
+                nulla luctus pharetra vulputate, felis tellus mollis orci, sed rhoncus
+                pronin sapien nunc accuan eget.
+              </p>
             </div>
           </div>
           <div class="service">
             <div class="content">
               <div class="tag">全区、快递实验室</div>
               <h2 class="title">上海市青浦区快递实验室</h2>
-              <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean euismod bibendum laoreet. Proin gravida dolor sit amet lacus accumsan et viverra justo commodo. Proin sodales pulvinar sic tempor. Sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nam fermentum, nulla luctus pharetra vulputate, felis tellus mollis orci, sed rhoncus pronin sapien nunc accuan eget.</p>
+              <p>
+                Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean euismod
+                bibendum laoreet. Proin gravida dolor sit amet lacus accumsan et viverra
+                justo commodo. Proin sodales pulvinar sic tempor. Sociis natoque penatibus
+                et magnis dis parturient montes, nascetur ridiculus mus. Nam fermentum,
+                nulla luctus pharetra vulputate, felis tellus mollis orci, sed rhoncus
+                pronin sapien nunc accuan eget.
+              </p>
             </div>
           </div>
         </div>
         <div class="row">
-             <div class="service">
+          <div class="service">
             <div class="content">
               <div class="tag">盈港街道、夏阳街道</div>
               <h2 class="title">上海市青浦区教育局·学区查询</h2>
-              <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean euismod bibendum laoreet. Proin gravida dolor sit amet lacus accumsan et viverra justo commodo. Proin sodales pulvinar sic tempor. Sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nam fermentum, nulla luctus pharetra vulputate, felis tellus mollis orci, sed rhoncus pronin sapien nunc accuan eget.</p>
+              <p>
+                Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean euismod
+                bibendum laoreet. Proin gravida dolor sit amet lacus accumsan et viverra
+                justo commodo. Proin sodales pulvinar sic tempor. Sociis natoque penatibus
+                et magnis dis parturient montes, nascetur ridiculus mus. Nam fermentum,
+                nulla luctus pharetra vulputate, felis tellus mollis orci, sed rhoncus
+                pronin sapien nunc accuan eget.
+              </p>
             </div>
           </div>
           <div class="service">
             <div class="content">
               <div class="tag">全区、快递实验室</div>
               <h2 class="title">上海市青浦区快递实验室</h2>
-              <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean euismod bibendum laoreet. Proin gravida dolor sit amet lacus accumsan et viverra justo commodo. Proin sodales pulvinar sic tempor. Sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nam fermentum, nulla luctus pharetra vulputate, felis tellus mollis orci, sed rhoncus pronin sapien nunc accuan eget.</p>
+              <p>
+                Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean euismod
+                bibendum laoreet. Proin gravida dolor sit amet lacus accumsan et viverra
+                justo commodo. Proin sodales pulvinar sic tempor. Sociis natoque penatibus
+                et magnis dis parturient montes, nascetur ridiculus mus. Nam fermentum,
+                nulla luctus pharetra vulputate, felis tellus mollis orci, sed rhoncus
+                pronin sapien nunc accuan eget.
+              </p>
             </div>
           </div>
         </div>
@@ -214,8 +281,8 @@ export default {
       tabs: [
         { key: "data-governance", icon: "", title: "数据治理" },
         { key: "business-app", icon: "", title: "业务应用" },
-        { key: "comprehensive-decision", icon: "", title: "综合决策" }
-      ]
+        { key: "comprehensive-decision", icon: "", title: "综合决策" },
+      ],
     };
   },
   mounted() {
@@ -223,6 +290,11 @@ export default {
     this.setChart();
   },
   methods: {
+    gotoDetail() {
+      this.$router.push({
+        path: "/skszk",
+      });
+    },
     // 处理调用总数数字
     handleTotalCallNumber(num) {
       // 667507060
@@ -244,11 +316,11 @@ export default {
       let option = {
         tooltip: {
           trigger: "axis", // 坐标轴触发提示
-          axisPointer: { type: "shadow" }
+          axisPointer: { type: "shadow" },
         },
         legend: {
           data: ["tokyo", "london"],
-          textStyle: { color: "#fff" } // 图例文字颜色
+          textStyle: { color: "#fff" }, // 图例文字颜色
         },
         xAxis: {
           type: "category",
@@ -263,11 +335,11 @@ export default {
             "Aug",
             "Sep",
             "Oct",
-            "Nov"
+            "Nov",
           ],
           axisLine: { lineStyle: { color: "#fff" } }, // 坐标轴颜色
           axisTick: { show: false }, // 隐藏刻度
-          splitLine: { show: false } // 隐藏分割线
+          splitLine: { show: false }, // 隐藏分割线
         },
         yAxis: {
           type: "value",
@@ -276,9 +348,9 @@ export default {
           splitLine: {
             lineStyle: {
               type: "dashed", // 虚线网格
-              color: "#fff"
-            }
-          }
+              color: "#fff",
+            },
+          },
         },
         series: [
           {
@@ -288,7 +360,7 @@ export default {
             lineStyle: { color: "#42a5f5" }, // 蓝色线条
             itemStyle: { color: "#42a5f5" }, // 节点颜色
             symbol: "circle", // 节点形状
-            symbolSize: 6 // 节点大小
+            symbolSize: 6, // 节点大小
           },
           {
             name: "london",
@@ -297,14 +369,14 @@ export default {
             lineStyle: { color: "#4caf50" }, // 绿色线条
             itemStyle: { color: "#4caf50" },
             symbol: "circle",
-            symbolSize: 6
-          }
-        ]
+            symbolSize: 6,
+          },
+        ],
       };
       // 绘制图表
       myChart.setOption(option);
-    }
-  }
+    },
+  },
 };
 </script>
 
@@ -788,4 +860,4 @@ export default {
     }
   }
 }
-</style>
+</style>

+ 77 - 16
src/views/Yxgl.vue

@@ -1,31 +1,92 @@
 <template>
-    <div class="yxgl container">
-        <router-view />
+  <div id="yxgl_box">
+    <el-menu
+      :default-active="menuActive"
+      class="el-menu-vertical-demo"
+      :collapse="isCollapse"
+      @open="handleOpen"
+      @close="handleClose"
+    >
+      <el-sub-menu index="/#">
+        <template #title>
+          <el-icon><Platform /></el-icon>
+          <span>运行中心</span>
+        </template>
+        <el-menu-item index="StatisticalAnalysis">
+          <el-icon><Histogram /></el-icon>
+          <template #title>统计分析</template>
+        </el-menu-item>
+      </el-sub-menu>
+      <el-sub-menu index="2">
+        <template #title>
+          <el-icon><Tools /></el-icon>
+          <span>管理中心</span>
+        </template>
+        <el-menu-item index="oauth">
+          <el-icon><UserFilled /></el-icon>
+          <template #title>用户权限</template>
+        </el-menu-item>
+        <el-menu-item index="dms">
+          <el-icon><Ticket /></el-icon>
+          <template #title>数据管理</template>
+        </el-menu-item>
+      </el-sub-menu>
+    </el-menu>
+    <div
+      class="viewBox"
+      :style="{ width: isCollapse ? 'calc(100vw - 60px)' : 'calc(100% - 200px)' }"
+    >
+      <StatisticalAnalysis />
     </div>
+  </div>
 </template>
 
 <script>
+import StatisticalAnalysis from "@/views/yxgl/StatisticalAnalysis.vue";
 export default {
-    name: "yxgl",
-    data() {
-        return {
-
-        };
+  name: "yxgl",
+  data() {
+    return {
+      isCollapse: false,
+      menuActive: "StatisticalAnalysis",
+    };
+  },
+  // 2. 局部注册组件(键值同名可简写)
+  components: {
+    StatisticalAnalysis, // 完整写法:MyButton: MyButton
+  },
+  mounted() {},
+  methods: {
+    handleOpen(key, keyPath) {
+      console.log(key, keyPath);
     },
-    mounted() {
+    handleClose(key, keyPath) {
+      console.log(key, keyPath);
     },
-    methods: {
-    }
+  },
 };
 </script>
 
 <style lang="less" scoped>
-.container {
-    width: 1920px;
-    margin: 0 auto;
+#yxgl_box {
+  width: 100vw;
+  margin: 0;
+  display: flex;
 }
 
-.yxgl {
-    position: relative;
+.el-menu-vertical-demo:not(.el-menu--collapse) {
+  width: 200px;
+  height: 100vh;
+  margin: 0;
+  //   background: #08224a;
+}
+.viewBox {
+  width: calc(100vw - 200px);
+  margin: 0;
+  height: 100vh;
+  position: relative;
+  overflow: hidden;
+  overflow-y: auto;
+  background-color: #eee;
 }
-</style>
+</style>

+ 90 - 15
src/views/skszk/Example.vue

@@ -1,27 +1,102 @@
 <template>
-    <div class="example">
-        skszk example
-    </div>
+  <!-- 时空算子库 -->
+  <div class="example">
+    <div id="skysceneryContainer"></div>
+    <!-- 操作栏 -->
+    <ControlPanel ref="controlPanel" />
+  </div>
 </template>
 
 <script>
+import ControlPanel from "../../components/skszk/controlPanel.vue";
+// 需要开发出一个时空算子库的示例页面:需要能绘制点线面的工具,且能包装绘制的几何对象传入到后台接口中
 export default {
-    name: "",
-    data() {
-        return {
+  name: "SkszkExample",
+  // 2. 局部注册组件(键值同名可简写)
+  components: {
+    ControlPanel, // 完整写法:MyButton: MyButton
+  },
+  data() {
+    return {};
+  },
+  mounted() {
+    window.SkySceneryConfig = {};
+    window.loadScripts([systemConfig.scriptMain]).then(() => {
+      this.creatMap();
+    });
+  },
+  methods: {
+    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,
+      });
 
-        };
-    },
-    mounted() {
+      // 添加地图服务
+      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.1, 31, 30000.0), // 设置位置
+        orientation: {
+          heading: SkyScenery.Math.toRadians(0.0), // 方向
+          pitch: SkyScenery.Math.toRadians(-90.0), // 倾斜角度
+          roll: 0,
+        },
+      });
+      this.$refs["controlPanel"].initDrawHandler();
     },
-    methods: {
-    }
+  },
 };
 </script>
 
 <style lang="less" scoped>
-.container {
-    width: 1920px;
-    margin: 0 auto;
+.example {
+  width: 100%;
+  height: 100vh;
+  position: relative;
+  overflow: hidden;
+}
+
+#skysceneryContainer {
+  width: 70vw;
+  max-width: calc(100vw - 500px);
+  height: 100%;
 }
-</style>
+</style>

+ 0 - 27
src/views/wgn/Example.vue

@@ -1,27 +0,0 @@
-<template>
-    <div class="example">
-        wgn example
-    </div>
-</template>
-
-<script>
-export default {
-    name: "",
-    data() {
-        return {
-
-        };
-    },
-    mounted() {
-    },
-    methods: {
-    }
-};
-</script>
-
-<style lang="less" scoped>
-.container {
-    width: 1920px;
-    margin: 0 auto;
-}
-</style>

+ 290 - 0
src/views/wgn/index.vue

@@ -0,0 +1,290 @@
+<template>
+  <div class="container">
+    <div class="server_title">
+      <el-image
+        style="width: 824px; height: 786px"
+        src="static/images/wgn_title.png"
+        fit="cover"
+      />
+      <div class="server_title_text">
+        <div class="server_title_text_title">微功能服务</div>
+        <div class="server_title_text_content">
+          微功能子系统专注于提供强大的空间计算与数据处理能力,是支撑青浦区三维数字底板空间分析与数据流转的核心模块,聚焦
+          “精准计算 + 灵活处理”
+          两大核心能力,所有功能均以标准化服务接口形式对外提供(支持第三方委办、上层应用系统调用),同时在系统内部集成可视化操作界面,实现
+          “计算 - 分析 - 展示” 闭环。
+        </div>
+      </div>
+    </div>
+    <div class="server_list_box">
+      <div class="server_list_box_title">微功能列表</div>
+      <div class="server_list_box_search">
+        功能搜索
+        <el-input
+          v-model="searchStr"
+          style="width: 525px; margin-left: 37px"
+          placeholder="输入搜索关键词"
+          class="input-with-select"
+          clearable
+          size="large"
+          @change="searchServerList()"
+        >
+          <template #append>
+            <el-button icon="Search" @click="searchServerList()" />
+          </template>
+        </el-input>
+      </div>
+      <div class="server_list_box_table" v-if="dmsServerList">
+        <div
+          v-for="item in dmsServerList"
+          :key="item.c_scene_name"
+          class="server_list_box_table_item"
+        >
+          <div class="server_list_box_table_item_content">
+            <div class="server_list_box_table_item_content_title">{{ item.title }}</div>
+            <div class="server_list_box_table_item_content_text">{{ item.content }}</div>
+            <div class="server_list_box_table_item_content_button_box">
+              <div
+                class="server_list_box_table_item_content_button_box_item"
+                @click.stop="handleOnlineDemo(item)"
+              >
+                在线演示
+              </div>
+              <div
+                class="server_list_box_table_item_content_button_box_item"
+                @click.stop="handleApply(item)"
+              >
+                申请使用
+              </div>
+            </div>
+          </div>
+          <div class="server_list_box_table_item_image">
+            <el-image
+              style="width: 690px; height: 410px"
+              :src="dmsDataProxy + item.c_picture"
+              fit="cover"
+            />
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { getDmsData } from "../../api/wgn";
+export default {
+  name: "微功能服务",
+  data() {
+    return {
+      // 搜索关键词
+      searchStr: "",
+      // 微功能服务列表
+      dmsServerList: [],
+      // DMS数据代理地址
+      dmsDataProxy: "",
+    };
+  },
+  mounted() {
+    this.dmsDataProxy = systemConfig.dmsDataProxy;
+    this.searchServerList();
+  },
+  methods: {
+    // 搜索微功能服务
+    searchServerList() {
+      let requestParams = {
+        columnId: 1651,
+        states: 0,
+        pageSize: 999,
+        page: 0,
+      };
+      if (this.searchStr) {
+        requestParams.search = JSON.stringify([
+          {
+            field: "title",
+            searchType: 2,
+            content: { value: "%" + this.searchStr + "%" },
+          },
+        ]);
+      }
+      // 获取微功能服务列表
+      getDmsData(requestParams)
+        .then((res) => {
+          if (res.code === 200) {
+            this.dmsServerList = res.content.data;
+            this.$message({
+              message: "搜索到" + this.dmsServerList.length + "条微功能服务",
+              type: "success",
+            });
+          } else {
+            this.$message({
+              message: "搜索到0条微功能服务",
+              type: "warning",
+            });
+          }
+        })
+        .catch((e) => {
+          this.$message({
+            message: "搜索微功能服务失败" + e,
+            type: "error",
+          });
+        });
+    },
+    // 申请使用微功能服务
+    handleApply(item) {
+      this.$confirm("确认申请使用" + item.title + "吗?", "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning",
+      })
+        .then(() => {
+          // 确认申请使用
+          this.$message({
+            message: "申请使用" + item.title + "?等url吧!",
+            type: "success",
+          });
+        })
+        .catch(() => {
+          // 取消申请使用
+        });
+    },
+    // 在线演示微功能服务
+    handleOnlineDemo(item) {
+      // 1. 解析目标路由(支持传参、命名路由等)
+      const routeData = this.$router.resolve({
+        path: "/skszk", // 时空算子库
+        query: { sceneId: item.c_scene_name },
+      });
+      // 2. 打开新窗口(_blank 表示新窗口)
+      window.open(routeData.href, "_blank");
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.container {
+  width: 100vw;
+  margin: 0 auto;
+  background-color: #08224a;
+}
+.server_title {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+.server_title_text {
+  width: calc(100vw - 824px);
+  height: 786px;
+  background-color: #1c2631;
+  color: #fff;
+  padding: 0 160px 0 60px;
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  overflow: hidden;
+  &_title {
+    font-size: 64px;
+    font-weight: bold;
+    letter-spacing: 0.5rem;
+  }
+  &_content {
+    margin-top: 77px;
+    font-size: 22px;
+  }
+}
+.server_list_box {
+  width: 100vw;
+  background-color: rgba(0, 0, 0, 0.15);
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  color: #fff;
+  &_title {
+    margin-top: 100px;
+    font-size: 35px;
+    font-weight: bold;
+    position: relative;
+    &::after {
+      content: "";
+      position: absolute;
+      bottom: -12px;
+      left: 50%;
+      transform: translateX(-50%);
+      width: 100px;
+      height: 4px;
+      background-image: linear-gradient(to right, #1d88f0, #00bfff);
+    }
+  }
+  &_search {
+    margin-top: 100px;
+    font-size: 25px;
+    display: flex;
+    flex-wrap: nowrap;
+    align-items: center;
+    width: -webkit-fill-available;
+    justify-content: center;
+  }
+  &_table {
+    margin-top: 50px;
+    width: 100vw;
+    &_item {
+      display: flex;
+      justify-content: space-evenly;
+      align-items: center;
+      padding: 100px 0;
+
+      &:nth-child(odd) {
+        flex-direction: row;
+      }
+      &:nth-child(even) {
+        flex-direction: row-reverse;
+      }
+      &_content {
+        width: 760px;
+        height: 100px;
+        display: flex;
+        flex-direction: column;
+        justify-content: center;
+        align-items: center;
+        &_title {
+          font-size: 35px;
+          font-weight: bold;
+          color: #4095e5;
+        }
+        &_text {
+          margin-top: 35px;
+          font-size: 18px;
+        }
+        &_button_box {
+          display: flex;
+          justify-content: space-evenly;
+          width: 100%;
+          &_item {
+            margin-top: 35px;
+            font-size: 20px;
+            letter-spacing: 0.2rem;
+            color: #4095e5;
+            border: 1px solid #4095e5;
+            border-radius: 10px;
+            padding: 10px 36px;
+            cursor: pointer;
+            &:hover {
+              background-color: #4095e5;
+              color: #fff;
+            }
+          }
+        }
+      }
+      &_image {
+        width: 690px;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        border-radius: 30px;
+        overflow: hidden;
+      }
+    }
+  }
+}
+</style>

+ 0 - 27
src/views/yxgl/Example.vue

@@ -1,27 +0,0 @@
-<template>
-    <div class="example">
-        yxgl example
-    </div>
-</template>
-
-<script>
-export default {
-    name: "",
-    data() {
-        return {
-
-        };
-    },
-    mounted() {
-    },
-    methods: {
-    }
-};
-</script>
-
-<style lang="less" scoped>
-.container {
-    width: 1920px;
-    margin: 0 auto;
-}
-</style>

+ 243 - 0
src/views/yxgl/StatisticalAnalysis.vue

@@ -0,0 +1,243 @@
+<template>
+  <div class="mainBox">
+    <!-- 搜索区域 -->
+    <div class="searchBox">
+      <div>
+        <el-date-picker
+          v-model="dateValue"
+          type="daterange"
+          unlink-panels
+          range-separator="到"
+          start-placeholder="开始时间"
+          end-placeholder="结束时间"
+          :shortcuts="shortcuts"
+          size="large"
+        />
+      </div>
+    </div>
+    <!-- 服务调用card -->
+    <div class="flex">
+      <card
+        class="card flex"
+        :title="'服务机构总数'"
+        :value="8"
+        :growth="'较上个月增长了12%'"
+        iconColor="#2563db"
+        :upStatus="1"
+      />
+      <card
+        class="card flex"
+        :title="'服务总数'"
+        :value="100"
+        :growth="'较上个月下降了12%'"
+        iconColor="#16a34a"
+        :upStatus="-1"
+      />
+      <card
+        class="card flex"
+        :title="'服务调用总次数'"
+        :value="1000"
+        :growth="'较上个月增长了12%'"
+        iconColor="#9333ea"
+        :upStatus="1"
+      />
+      <card
+        class="card flex"
+        :title="'服务类别数量'"
+        :value="10"
+        :growth="'与上月持平'"
+        iconColor="#ca8a04"
+        :upStatus="0"
+      />
+    </div>
+    <!-- 服务类信息统计 -->
+    <div class="bigCard">
+      <div class="bigCard_title">服务类信息统计</div>
+      <div class="tools">
+        <el-button>
+          <el-icon><Upload /></el-icon> 导出数据
+        </el-button>
+        <el-button type="primary">
+          <el-icon><TrendCharts /></el-icon>
+          详细报告
+        </el-button>
+      </div>
+      <div class="flex">
+        <div style="width: 48%; height: 400px">
+          <EchartsDome title="服务调用趋势(近30天)" />
+        </div>
+        <div style="width: 48%; height: 400px">echarts2</div>
+      </div>
+      <div style="width: 100%; height: 400px">tables</div>
+    </div>
+    <!-- 用户信息统计 -->
+    <div class="bigCard">用户信息统计</div>
+    <!-- 应用类信息统计 -->
+    <div class="bigCard">应用类信息统计</div>
+    <!-- 数据类信息统计 -->
+    <div class="bigCard">数据类信息统计</div>
+    <!-- 区级特色信息统计 -->
+    <div class="bigCard">区级特色信息统计</div>
+  </div>
+</template>
+
+<script>
+import card from "@/components/yxgl/card.vue";
+import EchartsDome from "@/components/yxgl/EchartsDome.vue";
+export default {
+  name: "",
+  components: {
+    card,
+    EchartsDome,
+  },
+  data() {
+    return {
+      dateValue: "",
+      shortcuts: this.shortcuts(),
+    };
+  },
+  mounted() {},
+  methods: {
+    shortcuts() {
+      return [
+        {
+          text: "最近7天",
+          value: () => {
+            const end = new Date();
+            const start = new Date();
+            start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
+            return [start, end];
+          },
+        },
+        {
+          text: "最近30天",
+          value: () => {
+            const end = new Date();
+            const start = new Date();
+            start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
+            return [start, end];
+          },
+        },
+        {
+          text: "最近90天",
+          value: () => {
+            const end = new Date();
+            const start = new Date();
+            start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
+            return [start, end];
+          },
+        },
+        {
+          text: "最近1年",
+          value: () => {
+            const end = new Date();
+            const start = new Date();
+            start.setTime(start.getTime() - 3600 * 1000 * 24 * 365);
+            return [start, end];
+          },
+        },
+      ];
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.mainBox {
+  width: calc(100% - 60px);
+  margin: 30px;
+  & > div {
+    margin: 20px 0;
+    display: flex;
+    overflow: hidden;
+  }
+  .card {
+    width: calc(25% - 56px);
+    border-radius: 5px;
+    padding: 20px 18px;
+    background: #ffffff;
+  }
+
+  .bigCard {
+    width: calc(100% - 36px);
+    border-radius: 5px;
+    padding: 20px 18px;
+    background: #ffffff;
+    position: relative;
+    flex-direction: column;
+    .tools {
+      position: absolute;
+      top: 20px;
+      right: 20px;
+      display: flex;
+      flex-direction: row;
+      justify-content: flex-end;
+    }
+    &_title {
+      font-size: 20px;
+      font-weight: bold;
+    }
+  }
+
+  .flex {
+    display: flex;
+    flex-direction: row;
+    justify-content: space-between;
+  }
+
+  .searchBox {
+    width: 100%;
+    height: 50px;
+    line-height: 50px;
+    text-align: center;
+    font-size: 20px;
+    font-weight: bold;
+    flex-direction: row-reverse;
+    display: flex;
+  }
+}
+
+// 日期选择框样式
+.demo-date-picker {
+  display: flex;
+  width: 100%;
+  padding: 0;
+  flex-wrap: wrap;
+}
+
+.demo-date-picker .block {
+  padding: 1.5rem 0;
+  text-align: center;
+  border-right: solid 1px var(--el-border-color);
+  flex: 1;
+  min-width: 400px;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+}
+
+.demo-date-picker .block:last-child {
+  border-right: none;
+}
+
+.demo-date-picker .demonstration {
+  display: block;
+  color: var(--el-text-color-secondary);
+  font-size: 14px;
+  margin-bottom: 1rem;
+}
+
+@media screen and (max-width: 1200px) {
+  .demo-date-picker .block {
+    flex: 0 0 100%;
+    padding: 1rem 0;
+    min-width: auto;
+    border-right: none;
+    border-bottom: solid 1px var(--el-border-color);
+  }
+
+  .demo-date-picker .block:last-child {
+    border-bottom: none;
+  }
+}
+</style>

+ 7 - 0
vue.config.js

@@ -95,6 +95,13 @@ module.exports = defineConfig({
           '^/proxy_dms': ''
         }
       },
+      '/oneMap/': {
+        target: 'http://127.0.0.1:10099/qpyzt',
+        changeOrigin: true,
+        pathRewrite: {
+          '^/oneMap': ''
+        }
+      },
     },
   }
 })

Unele fișiere nu au fost afișate deoarece prea multe fișiere au fost modificate în acest diff