Browse Source

video play UI 88%

release_0.0.2
巴林闲侠 2 years ago
parent
commit
acc1f98e13
  1. 29
      code/VideoAccess-VCMP/api/app/lib/controllers/camera/create.js
  2. 9
      code/VideoAccess-VCMP/api/app/lib/models/gb_camera.js
  3. 3
      code/VideoAccess-VCMP/api/app/lib/routes/camera/index.js
  4. 30
      code/VideoAccess-VCMP/api/app/lib/utils/camera.js
  5. 2
      code/VideoAccess-VCMP/web/client/src/components/index.js
  6. 244
      code/VideoAccess-VCMP/web/client/src/components/videoPlay.jsx
  7. 0
      code/VideoAccess-VCMP/web/client/src/components/videoPlay.less
  8. 83
      code/VideoAccess-VCMP/web/client/src/components/videoPlayer/videoOperation.jsx
  9. 74
      code/VideoAccess-VCMP/web/client/src/components/videoPlayer/videoOperationCloudControl.jsx
  10. 33
      code/VideoAccess-VCMP/web/client/src/components/videoPlayer/videoOperationSpeed.jsx
  11. 33
      code/VideoAccess-VCMP/web/client/src/components/videoPlayer/videoOperationTalk.jsx
  12. 74
      code/VideoAccess-VCMP/web/client/src/components/videoPlayer/videoOperationVoice.jsx
  13. 140
      code/VideoAccess-VCMP/web/client/src/components/videoPlayer/videoPlay.jsx
  14. 23
      code/VideoAccess-VCMP/web/client/src/components/videoPlayer/videoPlay.less
  15. 0
      code/VideoAccess-VCMP/web/client/src/components/videoPlayer/videoPlayModal.jsx
  16. 0
      code/VideoAccess-VCMP/web/client/src/components/videoPlayer/videoPlayModal.less
  17. 53
      code/VideoAccess-VCMP/web/client/src/components/videoPlayer/voiceHeader.jsx
  18. 7
      code/VideoAccess-VCMP/web/client/src/layout/components/sider/index.jsx
  19. 10
      code/VideoAccess-VCMP/web/client/src/layout/index.jsx
  20. 1
      code/VideoAccess-VCMP/web/package.json

29
code/VideoAccess-VCMP/api/app/lib/controllers/camera/create.js

