diff --git a/ah-jjzhgd-web/package.json b/ah-jjzhgd-web/package.json
index 2ac725f..b6f28a2 100644
--- a/ah-jjzhgd-web/package.json
+++ b/ah-jjzhgd-web/package.json
@@ -26,7 +26,9 @@
"echarts": "4.2.1",
"element-ui": "2.15.13",
"file-saver": "2.0.1",
+ "flv.js": "^1.6.2",
"fuse.js": "3.4.4",
+ "jquery": "^3.7.1",
"js-cookie": "2.2.0",
"jsonlint": "1.6.3",
"jszip": "3.2.1",
@@ -35,10 +37,11 @@
"normalize.css": "7.0.0",
"nprogress": "0.2.0",
"path-to-regexp": "2.4.0",
- "qs": "^6.11.2",
+ "qs": "^6.12.0",
"screenfull": "4.2.0",
"script-loader": "0.7.2",
"sortablejs": "1.8.4",
+ "video.js": "^8.10.0",
"vue": "2.6.10",
"vue-count-to": "1.0.13",
"vue-router": "3.0.2",
diff --git a/ah-jjzhgd-web/src/api/video/index.js b/ah-jjzhgd-web/src/api/video/index.js
new file mode 100644
index 0000000..7763034
--- /dev/null
+++ b/ah-jjzhgd-web/src/api/video/index.js
@@ -0,0 +1,26 @@
+import request from '@/utils/videoRequest'
+import qs from 'qs'
+import $ from 'jquery'
+
+// 视频监控相关操作
+export function handleVideoGet(url, data) {
+ return request({
+ url,
+ method: 'get',
+ params: data
+ })
+}
+
+export function handleVideoPost(url, data, scb) {
+ $.ajax({
+ type: 'post',
+ url: url,
+ data: data,
+ traditional: true,
+ dataType: 'json',
+ async: true,
+ complete: function(res) {
+ scb && scb(res)
+ }
+ })
+}
diff --git a/ah-jjzhgd-web/src/main.js b/ah-jjzhgd-web/src/main.js
index 3729c66..94d30f5 100644
--- a/ah-jjzhgd-web/src/main.js
+++ b/ah-jjzhgd-web/src/main.js
@@ -21,6 +21,10 @@ import './utils/error-log' // error log
import * as filters from './filters' // global filters
import qs from 'qs'
+
+import Video from 'video.js'
+import 'video.js/dist/video-js.min.css'
+
/**
* If you don't want to use mock-server
* you want to use MockJs for mock api
@@ -35,7 +39,7 @@ import qs from 'qs'
// }
Vue.use(Element, {
- size: Cookies.get('size') || 'medium', // set element-ui default size
+ size: Cookies.get('size') || 'medium' // set element-ui default size
// locale: enLang // 如果使用中文,无需设置,请删除
})
@@ -52,3 +56,5 @@ new Vue({
store,
render: h => h(App)
})
+
+Vue.prototype.$video = Video
diff --git a/ah-jjzhgd-web/src/utils/videoRequest.js b/ah-jjzhgd-web/src/utils/videoRequest.js
new file mode 100644
index 0000000..7a9863b
--- /dev/null
+++ b/ah-jjzhgd-web/src/utils/videoRequest.js
@@ -0,0 +1,53 @@
+import axios from 'axios'
+import { MessageBox, Message } from 'element-ui'
+import store from '@/store'
+import { getToken, removeToken } from '@/utils/auth'
+import router from '@/router' // @表示src目录
+
+const service = axios.create({
+ baseURL: process.env.VUE_APP_BASE_API,
+ timeout: 10000
+})
+
+service.interceptors.request.use(
+ config => {
+ if (store.getters.token) {
+ config.headers['Authorization'] = getToken()
+ }
+ return config
+ },
+ error => {
+ return Promise.reject(error)
+ }
+)
+
+// response interceptor
+service.interceptors.response.use(
+ response => {
+ const res = response.data
+ // console.log(res)
+ if (res.code === 401) {
+ removeToken()
+ MessageBox.confirm('登录已过期', '退出登录', {
+ confirmButtonText: '重新登录',
+ cancelButtonText: '取消',
+ type: 'warning'
+ }).then(() => {
+ router.push({ path: '/login' })
+ })
+ return
+ }
+ return res
+ },
+ error => {
+ console.log('err' + error) // for debug
+ Message({
+ message: error.message,
+ type: 'error',
+ duration: 5 * 1000
+ })
+ return Promise.reject(error)
+ }
+)
+
+export default service
diff --git a/ah-jjzhgd-web/src/views/basic/progress/components/ProcessTable2.vue b/ah-jjzhgd-web/src/views/basic/progress/components/ProcessTable2.vue
index 859759a..78f0f2d 100644
--- a/ah-jjzhgd-web/src/views/basic/progress/components/ProcessTable2.vue
+++ b/ah-jjzhgd-web/src/views/basic/progress/components/ProcessTable2.vue
@@ -41,7 +41,8 @@
-
+
+
@@ -102,6 +103,7 @@
+
@@ -122,7 +124,7 @@
label-width="120px"
>
-
+
@@ -288,6 +290,9 @@ export default {
// 新增
handleCreateProcess() {
this.dialogStatus = 'create'
+ if (this.processType === '变电' && this.temp.gxId) {
+ this.temp.nowGxId = this.temp.gxId
+ }
this.dialogFormVisible = true
},
createData() {
@@ -303,8 +308,6 @@ export default {
})
this.dialogFormVisible = false
this.getList2()
- }).finally(() => {
- // this.dialogFormVisible = false
})
}
})
diff --git a/ah-jjzhgd-web/src/views/device/sideband/index.vue b/ah-jjzhgd-web/src/views/device/sideband/index.vue
index 6772586..d940753 100644
--- a/ah-jjzhgd-web/src/views/device/sideband/index.vue
+++ b/ah-jjzhgd-web/src/views/device/sideband/index.vue
@@ -41,7 +41,7 @@
-
+
@@ -80,12 +80,12 @@
-
-
-
-
-
-
+
+
+
+
+
+
@@ -173,8 +173,8 @@ export default {
bdName: [{ required: true, message: '不能为空', trigger: 'blur' }],
bdCode: [{ required: true, message: '不能为空', trigger: 'blur' }],
bdIp: [{ required: true, message: '不能为空', trigger: 'blur' }],
- bdType: [{ required: true, message: '不能为空', trigger: 'change' }],
- typeCode: [{ required: true, message: '不能为空', trigger: 'change' }],
+ // bdType: [{ required: true, message: '不能为空', trigger: 'change' }],
+ // typeCode: [{ required: true, message: '不能为空', trigger: 'change' }],
gtId: [{ required: false, message: '不能为空', trigger: 'change' }],
bidCode: [{ required: false, message: '不能为空', trigger: 'change' }]
}
diff --git a/ah-jjzhgd-web/src/views/risk/dailyTask/components/RemoteWatchModal.vue b/ah-jjzhgd-web/src/views/risk/dailyTask/components/RemoteWatchModal.vue
index d5fd862..b6c9db1 100644
--- a/ah-jjzhgd-web/src/views/risk/dailyTask/components/RemoteWatchModal.vue
+++ b/ah-jjzhgd-web/src/views/risk/dailyTask/components/RemoteWatchModal.vue
@@ -5,76 +5,109 @@
+
+
+
+
-
![]()
-
本地录像
-
-
-
![]()
-
远程录像
+
![]()
+
录像
+
+
+
+
-
![]()
-
本地抓拍
-
-
-
![]()
-
远程抓拍
+
![]()
+
抓拍
-
![]()
-
聚焦
+
![]()
+
近焦
-
![]()
-
聚焦
+
![]()
+
远焦
-
![]()
-
缩放
+
![]()
+
缩小
-
![]()
-
缩放
+
![]()
+
放大
-
快捷键设置
+
+
+
+
+
+
+ 查询
+
+
+
+
+
+
+
+
+
-
@@ -129,9 +162,13 @@ import farFocus from '@/assets/images/video/farFocus.png'
import amplify from '@/assets/images/video/amplify.png'
import shrink from '@/assets/images/video/shrink.png'
-import { getRemoteWatchDetail } from '@/api/risk/dailyTask'
+import { getRemoteWatchDetail, getRemoteWatchTime } from '@/api/risk/dailyTask'
import _ from 'lodash/fp'
+import flvjs from 'flv.js'
+import { videoConfig } from '@/views/risk/dailyTask/config/video'
+import moment from 'moment'
+
const tmp = {
bidName: '',
sgStatus: '',
@@ -156,6 +193,18 @@ export default {
props: ['componentVisible', 'currentId'],
data() {
return {
+ // 视频播放实例
+ videoPlayer: null,
+ startVideoStatus: false,
+ mediaRecorder: null,
+ recordedChunks: [],
+ videoUrl: null,
+ //
+ list: [],
+ listQuery: {
+ classId: '',
+ workDay: moment().format('YYYY-MM-DD')
+ },
detail: _.cloneDeep(tmp),
workerList: [],
operateIcon: {
@@ -188,9 +237,14 @@ export default {
visible(val) {
if (val) {
this.getList()
+ this.initVideo()
+ this.handleFilter()
}
}
},
+ mounted() {
+ // this.initVideo()
+ },
methods: {
getList() {
getRemoteWatchDetail({ classId: this.currentId }).then(res => {
@@ -200,8 +254,194 @@ export default {
})
},
handleCloseModal() {
- this.detail = _.cloneDeep(tmp)
this.workerList = []
+ this.list = []
+ this.detail = _.cloneDeep(tmp)
+ this.closeVideo()
+ },
+ handleFilter() {
+ this.listQuery.classId = this.currentId
+ getRemoteWatchTime(this.listQuery).then(res => {
+ this.list = res.data
+ })
+ },
+ // 初始化视频监控
+ initVideo() {
+ this.$nextTick(() => {
+ const videoElement = this.$refs.videoPlayer
+ const url = `${videoConfig.url}/stream.flv?puid=${videoConfig.puid}&idx=${videoConfig.idx}&stream=${videoConfig.stream}&token=${videoConfig.token}`
+ try {
+ this.videoPlayer = flvjs.createPlayer({
+ type: 'flv',
+ url: url,
+ isLive: true,
+ hasAudio: false
+ }, {
+ enableWorker: false,
+ autoCleanupSourceBuffer: true, // 清理缓冲区
+ enableStashBuffer: false,
+ stashInitialSize: 128, // 减少首桢显示等待时长
+ statisticsInfoReportInterval: 600
+ })
+
+ this.videoPlayer.attachMediaElement(videoElement)
+ this.videoPlayer.load()
+
+ setTimeout(() => {
+ this.videoPlayer.play()
+ }, 200)
+ } catch (err) {
+ console.log(err)
+ }
+ })
+ },
+ // 销毁视频监控
+ closeVideo() {
+ if (this.videoPlayer) {
+ this.videoPlayer.pause()
+ this.videoPlayer.unload()
+ this.videoPlayer.detachMediaElement()
+ this.videoPlayer.destroy()
+ }
+ },
+ // 抓拍
+ getPic() {
+ const video = this.$refs.videoPlayer
+ const canvas = document.createElement('canvas')
+ canvas.width = video.videoWidth
+ canvas.height = video.videoHeight
+ const context = canvas.getContext('2d')
+ context.drawImage(video, 0, 0, canvas.width, canvas.height)
+ const dataURL = canvas.toDataURL('image/png')
+ const link = document.createElement('a')
+ link.download = 'screenshot.png'
+ link.href = dataURL
+ document.body.appendChild(link)
+ link.click()
+ document.body.removeChild(link)
+ },
+ // 录像
+ handlePhoto() {
+ this.startVideoStatus = !this.startVideoStatus
+ if (this.startVideoStatus) { // 开始录像
+ this.$message({
+ showClose: true,
+ message: '录制中···',
+ type: 'success',
+ duration: 2000
+ })
+ this.startRecording()
+ } else { // 结束录像
+ this.$message({
+ showClose: true,
+ message: '结束录制并下载文件中···',
+ type: 'success',
+ duration: 2000
+ })
+ this.stopRecording()
+ }
+ },
+ startRecording() {
+ const videoPlayer = this.$refs.videoPlayer
+ const stream = videoPlayer.captureStream()
+ this.mediaRecorder = new MediaRecorder(stream)
+
+ this.mediaRecorder.ondataavailable = event => {
+ if (event.data.size > 0) {
+ this.recordedChunks.push(event.data)
+ }
+ }
+
+ this.mediaRecorder.onstop = () => {
+ const blob = new Blob(this.recordedChunks, { type: 'video/mp4' })
+ this.recordedChunks = []
+ this.videoUrl = URL.createObjectURL(blob)
+ this.downloadVideo() // 自动触发下载
+ }
+
+ this.mediaRecorder.start()
+ },
+ stopRecording() {
+ if (this.mediaRecorder && this.mediaRecorder.state !== 'inactive') {
+ this.mediaRecorder.stop()
+ }
+ },
+ downloadVideo() {
+ const link = document.createElement('a')
+ link.href = this.videoUrl
+ link.download = 'recorded_video.mp4'
+ link.style.display = 'none'
+ document.body.appendChild(link)
+ link.click()
+ document.body.removeChild(link)
+ },
+ // 监控操作
+ handleControl(type) {
+ switch (type) {
+ case 'up':
+ videoConfig.handle(type, (res) => {
+ console.log(res)
+ })
+ break
+ case 'down':
+ videoConfig.handle(type, (res) => {
+ console.log(res)
+ })
+ break
+ case 'left':
+ videoConfig.handle(type, (res) => {
+ console.log(res)
+ })
+ break
+ case 'right':
+ videoConfig.handle(type, (res) => {
+ console.log(res)
+ })
+ break
+ case 'stopTurn':
+ videoConfig.handle(type, (res) => {
+ console.log(res)
+ })
+ break
+ case 'near':
+ videoConfig.handle(type, (res) => {
+ console.log(res)
+ })
+ break
+ case 'far':
+ videoConfig.handle(type, (res) => {
+ console.log(res)
+ })
+ break
+ case 'stopFocus':
+ videoConfig.handle(type, (res) => {
+ console.log(res)
+ })
+ break
+ case 'shrink':
+ videoConfig.handle(type, (res) => {
+ console.log(res)
+ })
+ break
+ case 'amplify':
+ videoConfig.handle(type, (res) => {
+ console.log(res)
+ })
+ break
+ case 'stopZoom':
+ videoConfig.handle(type, (res) => {
+ console.log(res)
+ })
+ break
+ case 'capture':
+ this.getPic()
+ break
+ case 'video':
+ this.handlePhoto()
+ break
+ default:
+ break
+ }
}
}
}
@@ -243,6 +483,7 @@ export default {
box-sizing: border-box;
.operate-left, .operate-right {
width: 15%;
+ text-align: center;
box-sizing: border-box;
}
.operate-center {
@@ -270,13 +511,25 @@ export default {
}
}
.watch-operate__keyboard {
- height: 40px;
+ flex: 1;
box-sizing: border-box;
+ padding-right: 10px;
}
}
.watch-play {
flex: 1;
height: 100%;
+ .video-player-wrap {
+ width: 100%;
+ height: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background-color: #000000;
+ .video-player {
+ width: 100%;
+ }
+ }
}
}
.main {
diff --git a/ah-jjzhgd-web/src/views/risk/dailyTask/config/video.js b/ah-jjzhgd-web/src/views/risk/dailyTask/config/video.js
new file mode 100644
index 0000000..332bc3e
--- /dev/null
+++ b/ah-jjzhgd-web/src/views/risk/dailyTask/config/video.js
@@ -0,0 +1,124 @@
+import { handleVideoPost } from '@/api/video'
+
+// 清新视频监控配置
+export const videoConfig = {
+ url: 'http://220.248.250.31:29605/icvs2',
+ baseUrl: 'http://220.248.250.31:29605/icvs2',
+ // baseUrl: '/videoPlayer',
+ puid: '201115201616581263',
+ idx: 0,
+ stream: 0,
+ token: '123456',
+ handle: function(type, scb) {
+ let url = ''
+ let data = {}
+ switch (type) {
+ case 'up':
+ url = `${this.baseUrl}/PTZ/C_PTZ_Turn?token=${this.token}`
+ data = {
+ puid: this.puid,
+ idx: this.idx,
+ motion: type
+ }
+ break
+ case 'down':
+ url = `${this.baseUrl}/PTZ/C_PTZ_Turn?token=${this.token}`
+ data = {
+ puid: this.puid,
+ idx: this.idx,
+ motion: type
+ }
+ break
+ case 'left':
+ url = `${this.baseUrl}/PTZ/C_PTZ_Turn?token=${this.token}`
+ data = {
+ puid: this.puid,
+ idx: this.idx,
+ motion: type
+ }
+ break
+ case 'right':
+ url = `${this.baseUrl}/PTZ/C_PTZ_Turn?token=${this.token}`
+ data = {
+ puid: this.puid,
+ idx: this.idx,
+ motion: type
+ }
+ break
+ case 'stopTurn':
+ url = `${this.baseUrl}/PTZ/C_PTZ_Turn?token=${this.token}`
+ data = {
+ puid: this.puid,
+ idx: this.idx,
+ motion: 'stop'
+ }
+ break
+ case 'near':
+ url = `${this.baseUrl}/PTZ/C_PTZ_MakeFocusNear?token=${this.token}`
+ data = {
+ puid: this.puid,
+ idx: this.idx
+ }
+ break
+ case 'far':
+ url = `${this.baseUrl}/PTZ/C_PTZ_MakeFocusFar?token=${this.token}`
+ data = {
+ puid: this.puid,
+ idx: this.idx
+ }
+ break
+ case 'stopFocus':
+ url = `${this.baseUrl}/PTZ/C_PTZ_StopFocusMove?token=${this.token}`
+ data = {
+ puid: this.puid,
+ idx: this.idx
+ }
+ break
+ case 'shrink':
+ url = `${this.baseUrl}/PTZ/C_PTZ_ZoomOutPicture?token=${this.token}`
+ data = {
+ puid: this.puid,
+ idx: this.idx
+ }
+ break
+ case 'amplify':
+ url = `${this.baseUrl}/PTZ/C_PTZ_ZoomInPicture?token=${this.token}`
+ data = {
+ puid: this.puid,
+ idx: this.idx
+ }
+ break
+ case 'stopZoom':
+ url = `${this.baseUrl}/PTZ/C_PTZ_StopPictureZoom?token=${this.token}`
+ data = {
+ puid: this.puid,
+ idx: this.idx
+ }
+ break
+ case 'startStorage':
+ url = `${this.baseUrl}/CSS/C_CSS_StartManualStorage?token=${this.token}`
+ data = {
+ puid: this.puid,
+ idx: this.idx
+ }
+ break
+ case 'endStorage':
+ url = `${this.baseUrl}/CSS/C_CSS_StopManualStorage?token=${this.token}`
+ data = {
+ puid: this.puid,
+ idx: this.idx
+ }
+ break
+ case 'loadStorage':
+ url = `${this.baseUrl}/CSS/C_CSS_QueryStorageFiles?token=${this.token}`
+ data = {
+ puid: this.puid,
+ idx: this.idx
+ }
+ break
+ default:
+ break
+ }
+ return handleVideoPost(url, data, scb)
+ }
+}
diff --git a/ah-jjzhgd-web/src/views/risk/dailyTask/index.vue b/ah-jjzhgd-web/src/views/risk/dailyTask/index.vue
index 5a8be58..239023d 100644
--- a/ah-jjzhgd-web/src/views/risk/dailyTask/index.vue
+++ b/ah-jjzhgd-web/src/views/risk/dailyTask/index.vue
@@ -37,10 +37,6 @@
导出
-
-
- 监控
-