无人机管理后台前端(已迁走)
张含笑
2025-08-05 c832bf2e80ac465e71b7a1c1f7a59d4252030989
src/views/device/components/DockControlPanel.vue
@@ -8,14 +8,9 @@
    <div class="control-cmd-wrapper">
      <div class="control-cmd-header">
        远程调试
        <el-switch
          v-model:checked="debugStatus"
          @change="onDeviceStatusChange"
          active-text="开"
          inactive-text="关"
          class="debug-btn"
          style="--el-switch-on-color: #13ce66; --el-switch-off-color: #ff4949"
        />
        <el-switch v-model="debugStatus" @change="onDeviceStatusChange" active-text="开" inactive-text="关"
          class="debug-btn" style="--el-switch-on-color: #13ce66; --el-switch-off-color: #ff4949" />
        <el-divider />
      </div>
      <div class="control-cmd-box">
        <div v-for="(cmdItem, index) in cmdList" :key="cmdItem.cmdKey" class="control-cmd-item">
@@ -24,155 +19,339 @@
            <div class="item-status">{{ cmdItem.status }}</div>
          </div>
          <div class="control-cmd-item-right">
            <el-button
              :disabled="!debugStatus || cmdItem.disabled"
              :loading="cmdItem.loading"
              size="small"
              type="primary"
              @click="sendControlCmd(cmdItem, index)"
            >
            <el-button :disabled="!debugStatus || cmdItem.disabled" :loading="cmdItem.loading" size="small"
              type="primary" @click="sendControlCmd(cmdItem, index)">
              {{ cmdItem.operateText }}
            </el-button>
          </div>
        </div>
      </div>
      <el-divider v-if="deviceInfo">无人机设置</el-divider>
      <div class="control-cmd-box" v-for="(item, index) in cameras">
        <div>
          <el-form-item label="摄像头设置">
            <el-select @change="cameraSettings($event, item)" v-model="videoValue" placeholder="请选择">
              <el-option v-for="item in videoType" :key="item.value" :label="item.label" :value="item.value">
              </el-option>
            </el-select>
          </el-form-item>
          <el-form-item label="调色盘样式" v-if="videoValue == 'ir'">
            <el-select @change="changs($event, item)" v-model="valueStyle" placeholder="请选择">
              <el-option v-for="item in paletteOptions" :key="item.value" :label="item.label" :value="item.value">
              </el-option>
            </el-select>
          </el-form-item>
        </div>
        <div>
          <el-form-item label="相机模式">
            <el-select @change="photoAndVideoCmdSettings($event, item)" v-model="cameraModeValue" placeholder="请选择">
              <el-option v-for="item in cameraMode" :key="item.value" :label="item.label" :value="item.value">
              </el-option>
            </el-select>
          </el-form-item>
          <el-form-item label="照片存储设置" v-if="cameraModeValue == 0">
            <el-select v-model="photo_storage_settings" collapse-tags @change="photoStorageTypeChang($event, item)"
              multiple placeholder="请选择">
              <el-option v-for="item in photoStorageType" :key="item.value" :label="item.label" :value="item.value">
              </el-option>
            </el-select>
          </el-form-item>
          <el-form-item label="视频存储设置" v-if="cameraModeValue == 1">
            <el-select v-model="video_storage_settings" collapse-tags @change="videoStorageTypeChang($event, item)"
              multiple placeholder="请选择">
              <el-option v-for="item in photoStorageType" :key="item.value" :label="item.label" :value="item.value">
              </el-option>
            </el-select>
          </el-form-item>
        </div>
      </div>
    </div>
  </div>
