| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707 |
- <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">
- <div v-if="deviceInfo.device">
- {{ getTextByModeCode(deviceInfo.device.mode_code) }}
- </div>
- <div v-else>
- 当前正常
- </div>
- </div>
- </div>
- <div class="content-osd-head-right-bottom">
- <div class="content-osd-head-right-bottom-button">
- <span class="openLiveButton" @click="state.deviceLiveVisible = true" v-if="!state.deviceLiveVisible">
- 开启直播
- </span>
- <span class="openLiveButton" @click="state.deviceLiveVisible = 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.deviceLiveVisible">
- <a-select style="width: 125px;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: 125px;margin-right: 5px;" placeholder="清晰度" v-model:value="state.clarityValue">
- <a-select-option v-for="item in clarityList" :key="item.value" :value="item.value">
- {{ item.label }}
- </a-select-option>
- </a-select>
- <a-tooltip title="播放">
- <a-button style="margin-right: 5px;" :icon="h(PlaySquareOutlined)" @click="onStartLive" />
- </a-tooltip>
- <a-tooltip title="停止">
- <a-button :icon="h(PoweroffOutlined)" @click="onStopLive" />
- </a-tooltip>
- </div>
- </div>
- </div>
- </div>
- <LivePlayer :text="state.playerText" :url="state.playerUrl" v-if="state.deviceLiveVisible" />
- <div class="battery-slide">
- <div style="background: #535759;" class="width-100"></div>
- <div class="capacity-percent" :style="{ width: capacity + '%' }"></div>
- </div>
- <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;">
- {{ windDirection }}
- </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 '../icons/info/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 { getLiveCapacity, startLivestream, stopLivestream } from '/@/api/manage';
- import { getTextByModeCode, getWindDirection } from '/@/utils/index'
- interface Props {
- osdInfo: any
- deviceInfo: any
- };
- const props = withDefaults(defineProps<Props>(), {
- });
- // 遥控器信号
- const controllerSignal = computed(() => {
- const info = props.deviceInfo?.gateway;
- if (info?.wireless_link) {
- return info.wireless_link['4g_gnd_quality'];
- } else {
- return 0;
- }
- });
- // 飞机信号
- const aircraftSignal = computed(() => {
- const info = props.deviceInfo?.gateway;
- if (info?.wireless_link) {
- return info.wireless_link['4g_uav_quality'];
- } else {
- return 0;
- }
- });
- const RTK = computed(() => {
- const info = props.deviceInfo?.device;
- if (info?.position_state) {
- return info.position_state.rtk_number;
- } else {
- return 0;
- }
- });
- const GPS = computed(() => {
- const info = props.deviceInfo?.device;
- if (info?.position_state) {
- return info.position_state.gps_number;
- } else {
- return 0;
- }
- });
- // 电池容量
- const capacity = computed(() => {
- const info = props.deviceInfo?.device;
- if (info?.battery) {
- return info.battery.capacity_percent;
- } else {
- return 0;
- }
- });
- const windDirection = computed(() => {
- const info = props.deviceInfo?.device;
- if (info?.wind_direction) {
- return getWindDirection(info.wind_direction);
- } else {
- return '';
- }
- });
- const windSpeed = computed(() => {
- const info = props.deviceInfo?.device;
- if (info?.wind_speed) {
- if (info.wind_speed === '--') {
- return info.wind_speed;
- } else {
- return info.wind_speed.toFixed(2) + ' m/s';
- }
- } else {
- return 0 + ' m/s';
- }
- });
- const ASL = computed(() => {
- const info = props.deviceInfo?.device;
- if (info?.height) {
- if (info.height === '--') {
- return info.height;
- } else {
- return info.height.toFixed(2) + ' m';
- }
- } else {
- return 0 + ' m';
- }
- });
- const AGL = computed(() => {
- const info = props.deviceInfo?.device;
- if (info?.elevation) {
- if (info.elevation === '--') {
- return info.elevation;
- } else {
- return info.elevation.toFixed(2) + ' m';
- }
- } else {
- return 0 + ' m';
- }
- });
- const HS = computed(() => {
- const info = props.deviceInfo?.device;
- if (info?.horizontal_speed) {
- if (info.horizontal_speed === '--') {
- return info.horizontal_speed;
- } else {
- return info.horizontal_speed.toFixed(2) + ' m/s';
- }
- } else {
- return 0 + ' m/s';
- }
- });
- const homeDistance = computed(() => {
- const info = props.deviceInfo?.device;
- if (info?.home_distance) {
- if (info.home_distance === '--') {
- return info.home_distance;
- } else {
- return info.home_distance.toFixed(2) + ' m';
- }
- } else {
- return 0 + ' 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 {
- deviceLiveVisible: boolean,
- cameraList: SelectOption[],
- cameraValue?: string,
- videoList: SelectOption[],
- videoValue?: string,
- clarityValue: number,
- videoId: string,
- playerText: string,
- playerUrl: string,
- }
- const state: State = reactive({
- deviceLiveVisible: 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 onStartLive = async () => {
- const { cameraValue, videoValue } = state;
- if (!cameraValue) {
- return message.warn('请选择摄像头');
- }
- const videoId = `${props.osdInfo.sn}/${cameraValue}/${videoValue || 'normal-0'}`;
- state.videoId = videoId;
- try {
- const res = await startLivestream({
- protocol: location.protocol.slice(0, -1),
- 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 onStopLive = 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;
- background-color: #232323;
- position: absolute;
- left: 10px;
- top: 10px;
- &-title {
- width: 100%;
- height: 40px;
- 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;
- margin-bottom: 5px
- }
- }
- &-right {
- flex: 1;
- 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;
- white-space: nowrap;
- overflow: hidden;
- }
- }
- &-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;
- }
- }
- }
- }
- }
- .battery-slide {
- width: 100%;
- .capacity-percent {
- background: #00ee8b;
- }
- .return-home {
- background: #ff9f0a;
- }
- .landing {
- background: #f5222d;
- }
- .battery {
- background: white;
- border-radius: 1px;
- width: 8px;
- height: 4px;
- margin-top: -3px;
- }
- }
- .battery-slide>div {
- position: relative;
- margin-top: -2px;
- min-height: 2px;
- border-radius: 2px;
- white-space: nowrap;
- }
- </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>
|