yb 3 روز پیش
والد
کامیت
baff320dc7
1فایلهای تغییر یافته به همراه47 افزوده شده و 96 حذف شده
  1. 47 96
      src/views/live-stream/index.vue

+ 47 - 96
src/views/live-stream/index.vue

@@ -103,7 +103,7 @@
             <el-button type="primary" link @click="handleEdit(row)">
               <Icon icon="mdi:note-edit-outline" width="20" height="20" />
             </el-button>
-            <el-button type="primary" link @click="handleViewCloudflare(row)">
+            <el-button data-id="live-stream-play-btn" type="primary" link @click="handleViewCloudflare(row)">
               <Icon icon="mdi:play-circle-outline" width="20" height="20" />
             </el-button>
             <el-button type="danger" link @click="handleDelete(row)">
@@ -281,7 +281,9 @@
                     :disabled="!hasActivePoints"
                     @click="playTimeline"
                   >
-                    <el-icon v-if="!isTimelinePlaying"><VideoPlay /></el-icon>
+                    <el-icon v-if="!isTimelinePlaying">
+                      <VideoPlay />
+                    </el-icon>
                     {{ isTimelinePlaying ? t('巡航中...') : t('播放巡航') }}
                   </el-button>
                   <el-button v-if="isTimelinePlaying" size="small" type="danger" @click="stopTimeline">
@@ -495,7 +497,9 @@
                         @click.stop="loadPTZPresets"
                         :loading="presetsLoading"
                       >
-                        <el-icon><Refresh /></el-icon>
+                        <el-icon>
+                          <Refresh />
+                        </el-icon>
                       </el-button>
                     </div>
                   </template>
@@ -509,13 +513,19 @@
                       <span class="preset-name">{{ preset.name || `Preset ${preset.id}` }}</span>
                       <div class="preset-actions">
                         <el-tooltip :content="t('跳转')" placement="top">
-                          <el-icon class="action-icon" @click="handleGotoPTZPreset(preset)"><Position /></el-icon>
+                          <el-icon class="action-icon" @click="handleGotoPTZPreset(preset)">
+                            <Position />
+                          </el-icon>
                         </el-tooltip>
                         <el-tooltip :content="t('设置')" placement="top">
-                          <el-icon class="action-icon" @click="handleEditPreset(preset)"><Setting /></el-icon>
+                          <el-icon class="action-icon" @click="handleEditPreset(preset)">
+                            <Setting />
+                          </el-icon>
                         </el-tooltip>
                         <el-tooltip :content="t('删除')" placement="top">
-                          <el-icon class="action-icon delete" @click="handleDeletePreset(preset)"><Close /></el-icon>
+                          <el-icon class="action-icon delete" @click="handleDeletePreset(preset)">
+                            <Close />
+                          </el-icon>
                         </el-tooltip>
                       </div>
                     </div>
@@ -530,12 +540,7 @@
                 <!-- 摄像头信息 -->
                 <el-collapse-item name="camera">
                   <template #title>
-                    <div class="collapse-title-with-action">
-                      <span class="collapse-title">{{ t('摄像头信息') }}</span>
-                      <el-button type="primary" link size="small" @click.stop="showCameraConfigDialog">
-                        <el-icon><Setting /></el-icon>
-                      </el-button>
-                    </div>
+                    <span class="collapse-title">{{ t('摄像头信息') }}</span>
                   </template>
                   <div class="camera-info-content" v-loading="capabilitiesLoading">
                     <template v-if="cameraCapabilities">
@@ -568,7 +573,7 @@
                     </template>
                     <el-empty
                       v-else-if="!capabilitiesLoading"
-                      :description="cameraConnection.host ? t('点击刷新加载') : t('请配置摄像头连接')"
+                      :description="currentMediaStream?.cameraId ? t('点击刷新加载') : t('请选择直播流')"
                       :image-size="40"
                     />
                   </div>
@@ -599,28 +604,6 @@
         </div>
       </template>
     </el-drawer>
-
-    <!-- 摄像头连接配置对话框 -->
-    <el-dialog v-model="cameraConfigDialogVisible" :title="t('摄像头连接配置')" width="450px" destroy-on-close>
-      <el-form label-width="80px">
-        <el-form-item :label="t('IP地址')">
-          <el-input v-model="cameraConnection.host" placeholder="192.168.0.64" />
-        </el-form-item>
-        <el-form-item :label="t('用户名')">
-          <el-input v-model="cameraConnection.username" placeholder="admin" />
-        </el-form-item>
-        <el-form-item :label="t('密码')">
-          <el-input v-model="cameraConnection.password" type="password" show-password placeholder="******" />
-        </el-form-item>
-        <el-form-item :label="t('通道')">
-          <el-input-number v-model="cameraConnection.channel" :min="1" :max="32" />
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <el-button @click="cameraConfigDialogVisible = false">{{ t('取消') }}</el-button>
-        <el-button type="primary" @click="saveCameraConnection">{{ t('保存') }}</el-button>
-      </template>
-    </el-dialog>
   </div>
 </template>
 