</template>
<script setup>
import EventBus from '@/event-bus';
import { EBizCode, ELocalStorageKey, ERouterName } from '@/types';
import { useConnectWebSocket } from '@/hooks/use-connect-websocket';
import { getWebsocketUrl } from '@/websocket/util/config';
import { ElMessage } from 'element-plus';
import EventBus from '@/event-bus'
import { EBizCode, ELocalStorageKey, ERouterName } from '@/types'
import { useConnectWebSocket } from '@/hooks/use-connect-websocket'
import { getWebsocketUrl } from '@/websocket/util/config'
import { defineProps, ref, watch, reactive } from 'vue'
import { cmdList as baseCmdList } from '@/types/device-cmd'
import { useDockControl } from './use-dock-control'
import { EDockModeCode } from '@/types/device'
import { updateDeviceCmdInfoByOsd, updateDeviceCmdInfoByExecuteInfo } from '@/utils/device-cmd'
import { setThermalCurrentPaletteStyle, setPhotoStorageSet, setVideoStorageSet, getLiveStatus, setStreamsSwitch, photoAndVideoCmd } from '@/api/device-setting'
import { defineProps, ref, watch } from 'vue';
import { cmdList as baseCmdList } from '@/types/device-cmd';
import { useDockControl } from './use-dock-control';
import { EDockModeCode } from '@/types/device';
import { updateDeviceCmdInfoByOsd, updateDeviceCmdInfoByExecuteInfo } from '@/utils/device-cmd';
import Store from '@/store'
import Store from '@/store';
const valueStyle = ref(0)
const cameraModeValue = ref(0)
const videoValue = ref("zoom")
const photo_storage_settings = ref([])
const video_storage_settings = ref([])
const props = defineProps(['sn', 'deviceInfo']);
const initCmdList = baseCmdList.map(cmdItem => Object.assign({}, cmdItem));
const cmdList = ref(initCmdList);
// 定义一个数据videoList
const videoList = reactive([])
// 摄像头信息
let cameras = reactive([])
// let cameras = reactive([
//   {
//     camera_mode: 0,
//     liveview_world_region: {
//       bottom: 0.5515424609184265,
//       left: 0.42740535736083984,
//       right: 0.5581043362617493,
//       top: 0.42277535796165466
//     },
//     payload_index: "81-0-0",
//     photo_state: 0,
//     record_time: 0,
//     recording_state: 0,
//     remain_photo_num: 3931,
//     remain_record_duration: 0,
//     zoom_factor: 6.9999943,
//     ir_zoom_factor: 2,
//     photo_storage_settings: [
//       "vision",
//       "ir"
//     ],
//     video_storage_settings: [
//       "vision"
//     ]
//   }
// ])
// 无人机信息
let deviceInfo = ref()
// camera_mode {"0":"拍照","1":"录像","2":"智能低光","3":"全景拍照","-1":"不支持的模式"}
const cameraMode = [
  { label: '拍照', value: 0 },
  { label: '录像', value: 1 },
  { label: '智能低光', value: 2 },
  { label: '全景拍照', value: 3 },
  // { label: '不支持的模式', value: -1 },
]
// 调色盘选项数据
const paletteOptions = [
  { label: '白热', value: 0 },
  { label: '黑热', value: 1 },
  { label: '描红', value: 2 },
  { label: '医疗', value: 3 },
  { label: '彩虹1', value: 5 },
  { label: '铁红', value: 6 },
  { label: '北极', value: 8 },
  { label: '熔岩', value: 11 },
  { label: '热铁', value: 12 },
  { label: '彩虹2', value: 13 },
]
// 视频类型 ZOOM("zoom"), WIDE("wide"), THERMAL("thermal"), NORMAL("normal"), IR("ir");
const videoType = [
  { label: '变焦', value: 'zoom' },
  { label: '广角', value: 'wide' },
  // { label: '热红外', value: 'thermal' },
  // { label: '正常', value: 'normal' },
  { label: '红外', value: 'ir' },
]
// 拍照存储类型{current, wide, zoom, ir},可多选
const photoStorageType = [
  { label: '当前', value: 'current' },
  { label: '广角', value: 'wide' },
  { label: '变焦', value: 'zoom' },
  { label: '可见光', value: 'vision' },
  { label: '红外', value: 'ir' },
]
const props = defineProps(['sn', 'deviceInfo'])
const initCmdList = baseCmdList.map(cmdItem => Object.assign({}, cmdItem))
const cmdList = ref(initCmdList)
// dock 控制指令
const debugStatus = ref(props.deviceInfo.mode_code == EDockModeCode.Remote_Debugging)
// 根据机场指令执行状态更新信息
watch(
  () => Store.getters.devicesCmdExecuteInfo,
  devicesCmdExecuteInfo => {
    // console.log('设备指令执行状态变化', devicesCmdExecuteInfo)
    if (props.sn && devicesCmdExecuteInfo[props.sn]) {
      updateDeviceCmdInfoByExecuteInfo(cmdList.value, devicesCmdExecuteInfo[props.sn]);
      updateDeviceCmdInfoByExecuteInfo(cmdList.value, devicesCmdExecuteInfo[props.sn])
    }
  },
  {
    immediate: true,
    deep: true,
  }
);
// 根据设备osd信息更新信息
)
watch(
  () => props.deviceInfo,
  value => {
    updateDeviceCmdInfoByOsd(cmdList.value, value);
    // console.log('deviceInfo', value)
  () => Store.state.device.videoSurveillance,
  newObj => {
    // console.log('视频类型监听中', newObj)
    if (newObj && newObj.live_status && newObj.live_status.length) {
      let arr = newObj.live_status || []
      if (videoList.length == arr.length) {
        return
      }
      arr.forEach(element => {
        let video_id = element.video_id
        element.payIndex = video_id.split('/')[1].split('-')[1]
        if (props.deviceInfo.child_sn == video_id.split('/')[0]) {
          if (element.video_type == 'normal' || element.video_type == 'thermal') {
            videoValue.value = 'wide'
          } else {
            videoValue.value = element.video_type
          }
        }
      })
      videoList.push(...arr) // 添加新元素
    }
  },
  {
    immediate: true,
    deep: true,
  }
);
)
// dock 控制指令
const debugStatus = ref(
  props.deviceInfo.dock?.basic_osd.mode_code === EDockModeCode.Remote_Debugging
);
async function onDeviceStatusChange(status) {
  let result = false;
  if (status) {
    result = await dockDebugOnOff(props.sn, true);
  } else {
    result = await dockDebugOnOff(props.sn, false);
// 根据设备osd信息更新信息
watch(
  () => Store.getters.deviceState,
  value => {
    // console.log('设备状态变化', value)
    if (props.sn && (value.currentSn === props.deviceInfo.child_sn || value.currentSn === props.deviceInfo.device_sn)) {
      debugStatus.value = value.dockInfo[props.deviceInfo.device_sn]?.basic_osd?.mode_code === EDockModeCode.Remote_Debugging
      const devices = {}
      devices['device'] = value.deviceInfo[props.deviceInfo.child_sn]
      devices['dock'] = value.dockInfo[props.deviceInfo.device_sn]
      devices['gateway'] = value.gatewayInfo
      let cameraList = value.deviceInfo[props.deviceInfo.child_sn]?.cameras || []
      cameras = cameraList
      // console.log('设备osd.cameras信息变化', cameras)
      // 判断cameras列表长度是否为0,如果为0,则不执行后续操作
      if (cameras.length > 0) {
        cameraModeValue.value = cameras[0].camera_mode
        photo_storage_settings.value = cameras[0]?.photo_storage_settings ?? [];
        video_storage_settings.value = cameras[0]?.video_storage_settings ?? [];
      }
      deviceInfo = ref(value.deviceInfo[props.deviceInfo.child_sn])
      updateDeviceCmdInfoByOsd(cmdList.value, devices)
    }
  },
  {
    immediate: true,
    deep: true,
  }
)
onMounted(() => {
  getLiveStatuss()
});
  console.log(result, '---1------');
async function getLiveStatuss() {
  let result = await getLiveStatus(props.sn)
  result.data.data.live_status.forEach(item => {
    if (props.deviceInfo.child_sn == item.video_id.split('/')[0]) {
      if (item.video_type == 'normal' || item.video_type == 'thermal') {
        videoValue.value = 'wide'
      } else {
        videoValue.value = item.video_type
      }
    }
  })
}
// 远程控制开关
async function onDeviceStatusChange(status) {
  let result = false
  if (status) {
    result = await dockDebugOnOff(props.sn, true)
  } else {
    result = await dockDebugOnOff(props.sn, false)
  }
  if (!result) {
    if (status) {
      debugStatus.value = false;
      debugStatus.value = false
    } else {
      debugStatus.value = true;
      debugStatus.value = true
    }
  }
}
const { sendDockControlCmd, dockDebugOnOff } = useDockControl();
const { sendDockControlCmd, dockDebugOnOff } = useDockControl()
async function sendControlCmd(cmdItem, index) {
  const params = {
    sn: props.sn,
    cmd: cmdItem.cmdKey,
    action: cmdItem.action,
  };
  const success = await sendDockControlCmd(params, true);
  }
  const success = await sendDockControlCmd(params, true)
  if (success) {
    // updateDeviceSingleCmdInfo(cmdList.value[index])
    updateDeviceSingleCmdInfo(cmdList.value[index])
  }
}
// webSocket 监听
const messageHandler = async payload => {
  if (!payload) {
    return;
    return
  }
  switch (payload.biz_code) {
    case EBizCode.GatewayOsd: {
      Store.commit('SET_GATEWAY_INFO', payload.data);
      break;
      Store.commit('SET_GATEWAY_INFO', payload.data)
      break
    }
    // 飞行器
    case EBizCode.DeviceOsd: {
      Store.commit('SET_DEVICE_INFO', payload.data);
      break;
      Store.commit('SET_DEVICE_INFO', payload.data)
      break
    }
    // 机场
    case EBizCode.DockOsd: {
      // if (payload.data.sn === workspace_id.value) {
      Store.commit('SET_DOCK_INFO', payload.data);
      // console.log(payload.data, 'data')
      // }
      break;
      Store.commit('SET_DOCK_INFO', payload.data)
      break
    }
    case EBizCode.MapElementCreate: {
      Store.commit('SET_MAP_ELEMENT_CREATE', payload.data);
      break;
      Store.commit('SET_MAP_ELEMENT_CREATE', payload.data)
      break
    }
    case EBizCode.MapElementUpdate: {
      Store.commit('SET_MAP_ELEMENT_UPDATE', payload.data);
      break;
      Store.commit('SET_MAP_ELEMENT_UPDATE', payload.data)
      break
    }
    case EBizCode.MapElementDelete: {
      Store.commit('SET_MAP_ELEMENT_DELETE', payload.data);
      break;
      Store.commit('SET_MAP_ELEMENT_DELETE', payload.data)
      break
    }
    case EBizCode.DeviceOnline: {
      Store.commit('SET_DEVICE_ONLINE', payload.data);
      break;
      Store.commit('SET_DEVICE_ONLINE', payload.data)
      break
    }
    case EBizCode.DeviceOffline: {
      Store.commit('SET_DEVICE_OFFLINE', payload.data);
      break;
      Store.commit('SET_DEVICE_OFFLINE', payload.data)
      break
    }
    case EBizCode.FlightTaskProgress:
    case EBizCode.FlightTaskMediaProgress:
    case EBizCode.FlightTaskMediaHighestPriority: {
      EventBus.emit('flightTaskWs', payload);
      break;
      EventBus.emit('flightTaskWs', payload)
      break
    }
    case EBizCode.DeviceHms: {
      Store.commit('SET_DEVICE_HMS_INFO', payload.data);
      break;
      Store.commit('SET_DEVICE_HMS_INFO', payload.data)
      break
    }
    case EBizCode.VideoSurveillance: {
      if (payload.data.live_status && payload.data.live_status.length) {
        Store.commit('SAVE_VIDEO_SURVEILLANCE_INFO', payload.data)
      }
      break
    }
    case EBizCode.DeviceReboot:
    case EBizCode.DroneOpen:
@@ -189,25 +368,93 @@
        biz_code: payload.biz_code,
        timestamp: payload.timestamp,
        ...payload.data,
      });
      break;
      })
      break
    }
    case EBizCode.ControlSourceChange:
    case EBizCode.FlyToPointProgress:
    case EBizCode.TakeoffToPointProgress:
    case EBizCode.JoystickInvalidNotify:
    case EBizCode.DrcStatusNotify: {
      EventBus.emit('droneControlWs', payload);
      break;
      EventBus.emit('droneControlWs', payload)
      break
    }
    default:
      break;
      break
  }
};
}
const webSorketUrl = getWebsocketUrl() + '&workspace-id=' + props.deviceInfo.workspace_id;
const webSorketUrl = getWebsocketUrl() + '&workspace-id=' + props.deviceInfo.workspace_id
// 监听ws 消息
useConnectWebSocket(messageHandler, webSorketUrl);
useConnectWebSocket(messageHandler, webSorketUrl)
// 添加 changs 方法
async function changs(value, item) {
  const payload = {
    [item.payload_index]: {
      thermal_current_palette_style: value
    }
  }
  console.log('payload', payload)
  let data = await setThermalCurrentPaletteStyle(props.sn, props.deviceInfo.workspace_id, payload)
  if (data.data.code === 0) {
    ElMessage.success('修改成功');
  }
}
// 摄像头设置
async function cameraSettings(value, item) {
  const payload = {
    // 1581F6Q8D245U00G57GJ/81-0-0/normal-0
    video_id: props.deviceInfo.child_sn + '/' + item.payload_index + '/normal-0',
    video_type: value
  }
  let data = await setStreamsSwitch(payload)
  if (data.data.code === 0) {
    ElMessage.success('修改成功');
  }
}
// 相机模式设置
async function photoAndVideoCmdSettings(value, item) {
  // photo 0 video_start 1 video_stop
  item.camera_mode = value
  let data = await photoAndVideoCmd(props.deviceInfo.child_sn, value)
  if (data.data.code == 0) {
    ElMessage.success('修改成功');
  }
}
// 照片存储设置
async function photoStorageTypeChang(value, item) {
  // value 不能为空
  if (!value) {
    ElMessage.error('请选择存储方式');
    return;
  }
  const payload = {
    payload_index: item.payload_index,
    photo_storage_settings: value
  }
  item.photo_storage_settings = value
  let data = await setPhotoStorageSet(props.sn, payload)
  if (data.data.code == 0) {
    ElMessage.success('修改成功');
  }
}
// 视频存储设置
async function videoStorageTypeChang(value, item) {
  if (!value) {
    ElMessage.error('请选择存储方式');
    return;
  }
  const payload = {
    payload_index: item.payload_index,
    video_storage_settings: value
  }
  item.video_storage_settings = value
  let data = await setVideoStorageSet(props.sn, payload)
  if (data.data.code == 0) {
    ElMessage.success('修改成功');
  }
}
</script>
<style lang="scss" scoped>
@@ -227,7 +474,7 @@
      .debug-btn {
        margin-left: 10px;
        border: 1px solid #585858;
        // border: 1px solid #585858;
      }
    }
@@ -235,16 +482,18 @@
      display: flex;
      flex-wrap: wrap;
      justify-content: space-between;
      padding: 4px 10px;
      padding: 4px 15px;
      .control-cmd-item {
        width: 220px;
        width: 320px;
        height: 58px;
        display: flex;
        align-items: center;
        justify-content: space-between;
        border: 1px solid #666;
        margin: 4px 0;
        margin: 8px 0;
        padding: 0 8px;
        border-radius: 2px;
        .control-cmd-item-left {
          display: flex;
@@ -257,5 +506,30 @@
      }
    }
  }
  ::v-deep {
    .el-form {
      display: flex;
      flex-wrap: wrap;
      justify-content: space-between;
      padding: 4px 15px;
    }
    .el-form-item {
      width: 320px;
      height: 58px;
      display: flex;
      align-items: center;
      justify-content: space-between;
      border: 1px solid #666;
      margin: 8px 0;
      padding: 0 8px;
      border-radius: 2px;
    }
    .el-select__wrapper {
      width: 160px;
    }
  }
}
</style>
</style>