| zy-acs-cv/src/main/webapp/views/pipeline.html | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| zy-acs-hex/dashboard.html | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| zy-acs-hex/pom.xml | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| zy-acs-hex/src/main/java/com/zy/acs/hex/HexApplication.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| zy-acs-hex/src/main/java/com/zy/acs/hex/config/WebMvcConfig.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| zy-acs-hex/src/main/java/com/zy/acs/hex/controller/DeviceLogController.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| zy-acs-hex/src/main/java/com/zy/acs/hex/controller/RouterController.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| zy-acs-hex/src/main/resources/application.yml | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| zy-acs-hex/src/main/webapp/views/dashboard.html | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| zy-acs-hex/src/main/webapp/views/index.html | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| zy-acs-hex/src/main/webapp/views/login.html | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
zy-acs-cv/src/main/webapp/views/pipeline.html
@@ -841,6 +841,7 @@ </div> <div class="modal-footer"> <button type="button" class="btn btn-primary" id="save">保存</button> <button type="button" class="btn btn-secondary" id="clear">清除</button> <button type="button" class="btn btn-secondary" id="cancel">取消</button> </div> </div> @@ -1090,6 +1091,12 @@ }); }); // 清除任务号和目标站点 $(document).on('click', '#clear', function () { $('#workNo').val(0); $('#staNo').val(0); }); // 取消站点信息修改 $(document).on('click', '#cancel', function () { closeModal(); zy-acs-hex/dashboard.html
New file @@ -0,0 +1,392 @@ <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>机器人数据监控</title> <!-- 引入 layui-vue 样式 --> <link href="https://cdn.jsdelivr.net/npm/layui-vue@2.1.0/dist/index.css" rel="stylesheet"> <!-- 引入 Vue 3 --> <script src="https://cdn.jsdelivr.net/npm/vue@3.3.4/dist/vue.global.min.js"></script> <!-- 引入 layui-vue --> <script src="https://cdn.jsdelivr.net/npm/layui-vue@2.1.0/dist/index.js"></script> <!-- 引入 Chart.js --> <script src="/static/js/chart.umd.min.js"></script> <!-- 引入 Font Awesome --> <link href="/static/css/font-awesome.min.css" rel="stylesheet"> <style> body { 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; } </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> <!-- 主内容区 --> <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> </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> </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 </div> </lay-footer> </div> <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 currentPage = ref(1); const pageSize = ref(10); const total = ref(24); // 实时数据 const realtimeData = ref([]); // 图表引用 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 } }, 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> </body> </html> zy-acs-hex/pom.xml
@@ -10,6 +10,8 @@ </parent> <artifactId>zy-acs-hex</artifactId> <version>1.0.0</version> <packaging>war</packaging> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> zy-acs-hex/src/main/java/com/zy/acs/hex/HexApplication.java
@@ -3,16 +3,19 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; import org.springframework.context.annotation.ComponentScan; import org.springframework.scheduling.annotation.EnableScheduling; @EnableScheduling @ComponentScan(basePackages = {"com.zy.component", "com.zy.acs"}) @SpringBootApplication public class HexApplication { public class HexApplication extends SpringBootServletInitializer { public static void main(String[] args) { SpringApplication.run(HexApplication.class, args); } } zy-acs-hex/src/main/java/com/zy/acs/hex/config/WebMvcConfig.java
New file @@ -0,0 +1,69 @@ package com.zy.acs.hex.config; 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; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * WebMvc配置, 拦截器、资源映射等都在此配置 * * @author vincent * @since 2019-06-12 10:11:16 */ @Configuration public class WebMvcConfig implements WebMvcConfigurer { /** * token通过header传递的名称 */ public static final String TOKEN_HEADER_NAME = "Authorization"; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(getAsyncHandlerInterceptor()) .addPathPatterns("/**") ; } @Bean public AsyncHandlerInterceptor getAsyncHandlerInterceptor() { return new AsyncHandlerInterceptor(){ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { cors(response); return true; } }; } @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { // 配置静态资源处理器 registry.addResourceHandler("/static/**") .addResourceLocations("/static/"); // 配置视图文件处理器 registry.addResourceHandler("/views/**") .addResourceLocations("/views/"); } public static void cors(HttpServletResponse response){ // 跨域设置 response.setHeader("Access-Control-Max-Age", "3600"); response.setHeader("Access-Control-Allow-Origin", "*"); response.setHeader("Access-Control-Allow-Methods", "*"); response.setHeader("Access-Control-Allow-Headers", "*"); response.setHeader("Access-Control-Expose-Headers", TOKEN_HEADER_NAME); } } zy-acs-hex/src/main/java/com/zy/acs/hex/controller/DeviceLogController.java
New file @@ -0,0 +1,37 @@ package com.zy.acs.hex.controller; import com.zy.acs.common.domain.mq.DeviceMessage; import com.zy.acs.framework.common.R; 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 java.util.List; @RestController @Slf4j @RequestMapping(value = "/deviceLog") public class DeviceLogController { @Autowired private InfluxDBService influxDBService; /** * 查询最新的十条数据 * * @return */ @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); } } zy-acs-hex/src/main/java/com/zy/acs/hex/controller/RouterController.java
New file @@ -0,0 +1,37 @@ 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; import javax.servlet.http.HttpServletResponse; /** * Created by vincent on 2019-07-30 */ @Controller public class RouterController { @RequestMapping("/") public void index(HttpServletResponse response) { try { response.sendRedirect( "/views/index.html"); } catch (Exception ex) { ex.printStackTrace(); } } @RequestMapping("/login") public void login(HttpServletResponse response) { try { response.sendRedirect( "/views/login.html"); } catch (Exception ex) { ex.printStackTrace(); } } } zy-acs-hex/src/main/resources/application.yml
@@ -5,7 +5,7 @@ name: rcs-hex # RabbitMQ配置 rabbitmq: host: 192.168.133.173 host: localhost port: 5672 username: root password: xltys1995 @@ -26,9 +26,10 @@ # --add-opens java.base/java.nio=ALL-UNNAMED influxdb3: enabled: true url: http://192.168.133.173:8181 #token: apiv3_Jx1SvmBMV_kikGhc4eZJQbeGmNYN7KX1GdpoR9MClkKzMxSJ0MPKM_O2Xt3o1hVyRikMmlxZ_h9zfy6ybC5Idg url: http://localhost:8181 token: apiv3_Jx1SvmBMV_kikGhc4eZJQbeGmNYN7KX1GdpoR9MClkKzMxSJ0MPKM_O2Xt3o1hVyRikMmlxZ_h9zfy6ybC5Idg database: rcs token: apiv3_116RKycNhxbf62Nys4zthC05aRD-aidzhEpEpLtsFuedhJTaCtVklNrzHs9LHxBWMuzDclBHVgToGoQuWGiIIA # 虚拟机的token #token: apiv3_116RKycNhxbf62Nys4zthC05aRD-aidzhEpEpLtsFuedhJTaCtVklNrzHs9LHxBWMuzDclBHVgToGoQuWGiIIA retention-period: 30d createDatabaseUrl: ${influxdb3.url}/api/v3/configure/database zy-acs-hex/src/main/webapp/views/dashboard.html
File was deleted zy-acs-hex/src/main/webapp/views/index.html
New file @@ -0,0 +1,165 @@ <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>机器人日志系统</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: Arial, sans-serif; background-color: #f0f2f5; } .header { background-color: #1890ff; color: white; padding: 15px 20px; display: flex; justify-content: space-between; align-items: center; } .header h1 { font-size: 20px; } .header button { background-color: transparent; color: white; border: 1px solid white; padding: 5px 15px; border-radius: 4px; cursor: pointer; } .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; text-align: left; border-bottom: 1px solid #ddd; } th { background-color: #f5f5f5; font-weight: bold; } tr:hover { background-color: #f5f5f5; } .loading { text-align: center; padding: 20px; color: #666; } .error { text-align: center; padding: 20px; color: red; } </style> </head> <body> <div class="header"> <h1>机器人上下行日志</h1> <button id="logoutBtn">登出</button> </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> <script> // 检查登录状态 function checkLogin() { if (!localStorage.getItem('loggedIn')) { window.location.href = '/login'; } } // 登出功能 document.getElementById('logoutBtn').addEventListener('click', function() { localStorage.removeItem('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('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); }); } else { document.getElementById('error').textContent = '暂无日志数据'; 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'; }); } // 刷新按钮点击事件 document.getElementById('refreshBtn').addEventListener('click', loadLogData); // 页面加载时检查登录状态并加载数据 checkLogin(); loadLogData(); </script> </body> </html> zy-acs-hex/src/main/webapp/views/login.html
New file @@ -0,0 +1,104 @@ <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>登录 - 机器人日志系统</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: Arial, sans-serif; background-color: #f0f2f5; 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); width: 100%; max-width: 400px; } h2 { text-align: center; margin-bottom: 30px; color: #333; } .form-group { margin-bottom: 20px; } label { display: block; margin-bottom: 8px; color: #666; } input { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 16px; } button { width: 100%; padding: 12px; background-color: #1890ff; color: white; border: none; border-radius: 4px; font-size: 16px; cursor: pointer; margin-top: 10px; } button:hover { background-color: #40a9ff; } .error-message { color: red; margin-top: 10px; text-align: center; } </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> </body> </html>