فهرست منبع

更新监控页面模板和SSE连接,支持JSK、PF和PAU设备类型,并优化Dockerfile

huangyan 1 هفته پیش
والد
کامیت
c530d8eb5e
6فایلهای تغییر یافته به همراه494 افزوده شده و 139 حذف شده
  1. 59 3
      Dockerfile
  2. 27 8
      internal/handler/intelligentbuildingcontrol.go
  3. 10 9
      templates/JSK.html
  4. 388 109
      templates/PAU.html
  5. 10 10
      templates/PF.html
  6. BIN
      templates/static/images/PAU.png

+ 59 - 3
Dockerfile

@@ -1,4 +1,60 @@
-FROM ubuntu:latest
-LABEL authors="huangyan"
+# 第一阶段:构建阶段,使用官方 Golang 镜像编译程序
+FROM golang:1.23-alpine AS builder
 
-ENTRYPOINT ["top", "-b"]
+# 安装必要的构建工具(git 用于获取 GitHub 依赖,ca-certificates 用于 HTTPS 连接)
+RUN apk add --no-cache git ca-certificates
+
+# 设置工作目录
+WORKDIR /app
+
+# 设置 Go 环境变量
+ENV CGO_ENABLED=0 \
+    GOOS=linux \
+    GOARCH=amd64 \
+    GOPROXY=https://goproxy.cn,direct
+
+# 复制 go.mod 和 go.sum 文件
+COPY go.mod go.sum ./
+
+# 下载依赖
+RUN go mod download
+
+# 复制源代码
+COPY . .
+
+# 编译应用程序
+RUN go build -ldflags="-w -s" -o city_chips ./cmd/server
+
+# 第二阶段:运行阶段,使用极简的 alpine 镜像
+FROM alpine:latest
+
+# 安装必要的运行时依赖
+RUN apk --no-cache add ca-certificates tzdata && \
+    addgroup -g 1001 -S appgroup && \
+    adduser -u 1001 -S appuser -G appgroup
+
+# 设置时区
+ENV TZ=Asia/Shanghai
+
+# 设置工作目录
+WORKDIR /app
+
+# 从构建阶段复制二进制文件
+COPY --from=builder /app/city_chips ./
+
+# 复制配置文件和模板文件
+COPY --from=builder /app/config ./config
+COPY --from=builder /app/templates ./templates
+
+# 创建日志目录
+RUN mkdir -p storage/logs && \
+    chown -R appuser:appgroup /app
+
+# 切换到非 root 用户
+USER appuser
+
+# 暴露应用端口(根据配置文件,默认是 8000)
+EXPOSE 8000
+
+# 运行应用程序
+CMD ["./city_chips"]

+ 27 - 8
internal/handler/intelligentbuildingcontrol.go

