|
|
|
@ -9,6 +9,8 @@ |
|
|
|
:selected-code="selectedCode" |
|
|
|
:loading="loading" |
|
|
|
:use-agg-api="isAggMode" |
|
|
|
:station-code="embedStationCode" |
|
|
|
:default-expanded="embedTreeExpanded" |
|
|
|
@channels-loaded="handleTreeChannels" |
|
|
|
@select="selectChannel" |
|
|
|
@open="openChannel" |
|
|
|
@ -79,6 +81,28 @@ |
|
|
|
</main> |
|
|
|
|
|
|
|
<aside v-show="embed ? embedControlVisible : controlPanelExpanded" class="control-panel"> |
|
|
|
<section v-if="embed" class="control-section embed-layout-section"> |
|
|
|
<div class="section-title"><span>画面布局</span></div> |
|
|
|
<div class="embed-layout-switch" aria-label="画面布局"> |
|
|
|
<button |
|
|
|
v-for="item in layouts" |
|
|
|
:key="item.value" |
|
|
|
type="button" |
|
|
|
:class="{ active: layout === item.value }" |
|
|
|
:title="item.label" |
|
|
|
:aria-label="item.label" |
|
|
|
@click="changeLayout(item.value)" |
|
|
|
> |
|
|
|
<span |
|
|
|
class="embed-layout-icon" |
|
|
|
:class="`embed-layout-icon-${item.columns}`" |
|
|
|
:style="{ gridTemplateColumns: `repeat(${item.columns}, auto)` }" |
|
|
|
> |
|
|
|
<i v-for="cell in item.value" :key="cell" /> |
|
|
|
</span> |
|
|
|
</button> |
|
|
|
</div> |
|
|
|
</section> |
|
|
|
<section class="control-section"> |
|
|
|
<div class="section-title"><span>云台控制</span><small>{{ selectedChannelName }}</small></div> |
|
|
|
<div class="ptz-grid"> |
|
|
|
@ -214,7 +238,15 @@ const PTZ_CODES = { |
|
|
|
props: { |
|
|
|
embed: { type: Boolean, default: false }, |
|
|
|
// 嵌入页先出画面,token 就绪后再拉设备和视频 |
|
|
|
sessionReady: { type: Boolean, default: true } |
|
|
|
sessionReady: { type: Boolean, default: true }, |
|
|
|
// 地址参数:只看某一个场站,空字符串为全部场站 |
|
|
|
embedStationCode: { type: String, default: '' }, |
|
|
|
// 地址参数:导航树默认展开 |
|
|
|
embedTreeExpanded: { type: Boolean, default: false }, |
|
|
|
// 地址参数:云台控制面板默认展开 |
|
|
|
embedControlExpanded: { type: Boolean, default: false }, |
|
|
|
// 地址参数:默认宫格,1 / 4 / 9 / 16 |
|
|
|
embedLayout: { type: Number, default: 9 } |
|
|
|
}, |
|
|
|
data() { |
|
|
|
return { |
|
|
|
@ -239,8 +271,8 @@ const PTZ_CODES = { |
|
|
|
layout: 9, |
|
|
|
fullscreenIndex: -1, |
|
|
|
layouts: [ |
|
|
|
{ value: 1, label: '单画面' }, { value: 4, label: '四画面' }, |
|
|
|
{ value: 9, label: '九画面' }, { value: 16, label: '十六画面' } |
|
|
|
{ value: 1, columns: 1, label: '单画面' }, { value: 4, columns: 2, label: '四画面' }, |
|
|
|
{ value: 9, columns: 3, label: '九画面' }, { value: 16, columns: 4, label: '十六画面' } |
|
|
|
], |
|
|
|
slots: Array.from({ length: 16 }, () => null), |
|
|
|
activeIndex: 0, |
|
|
|
@ -327,6 +359,11 @@ const PTZ_CODES = { |
|
|
|
this.$root.$emit('video-control-panel-updated', this.controlPanelExpanded) |
|
|
|
} |
|
|
|
document.addEventListener('fullscreenchange', this.handleFullscreenChange) |
|
|
|
if (this.embed) { |
|
|
|
this.layout = this.normalizeLayout(this.embedLayout) |
|
|
|
this.embedTreeVisible = this.embedTreeExpanded |
|
|
|
this.embedControlVisible = this.embedControlExpanded |
|
|
|
} |
|
|
|
if (!this.embed || this.sessionReady) this.loadConfig() |
|
|
|
}, |
|
|
|
async activated() { |
|
|
|
@ -592,9 +629,14 @@ const PTZ_CODES = { |
|
|
|
this.ptzRepeatTimer = null |
|
|
|
this.streamCleanupPromise = this.closeAll() |
|
|
|
}, |
|
|
|
normalizeLayout(layout) { |
|
|
|
const value = Number(layout) |
|
|
|
return [1, 4, 9, 16].includes(value) ? value : 9 |
|
|
|
}, |
|
|
|
changeLayout(layout) { |
|
|
|
if (layout === this.layout) return |
|
|
|
this.layout = layout |
|
|
|
const nextLayout = this.normalizeLayout(layout) |
|
|
|
if (nextLayout === this.layout) return |
|
|
|
this.layout = nextLayout |
|
|
|
this.$root.$emit('video-layout-updated', layout) |
|
|
|
this.populateDefaultLayout() |
|
|
|
}, |
|
|
|
@ -951,4 +993,13 @@ const PTZ_CODES = { |
|
|
|
.embed-toggle-left { left: 0; } |
|
|
|
.embed-toggle-right { right: 0; } |
|
|
|
.embed-toggle-right.open { right: 242px; } |
|
|
|
.embed-layout-switch { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 6px; } |
|
|
|
.embed-layout-switch button { height: 36px; padding: 0; display: flex; align-items: center; justify-content: center; border: 1px solid rgba(39,135,237,.55); color: #7eb6ef; background: rgba(10,43,103,.55); cursor: pointer; } |
|
|
|
.embed-layout-switch button:hover, .embed-layout-switch button.active { color: #54ffff; border-color: #54ffff; background: rgba(23,107,194,.72); } |
|
|
|
.embed-layout-icon { display: grid; gap: 1px; } |
|
|
|
.embed-layout-icon i { width: 4px; height: 4px; border: 1px solid currentColor; box-sizing: border-box; } |
|
|
|
.embed-layout-icon-1 i { width: 14px; height: 14px; } |
|
|
|
.embed-layout-icon-2 i { width: 6px; height: 6px; } |
|
|
|
.embed-layout-icon-4 { gap: 1px; } |
|
|
|
.embed-layout-icon-4 i { width: 3px; height: 3px; } |
|
|
|
</style> |