CzRger 1 year ago
parent
commit
236489e131

+ 17 - 16
index.html

@@ -2,22 +2,23 @@
 <html lang="en">
 
 <head>
-  <meta charset="UTF-8" />
-  <meta name="referrer" content="no-referrer" />
-  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-  <meta name="keywords" content="GIS一张图" />
-  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-  <meta http-equiv="X-UA-Compatible" content="IE=8"/>
-  <meta http-equiv="Pragma" content="no-cache"/>
-  <meta http-equiv="cache-control" content="no-cache, no-store, must-revalidate"/>
-  <meta http-equiv="Cache" content="no-cache"/>
-  <meta http-equiv="expires" content="0"/>
-<!--  <script src="/gifler.js"></script>-->
-  <title><{VITE_PROJECT_TITLE}></title>
-<!--  <link rel="stylesheet" href="/Cesium/Widgets/widgets.css">-->
-<!--  <script src="/Cesium/Cesium.js"></script>-->
-  <script src="/tagcanvas.min.js"></script>
-  <script src="/config.js"></script>
+    <meta charset="UTF-8" />
+    <meta name="referrer" content="no-referrer" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <meta name="keywords" content="GIS一张图" />
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+    <meta http-equiv="X-UA-Compatible" content="IE=8"/>
+    <meta http-equiv="Pragma" content="no-cache"/>
+    <meta http-equiv="cache-control" content="no-cache, no-store, must-revalidate"/>
+    <meta http-equiv="Cache" content="no-cache"/>
+    <meta http-equiv="expires" content="0"/>
+    <!--  <script src="/gifler.js"></script>-->
+    <title><{VITE_PROJECT_TITLE}></title>
+    <!--  <link rel="stylesheet" href="/Cesium/Widgets/widgets.css">-->
+    <!--  <script src="/Cesium/Cesium.js"></script>-->
+    <script src="/tagcanvas.min.js"></script>
+    <script src="/kmedia.js"></script>
+    <script src="/config.js"></script>
 </head>
 
 <body style="overflow: hidden;">

+ 17 - 0
src/api/modules/enterprise.ts

@@ -0,0 +1,17 @@
+import { handle } from '../index'
+
+const suffix = 'business-api'
+
+//  企业信息列表查询
+export const enterpriseQuery = (params: any) => handle({
+  url: `/${suffix}/enterpriseQuery`,
+  method: 'post',
+  params
+})
+
+//  根据圆心和半径查找周边企业设备
+export const deviceQuery = (params: any) => handle({
+  url: `/${suffix}/deviceQuery`,
+  method: 'post',
+  params
+})

File diff suppressed because it is too large
+ 1 - 0
src/assets/svg/business/close_4.svg


BIN
src/components/easyMap/images/bg-lt.png


+ 1 - 1
src/components/vue-drag-resize/vue-drag-resize.js

