餐饮油烟智能监测与监管一体化平台
riku
2026-03-20 59af55dc3e72f8f2655ae06af9d1b6f766bac423
src/utils/map/marks.js
@@ -6,17 +6,22 @@
import { useToolboxStore } from '@/stores/toolbox'
import util from './util'
import * as echarts from 'echarts'
import exceedIcon from '@/assets/exceed.png'
import exceptionIcon from '@/assets/exception.png'
import offlineIcon from '@/assets/offline.png'
const toolboxStore = useToolboxStore()
var _massMarks = undefined
// 环信码等级和对应颜色
const ringCodeLevelColors = [
  '#52c41a', // 绿色
  '#faad14', // 黄色
  '#f5222d', // 红色
]
// 状态图标配置
const statusIcons = {
  exceed: exceedIcon, // 油烟浓度超标
  exception: exceptionIcon, // 供电异常
  offline: offlineIcon, // 设备或网络异常
  online: createCustomMarkerOnline(), // 在线状态
  offlineStatus: createCustomMarkerOffline(), // 离线状态
}
/**
 * 绘制海量点标记
@@ -28,6 +33,8 @@
 * @param {number} shops[].shop.longitude 经度
 * @param {string} shops[].shop.ringCodeLevel 最新环信码等级
 * @param {string} shops[].shop.ringCodePublishTime 最新环信码发布时间
 * @param {boolean} shops[].shop.isOnline 在线状态
 * @param {number} shops[].shop.exceptionStatus 异常状态(0: 油烟浓度超标, 1: 供电异常, 2: 设备或网络异常, 3: 无异常)
 * @param {Array} shops[].recentData 近1小时的监测数据
 * @param {string} shops[].recentData[].sampleTime 数据采样时间
 * @param {number} shops[].recentData[].oilSmokeConcentration 油烟浓度
@@ -36,21 +43,65 @@
 */
function drawMassMarks(shops) {
  // 配置样式
  const massMarksStyle = ringCodeLevelColors.map((color, index) => ({
    url: createCustomMarker(color),
    size: new AMap.Size(20, 20),
    anchor: new AMap.Pixel(10, 10),
  }))
  const massMarksStyle = [
    {
      url: statusIcons.exceed,
      size: new AMap.Size(20, 20),
      anchor: new AMap.Pixel(10, 10),
    },
    {
      url: statusIcons.exception,
      size: new AMap.Size(20, 20),
      anchor: new AMap.Pixel(10, 10),
    },
    {
      url: statusIcons.offline,
      size: new AMap.Size(20, 20),
      anchor: new AMap.Pixel(10, 10),
    },
    {
      url: statusIcons.online,
      size: new AMap.Size(32, 32),
      anchor: new AMap.Pixel(10, 10),
    },
    {
      url: statusIcons.offlineStatus,
      size: new AMap.Size(32, 32),
      anchor: new AMap.Pixel(10, 10),
    },
  ]
  // 准备海量点数据
  const massMarksData = shops.map((shop, index) => {
    // 根据环信码等级获取颜色
    const color = getColorByRingCodeLevel(shop.shop.ringCodeLevel)
    // 根据异常状态和在线状态获取样式索引
    let styleIndex = 3 // 默认在线状态
    if (shop.shop.exceptionStatus !== undefined) {
      switch (shop.shop.exceptionStatus) {
        case 0: // 油烟浓度超标
          styleIndex = 0
          break
        case 1: // 供电异常
          styleIndex = 1
          break
        case 2: // 设备或网络异常
          styleIndex = 2
          break
        case 3: // 无异常,根据在线状态决定
          styleIndex = shop.shop.isOnline ? 3 : 4
          break
        default:
          styleIndex = shop.shop.isOnline ? 3 : 4
      }
    } else {
      // 没有异常状态时,根据在线状态决定
      styleIndex = shop.shop.isOnline ? 3 : 4
    }
    return {
      id: index,
      name: shop.shop.name,
      lnglat: [shop.shop.longitude, shop.shop.latitude],
      style: shop.shop.ringCodeLevel, // 样式索引,对应 massMarksStyle
      style: styleIndex, // 样式索引,对应 massMarksStyle
      extData: shop, // 存储完整的店铺信息
    }
  })
@@ -134,6 +185,46 @@
}
/**
 * 根据异常状态获取中文
 * @param {number} status 异常状态
 * @returns {string} 中文表示
 */
function getExceptionStatusText(status) {
  switch (status + '') {
    case '0':
      return '油烟浓度超标'
    case '1':
      return '供电异常'
    case '2':
      return '设备或网络异常'
    case '3':
      return '无异常'
    default:
      return '未知状态'
  }
}
/**
 * 根据异常状态获取颜色
 * @param {number} status 异常状态
 * @returns {string} 颜色值
 */
