视频播放操作逻辑优化
This commit is contained in:
BIN
apps/web-antd/src/assets/tree/player-err.png
Normal file
BIN
apps/web-antd/src/assets/tree/player-err.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
BIN
apps/web-antd/src/assets/tree/playering.png
Normal file
BIN
apps/web-antd/src/assets/tree/playering.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
BIN
apps/web-antd/src/assets/tree/unplayer.png
Normal file
BIN
apps/web-antd/src/assets/tree/unplayer.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
@@ -7,17 +7,20 @@ import type { TreeNode } from '#/api/common';
|
||||
|
||||
defineOptions({ inheritAttrs: false });
|
||||
|
||||
withDefaults(defineProps<{ showSearch?: boolean }>(), { showSearch: true });
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
showSearch?: boolean;
|
||||
currentSelectKey?: string;
|
||||
selectKeys?: string[];
|
||||
}>(),
|
||||
{
|
||||
showSearch: true,
|
||||
currentSelectKey: '',
|
||||
selectKeys: [],
|
||||
},
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
||||
checked: [];
|
||||
/**
|
||||
* 点击节点的事件
|
||||
*/
|
||||
reload: [];
|
||||
select: [];
|
||||
}>();
|
||||
const emit = defineEmits(['selected', 'reload', 'checked']);
|
||||
|
||||
const searchValue = defineModel('searchValue', {
|
||||
type: String,
|
||||
@@ -37,6 +40,10 @@ async function loadChannelTree() {
|
||||
showTreeSkeleton.value = false;
|
||||
}
|
||||
|
||||
function onSelect(key: any, selectNode: any) {
|
||||
emit('selected', key, selectNode);
|
||||
}
|
||||
|
||||
async function handleReload() {
|
||||
await loadChannelTree();
|
||||
emit('reload');
|
||||
@@ -46,21 +53,11 @@ onMounted(loadChannelTree);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="h-[800px]" :class="$attrs.class">
|
||||
<Skeleton
|
||||
:loading="showTreeSkeleton"
|
||||
:paragraph="{ rows: 8 }"
|
||||
active
|
||||
class="p-[8px]"
|
||||
>
|
||||
<div
|
||||
class="flex h-full flex-col overflow-y-auto rounded-lg"
|
||||
>
|
||||
<div :class="$attrs.class">
|
||||
<Skeleton :loading="showTreeSkeleton" :paragraph="{ rows: 8 }" active>
|
||||
<div class="h-full">
|
||||
<!-- 固定在顶部 必须加上bg-background背景色 否则会产生'穿透'效果 -->
|
||||
<divx
|
||||
v-if="showSearch"
|
||||
class="z-100 sticky left-0 top-0 p-[8px]"
|
||||
>
|
||||
<div v-if="showSearch" class="z-100 sticky left-0 top-0 p-[8px]">
|
||||
<InputSearch
|
||||
v-model:value="searchValue"
|
||||
:placeholder="$t('pages.common.search')"
|
||||
@@ -72,21 +69,30 @@ onMounted(loadChannelTree);
|
||||
</a-button>
|
||||
</template>
|
||||
</InputSearch>
|
||||
</divx>
|
||||
<div class="h-full overflow-x-hidden px-[8px]">
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="h-[calc(100%-40px)] overflow-y-auto overflow-x-hidden px-[8px]"
|
||||
>
|
||||
<Tree
|
||||
v-bind="$attrs"
|
||||
v-if="channelTree.length > 0"
|
||||
:class="$attrs.class"
|
||||
:show-line="{ showLeafIcon: false }"
|
||||
:tree-data="channelTree"
|
||||
:virtual="false"
|
||||
default-expand-all
|
||||
checkable
|
||||
@select="$emit('select')"
|
||||
@check="$emit('checked')"
|
||||
@select="onSelect"
|
||||
>
|
||||
<template #title="{ label }">
|
||||
<template #title="{ label, level, key }">
|
||||
<div class="flex">
|
||||
<div v-if="level == 2" class="tree-icon">
|
||||
<div
|
||||
v-if="selectKeys.indexOf(key) > -1"
|
||||
class="icon playing"
|
||||
></div>
|
||||
<div v-else class="icon unplay"></div>
|
||||
</div>
|
||||
<span :style="currentSelectKey == key?'color:blue':''">
|
||||
<span v-if="label.indexOf(searchValue) > -1">
|
||||
{{ label.substring(0, label.indexOf(searchValue)) }}
|
||||
<span style="color: #f50">{{ searchValue }}</span>
|
||||
@@ -96,7 +102,12 @@ onMounted(loadChannelTree);
|
||||
)
|
||||
}}
|
||||
</span>
|
||||
<span v-else>{{ label }}</span>
|
||||
|
||||
<span v-else>
|
||||
<span>{{ label }}</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</Tree>
|
||||
</div>
|
||||
@@ -104,3 +115,25 @@ onMounted(loadChannelTree);
|
||||
</Skeleton>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.tree-icon {
|
||||
display: inline-block;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
|
||||
.unplay {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: url('/src/assets/tree/unplayer.png') no-repeat;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
.playing {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: url('/src/assets/tree/playering.png') no-repeat;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -1,9 +1,15 @@
|
||||
<template>
|
||||
<Page class="h-full w-full">
|
||||
<Page style="height: calc(100vh - 88px)" class="video-page h-full w-full">
|
||||
<!-- 设备分组区域 -->
|
||||
<div class="flex h-full gap-[8px]">
|
||||
<div class="c-tree bg-background h-full pb-[5px]">
|
||||
<ChannelTree class="w-[300px]" @check="onNodeChecked" />
|
||||
<div class="c-tree bg-background h-full overflow-hidden pb-[5px]">
|
||||
<ChannelTree
|
||||
class="h-full w-[300px]"
|
||||
:currentSelectKey="currentSelectKey"
|
||||
:selectKeys="selectKeys"
|
||||
@check="onNodeChecked"
|
||||
@select="onTreeSelect"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 设备分组区域 -->
|
||||
@@ -79,14 +85,17 @@ const playerStyle = ref({
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
});
|
||||
const currentSelectPlayerIndex = ref(-1);
|
||||
|
||||
const currentSelectPlayerIndex = ref(1);
|
||||
|
||||
function playerSelect(index: number) {
|
||||
if (index === currentSelectPlayerIndex.value) {
|
||||
currentSelectPlayerIndex.value = -1;
|
||||
return;
|
||||
}
|
||||
currentSelectPlayerIndex.value = index;
|
||||
const player = playerList[index - 1];
|
||||
if (player) {
|
||||
currentSelectKey.value = player.key;
|
||||
} else {
|
||||
currentSelectKey.value = '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -170,6 +179,48 @@ function handleParentNoe(node: any, newNode: any[] = []) {
|
||||
|
||||
// 播放器数据, 每一个位置代表页面上行的一个矩形
|
||||
const playerList: any[] = [];
|
||||
// 当前选择播放器的key
|
||||
const currentSelectKey = ref('');
|
||||
// 当前所有的播放设备key
|
||||
const selectKeys = ref<string[]>([]);
|
||||
|
||||
function onTreeSelect(_key: any, selectNode: any) {
|
||||
const {
|
||||
selected,
|
||||
node: { level, data },
|
||||
} = selectNode;
|
||||
// 代表点击的是摄像头
|
||||
if (level === 2) {
|
||||
// 播放
|
||||
if (selected) {
|
||||
doPlayer(data, currentSelectPlayerIndex.value - 1);
|
||||
}
|
||||
// 取消播放
|
||||
else {
|
||||
for (let i = 0; i < playerNum.value; i++) {
|
||||
const player = playerList[i];
|
||||
if (player && player.data.id === data.id) {
|
||||
closePlayer(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
message.error('请选择摄像头');
|
||||
}
|
||||
}
|
||||
|
||||
function treeSelectHandle() {
|
||||
// 此处player可能已经释放所以不可能在取到只
|
||||
const player = playerList[currentSelectPlayerIndex.value - 1];
|
||||
currentSelectKey.value = player ? player.key : '';
|
||||
const arr: string[] = [];
|
||||
playerList.forEach((item: any) => {
|
||||
if (item && item.key) {
|
||||
arr.push(item.key);
|
||||
}
|
||||
});
|
||||
selectKeys.value = arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 节点选中时间处理
|
||||
@@ -302,13 +353,15 @@ function streamProxy(nodeData: any, cb: Function) {
|
||||
* 开始播放视频流
|
||||
* @param nodeData 播放的节点数据
|
||||
* @param index 播放器的索引信息
|
||||
* @param callback 视频播放后的回调
|
||||
*/
|
||||
function doPlayer(nodeData: any, index: number = 0) {
|
||||
console.log('index=', index);
|
||||
if (mpegts.isSupported()) {
|
||||
streamProxy(nodeData, (res: AddStreamProxyResult) => {
|
||||
const host = window.location.host;
|
||||
const url = `ws://${host}/${res.app}/${res.streamId}.live.flv`;
|
||||
// const host = window.location.host;
|
||||
// const url = `ws://${host}/${res.app}/${res.streamId}.live.flv`;
|
||||
const url = `ws://183.230.235.66:11010/${res.app}/${res.streamId}.live.flv`;
|
||||
// 将url 绑定到 nodeData
|
||||
nodeData.url = url;
|
||||
closePlayer(index);
|
||||
@@ -338,6 +391,8 @@ function doPlayer(nodeData: any, index: number = 0) {
|
||||
key: nodeData.id,
|
||||
data: nodeData,
|
||||
};
|
||||
// 播放完成后, 需要处理树组件的状态
|
||||
treeSelectHandle();
|
||||
} else {
|
||||
console.log('视频播放元素获取异常');
|
||||
}
|
||||
@@ -353,6 +408,7 @@ function closePlayVieo(plInfo: any) {
|
||||
plInfo.pause(); // 暂停
|
||||
plInfo.unload(); // 卸载
|
||||
plInfo.destroy(); // 销毁
|
||||
treeSelectHandle();
|
||||
} catch (e) {
|
||||
console.log('播放器关闭失败,e=', e);
|
||||
}
|
||||
@@ -369,6 +425,7 @@ function closePlayer(index: number) {
|
||||
player.unload(); // 卸载
|
||||
player.destroy(); // 销毁
|
||||
playerList[index] = null;
|
||||
treeSelectHandle();
|
||||
} catch (e) {
|
||||
console.log('播放器关闭失败,e=', e);
|
||||
}
|
||||
@@ -415,6 +472,10 @@ onUnmounted(() => {
|
||||
}
|
||||
}
|
||||
|
||||
.c-tree {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.player.selected {
|
||||
border: 2px solid deepskyblue;
|
||||
}
|
||||
|
Reference in New Issue
Block a user