@@ -645,7 +628,6 @@ import {
   ZoomIn,
   ZoomOut,
   Close,
-  Setting,
   Position
 } from '@element-plus/icons-vue'
 import { Icon } from '@iconify/vue'
@@ -657,19 +639,13 @@ import { adminListCameras } from '@/api/camera'
 import { startStreamTask, stopStreamTask, getStreamPlayback } from '@/api/stream-push'
 import VideoPlayer from '@/components/VideoPlayer.vue'
 import CodeEditor from '@/components/CodeEditor.vue'
-import { getPresets, gotoPreset, type PresetInfo } from '@/api/camera'
+import { getPresets, gotoPreset, type PresetInfo, presetList, presetGoto, presetSet, presetRemove } from '@/api/camera'
 import {
-  getPTZPresets,
-  gotoPTZPreset,
-  removePTZPreset,
-  getPTZCapabilities,
-  setPTZPreset,
   startPTZ,
   stopPTZ,
   startZoom,
   type PTZPresetInfo,
   type PTZCapabilities,
-  type PTZConfig,
   type PTZDirectionKey,
   type PTZZoomKey
 } from '@/api/ptz'
@@ -726,15 +702,12 @@ const ptzPresetList = ref<PTZPresetInfo[]>([])
 const activePresetId = ref<string | null>(null)
 const cameraCapabilities = ref<PTZCapabilities | null>(null)
 const capabilitiesLoading = ref(false)
-const cameraConfigDialogVisible = ref(false)
-
-// 摄像头连接配置 (从 localStorage 读取)
-const cameraConnection = reactive<PTZConfig>({
-  host: localStorage.getItem('ptz_camera_host') || '',
-  username: localStorage.getItem('ptz_camera_username') || '',
-  password: localStorage.getItem('ptz_camera_password') || '',
-  channel: 1
-})
+// PTZ 配置 (使用 device ID)
+const ptzChannel = ref(1)
+const ptzConfig = computed(() => ({
+  host: currentMediaStream.value?.cameraId || '',
+  channel: ptzChannel.value
+}))
 
 // 可折叠面板
 const activePanels = ref(['ptz', 'preset', 'camera'])
