10 KiB
10 KiB
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>()
导入顺序
// 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 响应
常用模式
跨窗口通信
import { broadcast, onCrossWindowMessage } from '@/utils/crossWindowChannel'
// 发送
broadcast.sendDialog(data)
// 接收
onCrossWindowMessage((msg) => { /* 处理 */ })
事件总线
import eventBus from '@/utils/eventBus'
eventBus.emit('showDialog', data)
eventBus.on('showDialog', (data) => { /* 处理 */ })
告警列表导出(支持跨页勾选)
// 选中导出: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/...),导致:
- 图片无法显示 - 告警列表、详情、通道预览图等全部异常
- 分页 next/previous URL 异常 - 指向
http://127.0.0.1:8000/... - Channel/CenterTop 摄像头预览图异常 -
http://127.0.0.1:8000/media/camera/...
问题原因
后端 Django 在内部请求自己的 API 时,返回的 JSON 数据中使用的是内部地址 127.0.0.1:8000,而不是前端实际访问的服务器地址。
解决方案
在 boxApi.ts 中添加 normalizeBackendUrls 方法,将后端返回的 URL 统一转换为正确地址。
修改原理
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.jpghttp://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):
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(非安全模式在可开启通知权限)
- 权限开启允许不安全文件