yb il y a 1 semaine
Parent
commit
543bd8aafb
4 fichiers modifiés avec 82 ajouts et 38 suppressions
  1. 9 5
      src/locales/en.json
  2. 5 1
      src/locales/zh-cn.json
  3. 38 24
      src/types/index.ts
  4. 30 8
      src/views/lss/index.vue

+ 9 - 5
src/locales/en.json

@@ -14,7 +14,7 @@
   "RTSP端口": "RTSP Port",
   "Stream 测试": "Stream Test",
   "Video ID": "Video ID",
-  "ably信息": "ably Information",
+  "ably": "ably",
   "button.cancel": "Cancel",
   "button.confirm": "Confirm",
   "button.disable": "Disable",
@@ -23,6 +23,7 @@
   "button.selectedSsers": "Selected Users",
   "button.whether": "Whether",
   "errorCode.0": "Success",
+  "hold": "hold",
   "iframe 模式": "iframe Mode",
   "input.SelectAll": "Select All",
   "login.confirmPassword": "Confirm Password",
@@ -97,6 +98,7 @@
   "开始日期": "Start Date",
   "当前任务": "Current Task",
   "当前状态": "Current Status",
+  "心跳": "heartbeat",
   "忘记密码?": "Forgot password?",
   "快捷操作": "Quick Actions",
   "快速测试": "Quick Test",
@@ -132,7 +134,7 @@
   "播放方式": "Playback Method",
   "播放测试视频": "Play Test Video",
   "操作": "Actions",
-  "放大": "",
+  "放大": "Zoom In",
   "数据更新时间": "Last Updated",
   "新增": "Add",
   "新增 Live Stream": "Add Live Stream",
@@ -163,6 +165,7 @@
   "欢迎回来": "Welcome Back",
   "欢迎回来,这是您的数据概览": "Welcome back, here is your data overview",
   "正常": "Normal",
+  "活跃": "Active",
   "流媒体播放": "Media Playback",
   "测试播放": "Test Playback",
   "测试视频": "Test Video",
@@ -171,6 +174,7 @@
   "清空": "Clear",
   "版本": "Version",
   "状态": "Status",
+  "状态(心跳)": "Status (heartbeat)",
   "生成地址": "Generate URL",
   "生成的地址": "Generated URL",
   "用户": "Users",
@@ -234,7 +238,7 @@
   "请输入正确的IP地址": "Please enter a valid IP address",
   "请输入用户名": "Please enter username",
   "请输入视频地址并点击播放": "Please enter video URL and click play",
-  "请选择 LSS 节点": "",
+  "请选择 LSS 节点": "Please select LSS node",
   "请选择视频源并点击播放": "Please select video source and click play",
   "跳转失败": "Jump failed",
   "转换服务地址": "Proxy Service URL",
@@ -247,10 +251,10 @@
   "速度": "Speed",
   "配置说明": "Configuration Description",
   "重置": "Reset",
-  "错误": "",
+  "错误": "Error",
   "静音": "Muted",
   "项": "items",
-  "预置位": "",
+  "预置位": "Preset",
   "默认分辨率": "Default Resolution",
   "默认端口": "Default Port",
   "默认视角": "Default View"

+ 5 - 1
src/locales/zh-cn.json

@@ -14,7 +14,7 @@
   "RTSP端口": "RTSP端口",
   "Stream 测试": "Stream 测试",
   "Video ID": "Video ID",
-  "ably信息": "ably信息",
+  "ably": "ably",
   "button.cancel": "button.cancel",
   "button.confirm": "button.confirm",
   "button.disable": "button.disable",
@@ -23,6 +23,7 @@
   "button.selectedSsers": "button.selectedSsers",
   "button.whether": "button.whether",
   "errorCode.0": "errorCode.0",
+  "hold": "hold",
   "iframe 模式": "iframe 模式",
   "input.SelectAll": "input.SelectAll",
   "login.confirmPassword": "login.confirmPassword",
@@ -97,6 +98,7 @@
   "开始日期": "开始日期",
   "当前任务": "当前任务",
   "当前状态": "当前状态",
