|
|
@@ -387,8 +387,8 @@
|
|
|
<div class="image-container" ref="imageContainerRef">
|
|
|
<img
|
|
|
ref="mainImageRef"
|
|
|
- :src="showScanningEffect ? uploadedImageUrl : annotatedImageUrl"
|
|
|
- :alt="showScanningEffect ? '用户上传图片' : '隐患提示图片'"
|
|
|
+ :src="annotatedImageUrl || uploadedImageUrl"
|
|
|
+ alt="隐患识别图片"
|
|
|
class="main-image"
|
|
|
@click="openImagePreview()"
|
|
|
style="cursor: pointer; transform: none !important;"
|
|
|
@@ -396,6 +396,30 @@
|
|
|
@error="handleMainImageError"
|
|
|
/>
|
|
|
|
|
|
+ <!-- 动态检测框覆盖层 -->
|
|
|
+ <div
|
|
|
+ v-if="!showScanningEffect && detectionResult?.detections"
|
|
|
+ class="detection-boxes-overlay"
|
|
|
+ >
|
|
|
+ <div
|
|
|
+ v-for="(detection, index) in detectionResult.detections"
|
|
|
+ :key="index"
|
|
|
+ class="detection-box"
|
|
|
+ :class="{
|
|
|
+ 'detection-box-selected': selectedKeyElement === normalizeLabel(detection.label),
|
|
|
+ 'detection-box-hover': true
|
|
|
+ }"
|
|
|
+ :style="getDetectionBoxStyle(detection)"
|
|
|
+ @click.stop="handleDetectionBoxClick(detection)"
|
|
|
+ @mouseenter="handleDetectionBoxHover(detection, true)"
|
|
|
+ @mouseleave="handleDetectionBoxHover(detection, false)"
|
|
|
+ >
|
|
|
+ <div class="detection-label">
|
|
|
+ {{ normalizeLabel(detection.label) }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
<div
|
|
|
v-if="
|
|
|
!showScanningEffect &&
|
|
|
@@ -1569,6 +1593,11 @@ const handleHistoryItem = async (historyItem) => {
|
|
|
|
|
|
if (detailResponse.statusCode === 200 || detailResponse.code === 200) {
|
|
|
const detailData = detailResponse.data;
|
|
|
+
|
|
|
+ // 确保 element_hazards 正确加载
|
|
|
+ const elementHazards = detailData.element_hazards || {};
|
|
|
+ console.log('[历史记录] element_hazards:', elementHazards);
|
|
|
+
|
|
|
detectionResult.value = {
|
|
|
scene_name: detailData.tag_type || "",
|
|
|
labels: detailData.labels,
|
|
|
@@ -1579,7 +1608,7 @@ const handleHistoryItem = async (historyItem) => {
|
|
|
: 0
|
|
|
: 0,
|
|
|
third_scenes: detailData.third_scenes || [],
|
|
|
- element_hazards: detailData.element_hazards || {},
|
|
|
+ element_hazards: elementHazards,
|
|
|
detections: detailData.detections || [],
|
|
|
};
|
|
|
selectedScenario.value = detectionResult.value.scene_name || "";
|
|
|
@@ -1589,6 +1618,7 @@ const handleHistoryItem = async (historyItem) => {
|
|
|
detailData.recognition_image_url ||
|
|
|
detailData.original_image_url;
|
|
|
annotatedImageUrl.value = newImageUrl;
|
|
|
+ uploadedImageUrl.value = detailData.original_image_url || newImageUrl;
|
|
|
if (newImageUrl) {
|
|
|
await waitForImageLoad(newImageUrl);
|
|
|
}
|
|
|
@@ -1596,7 +1626,16 @@ const handleHistoryItem = async (historyItem) => {
|
|
|
const tagType =
|
|
|
detailData.tag_type || getTagTypeFromLabels(detailData.labels);
|
|
|
historyItem.tagType = tagType;
|
|
|
+
|
|
|
+ // 确保显示所有隐患卡片
|
|
|
setVisibleHazardCards(detectionResult.value?.third_scenes || []);
|
|
|
+
|
|
|
+ console.log('[历史记录] 加载完成:', {
|
|
|
+ scene: detectionResult.value.scene_name,
|
|
|
+ labels: detectionResult.value.display_labels,
|
|
|
+ hazards: detectionResult.value.third_scenes,
|
|
|
+ elementHazards: detectionResult.value.element_hazards
|
|
|
+ });
|
|
|
} else {
|
|
|
ElMessage.error("获取记录详情失败");
|
|
|
detectionResult.value = {
|
|
|
@@ -1621,6 +1660,7 @@ const handleHistoryItem = async (historyItem) => {
|
|
|
item.isActive = item.id === historyItem.id;
|
|
|
});
|
|
|
} catch (error) {
|
|
|
+ console.error('[历史记录] 加载失败:', error);
|
|
|
ElMessage.error("获取记录详情失败");
|
|
|
isDragOver.value = false;
|
|
|
} finally {
|
|
|
@@ -2473,6 +2513,59 @@ watch(
|
|
|
}
|
|
|
);
|
|
|
|
|
|
+const getDetectionBoxStyle = (detection) => {
|
|
|
+ if (!detection || !Array.isArray(detection.box) || detection.box.length < 4) {
|
|
|
+ return { display: 'none' };
|
|
|
+ }
|
|
|
+
|
|
|
+ const container = imageContainerRef.value;
|
|
|
+ const imageElement = mainImageRef.value;
|
|
|
+
|
|
|
+ if (!container || !imageElement || !imageElement.naturalWidth || !imageElement.naturalHeight) {
|
|
|
+ return { display: 'none' };
|
|
|
+ }
|
|
|
+
|
|
|
+ const containerWidth = container.clientWidth;
|
|
|
+ const containerHeight = container.clientHeight;
|
|
|
+ const naturalWidth = imageElement.naturalWidth;
|
|
|
+ const naturalHeight = imageElement.naturalHeight;
|
|
|
+
|
|
|
+ const scale = Math.min(
|
|
|
+ containerWidth / naturalWidth,
|
|
|
+ containerHeight / naturalHeight
|
|
|
+ );
|
|
|
+ const renderedWidth = naturalWidth * scale;
|
|
|
+ const renderedHeight = naturalHeight * scale;
|
|
|
+ const offsetX = (containerWidth - renderedWidth) / 2;
|
|
|
+ const offsetY = (containerHeight - renderedHeight) / 2;
|
|
|
+
|
|
|
+ const [x1, y1, x2, y2] = detection.box.map((value) => Number(value) || 0);
|
|
|
+
|
|
|
+ const boxLeft = offsetX + x1 * scale;
|
|
|
+ const boxTop = offsetY + y1 * scale;
|
|
|
+ const boxWidth = (x2 - x1) * scale;
|
|
|
+ const boxHeight = (y2 - y1) * scale;
|
|
|
+
|
|
|
+ return {
|
|
|
+ position: 'absolute',
|
|
|
+ left: `${boxLeft}px`,
|
|
|
+ top: `${boxTop}px`,
|
|
|
+ width: `${boxWidth}px`,
|
|
|
+ height: `${boxHeight}px`,
|
|
|
+ };
|
|
|
+};
|
|
|
+
|
|
|
+const handleDetectionBoxClick = (detection) => {
|
|
|
+ const label = normalizeLabel(detection?.label || '');
|
|
|
+ if (label) {
|
|
|
+ toggleKeyElement(label);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const handleDetectionBoxHover = (detection, isHovering) => {
|
|
|
+ // 可以在这里添加悬停效果逻辑
|
|
|
+};
|
|
|
+
|
|
|
const handleWindowResize = () => {
|
|
|
updateElementOverlayPosition();
|
|
|
};
|
|
|
@@ -3278,6 +3371,59 @@ onBeforeUnmount(() => {
|
|
|
cursor: pointer;
|
|
|
}
|
|
|
|
|
|
+.detail-view .detail-content .image-section .detection-boxes-overlay {
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ pointer-events: none;
|
|
|
+}
|
|
|
+
|
|
|
+.detail-view .detail-content .image-section .detection-box {
|
|
|
+ pointer-events: auto;
|
|
|
+ border: 2px solid #3b82f6;
|
|
|
+ background: rgba(59, 130, 246, 0.1);
|
|
|
+ cursor: pointer;
|
|
|
+ transition: all 0.2s ease;
|
|
|
+}
|
|
|
+
|
|
|
+.detail-view .detail-content .image-section .detection-box:hover {
|
|
|
+ border-color: #2563eb;
|
|
|
+ background: rgba(37, 99, 235, 0.15);
|
|
|
+ box-shadow: 0 0 12px rgba(59, 130, 246, 0.4);
|
|
|
+}
|
|
|
+
|
|
|
+.detail-view .detail-content .image-section .detection-box-selected {
|
|
|
+ border-color: #ef4444;
|
|
|
+ background: rgba(239, 68, 68, 0.15);
|
|
|
+ border-width: 3px;
|
|
|
+}
|
|
|
+
|
|
|
+.detail-view .detail-content .image-section .detection-box-selected:hover {
|
|
|
+ border-color: #dc2626;
|
|
|
+ background: rgba(220, 38, 38, 0.2);
|
|
|
+ box-shadow: 0 0 16px rgba(239, 68, 68, 0.5);
|
|
|
+}
|
|
|
+
|
|
|
+.detail-view .detail-content .image-section .detection-label {
|
|
|
+ position: absolute;
|
|
|
+ top: -24px;
|
|
|
+ left: 0;
|
|
|
+ background: #3b82f6;
|
|
|
+ color: white;
|
|
|
+ padding: 2px 8px;
|
|
|
+ border-radius: 4px;
|
|
|
+ font-size: 12px;
|
|
|
+ font-weight: 500;
|
|
|
+ white-space: nowrap;
|
|
|
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
|
|
+}
|
|
|
+
|
|
|
+.detail-view .detail-content .image-section .detection-box-selected .detection-label {
|
|
|
+ background: #ef4444;
|
|
|
+}
|
|
|
+
|
|
|
.detail-view .detail-content .image-section .element-overlay-card {
|
|
|
position: absolute;
|
|
|
width: 260px;
|