iot-ui-vue/src/views/media/Device/Channel/Live/index.vue

354 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!-- 视频设备 - 播放 -->
<template>
<j-modal
v-model:visible="_vis"
title="播放"
:width="type === 'share'? '100%' : _type ? 1200 : 900"
:class="{share: type === 'share'}"
:maskClosable="false"
@ok="_vis = false"
:destroyOnClose="true"
>
<template #closeIcon>
<j-button :disabled="type === 'share'" type="text"><AIcon type="CloseOutlined" /></j-button>
</template>
<div class="media-live-tool">
<j-radio-group
v-model:value="mediaType"
button-style="solid"
@change="mediaStart"
>
<j-radio-button value="mp4">MP4</j-radio-button>
<j-radio-button value="flv">FLV</j-radio-button>
<j-radio-button value="m3u8">HLS</j-radio-button>
<!-- <j-radio-button value='rtc'>RTC</j-radio-button> -->
</j-radio-group>
<div class="media-live-share" v-if="type !== 'share'">
<j-button type="link" @click="onShare"
><AIcon type="ShareAltOutlined" />分享视频</j-button
>
</div>
</div>
<div class="media-live" @mouseenter="visibleChange" @mouseleave="mouseleave">
<div class="media-live-video">
<div
:class="mediaToolClass"
@mouseenter="showTool = true"
>
<div class="tool-item" v-if="type !== 'share'">
<template v-if="isRecord === 0">
<j-dropdown
trigger="click"
@click="showToolLock = true"
>
<div>开始录像</div>
<template #overlay>
<j-menu @click="recordStart">
<j-menu-item
key="true"
v-if="_type"
>
<span style="padding-right: 12px"
>本地存储</span
>
<j-tooltip title="存储在设备本地">
<a-icon
type="QuestionCircleOutlined"
/>
</j-tooltip>
</j-menu-item>
<j-menu-item key="false">
<span style="padding-right: 12px"
>云端存储</span
>
<j-tooltip title="存储在服务器中">
<a-icon
type="QuestionCircleOutlined"
/>
</j-tooltip>
</j-menu-item>
</j-menu>
</template>
</j-dropdown>
</template>
<div v-else-if="isRecord === 1">请求录像中</div>
<div
v-else-if="isRecord === 2"
@click.stop="recordStop"
>
停止录像
</div>
</div>
<div class="tool-item" @click.stop="handleRefresh">
刷新
</div>
<div class="tool-item">
<j-popconfirm
title="重置将断开直播, 可能会影响其他播放者"
@confirm="handleReset"
>
重置
</j-popconfirm>
</div>
</div>
<LivePlayer
ref="player"
:live="true"
:url="url"
:protocol="mediaType"
autoplay
/>
</div>
<div class="media-live-actions" v-if="_type">
<div class="actions-tool">
<MediaTool
@onMouseDown="handleMouseDown"
@onMouseUp="handleMouseUp"
>
<template #center>
<div class="center">
<div>转速控制</div>
<j-dropdown>
<span
>{{ _speed }}<AIcon type="DownOutlined"
/></span>
<template #overlay>
<j-menu @click="onMenuChange">
<j-menu-item
:key="item.value"
v-for="item in speedList"
>
{{ item.label }}
</j-menu-item>
</j-menu>
</template>
</j-dropdown>
</div>
</template>
</MediaTool>
</div>
<Preset v-if='data.ptzType.value ===0 || data.ptzType.value === 1' :data="data" @refresh="onRefresh" />
</div>
</div>
<template #footer>
<j-space v-if="type !== 'share'">
<j-button @click="_vis = false">取消</j-button>
<j-button @click="_vis = false" type="primary">确定</j-button>
</j-space>
</template>
</j-modal>
<Share v-if="visible" :data="data" @close="visible = false" />
</template>
<script setup lang="ts">
import { PropType } from 'vue';
import LivePlayer from '@/components/Player/index.vue';
import MediaTool from '@/components/Player/mediaTool.vue';
import channelApi from '@/api/media/channel';
import Share from './Share.vue';
import Preset from './Preset.vue';
type Emits = {
(e: 'update:visible', data: boolean): void;
(e: 'refresh'): void;
};
const emit = defineEmits<Emits>();
const props = defineProps({
visible: { type: Boolean, default: false },
data: {
type: Object as PropType<Partial<Record<string, any>>>,
default: () => ({}),
},
type: {
type: String as PropType<'share' | 'normal'>,
default: 'normal',
},
});
const route = useRoute();
const _vis = computed({
get: () => props.visible,
set: (val) => emit('update:visible', val),
});
// 播放器
const player = ref();
// 视频地址
const url = ref('');
// 视频类型
const mediaType = ref<'mp4' | 'flv' | 'hls' | 'rtc'>('mp4');
const showTool = ref(false);
const showToolLock = ref(false);
const visible = ref(false);
const _type = computed(() => {
return route.query.type !== 'fixed-media'
})
const speedList = [
{ label: '高', value: 180 },
{ label: '中', value: 90 },
{ label: '低', value: 45 },
];
const speed = ref(90);
const _speed = computed(() => {
return speedList.find((item) => item.value === speed.value)?.label;
});
const onMenuChange = (val: any) => {
speed.value = val.key;
};
const mouseleave = () => {
if (!showToolLock.value) {
showTool.value = false;
}
};
const visibleChange = () => {
showTool.value = true;
};
const getPopupContainer = (trigger: HTMLElement) => {
return trigger?.parentNode || document.body;
};
const mediaToolClass = computed(() => {
return {
'media-tool': true,
'media-tool-show': showTool.value,
};
});
/**
* 媒体开始播放
*/
const mediaStart = () => {
url.value = channelApi.ptzStart(
props.data.deviceId,
props.data.channelId,
mediaType.value,
);
};
// 录像状态
const isRecord = ref(0); // 0停止录像 1请求录像中 2开始录像
/**
* 查询录像状态
*/
const getIsRecord = async () => {
const { result } = await channelApi.ptzIsRecord(
props.data.deviceId,
props.data.channelId,
);
isRecord.value = result ? 2 : 0;
};
/**
* 开始录像
*/
const recordStart = async ({ key }: { key: string }) => {
showToolLock.value = false;
showTool.value = false;
isRecord.value = 1;
const local = key === 'true';
const res = await channelApi
.recordStart(props.data.deviceId, props.data.channelId, { local })
.catch(() => ({ success: false }));
if (res.success) {
isRecord.value = 2;
} else {
isRecord.value = 0;
}
};
/**
* 停止录像
*/
const recordStop = async () => {
const res = await channelApi.recordStop(
props.data.deviceId,
props.data.channelId,
);
if (res.success) {
isRecord.value = 0;
}
};
/**
* 刷新
*/
const handleRefresh = () => {
// player.value.play();
url.value = '';
setTimeout(() => {
mediaStart();
}, 500);
};
/**
* 重置
*/
const handleReset = async () => {
channelApi.mediaStop(props.data.deviceId, props.data.channelId);
};
/**
* 点击控制按钮
* @param type 控制类型
*/
const handleMouseDown = (type: string) => {
channelApi.ptzTool(props.data.deviceId, props.data.channelId, type, speed.value);
};
const handleMouseUp = () => {
channelApi.ptzStop(props.data.deviceId, props.data.channelId);
};
/**
* 分享视频
*/
const onShare = () => {
visible.value = true;
};
const onRefresh = () => {
emit('refresh')
}
watch(
() => _vis.value,
(val: boolean) => {
if (val) {
mediaStart();
getIsRecord();
} else {
// url置空, 即销毁播放器
url.value = '';
}
},
{
immediate: true
}
);
</script>
<style lang="less" scoped>
@import './index.less';
:deep(.live-player-stretch-btn) {
display: none;
}
:deep(.vjs-icon-spinner) {
display: none;
}
.center {
display: flex;
flex-direction: column;
align-items: center;
}
</style>