feat: 视频播放组件封装
This commit is contained in:
parent
86ff238df7
commit
8a3acd57ee
|
@ -1,6 +1,6 @@
|
||||||
import server from '@/utils/request';
|
import server from '@/utils/request';
|
||||||
import { LocalStore } from '@/utils/comm';
|
import { LocalStore } from '@/utils/comm';
|
||||||
import { TOKEN_KEY } from '@/utils/variable';
|
import { BASE_API_PATH, TOKEN_KEY } from '@/utils/variable';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
// 列表
|
// 列表
|
||||||
|
@ -19,7 +19,7 @@ export default {
|
||||||
// ========== 视频播放 ==========
|
// ========== 视频播放 ==========
|
||||||
// 开始直播
|
// 开始直播
|
||||||
ptzStart: (deviceId: string, channelId: string, type: string) =>
|
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`),
|
ptzStop: (deviceId: string, channelId: string) => server.post(`/media/device/${deviceId}/${channelId}/_ptz/STOP`),
|
||||||
|
|
|
@ -1,25 +1,36 @@
|
||||||
<!-- 视频播放 -->
|
<!-- 视频播放 -->
|
||||||
<template>
|
<template>
|
||||||
<vue3videoPlay
|
<vue3videoPlay v-bind="options" />
|
||||||
v-bind="options"
|
|
||||||
poster="https://cdn.jsdelivr.net/gh/xdlumia/files/video-play/ironMan.jpg"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import 'vue3-video-play/dist/style.css';
|
import 'vue3-video-play/dist/style.css';
|
||||||
import vue3videoPlay from 'vue3-video-play';
|
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({
|
const options = reactive({
|
||||||
|
...props,
|
||||||
width: '500px', //播放器高度
|
width: '500px', //播放器高度
|
||||||
height: '280px', //播放器高度
|
height: '280px', //播放器高度
|
||||||
color: '#409eff', //主题色
|
color: '#409eff', //主题色
|
||||||
title: '', //视频名称
|
title: '', //视频名称
|
||||||
src: 'https://cdn.jsdelivr.net/gh/xdlumia/files/video-play/IronMan.mp4', //视频源
|
// src: props.src,
|
||||||
|
// type: props.type,
|
||||||
muted: false, //静音
|
muted: false, //静音
|
||||||
webFullScreen: false,
|
webFullScreen: false,
|
||||||
speedRate: ['0.75', '1.0', '1.25', '1.5', '2.0'], //播放倍速
|
speedRate: ['0.75', '1.0', '1.25', '1.5', '2.0'], //播放倍速
|
||||||
autoPlay: false, //自动播放
|
autoPlay: true, //自动播放
|
||||||
loop: false, //循环播放
|
loop: false, //循环播放
|
||||||
mirror: false, //镜像画面
|
mirror: false, //镜像画面
|
||||||
ligthOff: false, //关灯模式
|
ligthOff: false, //关灯模式
|
||||||
|
@ -37,5 +48,3 @@ const options = reactive({
|
||||||
], //显示所有按钮,
|
], //显示所有按钮,
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped></style>
|
|
||||||
|
|
|
@ -2,16 +2,32 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="live-player-tools">
|
<div class="live-player-tools">
|
||||||
<div class="direction">
|
<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" />
|
<AIcon class="direction-icon" type="CaretUpOutlined" />
|
||||||
</div>
|
</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" />
|
<AIcon class="direction-icon" type="CaretRightOutlined" />
|
||||||
</div>
|
</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" />
|
<AIcon class="direction-icon" type="CaretLeftOutlined" />
|
||||||
</div>
|
</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" />
|
<AIcon class="direction-icon" type="CaretDownOutlined" />
|
||||||
</div>
|
</div>
|
||||||
<div class="direction-audio">
|
<div class="direction-audio">
|
||||||
|
@ -19,17 +35,31 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="zoom">
|
<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" />
|
<AIcon type="PlusOutlined" />
|
||||||
</div>
|
</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" />
|
<AIcon type="MinusOutlined" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</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>
|
<style lang="less" scoped>
|
||||||
@import './mediaTool.less';
|
@import './mediaTool.less';
|
||||||
|
|
|
@ -6,13 +6,14 @@
|
||||||
cancelText="取消"
|
cancelText="取消"
|
||||||
okText="确定"
|
okText="确定"
|
||||||
width="800px"
|
width="800px"
|
||||||
|
:maskClosable="false"
|
||||||
@ok="_vis = false"
|
@ok="_vis = false"
|
||||||
@cancel="_vis = false"
|
@cancel="_vis = false"
|
||||||
>
|
>
|
||||||
<div class="media-live">
|
<div class="media-live">
|
||||||
<div class="media-live-video">
|
<div class="media-live-video">
|
||||||
<div class="media-tool">
|
<div class="media-tool">
|
||||||
<div class="tool-item">
|
<div class="tool-item" @click.stop="handleRecord">
|
||||||
{{
|
{{
|
||||||
isRecord === 0
|
isRecord === 0
|
||||||
? '开始录像'
|
? '开始录像'
|
||||||
|
@ -22,17 +23,24 @@
|
||||||
}}
|
}}
|
||||||
</div>
|
</div>
|
||||||
<div class="tool-item">刷新</div>
|
<div class="tool-item">刷新</div>
|
||||||
<div class="tool-item">重置</div>
|
<div class="tool-item" @click.stop="handleReset">重置</div>
|
||||||
</div>
|
</div>
|
||||||
<LivePlayer :url="url" />
|
<LivePlayer :src="src" :type="mediaType" />
|
||||||
</div>
|
</div>
|
||||||
<MediaTool />
|
<MediaTool
|
||||||
|
@onMouseDown="handleMouseDown"
|
||||||
|
@onMouseUp="handleMouseUp"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="media-live-tool">
|
<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="mp4">MP4</a-radio-button>
|
||||||
<a-radio-button value="flv">FLV</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>
|
</a-radio-group>
|
||||||
</div>
|
</div>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
|
@ -42,6 +50,7 @@
|
||||||
import { PropType } from 'vue';
|
import { PropType } from 'vue';
|
||||||
import LivePlayer from '@/components/Player/index.vue';
|
import LivePlayer from '@/components/Player/index.vue';
|
||||||
import MediaTool from '@/components/Player/mediaTool.vue';
|
import MediaTool from '@/components/Player/mediaTool.vue';
|
||||||
|
import channelApi from '@/api/media/channel';
|
||||||
|
|
||||||
type Emits = {
|
type Emits = {
|
||||||
(e: 'update:visible', data: boolean): void;
|
(e: 'update:visible', data: boolean): void;
|
||||||
|
@ -61,14 +70,89 @@ const _vis = computed({
|
||||||
set: (val) => emit('update:visible', val),
|
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(
|
watch(
|
||||||
() => _vis.value,
|
() => _vis.value,
|
||||||
(val: boolean) => {},
|
(val: boolean) => {
|
||||||
|
if (val) {
|
||||||
|
mediaStart();
|
||||||
|
getIsRecord();
|
||||||
|
}
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const isRecord = ref(0);
|
|
||||||
const url = ref('');
|
|
||||||
const mediaType = ref('mp4');
|
|
||||||
</script>
|
</script>
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
@import './index.less';
|
@import './index.less';
|
||||||
|
|
|
@ -83,7 +83,7 @@
|
||||||
:channelData="channelData"
|
:channelData="channelData"
|
||||||
@submit="listRef.reload()"
|
@submit="listRef.reload()"
|
||||||
/>
|
/>
|
||||||
<Live v-model:visible="playerVis" />
|
<Live v-model:visible="playerVis" :data="channelData" />
|
||||||
</page-container>
|
</page-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -205,6 +205,7 @@ const getActions = (
|
||||||
},
|
},
|
||||||
icon: 'VideoCameraOutlined',
|
icon: 'VideoCameraOutlined',
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
|
channelData.value = cloneDeep(data);
|
||||||
playerVis.value = true;
|
playerVis.value = true;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue