<!--监测设备一张图-->
<template>
  <div class="gis-monitor-dashboard">
    <!-- 顶部导航栏 -->
    <div class="dashboard-header">
      <div class="header-actions">
        <el-badge :value="onlineCount" type="primary">
          <el-button type="primary" plain :icon="Connection">在线设备</el-button>
        </el-badge>
        <el-badge :value="alertCount" type="danger">
          <el-button type="danger" plain :icon="WarningFilled">告警设备</el-button>
        </el-badge>
     </div>
    </div>

    <!-- 主体区域：左侧设备列表 + 右侧GIS地图 -->
    <div class="main-layout">
      <!-- 左侧：设备列表 & 筛选 -->
      <div class="device-panel">
        <div class="panel-header">
          <span>监测设备列表</span>
          <el-input
              v-model="searchKeyword"
              placeholder="搜索设备名称/编号"
              clearable
              :prefix-icon="Search"
              size="small"
          />
        </div>
        <div class="filter-tabs">
          <el-radio-group v-model="deviceTypeFilter" size="small" @change="filterDevices">
            <el-radio-button label="all">全部</el-radio-button>
            <el-radio-button label="smart">智能井盖</el-radio-button>
            <el-radio-button label="standard">普通井盖</el-radio-button>
            <el-radio-button label="explosion">防爆井盖</el-radio-button>
          </el-radio-group>
        </div>
        <div class="device-list-scroll">
          <div
              v-for="device in paginatedDevices"
              :key="device.id"
              class="device-item"
              :class="{ active: selectedDevice?.id === device.id }"
              @click="selectDevice(device)"
          >
            <div class="device-status-dot">
              <el-badge :value="device.status === 'fault' ? '!' : ''" :type="device.status === 'fault' ? 'danger' : 'primary'">
                <div class="status-icon" :style="{ backgroundColor: getStatusColor(device.status) }"></div>
              </el-badge>
            </div>
            <div class="device-info">
              <div class="device-name">{{ device.name }}</div>
              <div class="device-location">
                <el-icon><Location /></el-icon> {{ device.shortAddress }}
              </div>
              <div class="device-meta">
                <el-tag size="small" :type="getDeviceTypeTag(device.type)">{{ device.typeName }}</el-tag>
                <span class="device-time">更新: {{ device.updateTime }}</span>
              </div>
            </div>
          </div>
          <!-- 分页 -->
          <div class="pagination-area">
            <el-pagination
                small
                background
                layout="prev, pager, next"
                :total="filteredDevices.length"
                :page-size="pageSize"
                v-model:current-page="currentPage"
            />
          </div>
        </div>
      </div>

      <!-- 右侧：GIS地图模拟展示区 -->
      <div class="gis-container">
        <div class="map-toolbar">
          <div class="tool-group">
            <el-button size="small" :icon="ZoomIn" @click="zoomIn">放大</el-button>
            <el-button size="small" :icon="ZoomOut" @click="zoomOut">缩小</el-button>
            <el-button size="small" :icon="RefreshRight" @click="resetView">重置视图</el-button>
          </div>
          <div class="legend">
            <span><i class="legend-dot online"></i> 在线</span>
            <span><i class="legend-dot offline"></i> 离线</span>
            <span><i class="legend-dot fault"></i> 故障</span>
            <span><i class="legend-dot selected"></i> 选中设备</span>
          </div>
        </div>

        <!-- 模拟的GIS地图区域 - 使用Canvas绘制并模拟标记点 -->
        <div class="map-area" ref="mapContainer">
          <canvas id="gisCanvas" ref="canvasRef" @click="handleMapClick"></canvas>
          <!-- 悬浮信息卡 -->
          <div v-if="hoverDevice" class="hover-tooltip" :style="{ top: hoverY + 'px', left: hoverX + 'px' }">
            <div class="tooltip-title">{{ hoverDevice.name }}</div>
            <div>状态: {{ getStatusText(hoverDevice.status) }}</div>
            <div>位置: {{ hoverDevice.location }}</div>
          </div>
        </div>

        <!-- 右下角 选中设备详情卡片 -->
        <transition name="slide-fade">
          <div v-if="selectedDevice" class="detail-card">
            <div class="card-header">
              <strong>设备详情</strong>
              <el-icon class="close-icon" @click="clearSelected"><Close /></el-icon>
            </div>
            <div class="card-body">
              <el-descriptions :column="1" border size="small">
                <el-descriptions-item label="设备编号">{{ selectedDevice.code }}</el-descriptions-item>
                <el-descriptions-item label="设备名称">{{ selectedDevice.name }}</el-descriptions-item>
                <el-descriptions-item label="设备类型">{{ selectedDevice.typeName }}</el-descriptions-item>
                <el-descriptions-item label="运维状态">
                  <el-tag :type="getStatusType(selectedDevice.status)" size="small">{{ getStatusText(selectedDevice.status) }}</el-tag>
                </el-descriptions-item>
                <el-descriptions-item label="经纬度">{{ selectedDevice.lng }}, {{ selectedDevice.lat }}</el-descriptions-item>
                <el-descriptions-item label="详细地址">{{ selectedDevice.location }}</el-descriptions-item>
                <el-descriptions-item label="最后通讯">{{ selectedDevice.lastTime }}</el-descriptions-item>
                <el-descriptions-item label="电池电量" v-if="selectedDevice.battery !== undefined">{{ selectedDevice.battery }}%</el-descriptions-item>
              </el-descriptions>
            </div>
          </div>
        </transition>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, computed, onMounted, reactive, nextTick } from 'vue'
