parent
562223fec4
commit
43a49e95fb
|
@ -67,4 +67,7 @@ export default {
|
||||||
|
|
||||||
// 播放云端回放
|
// 播放云端回放
|
||||||
playbackStart: (recordId: string) => server.get(`/media/record/${recordId}.mp4`),
|
playbackStart: (recordId: string) => server.get(`/media/record/${recordId}.mp4`),
|
||||||
|
|
||||||
|
// 设备预置位相关接口
|
||||||
|
opFunction: (deviceId: string, functionId: string, data?: any) => server.post(`/device/invoked/${deviceId}/function/${functionId}`, data)
|
||||||
}
|
}
|
|
@ -44,7 +44,7 @@
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 45%;
|
width: 45%;
|
||||||
height: 45%;
|
height: 45%;
|
||||||
font-size: 30px;
|
// font-size: 30px;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
transform: translate(-50%, -50%) rotateZ(-45deg);
|
transform: translate(-50%, -50%) rotateZ(-45deg);
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="direction-audio">
|
<div class="direction-audio">
|
||||||
<!-- <AIcon type="AudioOutlined" /> -->
|
<slot name="center"><!-- <AIcon type="AudioOutlined" /> --></slot>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="zoom">
|
<div class="zoom">
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { createRouter, createWebHashHistory } from 'vue-router'
|
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||||
import menus, { AccountCenterBindPath, InitHomePath, InitLicense, LoginPath, OauthPath } from './menu'
|
import menus, { AccountCenterBindPath, InitHomePath, InitLicense, LoginPath, OauthPath, VideoSharePath } from './menu'
|
||||||
import { cleanToken, getToken } from '@/utils/comm'
|
import { cleanToken, getToken } from '@/utils/comm'
|
||||||
import { useUserInfo } from '@/store/userInfo'
|
import { useUserInfo } from '@/store/userInfo'
|
||||||
import { useSystem } from '@/store/system'
|
import { useSystem } from '@/store/system'
|
||||||
|
@ -15,7 +15,7 @@ const router = createRouter({
|
||||||
})
|
})
|
||||||
|
|
||||||
const filterPath = [ InitHomePath ]
|
const filterPath = [ InitHomePath ]
|
||||||
const noTokenPath = [ AccountCenterBindPath, OauthPath, InitLicense ]
|
const noTokenPath = [ AccountCenterBindPath, OauthPath, InitLicense, VideoSharePath ]
|
||||||
|
|
||||||
router.beforeEach((to, from, next) => {
|
router.beforeEach((to, from, next) => {
|
||||||
// TODO 切换路由取消请求
|
// TODO 切换路由取消请求
|
||||||
|
|
|
@ -5,6 +5,7 @@ export const InitLicense = '/init-license'
|
||||||
export const NotificationSubscriptionCode = 'message-subscribe'
|
export const NotificationSubscriptionCode = 'message-subscribe'
|
||||||
export const NotificationRecordCode = 'account/NotificationRecord'
|
export const NotificationRecordCode = 'account/NotificationRecord'
|
||||||
export const OauthPath = '/oauth'
|
export const OauthPath = '/oauth'
|
||||||
|
export const VideoSharePath = '/media/device/Share'
|
||||||
|
|
||||||
export const AccountMenu = {
|
export const AccountMenu = {
|
||||||
path: '/account',
|
path: '/account',
|
||||||
|
@ -86,5 +87,9 @@ export default [
|
||||||
},
|
},
|
||||||
component: () => import('@/views/oauth/WeChat.vue')
|
component: () => import('@/views/oauth/WeChat.vue')
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: VideoSharePath,
|
||||||
|
component: () => import('@/views/media/Device/Channel/Share/index.vue')
|
||||||
|
},
|
||||||
AccountMenu
|
AccountMenu
|
||||||
]
|
]
|
|
@ -0,0 +1,200 @@
|
||||||
|
<template>
|
||||||
|
<j-table
|
||||||
|
size="small"
|
||||||
|
:columns="columns"
|
||||||
|
:dataSource="dataSource"
|
||||||
|
:pagination="false"
|
||||||
|
:scroll="{ y: 200 }"
|
||||||
|
>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.dataIndex === 'actions'">
|
||||||
|
<j-button
|
||||||
|
type="link"
|
||||||
|
style="padding: 0"
|
||||||
|
v-if="!record.flag"
|
||||||
|
:disabled="loading"
|
||||||
|
@click="onSetting(record)"
|
||||||
|
>设置</j-button
|
||||||
|
>
|
||||||
|
<template v-else>
|
||||||
|
<j-space>
|
||||||
|
<j-button
|
||||||
|
danger
|
||||||
|
type="link"
|
||||||
|
:disabled="loading"
|
||||||
|
style="padding: 0"
|
||||||
|
@click="onDelete(record)"
|
||||||
|
>删除</j-button
|
||||||
|
>
|
||||||
|
<j-button
|
||||||
|
type="link"
|
||||||
|
:disabled="loading"
|
||||||
|
style="padding: 0"
|
||||||
|
@click="onInvoke(record)"
|
||||||
|
>调用</j-button
|
||||||
|
>
|
||||||
|
</j-space>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="column.dataIndex === 'name'">
|
||||||
|
<j-input
|
||||||
|
:disabled="record.flag"
|
||||||
|
v-model:value="record[column.dataIndex]"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
{{ record[column.dataIndex] }}
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</j-table>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import channelApi from '@/api/media/channel';
|
||||||
|
import { onlyMessage } from '@/utils/comm';
|
||||||
|
import { isNumber, unionBy } from 'lodash-es';
|
||||||
|
import { PropType } from 'vue';
|
||||||
|
|
||||||
|
type Item = { id: string | number; name: string; flag?: boolean };
|
||||||
|
const props = defineProps({
|
||||||
|
data: {
|
||||||
|
type: Object as PropType<Partial<Record<string, any>>>,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: '序号',
|
||||||
|
dataIndex: 'id',
|
||||||
|
width: 60,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '预置点位',
|
||||||
|
dataIndex: 'name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
dataIndex: 'actions',
|
||||||
|
width: 90,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const init = new Array(50).fill(0).map((_, index) => {
|
||||||
|
return {
|
||||||
|
id: String(index + 1),
|
||||||
|
name: `预置点${index + 1}`,
|
||||||
|
flag: false,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const dataSource = ref<Item[]>(init);
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
|
const handleSearch = async (id: string, arr: Item[]) => {
|
||||||
|
const resp = await channelApi.opFunction(id, 'QueryPreset');
|
||||||
|
if (resp.status === 200) {
|
||||||
|
dataSource.value = unionBy([ ...arr, ...init], 'id').map((item) => {
|
||||||
|
const _item = (resp.result?.[0] || []).find(
|
||||||
|
(i: any) => i.id === item.id,
|
||||||
|
);
|
||||||
|
if (_item) {
|
||||||
|
return {
|
||||||
|
..._item,
|
||||||
|
flag: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const saveInfo = async (preset: Item[]) => {
|
||||||
|
const resp = await channelApi.update(props.data.id, {
|
||||||
|
id: props.data.id,
|
||||||
|
address: props.data.address,
|
||||||
|
channelId: props.data.channelId,
|
||||||
|
description: props.data.description,
|
||||||
|
deviceId: props.data.deviceId,
|
||||||
|
name: props.data.name,
|
||||||
|
manufacturer: props.data.manufacturer,
|
||||||
|
ptzType: props.data.ptzType?.value || 0,
|
||||||
|
others: {
|
||||||
|
...props.data?.others,
|
||||||
|
preset
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (resp.status === 200) {
|
||||||
|
console.log(resp);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onFunction = (id: string, functionId: string, params: any) => {
|
||||||
|
loading.value = true;
|
||||||
|
channelApi
|
||||||
|
.opFunction(id, functionId, params)
|
||||||
|
.then(async (resp) => {
|
||||||
|
if (resp.status === 200) {
|
||||||
|
onlyMessage('操作成功!');
|
||||||
|
const preset = dataSource.value.map((item) => {
|
||||||
|
return {
|
||||||
|
id: item.id,
|
||||||
|
name: item.name,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
if (params?.operation === 'SET') {
|
||||||
|
// 保存名称
|
||||||
|
await saveInfo(preset);
|
||||||
|
}
|
||||||
|
if (props.data?.deviceId) {
|
||||||
|
await handleSearch(props.data?.deviceId, preset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSetting = (obj: Item) => {
|
||||||
|
if (!obj.id) return;
|
||||||
|
const params = {
|
||||||
|
operation: 'SET',
|
||||||
|
presetIndex: isNumber(obj.id) ? Number(obj.id) : obj.id,
|
||||||
|
channel: props.data?.channelId,
|
||||||
|
};
|
||||||
|
onFunction(props.data?.deviceId, 'Preset', params);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onInvoke = (obj: Item) => {
|
||||||
|
if (!obj.id) return;
|
||||||
|
const params = {
|
||||||
|
operation: 'CALL',
|
||||||
|
presetIndex: isNumber(obj.id) ? Number(obj.id) : obj.id,
|
||||||
|
channel: props.data?.channelId,
|
||||||
|
};
|
||||||
|
onFunction(props.data?.deviceId, 'Preset', params);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDelete = (obj: Item) => {
|
||||||
|
if (!obj.id) return;
|
||||||
|
const params = {
|
||||||
|
operation: 'DEL',
|
||||||
|
presetIndex: isNumber(obj.id) ? Number(obj.id) : obj.id,
|
||||||
|
channel: props.data?.channelId,
|
||||||
|
};
|
||||||
|
onFunction(props.data?.deviceId, 'Preset', params);
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.data.deviceId,
|
||||||
|
() => {
|
||||||
|
if (props.data?.deviceId) {
|
||||||
|
handleSearch(props.data?.deviceId, props.data?.others?.preset);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
</script>
|
|
@ -0,0 +1,61 @@
|
||||||
|
<template>
|
||||||
|
<j-modal visible @cancel="emit('close')" :closable="false">
|
||||||
|
<div class="content">
|
||||||
|
<div style="margin-bottom: 5px;">
|
||||||
|
复制下方链接,分享{{ data.name }}视频界面
|
||||||
|
</div>
|
||||||
|
<j-input-group compact>
|
||||||
|
<j-input
|
||||||
|
v-model:value="url"
|
||||||
|
ref="urlRef"
|
||||||
|
style="width: calc(100% - 50px)"
|
||||||
|
/>
|
||||||
|
<j-tooltip title="复制">
|
||||||
|
<j-button @click="onCopy">
|
||||||
|
<template #icon><AIcon type="CopyOutlined" /></template>
|
||||||
|
</j-button>
|
||||||
|
</j-tooltip>
|
||||||
|
</j-input-group>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<j-button type="primary" @click="emit('close')">确定</j-button>
|
||||||
|
</template>
|
||||||
|
</j-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { getToken } from '@/utils/comm';
|
||||||
|
import { TOKEN_KEY } from '@/utils/variable';
|
||||||
|
import { PropType } from 'vue';
|
||||||
|
|
||||||
|
const emit = defineEmits(['close', 'save']);
|
||||||
|
const props = defineProps({
|
||||||
|
data: {
|
||||||
|
type: Object as PropType<Partial<Record<string, any>>>,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const token = getToken();
|
||||||
|
const route = useRoute();
|
||||||
|
const url = ref('');
|
||||||
|
const urlRef = ref<HTMLInputElement>()
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
url.value = `${window.location.origin}#/media/device/Share?deviceId=${props.data.deviceId}&channelId=${props.data.channelId}&type=${route.query.type}&${TOKEN_KEY}=${token}`
|
||||||
|
})
|
||||||
|
|
||||||
|
const onCopy = () => {
|
||||||
|
if(urlRef.value) {
|
||||||
|
urlRef.value.select()
|
||||||
|
document.execCommand('copy')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin: 60px 10px 40px 10px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,22 +1,21 @@
|
||||||
.media-live {
|
.media-live {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
.live-player-tools {
|
// .live-player-tools {
|
||||||
flex-basis: 230px;
|
// flex-basis: 300px;
|
||||||
|
|
||||||
.direction-item {
|
// .direction-item {
|
||||||
font-size: 30px !important;
|
// font-size: 30px !important;
|
||||||
}
|
// }
|
||||||
|
|
||||||
.zoom-item {
|
// .zoom-item {
|
||||||
font-size: 20px !important;
|
// font-size: 20px !important;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
.media-live-video {
|
.media-live-video {
|
||||||
position: relative;
|
position: relative;
|
||||||
flex-grow: 1;
|
flex: 1;
|
||||||
width: 0;
|
|
||||||
|
|
||||||
.media-tool {
|
.media-tool {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -50,9 +49,20 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.media-live-actions {
|
||||||
|
width: 300px;
|
||||||
|
margin-left: 10px;
|
||||||
|
|
||||||
|
.actions-tool {
|
||||||
|
padding: 0 40px 5px 40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.media-live-tool {
|
.media-live-tool {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin-top: 24px;
|
margin-bottom: 24px;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,49 +3,82 @@
|
||||||
<j-modal
|
<j-modal
|
||||||
v-model:visible="_vis"
|
v-model:visible="_vis"
|
||||||
title="播放"
|
title="播放"
|
||||||
cancelText="取消"
|
:width="_type ? 1200 : 900"
|
||||||
okText="确定"
|
|
||||||
width="800px"
|
|
||||||
:maskClosable="false"
|
:maskClosable="false"
|
||||||
@ok="_vis = false"
|
@ok="_vis = false"
|
||||||
@cancel="_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-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">
|
<div class="media-live">
|
||||||
<div class="media-live-video">
|
<div class="media-live-video">
|
||||||
<div :class="mediaToolClass" @mouseleave='mouseleave' @mouseenter='showTool = true' >
|
<div
|
||||||
<div class="tool-item" >
|
:class="mediaToolClass"
|
||||||
<template v-if='isRecord === 0'>
|
@mouseleave="mouseleave"
|
||||||
<j-dropdown trigger='click' @visibleChange='visibleChange' @click='showToolLock = true'>
|
@mouseenter="showTool = true"
|
||||||
<div>
|
>
|
||||||
开始录像
|
<div class="tool-item" v-if="type !== 'share'">
|
||||||
</div>
|
<template v-if="isRecord === 0">
|
||||||
<template #overlay>
|
<j-dropdown
|
||||||
<j-menu @click="recordStart">
|
trigger="click"
|
||||||
<j-menu-item key='true' v-if='route.query.type !== "fixed-media"'>
|
@visibleChange="visibleChange"
|
||||||
<span style='padding-right: 12px;'>本地存储</span>
|
@click="showToolLock = true"
|
||||||
<j-tooltip title='存储在设备本地'>
|
>
|
||||||
<a-icon type='QuestionCircleOutlined' />
|
<div>开始录像</div>
|
||||||
</j-tooltip>
|
<template #overlay>
|
||||||
</j-menu-item>
|
<j-menu @click="recordStart">
|
||||||
<j-menu-item key='false'>
|
<j-menu-item
|
||||||
<span style='padding-right: 12px;'>云端存储</span>
|
key="true"
|
||||||
<j-tooltip title='存储在服务器中'>
|
v-if="_type"
|
||||||
<a-icon type='QuestionCircleOutlined' />
|
>
|
||||||
</j-tooltip>
|
<span style="padding-right: 12px"
|
||||||
</j-menu-item>
|
>本地存储</span
|
||||||
</j-menu>
|
>
|
||||||
|
<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>
|
</template>
|
||||||
</j-dropdown>
|
<div v-else-if="isRecord === 1">请求录像中</div>
|
||||||
</template>
|
<div
|
||||||
<div v-else-if='isRecord === 1'>
|
v-else-if="isRecord === 2"
|
||||||
请求录像中
|
@click.stop="recordStop"
|
||||||
|
>
|
||||||
|
停止录像
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if='isRecord === 2' @click.stop="recordStop">
|
|
||||||
停止录像
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="tool-item" @click.stop="handleRefresh">
|
<div class="tool-item" @click.stop="handleRefresh">
|
||||||
刷新
|
刷新
|
||||||
|
@ -67,23 +100,45 @@
|
||||||
autoplay
|
autoplay
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<MediaTool
|
<div class="media-live-actions" v-if="_type">
|
||||||
@onMouseDown="handleMouseDown"
|
<div class="actions-tool">
|
||||||
@onMouseUp="handleMouseUp"
|
<MediaTool
|
||||||
/>
|
@onMouseDown="handleMouseDown"
|
||||||
</div>
|
@onMouseUp="handleMouseUp"
|
||||||
<div class="media-live-tool">
|
>
|
||||||
<j-radio-group
|
<template #center>
|
||||||
v-model:value="mediaType"
|
<div class="center">
|
||||||
button-style="solid"
|
<div>转速控制</div>
|
||||||
@change="mediaStart"
|
<j-dropdown>
|
||||||
>
|
<span
|
||||||
<j-radio-button value="mp4">MP4</j-radio-button>
|
>{{ _speed }}<AIcon type="DownOutlined"
|
||||||
<j-radio-button value="flv">FLV</j-radio-button>
|
/></span>
|
||||||
<j-radio-button value="m3u8">HLS</j-radio-button>
|
<template #overlay>
|
||||||
</j-radio-group>
|
<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 :data="data" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<j-space>
|
||||||
|
<j-button :disabled="type === 'share'" @click="_vis = false">取消</j-button>
|
||||||
|
<j-button :disabled="type === 'share'" @click="_vis = false" type="primary">确定</j-button>
|
||||||
|
</j-space>
|
||||||
|
</template>
|
||||||
</j-modal>
|
</j-modal>
|
||||||
|
<Share v-if="visible" :data="data" @close="visible = false" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
@ -91,6 +146,8 @@ 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';
|
import channelApi from '@/api/media/channel';
|
||||||
|
import Share from './Share.vue';
|
||||||
|
import Preset from './Preset.vue';
|
||||||
|
|
||||||
type Emits = {
|
type Emits = {
|
||||||
(e: 'update:visible', data: boolean): void;
|
(e: 'update:visible', data: boolean): void;
|
||||||
|
@ -103,6 +160,10 @@ const props = defineProps({
|
||||||
type: Object as PropType<Partial<Record<string, any>>>,
|
type: Object as PropType<Partial<Record<string, any>>>,
|
||||||
default: () => ({}),
|
default: () => ({}),
|
||||||
},
|
},
|
||||||
|
type: {
|
||||||
|
type: String as PropType<'share' | 'normal'>,
|
||||||
|
default: 'normal',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
@ -118,29 +179,50 @@ const player = ref();
|
||||||
const url = ref('');
|
const url = ref('');
|
||||||
// 视频类型
|
// 视频类型
|
||||||
const mediaType = ref<'mp4' | 'flv' | 'hls'>('mp4');
|
const mediaType = ref<'mp4' | 'flv' | 'hls'>('mp4');
|
||||||
const showTool = ref(false)
|
const showTool = ref(false);
|
||||||
const showToolLock = 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 = () => {
|
const mouseleave = () => {
|
||||||
if (!showToolLock.value) {
|
if (!showToolLock.value) {
|
||||||
showTool.value = false
|
showTool.value = false;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const visibleChange = (v: boolean) => {
|
const visibleChange = (v: boolean) => {
|
||||||
showTool.value = v
|
showTool.value = v;
|
||||||
}
|
};
|
||||||
|
|
||||||
const getPopupContainer = (trigger: HTMLElement) => {
|
const getPopupContainer = (trigger: HTMLElement) => {
|
||||||
return trigger?.parentNode || document.body
|
return trigger?.parentNode || document.body;
|
||||||
}
|
};
|
||||||
|
|
||||||
const mediaToolClass = computed(() => {
|
const mediaToolClass = computed(() => {
|
||||||
return {
|
return {
|
||||||
'media-tool': true,
|
'media-tool': true,
|
||||||
'media-tool-show': showTool.value
|
'media-tool-show': showTool.value,
|
||||||
}
|
};
|
||||||
})
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 媒体开始播放
|
* 媒体开始播放
|
||||||
|
@ -150,7 +232,7 @@ const mediaStart = () => {
|
||||||
props.data.deviceId,
|
props.data.deviceId,
|
||||||
props.data.channelId,
|
props.data.channelId,
|
||||||
mediaType.value,
|
mediaType.value,
|
||||||
)
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 录像状态
|
// 录像状态
|
||||||
|
@ -169,36 +251,33 @@ const getIsRecord = async () => {
|
||||||
/**
|
/**
|
||||||
* 开始录像
|
* 开始录像
|
||||||
*/
|
*/
|
||||||
const recordStart = async ({ key }: { key: string}) => {
|
const recordStart = async ({ key }: { key: string }) => {
|
||||||
showToolLock.value = false
|
showToolLock.value = false;
|
||||||
showTool.value = false
|
showTool.value = false;
|
||||||
isRecord.value = 1;
|
isRecord.value = 1;
|
||||||
const local = key === 'true'
|
const local = key === 'true';
|
||||||
const res = await channelApi.recordStart(
|
const res = await channelApi
|
||||||
props.data.deviceId,
|
.recordStart(props.data.deviceId, props.data.channelId, { local })
|
||||||
props.data.channelId,
|
.catch(() => ({ success: false }));
|
||||||
{ local },
|
if (res.success) {
|
||||||
).catch(() => ({ success: false }))
|
isRecord.value = 2;
|
||||||
if (res.success) {
|
} else {
|
||||||
isRecord.value = 2;
|
isRecord.value = 0;
|
||||||
} else {
|
}
|
||||||
isRecord.value = 0;
|
};
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 停止录像
|
* 停止录像
|
||||||
*/
|
*/
|
||||||
const recordStop = async () => {
|
const recordStop = async () => {
|
||||||
const res = await channelApi.recordStop(
|
const res = await channelApi.recordStop(
|
||||||
props.data.deviceId,
|
props.data.deviceId,
|
||||||
props.data.channelId
|
props.data.channelId,
|
||||||
);
|
);
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
isRecord.value = 0;
|
isRecord.value = 0;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 刷新
|
* 刷新
|
||||||
|
@ -223,12 +302,19 @@ const handleReset = async () => {
|
||||||
* @param type 控制类型
|
* @param type 控制类型
|
||||||
*/
|
*/
|
||||||
const handleMouseDown = (type: string) => {
|
const handleMouseDown = (type: string) => {
|
||||||
channelApi.ptzTool(props.data.deviceId, props.data.channelId, type);
|
channelApi.ptzTool(props.data.deviceId, props.data.channelId, type, speed.value);
|
||||||
};
|
};
|
||||||
const handleMouseUp = () => {
|
const handleMouseUp = () => {
|
||||||
channelApi.ptzStop(props.data.deviceId, props.data.channelId);
|
channelApi.ptzStop(props.data.deviceId, props.data.channelId);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分享视频
|
||||||
|
*/
|
||||||
|
const onShare = () => {
|
||||||
|
visible.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => _vis.value,
|
() => _vis.value,
|
||||||
(val: boolean) => {
|
(val: boolean) => {
|
||||||
|
@ -240,14 +326,23 @@ watch(
|
||||||
url.value = '';
|
url.value = '';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
@import './index.less';
|
@import './index.less';
|
||||||
:deep(.live-player-stretch-btn){
|
:deep(.live-player-stretch-btn) {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
:deep(.vjs-icon-spinner){
|
:deep(.vjs-icon-spinner) {
|
||||||
display: none;
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
<template>
|
||||||
|
<Live :visible="true" type="share" :data="playData" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { LocalStore } from '@/utils/comm';
|
||||||
|
import { TOKEN_KEY } from '@/utils/variable';
|
||||||
|
import Live from '../Live/index.vue';
|
||||||
|
|
||||||
|
const playData = ref({
|
||||||
|
deviceId: '',
|
||||||
|
channelId: '',
|
||||||
|
type: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
// 获取url信息
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
const obj: any = unref(route.query) || {};
|
||||||
|
playData.value = {
|
||||||
|
deviceId: obj?.deviceId || '',
|
||||||
|
channelId: obj?.channelId || '',
|
||||||
|
type: obj?.type
|
||||||
|
};
|
||||||
|
if(obj?.[TOKEN_KEY]){
|
||||||
|
LocalStore.set(TOKEN_KEY, obj?.[TOKEN_KEY]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
Loading…
Reference in New Issue