Bläddra i källkod

项目工作空间-机场

李富豪 1 år sedan
förälder
incheckning
95435619cb
28 ändrade filer med 1178 tillägg och 197 borttagningar
  1. 9 5
      Web/src/components/GMap.vue
  2. 633 0
      Web/src/components/airport/components/InfoModal.vue
  3. 5 0
      Web/src/components/airport/icons/aircraft.svg
  4. 0 0
      Web/src/components/airport/icons/dockInfo.svg
  5. 1 0
      Web/src/components/airport/icons/info.svg
  6. 11 0
      Web/src/components/airport/icons/info/controller.svg
  7. 11 0
      Web/src/components/airport/icons/info/controllerError.svg
  8. 10 0
      Web/src/components/airport/icons/info/gps.svg
  9. 23 0
      Web/src/components/airport/icons/info/gpsError.svg
  10. 10 0
      Web/src/components/airport/icons/info/home.svg
  11. 10 0
      Web/src/components/airport/icons/info/network.svg
  12. 27 0
      Web/src/components/airport/icons/info/networkError.svg
  13. 11 0
      Web/src/components/airport/icons/info/rtk.svg
  14. 21 0
      Web/src/components/airport/icons/info/rtkError.svg
  15. 7 0
      Web/src/components/airport/icons/info/signalFive.svg
  16. 7 0
      Web/src/components/airport/icons/info/signalFour.svg
  17. 7 0
      Web/src/components/airport/icons/info/signalThree.svg
  18. 5 0
      Web/src/components/airport/icons/info/wind.svg
  19. 1 0
      Web/src/components/airport/icons/info_selected.svg
  20. 0 0
      Web/src/components/airport/icons/task.svg
  21. 278 0
      Web/src/components/airport/index.vue
  22. 1 2
      Web/src/components/onLineDevice/components/InfoModal.vue
  23. 0 1
      Web/src/components/onLineDevice/icons/deviceInfo.svg
  24. 1 0
      Web/src/components/onLineDevice/icons/info.svg
  25. 1 0
      Web/src/components/onLineDevice/icons/info_selected.svg
  26. 18 45
      Web/src/components/onLineDevice/index.vue
  27. 7 144
      Web/src/pages/page-web/projects/tsa.vue
  28. 63 0
      Web/src/utils/index.ts

+ 9 - 5
Web/src/components/GMap.vue

@@ -39,10 +39,9 @@
         WGS 84
       </div>
     </div>
-    <!-- 飞机OSD -->
-    <OsdInfoModal type="MANUAL" :osdInfo="osdVisible" :deviceInfo="deviceInfo"
-      v-if="osdVisible.visible && !osdVisible.is_dock" />
     <!-- 机场OSD -->
+    <!-- <AirportOsdInfoModal :osdInfo="osdVisible" :deviceInfo="deviceInfo"
+      v-if="osdVisible.visible && osdVisible.is_dock" /> -->
     <div v-if="osdVisible.visible && osdVisible.is_dock" v-drag-window class="osd-panel fz12">
       <div class="drag-title fz16 pl5 pr5 flex-align-center flex-row flex-justify-between"
         style="border-bottom: 1px solid #515151; height: 10%;">
@@ -378,6 +377,9 @@
       <DroneControlPanel :sn="osdVisible.gateway_sn" :deviceInfo="deviceInfo" :payloads="osdVisible.payloads">
       </DroneControlPanel>
     </div>
+    <!-- 飞机OSD -->
+    <DeviceOsdInfoModal :osdInfo="osdVisible" :deviceInfo="deviceInfo"
+      v-if="osdVisible.visible && !osdVisible.is_dock" />
     <!-- liveview -->
     <div class="liveview" v-if="livestreamOthersVisible" v-drag-window>
       <div style="height: 40px; width: 100%" class="drag-title"></div>
@@ -407,7 +409,8 @@ import {
 } from '../utils/map-layer-utils'
 import { postElementsReq } from '/@/api/layer'
 import { MapDoodleType, MapElementEnum } from '/@/constants/map'
-import OsdInfoModal from '/@/components/onLineDevice/components/InfoModal.vue'
+import AirportOsdInfoModal from '/@/components/airport/components/InfoModal.vue'
+import DeviceOsdInfoModal from '/@/components/onLineDevice/components/InfoModal.vue'
 import { useGMapManage } from '/@/hooks/use-g-map'
 import { useGMapCover } from '/@/hooks/use-g-map-cover'
 import { useMouseTool } from '/@/hooks/use-mouse-tool'
