|
|
@ -7,6 +7,7 @@ |
|
|
:channels="channels" |
|
|
:channels="channels" |
|
|
:selected-code="selectedCode" |
|
|
:selected-code="selectedCode" |
|
|
:loading="loading" |
|
|
:loading="loading" |
|
|
|
|
|
:use-agg-api="isAggMode" |
|
|
@channels-loaded="handleTreeChannels" |
|
|
@channels-loaded="handleTreeChannels" |
|
|
@select="selectChannel" |
|
|
@select="selectChannel" |
|
|
@open="openChannel" |
|
|
@open="openChannel" |
|
|
@ -171,6 +172,7 @@ import { |
|
|
getNvrConfig, startLive, getLiveStatus, stopLive, controlPtz, |
|
|
getNvrConfig, startLive, getLiveStatus, stopLive, controlPtz, |
|
|
getPresetList, createPreset, createSnapshot, downloadSnapshot, controlWiper |
|
|
getPresetList, createPreset, createSnapshot, downloadSnapshot, controlWiper |
|
|
} from '@/api/nvrVideo' |
|
|
} from '@/api/nvrVideo' |
|
|
|
|
|
import { startRealtimeVideo, stopRealtimeVideo, aggPtzControl, aggPresetList, aggPresetCreate } from '@/api/portal' |
|
|
|
|
|
|
|
|
const PTZ_CODES = { |
|
|
const PTZ_CODES = { |
|
|
UP: 2, DOWN: 3, LEFT: 4, UP_LEFT: 5, DOWN_LEFT: 6, |
|
|
UP: 2, DOWN: 3, LEFT: 4, UP_LEFT: 5, DOWN_LEFT: 6, |
|
|
@ -256,6 +258,8 @@ export default { |
|
|
} |
|
|
} |
|
|
}, |
|
|
}, |
|
|
computed: { |
|
|
computed: { |
|
|
|
|
|
// 汇聚门户进入的会话:设备树与拉流走首页同款聚合接口 |
|
|
|
|
|
isAggMode() { return this.$store.getters.fromPortal }, |
|
|
capacity() { return this.layout }, |
|
|
capacity() { return this.layout }, |
|
|
visibleSlots() { return this.slots.slice(0, this.capacity) }, |
|
|
visibleSlots() { return this.slots.slice(0, this.capacity) }, |
|
|
activeSlot() { return this.slots[this.activeIndex] || null }, |
|
|
activeSlot() { return this.slots[this.activeIndex] || null }, |
|
|
@ -330,6 +334,13 @@ export default { |
|
|
this.loading = true |
|
|
this.loading = true |
|
|
this.connectionText = '读取配置' |
|
|
this.connectionText = '读取配置' |
|
|
try { |
|
|
try { |
|
|
|
|
|
if (this.isAggMode) { |
|
|
|
|
|
// 汇聚模式没有单站 NVR 配置,树由聚合接口提供 |
|
|
|
|
|
this.config = {} |
|
|
|
|
|
this.connectionText = '汇聚模式' |
|
|
|
|
|
this.connectionState = 'online' |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
const config = await getNvrConfig() |
|
|
const config = await getNvrConfig() |
|
|
this.config = config || {} |
|
|
this.config = config || {} |
|
|
// Keep config as metadata only until the device tree has supplied the |
|
|
// Keep config as metadata only until the device tree has supplied the |
|
|
@ -446,12 +457,20 @@ export default { |
|
|
this.presets = [] |
|
|
this.presets = [] |
|
|
this.presetId = null |
|
|
this.presetId = null |
|
|
try { |
|
|
try { |
|
|
slot.startPromise = startLive(channel, streamType) |
|
|
|
|
|
|
|
|
// 汇聚模式:拉流走首页同款聚合接口(响应含 hls/flv,applyLiveResult 直接兼容) |
|
|
|
|
|
slot.startPromise = this.isAggMode |
|
|
|
|
|
? startRealtimeVideo({ stationCode: channel.stationCode, cameraCode: channel.cameraCode }) |
|
|
|
|
|
: startLive(channel, streamType) |
|
|
const result = await slot.startPromise |
|
|
const result = await slot.startPromise |
|
|
if (this.slots[index] !== slot) return |
|
|
if (this.slots[index] !== slot) return |
|
|
this.applyLiveResult(index, slot, result) |
|
|
this.applyLiveResult(index, slot, result) |
|
|
if (!slot.streamSource.hlsUrl && !slot.streamSource.flvUrl) { |
|
|
if (!slot.streamSource.hlsUrl && !slot.streamSource.flvUrl) { |
|
|
this.pollLive(index, slot, 0) |
|
|
|
|
|
|
|
|
if (this.isAggMode) { |
|
|
|
|
|
slot.loading = false |
|
|
|
|
|
this.$message.error('视频流启动失败,请稍后重试') |
|
|
|
|
|
} else { |
|
|
|
|
|
this.pollLive(index, slot, 0) |
|
|
|
|
|
} |
|
|
return |
|
|
return |
|
|
} |
|
|
} |
|
|
if (index === this.activeIndex) this.loadPresets(true) |
|
|
if (index === this.activeIndex) this.loadPresets(true) |
|
|
@ -507,7 +526,13 @@ export default { |
|
|
if (slot.startPromise) { |
|
|
if (slot.startPromise) { |
|
|
try { await slot.startPromise } catch (_) { /* no stream session was created */ } |
|
|
try { await slot.startPromise } catch (_) { /* no stream session was created */ } |
|
|
} |
|
|
} |
|
|
try { await stopLive(slot.cameraCode) } catch (_) { /* release the UI slot first */ } |
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
if (this.isAggMode && slot.stationCode) { |
|
|
|
|
|
await stopRealtimeVideo({ stationCode: slot.stationCode, cameraCode: slot.cameraCode }) |
|
|
|
|
|
} else { |
|
|
|
|
|
await stopLive(slot.cameraCode) |
|
|
|
|
|
} |
|
|
|
|
|
} catch (_) { /* release the UI slot first */ } |
|
|
if (notify) this.$message.success('视频通道已关闭') |
|
|
if (notify) this.$message.success('视频通道已关闭') |
|
|
}, |
|
|
}, |
|
|
closeAll() { |
|
|
closeAll() { |
|
|
@ -572,7 +597,13 @@ export default { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
this.$set(this.slots, index, null) |
|
|
this.$set(this.slots, index, null) |
|
|
try { await stopLive(failedSlot.cameraCode) } catch (_) { /* continue switching cameras */ } |
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
if (this.isAggMode && failedSlot.stationCode) { |
|
|
|
|
|
await stopRealtimeVideo({ stationCode: failedSlot.stationCode, cameraCode: failedSlot.cameraCode }) |
|
|
|
|
|
} else { |
|
|
|
|
|
await stopLive(failedSlot.cameraCode) |
|
|
|
|
|
} |
|
|
|
|
|
} catch (_) { /* continue switching cameras */ } |
|
|
if (!this.pageActive) return |
|
|
if (!this.pageActive) return |
|
|
if (nextChannel) { |
|
|
if (nextChannel) { |
|
|
await this.startChannel(nextChannel, index, Array.from(attemptedCodes)) |
|
|
await this.startChannel(nextChannel, index, Array.from(attemptedCodes)) |
|
|
@ -611,10 +642,23 @@ export default { |
|
|
this.$router.push({ name: 'VideoHistoryPlayback', query: { cameraCode: slot.cameraCode }}) |
|
|
this.$router.push({ name: 'VideoHistoryPlayback', query: { cameraCode: slot.cameraCode }}) |
|
|
}, |
|
|
}, |
|
|
async sendPtz(controlCode, controlPara1 = '', controlPara2 = '', cameraCode = '') { |
|
|
async sendPtz(controlCode, controlPara1 = '', controlPara2 = '', cameraCode = '') { |
|
|
const targetCode = cameraCode || (this.controlTarget && this.controlTarget.cameraCode) |
|
|
|
|
|
|
|
|
const target = cameraCode |
|
|
|
|
|
? this.slots.find(slot => slot && slot.cameraCode === cameraCode) || this.controlTarget |
|
|
|
|
|
: this.controlTarget |
|
|
|
|
|
const targetCode = (target && target.cameraCode) || cameraCode |
|
|
if (!targetCode) { this.$message.warning('请先选择有视频的画面'); return } |
|
|
if (!targetCode) { this.$message.warning('请先选择有视频的画面'); return } |
|
|
const result = await controlPtz({ cameraCode: targetCode, controlCode, controlPara1, controlPara2 }) |
|
|
|
|
|
if (!result || Number(result.resultCode) !== 0) throw new Error('云台控制失败') |
|
|
|
|
|
|
|
|
let result |
|
|
|
|
|
if (this.isAggMode) { |
|
|
|
|
|
// 汇聚模式:stationCode 取当前控制摄像头所属的站(而非右上角站名筛选) |
|
|
|
|
|
const stationCode = target && target.stationCode |
|
|
|
|
|
if (!stationCode) { this.$message.warning('未获取到该摄像头所属站点信息'); return } |
|
|
|
|
|
result = await aggPtzControl({ stationCode, cameraCode: targetCode, controlCode, controlPara1, controlPara2 }) |
|
|
|
|
|
} else { |
|
|
|
|
|
result = await controlPtz({ cameraCode: targetCode, controlCode, controlPara1, controlPara2 }) |
|
|
|
|
|
} |
|
|
|
|
|
// 兼容两种响应:聚合代理可能包一层 data;resultCode 非 0 视为失败 |
|
|
|
|
|
const body = result && Object.prototype.hasOwnProperty.call(result, 'data') && result.data ? result.data : result |
|
|
|
|
|
if (body && body.resultCode !== undefined && Number(body.resultCode) !== 0) throw new Error('云台控制失败') |
|
|
}, |
|
|
}, |
|
|
startPtz(action) { |
|
|
startPtz(action) { |
|
|
if (action === 'STOP') { |
|
|
if (action === 'STOP') { |
|
|
@ -651,7 +695,17 @@ export default { |
|
|
const cameraCode = this.controlTarget && this.controlTarget.cameraCode |
|
|
const cameraCode = this.controlTarget && this.controlTarget.cameraCode |
|
|
if (!cameraCode) return |
|
|
if (!cameraCode) return |
|
|
try { |
|
|
try { |
|
|
const result = await getPresetList(cameraCode) |
|
|
|
|
|
|
|
|
let result |
|
|
|
|
|
if (this.isAggMode) { |
|
|
|
|
|
// 汇聚模式:stationCode 取当前摄像头所属的站 |
|
|
|
|
|
const stationCode = this.controlTarget && this.controlTarget.stationCode |
|
|
|
|
|
if (!stationCode) { this.$message.warning('未获取到该摄像头所属站点信息'); return } |
|
|
|
|
|
result = await aggPresetList({ stationCode, cameraCode }) |
|
|
|
|
|
} else { |
|
|
|
|
|
result = await getPresetList(cameraCode) |
|
|
|
|
|
} |
|
|
|
|
|
// 兼容聚合代理包一层 data 的响应 |
|
|
|
|
|
result = result && Object.prototype.hasOwnProperty.call(result, 'data') && result.data ? result.data : result |
|
|
if (!this.controlTarget || this.controlTarget.cameraCode !== cameraCode) return |
|
|
if (!this.controlTarget || this.controlTarget.cameraCode !== cameraCode) return |
|
|
const source = result && result.ptzPresetInfoList && result.ptzPresetInfoList.ptzPresetInfo |
|
|
const source = result && result.ptzPresetInfoList && result.ptzPresetInfoList.ptzPresetInfo |
|
|
this.presets = (Array.isArray(source) ? source : []).map(item => ({ id: Number(item.presetIndex), name: item.presetName || `预置位 ${item.presetIndex}` })).filter(item => item.id > 0) |
|
|
this.presets = (Array.isArray(source) ? source : []).map(item => ({ id: Number(item.presetIndex), name: item.presetName || `预置位 ${item.presetIndex}` })).filter(item => item.id > 0) |
|
|
@ -687,7 +741,20 @@ export default { |
|
|
} |
|
|
} |
|
|
try { |
|
|
try { |
|
|
await this.$confirm(`确认保存预置位 ${this.newPresetId}${this.newPresetName ? ` · ${this.newPresetName}` : ''}?`, '保存预置位') |
|
|
await this.$confirm(`确认保存预置位 ${this.newPresetId}${this.newPresetName ? ` · ${this.newPresetName}` : ''}?`, '保存预置位') |
|
|
await createPreset({ cameraCode: channel.cameraCode, nvrIp: channel.nvrIp, channel: Number(channel.channel), preset: this.newPresetId, presetName: this.newPresetName || null }) |
|
|
|
|
|
|
|
|
if (this.isAggMode) { |
|
|
|
|
|
// 汇聚模式:保存走代理,stationCode 取该摄像头所属的站 |
|
|
|
|
|
if (!channel.stationCode) { this.$message.warning('未获取到该摄像头所属站点信息'); return } |
|
|
|
|
|
await aggPresetCreate({ |
|
|
|
|
|
stationCode: channel.stationCode, |
|
|
|
|
|
cameraCode: channel.cameraCode, |
|
|
|
|
|
nvrIp: channel.nvrIp, |
|
|
|
|
|
channel: Number(channel.channel), |
|
|
|
|
|
preset: this.newPresetId, |
|
|
|
|
|
presetName: this.newPresetName || null |
|
|
|
|
|
}) |
|
|
|
|
|
} else { |
|
|
|
|
|
await createPreset({ cameraCode: channel.cameraCode, nvrIp: channel.nvrIp, channel: Number(channel.channel), preset: this.newPresetId, presetName: this.newPresetName || null }) |
|
|
|
|
|
} |
|
|
await this.loadPresets(true) |
|
|
await this.loadPresets(true) |
|
|
this.$message.success('预置位保存成功') |
|
|
this.$message.success('预置位保存成功') |
|
|
} catch (error) { if (error !== 'cancel' && error !== 'close') this.$message.error(error.message) } |
|
|
} catch (error) { if (error !== 'cancel' && error !== 'close') this.$message.error(error.message) } |
|
|
|