4.3日报

继续完善服务外包杯,完善前端界面

<template>
  <div class="home-container">
    <header class="app-header">
      <img src="@/assets/logo.png" alt="消防AI" class="logo">
      <h1>浓烟环境人体识别系统</h1>
      <div class="status-indicator" :class="connectionStatus"></div>
    </header>

    <div class="main-content">
      <DualCameraView 
        :visibleImage="visibleImage"
        :infraredImage="infraredImage"
        :detections="detections"
        @frame-click="handleFrameClick"
      />
      
      <div class="sidebar">
        <SmokeDensityMeter 
          :density="smokeDensity"
          :thresholds="[0.3, 0.6, 0.8]"
        />
        
        <DetectionResults 
          :detections="detections"
          :selectedDetection="selectedDetection"
          @select="selectDetection"
        />
        
        <AlertPanel 
          :criticalDetections="criticalDetections"
          v-if="criticalDetections.length > 0"
        />
      </div>
    </div>
    
    <div class="control-panel">
      <button @click="connectToRobot" class="control-btn primary">
        {{ isConnected ? '已连接机器人' : '连接消防机器人' }}
      </button>
      <button @click="toggleRecording" class="control-btn" :class="{active: isRecording}">
        {{ isRecording ? '停止记录' : '开始记录' }}
      </button>
      <button @click="exportReport" class="control-btn secondary">
        导出救援报告
      </button>
    </div>
  </div>
</template>

<script>
import DualCameraView from '@/components/DualCameraView.vue'
import SmokeDensityMeter from '@/components/SmokeDensityMeter.vue'
import DetectionResults from '@/components/DetectionResults.vue'
import AlertPanel from '@/components/AlertPanel.vue'

export default {
  components: {
    DualCameraView,
    SmokeDensityMeter,
    DetectionResults,
    AlertPanel
  },
  data() {
    return {
      visibleImage: null,
      infraredImage: null,
      detections: [],
      selectedDetection: null,
      smokeDensity: 0.45,
      isConnected: false,
      isRecording: false,
      connectionStatus: 'disconnected',
      socket: null
    }
  },
  computed: {
    criticalDetections() {
      return this.detections.filter(d => d.confidence > 0.7 && d.status === 'critical')
    }
  },
  mounted() {
    this.initWebSocket()
    this.simulateData()
  },
  methods: {
    initWebSocket() {
      this.socket = new WebSocket('ws://your-backend-url/ws')
      
      this.socket.onopen = () => {
        this.connectionStatus = 'connected'
        this.isConnected = true
      }
      
      this.socket.onmessage = (event) => {
        const data = JSON.parse(event.data)
        this.processIncomingData(data)
      }
      
      this.socket.onclose = () => {
        this.connectionStatus = 'disconnected'
        this.isConnected = false
      }
    },
    
    processIncomingData(data) {
      this.visibleImage = data.visible_image
      this.infraredImage = data.infrared_image
      this.detections = data.detections
      this.smokeDensity = data.smoke_density
    },
    
    connectToRobot() {
      if (!this.isConnected) {
        this.initWebSocket()
      } else {
        this.socket.close()
      }
    },
    
    toggleRecording() {
      this.isRecording = !this.isRecording
      if (this.isRecording) {
        this.sendCommand('start_recording')
      } else {
        this.sendCommand('stop_recording')
      }
    },
    
    sendCommand(command) {
      if (this.socket && this.socket.readyState === WebSocket.OPEN) {
        this.socket.send(JSON.stringify({ command }))
      }
    },
    
    handleFrameClick(position) {
      // 找到点击位置附近的检测结果
      this.selectedDetection = this.detections.find(det => {
        return this.isPointInRect(position, det.bbox)
      })
    },
    
    isPointInRect(point, rect) {
      return point.x >= rect.x && point.x <= rect.x + rect.width &&
             point.y >= rect.y && point.y <= rect.y + rect.height
    },
    
    selectDetection(detection) {
      this.selectedDetection = detection
    },
    
    exportReport() {
      // 生成并导出PDF报告
      console.log('导出救援报告')
    },
    
    simulateData() {
      // 演示用模拟数据
      setInterval(() => {
        if (!this.isConnected) {
          this.detections = this.generateRandomDetections()
          this.smokeDensity = Math.min(1, Math.max(0, this.smokeDensity + (Math.random() - 0.5) * 0.1))
        }
      }, 1000)
    },
    
    generateRandomDetections() {
      const count = Math.floor(Math.random() * 3) + 1
      return Array.from({ length: count }, (_, i) => ({
        id: Date.now() + i,
        bbox: {
          x: Math.random() * 0.8,
          y: Math.random() * 0.8,
          width: 0.1 + Math.random() * 0.2,
          height: 0.1 + Math.random() * 0.3
        },
        confidence: (0.5 + Math.random() * 0.5).toFixed(2),
        status: Math.random() > 0.7 ? 'critical' : 'normal',
        timestamp: new Date().toLocaleTimeString()
      }))
    }
  }
}
</script>

<style scoped>
.home-container {
  display: flex;
  flex-direction: column;
  height: 100vh;
  background-color: #1a1a2e;
  color: #e6e6e6;
}

.app-header {
  display: flex;
  align-items: center;
  padding: 1rem;
  background-color: #16213e;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
}

.logo {
  height: 40px;
  margin-right: 15px;
}

.status-indicator {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  margin-left: auto;
}

.status-indicator.connected {
  background-color: #4ade80;
  box-shadow: 0 0 8px #4ade80;
}

.status-indicator.disconnected {
  background-color: #f87171;
}

.main-content {
  display: flex;
  flex: 1;
  overflow: hidden;
}

.sidebar {
  width: 300px;
  background-color: #0f3460;
  padding: 1rem;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.control-panel {
  display: flex;
  padding: 0.5rem;
  background-color: #16213e;
  gap: 0.5rem;
}

.control-btn {
  padding: 0.5rem 1rem;
  border: none;
  border-radius: 4px;
  background-color: #374151;
  color: white;
  cursor: pointer;
  transition: all 0.2s;
}

.control-btn.primary {
  background-color: #3b82f6;
}

.control-btn.secondary {
  background-color: #10b981;
}

.control-btn.active {
  background-color: #ef4444;
  box-shadow: 0 0 0 2px #fca5a5;
}

.control-btn:hover {
  opacity: 0.9;
  transform: translateY(-1px);
}
</style>

 

posted @ 2025-04-09 11:15  Code13  阅读(9)  评论(0)    收藏  举报