| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397 |
- import { ref, type Ref } from 'vue'
- import { ElMessage, ElMessageBox } from 'element-plus'
- import { useI18n } from 'vue-i18n'
- import {
- type PresetInfo,
- presetList,
- presetGoto,
- presetSet,
- presetRemove,
- getPTZCapabilities,
- ptzControl
- } from '@/api/camera'
- import type { LiveStreamDTO, PTZAction } from '@/types'
- import type { PTZCapabilities, LocalPreset, TimelinePoint } from '../types'
- const directionToAction: Record<string, PTZAction> = {
- UP: 'up',
- DOWN: 'down',
- LEFT: 'left',
- RIGHT: 'right',
- STOP: 'stop'
- }
- export function usePTZ(
- currentMediaStream: Ref<LiveStreamDTO | null>,
- timelinePoints: Ref<TimelinePoint[]>,
- callbacks: {
- sortAndRenumberPoints: () => void
- saveTimelineConfig: () => void
- selectedPoint: Ref<TimelinePoint | null>
- }
- ) {
- const { t } = useI18n({ useScope: 'global' })
- const ptzSpeed = ref(50)
- const zoomValue = ref(0)
- const ptzPresetList = ref<PresetInfo[]>([])
- const activePresetId = ref<string | null>(null)
- const editingPresetId = ref<string | null>(null)
- const editingPresetName = ref('')
- const cameraCapabilities = ref<PTZCapabilities | null>(null)
- const capabilitiesLoading = ref(false)
- const presetsLoading = ref(false)
- const activePanels = ref(['ptz', 'preset', 'camera'])
- function hasCameraConnection(): boolean {
- return !!currentMediaStream.value?.cameraId
- }
- async function handlePTZ(direction: string) {
- const cameraId = currentMediaStream.value?.cameraId
- if (!cameraId) {
- ElMessage.warning(t('请先选择直播流'))
- return
- }
- try {
- const command = directionToAction[direction] || 'stop'
- const res = await ptzControl({ cameraId, command, speed: ptzSpeed.value })
- if (!res.success) {
- console.error('PTZ 控制失败', res.errMsg)
- }
- } catch (error) {
- console.error('PTZ 控制失败', error)
- }
- }
- async function handlePTZStop() {
- const cameraId = currentMediaStream.value?.cameraId
- if (!cameraId) return
- try {
- await ptzControl({ cameraId, command: 'stop' })
- } catch (error) {
- console.error('PTZ 停止失败', error)
- }
- }
- function formatZoomTooltip(val: number) {
- if (val === 0) return t('停止')
- return val > 0 ? `${t('放大')} ${val}` : `${t('缩小')} ${Math.abs(val)}`
- }
- async function handleZoomChange(val: number) {
- const cameraId = currentMediaStream.value?.cameraId
- if (!cameraId) return
- if (val === 0) {
- await ptzControl({ cameraId, command: 'stop' })
- return
- }
- const command = val > 0 ? 'zoom_in' : 'zoom_out'
- await ptzControl({ cameraId, command, speed: Math.abs(val) })
- }
- async function handleZoomRelease() {
- zoomValue.value = 0
- const cameraId = currentMediaStream.value?.cameraId
- if (!cameraId) return
- await ptzControl({ cameraId, command: 'stop' })
- }
- async function handleZoomIn() {
- const cameraId = currentMediaStream.value?.cameraId
- if (!cameraId) {
- ElMessage.warning(t('请先选择直播流'))
- return
- }
- try {
- const res = await ptzControl({ cameraId, command: 'zoom_in', speed: ptzSpeed.value })
- if (!res.success) console.error('Zoom in 失败', res.errMsg)
- } catch (error) {
- console.error('Zoom in 失败', error)
- }
- }
- async function handleZoomOut() {
- const cameraId = currentMediaStream.value?.cameraId
- if (!cameraId) {
- ElMessage.warning(t('请先选择直播流'))
- return
- }
- try {
- const res = await ptzControl({ cameraId, command: 'zoom_out', speed: ptzSpeed.value })
- if (!res.success) console.error('Zoom out 失败', res.errMsg)
- } catch (error) {
- console.error('Zoom out 失败', error)
- }
- }
- async function loadPTZPresets() {
- const cameraId = currentMediaStream.value?.cameraId
- if (!cameraId) {
- ElMessage.warning(t('请先选择直播流'))
- return
- }
- presetsLoading.value = true
- try {
- const res = await presetList({ cameraId })
- if (res.code === 200 && res.data) {
- ptzPresetList.value = res.data as PresetInfo[]
- } else {
- ptzPresetList.value = []
- if (!res.success) ElMessage.error(res.errMsg || t('加载预置位失败'))
- }
- } catch (error) {
- console.error('加载 PTZ 预置位失败', error)
- ptzPresetList.value = []
- } finally {
- presetsLoading.value = false
- }
- }
- async function handleGotoPTZPreset(preset: PresetInfo) {
- const cameraId = currentMediaStream.value?.cameraId
- if (!cameraId) {
- ElMessage.warning(t('请先配置摄像头连接'))
- return
- }
- try {
- activePresetId.value = preset.id
- const res = await presetGoto({ cameraId, presetId: parseInt(preset.id) })
- if (res.code === 200) {
- ElMessage.success(`${t('已跳转到预置位')}: ${preset.name || preset.id}`)
- } else {
- ElMessage.error(res.errMsg || t('跳转失败'))
- }
- } catch (error) {
- console.error('跳转 PTZ 预置位失败', error)
- ElMessage.error(t('跳转失败'))
- }
- }
- async function handleEditPreset(preset: { id: string; name: string }) {
- const cameraId = currentMediaStream.value?.cameraId
- if (!cameraId) {
- ElMessage.warning(t('请先配置摄像头连接'))
- return
- }
- try {
- await ElMessageBox.confirm(
- `${t('将当前摄像头位置保存到预置位')} "${preset.name || `Preset ${preset.id}`}"?`,
- t('设置预置位'),
- { type: 'info' }
- )
- const res = await presetSet({
- cameraId,
- presetId: parseInt(preset.id),
- presetName: preset.name || `Preset ${preset.id}`
- })
- if (res.code === 200) {
- ElMessage.success(`${t('预置位设置成功')}: ${preset.name || `Preset ${preset.id}`}`)
- } else {
- ElMessage.error(res.errMsg || t('设置失败'))
- }
- } catch (error) {
- if ((error as Error).toString().includes('cancel')) return
- console.error('设置预置位失败', error)
- ElMessage.error(t('设置失败'))
- }
- }
- async function handleDeletePreset(preset: { id: string; name: string }) {
- const cameraId = currentMediaStream.value?.cameraId
- if (!cameraId) {
- ElMessage.warning(t('请先配置摄像头连接'))
- return
- }
- try {
- await ElMessageBox.confirm(`${t('确定删除预置位')} "${preset.name || `Preset ${preset.id}`}"?`, t('删除确认'), {
- type: 'warning'
- })
- const res = await presetRemove({ cameraId, presetId: parseInt(preset.id) })
- if (res.success) {
- ElMessage.success(t('删除成功'))
- loadPTZPresets()
- } else {
- ElMessage.error(res.errMsg || t('删除失败'))
- }
- } catch (error) {
- if (error !== 'cancel') {
- console.error('删除预置位失败', error)
- ElMessage.error(t('删除失败'))
- }
- }
- }
- // Local preset operations (based on timelinePoints)
- function startEditPresetName(preset: LocalPreset) {
- editingPresetId.value = preset.id
- editingPresetName.value = preset.name
- }
- function cancelEditPresetName() {
- editingPresetId.value = null
- editingPresetName.value = ''
- }
- function savePresetName(preset: LocalPreset) {
- const newName = editingPresetName.value.trim()
- if (!newName || newName === preset.name) {
- cancelEditPresetName()
- return
- }
- const point = timelinePoints.value.find((p) => p.id === preset.pointId)
- if (!point) {
- cancelEditPresetName()
- return
- }
- point.presetName = newName
- callbacks.saveTimelineConfig()
- cancelEditPresetName()
- }
- async function handleGotoLocalPreset(preset: LocalPreset) {
- const cameraId = currentMediaStream.value?.cameraId
- if (!cameraId) {
- ElMessage.warning(t('请先配置摄像头连接'))
- return
- }
- const point = timelinePoints.value.find((p) => p.id === preset.pointId)
- if (!point?.presetId) {
- ElMessage.warning(t('未找到对应的预置位'))
- return
- }
- try {
- activePresetId.value = preset.id
- const res = await presetGoto({ cameraId, presetId: point.presetId })
- if (res.code === 200) {
- ElMessage.success(`${t('已跳转到')}: ${preset.name}`)
- } else {
- ElMessage.error(res.errMsg || t('跳转失败'))
- }
- } catch (error) {
- console.error('跳转预置位失败', error)
- ElMessage.error(t('跳转失败'))
- }
- }
- async function handleUpdateLocalPreset(preset: LocalPreset) {
- const cameraId = currentMediaStream.value?.cameraId
- if (!cameraId) {
- ElMessage.warning(t('请先配置摄像头连接'))
- return
- }
- const point = timelinePoints.value.find((p) => p.id === preset.pointId)
- if (!point?.presetId) {
- ElMessage.warning(t('未找到对应的预置位'))
- return
- }
- try {
- await ElMessageBox.confirm(`${t('将当前摄像头位置保存到预置位')} "${preset.name}"?`, t('设置预置位'), {
- type: 'info'
- })
- const res = await presetSet({
- cameraId,
- presetId: point.presetId,
- presetName: preset.name
- })
- if (res.code === 200) {
- ElMessage.success(`${t('预置位设置成功')}: ${preset.name}`)
- } else {
- ElMessage.error(res.errMsg || t('设置失败'))
- }
- } catch (error) {
- if ((error as Error).toString().includes('cancel')) return
- console.error('设置预置位失败', error)
- ElMessage.error(t('设置失败'))
- }
- }
- async function handleDeleteLocalPreset(preset: LocalPreset) {
- const cameraId = currentMediaStream.value?.cameraId
- if (!cameraId) {
- ElMessage.warning(t('请先配置摄像头连接'))
- return
- }
- const point = timelinePoints.value.find((p) => p.id === preset.pointId)
- if (!point?.presetId) {
- ElMessage.warning(t('未找到对应的预置位'))
- return
- }
- try {
- await ElMessageBox.confirm(`${t('确定删除预置位')} "${preset.name}"?`, t('删除确认'), {
- type: 'warning'
- })
- const res = await presetRemove({ cameraId, presetId: point.presetId })
- if (res.success) {
- const index = timelinePoints.value.findIndex((p) => p.id === preset.pointId)
- if (index !== -1) {
- timelinePoints.value.splice(index, 1)
- callbacks.sortAndRenumberPoints()
- callbacks.saveTimelineConfig()
- if (callbacks.selectedPoint.value?.id === preset.pointId) {
- callbacks.selectedPoint.value = null
- }
- }
- ElMessage.success(t('删除成功'))
- } else {
- ElMessage.error(res.errMsg || t('删除失败'))
- }
- } catch (error) {
- if (error !== 'cancel') {
- console.error('删除预置位失败', error)
- ElMessage.error(t('删除失败'))
- }
- }
- }
- async function loadCameraCapabilities() {
- const cameraId = currentMediaStream.value?.cameraId
- if (!cameraId) return
- capabilitiesLoading.value = true
- try {
- const res = await getPTZCapabilities({ cameraId })
- if (res.success && res.data) {
- cameraCapabilities.value = res.data as PTZCapabilities
- } else {
- cameraCapabilities.value = null
- }
- } catch (error) {
- console.error('加载摄像头能力失败', error)
- cameraCapabilities.value = null
- } finally {
- capabilitiesLoading.value = false
- }
- }
- return {
- ptzSpeed,
- zoomValue,
- ptzPresetList,
- activePresetId,
- editingPresetId,
- editingPresetName,
- cameraCapabilities,
- capabilitiesLoading,
- presetsLoading,
- activePanels,
- hasCameraConnection,
- handlePTZ,
- handlePTZStop,
- formatZoomTooltip,
- handleZoomChange,
- handleZoomRelease,
- handleZoomIn,
- handleZoomOut,
- loadPTZPresets,
- handleGotoPTZPreset,
- handleEditPreset,
- handleDeletePreset,
- startEditPresetName,
- cancelEditPresetName,
- savePresetName,
- handleGotoLocalPreset,
- handleUpdateLocalPreset,
- handleDeleteLocalPreset,
- loadCameraCapabilities
- }
- }
|