From 48c1de18235020edff108339ed1d12bade8a2b90 Mon Sep 17 00:00:00 2001
From: Junjie <DELL@qq.com>
Date: 星期一, 08 十二月 2025 16:37:02 +0800
Subject: [PATCH] #
---
src/main/java/com/zy/asrs/controller/DeviceLogController.java | 338 ++++++++++++++++++++++++++++
src/main/webapp/static/js/deviceLogs/deviceLogs.js | 228 +++++++++++++++++++
src/main/webapp/views/deviceLogs/deviceLogs.html | 96 ++++++++
3 files changed, 662 insertions(+), 0 deletions(-)
diff --git a/src/main/java/com/zy/asrs/controller/DeviceLogController.java b/src/main/java/com/zy/asrs/controller/DeviceLogController.java
new file mode 100644
index 0000000..d5537e9
--- /dev/null
+++ b/src/main/java/com/zy/asrs/controller/DeviceLogController.java
@@ -0,0 +1,338 @@
+package com.zy.asrs.controller;
+
+import com.core.annotations.ManagerAuth;
+import com.core.common.Cools;
+import com.core.common.R;
+import com.zy.common.web.BaseController;
+import com.zy.core.enums.SlaveType;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletResponse;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+
+@RestController
+public class DeviceLogController extends BaseController {
+
+ @Value("${deviceLogStorage.loggingPath}")
+ private String loggingPath;
+
+ private static class ProgressInfo {
+ long totalRaw;
+ long processedRaw;
+ int totalCount;
+ int processedCount;
+ boolean finished;
+ }
+
+ private static final Map<String, ProgressInfo> DOWNLOAD_PROGRESS = new ConcurrentHashMap<>();
+
+ @RequestMapping(value = "/deviceLog/dates/auth")
+ @ManagerAuth
+ public R dates() {
+ try {
+ Path baseDir = Paths.get(loggingPath);
+ if (!Files.exists(baseDir)) {
+ return R.ok(new ArrayList<>());
+ }
+ List<String> days = Files.list(baseDir)
+ .filter(Files::isDirectory)
+ .map(p -> p.getFileName().toString())
+ .filter(name -> name.length() == 8 && name.chars().allMatch(Character::isDigit))
+ .sorted()
+ .collect(Collectors.toList());
+ Map<String, Map<String, List<String>>> grouped = new LinkedHashMap<>();
+ for (String day : days) {
+ String year = day.substring(0, 4);
+ String month = day.substring(4, 6);
+ grouped.computeIfAbsent(year, k -> new LinkedHashMap<>())
+ .computeIfAbsent(month, k -> new ArrayList<>())
+ .add(day);
+ }
+ List<Map<String, Object>> tree = new ArrayList<>();
+ for (Map.Entry<String, Map<String, List<String>>> yEntry : grouped.entrySet()) {
+ Map<String, Object> yNode = new HashMap<>();
+ yNode.put("title", yEntry.getKey());
+ yNode.put("id", yEntry.getKey());
+ List<Map<String, Object>> mChildren = new ArrayList<>();
+ for (Map.Entry<String, List<String>> mEntry : yEntry.getValue().entrySet()) {
+ Map<String, Object> mNode = new HashMap<>();
+ mNode.put("title", mEntry.getKey());
+ mNode.put("id", yEntry.getKey() + "-" + mEntry.getKey());
+ List<Map<String, Object>> dChildren = new ArrayList<>();
+ for (String d : mEntry.getValue()) {
+ Map<String, Object> dNode = new HashMap<>();
+ dNode.put("title", d.substring(6, 8));
+ dNode.put("id", d);
+ dNode.put("day", d);
+ dChildren.add(dNode);
+ }
+ mNode.put("children", dChildren);
+ mChildren.add(mNode);
+ }
+ yNode.put("children", mChildren);
+ tree.add(yNode);
+ }
+ return R.ok(tree);
+ } catch (Exception e) {
+ return R.error("璇诲彇鏃ユ湡澶辫触");
+ }
+ }
+
+ @RequestMapping(value = "/deviceLog/day/{day}/devices/auth")
+ @ManagerAuth
+ public R devices(@PathVariable("day") String day) {
+ try {
+ if (day == null || day.length() != 8 || !day.chars().allMatch(Character::isDigit)) {
+ return R.error("鏃ユ湡鏍煎紡閿欒");
+ }
+ Path dayDir = Paths.get(loggingPath, day);
+ if (!Files.exists(dayDir) || !Files.isDirectory(dayDir)) {
+ return R.ok(new ArrayList<>());
+ }
+ List<Path> files = Files.list(dayDir)
+ .filter(p -> !Files.isDirectory(p) && p.getFileName().toString().endsWith(".log"))
+ .collect(Collectors.toList());
+ Map<String, Map<String, Object>> deviceMap = new HashMap<>();
+ for (Path p : files) {
+ String name = p.getFileName().toString();
+ String[] parts = name.split("_");
+ if (parts.length < 4) {
+ continue;
+ }
+ String deviceNo = parts[1];
+ String type = parts[0];
+ Map<String, Object> info = deviceMap.computeIfAbsent(deviceNo, k -> {
+ Map<String, Object> map = new HashMap<>();
+ map.put("deviceNo", deviceNo);
+ map.put("types", new HashSet<String>());
+ map.put("fileCount", 0);
+ return map;
+ });
+ ((Set<String>) info.get("types")).add(type);
+ info.put("fileCount", ((Integer) info.get("fileCount")) + 1);
+ }
+ List<Map<String, Object>> res = deviceMap.values().stream().map(m -> {
+ Map<String, Object> x = new HashMap<>();
+ x.put("deviceNo", m.get("deviceNo"));
+ x.put("types", ((Set<String>) m.get("types")).stream().collect(Collectors.toList()));
+ x.put("fileCount", m.get("fileCount"));
+ return x;
+ }).collect(Collectors.toList());
+ return R.ok(res);
+ } catch (Exception e) {
+ return R.error("璇诲彇璁惧鍒楄〃澶辫触");
+ }
+ }
+
+ @RequestMapping(value = "/deviceLog/day/{day}/download/auth")
+ @ManagerAuth
+ public void download(@PathVariable("day") String day,
+ @RequestParam("type") String type,
+ @RequestParam("deviceNo") String deviceNo,
+ @RequestParam(value = "offset", required = false) Integer offset,
+ @RequestParam(value = "limit", required = false) Integer limit,
+ @RequestParam(value = "progressId", required = false) String progressId,
+ HttpServletResponse response) {
+ try {
+ String dayClean = day == null ? null : day.replaceAll("\\D", "");
+ if (dayClean == null || dayClean.length() != 8 || !dayClean.chars().allMatch(Character::isDigit)) {
+ response.setStatus(400);
+ return;
+ }
+ if (type == null || SlaveType.findInstance(type) == null) {
+ response.setStatus(400);
+ return;
+ }
+ if (deviceNo == null || !deviceNo.chars().allMatch(Character::isDigit)) {
+ response.setStatus(400);
+ return;
+ }
+ Path dayDir = Paths.get(loggingPath, dayClean);
+ if (!Files.exists(dayDir) || !Files.isDirectory(dayDir)) {
+ response.setStatus(404);
+ return;
+ }
+ List<Path> files = Files.list(dayDir)
+ .filter(p -> {
+ String name = p.getFileName().toString();
+ String prefix = type + "_" + deviceNo + "_" + dayClean + "_";
+ return name.endsWith(".log") && name.startsWith(prefix);
+ }).collect(Collectors.toList());
+ // 鎺掑簭锛堟寜鏂囦欢涓殑绱㈠紩鍙烽�掑锛�
+ String prefix = type + "_" + deviceNo + "_" + dayClean + "_";
+ files.sort(Comparator.comparingInt(p -> {
+ String n = p.getFileName().toString();
+ try {
+ String suf = n.substring(prefix.length(), n.length() - 4);
+ return Integer.parseInt(suf);
+ } catch (Exception e) {
+ return Integer.MAX_VALUE;
+ }
+ }));
+ int from = offset == null || offset < 0 ? 0 : offset;
+ int max = limit == null || limit <= 0 ? 200 : limit;
+ int to = Math.min(files.size(), from + max);
+ if (from >= files.size()) {
+ response.setStatus(404);
+ return;
+ }
+ files = files.subList(from, to);
+ if (files.isEmpty()) {
+ response.setStatus(404);
+ return;
+ }
+ ProgressInfo info;
+ String id = progressId;
+ if (Cools.isEmpty(id)) {
+ id = UUID.randomUUID().toString();
+ }
+ List<Path> finalFiles = files;
+ info = DOWNLOAD_PROGRESS.computeIfAbsent(id, k -> {
+ ProgressInfo x = new ProgressInfo();
+ x.totalCount = finalFiles.size();
+ long sum = 0L;
+ for (Path f : finalFiles) {
+ try { sum += Files.size(f); } catch (Exception ignored) {}
+ }
+ x.totalRaw = sum;
+ x.processedRaw = 0L;
+ x.processedCount = 0;
+ x.finished = false;
+ return x;
+ });
+ response.reset();
+ response.setContentType("application/zip");
+ String filename = type + "_" + deviceNo + "_" + dayClean + ".zip";
+ response.setHeader("Content-Disposition", "attachment; filename=" + filename);
+ long totalRawSize = 0L;
+ for (Path f : files) {
+ try { totalRawSize += Files.size(f); } catch (Exception ignored) {}
+ }
+ response.setHeader("X-Total-Size", String.valueOf(totalRawSize));
+ response.setHeader("X-File-Count", String.valueOf(files.size()));
+ response.setHeader("X-Progress-Id", id);
+ try (java.util.zip.ZipOutputStream zos = new java.util.zip.ZipOutputStream(response.getOutputStream())) {
+ for (Path f : files) {
+ java.util.zip.ZipEntry entry = new java.util.zip.ZipEntry(f.getFileName().toString());
+ zos.putNextEntry(entry);
+ Files.copy(f, zos);
+ zos.closeEntry();
+ try {
+ info.processedRaw += Files.size(f);
+ } catch (Exception ignored) {}
+ info.processedCount += 1;
+ }
+ zos.finish();
+ info.finished = true;
+ }
+ } catch (Exception e) {
+ try { response.setStatus(500); } catch (Exception ignore) {}
+ }
+ }
+
+ @RequestMapping(value = "/deviceLog/download/init/auth")
+ @ManagerAuth
+ public R init(@org.springframework.web.bind.annotation.RequestBody com.alibaba.fastjson.JSONObject param) {
+ try {
+ String day = param.getString("day");
+ String type = param.getString("type");
+ String deviceNo = param.getString("deviceNo");
+ Integer offset = param.getInteger("offset");
+ Integer limit = param.getInteger("limit");
+ String dayClean = Cools.isEmpty(day) ? null : day.replaceAll("\\D", "");
+ if (Cools.isEmpty(dayClean) || dayClean.length() != 8 || !dayClean.chars().allMatch(Character::isDigit)) {
+ return R.error("鏃ユ湡鏍煎紡閿欒");
+ }
+ if (Cools.isEmpty(type) || SlaveType.findInstance(type) == null) {
+ return R.error("璁惧绫诲瀷閿欒");
+ }
+ if (Cools.isEmpty(deviceNo) || !deviceNo.chars().allMatch(Character::isDigit)) {
+ return R.error("璁惧缂栧彿閿欒");
+ }
+ Path dayDir = Paths.get(loggingPath, dayClean);
+ if (!Files.exists(dayDir) || !Files.isDirectory(dayDir)) {
+ return R.error("褰撴棩鐩綍涓嶅瓨鍦�");
+ }
+ List<Path> files = Files.list(dayDir)
+ .filter(p -> {
+ String name = p.getFileName().toString();
+ String prefix = type + "_" + deviceNo + "_" + dayClean + "_";
+ return name.endsWith(".log") && name.startsWith(prefix);
+ }).collect(Collectors.toList());
+ String prefix = type + "_" + deviceNo + "_" + dayClean + "_";
+ files.sort(Comparator.comparingInt(p -> {
+ String n = p.getFileName().toString();
+ try {
+ String suf = n.substring(prefix.length(), n.length() - 4);
+ return Integer.parseInt(suf);
+ } catch (Exception e) {
+ return Integer.MAX_VALUE;
+ }
+ }));
+ int from = offset == null || offset < 0 ? 0 : offset;
+ int max = limit == null || limit <= 0 ? 200 : limit;
+ int to = Math.min(files.size(), from + max);
+ if (from >= files.size()) {
+ return R.error("璧峰搴忓彿瓒呭嚭鑼冨洿");
+ }
+ files = files.subList(from, to);
+ String id = UUID.randomUUID().toString();
+ ProgressInfo info = new ProgressInfo();
+ info.totalCount = files.size();
+ long sum = 0L;
+ for (Path f : files) {
+ try { sum += Files.size(f); } catch (Exception ignored) {}
+ }
+ info.totalRaw = sum;
+ info.processedRaw = 0L;
+ info.processedCount = 0;
+ info.finished = false;
+ DOWNLOAD_PROGRESS.put(id, info);
+ Map<String, Object> res = new HashMap<>();
+ res.put("progressId", id);
+ res.put("totalSize", info.totalRaw);
+ res.put("fileCount", info.totalCount);
+ return R.ok(res);
+ } catch (Exception e) {
+ return R.error("鍒濆鍖栧け璐�");
+ }
+ }
+
+ @RequestMapping(value = "/deviceLog/download/progress/auth")
+ @ManagerAuth
+ public R progress(String id) {
+ ProgressInfo info = DOWNLOAD_PROGRESS.get(id);
+ if (info == null) {
+ return R.error("鏃犳晥杩涘害");
+ }
+ long total = info.totalRaw;
+ long done = info.processedRaw;
+ int percent;
+ if (info.finished) {
+ percent = 100;
+ } else if (total > 0) {
+ percent = (int) Math.min(99, (done * 100L) / total);
+ } else if (info.totalCount > 0) {
+ percent = (int) Math.min(99, (info.processedCount * 100L) / info.totalCount);
+ } else {
+ percent = 0;
+ }
+ Map<String, Object> res = new HashMap<>();
+ res.put("percent", percent);
+ res.put("processedSize", done);
+ res.put("totalSize", total);
+ res.put("processedCount", info.processedCount);
+ res.put("totalCount", info.totalCount);
+ res.put("finished", info.finished);
+ return R.ok(res);
+ }
+}
diff --git a/src/main/webapp/static/js/deviceLogs/deviceLogs.js b/src/main/webapp/static/js/deviceLogs/deviceLogs.js
new file mode 100644
index 0000000..31246f0
--- /dev/null
+++ b/src/main/webapp/static/js/deviceLogs/deviceLogs.js
@@ -0,0 +1,228 @@
+layui.use(['tree', 'layer', 'form', 'element'], function() {
+ var tree = layui.tree;
+ var $ = layui.jquery;
+ var layer = layui.layer;
+ var form = layui.form;
+ var element = layui.element;
+
+ var currentDay = null;
+
+ function buildMonthTree(data) {
+ var monthMap = {};
+ (data || []).forEach(function (y) {
+ (y.children || []).forEach(function (m) {
+ var month = m.title;
+ var arr = monthMap[month] || (monthMap[month] = []);
+ (m.children || []).forEach(function (d) {
+ arr.push({ title: d.title, id: d.id });
+ });
+ });
+ });
+ var result = [];
+ Object.keys(monthMap).sort().forEach(function (month) {
+ result.push({ title: month + '鏈�', id: month, children: monthMap[month] });
+ });
+ return result;
+ }
+
+ function loadDateTree() {
+ $.ajax({
+ url: baseUrl + "/deviceLog/dates/auth",
+ headers: {'token': localStorage.getItem('token')},
+ method: 'GET',
+ beforeSend: function () {
+ layer.load(1, {shade: [0.1,'#fff']});
+ },
+ success: function (res) {
+ layer.closeAll('loading');
+ if (res.code === 200) {
+ var monthTree = buildMonthTree(res.data);
+ tree.render({
+ elem: '#date-tree',
+ id: 'dateTree',
+ data: monthTree,
+ click: function(obj){
+ var node = obj.data;
+ if (node.id && node.id.length === 8) {
+ currentDay = node.id;
+ $('#selected-day').val(currentDay);
+ loadDevices(currentDay);
+ }
+ }
+ });
+ } else if (res.code === 403) {
+ top.location.href = baseUrl + "/";
+ } else {
+ layer.msg(res.msg || '鍔犺浇鏃ユ湡澶辫触', {icon: 2});
+ }
+ }
+ });
+ }
+
+ function loadDevices(day) {
+ $('#device-list').html('');
+ $.ajax({
+ url: baseUrl + "/deviceLog/day/" + day + "/devices/auth",
+ headers: {'token': localStorage.getItem('token')},
+ method: 'GET',
+ beforeSend: function () {
+ layer.load(1, {shade: [0.1,'#fff']});
+ },
+ success: function (res) {
+ layer.closeAll('loading');
+ if (res.code === 200) {
+ if (!res.data || res.data.length === 0) {
+ $('#device-list').html('<div class="layui-text">褰撴棩鏈壘鍒拌澶囨棩蹇�</div>');
+ return;
+ }
+ var html = '';
+ res.data.forEach(function(item){
+ var types = item.types || [];
+ var typeBtns = '';
+ types.forEach(function(t){
+ typeBtns += '<button class="layui-btn layui-btn-xs" data-type="' + t + '" data-device-no="' + item.deviceNo + '">涓嬭浇(' + t + ')</button>';
+ });
+ html += '<div class="layui-col-xs12" style="margin-bottom:8px;">' +
+ '<div class="layui-card">' +
+ '<div class="layui-card-body">' +
+ '<span>璁惧缂栧彿锛�<b>' + item.deviceNo + '</b></span>' +
+ '<span style="margin-left:20px;">绫诲瀷锛�' + types.join(',') + '</span>' +
+ '<span style="margin-left:20px;">鏂囦欢鏁帮細' + item.fileCount + '</span>' +
+ '<span style="margin-left:20px;">' + typeBtns + '</span>' +
+ '</div>' +
+ '</div>' +
+ '</div>';
+ });
+ $('#device-list').html(html);
+ } else if (res.code === 403) {
+ top.location.href = baseUrl + "/";
+ } else {
+ layer.msg(res.msg || '鍔犺浇璁惧澶辫触', {icon: 2});
+ }
+ }
+ });
+ }
+
+ function downloadDeviceLog(day, type, deviceNo) {
+ if (!day) {
+ layer.msg('璇峰厛閫夋嫨鏃ユ湡', {icon: 2});
+ return;
+ }
+ if (!type) {
+ layer.msg('璇烽�夋嫨璁惧绫诲瀷', {icon: 2});
+ return;
+ }
+ if (!deviceNo) {
+ layer.msg('璇疯緭鍏ヨ澶囩紪鍙�', {icon: 2});
+ return;
+ }
+ var offsetVal = parseInt($('#file-offset').val());
+ var limitVal = parseInt($('#file-limit').val());
+ var offset = isNaN(offsetVal) || offsetVal < 0 ? 0 : offsetVal;
+ var limit = isNaN(limitVal) || limitVal <= 0 ? 200 : limitVal;
+ $.ajax({
+ url: baseUrl + "/deviceLog/download/init/auth",
+ headers: {'token': localStorage.getItem('token')},
+ method: 'POST',
+ data: JSON.stringify({ day: day, type: type, deviceNo: deviceNo, offset: offset, limit: limit }),
+ dataType:'json',
+ contentType:'application/json;charset=UTF-8',
+ success: function (res) {
+ if (res.code !== 200) {
+ layer.msg(res.msg || '鍒濆鍖栧け璐�', {icon: 2});
+ return;
+ }
+ var pid = res.data.progressId;
+ var progressIndex = layer.open({
+ type: 1,
+ title: '涓嬭浇涓�',
+ area: ['520px', '200px'],
+ content: '<div style="padding:16px;">' +
+ '<div class="layui-text" style="margin-bottom:15px;">鍘嬬缉鐢熸垚杩涘害</div>' +
+ '<div class="layui-progress" lay-showPercent="true" lay-filter="buildProgress">' +
+ '<div class="layui-progress-bar" style="width:0%"><span class="layui-progress-text">0%</span></div>' +
+ '</div>' +
+ '<div class="layui-text" style="margin:12px 0 15px;">涓嬭浇鎺ユ敹杩涘害</div>' +
+ '<div class="layui-progress" lay-showPercent="true" lay-filter="receiveProgress">' +
+ '<div class="layui-progress-bar" style="width:0%"><span class="layui-progress-text">0%</span></div>' +
+ '</div>' +
+ '</div>'
+ });
+ var timer = setInterval(function(){
+ $.ajax({
+ url: baseUrl + '/deviceLog/download/progress/auth',
+ headers: {'token': localStorage.getItem('token')},
+ method: 'GET',
+ data: { id: pid },
+ success: function (p) {
+ if (p.code === 200) {
+ var percent = p.data.percent || 0;
+ element.progress('buildProgress', percent + '%');
+ // 闅愯棌瀹炴椂澶у皬锛屼笉鏇存柊鏂囧瓧
+ }
+ }
+ });
+ }, 500);
+
+ $.ajax({
+ url: baseUrl + "/deviceLog/day/" + day + "/download/auth?type=" + encodeURIComponent(type) + "&deviceNo=" + encodeURIComponent(deviceNo) + "&offset=" + offset + "&limit=" + limit + "&progressId=" + encodeURIComponent(pid),
+ headers: {'token': localStorage.getItem('token')},
+ method: 'GET',
+ xhrFields: { responseType: 'blob' },
+ xhr: function(){
+ var xhr = new window.XMLHttpRequest();
+ xhr.onprogress = function(e){
+ var percent = 0;
+ if (e.lengthComputable && e.total > 0) {
+ percent = Math.floor(e.loaded / e.total * 100);
+ element.progress('receiveProgress', percent + '%');
+ }
+ // 闅愯棌瀹炴椂澶у皬锛屼笉鏇存柊鏂囧瓧
+ };
+ return xhr;
+ },
+ success: function (data, status, xhr) {
+ var disposition = xhr.getResponseHeader('Content-Disposition') || '';
+ var filename = type + '_' + deviceNo + '_' + day + '.zip';
+ var match = /filename=(.+)/.exec(disposition);
+ if (match && match[1]) {
+ filename = decodeURIComponent(match[1]);
+ }
+ element.progress('buildProgress', '100%');
+ element.progress('receiveProgress', '100%');
+ var blob = new Blob([data], {type: 'application/zip'});
+ var link = document.createElement('a');
+ var url = window.URL.createObjectURL(blob);
+ link.href = url;
+ link.download = filename;
+ document.body.appendChild(link);
+ link.click();
+ document.body.removeChild(link);
+ window.URL.revokeObjectURL(url);
+ clearInterval(timer);
+ setTimeout(function(){ layer.close(progressIndex); }, 300);
+ },
+ error: function () {
+ clearInterval(timer);
+ layer.close(progressIndex);
+ layer.msg('涓嬭浇澶辫触鎴栨湭鎵惧埌鏃ュ織', {icon: 2});
+ }
+ });
+ }
+ });
+ }
+
+ $(document).on('click', '#download-btn', function () {
+ downloadDeviceLog(currentDay, $('#device-type-input').val(), $('#device-no-input').val());
+ });
+
+ $(document).on('click', '#device-list .layui-btn', function () {
+ var deviceNo = $(this).attr('data-device-no');
+ var type = $(this).attr('data-type');
+ downloadDeviceLog(currentDay, type, deviceNo);
+ });
+
+ loadDateTree();
+ limit();
+});
+
diff --git a/src/main/webapp/views/deviceLogs/deviceLogs.html b/src/main/webapp/views/deviceLogs/deviceLogs.html
new file mode 100644
index 0000000..c09cc42
--- /dev/null
+++ b/src/main/webapp/views/deviceLogs/deviceLogs.html
@@ -0,0 +1,96 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+ <meta charset="utf-8">
+ <title>璁惧鏃ュ織</title>
+ <meta name="renderer" content="webkit">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
+ <link rel="stylesheet" href="../../static/layui/css/layui.css" media="all">
+ <link rel="stylesheet" href="../../static/css/admin.css?v=318" media="all">
+ <link rel="stylesheet" href="../../static/css/cool.css" media="all">
+</head>
+<body>
+
+<div class="layui-fluid">
+ <div class="layui-row">
+ <div class="layui-col-md3">
+ <div class="layui-card">
+ <div class="layui-card-header">鏃ユ湡</div>
+ <div class="layui-card-body">
+ <div id="date-tree"></div>
+ </div>
+ </div>
+ </div>
+ <div class="layui-col-md9">
+ <div class="layui-card">
+ <div class="layui-card-header">鏃ュ織涓嬭浇</div>
+ <div class="layui-card-body">
+ <form class="layui-form toolbar" id="search-box">
+ <div class="layui-form-item">
+ <div class="layui-inline">
+ <label class="layui-form-label">閫変腑鏃ユ湡锛�</label>
+ <div class="layui-input-inline">
+ <input id="selected-day" class="layui-input" type="text" placeholder="yyyyMMdd" readonly>
+ </div>
+ </div>
+ <div class="layui-inline">
+ <label class="layui-form-label">璁惧绫诲瀷锛�</label>
+ <div class="layui-input-inline">
+ <select id="device-type-input" class="layui-input">
+ <option value="">璇烽�夋嫨</option>
+ <option value="Crn">Crn</option>
+ <option value="Devp">Devp</option>
+ <option value="Rgv">Rgv</option>
+ </select>
+ </div>
+ </div>
+ <div class="layui-inline">
+ <label class="layui-form-label">璁惧缂栧彿锛�</label>
+ <div class="layui-input-inline">
+ <input id="device-no-input" class="layui-input" type="text" placeholder="璇疯緭鍏ヨ澶囩紪鍙�">
+ </div>
+ </div>
+ <div class="layui-inline">
+ <label class="layui-form-label">璧峰搴忓彿锛�</label>
+ <div class="layui-input-inline">
+ <input id="file-offset" class="layui-input" type="text" placeholder="榛樿0">
+ </div>
+ </div>
+ <div class="layui-inline">
+ <label class="layui-form-label">鏈�澶ф枃浠舵暟锛�</label>
+ <div class="layui-input-inline">
+ <input id="file-limit" class="layui-input" type="text" placeholder="榛樿200">
+ </div>
+ </div>
+ <div class="layui-inline">
+ <button id="download-btn" type="button" class="layui-btn layui-btn-normal">涓嬭浇</button>
+ </div>
+ </div>
+ </form>
+
+ <hr class="layui-bg-gray">
+
+ <div class="layui-row">
+ <div class="layui-col-xs12">
+ <div class="layui-card">
+ <div class="layui-card-header">璇ユ棩璁惧鍒楄〃</div>
+ <div class="layui-card-body">
+ <div id="device-list" class="layui-row"></div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
+<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
+<script type="text/javascript" src="../../static/js/common.js" charset="utf-8"></script>
+<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
+<script type="text/javascript" src="../../static/js/deviceLogs/deviceLogs.js" charset="utf-8"></script>
+</body>
+</html>
--
Gitblit v1.9.1