API UPDATE FIRST
This commit is contained in:
313
AGENTS.md
Normal file
313
AGENTS.md
Normal 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(非安全模式在可开启通知权限)
|
||||
|
||||
- 权限开启允许不安全文件
|
||||
Reference in New Issue
Block a user