@@ -12,6 +12,7 @@ import (
 	"net/http"
 	"regexp"
 	"strconv"
+	"strings"
 	"time"
 
 	"github.com/spf13/viper"
@@ -506,14 +507,32 @@ func (h *IntelligentBuildingControlHandler) GetDevices(ctx *gin.Context) {
 // ConfigurationDiagram 获取设备组态图
 func (h *IntelligentBuildingControlHandler) ConfigurationDiagram(c *gin.Context) {
 	// 获取查询参数
-	deviceName := c.Query("deviceName")
-	if deviceName == "" {
-		c.String(http.StatusBadRequest, "缺少 deviceName 参数")
+	//type-deviceName
+	id := c.Query("id")
+
+	if len(id) == 0 {
+		resp.HandleError(c, 1201, "设备名称不能为空", nil)
 		return
 	}
-
-	// 传递变量给模板
-	c.HTML(http.StatusOK, "PAU.html", gin.H{
-		"deviceName": deviceName,
-	})
+	split := strings.Split(id, "-")
+	if len(split) != 2 {
+		resp.HandleError(c, 1201, "设备名称格式错误", nil)
+		return
+	}
+	deviceName := split[1]
+	types := split[0]
+	switch types {
+	case "PAU":
+		c.HTML(http.StatusOK, "PAU.html", gin.H{
+			"deviceName": deviceName,
+		})
+	case "JSK":
+		c.HTML(http.StatusOK, "JSK.html", gin.H{
+			"deviceName": deviceName,
+		})
+	case "PF":
+		c.HTML(http.StatusOK, "PF.html", gin.H{
+			"deviceName": deviceName,
+		})
+	}
 }

+ 10 - 9
templates/JSK.html

@@ -4,7 +4,7 @@
 <head>
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  <title>新风机组监控</title>
+  <title>集水坑监控</title>
   <script src="/static/css/tailwindcss.css"></script>
 
   <script>
@@ -70,13 +70,13 @@
       <i class="fa fa-sitemap text-primary mr-1"></i>系统流程图
     </h2>
     <div class="flex items-center justify-between">
-      <div class="bg-blue-100 px-2 py-1 rounded text-primary font-medium text-sm">
-        <i class="fa fa-arrow-right mr-1"></i> 新风
-      </div>
-      <img src="/static/images/PAU.png" alt="新风机组流程图" class="max-w-[80%] h-auto rounded shadow-sm">
-      <div class="bg-green-100 px-2 py-1 rounded text-green-600 font-medium text-sm">
-        送风 <i class="fa fa-arrow-right ml-1"></i>
-      </div>
+<!--      <div class="bg-blue-100 px-2 py-1 rounded text-primary font-medium text-sm">-->
+<!--        <i class="fa fa-arrow-right mr-1"></i> 新风-->
+<!--      </div>-->
+      <img src="/static/images/JSK.png" alt="新风机组流程图" class="max-w-[80%] h-auto rounded shadow-sm">
+<!--      <div class="bg-green-100 px-2 py-1 rounded text-green-600 font-medium text-sm">-->
+<!--        送风 <i class="fa fa-arrow-right ml-1"></i>-->
+<!--      </div>-->
     </div>
   </div>
 
@@ -369,9 +369,10 @@
 
     // 初始化连接状态
     updateConnectionStatus('连接中...', false);
+    let urls ="./pointSSE?deviceName="+{{.deviceName}}
 
     // 实际SSE连接代码
-    const eventSource = new EventSource('./pointSSE');
+    const eventSource = new EventSource(urls);
 
     eventSource.onopen = () => {
       updateConnectionStatus('已连接', true);

+ 388 - 109
templates/PAU.html

@@ -1,128 +1,407 @@
 <!DOCTYPE html>
-<html lang="zh">
+<html lang="zh-CN">
+
 <head>
-  <meta charset="UTF-8" />
-  <title>设备组态图 - {{ .deviceName }}</title>
-  <style>
-    body {
-      font-family: Arial, sans-serif;
-      background-color: #f4f6f8;
-      padding: 20px;
-    }
-    h1 {
-      text-align: center;
-      color: #333;
-    }
-    .dashboard {
-      display: grid;
-      grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
-      gap: 20px;
-    }
-    .card {
-      background: white;
-      border-radius: 8px;
-      box-shadow: 0 2px 6px rgba(0,0,0,0.1);
-      padding: 20px;
-    }
-    .card h3 {
-      margin-top: 0;
-      font-size: 1.2em;
-      color: #555;
-    }
-    .value {
-      font-size: 1.5em;
-      font-weight: bold;
-      color: #007bff;
-      margin-top: 10px;
-    }
-    .status-on {
-      color: green;
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>新风机组监控</title>
+  <script src="/static/css/tailwindcss.css"></script>
+
+  <script>
+    tailwind.config = {
+      theme: {
+        extend: {
+          colors: {
+            primary: '#0f766e',
+            secondary: '#0d9488',
+            accent: '#14b8a6',
+            neutral: '#134e4a',
+          },
+          fontFamily: {
+            sans: ['Inter', 'system-ui', 'sans-serif'],
+          },
+        },
+      }
     }
-    .status-off {
-      color: red;
+  </script>
+
+  <style type="text/tailwindcss">
+    @layer utilities {
+      .card-shadow {
+        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
+      }
+      .status-normal {
+        @apply text-emerald-500;
+      }
+      .status-warning {
+        @apply text-amber-500;
+      }
+      .status-alarm {
+        @apply text-rose-500;
+      }
+      .data-update {
+        animation: pulse 1s ease-in-out;
+      }
+      @keyframes pulse {
+        0%, 100% { opacity: 1; }
+        50% { opacity: 0.6; }
+      }
     }
   </style>
 </head>
-<body>
 
-<h1>{{ .deviceName }} 组态图</h1>
+<body class="bg-gradient-to-br from-primary to-neutral text-gray-800 min-h-screen p-3">
+<div class="container mx-auto max-w-7xl">
+  <!-- 头部信息 - 更紧凑 -->
+  <header class="mb-4">
+    <div class="flex justify-between items-center">
+      <h1 class="text-[clamp(1.2rem,2vw,1.6rem)] font-bold text-white">{{.deviceName}}</h1>
+      <div class="flex items-center bg-white/10 px-2 py-1 rounded-lg">
+        <i class="fa fa-refresh mr-1 text-white text-sm"></i>
+        <span id="connection-status" class="text-white text-xs">连接中...</span>
+      </div>
+    </div>
+    <div class="h-1 w-full bg-gradient-to-r from-accent to-transparent rounded-full mt-1"></div>
+  </header>
+
+  <!-- 流程图区域 - 调整大小 -->
+  <div class="bg-white/90 rounded-lg p-4 mb-4 card-shadow">
+    <h2 class="text-base font-semibold mb-2 flex items-center">
+      <i class="fa fa-sitemap text-primary mr-1"></i>系统流程图
+    </h2>
+    <div class="flex items-center justify-between">
+<!--      <div class="bg-blue-100 px-2 py-1 rounded text-primary font-medium text-sm">-->
+<!--        <i class="fa fa-arrow-right mr-1"></i> 新风-->
+<!--      </div>-->
+      <img src="/static/images/PAU.png" alt="新风机组流程图" class="max-w-[80%] h-auto rounded shadow-sm">
+<!--      <div class="bg-green-100 px-2 py-1 rounded text-green-600 font-medium text-sm">-->
+<!--        送风 <i class="fa fa-arrow-right ml-1"></i>-->
+<!--      </div>-->
+    </div>
+  </div>
+
+  <!-- 主要内容区域 - 紧凑布局 -->
+  <div id="main-content" class="space-y-4">
+    <!-- 数据卡片区域 - 更密的网格 -->
+    <div id="data-cards" class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-3">
+      <!-- 数据卡片将在这里动态生成 -->
+    </div>
+
+    <!-- 状态和控制参数合并区域 - 节省空间 -->
+    <div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
+      <!-- 状态参数区域 -->
+      <div id="status-section" class="bg-white/90 rounded-lg p-3 card-shadow hidden">
+        <h2 class="text-base font-semibold mb-2 flex items-center text-primary">
+          <i class="fa fa-tachometer mr-1"></i>状态参数
+        </h2>
+        <div id="status-parameters" class="grid grid-cols-1 sm:grid-cols-2 gap-2">
+          <!-- 状态参数将在这里动态生成 -->
+        </div>
+      </div>
 
-<div class="dashboard" id="dashboard">
-  <!-- 动态生成卡片 -->
+      <!-- 控制参数区域 -->
+      <div id="control-section" class="bg-white/90 rounded-lg p-3 card-shadow hidden">
+        <h2 class="text-base font-semibold mb-2 flex items-center text-primary">
+          <i class="fa fa-sliders mr-1"></i>控制参数
+        </h2>
+        <div id="control-parameters" class="space-y-2">
+          <!-- 控制参数将在这里动态生成 -->
+        </div>
+      </div>
+    </div>
+
+    <!-- 数据更新日志 - 缩小高度 -->
+    <div id="log-section" class="bg-white/90 rounded-lg p-3 card-shadow">
+      <h2 class="text-base font-semibold mb-2 flex items-center text-primary">
+        <i class="fa fa-history mr-1"></i>数据更新日志
+      </h2>
+      <div id="update-log" class="text-xs text-gray-600 max-h-20 overflow-y-auto space-y-0.5">
+        <p class="text-gray-500">等待数据更新...</p>
+      </div>
+    </div>
+  </div>
 </div>
 
 <script>
-const deviceName = "{{ .deviceName }}";
-const eventSource = new EventSource(`./pointSSE?deviceName=${deviceName}`);
-
-const dashboard = document.getElementById("dashboard");
-
-// 初始化卡片 DOM 结构
-const fields = {
-  "冬夏季模式": { type: "text", label: "冬夏模式" },
-  "回风二氧化碳": { type: "number", unit: "ppm", label: "回风 CO₂" },
-  "回风温度": { type: "number", unit: "℃", label: "回风温度" },
-  "回风阀反馈": { type: "number", unit: "%", label: "回风阀开度" },
-  "故障报警": { type: "boolean", label: "故障报警" },
-  "新风温度": { type: "number", unit: "℃", label: "新风温度" },
-  "新风阀反馈": { type: "number", unit: "%", label: "新风阀开度" },
-  "水阀反馈": { type: "number", unit: "%", label: "水阀开度" },
-  "自动状态": { type: "boolean", label: "自动状态" },
-  "过滤网压差": { type: "boolean", label: "过滤网压差" },
-  "运行状态": { type: "boolean", label: "运行状态" },
-  "送风温度": { type: "number", unit: "℃", label: "送风温度" },
-  "送风湿度": { type: "number", unit: "%", label: "送风湿度" },
-  "防冻开关": { type: "boolean", label: "防冻开关" },
-  "风机压差": { type: "boolean", label: "风机压差" }
-};
-
-// 创建初始卡片
-function createCards() {
-  Object.keys(fields).forEach(key => {
-    const card = document.createElement("div");
-    card.className = "card";
-    card.id = `card-${key}`;
-    card.innerHTML = `
-      <h3>${fields[key].label}</h3>
-      <div class="value" id="value-${key}">--</div>
-    `;
-    dashboard.appendChild(card);
-  });
-}
-
-createCards();
-
-// 处理 SSE 数据更新
-eventSource.onmessage = function(event) {
-  try {
-    const data = JSON.parse(event.data)?.data || {};
-    for (const key in data) {
-      const value = data[key];
-      const el = document.getElementById(`value-${key}`);
-      if (!el) continue;
-
-      // 类型判断并格式化显示
-      const field = fields[key];
-      if (field?.type === 'boolean') {
-        el.textContent = value === "是" ? "是" : "否";
-        el.className = `value ${value === "是" ? "status-on" : "status-off"}`;
-      } else if (field?.type === 'number') {
-        el.textContent = `${parseFloat(value).toFixed(2)}${field.unit || ''}`;
-        el.className = "value"; // 修复此处
+  // 数据类型配置 - 定义不同字段的显示和交互方式
+  const fieldConfig = {
+    // 温度类字段
+    "新风温度": { type: "temperature", unit: "°C", icon: "fa-thermometer-half", color: "blue" },
+    "送风温度": { type: "temperature", unit: "°C", icon: "fa-thermometer-half", color: "green" },
+    "回风温度": { type: "temperature", unit: "°C", icon: "fa-thermometer-half", color: "purple" },
+
+    // 湿度类字段
+    "送风湿度": { type: "humidity", unit: "%", icon: "fa-tint", color: "blue" },
+
+    // 二氧化碳
+    "回风二氧化碳": { type: "co2", unit: "ppm", icon: "fa-leaf", color: "green" },
+
+    // 阀门反馈
+    "回风阀反馈": { type: "valve", unit: "%", icon: "fa-exchange", color: "gray" },
+    "新风阀反馈": { type: "valve", unit: "%", icon: "fa-exchange", color: "blue" },
+    "水阀反馈": { type: "valve", unit: "%", icon: "fa-tint", color: "cyan" },
+
+    // 模式类(开关)
+    "冬夏季模式": { type: "mode", trueText: "夏季模式", falseText: "冬季模式", section: "control" },
+
+    // 状态类
+    "运行状态": { type: "status", trueText: "运行中", falseText: "停止", trueClass: "status-normal", falseClass: "status-warning" },
+    "故障报警": { type: "status", trueText: "故障", falseText: "正常", trueClass: "status-alarm", falseClass: "status-normal", invert: true },
+    "自动状态": { type: "status", trueText: "自动", falseText: "手动", trueClass: "status-normal", falseClass: "status-warning" },
+    "过滤网压差": { type: "status", trueText: "异常", falseText: "正常", trueClass: "status-alarm", falseClass: "status-normal", invert: true },
+    "防冻开关": { type: "status", trueText: "触发", falseText: "正常", trueClass: "status-alarm", falseClass: "status-normal", invert: true },
+    "风机压差": { type: "status", trueText: "异常", falseText: "正常", trueClass: "status-alarm", falseClass: "status-normal", invert: true }
+  };
+
+  // 连接SSE并处理数据
+  function connectSSE() {
+    const statusElement = document.getElementById('connection-status');
+    const logElement = document.getElementById('update-log');
+    const dataCardsContainer = document.getElementById('data-cards');
+    const statusParametersContainer = document.getElementById('status-parameters');
+    const controlParametersContainer = document.getElementById('control-parameters');
+    const statusSection = document.getElementById('status-section');
+    const controlSection = document.getElementById('control-section');
+
+    // 跟踪已创建的元素,避免重复创建
+    const createdElements = new Set();
+
+    // 更新连接状态
+    function updateConnectionStatus(status, isConnected) {
+      statusElement.textContent = status;
+      if (isConnected) {
+        statusElement.classList.remove('text-amber-400', 'text-rose-400');
+        statusElement.classList.add('text-emerald-400');
+      } else if (status.includes('连接中')) {
+        statusElement.classList.remove('text-emerald-400', 'text-rose-400');
+        statusElement.classList.add('text-amber-400');
       } else {
-        el.textContent = value;
-        el.className = "value"; // 保持一致性
+        statusElement.classList.remove('text-emerald-400', 'text-amber-400');
+        statusElement.classList.add('text-rose-400');
       }
     }
-  } catch (e) {
-    console.error("解析数据失败", e);
-  }
-};
 
+    // 添加日志记录
+    function addLogEntry(message) {
+      const now = new Date();
+      const timeString = now.toLocaleTimeString();
+      const logEntry = document.createElement('p');
+      logEntry.innerHTML = `<span class="text-primary">[${timeString}]</span> ${message}`;
+
+      // 移除"等待数据更新..."提示
+      if (logElement.querySelector('.text-gray-500')) {
+        logElement.innerHTML = '';
+      }
+
+      logElement.appendChild(logEntry);
+      logElement.scrollTop = logElement.scrollHeight;
+    }
+
+    // 创建或更新数据卡片 - 更紧凑的卡片
+    function createOrUpdateDataCard(field, value) {
+      const config = fieldConfig[field] || { type: "generic", unit: "", icon: "fa-dashboard", color: "gray" };
+      const elementId = `card-${field.replace(/\s+/g, '-')}`;
+
+      // 如果元素已存在,只更新值
+      if (createdElements.has(elementId)) {
+        const valueElement = document.getElementById(`${elementId}-value`);
+        if (valueElement) {
+          valueElement.textContent = `${value}${config.unit}`;
+          valueElement.classList.add('data-update');
+          setTimeout(() => valueElement.classList.remove('data-update'), 1000);
+        }
+        return;
+      }
+
+      // 创建新的数据卡片
+      const card = document.createElement('div');
+      card.id = elementId;
+      card.className = "bg-white/90 rounded-lg p-2 card-shadow transform transition-all hover:scale-[1.02]";
+
+      // 确定图标颜色类
+      const iconColorClass = config.color === 'blue' ? 'text-blue-600' :
+              config.color === 'green' ? 'text-green-600' :
+                      config.color === 'purple' ? 'text-purple-600' :
+                              config.color === 'cyan' ? 'text-cyan-600' :
+                                      'text-gray-600';
 
-eventSource.onerror = function(err) {
-  console.error("SSE 错误:", err);
-};
+      const bgColorClass = config.color === 'blue' ? 'bg-blue-100' :
+              config.color === 'green' ? 'bg-green-100' :
+                      config.color === 'purple' ? 'bg-purple-100' :
+                              config.color === 'cyan' ? 'bg-cyan-100' :
+                                      'bg-gray-100';
+
+      card.innerHTML = `
+          <div class="flex justify-between items-start">
+            <div>
+              <p class="text-gray-500 text-xs">${field}</p>
+              <h3 id="${elementId}-value" class="text-lg font-bold mt-0.5">${value}${config.unit}</h3>
+            </div>
+            <div class="${bgColorClass} p-1.5 rounded-full">
+              <i class="fa ${config.icon} ${iconColorClass} text-sm"></i>
+            </div>
+          </div>
+        `;
+
+      dataCardsContainer.appendChild(card);
+      createdElements.add(elementId);
+    }
+
+    // 创建或更新状态参数 - 更紧凑的参数项
+    function createOrUpdateStatusParameter(field, value) {
+      const config = fieldConfig[field] || { type: "generic", trueText: "是", falseText: "否" };
+      const elementId = `status-${field.replace(/\s+/g, '-')}`;
+
+      // 确定应该显示在哪个区域
+      const targetContainer = config.section === "control" ? controlParametersContainer : statusParametersContainer;
+      const targetSection = config.section === "control" ? controlSection : statusSection;
+
+      // 显示对应的区域
+      targetSection.classList.remove('hidden');
+
+      // 如果元素已存在,只更新值
+      if (createdElements.has(elementId)) {
+        const valueElement = document.getElementById(`${elementId}-value`);
+        if (valueElement) {
+          if (config.type === "status") {
+            const isTrue = value === "是";
+            const displayValue = config.invert ? !isTrue : isTrue;
+            const text = displayValue ? config.trueText : config.falseText;
+
+            valueElement.textContent = text;
+            valueElement.className = `px-2 py-0.5 rounded-full text-xs ${displayValue ? config.trueClass : config.falseClass}`;
+          } else if (config.type === "mode") {
+            const isChecked = value === "是";
+            valueElement.checked = isChecked;
+          } else {
+            valueElement.textContent = value;
+          }
+
+          valueElement.classList.add('data-update');
+          setTimeout(() => valueElement.classList.remove('data-update'), 1000);
+        }
+        return;
+      }
+
+      // 创建新的参数元素
+      const parameter = document.createElement('div');
+      parameter.id = elementId;
+
+      if (config.type === "status") {
+        const isTrue = value === "是";
+        const displayValue = config.invert ? !isTrue : isTrue;
+        const text = displayValue ? config.trueText : config.falseText;
+
+        parameter.className = config.section === "control"
+                ? "flex justify-between items-center border-b border-gray-100 pb-2"
+                : "bg-gray-50 p-2.5 rounded text-sm";
+
+        parameter.innerHTML = `
+            <label class="font-medium text-sm">${field}</label>
+            <span id="${elementId}-value" class="px-2 py-0.5 rounded-full text-xs ${displayValue ? config.trueClass : config.falseClass}">
+              ${text}
+            </span>
+          `;
+      } else if (config.type === "mode") {
+        const isChecked = value === "是";
+
+        parameter.className = "flex justify-between items-center border-b border-gray-100 pb-2";
+        parameter.innerHTML = `
+            <label class="font-medium text-sm">${field}</label>
+            <label class="inline-flex items-center cursor-pointer">
+              <input type="checkbox" id="${elementId}-value" ${isChecked ? 'checked' : ''} class="sr-only peer">
+              <div class="relative w-9 h-5 bg-gray-200 peer-focus:outline-none peer-focus:ring-2 peer-focus:ring-primary rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[1px] after:left-[1px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-4 after:w-4 after:transition-all peer-checked:bg-primary"></div>
+              <span class="ml-2 text-xs font-medium">${isChecked ? config.trueText : config.falseText}</span>
+            </label>
+          `;
+
+        // 添加模式切换事件
+        setTimeout(() => {
+          const checkbox = document.getElementById(`${elementId}-value`);
+          if (checkbox) {
+            checkbox.addEventListener('change', function() {
+              const newValue = this.checked ? "是" : "否";
+              const displayText = this.checked ? config.trueText : config.falseText;
+              this.nextElementSibling.nextElementSibling.textContent = displayText;
+
+              addLogEntry(`${field}已切换为${displayText}`);
+              // 这里可以添加发送到服务器的逻辑
+            });
+          }
+        }, 0);
+      } else {
+        parameter.className = config.section === "control"
+                ? "flex justify-between items-center border-b border-gray-100 pb-2"
+                : "bg-gray-50 p-2.5 rounded text-sm";
+
+        parameter.innerHTML = `
+            <label class="font-medium text-sm">${field}</label>
+            <span id="${elementId}-value" class="text-base font-semibold">${value}</span>
+          `;
+      }
+
+      targetContainer.appendChild(parameter);
+      createdElements.add(elementId);
+    }
+
+    // 判断字段应该显示的位置
+    function isStatusOrControlField(field) {
+      const config = fieldConfig[field];
+      return config && (config.type === "status" || config.type === "mode");
+    }
+
+    // 处理接收到的SSE数据
+    function handleSSEData(data) {
+      addLogEntry(`接收${Object.keys(data).length}个字段数据`);
+
+      // 处理每个返回的字段
+      Object.keys(data).forEach(field => {
+        const value = data[field];
+
+        // 根据字段类型决定显示方式和位置
+        if (isStatusOrControlField(field)) {
+          createOrUpdateStatusParameter(field, value);
+        } else {
+          createOrUpdateDataCard(field, value);
+        }
+      });
+    }
+
+    // 初始化连接状态
+    updateConnectionStatus('连接中...', false);
+    let urls ="./pointSSE?deviceName="+{{.deviceName}}
+    // 实际SSE连接代码
+      console.log("url地址===========>",urls)
+    const eventSource = new EventSource(urls);
+
+    eventSource.onopen = () => {
+      updateConnectionStatus('已连接', true);
+      addLogEntry('SSE连接已建立');
+    };
+
+    eventSource.onmessage = (event) => {
+      try {
+        const data = JSON.parse(event.data);
+        if (data.code === 200 && data.data) {
+          handleSSEData(data.data);
+        }
+      } catch (error) {
+        addLogEntry(`数据解析错误: ${error.message}`);
+        console.error('SSE数据解析错误:', error);
+      }
+    };
+
+    eventSource.onerror = (error) => {
+      updateConnectionStatus('连接错误', false);
+      addLogEntry('SSE连接发生错误,正在重试...');
+      console.error('SSE错误:', error);
+    };
+
+  }
+
+  // 页面加载完成后连接SSE
+  document.addEventListener('DOMContentLoaded', connectSSE);
 </script>
 </body>
+
 </html>

+ 10 - 10
templates/PF.html

@@ -4,7 +4,7 @@
 <head>
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  <title>新风机组监控</title>
+  <title>排风</title>
   <script src="/static/css/tailwindcss.css"></script>
 
   <script>
@@ -70,13 +70,13 @@
       <i class="fa fa-sitemap text-primary mr-1"></i>系统流程图
     </h2>
     <div class="flex items-center justify-between">
-      <div class="bg-blue-100 px-2 py-1 rounded text-primary font-medium text-sm">
-        <i class="fa fa-arrow-right mr-1"></i> 新风
-      </div>
-      <img src="/static/images/PAU.png" alt="新风机组流程图" class="max-w-[80%] h-auto rounded shadow-sm">
-      <div class="bg-green-100 px-2 py-1 rounded text-green-600 font-medium text-sm">
-        送风 <i class="fa fa-arrow-right ml-1"></i>
-      </div>
+<!--      <div class="bg-blue-100 px-2 py-1 rounded text-primary font-medium text-sm">-->
+<!--        <i class="fa fa-arrow-right mr-1"></i> 新风-->
+<!--      </div>-->
+      <img src="/static/images/PF.png" alt="新风机组流程图" class="max-w-[80%] h-auto rounded shadow-sm">
+<!--      <div class="bg-green-100 px-2 py-1 rounded text-green-600 font-medium text-sm">-->
+<!--        送风 <i class="fa fa-arrow-right ml-1"></i>-->
+<!--      </div>-->
     </div>
   </div>
 
@@ -369,9 +369,9 @@
 
     // 初始化连接状态
     updateConnectionStatus('连接中...', false);
-
+    let urls ="./pointSSE?deviceName="+{{.deviceName}}
     // 实际SSE连接代码
-    const eventSource = new EventSource('./pointSSE');
+    const eventSource = new EventSource(urls);
 
     eventSource.onopen = () => {
       updateConnectionStatus('已连接', true);

BIN
templates/static/images/PAU.png