diff --git a/.env.development b/.env.development index 2f7e897..09664d8 100644 --- a/.env.development +++ b/.env.development @@ -7,5 +7,7 @@ ENV = 'development' # 若依管理系统/开发环境 VUE_APP_BASE_API = '/dev-api' +VUE_APP_NVR_API = '/nvr-api' + # 路由懒加载 VUE_CLI_BABEL_TRANSPILE_MODULES = true diff --git a/.env.production b/.env.production index 8188545..ec7891a 100644 --- a/.env.production +++ b/.env.production @@ -6,3 +6,5 @@ ENV = 'production' # 若依管理系统/生产环境 VUE_APP_BASE_API = '/prod-api' + +VUE_APP_NVR_API = 'http://192.168.1.86:28081' diff --git a/.env.staging b/.env.staging index 2d51ff0..013ebde 100644 --- a/.env.staging +++ b/.env.staging @@ -10,3 +10,5 @@ ENV = 'staging' # 若依管理系统/测试环境 VUE_APP_BASE_API = '/stage-api' + +VUE_APP_NVR_API = '/nvr-api' diff --git a/package.json b/package.json index 8fa91c9..4d87b20 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "flv.js": "^1.6.2", "fuse.js": "6.4.3", "highlight.js": "9.18.5", + "hls.js": "^1.5.17", "js-beautify": "1.13.0", "js-cookie": "3.0.1", "jsencrypt": "3.0.0-rc.1", diff --git a/src/api/nvrVideo.js b/src/api/nvrVideo.js new file mode 100644 index 0000000..39495c9 --- /dev/null +++ b/src/api/nvrVideo.js @@ -0,0 +1,80 @@ +import axios from 'axios' + +const nvr = axios.create({ + baseURL: process.env.VUE_APP_NVR_API || '/nvr-api', + timeout: 30000, + withCredentials: false +}) + +nvr.interceptors.response.use( + response => response, + error => { + const response = error.response + const data = response && response.data + const message = data && (data.message || data.msg) + const normalized = new Error(message || error.message || 'NVR 服务请求失败') + normalized.status = response && response.status + normalized.response = response + return Promise.reject(normalized) + } +) + +const data = promise => promise.then(response => response.data) +const uiBase = '/hik/nvr/playback/hls/ui' +const playbackBase = '/hik/nvr/playback/hls' + +export function getNvrConfig() { + return data(nvr.get(`${uiBase}/config`)) +} + +export function startLive(cameraCode) { + return data(nvr.post(`${uiBase}/actions/live/start`, { cameraCode, streamType: 1 })) +} + +export function getLiveStatus(cameraCode) { + return data(nvr.get(`${uiBase}/actions/live/status`, { params: { cameraCode }})) +} + +export function stopLive(cameraCode) { + return data(nvr.post(`${uiBase}/actions/live/stop`, { cameraCode })) +} + +export function controlPtz(payload) { + return data(nvr.post('/device/ptzcontrol', payload)) +} + +export function getPresetList(deviceCode, domainCode) { + return data(nvr.get(`/device/ptzpresetlist/${encodeURIComponent(deviceCode)}/${encodeURIComponent(domainCode)}`)) +} + +export function createPreset(payload) { + return data(nvr.post(`${uiBase}/actions/preset/create`, payload)) +} + +export function createSnapshot(deviceCode, domainCode) { + return data(nvr.get(`/platform/platformSnapshot/${encodeURIComponent(deviceCode)}/${encodeURIComponent(domainCode)}`)) +} + +export function downloadSnapshot(taskId) { + return nvr.get(`/platform/snapshot/${encodeURIComponent(taskId)}`, { responseType: 'blob' }) +} + +export function createPlaybackSession(payload) { + return data(nvr.post(`${uiBase}/sessions`, payload)) +} + +export function getPlaybackSession(sessionId) { + return data(nvr.get(`${playbackBase}/sessions/${encodeURIComponent(sessionId)}`)) +} + +export function controlPlayback(sessionId, payload) { + return data(nvr.post(`${uiBase}/sessions/${encodeURIComponent(sessionId)}/controls`, payload)) +} + +export function deletePlaybackSession(sessionId) { + return data(nvr.delete(`${playbackBase}/sessions/${encodeURIComponent(sessionId)}`)) +} + +export function downloadPlaybackClip(payload) { + return nvr.post('/hik/nvr/playback/clips/download', payload, { responseType: 'blob', timeout: 120000 }) +} diff --git a/src/assets/images/nav/nav-area.png b/src/assets/images/nav/nav-area.png new file mode 100644 index 0000000..85112ba Binary files /dev/null and b/src/assets/images/nav/nav-area.png differ diff --git a/src/assets/images/nav/nav-base-data.png b/src/assets/images/nav/nav-base-data.png new file mode 100644 index 0000000..81f1200 Binary files /dev/null and b/src/assets/images/nav/nav-base-data.png differ diff --git a/src/assets/images/nav/nav-defect.png b/src/assets/images/nav/nav-defect.png new file mode 100644 index 0000000..2a37be3 Binary files /dev/null and b/src/assets/images/nav/nav-defect.png differ diff --git a/src/assets/images/nav/nav-device.png b/src/assets/images/nav/nav-device.png new file mode 100644 index 0000000..fde1f8f Binary files /dev/null and b/src/assets/images/nav/nav-device.png differ diff --git a/src/assets/images/nav/nav-home.png b/src/assets/images/nav/nav-home.png new file mode 100644 index 0000000..793c828 Binary files /dev/null and b/src/assets/images/nav/nav-home.png differ diff --git a/src/assets/images/nav/nav-monitor.png b/src/assets/images/nav/nav-monitor.png new file mode 100644 index 0000000..5f44ae3 Binary files /dev/null and b/src/assets/images/nav/nav-monitor.png differ diff --git a/src/assets/images/nav/nav-patrol-plan.png b/src/assets/images/nav/nav-patrol-plan.png new file mode 100644 index 0000000..2b26068 Binary files /dev/null and b/src/assets/images/nav/nav-patrol-plan.png differ diff --git a/src/assets/images/nav/nav-patrol-report.png b/src/assets/images/nav/nav-patrol-report.png new file mode 100644 index 0000000..0232dbd Binary files /dev/null and b/src/assets/images/nav/nav-patrol-report.png differ diff --git a/src/assets/images/nav/nav-patrol-schedule.png b/src/assets/images/nav/nav-patrol-schedule.png new file mode 100644 index 0000000..47c9c00 Binary files /dev/null and b/src/assets/images/nav/nav-patrol-schedule.png differ diff --git a/src/assets/images/nav/nav-patrol-task.png b/src/assets/images/nav/nav-patrol-task.png new file mode 100644 index 0000000..f8d2ebb Binary files /dev/null and b/src/assets/images/nav/nav-patrol-task.png differ diff --git a/src/assets/images/nav/nav-site-implementation.png b/src/assets/images/nav/nav-site-implementation.png new file mode 100644 index 0000000..d74ab9e Binary files /dev/null and b/src/assets/images/nav/nav-site-implementation.png differ diff --git a/src/assets/images/nav/nav-site.png b/src/assets/images/nav/nav-site.png new file mode 100644 index 0000000..c88e10d Binary files /dev/null and b/src/assets/images/nav/nav-site.png differ diff --git a/src/assets/images/nav/nav-system.png b/src/assets/images/nav/nav-system.png new file mode 100644 index 0000000..d81524e Binary files /dev/null and b/src/assets/images/nav/nav-system.png differ diff --git a/src/assets/images/nav/nav-task-management.png b/src/assets/images/nav/nav-task-management.png new file mode 100644 index 0000000..76da924 Binary files /dev/null and b/src/assets/images/nav/nav-task-management.png differ diff --git a/src/assets/images/nav/nav-video-playback.png b/src/assets/images/nav/nav-video-playback.png new file mode 100644 index 0000000..5708c7f Binary files /dev/null and b/src/assets/images/nav/nav-video-playback.png differ diff --git a/src/assets/images/nav/nav-video-preview.png b/src/assets/images/nav/nav-video-preview.png new file mode 100644 index 0000000..d02ef2f Binary files /dev/null and b/src/assets/images/nav/nav-video-preview.png differ diff --git a/src/components/Breadcrumb/index.vue b/src/components/Breadcrumb/index.vue index c8e855e..e77313a 100644 --- a/src/components/Breadcrumb/index.vue +++ b/src/components/Breadcrumb/index.vue @@ -46,7 +46,7 @@ export default { matched = router.matched.filter(item => item.meta && item.meta.title) } // 判断是否为首页 - if (!this.isDashboard(matched[0])) { + if (!this.isPatrolWorkspace() && !this.isDashboard(matched[0])) { matched = [{ path: "/index", meta: { title: "首页" } }].concat(matched) } this.levelList = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false) @@ -85,6 +85,9 @@ export default { } this.$router.push(path) }, + isPatrolWorkspace() { + return this.$route.matched.some(route => route.meta && route.meta.patrolWorkspace) + }, titleFn(item, children) { let str = ""; let lang = localStorage.getItem("language"); diff --git a/src/layout/components/AppMain.vue b/src/layout/components/AppMain.vue index 07b87a1..e808486 100644 --- a/src/layout/components/AppMain.vue +++ b/src/layout/components/AppMain.vue @@ -66,6 +66,14 @@ export default { padding-top: 108px; } +.app-main.patrolWorkspace.hasTagsView { + height: calc(100vh - 40px); +} + +.fixed-header + .app-main.patrolWorkspace.hasTagsView { + padding-top: 40px; +} + diff --git a/src/router/index.js b/src/router/index.js index ea124f2..f57b2d9 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -61,6 +61,69 @@ export const constantRoutes = [ component: () => import('@/views/error/401'), hidden: true }, + { + path: '/inspection-workspace', + component: Layout, + redirect: '/inspection-workspace/task', + hidden: true, + children: [ + { + path: 'task', + component: () => import('@/views/cards/taskSummary/inspectionTask'), + name: 'InspectionWorkspaceTask', + meta: { title: '巡视任务', activeMenu: '/inspection-workspace/task', patrolWorkspace: true } + }, + { + path: 'plan', + component: () => import('@/views/cards/patrolPlan/notstart'), + name: 'InspectionWorkspacePlan', + meta: { title: '巡视方案', activeMenu: '/inspection-workspace/plan', patrolWorkspace: true } + }, + { + path: 'points', + component: () => import('@/views/cards/site_management/site_management'), + name: 'InspectionWorkspacePoints', + meta: { title: '点位管理', activeMenu: '/inspection-workspace/points', patrolWorkspace: true } + }, + { + path: 'report', + component: () => import('@/views/cards/patrolReport/InspectionReporter'), + name: 'InspectionWorkspaceReport', + meta: { title: '巡视报告', activeMenu: '/inspection-workspace/report', patrolWorkspace: true } + }, + { + path: 'report/detail', + component: () => import('@/views/cards/patrolReport/patrolReportDetail'), + name: 'InspectionWorkspaceReportDetail', + hidden: true, + meta: { title: '巡视报告详情', activeMenu: '/inspection-workspace/report', patrolWorkspace: true } + }, + { + path: 'schedule', + component: () => import('@/views/cards/tasksShow/index'), + name: 'InspectionWorkspaceSchedule', + meta: { title: '巡视计划', activeMenu: '/inspection-workspace/schedule', patrolWorkspace: true } + }, + { + path: 'defects', + component: () => import('@/views/cards/defectRecord/index'), + name: 'InspectionWorkspaceDefects', + meta: { title: '缺陷记录', activeMenu: '/inspection-workspace/defects', patrolWorkspace: true } + }, + { + path: 'video-preview', + component: () => import('@/views/video/LivePreview'), + name: 'InspectionWorkspaceVideoPreview', + meta: { title: '视频预览', activeMenu: '/inspection-workspace/video-preview', patrolWorkspace: true } + }, + { + path: 'video-playback', + component: () => import('@/views/video/HistoryPlayback'), + name: 'InspectionWorkspaceVideoPlayback', + meta: { title: '历史回放', activeMenu: '/inspection-workspace/video-playback', patrolWorkspace: true } + } + ] + }, { path: '', component: Layout, @@ -181,6 +244,34 @@ export const constantRoutes = [ meta: { title: '缺陷记录', activeMenu: '/defect-record' } } ] + }, + { + path: '/video-preview', + component: Layout, + redirect: '/video-preview/index', + meta: { title: '视频预览' }, + children: [ + { + path: 'index', + component: () => import('@/views/video/LivePreview'), + name: 'VideoLivePreview', + meta: { title: '视频预览', activeMenu: '/video-preview' } + } + ] + }, + { + path: '/video-playback', + component: Layout, + redirect: '/video-playback/index', + meta: { title: '历史回放' }, + children: [ + { + path: 'index', + component: () => import('@/views/video/HistoryPlayback'), + name: 'VideoHistoryPlayback', + meta: { title: '历史回放', activeMenu: '/video-playback' } + } + ] } ] diff --git a/src/views/cards/components/ImagePreviewZoomTemperature/index.vue b/src/views/cards/components/ImagePreviewZoomTemperature/index.vue index 2892400..fa00bfe 100644 --- a/src/views/cards/components/ImagePreviewZoomTemperature/index.vue +++ b/src/views/cards/components/ImagePreviewZoomTemperature/index.vue @@ -31,7 +31,7 @@ --> diff --git a/src/views/video/HistoryPlayback.vue b/src/views/video/HistoryPlayback.vue new file mode 100644 index 0000000..6495314 --- /dev/null +++ b/src/views/video/HistoryPlayback.vue @@ -0,0 +1,357 @@ + + + + + diff --git a/src/views/video/LivePreview.vue b/src/views/video/LivePreview.vue new file mode 100644 index 0000000..d4c127d --- /dev/null +++ b/src/views/video/LivePreview.vue @@ -0,0 +1,475 @@ + + + + + diff --git a/src/views/video/components/NvrDeviceTree.vue b/src/views/video/components/NvrDeviceTree.vue new file mode 100644 index 0000000..bfd1cef --- /dev/null +++ b/src/views/video/components/NvrDeviceTree.vue @@ -0,0 +1,186 @@ + + + + + diff --git a/src/views/video/components/NvrStreamPlayer.vue b/src/views/video/components/NvrStreamPlayer.vue new file mode 100644 index 0000000..a04742d --- /dev/null +++ b/src/views/video/components/NvrStreamPlayer.vue @@ -0,0 +1,154 @@ + + + + + diff --git a/vue.config.js b/vue.config.js index 53cd73a..0d46ee5 100644 --- a/vue.config.js +++ b/vue.config.js @@ -34,7 +34,15 @@ module.exports = { host: '0.0.0.0', port: port, open: true, + historyApiFallback: true, proxy: { + '/nvr-api': { + target: 'http://192.168.1.86:28081', + changeOrigin: true, + pathRewrite: { + '^/nvr-api': '' + } + }, // detail: https://cli.vuejs.org/config/#devserver-proxy [process.env.VUE_APP_BASE_API]: { // target: `http://localhost:8080`,