feat: 视频播放组件封装

This commit is contained in:
JiangQiming 2023-03-02 20:23:21 +08:00
parent 86ff238df7
commit 8a3acd57ee
5 changed files with 153 additions and 29 deletions

View File

@ -1,6 +1,6 @@
import server from '@/utils/request';
import { LocalStore } from '@/utils/comm';
import { TOKEN_KEY } from '@/utils/variable';
import { BASE_API_PATH, TOKEN_KEY } from '@/utils/variable';
export default {
// 列表
@ -19,7 +19,7 @@ export default {
// ========== 视频播放 ==========
// 开始直播
ptzStart: (deviceId: string, channelId: string, type: string) =>
`/media/device/${deviceId}/${channelId}/live.${type}?:X_Access_Token=${LocalStore.get(TOKEN_KEY)}`,
`${BASE_API_PATH}/media/device/${deviceId}/${channelId}/live.${type}?:X_Access_Token=${LocalStore.get(TOKEN_KEY)}`,
// 云台控制-停止
ptzStop: (deviceId: string, channelId: string) => server.post(`/media/device/${deviceId}/${channelId}/_ptz/STOP`),

View File

@ -1,25 +1,36 @@
<!-- 视频播放 -->
<template>
<vue3videoPlay
v-bind="options"
poster="https://cdn.jsdelivr.net/gh/xdlumia/files/video-play/ironMan.jpg"
/>
<vue3videoPlay v-bind="options" />
</template>
<script setup lang="ts">
import 'vue3-video-play/dist/style.css';
import vue3videoPlay from 'vue3-video-play';
const props = defineProps({
src: { type: String, default: '' },
type: { type: String, default: 'mp4' },
});
watch(
() => props.src,
(val: string) => {
options.src = val;
},
);
const options = reactive({
...props,
width: '500px', //
height: '280px', //
color: '#409eff', //
title: '', //
src: 'https://cdn.jsdelivr.net/gh/xdlumia/files/video-play/IronMan.mp4', //
// src: props.src,
// type: props.type,
muted: false, //
webFullScreen: false,
speedRate: ['0.75', '1.0', '1.25', '1.5', '2.0'], //
autoPlay: false, //
autoPlay: true, //
loop: false, //
mirror: false, //
ligthOff: false, //
@ -37,5 +48,3 @@ const options = reactive({
], //,
});
</script>
<style lang="less" scoped></style>

View File

@ -2,16 +2,32 @@
<template>
<div class="live-player-tools">
<div class="direction">
<div class="direction-item up">
<div
class="direction-item up"
@mousedown="emit('onMouseDown', 'UP')"
@mouseup="emit('onMouseUp', 'UP')"
>
<AIcon class="direction-icon" type="CaretUpOutlined" />
</div>
<div class="direction-item right">
<div
class="direction-item right"
@mousedown="emit('onMouseDown', 'RIGHT')"
@mouseup="emit('onMouseUp', 'RIGHT')"
>
<AIcon class="direction-icon" type="CaretRightOutlined" />
</div>
<div class="direction-item left">
<div
class="direction-item left"
@mousedown="emit('onMouseDown', 'LEFT')"
@mouseup="emit('onMouseUp', 'LEFT')"
>
<AIcon class="direction-icon" type="CaretLeftOutlined" />
</div>
<div class="direction-item down">
<div
class="direction-item down"
@mousedown="emit('onMouseDown', 'DOWN')"
@mouseup="emit('onMouseUp', 'DOWN')"
>
<AIcon class="direction-icon" type="CaretDownOutlined" />
</div>
<div class="direction-audio">
@ -19,17 +35,31 @@
</div>
</div>
<div class="zoom">
<div class="zoom-item zoom-in">
<div
class="zoom-item zoom-in"
@mousedown="emit('onMouseDown', 'ZOOM_IN')"
@mouseup="emit('onMouseUp', 'ZOOM_IN')"
>
<AIcon type="PlusOutlined" />
</div>
<div class="zoom-item zoom-out">
<div
class="zoom-item zoom-out"
@mousedown="emit('onMouseDown', 'ZOOM_OUT')"
@mouseup="emit('onMouseUp', 'ZOOM_OUT')"
>
<AIcon type="MinusOutlined" />
</div>
</div>
</div>
</template>
<script setup lang="ts"></script>
<script setup lang="ts">
type Emits = {
(e: 'onMouseDown', type: string): void;
(e: 'onMouseUp', type: string): void;
};
const emit = defineEmits<Emits>();
</script>
<style lang="less" scoped>
@import './mediaTool.less';

View File

@ -6,13 +6,14 @@
cancelText="取消"
okText="确定"
width="800px"
:maskClosable="false"
@ok="_vis = false"
@cancel="_vis = false"
>
<div class="media-live">
<div class="media-live-video">
<div class="media-tool">
<div class="tool-item">
<div class="tool-item" @click.stop="handleRecord">
{{
isRecord === 0
? '开始录像'
@ -22,17 +23,24 @@
}}
</div>
<div class="tool-item">刷新</div>
<div class="tool-item">重置</div>
<div class="tool-item" @click.stop="handleReset">重置</div>
</div>
<LivePlayer :url="url" />
<LivePlayer :src="src" :type="mediaType" />
</div>
<MediaTool />
<MediaTool
@onMouseDown="handleMouseDown"
@onMouseUp="handleMouseUp"
/>
</div>
<div class="media-live-tool">
<a-radio-group v-model:value="mediaType" button-style="solid">
<a-radio-group
v-model:value="mediaType"
button-style="solid"
@change="mediaStart"
>
<a-radio-button value="mp4">MP4</a-radio-button>
<a-radio-button value="flv">FLV</a-radio-button>
<a-radio-button value="hls">HLS</a-radio-button>
<a-radio-button value="m3u8">HLS</a-radio-button>
</a-radio-group>
</div>
</a-modal>
@ -42,6 +50,7 @@
import { PropType } from 'vue';
import LivePlayer from '@/components/Player/index.vue';
import MediaTool from '@/components/Player/mediaTool.vue';
import channelApi from '@/api/media/channel';
type Emits = {
(e: 'update:visible', data: boolean): void;
@ -61,14 +70,89 @@ const _vis = computed({
set: (val) => emit('update:visible', val),
});
//
const src = ref('');
//
const mediaType = ref('mp4');
/**
* 媒体开始播放
*/
const mediaStart = () => {
src.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 handleRecord = async () => {
if (isRecord.value === 0) {
isRecord.value = 1;
const res = await channelApi.recordStart(
props.data.deviceId,
props.data.channelId,
{ local: false },
);
if (res.success) {
isRecord.value = 2;
} else {
isRecord.value = 0;
}
} else if (isRecord.value === 2) {
const res = await channelApi.recordStop(
props.data.deviceId,
props.data.channelId,
{ local: false },
);
if (res.success) {
isRecord.value = 0;
}
}
};
/**
* 重置
*/
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);
};
const handleMouseUp = () => {
channelApi.ptzStop(props.data.deviceId, props.data.channelId);
};
watch(
() => _vis.value,
(val: boolean) => {},
(val: boolean) => {
if (val) {
mediaStart();
getIsRecord();
}
},
);
const isRecord = ref(0);
const url = ref('');
const mediaType = ref('mp4');
</script>
<style lang="less" scoped>
@import './index.less';

View File

@ -83,7 +83,7 @@
:channelData="channelData"
@submit="listRef.reload()"
/>
<Live v-model:visible="playerVis" />
<Live v-model:visible="playerVis" :data="channelData" />
</page-container>
</template>
@ -205,6 +205,7 @@ const getActions = (
},
icon: 'VideoCameraOutlined',
onClick: () => {
channelData.value = cloneDeep(data);
playerVis.value = true;
},
},