import { Location, Connection, WarningFilled, Search, ZoomIn, ZoomOut, RefreshRight, Close } from '@element-plus/icons-vue'

// ---------- 模拟设备数据（含地理位置）----------
const mockDevices = [
  { id: 1, code: 'MN-1001', name: '人民路智能井盖', type: 'smart', typeName: '智能井盖', status: 'online', lng: 121.487, lat: 31.249, location: '人民路与解放路口东50m', shortAddress: '人民路解放路口', updateTime: '10:23', lastTime: '2025-04-02 10:23:11', battery: 87 },
  { id: 2, code: 'MN-1002', name: '滨江路防爆井盖', type: 'explosion', typeName: '防爆井盖', status: 'maintenance', lng: 121.502, lat: 31.235, location: '滨江路化工园区南门', shortAddress: '滨江路化工园', updateTime: '09:45', lastTime: '2025-04-02 09:45:33', battery: 34 },
  { id: 3, code: 'MN-1003', name: '老城区铸铁井盖', type: 'standard', typeName: '普通井盖', status: 'fault', lng: 121.478, lat: 31.258, location: '古城街北段树下', shortAddress: '古城街北段', updateTime: '昨日', lastTime: '2025-04-01 16:20:10', battery: null },
  { id: 4, code: 'MN-1004', name: '高新区智能井盖', type: 'smart', typeName: '智能井盖', status: 'online', lng: 121.512, lat: 31.242, location: '高新大道云计算中心旁', shortAddress: '高新大道云中心', updateTime: '11:02', lastTime: '2025-04-02 11:02:45', battery: 96 },
  { id: 5, code: 'MN-1005', name: '南港路井盖', type: 'standard', typeName: '普通井盖', status: 'offline', lng: 121.495, lat: 31.228, location: '南港路旧车站段', shortAddress: '南港路车站', updateTime: '2025-03-30', lastTime: '2025-03-30 14:00:00', battery: null },
  { id: 6, code: 'SM-2001', name: '智慧园林监测井盖', type: 'smart', typeName: '智能井盖', status: 'online', lng: 121.466, lat: 31.265, location: '中央公园北门', shortAddress: '中央公园', updateTime: '10:55', lastTime: '2025-04-02 10:55:00', battery: 72 },
  { id: 7, code: 'EX-3002', name: '石化区防爆井盖', type: 'explosion', typeName: '防爆井盖', status: 'maintenance', lng: 121.524, lat: 31.239, location: '石化大道与滨海路交叉口', shortAddress: '石化大道', updateTime: '08:30', lastTime: '2025-04-02 08:30:22', battery: 41 }
]

// 状态筛选及分页
const searchKeyword = ref('')
const deviceTypeFilter = ref('all')
const currentPage = ref(1)
const pageSize = 6

const filteredDevices = computed(() => {
  let list = mockDevices
  if (searchKeyword.value) {
    const kw = searchKeyword.value.toLowerCase()
    list = list.filter(d => d.name.toLowerCase().includes(kw) || d.code.toLowerCase().includes(kw))
  }
  if (deviceTypeFilter.value !== 'all') {
    list = list.filter(d => d.type === deviceTypeFilter.value)
  }
  return list
})

const paginatedDevices = computed(() => {
  const start = (currentPage.value - 1) * pageSize
  return filteredDevices.value.slice(start, start + pageSize)
})

// 统计在线/告警数量
const onlineCount = computed(() => mockDevices.filter(d => d.status === 'online').length)
const alertCount = computed(() => mockDevices.filter(d => d.status === 'fault' || d.status === 'maintenance').length)

// GIS地图画布相关
const mapContainer = ref(null)
const canvasRef = ref(null)
let ctx = null
let mapWidth = 0, mapHeight = 0
let mapZoom = 1
let mapOffsetX = 0, mapOffsetY = 0  // 用于拖拽偏移(简易)
const baseBounds = { minX: 121.45, maxX: 121.55, minY: 31.22, maxY: 31.28 } // 经纬度范围

// 选中的设备
const selectedDevice = ref(null)
const hoverDevice = ref(null)
const hoverX = ref(0), hoverY = ref(0)

function getDeviceScreenPos(lng, lat) {
  // 将经度/纬度映射到canvas坐标 (模拟墨卡托简易映射)
  const xPercent = (lng - baseBounds.minX) / (baseBounds.maxX - baseBounds.minX)
  const yPercent = 1 - (lat - baseBounds.minY) / (baseBounds.maxY - baseBounds.minY)
  let x = xPercent * mapWidth
  let y = yPercent * mapHeight
  // 应用缩放和平移 (目前平移没有实现拖动，留出接口)
  const centerX = mapWidth / 2, centerY = mapHeight / 2
  x = (x - centerX) * mapZoom + centerX + mapOffsetX
  y = (y - centerY) * mapZoom + centerY + mapOffsetY
  return { x, y }
}

function drawMapBackground() {
  if (!ctx) return
  // 绘制模拟地图样式：网格+绿地/道路风格
  ctx.fillStyle = "#e9f5e8"
  ctx.fillRect(0, 0, mapWidth, mapHeight)
  // 绘制道路网格线（示意）
  ctx.beginPath()
  ctx.strokeStyle = "#dcdcdc"
  ctx.lineWidth = 1
  for (let i = 0; i < 10; i++) {
    const x = (i / 10) * mapWidth
    ctx.moveTo(x, 0)
    ctx.lineTo(x, mapHeight)
    ctx.stroke()
    const y = (i / 10) * mapHeight
    ctx.moveTo(0, y)
    ctx.lineTo(mapWidth, y)
    ctx.stroke()
  }
  // 绘制一些绿地色块
  ctx.fillStyle = "#b8e0b4"
  ctx.fillRect(20, 30, 150, 100)
  ctx.fillStyle = "#a0cf9c"
  ctx.fillRect(mapWidth-180, 60, 160, 120)
  ctx.fillStyle = "#c8e6c9"
  ctx.fillRect(80, mapHeight-140, 200, 80)
  // 主要河流示意
  ctx.beginPath()
  ctx.strokeStyle = "#4fc3f7"
  ctx.lineWidth = 8
  ctx.moveTo(120, mapHeight-60)
  ctx.quadraticCurveTo(mapWidth/2, mapHeight-150, mapWidth-80, mapHeight-30)
  ctx.stroke()
}

function drawAllDevices() {
  if (!ctx) return
  mockDevices.forEach(device => {
    const { x, y } = getDeviceScreenPos(device.lng, device.lat)
    // 超出画布范围但允许简单绘制
    if (x < -20 || x > mapWidth+20 || y < -20 || y > mapHeight+20) return
    // 根据状态选择图标样式
    let color = "#67C23A"  // online
    if (device.status === 'offline') color = "#909399"
    if (device.status === 'fault') color = "#F56C6C"
    if (device.status === 'maintenance') color = "#E6A23C"
    if (selectedDevice.value?.id === device.id) {
      ctx.shadowBlur = 12
      ctx.shadowColor = "rgba(64,158,255,0.8)"
      // 外发光
      ctx.beginPath()
      ctx.arc(x, y, 16, 0, 2 * Math.PI)
      ctx.fillStyle = "rgba(64,158,255,0.2)"
      ctx.fill()
    } else {
      ctx.shadowBlur = 0
    }
    // 绘制标记：圆形+井盖样式
    ctx.beginPath()
    ctx.arc(x, y, 12, 0, 2 * Math.PI)
    ctx.fillStyle = color
    ctx.fill()
    ctx.strokeStyle = "#fff"
    ctx.lineWidth = 2
    ctx.stroke()
    ctx.fillStyle = "#ffffff"
    ctx.font = "bold 14px 'Segoe UI'"
    ctx.shadowBlur = 0
    ctx.fillText(device.name.length > 6 ? device.name.slice(0,4)+'..' : device.name, x-18, y-8)
    // 若故障显示感叹号
    if (device.status === 'fault') {
      ctx.fillStyle = "#fff"
      ctx.font = "bold 14px sans-serif"
      ctx.fillText("!", x-4, y+5)
    }
  })
}

