CzRger 1 year ago
parent
commit
9f81b65ed8

+ 37 - 0
src/api/modules/ship-test/track.ts

@@ -0,0 +1,37 @@
+import { handle } from '../../index'
+
+const suffix = 'ax-node-api'
+
+export const shipTestTrackList = (params: any) => handle({
+  url: `/${suffix}/ship-test/track/list`,
+  method: 'post',
+  params
+})
+export const shipTestTrackPage = (params: any) => handle({
+  url: `/${suffix}/ship-test/track/page`,
+  method: 'post',
+  params
+})
+export const shipTestTrackInfo = (id: any) => handle({
+  url: `/${suffix}/ship-test/track/info/${id}`,
+  method: 'get',
+})
+export const shipTestTrackAdd = (params: any) => handle({
+  url: `/${suffix}/ship-test/track/add`,
+  method: 'post',
+  params
+})
+export const shipTestTrackEdit = (params: any) => handle({
+  url: `/${suffix}/ship-test/track/edit`,
+  method: 'put',
+  params
+})
+export const shipTestTrackDel = (params: any) => handle({
+  url: `/${suffix}/ship-test/track/del/`,
+  method: 'delete',
+  params
+})
+export const shipTestTrackInfoBySourceId = (sourceId: any) => handle({
+  url: `/${suffix}/ship-test/track/infoBySourceId/${sourceId}`,
+  method: 'get',
+})

+ 8 - 0
src/router/ship-test.ts

@@ -25,6 +25,14 @@ const aisTestRouter = [
           title: '参数配置'
         }
       },
+      {
+        name: '4e53c650-a86c-4fde-83b2-eb316dd5e66a2222',
+        path: 'track',
+        component: () => import('@/views/ship-test/manage/track/index.vue'),
+        meta: {
+          title: '轨迹信息'
+        }
+      },
     ]
   },
 ]

+ 45 - 11
src/views/ship-test/business/ship-filter.vue

@@ -73,6 +73,7 @@ import {useRouter, useRoute} from 'vue-router'
 import {ElMessage, ElMessageBox} from "element-plus";
 import * as source from "ol/source";
 import * as layer from "ol/layer";
