From bc56530307e0e92b94a1abb5d38368f04b92e990 Mon Sep 17 00:00:00 2001
From: zhang <zc857179121@qq.com>
Date: 星期四, 12 三月 2026 15:52:15 +0800
Subject: [PATCH] 1
---
zy-acs-hex/src/main/java/com/zy/acs/hex/controller/TestController.java | 3
zy-acs-hex/src/main/java/com/zy/acs/hex/utils/StrUtils.java | 2
zy-acs-hex/dashboard.html | 728 +++++++++++---------
zy-acs-hex/src/main/java/com/zy/acs/hex/controller/LoginController.java | 38 +
zy-acs-hex/src/main/java/com/zy/acs/hex/controller/RouterController.java | 7
zy-acs-hex/src/main/webapp/views/index.html | 706 +++++++++++++++++---
zy-acs-common/src/main/java/com/zy/acs/common/domain/mq/DeviceMessage.java | 2
zy-acs-hex/src/main/java/com/zy/acs/hex/enums/ProtocolType.java | 101 ++
zy-acs-hex/src/main/java/com/zy/acs/hex/utils/HttpGo.java | 60 +
zy-acs-hex/src/main/java/com/zy/acs/hex/domain/SelectOption.java | 19
zy-acs-hex/src/main/java/com/zy/acs/hex/influxdb/task/InfluxDbScheduler.java | 2
zy-acs-hex/src/main/java/com/zy/acs/hex/domain/DeviceLog.java | 18
zy-acs-hex/src/main/webapp/views/login.html | 148 ++-
component/component-Influxdb/src/main/java/com/zy/component/influxdb/service/InfluxDBService.java | 19
component/component-Influxdb/src/main/java/com/zy/component/influxdb/domain/BaseMessage.java | 12
zy-acs-hex/src/main/resources/application.yml | 5
zy-acs-hex/src/main/java/com/zy/acs/hex/config/WebMvcConfig.java | 9
zy-acs-hex/src/main/java/com/zy/acs/hex/domain/GenericQuery.java | 16
zy-acs-hex/src/main/java/com/zy/acs/hex/controller/ProxyController.java | 33
zy-acs-hex/src/main/java/com/zy/acs/hex/controller/SelectTypeController.java | 58 +
zy-acs-hex/src/main/java/com/zy/acs/hex/enums/DirectionType.java | 22
zy-acs-cv/src/main/resources/application.yml | 8
zy-acs-gateway/src/main/java/com/zy/acs/gateway/controller/UtilsController.java | 25
zy-acs-hex/src/main/java/com/zy/acs/hex/controller/DeviceLogController.java | 59 +
24 files changed, 1,579 insertions(+), 521 deletions(-)
diff --git a/component/component-Influxdb/src/main/java/com/zy/component/influxdb/domain/BaseMessage.java b/component/component-Influxdb/src/main/java/com/zy/component/influxdb/domain/BaseMessage.java
new file mode 100644
index 0000000..ac5431c
--- /dev/null
+++ b/component/component-Influxdb/src/main/java/com/zy/component/influxdb/domain/BaseMessage.java
@@ -0,0 +1,12 @@
+package com.zy.component.influxdb.domain;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+@Data
+public class BaseMessage implements Serializable {
+
+ private Long timestamp;
+}
diff --git a/component/component-Influxdb/src/main/java/com/zy/component/influxdb/service/InfluxDBService.java b/component/component-Influxdb/src/main/java/com/zy/component/influxdb/service/InfluxDBService.java
index 14f96b2..924ee65 100644
--- a/component/component-Influxdb/src/main/java/com/zy/component/influxdb/service/InfluxDBService.java
+++ b/component/component-Influxdb/src/main/java/com/zy/component/influxdb/service/InfluxDBService.java
@@ -6,6 +6,7 @@
import com.influxdb.v3.client.query.QueryOptions;
import com.influxdb.v3.client.query.QueryType;
import com.influxdb.v3.client.write.WritePrecision;
+import com.zy.component.influxdb.domain.BaseMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -15,6 +16,7 @@
import java.lang.reflect.InvocationTargetException;
import java.time.Instant;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@@ -68,17 +70,25 @@
}
return null;
}
-
/**
* 鏌ヨ鏁版嵁
*
* @param sql sql璇彞
* @return 鏌ヨ缁撴灉鍒楄〃
*/
- public <T> List<T> queryPoints(String sql, Class<T> clazz) {
+ public <T extends BaseMessage> List<T> queryPoints(String sql, Class<T> clazz) {
+ return queryPoints(sql,new HashMap<>(),clazz);
+ }
+ /**
+ * 鏌ヨ鏁版嵁
+ *
+ * @param sql sql璇彞
+ * @return 鏌ヨ缁撴灉鍒楄〃
+ */
+ public <T extends BaseMessage> List<T> queryPoints(String sql,Map<String,Object> queryParams, Class<T> clazz) {
try {
// 鎵ц鏌ヨ
- Stream<PointValues> queryPoints = influxDBClient.queryPoints(sql);
+ Stream<PointValues> queryPoints = influxDBClient.queryPoints(sql, queryParams);
Field[] declaredFields = clazz.getDeclaredFields();
// 鍒涘缓涓�涓垪琛ㄧ敤浜庡瓨鍌ㄧ粨鏋�
@@ -124,10 +134,9 @@
}
}
}
+ newInstance.setTimestamp(point.getTimestamp().longValue());
return newInstance;
}).collect(Collectors.toList());
-
- logger.info("鏌ヨ鏁版嵁锛歿}", result);
return result;
} catch (Exception e) {
logger.error("Failed to query data from the database.");
diff --git a/zy-acs-common/src/main/java/com/zy/acs/common/domain/mq/DeviceMessage.java b/zy-acs-common/src/main/java/com/zy/acs/common/domain/mq/DeviceMessage.java
index d45dc7e..c1ab843 100644
--- a/zy-acs-common/src/main/java/com/zy/acs/common/domain/mq/DeviceMessage.java
+++ b/zy-acs-common/src/main/java/com/zy/acs/common/domain/mq/DeviceMessage.java
@@ -9,12 +9,14 @@
@Data
@AllArgsConstructor
+@NoArgsConstructor
public class DeviceMessage implements Serializable {
private String sourceHexStr;
private Long timestamp = System.currentTimeMillis();
+
public DeviceMessage(String sourceHexStr) {
this.sourceHexStr = sourceHexStr;
}
diff --git a/zy-acs-cv/src/main/resources/application.yml b/zy-acs-cv/src/main/resources/application.yml
index 0a4f054..2199fc9 100644
--- a/zy-acs-cv/src/main/resources/application.yml
+++ b/zy-acs-cv/src/main/resources/application.yml
@@ -35,10 +35,10 @@
mark: 10
max-retries: 3
retry-delay: 800
- - type: FAKEUSER
- mark: 20
- max-retries: 2
- retry-delay: 800
+# - type: FAKEUSER
+# mark: 20
+# max-retries: 2
+# retry-delay: 800
- type: APPLYLOC
mark: 30
max-retries: 2
diff --git a/zy-acs-gateway/src/main/java/com/zy/acs/gateway/controller/UtilsController.java b/zy-acs-gateway/src/main/java/com/zy/acs/gateway/controller/UtilsController.java
new file mode 100644
index 0000000..5f5bdf1
--- /dev/null
+++ b/zy-acs-gateway/src/main/java/com/zy/acs/gateway/controller/UtilsController.java
@@ -0,0 +1,25 @@
+package com.zy.acs.gateway.controller;
+
+import com.zy.acs.framework.common.R;
+import com.zy.acs.gateway.utils.PacCoder;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+
+
+@RestController
+@Slf4j
+@RequestMapping(value = "/utils")
+public class UtilsController {
+
+ /**
+ * 鏌ヨ鏈�鏂扮殑鍗佹潯鏁版嵁
+ *
+ * @return
+ */
+ @RequestMapping(value = "/decode/{hex}")
+ @ResponseBody
+ public R query(@PathVariable String hex) {
+ return R.ok(PacCoder.decode(hex));
+ }
+
+}
diff --git a/zy-acs-hex/dashboard.html b/zy-acs-hex/dashboard.html
index 378c261..3e8001b 100644
--- a/zy-acs-hex/dashboard.html
+++ b/zy-acs-hex/dashboard.html
@@ -19,21 +19,27 @@
font-family: 'Inter', sans-serif;
background-color: #f5f7fa;
}
+
.layui-card {
margin-bottom: 16px;
}
+
.layui-card-header {
font-weight: 600;
}
+
.status-online {
color: #10b981;
}
+
.status-offline {
color: #ef4444;
}
+
.chart-container {
height: 300px;
}
+
.realtime-container {
height: 160px;
overflow-y: auto;
@@ -41,352 +47,422 @@
</style>
</head>
<body>
- <div id="app">
- <!-- 椤堕儴瀵艰埅鏍� -->
- <lay-header height="60px" bg-color="#fff" shadow>
- <template #left>
- <div class="flex items-center space-x-2">
- <i class="fa fa-android text-2xl" style="color: #3b82f6"></i>
- <h1 class="text-xl font-bold" style="color: #1e293b">鏈哄櫒浜烘暟鎹洃鎺�</h1>
- </div>
- </template>
- <template #right>
- <div class="flex items-center space-x-4">
- <lay-input placeholder="鎼滅储鏈哄櫒浜�..." prefix-icon="search" style="width: 200px"></lay-input>
- <lay-button type="primary" @click="refreshData">
- <i class="fa fa-refresh mr-1"></i> 鍒锋柊
- </lay-button>
- </div>
- </template>
- </lay-header>
+<div id="app">
+ <!-- 椤堕儴瀵艰埅鏍� -->
+ <lay-header height="60px" bg-color="#fff" shadow>
+ <template #left>
+ <div class="flex items-center space-x-2">
+ <i class="fa fa-android text-2xl" style="color: #3b82f6"></i>
+ <h1 class="text-xl font-bold" style="color: #1e293b">鏈哄櫒浜烘暟鎹洃鎺�</h1>
+ </div>
+ </template>
+ <template #right>
+ <div class="flex items-center space-x-4">
+ <lay-input placeholder="鎼滅储鏈哄櫒浜�..." prefix-icon="search" style="width: 200px"></lay-input>
+ <lay-button type="primary" @click="refreshData">
+ <i class="fa fa-refresh mr-1"></i> 鍒锋柊
+ </lay-button>
+ </div>
+ </template>
+ </lay-header>
- <!-- 涓诲唴瀹瑰尯 -->
- <lay-container style="padding: 20px">
- <!-- 鐘舵�佹瑙� -->
- <lay-row :gutter="16">
- <lay-col :span="6">
- <lay-card shadow>
- <div class="flex items-center justify-between">
- <div>
- <p style="color: #64748b; font-size: 14px">鎬绘満鍣ㄤ汉鏁�</p>
- <h3 style="font-size: 24px; font-weight: bold; color: #1e293b">24</h3>
- </div>
- <div style="width: 48px; height: 48px; border-radius: 50%; background-color: #dbeafe; display: flex; align-items: center; justify-content: center">
- <i class="fa fa-microchip text-xl" style="color: #3b82f6"></i>
- </div>
+ <!-- 涓诲唴瀹瑰尯 -->
+ <lay-container style="padding: 20px">
+ <!-- 鐘舵�佹瑙� -->
+ <lay-row :gutter="16">
+ <lay-col :span="6">
+ <lay-card shadow>
+ <div class="flex items-center justify-between">
+ <div>
+ <p style="color: #64748b; font-size: 14px">鎬绘満鍣ㄤ汉鏁�</p>
+ <h3 style="font-size: 24px; font-weight: bold; color: #1e293b">24</h3>
</div>
- </lay-card>
- </lay-col>
- <lay-col :span="6">
- <lay-card shadow>
- <div class="flex items-center justify-between">
- <div>
- <p style="color: #64748b; font-size: 14px">鍦ㄧ嚎鏈哄櫒浜�</p>
- <h3 style="font-size: 24px; font-weight: bold; color: #10b981">18</h3>
- </div>
- <div style="width: 48px; height: 48px; border-radius: 50%; background-color: #d1fae5; display: flex; align-items: center; justify-content: center">
- <i class="fa fa-check-circle text-xl" style="color: #10b981"></i>
- </div>
- </div>
- </lay-card>
- </lay-col>
- <lay-col :span="6">
- <lay-card shadow>
- <div class="flex items-center justify-between">
- <div>
- <p style="color: #64748b; font-size: 14px">绂荤嚎鏈哄櫒浜�</p>
- <h3 style="font-size: 24px; font-weight: bold; color: #ef4444">6</h3>
- </div>
- <div style="width: 48px; height: 48px; border-radius: 50%; background-color: #fee2e2; display: flex; align-items: center; justify-content: center">
- <i class="fa fa-exclamation-circle text-xl" style="color: #ef4444"></i>
- </div>
- </div>
- </lay-card>
- </lay-col>
- <lay-col :span="6">
- <lay-card shadow>
- <div class="flex items-center justify-between">
- <div>
- <p style="color: #64748b; font-size: 14px">浠婃棩鏁版嵁閲�</p>
- <h3 style="font-size: 24px; font-weight: bold; color: #f59e0b">1.2k</h3>
- </div>
- <div style="width: 48px; height: 48px; border-radius: 50%; background-color: #fef3c7; display: flex; align-items: center; justify-content: center">
- <i class="fa fa-database text-xl" style="color: #f59e0b"></i>
- </div>
- </div>
- </lay-card>
- </lay-col>
- </lay-row>
-
- <!-- 鏁版嵁鍥捐〃 -->
- <lay-row :gutter="16" style="margin-top: 16px">
- <lay-col :span="12">
- <lay-card shadow>
- <template #header>
- <div class="flex justify-between items-center">
- <h2 style="font-size: 16px; font-weight: 600; color: #1e293b">涓婅鏁版嵁瓒嬪娍</h2>
- <div class="flex space-x-2">
- <lay-button size="sm" type="primary">灏忔椂</lay-button>
- <lay-button size="sm">澶�</lay-button>
- <lay-button size="sm">鍛�</lay-button>
- </div>
- </div>
- </template>
- <div class="chart-container">
- <canvas ref="upDataChart"></canvas>
- </div>
- </lay-card>
- </lay-col>
- <lay-col :span="12">
- <lay-card shadow>
- <template #header>
- <div class="flex justify-between items-center">
- <h2 style="font-size: 16px; font-weight: 600; color: #1e293b">涓嬭鏁版嵁瓒嬪娍</h2>
- <div class="flex space-x-2">
- <lay-button size="sm" type="primary">灏忔椂</lay-button>
- <lay-button size="sm">澶�</lay-button>
- <lay-button size="sm">鍛�</lay-button>
- </div>
- </div>
- </template>
- <div class="chart-container">
- <canvas ref="downDataChart"></canvas>
- </div>
- </lay-card>
- </lay-col>
- </lay-row>
-
- <!-- 璁惧鏁版嵁琛ㄦ牸 -->
- <lay-card shadow style="margin-top: 16px">
- <template #header>
- <div class="flex justify-between items-center">
- <h2 style="font-size: 16px; font-weight: 600; color: #1e293b">鏈哄櫒浜烘暟鎹垪琛�</h2>
- <div class="flex space-x-2">
- <lay-button size="sm">
- <i class="fa fa-filter mr-1"></i> 绛涢��
- </lay-button>
- <lay-button size="sm">
- <i class="fa fa-download mr-1"></i> 瀵煎嚭
- </lay-button>
+ <div style="width: 48px; height: 48px; border-radius: 50%; background-color: #dbeafe; display: flex; align-items: center; justify-content: center">
+ <i class="fa fa-microchip text-xl" style="color: #3b82f6"></i>
</div>
</div>
- </template>
- <lay-table :data="devices" :height="400">
- <lay-table-column prop="id" label="璁惧ID" width="120"></lay-table-column>
- <lay-table-column prop="name" label="璁惧鍚嶇О" width="150"></lay-table-column>
- <lay-table-column prop="status" label="鐘舵��" width="100">
- <template #default="{ row }">
- <lay-badge v-if="row.status === 'online'" type="success">鍦ㄧ嚎</lay-badge>
- <lay-badge v-else type="danger">绂荤嚎</lay-badge>
- </template>
- </lay-table-column>
- <lay-table-column prop="upData" label="涓婅鏁版嵁" width="120"></lay-table-column>
- <lay-table-column prop="downData" label="涓嬭鏁版嵁" width="120"></lay-table-column>
- <lay-table-column prop="lastComm" label="鏈�鍚庨�氫俊" width="150"></lay-table-column>
- <lay-table-column label="鎿嶄綔" width="150">
- <template #default="{ row }">
- <lay-button size="sm" type="primary" style="margin-right: 8px">
- <i class="fa fa-eye"></i>
- </lay-button>
- <lay-button size="sm" type="warning" style="margin-right: 8px">
- <i class="fa fa-edit"></i>
- </lay-button>
- <lay-button size="sm" type="danger">
- <i class="fa fa-trash"></i>
- </lay-button>
- </template>
- </lay-table-column>
- </lay-table>
- <div class="flex justify-between items-center mt-4">
- <p style="color: #64748b; font-size: 14px">鏄剧ず 1-10 鏉★紝鍏� 24 鏉�</p>
- <lay-pagination
+ </lay-card>
+ </lay-col>
+ <lay-col :span="6">
+ <lay-card shadow>
+ <div class="flex items-center justify-between">
+ <div>
+ <p style="color: #64748b; font-size: 14px">鍦ㄧ嚎鏈哄櫒浜�</p>
+ <h3 style="font-size: 24px; font-weight: bold; color: #10b981">18</h3>
+ </div>
+ <div style="width: 48px; height: 48px; border-radius: 50%; background-color: #d1fae5; display: flex; align-items: center; justify-content: center">
+ <i class="fa fa-check-circle text-xl" style="color: #10b981"></i>
+ </div>
+ </div>
+ </lay-card>
+ </lay-col>
+ <lay-col :span="6">
+ <lay-card shadow>
+ <div class="flex items-center justify-between">
+ <div>
+ <p style="color: #64748b; font-size: 14px">绂荤嚎鏈哄櫒浜�</p>
+ <h3 style="font-size: 24px; font-weight: bold; color: #ef4444">6</h3>
+ </div>
+ <div style="width: 48px; height: 48px; border-radius: 50%; background-color: #fee2e2; display: flex; align-items: center; justify-content: center">
+ <i class="fa fa-exclamation-circle text-xl" style="color: #ef4444"></i>
+ </div>
+ </div>
+ </lay-card>
+ </lay-col>
+ <lay-col :span="6">
+ <lay-card shadow>
+ <div class="flex items-center justify-between">
+ <div>
+ <p style="color: #64748b; font-size: 14px">浠婃棩鏁版嵁閲�</p>
+ <h3 style="font-size: 24px; font-weight: bold; color: #f59e0b">1.2k</h3>
+ </div>
+ <div style="width: 48px; height: 48px; border-radius: 50%; background-color: #fef3c7; display: flex; align-items: center; justify-content: center">
+ <i class="fa fa-database text-xl" style="color: #f59e0b"></i>
+ </div>
+ </div>
+ </lay-card>
+ </lay-col>
+ </lay-row>
+
+ <!-- 鏁版嵁鍥捐〃 -->
+ <lay-row :gutter="16" style="margin-top: 16px">
+ <lay-col :span="12">
+ <lay-card shadow>
+ <template #header>
+ <div class="flex justify-between items-center">
+ <h2 style="font-size: 16px; font-weight: 600; color: #1e293b">涓婅鏁版嵁瓒嬪娍</h2>
+ <div class="flex space-x-2">
+ <lay-button size="sm" type="primary">灏忔椂</lay-button>
+ <lay-button size="sm">澶�</lay-button>
+ <lay-button size="sm">鍛�</lay-button>
+ </div>
+ </div>
+ </template>
+ <div class="chart-container">
+ <canvas ref="upDataChart"></canvas>
+ </div>
+ </lay-card>
+ </lay-col>
+ <lay-col :span="12">
+ <lay-card shadow>
+ <template #header>
+ <div class="flex justify-between items-center">
+ <h2 style="font-size: 16px; font-weight: 600; color: #1e293b">涓嬭鏁版嵁瓒嬪娍</h2>
+ <div class="flex space-x-2">
+ <lay-button size="sm" type="primary">灏忔椂</lay-button>
+ <lay-button size="sm">澶�</lay-button>
+ <lay-button size="sm">鍛�</lay-button>
+ </div>
+ </div>
+ </template>
+ <div class="chart-container">
+ <canvas ref="downDataChart"></canvas>
+ </div>
+ </lay-card>
+ </lay-col>
+ </lay-row>
+
+ <!-- 璁惧鏁版嵁琛ㄦ牸 -->
+ <lay-card shadow style="margin-top: 16px">
+ <template #header>
+ <div class="flex justify-between items-center">
+ <h2 style="font-size: 16px; font-weight: 600; color: #1e293b">鏈哄櫒浜烘暟鎹垪琛�</h2>
+ <div class="flex space-x-2">
+ <lay-button size="sm">
+ <i class="fa fa-filter mr-1"></i> 绛涢��
+ </lay-button>
+ <lay-button size="sm">
+ <i class="fa fa-download mr-1"></i> 瀵煎嚭
+ </lay-button>
+ </div>
+ </div>
+ </template>
+ <lay-table :data="devices" :height="400">
+ <lay-table-column prop="id" label="璁惧ID" width="120"></lay-table-column>
+ <lay-table-column prop="name" label="璁惧鍚嶇О" width="150"></lay-table-column>
+ <lay-table-column prop="status" label="鐘舵��" width="100">
+ <template #default="{ row }">
+ <lay-badge v-if="row.status === 'online'" type="success">鍦ㄧ嚎</lay-badge>
+ <lay-badge v-else type="danger">绂荤嚎</lay-badge>
+ </template>
+ </lay-table-column>
+ <lay-table-column prop="upData" label="涓婅鏁版嵁" width="120"></lay-table-column>
+ <lay-table-column prop="downData" label="涓嬭鏁版嵁" width="120"></lay-table-column>
+ <lay-table-column prop="lastComm" label="鏈�鍚庨�氫俊" width="150"></lay-table-column>
+ <lay-table-column label="鎿嶄綔" width="150">
+ <template #default="{ row }">
+ <lay-button size="sm" type="primary" style="margin-right: 8px">
+ <i class="fa fa-eye"></i>
+ </lay-button>
+ <lay-button size="sm" type="warning" style="margin-right: 8px">
+ <i class="fa fa-edit"></i>
+ </lay-button>
+ <lay-button size="sm" type="danger">
+ <i class="fa fa-trash"></i>
+ </lay-button>
+ </template>
+ </lay-table-column>
+ </lay-table>
+ <div class="flex justify-between items-center mt-4">
+ <p style="color: #64748b; font-size: 14px">鏄剧ず 1-10 鏉★紝鍏� 24 鏉�</p>
+ <lay-pagination
v-model:current="currentPage"
v-model:limit="pageSize"
:total="total"
:limits="[10, 20, 50, 100]"
layout="prev, pager, next, jumper, sizes, total"
- ></lay-pagination>
- </div>
- </lay-card>
-
- <!-- 瀹炴椂鏁版嵁鏇存柊 -->
- <lay-card shadow style="margin-top: 16px">
- <template #header>
- <h2 style="font-size: 16px; font-weight: 600; color: #1e293b">瀹炴椂鏁版嵁鏇存柊</h2>
- </template>
- <div class="realtime-container p-2 border border-gray-200 rounded-lg">
- <div v-for="(item, index) in realtimeData" :key="index" class="py-1 border-b border-gray-100">
- <span style="color: #64748b; font-size: 12px">{{ item.timestamp }}</span>
- <span style="color: #1e293b; margin-left: 10px">{{ item.message }}</span>
- </div>
- </div>
- </lay-card>
- </lay-container>
-
- <!-- 椤佃剼 -->
- <lay-footer height="60px" bg-color="#fff" shadow>
- <div class="text-center" style="color: #64748b; font-size: 14px">
- 漏 2026 鏈哄櫒浜烘暟鎹洃鎺х郴缁� | 鐗堟湰 1.0.0
+ ></lay-pagination>
</div>
- </lay-footer>
- </div>
+ </lay-card>
- <script>
- const { createApp, ref, onMounted } = Vue;
- const app = createApp({
- components: {
- LayHeader: layui.LayHeader,
- LayContainer: layui.LayContainer,
- LayRow: layui.LayRow,
- LayCol: layui.LayCol,
- LayCard: layui.LayCard,
- LayInput: layui.LayInput,
- LayButton: layui.LayButton,
- LayTable: layui.LayTable,
- LayTableColumn: layui.LayTableColumn,
- LayBadge: layui.LayBadge,
- LayPagination: layui.LayPagination,
- LayFooter: layui.LayFooter
- },
- setup() {
- // 妯℃嫙鏈哄櫒浜烘暟鎹�
- const devices = ref([
- { id: 'ROB-001', name: '閰嶉�佹満鍣ㄤ汉1鍙�', status: 'online', upData: '2.4KB', downData: '0.8KB', lastComm: '2鍒嗛挓鍓�' },
- { id: 'ROB-002', name: '閰嶉�佹満鍣ㄤ汉2鍙�', status: 'online', upData: '1.8KB', downData: '0.5KB', lastComm: '5鍒嗛挓鍓�' },
- { id: 'ROB-003', name: '宸℃鏈哄櫒浜�1鍙�', status: 'offline', upData: '0KB', downData: '0KB', lastComm: '2灏忔椂鍓�' },
- { id: 'ROB-004', name: '閰嶉�佹満鍣ㄤ汉3鍙�', status: 'online', upData: '3.2KB', downData: '1.2KB', lastComm: '1鍒嗛挓鍓�' },
- { id: 'ROB-005', name: '宸℃鏈哄櫒浜�2鍙�', status: 'online', upData: '1.5KB', downData: '0.6KB', lastComm: '3鍒嗛挓鍓�' },
- { id: 'ROB-006', name: '閰嶉�佹満鍣ㄤ汉4鍙�', status: 'offline', upData: '0KB', downData: '0KB', lastComm: '5灏忔椂鍓�' },
- { id: 'ROB-007', name: '宸℃鏈哄櫒浜�3鍙�', status: 'online', upData: '2.1KB', downData: '0.9KB', lastComm: '4鍒嗛挓鍓�' },
- { id: 'ROB-008', name: '閰嶉�佹満鍣ㄤ汉5鍙�', status: 'online', upData: '2.8KB', downData: '1.1KB', lastComm: '2鍒嗛挓鍓�' },
- { id: 'ROB-009', name: '宸℃鏈哄櫒浜�4鍙�', status: 'offline', upData: '0KB', downData: '0KB', lastComm: '1澶╁墠' },
- { id: 'ROB-010', name: '閰嶉�佹満鍣ㄤ汉6鍙�', status: 'online', upData: '1.9KB', downData: '0.7KB', lastComm: '6鍒嗛挓鍓�' }
- ]);
+ <!-- 瀹炴椂鏁版嵁鏇存柊 -->
+ <lay-card shadow style="margin-top: 16px">
+ <template #header>
+ <h2 style="font-size: 16px; font-weight: 600; color: #1e293b">瀹炴椂鏁版嵁鏇存柊</h2>
+ </template>
+ <div class="realtime-container p-2 border border-gray-200 rounded-lg">
+ <div v-for="(item, index) in realtimeData" :key="index" class="py-1 border-b border-gray-100">
+ <span style="color: #64748b; font-size: 12px">{{ item.timestamp }}</span>
+ <span style="color: #1e293b; margin-left: 10px">{{ item.message }}</span>
+ </div>
+ </div>
+ </lay-card>
+ </lay-container>
- // 鍒嗛〉鏁版嵁
- const currentPage = ref(1);
- const pageSize = ref(10);
- const total = ref(24);
+ <!-- 椤佃剼 -->
+ <lay-footer height="60px" bg-color="#fff" shadow>
+ <div class="text-center" style="color: #64748b; font-size: 14px">
+ 漏 2026 鏈哄櫒浜烘暟鎹洃鎺х郴缁� | 鐗堟湰 1.0.0
+ </div>
+ </lay-footer>
+</div>
- // 瀹炴椂鏁版嵁
- const realtimeData = ref([]);
+<script>
+ const {createApp, ref, onMounted} = Vue;
+ const app = createApp({
+ components: {
+ LayHeader: layui.LayHeader,
+ LayContainer: layui.LayContainer,
+ LayRow: layui.LayRow,
+ LayCol: layui.LayCol,
+ LayCard: layui.LayCard,
+ LayInput: layui.LayInput,
+ LayButton: layui.LayButton,
+ LayTable: layui.LayTable,
+ LayTableColumn: layui.LayTableColumn,
+ LayBadge: layui.LayBadge,
+ LayPagination: layui.LayPagination,
+ LayFooter: layui.LayFooter
+ },
+ setup() {
+ // 妯℃嫙鏈哄櫒浜烘暟鎹�
+ const devices = ref([
+ {
+ id: 'ROB-001',
+ name: '閰嶉�佹満鍣ㄤ汉1鍙�',
+ status: 'online',
+ upData: '2.4KB',
+ downData: '0.8KB',
+ lastComm: '2鍒嗛挓鍓�'
+ },
+ {
+ id: 'ROB-002',
+ name: '閰嶉�佹満鍣ㄤ汉2鍙�',
+ status: 'online',
+ upData: '1.8KB',
+ downData: '0.5KB',
+ lastComm: '5鍒嗛挓鍓�'
+ },
+ {
+ id: 'ROB-003',
+ name: '宸℃鏈哄櫒浜�1鍙�',
+ status: 'offline',
+ upData: '0KB',
+ downData: '0KB',
+ lastComm: '2灏忔椂鍓�'
+ },
+ {
+ id: 'ROB-004',
+ name: '閰嶉�佹満鍣ㄤ汉3鍙�',
+ status: 'online',
+ upData: '3.2KB',
+ downData: '1.2KB',
+ lastComm: '1鍒嗛挓鍓�'
+ },
+ {
+ id: 'ROB-005',
+ name: '宸℃鏈哄櫒浜�2鍙�',
+ status: 'online',
+ upData: '1.5KB',
+ downData: '0.6KB',
+ lastComm: '3鍒嗛挓鍓�'
+ },
+ {
+ id: 'ROB-006',
+ name: '閰嶉�佹満鍣ㄤ汉4鍙�',
+ status: 'offline',
+ upData: '0KB',
+ downData: '0KB',
+ lastComm: '5灏忔椂鍓�'
+ },
+ {
+ id: 'ROB-007',
+ name: '宸℃鏈哄櫒浜�3鍙�',
+ status: 'online',
+ upData: '2.1KB',
+ downData: '0.9KB',
+ lastComm: '4鍒嗛挓鍓�'
+ },
+ {
+ id: 'ROB-008',
+ name: '閰嶉�佹満鍣ㄤ汉5鍙�',
+ status: 'online',
+ upData: '2.8KB',
+ downData: '1.1KB',
+ lastComm: '2鍒嗛挓鍓�'
+ },
+ {
+ id: 'ROB-009',
+ name: '宸℃鏈哄櫒浜�4鍙�',
+ status: 'offline',
+ upData: '0KB',
+ downData: '0KB',
+ lastComm: '1澶╁墠'
+ },
+ {
+ id: 'ROB-010',
+ name: '閰嶉�佹満鍣ㄤ汉6鍙�',
+ status: 'online',
+ upData: '1.9KB',
+ downData: '0.7KB',
+ lastComm: '6鍒嗛挓鍓�'
+ }
+ ]);
- // 鍥捐〃寮曠敤
- const upDataChart = ref(null);
- const downDataChart = ref(null);
+ // 鍒嗛〉鏁版嵁
+ const currentPage = ref(1);
+ const pageSize = ref(10);
+ const total = ref(24);
- // 鍒锋柊鏁版嵁
- const refreshData = () => {
- console.log('鍒锋柊鏁版嵁');
- // 杩欓噷鍙互娣诲姞瀹為檯鐨勫埛鏂伴�昏緫
- };
+ // 瀹炴椂鏁版嵁
+ const realtimeData = ref([]);
- // 鍒濆鍖栧浘琛�
- const initCharts = () => {
- // 涓婅鏁版嵁鍥捐〃
- const upCtx = upDataChart.value.getContext('2d');
- new Chart(upCtx, {
- type: 'line',
- data: {
- labels: ['00:00', '03:00', '06:00', '09:00', '12:00', '15:00', '18:00', '21:00'],
- datasets: [{
- label: '涓婅鏁版嵁 (KB)',
- data: [12, 19, 15, 25, 22, 30, 28, 35],
- borderColor: '#3b82f6',
- backgroundColor: 'rgba(59, 130, 246, 0.1)',
- tension: 0.4,
- fill: true
- }]
+ // 鍥捐〃寮曠敤
+ const upDataChart = ref(null);
+ const downDataChart = ref(null);
+
+ // 鍒锋柊鏁版嵁
+ const refreshData = () => {
+ console.log('鍒锋柊鏁版嵁');
+ // 杩欓噷鍙互娣诲姞瀹為檯鐨勫埛鏂伴�昏緫
+ };
+
+ // 鍒濆鍖栧浘琛�
+ const initCharts = () => {
+ // 涓婅鏁版嵁鍥捐〃
+ const upCtx = upDataChart.value.getContext('2d');
+ new Chart(upCtx, {
+ type: 'line',
+ data: {
+ labels: ['00:00', '03:00', '06:00', '09:00', '12:00', '15:00', '18:00', '21:00'],
+ datasets: [{
+ label: '涓婅鏁版嵁 (KB)',
+ data: [12, 19, 15, 25, 22, 30, 28, 35],
+ borderColor: '#3b82f6',
+ backgroundColor: 'rgba(59, 130, 246, 0.1)',
+ tension: 0.4,
+ fill: true
+ }]
+ },
+ options: {
+ responsive: true,
+ maintainAspectRatio: false,
+ plugins: {
+ legend: {
+ display: false
+ }
},
- options: {
- responsive: true,
- maintainAspectRatio: false,
- plugins: {
- legend: {
- display: false
- }
- },
- scales: {
- y: {
- beginAtZero: true
- }
+ scales: {
+ y: {
+ beginAtZero: true
}
}
- });
-
- // 涓嬭鏁版嵁鍥捐〃
- const downCtx = downDataChart.value.getContext('2d');
- new Chart(downCtx, {
- type: 'line',
- data: {
- labels: ['00:00', '03:00', '06:00', '09:00', '12:00', '15:00', '18:00', '21:00'],
- datasets: [{
- label: '涓嬭鏁版嵁 (KB)',
- data: [5, 8, 6, 12, 10, 15, 13, 18],
- borderColor: '#10b981',
- backgroundColor: 'rgba(16, 185, 129, 0.1)',
- tension: 0.4,
- fill: true
- }]
- },
- options: {
- responsive: true,
- maintainAspectRatio: false,
- plugins: {
- legend: {
- display: false
- }
- },
- scales: {
- y: {
- beginAtZero: true
- }
- }
- }
- });
- };
-
- // 妯℃嫙瀹炴椂鏁版嵁鏇存柊
- const simulateRealtimeData = () => {
- const messages = [
- 'ROB-001 閰嶉�佹満鍣ㄤ汉1鍙�: 杩愯涓紝浣嶇疆: A1鍖�',
- 'ROB-002 閰嶉�佹満鍣ㄤ汉2鍙�: 寰呮満涓紝浣嶇疆: B2鍖�',
- 'ROB-004 閰嶉�佹満鍣ㄤ汉3鍙�: 鍏呯數涓紝鐢甸噺: 85%',
- 'ROB-005 宸℃鏈哄櫒浜�2鍙�: 宸℃涓紝宸插畬鎴�3/5浠诲姟',
- 'ROB-007 宸℃鏈哄櫒浜�3鍙�: 寰呮満涓紝浣嶇疆: C3鍖�',
- 'ROB-008 閰嶉�佹満鍣ㄤ汉5鍙�: 杩愯涓紝浣嶇疆: D4鍖�'
- ];
-
- setInterval(() => {
- const message = messages[Math.floor(Math.random() * messages.length)];
- const timestamp = new Date().toLocaleTimeString();
- realtimeData.value.unshift({ timestamp, message });
- // 闄愬埗鏄剧ず鏉℃暟
- if (realtimeData.value.length > 20) {
- realtimeData.value.pop();
- }
- }, 2000);
- };
-
- // 椤甸潰鍔犺浇瀹屾垚鍚庡垵濮嬪寲
- onMounted(() => {
- initCharts();
- simulateRealtimeData();
+ }
});
- return {
- devices,
- currentPage,
- pageSize,
- total,
- realtimeData,
- upDataChart,
- downDataChart,
- refreshData
- };
- }
- });
- app.mount('#app');
- </script>
+ // 涓嬭鏁版嵁鍥捐〃
+ const downCtx = downDataChart.value.getContext('2d');
+ new Chart(downCtx, {
+ type: 'line',
+ data: {
+ labels: ['00:00', '03:00', '06:00', '09:00', '12:00', '15:00', '18:00', '21:00'],
+ datasets: [{
+ label: '涓嬭鏁版嵁 (KB)',
+ data: [5, 8, 6, 12, 10, 15, 13, 18],
+ borderColor: '#10b981',
+ backgroundColor: 'rgba(16, 185, 129, 0.1)',
+ tension: 0.4,
+ fill: true
+ }]
+ },
+ options: {
+ responsive: true,
+ maintainAspectRatio: false,
+ plugins: {
+ legend: {
+ display: false
+ }
+ },
+ scales: {
+ y: {
+ beginAtZero: true
+ }
+ }
+ }
+ });
+ };
+
+ // 妯℃嫙瀹炴椂鏁版嵁鏇存柊
+ const simulateRealtimeData = () => {
+ const messages = [
+ 'ROB-001 閰嶉�佹満鍣ㄤ汉1鍙�: 杩愯涓紝浣嶇疆: A1鍖�',
+ 'ROB-002 閰嶉�佹満鍣ㄤ汉2鍙�: 寰呮満涓紝浣嶇疆: B2鍖�',
+ 'ROB-004 閰嶉�佹満鍣ㄤ汉3鍙�: 鍏呯數涓紝鐢甸噺: 85%',
+ 'ROB-005 宸℃鏈哄櫒浜�2鍙�: 宸℃涓紝宸插畬鎴�3/5浠诲姟',
+ 'ROB-007 宸℃鏈哄櫒浜�3鍙�: 寰呮満涓紝浣嶇疆: C3鍖�',
+ 'ROB-008 閰嶉�佹満鍣ㄤ汉5鍙�: 杩愯涓紝浣嶇疆: D4鍖�'
+ ];
+
+ setInterval(() => {
+ const message = messages[Math.floor(Math.random() * messages.length)];
+ const timestamp = new Date().toLocaleTimeString();
+ realtimeData.value.unshift({timestamp, message});
+ // 闄愬埗鏄剧ず鏉℃暟
+ if (realtimeData.value.length > 20) {
+ realtimeData.value.pop();
+ }
+ }, 2000);
+ };
+
+ // 椤甸潰鍔犺浇瀹屾垚鍚庡垵濮嬪寲
+ onMounted(() => {
+ initCharts();
+ simulateRealtimeData();
+ });
+
+ return {
+ devices,
+ currentPage,
+ pageSize,
+ total,
+ realtimeData,
+ upDataChart,
+ downDataChart,
+ refreshData
+ };
+ }
+ });
+ app.mount('#app');
+</script>
</body>
</html>
\ No newline at end of file
diff --git a/zy-acs-hex/src/main/java/com/zy/acs/hex/config/WebMvcConfig.java b/zy-acs-hex/src/main/java/com/zy/acs/hex/config/WebMvcConfig.java
index 5cb4541..ff35e3d 100644
--- a/zy-acs-hex/src/main/java/com/zy/acs/hex/config/WebMvcConfig.java
+++ b/zy-acs-hex/src/main/java/com/zy/acs/hex/config/WebMvcConfig.java
@@ -3,7 +3,6 @@
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.AsyncHandlerInterceptor;
-import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@@ -36,28 +35,28 @@
@Bean
public AsyncHandlerInterceptor getAsyncHandlerInterceptor() {
- return new AsyncHandlerInterceptor(){
+ return new AsyncHandlerInterceptor() {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
- cors(response);
+ cors(response);
return true;
}
};
}
-
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// 閰嶇疆闈欐�佽祫婧愬鐞嗗櫒
registry.addResourceHandler("/static/**")
+ .addResourceLocations("classpath:/static/")
.addResourceLocations("/static/");
// 閰嶇疆瑙嗗浘鏂囦欢澶勭悊鍣�
registry.addResourceHandler("/views/**")
.addResourceLocations("/views/");
}
- public static void cors(HttpServletResponse response){
+ public static void cors(HttpServletResponse response) {
// 璺ㄥ煙璁剧疆
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Origin", "*");
diff --git a/zy-acs-hex/src/main/java/com/zy/acs/hex/controller/DeviceLogController.java b/zy-acs-hex/src/main/java/com/zy/acs/hex/controller/DeviceLogController.java
index 047b845..8a8e6b2 100644
--- a/zy-acs-hex/src/main/java/com/zy/acs/hex/controller/DeviceLogController.java
+++ b/zy-acs-hex/src/main/java/com/zy/acs/hex/controller/DeviceLogController.java
@@ -1,16 +1,20 @@
package com.zy.acs.hex.controller;
-import com.zy.acs.common.domain.mq.DeviceMessage;
+import com.influxdb.v3.client.PointValues;
import com.zy.acs.framework.common.R;
+import com.zy.acs.hex.domain.DeviceLog;
import com.zy.component.influxdb.service.InfluxDBService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.ResponseBody;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.bind.annotation.*;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
+import java.util.stream.Stream;
@RestController
@Slf4j
@@ -28,10 +32,49 @@
*/
@GetMapping(value = "/query")
@ResponseBody
- public R query() {
- List<DeviceMessage> deviceMessages = influxDBService.queryPoints("select * from device order by time desc limit 10", DeviceMessage.class);
- return R.ok(deviceMessages);
+ public R query(@RequestParam(required = false, defaultValue = "device") String measurement,
+ @RequestParam(required = false) Map<String, Object> conditions,
+ @RequestParam(required = false, defaultValue = "100") Integer limit,
+ @RequestParam(required = false, defaultValue = "time") String orderBy,
+ @RequestParam(required = false, defaultValue = "DESC") String orderDirection) {
+ return R.ok(getData(measurement, conditions, limit, orderBy, orderDirection));
}
+ /**
+ * 閫氱敤鏌ヨ鏂规硶锛屾敮鎸佸姩鎬佹潯浠�
+ */
+ private List<DeviceLog> getData(String measurement, Map<String, Object> conditions,int limit, String orderBy, String orderDirection) {
+ // 鏋勫缓鏌ヨ璇彞
+ StringBuilder sqlBuilder = new StringBuilder("SELECT * FROM ").append(measurement).append(" WHERE 1=1");
+ Map<String, Object> params = new HashMap<>();
+
+ // 鍔ㄦ�佹坊鍔犳潯浠�
+ if (conditions != null && !conditions.isEmpty()) {
+ if (conditions.get("startTime") != null) {
+ if (conditions.get("startTime") != null) {
+ sqlBuilder.append(" AND ").append("time").append(" >= :").append("startTime");
+ params.put("startTime", conditions.get("startTime"));
+ }
+ }else if (conditions.get("endTime") != null) {
+ if (conditions.get("endTime") != null) {
+ sqlBuilder.append(" AND ").append("time").append(" <= :").append("endTime");
+ params.put("endTime", conditions.get("endTime"));
+ }
+ }else {
+ conditions.forEach((key, value) -> {
+ if (value != null) {
+ sqlBuilder.append(" AND ").append(key).append(" = :").append(key);
+ params.put(key, value);
+ }
+ });
+ }
+ }
+ // 娣诲姞鎺掑簭鍜岄檺鍒�
+ sqlBuilder.append(" ORDER BY ").append(orderBy).append(" ").append(orderDirection);
+ sqlBuilder.append(" LIMIT :limit");
+ params.put("limit", limit);
+ return influxDBService.queryPoints(sqlBuilder.toString(),params, DeviceLog.class);
+ }
+
}
diff --git a/zy-acs-hex/src/main/java/com/zy/acs/hex/controller/LoginController.java b/zy-acs-hex/src/main/java/com/zy/acs/hex/controller/LoginController.java
new file mode 100644
index 0000000..6b15004
--- /dev/null
+++ b/zy-acs-hex/src/main/java/com/zy/acs/hex/controller/LoginController.java
@@ -0,0 +1,38 @@
+package com.zy.acs.hex.controller;
+
+import com.zy.acs.framework.common.R;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 鐧诲綍鎺у埗鍣�
+ */
+@RestController
+@RequestMapping(value = "/login")
+public class LoginController {
+
+ @Value("${login.username}")
+ private String username;
+
+ @Value("${login.password}")
+ private String password;
+
+ /**
+ * 鐧诲綍鎺ュ彛
+ *
+ * @param user 鐢ㄦ埛鍚�
+ * @param pass 瀵嗙爜
+ * @return 鐧诲綍缁撴灉
+ */
+ @PostMapping(value = "/auth")
+ public R login(@RequestParam String user, @RequestParam String pass) {
+ if (username.equals(user) && password.equals(pass)) {
+ return R.ok("鐧诲綍鎴愬姛");
+ } else {
+ return R.error("鐢ㄦ埛鍚嶆垨瀵嗙爜閿欒");
+ }
+ }
+}
diff --git a/zy-acs-hex/src/main/java/com/zy/acs/hex/controller/ProxyController.java b/zy-acs-hex/src/main/java/com/zy/acs/hex/controller/ProxyController.java
new file mode 100644
index 0000000..0730ecf
--- /dev/null
+++ b/zy-acs-hex/src/main/java/com/zy/acs/hex/controller/ProxyController.java
@@ -0,0 +1,33 @@
+package com.zy.acs.hex.controller;
+
+import com.zy.acs.framework.common.R;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.client.RestTemplate;
+
+@RestController
+@RequestMapping(value = "/proxy")
+public class ProxyController {
+
+ private final RestTemplate restTemplate = new RestTemplate();
+
+ @GetMapping(value = "/decode")
+ public R decode(@RequestParam String hexData) {
+ try {
+ String url = "http://127.0.0.1:9060/utils/decode/" + hexData;
+ HttpHeaders headers = new HttpHeaders();
+ headers.set("Content-Type", "application/json");
+ HttpEntity<String> entity = new HttpEntity<>(headers);
+ ResponseEntity<Object> response = restTemplate.exchange(url, HttpMethod.GET, entity, Object.class);
+ return R.ok(response.getBody());
+ } catch (Exception e) {
+ return R.error("瑙f瀽澶辫触: " + e.getMessage());
+ }
+ }
+}
diff --git a/zy-acs-hex/src/main/java/com/zy/acs/hex/controller/RouterController.java b/zy-acs-hex/src/main/java/com/zy/acs/hex/controller/RouterController.java
index 9fcbbc2..9d931ac 100644
--- a/zy-acs-hex/src/main/java/com/zy/acs/hex/controller/RouterController.java
+++ b/zy-acs-hex/src/main/java/com/zy/acs/hex/controller/RouterController.java
@@ -1,6 +1,5 @@
package com.zy.acs.hex.controller;
-import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -13,12 +12,10 @@
public class RouterController {
-
-
@RequestMapping("/")
public void index(HttpServletResponse response) {
try {
- response.sendRedirect( "/views/index.html");
+ response.sendRedirect("/views/index.html");
} catch (Exception ex) {
ex.printStackTrace();
}
@@ -27,7 +24,7 @@
@RequestMapping("/login")
public void login(HttpServletResponse response) {
try {
- response.sendRedirect( "/views/login.html");
+ response.sendRedirect("/views/login.html");
} catch (Exception ex) {
ex.printStackTrace();
}
diff --git a/zy-acs-hex/src/main/java/com/zy/acs/hex/controller/SelectTypeController.java b/zy-acs-hex/src/main/java/com/zy/acs/hex/controller/SelectTypeController.java
new file mode 100644
index 0000000..bd954a1
--- /dev/null
+++ b/zy-acs-hex/src/main/java/com/zy/acs/hex/controller/SelectTypeController.java
@@ -0,0 +1,58 @@
+package com.zy.acs.hex.controller;
+
+import com.zy.acs.framework.common.R;
+import com.zy.acs.hex.domain.SelectOption;
+import com.zy.acs.hex.enums.DirectionType;
+import com.zy.acs.hex.enums.ProtocolType;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+@RestController
+@Slf4j
+@RequestMapping(value = "/deviceLog")
+public class SelectTypeController {
+
+
+ /**
+ * 鏌ヨ娑堟伅绫诲瀷
+ *
+ * @return
+ */
+ @GetMapping(value = "/queryType")
+ @ResponseBody
+ public R queryType() {
+ DirectionType[] values = DirectionType.values();
+ List<SelectOption> messageTypes = new ArrayList<>();
+ for (DirectionType value : values) {
+ messageTypes.add(new SelectOption(value.getText(), value.name().toLowerCase()));
+ }
+ return R.ok(messageTypes);
+ }
+
+ /**
+ * 鏌ヨ鏍囩绫诲瀷
+ *
+ * @return
+ */
+ @GetMapping(value = "/queryEvent")
+ @ResponseBody
+ public R queryEvent(@RequestParam(required = false) DirectionType directionType) {
+ List<SelectOption> messageTypes = new ArrayList<>();
+ if (directionType == null) {
+ ProtocolType[] values = ProtocolType.values();
+ for (ProtocolType value : values) {
+ messageTypes.add(new SelectOption(value.name(), value.getDirection().getText() + "-" + value.getDes() + value.name()));
+ }
+ return R.ok(messageTypes);
+ }
+ List<ProtocolType> protocolTypes = ProtocolType.listByDirectionType(directionType);
+ for (ProtocolType value : protocolTypes) {
+ messageTypes.add(new SelectOption(value.name(), value.getDirection().getText() + "-" + value.getDes() + value.name()));
+ }
+ return R.ok(messageTypes);
+ }
+}
diff --git a/zy-acs-hex/src/main/java/com/zy/acs/hex/controller/TestController.java b/zy-acs-hex/src/main/java/com/zy/acs/hex/controller/TestController.java
index ba77e21..37c4386 100644
--- a/zy-acs-hex/src/main/java/com/zy/acs/hex/controller/TestController.java
+++ b/zy-acs-hex/src/main/java/com/zy/acs/hex/controller/TestController.java
@@ -2,6 +2,7 @@
import com.zy.acs.common.domain.mq.DeviceMessage;
import com.zy.acs.hex.constant.RabbitConstant;
+import com.zy.acs.hex.domain.DeviceLog;
import com.zy.component.influxdb.service.InfluxDBService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
@@ -68,7 +69,7 @@
@GetMapping(value = "/query2")
@ResponseBody
public Object queryTest2() {
- return influxDBService.queryPoints("select * from device order by time desc limit 10", DeviceMessage.class);
+ return influxDBService.queryPoints("select * from device order by time desc limit 10", DeviceLog.class);
}
}
diff --git a/zy-acs-hex/src/main/java/com/zy/acs/hex/domain/DeviceLog.java b/zy-acs-hex/src/main/java/com/zy/acs/hex/domain/DeviceLog.java
new file mode 100644
index 0000000..bfd76fc
--- /dev/null
+++ b/zy-acs-hex/src/main/java/com/zy/acs/hex/domain/DeviceLog.java
@@ -0,0 +1,18 @@
+package com.zy.acs.hex.domain;
+
+import com.zy.component.influxdb.domain.BaseMessage;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class DeviceLog extends BaseMessage implements Serializable {
+
+ private String deviceId;
+
+ private String event;
+
+ private String type;
+
+ private String sourceHexStr;
+}
diff --git a/zy-acs-hex/src/main/java/com/zy/acs/hex/domain/GenericQuery.java b/zy-acs-hex/src/main/java/com/zy/acs/hex/domain/GenericQuery.java
new file mode 100644
index 0000000..6fb4f5d
--- /dev/null
+++ b/zy-acs-hex/src/main/java/com/zy/acs/hex/domain/GenericQuery.java
@@ -0,0 +1,16 @@
+package com.zy.acs.hex.domain;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+@Data
+public class GenericQuery implements Serializable {
+
+ private Map<String, Object> conditions = new HashMap<>(); // 鍔ㄦ�佹煡璇㈡潯浠�
+ private int limit = 100; // 榛樿杩斿洖100鏉�
+ private String orderBy = "time"; // 榛樿鎸夋椂闂存帓搴�
+ private String orderDirection = "DESC"; // 榛樿闄嶅簭
+}
diff --git a/zy-acs-hex/src/main/java/com/zy/acs/hex/domain/SelectOption.java b/zy-acs-hex/src/main/java/com/zy/acs/hex/domain/SelectOption.java
new file mode 100644
index 0000000..d679807
--- /dev/null
+++ b/zy-acs-hex/src/main/java/com/zy/acs/hex/domain/SelectOption.java
@@ -0,0 +1,19 @@
+package com.zy.acs.hex.domain;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class SelectOption implements Serializable {
+ private String value;
+ private String label;
+
+ public SelectOption() {
+ }
+
+ public SelectOption(String value, String label) {
+ this.value = value;
+ this.label = label;
+ }
+}
diff --git a/zy-acs-hex/src/main/java/com/zy/acs/hex/enums/DirectionType.java b/zy-acs-hex/src/main/java/com/zy/acs/hex/enums/DirectionType.java
new file mode 100644
index 0000000..bcc1c7a
--- /dev/null
+++ b/zy-acs-hex/src/main/java/com/zy/acs/hex/enums/DirectionType.java
@@ -0,0 +1,22 @@
+package com.zy.acs.hex.enums;
+
+public enum DirectionType {
+
+ DOWN("涓嬭"),
+ UP("涓婅"),
+ ;
+
+ private String text;
+
+ DirectionType(String text) {
+ this.text = text;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public void setText(String text) {
+ this.text = text;
+ }
+}
diff --git a/zy-acs-hex/src/main/java/com/zy/acs/hex/enums/ProtocolType.java b/zy-acs-hex/src/main/java/com/zy/acs/hex/enums/ProtocolType.java
new file mode 100644
index 0000000..cb5c9fd
--- /dev/null
+++ b/zy-acs-hex/src/main/java/com/zy/acs/hex/enums/ProtocolType.java
@@ -0,0 +1,101 @@
+package com.zy.acs.hex.enums;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 鏍囪瘑鏋氫妇
+ * Created by vincent on 2019-04-02
+ */
+public enum ProtocolType {
+
+ // 涓嬭 -------------------------------------------------------------------
+
+ PATH_COMMAND(0x01, "璺緞鏁版嵁鍖�", DirectionType.DOWN),
+
+ PICK_PLACE_ACK(0x06, "鍙栨斁璐у簲绛斿寘", DirectionType.DOWN),
+
+ ACTION_COMMAND(0x02, "鍔ㄤ綔鍛戒护鍖�", DirectionType.DOWN),
+
+ HEARTBEAT_COMMAND(0x03, "蹇冭烦鍖�", DirectionType.DOWN),
+
+ FAULT_CLEAR_COMMAND(0x04, "鏁呴殰鍛戒护鍖�", DirectionType.DOWN),
+
+ ACTIVATION_COMMAND(0x80, "婵�娲诲寘", DirectionType.DOWN),
+
+ LOGIN_ACK(0xF0, "鐧诲綍搴旂瓟鍖�", DirectionType.DOWN),
+
+ ACTION_SUCCESS_ACK(0xA1, "鍔ㄤ綔瀹屾垚鎴愬姛搴旂瓟", DirectionType.DOWN),
+
+ ACTION_FAIL_ACK(0xA0, "鍔ㄤ綔瀹屾垚澶辫触搴旂瓟", DirectionType.DOWN),
+
+ PATH_ACK_RESPONSE(0xB1, "璺緞鏁版嵁鍖呭洖澶峚ck", DirectionType.DOWN),
+
+ // 涓婅 -------------------------------------------------------------------
+
+ PATH_ACK(0x01, "璺緞搴旂瓟鍖�", DirectionType.UP),
+
+ PICK_PLACE_REQUEST(0x06, "鍙栨斁璐ц姹傚寘", DirectionType.UP),
+
+ COMMAND_ACK(0x02, "鍛戒护搴旂瓟鍖�", DirectionType.UP),
+
+ ACTION_COMPLETE(0x11, "鍔ㄤ綔瀹屾垚鍖�", DirectionType.UP),
+
+ DATA_CODE_REPORT(0x12, "鏈夌爜瀹炴椂鏁版嵁鍖�", DirectionType.UP),
+
+ DATA_WITHOUT_CODE_REPORT(0x13, "鏃犵爜瀹炴椂鏁版嵁鍖�", DirectionType.UP),
+
+ HEARTBEAT_REPORT(0x03, "蹇冭烦鍖�", DirectionType.UP),
+
+ FAULT_REPORT(0x04, "鏁呴殰鏁版嵁鍖�", DirectionType.UP),
+
+ HANDLE_FALUT_ACK(0x14, "鏁呴殰娓呴櫎搴旂瓟鍖�", DirectionType.UP),
+
+ SILO_REPORT(0x70, "鏂欎粨淇℃伅鍖�", DirectionType.UP),
+
+ LOGIN_REPORT(0xF0, "鏈哄櫒浜虹櫥闄嗘暟鎹寘", DirectionType.UP),
+
+ ;
+
+ private final int code; // 缂栫爜
+ private final String des; // 鎻忚堪
+ private final DirectionType direction;
+
+ ProtocolType(int code, String des, DirectionType direction) {
+ this.code = code;
+ this.des = des;
+ this.direction = direction;
+ }
+
+ public int getCode() {
+ return code;
+ }
+
+ public String getDes() {
+ return des;
+ }
+
+ public DirectionType getDirection() {
+ return direction;
+ }
+
+ public static List<ProtocolType> listByDirectionType(DirectionType direction) {
+ List<ProtocolType> protocolTypes = new ArrayList<ProtocolType>();
+ for (ProtocolType type : ProtocolType.values()) {
+ if (type.getDirection() == direction) {
+ protocolTypes.add(type);
+ }
+ }
+ return protocolTypes;
+ }
+
+ public static ProtocolType getByCode(int code, DirectionType direction) {
+ for (ProtocolType type : ProtocolType.values()) {
+ if (type.getCode() == code && type.getDirection() == direction) {
+ return type;
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/zy-acs-hex/src/main/java/com/zy/acs/hex/influxdb/task/InfluxDbScheduler.java b/zy-acs-hex/src/main/java/com/zy/acs/hex/influxdb/task/InfluxDbScheduler.java
index 903e203..807069a 100644
--- a/zy-acs-hex/src/main/java/com/zy/acs/hex/influxdb/task/InfluxDbScheduler.java
+++ b/zy-acs-hex/src/main/java/com/zy/acs/hex/influxdb/task/InfluxDbScheduler.java
@@ -61,7 +61,7 @@
parames.put("retention-period", retentionPeriod);
HttpGo.HttpResponse postResponse = this.http.postJson(createDatabaseUrl, headers, JSON.toJSONString(parames));
log.info("鏄惁鍒涘缓鏁版嵁搴擄細{}", postResponse);
- }else {
+ } else {
log.info("鏁版嵁搴擄細{}", response.body());
}
} catch (IOException e) {
diff --git a/zy-acs-hex/src/main/java/com/zy/acs/hex/utils/HttpGo.java b/zy-acs-hex/src/main/java/com/zy/acs/hex/utils/HttpGo.java
index a0d27f5..9fce2c9 100644
--- a/zy-acs-hex/src/main/java/com/zy/acs/hex/utils/HttpGo.java
+++ b/zy-acs-hex/src/main/java/com/zy/acs/hex/utils/HttpGo.java
@@ -18,7 +18,7 @@
/**
* Minimal OkHttp wrapper: GET / POST only.
- *
+ * <p>
* - fluent API (get / postJson / postForm)
* - default singleton instance (thread-safe)
* - per-request headers + default headers
@@ -41,7 +41,9 @@
: Collections.unmodifiableMap(new LinkedHashMap<>(defaultHeaders));
}
- /** Shared default instance (safe SSL by default). */
+ /**
+ * Shared default instance (safe SSL by default).
+ */
public static HttpGo defaults() {
return Holder.DEFAULT;
}
@@ -78,12 +80,16 @@
// ===================== POST =====================
- /** POST JSON string payload (null/blank -> "{}"). */
+ /**
+ * POST JSON string payload (null/blank -> "{}").
+ */
public HttpResponse postJson(String url, String json) throws IOException {
return postJson(url, null, json);
}
- /** POST JSON string payload (null/blank -> "{}"). */
+ /**
+ * POST JSON string payload (null/blank -> "{}").
+ */
public HttpResponse postJson(String url, Map<String, String> headers, String json) throws IOException {
String payload = (json == null || json.trim().isEmpty()) ? "{}" : json;
RequestBody body = RequestBody.create(payload, JSON);
@@ -99,12 +105,16 @@
return execute(rb.build());
}
- /** POST x-www-form-urlencoded fields. */
+ /**
+ * POST x-www-form-urlencoded fields.
+ */
public HttpResponse postForm(String url, Map<String, String> formFields) throws IOException {
return postForm(url, null, formFields);
}
- /** POST x-www-form-urlencoded fields. */
+ /**
+ * POST x-www-form-urlencoded fields.
+ */
public HttpResponse postForm(String url, Map<String, String> headers, Map<String, String> formFields) throws IOException {
FormBody.Builder fb = new FormBody.Builder(DEFAULT_CHARSET);
if (formFields != null) {
@@ -169,10 +179,21 @@
this.tookMs = tookMs;
}
- public int statusCode() { return statusCode; }
- public Headers headers() { return headers; }
- public String body() { return body; }
- public long tookMs() { return tookMs; }
+ public int statusCode() {
+ return statusCode;
+ }
+
+ public Headers headers() {
+ return headers;
+ }
+
+ public String body() {
+ return body;
+ }
+
+ public long tookMs() {
+ return tookMs;
+ }
public boolean is2xx() {
return statusCode >= 200 && statusCode < 300;
@@ -219,7 +240,9 @@
return this;
}
- /** Trust ALL certificates. ONLY for internal testing/self-signed endpoints. */
+ /**
+ * Trust ALL certificates. ONLY for internal testing/self-signed endpoints.
+ */
public Builder trustAllSsl(boolean enable) {
this.trustAllSsl = enable;
return this;
@@ -250,9 +273,18 @@
TrustAll() {
try {
this.trustManager = new X509TrustManager() {
- @Override public void checkClientTrusted(X509Certificate[] chain, String authType) { }
- @Override public void checkServerTrusted(X509Certificate[] chain, String authType) { }
- @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
+ @Override
+ public void checkClientTrusted(X509Certificate[] chain, String authType) {
+ }
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] chain, String authType) {
+ }
+
+ @Override
+ public X509Certificate[] getAcceptedIssuers() {
+ return new X509Certificate[0];
+ }
};
SSLContext sslContext = SSLContext.getInstance("TLS");
diff --git a/zy-acs-hex/src/main/java/com/zy/acs/hex/utils/StrUtils.java b/zy-acs-hex/src/main/java/com/zy/acs/hex/utils/StrUtils.java
index 48e614b..89ab200 100644
--- a/zy-acs-hex/src/main/java/com/zy/acs/hex/utils/StrUtils.java
+++ b/zy-acs-hex/src/main/java/com/zy/acs/hex/utils/StrUtils.java
@@ -16,7 +16,7 @@
String[] parts = routingKey.split("\\.");
if (parts.length == 4) {
Map<String, String> data = new HashMap<>();
- data.put(InfluxDBConstant.DEVICE_MEASUREMENT_TAG_TYPE, parts[1]);
+ data.put(InfluxDBConstant.DEVICE_MEASUREMENT_TAG_TYPE, parts[1].toUpperCase());
data.put(InfluxDBConstant.DEVICE_MEASUREMENT_TAG_DEVICEID, parts[2]);
data.put(InfluxDBConstant.DEVICE_MEASUREMENT_TAG_EVENT, parts[3]);
return data;
diff --git a/zy-acs-hex/src/main/resources/application.yml b/zy-acs-hex/src/main/resources/application.yml
index 6901000..8e935c9 100644
--- a/zy-acs-hex/src/main/resources/application.yml
+++ b/zy-acs-hex/src/main/resources/application.yml
@@ -33,3 +33,8 @@
#token: apiv3_116RKycNhxbf62Nys4zthC05aRD-aidzhEpEpLtsFuedhJTaCtVklNrzHs9LHxBWMuzDclBHVgToGoQuWGiIIA
retention-period: 30d
createDatabaseUrl: ${influxdb3.url}/api/v3/configure/database
+
+# 鐧诲綍閰嶇疆
+login:
+ username: admin
+ password: admin123
diff --git a/zy-acs-hex/src/main/webapp/views/index.html b/zy-acs-hex/src/main/webapp/views/index.html
index 63acba4..47f972d 100644
--- a/zy-acs-hex/src/main/webapp/views/index.html
+++ b/zy-acs-hex/src/main/webapp/views/index.html
@@ -10,156 +10,650 @@
padding: 0;
box-sizing: border-box;
}
+
body {
- font-family: Arial, sans-serif;
- background-color: #f0f2f5;
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+ background-color: #f5f7fa;
+ color: #333;
}
+
.header {
- background-color: #1890ff;
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
- padding: 15px 20px;
+ padding: 15px 30px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
+ }
+
+ .header h1 {
+ font-size: 22px;
+ font-weight: 600;
+ }
+
+ .header button {
+ background-color: rgba(255, 255, 255, 0.2);
+ color: white;
+ border: 1px solid rgba(255, 255, 255, 0.3);
+ padding: 8px 20px;
+ border-radius: 20px;
+ cursor: pointer;
+ font-size: 14px;
+ transition: all 0.3s ease;
+ }
+
+ .header button:hover {
+ background-color: rgba(255, 255, 255, 0.3);
+ }
+
+ .container {
+ padding: 30px;
+ }
+
+ .filter-section {
+ background-color: white;
+ padding: 20px;
+ border-radius: 12px;
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
+ margin-bottom: 20px;
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+ gap: 15px;
+ animation: fadeIn 0.5s ease-in-out;
+ }
+
+ @keyframes fadeIn {
+ from {
+ opacity: 0;
+ transform: translateY(20px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+ }
+
+ .filter-group {
+ display: flex;
+ flex-direction: column;
+ }
+
+ .filter-group label {
+ margin-bottom: 8px;
+ font-weight: 500;
+ color: #666;
+ font-size: 14px;
+ }
+
+ .filter-group select,
+ .filter-group input {
+ padding: 10px 12px;
+ border: 2px solid #e0e0e0;
+ border-radius: 8px;
+ font-size: 14px;
+ transition: border-color 0.3s ease;
+ }
+
+ .filter-group select:focus,
+ .filter-group input:focus {
+ outline: none;
+ border-color: #667eea;
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
+ }
+
+ .filter-actions {
+ grid-column: 1 / -1;
+ display: flex;
+ gap: 10px;
+ margin-top: 10px;
+ }
+
+ .btn {
+ padding: 10px 20px;
+ border: none;
+ border-radius: 8px;
+ font-size: 14px;
+ font-weight: 500;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ }
+
+ .btn-primary {
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ color: white;
+ }
+
+ .btn-primary:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
+ }
+
+ .btn-secondary {
+ background-color: #f0f0f0;
+ color: #333;
+ }
+
+ .btn-secondary:hover {
+ background-color: #e0e0e0;
+ }
+
+ .log-section {
+ background-color: white;
+ border-radius: 12px;
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
+ overflow: hidden;
+ }
+
+ .log-header {
+ padding: 20px;
+ border-bottom: 1px solid #f0f0f0;
display: flex;
justify-content: space-between;
align-items: center;
}
- .header h1 {
- font-size: 20px;
+
+ .log-header h2 {
+ font-size: 18px;
+ font-weight: 600;
+ color: #333;
}
- .header button {
- background-color: transparent;
- color: white;
- border: 1px solid white;
- padding: 5px 15px;
- border-radius: 4px;
- cursor: pointer;
+
+ .log-content {
+ padding: 0;
}
- .header button:hover {
- background-color: rgba(255,255,255,0.1);
- }
- .container {
- padding: 20px;
- }
- .refresh-btn {
- background-color: #1890ff;
- color: white;
- border: none;
- padding: 8px 16px;
- border-radius: 4px;
- cursor: pointer;
- margin-bottom: 20px;
- }
- .refresh-btn:hover {
- background-color: #40a9ff;
- }
+
table {
width: 100%;
border-collapse: collapse;
- background-color: white;
- box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
+
th, td {
- padding: 12px;
+ padding: 15px 20px;
text-align: left;
- border-bottom: 1px solid #ddd;
+ border-bottom: 1px solid #f0f0f0;
}
+
th {
- background-color: #f5f5f5;
- font-weight: bold;
+ background-color: #f9f9f9;
+ font-weight: 600;
+ color: #666;
+ font-size: 14px;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
}
+
tr:hover {
- background-color: #f5f5f5;
+ background-color: #f9f9f9;
}
+
+ td {
+ font-size: 14px;
+ color: #555;
+ }
+
.loading {
text-align: center;
- padding: 20px;
- color: #666;
+ padding: 60px;
+ color: #999;
+ font-size: 16px;
}
+
.error {
text-align: center;
- padding: 20px;
- color: red;
+ padding: 60px;
+ color: #ff4d4f;
+ font-size: 16px;
+ }
+
+ .empty {
+ text-align: center;
+ padding: 60px;
+ color: #999;
+ font-size: 16px;
+ }
+
+ .status-badge {
+ display: inline-block;
+ padding: 4px 12px;
+ border-radius: 12px;
+ font-size: 12px;
+ font-weight: 500;
+ }
+
+ .status-up {
+ background-color: #e6f7ee;
+ color: #52c41a;
+ }
+
+ .status-down {
+ background-color: #fff2e8;
+ color: #fa8c16;
+ }
+
+ @media (max-width: 768px) {
+ .container {
+ padding: 15px;
+ }
+
+ .filter-section {
+ grid-template-columns: 1fr;
+ }
+
+ .filter-actions {
+ flex-direction: column;
+ }
+
+ .btn {
+ width: 100%;
+ }
+
+ table {
+ display: block;
+ overflow-x: auto;
+ }
}
</style>
</head>
<body>
- <div class="header">
- <h1>鏈哄櫒浜轰笂涓嬭鏃ュ織</h1>
- <button id="logoutBtn">鐧诲嚭</button>
+<div class="header">
+ <h1>鏈哄櫒浜轰笂涓嬭鏃ュ織</h1>
+ <button id="logoutBtn">鐧诲嚭</button>
+</div>
+<div class="container">
+ <div class="filter-section">
+ <div class="filter-group">
+ <label for="deviceId">灏忚溅缂栧彿</label>
+ <input type="text" id="deviceId" placeholder="璇疯緭鍏ュ皬杞︾紪鍙�">
+ </div>
+ <div class="filter-group">
+ <label for="messageType">娑堟伅绫诲瀷</label>
+ <select id="messageType">
+ <option value="">鍏ㄩ儴</option>
+ <!-- 浠庡悗鍙版帴鍙h幏鍙� -->
+ </select>
+ </div>
+ <div class="filter-group">
+ <label for="tag">鏍囩</label>
+ <select id="tag">
+ <option value="">鍏ㄩ儴</option>
+ <!-- 浠庡悗鍙版帴鍙h幏鍙� -->
+ </select>
+ </div>
+ <div class="filter-group">
+ <label for="startTime">寮�濮嬫椂闂�</label>
+ <input type="datetime-local" id="startTime" step="1">
+ </div>
+ <div class="filter-group">
+ <label for="endTime">缁撴潫鏃堕棿</label>
+ <input type="datetime-local" id="endTime" step="1">
+ </div>
+ <div class="filter-actions">
+ <button class="btn btn-primary" id="searchBtn">鏌ヨ</button>
+ <button class="btn btn-secondary" id="resetBtn">閲嶇疆</button>
+ <button class="btn btn-secondary" id="refreshBtn">鍒锋柊</button>
+ </div>
</div>
- <div class="container">
- <button class="refresh-btn" id="refreshBtn">鍒锋柊鏁版嵁</button>
- <div id="loading" class="loading">鍔犺浇涓�...</div>
- <div id="error" class="error" style="display: none;"></div>
- <table id="logTable" style="display: none;">
- <thead>
- <tr>
- <th>鏃堕棿</th>
- <th>璁惧ID</th>
- <th>娑堟伅绫诲瀷</th>
- <th>娑堟伅鍐呭</th>
- </tr>
- </thead>
- <tbody id="logTableBody">
- </tbody>
- </table>
+
+ <div class="log-section">
+ <div class="log-header">
+ <h2>鏃ュ織璁板綍</h2>
+ <span id="recordCount">鍏� 0 鏉¤褰�</span>
+ </div>
+ <div class="log-content">
+ <div id="loading" class="loading">鍔犺浇涓�...</div>
+ <div id="error" class="error" style="display: none;"></div>
+ <div id="empty" class="empty" style="display: none;">鏆傛棤绗﹀悎鏉′欢鐨勬棩蹇楄褰�</div>
+ <table id="logTable" style="display: none;">
+ <thead>
+ <tr>
+ <th>鏃堕棿</th>
+ <th>璁惧ID</th>
+ <th>娑堟伅绫诲瀷</th>
+ <th>鏍囩</th>
+ <th>娑堟伅鍐呭</th>
+ <th>鎿嶄綔</th>
+ </tr>
+ </thead>
+ <tbody id="logTableBody">
+ </tbody>
+ </table>
+ </div>
</div>
- <script>
- // 妫�鏌ョ櫥褰曠姸鎬�
- function checkLogin() {
- if (!localStorage.getItem('loggedIn')) {
- window.location.href = '/login';
- }
- }
-
- // 鐧诲嚭鍔熻兘
- document.getElementById('logoutBtn').addEventListener('click', function() {
- localStorage.removeItem('loggedIn');
+</div>
+<script>
+ // 妫�鏌ョ櫥褰曠姸鎬�
+ function checkLogin() {
+ if (!localStorage.getItem('loggedIn')) {
window.location.href = '/login';
- });
-
- // 鍔犺浇鏃ュ織鏁版嵁
- function loadLogData() {
- document.getElementById('loading').style.display = 'block';
- document.getElementById('error').style.display = 'none';
- document.getElementById('logTable').style.display = 'none';
-
- fetch('/deviceLog/query')
- .then(response => response.json())
- .then(data => {
- document.getElementById('loading').style.display = 'none';
- if (data && data.length > 0) {
+ }
+ }
+
+ // 鐧诲嚭鍔熻兘
+ document.getElementById('logoutBtn').addEventListener('click', function () {
+ localStorage.removeItem('loggedIn');
+ window.location.href = '/login';
+ });
+
+ // 閲嶇疆绛涢�夋潯浠�
+ document.getElementById('resetBtn').addEventListener('click', function () {
+ document.getElementById('deviceId').value = '';
+ document.getElementById('messageType').value = '';
+ document.getElementById('tag').value = '';
+ document.getElementById('startTime').value = '';
+ document.getElementById('endTime').value = '';
+ loadLogData();
+ });
+
+ // 鏌ヨ鎸夐挳鐐瑰嚮浜嬩欢
+ document.getElementById('searchBtn').addEventListener('click', loadLogData);
+
+ // 鍒锋柊鎸夐挳鐐瑰嚮浜嬩欢
+ document.getElementById('refreshBtn').addEventListener('click', loadLogData);
+
+ // 鍔犺浇鏃ュ織鏁版嵁
+ function loadLogData() {
+ document.getElementById('loading').style.display = 'block';
+ document.getElementById('error').style.display = 'none';
+ document.getElementById('empty').style.display = 'none';
+ document.getElementById('logTable').style.display = 'none';
+
+ // 鑾峰彇绛涢�夋潯浠�
+ const deviceId = document.getElementById('deviceId').value;
+ const messageType = document.getElementById('messageType').value;
+ const tag = document.getElementById('tag').value;
+ const startTime = document.getElementById('startTime').value;
+ const endTime = document.getElementById('endTime').value;
+
+ // 鏋勫缓鏌ヨ鍙傛暟
+ const params = new URLSearchParams();
+ if (deviceId) params.append('deviceId', deviceId);
+ if (messageType) params.append('type', messageType);
+ if (tag) params.append('event', tag);
+ if (startTime) {
+ // 浣跨敤鍘熷鐨勬棩鏈熸牸寮�
+ params.append('startTime', startTime+"Z");
+ }
+ if (endTime) {
+ // 浣跨敤鍘熷鐨勬棩鏈熸牸寮�
+ params.append('endTime', endTime+"Z");
+ }
+
+ // 璋冪敤鏌ヨ鎺ュ彛
+ fetch(`/deviceLog/query?${params.toString()}`)
+ .then(response => response.json())
+ .then(data => {
+ document.getElementById('loading').style.display = 'none';
+
+ // 妫�鏌ユ帴鍙h繑鍥炴牸寮�
+ if (data && data.code === 200 && data.data) {
+ // 搴旂敤绛涢�夋潯浠讹紙濡傛灉鍚庡彴娌℃湁澶勭悊绛涢�夛級
+ let filteredData = data.data;
+ if (deviceId) {
+ // 妯$硦鍖归厤璁惧ID
+ filteredData = filteredData.filter(item => {
+ return item.deviceId && item.deviceId.includes(deviceId);
+ });
+ }
+ if (messageType) {
+ // 鐩存帴浣跨敤messageType杩涜鍖归厤
+ filteredData = filteredData.filter(item => {
+ return item.type && item.type === messageType;
+ });
+ }
+ if (tag) {
+ // 浣跨敤event瀛楁浣滀负鏍囩杩涜绛涢��
+ filteredData = filteredData.filter(item => {
+ return item.event && item.event === tag;
+ });
+ }
+ if (startTime) {
+ const start = new Date(startTime).getTime();
+ filteredData = filteredData.filter(item => {
+ const itemTime = new Date(item.timestamp).getTime();
+ return itemTime >= start;
+ });
+ }
+ if (endTime) {
+ const end = new Date(endTime).getTime();
+ filteredData = filteredData.filter(item => {
+ const itemTime = new Date(item.timestamp).getTime();
+ return itemTime <= end;
+ });
+ }
+
+ // 鏇存柊璁板綍鏁�
+ document.getElementById('recordCount').textContent = `鍏� ${filteredData.length} 鏉¤褰昤;
+
+ if (filteredData && filteredData.length > 0) {
document.getElementById('logTable').style.display = 'table';
const tbody = document.getElementById('logTableBody');
tbody.innerHTML = '';
-
- data.forEach(item => {
- const row = document.createElement('tr');
- row.innerHTML = `
- <td>${item.time || '-'}</td>
- <td>${item.deviceId || '-'}</td>
- <td>${item.messageType || '-'}</td>
- <td>${item.messageContent || '-'}</td>
- `;
- tbody.appendChild(row);
- });
+
+ filteredData.forEach(item => {
+ const row = document.createElement('tr');
+ const statusClass = item.type === 'up' ? 'status-up' : 'status-down';
+ const statusText = item.type === 'up' ? '涓婅' : '涓嬭';
+ const sourceHexStr = item.sourceHexStr || '';
+
+ // 鏍煎紡鍖栨椂闂存埑
+ let formattedTime = '-';
+ if (item.timestamp) {
+ try {
+ const date = new Date(item.timestamp/1000/1000);
+ formattedTime = date.toLocaleString('zh-CN', {
+ year: 'numeric',
+ month: '2-digit',
+ day: '2-digit',
+ hour: '2-digit',
+ minute: '2-digit',
+ second: '2-digit'
+ });
+ } catch (e) {
+ formattedTime = item.timestamp;
+ }
+ }
+
+ row.innerHTML = `
+ <td>${formattedTime}</td>
+ <td>${item.deviceId || '-'}</td>
+ <td><span class="status-badge ${statusClass}">${statusText}</span></td>
+ <td>${item.event || '-'}</td>
+ <td>${sourceHexStr}</td>
+ <td>
+ <button class="btn btn-secondary parse-btn" data-hex="${sourceHexStr}">瑙f瀽</button>
+ </td>
+ `;
+ tbody.appendChild(row);
+ });
+
+ // 涓鸿В鏋愭寜閽坊鍔犵偣鍑讳簨浠�
+ document.querySelectorAll('.parse-btn').forEach(btn => {
+ btn.addEventListener('click', function() {
+ const hexData = this.getAttribute('data-hex');
+ console.log('瑙f瀽鎸夐挳鐐瑰嚮锛宧exData:', hexData);
+ if (hexData) {
+ parseHexData(hexData);
+ } else {
+ alert('娌℃湁鍙В鏋愮殑娑堟伅鍐呭');
+ }
+ });
+ });
} else {
- document.getElementById('error').textContent = '鏆傛棤鏃ュ織鏁版嵁';
- document.getElementById('error').style.display = 'block';
+ document.getElementById('empty').style.display = 'block';
}
- })
- .catch(error => {
- document.getElementById('loading').style.display = 'none';
- document.getElementById('error').textContent = '鍔犺浇鏁版嵁澶辫触: ' + error.message;
+ } else {
+ document.getElementById('error').textContent = '鍔犺浇鏁版嵁澶辫触: ' + (data.message || '鏈煡閿欒');
document.getElementById('error').style.display = 'block';
- });
+ }
+ })
+ .catch(error => {
+ document.getElementById('loading').style.display = 'none';
+ document.getElementById('error').textContent = '鍔犺浇鏁版嵁澶辫触: ' + error.message;
+ document.getElementById('error').style.display = 'block';
+ });
+ }
+
+ // 浠庡悗鍙版帴鍙h幏鍙栨秷鎭被鍨嬪拰鏍囩
+ function loadFilterOptions() {
+ // 鑾峰彇娑堟伅绫诲瀷
+ fetch('/deviceLog/queryType')
+ .then(response => response.json())
+ .then(data => {
+ if (data && data.code === 200 && data.data) {
+ const messageTypeSelect = document.getElementById('messageType');
+ messageTypeSelect.innerHTML = '<option value="">鍏ㄩ儴</option>';
+ data.data.forEach(type => {
+ const option = document.createElement('option');
+ option.value = type.label;
+ option.textContent = type.value;
+ messageTypeSelect.appendChild(option);
+ });
+ // 娑堟伅绫诲瀷鍙樺寲鏃讹紝閲嶆柊鍔犺浇鏍囩
+ messageTypeSelect.addEventListener('change', function () {
+ loadTags(this.value);
+ });
+ // 鍒濆鍔犺浇鏍囩
+ loadTags('');
+ }
+ })
+ .catch(error => {
+ console.error('鍔犺浇娑堟伅绫诲瀷澶辫触:', error);
+ });
+ }
+
+ // 鍔犺浇鏍囩
+ function loadTags(directionType) {
+ let url = '/deviceLog/queryEvent';
+ if (directionType) {
+ url += '?directionType=' + directionType;
+ }
+ fetch(url)
+ .then(response => response.json())
+ .then(data => {
+ if (data && data.code === 200 && data.data) {
+ const tagSelect = document.getElementById('tag');
+ tagSelect.innerHTML = '<option value="">鍏ㄩ儴</option>';
+ data.data.forEach(tag => {
+ const option = document.createElement('option');
+ option.value = tag.value;
+ option.textContent = tag.label;
+ tagSelect.appendChild(option);
+ });
+ }
+ })
+ .catch(error => {
+ console.error('鍔犺浇鏍囩澶辫触:', error);
+ });
+ }
+
+ // 瑙f瀽鍗佸叚杩涘埗鏁版嵁
+ function parseHexData(hexData) {
+ console.log('寮�濮嬭В鏋愶紝hexData:', hexData);
+
+ // 鍏堝叧闂箣鍓嶇殑鍔犺浇鐘舵�佸拰缁撴灉寮圭獥
+ const oldLoading = document.getElementById('parseLoading');
+ if (oldLoading) {
+ oldLoading.remove();
}
- // 鍒锋柊鎸夐挳鐐瑰嚮浜嬩欢
- document.getElementById('refreshBtn').addEventListener('click', loadLogData);
+ const oldResult = document.querySelector('[id^="resultDiv"]');
+ if (oldResult) {
+ oldResult.remove();
+ }
+
+ // 鏄剧ず鍔犺浇鐘舵��
+ const loadingDiv = document.createElement('div');
+ loadingDiv.className = 'loading';
+ loadingDiv.textContent = '瑙f瀽涓�...';
+ loadingDiv.style.position = 'fixed';
+ loadingDiv.style.top = '50%';
+ loadingDiv.style.left = '50%';
+ loadingDiv.style.transform = 'translate(-50%, -50%)';
+ loadingDiv.style.backgroundColor = 'rgba(255, 255, 255, 0.9)';
+ loadingDiv.style.padding = '20px';
+ loadingDiv.style.borderRadius = '8px';
+ loadingDiv.style.boxShadow = '0 2px 10px rgba(0,0,0,0.1)';
+ loadingDiv.id = 'parseLoading';
+ document.body.appendChild(loadingDiv);
+
+ // 鏋勫缓璇锋眰URL
+const url = `/proxy/decode?hexData=${encodeURIComponent(hexData)}`;
+console.log('璇锋眰URL:', url);
+
+ // 璋冪敤瑙f瀽鎺ュ彛
+ fetch(url, {
+ method: 'GET',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Accept': 'application/json'
+ },
+ mode: 'cors' // 鍏佽璺ㄥ煙璇锋眰
+ })
+ .then(response => {
+ console.log('鍝嶅簲鐘舵��:', response.status);
+ console.log('鍝嶅簲澶�:', response.headers);
+ if (!response.ok) {
+ throw new Error(`瑙f瀽澶辫触锛岀姸鎬佺爜: ${response.status}`);
+ }
+ return response.json();
+ })
+ .then(data => {
+ console.log('瑙f瀽缁撴灉:', data);
+ // 绉婚櫎鍔犺浇鐘舵��
+ document.getElementById('parseLoading').remove();
+
+ // 鏄剧ず瑙f瀽缁撴灉
+ const resultDiv = document.createElement('div');
+ resultDiv.id = 'resultDiv_' + Date.now(); // 娣诲姞鍞竴ID
+ resultDiv.style.position = 'fixed';
+ resultDiv.style.top = '50%';
+ resultDiv.style.left = '50%';
+ resultDiv.style.transform = 'translate(-50%, -50%)';
+ resultDiv.style.backgroundColor = 'white';
+ resultDiv.style.padding = '20px';
+ resultDiv.style.borderRadius = '8px';
+ resultDiv.style.boxShadow = '0 2px 20px rgba(0,0,0,0.2)';
+ resultDiv.style.maxWidth = '80%';
+ resultDiv.style.maxHeight = '80%';
+ resultDiv.style.overflow = 'auto';
+ resultDiv.style.zIndex = '1000';
+
+ // 鏋勫缓缁撴灉HTML
+ let resultHTML = '<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px;">';
+ resultHTML += '<h3 style="margin: 0;">瑙f瀽缁撴灉</h3>';
+ resultHTML += '<button id="closeResult" style="padding: 5px 10px; background-color: #f0f0f0; color: #333; border: none; border-radius: 4px; cursor: pointer;">脳</button>';
+ resultHTML += '</div>';
+ resultHTML += '<pre style="background-color: #f5f5f5; padding: 10px; border-radius: 4px; font-family: monospace; margin: 0;">';
+ resultHTML += JSON.stringify(data, null, 2);
+ resultHTML += '</pre>';
+
+ resultDiv.innerHTML = resultHTML;
+ document.body.appendChild(resultDiv);
+
+ // 鍏抽棴鎸夐挳鐐瑰嚮浜嬩欢
+ document.getElementById('closeResult').addEventListener('click', function() {
+ resultDiv.remove();
+ });
+ })
+ .catch(error => {
+ console.error('瑙f瀽閿欒:', error);
+ // 绉婚櫎鍔犺浇鐘舵��
+ document.getElementById('parseLoading').remove();
+
+ // 鏄剧ず璇︾粏鐨勯敊璇俊鎭�
+ let errorMessage = '瑙f瀽澶辫触: ' + error.message;
+ if (error.message.includes('Failed to fetch')) {
+ errorMessage += '\n鍙兘鐨勫師鍥狅細\n1. 瑙f瀽鏈嶅姟鏈惎鍔╘n2. 璺ㄥ煙闂\n3. 缃戠粶杩炴帴闂';
+ }
+ alert(errorMessage);
+ });
+ }
// 椤甸潰鍔犺浇鏃舵鏌ョ櫥褰曠姸鎬佸苟鍔犺浇鏁版嵁
checkLogin();
+ loadFilterOptions();
loadLogData();
- </script>
+</script>
</body>
</html>
\ No newline at end of file
diff --git a/zy-acs-hex/src/main/webapp/views/login.html b/zy-acs-hex/src/main/webapp/views/login.html
index f39e7b1..707fb4c 100644
--- a/zy-acs-hex/src/main/webapp/views/login.html
+++ b/zy-acs-hex/src/main/webapp/views/login.html
@@ -10,95 +10,153 @@
padding: 0;
box-sizing: border-box;
}
+
body {
- font-family: Arial, sans-serif;
- background-color: #f0f2f5;
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
+
.login-container {
background-color: white;
padding: 40px;
- border-radius: 8px;
- box-shadow: 0 2px 10px rgba(0,0,0,0.1);
+ border-radius: 12px;
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
width: 100%;
max-width: 400px;
+ animation: fadeIn 0.5s ease-in-out;
}
+
+ @keyframes fadeIn {
+ from {
+ opacity: 0;
+ transform: translateY(20px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+ }
+
h2 {
text-align: center;
margin-bottom: 30px;
color: #333;
+ font-size: 24px;
+ font-weight: 600;
}
+
.form-group {
margin-bottom: 20px;
}
+
label {
display: block;
margin-bottom: 8px;
color: #666;
+ font-weight: 500;
}
+
input {
width: 100%;
- padding: 10px;
- border: 1px solid #ddd;
- border-radius: 4px;
+ padding: 12px 16px;
+ border: 2px solid #e0e0e0;
+ border-radius: 8px;
font-size: 16px;
+ transition: border-color 0.3s ease;
}
+
+ input:focus {
+ outline: none;
+ border-color: #667eea;
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
+ }
+
button {
width: 100%;
- padding: 12px;
- background-color: #1890ff;
+ padding: 14px;
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
- border-radius: 4px;
+ border-radius: 8px;
font-size: 16px;
+ font-weight: 600;
cursor: pointer;
margin-top: 10px;
+ transition: transform 0.2s ease;
}
+
button:hover {
- background-color: #40a9ff;
+ transform: translateY(-2px);
}
+
+ button:active {
+ transform: translateY(0);
+ }
+
.error-message {
- color: red;
- margin-top: 10px;
+ color: #ff4d4f;
+ margin-top: 12px;
text-align: center;
+ font-size: 14px;
}
</style>
</head>
<body>
- <div class="login-container">
- <h2>鏈哄櫒浜烘棩蹇楃郴缁�</h2>
- <form id="loginForm">
- <div class="form-group">
- <label for="username">鐢ㄦ埛鍚�</label>
- <input type="text" id="username" name="username" required>
- </div>
- <div class="form-group">
- <label for="password">瀵嗙爜</label>
- <input type="password" id="password" name="password" required>
- </div>
- <button type="submit">鐧诲綍</button>
- <div id="errorMessage" class="error-message"></div>
- </form>
- </div>
- <script>
- document.getElementById('loginForm').addEventListener('submit', function(e) {
- e.preventDefault();
- const username = document.getElementById('username').value;
- const password = document.getElementById('password').value;
-
- // 绠�鍗曠殑鐧诲綍楠岃瘉锛堝疄闄呴」鐩腑搴旇璋冪敤鍚庣API锛�
- if (username === 'admin' && password === 'admin123') {
- // 瀛樺偍鐧诲綍鐘舵��
- localStorage.setItem('loggedIn', 'true');
- // 璺宠浆鍒颁富椤甸潰
- window.location.href = '/';
- } else {
- document.getElementById('errorMessage').textContent = '鐢ㄦ埛鍚嶆垨瀵嗙爜閿欒';
- }
- });
- </script>
+<div class="login-container">
+ <h2>鏈哄櫒浜烘棩蹇楃郴缁�</h2>
+ <form id="loginForm">
+ <div class="form-group">
+ <label for="username">鐢ㄦ埛鍚�</label>
+ <input type="text" id="username" name="username" required>
+ </div>
+ <div class="form-group">
+ <label for="password">瀵嗙爜</label>
+ <input type="password" id="password" name="password" required>
+ </div>
+ <button type="submit">鐧诲綍</button>
+ <div id="errorMessage" class="error-message"></div>
+ </form>
+</div>
+<script>
+ document.getElementById('loginForm').addEventListener('submit', function (e) {
+ e.preventDefault();
+ const username = document.getElementById('username').value;
+ const password = document.getElementById('password').value;
+ const errorMessage = document.getElementById('errorMessage');
+
+ // 鏄剧ず鍔犺浇鐘舵��
+ errorMessage.textContent = '鐧诲綍涓�...';
+
+ // 璋冪敤鍚庣鐧诲綍鎺ュ彛
+ fetch('/login/auth', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded'
+ },
+ body: `user=${encodeURIComponent(username)}&pass=${encodeURIComponent(password)}`
+ })
+ .then(response => response.json())
+ .then(data => {
+ if (data && data.code === 200) {
+ // 鐧诲綍鎴愬姛锛屽瓨鍌ㄧ櫥褰曠姸鎬�
+ localStorage.setItem('loggedIn', 'true');
+ // 璺宠浆鍒颁富椤甸潰
+ window.location.href = '/';
+ } else {
+ // 鐧诲綍澶辫触锛屾樉绀洪敊璇俊鎭�
+ errorMessage.textContent = data.message || '鐢ㄦ埛鍚嶆垨瀵嗙爜閿欒';
+ }
+ })
+ .catch(error => {
+ // 缃戠粶閿欒锛屾樉绀洪敊璇俊鎭�
+ errorMessage.textContent = '鐧诲綍澶辫触锛岃绋嶅悗閲嶈瘯';
+ console.error('鐧诲綍澶辫触:', error);
+ });
+ });
+</script>
</body>
</html>
\ No newline at end of file
--
Gitblit v1.9.1