@@ -464,7 +467,8 @@ export default defineComponent({
     RobotFilled,
     ArrowUpOutlined,
     ArrowDownOutlined,
-    OsdInfoModal,
+    AirportOsdInfoModal,
+    DeviceOsdInfoModal,
     DockControlPanel,
     DroneControlPanel,
     CarryOutOutlined,

+ 633 - 0
Web/src/components/airport/components/InfoModal.vue

@@ -0,0 +1,633 @@
+<template>
+  <div v-drag-window class="content infoModal-content">
+    <div class="content-title">
+      <div class="drag-title">
+        <span>{{ osdInfo.callsign }}</span>
+      </div>
+      <a class="fz16" style="color: white;" @click="() => osdInfo.visible = false">
+        <CloseOutlined />
+      </a>
+    </div>
+    <div class="content-osd">
+      <div class="content-osd-head">
+        <div class="content-osd-head-icon">
+          <div class="content-osd-head-icon-image">
+            <a-image :src="M30Src" :preview="false" />
+          </div>
+          <a-tooltip :title="osdInfo.model">
+            <div class="content-osd-head-icon-text">
+              {{ osdInfo.model }}
+            </div>
+          </a-tooltip>
+        </div>
+        <div class="content-osd-head-right">
+          <div class="content-osd-head-right-top">
+            <div class="content-osd-head-right-top-style">
+              航线飞行
+            </div>
+            <div class="content-osd-head-right-top-status">
+              {{ getTextByModeCode(deviceInfo.device.mode_code) }}
+            </div>
+          </div>
+          <div class="content-osd-head-right-bottom">
+            <div class="content-osd-head-right-bottom-button">
+              <span class="openLiveButton" @click="state.deviceLiveStatus = true" v-if="!state.deviceLiveStatus">
+                开启直播
+              </span>
+              <span class="openLiveButton" @click="state.deviceLiveStatus = false" v-else>
+                关闭直播
+              </span>
+            </div>
+            <div class="content-osd-head-right-bottom-text">
+              如需切换直播请停止后重新发起
+            </div>
+          </div>
+          <div class="content-osd-head-right-select">
+            <div v-if="state.deviceLiveStatus">
+              <a-select style="width: 130px;margin-right: 5px;" placeholder="摄像头" v-model:value="state.cameraValue">
+                <a-select-option v-for="item in state.cameraList" :key="item.value" :value="item.value"
+                  @click="onCameraSelect(item)">
+                  {{ item.label }}
+                </a-select-option>
+              </a-select>
+              <a-select style="width: 130px;margin-right: 5px;" placeholder="清晰度" v-model:value="state.clarityValue"
+                @select="onClaritySelect">
+                <a-select-option v-for="item in clarityList" :key="item.value" :value="item.value">
+                  {{ item.label }}
+                </a-select-option>
+              </a-select>
+              <a-button style="margin-right: 5px;" :icon="h(PlaySquareOutlined)" @click="onStart" />
+              <a-button :icon="h(PoweroffOutlined)" @click="onStop" />
+            </div>
+          </div>
+        </div>
+      </div>
+      <div class="battery-slide">
+        <div style="background: #535759;" class="width-100"></div>
+        <div class="capacity-percent" :style="{ width: deviceInfo.device.battery.capacity_percent + '%' }"></div>
+      </div>
+      <LivePlayer :text="state.playerText" :url="state.playerUrl" v-if="state.deviceLiveStatus" />
+      <div class="content-osd-info">
+        <a-row style="margin-bottom: 5px;">
+          <a-col span="6">
+            <div style="margin-top: -3px;" class="content-osd-info-item">
+              <a-tooltip title="遥控器信号">
+                <div style="display: flex;align-items: flex-end;">
+                  <img style="margin-right: 5px;" :src="controllerSrc" v-if="controllerSignal > 0">
+                  <img style="margin-right: 5px;" :src="controllerErrorSrc" v-else>
+                  <div>
+                    <img :src="signalThreeSrc" v-if="controllerSignal <= 3">
+                    <img :src="signalFourSrc" v-else-if="controllerSignal === 4">
+                    <img :src="signalFiveSrc" v-else-if="controllerSignal === 5">
+                  </div>
+                </div>
+              </a-tooltip>
+              <a-tooltip title="飞行器信号">
+                <div style="display: flex;align-items: flex-end;">
+                  <img style="width: 15px;margin-right: 5px;" :src="networkSrc" v-if="aircraftSignal > 0">
+                  <img style="width: 15px;margin-right: 5px;" :src="networkErrorSrc" v-else>
+                  <div>
+                    <img :src="signalThreeSrc" v-if="aircraftSignal <= 3">
+                    <img :src="signalFourSrc" v-else-if="aircraftSignal === 4">
+                    <img :src="signalFiveSrc" v-else-if="aircraftSignal === 5">
+                  </div>
+                </div>
+              </a-tooltip>
+            </div>
+          </a-col>
+          <a-col span="6">
+            <div class="content-osd-info-item">
+              <a-tooltip title="RTK">
+                <div style="display: flex;align-items: center;margin-right: 5px;">
+                  <img style="width: 24px;margin-right: 5px;" :src="rtkSrc" v-if="RTK > 0">
+                  <img style="width: 24px; margin-right: 5px;" :src="rtkErrorSrc" v-else>
+                  <div>
+                    {{ RTK }}
+                  </div>
+                </div>
+              </a-tooltip>
+              <a-tooltip title="GPS">
+                <div style="display: flex;align-items: center;">
+                  <img style="margin-right: 5px;" :src="gpsSrc" v-if="GPS > 0">
+                  <img style="margin-right: 5px;" :src="gpsErrorSrc" v-else>
+                  <div>
+                    {{ GPS }}
+                  </div>
+                </div>
+              </a-tooltip>
+            </div>
+          </a-col>
+          <a-col span="6">
+            <a-tooltip title="电量">
+              <div class="content-osd-info-item">
+                <div style="margin-right: 5px;">
+                  <!-- <img :src="batteryOneSrc" v-if="capacity >= 75">
+                  <img :src="batteryTwoSrc" v-else-if="capacity >= 50 && capacity < 75">
+                  <img :src="batteryThreeSrc" v-else-if="capacity >= 25 && capacity < 50">
+                  <img :src="batteryFourSrc" v-else-if="capacity < 25"> -->
+                </div>
+                <div>
+                  {{ capacity + '%' }}
+                </div>
+              </div>
+            </a-tooltip>
+          </a-col>
+          <a-col span="6">
+            <a-tooltip title="风向速度">
+              <div class="content-osd-info-item">
+                <img style="margin-right: 5px;" :src="windSrc">
+                <div style="font-weight: bold;margin-right: 5px;">
+                  W.S
+                </div>
+                <div>
+                  {{ windSpeed }}
+                </div>
+              </div>
+            </a-tooltip>
+          </a-col>
+        </a-row>
+        <a-row>
+          <a-col span="6">
+            <a-tooltip title="海拔高度">
+              <div class="content-osd-info-item">
+                <div style="font-weight: bold;margin-right: 5px;">
+                  ASL
+                </div>
+                <div>
+                  {{ ASL }}
+                </div>
+              </div>
+            </a-tooltip>
+          </a-col>
+          <a-col span="6">
+            <a-tooltip title="离地高度">
+              <div class="content-osd-info-item">
+                <div style="font-weight: bold;margin-right: 5px;">
+                  AGL
+                </div>
+                <div>
+                  {{ AGL }}
+                </div>
+              </div>
+            </a-tooltip>
+          </a-col>
+          <a-col span="6">
+            <a-tooltip title="水平速度">
+              <div class="content-osd-info-item">
+                <div style="font-weight: bold;margin-right: 5px;">
+                  H.S
+                </div>
+                <div>
+                  {{ HS }}
+                </div>
+              </div>
+            </a-tooltip>
+          </a-col>
+          <a-col span="6">
+            <a-tooltip title="当前高度">
+              <div class="content-osd-info-item">
+                <img style="margin-right: 5px;" :src="homeSrc">
+                <div>
+                  {{ homeDistance }}
+                </div>
+              </div>
+            </a-tooltip>
+          </a-col>
+        </a-row>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { h, computed, reactive, onMounted } from 'vue';
+import { message } from 'ant-design-vue';
+import { CloseOutlined, PlaySquareOutlined, PoweroffOutlined } from '@ant-design/icons-vue'
+import LivePlayer from '/@/components/livePlayer/index.vue';
+import M30Src from '/@/assets/icons/m30.png';
+import controllerSrc from '../icons/info/controller.svg';
+import controllerErrorSrc from '../icons/info/controllerError.svg';
+import networkSrc from '../icons/info/network.svg';
+import networkErrorSrc from '../icons/info/networkError.svg';
+import signalThreeSrc from '../icons/info/signalThree.svg';
+import signalFourSrc from '../icons/info/signalFour.svg';
+import signalFiveSrc from '../icons/info/signalFive.svg';
+import rtkSrc from '../icons/info/rtk.svg';
+import rtkErrorSrc from '../icons/info/rtkError.svg';
+import gpsSrc from '../icons/info/gps.svg';
+import gpsErrorSrc from '../icons/info/gpsError.svg';
+// import batteryOneSrc from '../icons/batteryOne.svg';
+// import batteryTwoSrc from '../icons/batteryTwo.svg';
+// import batteryThreeSrc from '../icons/batteryThree.svg';
+// import batteryFourSrc from '../icons/batteryFour.svg';
+import windSrc from '../icons/info/wind.svg';
+import homeSrc from '../icons/info/home.svg';
+import { CURRENT_CONFIG as config } from '/@/api/http/config';
+import { getLiveCapacity, startLivestream, stopLivestream } from '/@/api/manage';
+import { getTextByModeCode } from '/@/utils/index'
+
+interface Props {
+  osdInfo: any
+  deviceInfo: any
+};
+
+const props = withDefaults(defineProps<Props>(), {
+
+});
+
+// 遥控器信号
+const controllerSignal = computed(() => {
+  const info = props.deviceInfo.gateway;
+  if (info && info.wireless_link) {
+    return info.wireless_link['4g_gnd_quality'];
+  } else {
+    return 0;
+  }
+});
+
+// 飞机信号
+const aircraftSignal = computed(() => {
+  const info = props.deviceInfo.gateway;
+  if (info && info.wireless_link) {
+    return info.wireless_link['4g_uav_quality'];
+  } else {
+    return 0;
+  }
+});
+
+const RTK = computed(() => {
+  const info = props.deviceInfo.device;
+  return info.position_state.rtk_number;
+});
+
+const GPS = computed(() => {
+  const info = props.deviceInfo.device;
+  return info.position_state.gps_number;
+});
+
+// 电池容量
+const capacity = computed(() => {
+  const info = props.deviceInfo.device;
+  if (info) {
+    return info.battery.capacity_percent;
+  } else {
+    return 0;
+  }
+});
+
+const windSpeed = computed(() => {
+  const info = props.deviceInfo.device;
+  if (info.wind_speed === '--') {
+    return info.wind_speed;
+  } else {
+    return (info.wind_speed / 10).toFixed(2) + ' m/s';
+  }
+});
+
+const ASL = computed(() => {
+  const info = props.deviceInfo.device;
+  if (info.height === '--') {
+    return info.height;
+  } else {
+    return info.height.toFixed(2) + ' m';
+  }
+});
+
+const AGL = computed(() => {
+  const info = props.deviceInfo.device;
+  if (info.elevation === '--') {
+    return info.elevation;
+  } else {
+    return info.elevation.toFixed(2) + ' m';
+  }
+});
+
+const HS = computed(() => {
+  const info = props.deviceInfo.device;
+  if (info.horizontal_speed === '--') {
+    return info.horizontal_speed;
+  } else {
+    return info.horizontal_speed.toFixed(2) + ' m/s';
+  }
+});
+
+const homeDistance = computed(() => {
+  const info = props.deviceInfo.device;
+  if (info.home_distance === '--') {
+    return info.home_distance;
+  } else {
+    return info.home_distance.toFixed(2) + ' m';
+  }
+});
+
+interface SelectOption {
+  value: any,
+  label: string,
+  more?: any
+}
+
+const clarityList: SelectOption[] = [
+  {
+    value: 0,
+    label: '自适应'
+  },
+  {
+    value: 1,
+    label: '流畅'
+  },
+  {
+    value: 2,
+    label: '标清'
+  },
+  {
+    value: 3,
+    label: '高清'
+  },
+  {
+    value: 4,
+    label: '超清'
+  }
+]
+
+interface State {
+  deviceLiveStatus: boolean,
+  cameraList: SelectOption[],
+  cameraValue?: string,
+  videoList: SelectOption[],
+  videoValue?: string,
+  clarityValue: number,
+  videoId: string,
+  playerText: string,
+  playerUrl: string,
+}
+
+const state: State = reactive({
+  deviceLiveStatus: false,// 设备直播状态
+  cameraList: [],
+  cameraValue: undefined,
+  videoList: [],
+  videoValue: undefined,
+  clarityValue: 0,
+  videoId: '',
+  playerText: '',
+  playerUrl: '',
+})
+
+const fetchLiveCapacity = async () => {
+  try {
+    const res = await getLiveCapacity({});
+    if (res.code === 0) {
+      const deviceInfo = res.data.filter((item: any) => item.sn === props.osdInfo.sn)[0];
+      const cameras_list = deviceInfo.cameras_list || [];
+      const cameraList = cameras_list.map((item: any) => {
+        return {
+          label: item.name,
+          value: item.index,
+          more: item.videos_list
+        }
+      })
+      state.cameraList = cameraList;
+    }
+  } catch (e: any) {
+    console.error(e);
+  }
+}
+
+onMounted(async () => {
+  await fetchLiveCapacity()
+})
+
+const onCameraSelect = (record: SelectOption) => {
+  state.cameraValue = record.value;
+  if (!record.more) {
+    return
+  }
+  const videoList = record.more.map((ele: any) => {
+    return {
+      label: ele.type,
+      value: ele.index,
+      more: ele.switch_video_types
+    }
+  })
+  state.videoList = videoList;
+  if (videoList.length === 0) {
+    return;
+  }
+  const firstVideo: SelectOption = videoList[0];
+  state.videoValue = firstVideo.value;
+}
+
+const onClaritySelect = (value: any) => {
+  state.clarityValue = value;
+}
+
+const onStart = async () => {
+  const { cameraValue, videoValue } = state;
+  if (!cameraValue) {
+    return message.warn('请选择摄像头');
+  }
+  const videoId = `${props.osdInfo.sn}/${cameraValue}/${videoValue || 'normal-0'}`;
+  state.videoId = videoId;
+  const liveURL = config.rtmpURL;
+  try {
+    const res = await startLivestream({
+      url: liveURL,
+      video_id: videoId,
+      url_type: 1,// RTMP 
+      video_quality: state.clarityValue
+    });
+    if (res.code !== 0) {
+      state.playerText = res.message;
+    } else {
+      const playerUrl = res.data.url;
+      state.playerText = '';
+      state.playerUrl = playerUrl;
+      message.success('已开启直播');
+    }
+  } catch (e: any) {
+    console.error(e);
+  }
+}
+
+const onStop = async () => {
+  const { cameraValue, videoValue } = state;
+  if (!cameraValue) {
+    return message.warn('请选择摄像头');
+  }
+  const videoId = `${props.osdInfo.sn}/${cameraValue}/${videoValue || 'normal-0'}`;
+  const res = await stopLivestream({
+    video_id: videoId,
+  });
+  if (res.code === 0) {
+    state.videoId = '';
+    state.playerUrl = '';
+    message.success('已停止直播');
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.content {
+  width: 420px;
+  color: #fff;
+  border-radius: 4px;
+  overflow: hidden;
+  // background-color: rgba(0, 0, 0, 0.8);
+  background-color: #232323;
+  position: absolute;
+  left: 10px;
+  top: 10px;
+
+  &-title {
+    padding: 5px;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    border: 1px solid #535759;
+  }
+
+  &-osd {
+    font-size: 12px;
+
+    &-head {
+      display: flex;
+
+      &-icon {
+        width: 80px;
+        padding: 5px;
+        background: #3d3d3d;
+        display: flex;
+        flex-direction: column;
+        justify-content: center;
+        align-items: center;
+
+        &-image {
+          width: 80%;
+        }
+
+        &-text {
+          text-align: center;
+          white-space: nowrap;
+          overflow: hidden;
+        }
+      }
+
+      &-right {
+        width: 100%;
+        padding: 5px;
+
+        &-top {
+          margin: 10px 0;
+          display: flex;
+          align-items: center;
+
+          &-style {
+            width: 100px;
+            color: #2a994b;
+            border-right: 1px solid #535759;
+            margin-right: 5px;
+          }
+
+          &-status {
+            flex: 1;
+            padding-left: 5px;
+            background: #4a4d4e;
+          }
+        }
+
+        &-bottom {
+          display: flex;
+          align-items: center;
+
+          &-button {
+            width: 100px;
+            border-right: 1px solid #535759;
+            margin-right: 5px;
+
+            .openLiveButton {
+              padding: 2px 4px;
+              border: 1.5px solid #535759;
+              border-radius: 2px;
+              cursor: pointer;
+            }
+          }
+
+          &-text {
+            color: #535759;
+          }
+        }
+
+        &-select {
+          margin: 10px 0 5px;
+        }
+      }
+    }
+
+    &-info {
+      padding: 5px;
+
+      &-item {
+        display: flex;
+        align-items: center;
+
+        img {
+          width: 13px;
+          height: 13px;
+        }
+      }
+    }
+  }
+}
+</style>
+
+<style lang="scss">
+.infoModal-content {
+
+  // 修改按钮样式
+  .ant-btn {
+    color: #FFFFFF;
+    background: transparent;
+    border: 1px solid #535759;
+
+    &:hover {
+      color: #fff;
+      background-color: transparent;
+      border-color: #535759;
+    }
+
+    &:focus {
+      color: #fff;
+      background-color: transparent;
+      border-color: #535759;
+    }
+  }
+
+  .ant-select-selector {
+    color: #FFFFFF;
+    background: transparent !important;
+    border: 1px solid #535759 !important;
+    box-shadow: none !important;
+  }
+
+  .ant-select-arrow {
+    color: #fff;
+  }
+
+  .ant-select-dropdown {
+    background-color: transparent;
+
+    .ant-select-item {
+      background-color: #000000 !important;
+      color: #fff;
+
+      &:hover {
+        background-color: #4a4a4a !important;
+      }
+    }
+
+    .ant-select-item-option-selected {
+      background-color: #3a3a3a;
+    }
+  }
+}
+</style>

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 5 - 0
Web/src/components/airport/icons/aircraft.svg


+ 0 - 0
Web/src/components/onLineDevice/icons/dockInfo.svg → Web/src/components/airport/icons/dockInfo.svg


+ 1 - 0
Web/src/components/airport/icons/info.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1725866227895" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1469" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M64.64 128.32v680.896h887.36V128.32H64.64z m815.68 609.152H136.32V200h744v537.472z m-655.36 130.752h566.72v71.68H224.96v-71.68z" fill="#ffffff" p-id="1470"></path><path d="M262.592 428.16l129.408-118.912 48.512 52.8L311.04 480.96l-48.512-52.8z m277.12 121.792l129.408-118.976 48.512 52.8-129.408 118.912-48.512-52.736z m-209.536 4.16l266.368-244.864 48.512 52.8-266.368 244.8-48.512-52.736z" fill="#ffffff" p-id="1471"></path></svg>

+ 11 - 0
Web/src/components/airport/icons/info/controller.svg

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>Icons/rc_controller</title>
+    <g id="Icons/rc_controller" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="controller" fill="#F0F1F0">
+            <path d="M10,0 L10,12 L22,12 L22,0 L26,0 L26,12 L32,12 L32,32 L0,32 L0,12 L6,12 L6,0 L10,0 Z M8,16 C4.6862915,16 2,18.6862915 2,22 C2,25.3137085 4.6862915,28 8,28 C11.3137085,28 14,25.3137085 14,22 C14,18.6862915 11.3137085,16 8,16 Z M24,16 C20.6862915,16 18,18.6862915 18,22 C18,25.3137085 20.6862915,28 24,28 C27.3137085,28 30,25.3137085 30,22 C30,18.6862915 27.3137085,16 24,16 Z" id="形状结合"></path>
+            <circle id="椭圆形" cx="8" cy="22" r="4"></circle>
+            <circle id="椭圆形" cx="24" cy="22" r="4"></circle>
+        </g>
+    </g>
+</svg>

+ 11 - 0
Web/src/components/airport/icons/info/controllerError.svg

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>Icons/rc_controller</title>
+    <g id="Icons/rc_controller" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="controller" fill="#FF0000">
+            <path d="M10,0 L10,12 L22,12 L22,0 L26,0 L26,12 L32,12 L32,32 L0,32 L0,12 L6,12 L6,0 L10,0 Z M8,16 C4.6862915,16 2,18.6862915 2,22 C2,25.3137085 4.6862915,28 8,28 C11.3137085,28 14,25.3137085 14,22 C14,18.6862915 11.3137085,16 8,16 Z M24,16 C20.6862915,16 18,18.6862915 18,22 C18,25.3137085 20.6862915,28 24,28 C27.3137085,28 30,25.3137085 30,22 C30,18.6862915 27.3137085,16 24,16 Z" id="形状结合"></path>
+            <circle id="椭圆形" cx="8" cy="22" r="4"></circle>
+            <circle id="椭圆形" cx="24" cy="22" r="4"></circle>
+        </g>
+    </g>
+</svg>

+ 10 - 0
Web/src/components/airport/icons/info/gps.svg

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>Icons/GPS</title>
+    <g id="Icons/GPS" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="GPS" transform="translate(15.604200, 15.715800) scale(-1, 1) translate(-15.604200, -15.715800) translate(1.000000, 1.000000)" fill="#FFFFFF" fill-rule="nonzero">
+            <path d="M23.6084,16.6316 C24.0502278,16.6316 24.4084,16.9897722 24.4084,17.4316 C24.4084,21.4080502 21.1848502,24.6316 17.2084,24.6316 C16.7665722,24.6316 16.4084,24.2734278 16.4084,23.8316 C16.4084,23.3897722 16.7665722,23.0316 17.2084,23.0316 C20.3011946,23.0316 22.8084,20.5243946 22.8084,17.4316 C22.8084,16.9897722 23.1665722,16.6316 23.6084,16.6316 L23.6084,16.6316 Z" id="路径"></path>
+            <path d="M28.4084,16.6316 C28.8502278,16.6316 29.2084,16.9897722 29.2084,17.4316 C29.2084,24.059017 23.835817,29.4316 17.2084,29.4316 C16.7665722,29.4316 16.4084,29.0734278 16.4084,28.6316 C16.4084,28.1897722 16.7665722,27.8316 17.2084,27.8316 C22.9521614,27.8316 27.6084,23.1753614 27.6084,17.4316 C27.6084,16.9897722 27.9665722,16.6316 28.4084,16.6316 L28.4084,16.6316 Z M20.9428,0.3804 C21.4468,-0.1268 22.1188,-0.1268 22.6228,0.3804 L27.9972,5.7852 C28.4996,6.2924 28.4996,6.9692 27.9972,7.4748 L22.6564,12.7116 C22.1524,13.2188 21.482,13.2188 20.9764,12.7116 L19.13,10.854 L18.122,11.8668 L19.97,13.7244 C20.474,14.2316 20.474,14.9084 19.97,15.414 L15.4356,19.974 C14.9316,20.4812 14.2596,20.4812 13.7556,19.974 L11.9092,18.118 L10.7332,19.2988 L12.5812,21.1564 C13.0852,21.6636 13.0852,22.3404 12.7492,22.6764 L7.5972,27.7708 C7.0948,28.2764 6.4228,28.2764 5.9188,27.7708 L0.378,22.534 C-0.126,22.0268 -0.126,21.35 0.378,20.8444 L5.5284,15.7516 C6.0324,15.2444 6.7028,15.2444 7.2084,15.7516 L9.0548,17.6092 L10.2292,16.4268 L3.6804,9.8396 C3.1764,9.3324 3.1764,8.6572 3.6804,8.15 L8.2164,3.5916 C8.7204,3.0844 9.3924,3.0844 9.8964,3.5916 L16.2772,10.3484 L17.2852,9.334 L15.4372,7.478 C14.9332,6.9692 14.9332,6.294 15.4372,5.7868 L20.9428,0.3804 Z M11.1636,7.8316 L7.8164,11.0988 L9.0868,12.3036 L12.4084,9.1244 L11.1636,7.8316 Z" id="形状"></path>
+        </g>
+    </g>
+</svg>

+ 23 - 0
Web/src/components/airport/icons/info/gpsError.svg

@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>Icons/GPS_Error</title>
+    <defs>
+        <rect id="path-1" x="0" y="15" width="32" height="2"></rect>
+        <filter x="-6.2%" y="-50.0%" width="112.5%" height="300.0%" filterUnits="objectBoundingBox" id="filter-2">
+            <feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
+            <feGaussianBlur stdDeviation="0.5" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
+            <feColorMatrix values="0 0 0 0 0   0 0 0 0 0   0 0 0 0 0  0 0 0 0.87 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
+        </filter>
+    </defs>
+    <g id="Icons/GPS_Error" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="GPS" transform="translate(15.604200, 15.715800) scale(-1, 1) translate(-15.604200, -15.715800) translate(1.000000, 1.000000)" fill="#E02020" fill-rule="nonzero">
+            <path d="M23.6084,16.6316 C24.0502278,16.6316 24.4084,16.9897722 24.4084,17.4316 C24.4084,21.4080502 21.1848502,24.6316 17.2084,24.6316 C16.7665722,24.6316 16.4084,24.2734278 16.4084,23.8316 C16.4084,23.3897722 16.7665722,23.0316 17.2084,23.0316 C20.3011946,23.0316 22.8084,20.5243946 22.8084,17.4316 C22.8084,16.9897722 23.1665722,16.6316 23.6084,16.6316 L23.6084,16.6316 Z" id="路径"></path>
+            <path d="M28.4084,16.6316 C28.8502278,16.6316 29.2084,16.9897722 29.2084,17.4316 C29.2084,24.059017 23.835817,29.4316 17.2084,29.4316 C16.7665722,29.4316 16.4084,29.0734278 16.4084,28.6316 C16.4084,28.1897722 16.7665722,27.8316 17.2084,27.8316 C22.9521614,27.8316 27.6084,23.1753614 27.6084,17.4316 C27.6084,16.9897722 27.9665722,16.6316 28.4084,16.6316 L28.4084,16.6316 Z" id="路径"></path>
+            <path d="M18.392,17 L15.4356,19.974 C14.9316,20.4812 14.2596,20.4812 13.7556,19.974 L11.9092,18.118 L10.7332,19.2988 L12.5812,21.1564 C13.0852,21.6636 13.0852,22.3404 12.7492,22.6764 L7.5972,27.7708 C7.0948,28.2764 6.4228,28.2764 5.9188,27.7708 L0.378,22.534 C-0.126,22.0268 -0.126,21.35 0.378,20.8444 L4.265,17 L8.449,17 L9.0548,17.6092 L9.659,17 L18.392,17 Z M22.6228,0.3804 L27.9972,5.7852 C28.4996,6.2924 28.4996,6.9692 27.9972,7.4748 L22.6564,12.7116 C22.1524,13.2188 21.482,13.2188 20.9764,12.7116 L19.13,10.854 L18.122,11.8668 L19.97,13.7244 C20.3458442,14.1026306 20.4414129,14.5751761 20.256706,15.0007074 L8.811,15 L3.6804,9.8396 C3.1764,9.3324 3.1764,8.6572 3.6804,8.15 L8.2164,3.5916 C8.7204,3.0844 9.3924,3.0844 9.8964,3.5916 L16.2772,10.3484 L17.2852,9.334 L15.4372,7.478 C14.9332,6.9692 14.9332,6.294 15.4372,5.7868 L20.9428,0.3804 C21.4468,-0.1268 22.1188,-0.1268 22.6228,0.3804 Z M11.1636,7.8316 L7.8164,11.0988 L9.0868,12.3036 L12.4084,9.1244 L11.1636,7.8316 Z" id="形状"></path>
+        </g>
+        <g id="矩形">
+            <use fill="black" fill-opacity="1" filter="url(#filter-2)" xlink:href="#path-1"></use>
+            <use fill="#E02020" fill-rule="evenodd" xlink:href="#path-1"></use>
+        </g>
+    </g>
+</svg>

+ 10 - 0
Web/src/components/airport/icons/info/home.svg

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>Icons/Home</title>
+    <g id="Icons/Home" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <circle id="椭圆形" stroke="#C7A443" stroke-width="2" cx="16" cy="16" r="15"></circle>
+        <g id="H" transform="translate(8.000000, 6.000000)" fill="#C7A443" fill-rule="nonzero">
+            <polygon id="路径" points="0 0 0 20 4 20 4 11.5 12 11.5 12 20 16 20 16 0 12 -8.32667268e-16 12 8 4 8 4 -8.32667268e-16"></polygon>
+        </g>
+    </g>
+</svg>

+ 10 - 0
Web/src/components/airport/icons/info/network.svg

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="50px" height="32px" viewBox="0 0 50 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>Icons/4G</title>
+    <g id="Icons/4G" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="4G" transform="translate(1.000000, 2.000000)" fill="#FFFFFF" fill-rule="nonzero">
+            <path d="M36.331924,0 C39.7917375,0 42.5015915,0.824955542 44.4514864,2.47486662 C46.4013814,4.12477771 47.5613189,6.52964814 47.9212995,9.68447805 L42.066615,9.68447805 C41.7966295,8.014568 41.2066613,6.78963406 40.2917106,6.01467586 C39.3767599,5.23971766 38.1168278,4.85473833 36.5119143,4.85473833 C34.4620248,4.85473833 32.9121083,5.61969711 31.8371663,7.15461442 C30.7722236,8.6945314 30.227253,10.9844081 30.2172536,14.0142448 L30.2172536,15.8791442 C30.2172536,18.9439791 30.7972223,21.2538546 31.9571598,22.8187703 C33.122097,24.383686 34.8270052,25.1686436 37.0718841,25.1686436 C39.3317623,25.1686436 40.9416756,24.6836697 41.9066236,23.7187218 L41.9066236,18.6939926 L36.4319187,18.6939926 L36.4319187,14.2642313 L47.9212995,14.2642313 L47.9212995,25.9136034 C46.8363579,27.2135333 45.3064404,28.2134795 43.3315468,28.928441 C41.2173229,29.6653271 38.9906737,30.0274114 36.7519013,29.9983833 C34.2270374,29.9983833 32.0121568,29.448413 30.1022598,28.3484722 C28.1818149,27.2277754 26.6447461,25.5530326 25.6924974,23.5437311 C24.6575532,21.4438443 24.1275817,18.9739775 24.0975834,16.14413 L24.0975834,14.1592369 C24.0975834,11.244394 24.5925567,8.71953013 25.5725039,6.58964491 C26.45906,4.56298594 27.9412943,2.85405694 29.8222748,1.68990889 C31.6721751,0.564969514 33.8420582,0 36.331924,0 L36.331924,0 Z" id="路径"></path>
+            <path d="M18.0290284,0.399978405 L18.0290284,18.6089971 L21.3338503,18.6089971 L21.3338503,23.2787455 L18.0290284,23.2787455 L18.0290284,29.5984049 L12.229341,29.5984049 L12.229341,23.2787455 L0.26498568,23.2787455 L0,19.6289421 L12.1693442,0.399978405 L18.0290284,0.399978405 Z M12.2343406,8.29955276 L12.229341,8.29955276 L11.8493614,8.96451685 L5.77468879,18.6089971 L12.2343406,18.6089971 L12.2343406,8.29955276 Z" id="形状结合"></path>
+        </g>
+    </g>
+</svg>

+ 27 - 0
Web/src/components/airport/icons/info/networkError.svg

@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="50px" height="32px" viewBox="0 0 50 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>Icons/4G-Error</title>
+    <defs>
+        <path d="M2,2 L49,30" id="path-1"></path>
+        <filter x="-10.1%" y="-13.4%" width="120.2%" height="133.9%" filterUnits="objectBoundingBox" id="filter-2">
+            <feMorphology radius="2" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1"></feMorphology>
+            <feOffset dx="0" dy="1" in="shadowSpreadOuter1" result="shadowOffsetOuter1"></feOffset>
+            <feMorphology radius="2" operator="erode" in="SourceAlpha" result="shadowInner"></feMorphology>
+            <feOffset dx="0" dy="1" in="shadowInner" result="shadowInner"></feOffset>
+            <feComposite in="shadowOffsetOuter1" in2="shadowInner" operator="out" result="shadowOffsetOuter1"></feComposite>
+            <feGaussianBlur stdDeviation="0.5" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
+            <feColorMatrix values="0 0 0 0 0   0 0 0 0 0   0 0 0 0 0  0 0 0 0.87 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
+        </filter>
+    </defs>
+    <g id="Icons/4G-Error" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="G" transform="translate(25.097583, 2.000000)" fill="#E02020" fill-rule="nonzero">
+            <path d="M20.3539031,2.47486662 C22.303798,4.12477771 23.4637355,6.52964814 23.8237161,9.68447805 L17.9690316,9.68447805 C17.6990462,8.014568 17.1090779,6.78963406 16.1941272,6.01467586 C15.2791765,5.23971766 14.0192444,4.85473833 12.4143309,4.85473833 C10.3644414,4.85473833 8.81452493,5.61969711 7.7395829,7.15461442 C6.67464029,8.6945314 6.12966962,10.9844081 6.1196702,14.0142448 L6.1196702,15.8791442 C6.1196702,15.9486637 6.1199686,16.0177948 6.12056541,16.0865375 C6.14627941,19.0483854 16.8440922,24.6836697 17.8090403,23.7187218 C17.8090403,23.4235874 17.8090403,23.2022366 17.8090403,23.0546694 C17.8090403,22.0856301 17.8090403,20.6320712 17.8090403,18.6939926 L12.3343353,18.6939926 L12.3343353,14.2642313 L23.8237161,14.2642313 L23.8237161,25.9136034 C23.6897448,26.074122 23.5489885,26.2300665 23.4014473,26.3814651 C22.7032514,27.0979148 14.902769,23.0238387 0,14.1592369 C0,13.5836026 0.0193038911,13.0231774 0.0578346593,12.4779998 C0.214412367,10.2625585 0.688497053,8.29891225 1.47492055,6.58964491 C2.36147666,4.56298594 3.84371095,2.85405694 5.72469145,1.68990889 C7.57459179,0.564969514 9.74447481,0 12.2343406,0 C15.6941541,0 18.4040082,0.824955542 20.3539031,2.47486662 Z" id="路径"></path>
+            <path d="M6.9948603,21.2644916 C7.34648919,21.9973257 7.6347279,22.5154186 7.85957643,22.8187703 C9.02451361,24.383686 10.7294218,25.1686436 12.9743008,25.1686436 C13.1657884,25.1686436 13.3526093,25.1651616 13.5347667,25.1582006 C13.6333814,25.1544322 13.7509928,25.1544322 13.8876009,25.1582006 L19.7231053,28.6721875 C19.4843214,28.8114145 19.3212741,28.8968323 19.2339635,28.928441 C17.1197395,29.6653271 14.8930903,30.0274114 12.654318,29.9983833 C10.129454,29.9983833 7.91457343,29.448413 6.00467644,28.3484722 C4.08423157,27.2277754 2.54716277,25.5530326 1.59491409,23.5437311 C0.985188294,22.3066064 0.463778082,20.1642872 0.0306834521,17.1167736 L6.9948603,21.2644916 Z" id="路径"></path>
+        </g>
+        <path d="M8.7174297,9.43451545 L12.415,11.6539784 L6.77468879,20.6089971 L13.2343406,20.6089971 L13.234,12.1449784 L19.0290284,15.6246796 L19.0290284,20.6089971 L22.3338503,20.6089971 L22.3338503,25.2787455 L19.0290284,25.2787455 L19.0290284,31.5984049 L13.229341,31.5984049 L13.229341,25.2787455 L1.26498568,25.2787455 L1,21.6289421 L8.7174297,9.43451545 Z M9.6086671,8.02625783 L13.1693442,2.39997841 L19.0290284,2.39997841 L19.0290284,14.0377107 L9.6086671,8.02625783 Z" id="形状结合" fill="#E02020" fill-rule="nonzero"></path>
+        <g id="直线">
+            <use fill="black" fill-opacity="1" filter="url(#filter-2)" xlink:href="#path-1"></use>
+            <use stroke="#E02020" stroke-width="4" xlink:href="#path-1"></use>
+        </g>
+    </g>
+</svg>

+ 11 - 0
Web/src/components/airport/icons/info/rtk.svg

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="82px" height="32px" viewBox="0 0 82 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>Icons/RTK</title>
+    <g id="Icons/RTK" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="RTK" transform="translate(2.800000, 2.440000)" fill="#FFFFFF" fill-rule="nonzero">
+            <path d="M0,0 L0,28.56 L4.68,28.56 L4.68,16.92 L12.08,16.92 C13.68,16.92 14.92,17.24 15.76,17.96 C16.56,18.64 17.04,19.76 17.2,21.32 L17.56,24.84 C17.72,26.44 18.12,27.68 18.8,28.56 L23.88,28.56 C22.92,27.52 22.32,26.12 22.16,24.36 L21.68,19.76 C21.36,16.96 19.96,15.32 17.48,14.76 L17.48,14.68 C19,14.28 20.24,13.44 21.12,12.16 C21.92,11 22.32,9.68 22.32,8.2 C22.32,5.48 21.4,3.4 19.64,1.96 C17.96,0.64 15.64,0 12.72,0 L0,0 Z M4.68,4 L12.08,4 C14,4 15.4,4.32 16.28,5.04 C17.16,5.72 17.6,6.84 17.6,8.4 C17.6,9.84 17.16,10.96 16.28,11.76 C15.32,12.52 13.92,12.92 12.08,12.92 L4.68,12.92 L4.68,4 Z" id="形状"></path>
+            <polygon id="路径" points="24.48 0 24.48 4 33.84 4 33.84 28.56 38.52 28.56 38.52 4 47.88 4 47.88 0"></polygon>
+            <polygon id="路径" points="68.04 0 54.76 13.48 54.76 0 50.08 0 50.08 28.56 54.76 28.56 54.76 18.72 58.24 15.32 69.52 28.56 75.84 28.56 61.4 12.24 73.96 0"></polygon>
+        </g>
+    </g>
+</svg>

+ 21 - 0
Web/src/components/airport/icons/info/rtkError.svg

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="82px" height="32px" viewBox="0 0 82 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>Icons/RTK-Error</title>
+    <defs>
+        <rect id="path-1" x="0" y="15" width="82" height="4"></rect>
+        <filter x="-8.5%" y="-125.0%" width="117.1%" height="450.0%" filterUnits="objectBoundingBox" id="filter-2">
+            <feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
+            <feGaussianBlur stdDeviation="2" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
+            <feColorMatrix values="0 0 0 0 0   0 0 0 0 0   0 0 0 0 0  0 0 0 0.5 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
+        </filter>
+    </defs>
+    <g id="Icons/RTK-Error" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <path d="M41.32,20 L41.32,31 L36.64,31 L36.64,20 L41.32,20 Z M50.68,2.44 L50.68,6.44 L41.32,6.44 L41.32,17 L36.64,17 L36.64,6.44 L27.28,6.44 L27.28,2.44 L50.68,2.44 Z" id="形状结合" fill="#E02020" fill-rule="nonzero"></path>
+        <path d="M68.907,20 L78.64,31 L72.32,31 L62.949,20 L68.907,20 Z M58.747,20 L57.56,21.16 L57.56,31 L52.88,31 L52.88,20 L58.747,20 Z M57.56,2.44 L57.56,15.92 L70.84,2.44 L76.76,2.44 L64.2,14.68 L66.253,17 L52.88,17 L52.88,2.44 L57.56,2.44 Z" id="形状结合" fill="#E02020" fill-rule="nonzero"></path>
+        <path d="M23.9108823,19.9996059 C24.1954538,20.6348258 24.3851598,21.3701479 24.48,22.2 L24.48,22.2 L24.96,26.8 C25.12,28.56 25.72,29.96 26.68,31 L26.68,31 L21.6,31 C20.92,30.12 20.52,28.88 20.36,27.28 L20.36,27.28 L20,23.76 C19.84,22.2 19.36,21.08 18.56,20.4 C18.3843512,20.2494439 18.1912124,20.1163779 17.9809492,20.0000705 Z M7.48,20 L7.48,31 L2.8,31 L2.8,20 L7.48,20 Z M15.52,2.44 C18.44,2.44 20.76,3.08 22.44,4.4 C24.2,5.84 25.12,7.92 25.12,10.64 C25.12,12.12 24.72,13.44 23.92,14.6 C23.2772687,15.5348818 22.4424956,16.2350458 21.44685,16.7004919 C21.2653523,16.7853387 21.0785087,16.8623859 20.8865081,16.9316333 C20.8187079,16.9560863 20.7502646,16.9795667 20.6811866,17.0020745 L2.8,17 L2.8,2.44 Z M14.88,6.44 L7.48,6.44 L7.48,15.36 L14.88,15.36 C16.72,15.36 18.12,14.96 19.08,14.2 C19.96,13.4 20.4,12.28 20.4,10.84 C20.4,9.28 19.96,8.16 19.08,7.48 C18.2,6.76 16.8,6.44 14.88,6.44 L14.88,6.44 Z" id="形状" fill="#E02020" fill-rule="nonzero"></path>
+        <g id="矩形">
+            <use fill="black" fill-opacity="1" filter="url(#filter-2)" xlink:href="#path-1"></use>
+            <use fill="#E02020" fill-rule="evenodd" xlink:href="#path-1"></use>
+        </g>
+    </g>
+</svg>

+ 7 - 0
Web/src/components/airport/icons/info/signalFive.svg

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>Icons/rc_4G</title>
+    <g id="Icons/rc_4G" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <path d="M4,25 C4.55228475,25 5,25.4477153 5,26 L5,31 C5,31.5522847 4.55228475,32 4,32 L2,32 C1.44771525,32 1,31.5522847 1,31 L1,26 C1,25.4477153 1.44771525,25 2,25 L4,25 Z M10.5,20 C11.0522847,20 11.5,20.4477153 11.5,21 L11.5,31 C11.5,31.5522847 11.0522847,32 10.5,32 L8.5,32 C7.94771525,32 7.5,31.5522847 7.5,31 L7.5,21 C7.5,20.4477153 7.94771525,20 8.5,20 L10.5,20 Z M17,15 C17.5522847,15 18,15.4477153 18,16 L18,31 C18,31.5522847 17.5522847,32 17,32 L15,32 C14.4477153,32 14,31.5522847 14,31 L14,16 C14,15.4477153 14.4477153,15 15,15 L17,15 Z M23.5,10 C24.0522847,10 24.5,10.4477153 24.5,11 L24.5,31 C24.5,31.5522847 24.0522847,32 23.5,32 L21.5,32 C20.9477153,32 20.5,31.5522847 20.5,31 L20.5,11 C20.5,10.4477153 20.9477153,10 21.5,10 L23.5,10 Z M30,5 C30.5522847,5 31,5.44771525 31,6 L31,31 C31,31.5522847 30.5522847,32 30,32 L28,32 C27.4477153,32 27,31.5522847 27,31 L27,6 C27,5.44771525 27.4477153,5 28,5 L30,5 Z" id="形状结合" fill="#FFFFFF"></path>
+    </g>
+</svg>

+ 7 - 0
Web/src/components/airport/icons/info/signalFour.svg

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>Icons/rc_4G_4</title>
+    <g id="Icons/rc_4G_4" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <path d="M4,25 C4.55228475,25 5,25.4477153 5,26 L5,31 C5,31.5522847 4.55228475,32 4,32 L2,32 C1.44771525,32 1,31.5522847 1,31 L1,26 C1,25.4477153 1.44771525,25 2,25 L4,25 Z M10.5,20 C11.0522847,20 11.5,20.4477153 11.5,21 L11.5,31 C11.5,31.5522847 11.0522847,32 10.5,32 L8.5,32 C7.94771525,32 7.5,31.5522847 7.5,31 L7.5,21 C7.5,20.4477153 7.94771525,20 8.5,20 L10.5,20 Z M17,15 C17.5522847,15 18,15.4477153 18,16 L18,31 C18,31.5522847 17.5522847,32 17,32 L15,32 C14.4477153,32 14,31.5522847 14,31 L14,16 C14,15.4477153 14.4477153,15 15,15 L17,15 Z M23.5,10 C24.0522847,10 24.5,10.4477153 24.5,11 L24.5,31 C24.5,31.5522847 24.0522847,32 23.5,32 L21.5,32 C20.9477153,32 20.5,31.5522847 20.5,31 L20.5,11 C20.5,10.4477153 20.9477153,10 21.5,10 L23.5,10 Z" id="形状结合" fill="#FFFFFF"></path>
+    </g>
+</svg>

+ 7 - 0
Web/src/components/airport/icons/info/signalThree.svg

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>Icons/rc_4G_3</title>
+    <g id="Icons/rc_4G_3" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <path d="M4,25 C4.55228475,25 5,25.4477153 5,26 L5,31 C5,31.5522847 4.55228475,32 4,32 L2,32 C1.44771525,32 1,31.5522847 1,31 L1,26 C1,25.4477153 1.44771525,25 2,25 L4,25 Z M10.5,20 C11.0522847,20 11.5,20.4477153 11.5,21 L11.5,31 C11.5,31.5522847 11.0522847,32 10.5,32 L8.5,32 C7.94771525,32 7.5,31.5522847 7.5,31 L7.5,21 C7.5,20.4477153 7.94771525,20 8.5,20 L10.5,20 Z M17,15 C17.5522847,15 18,15.4477153 18,16 L18,31 C18,31.5522847 17.5522847,32 17,32 L15,32 C14.4477153,32 14,31.5522847 14,31 L14,16 C14,15.4477153 14.4477153,15 15,15 L17,15 Z" id="形状结合" fill="#FFFFFF"></path>
+    </g>
+</svg>

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 5 - 0
Web/src/components/airport/icons/info/wind.svg


+ 1 - 0
Web/src/components/airport/icons/info_selected.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1725866227895" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1469" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M64.64 128.32v680.896h887.36V128.32H64.64z m815.68 609.152H136.32V200h744v537.472z m-655.36 130.752h566.72v71.68H224.96v-71.68z" fill="#448ef7" p-id="1470"></path><path d="M262.592 428.16l129.408-118.912 48.512 52.8L311.04 480.96l-48.512-52.8z m277.12 121.792l129.408-118.976 48.512 52.8-129.408 118.912-48.512-52.736z m-209.536 4.16l266.368-244.864 48.512 52.8-266.368 244.8-48.512-52.736z" fill="#448ef7" p-id="1471"></path></svg>

+ 0 - 0
Web/src/components/onLineDevice/icons/task.svg → Web/src/components/airport/icons/task.svg


+ 278 - 0
Web/src/components/airport/index.vue

@@ -0,0 +1,278 @@
+<template>
+  <div class="content" @click="onClickLocation">
+    <div class="content-title">
+      <div class="content-title-left">
+        <img class="content-title-left-icon" :src="taskSrc">
+        <div>
+          无任务
+        </div>
+      </div>
+      <div class="content-title-right">
+        {{ getTextByDockTaskCode(dockInfo[dock.sn] ? dockInfo[dock.sn].link_osd.flighttask_step_code :
+          EDockModeCode.Disconnected) }}
+      </div>
+    </div>
+    <div class="content-info">
+      <div class="content-info-left">
+        <div class="content-info-left-one">
+          {{ dock.gateway.callsign }} - {{ dock.callsign }}
+        </div>
+        <div class="content-info-left-two">
+          <div class="content-info-left-two-info">
+            <img class="content-info-left-three-info-icon" :src="aircraftSrc">
+            <div class="content-info-left-two-info-text">
+              <span class="scrollable-text">
+                {{ getTextByModeCode(deviceInfo[dock.sn] ? deviceInfo[dock.sn].mode_code : EModeCode.Disconnected) }}
+              </span>
+            </div>
+          </div>
+          <div class="content-info-left-two-other">
+            <div v-if="aircraftLogInfo">
+              {{ aircraftLogInfo }}
+            </div>
+            <div v-else>
+              N/A
+            </div>
+          </div>
+        </div>
+        <div class="content-info-left-three">
+          <div class="content-info-left-three-info">
+            <img class="content-info-left-three-info-icon" :src="dockSrc">
+            <div class="content-info-left-three-info-text">
+              {{ getTextByDockModeCode(dockInfo[dock.gateway.sn] ? dockInfo[dock.gateway.sn].basic_osd?.mode_code
+                : EDockModeCode.Disconnected) }}
+            </div>
+          </div>
+          <div class="content-info-left-three-other">
+            <div v-if="hangarLogInfo">
+              {{ hangarLogInfo }}
+            </div>
+            <div v-else>
+              N/A
+            </div>
+          </div>
+        </div>
+      </div>
+      <div class="content-info-right">
+        <a-button type="text"
+          :disabled="!(deviceInfo[dock.sn] && deviceInfo[dock.sn].mode_code !== EDockModeCode.Disconnected)"
+          @click.stop="onClickLookInfo">
+          <img :src="infoSrc">
+        </a-button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { computed } from 'vue';
+import taskSrc from './icons/task.svg';
+import aircraftSrc from './icons/aircraft.svg';
+import dockSrc from './icons/dockInfo.svg';
+import infoSrc from './icons/info.svg';
+import infoSelectedSrc from './icons/info_selected.svg';
+import { useMyStore } from '/@/store';
+import { getTextByModeCode, getTextByDockModeCode, getTextByDockTaskCode } from '/@/utils/index'
+import { OnlineDevice, EModeCode, EDockModeCode } from '/@/types/device';
+import { wgs84togcj02 } from '/@/vendors/coordtransform'
+import { getRoot } from '/@/root';
+
+interface Props {
+  dock: OnlineDevice,
+  onClickLookInfo: () => void,
+};
+
+const props = withDefaults(defineProps<Props>(), {
+
+});
+
+const root: any = getRoot()
+
+const store = useMyStore()
+
+const deviceInfo = computed(() => store.state.deviceState.deviceInfo)
+const dockInfo = computed(() => store.state.deviceState.dockInfo)
+
+// 飞行器告警信息
+const aircraftLogInfo = computed(() => {
+  const hmsList = store.state.hmsInfo[props.dock.sn] || [];
+  const hmsInfo = hmsList[hmsList.length - 1];
+  if (hmsInfo) {
+    return hmsInfo.message_zh;
+  } else {
+    return ''
+  }
+})
+
+// 机库告警信息
+const hangarLogInfo = computed(() => {
+  const hmsList = store.state.hmsInfo[props.dock.gateway.sn] || [];
+  const hmsInfo = hmsList[hmsList.length - 1];
+  if (hmsInfo) {
+    return hmsInfo.message_zh;
+  } else {
+    return ''
+  }
+})
+
+const onClickLocation = () => {
+  const sn = props.dock.gateway.sn;
+  const gatewayInfo = store.state.deviceState.gatewayInfo[sn];
+  if (gatewayInfo) {
+    const coordinate = wgs84togcj02(gatewayInfo.longitude, gatewayInfo.latitude);
+    root.$map.setCenter(coordinate)
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.content {
+  width: 208px;
+  border: 1px solid #3d3d3d;
+  border-radius: 4px;
+  overflow: hidden;
+  color: #FFFFFF;
+  cursor: pointer;
+
+  &-title {
+    width: 100%;
+    height: 30px;
+    border-bottom: 1px solid #3d3d3d;
+    display: flex;
+    align-items: center;
+
+    &-left {
+      width: 80px;
+      height: 100%;
+      padding-left: 5px;
+      background: #4d4d4d;
+      display: flex;
+      align-items: center;
+
+      &-icon {
+        width: 15px;
+        height: 15px;
+        margin-right: 5px;
+      }
+    }
+
+    &-right {
+      margin-left: 5px;
+    }
+  }
+
+  &-info {
+    display: flex;
+
+    &-left {
+      width: calc(100% - 20px);
+      padding: 5px;
+      font-size: 12px;
+
+      &-one {
+        width: 100%;
+        height: 24px;
+      }
+
+      &-two {
+        width: 100%;
+        height: 24px;
+        display: flex;
+        margin-bottom: 5px;
+
+        &-info {
+          width: 75px;
+          padding-left: 5px;
+          background: #3d3d3d;
+          display: flex;
+          align-items: center;
+
+          &-icon {
+            width: 14px;
+            height: 14px;
+            margin-right: 5px;
+          }
+
+          &-text {
+            width: 50px;
+            white-space: nowrap;
+            overflow: hidden;
+          }
+        }
+
+        &-other {
+          width: calc(100% - 75px);
+          padding-left: 5px;
+          background: #4a4d4e;
+          display: flex;
+          align-items: center;
+        }
+      }
+
+      &-three {
+        width: 100%;
+        height: 24px;
+        display: flex;
+        margin-bottom: 5px;
+
+        &-info {
+          width: 75px;
+          padding-left: 5px;
+          background: #3d3d3d;
+          display: flex;
+          align-items: center;
+
+          &-icon {
+            width: 14px;
+            height: 14px;
+            margin-right: 5px;
+          }
+
+          &-text {
+            white-space: nowrap;
+            overflow: hidden;
+            text-overflow: ellipsis;
+          }
+        }
+
+        &-other {
+          width: calc(100% - 75px);
+          padding-left: 5px;
+          background: #4a4d4e;
+          display: flex;
+          align-items: center;
+        }
+      }
+    }
+
+    &-right {
+      width: 24px;
+      background: #3d3d3d;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+
+      img {
+        width: 14px;
+        height: 14px;
+      }
+    }
+  }
+
+  .scrollable-text {
+    width: 100%;
+    display: inline-block;
+    animation: scroll-left 5s linear infinite;
+
+    @keyframes scroll-left {
+      0% {
+        transform: translateX(100%);
+      }
+
+      100% {
+        transform: translateX(-100%);
+      }
+    }
+  }
+}
+</style>

+ 1 - 2
Web/src/components/onLineDevice/components/InfoModal.vue

@@ -23,7 +23,7 @@
         <div class="content-osd-head-right">
           <div class="content-osd-head-right-top">
             <div class="content-osd-head-right-top-style">
-              {{ type === 'MANUAL' ? '手动飞行' : '航线飞行' }}
+              手动飞行
             </div>
             <div class="content-osd-head-right-top-status">
               {{ getTextByModeCode(deviceInfo.device.mode_code) }}
@@ -227,7 +227,6 @@ import { getLiveCapacity, startLivestream, stopLivestream } from '/@/api/manage'
 import { getTextByModeCode } from '/@/utils/index'
 
 interface Props {
-  type: 'MANUAL' | 'AIRPORT',
   osdInfo: any
   deviceInfo: any
 };

+ 0 - 1
Web/src/components/onLineDevice/icons/deviceInfo.svg

@@ -1 +0,0 @@
-<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1724134053666" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6991" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M944.38 70.19h-864c-44.19 0-80 35.81-80 80v571.43c0 44.19 35.81 80 80 80h864c44.18 0 80-35.81 80-80V150.19c0-44.19-35.82-80-80-80z m5.45 603.45c0 26.26-21.29 47.55-47.55 47.55H750.12V445.41c0-26.26-21.28-47.55-47.55-47.55-26.26 0-47.55 21.29-47.55 47.55v275.78h-95.1V350.31c0-26.26-21.28-47.55-47.55-47.55-26.26 0-47.55 21.29-47.55 47.55v370.88h-95.1v-199.7c0-26.26-21.28-47.55-47.55-47.55-26.26 0-47.55 21.28-47.55 47.55v199.7H122.49c-26.26 0-47.55-21.29-47.55-47.55V198.16c0-26.26 21.29-47.55 47.55-47.55h779.79c26.26 0 47.55 21.29 47.55 47.55v475.48zM722.67 874.76H302.09c-25.25 0-45.71 20.47-45.71 45.71 0 25.25 20.47 45.71 45.71 45.71h420.58c25.24 0 45.71-20.46 45.71-45.71 0-25.24-20.47-45.71-45.71-45.71z m0 0" fill="#ffffff" p-id="6992"></path></svg>

+ 1 - 0
Web/src/components/onLineDevice/icons/info.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1725866227895" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1469" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M64.64 128.32v680.896h887.36V128.32H64.64z m815.68 609.152H136.32V200h744v537.472z m-655.36 130.752h566.72v71.68H224.96v-71.68z" fill="#ffffff" p-id="1470"></path><path d="M262.592 428.16l129.408-118.912 48.512 52.8L311.04 480.96l-48.512-52.8z m277.12 121.792l129.408-118.976 48.512 52.8-129.408 118.912-48.512-52.736z m-209.536 4.16l266.368-244.864 48.512 52.8-266.368 244.8-48.512-52.736z" fill="#ffffff" p-id="1471"></path></svg>

+ 1 - 0
Web/src/components/onLineDevice/icons/info_selected.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1725866227895" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1469" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M64.64 128.32v680.896h887.36V128.32H64.64z m815.68 609.152H136.32V200h744v537.472z m-655.36 130.752h566.72v71.68H224.96v-71.68z" fill="#448ef7" p-id="1470"></path><path d="M262.592 428.16l129.408-118.912 48.512 52.8L311.04 480.96l-48.512-52.8z m277.12 121.792l129.408-118.976 48.512 52.8-129.408 118.912-48.512-52.736z m-209.536 4.16l266.368-244.864 48.512 52.8-266.368 244.8-48.512-52.736z" fill="#448ef7" p-id="1471"></path></svg>

+ 18 - 45
Web/src/components/onLineDevice/index.vue

@@ -1,16 +1,5 @@
 <template>
   <div class="content" @click="onClickLocation">
-    <div class="content-title" v-if="type === 'AIRPORT'">
-      <div class="content-title-left">
-        <img class="content-title-left-icon" :src="taskSrc">
-        <div>
-          无任务
-        </div>
-      </div>
-      <div class="content-title-right">
-        N/A
-      </div>
-    </div>
     <div class="content-info">
       <div class="content-info-left">
         <div class="content-info-left-one">
@@ -27,7 +16,12 @@
             </div>
           </div>
           <div class="content-info-left-two-other">
-            {{ deviceReason }}
+            <div v-if="deviceReason">
+              {{ deviceReason }}
+            </div>
+            <div v-else>
+              N/A
+            </div>
           </div>
         </div>
         <div class="content-info-left-three">
@@ -47,12 +41,18 @@
                 {{ capacity }}%
               </div>
             </div>
+            <div v-else>
+              N/A
+            </div>
           </div>
         </div>
       </div>
       <div class="content-info-right">
-        <img :src="deviceInfoSrc" @click.stop="onClickLookInfo" v-if="type === 'MANUAL'">
-        <img :src="dockInfoSrc" @click.stop="onClickLookInfo" v-else-if="type === 'AIRPORT'">
+        <a-button type="text"
+          :disabled="!(deviceInfo[device.sn] && deviceInfo[device.sn].mode_code !== EModeCode.Disconnected)"
+          @click.stop="onClickLookInfo">
+          <img :src="infoSrc">
+        </a-button>
       </div>
     </div>
   </div>
@@ -60,15 +60,14 @@
 
 <script lang="ts" setup>
 import { computed } from 'vue';
-import taskSrc from './icons/task.svg';
 import controllerSrc from './icons/controller.svg';
 import aircraftSrc from './icons/aircraft.svg';
 import batteryOneSrc from './icons/batteryOne.svg';
 import batteryTwoSrc from './icons/batteryTwo.svg';
 import batteryThreeSrc from './icons/batteryThree.svg';
 import batteryFourSrc from './icons/batteryFour.svg';
-import deviceInfoSrc from './icons/deviceInfo.svg';
-import dockInfoSrc from './icons/dockInfo.svg';
+import infoSrc from './icons/info.svg';
+import infoSelectedSrc from './icons/info_selected.svg';
 import { useMyStore } from '/@/store';
 import { getTextByModeCode } from '/@/utils/index'
 import { OnlineDevice, EModeCode } from '/@/types/device';
@@ -76,7 +75,6 @@ import { wgs84togcj02 } from '/@/vendors/coordtransform'
 import { getRoot } from '/@/root';
 
 interface Props {
-  type: 'MANUAL' | 'AIRPORT',
   device: OnlineDevice,
   onClickLookInfo: () => void,
 };
@@ -130,33 +128,6 @@ const onClickLocation = () => {
   color: #FFFFFF;
   cursor: pointer;
 
-  &-title {
-    width: 100%;
-    height: 30px;
-    border-bottom: 1px solid #3d3d3d;
-    display: flex;
-    align-items: center;
-
-    &-left {
-      width: 80px;
-      height: 100%;
-      padding-left: 5px;
-      background: #4d4d4d;
-      display: flex;
-      align-items: center;
-
-      &-icon {
-        width: 15px;
-        height: 15px;
-        margin-right: 5px;
-      }
-    }
-
-    &-right {
-      margin-left: 5px;
-    }
-  }
-
   &-info {
     display: flex;
 
@@ -236,6 +207,8 @@ const onClickLocation = () => {
           height: 100%;
           padding-left: 5px;
           background: #4a4d4e;
+          display: flex;
+          align-items: center;
 
           .capacity {
             width: 100%;

+ 7 - 144
Web/src/pages/page-web/projects/tsa.vue

@@ -7,148 +7,10 @@
           <div v-if="onlineDocks.data.length === 0" style="height: 100px; color: white;">
             <a-empty :image="noData" :image-style="{ height: '60px' }" />
           </div>
-          <div v-else class="fz12" style="color: white;">
-            <div class="airport-item" v-for="dock in onlineDocks.data" :key="dock.sn">
-              <div style="border-radius: 2px; height: 100%; width: 100%;"
-                class="flex-row flex-justify-between flex-align-center">
-                <div style="float: left; padding: 0px 5px 8px 8px; width: 88%">
-                  <div style="width: 80%; height: 30px; line-height: 30px; font-size: 16px;">
-                    <a-tooltip :title="`${dock.gateway.callsign} - ${dock.callsign ?? 'No Drone'}`">
-                      <div class="text-hidden" style="max-width: 200px;">
-                        {{ dock.gateway.callsign }} - {{ dock.callsign ?? 'No Drone' }}
-                      </div>
-                    </a-tooltip>
-                  </div>
-                  <div class="mt5 flex-align-center flex-row flex-justify-between" style="background: #595959;">
-                    <div class="flex-align-center flex-row">
-                      <span class="ml5 mr5">
-                        <RobotOutlined />
-                      </span>
-                      <div class="font-bold text-hidden" style="max-width: 80px;"
-                        :style="dockInfo[dock.gateway.sn] && dockInfo[dock.gateway.sn].basic_osd?.mode_code !== EDockModeCode.Disconnected ? 'color: #00ee8b' : 'color: red;'">
-                        {{ dockInfo[dock.gateway.sn] ? EDockModeCode[dockInfo[dock.gateway.sn].basic_osd?.mode_code] :
-                          EDockModeCode[EDockModeCode.Disconnected] }}
-                      </div>
-                    </div>
-                    <div class="mr5 flex-align-center flex-row" style="width: 85px; margin-right: 0; height: 18px;">
-                      <div v-if="hmsInfo[dock.gateway.sn]" class="flex-align-center flex-row">
-                        <div :class="hmsInfo[dock.gateway.sn][0].level === EHmsLevel.CAUTION ? 'caution-blink' :
-                          hmsInfo[dock.gateway.sn][0].level === EHmsLevel.WARN ? 'warn-blink' : 'notice-blink'"
-                          style="width: 18px; height: 16px; text-align: center;">
-                          <span :style="hmsInfo[dock.gateway.sn].length > 99 ? 'font-size: 11px' : 'font-size: 12px'">{{
-                            hmsInfo[dock.gateway.sn].length }}</span>
-                          <span class="fz10">{{ hmsInfo[dock.gateway.sn].length > 99 ? '+' : '' }}</span>
-                        </div>
-                        <a-popover trigger="click" placement="bottom" color="black"
-                          v-model:visible="hmsVisible[dock.gateway.sn]"
-                          @visibleChange="readHms(hmsVisible[dock.gateway.sn], dock.gateway.sn)"
-                          :overlayStyle="{ width: '200px', height: '300px' }">
-                          <div :class="hmsInfo[dock.gateway.sn][0].level === EHmsLevel.CAUTION ? 'caution' :
-                            hmsInfo[dock.gateway.sn][0].level === EHmsLevel.WARN ? 'warn' : 'notice'"
-                            style="margin-left: 3px; width: 62px; height: 16px;">
-                            <span class="word-loop">{{ hmsInfo[dock.gateway.sn][0].message_en }}</span>
-                          </div>
-                          <template #content>
-                            <a-collapse style="background: black; height: 300px; overflow-y: auto;" :bordered="false"
-                              expand-icon-position="right" :accordion="true">
-                              <a-collapse-panel v-for="hms in hmsInfo[dock.gateway.sn]" :key="hms.hms_id"
-                                :showArrow="false"
-                                style=" margin: 0 auto 3px auto; border: 0; width: 140px; border-radius: 3px"
-                                :class="hms.level === EHmsLevel.CAUTION ? 'caution' : hms.level === EHmsLevel.WARN ? 'warn' : 'notice'">
-                                <template #header="{ isActive }">
-                                  <div class="flex-row flex-align-center" style="width: 130px;">
-                                    <div style="width: 110px;">
-                                      <span class="word-loop">{{ hms.message_en }}</span>
-                                    </div>
-                                    <div style="width: 20px; height: 15px; font-size: 10px; z-index: 2 "
-                                      class="flex-row flex-align-center flex-justify-center"
-                                      :class="hms.level === EHmsLevel.CAUTION ? 'caution' : hms.level === EHmsLevel.WARN ? 'warn' : 'notice'">
-                                      <DoubleRightOutlined :rotate="isActive ? 90 : 0" />
-                                    </div>
-                                  </div>
-                                </template>
-                                <a-tooltip :title="hms.create_time">
-                                  <div style="color: white;" class="text-hidden">{{ hms.create_time }}</div>
-                                </a-tooltip>
-                              </a-collapse-panel>
-                            </a-collapse>
-                          </template>
-                        </a-popover>
-                      </div>
-                      <div v-else class="width-100" style="height: 90%; background: rgba(0, 0, 0, 0.35)"></div>
-                    </div>
-                  </div>
-                  <div class="mt5 flex-align-center flex-row flex-justify-between" style="background: #595959;">
-                    <div class="flex-row">
-                      <span class="ml5 mr5">
-                        <RocketOutlined />
-                      </span>
-                      <div class="font-bold text-hidden" style="max-width: 80px"
-                        :style="deviceInfo[dock.sn] && deviceInfo[dock.sn].mode_code !== EModeCode.Disconnected ? 'color: #00ee8b' : 'color: red;'">
-                        {{ deviceInfo[dock.sn] ? EModeCode[deviceInfo[dock.sn].mode_code] :
-                          EModeCode[EModeCode.Disconnected] }}
-                      </div>
-                    </div>
-                    <div class="mr5 flex-align-center flex-row" style="width: 85px; margin-right: 0; height: 18px;">
-                      <div v-if="hmsInfo[dock.sn]" class="flex-align-center flex-row">
-                        <div :class="hmsInfo[dock.sn][0].level === EHmsLevel.CAUTION ? 'caution-blink' :
-                          hmsInfo[dock.sn][0].level === EHmsLevel.WARN ? 'warn-blink' : 'notice-blink'"
-                          style="width: 18px; height: 16px; text-align: center;">
-                          <span :style="hmsInfo[dock.sn].length > 99 ? 'font-size: 11px' : 'font-size: 12px'">{{
-                            hmsInfo[dock.sn].length
-                            }}</span>
-                          <span class="fz10">{{ hmsInfo[dock.sn].length > 99 ? '+' : '' }}</span>
-                        </div>
-                        <a-popover trigger="click" placement="bottom" color="black"
-                          v-model:visible="hmsVisible[dock.sn]" @visibleChange="readHms(hmsVisible[dock.sn], dock.sn)"
-                          :overlayStyle="{ width: '200px', height: '300px' }">
-                          <div :class="hmsInfo[dock.sn][0].level === EHmsLevel.CAUTION ? 'caution' :
-                            hmsInfo[dock.sn][0].level === EHmsLevel.WARN ? 'warn' : 'notice'"
-                            style="margin-left: 3px; width: 62px; height: 16px;">
-                            <span class="word-loop">{{ hmsInfo[dock.sn][0].message_en }}</span>
-                          </div>
-                          <template #content>
-                            <a-collapse style="background: black; height: 300px; overflow-y: auto;" :bordered="false"
-                              expand-icon-position="right" :accordion="true">
-                              <a-collapse-panel v-for="hms in hmsInfo[dock.sn]" :key="hms.hms_id" :showArrow="false"
-                                style=" margin: 0 auto 3px auto; border: 0; width: 140px; border-radius: 3px"
-                                :class="hms.level === EHmsLevel.CAUTION ? 'caution' : hms.level === EHmsLevel.WARN ? 'warn' : 'notice'">
-                                <template #header="{ isActive }">
-                                  <div class="flex-row flex-align-center" style="width: 130px;">
-                                    <div style="width: 110px;">
-                                      <span class="word-loop">{{ hms.message_en }}</span>
-                                    </div>
-                                    <div style="width: 20px; height: 15px; font-size: 10px; z-index: 2 "
-                                      class="flex-row flex-align-center flex-justify-center"
-                                      :class="hms.level === EHmsLevel.CAUTION ? 'caution' : hms.level === EHmsLevel.WARN ? 'warn' : 'notice'">
-                                      <DoubleRightOutlined :rotate="isActive ? 90 : 0" />
-                                    </div>
-                                  </div>
-                                </template>
-                                <a-tooltip :title="hms.create_time">
-                                  <div style="color: white;" class="text-hidden">{{ hms.create_time }}</div>
-                                </a-tooltip>
-                              </a-collapse-panel>
-                            </a-collapse>
-                          </template>
-                        </a-popover>
-                      </div>
-                      <div v-else class="width-100" style="height: 90%; background: rgba(0, 0, 0, 0.35)"></div>
-                    </div>
-                  </div>
-                </div>
-                <div style="float: right; background: #595959; height: 100%; width: 40px;"
-                  class="flex-row flex-justify-center flex-align-center">
-                  <div class="fz16" @click="switchVisible(dock, true)">
-                    <a v-if="osdVisible.gateway_sn === dock.gateway.sn && osdVisible.visible">
-                      <EyeOutlined />
-                    </a>
-                    <a v-else>
-                      <EyeInvisibleOutlined />
-                    </a>
-                  </div>
-                </div>
-              </div>
+          <div v-else>
+            <div :style="{ 'margin-top': index === 0 ? '' : '10px' }" v-for="(dock, index) in onlineDocks.data"
+              :key="dock.sn">
+              <Airport :dock="dock" :onClickLookInfo="() => switchVisible(dock, true)" />
             </div>
           </div>
         </a-collapse-panel>
@@ -159,7 +21,7 @@
           <div v-else>
             <div :style="{ 'margin-top': index === 0 ? '' : '10px' }" v-for="(device, index) in onlineDevices.data"
               :key="device.sn">
-              <OnLineDevice type="MANUAL" :device="device" :onClickLookInfo="() => switchVisible(device, false)" />
+              <OnLineDevice :device="device" :onClickLookInfo="() => switchVisible(device, false)" />
             </div>
           </div>
         </a-collapse-panel>
@@ -180,6 +42,7 @@ import noData from '/@/assets/icons/no-data.png'
 import { useMyStore } from '/@/store'
 import { getDeviceTopo, getUnreadDeviceHms, updateDeviceHms } from '/@/api/manage'
 import { EHmsLevel } from '/@/types/enums'
+import Airport from '/@/components/airport/index.vue'
 import OnLineDevice from '/@/components/onLineDevice/index.vue'
 import { OnlineDevice, EModeCode, EDockModeCode } from '/@/types/device'
 import { EDeviceTypeName } from '/@/types'
@@ -484,4 +347,4 @@ function readHms(visiable: boolean, sn: string) {
     -webkit-transform: translateX(-100%);
   }
 }
-</style>
+</style>

+ 63 - 0
Web/src/utils/index.ts

@@ -66,6 +66,69 @@ export const getTextByModeCode = (code: number) => {
     return text;
 }
 
+export const getTextByDockModeCode = (code: number) => {
+    let text = '';
+    switch (code) {
+        case -1:
+            text = '设备已离线';
+            break;
+        case 0:
+            text = '空闲中';
+            break;
+        case 1:
+            text = '现场调试';
+            break;
+        case 2:
+            text = '远程调试';
+            break;
+        case 3:
+            text = '固件升级中';
+            break;
+        case 4:
+            text = '作业中';
+            break;
+        default:
+            break;
+    }
+    return text;
+}
+
+export const getTextByDockTaskCode = (code: number) => {
+    let text = '';
+    switch (code) {
+        case -1:
+            text = '--';
+            break;
+        case 0:
+            text = '作业准备中';
+            break;
+        case 1:
+            text = '飞行作业中';
+            break;
+        case 2:
+            text = '作业后状态恢复';
+            break;
+        case 3:
+            text = '自定义飞行区更新中';
+            break;
+        case 4:
+            text = '地形障碍物更新中';
+            break;
+        case 5:
+            text = '任务空闲';
+            break;
+        case 255:
+            text = '飞行器异常';
+            break;
+        case 256:
+            text = '未知状态';
+            break;
+        default:
+            break;
+    }
+    return text;
+}
+
 // 获取项目ID
 export const getWorkspaceId = () => {
     return localStorage.getItem(ELocalStorageKey.WorkspaceId) || '';

Vissa filer visades inte eftersom för många filer har ändrats