Просмотр исходного кода

feat(camera-management): add comprehensive camera management documentation and enhance localization support

- Introduced a new documentation file for camera management API, detailing endpoints for listing, adding, updating, and deleting cameras, along with request and response structures.
- Updated English and Chinese localization files to include new terms related to camera management, enhancing user interface clarity.
- Refactored camera-related types to include optional fields for improved flexibility in API requests.
yb 1 неделя назад
Родитель
Сommit
99b1d48442

+ 798 - 0
docs/api_torna/camera_manage.md

@@ -0,0 +1,798 @@
+# 文档
+
+## 摄像头管理
+
+### 获取摄像头列表(分页)
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `POST` http://localhost:10050/api/admin/cameras/list
+- 开发环境: `POST` https://tg-live-game.pwtk.cc/api/admin/cameras/list
+
+描述:获取摄像头列表(分页)
+
+ContentType:`application/json`
+
+#### 请求参数
+
+##### Body Parameter
+
+| 名称    | 类型    | 必填 | 最大长度 | 描述                                            | 示例值    |
+| ------- | ------- | ---- | -------- | ----------------------------------------------- | --------- |
+| page    | Integer | 否   | -        | 页码 (从 1 开始)                                | 1         |
+| size    | Integer | 否   | -        | 每页条数                                        | 10        |
+| keyword | String  | 否   | -        | 搜索关键词 (模糊匹配名称、ID 等)                | 摄像头    |
+| enabled | Boolean | 否   | -        | 启用状态过滤 (null=全部, true=启用, false=禁用) | true      |
+| sortBy  | String  | 否   | -        | 排序字段                                        | createdAt |
+| sortDir | String  | 否   | -        | 排序方向 (ASC/DESC)                             | DESC      |
+| lssId   | String  | 否   | -        | LSS 节点 ID 过滤                                | lss_001   |
+| status  | String  | 否   | -        | 在线状态过滤 (ONLINE/OFFLINE)                   | ONLINE    |
+
+#### 请求示例
+
+```
+{
+    "page": 1,
+    "size": 10,
+    "keyword": "摄像头",
+    "enabled": true,
+    "sortBy": "createdAt",
+    "sortDir": "DESC",
+    "lssId": "lss_001",
+    "status": "ONLINE"
+}
+```
+
+#### 响应参数
+
+| 名称 | 类型 | 必填 | 最大长度 | 描述 | 示例值 |
+| --- | --- | --- | --- | --- | --- |
+| success | Boolean | 否 | - | 请求是否成功 | true |
+| errCode | String | 否 | - | 错误码(失败时返回) |  |
+| errMessage | String | 否 | - | 错误信息(失败时返回) |  |
+| data | object | 否 |  | 响应数据 (ActualType: PageResponse) |  |
+| └ list | List<T> | 否 |  | 数据列表 (ActualType: T) |  |
+| └ id | Long | 否 | - | 主键 ID | 1 |
+| └ cameraId | String | 否 | - | 摄像头 ID | cam_001 |
+| └ name | String | 否 | - | 名称 | 主摄像头 |
+| └ ip | String | 否 | - | IP 地址 | 192.168.1.100 |
+| └ port | Integer | 否 | - | 端口 | 80 |
+| └ username | String | 否 | - | 用户名 | admin |
+| └ brand | String | 否 | - | 品牌 | hikvision |
+| └ capability | String | 否 | - | 能力: switch_only, ptz_enabled | ptz_enabled |
+| └ status | String | 否 | - | 状态: ONLINE, OFFLINE | ONLINE |
+| └ lssId | String | 否 | - | 绑定的 LSS 节点 ID | lss_001 |
+| └ model | String | 否 | - | 摄像头型号 | DS-2CD2T47G2-LSU/SL |
+| └ rtspUrl | String | 否 | - | RTSP 推流地址 | rtsp://admin:password@192.168.1.100:554/stream1 |
+| └ channelNo | String | 否 | - | 通道号 | 1 |
+| └ remark | String | 否 | - | 备注 |  |
+| └ enabled | Boolean | 否 | - | 是否启用 | true |
+| └ createdAt | LocalDateTime | 否 | - | 创建时间 | 2026-01-07T10:00:00 |
+| └ updatedAt | LocalDateTime | 否 | - | 更新时间 | 2026-01-07T10:00:00 |
+| └ page | Integer | 否 | - | 当前页码 (从 1 开始) | 1 |
+| └ size | Integer | 否 | - | 每页条数 | 10 |
+| └ total | Long | 否 | - | 总记录数 | 100 |
+| └ totalPages | Integer | 否 | - | 总页数 | 10 |
+| └ hasNext | Boolean | 否 | - | 是否有下一页 | true |
+| └ hasPrevious | Boolean | 否 | - | 是否有上一页 | false |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": {
+        "list": [
+            {
+                "id": 1,
+                "cameraId": "cam_001",
+                "name": "主摄像头",
+                "ip": "192.168.1.100",
+                "port": 80,
+                "username": "admin",
+                "brand": "hikvision",
+                "capability": "ptz_enabled",
+                "status": "ONLINE",
+                "lssId": "lss_001",
+                "model": "DS-2CD2T47G2-LSU/SL",
+                "rtspUrl": "rtsp://admin:password@192.168.1.100:554/stream1",
+                "channelNo": "1",
+                "remark": "string",
+                "enabled": true,
+                "createdAt": "2026-01-07T10:00:00",
+                "updatedAt": "2026-01-07T10:00:00"
+            }
+        ],
+        "page": 1,
+        "size": 10,
+        "total": 100,
+        "totalPages": 10,
+        "hasNext": true,
+        "hasPrevious": false
+    }
+}
+```
+
+#### 错误码
+
+无
+
+### 获取摄像头列表(全部,不分页)
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `GET` http://localhost:10050/api/admin/cameras/listAll
+- 开发环境: `GET` https://tg-live-game.pwtk.cc/api/admin/cameras/listAll
+
+描述:获取摄像头列表(全部,不分页)
+
+ContentType:`application/x-www-form-urlencoded;charset=UTF-8`
+
+#### 请求参数
+
+##### Query Parameter
+
+| 名称      | 类型   | 必填 | 最大长度 | 描述            | 示例值      |
+| --------- | ------ | ---- | -------- | --------------- | ----------- |
+| machineId | string | 否   | -        | 机器 ID(可选) | machine_001 |
+
+#### 响应参数
+
+| 名称 | 类型 | 必填 | 最大长度 | 描述 | 示例值 |
+| --- | --- | --- | --- | --- | --- |
+| success | Boolean | 否 | - | 请求是否成功 | true |
+| errCode | String | 否 | - | 错误码(失败时返回) |  |
+| errMessage | String | 否 | - | 错误信息(失败时返回) |  |
+| data | array | 否 |  | 响应数据 (ActualType: List) |  |
+| └ id | Long | 否 | - | 主键 ID | 1 |
+| └ cameraId | String | 否 | - | 摄像头 ID | cam_001 |
+| └ name | String | 否 | - | 名称 | 主摄像头 |
+| └ ip | String | 否 | - | IP 地址 | 192.168.1.100 |
+| └ port | Integer | 否 | - | 端口 | 80 |
+| └ username | String | 否 | - | 用户名 | admin |
+| └ brand | String | 否 | - | 品牌 | hikvision |
+| └ capability | String | 否 | - | 能力: switch_only, ptz_enabled | ptz_enabled |
+| └ status | String | 否 | - | 状态: ONLINE, OFFLINE | ONLINE |
+| └ lssId | String | 否 | - | 绑定的 LSS 节点 ID | lss_001 |
+| └ model | String | 否 | - | 摄像头型号 | DS-2CD2T47G2-LSU/SL |
+| └ rtspUrl | String | 否 | - | RTSP 推流地址 | rtsp://admin:password@192.168.1.100:554/stream1 |
+| └ channelNo | String | 否 | - | 通道号 | 1 |
+| └ remark | String | 否 | - | 备注 |  |
+| └ enabled | Boolean | 否 | - | 是否启用 | true |
+| └ createdAt | LocalDateTime | 否 | - | 创建时间 | 2026-01-07T10:00:00 |
+| └ updatedAt | LocalDateTime | 否 | - | 更新时间 | 2026-01-07T10:00:00 |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": [
+        {
+            "id": 1,
+            "cameraId": "cam_001",
+            "name": "主摄像头",
+            "ip": "192.168.1.100",
+            "port": 80,
+            "username": "admin",
+            "brand": "hikvision",
+            "capability": "ptz_enabled",
+            "status": "ONLINE",
+            "lssId": "lss_001",
+            "model": "DS-2CD2T47G2-LSU/SL",
+            "rtspUrl": "rtsp://admin:password@192.168.1.100:554/stream1",
+            "channelNo": "1",
+            "remark": "string",
+            "enabled": true,
+            "createdAt": "2026-01-07T10:00:00",
+            "updatedAt": "2026-01-07T10:00:00"
+        }
+    ]
+}
+```
+
+#### 错误码
+
+无
+
+### 获取摄像头详情
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `GET` http://localhost:10050/api/admin/cameras/detail
+- 开发环境: `GET` https://tg-live-game.pwtk.cc/api/admin/cameras/detail
+
+描述:获取摄像头详情
+
+ContentType:`application/x-www-form-urlencoded;charset=UTF-8`
+
+#### 请求参数
+
+##### Query Parameter
+
+| 名称 | 类型  | 必填 | 最大长度 | 描述          | 示例值 |
+| ---- | ----- | ---- | -------- | ------------- | ------ |
+| id   | int64 | 是   | -        | 摄像头主键 ID | 1      |
+
+#### 响应参数
+
+| 名称 | 类型 | 必填 | 最大长度 | 描述 | 示例值 |
+| --- | --- | --- | --- | --- | --- |
+| success | Boolean | 否 | - | 请求是否成功 | true |
+| errCode | String | 否 | - | 错误码(失败时返回) |  |
+| errMessage | String | 否 | - | 错误信息(失败时返回) |  |
+| data | object | 否 |  | 响应数据 (ActualType: CameraInfoDTO) |  |
+| └ id | Long | 否 | - | 主键 ID | 1 |
+| └ cameraId | String | 否 | - | 摄像头 ID | cam_001 |
+| └ name | String | 否 | - | 名称 | 主摄像头 |
+| └ ip | String | 否 | - | IP 地址 | 192.168.1.100 |
+| └ port | Integer | 否 | - | 端口 | 80 |
+| └ username | String | 否 | - | 用户名 | admin |
+| └ brand | String | 否 | - | 品牌 | hikvision |
+| └ capability | String | 否 | - | 能力: switch_only, ptz_enabled | ptz_enabled |
+| └ status | String | 否 | - | 状态: ONLINE, OFFLINE | ONLINE |
+| └ lssId | String | 否 | - | 绑定的 LSS 节点 ID | lss_001 |
+| └ model | String | 否 | - | 摄像头型号 | DS-2CD2T47G2-LSU/SL |
+| └ rtspUrl | String | 否 | - | RTSP 推流地址 | rtsp://admin:password@192.168.1.100:554/stream1 |
+| └ channelNo | String | 否 | - | 通道号 | 1 |
+| └ remark | String | 否 | - | 备注 |  |
+| └ enabled | Boolean | 否 | - | 是否启用 | true |
+| └ createdAt | LocalDateTime | 否 | - | 创建时间 | 2026-01-07T10:00:00 |
+| └ updatedAt | LocalDateTime | 否 | - | 更新时间 | 2026-01-07T10:00:00 |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": {
+        "id": 1,
+        "cameraId": "cam_001",
+        "name": "主摄像头",
+        "ip": "192.168.1.100",
+        "port": 80,
+        "username": "admin",
+        "brand": "hikvision",
+        "capability": "ptz_enabled",
+        "status": "ONLINE",
+        "lssId": "lss_001",
+        "model": "DS-2CD2T47G2-LSU/SL",
+        "rtspUrl": "rtsp://admin:password@192.168.1.100:554/stream1",
+        "channelNo": "1",
+        "remark": "string",
+        "enabled": true,
+        "createdAt": "2026-01-07T10:00:00",
+        "updatedAt": "2026-01-07T10:00:00"
+    }
+}
+```
+
+#### 错误码
+
+无
+
+### 添加摄像头
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `POST` http://localhost:10050/api/admin/cameras/add
+- 开发环境: `POST` https://tg-live-game.pwtk.cc/api/admin/cameras/add
+
+描述:添加摄像头
+
+ContentType:`application/json`
+
+#### 请求参数
+
+##### Body Parameter
+
+| 名称 | 类型 | 必填 | 最大长度 | 描述 | 示例值 |
+| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
+| cameraId | String | 是 | 50 | 摄像头 ID(唯一标识)<br>Validate[max: 50; ] | cam_001 |
+| name | String | 是 | 100 | 名称<br>Validate[max: 100; ] | 主摄像头 |
+| ip | String | 是 | - | IP 地址<br>Validate[regexp: ^((25[0-5] | 2[0-4]\\d | [01]?\\d\\d?)\\.){3}(25[0-5] | 2[0-4]\\d | [01]?\\d\\d?)$; ] | 192.168.1.100 |
+| port | Integer | 否 | - | 端口 | 80 |
+| username | String | 否 | 50 | 用户名<br>Validate[max: 50; ] | admin |
+| password | String | 否 | 100 | 密码<br>Validate[max: 100; ] | password123 |
+| brand | String | 否 | 30 | 品牌<br>Validate[max: 30; ] | hikvision |
+| capability | String | 否 | - | 能力: switch_only, ptz_enabled<br>Validate[regexp: ^(switch_only | ptz_enabled)$; ] | ptz_enabled |
+| lssId | String | 否 | - | 绑定的 LSS 节点 ID | lss_001 |
+| model | String | 否 | 100 | 摄像头型号<br>Validate[max: 100; ] | DS-2CD2T47G2-LSU/SL |
+| rtspUrl | String | 否 | 500 | RTSP 推流地址<br>Validate[max: 500; ] | rtsp://admin:password@192.168.1.100:554/stream1 |
+| channelNo | String | 否 | 20 | 通道号 (用于多通道摄像头)<br>Validate[max: 20; ] | 1 |
+| remark | String | 否 | 500 | 备注<br>Validate[max: 500; ] |  |
+
+#### 请求示例
+
+```
+{
+    "cameraId": "cam_001",
+    "name": "主摄像头",
+    "ip": "192.168.1.100",
+    "port": 80,
+    "username": "admin",
+    "password": "password123",
+    "brand": "hikvision",
+    "capability": "ptz_enabled",
+    "lssId": "lss_001",
+    "model": "DS-2CD2T47G2-LSU/SL",
+    "rtspUrl": "rtsp://admin:password@192.168.1.100:554/stream1",
+    "channelNo": "1",
+    "remark": "string"
+}
+```
+
+#### 响应参数
+
+| 名称 | 类型 | 必填 | 最大长度 | 描述 | 示例值 |
+| --- | --- | --- | --- | --- | --- |
+| success | Boolean | 否 | - | 请求是否成功 | true |
+| errCode | String | 否 | - | 错误码(失败时返回) |  |
+| errMessage | String | 否 | - | 错误信息(失败时返回) |  |
+| data | object | 否 |  | 响应数据 (ActualType: CameraInfoDTO) |  |
+| └ id | Long | 否 | - | 主键 ID | 1 |
+| └ cameraId | String | 否 | - | 摄像头 ID | cam_001 |
+| └ name | String | 否 | - | 名称 | 主摄像头 |
+| └ ip | String | 否 | - | IP 地址 | 192.168.1.100 |
+| └ port | Integer | 否 | - | 端口 | 80 |
+| └ username | String | 否 | - | 用户名 | admin |
+| └ brand | String | 否 | - | 品牌 | hikvision |
+| └ capability | String | 否 | - | 能力: switch_only, ptz_enabled | ptz_enabled |
+| └ status | String | 否 | - | 状态: ONLINE, OFFLINE | ONLINE |
+| └ lssId | String | 否 | - | 绑定的 LSS 节点 ID | lss_001 |
+| └ model | String | 否 | - | 摄像头型号 | DS-2CD2T47G2-LSU/SL |
+| └ rtspUrl | String | 否 | - | RTSP 推流地址 | rtsp://admin:password@192.168.1.100:554/stream1 |
+| └ channelNo | String | 否 | - | 通道号 | 1 |
+| └ remark | String | 否 | - | 备注 |  |
+| └ enabled | Boolean | 否 | - | 是否启用 | true |
+| └ createdAt | LocalDateTime | 否 | - | 创建时间 | 2026-01-07T10:00:00 |
+| └ updatedAt | LocalDateTime | 否 | - | 更新时间 | 2026-01-07T10:00:00 |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": {
+        "id": 1,
+        "cameraId": "cam_001",
+        "name": "主摄像头",
+        "ip": "192.168.1.100",
+        "port": 80,
+        "username": "admin",
+        "brand": "hikvision",
+        "capability": "ptz_enabled",
+        "status": "ONLINE",
+        "lssId": "lss_001",
+        "model": "DS-2CD2T47G2-LSU/SL",
+        "rtspUrl": "rtsp://admin:password@192.168.1.100:554/stream1",
+        "channelNo": "1",
+        "remark": "string",
+        "enabled": true,
+        "createdAt": "2026-01-07T10:00:00",
+        "updatedAt": "2026-01-07T10:00:00"
+    }
+}
+```
+
+#### 错误码
+
+无
+
+### 更新摄像头
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `POST` http://localhost:10050/api/admin/cameras/update
+- 开发环境: `POST` https://tg-live-game.pwtk.cc/api/admin/cameras/update
+
+描述:更新摄像头
+
+ContentType:`application/json`
+
+#### 请求参数
+
+##### Body Parameter
+
+| 名称 | 类型 | 必填 | 最大长度 | 描述 | 示例值 |
+| --- | --- | --- | --- | --- | --- |
+| id | Long | 否 | - | 摄像头数据库 ID | 1 |
+| name | String | 否 | - | 名称 | 主摄像头 |
+| ip | String | 否 | - | IP 地址 | 192.168.1.100 |
+| port | Integer | 否 | - | 端口 | 80 |
+| username | String | 否 | - | 用户名 | admin |
+| password | String | 否 | - | 密码(不传或为空则不修改) | password123 |
+| brand | String | 否 | - | 品牌 | hikvision |
+| capability | String | 否 | - | 能力: switch_only, ptz_enabled | ptz_enabled |
+| lssId | String | 否 | - | 绑定的 LSS 节点 ID | lss_001 |
+| model | String | 否 | - | 摄像头型号 | DS-2CD2T47G2-LSU/SL |
+| rtspUrl | String | 否 | - | RTSP 推流地址 | rtsp://admin:password@192.168.1.100:554/stream1 |
+| channelNo | String | 否 | - | 通道号 | 1 |
+| remark | String | 否 | - | 备注 |  |
+| enabled | Boolean | 否 | - | 启用状态 | true |
+
+#### 请求示例
+
+```
+{
+    "id": 1,
+    "name": "主摄像头",
+    "ip": "192.168.1.100",
+    "port": 80,
+    "username": "admin",
+    "password": "password123",
+    "brand": "hikvision",
+    "capability": "ptz_enabled",
+    "lssId": "lss_001",
+    "model": "DS-2CD2T47G2-LSU/SL",
+    "rtspUrl": "rtsp://admin:password@192.168.1.100:554/stream1",
+    "channelNo": "1",
+    "remark": "string",
+    "enabled": true
+}
+```
+
+#### 响应参数
+
+| 名称 | 类型 | 必填 | 最大长度 | 描述 | 示例值 |
+| --- | --- | --- | --- | --- | --- |
+| success | Boolean | 否 | - | 请求是否成功 | true |
+| errCode | String | 否 | - | 错误码(失败时返回) |  |
+| errMessage | String | 否 | - | 错误信息(失败时返回) |  |
+| data | object | 否 |  | 响应数据 (ActualType: CameraInfoDTO) |  |
+| └ id | Long | 否 | - | 主键 ID | 1 |
+| └ cameraId | String | 否 | - | 摄像头 ID | cam_001 |
+| └ name | String | 否 | - | 名称 | 主摄像头 |
+| └ ip | String | 否 | - | IP 地址 | 192.168.1.100 |
+| └ port | Integer | 否 | - | 端口 | 80 |
+| └ username | String | 否 | - | 用户名 | admin |
+| └ brand | String | 否 | - | 品牌 | hikvision |
+| └ capability | String | 否 | - | 能力: switch_only, ptz_enabled | ptz_enabled |
+| └ status | String | 否 | - | 状态: ONLINE, OFFLINE | ONLINE |
+| └ lssId | String | 否 | - | 绑定的 LSS 节点 ID | lss_001 |
+| └ model | String | 否 | - | 摄像头型号 | DS-2CD2T47G2-LSU/SL |
+| └ rtspUrl | String | 否 | - | RTSP 推流地址 | rtsp://admin:password@192.168.1.100:554/stream1 |
+| └ channelNo | String | 否 | - | 通道号 | 1 |
+| └ remark | String | 否 | - | 备注 |  |
+| └ enabled | Boolean | 否 | - | 是否启用 | true |
+| └ createdAt | LocalDateTime | 否 | - | 创建时间 | 2026-01-07T10:00:00 |
+| └ updatedAt | LocalDateTime | 否 | - | 更新时间 | 2026-01-07T10:00:00 |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": {
+        "id": 1,
+        "cameraId": "cam_001",
+        "name": "主摄像头",
+        "ip": "192.168.1.100",
+        "port": 80,
+        "username": "admin",
+        "brand": "hikvision",
+        "capability": "ptz_enabled",
+        "status": "ONLINE",
+        "lssId": "lss_001",
+        "model": "DS-2CD2T47G2-LSU/SL",
+        "rtspUrl": "rtsp://admin:password@192.168.1.100:554/stream1",
+        "channelNo": "1",
+        "remark": "string",
+        "enabled": true,
+        "createdAt": "2026-01-07T10:00:00",
+        "updatedAt": "2026-01-07T10:00:00"
+    }
+}
+```
+
+#### 错误码
+
+无
+
+### 删除摄像头
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `POST` http://localhost:10050/api/admin/cameras/delete
+- 开发环境: `POST` https://tg-live-game.pwtk.cc/api/admin/cameras/delete
+
+描述:删除摄像头
+
+ContentType:`application/x-www-form-urlencoded;charset=UTF-8`
+
+#### 请求参数
+
+##### Query Parameter
+
+| 名称 | 类型  | 必填 | 最大长度 | 描述          | 示例值 |
+| ---- | ----- | ---- | -------- | ------------- | ------ |
+| id   | int64 | 是   | -        | 摄像头主键 ID | 1      |
+
+#### 响应参数
+
+| 名称       | 类型    | 必填 | 最大长度 | 描述                        | 示例值 |
+| ---------- | ------- | ---- | -------- | --------------------------- | ------ |
+| success    | Boolean | 否   | -        | 请求是否成功                | true   |
+| errCode    | String  | 否   | -        | 错误码(失败时返回)        |        |
+| errMessage | String  | 否   | -        | 错误信息(失败时返回)      |        |
+| data       | object  | 否   | -        | 响应数据 (ActualType: Void) |        |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": {}
+}
+```
+
+#### 错误码
+
+无
+
+### 检测摄像头连通性
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `POST` http://localhost:10050/api/admin/cameras/check
+- 开发环境: `POST` https://tg-live-game.pwtk.cc/api/admin/cameras/check
+
+描述:检测摄像头连通性
+
+ContentType:`application/x-www-form-urlencoded;charset=UTF-8`
+
+#### 请求参数
+
+##### Query Parameter
+
+| 名称 | 类型  | 必填 | 最大长度 | 描述          | 示例值 |
+| ---- | ----- | ---- | -------- | ------------- | ------ |
+| id   | int64 | 是   | -        | 摄像头主键 ID | 1      |
+
+#### 响应参数
+
+| 名称       | 类型    | 必填 | 最大长度 | 描述                           | 示例值 |
+| ---------- | ------- | ---- | -------- | ------------------------------ | ------ |
+| success    | Boolean | 否   | -        | 请求是否成功                   | true   |
+| errCode    | String  | 否   | -        | 错误码(失败时返回)           |        |
+| errMessage | String  | 否   | -        | 错误信息(失败时返回)         |        |
+| data       | boolean | 否   | -        | 响应数据 (ActualType: Boolean) |        |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": false
+}
+```
+
+#### 错误码
+
+无
+
+### 获取摄像头快照
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `GET` http://localhost:10050/api/admin/cameras/snapshot
+- 开发环境: `GET` https://tg-live-game.pwtk.cc/api/admin/cameras/snapshot
+
+描述:获取摄像头快照
+
+ContentType:`application/x-www-form-urlencoded;charset=UTF-8`
+
+#### 请求参数
+
+##### Query Parameter
+
+| 名称 | 类型  | 必填 | 最大长度 | 描述          | 示例值 |
+| ---- | ----- | ---- | -------- | ------------- | ------ |
+| id   | int64 | 是   | -        | 摄像头主键 ID | 1      |
+
+#### 响应参数
+
+| 名称       | 类型          | 必填 | 最大长度 | 描述                        | 示例值 |
+| ---------- | ------------- | ---- | -------- | --------------------------- | ------ |
+| success    | Boolean       | 否   | -        | 请求是否成功                | true   |
+| errCode    | String        | 否   | -        | 错误码(失败时返回)        |        |
+| errMessage | String        | 否   | -        | 错误信息(失败时返回)      |        |
+| data       | array         | 否   |          | 响应数据 (ActualType: byte) |        |
+| └ -        | array[byte[]] | 否   | -        | array of byte[]             |        |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": [
+        {
+            " -": ""
+        }
+    ]
+}
+```
+
+#### 错误码
+
+无
+
+### PTZ 控制
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `POST` http://localhost:10050/api/admin/cameras/ptz
+- 开发环境: `POST` https://tg-live-game.pwtk.cc/api/admin/cameras/ptz
+
+描述:PTZ 控制
+
+ContentType:`application/json`
+
+#### 请求参数
+
+##### Body Parameter
+
+| 名称   | 类型   | 必填 | 最大长度 | 描述             | 示例值 |
+| ------ | ------ | ---- | -------- | ---------------- | ------ |
+| id     | Long   | 否   | -        | 摄像头 ID        | 1      |
+| action | String | 否   | -        | PTZ 动作         | UP     |
+| speed  | Float  | 否   | -        | 速度 (0.0 - 1.0) | 0.5    |
+
+#### 请求示例
+
+```
+{
+    "id": 1,
+    "action": "UP",
+    "speed": 0.5
+}
+```
+
+#### 响应参数
+
+| 名称       | 类型    | 必填 | 最大长度 | 描述                           | 示例值 |
+| ---------- | ------- | ---- | -------- | ------------------------------ | ------ |
+| success    | Boolean | 否   | -        | 请求是否成功                   | true   |
+| errCode    | String  | 否   | -        | 错误码(失败时返回)           |        |
+| errMessage | String  | 否   | -        | 错误信息(失败时返回)         |        |
+| data       | boolean | 否   | -        | 响应数据 (ActualType: Boolean) |        |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": false
+}
+```
+
+#### 错误码
+
+无
+
+### 获取摄像头预置位列表
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `POST` http://localhost:10050/api/admin/cameras/presetList
+- 开发环境: `POST` https://tg-live-game.pwtk.cc/api/admin/cameras/presetList
+
+描述:获取摄像头预置位列表
+
+ContentType:`application/x-www-form-urlencoded;charset=UTF-8`
+
+#### 请求参数
+
+##### Query Parameter
+
+| 名称    | 类型  | 必填 | 最大长度 | 描述           | 示例值 |
+| ------- | ----- | ---- | -------- | -------------- | ------ |
+| id      | int64 | 是   | -        | 摄像头主键 ID  | 1      |
+| channel | int32 | 否   | -        | 通道号(可选) | 1      |
+
+#### 响应参数
+
+| 名称       | 类型    | 必填 | 最大长度 | 描述                          | 示例值 |
+| ---------- | ------- | ---- | -------- | ----------------------------- | ------ |
+| success    | Boolean | 否   | -        | 请求是否成功                  | true   |
+| errCode    | String  | 否   | -        | 错误码(失败时返回)          |        |
+| errMessage | String  | 否   | -        | 错误信息(失败时返回)        |        |
+| data       | string  | 否   | -        | 响应数据 (ActualType: String) |        |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": "string"
+}
+```
+
+#### 错误码
+
+无
+
+### 获取摄像头设备信息
+
+维护人:TG Live
+
+#### URL
+
+- 本地开发环境: `POST` http://localhost:10050/api/admin/cameras/deviceInfo
+- 开发环境: `POST` https://tg-live-game.pwtk.cc/api/admin/cameras/deviceInfo
+
+描述:获取摄像头设备信息
+
+ContentType:`application/x-www-form-urlencoded;charset=UTF-8`
+
+#### 请求参数
+
+##### Query Parameter
+
+| 名称 | 类型  | 必填 | 最大长度 | 描述          | 示例值 |
+| ---- | ----- | ---- | -------- | ------------- | ------ |
+| id   | int64 | 是   | -        | 摄像头主键 ID | 1      |
+
+#### 响应参数
+
+| 名称       | 类型    | 必填 | 最大长度 | 描述                          | 示例值 |
+| ---------- | ------- | ---- | -------- | ----------------------------- | ------ |
+| success    | Boolean | 否   | -        | 请求是否成功                  | true   |
+| errCode    | String  | 否   | -        | 错误码(失败时返回)          |        |
+| errMessage | String  | 否   | -        | 错误信息(失败时返回)        |        |
+| data       | string  | 否   | -        | 响应数据 (ActualType: String) |        |
+
+#### 响应示例
+
+```
+{
+    "success": true,
+    "errCode": "string",
+    "errMessage": "string",
+    "data": "string"
+}
+```
+
+#### 错误码
+
+无

+ 45 - 0
src/locales/en.json

@@ -2,10 +2,15 @@
   "Cloudflare Stream": "Cloudflare Stream",
   "Cloudflare Stream 配置": "Cloudflare Stream Configuration",
   "IP地址": "IP Address",
+  "LSS": "",
+  "Logo URL": "",
+  "RTSP URL模板": "",
   "RTSP 地址": "RTSP URL",
   "RTSP 流": "RTSP Stream",
   "RTSP 流需要通过服务端转换为 HLS/WebRTC 后才能在浏览器播放": "RTSP streams need to be converted to HLS/WebRTC via server before playing in browser",
   "RTSP地址": "RTSP URL",
+  "RTSP端口": "",
+  "Stream SN": "",
   "Stream 测试": "Stream Test",
   "Video ID": "Video ID",
   "button.cancel": "Cancel",
@@ -21,14 +26,18 @@
   "login.confirmPassword": "Confirm Password",
   "login.editpassWord": "Edit Password",
   "login.passWord": "Password",
+  "name": "Name",
   "pop.ScanQRcode": "Scan QR Code",
   "pop.getsecretkey": "Get Secret Key",
   "pop.secretkey": "Secret Key",
   "pop.systemIdentity": "System Identity",
+  "stream sn": "Stream SN",
   "table.accountNumber": "Account Number",
   "table.mobilePhoneNumber": "Mobile Phone Number",
   "table.qrcode": "QR Code",
   "两次输入的密码不一致": "The passwords entered twice do not match",
+  "个厂家": "Factory",
+  "个厂家吗?": "Factory?",
   "事件日志": "Event Log",
   "仅切换": "Switch Only",
   "仅在前端直接调用 API 时需要(不推荐)": "Only needed when directly calling the API in the frontend (not recommended)",
@@ -40,11 +49,21 @@
   "停止": "Stop",
   "全屏": "Fullscreen",
   "全部": "All",
+  "关闭": "",
+  "关闭失败": "",
+  "关闭时间": "",
   "创建时间": "Created At",
+  "初始化失败": "",
+  "初始化成功": "",
+  "初始化默认数据": "",
   "删除": "Delete",
   "删除失败": "Delete failed",
   "删除成功": "Deleted successfully",
   "刷新数据": "Refresh",
+  "功能": "",
+  "协议支持": "",
+  "厂家代码": "",
+  "厂家名称": "",
   "原密码": "Old Password",
   "取消": "Cancel",
   "取消选择": "Clear Selection",
@@ -54,19 +73,27 @@
   "名称": "Name",
   "吗?": "?",
   "否": "No",
+  "启动": "Start",
+  "启动失败": "Start failed",
+  "启动成功": "Start successful",
+  "启动时间": "",
   "启用": "Enabled",
   "启用状态": "Status",
+  "命令模板": "Command Template",
   "品牌": "Brand",
   "在线": "Online",
+  "地址": "Address",
   "复制": "Copy",
   "如何获取 Customer Subdomain": "How to get Customer Subdomain",
   "密码": "Password",
   "密码长度不能少于6位": "Password length must be at least 6 characters",
+  "已关闭": "Closed",
   "已启用": "Enabled",
   "已禁用": "Disabled",
   "已选择": "Selected",
   "序号": "No.",
   "开始日期": "Start Date",
+  "当前任务": "Current Task",
   "当前状态": "Current Status",
   "忘记密码?": "Forgot password?",
   "快捷操作": "Quick Actions",
@@ -78,15 +105,20 @@
   "批量删除": "Batch Delete",
   "批量删除失败": "Batch delete failed",
   "技术支持": "Support",
+  "排序": "Sort",
+  "排序号": "",
+  "推流方式": "Push Stream Method",
   "推荐通过后端代理调用,避免暴露 Token": "Recommended to call through the backend proxy to avoid exposing the Token",
   "描述": "Description",
   "提示": "Notice",
+  "摄像头": "Camera",
   "摄像头ID": "Camera ID",
   "摄像头在线率": "Camera Online Rate",
   "摄像头总数": "Total Cameras",
   "摄像头数": "Cameras",
   "摄像头管理": "Camera Management",
   "摄像头管理系统": "Camera Management",
+  "摄像头编号": "Camera ID",
   "摄像头连接失败": "Camera connection failed",
   "摄像头连接正常": "Camera connection successful",
   "播放": "Play",
@@ -97,6 +129,8 @@
   "操作": "Actions",
   "数据更新时间": "Last Updated",
   "新增": "Add",
+  "新增 LiveStream": "",
+  "新增厂家": "",
   "新增成功": "Added successfully",
   "新增摄像头": "Add Camera",
   "新增机器": "Add Machine",
@@ -109,6 +143,7 @@
   "机器ID": "Machine ID",
   "机器总数": "Total Machines",
   "机器管理": "Machine Management",
+  "查看": "",
   "查询": "Search",
   "检测": "Test",
   "检测失败": "Test failed",
@@ -133,6 +168,8 @@
   "直接 URL": "Direct URL",
   "直接 URL 播放": "Direct URL Playback",
   "确定": "Confirm",
+  "确定要初始化默认厂家数据吗?这将添加预设的摄像头厂家信息。": "",
+  "确定要删除厂家": "Are you sure you want to delete factory",
   "确定要删除摄像头": "Are you sure you want to delete camera",
   "确定要删除机器": "Are you sure you want to delete machine",
   "确定要删除选中的": "Are you sure you want to delete the selected",
@@ -145,6 +182,8 @@
   "系统运行正常": "System running normally",
   "结束日期": "End Date",
   "编辑": "Edit",
+  "编辑 LiveStream": "Edit LiveStream",
+  "编辑厂家": "Edit Factory",
   "编辑摄像头": "Edit Camera",
   "编辑机器": "Edit Machine",
   "能力": "Capabilities",
@@ -153,6 +192,7 @@
   "至": "to",
   "获取机器列表失败": "Failed to get machine list",
   "获取统计数据失败": "Failed to get statistics",
+  "观看": "Watch",
   "观看统计": "Watching Statistics",
   "视频地址": "Video URL",
   "视频播放测试": "Video Playback Test",
@@ -160,6 +200,8 @@
   "请再次输入新密码": "Please enter the new password again",
   "请联系管理员重置密码": "Please contact the administrator to reset your password",
   "请输入IP地址": "Please enter IP address",
+  "请输入厂家代码": "Please enter factory code",
+  "请输入厂家名称": "Please enter factory name",
   "请输入原密码": "Please enter the old password",
   "请输入名称": "Please enter name",
   "请输入密码": "Please enter password",
@@ -169,6 +211,7 @@
   "请输入正确的IP地址": "Please enter a valid IP address",
   "请输入用户名": "Please enter username",
   "请输入视频地址并点击播放": "Please enter video URL and click play",
+  "请选择推流方式": "Please select push stream method",
   "请选择视频源并点击播放": "Please select video source and click play",
   "转换服务地址": "Proxy Service URL",
   "退出登录": "Logout",
@@ -181,5 +224,7 @@
   "重置": "Reset",
   "静音": "Muted",
   "项": "items",
+  "默认分辨率": "Default Resolution",
+  "默认端口": "Default Port",
   "默认视角": "Default View"
 }

+ 45 - 0
src/locales/zh-cn.json

@@ -2,10 +2,15 @@
   "Cloudflare Stream": "Cloudflare Stream",
   "Cloudflare Stream 配置": "Cloudflare Stream 配置",
   "IP地址": "IP地址",
+  "LSS": "LSS",
+  "Logo URL": "Logo URL",
+  "RTSP URL模板": "RTSP URL模板",
   "RTSP 地址": "RTSP 地址",
   "RTSP 流": "RTSP 流",
   "RTSP 流需要通过服务端转换为 HLS/WebRTC 后才能在浏览器播放": "RTSP 流需要通过服务端转换为 HLS/WebRTC 后才能在浏览器播放",
   "RTSP地址": "RTSP地址",
+  "RTSP端口": "RTSP端口",
+  "Stream SN": "Stream SN",
   "Stream 测试": "Stream 测试",
   "Video ID": "Video ID",
   "button.cancel": "button.cancel",
@@ -21,14 +26,18 @@
   "login.confirmPassword": "login.confirmPassword",
   "login.editpassWord": "login.editpassWord",
   "login.passWord": "login.passWord",
+  "name": "name",
   "pop.ScanQRcode": "pop.ScanQRcode",
   "pop.getsecretkey": "pop.getsecretkey",
   "pop.secretkey": "pop.secretkey",
   "pop.systemIdentity": "pop.systemIdentity",
+  "stream sn": "stream sn",
   "table.accountNumber": "table.accountNumber",
   "table.mobilePhoneNumber": "table.mobilePhoneNumber",
   "table.qrcode": "table.qrcode",
   "两次输入的密码不一致": "两次输入的密码不一致",
+  "个厂家": "个厂家",
+  "个厂家吗?": "个厂家吗?",
   "事件日志": "事件日志",
   "仅切换": "仅切换",
   "仅在前端直接调用 API 时需要(不推荐)": "仅在前端直接调用 API 时需要(不推荐)",
@@ -40,11 +49,21 @@
   "停止": "停止",
   "全屏": "全屏",
   "全部": "全部",
+  "关闭": "关闭",
+  "关闭失败": "关闭失败",
+  "关闭时间": "关闭时间",
   "创建时间": "创建时间",
+  "初始化失败": "初始化失败",
+  "初始化成功": "初始化成功",
+  "初始化默认数据": "初始化默认数据",
   "删除": "删除",
   "删除失败": "删除失败",
   "删除成功": "删除成功",
   "刷新数据": "刷新数据",
+  "功能": "功能",
+  "协议支持": "协议支持",
+  "厂家代码": "厂家代码",
+  "厂家名称": "厂家名称",
   "原密码": "原密码",
   "取消": "取消",
   "取消选择": "取消选择",
@@ -54,19 +73,27 @@
   "名称": "名称",
   "吗?": "吗?",
   "否": "否",
+  "启动": "启动",
+  "启动失败": "启动失败",
+  "启动成功": "启动成功",
+  "启动时间": "启动时间",
   "启用": "启用",
   "启用状态": "启用状态",
+  "命令模板": "命令模板",
   "品牌": "品牌",
   "在线": "在线",
+  "地址": "地址",
   "复制": "复制",
   "如何获取 Customer Subdomain": "如何获取 Customer Subdomain",
   "密码": "密码",
   "密码长度不能少于6位": "密码长度不能少于6位",
+  "已关闭": "已关闭",
   "已启用": "已启用",
   "已禁用": "已禁用",
   "已选择": "已选择",
   "序号": "序号",
   "开始日期": "开始日期",
+  "当前任务": "当前任务",
   "当前状态": "当前状态",
   "忘记密码?": "忘记密码?",
   "快捷操作": "快捷操作",
@@ -78,15 +105,20 @@
   "批量删除": "批量删除",
   "批量删除失败": "批量删除失败",
   "技术支持": "技术支持",
+  "排序": "排序",
+  "排序号": "排序号",
+  "推流方式": "推流方式",
   "推荐通过后端代理调用,避免暴露 Token": "推荐通过后端代理调用,避免暴露 Token",
   "描述": "描述",
   "提示": "提示",
+  "摄像头": "摄像头",
   "摄像头ID": "摄像头ID",
   "摄像头在线率": "摄像头在线率",
   "摄像头总数": "摄像头总数",
   "摄像头数": "摄像头数",
   "摄像头管理": "摄像头管理",
   "摄像头管理系统": "摄像头管理系统",
+  "摄像头编号": "摄像头编号",
   "摄像头连接失败": "摄像头连接失败",
   "摄像头连接正常": "摄像头连接正常",
   "播放": "播放",
