@@ -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; }
diff --git a/src/views/video/LivePreviewEmbed.vue b/src/views/video/LivePreviewEmbed.vue
index 939694e..c6f93ec 100644
--- a/src/views/video/LivePreviewEmbed.vue
+++ b/src/views/video/LivePreviewEmbed.vue
@@ -1,6 +1,13 @@
@@ -25,13 +32,33 @@ function readAccessToken(result) {
return ''
}
+const LAYOUTS = [1, 4, 9, 16]
+
+function readQuery(route) {
+ const query = route && route.query ? route.query : {}
+ const layout = Number(query.layout)
+ const tree = String(query.tree == null ? '' : query.tree).toLowerCase()
+ const control = String(query.control == null ? '' : query.control).toLowerCase()
+ return {
+ stationCode: query.stationCode ? String(query.stationCode) : '',
+ treeExpanded: tree === '1' || tree === 'open' || tree === 'true',
+ controlExpanded: control === '1' || control === 'open' || control === 'true',
+ layout: LAYOUTS.includes(layout) ? layout : 9
+ }
+}
+
export default {
name: 'LivePreviewEmbed',
components: { LivePreview },
data() {
+ const query = readQuery(this.$route)
return {
sessionReady: false,
- bootError: ''
+ bootError: '',
+ stationCode: query.stationCode,
+ treeExpanded: query.treeExpanded,
+ controlExpanded: query.controlExpanded,
+ layout: query.layout
}
},
created() {
diff --git a/src/views/video/components/NvrDeviceTree.vue b/src/views/video/components/NvrDeviceTree.vue
index 9ca1b0a..59ca175 100644
--- a/src/views/video/components/NvrDeviceTree.vue
+++ b/src/views/video/components/NvrDeviceTree.vue
@@ -87,7 +87,11 @@ export default {
loading: { type: Boolean, default: false },
openHint: { type: String, default: '单击通道打开实时预览' },
// 汇聚门户模式:设备树改走 /agg/tree/channel(节点带 stationCode,结构与单站树一致)
- useAggApi: { type: Boolean, default: false }
+ useAggApi: { type: Boolean, default: false },
+ // 嵌入页把地址里的 stationCode 传给汇聚设备树接口;空字符串表示全部场站
+ stationCode: { type: String, default: '' },
+ // 嵌入页可指定导航树默认展开或收起
+ defaultExpanded: { type: Boolean, default: true }
},
data() {
return {
@@ -117,13 +121,17 @@ export default {
this.internalLoading = true
try {
const queryParams = { patroldeviceName: '', onlineStatus: '', patroldeviceTypeFlag: 'ipc' }
+ if (this.useAggApi && this.stationCode) queryParams.stationCode = this.stationCode
const response = this.useAggApi
? await getChannelTree(queryParams)
: await getAreaEqubookTreeSelect(queryParams)
- this.treeNodes = Array.isArray(response.data) ? response.data : []
+ const nodes = Array.isArray(response.data) ? response.data : []
+ this.treeNodes = nodes
this.expandedKeys = []
- const walk = nodes => (nodes || []).forEach(node => { if (node.children && node.children.length) { this.expandedKeys.push(node.id); walk(node.children) } })
- walk(this.treeNodes)
+ if (this.defaultExpanded) {
+ const walk = items => (items || []).forEach(node => { if (node.children && node.children.length) { this.expandedKeys.push(node.id); walk(node.children) } })
+ walk(this.treeNodes)
+ }
this.refreshTree()
this.$emit('channels-loaded', this.channelNodes.map(this.normalizeChannel))
} catch (error) {