husq
2023-09-14 004f9514d4858201ce116b878e464931bd5c5df8
基于demo源码修改功能
11 files modified
254 ■■■■■ changed files
src/api/http/config.ts 14 ●●●● patch | view | raw | blame | history
src/api/manage.ts 6 ●●●● patch | view | raw | blame | history
src/components/common/sidebar.vue 2 ●●● patch | view | raw | blame | history
src/pages/page-web/projects/components/DockConfig.vue 116 ●●●●● patch | view | raw | blame | history
src/pages/page-web/projects/project_list/list_page/components/ProjectList.vue 6 ●●●● patch | view | raw | blame | history
src/pages/page-web/projects/tsa.vue 83 ●●●●● patch | view | raw | blame | history
src/pages/page-web/projects/workspace.vue 2 ●●● patch | view | raw | blame | history
src/store/index.ts 10 ●●●●● patch | view | raw | blame | history
src/types/device.ts 9 ●●●● patch | view | raw | blame | history
src/websocket/index.ts 2 ●●● patch | view | raw | blame | history
src/websocket/util/config.ts 4 ●●●● patch | view | raw | blame | history
src/api/http/config.ts
@@ -1,17 +1,7 @@
/*
 * @Author: 胡思旗 931347610@qq.com
 * @Date: 2023-08-22 09:55:39
 * @LastEditors: husq 931347610@qq.com
 * @LastEditTime: 2023-09-13 19:01:16
 * @FilePath: \Cloud-API-Demo-Web\src\api\http\config.ts
 * @Description:
 *
 * Copyright (c) 2023 by ${git_name_email}, All Rights Reserved.
 */
export const CURRENT_CONFIG = {
  // license
  appId: 'Please enter the app id.', // You need to go to the development website to apply.
  appId: '0a472a3b0f1543ab9e66d1ad4e6a91c8', // You need to go to the development website to apply.
  appKey: 'Please enter the app key.', // You need to go to the development website to apply.
  appLicense: 'Please enter the app license.', // You need to go to the development website to apply.
@@ -20,7 +10,7 @@
  baseURL: 'http://192.168.1.198:6789', // This url must end with "/". Example: 'http://192.168.1.1:6789/'
  // ws://172.16.13.129:6789/api/v1/ws
  // ws://192.168.1.198:1883/
  websocketURL: 'ws://192.168.1.198:8600/drone/api/v1/ws', // Example: 'ws://192.168.1.198:6789/api/v1/ws'
  websocketURL: 'ws://192.168.1.198:6789/api/v1/ws', // Example: 'ws://192.168.1.198:6789/api/v1/ws'
  // livestreaming
  // RTMP  Note: This IP is the address of the streaming server. If you want to see livestream on web page, you need to convert the RTMP stream to WebRTC stream.
src/api/manage.ts
@@ -91,9 +91,9 @@
}
// 获取在线设备信息
export const getDeviceTopo = async function (params:any): Promise<IWorkspaceResponse<any>> {
  const url = `${HTTP_PREFIX_MANAGE}/manage/device`
  const result = await request.get(url, { params })
export const getDeviceTopo = async function (workspace_id: string): Promise<IWorkspaceResponse<any>> {
  const url = `${HTTP_PREFIX}/devices/${workspace_id}/devices`
  const result = await request.get(url)
  return result.data
}
src/components/common/sidebar.vue
@@ -54,7 +54,7 @@
    const root = getRoot()
    const options = [
      { key: 0, label: '团队', path: '/' + ERouterName.TSA, icon: 'TeamOutlined' },
      // { key: 1, label: '直播', path: '/' + ERouterName.LIVESTREAM, icon: 'VideoCameraOutlined' },
      { key: 1, label: '直播', path: '/' + ERouterName.LIVESTREAM, icon: 'VideoCameraOutlined' },
      { key: 2, label: '标注', path: '/' + ERouterName.LAYER, icon: 'EnvironmentOutlined' },
      { key: 3, label: '媒体库', path: '/' + ERouterName.MEDIA, icon: 'PictureOutlined' },
      { key: 4, label: '航线库', path: '/' + ERouterName.WAYLINE, icon: 'NodeIndexOutlined' },
src/pages/page-web/projects/components/DockConfig.vue
@@ -1,3 +1,13 @@
<!--
 * @Author: husq 931347610@qq.com
 * @Date: 2023-09-13 18:21:08
 * @LastEditors: husq 931347610@qq.com
 * @LastEditTime: 2023-09-14 11:40:03
 * @FilePath: \Cloud-API-Demo-Web\src\pages\page-web\projects\components\DockConfig.vue
 * @Description:
 *
 * Copyright (c) 2023 by ${git_name_email}, All Rights Reserved.
-->
<template>
    <div class="config_dock">
        <div class="w100 option flex-display">
@@ -35,7 +45,7 @@
                </div>
                <div class="option-btn flex-display flex-justify-between">
                    <div class="btn">
                        <a-button type="primary" @click="videoControl = !videoControl" :class="{ unchecked: !videoControl }"
                        <a-button type="primary" @click="control" :class="{ unchecked: !videoControl }"
                            style="width: 100%;">
                            <template #icon>
                                <VideoCameraOutlined />
@@ -55,12 +65,12 @@
                </div>
            </div>
        </div>
        <div class="video">
        <div class="video" v-if="videoControl">
            <!-- <div class="video-option">
                <a-tooltip title="关闭补光灯"></a-tooltip>
            </div> -->
            <video auto src="https://cdn.jsdelivr.net/gh/xdlumia/files/video-play/IronMan.mp4"
                style="width: 100%;height: 200px;"></video>
                style="width: 100%;height: 200px;" id="video-webrtc" ref="videowebrtc" controls class="mt20"></video>
        </div>
        <div class="w100 option flex-display aerial">
            <div class="option-left-icon flex-display">
@@ -123,8 +133,8 @@
                </div>
                <div class="mb5 select-box flex-display flex-align-center">
                    <span class="f10 label mr15">失联动作</span>
                    <a-select :dropdownMatchSelectWidth="false" class="select-style" :bordered="false" size="small" ref="select"
                        v-model:value="option.lose_connection" style="width: 60px" @change="handleChange">
                    <a-select :dropdownMatchSelectWidth="false" class="select-style" :bordered="false" size="small"
                        ref="select" v-model:value="option.lose_connection" style="width: 60px" @change="handleChange">
                        <a-select-option value="1">悬停</a-select-option>
                        <a-select-option value="2">返航</a-select-option>
                        <a-select-option value="3">继续执行</a-select-option>
@@ -137,10 +147,20 @@
<script setup lang="ts">
import { VideoCameraOutlined, RobotOutlined, ToolOutlined, CloudOutlined, ThunderboltOutlined, FundProjectionScreenOutlined, UploadOutlined, BoxPlotOutlined } from '@ant-design/icons-vue'
import Drawer from '/@/components/Drawer/Drawer.vue'
import { getLiveCapacity, setLivestreamQuality, startLivestream, stopLivestream } from '/@/api/manage'
import { message } from 'ant-design-vue'
import jswebrtc from '/@/vendors/jswebrtc.min.js'
import { CURRENT_CONFIG as config } from '/@/api/http/config'
const nonSwitchable = 'normal'
// 视频ref
const videowebrtc = ref(null)
// 是否显示视频
const videoControl = ref(false)
// 是否显示操作  页面暂未加
const optionControl = ref(false)
// 一键起飞操作
const flyControl = ref(false)
// 操作设置
const option = ref({
  safe_height: 60,
  fly_height: 60,
@@ -149,6 +169,87 @@
})
const handleChange = (e: string) => {
  console.log(e, 'eee')
}
// 画质设置
interface SelectOption {
  value: any,
  label: string,
  more?: any
}
const clarityList: SelectOption[] = [
  {
    value: 0,
    label: 'Adaptive'
  },
  {
    value: 1,
    label: 'Smooth'
  },
  {
    value: 2,
    label: 'Standard'
  },
  {
    value: 3,
    label: 'HD'
  },
  {
    value: 4,
    label: 'Super Clear'
  }
]
// 视频配置
const videoOption = ref({
  url_type: 3,
  url: '',
  video_id: '',
  video_quality: 3,
})
const control = () => {
  videoControl.value = !videoControl.value
  if (videoControl.value) {
    onStart()
  } else {
    onPause()
  }
}
// 开启视频直播
const onStart = async () => {
  videoOption.value.video_id = `${'设备id'}/${'默认设备第一个相机id'}/${nonSwitchable}-0`
  const liveURL = `serverIP=${config.gbServerIp}&serverPort=${config.gbServerPort}&serverID=${config.gbServerId}&agentID=${config.gbAgentId}&agentPassword=${config.gbPassword}&localPort=${config.gbAgentPort}&channel=${config.gbAgentChannel}`
  const res = await startLivestream({
    url: liveURL,
    video_id: videoOption.value.video_id,
    url_type: videoOption.value.url_type,
    video_quality: videoOption.value.video_quality,
  })
  if (res.code !== 0) return
  const url = res.data.url
  const videoElement = videowebrtc.value
  message.loading({
    content: 'Loding...',
    duration: 4,
    onClose () {
      const player = new jswebrtc.Player(url, {
        video: videoElement,
        autoplay: true,
        onPlay: (obj: any) => {
          console.log('start play livestream')
        }
      })
    }
  })
}
// 暂停直播
const onPause = async () => {
  stopLivestream({
    video_id: videoOption.value.video_id
  }).then(res => {
    if (res.code === 0) {
      message.success(res.message)
      console.log('stop play livestream')
    }
  })
}
</script>
@@ -279,4 +380,5 @@
            background-color: hsla(0, 0%, 100%, .1);
        }
    }
}</style>
}
</style>
src/pages/page-web/projects/project_list/list_page/components/ProjectList.vue
@@ -16,9 +16,9 @@
                    <a-menu-item>
                      <a @click="goEdit(item)">编辑</a>
                    </a-menu-item>
                    <a-menu-item>
                    <!-- <a-menu-item>
                      <a @click="editStatus(item)">归档</a>
                    </a-menu-item>
                    </a-menu-item> -->
                    <a-menu-item>
                      <a @click="deleteItem(item)">删除</a>
                    </a-menu-item>