@@ -329,7 +329,7 @@ export default {
         },
 
         bodyDown(ev) {
-            if (['INPUT', 'TEXTAREA', 'CANVAS'].includes(ev.target.nodeName) || ev.target.className.includes('el-slider')) {
+            if (['INPUT', 'TEXTAREA', 'CANVAS'].includes(ev.target.nodeName) || ev.target.className?.includes('el-slider')) {
                 return;
             }
             this.initResize()

File diff suppressed because it is too large
+ 179 - 0
src/out/kmedia.js


+ 104 - 0
src/views/gis/business/common/VideoPlayKeda.vue

@@ -0,0 +1,104 @@
+<template>
+  <VueDragResize
+      class="drag"
+      :isActive="false"
+      :z="9999"
+      :layout="layout"
+      :isResizable="true"
+      :parentLimitation="true"
+      @resizing="resize"
+      @dragging="resize"
+  >
+    <div class="video-play-keda" @mouseenter="showHead = true" @mouseleave="showHead = false">
+      <div class="video-play-keda-head" v-if="showHead">
+        <div class="video-play-keda-head-name">{{form.name}}</div>
+        <SvgIcon class="__hover" name="close_4" size="20" color="#8FFFFF" @click="$emit('close')"/>
+      </div>
+      <KedaCom class="keda" :videoCode="form.deviceid"/>
+    </div>
+  </VueDragResize>
+</template>
+
+<script lang="ts">
+import {
+  defineComponent,
+  onMounted,
+  ref,
+  toRefs,
+  reactive,
+  watch,
+  getCurrentInstance,
+  ComponentInternalInstance,
+  computed, nextTick
+} from "vue";
+import { useStore } from "vuex";
+import { useRouter } from "vue-router";
+import VueDragResize from '@/components/vue-drag-resize/index.vue'
+import KedaCom from './keda-com.vue'
+
+export default defineComponent({
+  name: "",
+  components: {
+    VueDragResize,
+    KedaCom
+  },
+  props: {
+    layout: {},
+    form: {}
+  },
+  setup(props, { emit }) {
+    const store = useStore();
+    const that = (getCurrentInstance() as ComponentInternalInstance).appContext.config.globalProperties;
+    const state = reactive({
+      showHead: false
+    });
+    const resize = (layout) => {
+      emit('update:layout', Object.assign(layout, {
+        height: 'auto'
+      }))
+    }
+
+    onMounted(() => {
+      nextTick(() => {
+        console.log(props.form)
+      })
+    })
+    return {
+      ...toRefs(state),
+      resize,
+    }
+  },
+});
+</script>
+<style scoped lang="scss">
+.video-play-keda {
+  width: 100%;
+  height: 100%;
+  position: relative;
+  .video-play-keda-head {
+    position: absolute;
+    top: 0;
+    width: 100%;
+    display: flex;
+    align-items: center;
+    height: 44px;
+    background: linear-gradient(180deg, #24358F 0%, rgba(36,53,143,0) 100%);
+    z-index: 2;
+    .video-play-keda-head-name {
+      font-size: 16px;
+      font-family: PingFang SC, PingFang SC;
+      font-weight: 400;
+      color: #FFFFFF;
+      margin-left: 10px;
+    }
+    .svg-icon {
+      margin-left: auto;
+      margin-right: 10px;
+    }
+  }
+  .keda {
+    width: 100%;
+    height: 100%;
+  }
+}
+</style>

+ 70 - 0
src/views/gis/business/common/keda-com.vue

@@ -0,0 +1,70 @@
+<template>
+  <div class="keda-com">
+    <div class="container" ref="ref_video"/>
+  </div>
+</template>
+
+<script lang="ts">
+import {
+  defineComponent,
+  computed,
+  onMounted,
+  ref,
+  reactive,
+  watch,
+  getCurrentInstance,
+  ComponentInternalInstance,
+  toRefs,
+  nextTick
+} from 'vue'
+import {useStore} from 'vuex'
+import {useRouter, useRoute} from 'vue-router'
+import outputOptions from "./keda-options";
+
+export default defineComponent({
+  name: '',
+  components: {},
+  props: {
+    videoCode: { required: true, type: String }
+  },
+  setup(props) {
+    const store = useStore();
+    const router = useRouter();
+    const route = useRoute();
+    const that = (getCurrentInstance() as ComponentInternalInstance).appContext.config.globalProperties
+    const state = reactive({
+      video: null,
+    })
+    const ref_video = ref()
+    watch(() => props.videoCode, (n) => {
+      console.log(n)
+      if (n) {
+        initVideo()
+      }
+    })
+    const initVideo = () => {
+      if (props.videoCode) {
+        state.video = new KMedia(outputOptions(ref_video.value, props.videoCode));
+      }
+    }
+    onMounted(() => {
+      initVideo()
+    })
+    return {
+      ...toRefs(state),
+      ref_video
+    }
+  },
+})
+</script>
+
+<style scoped lang="scss">
+  .keda-com {
+    width: 400px;
+    height: 300px;
+    display: flex;
+    .container {
+      flex: 1;
+    }
+  }
+</style>

+ 55 - 0
src/views/gis/business/common/keda-options.ts

@@ -0,0 +1,55 @@
+const option = {
+  selector: "#videOne",
+  mediaUrl: "./static/demo.mp4",
+  mediaType: "video",
+  playType: "liveStreaming",
+  progressDisplayTime: false,
+  theme: "blue",
+  autoplay: true,
+  muted: true,
+  videoFit:'cover',
+  noBorder: true,
+  hideControlsBar: true,
+  tools: [
+    "backward",
+    "play",
+    "forward",
+    "timings",
+    "playbackRate",
+    "backplay",
+    "ptz",
+    "videoClip",
+    "videoRecorder",
+    "digitalZoom",
+    "screenSplit",
+    "snapshot",
+    "multiShot",
+    "imgControl",
+    "loop",
+    "volume",
+    "requestFullscreen"
+  ],
+  restore: false,
+  loading: {
+    beforeStream: "初始化中",
+    beforePicture: "视频加载中",
+    timeout: "视频加载已超时"
+  },
+  mediaInfo: {
+    file: {},
+    liveStreaming: {
+      websocketUrl: "ws://59.212.10.165:8081",
+      devId: "46040052001321000402"
+    },
+    nonLiveStreaming: {}
+  },
+  onload: function () {}
+};
+
+function outputOptions(selector: any, devId: string) {
+  option.selector = selector;
+  option.mediaInfo.liveStreaming.devId = devId;
+  return option;
+}
+
+export default outputOptions;

+ 411 - 183
src/views/gis/layout/index.vue

@@ -50,73 +50,100 @@
     <div class="gis-content">
       <RouterViewCom/>
     </div>
-    <div ref="ref_qyDom" class="qy-info" :class="`qy--info-${qyParams.qyInfo.tab}`">
-      <div class="qy-bg-icon qy-bg-icon-1"/>
-      <div class="qy-bg-icon qy-bg-icon-2"/>
-      <div class="qy-bg-icon qy-bg-icon-3"/>
-      <div class="qy-bg-icon qy-bg-icon-4"/>
-      <div class="qy-main">
-        <div class="qy-main-head">
-          <div class="qy-main-head-tips">【企业】</div>
-          <div class="qy-main-head-name">{{ qyParams.qyInfo.name }}</div>
-          <SvgIcon class="__hover" name="tips" size="14" color="#8FFFFF" @click="onCloseQy"/>
-        </div>
-        <div class="qy-main-tab">
-          <template v-for="item in [
+    <VideoPlayKedaCom v-if="showVideo" v-model:layout="videoLayout" :form="qyParams.sbInfo" @close="showVideo = false"/>
+  </div>
+
+  <div ref="ref_qyDom" class="qy-info" :class="`qy--info-${qyParams.qyInfo.tab}`">
+    <div class="qy-bg-icon qy-bg-icon-1"/>
+    <div class="qy-bg-icon qy-bg-icon-2"/>
+    <div class="qy-bg-icon qy-bg-icon-3"/>
+    <div class="qy-bg-icon qy-bg-icon-4"/>
+    <div class="qy-main">
+      <div class="qy-main-head">
+        <div class="qy-main-head-tips">【企业】</div>
+        <div class="qy-main-head-name">{{ qyParams.qyInfo.name }}</div>
+        <SvgIcon class="__hover" name="close_4" size="14" color="#8FFFFF" @click="onCloseQy"/>
+      </div>
+      <div class="qy-main-tab">
+        <template v-for="item in [
               {label: '基本信息', value: '1', disabled: false},
               {label: '税收信息', value: '2', disabled: true},
               {label: '运输车辆', value: '3', disabled: true},
               {label: '能耗消息', value: '4', disabled: true},
               {label: '周边设备', value: '5', disabled: false},
           ]">
-            <div class="qy-main-tab-item __hover" :class="{active: item.value === qyParams.qyInfo.tab, disabled: item.disabled}" @click="item.disabled ? undefined : qyParams.qyInfo.tab = item.value">{{item.label}}</div>
-          </template>
-        </div>
-        <div v-if="qyParams.qyInfo.tab === '1'" class="qy-main-content-1">
-          <div>企业名称:{{qyParams.qyInfo[qyParams.qyInfo.tab].name}}</div>
-          <div>企业法人:{{qyParams.qyInfo[qyParams.qyInfo.tab].people}}</div>
-          <div>统一社会信用代码:{{qyParams.qyInfo[qyParams.qyInfo.tab].number}}</div>
-          <div>经营地址:{{qyParams.qyInfo[qyParams.qyInfo.tab].address}}</div>
+          <div class="qy-main-tab-item __hover" :class="{active: item.value === qyParams.qyInfo.tab, disabled: item.disabled}" @click="item.disabled ? undefined : qyParams.qyInfo.tab = item.value">{{item.label}}</div>
+        </template>
+      </div>
+      <div v-if="qyParams.qyInfo.tab === '1'" class="qy-main-content-1">
+        <div>企业名称:{{qyParams.qyInfo[qyParams.qyInfo.tab].name}}</div>
+        <div>企业法人:{{qyParams.qyInfo[qyParams.qyInfo.tab].people}}</div>
+        <div>统一社会信用代码:{{qyParams.qyInfo[qyParams.qyInfo.tab].number}}</div>
+        <div>经营地址:{{qyParams.qyInfo[qyParams.qyInfo.tab].address}}</div>
+      </div>
+      <div v-else-if="qyParams.qyInfo.tab === '5'" class="qy-main-content-5">
+        <div class="qy-main-content-5-radius">
+          周边范围:
+          <div class="radius-min __hover" @click="qyParams.qyInfo[qyParams.qyInfo.tab].radius > 1 ? (qyParams.qyInfo[qyParams.qyInfo.tab].radius--) : undefined">-</div>
+          <CusFormColumn
+              link="number"
+              label=""
+              :min="1"
+              :max="30"
+              :clearable="false"
+              v-model:param="qyParams.qyInfo[qyParams.qyInfo.tab].radius"
+              @blur="handleRangeBlur"/>
+          <div class="radius-max __hover" @click="qyParams.qyInfo[qyParams.qyInfo.tab].radius < 30 ? (qyParams.qyInfo[qyParams.qyInfo.tab].radius++) : undefined">+</div>
+          km
+          <div class="submit __hover" @click="onRadiusSubmit">确定</div>
+          <div class="reset __hover" @click="onRadiusReset">重置</div>
         </div>
-        <div v-else-if="qyParams.qyInfo.tab === '5'" class="qy-main-content-5">
-          <div class="qy-main-content-5-radius">
-            周边范围:
-            <div class="radius-min __hover" @click="qyParams.qyInfo[qyParams.qyInfo.tab].radius > 1 ? (qyParams.qyInfo[qyParams.qyInfo.tab].radius--) : undefined">-</div>
-            <CusFormColumn
-                link="number"
-                label=""
-                :min="1"
-                :max="30"
-                :clearable="false"
-                v-model:param="qyParams.qyInfo[qyParams.qyInfo.tab].radius"
-                @blur="handleRangeBlur"/>
-            <div class="radius-max __hover" @click="qyParams.qyInfo[qyParams.qyInfo.tab].radius < 30 ? (qyParams.qyInfo[qyParams.qyInfo.tab].radius++) : undefined">+</div>
-            km
-            <div class="submit __hover" @click="onRadiusSubmit">确定</div>
-            <div class="reset __hover">重置</div>
-          </div>
-          <div class="qy-main-content-5-table" :style="`height: ${25 + (qyParams.qyInfo[qyParams.qyInfo.tab].tableData.length > 5 ? 5 * 25 : qyParams.qyInfo[qyParams.qyInfo.tab].tableData.length * 25)}px;`">
-            <el-auto-resizer>
-              <template #default="{ height, width }">
-                <el-table-v2
-                    class="__gis-overlay_table-v2"
-                    :columns="qyParams.qyInfo[qyParams.qyInfo.tab].tableHead"
-                    :data="qyParams.qyInfo[qyParams.qyInfo.tab].tableData"
-                    :width="width"
-                    :height="height"
-                    fixed
-                    :row-height="25"
-                    :header-height="25"
-                >
-                  <template #empty></template>
-                </el-table-v2>
-              </template>
-            </el-auto-resizer>
-          </div>
+        <div class="qy-main-content-5-table" :style="`height: ${25 + (qyParams.qyInfo[qyParams.qyInfo.tab].tableData.length > 5 ? 5 * 25 : qyParams.qyInfo[qyParams.qyInfo.tab].tableData.length * 25)}px;`" v-loading="qyParams.qyInfo[qyParams.qyInfo.tab].loading" element-loading-background="rgba(0, 0, 0, 0.3)">
+          <el-auto-resizer>
+            <template #default="{ height, width }">
+              <V2Table
+                  :width="width"
+                  :height="height"
+                  :data="qyParams.qyInfo[qyParams.qyInfo.tab].tableData"
+                  :center="qyParams.qyInfo['5'].center"
+              />
+              <!--                <el-table-v2-->
+              <!--                    class="__gis-overlay_table-v2"-->
+              <!--                    :columns="qyParams.qyInfo[qyParams.qyInfo.tab].tableHead"-->
+              <!--                    :data="qyParams.qyInfo[qyParams.qyInfo.tab].tableData"-->
+              <!--                    :width="width"-->
+              <!--                    :height="height"-->
+              <!--                    fixed-->
+              <!--                    :row-height="25"-->
+              <!--                    :header-height="25"-->
+              <!--                >-->
+              <!--                  <template #empty></template>-->
+              <!--                </el-table-v2>-->
+            </template>
+          </el-auto-resizer>
         </div>
       </div>
     </div>
   </div>
+  <div ref="ref_sbDom" class="sb-info">
+    <div class="sb-info-head">
+      <SvgIcon class="__hover" name="tips" size="14" color="#8FFFFF"/>设备
+    </div>
+    <div class="sb-info-close __hover" @click="onCloseSb">
+      <img src="@/components/easyMap/images/close.png" alt=""/>
+    </div>
+    <div class="sb-main">
+      <div class="sb-item">
+        <div class="sb-item-label">名称:</div>
+        <div class="sb-item-value">{{qyParams.sbInfo?.name}}</div>
+      </div>
+      <div class="sb-item">
+        <div class="sb-item-label">状态:</div>
+        <div class="sb-item-value">{{qyParams.sbInfo?.online === '0' ? '在线' : '离线'}}</div>
+      </div>
+      <div class="play-button __hover" @click="showVideo = true">视频调阅</div>
+    </div>
+  </div>
 </template>
 
 <script lang="ts">
@@ -156,7 +183,9 @@ import { v4 } from "uuid";
 import * as proj from "ol/proj";
 import * as turf from '@turf/turf'
 import * as geom from 'ol/geom';
-
+import VideoPlayKedaCom from "@/views/gis/business/common/VideoPlayKeda.vue";
+import V2Table from "./v2-table.vue";
+import {deviceQuery, enterpriseQuery} from "@/api/modules/enterprise";
 
 export default defineComponent({
   name: '',
@@ -169,6 +198,8 @@ export default defineComponent({
     PositionCom,
     BaseCom,
     ExampleCom,
+    VideoPlayKedaCom,
+    V2Table
   },
   props: {},
   setup(props, {emit}) {
@@ -194,8 +225,21 @@ export default defineComponent({
         qyInfo: <any>{},
         analysisLayer: <any>null,
         analysisSource: <any>null,
-        analysisCircle: <any>null
-      }
+        analysisCircle: <any>null,
+        tempSbFeature: <any>null,
+        sbOverlay: <any>null,
+        sbInfo: <any>{
+          name: '505县道新安村路口1-枪机-0110580',
+          code: '46044123124125125',
+        },
+      },
+      videoLayout: {
+        width: 640,
+        height: 366,
+        left: 1200,
+        top: 460
+      },
+      showVideo: false
     })
     const ToolsMapper = [
       {label: '图层', value: 'element', com: ElementCom},
@@ -214,22 +258,41 @@ export default defineComponent({
       state.map.on('singleclick', e => {
         let flag = false
         map.forEachFeatureAtPixel(e.pixel, (f) => {
-          if (!flag) {
+          if (!flag && f.get('featureType')) {
             flag = true
-            //  恢复上一个要素的样式
-            if (state.qyParams.tempFeature) {
-              state.qyParams.tempFeature.get('resetStyle')()
-            }
-            //  新的要素
-            if (f.get('isAnalysis')) {
-              f.setStyle(f.get('analysisActiveStyle'))
-            } else {
-              f.setStyle(f.get('activeStyle'))
+            if (f.get('featureType') === 'qy') {  //  企业
+              //  恢复上一个要素的样式
+              if (f.getId() !== state.qyParams.tempFeature?.getId()) {
+                state.qyParams.tempFeature?.get('resetStyle')?.()
+                state.qyParams.analysisSource?.clear()
+                //  新的要素
+                if (f.get('isAnalysis')) {
+                  f.setStyle(f.get('analysisActiveStyle'))
+                } else {
+                  f.setStyle(f.get('activeStyle'))
+                }
+                state.qyParams.qyInfo = f.get('info')
+                //  备份新的要素
+                state.qyParams.tempFeature = f
+              }
+              state.qyParams.overlay.setPosition(f.getGeometry().getCoordinates())
+            } else if (f.get('featureType') === 'sb') { //  设备
+              //  恢复上一个要素的样式
+              if (f.getId() !== state.qyParams.tempSbFeature?.getId()) {
+                state.qyParams.tempSbFeature?.get('resetStyle')?.()
+                //  新的要素
+                if (f.get('isAnalysis')) {
+                  f.setStyle(f.get('analysisActiveStyle'))
+                } else {
+                  f.setStyle(f.get('activeStyle'))
+                }
+                state.qyParams.sbInfo = f.get('info')
+                state.qyParams.sbOverlay.setPosition(f.getGeometry().getCoordinates())
+                //  备份新的要素
+                state.qyParams.tempSbFeature = f
+              }
+              state.qyParams.overlay.setPosition(undefined)
             }
-            state.qyParams.qyInfo = f.get('info')
-            state.qyParams.overlay.setPosition(f.getGeometry().getCoordinates())
-            //  备份新的要素
-            state.qyParams.tempFeature = f
           }
         }, {
           hitTolerance: 0,
@@ -277,98 +340,130 @@ export default defineComponent({
       }
     }
     const ref_qyDom = ref()
+    const ref_sbDom = ref()
     const initQYLayer = () => {
-      const arr: any = []
-      for (let i = 0; i < 105; i++) {
-        const obj = {
-          name: '企业企业企业企业企业_' + i,
-          wkt: `POINT(${that.$util.randomNum(108.738329, 110.912130, 6)} ${that.$util.randomNum(18.154784, 20, 6)})`,
-        }
-        if (i % 2 === 0) {
-          obj.type = 'lgszyjkscsb'
-        } else if (i % 2 === 1) {
-          obj.type = 'jgzzmgs'
-        } else if (i % 2 === 2) {
-          obj.type = 'lgsjkyfl'
-        }
-        arr.push(obj)
-      }
-      const features = arr.map(v => {
-        const feat: any = new format.WKT().readFeature(v.wkt)
-        feat.set('defaultStyle', QyStyle.qyStyle(v.type))
-        feat.set('activeStyle', [...CommonStyle.activeStyle(), ...QyStyle.qyStyle(v.type)])
-        feat.set('analysisStyle', [...CommonStyle.analysisStyle(), ...QyStyle.qyStyle(v.type)])
-        feat.set('analysisActiveStyle', [...CommonStyle.activeStyle(), ...CommonStyle.analysisStyle(), ...QyStyle.qyStyle(v.type)])
-        feat.set('isAnalysis', false)
-        feat.set('resetStyle', () => {
-          if (feat.get('isAnalysis')) {
-            feat.setStyle(feat.get('analysisStyle'))
-          } else {
-            feat.setStyle(feat.get('defaultStyle'))
-          }
-        })
-        const obj = {
-          coordinates: feat.getGeometry().getCoordinates(),
-          tab: '1',
-          name: v.name,
-          1: {
-            name: v.name,
-            people: '张三',
-            number: '4812346556asfas',
-            address: '海南省海口市海口区海口镇哈哈哈'
-          },
-          2: {},
-          3: {},
-          4: {},
-          5: {
-            radius: 10,
-            tableHead: [
-              {width: 1, title: '序号', align: 'center', cellRenderer: ({ rowIndex }) => `${rowIndex + 1}`},
-              {width: 1, title: '名称', dataKey: 'name'},
-              {width: 1, title: '状态', align: 'center', cellRenderer: ({rowData}) => rowData.status === '0' ? '在线' : '离线'},
-              {width: 1, title: '类型', align: 'center', dataKey: 'typeName'},
-              {width: 1, title: '距离', align: 'right', cellRenderer: ({rowData}) => {
-                  const start = feat.getGeometry().getCoordinates()
-                  const end = that.$easyMap.formatPosition.wptTcpt(rowData.wkt)
-                  return turf.distance(start, end, {units: 'kilometers'}).toFixed(1) + 'km';
+      enterpriseQuery({
+        "pageNumber":1,
+        "pageSize":200,
+        // "entName":null,
+        // "entType":"加工增值"
+      }).then((res: any) => {
+        if (res.resp_code === 0 && res.datas?.length > 0) {
+          // for (let i = 0; i < 105; i++) {
+          //   const obj = {
+          //     name: '企业企业企业企业企业_' + i,
+          //     wkt: `POINT(${that.$util.randomNum(108.738329, 110.912130, 6)} ${that.$util.randomNum(18.154784, 20, 6)})`,
+          //   }
+          //   if (i % 2 === 0) {
+          //     obj.type = 'lgszyjkscsb'
+          //   } else if (i % 2 === 1) {
+          //     obj.type = 'jgzzmgs'
+          //   } else if (i % 2 === 2) {
+          //     obj.type = 'lgsjkyfl'
+          //   }
+          //   arr.push(obj)
+          // }
+          const features: any = []
+          res.datas.forEach(v => {
+            try {
+              const feat: any = new format.WKT().readFeature(`POINT(${v.longitude} ${v.latitude})`)
+              let type = ''
+              if (v.qykx = '零关税自用进口生产设备') {
+                type = 'lgszyjkscsb'
+              } else if (v.qykx = '加工增值免关税') {
+                type = 'jgzzmgs'
+              } else if (v.qykx = '零关税进口原辅料') {
+                type = 'lgsjkyfl'
+              }
+              feat.set('defaultStyle', QyStyle.qyStyle(type))
+              feat.set('activeStyle', [...CommonStyle.activeStyle(), ...QyStyle.qyStyle(type)])
+              feat.set('analysisStyle', [...CommonStyle.analysisStyle(), ...QyStyle.qyStyle(type)])
+              feat.set('analysisActiveStyle', [...CommonStyle.activeStyle(), ...CommonStyle.analysisStyle(), ...QyStyle.qyStyle(type)])
+              feat.set('isAnalysis', false)
+              feat.set('resetStyle', () => {
+                if (feat.get('isAnalysis')) {
+                  feat.setStyle(feat.get('analysisStyle'))
+                } else {
+                  feat.setStyle(feat.get('defaultStyle'))
                 }
-              },
-            ],
-            tableData: []
-          },
-        }
-        feat.set('info', obj)
-        feat.setStyle(feat.get('defaultStyle'))
-        return feat
-      })
-      state.qyParams.source = new source.Vector({
-        features: features,
-        wrapX: false
-      })
-      state.qyParams.layer = new layer.VectorImage({
-        source: state.qyParams.source,
-        zIndex: 10
-      })
-      state.map.addLayer(state.qyParams.layer)
-      //  详情
-      state.qyParams.overlay = new ol.Overlay({
-        id: v4(),
-        element: ref_qyDom.value,
-        autoPan: false,
-        offset: [0, -60],
-        positioning: 'bottom-center',
-        stopEvent: true,
-        autoPanAnimation: {
-          duration: 250
+              })
+              const obj = {
+                coordinates: feat.getGeometry().getCoordinates(),
+                tab: '1',
+                name: v.qymc,
+                1: {
+                  name: v.qymc,
+                  people: v.lerep,
+                  number: v.uniscid,
+                  address: v.dom
+                },
+                2: {},
+                3: {},
+                4: {},
+                5: {
+                  radius: 1,
+                  center: feat.getGeometry().getCoordinates(),
+                  tableData: [],
+                  loading: false
+                },
+              }
+              feat.set('info', obj)
+              feat.set('featureType', 'qy')
+              feat.setStyle(feat.get('defaultStyle'))
+              feat.setId(v.id)
+              features.push(feat)
+            } catch (e) {
+              console.error('异常企业:', v)
+            }
+          })
+          state.qyParams.source = new source.Vector({
+            features: features,
+            wrapX: false
+          })
+          state.qyParams.layer = new layer.VectorImage({
+            source: state.qyParams.source,
+            zIndex: 10
+          })
+          state.map.addLayer(state.qyParams.layer)
+          //  详情
+          state.qyParams.overlay = new ol.Overlay({
+            id: v4(),
+            element: ref_qyDom.value,
+            autoPan: false,
+            offset: [0, -60],
+            positioning: 'bottom-center',
+            stopEvent: true,
+            autoPanAnimation: {
+              duration: 250
+            }
+          })
+          state.map.addOverlay(state.qyParams.overlay)
+          //  详情
+          state.qyParams.sbOverlay = new ol.Overlay({
+            id: v4(),
+            element: ref_sbDom.value,
+            autoPan: false,
+            offset: [0, -60],
+            positioning: 'bottom-center',
+            stopEvent: true,
+            autoPanAnimation: {
+              duration: 250
+            }
+          })
+          state.map.addOverlay(state.qyParams.sbOverlay)
         }
       })
-      state.map.addOverlay(state.qyParams.overlay)
     }
     const onCloseQy = () => {
       state.qyParams.overlay.setPosition(undefined)
       state.qyParams.tempFeature.get('resetStyle')()
       state.qyParams.tempFeature = null
     }
+    const onCloseSb = () => {
+      state.qyParams.sbOverlay.setPosition(undefined)
+      state.qyParams.tempSbFeature.get('resetStyle')()
+      state.qyParams.tempSbFeature = null
+    }
     const handleRangeBlur = () => {
       if (!state.qyParams.qyInfo['5'].radius) {
         state.qyParams.qyInfo['5'].radius = 10
@@ -419,35 +514,73 @@ export default defineComponent({
         setCircle()
         state.map.addLayer(state.qyParams.analysisLayer)
       }
-      state.qyParams.qyInfo['5'].tableData = []
-      for (let i = 0; i < 5000; i++) {
-        state.qyParams.qyInfo['5'].tableData.push({
-          name: '505县道新安村路口1-枪机-0110580_' + i,
-          status: i % 3 === 0 ? '1' : '0',
-          typeName: '公安类',
-          type: i % 3 === 0 ? 'galsb' : (i % 3 === 1 ? 'shlsb' : 'mylsb'),
-          wkt: `POINT(${that.$util.randomNum(108.738329, 110.912130, 6)} ${that.$util.randomNum(18.154784, 20, 6)})`
-        })
-      }
+
       state.qyParams.analysisSource.clear()
       state.qyParams.analysisSource.addFeature(state.qyParams.analysisCircle)
-      state.qyParams.analysisSource.addFeatures(state.qyParams.qyInfo['5'].tableData.map(v => {
-        const feat: any = new format.WKT().readFeature(v.wkt)
-        feat.set('defaultStyle', SbStyle.sbStyle(v.type))
-        feat.set('activeStyle', [...CommonStyle.activeStyle(), ...SbStyle.sbStyle(v.type)])
-        feat.set('analysisStyle', [...CommonStyle.analysisStyle(), ...SbStyle.sbStyle(v.type)])
-        feat.set('analysisActiveStyle', [...CommonStyle.activeStyle(), ...CommonStyle.analysisStyle(), ...SbStyle.sbStyle(v.type)])
-        feat.set('isAnalysis', true)
-        feat.set('resetStyle', () => {
-          if (feat.get('isAnalysis')) {
-            feat.setStyle(feat.get('analysisStyle'))
-          } else {
-            feat.setStyle(feat.get('defaultStyle'))
-          }
-        })
-        feat.setStyle(feat.get('analysisStyle'))
-        return feat
-      }))
+      state.qyParams.qyInfo['5'].tableData = []
+      state.qyParams.qyInfo['5'].loading = true
+      that.$api.deviceQuery({
+        lon: state.qyParams.qyInfo.coordinates[0],
+        lat: state.qyParams.qyInfo.coordinates[1],
+        radius: 0.5
+      }).then((res: any) => {
+        // console.log(res)
+        // for (let i = 0; i < 500; i++) {
+        //   state.qyParams.qyInfo['5'].tableData.push({
+        //     name: '505县道新安村路口1-枪机-0110580_' + i,
+        //     code: '46044123124125125',
+        //     status: i % 3 === 0 ? '1' : '0',
+        //     typeName: '公安类',
+        //     type: i % 3 === 0 ? 'galsb' : (i % 3 === 1 ? 'shlsb' : 'mylsb'),
+        //     wkt: `POINT(${that.$util.randomNum(108.738329, 110.912130, 6)} ${that.$util.randomNum(18.154784, 20, 6)})`
+        //   })
+        // }
+        if (res.resp_code === 0 && res.datas?.length > 0) {
+          console.log(res.datas?.length)
+          const features: any = []
+          res.datas?.forEach(v => {
+            try {
+              const feat: any = new format.WKT().readFeature(`POINT(${v.longitude} ${v.latitude})`)
+              let type = ''
+              if (v.type = '公安类') {
+                type = 'galsb'
+              } else if (v.type = '社会类') {
+                type = 'shlsb'
+              } else if (v.type = '民用类') {
+                type = 'mylsb'
+              }
+              feat.set('defaultStyle', SbStyle.sbStyle(type))
+              feat.set('activeStyle', [...CommonStyle.activeStyle(), ...SbStyle.sbStyle(type)])
+              feat.set('analysisStyle', [...CommonStyle.analysisStyle(), ...SbStyle.sbStyle(type)])
+              feat.set('analysisActiveStyle', [...CommonStyle.activeStyle(), ...CommonStyle.analysisStyle(), ...SbStyle.sbStyle(type)])
+              feat.set('isAnalysis', true)
+              feat.set('resetStyle', () => {
+                if (feat.get('isAnalysis')) {
+                  feat.setStyle(feat.get('analysisStyle'))
+                } else {
+                  feat.setStyle(feat.get('defaultStyle'))
+                }
+              })
+              feat.setStyle(feat.get('analysisStyle'))
+              feat.set('featureType', 'sb')
+              feat.set('info', v)
+              feat.setId(v.deviceid)
+              features.push(feat)
+              state.qyParams.qyInfo['5'].tableData.push(v)
+            } catch (e) {
+              console.error('异常设备', v)
+            }
+          })
+          state.qyParams.analysisSource.addFeatures(features)
+        }
+        state.qyParams.qyInfo['5'].loading = false
+      }).catch(() => {
+        state.qyParams.qyInfo['5'].loading = true
+      })
+    }
+    const onRadiusReset = () => {
+      state.qyParams.qyInfo['5'].radius = 10
+      onRadiusSubmit()
     }
     onMounted(() => {
     })
@@ -462,9 +595,12 @@ export default defineComponent({
       toolsHandleClick,
       ref_qyDom,
       onCloseQy,
+      onCloseSb,
       handleRangeBlur,
       setCircle,
-      onRadiusSubmit
+      onRadiusSubmit,
+      onRadiusReset,
+      ref_sbDom
     }
   },
 })
@@ -872,5 +1008,97 @@ export default defineComponent({
       }
     }
   }
+  .sb-info {
+    $footH: 10px;
+    width: 220px;
+    background: linear-gradient(180deg, #3874C9 0%, #0043C4 100%);
+    border-radius: 0px 4px 4px 4px;
+    position: relative;
+    display: flex;
+    justify-content: center;
+    &:after {
+      content: '';
+      position: absolute;
+      bottom: -$footH;
+      border-top: $footH solid #0043C4;
+      border-left: $footH solid transparent;
+      border-right: $footH solid transparent;
+    }
+    .sb-info-head {
+      min-width: 68px;
+      height: 18px;
+      position: absolute;
+      top: -18px;
+      left: 0;
+      font-size: 12px;
+      font-family: PingFang SC;
+      font-weight: 500;
+      color: #FFFFFF;
+      display: flex;
+      align-items: center;
+      line-height: 8px;
+      &:before {
+        z-index: -1;
+        content: '';
+        position: absolute;
+        width: 100%;
+        height: 100%;
+        background: linear-gradient(180deg, #3874C9 0%, #0043C4 100%);
+        border-radius: 2px 2px 0 0;/* 设置圆角 */
+        transform: perspective(20px)rotateX(4deg);
+        /* 镜头距离元素表面的位置为8px,x轴为1.1倍y轴为1.3倍,绕x轴旋转5度 */
+        transform-origin: bottom left;
+        /* bottom left = left bottom = 0 100% 中心点偏移量*/
+      }
+      .svg-icon {
+        margin: 0 4px 0 6px;
+      }
+    }
+    .sb-info-close {
+      position: absolute;
+      right: 0;
+      top: -16px;
+    }
+    .sb-main {
+      width: 100%;
+      height: auto;
+      padding: 10px;
+      .sb-item {
+        display: flex;
+        .sb-item-label {
+          width: 42px;
+          font-size: 14px;
+          font-family: PingFang SC;
+          font-weight: 600;
+          color: #08FFFF;
+          line-height: 20px;
+        }
+        .sb-item-value {
+          flex: 1;
+          font-size: 14px;
+          font-family: PingFang SC;
+          font-weight: 400;
+          color: #FFFFFF;
+          line-height: 20px;
+        }
+      }
+      .play-button {
+        width: 76px;
+        height: 24px;
+        background: #1280F1;
+        border-radius: 2px;
+        border: 1px solid #4BA0FF;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        font-size: 14px;
+        font-family: PingFang SC;
+        font-weight: 400;
+        color: #FFFFFF;
+        margin-left: calc((100% - 76px) / 2);
+        margin-top: 10px;
+      }
+    }
+  }
 }
 </style>

+ 77 - 0
src/views/gis/layout/v2-table.vue

@@ -0,0 +1,77 @@
+<template>
+  <el-table-v2
+      class="__gis-overlay_table-v2"
+      :columns="columns"
+      :data="data"
+      :width="width"
+      :height="height"
+      fixed
+      :row-height="25"
+      :header-height="25"
+  >
+    <template #empty></template>
+  </el-table-v2>
+</template>
+
+<script lang="tsx">
+import {
+  defineComponent,
+  computed,
+  onMounted,
+  ref,
+  reactive,
+  watch,
+  getCurrentInstance,
+  ComponentInternalInstance,
+  toRefs,
+  nextTick
+} from 'vue'
+import {useStore} from 'vuex'
+import {useRouter, useRoute} from 'vue-router'
+import {ElMessage, ElMessageBox} from "element-plus";
+import * as turf from "@turf/turf";
+
+export default defineComponent({
+  name: '',
+  components: {},
+  props: {
+    head: {},
+    data: {},
+    width: {},
+    height: {},
+    center: {}
+  },
+  setup(props, {emit}) {
+    const store = useStore();
+    const router = useRouter();
+    const route = useRoute();
+    const that = (getCurrentInstance() as ComponentInternalInstance).appContext.config.globalProperties
+    const state = reactive({
+    })
+    const columns = [
+      {width: 1, title: '序号', align: 'center', cellRenderer: ({ rowIndex }) => `${rowIndex + 1}`},
+      {width: 1, title: '名称', dataKey: 'name'},
+      {width: 1, title: '状态', align: 'center', cellRenderer: ({rowData}) => (
+          <span style={rowData.online === '1' ? "color: #4CFFFF;" : ""}>{rowData.online === '1' ? '在线' : '离线'}</span>
+        )},
+      {width: 1, title: '类型', align: 'center', dataKey: 'type'},
+      {width: 1, title: '距离', align: 'right', cellRenderer: ({rowData}) => {
+          // const start = props.feat.getGeometry().getCoordinates()
+          // const end = that.$easyMap.formatPosition.wptTcpt(rowData.wkt)
+          const end = [Number(rowData.longitude), Number(rowData.latitude)]
+          return turf.distance(props.center, end, {units: 'kilometers'}).toFixed(1) + 'km';
+        }
+      },
+    ]
+    onMounted(() => {
+    })
+    return {
+      ...toRefs(state),
+      columns
+    }
+  },
+})
+</script>
+
+<style scoped lang="scss">
+</style>

+ 12 - 0
vite.config.ts

@@ -32,6 +32,11 @@ export default defineConfig({
       '@components': resolve(__dirname, 'src/components')
     },
   },
+  esbuild: {
+    jsxFactory: 'h',
+    jsxFragment: 'Fragment',
+    jsxInject: "import { h } from 'vue';"
+  },
   server: {
     port: 6969,
     // open: true,
@@ -63,6 +68,13 @@ export default defineConfig({
           return path.replace(/^\/BaseMap3-api/, '')
         }
       },
+      '/business-api/': {
+        target: 'http://192.168.31.26:7074/',
+        changeOrigin: true,
+        rewrite: path => {
+          return path.replace(/^\/business-api/, '')
+        }
+      },
     }
   },
   build: {