|
|
@ -5,7 +5,6 @@ |
|
|
v-show="!embed || embedTreeVisible" |
|
|
v-show="!embed || embedTreeVisible" |
|
|
class="tree-panel" |
|
|
class="tree-panel" |
|
|
:style="embedTreeStyle" |
|
|
:style="embedTreeStyle" |
|
|
@mousedown="startEmbedPanelDrag('tree', $event)" |
|
|
|
|
|
> |
|
|
> |
|
|
<nvr-device-tree |
|
|
<nvr-device-tree |
|
|
v-if="!embed || sessionReady" |
|
|
v-if="!embed || sessionReady" |
|
|
@ -98,7 +97,7 @@ |
|
|
@mousedown="startEmbedPanelDrag('control', $event)" |
|
|
@mousedown="startEmbedPanelDrag('control', $event)" |
|
|
> |
|
|
> |
|
|
<div class="control-panel-body"> |
|
|
<div class="control-panel-body"> |
|
|
<section v-if="embed" class="control-section embed-layout-section"> |
|
|
|
|
|
|
|
|
<section class="control-section embed-layout-section"> |
|
|
<div class="section-title"><span>画面布局</span></div> |
|
|
<div class="section-title"><span>画面布局</span></div> |
|
|
<div class="embed-layout-switch" aria-label="画面布局"> |
|
|
<div class="embed-layout-switch" aria-label="画面布局"> |
|
|
<button |
|
|
<button |
|
|
@ -122,12 +121,13 @@ |
|
|
</section> |
|
|
</section> |
|
|
<section class="control-section"> |
|
|
<section class="control-section"> |
|
|
<div class="section-title section-title-stack"><span>云台控制</span><small>{{ selectedChannelName }}</small></div> |
|
|
<div class="section-title section-title-stack"><span>云台控制</span><small>{{ selectedChannelName }}</small></div> |
|
|
<div class="ptz-grid"> |
|
|
|
|
|
|
|
|
<div class="ptz-grid" :class="{ 'is-disabled': !ptzEnabled }"> |
|
|
<button |
|
|
<button |
|
|
v-for="button in ptzButtons" |
|
|
v-for="button in ptzButtons" |
|
|
:key="button.action" |
|
|
:key="button.action" |
|
|
type="button" |
|
|
type="button" |
|
|
:title="button.label" |
|
|
:title="button.label" |
|
|
|
|
|
:disabled="!ptzEnabled" |
|
|
@mousedown.prevent="startPtz(button.action)" |
|
|
@mousedown.prevent="startPtz(button.action)" |
|
|
@mouseup.prevent="stopPtz" |
|
|
@mouseup.prevent="stopPtz" |
|
|
@mouseleave="stopPtz" |
|
|
@mouseleave="stopPtz" |
|
|
@ -137,13 +137,15 @@ |
|
|
<i :class="button.icon" /> |
|
|
<i :class="button.icon" /> |
|
|
</button> |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
<div class="lens-actions"> |
|
|
|
|
|
|
|
|
<div class="lens-actions" :class="{ 'is-disabled': !ptzEnabled }"> |
|
|
<div v-for="tool in lensTools" :key="tool.label" class="lens-tool" :title="tool.label"> |
|
|
<div v-for="tool in lensTools" :key="tool.label" class="lens-tool" :title="tool.label"> |
|
|
<img :src="tool.icon" :alt="tool.label"> |
|
|
|
|
|
|
|
|
<img class="lens-glyph" :src="tool.leftIcon" :alt="tool.leftLabel"> |
|
|
|
|
|
<img class="lens-glyph" :src="tool.rightIcon" :alt="tool.rightLabel"> |
|
|
<button |
|
|
<button |
|
|
type="button" |
|
|
type="button" |
|
|
:aria-label="tool.leftLabel" |
|
|
:aria-label="tool.leftLabel" |
|
|
:title="tool.leftLabel" |
|
|
:title="tool.leftLabel" |
|
|
|
|
|
:disabled="!ptzEnabled" |
|
|
@mousedown.prevent="startPtz(tool.leftAction)" |
|
|
@mousedown.prevent="startPtz(tool.leftAction)" |
|
|
@mouseup.prevent="stopPtz" |
|
|
@mouseup.prevent="stopPtz" |
|
|
@mouseleave="stopPtz" |
|
|
@mouseleave="stopPtz" |
|
|
@ -154,6 +156,7 @@ |
|
|
type="button" |
|
|
type="button" |
|
|
:aria-label="tool.rightLabel" |
|
|
:aria-label="tool.rightLabel" |
|
|
:title="tool.rightLabel" |
|
|
:title="tool.rightLabel" |
|
|
|
|
|
:disabled="!ptzEnabled" |
|
|
@mousedown.prevent="startPtz(tool.rightAction)" |
|
|
@mousedown.prevent="startPtz(tool.rightAction)" |
|
|
@mouseup.prevent="stopPtz" |
|
|
@mouseup.prevent="stopPtz" |
|
|
@mouseleave="stopPtz" |
|
|
@mouseleave="stopPtz" |
|
|
@ -162,9 +165,9 @@ |
|
|
/> |
|
|
/> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
<div class="speed-row"> |
|
|
|
|
|
|
|
|
<div class="speed-row" :class="{ 'is-disabled': !ptzEnabled }"> |
|
|
<span>速度</span> |
|
|
<span>速度</span> |
|
|
<input v-model.number="ptzSpeed" type="range" min="1" max="7" :style="{ background: `linear-gradient(to right, #2787ed 0%, #2787ed ${((ptzSpeed - 1) / 6) * 100}%, transparent ${((ptzSpeed - 1) / 6) * 100}%, transparent 100%)` }"> |
|
|
|
|
|
|
|
|
<input v-model.number="ptzSpeed" type="range" min="1" max="7" :disabled="!ptzEnabled" :style="{ background: `linear-gradient(to right, #2787ed 0%, #2787ed ${((ptzSpeed - 1) / 6) * 100}%, transparent ${((ptzSpeed - 1) / 6) * 100}%, transparent 100%)` }"> |
|
|
<el-input-number |
|
|
<el-input-number |
|
|
v-model="ptzSpeed" |
|
|
v-model="ptzSpeed" |
|
|
class="speed-input" |
|
|
class="speed-input" |
|
|
@ -172,37 +175,38 @@ |
|
|
:max="7" |
|
|
:max="7" |
|
|
size="mini" |
|
|
size="mini" |
|
|
:controls="false" |
|
|
:controls="false" |
|
|
|
|
|
:disabled="!ptzEnabled" |
|
|
/> |
|
|
/> |
|
|
</div> |
|
|
</div> |
|
|
<div class="preset-quick-actions"> |
|
|
|
|
|
<button type="button" title="调用预置位" @click="callPreset"> |
|
|
|
|
|
|
|
|
<div class="preset-quick-actions" :class="{ 'is-disabled': !ptzEnabled }"> |
|
|
|
|
|
<button type="button" title="调用预置位" :disabled="!ptzEnabled" @click="callPreset"> |
|
|
<img :src="presetIcons.call" alt="调用预置位"> |
|
|
<img :src="presetIcons.call" alt="调用预置位"> |
|
|
</button> |
|
|
</button> |
|
|
<button type="button" title="新增预置位" @click="prepareNewPreset"> |
|
|
|
|
|
|
|
|
<button type="button" title="新增预置位" :disabled="!ptzEnabled" @click="prepareNewPreset"> |
|
|
<img :src="presetIcons.add" alt="新增预置位"> |
|
|
<img :src="presetIcons.add" alt="新增预置位"> |
|
|
</button> |
|
|
</button> |
|
|
<button type="button" title="编辑预置位" @click="prepareEditPreset"> |
|
|
|
|
|
|
|
|
<button type="button" title="编辑预置位" :disabled="!ptzEnabled" @click="prepareEditPreset"> |
|
|
<img :src="presetIcons.edit" alt="编辑预置位"> |
|
|
<img :src="presetIcons.edit" alt="编辑预置位"> |
|
|
</button> |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</section> |
|
|
</section> |
|
|
|
|
|
|
|
|
<section class="control-section"> |
|
|
|
|
|
<div class="section-title"><span>预置位</span><button type="button" @click="loadPresets"><i class="el-icon-refresh" /></button></div> |
|
|
|
|
|
<el-select v-model="presetId" size="small" placeholder="请选择预置位" style="width:100%"> |
|
|
|
|
|
|
|
|
<section class="control-section" :class="{ 'is-disabled': !ptzEnabled }"> |
|
|
|
|
|
<div class="section-title"><span>预置位</span><button type="button" :disabled="!ptzEnabled" @click="loadPresets"><i class="el-icon-refresh" /></button></div> |
|
|
|
|
|
<el-select v-model="presetId" size="small" placeholder="请选择预置位" style="width:100%" :disabled="!ptzEnabled"> |
|
|
<el-option v-for="item in presets" :key="item.id" :label="`${item.id} · ${item.name}`" :value="item.id" /> |
|
|
<el-option v-for="item in presets" :key="item.id" :label="`${item.id} · ${item.name}`" :value="item.id" /> |
|
|
</el-select> |
|
|
</el-select> |
|
|
<div class="preset-create"> |
|
|
<div class="preset-create"> |
|
|
<el-input-number v-model="newPresetId" :min="1" :max="255" size="mini" :controls="false" placeholder="编号" /> |
|
|
|
|
|
<el-input v-model.trim="newPresetName" size="mini" maxlength="32" placeholder="名称" /> |
|
|
|
|
|
|
|
|
<el-input-number v-model="newPresetId" :min="1" :max="255" size="mini" :controls="false" placeholder="编号" :disabled="!ptzEnabled" /> |
|
|
|
|
|
<el-input v-model.trim="newPresetName" size="mini" maxlength="32" placeholder="名称" :disabled="!ptzEnabled" /> |
|
|
</div> |
|
|
</div> |
|
|
<div class="section-actions"> |
|
|
<div class="section-actions"> |
|
|
<button type="button" @click="callPreset">调用</button> |
|
|
|
|
|
<button type="button" class="primary" @click="savePreset">保存</button> |
|
|
|
|
|
|
|
|
<button type="button" :disabled="!ptzEnabled" @click="callPreset">调用</button> |
|
|
|
|
|
<button type="button" class="primary" :disabled="!ptzEnabled" @click="savePreset">保存</button> |
|
|
</div> |
|
|
</div> |
|
|
</section> |
|
|
</section> |
|
|
|
|
|
|
|
|
<section v-if="!embed" class="control-section"> |
|
|
|
|
|
|
|
|
<section class="control-section"> |
|
|
<div class="section-title"><span>操作</span></div> |
|
|
<div class="section-title"><span>操作</span></div> |
|
|
<div class="operation-grid"> |
|
|
<div class="operation-grid"> |
|
|
<button type="button" class="wide" @click="capture"><i class="el-icon-camera" />抓图</button> |
|
|
<button type="button" class="wide" @click="capture"><i class="el-icon-camera" />抓图</button> |
|
|
@ -210,10 +214,10 @@ |
|
|
<button type="button" :class="{ active: !activeMuted }" @click="toggleMuted"> |
|
|
<button type="button" :class="{ active: !activeMuted }" @click="toggleMuted"> |
|
|
<i :class="activeMuted ? 'el-icon-turn-off-microphone' : 'el-icon-microphone'" />静音 |
|
|
<i :class="activeMuted ? 'el-icon-turn-off-microphone' : 'el-icon-microphone'" />静音 |
|
|
</button> |
|
|
</button> |
|
|
<button type="button" :disabled="wiperRunning" @click="wipeOnce"> |
|
|
|
|
|
|
|
|
<button type="button" :disabled="wiperRunning || !wiperEnabled" @click="wipeOnce"> |
|
|
<i :class="wiperRunning ? 'el-icon-loading' : 'el-icon-umbrella'" />{{ wiperRunning ? '擦拭中' : '雨刷器' }} |
|
|
<i :class="wiperRunning ? 'el-icon-loading' : 'el-icon-umbrella'" />{{ wiperRunning ? '擦拭中' : '雨刷器' }} |
|
|
</button> |
|
|
</button> |
|
|
<button type="button" @click="showPendingOperation('3D定位')"><i class="el-icon-aim" />3D定位</button> |
|
|
|
|
|
|
|
|
<button type="button" :disabled="!position3dEnabled" @click="showPendingOperation('3D定位')"><i class="el-icon-aim" />3D定位</button> |
|
|
</div> |
|
|
</div> |
|
|
</section> |
|
|
</section> |
|
|
</div> |
|
|
</div> |
|
|
@ -343,19 +347,19 @@ const PTZ_CODES = { |
|
|
], |
|
|
], |
|
|
lensTools: [ |
|
|
lensTools: [ |
|
|
{ |
|
|
{ |
|
|
label: '变倍', icon: require('@/assets/images/r1.png'), |
|
|
|
|
|
leftAction: 'ZOOM_OUT', leftLabel: '变倍缩小', |
|
|
|
|
|
rightAction: 'ZOOM_IN', rightLabel: '变倍放大' |
|
|
|
|
|
|
|
|
key: 'zoom', label: '变倍', |
|
|
|
|
|
leftAction: 'ZOOM_OUT', leftLabel: '变倍缩小', leftIcon: require('@/assets/images/ptz/zoom-left.png'), |
|
|
|
|
|
rightAction: 'ZOOM_IN', rightLabel: '变倍放大', rightIcon: require('@/assets/images/ptz/zoom-right.png') |
|
|
}, |
|
|
}, |
|
|
{ |
|
|
{ |
|
|
label: '聚焦', icon: require('@/assets/images/r2.png'), |
|
|
|
|
|
leftAction: 'FOCUS_FAR', leftLabel: '远聚焦', |
|
|
|
|
|
rightAction: 'FOCUS_NEAR', rightLabel: '近聚焦' |
|
|
|
|
|
|
|
|
key: 'focus', label: '聚焦', |
|
|
|
|
|
leftAction: 'FOCUS_FAR', leftLabel: '远聚焦', leftIcon: require('@/assets/images/ptz/focus-left.png'), |
|
|
|
|
|
rightAction: 'FOCUS_NEAR', rightLabel: '近聚焦', rightIcon: require('@/assets/images/ptz/focus-right.png') |
|
|
}, |
|
|
}, |
|
|
{ |
|
|
{ |
|
|
label: '光圈', icon: require('@/assets/images/r3.png'), |
|
|
|
|
|
leftAction: 'IRIS_CLOSE', leftLabel: '关闭光圈', |
|
|
|
|
|
rightAction: 'IRIS_OPEN', rightLabel: '打开光圈' |
|
|
|
|
|
|
|
|
key: 'iris', label: '光圈', |
|
|
|
|
|
leftAction: 'IRIS_CLOSE', leftLabel: '关闭光圈', leftIcon: require('@/assets/images/ptz/iris-left.png'), |
|
|
|
|
|
rightAction: 'IRIS_OPEN', rightLabel: '打开光圈', rightIcon: require('@/assets/images/ptz/iris-right.png') |
|
|
} |
|
|
} |
|
|
] |
|
|
] |
|
|
} |
|
|
} |
|
|
@ -379,6 +383,10 @@ const PTZ_CODES = { |
|
|
}, |
|
|
}, |
|
|
selectedChannel() { return this.controlTarget }, |
|
|
selectedChannel() { return this.controlTarget }, |
|
|
selectedChannelName() { return this.controlTarget ? (this.controlTarget.name || `通道 ${this.controlTarget.channel}`) : '未选择画面' }, |
|
|
selectedChannelName() { return this.controlTarget ? (this.controlTarget.name || `通道 ${this.controlTarget.channel}`) : '未选择画面' }, |
|
|
|
|
|
// 未选择画面时保持可点;选中后按 capabilities 置灰不支持的功能 |
|
|
|
|
|
ptzEnabled() { return this.hasCapability('ptz') }, |
|
|
|
|
|
wiperEnabled() { return this.hasCapability('wiper') }, |
|
|
|
|
|
position3dEnabled() { return this.hasCapability('position3d') }, |
|
|
embedTreeStyle() { |
|
|
embedTreeStyle() { |
|
|
if (!this.embed) return { width: this.treePanelWidth + 'px' } |
|
|
if (!this.embed) return { width: this.treePanelWidth + 'px' } |
|
|
const drag = this.embedPanelDrag.tree |
|
|
const drag = this.embedPanelDrag.tree |
|
|
@ -468,7 +476,7 @@ const PTZ_CODES = { |
|
|
}, |
|
|
}, |
|
|
startEmbedPanelDrag(panel, event) { |
|
|
startEmbedPanelDrag(panel, event) { |
|
|
if (event.button !== 0) return |
|
|
if (event.button !== 0) return |
|
|
if (!this.embed && panel !== 'control') return |
|
|
|
|
|
|
|
|
if (panel !== 'control') return |
|
|
if (event.target.closest('button, a, input, textarea, .el-input, .el-select, .el-input-number, .el-tree-node, .tree-content, .embed-toggle')) return |
|
|
if (event.target.closest('button, a, input, textarea, .el-input, .el-select, .el-input-number, .el-tree-node, .tree-content, .embed-toggle')) return |
|
|
const bounds = event.currentTarget.getBoundingClientRect() |
|
|
const bounds = event.currentTarget.getBoundingClientRect() |
|
|
const parent = event.currentTarget.offsetParent |
|
|
const parent = event.currentTarget.offsetParent |
|
|
@ -591,6 +599,13 @@ const PTZ_CODES = { |
|
|
if (status === undefined || status === null || status === '') return true |
|
|
if (status === undefined || status === null || status === '') return true |
|
|
return String(status) === '1' |
|
|
return String(status) === '1' |
|
|
}, |
|
|
}, |
|
|
|
|
|
hasCapability(key) { |
|
|
|
|
|
const channel = this.controlTarget |
|
|
|
|
|
if (!channel) return true |
|
|
|
|
|
const capabilities = channel.capabilities |
|
|
|
|
|
if (!capabilities || typeof capabilities !== 'object' || !Object.prototype.hasOwnProperty.call(capabilities, key)) return false |
|
|
|
|
|
return capabilities[key] === true |
|
|
|
|
|
}, |
|
|
onlineChannels(channels) { |
|
|
onlineChannels(channels) { |
|
|
return (channels || []).filter(item => this.isChannelOnline(item)) |
|
|
return (channels || []).filter(item => this.isChannelOnline(item)) |
|
|
}, |
|
|
}, |
|
|
@ -656,6 +671,7 @@ const PTZ_CODES = { |
|
|
await this.playChannels(channels) |
|
|
await this.playChannels(channels) |
|
|
}, |
|
|
}, |
|
|
openChannel(channel) { |
|
|
openChannel(channel) { |
|
|
|
|
|
if (channel && !this.isChannelOnline(channel)) return |
|
|
this.selectChannel(channel) |
|
|
this.selectChannel(channel) |
|
|
this.startChannel(channel) |
|
|
this.startChannel(channel) |
|
|
}, |
|
|
}, |
|
|
@ -676,7 +692,7 @@ const PTZ_CODES = { |
|
|
} |
|
|
} |
|
|
}, |
|
|
}, |
|
|
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 || !this.isChannelOnline(channel)) return |
|
|
const index = targetIndex |
|
|
const index = targetIndex |
|
|
await this.closeEarlierCopies(channel, index) |
|
|
await this.closeEarlierCopies(channel, index) |
|
|
if (!this.pageActive) return |
|
|
if (!this.pageActive) return |
|
|
@ -992,7 +1008,7 @@ const PTZ_CODES = { |
|
|
return |
|
|
return |
|
|
} |
|
|
} |
|
|
const code = PTZ_CODES[action] |
|
|
const code = PTZ_CODES[action] |
|
|
if (!code || this.ptzActive) return |
|
|
|
|
|
|
|
|
if (!code || this.ptzActive || !this.ptzEnabled) return |
|
|
if (!this.controlTarget) { this.$message.warning('请先选择有视频的画面'); return } |
|
|
if (!this.controlTarget) { this.$message.warning('请先选择有视频的画面'); return } |
|
|
this.ptzActive = true |
|
|
this.ptzActive = true |
|
|
this.ptzCameraCode = this.controlTarget.cameraCode |
|
|
this.ptzCameraCode = this.controlTarget.cameraCode |
|
|
@ -1014,7 +1030,7 @@ const PTZ_CODES = { |
|
|
}, |
|
|
}, |
|
|
async loadPresets(silent = false) { |
|
|
async loadPresets(silent = false) { |
|
|
const cameraCode = this.controlTarget && this.controlTarget.cameraCode |
|
|
const cameraCode = this.controlTarget && this.controlTarget.cameraCode |
|
|
if (!cameraCode) return |
|
|
|
|
|
|
|
|
if (!cameraCode || !this.ptzEnabled) return |
|
|
try { |
|
|
try { |
|
|
let result |
|
|
let result |
|
|
if (this.isAggMode) { |
|
|
if (this.isAggMode) { |
|
|
@ -1035,10 +1051,12 @@ const PTZ_CODES = { |
|
|
} catch (error) { if (!silent) this.$message.error(error.message) } |
|
|
} catch (error) { if (!silent) this.$message.error(error.message) } |
|
|
}, |
|
|
}, |
|
|
async callPreset() { |
|
|
async callPreset() { |
|
|
|
|
|
if (!this.ptzEnabled) return |
|
|
if (!this.presetId) { this.$message.warning('请选择预置位'); return } |
|
|
if (!this.presetId) { this.$message.warning('请选择预置位'); return } |
|
|
try { await this.sendPtz(11, String(this.presetId)); this.$message.success('预置位调用成功') } catch (error) { this.$message.error(error.message) } |
|
|
try { await this.sendPtz(11, String(this.presetId)); this.$message.success('预置位调用成功') } catch (error) { this.$message.error(error.message) } |
|
|
}, |
|
|
}, |
|
|
prepareNewPreset() { |
|
|
prepareNewPreset() { |
|
|
|
|
|
if (!this.ptzEnabled) return |
|
|
const used = new Set(this.presets.map(item => Number(item.id))) |
|
|
const used = new Set(this.presets.map(item => Number(item.id))) |
|
|
let nextId = 1 |
|
|
let nextId = 1 |
|
|
while (used.has(nextId) && nextId < 255) nextId += 1 |
|
|
while (used.has(nextId) && nextId < 255) nextId += 1 |
|
|
@ -1047,6 +1065,7 @@ const PTZ_CODES = { |
|
|
this.$message.info(`请填写预置位 ${nextId} 的名称后保存`) |
|
|
this.$message.info(`请填写预置位 ${nextId} 的名称后保存`) |
|
|
}, |
|
|
}, |
|
|
prepareEditPreset() { |
|
|
prepareEditPreset() { |
|
|
|
|
|
if (!this.ptzEnabled) return |
|
|
const preset = this.presets.find(item => item.id === this.presetId) |
|
|
const preset = this.presets.find(item => item.id === this.presetId) |
|
|
if (!preset) { this.$message.warning('请先选择需要编辑的预置位'); return } |
|
|
if (!preset) { this.$message.warning('请先选择需要编辑的预置位'); return } |
|
|
this.newPresetId = preset.id |
|
|
this.newPresetId = preset.id |
|
|
@ -1054,6 +1073,7 @@ const PTZ_CODES = { |
|
|
this.$message.info(`正在编辑预置位 ${preset.id}`) |
|
|
this.$message.info(`正在编辑预置位 ${preset.id}`) |
|
|
}, |
|
|
}, |
|
|
async savePreset() { |
|
|
async savePreset() { |
|
|
|
|
|
if (!this.ptzEnabled) return |
|
|
const channel = this.selectedChannel |
|
|
const channel = this.selectedChannel |
|
|
if (!channel) { this.$message.warning('请先选择摄像机'); return } |
|
|
if (!channel) { this.$message.warning('请先选择摄像机'); return } |
|
|
if (!Number.isInteger(this.newPresetId) || this.newPresetId < 1 || this.newPresetId > 255) { |
|
|
if (!Number.isInteger(this.newPresetId) || this.newPresetId < 1 || this.newPresetId > 255) { |
|
|
@ -1141,6 +1161,7 @@ const PTZ_CODES = { |
|
|
async wipeOnce() { |
|
|
async wipeOnce() { |
|
|
const cameraCode = this.controlTarget && this.controlTarget.cameraCode |
|
|
const cameraCode = this.controlTarget && this.controlTarget.cameraCode |
|
|
if (!cameraCode) { this.$message.warning('请先选择有视频的画面'); return } |
|
|
if (!cameraCode) { this.$message.warning('请先选择有视频的画面'); return } |
|
|
|
|
|
if (!this.wiperEnabled) return |
|
|
if (this.wiperRunning) return |
|
|
if (this.wiperRunning) return |
|
|
|
|
|
|
|
|
this.wiperRunning = true |
|
|
this.wiperRunning = true |
|
|
@ -1203,15 +1224,38 @@ const PTZ_CODES = { |
|
|
.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, .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; } |
|
|
.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; padding: 0; overflow: visible; border: 1px solid rgba(63,127,216,.72); border-radius: 2px; background: rgba(7, 24, 78, .42); color: #dcecff; font-size: 14px; line-height: normal; 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; } |
|
|
.control-section { padding-bottom: 12px; margin-bottom: 12px; border-bottom: 1px solid rgba(39, 128, 220, .28); }.section-title { height: 30px; min-width: 0; display: flex; align-items: center; justify-content: space-between; gap: 8px; overflow: hidden; color: #54ffff; }.section-title span { flex: none; white-space: nowrap; }.section-title small { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: right; color: #719bc8; }.section-title-stack { height: auto; flex-direction: column; align-items: flex-start; gap: 2px; overflow: visible; }.section-title-stack small { width: 100%; white-space: normal; text-align: left; line-height: 1.4; overflow: visible; text-overflow: unset; }.section-title button { border: 0; color: #75bfff; background: transparent; cursor: pointer; } |
|
|
|
|
|
|
|
|
.video-console:not(.is-embed) .control-panel-body { padding: 8px 10px; overflow-y: hidden; } |
|
|
|
|
|
.control-section { padding-bottom: 12px; margin-bottom: 12px; border-bottom: 1px solid rgba(39, 128, 220, .28); } |
|
|
|
|
|
.video-console:not(.is-embed) .control-section { padding-bottom: 8px; margin-bottom: 8px; } |
|
|
|
|
|
.video-console:not(.is-embed) .control-section:last-child { padding-bottom: 0; margin-bottom: 0; } |
|
|
|
|
|
.video-console:not(.is-embed) .section-title:not(.section-title-stack) { height: 26px; } |
|
|
|
|
|
.video-console:not(.is-embed) .section-title-stack { height: auto; margin-bottom: 4px; } |
|
|
|
|
|
.video-console:not(.is-embed) .ptz-grid { margin: 4px auto 8px; } |
|
|
|
|
|
.video-console:not(.is-embed) .lens-actions { margin-bottom: 8px; } |
|
|
|
|
|
.video-console:not(.is-embed) .preset-quick-actions { margin-top: 6px; } |
|
|
|
|
|
.video-console:not(.is-embed) .preset-create, |
|
|
|
|
|
.video-console:not(.is-embed) .section-actions, |
|
|
|
|
|
.video-console:not(.is-embed) .operation-grid { margin-top: 6px; }.section-title { height: 30px; min-width: 0; display: flex; align-items: center; justify-content: space-between; gap: 8px; overflow: hidden; color: #54ffff; }.section-title span { flex: none; white-space: nowrap; }.section-title small { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: right; color: #719bc8; }.section-title-stack { height: auto; flex-direction: column; align-items: flex-start; gap: 2px; overflow: visible; }.section-title-stack small { width: 100%; white-space: normal; text-align: left; line-height: 1.4; overflow: visible; text-overflow: unset; }.section-title button { border: 0; color: #75bfff; background: transparent; cursor: pointer; } |
|
|
.ptz-grid { width: 168px; margin: 6px auto 12px; display: grid; grid-template-columns: repeat(3, 50px); gap: 9px; }.ptz-grid button { width: 50px; height: 50px; border: 1px solid #3988ff; border-radius: 7px; color: #43c0ff; background: #01016b; box-shadow: none; cursor: pointer; transition: color .15s, background .15s, transform .15s; }.ptz-grid button i { font-size: 18px; font-weight: 700; }.ptz-grid button:hover { color: #43c0ff; background: #01016b; }.ptz-grid button:active { color: #ffffff; background: #01016b; transform: scale(.96); } |
|
|
.ptz-grid { width: 168px; margin: 6px auto 12px; display: grid; grid-template-columns: repeat(3, 50px); gap: 9px; }.ptz-grid button { width: 50px; height: 50px; border: 1px solid #3988ff; border-radius: 7px; color: #43c0ff; background: #01016b; box-shadow: none; cursor: pointer; transition: color .15s, background .15s, transform .15s; }.ptz-grid button i { font-size: 18px; font-weight: 700; }.ptz-grid button:hover { color: #43c0ff; background: #01016b; }.ptz-grid button:active { color: #ffffff; background: #01016b; transform: scale(.96); } |
|
|
.speed-row { display: grid; grid-template-columns: 34px minmax(0, 1fr) 48px; align-items: center; gap: 7px; color: #8eb7df; font-size: 12px; }.speed-row > input[type="range"] { min-width: 0; height: 8px; box-sizing: border-box; margin: 0; appearance: none; border: 1px solid #2787ed; border-radius: 4px; outline: none; cursor: pointer; }.speed-row > input[type="range"]::-webkit-slider-runnable-track { height: 6px; border: 0; border-radius: 3px; background: transparent; }.speed-row > input[type="range"]::-webkit-slider-thumb { width: 14px; height: 14px; margin-top: -4px; appearance: none; border: 0; border-radius: 50%; background: #2787ed; }.speed-row > input[type="range"]::-moz-range-track { height: 6px; border: 0; border-radius: 3px; background: transparent; }.speed-row > input[type="range"]::-moz-range-progress { background: transparent; }.speed-row > input[type="range"]::-moz-range-thumb { width: 14px; height: 14px; border: 0; border-radius: 50%; background: #2787ed; }.speed-input { width: 48px; } |
|
|
.speed-row { display: grid; grid-template-columns: 34px minmax(0, 1fr) 48px; align-items: center; gap: 7px; color: #8eb7df; font-size: 12px; }.speed-row > input[type="range"] { min-width: 0; height: 8px; box-sizing: border-box; margin: 0; appearance: none; border: 1px solid #2787ed; border-radius: 4px; outline: none; cursor: pointer; }.speed-row > input[type="range"]::-webkit-slider-runnable-track { height: 6px; border: 0; border-radius: 3px; background: transparent; }.speed-row > input[type="range"]::-webkit-slider-thumb { width: 14px; height: 14px; margin-top: -4px; appearance: none; border: 0; border-radius: 50%; background: #2787ed; }.speed-row > input[type="range"]::-moz-range-track { height: 6px; border: 0; border-radius: 3px; background: transparent; }.speed-row > input[type="range"]::-moz-range-progress { background: transparent; }.speed-row > input[type="range"]::-moz-range-thumb { width: 14px; height: 14px; border: 0; border-radius: 50%; background: #2787ed; }.speed-input { width: 48px; } |
|
|
.preset-quick-actions { display: flex; align-items: center; gap: 7px; margin: 9px 0 0 1px; }.preset-quick-actions button { display: grid; place-items: center; width: 24px; height: 24px; padding: 0; border: 1px solid rgba(48, 151, 248, .5); color: #58bfff; background: transparent; cursor: pointer; }.preset-quick-actions button:hover { background: rgba(31, 126, 222, .25); }.preset-quick-actions img { width: 18px; height: 18px; object-fit: contain; } |
|
|
.preset-quick-actions { display: flex; align-items: center; gap: 7px; margin: 9px 0 0 1px; }.preset-quick-actions button { display: grid; place-items: center; width: 24px; height: 24px; padding: 0; border: 1px solid rgba(48, 151, 248, .5); color: #58bfff; background: transparent; cursor: pointer; }.preset-quick-actions button:hover { background: rgba(31, 126, 222, .25); }.preset-quick-actions img { width: 18px; height: 18px; object-fit: contain; } |
|
|
.lens-actions { display: flex; align-items: center; justify-content: center; gap: 11px; min-height: 28px; margin: 0 0 10px; }.lens-tool { position: relative; width: 48px; height: 22px; overflow: hidden; }.lens-tool img { display: block; width: 100%; height: 100%; object-fit: fill; }.lens-tool button { position: absolute; top: 0; bottom: 0; width: 50%; padding: 0; border: 0; background: transparent; cursor: pointer; }.lens-tool button:first-of-type { left: 0; }.lens-tool button:last-of-type { right: 0; }.lens-tool button:active { background: rgba(73, 184, 255, .28); } |
|
|
|
|
|
.operation-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; margin-top: 9px; }.operation-grid button, .section-actions button { min-height: 29px; border: 1px solid rgba(47, 145, 241, .5); color: #abd4ff; background: #0b2b67; cursor: pointer; }.operation-grid button i { margin-right: 5px; }.operation-grid button.active, .section-actions button.primary { color: #fff; background: #176bc2; } |
|
|
|
|
|
|
|
|
.lens-actions { display: flex; align-items: center; justify-content: center; gap: 11px; min-height: 28px; margin: 0 0 10px; }.lens-tool { position: relative; width: 55px; height: 24px; display: grid; grid-template-columns: 1fr 1fr; border: 0; background: transparent; }.lens-glyph { display: block; width: 100%; height: 100%; object-fit: fill; pointer-events: none; }.lens-tool button { position: absolute; top: 0; bottom: 0; width: 50%; padding: 0; border: 0; background: transparent; cursor: pointer; }.lens-tool button:first-of-type { left: 0; }.lens-tool button:last-of-type { right: 0; }.lens-tool button:active { background: rgba(65, 187, 250, .18); } |
|
|
|
|
|
.operation-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; margin-top: 9px; }.operation-grid button, .section-actions button { min-height: 29px; border: 1px solid #4aa6ff; color: #d7eeff; background: transparent; font-weight: 600; cursor: pointer; }.operation-grid button i { margin-right: 5px; }.operation-grid button.active, .section-actions button.primary { color: #ffffff; border-color: #7ec4ff; background: transparent; } |
|
|
.operation-grid button:disabled { color: #6385aa; background: #081f4d; cursor: not-allowed; } |
|
|
.operation-grid button:disabled { color: #6385aa; background: #081f4d; cursor: not-allowed; } |
|
|
|
|
|
.ptz-grid.is-disabled, |
|
|
|
|
|
.lens-actions.is-disabled, |
|
|
|
|
|
.speed-row.is-disabled, |
|
|
|
|
|
.preset-quick-actions.is-disabled, |
|
|
|
|
|
.control-section.is-disabled { opacity: .45; } |
|
|
|
|
|
.ptz-grid button:disabled, |
|
|
|
|
|
.lens-tool button:disabled, |
|
|
|
|
|
.preset-quick-actions button:disabled, |
|
|
|
|
|
.section-actions button:disabled, |
|
|
|
|
|
.section-title button:disabled { cursor: not-allowed; } |
|
|
|
|
|
.ptz-grid button:disabled:hover, |
|
|
|
|
|
.ptz-grid button:disabled:active { color: inherit; background: transparent; transform: none; } |
|
|
.operation-grid .wide { grid-column: 1 / -1; } |
|
|
.operation-grid .wide { grid-column: 1 / -1; } |
|
|
::v-deep .el-select .el-input__inner, ::v-deep .preset-create .el-input__inner, ::v-deep .speed-input .el-input__inner { color: #cce6ff; border-color: rgba(52, 147, 241, .5); background: #09265e; } |
|
|
::v-deep .el-select .el-input__inner, ::v-deep .preset-create .el-input__inner, ::v-deep .speed-input .el-input__inner { color: #cce6ff; border-color: rgba(52, 147, 241, .5); background: #09265e; } |
|
|
::v-deep .speed-input .el-input__inner { height: 26px; padding: 0 7px; line-height: 26px; text-align: center; } |
|
|
::v-deep .speed-input .el-input__inner { height: 26px; padding: 0 7px; line-height: 26px; text-align: center; } |
|
|
@ -1224,9 +1268,112 @@ const PTZ_CODES = { |
|
|
.is-embed .console-body { position: relative; } |
|
|
.is-embed .console-body { position: relative; } |
|
|
.is-embed .monitor-workspace { position: absolute; inset: 0; z-index: 1; } |
|
|
.is-embed .monitor-workspace { position: absolute; inset: 0; z-index: 1; } |
|
|
.is-embed .tree-panel, |
|
|
.is-embed .tree-panel, |
|
|
.is-embed .control-panel { position: absolute; top: 0; bottom: 0; z-index: 4; overflow: visible; background: rgba(7, 24, 78, .8); } |
|
|
|
|
|
|
|
|
.is-embed .control-panel { |
|
|
|
|
|
position: absolute; |
|
|
|
|
|
top: 0; |
|
|
|
|
|
bottom: 0; |
|
|
|
|
|
z-index: 4; |
|
|
|
|
|
overflow: visible; |
|
|
|
|
|
background: rgba(7, 24, 78, .42); |
|
|
|
|
|
border: 1px solid rgba(63, 127, 216, .72); |
|
|
|
|
|
box-shadow: inset 0 0 18px rgba(36, 110, 200, .14), 0 0 8px rgba(36, 120, 210, .22); |
|
|
|
|
|
user-select: none; |
|
|
|
|
|
backdrop-filter: none; |
|
|
|
|
|
-webkit-backdrop-filter: none; |
|
|
|
|
|
} |
|
|
.is-embed .tree-panel { left: 0; } |
|
|
.is-embed .tree-panel { left: 0; } |
|
|
.is-embed .tree-panel .nvr-tree { background: transparent; } |
|
|
|
|
|
|
|
|
.is-embed .tree-panel ::v-deep .nvr-tree { background: transparent; border: 0 !important; } |
|
|
|
|
|
.is-embed .tree-panel ::v-deep .el-tree__empty-block { display: none; } |
|
|
|
|
|
.is-embed .tree-panel ::v-deep .tree-title, |
|
|
|
|
|
.is-embed .control-panel .control-section { border-color: rgba(90, 186, 255, .22); } |
|
|
|
|
|
.is-embed .tree-panel, |
|
|
|
|
|
.is-embed .control-panel, |
|
|
|
|
|
.is-embed .control-panel .section-title, |
|
|
|
|
|
.is-embed .tree-panel ::v-deep .tree-title { text-shadow: 0 1px 2px rgba(0, 10, 28, .9); } |
|
|
|
|
|
.is-embed .tree-panel ::v-deep .el-input__inner, |
|
|
|
|
|
.is-embed .control-panel ::v-deep .el-input__inner { |
|
|
|
|
|
color: #f4fbff !important; |
|
|
|
|
|
-webkit-text-fill-color: #f4fbff; |
|
|
|
|
|
font-weight: 600; |
|
|
|
|
|
background-color: transparent !important; |
|
|
|
|
|
border-color: #4aa6ff !important; |
|
|
|
|
|
box-shadow: none; |
|
|
|
|
|
} |
|
|
|
|
|
.is-embed .tree-panel ::v-deep .el-input__inner::-webkit-input-placeholder, |
|
|
|
|
|
.is-embed .control-panel ::v-deep .el-input__inner::-webkit-input-placeholder, |
|
|
|
|
|
.is-embed .tree-panel ::v-deep .el-input__inner::placeholder, |
|
|
|
|
|
.is-embed .control-panel ::v-deep .el-input__inner::placeholder { |
|
|
|
|
|
color: rgba(214, 242, 255, .78); |
|
|
|
|
|
-webkit-text-fill-color: rgba(214, 242, 255, .78); |
|
|
|
|
|
font-weight: 500; |
|
|
|
|
|
} |
|
|
|
|
|
.is-embed .control-panel ::v-deep .el-select .el-input .el-select__caret, |
|
|
|
|
|
.is-embed .tree-panel ::v-deep .el-select .el-input .el-select__caret { |
|
|
|
|
|
color: #e7f7ff; |
|
|
|
|
|
} |
|
|
|
|
|
.is-embed .tree-panel ::v-deep .tree-filters .el-button { |
|
|
|
|
|
color: #e7f8ff; |
|
|
|
|
|
border-color: #4aa6ff; |
|
|
|
|
|
background: transparent; |
|
|
|
|
|
box-shadow: none; |
|
|
|
|
|
} |
|
|
|
|
|
.control-panel .section-title { color: #7ad4ff; font-weight: 600; } |
|
|
|
|
|
.control-panel .section-title small { color: #d7eeff; } |
|
|
|
|
|
.control-panel .ptz-grid button { |
|
|
|
|
|
color: #7ad4ff; |
|
|
|
|
|
background: transparent; |
|
|
|
|
|
border-color: #4aa6ff; |
|
|
|
|
|
box-shadow: none; |
|
|
|
|
|
} |
|
|
|
|
|
.control-panel .ptz-grid button i { |
|
|
|
|
|
font-weight: 700; |
|
|
|
|
|
text-shadow: none; |
|
|
|
|
|
} |
|
|
|
|
|
.control-panel .ptz-grid button:hover, |
|
|
|
|
|
.control-panel .ptz-grid button:active { |
|
|
|
|
|
color: #ffffff; |
|
|
|
|
|
background: transparent; |
|
|
|
|
|
border-color: #7ec4ff; |
|
|
|
|
|
box-shadow: none; |
|
|
|
|
|
} |
|
|
|
|
|
.control-panel .speed-row { |
|
|
|
|
|
color: #f2fbff; |
|
|
|
|
|
font-weight: 600; |
|
|
|
|
|
} |
|
|
|
|
|
.control-panel .speed-row > input[type="range"] { |
|
|
|
|
|
border-color: #4aa6ff; |
|
|
|
|
|
} |
|
|
|
|
|
.control-panel .speed-row > input[type="range"]::-webkit-slider-thumb, |
|
|
|
|
|
.control-panel .speed-row > input[type="range"]::-moz-range-thumb { |
|
|
|
|
|
background: #8adfff; |
|
|
|
|
|
} |
|
|
|
|
|
.control-panel .lens-tool { |
|
|
|
|
|
border: 0; |
|
|
|
|
|
box-shadow: none; |
|
|
|
|
|
} |
|
|
|
|
|
.control-panel .preset-quick-actions button { |
|
|
|
|
|
border-color: #4aa6ff; |
|
|
|
|
|
} |
|
|
|
|
|
.control-panel .section-actions button, |
|
|
|
|
|
.control-panel .embed-layout-switch button { |
|
|
|
|
|
color: #d7eeff; |
|
|
|
|
|
border-color: #4aa6ff; |
|
|
|
|
|
font-weight: 600; |
|
|
|
|
|
background: transparent; |
|
|
|
|
|
box-shadow: none; |
|
|
|
|
|
} |
|
|
|
|
|
.control-panel .embed-layout-icon i { border-width: 1px; } |
|
|
|
|
|
.control-panel .embed-layout-switch button:hover, |
|
|
|
|
|
.control-panel .embed-layout-switch button.active, |
|
|
|
|
|
.control-panel .section-actions button.primary { |
|
|
|
|
|
color: #ffffff; |
|
|
|
|
|
border-color: #7ec4ff; |
|
|
|
|
|
background: transparent; |
|
|
|
|
|
box-shadow: none; |
|
|
|
|
|
} |
|
|
|
|
|
.is-embed .tree-panel ::v-deep .tree-content { scrollbar-color: rgba(96, 186, 255, .6) transparent; } |
|
|
|
|
|
.is-embed .tree-panel ::v-deep .tree-content::-webkit-scrollbar-track { background: transparent; } |
|
|
|
|
|
.is-embed .tree-panel ::v-deep .tree-content::-webkit-scrollbar-thumb { background: rgba(96, 186, 255, .55); border-color: transparent; box-shadow: 0 0 6px rgba(80, 190, 255, .45); } |
|
|
.is-embed .control-panel { right: 0; } |
|
|
.is-embed .control-panel { right: 0; } |
|
|
.is-embed .tree-panel button, |
|
|
.is-embed .tree-panel button, |
|
|
.is-embed .tree-panel input, |
|
|
.is-embed .tree-panel input, |
|
|
|