function getColorByExceptionStatus(status) {
  switch (status + '') {
    case '0':
      return '#f5222d' // 油烟浓度超标 - 红色
    case '1':
      return '#faad14' // 供电异常 - 黄色
    case '2':
      return '#8c8c8c' // 设备或网络异常 - 灰色
    case '3':
      return '#52c41a' // 无异常 - 绿色
    default:
      return '#8c8c8c' // 灰色
  }
}
/**
 * 创建自定义标记
 * @param {string} color 标记颜色
 * @returns {string} 标记的SVG URL
@@ -149,43 +240,157 @@
}
/**
 * 创建在线状态的SVG标记(油烟监测设备形象)
 * @returns {string} 标记的SVG URL
 */
function createCustomMarkerOnline() {
  const svg = `
    <svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
      <!-- 设备主体 - 圆角矩形 -->
      <rect x="5" y="8" width="22" height="16" rx="3" fill="#52c41a" stroke="white" stroke-width="2"/>
      <!-- 设备顶部 - 弧形 -->
      <path d="M5 8 Q16 3 27 8" stroke="white" stroke-width="2" fill="#389e0d"/>
      <!-- 设备底部 - 弧形 -->
      <path d="M5 24 Q16 29 27 24" stroke="white" stroke-width="2" fill="#389e0d"/>
      <!-- 设备显示屏 -->
      <rect x="8" y="11" width="16" height="10" rx="2" fill="white"/>
      <!-- 显示屏数据 -->
      <path d="M11 14 L21 14" stroke="#52c41a" stroke-width="1.5"/>
      <path d="M11 17 L18 17" stroke="#52c41a" stroke-width="1.5"/>
      <path d="M11 20 L15 20" stroke="#52c41a" stroke-width="1.5"/>
      <!-- 设备天线 -->
      <line x1="16" y1="8" x2="16" y2="3" stroke="white" stroke-width="1.5"/>
      <circle cx="16" cy="3" r="1.5" fill="white"/>
      <!-- 设备指示灯 -->
      <circle cx="27" cy="16" r="3" fill="#ffffff"/>
      <circle cx="27" cy="16" r="1.5" fill="#52c41a"/>
      <!-- 装饰线条 -->
      <line x1="5" y1="13" x2="6" y2="13" stroke="white" stroke-width="1.5"/>
      <line x1="5" y1="19" x2="6" y2="19" stroke="white" stroke-width="1.5"/>
    </svg>
  `
  return 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(svg)))
}
/**
 * 创建离线状态的SVG标记(油烟监测设备形象)
 * @returns {string} 标记的SVG URL
 */
function createCustomMarkerOffline() {
  const svg = `
    <svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
      <!-- 设备主体 - 圆角矩形 -->
      <rect x="5" y="8" width="22" height="16" rx="3" fill="#8c8c8c" stroke="white" stroke-width="2"/>
      <!-- 设备顶部 - 弧形 -->
      <path d="M5 8 Q16 3 27 8" stroke="white" stroke-width="2" fill="#666666"/>
      <!-- 设备底部 - 弧形 -->
      <path d="M5 24 Q16 29 27 24" stroke="white" stroke-width="2" fill="#666666"/>
      <!-- 设备显示屏 -->
      <rect x="8" y="11" width="16" height="10" rx="2" fill="white"/>
      <!-- 显示屏无数据 - 交叉线 -->
      <line x1="11" y1="12" x2="21" y2="22" stroke="#8c8c8c" stroke-width="2"/>
      <line x1="11" y1="22" x2="21" y2="12" stroke="#8c8c8c" stroke-width="2"/>
      <!-- 设备天线 -->
      <line x1="16" y1="8" x2="16" y2="3" stroke="white" stroke-width="1.5"/>
      <circle cx="16" cy="3" r="1.5" fill="white"/>
      <!-- 设备指示灯 -->
      <circle cx="27" cy="16" r="3" fill="#ffffff"/>
      <circle cx="27" cy="16" r="1.5" fill="#8c8c8c"/>
      <!-- 装饰线条 -->
      <line x1="5" y1="13" x2="6" y2="13" stroke="white" stroke-width="1.5"/>
      <line x1="5" y1="19" x2="6" y2="19" stroke="white" stroke-width="1.5"/>
    </svg>
  `
  return 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(svg)))
}
/**
 * 显示店铺信息窗口
 * @param {Object} shop 店铺对象
 */
