Przeglądaj źródła

feat(ptz): enhance PTZ control and preset management features

- Added new PTZ preset management functions including getting, setting, and removing presets.
- Implemented a timeline feature for managing PTZ preset points with playback capabilities.
- Updated the PTZ control panel in the live stream view to support new preset functionalities.
- Improved camera connection handling and added UI elements for camera configuration.
- Enhanced the overall layout and user experience in the live stream and PTZ control interfaces.
yb 2 dni temu
rodzic
commit
adba674a0e
3 zmienionych plików z 1050 dodań i 186 usunięć
  1. 172 8
      src/api/ptz.ts
  2. 876 176
      src/views/live-stream/index.vue
  3. 2 2
      src/views/lss/index.vue

+ 172 - 8
src/api/ptz.ts

@@ -2,6 +2,8 @@
  * PTZ 云台控制 API
  * 通过独立的 PTZ 后端服务调用 ONVIF 标准协议
  * 支持厂家: HIKVISION (海康威视), ANPVIZ
+ *
+ * 注意: 用户名和密码存储在后端服务器,前端不传递凭据
  */
 
 // ==================== 类型定义 ====================
@@ -11,8 +13,6 @@ export type CameraVendor = 'HIKVISION' | 'ANPVIZ' | 'DAHUA' | 'CT-IP500' | 'SVBC
 
 export interface PTZConfig {
   host: string
-  username: string
-  password: string
   channel?: number
   cameraId?: number
   vendor?: CameraVendor
@@ -87,8 +87,6 @@ async function sendPTZCommand(
       headers: { 'Content-Type': 'application/json' },
       body: JSON.stringify({
         host: config.host,
-        username: config.username,
-        password: config.password,
         channel: config.channel || 1,
         cameraId: config.cameraId,
         vendor: config.vendor || 'HIKVISION',
@@ -117,8 +115,6 @@ async function sendCommand(config: PTZConfig, command: PTZCommand): Promise<PTZR
       headers: { 'Content-Type': 'application/json' },
       body: JSON.stringify({
         host: config.host,
-        username: config.username,
-        password: config.password,
         channel: config.channel || 1,
         cameraId: config.cameraId,
         vendor: config.vendor || 'HIKVISION',
@@ -147,8 +143,6 @@ export async function getPTZStatus(config: PTZConfig): Promise<PTZResult & { dat
       headers: { 'Content-Type': 'application/json' },
       body: JSON.stringify({
         host: config.host,
-        username: config.username,
-        password: config.password,
         channel: config.channel || 1
       })
     })
@@ -227,3 +221,173 @@ export function stopZoom(config: PTZConfig): Promise<PTZResult> {
 export const zoomIn = (config: PTZConfig, speed?: number) => startZoom(config, 'IN', speed)
 export const zoomOut = (config: PTZConfig, speed?: number) => startZoom(config, 'OUT', speed)
 export const zoomStop = stopZoom
+
+// ==================== 预置位 API ====================
+
+// 预置位信息
+export interface PTZPresetInfo {
+  id: string
+  name: string
+}
+
+// PTZ 能力信息
+export interface PTZCapabilities {
+  absolutePanTilt?: {
+    panMin: number
+    panMax: number
+    tiltMin: number
+    tiltMax: number
+  }
+  absoluteZoom?: {
+    min: number
+    max: number
+  }
+  continuousPanTilt?: {
+    panMin: number
+    panMax: number
+    tiltMin: number
+    tiltMax: number
+  }
+  continuousZoom?: {
+    min: number
+    max: number
+  }
+  maxPresetNum?: number
+  controlProtocol?: {
+    options: string[]
+    current: string
+  }
+  specialPresetIds?: number[]
+  presetName?: {
+    supported: boolean
+    maxLength: number
+  }
+  support3DPosition?: boolean
+  supportPtzLimits?: boolean
+}
+
+// 摄像头连接参数 (与 PTZConfig 兼容)
+export type CameraConnection = PTZConfig
+
+/**
+ * 获取预置位列表
+ */
+export async function getPTZPresets(config: PTZConfig): Promise<PTZResult & { data?: PTZPresetInfo[] }> {
+  try {
+    const response = await fetch(`${PTZ_API_BASE}/preset/list`, {
+      method: 'POST',
+      headers: { 'Content-Type': 'application/json' },
+      body: JSON.stringify({
+        host: config.host,
+        channel: config.channel || 1
+      })
+    })
+
+    const data = await response.json()
+    return data.code === 200
+      ? { success: true, data: data.data }
+      : { success: false, error: data.msg || 'Unknown error' }
+  } catch (error) {
+    return { success: false, error: String(error) }
+  }
+}
+
+/**
+ * 跳转到预置位
+ */
+export async function gotoPTZPreset(config: PTZConfig, presetId: number): Promise<PTZResult> {
+  try {
+    const response = await fetch(`${PTZ_API_BASE}/preset/goto`, {
+      method: 'POST',
+      headers: { 'Content-Type': 'application/json' },
+      body: JSON.stringify({
+        host: config.host,
+        channel: config.channel || 1,
+        presetId
+      })
+    })
+
+    const data = await response.json()
+    return data.code === 200
+      ? { success: true, data: data.data }
+      : { success: false, error: data.msg || 'Unknown error' }
+  } catch (error) {
+    return { success: false, error: String(error) }
+  }
+}
+
+/**
+ * 设置预置位 (保存当前位置)
+ */
+export async function setPTZPreset(
+  config: PTZConfig,
+  presetId: number,
+  presetName?: string
+): Promise<PTZResult & { data?: { presetId: number; presetName: string } }> {
+  try {
+    const response = await fetch(`${PTZ_API_BASE}/preset/set`, {
+      method: 'POST',
+      headers: { 'Content-Type': 'application/json' },
+      body: JSON.stringify({
+        host: config.host,
+        channel: config.channel || 1,
+        presetId,
+        presetName
+      })
+    })
+
+    const data = await response.json()
+    return data.code === 200
+      ? { success: true, data: data.data }
+      : { success: false, error: data.msg || 'Unknown error' }
+  } catch (error) {
+    return { success: false, error: String(error) }
+  }
+}
+
+/**
+ * 删除预置位
+ */
+export async function removePTZPreset(config: PTZConfig, presetId: number): Promise<PTZResult> {
+  try {
+    const response = await fetch(`${PTZ_API_BASE}/preset/remove`, {
+      method: 'POST',
+      headers: { 'Content-Type': 'application/json' },
+      body: JSON.stringify({
+        host: config.host,
+        channel: config.channel || 1,
+        presetId
+      })
+    })
+
+    const data = await response.json()
+    return data.code === 200
+      ? { success: true, data: data.data }
+      : { success: false, error: data.msg || 'Unknown error' }
+  } catch (error) {
+    return { success: false, error: String(error) }
+  }
+}
+
+/**
+ * 获取 PTZ 能力参数
+ */
+export async function getPTZCapabilities(config: PTZConfig): Promise<PTZResult & { data?: PTZCapabilities }> {
+  try {
+    const response = await fetch(`${PTZ_API_BASE}/ptz/capabilities`, {
+      method: 'POST',
+      headers: { 'Content-Type': 'application/json' },
+      body: JSON.stringify({
+        host: config.host,
+        channel: config.channel || 1
+      })
+    })
+
+    const data = await response.json()
+    return data.code === 200
+      ? { success: true, data: data.data }
+      : { success: false, error: data.msg || 'Unknown error' }
+  } catch (error) {
+    return { success: false, error: String(error) }
+  }
+}

Plik diff jest za duży
+ 876 - 176
src/views/live-stream/index.vue


+ 2 - 2
src/views/lss/index.vue

@@ -728,14 +728,14 @@ async function handleViewCamera(row: CameraInfoDTO) {
         customClass: 'live-stream-dialog',
         distinguishCancelAndClose: true
       })
-      router.push(`/live-stream?cameraId=${row.cameraId}&lssId=${row.lssId}&action=create`)
+      router.push(`/live-stream-manage/list?cameraId=${row.cameraId}&lssId=${row.lssId}&action=create`)
     } catch {
       // 用户点击了取消,不做任何操作
     }
     return
   }
   // 有 streamSn,正常跳转
-  router.push(`/live-stream?cameraId=${row.cameraId}`)
+  router.push(`/live-stream-manage/list?cameraId=${row.cameraId}`)
 }
 
 // 格式化品牌

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików