API UPDATE FIRST

This commit is contained in:
龚皓
2026-06-10 11:46:45 +08:00
commit 9c01a78da5
113 changed files with 31146 additions and 0 deletions

313
AGENTS.md Normal file
View File

@@ -0,0 +1,313 @@
# AGENTS.md
本文档包含针对此 Vue 3 + TypeScript 本地告警管理应用的智能编码代理指南。
## 开发命令
| 命令 | 说明 |
|------|------|
| `npm install` | 安装依赖 |
| `npm run dev` | 启动热重载开发服务器8000端口 |
| `npm run build` | 构建生产版本(包含类型检查) |
| `npm run type-check` | 仅运行 TypeScript 类型检查 |
| `npm run preview` | 本地预览生产构建 |
> 本项目目前未配置测试框架。
## 项目结构
```
src/
├── components/ # 可复用 Vue 组件
├── html/ # 页面级 Vue 组件
├── utils/ # 工具函数和 API
│ ├── boxApi.ts # 主 API 接口
│ ├── useGlobalWebSocket.ts # WebSocket 管理
│ ├── crossWindowChannel.ts # 跨窗口通信
│ └── eventBus.ts # 事件总线mitt
├── router/ # Vue Router 配置
└── stores/ # Pinia 状态管理
```
## 代码风格指南
### TypeScript
- 路径别名:`@/` 映射到 `src/`
- 始终为 props 和数据接口添加类型
### Vue 组件
- 使用 `<script setup>` 组合式 API
- 使用 `defineProps<T>()``defineEmits<T>()`
### 导入顺序
```typescript
// 1. Vue 导入
import { ref, computed, onMounted } from 'vue'
// 2. 第三方库
import axios from 'axios'
import { ElMessage } from 'element-plus'
// 3. 内部导入(@ 别名)
import { BoxApi } from '@/utils/boxApi'
```
### 命名规范
| 类型 | 规范 | 示例 |
|------|------|------|
| 组件 | PascalCase | `CameraRules.vue` |
| 变量 | camelCase | `selectedAlerts` |
| 常量 | UPPER_SNAKE_CASE | `MAX_COUNT` |
| 函数 | fetch/handle/format 前缀 | `fetchEvents` |
### 错误处理
- 异步操作使用 try-catch
- 使用 `ElMessage.error()` 显示错误
- 在 axios 拦截器中处理 401 响应
## 常用模式
### 跨窗口通信
```typescript
import { broadcast, onCrossWindowMessage } from '@/utils/crossWindowChannel'
// 发送
broadcast.sendDialog(data)
// 接收
onCrossWindowMessage((msg) => { /* 处理 */ })
```
### 事件总线
```typescript
import eventBus from '@/utils/eventBus'
eventBus.emit('showDialog', data)
eventBus.on('showDialog', (data) => { /* 处理 */ })
```
### 告警列表导出(支持跨页勾选)
```typescript
// 选中导出selectedAlerts.value 存储勾选的 ID
// 无勾选:导出当前筛选条件下的所有数据
```
## 注意事项
- 这是一个安防摄像头告警管理系统
- 中文 UI 文本是有意保留的
- 跨窗口通信使用 BroadcastChannel需同源策略
- WebSocket 连接地址:`ws://192.168.28.32:8080/ws/event`
- API 基础地址:`http://192.168.28.32:8000/api/v1`
## 补充说明:
event接口
GET请求/api/v1/event
响应体如下:
```
{
"err": {
"ec": 0,
"dm": "ok"
},
"ret": {
"count": 1617,
"next": "http://127.0.0.1:8000/api/v1/events?limit=20&offset=20",
"previous": null,
"results": [
{
"id": 90908,
"camera_id": 1,
"started_at": "2026-04-01T09:30:00Z",
"ended_at": "2026-04-01T09:30:10Z",
"mediums": [
{
"id": 109656,
"name": "snapshot",
"file": "http://127.0.0.1:8000/media/mediums/2026/04/02/camera1_1775035800_wander_OHtwpT0.jpg",
"event_id": 90908
}
],
"camera": {
"id": 1,
"name": "421枪机",
"uri": "rtsp://admin:1234qwer@192.168.28.102:554/Streaming/Channels/202",
"mode": "on",
"status": "online",
"detect_params": {
"threshold": 0.5
},
"default_params": {},
"rules": [
{
"id": 1,
"unique_id": "1268f3f2-e0c5-47ea-aa1f-5b97b6dfb95d",
"camera": 1,
"name": "徘徊",
"mode": "on",
"algo": "wander",
"params": {},
"params_base": "",
"schedule": {},
"event_types": {
"wander": "人员徘徊"
}
},
{
"id": 32,
"unique_id": "9c5e2f2d-4d0e-4ca7-97ee-866462664f8e",
"camera": 1,
"name": "入侵",
"mode": "on",
"algo": "intrusion",
"params": {},
"params_base": "",
"schedule": {},
"event_types": {
"intrusion": "入侵"
}
},
{
"id": 35,
"unique_id": "fdd29eaa-c07a-43c4-ad6c-171d9dfadcf4",
"camera": 1,
"name": "未穿反光衣",
"mode": "on",
"algo": "no_reflective_clothing",
"params": {},
"params_base": "",
"schedule": {},
"event_types": {
"no_reflective_clothing": "未佩戴反光衣"
}
}
],
"should_push": false,
"config_params": {},
"sampling": false,
"note": {},
"snapshot": "http://127.0.0.1:8000/media/camera/camera1_snapshot.jpg",
"remote_id": -1,
"raw_address": "0b8342ec52e62d01dd3273f583d326ec",
"ip": "admin:1234qwer@192.168.28.102:554",
"port": 8082
},
"types": "wander",
"types_bits": 0,
"obj_types": {},
"uuid": "461aeb6e-10f3-4173-9b33-d540ce59511e",
"status": "pending",
"remark": null,
"should_push": false,
"metadata": {}
}]
```
## 本次修改记录2026年4月
### 问题描述
部署到服务器后,前端通过 HTTPS 访问 (`https://192.168.28.32`),但后端返回的 JSON 数据中包含内部地址(如 `http://127.0.0.1:8000/media/...`),导致:
1. **图片无法显示** - 告警列表、详情、通道预览图等全部异常
2. **分页 next/previous URL 异常** - 指向 `http://127.0.0.1:8000/...`
3. **Channel/CenterTop 摄像头预览图异常** - `http://127.0.0.1:8000/media/camera/...`
### 问题原因
后端 Django 在内部请求自己的 API 时,返回的 JSON 数据中使用的是内部地址 `127.0.0.1:8000`,而不是前端实际访问的服务器地址。
### 解决方案
`boxApi.ts` 中添加 `normalizeBackendUrls` 方法,将后端返回的 URL 统一转换为正确地址。
### 修改原理
```typescript
private normalizeBackendUrls(data: any): any {
// 强制使用 http + 8000 端口
const baseUrl = `http://${window.location.hostname}:8000`;
const replaceHost = (url: string | null | undefined): string | null => {
if (!url) return null;
return url
.replace(/https?:\/\/127\.0\.0\.1(?::8000)?/gi, baseUrl)
.replace(/https?:\/\/localhost(?::8000)?/gi, baseUrl);
};
// 处理多种数据格式...
}
```
**转换示例**
- `http://127.0.0.1:8000/media/xxx.jpg``http://192.168.28.32:8000/media/xxx.jpg`
- `http://localhost:8000/media/xxx.jpg``http://192.168.28.32:8000/media/xxx.jpg`
### 修改的文件
| 文件 | 修改内容 |
|------|---------|
| `src/utils/boxApi.ts` | 添加 normalizeBackendUrls 方法,在多个 API 接口中调用 |
| `src/utils/superboxApi.ts` | 添加 URL 规范化支持(备用) |
| `src/components/Cameras.vue` | 修复硬编码 IP `192.168.28.33` |
| `vite.config.ts` | 添加 /media 和 /ws 代理配置 |
### 已修复的接口
| 接口方法 | 返回数据 | 处理的字段 |
|---------|---------|-----------|
| `getEvents` | 事件列表 | `results[].mediums[].file`, `results[].camera.snapshot` |
| `getEventsByParams` | 事件列表 | 同上 + `next`, `previous` 分页链接 |
| `getEventsByUrl` | 事件列表 | 同上 |
| `getEventById` | 单个事件 | `mediums[].file`, `camera.snapshot` |
| `getAllCameras` | 摄像头列表 | `[].snapshot` |
| `getCameraById` | 单个摄像头 | `.snapshot` |
### 视频点播WebSocket说明
- **接口**`startCameraStream` 返回动态端口(如 8094
- **连接方式**:前端直接连接 `ws://192.168.28.32:8094/`
- **限制**:需要服务器开放对应端口,或配置 Apache 动态代理
- **Vite 代理**:仅开发环境有效,部署后需服务器端配置
### Vite 开发代理配置
开发环境代理配置(`vite.config.ts`
```typescript
proxy: {
'/api': {
target: 'http://192.168.28.32:8000',
changeOrigin: true,
},
'/media': {
target: 'http://192.168.28.32:8000',
changeOrigin: true,
},
'/ws': {
target: 'ws://192.168.28.32:8080',
ws: true,
}
}
```
**注意**此配置仅开发环境有效部署后需配置服务器端代理Apache/Nginx
### API 统一使用说明
- 项目统一使用 `BoxApi` 类进行 API 调用
- `SuperboxApi` 为遗留代码,未被页面使用
- 所有页面通过 `new BoxApi()` 创建实例调用接口
### 通道视频安全模式观看方式
> chrome://flags/#unsafely-treat-insecure-origin-as-secure非安全模式在可开启通知权限
- 权限开启允许不安全文件