@@ -97,6 +129,8 @@
   "操作": "操作",
   "数据更新时间": "数据更新时间",
   "新增": "新增",
+  "新增 LiveStream": "新增 LiveStream",
+  "新增厂家": "新增厂家",
   "新增成功": "新增成功",
   "新增摄像头": "新增摄像头",
   "新增机器": "新增机器",
@@ -109,6 +143,7 @@
   "机器ID": "机器ID",
   "机器总数": "机器总数",
   "机器管理": "机器管理",
+  "查看": "查看",
   "查询": "查询",
   "检测": "检测",
   "检测失败": "检测失败",
@@ -133,6 +168,8 @@
   "直接 URL": "直接 URL",
   "直接 URL 播放": "直接 URL 播放",
   "确定": "确定",
+  "确定要初始化默认厂家数据吗?这将添加预设的摄像头厂家信息。": "确定要初始化默认厂家数据吗?这将添加预设的摄像头厂家信息。",
+  "确定要删除厂家": "确定要删除厂家",
   "确定要删除摄像头": "确定要删除摄像头",
   "确定要删除机器": "确定要删除机器",
   "确定要删除选中的": "确定要删除选中的",
@@ -145,6 +182,8 @@
   "系统运行正常": "系统运行正常",
   "结束日期": "结束日期",
   "编辑": "编辑",
+  "编辑 LiveStream": "编辑 LiveStream",
+  "编辑厂家": "编辑厂家",
   "编辑摄像头": "编辑摄像头",
   "编辑机器": "编辑机器",
   "能力": "能力",
@@ -153,6 +192,7 @@
   "至": "至",
   "获取机器列表失败": "获取机器列表失败",
   "获取统计数据失败": "获取统计数据失败",
+  "观看": "观看",
   "观看统计": "观看统计",
   "视频地址": "视频地址",
   "视频播放测试": "视频播放测试",
@@ -160,6 +200,8 @@
   "请再次输入新密码": "请再次输入新密码",
   "请联系管理员重置密码": "请联系管理员重置密码",
   "请输入IP地址": "请输入IP地址",
+  "请输入厂家代码": "请输入厂家代码",
+  "请输入厂家名称": "请输入厂家名称",
   "请输入原密码": "请输入原密码",
   "请输入名称": "请输入名称",
   "请输入密码": "请输入密码",
@@ -169,6 +211,7 @@
   "请输入正确的IP地址": "请输入正确的IP地址",
   "请输入用户名": "请输入用户名",
   "请输入视频地址并点击播放": "请输入视频地址并点击播放",
+  "请选择推流方式": "请选择推流方式",
   "请选择视频源并点击播放": "请选择视频源并点击播放",
   "转换服务地址": "转换服务地址",
   "退出登录": "退出登录",
@@ -181,5 +224,7 @@
   "重置": "重置",
   "静音": "静音",
   "项": "项",
+  "默认分辨率": "默认分辨率",
+  "默认端口": "默认端口",
   "默认视角": "默认视角"
 }

+ 10 - 3
src/types/index.ts

@@ -151,10 +151,15 @@ export interface CameraInfoDTO {
   brand: string
   capability: 'switch_only' | 'ptz_enabled'
   status: 'ONLINE' | 'OFFLINE'
-  machineId: string
-  machineName: string
+  lssId?: string
+  model?: string
+  rtspUrl?: string
+  channelNo?: string
+  remark?: string
+  machineId?: string
+  machineName?: string
   enabled: boolean
-  channels: ChannelInfoDTO[]
+  channels?: ChannelInfoDTO[]
   createdAt: string
   updatedAt: string
 }