@@ -1358,13 +1331,12 @@ function handleFullscreen() {
 // PTZ 控制 (使用直连 PTZ API)
 async function handlePTZ(direction: string) {
   if (!hasCameraConnection()) {
-    ElMessage.warning(t('请先配置摄像头连接'))
-    showCameraConfigDialog()
+    ElMessage.warning(t('请先选择直播流'))
     return
   }
 
   try {
-    const res = await startPTZ(cameraConnection, direction as PTZDirectionKey, ptzSpeed.value)
+    const res = await startPTZ(ptzConfig.value, direction as PTZDirectionKey, ptzSpeed.value)
     if (!res.success) {
       console.error('PTZ 控制失败', res.error)
     }
@@ -1377,7 +1349,7 @@ async function handlePTZStop() {
   if (!hasCameraConnection()) return
 
   try {
-    await stopPTZ(cameraConnection)
+    await stopPTZ(ptzConfig.value)
   } catch (error) {
     console.error('PTZ 停止失败', error)
   }
@@ -1393,29 +1365,28 @@ async function handleZoomChange(val: number) {
   if (!hasCameraConnection()) return
 
   if (val === 0) {
-    await stopPTZ(cameraConnection)
+    await stopPTZ(ptzConfig.value)
     return
   }
 
   const direction: PTZZoomKey = val > 0 ? 'IN' : 'OUT'
-  await startZoom(cameraConnection, direction, Math.abs(val))
+  await startZoom(ptzConfig.value, direction, Math.abs(val))
 }
 
 async function handleZoomRelease() {
   zoomValue.value = 0
   if (!hasCameraConnection()) return
-  await stopPTZ(cameraConnection)
+  await stopPTZ(ptzConfig.value)
 }
 
 // 缩放按钮控制 (使用直连 PTZ API)
 async function handleZoomIn() {
   if (!hasCameraConnection()) {
-    ElMessage.warning(t('请先配置摄像头连接'))
-    showCameraConfigDialog()
+    ElMessage.warning(t('请先选择直播流'))
     return
   }
   try {
-    const res = await startZoom(cameraConnection, 'IN' as PTZZoomKey, ptzSpeed.value)
+    const res = await startZoom(ptzConfig.value, 'IN' as PTZZoomKey, ptzSpeed.value)
     if (!res.success) {
       console.error('Zoom in 失败', res.error)
     }
@@ -1426,12 +1397,11 @@ async function handleZoomIn() {
 
 async function handleZoomOut() {
   if (!hasCameraConnection()) {
-    ElMessage.warning(t('请先配置摄像头连接'))
-    showCameraConfigDialog()
+    ElMessage.warning(t('请先选择直播流'))
     return
   }
   try {
-    const res = await startZoom(cameraConnection, 'OUT' as PTZZoomKey, ptzSpeed.value)
+    const res = await startZoom(ptzConfig.value, 'OUT' as PTZZoomKey, ptzSpeed.value)
     if (!res.success) {
       console.error('Zoom out 失败', res.error)
     }
@@ -1488,20 +1458,19 @@ async function handleGotoPreset(preset: PresetInfo) {
 
 // 检查摄像头连接配置
 function hasCameraConnection(): boolean {
-  return !!(cameraConnection.host && cameraConnection.username && cameraConnection.password)
+  return !!currentMediaStream.value?.cameraId
 }
 
 // 加载 PTZ 预置位 (直连 PTZ 服务)
 async function loadPTZPresets() {
   if (!hasCameraConnection()) {
-    ElMessage.warning(t('请先配置摄像头连接'))
-    showCameraConfigDialog()
+    ElMessage.warning(t('请先选择直播流'))
     return
   }
 
   presetsLoading.value = true
   try {
-    const res = await getPTZPresets(cameraConnection)
+    const res = await getPTZPresets(ptzConfig.value)
     if (res.success && res.data) {
       ptzPresetList.value = res.data
     } else {
@@ -1527,7 +1496,7 @@ async function handleGotoPTZPreset(preset: PTZPresetInfo) {
 
   try {
     activePresetId.value = preset.id
-    const res = await gotoPTZPreset(cameraConnection, parseInt(preset.id))
+    const res = await gotoPTZPreset(ptzConfig.value, parseInt(preset.id))
     if (res.success) {
       ElMessage.success(`${t('已跳转到预置位')}: ${preset.name || preset.id}`)
     } else {
@@ -1557,7 +1526,7 @@ async function handleDeletePreset(preset: PTZPresetInfo) {
       type: 'warning'
     })
 
-    const res = await removePTZPreset(cameraConnection, parseInt(preset.id))
+    const res = await removePTZPreset(ptzConfig.value, parseInt(preset.id))
     if (res.success) {
       ElMessage.success(t('删除成功'))
       // 刷新预置位列表
@@ -1581,7 +1550,7 @@ async function loadCameraCapabilities() {
 
   capabilitiesLoading.value = true
   try {
-    const res = await getPTZCapabilities(cameraConnection)
+    const res = await getPTZCapabilities(ptzConfig.value)
     if (res.success && res.data) {
       cameraCapabilities.value = res.data
     } else {
@@ -1595,23 +1564,6 @@ async function loadCameraCapabilities() {
   }
 }
 
-// 显示摄像头配置对话框
-function showCameraConfigDialog() {
-  cameraConfigDialogVisible.value = true
-}
-
-// 保存摄像头连接配置
-function saveCameraConnection() {
-  localStorage.setItem('ptz_camera_host', cameraConnection.host)
-  localStorage.setItem('ptz_camera_username', cameraConnection.username)
-  localStorage.setItem('ptz_camera_password', cameraConnection.password)
-  cameraConfigDialogVisible.value = false
-  ElMessage.success(t('配置已保存'))
-  // 自动加载预置位和能力
-  loadPTZPresets()
-  loadCameraCapabilities()
-}
-
 // ==================== 时间轴功能 ====================
 
 // 格式化时间轴时间 (秒 -> m:ss)
@@ -1717,7 +1669,7 @@ function selectPoint(point: TimelinePoint) {
 
   // 如果已有预置位,跳转到该位置
   if (point.presetId && hasCameraConnection()) {
-    gotoPTZPreset(cameraConnection, point.presetId).then((res) => {
+    gotoPTZPreset(ptzConfig.value, point.presetId).then((res) => {
       if (res.success) {
         ElMessage.success(`${t('已跳转到')}: ${point.presetName || `Point ${point.id}`}`)
       }
@@ -1730,8 +1682,7 @@ async function saveCurrentPoint() {
   if (!selectedPoint.value) return
 
   if (!hasCameraConnection()) {
-    ElMessage.warning(t('请先配置摄像头连接'))
-    showCameraConfigDialog()
+    ElMessage.warning(t('请先选择直播流'))
     return
   }
 
@@ -1742,7 +1693,7 @@ async function saveCurrentPoint() {
 
   savingPreset.value = true
   try {
-    const res = await setPTZPreset(cameraConnection, presetId, presetName)
+    const res = await setPTZPreset(ptzConfig.value, presetId, presetName)
     if (res.success) {
       point.presetId = presetId
       point.presetName = presetName
@@ -1814,8 +1765,7 @@ async function playTimeline() {
   }
 
   if (!hasCameraConnection()) {
-    ElMessage.warning(t('请先配置摄像头连接'))
-    showCameraConfigDialog()
+    ElMessage.warning(t('请先选择直播流'))
     return
   }
 
@@ -1844,7 +1794,7 @@ async function playTimeline() {
 
       // 跳转到该预置位
       if (point.presetId) {
-        await gotoPTZPreset(cameraConnection, point.presetId)
+        await gotoPTZPreset(ptzConfig.value, point.presetId)
         selectedPoint.value = point
       }
 
@@ -2277,6 +2227,7 @@ onMounted(async () => {
         background-color: #333;
         border-color: #444;
       }
+
       .el-input__inner {
         color: #fff;
       }