| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564 |
- <template>
- <a-layout class="page">
- <a-layout-sider class="left" width="40%" style="border-radius: 4px;">
- <div style="width:90%; height: 90%; margin: 4vh">
- <a-layout style="height: 20%; margin-top: 3vh; background-color: white; ">
- <a-layout-sider width="25%" theme="light" align="center">
- <a-avatar :size="60" :src="cloudapi">
- </a-avatar>
- </a-layout-sider>
- <a-layout-content style="margin-left: 1vw;">
- <div style="height: 50%;">
- <a-dropdown :trigger="['click']">
- <span style="font-size: 16px; font-weight: bolder">{{ workspaceName }}</span>
- <template #overlay>
- <a-menu v-model:selectedKeys="state.selectedKeys" @select="onSelectMenu">
- <a-menu-item v-for="item in state.projectList" :key="item.id">
- {{ item.name }}
- </a-menu-item>
- </a-menu>
- </template>
- </a-dropdown>
- <RightOutlined style="float: right; margin-top: 5px; color: #8894a0" @click="showStatus" />
- </div>
- <div style="height: 50%;">
- <CloudSyncOutlined v-if="thingState === EStatusValue.CONNECTED" style="color: #75c5f6" />
- <SyncOutlined spin v-else />
- <span style="color: #737373; margin-left: 3px;">{{ thingState }}</span>
- </div>
- <a-drawer placement="right" v-model:visible="drawerVisible" width="340px">
- <div class="mb10 flex-row flex-justify-center flex-align-center">
- <p class="fz14" style="font-weight: 100;">
- 模块状态
- </p>
- </div>
- <div class="width-100 mb10 flex-align-start" v-for="m in modules" :key="m.name" style="height: 30px;">
- <div class="ml5" style="float: left; color: #000000;">{{ m.name }}:</div>
- <div class="ml10" style="float: right; margin-bottom: 8px;">
- <span :key="m.state" :class="m.state.value === EStatusValue.CONNECTED ? 'green' : 'red'">{{
- m.state.value }} </span>
- <a-button-group>
- <a-button class="ml5" type="primary" size="small" @click.stop="moduleInstall(m)">安装</a-button>
- <a-button class="ml5 mr5" type="danger" size="small" @click.stop="moduleUninstall(m)">卸载</a-button>
- </a-button-group>
- </div>
- <a-divider />
- </div>
- </a-drawer>
- </a-layout-content>
- </a-layout>
- <a-divider style="height: 2px; background-color: #f5f5f5; margin-top: 3vh;" />
- <a-button id="exitBtn" class="fz18" @click="confirmAgain"
- style="width: 10vw; height: 10vh; position: fixed; bottom: 13vh; left: 15vw; background-color: #e6e6e6; color: red; border: 0;"
- type="primary">
- 退出
- </a-button>
- <a-modal v-model:visible="exitVisible" width="300px" :closable="false">
- <template #footer>
- <a-button type="text" style="width: 48%; float: left;" @click="onBack">取消</a-button>
- <a-button type="text" style="width: 48%;" @click="onExit">确定</a-button>
- </template>
- <p>退出后,大疆无人机与该服务器之间的数据将不会同步</p>
- </a-modal>
- </div>
- </a-layout-sider>
- <a-layout-content class="right flex-column">
- <div class="mb5">
- <span class="ml5" style="color: #939393;">序列号</span>
- </div>
- <div class="fz16" style="background-color: white; border-radius: 4px;">
- <a-row style="border-bottom: 1px solid #f4f8f9; height: 45px;" align="middle">
- <a-col :span="1"></a-col>
- <a-col :span="9">
- 遥控器SN
- </a-col>
- <a-col :span="13" class="flex-align-end flex-column">
- <span style="color: #737373">{{ device.data.gateway_sn }}</span>
- </a-col>
- </a-row>
- <a-row style="border-bottom: 1px solid #f4f8f9; height: 45px;" align="middle"
- v-if="device.data.online_status && device.data.sn">
- <a-col :span="1"></a-col>
- <a-col :span="9">Aircraft Sn</a-col>
- <a-col :span="13" class="flex-align-end flex-column">
- <span style="color: #737373">{{ device.data.sn }}</span>
- </a-col>
- </a-row>
- </div>
- <div class="mt5 mb5">
- <span class="ml5" style="color: #939393;">设置</span>
- </div>
- <div class="fz16" style="background-color: white; border-radius: 4px;">
- <a-row v-if="device.data.online_status && device.data.sn"
- style="border-bottom: 1px solid #f4f8f9; height: 45px;" align="middle" @click="bindingDevice">
- <a-col :span="1"></a-col>
- <a-col :span="11">
- 设备绑定
- </a-col>
- <a-col :span="10" style="text-align: right">
- <span v-if="device.data.bound_status" style="color: #737373">Aircraft bound</span>
- <span v-else style="color: #737373">Aircraft not bound</span>
- </a-col>
- <a-col :span="2" class="flex-align-center flex-column">
- <RightOutlined style="color: #8894a0; font-size: 20px;" />
- </a-col>
- </a-row>
- <a-row style="border-bottom: 1px solid #f4f8f9; height: 45px;" align="middle" @click="onMediaSetting">
- <a-col :span="1"></a-col>
- <a-col :span="21">
- 上传媒体
- </a-col>
- <a-col :span="2" class="flex-align-center flex-column">
- <RightOutlined style="color: #8894a0; font-size: 20px;" />
- </a-col>
- </a-row>
- <a-row style="border-bottom: 1px solid #f4f8f9; height: 45px;" align="middle" @click="onLiveshareSetting">
- <a-col :span="1"></a-col>
- <a-col :span="21">
- 手动直播
- </a-col>
- <a-col :span="2" class="flex-align-center flex-column">
- <RightOutlined style="color: #8894a0; font-size: 20px;" />
- </a-col>
- </a-row>
- </div>
- </a-layout-content>
- </a-layout>
- </template>
- <script lang="ts" setup>
- import { message } from 'ant-design-vue'
- import { onMounted, reactive, ref } from 'vue'
- import { BindBody, bindDevice, getDeviceBySn, getPlatformInfo, getUserInfo } from '/@/api/manage'
- import apiPilot, { ApiParam, MapParam, ThingParam, WsParam } from '/@/api/pilot-bridge'
- import { getRoot } from '/@/root'
- import { EBizCode, EComponentName, EDownloadOwner, ELocalStorageKey, ERouterName, EStatusValue } from '/@/types'
- import cloudapi from '/@/assets/icons/cloudapi.png'
- import { SwapOutlined, RightOutlined, CloudSyncOutlined, SyncOutlined } from '@ant-design/icons-vue'
- import { useMyStore } from '/@/store'
- import { DeviceStatus } from '/@/types/device'
- import { useConnectWebSocket } from '/@/hooks/use-connect-websocket'
- import { apis } from "/@/api/custom/index";
- const root = getRoot()
- const gatewayState = ref<boolean>(localStorage.getItem(ELocalStorageKey.GatewayOnline) === 'true')
- const thingState = ref(EStatusValue.DISCONNECT)
- const apiState = ref(EStatusValue.DISCONNECT)
- const liveState = ref(EStatusValue.DISCONNECT)
- const wsState = ref(EStatusValue.DISCONNECT)
- const mapState = ref(EStatusValue.DISCONNECT)
- const tsaState = ref(EStatusValue.DISCONNECT)
- const mediaState = ref(EStatusValue.DISCONNECT)
- const waylineState = ref(EStatusValue.DISCONNECT)
- const workspaceName = ref<string>(localStorage.getItem(ELocalStorageKey.WorkspaceName)!)
- const username = ref(localStorage.getItem(ELocalStorageKey.Username)!)
- const wsId = ref(localStorage.getItem(ELocalStorageKey.WorkspaceId)!)
- const components = apiPilot.init()
- const exitVisible = ref(false)
- const drawerVisible = ref(false)
- let minitor: any
- interface DeviceInfoData {
- data: DeviceStatus
- }
- const device = reactive<DeviceInfoData>({
- data: {
- sn: '',
- online_status: false,
- device_callsign: '',
- user_id: '',
- user_callsign: '',
- bound_status: false,
- model: '',
- gateway_sn: EStatusValue.DISCONNECT,
- domain: -1
- }
- })
- const state = reactive({
- selectedKeys: [],
- projectList: [],
- })
- const onSelectMenu = (items: any) => {
- console.log(items);
- }
- const bindParam: BindBody = {
- device_sn: '',
- user_id: '',
- workspace_id: wsId.value
- }
- const modules = [{
- name: 'Cloud',
- state: thingState,
- module: EComponentName.Thing
- }, {
- name: 'Api',
- state: apiState,
- module: EComponentName.Api
- }, {
- name: 'Live',
- state: liveState,
- module: EComponentName.Liveshare
- }, {
- name: 'Ws',
- state: wsState,
- module: EComponentName.Ws
- }, {
- name: 'Map',
- state: mapState,
- module: EComponentName.Map
- }, {
- name: 'Tsa',
- state: tsaState,
- module: EComponentName.Tsa
- }, {
- name: 'Media',
- state: mediaState,
- module: EComponentName.Media
- }, {
- name: 'Wayline',
- state: waylineState,
- module: EComponentName.Mission
- }]
- const store = useMyStore()
- const messageHandler = async (payload: any) => {
- if (!payload) {
- return
- }
- switch (payload.biz_code) {
- case EBizCode.DeviceOnline: {
- console.info('online: ', payload)
- if (payload.data.sn === device.data.gateway_sn) {
- localStorage.setItem(ELocalStorageKey.GatewayOnline, gatewayState.value.toString())
- break
- }
- if (payload.data.gateway_sn === device.data.gateway_sn) {
- device.data = payload.data
- localStorage.setItem(ELocalStorageKey.Device, JSON.stringify(device.data))
- }
- break
- }
- case EBizCode.DeviceOffline: {
- console.info('offline: ', payload)
- if (payload.data.sn === device.data.sn) {
- device.data.online_status = payload.data.online_status
- localStorage.setItem(ELocalStorageKey.Device, JSON.stringify(device.data))
- }
- break
- }
- default:
- break
- }
- }
- // 监听ws 消息
- useConnectWebSocket(messageHandler)
- let bindNum: any
- onMounted(async () => {
- const res = await apis.fetchChangeRecordList();
- state.projectList = res.data
- state.selectedKeys = res.data
- apiPilot.onBackClickReg()
- apiPilot.onStopPlatform()
- const oldDevice = localStorage.getItem(ELocalStorageKey.Device)
- if (oldDevice) {
- device.data = JSON.parse(oldDevice)
- }
- window.connectCallback = (arg: any) => {
- connectCallback(arg)
- }
- window.wsConnectCallback = (arg: any) => {
- wsConnectCallback(arg)
- }
- device.data.gateway_sn = apiPilot.getRemoteControllerSN()
- if (device.data.gateway_sn === EStatusValue.DISCONNECT.toString()) {
- message.warn('Data is not available, please restart the remote control.')
- return
- }
- device.data.sn = apiPilot.getAircraftSN()
- getDeviceInfo()
- const isLoaded = apiPilot.isComponentLoaded(EComponentName.Thing)
- if (isLoaded) {
- username.value = '' + localStorage.getItem(ELocalStorageKey.Username)
- workspaceName.value = '' + localStorage.getItem(ELocalStorageKey.WorkspaceName)
- refreshStatus()
- apiPilot.setPlatformMessage(
- '' + localStorage.getItem(ELocalStorageKey.PlatformName),
- workspaceName.value,
- '' + localStorage.getItem(ELocalStorageKey.WorkspaceDesc)
- )
- return
- }
- setWorkspaceInfo()
- getUserInfo().then(res => {
- username.value = res.data.username
- localStorage.setItem(ELocalStorageKey.Username, username.value)
- // thing
- const param: ThingParam = {
- host: res.data.mqtt_addr,
- username: res.data.mqtt_username,
- password: res.data.mqtt_password,
- connectCallback: 'connectCallback'
- }
- components.set(EComponentName.Thing, param)
- apiPilot.loadComponent(EComponentName.Thing, components.get(EComponentName.Thing))
- bindParam.device_sn = device.data.gateway_sn
- bindParam.user_id = res.data.user_id
- bindParam.workspace_id = res.data.workspace_id
- })
- })
- const connectCallback = async (arg: any) => {
- if (arg) {
- thingState.value = EStatusValue.CONNECTED
- // liveshare
- apiPilot.loadComponent(EComponentName.Liveshare, components.get(EComponentName.Liveshare))
- // ws
- const wsParam: WsParam = components.get(EComponentName.Ws)
- wsParam.token = apiPilot.getToken()
- apiPilot.loadComponent(EComponentName.Ws, components.get(EComponentName.Ws))
- // map
- const mapParam: MapParam = components.get(EComponentName.Map)
- mapParam.userName = username.value
- apiPilot.loadComponent(EComponentName.Map, components.get(EComponentName.Map))
- // tsa
- apiPilot.loadComponent(EComponentName.Tsa, components.get(EComponentName.Tsa))
- // media
- apiPilot.loadComponent(EComponentName.Media, components.get(EComponentName.Media))
- apiPilot.setDownloadOwner(EDownloadOwner.Mine.valueOf())
- // mission
- apiPilot.loadComponent(EComponentName.Mission, {})
- bindNum = setInterval(() => {
- if (!bindParam.device_sn) {
- device.data.gateway_sn = apiPilot.getRemoteControllerSN()
- bindParam.device_sn = device.data.gateway_sn
- return
- }
- bindDevice(bindParam).then(bindRes => {
- if (bindRes.code !== 0) {
- message.error(bindRes.message)
- console.error(bindRes.message)
- } else {
- clearInterval(bindNum)
- }
- })
- }, 2000)
- setTimeout(getDeviceInfo, 3000)
- } else {
- thingState.value = EStatusValue.DISCONNECT
- }
- refreshStatus()
- }
- const wsConnectCallback = async (arg: any) => {
- if (arg) {
- wsState.value = EStatusValue.CONNECTED
- } else {
- wsState.value = EStatusValue.DISCONNECT
- }
- }
- const confirmAgain = () => {
- exitVisible.value = true
- }
- const onBack = () => {
- exitVisible.value = false
- }
- const onExit = () => {
- localStorage.clear()
- apiPilot.stopwebview()
- }
- const bindingDevice = async () => {
- root.$router.push(ERouterName.PILOT_BIND)
- }
- const onMediaSetting = async (e: any) => {
- root.$router.push(ERouterName.PILOT_MEDIA)
- }
- const onLiveshareSetting = async (e: any) => {
- root.$router.push(ERouterName.PILOT_LIVESHARE)
- }
- const onOpen3rdApp = () => {
- const packageName = 'com.dji.sample'
- const isInstalled = apiPilot.isAppInstalled(packageName)
- if (isInstalled) {
- window.open('https://www.dji.com')
- } else {
- message.error(packageName + ' is not installed.')
- }
- }
- const showStatus = async () => {
- minitor = setInterval(() => {
- refreshStatus()
- if (!drawerVisible.value) {
- clearInterval(minitor)
- }
- }, 2000)
- drawerVisible.value = true
- }
- function setWorkspaceInfo() {
- if (localStorage.getItem(ELocalStorageKey.WorkspaceName)) {
- apiPilot.setPlatformMessage(
- '' + localStorage.getItem(ELocalStorageKey.PlatformName),
- workspaceName.value,
- '' + localStorage.getItem(ELocalStorageKey.WorkspaceDesc)
- )
- apiPilot.setWorkspaceId(wsId.value)
- return
- }
- getPlatformInfo().then(res => {
- console.log(res)
- workspaceName.value = res.data.workspace_name
- wsId.value = res.data.workspace_id
- localStorage.setItem(ELocalStorageKey.PlatformName, res.data.platform_name)
- localStorage.setItem(ELocalStorageKey.WorkspaceName, workspaceName.value)
- localStorage.setItem(ELocalStorageKey.WorkspaceDesc, res.data.workspace_desc)
- apiPilot.setPlatformMessage(
- res.data.platform_name,
- workspaceName.value,
- res.data.workspace_desc
- )
- apiPilot.setWorkspaceId(wsId.value)
- })
- }
- function refreshStatus() {
- thingState.value = apiPilot.thingGetConnectState() ? EStatusValue.CONNECTED : EStatusValue.DISCONNECT
- apiState.value = apiPilot.isComponentLoaded(EComponentName.Api) ? EStatusValue.CONNECTED : EStatusValue.DISCONNECT
- liveState.value = apiPilot.isComponentLoaded(EComponentName.Liveshare) ? EStatusValue.CONNECTED : EStatusValue.DISCONNECT
- wsState.value = apiPilot.isComponentLoaded(EComponentName.Ws) && apiPilot.wsGetConnectState()
- ? EStatusValue.CONNECTED
- : EStatusValue.DISCONNECT
- mapState.value = apiPilot.isComponentLoaded(EComponentName.Map) ? EStatusValue.CONNECTED : EStatusValue.DISCONNECT
- tsaState.value = apiPilot.isComponentLoaded(EComponentName.Tsa) ? EStatusValue.CONNECTED : EStatusValue.DISCONNECT
- mediaState.value = apiPilot.isComponentLoaded(EComponentName.Media) ? EStatusValue.CONNECTED : EStatusValue.DISCONNECT
- waylineState.value = apiPilot.isComponentLoaded(EComponentName.Mission) ? EStatusValue.CONNECTED : EStatusValue.DISCONNECT
- }
- function moduleInstall(m: any) {
- let param
- switch (m.module) {
- case EComponentName.Thing:
- param = apiPilot.thingGetConfigs()
- break
- case EComponentName.Api: {
- const apiParam: ApiParam = {
- host: apiPilot.getHost(),
- token: apiPilot.getToken()
- }
- param = apiParam
- break
- }
- case EComponentName.Map: {
- const mapParam: MapParam = components.get(EComponentName.Map)
- mapParam.userName = '' + localStorage.getItem(ELocalStorageKey.Username)
- param = mapParam
- break
- }
- case EComponentName.Ws: {
- const wsParam: WsParam = components.get(EComponentName.Ws)
- wsParam.token = '' + localStorage.getItem(ELocalStorageKey.Token)
- param = wsParam
- break
- }
- default:
- param = components.get(m.module)
- }
- components.set(m.module, param)
- console.info(components.get(m.module))
- apiPilot.loadComponent(m.module, components.get(m.module))
- refreshStatus()
- }
- function moduleUninstall(m: any) {
- message.info('uninstall ' + m.module)
- apiPilot.unloadComponent(m.module)
- refreshStatus()
- }
- function getDeviceInfo() {
- if (!device.data.sn || device.data.sn === EStatusValue.DISCONNECT) {
- return
- }
- getDeviceBySn(bindParam.workspace_id, device.data.sn).then(res => {
- if (res.code !== 0) {
- return
- }
- device.data.online_status = res.data.status
- device.data.bound_status = res.data.bound_status
- device.data.device_callsign = res.data.nickname
- device.data.model = res.data.device_name
- localStorage.setItem(ELocalStorageKey.Device, JSON.stringify(device.data))
- })
- }
- </script>
- <style lang="scss" scoped>
- @import '/@/styles/index.scss';
- .page {
- display: flex;
- position: absolute;
- transition: width 0.2s ease;
- height: 100%;
- width: 100%;
- .left {
- height: 90%;
- background-color: white;
- margin-top: 6vh;
- margin-left: 2vh;
- }
- .right {
- height: 90%;
- margin-top: 6vh;
- margin-left: 5vh;
- margin-right: 5vh;
- }
- }
- .green {
- color: green
- }
- .red {
- color: red;
- }
- #exitBtn:hover :active {
- background-color: rgb(77, 75, 75);
- width: 10vw;
- height: 10vh;
- position: fixed;
- bottom: 13vh;
- left: 15vw;
- line-height: 10vh;
- }
- </style>
|