function showShopInfoWindow(shop) {
  // 准备信息窗口内容
  // const content = `
  //   <div style="padding: 10px; max-width: 300px;">
  //     <h3 style="margin: 0 0 10px 0; color: #333;">${shop.shop.name}</h3>
  //     <div style="font-size: 14px; line-height: 1.5;">
  //       <p><strong>地址:</strong>${shop.shop.address}</p>
  //       <p><strong>环信码等级:</strong><span style="color: ${getColorByRingCodeLevel(shop.shop.ringCodeLevel)}">${shop.shop.ringCodeLevel}</span></p>
  //       <p><strong>环信码发布时间:</strong>${shop.shop.ringCodePublishTime}</p>
  //       <h4 style="margin: 10px 0 5px 0; color: #666;">近1小时监测数据</h4>
  //       <div style="max-height: 150px; overflow-y: auto;">
  //         ${shop.recentData
  //           .map(
  //             (item) => `
  //           <div style="padding: 5px; border-bottom: 1px solid #f0f0f0;">
  //             <p><strong>采样时间:</strong>${item.sampleTime}</p>
  //             <p><strong>油烟浓度:</strong>${item.oilSmokeConcentration} mg/m³</p>
  //             <p><strong>净化器电流:</strong>${item.purifierCurrent} A</p>
  //             <p><strong>风机电流:</strong>${item.fanCurrent} A</p>
  //           </div>
  //         `,
  //           )
  //           .join('')}
  //       </div>
  //     </div>
  //   </div>
  // `
  // 获取在线状态文本
  const onlineStatusText = shop.shop.isOnline ? '在线' : '离线'
  // 获取异常状态文本
  const exceptionStatusText = getExceptionStatusText(shop.shop.exceptionStatus)
  // 获取异常状态颜色
  const exceptionStatusColor = getColorByExceptionStatus(shop.shop.exceptionStatus)
  // 根据状态获取对应的图标
  let statusIcon = statusIcons.online // 默认在线图标
  if (shop.shop.exceptionStatus !== undefined) {
    switch (shop.shop.exceptionStatus) {
      case 0:
        statusIcon = statusIcons.exceed
        break
      case 1:
        statusIcon = statusIcons.exception
        break
      case 2:
        statusIcon = statusIcons.offline
        break
      case 3:
        statusIcon = shop.shop.isOnline ? statusIcons.online : statusIcons.offlineStatus
        break
      default:
        statusIcon = shop.shop.isOnline ? statusIcons.online : statusIcons.offlineStatus
    }
  } else {
    statusIcon = shop.shop.isOnline ? statusIcons.online : statusIcons.offlineStatus
  }
  // 根据在线状态获取图标
  const onlineIcon = shop.shop.isOnline ? statusIcons.online : statusIcons.offlineStatus
  // 根据异常状态获取图标
  let exceptionIcon = statusIcons.online // 默认在线图标
  if (shop.shop.exceptionStatus !== undefined) {
    switch (shop.shop.exceptionStatus) {
      case 0:
        exceptionIcon = statusIcons.exceed
        break
      case 1:
        exceptionIcon = statusIcons.exception
        break
      case 2:
        exceptionIcon = statusIcons.offline
        break
      case 3:
        exceptionIcon = statusIcons.online
        break
      default:
        exceptionIcon = statusIcons.online
    }
  }
  const content = `
    <div style="padding: 10px; width: 400px;">
      <h3 style="margin: 0 0 10px 0; color: #333;">${shop.shop.name}</h3>
      <div style="font-size: 14px; line-height: 1.5;">
        <p><strong>地址:</strong>${shop.shop.address}</p>
        <p><strong>环信码等级:</strong><span style="color: ${getColorByRingCodeLevel(shop.shop.ringCodeLevel)}">${getRingCodeLevelText(shop.shop.ringCodeLevel)}</span></p>
        <p><strong>环信码发布时间:</strong>${shop.shop.ringCodePublishTime}</p>
        <div style="display: flex; flex-direction: row;">
          <span style="flex:1"><strong>在线状态:</strong><span style="color: ${shop.shop.isOnline ? '#52c41a' : '#8c8c8c'}">${onlineStatusText}</span> </span>
          <span style="flex:1"><strong>异常状态:</strong><span style="color: ${exceptionStatusColor}">${exceptionStatusText}</span></span>
        </div>
        <div style="display: flex; flex-direction: row;">
          <span style="flex:1"><strong>环信码等级:</strong><span style="color: ${getColorByRingCodeLevel(shop.shop.ringCodeLevel)}">${getRingCodeLevelText(shop.shop.ringCodeLevel)}</span></span>
          <span style="flex:1"><strong>发布时间:</strong>${shop.shop.ringCodePublishTime}</span>
        </div>
        <h4 style="margin: 10px 0 5px 0; color: #666;">近1小时监测数据</h4>
        <div id="infowindowChartContainer" style="width: 100%; height: 250px;"></div>
      </div>