From 3f615df738f2e1150fab099c00cfec4e4c43196b Mon Sep 17 00:00:00 2001
From: tuqin <tuqin@tuqin.cn>
Date: 星期一, 26 十月 2026 09:24:45 +0800
Subject: [PATCH] feat(环保门禁): 智能巡检回传详情(AI分析/无人机轨迹结构化展示)、异常报警15秒轮询+弹窗提醒、车辆手机采集加拍照上传与移动端适配
---
src/views/environmentAccess/intelligentInspectionManagement/index.vue | 152 +++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 148 insertions(+), 4 deletions(-)
diff --git a/src/views/environmentAccess/intelligentInspectionManagement/index.vue b/src/views/environmentAccess/intelligentInspectionManagement/index.vue
index ada1f9a..214f7b0 100644
--- a/src/views/environmentAccess/intelligentInspectionManagement/index.vue
+++ b/src/views/environmentAccess/intelligentInspectionManagement/index.vue
@@ -61,8 +61,8 @@
<el-col :span="24"><el-form-item label="宸℃缁撴灉/璁板綍锛�"><el-input v-model="form.inspectionResult" type="textarea" :rows="2" placeholder="璇疯緭鍏ュ贰妫�缁撴灉" /></el-form-item></el-col>
</el-row>
<el-row :gutter="30">
- <el-col :span="12"><el-form-item label="AI瑙嗛鍒嗘瀽缁撴灉锛�"><el-input v-model="form.aiAnalysis" type="textarea" :rows="2" placeholder="棰勭暀锛欰I瑙嗛鍒嗘瀽缁撴灉" /></el-form-item></el-col>
- <el-col :span="12"><el-form-item label="鏃犱汉鏈�/鏈哄櫒浜烘暟鎹細"><el-input v-model="form.uavData" type="textarea" :rows="2" placeholder="棰勭暀锛氭棤浜烘満/鏈哄櫒浜哄洖浼犳暟鎹�" /></el-form-item></el-col>
+ <el-col :span="12"><el-form-item label="AI瑙嗛鍒嗘瀽缁撴灉锛�"><el-input v-model="form.aiAnalysis" type="textarea" :rows="2" placeholder="璁惧鍥炰紶鍚庤嚜鍔ㄥ啓鍏ワ紱涔熷彲鎵嬪伐褰曞叆" /></el-form-item></el-col>
+ <el-col :span="12"><el-form-item label="鏃犱汉鏈�/鏈哄櫒浜烘暟鎹細"><el-input v-model="form.uavData" type="textarea" :rows="2" placeholder="璁惧鍥炰紶鍚庤嚜鍔ㄥ啓鍏ワ紱涔熷彲鎵嬪伐褰曞叆" /></el-form-item></el-col>
</el-row>
<el-row :gutter="30">
<el-col :span="24"><el-form-item label="宸℃鎶ュ憡锛�"><el-input v-model="form.reportPath" type="textarea" :rows="2" placeholder="鑷姩鐢熸垚/濉啓宸℃鎶ュ憡" /></el-form-item></el-col>
@@ -85,12 +85,78 @@
<el-button @click="reportVisible = false">鍏抽棴</el-button>
</template>
</el-dialog>
+
+ <!-- 宸℃鍥炰紶璇︽儏锛欰I 瑙嗛鍒嗘瀽 + 鏃犱汉鏈�/鏈哄櫒浜哄洖浼犳暟鎹� -->
+ <el-dialog v-model="traceVisible" :title="traceTitle" width="75%" top="6vh">
+ <div v-if="!traceHasData" class="trace-empty">
+ 璇ュ贰妫�浠诲姟鏆傛棤璁惧鍥炰紶鏁版嵁銆傛棤浜烘満 / 鏈哄櫒浜� / AI 鎽勫儚澶撮�氳繃鍥炰紶鎺ュ彛涓婃姤鍚庯紝杩欓噷浼氳嚜鍔ㄥ睍绀鸿瘑鍒粨鏋溿�佺幇鍦虹収鐗囦笌杞ㄨ抗銆�
+ </div>
+ <template v-else>
+ <div class="trace-block">
+ <div class="trace-title">鏁版嵁鏉ユ簮</div>
+ <el-descriptions :column="4" border size="small">
+ <el-descriptions-item label="鏉ユ簮绫诲瀷">{{ deviceTypeText(trace.device.deviceType) }}</el-descriptions-item>
+ <el-descriptions-item label="璁惧缂栧彿">{{ trace.device.deviceCode || "-" }}</el-descriptions-item>
+ <el-descriptions-item label="宸℃鍖哄煙">{{ trace.device.area || "-" }}</el-descriptions-item>
+ <el-descriptions-item label="璁惧鐢甸噺">{{ trace.device.battery != null ? trace.device.battery + "%" : "-" }}</el-descriptions-item>
+ <el-descriptions-item label="閲囬泦鏃堕棿">{{ trace.device.collectedAt || "-" }}</el-descriptions-item>
+ <el-descriptions-item label="绯荤粺鎺ユ敹鏃堕棿">{{ trace.device.receivedAt || "-" }}</el-descriptions-item>
+ <el-descriptions-item label="閲囬泦鍧愭爣">{{ coordText }}</el-descriptions-item>
+ <el-descriptions-item label="浣滀笟楂樺害">{{ trace.device.altitude != null ? trace.device.altitude + " m" : "-" }}</el-descriptions-item>
+ </el-descriptions>
+ </div>
+
+ <div class="trace-block">
+ <div class="trace-title">AI 瑙嗛鍒嗘瀽缁撴灉</div>
+ <div class="trace-summary">{{ trace.ai.summary || "鏈鍥炰紶鏈檮甯� AI 缁撹" }}</div>
+ <el-table v-if="aiObjects.length" :data="aiObjects" border size="small">
+ <el-table-column type="index" label="搴忓彿" width="60" align="center" />
+ <el-table-column prop="object" label="璇嗗埆瀵硅薄" min-width="140" show-overflow-tooltip />
+ <el-table-column prop="conclusion" label="璇嗗埆缁撹" min-width="160" show-overflow-tooltip />
+ <el-table-column label="缃俊搴�" width="110" align="center">
+ <template #default="{ row }">{{ row.confidence != null ? (Number(row.confidence) * 100).toFixed(1) + "%" : "-" }}</template>
+ </el-table-column>
+ <el-table-column prop="frameTime" label="鎴抚鏃堕棿" width="170" />
+ </el-table>
+ <div v-else class="trace-sub-empty">鏆傛棤缁撴瀯鍖栬瘑鍒潯鐩�</div>
+ </div>
+
+ <div v-if="tracePhotos.length" class="trace-block">
+ <div class="trace-title">鐜板満閲囬泦鐓х墖</div>
+ <div class="trace-photos">
+ <el-image
+ v-for="(p, i) in tracePhotos"
+ :key="i"
+ :src="p"
+ :preview-src-list="tracePhotos"
+ :initial-index="i"
+ fit="cover"
+ class="trace-photo"
+ />
+ </div>
+ </div>
+
+ <div v-if="traceTrack.length" class="trace-block">
+ <div class="trace-title">璁惧宸℃杞ㄨ抗</div>
+ <el-table :data="traceTrack" border size="small" max-height="200">
+ <el-table-column type="index" label="搴忓彿" width="60" align="center" />
+ <el-table-column prop="t" label="鏃堕棿" width="140" />
+ <el-table-column prop="lng" label="缁忓害" width="140" />
+ <el-table-column prop="lat" label="绾害" width="140" />
+ <el-table-column prop="alt" label="楂樺害(m)" width="110" />
+ </el-table>
+ </div>
+ </template>
+ <template #footer>
+ <el-button @click="traceVisible = false">鍏抽棴</el-button>
+ </template>
+ </el-dialog>
</div>
</template>
<script setup>
import { Search } from "@element-plus/icons-vue";
-import { ref, reactive, toRefs, onMounted, getCurrentInstance } from "vue";
+import { ref, reactive, toRefs, onMounted, computed, getCurrentInstance } from "vue";
import { ElMessageBox } from "element-plus";
import { listInspection, addInspection, updateInspection, deleteInspection, generateInspectionReport } from "@/api/environmentAccess/inspection.js";
@@ -112,8 +178,9 @@
{ label: "鐘舵��", prop: "status", width: 90, dataType: "tag", formatData: (p) => statusText(p), formatType: (p) => statusType(p) },
{ label: "宸℃缁撴灉", prop: "inspectionResult", showOverflowTooltip: true },
{
- dataType: "action", label: "鎿嶄綔", align: "center", fixed: "right", width: 100,
+ dataType: "action", label: "鎿嶄綔", align: "center", fixed: "right", width: 200,
operation: [
+ { name: "鍥炰紶璇︽儏", type: "text", clickFun: (row) => openTrace(row) },
{ name: "缂栬緫", type: "text", clickFun: (row) => openForm("edit", row) },
{ name: "鐢熸垚鎶ュ憡", type: "text", clickFun: (row) => openReport(row) },
],
@@ -149,6 +216,47 @@
a.download = reportTitle.value + ".doc";
a.click();
URL.revokeObjectURL(a.href);
+};
+
+/* ---------- 宸℃鍥炰紶璇︽儏锛圓I 瑙嗛鍒嗘瀽 + 鏃犱汉鏈�/鏈哄櫒浜哄洖浼狅級 ---------- */
+const traceVisible = ref(false);
+const traceTitle = ref("宸℃鍥炰紶璇︽儏");
+const trace = ref({ device: {}, ai: {}, photos: [], track: [] });
+const deviceTypeText = (t) => ({ UAV: "鏃犱汉鏈�", ROBOT: "宸℃鏈哄櫒浜�", CAMERA: "AI 鎽勫儚澶�" }[t] || t || "-");
+const safeJson = (txt) => {
+ if (!txt) return null;
+ try {
+ const v = JSON.parse(txt);
+ return v && typeof v === "object" ? v : null;
+ } catch (e) {
+ return null;
+ }
+};
+const traceHasData = computed(() => {
+ const d = trace.value.device || {};
+ const ai = trace.value.ai || {};
+ return !!(d.deviceCode || d.deviceType || ai.summary || (ai.objects && ai.objects.length));
+});
+const aiObjects = computed(() => (trace.value.ai && trace.value.ai.objects) || []);
+const tracePhotos = computed(() => (trace.value.device && trace.value.device.photos) || []);
+const traceTrack = computed(() => (trace.value.device && trace.value.device.track) || []);
+const coordText = computed(() => {
+ const d = trace.value.device || {};
+ if (d.longitude == null || d.latitude == null) return "-";
+ return d.longitude + ", " + d.latitude;
+});
+const openTrace = (row) => {
+ const uav = safeJson(row.uavData);
+ const aiParsed = safeJson(row.aiAnalysis);
+ const ai = aiParsed || { summary: row.aiAnalysis || "", objects: [] };
+ trace.value = {
+ device: uav || {},
+ ai: ai,
+ photos: (uav && uav.photos) || [],
+ track: (uav && uav.track) || [],
+ };
+ traceTitle.value = (row.inspectionName || "鏅鸿兘宸℃") + " - 鍥炰紶璇︽儏";
+ traceVisible.value = true;
};
const handleQuery = () => { page.current = 1; getList(); };
@@ -192,4 +300,40 @@
<style scoped lang="scss">
.ml10 { margin-left: 10px; }
.table_list { margin-top: unset; }
+.trace-block { margin-bottom: 18px; }
+.trace-title {
+ font-size: 14px;
+ font-weight: 600;
+ color: #303133;
+ border-left: 3px solid #409eff;
+ padding-left: 8px;
+ margin-bottom: 10px;
+}
+.trace-summary {
+ font-size: 13px;
+ color: #606266;
+ background: #f5f7fa;
+ border-radius: 4px;
+ padding: 8px 12px;
+ margin-bottom: 10px;
+ line-height: 1.6;
+}
+.trace-sub-empty,
+.trace-empty {
+ color: #909399;
+ font-size: 13px;
+ line-height: 1.8;
+}
+.trace-empty {
+ padding: 24px 8px;
+ text-align: center;
+}
+.trace-photos { display: flex; flex-wrap: wrap; gap: 10px; }
+.trace-photo {
+ width: 132px;
+ height: 96px;
+ border-radius: 4px;
+ border: 1px solid #ebeef5;
+ cursor: pointer;
+}
</style>
--
Gitblit v1.9.3