iot-ui-vue/src/components/Player/ScreenPlayer.vue

423 lines
14 KiB
Vue

<!-- 分屏组件 -->
<template>
<div class="live-player-warp">
<div class="live-player-content">
<!-- 工具栏 -->
<div class="player-screen-tool" v-if="showScreen">
<a-radio-group
v-model:value="screen"
button-style="solid"
@change="handleScreenChange"
>
<a-radio-button :value="1">单屏</a-radio-button>
<a-radio-button :value="4">四分屏</a-radio-button>
<a-radio-button :value="9">九分屏</a-radio-button>
<a-radio-button :value="0">全屏</a-radio-button>
</a-radio-group>
<div class="screen-tool-save">
<a-tooltip title="可保存分屏配置记录">
<AIcon type="QuestionCircleOutlined" />
</a-tooltip>
<a-popover
v-model:visible="visible"
trigger="click"
title="分屏名称"
>
<template #content>
<a-form
ref="formRef"
:model="formData"
layout="vertical"
>
<a-form-item
name="name"
:rules="[
{
required: true,
message: '请输入名称',
},
{
max: 64,
message: '最多可输入64个字符',
},
]"
>
<a-textarea v-model:value="formData.name" />
</a-form-item>
<a-button
type="primary"
@click="saveHistory"
:loading="loading"
style="width: 100%; margin-top: 16px"
>
保存
</a-button>
</a-form>
</template>
<a-dropdown-button
type="primary"
@click="visible = true"
>
保存
<template #overlay>
<a-menu>
<a-empty
v-if="!historyList.length"
description="暂无数据"
/>
<a-menu-item
v-for="(item, index) in historyList"
:key="`his${index}`"
@click="handleHistory(item)"
>
<a-space>
<span>{{ item.name }}</span>
<a-popconfirm
title="确认删除?"
ok-text="确认"
cancel-text="取消"
@confirm="(e: any) => {
e?.stopPropagation();
deleteHistory(item.key);
}
"
>
<AIcon
type="DeleteOutlined"
@click="
(e:any) =>
e?.stopPropagation()
"
/>
</a-popconfirm>
</a-space>
</a-menu-item>
</a-menu>
</template>
</a-dropdown-button>
</a-popover>
</div>
</div>
<!-- 播放器 -->
<div class="player-body">
<div
ref="fullscreenRef"
class="player-screen"
:class="`screen-${screen}`"
>
<template v-for="(item, index) in players" :key="item.key">
<div
class="player-screen-item"
:class="{
active:
showScreen &&
playerActive === index &&
!isFullscreen,
'full-screen': isFullscreen,
}"
:style="{ display: item.show ? 'block' : 'none' }"
@click="playerActive = index"
>
<div
class="media-btn-refresh"
:style="{
display: item.url ? 'block' : 'none',
}"
@click="handleRefresh($event, item, index)"
>
刷新
</div>
<LivePlayer
:src="item.url"
:width="screenWidth"
:height="screenHeight"
/>
</div>
</template>
</div>
</div>
<!-- 控制器 -->
</div>
<MediaTool @onMouseDown="handleMouseDown" @onMouseUp="handleMouseUp" />
</div>
</template>
<script setup lang="ts">
import { useFullscreen } from '@vueuse/core';
import {
deleteSearchHistory,
getSearchHistory,
saveSearchHistory,
} from '@/api/comm';
import { message } from 'ant-design-vue';
import LivePlayer from '@/components/Player/index.vue';
import MediaTool from '@/components/Player/mediaTool.vue';
type Player = {
id?: string;
url?: string;
channelId?: string;
key: string;
show: boolean;
};
interface ScreenProps {
url?: string;
id?: string;
channelId: string;
className?: string;
historyHandle?: (deviceId: string, channelId: string) => string;
/**
*
* @param id 当前选中播发视频ID
* @param type 当前操作动作
*/
onMouseDown?: (deviceId: string, channelId: string, type: string) => void;
/**
*
* @param id 当前选中播发视频ID
* @param type 当前操作动作
*/
onMouseUp?: (deviceId: string, channelId: string, type: string) => void;
showScreen?: boolean;
}
const props = defineProps<ScreenProps>();
const DEFAULT_SAVE_CODE = 'screen-save';
const screen = ref(1);
const players = ref<Player[]>([]);
const playerActive = ref(0);
const historyList = ref<any[]>([]);
const visible = ref(false);
const loading = ref(false);
const fullscreenRef = ref(null);
const screenWidth = ref('');
const screenHeight = ref('');
const { isFullscreen, enter, exit, toggle } = useFullscreen(
fullscreenRef.value,
);
const formRef = ref();
const formData = ref({
name: '',
});
const reloadPlayer = (
id: string,
channelId: string,
url: string,
index: number,
) => {
const olPlayers = [...players.value];
olPlayers[index] = {
id: '',
channelId: '',
url: '',
key: olPlayers[index].key,
show: true,
};
const newPlayer = {
id,
url,
channelId,
key: olPlayers[index].key,
show: true,
};
players.value = [...olPlayers];
setTimeout(() => {
olPlayers[index] = newPlayer;
players.value = [...olPlayers];
}, 1000);
};
const replaceVideo = (id: string, channelId: string, url: string) => {
const olPlayers = [...players.value];
const newPlayer = {
id,
url,
channelId,
key: olPlayers[playerActive.value].key,
show: true,
};
if (olPlayers[playerActive.value].url === url) {
// 刷新视频
reloadPlayer(id, channelId, url, playerActive.value);
} else {
olPlayers[playerActive.value] = newPlayer;
players.value = olPlayers;
}
if (playerActive.value === screen.value - 1) {
// 当前位置为分屏最后一位
playerActive.value = 0;
} else {
playerActive.value += 1;
}
};
const handleHistory = (item: any) => {
if (props.historyHandle) {
const log = JSON.parse(item.content || '{}');
screen.value = log.screen;
const oldPlayers = [...players.value];
players.value = oldPlayers.map((oldPlayer, index) => {
oldPlayer.show = false;
if (index < log.screen) {
const { deviceId, channelId } = log.players[index];
return {
...oldPlayer,
id: deviceId,
channelId: deviceId,
url: deviceId
? props.historyHandle!(deviceId, channelId)
: '',
show: true,
};
}
return oldPlayer;
});
}
};
const getHistory = async () => {
const res = await getSearchHistory(DEFAULT_SAVE_CODE);
if (res.success) {
historyList.value = res.result;
}
};
const deleteHistory = async (id: string) => {
const res = await deleteSearchHistory(DEFAULT_SAVE_CODE, id);
if (res.success) {
getHistory();
visible.value = false;
}
};
const saveHistory = async () => {
formRef.value
.validate()
.then(async () => {
const param = {
name: formData.value.name,
content: JSON.stringify({
screen: screen.value,
players: players.value.map((item: any) => ({
deviceId: item.id,
channelId: item.channelId,
})),
}),
};
loading.value = true;
const res = await saveSearchHistory(param, DEFAULT_SAVE_CODE);
loading.value = false;
if (res.success) {
visible.value = false;
getHistory();
message.success('保存成功');
formRef.value.resetFields();
} else {
message.error('保存失败');
}
})
.catch((err: any) => {
console.log(err);
});
};
const mediaInit = () => {
const newArr = [];
for (let i = 0; i < 9; i++) {
newArr.push({
id: '',
channelId: '',
url: '',
key: 'time_' + new Date().getTime() + i,
show: i === 0,
});
}
players.value = newArr;
};
const handleScreenChange = (e: any) => {
if (e.target.value) {
screenChange(e.target.value);
} else {
// 全屏操作
toggle();
}
};
const screenChange = (index: number) => {
players.value = players.value.map((m: any, i: number) => ({
id: '',
channelId: '',
url: '',
updateTime: 0,
key: m.key,
show: i < index,
}));
playerActive.value = 0;
screen.value = index;
// if (screen.value === 4) {
// screenWidth.value = '350px';
// screenHeight.value = '2000px';
// }
};
const handleRefresh = (e: any, item: any, index: number) => {
e.stopPropagation();
if (item.url) {
reloadPlayer(item.id!, item.channelId!, item.url!, index);
}
};
/**
* 点击控制按钮
* @param type 控制类型
*/
const handleMouseDown = (type: string) => {
const { id, channelId } = players.value[playerActive.value];
console.log('players.value: ', players.value);
console.log('playerActive.value: ', playerActive.value);
console.log('id: ', id);
console.log('channelId: ', channelId);
if (id && channelId && props.onMouseDown) {
props.onMouseDown(id, channelId, type);
}
};
const handleMouseUp = (type: string) => {
const { id, channelId } = players.value[playerActive.value];
if (id && channelId && props.onMouseUp) {
props.onMouseUp(id, channelId, type);
}
};
watch(
() => props.url,
(val) => {
if (val && props.id) {
replaceVideo(props.id, props.channelId, val);
}
},
);
watchEffect(() => {
if (props.showScreen !== false) {
getHistory();
}
mediaInit();
});
defineExpose({
replaceVideo,
});
</script>
<style lang="less" scoped>
@import './index.less';
</style>