+import * as format from "ol/format";
 
 export default defineComponent({
   name: '',
@@ -91,11 +92,16 @@ export default defineComponent({
       shipSource: <any>[],
       shipParams: <any>null,
       zoomWMS: true,
-      wsMap: new Map()
+      wsMap: new Map(),
+      wsLayer: <any>null
     })
     const initSource = () => {
       that.$api.shipTestShipFilterGetConfig().then(res => {
         state.shipSource = res.data
+        if (state.shipSource?.[0]) {
+          state.shipSource[0].active = true
+          onSubmit()
+        }
       })
     }
     watch(() => props.map, (n) => {
@@ -105,10 +111,10 @@ export default defineComponent({
     })
     const initMap = () => {
       props.map?.on('moveend', (e) => {
-        console.log(e)
         const zoom = e.map.getView().getZoom()
         if (zoom > 13) {
           state.zoomWMS = false
+          initWebSocketShip()
         } else {
           state.zoomWMS = true
         }
@@ -181,22 +187,21 @@ export default defineComponent({
         str.cql = `(BBOX(location, ${props.mapFunc.getBBOX().join(',')}) and ${shipCqlComputer.value})`
       }
       if (state.wsMap.has(state.shipParams.id)) {
-        state.wsMap.get(state.shipParams.id).websocket.send(JSON.stringify(str))
+        if (state.wsMap.get(state.shipParams.id).readyState === 1) {
+          state.wsMap.get(state.shipParams.id).send(JSON.stringify(str))
+        }
       } else {
         const ws = new WebSocket(state.shipParams.wsUrl)
-        state.wsMap.set(state.shipParams.id, {
-          websocket: ws,
-          layer: null
-        })
+        state.wsMap.set(state.shipParams.id, ws)
         ws.onopen = (e) => {
           ws.send(JSON.stringify(str))
         }
         ws.onmessage = (e) => {
           try {
             const json = JSON.parse(e.data)
-            const s = 'json.' + state.shipParams.wsDataFlag
+            const s = 'json' + state.shipParams.wsDataFlag
             const data = eval(s)
-            console.log(data)
+            initShip(data)
           } catch (e) {
           }
         }
@@ -215,8 +220,37 @@ export default defineComponent({
         }
       }
     }
-    const initShip = () => {
-
+    const initShip = (data) => {
+      if (!state.wsLayer) {
+        state.wsLayer = new layer.VectorImage({
+          zIndex: 10,
+        })
+        state.wsLayer.set('layerName', 'WSShip')
+        props.map.addLayer(state.wsLayer)
+      }
+      const ships = data
+      //  动态拼接数据的唯一标识DATA,不可修改
+      const features = ships.map(DATA => {
+        try {
+          const getKeyData = (key) => {
+            if (state.shipParams.track[key]) {
+              return eval(state.shipParams.track[key])
+            } else {
+              return null
+            }
+          }
+          const feat: any = new format.WKT().readFeature(getKeyData('trackWktKey'))
+          feat.setId(getKeyData('trackPointKey'))
+          return feat
+        } catch (e) {
+          console.log(e)
+        }
+      })
+      const vectorSource = new source.Vector({
+        features: features,
+        wrapX: false
+      });
+      state.wsLayer.setSource(vectorSource)
     }
     onMounted(() => {
       initSource()

+ 19 - 2
src/views/ship-test/manage/source/detail.vue

@@ -42,7 +42,7 @@
           required
           label="图层名称"
           v-model:param="cusDetail.tileName"/>
-        <div class="__cus-title_2">WebSocker</div>
+        <div class="__cus-title_2">WebSocket</div>
         <CusFormColumn
           :span="24"
           required
@@ -52,7 +52,24 @@
           :span="24"
           v-model:param="cusDetail.wsDataFlag">
           <template #label>
-            <el-tooltip content="实际返回数据的路径,空值的话直接取返回值,或者data、data.list">
+            <el-tooltip content="实际返回数据的路径,空值的话直接取返回值,对象的话例如.data">
+              <div>
+                数据标识<SvgIcon name="tips" color="#333333"/>
+              </div>
+            </el-tooltip>
+          </template>
+        </CusFormColumn>
+        <div class="__cus-title_2">历史轨迹WebSocket</div>
+        <CusFormColumn
+            :span="24"
+            required
+            label="地址"
+            v-model:param="cusDetail.historyWsUrl"/>
+        <CusFormColumn
+            :span="24"
+            v-model:param="cusDetail.historyWsDataFlag">
+          <template #label>
+            <el-tooltip content="实际返回数据的路径,空值的话直接取返回值,对象的话例如.data">
               <div>
                 数据标识<SvgIcon name="tips" color="#333333"/>
               </div>

+ 225 - 0
src/views/ship-test/manage/track/index.vue

@@ -0,0 +1,225 @@
+<template>
+  <div class="__normal-page">
+    <div class="__normal-tree">
+      <div class="__normal-tree_filter">
+        <el-input v-model="treeName" placeholder="请输入关键词进行搜索" clearable>
+          <template #suffix>
+            <img src="@/assets/images/cus/tree-search.png" alt=""/>
+          </template>
+        </el-input>
+      </div>
+      <div class="__normal-tree_content">
+        <el-tree
+            ref="ref_tree"
+            :data="treeData"
+            node-key="id"
+            :props="{
+              children: 'children',
+              label: 'name',
+            }"
+            @node-click="handleNodeClick"
+            :expand-on-click-node="false"
+            highlight-current
+            :filter-node-method="treeFilter"
+        />
+      </div>
+    </div>
+    <div class="__normal-content" v-if="currentSource">
+      <CusForm labelWidth="100px" ref="ref_form">
+        <div class="__cus-title_2">实时轨迹唯一标识(DATA.xxx形式,可任意拼接或使用字符串模板`POINT(${DATA.xxx} ${DATA.xxx})`或DATA.xxx + DATA.xxx)</div>
+        <CusFormColumn
+            :span="12"
+            required
+            label="轨迹"
+            v-model:param="form.trackKey"/>
+        <CusFormColumn
+            :span="12"
+            required
+            label="轨迹点"
+            v-model:param="form.trackPointKey"/>
+        <CusFormColumn
+            :span="12"
+            required
+            label="WKT坐标"
+            v-model:param="form.trackWktKey"/>
+        <CusFormColumn
+            :span="12"
+            required
+            label="速度"
+            v-model:param="form.trackSpeedKey"/>
+        <CusFormColumn
+            :span="12"
+            required
+            label="航向"
+            v-model:param="form.trackCourseKey"/>
+        <CusFormColumn
+            :span="12"
+            label="船艏向"
+            v-model:param="form.trackHeadKey"/>
+        <div class="__cus-title_2">历史轨迹唯一标识(DATA.xxx形式,可任意拼接或使用字符串模板`POINT(${DATA.xxx} ${DATA.xxx})`或DATA.xxx + DATA.xxx)</div>
+        <CusFormColumn
+            :span="12"
+            required
+            label="轨迹"
+            v-model:param="form.historyTrackKey"/>
+        <CusFormColumn
+            :span="12"
+            required
+            label="轨迹点"
+            v-model:param="form.historyTrackPointKey"/>
+        <CusFormColumn
+            :span="12"
+            required
+            label="WKT坐标"
+            v-model:param="form.historyTrackWktKey"/>
+        <CusFormColumn
+            :span="12"
+            required
+            label="速度"
+            v-model:param="form.historyTrackSpeedKey"/>
+        <CusFormColumn
+            :span="12"
+            required
+            label="航向"
+            v-model:param="form.historyTrackCourseKey"/>
+        <CusFormColumn
+            :span="12"
+            label="船艏向"
+            v-model:param="form.historyTrackHeadKey"/>
+      </CusForm>
+      <div class="buttons">
+        <el-button type="primary" @click="onSubmit">保存</el-button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts">
+import {
+  defineComponent,
+  onMounted,
+  ref,
+  toRefs,
+  reactive,
+  watch,
+  getCurrentInstance,
+  ComponentInternalInstance,
+  computed, nextTick,
+} from "vue";
+import { useStore } from "vuex";
+import { useRouter, useRoute } from "vue-router";
+import { ElMessage, ElMessageBox } from "element-plus";
+
+export default defineComponent({
+  name: "",
+  components: {
+  },
+  setup(props, {emit}) {
+    const store = useStore();
+    const that = (getCurrentInstance() as ComponentInternalInstance).appContext.config.globalProperties;
+    const state = reactive({
+      //  加载中
+      loading: false,
+      treeData: [],
+      treeName: '',
+      currentSource: <any>null,
+      form: <any>{}
+    });
+    const ref_form = ref()
+    //  获取字典
+    const initDictionary = () => {
+    }
+    const ref_tree = ref()
+    const handleNodeClick = (data) => {
+      state.currentSource = data
+      initTrack()
+    }
+    const treeFilter = (value, data) => {
+      if (!value) return true
+      return data.name.includes(value)
+    }
+    watch(() => state.treeName, (n) => {
+      ref_tree.value.filter(n)
+    })
+    const initSource = () => {
+      that.$api.shipTestSourceList().then(res => {
+        state.treeData = res.data
+      })
+      nextTick(() => {
+        ref_form.value?.reset()
+      })
+    }
+    const initTrack = () => {
+      that.$api.shipTestTrackInfoBySourceId(state.currentSource.id).then(res => {
+        if (res.code === 200) {
+          if (res.data?.[0]) {
+            state.form = res.data[0]
+          } else {
+            state.form = {
+              sourceId: state.currentSource.id
+            }
+          }
+        } else {
+          ElMessage.error(res.msg)
+        }
+      })
+    }
+    const onSubmit = () => {
+      ref_form.value.submit().then(() => {
+        ElMessageBox.confirm("是否提交?", "提示", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning",
+        }).then(() => {
+          state.loading = true
+          const apiHandle = state.form.id ? that.$api.shipTestTrackEdit(state.form) : that.$api.shipTestTrackAdd(state.form)
+          apiHandle.then(res => {
+            if (res.code === 200) {
+              ElMessage.success(state.form.id ? '编辑成功' : '新增成功')
+              initTrack()
+            } else {
+              ElMessage.error(res.msg)
+            }
+            state.loading = false
+          }).catch(() => {
+            state.loading = false
+          })
+        }).catch(() => {})
+      }).catch((e) => {
+        ElMessage({
+          message: e[0].message,
+          grouping: true,
+          type: 'warning',
+        })
+      })
+    }
+    onMounted(() => {
+      initDictionary()
+      initSource()
+    })
+    return {
+      ref_form,
+      ...toRefs(state),
+      handleNodeClick,
+      treeFilter,
+      ref_tree,
+      onSubmit
+    }
+  },
+});
+</script>
+<style scoped lang="scss">
+.__normal-content {
+  background-color: #FFFFFF;
+  padding: 20px;
+  .__cus-title_2 {
+    margin-bottom: 10px;
+  }
+  .buttons {
+    width: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+  }
+}
+</style>