@@ -292,7 +297,9 @@ export interface AdminPTZRequest {
 // 摄像头列表请求参数
 export interface CameraListRequest extends PageRequest {
   machineId?: string
+  lssId?: string
   status?: 'ONLINE' | 'OFFLINE'
+  enabled?: boolean
 }
 
 // 机器列表请求参数

+ 4 - 4
src/views/live-stream/index.vue

@@ -32,18 +32,18 @@
         height="100%"
         @sort-change="handleSortChange"
       >
-        <el-table-column prop="streamSn" :label="t('stream sn(生成)')" min-width="140" show-overflow-tooltip />
-        <el-table-column prop="name" :label="t('name(手填)')" min-width="120" show-overflow-tooltip>
+        <el-table-column prop="streamSn" :label="t('stream sn')" min-width="140" show-overflow-tooltip />
+        <el-table-column prop="name" :label="t('name')" min-width="120" show-overflow-tooltip>
           <template #default="{ row }">
             <el-link type="primary" @click="handleEdit(row)">{{ row.name }}</el-link>
           </template>
         </el-table-column>
-        <el-table-column prop="lssName" :label="t('LSS (下拉)')" min-width="120" show-overflow-tooltip>
+        <el-table-column prop="lssName" :label="t('LSS')" min-width="120" show-overflow-tooltip>
           <template #default="{ row }">
             <span>{{ row.lssName ? `${row.lssId}/${row.lssName}` : '-' }}</span>
           </template>
         </el-table-column>
-        <el-table-column prop="cameraName" :label="t('摄像头编号(下拉选择)')" min-width="150" show-overflow-tooltip>
+        <el-table-column prop="cameraName" :label="t('摄像头编号')" min-width="150" show-overflow-tooltip>
           <template #default="{ row }">
             <span>{{ row.cameraName || '-' }}</span>
           </template>

+ 82 - 5
src/views/lss/index.vue

@@ -71,14 +71,20 @@
             />
           </template>
         </el-table-column>
-        <el-table-column prop="ffmpegVersion" label="FFmpeg" min-width="120" show-overflow-tooltip />
-        <el-table-column label="詳情" width="70" align="center">
+        <el-table-column prop="ffmpegVersion" label="FFmpeg" show-overflow-tooltip />
+        <el-table-column :label="t('摄像头')" min-width="80" align="center">
+          <template #default="{ row }">
+            <el-button type="primary" link :icon="VideoCamera" @click="handleCameraList(row)" />
+          </template>
+        </el-table-column>
+        <el-table-column label="詳情" align="center">
           <template #default="{ row }">
             <el-button type="primary" link :icon="View" @click="handleViewDetail(row)" />
           </template>
         </el-table-column>
-        <el-table-column :label="t('操作')" width="70" align="center" fixed="right">
+        <el-table-column :label="t('操作')" align="center" fixed="right">
           <template #default="{ row }">
+            <el-button type="primary" link :icon="Edit" @click="handleEdit(row)" />
             <el-button type="danger" link :icon="Delete" @click="handleDelete(row)" />
           </template>
         </el-table-column>
@@ -112,6 +118,39 @@
       </el-descriptions>
     </el-drawer>
 
+    <!-- 摄像头列表抽屉 -->
+    <el-drawer
+      v-model="cameraDrawerVisible"
+      :title="`摄像头列表 - ${currentLss?.lssName || ''}`"
+      direction="rtl"
+      size="600px"
+      destroy-on-close
+    >
+      <div v-loading="cameraLoading">
+        <el-empty v-if="!cameraLoading && cameraList.length === 0" description="暂无关联摄像头" />
+        <el-table v-else :data="cameraList" stripe size="small">
+          <el-table-column prop="cameraId" label="摄像头 ID" min-width="120" show-overflow-tooltip />
+          <el-table-column prop="name" :label="t('名称')" min-width="120" show-overflow-tooltip />
+          <el-table-column prop="ip" label="IP" min-width="120" />
+          <el-table-column prop="status" :label="t('状态')" min-width="80" align="center">
+            <template #default="{ row }">
+              <el-tag :type="row.status === 'ONLINE' ? 'success' : 'danger'" size="small">
+                {{ row.status === 'ONLINE' ? '在线' : '离线' }}
+              </el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column prop="enabled" :label="t('启用')" min-width="70" align="center">
+            <template #default="{ row }">
+              <el-tag :type="row.enabled ? 'success' : 'info'" size="small">
+                {{ row.enabled ? '是' : '否' }}
+              </el-tag>
+            </template>
+          </el-table-column>
+        </el-table>
+        <div v-if="cameraList.length > 0" class="camera-count">共 {{ cameraList.length }} 个摄像头</div>
+      </div>
+    </el-drawer>
+
     <!-- 分页 -->
     <div class="pagination-container">
       <el-pagination
@@ -130,10 +169,11 @@
 
 <script setup lang="ts">
 import { ref, reactive, onMounted } from 'vue'
-import { Search, RefreshRight, Delete, View } from '@element-plus/icons-vue'
+import { Search, RefreshRight, Delete, View, Edit, VideoCamera } from '@element-plus/icons-vue'
 import { ElMessage, ElMessageBox } from 'element-plus'
 import { listLssNodes, deleteLssNode, setLssNodeEnabled } from '@/api/lss'
-import type { LssNodeDTO, LssNodeStatus, LssNodeListRequest } from '@/types'
+import { adminListCameras } from '@/api/camera'
+import type { LssNodeDTO, LssNodeStatus, LssNodeListRequest, CameraInfoDTO } from '@/types'
 import dayjs from 'dayjs'
 import { useI18n } from 'vue-i18n'
 
@@ -185,6 +225,11 @@ const tableRef = ref()
 const detailDrawerVisible = ref(false)
 const currentLss = ref<LssNodeDTO | null>(null)
 
+// 摄像头列表抽屉状态
+const cameraDrawerVisible = ref(false)
+const cameraLoading = ref(false)
+const cameraList = ref<CameraInfoDTO[]>([])
+
 // 排序状态
 const sortState = reactive<{
   sortBy: string
@@ -283,6 +328,31 @@ function handleViewDetail(row: LssNodeDTO) {
   detailDrawerVisible.value = true
 }
 
+function handleEdit(row: LssNodeDTO) {
+  handleCameraList(row)
+}
+
+async function handleCameraList(row: LssNodeDTO) {
+  currentLss.value = row
+  cameraDrawerVisible.value = true
+  cameraLoading.value = true
+  cameraList.value = []
+
+  try {
+    const res = await adminListCameras({ lssId: row.lssId })
+    if (res.success && res.data) {
+      cameraList.value = res.data.list || []
+    } else {
+      ElMessage.error(res.errMessage || '获取摄像头列表失败')
+    }
+  } catch (error) {
+    console.error('获取摄像头列表失败', error)
+    ElMessage.error('获取摄像头列表失败')
+  } finally {
+    cameraLoading.value = false
+  }
+}
+
 async function handleToggleEnabled(row: LssNodeDTO & { _switching?: boolean }, enabled: boolean) {
   row._switching = true
   try {
@@ -392,6 +462,13 @@ onMounted(() => {
   }
 }
 
+.camera-count {
+  margin-top: 16px;
+  text-align: right;
+  color: #666;
+  font-size: 14px;
+}
+
 // 抽屉样式
 :deep(.el-drawer) {
   .el-drawer__header {

+ 35 - 24
src/views/machine/index.vue

@@ -143,30 +143,37 @@
     </div>
 
     <!-- 新增/编辑弹窗 -->
-    <el-dialog v-model="dialogVisible" :title="dialogTitle" width="500px" destroy-on-close data-id="dialog-machine">
-      <el-form ref="formRef" :model="form" :rules="rules" label-width="80px" data-id="form-machine">
-        <el-form-item :label="t('机器ID')" prop="machineId">
-          <el-input v-model="form.machineId" placeholder="请输入机器ID" :disabled="isEdit" data-id="input-machine-id" />
-        </el-form-item>
-        <el-form-item :label="t('名称')" prop="name">
-          <el-input v-model="form.name" placeholder="请输入名称" data-id="input-name" />
-        </el-form-item>
-        <el-form-item :label="t('位置')" prop="location">
-          <el-input v-model="form.location" placeholder="请输入位置" data-id="input-location" />
-        </el-form-item>
-        <el-form-item :label="t('描述')" prop="description">
-          <el-input
-            v-model="form.description"
-            type="textarea"
-            :rows="3"
-            placeholder="请输入描述"
-            data-id="input-description"
-          />
-        </el-form-item>
-        <el-form-item v-if="isEdit" :label="t('启用状态')">
-          <el-switch v-model="form.enabled" data-id="switch-enabled" />
-        </el-form-item>
-      </el-form>
+    <el-dialog v-model="dialogVisible" :title="dialogTitle" destroy-on-close data-id="dialog-machine">
+      <div class="form-container">
+        <el-form ref="formRef" :model="form" :rules="rules" label-width="auto" data-id="form-machine">
+          <el-form-item :label="t('机器ID')" prop="machineId">
+            <el-input
+              v-model="form.machineId"
+              placeholder="请输入机器ID"
+              :disabled="isEdit"
+              data-id="input-machine-id"
+            />
+          </el-form-item>
+          <el-form-item :label="t('名称')" prop="name">
+            <el-input v-model="form.name" placeholder="请输入名称" data-id="input-name" />
+          </el-form-item>
+          <el-form-item :label="t('位置')" prop="location">
+            <el-input v-model="form.location" placeholder="请输入位置" data-id="input-location" />
+          </el-form-item>
+          <el-form-item :label="t('描述')" prop="description">
+            <el-input
+              v-model="form.description"
+              type="textarea"
+              :rows="3"
+              placeholder="请输入描述"
+              data-id="input-description"
+            />
+          </el-form-item>
+          <el-form-item v-if="isEdit" :label="t('启用状态')">
+            <el-switch v-model="form.enabled" data-id="switch-enabled" />
+          </el-form-item>
+        </el-form>
+      </div>
       <template #footer>
         <el-button data-id="btn-cancel" @click="dialogVisible = false">{{ t('取消') }}</el-button>
         <el-button type="primary" :loading="submitLoading" data-id="btn-submit" @click="handleSubmit">
@@ -513,6 +520,10 @@ onMounted(() => {
   box-sizing: border-box;
 }
 
+.form-container {
+  padding-top: 18px;
+}
+
 // 批量操作栏
 .batch-actions {
   flex-shrink: 0;