@ -382,6 +382,32 @@ async function verifyCascadeCamera (ctx) {
} }
} }
async function getCascadeCameraGrandParentSip (ctx) {
let errMsg = '查询级联设备失败'
try {
const { models } = ctx.fs.dc
const { cameraSerialNo } = ctx.query
const { utils: { getGbCameraLevel1ByStreamId } } = ctx.app.fs
const parent = await getGbCameraLevel1ByStreamId({
streamId: cameraSerialNo,
where: { ipctype: '级联' }
})
if (!parent) {
errMsg = `没有找到相应级联设备`
throw errMsg
}
ctx.status = 200;
ctx.body = parent
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {
message: typeof error == 'string' ? error : errMsg
}
}
}
async function createCascadeCamera (ctx) { async function createCascadeCamera (ctx) {
let errMsg = '添加级联摄像头信息失败' let errMsg = '添加级联摄像头信息失败'
const transaction = await ctx.fs.dc.orm.transaction(); const transaction = await ctx.fs.dc.orm.transaction();
@ -412,7 +438,7 @@ async function createCascadeCamera (ctx) {
sip: c.sipip, sip: c.sipip,
name: c.name name: c.name
} }
const added = addedCmeraRes.some(ac => ac.streamid == c.streamid) const added = addedCmeraRes.find(ac => ac.serialNo == c.streamid)
if (added) { if (added) {
let data = { let data = {
...storageData, ...storageData,
@ -459,5 +485,6 @@ module.exports = {
createIpcCamera, createIpcCamera,
getCascadeSipList, getCascadeSipList,
verifyCascadeCamera, verifyCascadeCamera,
getCascadeCameraGrandParentSip,
createCascadeCamera, createCascadeCamera,
}; };

9
code/VideoAccess-VCMP/api/app/lib/models/gb_camera.js

@ -131,6 +131,15 @@ module.exports = dc => {
primaryKey: false, primaryKey: false,
field: "Sipip", field: "Sipip",
autoIncrement: false autoIncrement: false
},
ipctype: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: null,
comment: null,
primaryKey: false,
field: "ipctype",
autoIncrement: false
} }
}, { }, {
tableName: "gbCamera", tableName: "gbCamera",

3
code/VideoAccess-VCMP/api/app/lib/routes/camera/index.js

@ -36,6 +36,9 @@ module.exports = function (app, router, opts) {
app.fs.api.logAttr['POST/camera/create/cascade'] = { content: '添加级联摄像头', visible: false }; app.fs.api.logAttr['POST/camera/create/cascade'] = { content: '添加级联摄像头', visible: false };
router.post('/camera/create/cascade', cameraCreate.createCascadeCamera); router.post('/camera/create/cascade', cameraCreate.createCascadeCamera);
app.fs.api.logAttr['GET/camera/cascade_device'] = { content: '获取级联摄像头父级设备', visible: false };
router.get('/camera/cascade_device', cameraCreate.getCascadeCameraGrandParentSip);
// 摄像头创建 END // 摄像头创建 END
app.fs.api.logAttr['GET/camera/project'] = { content: '获取摄像头列表及项目绑定信息', visible: false }; app.fs.api.logAttr['GET/camera/project'] = { content: '获取摄像头列表及项目绑定信息', visible: false };

30
code/VideoAccess-VCMP/api/app/lib/utils/camera.js

@ -27,6 +27,35 @@ module.exports = function (app, opts) {
return cameraRes return cameraRes
} }
async function getGbCameraLevel1ByStreamId ({ streamId, where = {} }) {
const { models } = app.fs.dc
if (!streamId) {
let errMsg = '参数错误'
throw errMsg
}
const findParent = async (streamId) => {
const parentRes = await models.GbCamera.findOne({
where: {
streamid: streamId,
...where
}
})
if (!parentRes || parentRes.level == 0) {
return parentRes
} if (!parentRes.parent) {
return
} else {
const lastLevelStreamId = parentRes.parent
return findParent(lastLevelStreamId)
}
}
const deviceRes = await findParent(streamId)
return deviceRes
}
async function verifyYingshiInfo ({ serialNo } = {}) { async function verifyYingshiInfo ({ serialNo } = {}) {
const { varifyYingshiBelongSecretBySerialNo } = app.fs.utils const { varifyYingshiBelongSecretBySerialNo } = app.fs.utils
const beloneSecret = await varifyYingshiBelongSecretBySerialNo(serialNo) const beloneSecret = await varifyYingshiBelongSecretBySerialNo(serialNo)
@ -69,6 +98,7 @@ module.exports = function (app, opts) {
} }
return { return {
getGbCameraLevel1ByStreamId,
getGbCameraLevel3ByStreamId, getGbCameraLevel3ByStreamId,
verifyYingshiInfo, verifyYingshiInfo,
verifyIpcInfo, verifyIpcInfo,

2
code/VideoAccess-VCMP/web/client/src/components/index.js

@ -2,7 +2,7 @@
import SimpleFileDownButton from './simpleFileDownButton' import SimpleFileDownButton from './simpleFileDownButton'
import Coming from './coming' import Coming from './coming'
import ReminderBox from './reminderBox' import ReminderBox from './reminderBox'
import VideoPlayModal from './videoPlayModal' import VideoPlayModal from './videoPlayer/videoPlayModal'
export { export {
SimpleFileDownButton, SimpleFileDownButton,

244
code/VideoAccess-VCMP/web/client/src/components/videoPlay.jsx

@ -1,244 +0,0 @@
import React, { useState, useEffect, useRef } from "react";
import { connect } from "react-redux";
import moment from 'moment'
import { Button, Modal, Col, Row, Space } from "@douyinfe/semi-ui";
import TextScroll from './textScroll'
import './videoPlay.less';
const VideoPlay = ({ height, width }) => {
const [jessibuca, setjessibuca] = useState(null)
const [playUrl, setPlayUrl] = useState('http://flv.bdplay.nodemedia.cn/live/bbb.flv')
const [isPlaying, setIsPlaying] = useState(false)
const [operationState, setoperationState] = useState()
const changeSelectState = (key) => {
const nextOperationState = JSON.parse(JSON.stringify(operationState))
for (let k in nextOperationState) {
if (k == key) {
nextOperationState[k].select = !nextOperationState[k].select
} else if (key !== 'fullScreen') {
nextOperationState[k].select = false
}
}
return nextOperationState
}
const operation = [{
key: 'control',
click: () => {
let nextOperationState = changeSelectState('control')
setoperationState(nextOperationState)
}
}, {
key: 'talk',
click: () => {
let nextOperationState = changeSelectState('talk')
setoperationState(nextOperationState)
}
}, {
key: 'fullScreen',
click: () => {
let nextOperationState = changeSelectState('fullScreen')
setoperationState(nextOperationState)
}
}, {
key: 'histroy',
click: () => {
}
},]
useEffect(() => {
create()
let nextOperationState = {}
for (let p of operation) {
nextOperationState[p.key] = {
select: false
}
}
setoperationState(nextOperationState)
}, [])
const create = () => {
let $container = document.getElementById('container');
const jessibuca = new window.Jessibuca({
container: $container,
videoBuffer: 0.2, //
isResize: false,
text: "",
loadingText: "加载中",
debug: true,
showBandwidth: true, //
operateBtns: {
fullscreen: true,
screenshot: true,
play: true,
audio: true,
},
forceNoOffscreen: false,
isNotMute: false,
});
setjessibuca(jessibuca)
}
useEffect(() => {
play()
}, [jessibuca])
const play = () => {
if (jessibuca && playUrl) {
jessibuca.play(playUrl);
setIsPlaying(true)
}
}
const pause = () => {
if (jessibuca) {
jessibuca.pause();
setIsPlaying(false)
}
}
const butStyle = {
border: '1px solid #fff', display: 'inline-block', color: '#fff', padding: '0 10px',
display: 'flex', alignItems: 'center', height: '64%', marginLeft: 12
}
return (
<>
<div style={{ height: height || '100%', width: width || '100%' }}>
<div style={{ position: 'relative', display: 'flex' }}>
{/* 顶部信息 */}
<div style={{
height: 42, lineHeight: '42px', background: '#00000026',
position: 'absolute', width: '100%', zIndex: 99,
color: '#fff'
}}>
<Row >
<Col span={9} style={{
backgroundImage: 'url(/assets/images/background/videoPlayBg.png)',
backgroundSize: '100% 100%',
backgroundRepeat: 'no-repeat',
textAlign: 'center'
}}>123</Col>
<Col span={15} style={{}}>
<div style={{ paddingRight: 12 }}>
<TextScroll content={['asdadasdasdasdasdasd', '123123']} duration={6} />
</div>
</Col>
</Row>
</div>
{/* 视频内容 */}
<div id="container" style={{ height: height || '100%', width: width || '100%' }}></div>
{/* 云控 对讲 对应操作内容 */}
{
operationState ?
operationState.control.select ?
<div style={{
position: 'absolute', top: 'calc(50% - 105px)', left: 'calc(50% - 125px)',
width: 210, height: 250, backgroundColor: '#00000014', borderRadius: 68
}}>
<div style={{
height: 148, width: 148, borderRadius: '100%', backgroundColor: '#2F53EA72', margin: '12px auto 18px',
position: 'relative',
}}>
{
[{
style: { top: 12, left: (148 - 24) / 2, }
}, {
style: { right: 12, top: (148 - 24) / 2, }
}, {
style: { bottom: 12, left: (148 - 24) / 2, }
}, {
style: { left: 12, top: (148 - 24) / 2, }
}].map((s, i) => {
return (
<img
src="/assets/images/background/up.png"
style={Object.assign({
height: 24, width: 24, display: 'inline-block', transform: `rotate(${i * 90}deg)`,
position: 'absolute'
}, s.style)}
/>
)
})
}
<div style={{
height: 32, width: 32, border: '2px solid #ffffff24', borderRadius: '100%',
position: 'absolute', top: (148 - 34) / 2, left: (148 - 34) / 2
}} />
</div>
{
[
[{ n: '+' }, { n: '焦距' }, { n: '-' }],
[{ n: '+' }, { n: '缩放' }, { n: '-' }]
].map(s => {
return (
<div style={{
width: 110, height: 22, margin: '0 auto 6px', display: 'flex', alignContent: 'center', justifyContent: 'space-around',
backgroundColor: '#2F53EA72', color: '#fff'
}}>
{
s.map((m, mi) => {
return (
<div style={{ textAlign: 'center', display: 'inline-block', cursor: mi != 1 ? 'pointer' : 'auto' }}>{m.n}</div>
)
})
}
</div>
)
})
}
</div> :
operationState.talk.select ?
<div style={{
position: 'absolute', top: 'calc(50% - 88px)', left: 'calc(50% - 156px)',
width: 312, height: 176, backgroundColor: '#000000A5',
}}>
<img src="/assets/images/background/talking.png" style={{ display: 'block', margin: '12px auto' }} />
<div style={{
height: 32, width: 88, textAlign: 'center', margin: 'auto', color: '#fff', backgroundColor: '#1859C1',
lineHeight: '32px'
}}>开始讲话</div>
</div> :
'' : ''
}
{/* 下方操作 */}
<div style={{
height: 42, lineHeight: '42px', background: 'linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.8) 100%)', padding: '0 12px',
display: 'flex', justifyContent: 'space-between',
position: 'absolute', bottom: 0, width: '100%', zIndex: 99
}}>
<div style={{ display: 'flex', alignItems: 'center' }}>
{
operationState ?
operation.map(p => {
return <img
src={`/assets/images/background/video-icon-${p.key}-${operationState[p.key].select ? 'select' : 'unselect'}.png`}
height={26}
style={{ marginRight: 24 }}
onClick={p.click}
/>
}) : ''
}
</div>
<div style={{ display: 'flex', alignItems: 'center' }}>
<div style={butStyle}>标清</div>
<div style={butStyle}>高清</div>
</div>
</div>
</div>
</div>
</>
)
}
function mapStateToProps (state) {
const { auth } = state;
return {
user: auth.user,
};
}
export default connect(mapStateToProps)(VideoPlay);

0
code/VideoAccess-VCMP/web/client/src/components/videoPlay.less

83
code/VideoAccess-VCMP/web/client/src/components/videoPlayer/videoOperation.jsx

@ -0,0 +1,83 @@
import React, { useState, useEffect, useRef } from "react";
import { connect } from "react-redux";
import moment from 'moment'
import VideoOperationCloudControl from './videoOperationCloudControl'
import VideoOperationTalk from './VideoOperationTalk'
import VideoOperationSpeed from './videoOperationSpeed'
import VideoOperationVoice from './VideoOperationVoice'
import { IconPause, IconPlay } from '@douyinfe/semi-icons';
import './videoPlay.less';
const VideoOperation = ({ operationState, operation, voiceDisY, setVoiceDisY }) => {
const butStyle = {
border: '1px solid #fff', display: 'inline-block', color: '#fff', padding: '0 10px',
display: 'flex', alignItems: 'center', height: '64%', marginLeft: 12, cursor: 'pointer',
position: 'relative'
}
return (
<>
{
operationState ?
operationState.control.select ?
<VideoOperationCloudControl /> :
operationState.talk.select ?
<VideoOperationTalk /> :
'' : ''
}
{/* 下方操作 */}
<div style={{
height: 42, lineHeight: '42px', background: 'linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.8) 100%)', padding: '0 12px',
display: 'flex', justifyContent: 'space-between',
position: 'absolute', bottom: 0, width: '100%', zIndex: 99, color: '#fff'
}}>
{
operationState ?
operationState.histroy.select ?
<>
<div style={{ display: 'flex', alignItems: 'center' }}>
<IconPause style={{ cursor: 'pointer' }} />
{/* <IconPlay style={{ cursor: 'pointer' }} /> */}
<span style={{ marginLeft: 12 }}>{moment().format('YYYY-MM-DD HH:mm:ss')}/{moment().format('YYYY-MM-DD HH:mm:ss')}</span>
</div>
<div style={{ display: 'flex', alignItems: 'center' }}>
<VideoOperationVoice voiceDisY={voiceDisY} setVoiceDisY={setVoiceDisY} />
<VideoOperationSpeed butStyle={butStyle} />
<div style={butStyle}>时间设置</div>
</div>
</>
:
<>
<div style={{ display: 'flex', alignItems: 'center' }}>
{
operationState ?
operation.map(p => {
return <img
src={`/assets/images/background/video-icon-${p.key}-${operationState[p.key].select ? 'select' : 'unselect'}.png`}
height={26}
style={{ marginRight: 24, cursor: 'pointer' }}
onClick={p.click}
/>
}) : ''
}
</div>
<div style={{ display: 'flex', alignItems: 'center' }}>
<div style={butStyle}>标清</div>
<div style={butStyle}>高清</div>
</div>
</> : ''
}
</div>
</>
)
}
function mapStateToProps (state) {
const { auth } = state;
return {
user: auth.user,
};
}
export default connect(mapStateToProps)(VideoOperation);

74
code/VideoAccess-VCMP/web/client/src/components/videoPlayer/videoOperationCloudControl.jsx

@ -0,0 +1,74 @@
import React, { useState, useEffect, useRef } from "react";
import { connect } from "react-redux";
import './videoPlay.less';
const VideoOperationCloudControl = ({ }) => {
return (
<div style={{
position: 'absolute', top: 'calc(50% - 105px)', left: 'calc(50% - 125px)',
width: 210, height: 250, backgroundColor: '#00000014', borderRadius: 68
}}>
<div style={{
height: 148, width: 148, borderRadius: '100%', backgroundColor: '#2F53EA72', margin: '12px auto 18px',
position: 'relative',
}}>
{
[{
style: { top: 12, left: (148 - 24) / 2, }
}, {
style: { right: 12, top: (148 - 24) / 2, }
}, {
style: { bottom: 12, left: (148 - 24) / 2, }
}, {
style: { left: 12, top: (148 - 24) / 2, }
}].map((s, i) => {
return (
<img
src="/assets/images/background/up.png"
style={Object.assign({
height: 24, width: 24, display: 'inline-block', transform: `rotate(${i * 90}deg)`,
position: 'absolute'
}, s.style)}
/>
)
})
}
<div style={{
height: 32, width: 32, border: '2px solid #ffffff24', borderRadius: '100%',
position: 'absolute', top: (148 - 34) / 2, left: (148 - 34) / 2
}} />
</div>
{
[
[{ n: '+' }, { n: '焦距' }, { n: '-' }],
[{ n: '+' }, { n: '缩放' }, { n: '-' }]
].map(s => {
return (
<div style={{
width: 110, height: 22, margin: '0 auto 6px', display: 'flex', alignContent: 'center', justifyContent: 'space-around',
backgroundColor: '#2F53EA72', color: '#fff'
}}>
{
s.map((m, mi) => {
return (
<div style={{ textAlign: 'center', display: 'inline-block', cursor: mi != 1 ? 'pointer' : 'auto' }}>{m.n}</div>
)
})
}
</div>
)
})
}
</div>
)
}
function mapStateToProps (state) {
const { auth } = state;
return {
user: auth.user,
};
}
export default connect(mapStateToProps)(VideoOperationCloudControl);

33
code/VideoAccess-VCMP/web/client/src/components/videoPlayer/videoOperationSpeed.jsx

@ -0,0 +1,33 @@
import React, { useState, useEffect, useRef } from "react";
import { connect } from "react-redux";
import './videoPlay.less';
const VideoOperationSpeed = ({ butStyle }) => {
return (
<div style={butStyle} className="video_speed_but">
<div className="video_speed_options" style={{
position: 'absolute', top: -24 * 3 - 24, backgroundColor: '#00000099', width: 60,
left: -6, padding: '8px 0'
}}>
{
[1, 2, 3].map((s) => {
return (
<div className="video_speed_option" style={{ textAlign: 'center', height: 24, lineHeight: '24px' }}>{s}.0 x</div>
)
})
}
</div>
倍速
</div>
)
}
function mapStateToProps (state) {
const { auth } = state;
return {
user: auth.user,
};
}
export default connect(mapStateToProps)(VideoOperationSpeed);

33
code/VideoAccess-VCMP/web/client/src/components/videoPlayer/videoOperationTalk.jsx

@ -0,0 +1,33 @@
import React, { useState, useEffect, useRef } from "react";
import { connect } from "react-redux";
import './videoPlay.less';
const VideoOperationTalk = ({ butStyle }) => {
return (
<div style={butStyle} className="video_speed_but">
<div className="video_speed_options" style={{
position: 'absolute', top: -24 * 3 - 24, backgroundColor: '#00000099', width: 60,
left: -6, padding: '8px 0'
}}>
{
[1, 2, 3].map((s) => {
return (
<div className="video_speed_option" style={{ textAlign: 'center', height: 24, lineHeight: '24px' }}>{s}.0 x</div>
)
})
}
</div>
倍速
</div>
)
}
function mapStateToProps (state) {
const { auth } = state;
return {
user: auth.user,
};
}
export default connect(mapStateToProps)(VideoOperationTalk);

74
code/VideoAccess-VCMP/web/client/src/components/videoPlayer/videoOperationVoice.jsx

@ -0,0 +1,74 @@
import React, { useState, useEffect, useRef } from "react";
import { connect } from "react-redux";
import { IconVolume2, } from '@douyinfe/semi-icons';
import './videoPlay.less';
const VideoOperationVoice = ({ voiceDisY, setVoiceDisY }) => {
return (
<div className="video_voice_but" style={{ display: 'flex', alignItems: 'center', position: 'relative', height: '100%', }}>
<div className="video_voice_options">
<div style={{
position: 'absolute', top: -100 - 32 - 16, backgroundColor: '#00000099', width: 32,
left: -9, padding: '4px 0 12px',
display: 'flex', justifyContent: 'center', flexDirection: 'column', alignItems: 'center',
}}>
<span style={{ height: 32, lineHeight: '32px', }}>{Math.abs(voiceDisY)}</span>
<div style={{
width: 2, height: 100, backgroundColor: '#ffffffa0', position: 'relative',
}} onMouseDown={(ev) => { console.log('object'); }}>
<div
style={{
height: 12, width: 12, borderRadius: '100%', backgroundColor: '#fff',
position: 'relative', left: -5, cursor: 'pointer',
bottom: -94
}}
id='voice_point'
onMouseDown={(ev) => {
ev.stopPropagation();
ev.preventDefault();
let oevent = ev;
let distanceY = oevent.clientY
let prev = Date.now();
const point = document.getElementById('voice_point')
document.onmousemove = function (ev) {
ev.stopPropagation();
ev.preventDefault();
//
let now = Date.now();
if (now - prev >= 0) {
let oevent = ev;
let y = voiceDisY + oevent.clientY - distanceY
if (y < -100) {
y = -100
} else if (y > 0) {
y = 0
}
setVoiceDisY(y)
point.style.bottom = -94 - y + 'px';
prev = Date.now();
}
};
document.onmouseup = function () {
document.onmousemove = null;
document.onmouseup = null;
};
}}
/>
</div>
</div>
</div>
<IconVolume2 style={{ cursor: 'pointer' }} />
</div>
)
}
function mapStateToProps (state) {
const { auth } = state;
return {
user: auth.user,
};
}
export default connect(mapStateToProps)(VideoOperationVoice);

140
code/VideoAccess-VCMP/web/client/src/components/videoPlayer/videoPlay.jsx

@ -0,0 +1,140 @@
import React, { useState, useEffect, useRef } from "react";
import { connect } from "react-redux";
import screenfull from 'screenfull';
import VideoHeader from './voiceHeader'
import VideoOperation from './videoOperation'
import './videoPlay.less';
const VideoPlay = ({ height, width }) => {
const [jessibuca, setjessibuca] = useState(null)
const [playUrl, setPlayUrl] = useState('http://flv.bdplay.nodemedia.cn/live/bbb.flv')
const [isPlaying, setIsPlaying] = useState(false)
const [operationState, setoperationState] = useState()
const [voiceDisY, setVoiceDisY] = useState(0)
const changeSelectState = (key) => {
const nextOperationState = JSON.parse(JSON.stringify(operationState))
for (let k in nextOperationState) {
if (k == key) {
nextOperationState[k].select = !nextOperationState[k].select
} else if (k !== 'fullScreen') {
nextOperationState[k].select = false
}
}
return nextOperationState
}
const operation = [{
key: 'control',
click: () => {
let nextOperationState = changeSelectState('control')
setoperationState(nextOperationState)
}
}, {
key: 'talk',
click: () => {
let nextOperationState = changeSelectState('talk')
setoperationState(nextOperationState)
}
}, {
key: 'fullScreen',
click: () => {
let nextOperationState = changeSelectState('fullScreen')
setoperationState(nextOperationState)
let player = document.getElementById('vcmp_videoplay')
if (screenfull.isEnabled) {
screenfull.toggle(player);
}
}
}, {
key: 'histroy',
click: () => {
let nextOperationState = changeSelectState('histroy')
setoperationState(nextOperationState)
}
},]
useEffect(() => {
create()
let nextOperationState = {}
for (let p of operation) {
nextOperationState[p.key] = {
select: false
}
}
setoperationState(nextOperationState)
}, [])
const create = () => {
let $container = document.getElementById('container');
const jessibuca = new window.Jessibuca({
container: $container,
videoBuffer: 0.2, //
isResize: false,
text: "",
loadingText: "加载中",
debug: true,
showBandwidth: true, //
operateBtns: {
fullscreen: true,
screenshot: true,
play: true,
audio: true,
},
forceNoOffscreen: false,
isNotMute: false,
});
setjessibuca(jessibuca)
play()
}
useEffect(() => {
play()
}, [jessibuca])
const play = () => {
if (jessibuca && playUrl) {
jessibuca.play(playUrl);
setIsPlaying(true)
}
}
const pause = () => {
if (jessibuca) {
jessibuca.pause();
setIsPlaying(false)
}
}
return (
<>
<div style={{ height: height || '100%', width: width || '100%' }}>
<div id="vcmp_videoplay" style={{ position: 'relative', display: 'flex', height: '100%', width: '100%' }}>
{/* 顶部信息 */}
<VideoHeader operationState={operationState} changeSelectState={changeSelectState} setoperationState={setoperationState} />
{/* 视频内容 */}
<div id="container"
style={{
height: '100%', width: '100%'
}}>
</div>
{/* 云控 对讲 对应操作内容 */}
<VideoOperation
operationState={operationState} operation={operation}
voiceDisY={voiceDisY} setVoiceDisY={setVoiceDisY}
/>
</div>
</div>
</>
)
}
function mapStateToProps (state) {
const { auth } = state;
return {
user: auth.user,
};
}
export default connect(mapStateToProps)(VideoPlay);

23
code/VideoAccess-VCMP/web/client/src/components/videoPlayer/videoPlay.less

@ -0,0 +1,23 @@
.video_speed_but:hover {
.video_speed_options {
display: block;
}
}
.video_speed_options {
display: none;
}
.video_speed_option:hover {
color: #A3B5FF;
}
.video_voice_but:hover {
.video_voice_options {
display: block;
}
}
.video_voice_options {
display: none;
}

0
code/VideoAccess-VCMP/web/client/src/components/videoPlayModal.jsx → code/VideoAccess-VCMP/web/client/src/components/videoPlayer/videoPlayModal.jsx

0
code/VideoAccess-VCMP/web/client/src/components/videoPlayModal.less → code/VideoAccess-VCMP/web/client/src/components/videoPlayer/videoPlayModal.less

53
code/VideoAccess-VCMP/web/client/src/components/videoPlayer/voiceHeader.jsx

@ -0,0 +1,53 @@
import React, { useState, useEffect, useRef } from "react";
import { connect } from "react-redux";
import moment from 'moment'
import { Col, Row, } from "@douyinfe/semi-ui";
import { IconReply } from '@douyinfe/semi-icons';
import TextScroll from '../textScroll'
import './videoPlay.less';
const VideoHeader = ({ operationState, changeSelectState, setoperationState }) => {
return (
<div style={{
height: 42, lineHeight: '42px', background: '#00000026',
position: 'absolute', width: '100%', zIndex: 99,
color: '#fff'
}}>
<Row>
<Col span={9} style={{
backgroundImage: 'url(/assets/images/background/videoPlayBg.png)',
backgroundSize: '100% 100%',
backgroundRepeat: 'no-repeat',
// textAlign: 'center',
padding: '0 12px'
}}>
{
operationState && operationState.histroy.select ?
<>
<IconReply style={{ marginRight: 12, cursor: 'pointer' }} onClick={() => {
let nextOperationState = changeSelectState('histroy')
setoperationState(nextOperationState)
}} />
</> : ''
}
{moment().format('YYYY-MM-DD HH:mm:ss')} xxxxxxx
</Col>
<Col span={15} style={{}}>
<div style={{ paddingRight: 12 }}>
<TextScroll content={['asdadasdasdasdasdasd', '123123']} duration={6} />
</div>
</Col>
</Row>
</div>
)
}
function mapStateToProps (state) {
const { auth } = state;
return {
user: auth.user,
};
}
export default connect(mapStateToProps)(VideoHeader);

7
code/VideoAccess-VCMP/web/client/src/layout/components/sider/index.jsx

@ -5,6 +5,7 @@ import { Nav } from '@douyinfe/semi-ui';
import { push } from 'react-router-redux'; import { push } from 'react-router-redux';
let scrollbar = null let scrollbar = null
const homePath = '/equipmentWarehouse/nvr'
const Sider = props => { const Sider = props => {
const { collapsed, clientHeight, dispatch, pathname } = props const { collapsed, clientHeight, dispatch, pathname } = props
const [items, setItems] = useState([]) const [items, setItems] = useState([])
@ -14,9 +15,10 @@ const Sider = props => {
useEffect(() => { useEffect(() => {
const { sections, dispatch, user } = props; const { sections, dispatch, user } = props;
let nextItems = [] let nextItems = []
let pathname_ = pathname == '/' ? homePath : pathname
const initKeys = (items, lastKeys) => { const initKeys = (items, lastKeys) => {
for (let it of items) { for (let it of items) {
if (it.to && it.to == pathname) { if (it.to && it.to == pathname_) {
lastKeys.selectedKeys.push(it.itemKey) lastKeys.selectedKeys.push(it.itemKey)
return lastKeys return lastKeys
} else if (it.items && it.items.length) { } else if (it.items && it.items.length) {
@ -67,6 +69,9 @@ const Sider = props => {
} }
scrollbar = new PerfectScrollbar('#page-slider', { suppressScrollX: true }); scrollbar = new PerfectScrollbar('#page-slider', { suppressScrollX: true });
if (pathname == '/') {
dispatch(push(homePath))
}
}, []) }, [])
useEffect(() => { useEffect(() => {

10
code/VideoAccess-VCMP/web/client/src/layout/index.jsx

@ -25,6 +25,7 @@ const Root = props => {
const [combineRoutes, setCombineRoutes] = useState([]) const [combineRoutes, setCombineRoutes] = useState([])
const [innnerRoutes, setInnerRoutes] = useState([]) const [innnerRoutes, setInnerRoutes] = useState([])
const [authCrossLoading, setAuthCrossLoading] = useState(true) const [authCrossLoading, setAuthCrossLoading] = useState(true)
const [resourceRoot, setResourceRoot] = useState({})
const flatRoutes = (routes) => { const flatRoutes = (routes) => {
const combineRoutes = []; const combineRoutes = [];
@ -127,7 +128,7 @@ const Root = props => {
store.dispatch(actions.auth.initAuth()); store.dispatch(actions.auth.initAuth());
store.dispatch(initWebSocket({})) store.dispatch(initWebSocket({}))
const resourceRoot = await store.dispatch(initApiRoot()) const resourceRoot = await store.dispatch(initApiRoot())
console.log(resourceRoot); setResourceRoot(resourceRoot.payload)
const combineRoutes = flatRoutes(innerRoutes); const combineRoutes = flatRoutes(innerRoutes);
setInnerRoutes(combineRoutes) setInnerRoutes(combineRoutes)
@ -153,6 +154,7 @@ const Root = props => {
// IOT system cross // IOT system cross
window.addEventListener('message', async function (e) { // message window.addEventListener('message', async function (e) { // message
const { data } = e const { data } = e
console.log(e);
if (data && data.action) { if (data && data.action) {
if (data.action == 'initUser') { if (data.action == 'initUser') {
await store.dispatch(actions.auth.initAuth(data.user)) await store.dispatch(actions.auth.initAuth(data.user))
@ -192,9 +194,13 @@ const Root = props => {
</ConfigProvider> </ConfigProvider>
: '' : ''
} }
<iframe id="iotAuth" src={"http://10.8.30.7:5200/cross"} style={{ position: 'absolute', top: 0, height: 1, visibility: 'hidden' }} frameBorder={0} > {
resourceRoot.iotAuthWeb ?
<iframe id="iotAuth" src={`${resourceRoot.iotAuthWeb}/cross`} style={{ position: 'absolute', top: 0, height: 1, visibility: 'hidden' }} frameBorder={0} >
<p>你的浏览器不支持 iframe</p> <p>你的浏览器不支持 iframe</p>
</iframe> </iframe>
: ''
}
</> </>
) )

1
code/VideoAccess-VCMP/web/package.json

@ -64,6 +64,7 @@
"moment": "^2.29.3", "moment": "^2.29.3",
"npm": "^7.20.6", "npm": "^7.20.6",
"perfect-scrollbar": "^1.5.5", "perfect-scrollbar": "^1.5.5",
"screenfull": "5.2.0",
"socket.io-client": "^4.5.0", "socket.io-client": "^4.5.0",
"socket.io-parser": "^4.2.0", "socket.io-parser": "^4.2.0",
"superagent": "^6.1.0", "superagent": "^6.1.0",

Loading…
Cancel
Save