feat(aiot): Edge截图方案替代ZLM截图,支持COS URL返回

- 新增 IAiScreenshotService 接口和实现:通过 Redis Stream 请求 Edge
  截图,轮询等待结果,支持 5 分钟缓存和 force 刷新
- AiRoiController.getSnap() 从 ZLM 二进制截图改为返回 JSON(含 COS URL)
- 前端 aiRoi.js 新增 getSnapUrl 方法
- roiConfig.vue 改为异步加载截图,增加 loading 状态和错误提示

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-27 17:25:32 +08:00
parent 152af7ec90
commit 3a601b37e6
5 changed files with 194 additions and 99 deletions

View File

@@ -62,3 +62,11 @@ export function updateAlgoParams(data) {
data: data
})
}
export function getSnapUrl(cameraCode, force = false) {
return request({
method: 'get',
url: '/api/ai/roi/snap',
params: { cameraCode, force }
})
}

View File

@@ -8,7 +8,7 @@
<div class="header-right">
<el-button size="small" type="primary" icon="el-icon-plus" @click="startDraw('rectangle')">画矩形</el-button>
<el-button size="small" type="primary" icon="el-icon-plus" @click="startDraw('polygon')">画多边形</el-button>
<el-button size="small" icon="el-icon-refresh" @click="refreshSnap">刷新截图</el-button>
<el-button size="small" icon="el-icon-refresh" :loading="snapLoading" @click="refreshSnap">刷新截图</el-button>
<el-button size="small" type="info" @click="handlePush">推送到边缘端</el-button>
</div>
</div>
@@ -87,7 +87,7 @@
<script>
import RoiCanvas from '@/views/roiConfig/components/RoiCanvas.vue'
import RoiAlgorithmBind from '@/views/roiConfig/components/RoiAlgorithmBind.vue'
import { queryRoiByCameraId, saveRoi, deleteRoi, queryRoiDetail } from '@/api/aiRoi'
import { queryRoiByCameraId, saveRoi, deleteRoi, queryRoiDetail, getSnapUrl } from '@/api/aiRoi'
import { pushConfig } from '@/api/aiConfig'
export default {
@@ -103,7 +103,8 @@ export default {
roiList: [],
selectedRoiId: null,
selectedRoiBindings: [],
snapUrl: ''
snapUrl: '',
snapLoading: false
}
},
computed: {
@@ -117,7 +118,7 @@ export default {
this.srcUrl = this.$route.query.srcUrl || ''
this.app = this.$route.query.app || ''
this.stream = this.$route.query.stream || ''
this.buildSnapUrl()
this.fetchSnap()
this.loadRois()
},
methods: {
@@ -146,14 +147,29 @@ export default {
startDraw(mode) {
this.drawMode = mode
},
buildSnapUrl() {
if (this.app && this.stream) {
const base = process.env.NODE_ENV === 'development' ? process.env.VUE_APP_BASE_API : ''
this.snapUrl = `${base}/api/ai/roi/snap?app=${encodeURIComponent(this.app)}&stream=${encodeURIComponent(this.stream)}&t=${Date.now()}`
}
fetchSnap(force = false) {
if (!this.cameraId) return
this.snapLoading = true
getSnapUrl(this.cameraId, force).then(res => {
const data = res.data || res
if (data.status === 'ok' && data.url) {
this.snapUrl = data.url
if (data.stale) {
this.$message.warning('截图为缓存数据,边缘设备可能离线')
}
} else if (data.status === 'timeout') {
this.$message.warning(data.message || '边缘设备响应超时')
} else {
this.$message.error(data.message || '截图失败')
}
}).catch(() => {
this.$message.error('截图请求失败,请检查网络')
}).finally(() => {
this.snapLoading = false
})
},
refreshSnap() {
this.buildSnapUrl()
this.fetchSnap(true)
},
onRoiDrawn(data) {
this.drawMode = null