function renderMap() {
  if (!canvasRef.value) return
  const canvas = canvasRef.value
  mapWidth = canvas.parentElement.clientWidth
  mapHeight = canvas.parentElement.clientHeight
  canvas.width = mapWidth
  canvas.height = mapHeight
  ctx = canvas.getContext('2d')
  drawMapBackground()
  drawAllDevices()
}

function zoomIn() {
  mapZoom = Math.min(mapZoom + 0.1, 2.5)
  renderMap()
}
function zoomOut() {
  mapZoom = Math.max(mapZoom - 0.1, 0.6)
  renderMap()
}
function resetView() {
  mapZoom = 1
  mapOffsetX = 0
  mapOffsetY = 0
  renderMap()
}

// 处理地图点击，根据位置选择设备
function handleMapClick(e) {
  const rect = canvasRef.value.getBoundingClientRect()
  const mouseX = (e.clientX - rect.left) * (mapWidth / rect.width)
  const mouseY = (e.clientY - rect.top) * (mapHeight / rect.height)
  let minDist = 20
  let clickedDevice = null
  mockDevices.forEach(device => {
    const { x, y } = getDeviceScreenPos(device.lng, device.lat)
    const dist = Math.hypot(mouseX - x, mouseY - y)
    if (dist < minDist) {
      minDist = dist
      clickedDevice = device
    }
  })
  if (clickedDevice) {
    selectedDevice.value = clickedDevice
  } else {
    selectedDevice.value = null
  }
}

// 鼠标悬浮效果（在canvas上跟踪设备悬浮）
function onCanvasMouseMove(e) {
  const rect = canvasRef.value.getBoundingClientRect()
  const mouseX = (e.clientX - rect.left) * (mapWidth / rect.width)
  const mouseY = (e.clientY - rect.top) * (mapHeight / rect.height)
  let hover = null
  let minDist = 15
  mockDevices.forEach(device => {
    const { x, y } = getDeviceScreenPos(device.lng, device.lat)
    const dist = Math.hypot(mouseX - x, mouseY - y)
    if (dist < minDist) {
      minDist = dist
      hover = device
      hoverX.value = e.clientX + 10
      hoverY.value = e.clientY - 30
    }
  })
  hoverDevice.value = hover
}

function selectDevice(device) {
  selectedDevice.value = device
  // 可选: 地图中心移动到设备（如果需要可触发pan,但保持简洁）
  // 可以提示位置闪烁（重新绘制）
  renderMap()
}
function clearSelected() {
  selectedDevice.value = null
  renderMap()
}
function filterDevices() {
  currentPage.value = 1
  // 仅影响左侧列表，地图上仍旧展示全部（可视化管理展示全局）
  renderMap()
}

function getStatusColor(status) {
  if (status === 'online') return '#67C23A'
  if (status === 'offline') return '#909399'
  if (status === 'fault') return '#F56C6C'
  return '#E6A23C'
}
function getStatusType(status) {
  if (status === 'online') return 'success'
  if (status === 'offline') return 'info'
  if (status === 'fault') return 'danger'
  return 'warning'
}
function getStatusText(status) {
  const map = { online: '在线', offline: '离线', fault: '故障', maintenance: '需维护' }
  return map[status] || status
}
function getDeviceTypeTag(type) {
  if (type === 'smart') return 'success'
  if (type === 'explosion') return 'danger'
  return ''
}

onMounted(() => {
  nextTick(() => {
    renderMap()
    window.addEventListener('resize', () => renderMap())
    if (canvasRef.value) {
      canvasRef.value.addEventListener('mousemove', onCanvasMouseMove)
    }
  })
})
</script>

