|
|
@ -79,8 +79,8 @@ |
|
|
</div> |
|
|
</div> |
|
|
<div class="camera-actions"> |
|
|
<div class="camera-actions"> |
|
|
<button v-if="!embed" type="button" title="历史回放" @click.stop="openPlayback(slot)"><i class="el-icon-time" /></button> |
|
|
<button v-if="!embed" type="button" title="历史回放" @click.stop="openPlayback(slot)"><i class="el-icon-time" /></button> |
|
|
<button type="button" title="全屏" @click.stop="fullscreen(index)"><i class="el-icon-full-screen" /></button> |
|
|
|
|
|
<button type="button" title="关闭" @click.stop="closeSlot(index)"><i class="el-icon-close" /></button> |
|
|
|
|
|
|
|
|
<button type="button" :title="fullscreenIndex === index ? '退出放大' : '全屏'" @click.stop="fullscreen(index)"><i class="el-icon-full-screen" /></button> |
|
|
|
|
|
<button type="button" :title="fullscreenIndex === index ? '退出放大' : '关闭'" @click.stop="closePreview(index)"><i class="el-icon-close" /></button> |
|
|
</div> |
|
|
</div> |
|
|
</template> |
|
|
</template> |
|
|
<div v-else class="empty-slot"> |
|
|
<div v-else class="empty-slot"> |
|
|
@ -405,6 +405,7 @@ const PTZ_CODES = { |
|
|
this.$root.$emit('video-control-panel-updated', this.controlPanelExpanded) |
|
|
this.$root.$emit('video-control-panel-updated', this.controlPanelExpanded) |
|
|
} |
|
|
} |
|
|
document.addEventListener('fullscreenchange', this.handleFullscreenChange) |
|
|
document.addEventListener('fullscreenchange', this.handleFullscreenChange) |
|
|
|
|
|
document.addEventListener('webkitfullscreenchange', this.handleFullscreenChange) |
|
|
if (this.embed) { |
|
|
if (this.embed) { |
|
|
this.layout = this.normalizeLayout(this.embedLayout) |
|
|
this.layout = this.normalizeLayout(this.embedLayout) |
|
|
this.embedTreeVisible = this.embedTreeExpanded |
|
|
this.embedTreeVisible = this.embedTreeExpanded |
|
|
@ -434,6 +435,7 @@ const PTZ_CODES = { |
|
|
this.lifecycleId += 1 |
|
|
this.lifecycleId += 1 |
|
|
this.pageActive = false |
|
|
this.pageActive = false |
|
|
document.removeEventListener('fullscreenchange', this.handleFullscreenChange) |
|
|
document.removeEventListener('fullscreenchange', this.handleFullscreenChange) |
|
|
|
|
|
document.removeEventListener('webkitfullscreenchange', this.handleFullscreenChange) |
|
|
if (!this.embed) { |
|
|
if (!this.embed) { |
|
|
this.$root.$off('video-layout-change', this.changeLayout) |
|
|
this.$root.$off('video-layout-change', this.changeLayout) |
|
|
this.$root.$off('video-control-panel-change', this.changeControlPanel) |
|
|
this.$root.$off('video-control-panel-change', this.changeControlPanel) |
|
|
@ -657,9 +659,27 @@ const PTZ_CODES = { |
|
|
this.selectChannel(channel) |
|
|
this.selectChannel(channel) |
|
|
this.startChannel(channel) |
|
|
this.startChannel(channel) |
|
|
}, |
|
|
}, |
|
|
|
|
|
isSameCamera(left, right) { |
|
|
|
|
|
if (!left || !right || !left.cameraCode || left.cameraCode !== right.cameraCode) return false |
|
|
|
|
|
if (left.stationCode && right.stationCode && left.stationCode !== right.stationCode) return false |
|
|
|
|
|
return true |
|
|
|
|
|
}, |
|
|
|
|
|
// 同一摄像头只保留最新的一路,避免宫格里出现两路相同画面 |
|
|
|
|
|
async closeEarlierCopies(channel, keepIndex) { |
|
|
|
|
|
const indexes = [] |
|
|
|
|
|
this.slots.forEach((slot, slotIndex) => { |
|
|
|
|
|
if (slotIndex === keepIndex || !this.isSameCamera(slot, channel)) return |
|
|
|
|
|
indexes.push(slotIndex) |
|
|
|
|
|
}) |
|
|
|
|
|
for (const slotIndex of indexes) { |
|
|
|
|
|
await this.closeSlot(slotIndex, false) |
|
|
|
|
|
} |
|
|
|
|
|
}, |
|
|
async startChannel(channel, targetIndex = this.activeIndex, attemptedCameraCodes = [], streamType = this.streamType) { |
|
|
async startChannel(channel, targetIndex = this.activeIndex, attemptedCameraCodes = [], streamType = this.streamType) { |
|
|
if (!this.pageActive || !channel || !channel.cameraCode) return |
|
|
if (!this.pageActive || !channel || !channel.cameraCode) return |
|
|
const index = targetIndex |
|
|
const index = targetIndex |
|
|
|
|
|
await this.closeEarlierCopies(channel, index) |
|
|
|
|
|
if (!this.pageActive) return |
|
|
const current = this.slots[index] |
|
|
const current = this.slots[index] |
|
|
if (current && current.cameraCode === channel.cameraCode && current.streamType === streamType) { |
|
|
if (current && current.cameraCode === channel.cameraCode && current.streamType === streamType) { |
|
|
this.controlTarget = current |
|
|
this.controlTarget = current |
|
|
@ -890,13 +910,35 @@ const PTZ_CODES = { |
|
|
this.$message.error((error && error.message) || '视频播放失败,暂无其他可用摄像头') |
|
|
this.$message.error((error && error.message) || '视频播放失败,暂无其他可用摄像头') |
|
|
} |
|
|
} |
|
|
}, |
|
|
}, |
|
|
|
|
|
fullscreenElement() { |
|
|
|
|
|
return document.fullscreenElement || document.webkitFullscreenElement || null |
|
|
|
|
|
}, |
|
|
|
|
|
exitFullscreen() { |
|
|
|
|
|
const exit = document.exitFullscreen || document.webkitExitFullscreen |
|
|
|
|
|
if (exit) return exit.call(document) |
|
|
|
|
|
}, |
|
|
fullscreen(index) { |
|
|
fullscreen(index) { |
|
|
const card = this.$refs.cameraCards && this.$refs.cameraCards[index] |
|
|
const card = this.$refs.cameraCards && this.$refs.cameraCards[index] |
|
|
if (card && card.requestFullscreen) card.requestFullscreen() |
|
|
|
|
|
|
|
|
if (!card) return |
|
|
|
|
|
if (this.fullscreenElement() === card) { |
|
|
|
|
|
this.exitFullscreen() |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
const request = card.requestFullscreen || card.webkitRequestFullscreen |
|
|
|
|
|
if (request) request.call(card) |
|
|
|
|
|
}, |
|
|
|
|
|
// 放大后右下角关闭和全屏按钮一样,只退出放大,不停掉这一路视频 |
|
|
|
|
|
closePreview(index) { |
|
|
|
|
|
const card = this.$refs.cameraCards && this.$refs.cameraCards[index] |
|
|
|
|
|
if (card && this.fullscreenElement() === card) { |
|
|
|
|
|
this.exitFullscreen() |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
this.closeSlot(index) |
|
|
}, |
|
|
}, |
|
|
// 放大(全屏)观看时切主码流,退出全屏后恢复当前布局对应的码流 |
|
|
// 放大(全屏)观看时切主码流,退出全屏后恢复当前布局对应的码流 |
|
|
async handleFullscreenChange() { |
|
|
async handleFullscreenChange() { |
|
|
const fsElement = document.fullscreenElement |
|
|
|
|
|
|
|
|
const fsElement = this.fullscreenElement() |
|
|
if (fsElement) { |
|
|
if (fsElement) { |
|
|
const cards = this.$refs.cameraCards || [] |
|
|
const cards = this.$refs.cameraCards || [] |
|
|
const index = cards.indexOf(fsElement) |
|
|
const index = cards.indexOf(fsElement) |
|
|
@ -1159,7 +1201,7 @@ const PTZ_CODES = { |
|
|
.camera-grid { flex: 1; min-height: 0; display: grid; gap: 3px; background: rgba(14, 75, 146, .5); border: 1px solid #176bc2; } |
|
|
.camera-grid { flex: 1; min-height: 0; display: grid; gap: 3px; background: rgba(14, 75, 146, .5); border: 1px solid #176bc2; } |
|
|
.camera-card { position: relative; min-width: 0; min-height: 0; overflow: hidden; border: 1px solid transparent; background: #050b24; cursor: pointer; }.camera-card.active { border-color: #54ffff; box-shadow: inset 0 0 0 1px rgba(84,255,255,.35); } |
|
|
.camera-card { position: relative; min-width: 0; min-height: 0; overflow: hidden; border: 1px solid transparent; background: #050b24; cursor: pointer; }.camera-card.active { border-color: #54ffff; box-shadow: inset 0 0 0 1px rgba(84,255,255,.35); } |
|
|
.camera-label { position: absolute; left: 0; right: 0; top: 0; height: 28px; padding: 0 8px; display: flex; align-items: center; justify-content: space-between; color: #d8eaff; background: linear-gradient(180deg, rgba(0,10,36,.92), transparent); font-size: 11px; pointer-events: none; user-select: none; }.camera-label i { display: inline-block; width: 6px; height: 6px; margin-right: 5px; border-radius: 50%; background: #24e39f; box-shadow: 0 0 6px #24e39f; } |
|
|
.camera-label { position: absolute; left: 0; right: 0; top: 0; height: 28px; padding: 0 8px; display: flex; align-items: center; justify-content: space-between; color: #d8eaff; background: linear-gradient(180deg, rgba(0,10,36,.92), transparent); font-size: 11px; pointer-events: none; user-select: none; }.camera-label i { display: inline-block; width: 6px; height: 6px; margin-right: 5px; border-radius: 50%; background: #24e39f; box-shadow: 0 0 6px #24e39f; } |
|
|
.camera-actions { position: absolute; right: 5px; bottom: 5px; display: none; gap: 3px; }.camera-card:hover .camera-actions { display: flex; }.camera-actions button { width: 26px; height: 25px; border: 1px solid rgba(66, 167, 255, .55); color: #bfe0ff; background: rgba(4,28,78,.88); cursor: pointer; } |
|
|
|
|
|
|
|
|
.camera-actions { position: absolute; right: 5px; bottom: 5px; display: none; gap: 3px; }.camera-card:hover .camera-actions, .camera-card:fullscreen .camera-actions, .camera-card:-webkit-full-screen .camera-actions { display: flex; }.camera-actions button { width: 26px; height: 25px; border: 1px solid rgba(66, 167, 255, .55); color: #bfe0ff; background: rgba(4,28,78,.88); cursor: pointer; } |
|
|
.empty-slot { height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px; color: #466f9c; background: radial-gradient(circle at center, #0b2a61, #050b24 70%); user-select: none; }.empty-slot i { font-size: 28px; }.empty-slot span { font-size: 11px; } |
|
|
.empty-slot { height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px; color: #466f9c; background: radial-gradient(circle at center, #0b2a61, #050b24 70%); user-select: none; }.empty-slot i { font-size: 28px; }.empty-slot span { font-size: 11px; } |
|
|
.control-panel { position: absolute; top: 0; right: 0; bottom: 0; z-index: 4; width: 242px; min-width: 242px; height: auto; margin: 0; overflow: visible; border: 1px solid rgba(63,127,216,.72); border-radius: 2px; background: rgba(7, 24, 78, .82); box-sizing: border-box; cursor: move; user-select: none; } |
|
|
.control-panel { position: absolute; top: 0; right: 0; bottom: 0; z-index: 4; width: 242px; min-width: 242px; height: auto; margin: 0; overflow: visible; border: 1px solid rgba(63,127,216,.72); border-radius: 2px; background: rgba(7, 24, 78, .82); box-sizing: border-box; cursor: move; user-select: none; } |
|
|
.control-panel-body { height: 100%; padding: 10px; overflow-x: hidden; overflow-y: auto; box-sizing: border-box; } |
|
|
.control-panel-body { height: 100%; padding: 10px; overflow-x: hidden; overflow-y: auto; box-sizing: border-box; } |
|
|
|