+  "心跳": "心跳",
   "忘记密码?": "忘记密码?",
   "快捷操作": "快捷操作",
   "快速测试": "快速测试",
@@ -163,6 +165,7 @@
   "欢迎回来": "欢迎回来",
   "欢迎回来,这是您的数据概览": "欢迎回来,这是您的数据概览",
   "正常": "正常",
+  "活跃": "活跃",
   "流媒体播放": "流媒体播放",
   "测试播放": "测试播放",
   "测试视频": "测试视频",
@@ -171,6 +174,7 @@
   "清空": "清空",
   "版本": "版本",
   "状态": "状态",
+  "状态(心跳)": "状态(心跳)",
   "生成地址": "生成地址",
   "生成的地址": "生成的地址",
   "用户": "用户",

+ 38 - 24
src/types/index.ts

@@ -143,37 +143,51 @@ export interface ChannelInfoDTO {
 export type CameraCapability = 'switch_only' | 'ptz_enabled'
 export type CameraHeartbeatStatus = 'active' | 'hold' | 'dead'
 // 摄像头详情 (Admin 用)
-export interface CameraInfoDTO {
-  id?: number // 主键ID
-  cameraId?: string // 摄像头ID
-  cameraName?: string // 设备名称
-  ip?: string // IP地址
-  port?: number // 端口
-  username?: string // 用户名
-  vendorName?: string // 厂商名称
-  brand?: string // 品牌(兼容旧数据)
-  capability?: CameraCapability // 能力
-  status?: CameraHeartbeatStatus // 心跳状态
-  lssId?: string // 绑定的 LSS 节点 ID
-  model?: string // 摄像头型号
-  rtspUrl?: string // RTSP 推流地址
-  channelNo?: string // 通道号
-  remark?: string // 备注
-  paramConfig?: string // 参数配置 JSON 字符串
-  runtimeParams?: string // 设备运行参数 JSON 字符串
-  enabled?: boolean // 是否启用
-  createdAt?: string // 创建时间(ISO)
-  updatedAt?: string // 更新时间(ISO)
+export interface CameraInfoDTO extends CameraAddRequest {
+  /* 主键ID */
+  id?: number
+  /* IP地址 */
+  ip?: string
+  /* 端口 */
+  port?: number
+  /* 用户名 */
+  username?: string
+  /* 品牌(兼容旧数据) */
+  brand?: string
+  /* 能力 */
+  capability?: CameraCapability
+  /* 心跳状态 */
+  status?: CameraHeartbeatStatus
+  /* 推流地址 */
+  rtspUrl?: string
+  /* 通道号 */
+  channelNo?: string
+  /* 备注 */
+  remark?: string
+  /* 是否启用 */
+  enabled?: boolean
+  /* 创建时间(ISO) */
+  createdAt?: string
+  /* 更新时间(ISO) */
+  updatedAt?: string
 }
 
 // 添加摄像头请求
 export interface CameraAddRequest {
-  cameraId: string
+  /* 摄像头ID */
+  cameraId?: string
+  /* 设备名称 */
   cameraName?: string
-  vendorName?: string
-  model?: string
+  /* 参数配置 JSON 字符串 */
   paramConfig?: string
+  /* 设备运行参数 JSON 字符串 */
   runtimeParams?: string
+  /* 绑定的 LSS 节点 ID */
+  lssId?: string
+  /* 摄像头型号 */
+  model?: string
+  /* 厂商名称 */
+  vendorName?: string
 }
 
 // 添加通道请求

+ 30 - 8
src/views/lss/index.vue

@@ -22,7 +22,7 @@
           />
         </el-form-item>
         <el-form-item>
-          <el-select v-model="searchForm.status" placeholder="心跳" clearable data-id="search-enabled">
+          <el-select v-model="searchForm.status" :placeholder="t('心跳')" clearable data-id="search-enabled">
             <el-option label="全部" value="" />
             <el-option label="active" :value="1" />
             <el-option label="hold" :value="2" />
@@ -173,16 +173,16 @@
               <el-form-item label="LSS ID:">
                 <span class="form-value">{{ currentLss?.lssId }}</span>
               </el-form-item>
-              <el-form-item label="名称:" prop="lssName">
+              <el-form-item :label="t('名称') + ':'" prop="lssName">
                 <el-input v-model="lssEditForm.lssName" placeholder="请输入名称" style="width: 180px" />
               </el-form-item>
-              <el-form-item label="地址:" prop="address">
+              <el-form-item :label="t('地址') + ':'" prop="address">
                 <el-input v-model="lssEditForm.address" placeholder="请输入地址" />
               </el-form-item>
-              <el-form-item label="IP:">
+              <el-form-item :label="t('IP') + ':'">
                 <span class="form-value">{{ lssEditForm?.ip }}</span>
               </el-form-item>
-              <el-form-item label="心跳:">
+              <el-form-item :label="t('心跳') + ':'">
                 <span class="heartbeat-status" :class="getHeartbeatClass(currentLss?.heartbeat)">
                   {{ formatHeartbeat(currentLss) }}
                   <span class="heartbeat-dot" :class="getHeartbeatClass(currentLss?.heartbeat)"></span>
@@ -250,7 +250,7 @@
               <!-- <el-table-column prop="ip" label="本地IP" min-width="110" /> -->
               <el-table-column prop="cameraId" label="设备ID" min-width="100" show-overflow-tooltip />
               <el-table-column prop="name" label="名称" min-width="100" show-overflow-tooltip />
-              <el-table-column label="状态(心跳)" min-width="140">
+              <el-table-column :label="t('状态(心跳)')" min-width="140">
                 <template #default="{ row }">
                   <span :class="['status-text', row.status === 'ONLINE' ? 'status-active' : 'status-dead']">
                     {{ formatCameraStatus(row) }}
@@ -344,7 +344,7 @@
               <!-- <el-table-column prop="ip" label="本地IP" min-width="110" /> -->
               <el-table-column prop="cameraId" label="设备ID" min-width="100" show-overflow-tooltip />
               <el-table-column prop="name" label="名称" min-width="100" show-overflow-tooltip />
-              <el-table-column label="状态(心跳)" min-width="140">
+              <el-table-column :label="t('状态(心跳)')" min-width="140">
                 <template #default="{ row }">
                   <span :class="['status-text', row.status === 'active' ? 'status-active' : 'status-dead']">
                     {{ formatCameraStatus(row) }}
@@ -655,6 +655,17 @@ function formatBrand(brand: string | undefined): string {
   return brand ? brandMap[brand] || brand.toUpperCase() : '-'
 }
 
+// 验证 JSON 格式
+function isValidJson(str: string): boolean {
+  if (!str || !str.trim()) return true // 空值视为有效
+  try {
+    JSON.parse(str)
+    return true
+  } catch {
+    return false
+  }
+}
+
 // 格式化心跳状态
 function formatHeartbeat(lss: LssNodeDTO | null | undefined): string {
   if (!lss) return '-'
@@ -1101,6 +1112,16 @@ async function handleSubmitCamera() {
   await cameraFormRef.value.validate(async (valid) => {
     if (!valid) return
 
+    // 验证 JSON 格式
+    if (!isValidJson(cameraForm.paramConfig)) {
+      ElMessage.error('参数配置格式错误,请输入有效的 JSON')
+      return
+    }
+    if (!isValidJson(cameraForm.runtimeParams)) {
+      ElMessage.error('设备运行参数格式错误,请输入有效的 JSON')
+      return
+    }
+
     cameraSubmitting.value = true
     try {
       if (isEditCamera.value) {
@@ -1145,7 +1166,8 @@ async function handleSubmitCamera() {
           vendorName: cameraForm.vendorName,
           model: cameraForm.model,
           paramConfig: cameraForm.paramConfig,
-          runtimeParams: cameraForm.runtimeParams
+          runtimeParams: cameraForm.runtimeParams,
+          lssId: currentLss.value?.lssId
         }
         const res = await adminAddCamera(data)
         if (res.success) {