<style scoped>
* {
  box-sizing: border-box;
}
.gis-monitor-dashboard {
  background: #f0f2f6;
  height: 100vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  font-family: 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif;
}
.dashboard-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  background: white;
  padding: 8px 24px;
  box-shadow: 0 2px 8px rgba(0,0,0,0.05);
  z-index: 10;
}
.logo-area {
  display: flex;
  align-items: center;
  gap: 12px;
}
.logo-area .title {
  font-size: 20px;
  font-weight: 600;
  color: #1f2d3d;
}
.header-actions {
  display: flex;
  gap: 16px;
  align-items: center;
}
.main-layout {
  flex: 1;
  display: flex;
  gap: 16px;
  padding: 16px;
  overflow: hidden;
}
.device-panel {
  width: 320px;
  background: white;
  border-radius: 16px;
  box-shadow: 0 4px 12px rgba(0,0,0,0.08);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  flex-shrink: 0;
}
.panel-header {
  padding: 14px 16px;
  border-bottom: 1px solid #eaeef2;
  font-weight: 600;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.filter-tabs {
  padding: 10px 12px;
  border-bottom: 1px solid #eceef5;
}
.device-list-scroll {
  flex: 1;
  overflow-y: auto;
  padding: 8px;
}
.device-item {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 12px;
  margin-bottom: 8px;
  background: #fafbfc;
  border-radius: 12px;
  transition: all 0.2s;
  cursor: pointer;
  border: 1px solid transparent;
}
.device-item.active {
  background: #ecf5ff;
  border-color: #409eff;
  box-shadow: 0 2px 6px rgba(64,158,255,0.2);
}
.device-status-dot .status-icon {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  margin-top: 6px;
}
.device-info {
  flex: 1;
}
.device-name {
  font-weight: 600;
  margin-bottom: 4px;
}
.device-location {
  font-size: 12px;
  color: #606266;
  display: flex;
  align-items: center;
  gap: 4px;
}
.device-meta {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: 6px;
  font-size: 11px;
  color: #909399;
}
.gis-container {
  flex: 1;
  background: #fff;
  border-radius: 20px;
  box-shadow: 0 4px 16px rgba(0,0,0,0.06);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  position: relative;
}
.map-toolbar {
  padding: 10px 16px;
  display: flex;
  justify-content: space-between;
  border-bottom: 1px solid #efefef;
  background: white;
  z-index: 2;
}
.legend {
  display: flex;
  gap: 16px;
  font-size: 12px;
  align-items: center;
}
.legend-dot {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 10px;
  margin-right: 4px;
}
.legend-dot.online { background-color: #67C23A; }
.legend-dot.offline { background-color: #909399; }
.legend-dot.fault { background-color: #F56C6C; }
.legend-dot.selected { background-color: #409eff; box-shadow: 0 0 0 2px rgba(64,158,255,0.4); }
.map-area {
  flex: 1;
  position: relative;
  background: #eef2ea;
  cursor: crosshair;
}
canvas {
  width: 100%;
  height: 100%;
  display: block;
}
.hover-tooltip {
  position: fixed;
  background: rgba(0,0,0,0.75);
  color: white;
  padding: 6px 12px;
  border-radius: 8px;
  font-size: 12px;
  pointer-events: none;
  z-index: 100;
  white-space: nowrap;
}
.detail-card {
  position: absolute;
  bottom: 20px;
  right: 20px;
  width: 280px;
  background: white;
  border-radius: 16px;
  box-shadow: 0 8px 24px rgba(0,0,0,0.15);
  z-index: 20;
  backdrop-filter: blur(2px);
  border: 1px solid #e0e4e8;
  overflow: hidden;
}
.card-header {
  display: flex;
  justify-content: space-between;
  padding: 12px 16px;
  background: #f8fafc;
  border-bottom: 1px solid #e9edf2;
  font-weight: 600;
}
.close-icon {
  cursor: pointer;
}
.card-body {
  padding: 12px;
}
.slide-fade-enter-active {
  transition: all 0.3s ease-out;
}
.slide-fade-leave-active {
  transition: all 0.2s cubic-bezier(1, 0.5, 0.8, 1);
}
.slide-fade-enter-from, .slide-fade-leave-to {
  transform: translateX(20px);
  opacity: 0;
}
.pagination-area {
  padding: 8px 0 4px;
  display: flex;
  justify-content: center;
}
</style>