@@ -132,7 +132,7 @@
  store.commit('SET_POINT_LIST', [])
  router.push({
    name: ERouterName.WORKSPACE,
    query: { id: item.id },
    query: { id: item.id, workSpaceId: item.workspace_id },
  })
}
const goCenter = (item:any, index:number) => {
src/pages/page-web/projects/tsa.vue
@@ -16,27 +16,31 @@
            <a-empty style="color: #fff;" :image="simpleImage" description="暂无数据" :image-style="{ height: '60px' }" />
          </div>
          <div v-else class="fz12" style="color: white;">
            {{ onlineDocks.data }}
            =========
            {{ dockInfo }}
            <div v-for="dock in onlineDocks.data" :key="dock.sn"
              style="background: #3c3c3c; height: 90px; margin-bottom: 10px;">
              <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>
                      132
                      <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] }}
                        {{ dockInfo[dock.gateway.sn] ? EDockModeText[dockInfo[dock.gateway.sn].basic_osd?.mode_code] :
                          EDockModeText[EDockModeCode.Disconnected] }}
                      </div>
                    </div>
                    <div class="mr5 flex-align-center flex-row" style="width: 85px; margin-right: 0; height: 18px;">
@@ -88,6 +92,7 @@
                      <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">
@@ -239,63 +244,36 @@
</template>
<script lang="ts" setup>
import { styleDrawer } from './common/common'
import { useConnectWebSocket } from '/@/hooks/use-connect-websocket'
import { flyTo } from '/@/hooks/use-center-point'
import { computed, onMounted, reactive, ref, watch, WritableComputedRef } from 'vue'
import { EDeviceTypeName, ELocalStorageKey } from '/@/types'
import Drawer from '/@/components/Drawer/Drawer.vue'
import noData from '/@/assets/icons/no-data.png'
import { Empty } from 'ant-design-vue'
import rc from '/@/assets/icons/rc.png'
import { OnlineDevice, EModeCode, OSDVisible, EDockModeCode, DeviceOsd } from '/@/types/device'
import { OnlineDevice, EModeCode, OSDVisible, EDockModeCode, DeviceOsd, EDockModeText } from '/@/types/device'
import { useMyStore } from '/@/store'
import { getDeviceTopo, getUnreadDeviceHms, updateDeviceHms } from '/@/api/manage'
import { RocketOutlined, EyeInvisibleOutlined, EyeOutlined, RobotOutlined, DoubleRightOutlined } from '@ant-design/icons-vue'
import { EHmsLevel } from '/@/types/enums'
import { UranusMqtt } from '/@/mqtt/index'
import Drawer from '/@/components/Drawer/Drawer.vue'
import { styleDrawer } from './common/common'
import DockConfig from './components/DockConfig.vue'
const showDrawer = ref(false)
const { appContext } = getCurrentInstance()
const global = appContext.config.globalProperties
const route = useRoute()
const simpleImage = Empty.PRESENTED_IMAGE_SIMPLE
// const simpleImage = Empty.PRESENTED_IMAGE_SIMPLE
const store = useMyStore()
const username = ref(localStorage.getItem(ELocalStorageKey.Username))
const workspaceId = ref(localStorage.getItem(ELocalStorageKey.WorkspaceId)!)
const osdVisible = ref({} as OSDVisible)
const hmsVisible = new Map<string, boolean>()
const scorllHeight = ref()
const showDrawer = ref(false)
const onlineDevices = reactive({
  data: [] as OnlineDevice[]
})
const onlineDocks = reactive({
  data: [
    {
      model: '我是model字段',
      callsign: '我是callsign字段',
      sn: '我是sn字段',
      mode: '我是mode字段',
      gateway: {
        model: 'gateway.model',
        callsign: 'gateway.callsign',
        sn: '我是gateway.sn字段',
        mode: '我是gateway.mode字段',
      },
      payload: [
        {
          index: 1,
          model: '2',
          control_source: 'A',
          payload_sn: 'payload_sn',
          payload_index: 'payload_index',
          payload_name: 'payload_name',
        },
      ]
    },
  ] as unknown as OnlineDevice[]
  data: [] as OnlineDevice[]
})
const deviceInfo = computed(() => store.state.deviceState.deviceInfo)
@@ -308,22 +286,21 @@
})
onMounted(() => {
  // flyTo(global.$viewer, route.params.longitude as unknown as number, route.params.latitude as unknown as number)
  // getOnlineTopo()
  // setTimeout(() => {
  //   watch(() => store.state.deviceStatusEvent,
  //     data => {
  //       getOnlineTopo()
  //       if (data.deviceOnline.sn) {
  //         getUnreadHms(data.deviceOnline.sn)
  //       }
  //     },
  //     {
  //       deep: true
  //     }
  //   )
  //   getOnlineDeviceHms()
  // }, 3000)
  getOnlineTopo()
  setTimeout(() => {
    watch(() => store.state.deviceStatusEvent,
      data => {
        getOnlineTopo()
        if (data.deviceOnline.sn) {
          getUnreadHms(data.deviceOnline.sn)
        }
      },
      {
        deep: true
      }
    )
    getOnlineDeviceHms()
  }, 3000)
  const element = document.getElementsByClassName('scrollbar').item(0) as HTMLDivElement
  const parent = element?.parentNode as HTMLDivElement
  scorllHeight.value = parent?.clientHeight - parent?.firstElementChild?.clientHeight
@@ -334,7 +311,7 @@
  //   const { data
}
// 监听ws 消息
useConnectWebSocket(messageHandler)
// useConnectWebSocket(messageHandler)
function getOnlineTopo () {
  getDeviceTopo(workspaceId.value).then((res) => {
    if (res.code !== 0) {
src/pages/page-web/projects/workspace.vue
@@ -115,7 +115,7 @@
}
// 监听ws 消息
// useConnectWebSocket(messageHandler)
useConnectWebSocket(messageHandler)
</script>
<style lang="scss" scoped>
src/store/index.ts
@@ -5,15 +5,13 @@
import { getLayers } from '/@/api/layer'
import { LayerType } from '/@/types/mapLayer'
import { WaylineFile } from '/@/types/wayline'
import getter from './getters'
import { DevicesCmdExecuteInfo } from '/@/types/device-cmd'
import map from './map'
import common from '/@/store/common'
const initStateFunc = () => ({
  Layers: [
    {
      name: '默认',
      name: 'default',
      id: '',
      is_distributed: true,
      elements: [],
@@ -22,7 +20,7 @@
      type: 1
    },
    {
      name: '共享',
      name: 'share',
      id: '',
      is_distributed: true,
      elements: [],
@@ -245,9 +243,7 @@
  mutations,
  actions,
  modules: {
    getter,
    map,
    common
    map
  },
}
src/types/device.ts
@@ -485,7 +485,14 @@
  M3E_Camera = '1-66-0' as any,
  M3T_Camera = '1-67-0' as any,
}
export enum EDockModeText {
  '已断开连接' = -1,
  '空闲' = 0,
  '调试' = 1,
  '远程调试' = 2,
  '正在升级' = 3,
  '工作中' = 4
}
export enum EDockModeCode {
  Disconnected = -1,
  Idle,
src/websocket/index.ts
@@ -69,7 +69,7 @@
  _onMessage (msg: MessageEvent) {
    const data = JSON.parse(msg.data)
    this._messageHandler && this._messageHandler(data)
    console.log('接受消息', msg)
    // console.log('接受消息', msg)
  }
  sendMessage = (message: WebSocketOptions): void => {
src/websocket/util/config.ts
@@ -5,8 +5,8 @@
export function getWebsocketUrl () {
  const token: string = localStorage.getItem(ELocalStorageKey.Token) || '' as string
  const url = CURRENT_CONFIG.websocketURL
  // const url = CURRENT_CONFIG.websocketURL + '?Authorization=' + encodeURI(token)
  // const url = CURRENT_CONFIG.websocketURL
  const url = CURRENT_CONFIG.websocketURL + '?x-auth-token=' + encodeURI(token)
  return url
}