From fd82105a3dfe347c4c9acb0410c117d8d67c9339 Mon Sep 17 00:00:00 2001
From: Junjie <fallin.jie@qq.com>
Date: 星期三, 18 三月 2026 10:40:16 +0800
Subject: [PATCH] #
---
src/main/webapp/views/deviceLogs/deviceLogs.html | 1504 +++++++++++++++++++++++++++++++++++++++++++++++++++------
1 files changed, 1,329 insertions(+), 175 deletions(-)
diff --git a/src/main/webapp/views/deviceLogs/deviceLogs.html b/src/main/webapp/views/deviceLogs/deviceLogs.html
index 2af2915..5d2d451 100644
--- a/src/main/webapp/views/deviceLogs/deviceLogs.html
+++ b/src/main/webapp/views/deviceLogs/deviceLogs.html
@@ -6,192 +6,1346 @@
<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">
- <!-- CSS -->
<link rel="stylesheet" href="../../static/vue/element/element.css">
<link rel="stylesheet" href="../../static/css/common.css">
<style>
- body { margin: 0; padding: 0; background-color: #f0f2f5; height: 100vh; overflow: hidden; }
- #app { height: 100%; padding: 10px; box-sizing: border-box; display: flex; flex-direction: column; }
- .main-container { flex: 1; display: flex; overflow: hidden; }
- .sidebar { width: 260px; margin-right: 10px; display: flex; flex-direction: column; }
- .content { flex: 1; display: flex; flex-direction: column; overflow: hidden; }
-
- .box-card { height: 100%; display: flex; flex-direction: column; border: none; box-shadow: 0 1px 4px rgba(0,21,41,.08); }
- .box-card .el-card__header { padding: 10px 15px; border-bottom: 1px solid #ebeef5; background: #fff; font-weight: bold; font-size: 15px; }
- .box-card .el-card__body { flex: 1; overflow: auto; padding: 15px; }
-
- .device-item { margin-bottom: 10px; }
- .device-card { background-color: #fff; border: 1px solid #e6ebf5; border-radius: 4px; transition: all .3s; }
- .device-card:hover { box-shadow: 0 2px 12px 0 rgba(0,0,0,.1); }
- .device-info { display: flex; justify-content: space-between; align-items: center; padding: 15px; }
- .device-info .info-text { font-size: 14px; color: #606266; }
- .device-info .info-text b { color: #303133; margin-right: 5px; }
- .device-info .tag-group { margin-left: 15px; }
-
- .control-bar { margin-bottom: 15px; padding: 15px; background: #fff; border-radius: 4px; box-shadow: 0 1px 4px rgba(0,21,41,.08); }
-
- /* Visualization styles */
- .vis-control-panel { margin-bottom: 10px; display: flex; align-items: center; background: #f5f7fa; padding: 10px; border-radius: 4px; }
- .vis-container { border: 1px solid #ebeef5; padding: 10px; border-radius: 4px; min-height: 400px; height: calc(80vh - 100px); overflow-y: auto; }
+ :root {
+ --dl-bg: linear-gradient(180deg, #edf3f7 0%, #e7edf4 100%);
+ --dl-panel-bg: rgba(248, 251, 253, 0.94);
+ --dl-panel-border: rgba(223, 232, 240, 0.96);
+ --dl-panel-shadow: 0 12px 26px rgba(114, 136, 164, 0.08);
+ --dl-text-main: #22384f;
+ --dl-text-sub: #708396;
+ --dl-accent: #6f95bd;
+ --dl-accent-strong: #557ca7;
+ --dl-success: #52b17e;
+ --dl-warning: #c78a3f;
+ --dl-danger: #c96660;
+ }
+
+ html, body {
+ width: 100%;
+ height: 100%;
+ margin: 0;
+ overflow: hidden;
+ }
+
+ body {
+ background: var(--dl-bg);
+ color: var(--dl-text-main);
+ }
+
+ #app {
+ width: 100%;
+ height: 100%;
+ }
+
+ .dl-shell {
+ width: 100%;
+ height: 100%;
+ padding: 16px;
+ box-sizing: border-box;
+ display: flex;
+ gap: 14px;
+ }
+
+ .dl-sidebar {
+ width: 320px;
+ min-width: 320px;
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ min-height: 0;
+ }
+
+ .dl-workbench {
+ flex: 1;
+ min-width: 0;
+ min-height: 0;
+ display: grid;
+ grid-template-columns: minmax(520px, 1.28fr) minmax(340px, 0.92fr);
+ gap: 14px;
+ }
+
+ .dl-center,
+ .dl-visual {
+ min-width: 0;
+ min-height: 0;
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ }
+
+ .dl-center {
+ order: 2;
+ }
+
+ .dl-visual {
+ order: 1;
+ }
+
+ .dl-panel {
+ border-radius: 20px;
+ border: 1px solid var(--dl-panel-border);
+ background: var(--dl-panel-bg);
+ box-shadow: var(--dl-panel-shadow);
+ overflow: hidden;
+ min-height: 0;
+ }
+
+ .dl-panel-head {
+ padding: 14px 16px 10px;
+ border-bottom: 1px solid rgba(228, 236, 243, 0.92);
+ background: rgba(255, 255, 255, 0.26);
+ }
+
+ .dl-panel-title {
+ font-size: 15px;
+ font-weight: 700;
+ color: var(--dl-text-main);
+ line-height: 1.25;
+ }
+
+ .dl-panel-desc {
+ margin-top: 5px;
+ font-size: 12px;
+ color: var(--dl-text-sub);
+ line-height: 1.5;
+ }
+
+ .dl-panel-body {
+ padding: 14px 16px 16px;
+ box-sizing: border-box;
+ min-height: 0;
+ height: 100%;
+ }
+
+ .dl-hero {
+ padding: 16px;
+ background: linear-gradient(135deg, rgba(255, 255, 255, 0.88) 0%, rgba(240, 246, 251, 0.82) 100%);
+ }
+
+ .dl-hero-title {
+ font-size: 19px;
+ font-weight: 700;
+ color: var(--dl-text-main);
+ line-height: 1.2;
+ }
+
+ .dl-hero-desc {
+ margin-top: 8px;
+ font-size: 12px;
+ line-height: 1.6;
+ color: var(--dl-text-sub);
+ }
+
+ .dl-hero-stats {
+ margin-top: 14px;
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 10px;
+ }
+
+ .dl-stat {
+ padding: 10px 12px;
+ border-radius: 14px;
+ border: 1px solid rgba(225, 233, 240, 0.94);
+ background: rgba(255, 255, 255, 0.7);
+ }
+
+ .dl-stat-value {
+ font-size: 18px;
+ font-weight: 700;
+ color: var(--dl-accent-strong);
+ }
+
+ .dl-stat-label {
+ margin-top: 4px;
+ font-size: 11px;
+ color: var(--dl-text-sub);
+ }
+
+ .dl-date-panel,
+ .dl-device-panel,
+ .dl-log-panel,
+ .dl-raw-panel,
+ .dl-visual-card,
+ .dl-device-summary,
+ .dl-timeline-panel {
+ display: flex;
+ flex-direction: column;
+ min-height: 0;
+ }
+
+ .dl-date-panel {
+ flex: 0 0 260px;
+ }
+
+ .dl-device-panel {
+ flex: 1;
+ }
+
+ .dl-quick-days {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+ margin-bottom: 14px;
+ }
+
+ .dl-day-chip {
+ padding: 0 10px;
+ height: 30px;
+ border-radius: 999px;
+ border: 1px solid rgba(219, 228, 236, 0.96);
+ background: rgba(255, 255, 255, 0.84);
+ color: #47627d;
+ cursor: pointer;
+ transition: all .16s ease;
+ }
+
+ .dl-day-chip.is-active {
+ border-color: rgba(111, 149, 189, 0.44);
+ background: rgba(111, 149, 189, 0.14);
+ color: var(--dl-accent-strong);
+ box-shadow: 0 8px 18px rgba(111, 149, 189, 0.16);
+ }
+
+ .dl-tree-wrap {
+ flex: 1;
+ min-height: 0;
+ overflow: auto;
+ padding-right: 4px;
+ scrollbar-gutter: stable;
+ }
+
+ .dl-type-tabs {
+ display: grid;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: 8px;
+ margin-bottom: 12px;
+ }
+
+ .dl-type-tab {
+ padding: 10px 12px;
+ border-radius: 14px;
+ border: 1px solid rgba(222, 231, 239, 0.96);
+ background: rgba(255, 255, 255, 0.72);
+ cursor: pointer;
+ text-align: left;
+ transition: all .16s ease;
+ }
+
+ .dl-type-tab.is-active {
+ border-color: rgba(111, 149, 189, 0.44);
+ background: rgba(111, 149, 189, 0.14);
+ box-shadow: 0 10px 20px rgba(111, 149, 189, 0.12);
+ }
+
+ .dl-type-tab-label {
+ font-size: 13px;
+ font-weight: 700;
+ color: var(--dl-text-main);
+ }
+
+ .dl-type-tab-meta {
+ margin-top: 4px;
+ font-size: 11px;
+ color: var(--dl-text-sub);
+ }
+
+ .dl-device-search {
+ margin-bottom: 12px;
+ }
+
+ .dl-device-list {
+ flex: 1;
+ min-height: 0;
+ overflow: auto;
+ padding-right: 4px;
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ scrollbar-gutter: stable;
+ }
+
+ .dl-device-item {
+ padding: 12px 13px;
+ border-radius: 16px;
+ border: 1px solid rgba(223, 232, 240, 0.94);
+ background: rgba(255, 255, 255, 0.72);
+ cursor: pointer;
+ transition: all .16s ease;
+ }
+
+ .dl-device-item.is-active {
+ border-color: rgba(111, 149, 189, 0.48);
+ background: rgba(111, 149, 189, 0.12);
+ box-shadow: 0 12px 24px rgba(111, 149, 189, 0.14);
+ }
+
+ .dl-device-item:hover {
+ transform: translateY(-1px);
+ }
+
+ .dl-device-name {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 8px;
+ font-size: 14px;
+ font-weight: 700;
+ color: var(--dl-text-main);
+ }
+
+ .dl-device-badge {
+ padding: 3px 8px;
+ border-radius: 999px;
+ background: rgba(111, 149, 189, 0.12);
+ color: var(--dl-accent-strong);
+ font-size: 10px;
+ font-weight: 700;
+ }
+
+ .dl-device-meta {
+ margin-top: 8px;
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ font-size: 11px;
+ color: var(--dl-text-sub);
+ }
+
+ .dl-date-panel .dl-panel-body,
+ .dl-device-panel .dl-panel-body {
+ display: flex;
+ flex-direction: column;
+ flex: 1;
+ min-height: 0;
+ height: auto;
+ }
+
+ .dl-device-summary .dl-panel-head {
+ padding: 12px 16px 8px;
+ }
+
+ .dl-device-summary .dl-panel-desc {
+ margin-top: 3px;
+ }
+
+ .dl-device-summary .dl-panel-body,
+ .dl-timeline-panel .dl-panel-body {
+ padding: 12px 16px 14px;
+ }
+
+ .dl-timeline-panel .dl-panel-head {
+ padding: 8px 14px 4px;
+ }
+
+ .dl-timeline-panel .dl-panel-desc {
+ display: none;
+ }
+
+ .dl-timeline-panel .dl-panel-body {
+ padding: 8px 14px 10px;
+ }
+
+ .dl-timeline-panel .dl-btn {
+ height: 30px;
+ padding: 0 12px;
+ border-radius: 10px;
+ }
+
+ .dl-raw-panel .dl-panel-body {
+ display: flex;
+ flex-direction: column;
+ flex: 1;
+ min-height: 0;
+ height: auto;
+ }
+
+ .dl-summary-toolbar {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 10px;
+ margin-bottom: 10px;
+ }
+
+ .dl-device-summary .dl-btn {
+ height: 30px;
+ padding: 0 12px;
+ border-radius: 10px;
+ }
+
+ .dl-summary-grid {
+ display: grid;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: 6px;
+ }
+
+ .dl-summary-cell {
+ padding: 9px 10px;
+ border-radius: 12px;
+ border: 1px solid rgba(224, 232, 239, 0.96);
+ background: rgba(255, 255, 255, 0.78);
+ }
+
+ .dl-summary-cell-label {
+ font-size: 10px;
+ line-height: 1.2;
+ color: var(--dl-text-sub);
+ }
+
+ .dl-summary-cell-value {
+ margin-top: 4px;
+ font-size: 12px;
+ font-weight: 700;
+ color: var(--dl-text-main);
+ line-height: 1.35;
+ word-break: normal;
+ overflow-wrap: anywhere;
+ }
+
+ .dl-summary-cell-sub {
+ margin-top: 2px;
+ font-size: 11px;
+ line-height: 1.3;
+ color: var(--dl-text-sub);
+ }
+
+ .dl-summary-cell.is-range,
+ .dl-summary-cell.is-current {
+ grid-column: 1 / -1;
+ }
+
+ .dl-summary-cell.is-range .dl-summary-cell-value,
+ .dl-summary-cell.is-range .dl-summary-cell-sub {
+ font-family: monospace;
+ }
+
+ .dl-inline-actions {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+ }
+
+ .dl-btn {
+ height: 34px;
+ padding: 0 14px;
+ border-radius: 12px;
+ border: none;
+ background: var(--dl-accent);
+ color: #fff;
+ font-size: 12px;
+ font-weight: 700;
+ cursor: pointer;
+ transition: transform .16s ease, box-shadow .16s ease;
+ box-shadow: 0 10px 18px rgba(111, 149, 189, 0.16);
+ }
+
+ .dl-btn:hover {
+ transform: translateY(-1px);
+ }
+
+ .dl-btn.is-ghost {
+ border: 1px solid rgba(219, 228, 236, 0.96);
+ background: rgba(255, 255, 255, 0.84);
+ color: #4b667f;
+ box-shadow: none;
+ }
+
+ .dl-btn[disabled] {
+ opacity: .5;
+ cursor: not-allowed;
+ transform: none;
+ box-shadow: none;
+ }
+
+ .dl-log-workspace {
+ flex: 1;
+ min-height: 0;
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ }
+
+ .dl-detail-panel {
+ flex: 1;
+ min-height: 0;
+ display: flex;
+ flex-direction: column;
+ }
+
+ .dl-detail-toolbar {
+ padding: 14px 16px 10px;
+ border-bottom: 1px solid rgba(228, 236, 243, 0.92);
+ display: flex;
+ align-items: flex-start;
+ justify-content: space-between;
+ gap: 10px;
+ }
+
+ .dl-detail-tabs {
+ display: inline-flex;
+ gap: 8px;
+ flex-wrap: wrap;
+ justify-content: flex-end;
+ }
+
+ .dl-detail-body {
+ flex: 1;
+ min-height: 0;
+ display: flex;
+ flex-direction: column;
+ }
+
+ .dl-log-status {
+ display: inline-flex;
+ align-items: center;
+ height: 24px;
+ padding: 0 9px;
+ border-radius: 999px;
+ font-size: 11px;
+ font-weight: 700;
+ margin-right: 8px;
+ }
+
+ .dl-tone-success { background: rgba(82, 177, 126, 0.12); color: #2d7650; }
+ .dl-tone-working { background: rgba(111, 149, 189, 0.12); color: #3f6286; }
+ .dl-tone-warning { background: rgba(214, 162, 94, 0.14); color: #9b6a24; }
+ .dl-tone-danger { background: rgba(201, 102, 96, 0.14); color: #a74d47; }
+ .dl-tone-muted { background: rgba(148, 163, 184, 0.14); color: #748397; }
+
+ .dl-log-list {
+ flex: 1;
+ min-height: 0;
+ overflow: auto;
+ padding: 12px 16px 16px;
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ scrollbar-gutter: stable;
+ }
+
+ .dl-raw-content {
+ flex: 1;
+ min-height: 0;
+ display: flex;
+ flex-direction: column;
+ padding: 12px 16px 16px;
+ box-sizing: border-box;
+ }
+
+ .dl-log-row {
+ padding: 10px 12px;
+ border-radius: 14px;
+ border: 1px solid rgba(223, 232, 240, 0.94);
+ background: rgba(255, 255, 255, 0.72);
+ cursor: pointer;
+ transition: all .16s ease;
+ }
+
+ .dl-log-row.is-active {
+ border-color: rgba(111, 149, 189, 0.5);
+ background: rgba(111, 149, 189, 0.12);
+ box-shadow: 0 12px 22px rgba(111, 149, 189, 0.12);
+ }
+
+ .dl-log-row-head {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 8px;
+ }
+
+ .dl-log-time {
+ font-size: 12px;
+ font-weight: 700;
+ color: var(--dl-accent-strong);
+ font-family: monospace;
+ }
+
+ .dl-log-title {
+ margin-top: 6px;
+ font-size: 12px;
+ font-weight: 700;
+ color: var(--dl-text-main);
+ line-height: 1.35;
+ }
+
+ .dl-log-meta-line {
+ margin-top: 4px;
+ font-size: 11px;
+ line-height: 1.4;
+ color: var(--dl-text-sub);
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
+ .dl-log-hint {
+ margin-top: 5px;
+ display: inline-flex;
+ align-items: center;
+ font-size: 11px;
+ color: var(--dl-text-sub);
+ }
+
+ .dl-picker-main {
+ flex: 1;
+ min-width: 0;
+ min-height: 0;
+ display: flex;
+ flex-direction: column;
+ }
+
+ .dl-picker-panel {
+ flex: 1;
+ min-height: 0;
+ display: flex;
+ flex-direction: column;
+ }
+
+ .dl-picker-panel .dl-panel-body {
+ display: flex;
+ flex-direction: column;
+ min-height: 0;
+ height: auto;
+ }
+
+ .dl-picker-toolbar {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) 280px;
+ gap: 12px;
+ align-items: start;
+ margin-bottom: 12px;
+ }
+
+ .dl-picker-side-tools {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ }
+
+ .dl-picker-current-day {
+ padding: 10px 12px;
+ border-radius: 14px;
+ border: 1px solid rgba(223, 232, 240, 0.94);
+ background: rgba(255, 255, 255, 0.72);
+ font-size: 12px;
+ color: var(--dl-text-sub);
+ }
+
+ .dl-picker-current-day strong {
+ display: block;
+ margin-top: 3px;
+ font-size: 13px;
+ color: var(--dl-text-main);
+ }
+
+ .dl-picker-device-grid {
+ flex: 1;
+ min-height: 0;
+ overflow: auto;
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
+ gap: 12px;
+ padding-right: 4px;
+ scrollbar-gutter: stable;
+ }
+
+ .dl-picker-device-grid .dl-device-item {
+ min-height: 112px;
+ text-align: left;
+ }
+
+ .dl-picker-span-all {
+ grid-column: 1 / -1;
+ }
+
+ .dl-viewer-shell {
+ flex: 1;
+ min-width: 0;
+ min-height: 0;
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ }
+
+ .dl-viewer-header .dl-panel-body {
+ height: auto;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 12px;
+ padding: 12px 16px;
+ }
+
+ .dl-viewer-header-main {
+ min-width: 0;
+ display: flex;
+ align-items: flex-start;
+ gap: 12px;
+ }
+
+ .dl-viewer-copy {
+ min-width: 0;
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+ }
+
+ .dl-viewer-meta {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+ }
+
+ .dl-viewer-meta-item {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ padding: 5px 10px;
+ border-radius: 999px;
+ border: 1px solid rgba(223, 232, 240, 0.94);
+ background: rgba(255, 255, 255, 0.72);
+ font-size: 11px;
+ color: var(--dl-text-sub);
+ }
+
+ .dl-viewer-meta-item strong {
+ color: var(--dl-text-main);
+ font-weight: 700;
+ }
+
+ .dl-viewer-grid {
+ flex: 1;
+ min-height: 0;
+ display: grid;
+ grid-template-columns: minmax(620px, 1.28fr) minmax(360px, 0.92fr);
+ gap: 12px;
+ }
+
+ .dl-viewer-main,
+ .dl-viewer-side {
+ min-width: 0;
+ min-height: 0;
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ }
+
+ .dl-viewer-side .dl-timeline-panel {
+ flex: 0 0 auto;
+ }
+
+ .dl-raw-toolbar {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 12px;
+ margin-bottom: 12px;
+ }
+
+ .dl-raw-meta {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px 12px;
+ font-size: 12px;
+ color: var(--dl-text-sub);
+ }
+
+ .dl-raw-tabs {
+ display: inline-flex;
+ gap: 8px;
+ }
+
+ .dl-tab-btn {
+ height: 30px;
+ padding: 0 12px;
+ border-radius: 999px;
+ border: 1px solid rgba(219, 228, 236, 0.96);
+ background: rgba(255, 255, 255, 0.84);
+ color: #4b667f;
+ font-size: 12px;
+ font-weight: 700;
+ cursor: pointer;
+ transition: all .16s ease;
+ }
+
+ .dl-tab-btn.is-active {
+ border-color: rgba(111, 149, 189, 0.44);
+ background: rgba(111, 149, 189, 0.14);
+ color: var(--dl-accent-strong);
+ }
+
+ .dl-json-box {
+ flex: 1;
+ min-height: 0;
+ overflow: auto;
+ padding: 14px;
+ border-radius: 16px;
+ border: 1px solid rgba(223, 232, 240, 0.94);
+ background: rgba(244, 248, 252, 0.88);
+ scrollbar-gutter: stable;
+ }
+
+ .dl-json-pre {
+ margin: 0;
+ font-size: 12px;
+ line-height: 1.55;
+ color: #2b425a;
+ font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
+ white-space: pre-wrap;
+ word-break: break-word;
+ }
+
+ .dl-json-note {
+ margin-top: 8px;
+ font-size: 11px;
+ color: var(--dl-text-sub);
+ }
+
+ .dl-timeline-toolbar {
+ display: grid;
+ grid-template-columns: auto minmax(0, 1fr) auto;
+ align-items: center;
+ gap: 8px 10px;
+ margin-bottom: 6px;
+ }
+
+ .dl-timeline-range {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ min-width: 0;
+ }
+
+ .dl-timeline-range .el-slider {
+ flex: 1 1 220px;
+ width: 100%;
+ min-width: 180px;
+ }
+
+ .dl-time-readout {
+ min-width: 196px;
+ font-size: 13px;
+ font-weight: 700;
+ color: var(--dl-text-main);
+ font-family: monospace;
+ text-align: right;
+ flex-shrink: 0;
+ }
+
+ .dl-timeline-side {
+ display: inline-flex;
+ align-items: center;
+ justify-content: flex-end;
+ gap: 8px;
+ min-width: 0;
+ }
+
+ .dl-range-meta {
+ margin-top: 4px;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 4px 10px;
+ font-size: 11px;
+ color: var(--dl-text-sub);
+ }
+
+ .dl-visual-card {
+ flex: 1;
+ min-height: 0;
+ }
+
+ .dl-visual-card .dl-panel-head {
+ padding: 12px 16px 8px;
+ }
+
+ .dl-visual-card .dl-panel-body {
+ height: 100%;
+ min-height: 0;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+ padding: 10px 12px 12px;
+ }
+
+ .dl-visual-card .mc-toolbar {
+ margin-bottom: 6px;
+ }
+
+ .dl-visual-card .mc-title {
+ font-size: 13px;
+ }
+
+ .dl-visual-card .mc-head {
+ padding: 8px 10px;
+ }
+
+ .dl-visual-card .mc-head-title {
+ font-size: 12px;
+ }
+
+ .dl-visual-card .mc-head-subtitle {
+ display: none;
+ }
+
+ .dl-visual-card .mc-body {
+ padding: 0 10px 8px;
+ }
+
+ .dl-visual-card .mc-detail-grid {
+ grid-template-columns: repeat(4, minmax(0, 1fr));
+ gap: 5px;
+ margin-top: 6px;
+ }
+
+ .dl-visual-card .mc-detail-cell {
+ padding: 7px 8px;
+ border-radius: 9px;
+ }
+
+ .dl-visual-card .mc-detail-label {
+ font-size: 10px;
+ }
+
+ .dl-visual-card .mc-detail-value {
+ margin-top: 2px;
+ font-size: 10px;
+ line-height: 1.25;
+ }
+
+ .dl-visual-card .mc-footer {
+ margin-top: 6px;
+ }
+
+ .dl-tree-wrap,
+ .dl-device-list,
+ .dl-log-list,
+ .dl-json-box,
+ .dl-visual-card .mc-collapse {
+ scrollbar-width: auto;
+ scrollbar-color: rgba(111, 149, 189, 0.9) rgba(225, 233, 241, 0.72);
+ }
+
+ .dl-visual-card .mc-collapse {
+ padding-right: 6px;
+ scrollbar-gutter: stable;
+ }
+
+ .dl-tree-wrap::-webkit-scrollbar,
+ .dl-device-list::-webkit-scrollbar,
+ .dl-log-list::-webkit-scrollbar,
+ .dl-json-box::-webkit-scrollbar,
+ .dl-visual-card .mc-collapse::-webkit-scrollbar {
+ width: 12px;
+ height: 12px;
+ }
+
+ .dl-tree-wrap::-webkit-scrollbar-track,
+ .dl-device-list::-webkit-scrollbar-track,
+ .dl-log-list::-webkit-scrollbar-track,
+ .dl-json-box::-webkit-scrollbar-track,
+ .dl-visual-card .mc-collapse::-webkit-scrollbar-track {
+ background: rgba(225, 233, 241, 0.78);
+ border-radius: 999px;
+ border: 2px solid rgba(248, 251, 253, 0.96);
+ }
+
+ .dl-tree-wrap::-webkit-scrollbar-thumb,
+ .dl-device-list::-webkit-scrollbar-thumb,
+ .dl-log-list::-webkit-scrollbar-thumb,
+ .dl-json-box::-webkit-scrollbar-thumb,
+ .dl-visual-card .mc-collapse::-webkit-scrollbar-thumb {
+ background: linear-gradient(180deg, rgba(111, 149, 189, 0.96) 0%, rgba(85, 124, 167, 0.96) 100%);
+ border-radius: 999px;
+ border: 2px solid rgba(248, 251, 253, 0.96);
+ }
+
+ .dl-tree-wrap::-webkit-scrollbar-thumb:hover,
+ .dl-device-list::-webkit-scrollbar-thumb:hover,
+ .dl-log-list::-webkit-scrollbar-thumb:hover,
+ .dl-json-box::-webkit-scrollbar-thumb:hover,
+ .dl-visual-card .mc-collapse::-webkit-scrollbar-thumb:hover {
+ background: linear-gradient(180deg, rgba(98, 136, 177, 0.98) 0%, rgba(74, 112, 154, 0.98) 100%);
+ }
+
+ .dl-empty,
+ .dl-loading {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: 100%;
+ min-height: 160px;
+ color: var(--dl-text-sub);
+ font-size: 13px;
+ text-align: center;
+ padding: 0 20px;
+ box-sizing: border-box;
+ }
+
+ .dl-loading i {
+ font-size: 22px;
+ margin-right: 8px;
+ }
+
+ @media (max-width: 1440px) {
+ .dl-workbench {
+ grid-template-columns: minmax(460px, 1.18fr) minmax(320px, 0.92fr);
+ }
+
+ .dl-picker-toolbar {
+ grid-template-columns: 1fr;
+ }
+
+ .dl-viewer-grid {
+ grid-template-columns: minmax(520px, 1.15fr) minmax(320px, 0.92fr);
+ }
+ }
+
+ @media (max-width: 1180px) {
+ .dl-workbench {
+ grid-template-columns: 1fr;
+ }
+
+ .dl-center,
+ .dl-visual {
+ order: initial;
+ }
+
+ .dl-summary-grid,
+ .dl-visual-card .mc-detail-grid {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ }
+
+ .dl-viewer-grid {
+ grid-template-columns: 1fr;
+ }
+ }
+
+ @media (max-width: 1560px) {
+ .dl-timeline-toolbar {
+ grid-template-columns: auto auto;
+ align-items: center;
+ justify-content: space-between;
+ }
+
+ .dl-timeline-range {
+ grid-column: 1 / -1;
+ width: 100%;
+ }
+
+ .dl-timeline-side {
+ justify-content: flex-end;
+ }
+ }
+
+ @media (max-width: 1320px) {
+ .dl-timeline-range {
+ flex-wrap: wrap;
+ }
+
+ .dl-time-readout {
+ min-width: 0;
+ width: 100%;
+ text-align: left;
+ }
+ }
</style>
</head>
<body>
-
<div id="app" v-cloak>
- <div class="main-container">
- <!-- Sidebar: Date Tree -->
- <div class="sidebar">
- <el-card class="box-card" :body-style="{padding: '10px'}">
- <div slot="header">鏃ユ湡閫夋嫨</div>
- <el-tree
- ref="dateTree"
- :data="dateTreeData"
- :props="defaultProps"
- node-key="id"
- :default-expanded-keys="defaultExpandedKeys"
- @node-click="handleNodeClick"
- highlight-current
- accordion>
- <span class="custom-tree-node" slot-scope="{ node, data }">
- <i v-if="data.children" class="el-icon-folder"></i>
- <i v-else class="el-icon-document"></i>
- <span style="margin-left: 5px;">{{ node.label }}</span>
- </span>
- </el-tree>
- </el-card>
- </div>
+ <div class="dl-shell">
+ <template v-if="viewMode === 'picker'">
+ <aside class="dl-sidebar">
+ <section class="dl-panel dl-hero">
+ <div class="dl-hero-title">璁惧鏃ュ織宸ヤ綔鍙�</div>
+ <div class="dl-hero-desc">鍏堥�夋嫨鏃ュ織鏃ユ湡鍜岃澶囷紝鍐嶈繘鍏ョ嫭绔嬬殑鏁版嵁鏌ョ湅椤点�傜瓫閫夊叆鍙e拰鏌ョ湅鎬佹媶寮�锛岄伩鍏嶈澶囧垪琛ㄨ鏁版嵁鐪嬫澘鎸ゅ帇銆�</div>
+ <div class="dl-hero-stats">
+ <div class="dl-stat">
+ <div class="dl-stat-value">{{ summaryStats.totalDevices || 0 }}</div>
+ <div class="dl-stat-label">璁惧鏁�</div>
+ </div>
+ <div class="dl-stat">
+ <div class="dl-stat-value">{{ summaryStats.totalFiles || 0 }}</div>
+ <div class="dl-stat-label">鏃ュ織鏂囦欢</div>
+ </div>
+ <div class="dl-stat">
+ <div class="dl-stat-value">{{ recentDays.length }}</div>
+ <div class="dl-stat-label">鏈�杩戞棩鏈�</div>
+ </div>
+ </div>
+ </section>
- <!-- Main Content -->
- <div class="content">
- <!-- Search Bar -->
- <div class="control-bar">
- <el-form :inline="true" :model="searchForm" size="small" style="margin-bottom: -18px;">
- <el-form-item label="閫変腑鏃ユ湡">
- <el-input v-model="searchForm.day" placeholder="yyyyMMdd" readonly style="width: 120px;" disabled></el-input>
- </el-form-item>
- <el-form-item label="璁惧绫诲瀷">
- <el-select v-model="searchForm.type" placeholder="鍏ㄩ儴" clearable style="width: 100px;">
- <el-option label="Crn" value="Crn"></el-option>
- <el-option label="Devp" value="Devp"></el-option>
- <el-option label="Rgv" value="Rgv"></el-option>
- </el-select>
- </el-form-item>
- <el-form-item label="璁惧缂栧彿">
- <el-input v-model="searchForm.deviceNo" placeholder="璇疯緭鍏ョ紪鍙�" style="width: 120px;" clearable></el-input>
- </el-form-item>
- <el-form-item label="璧峰搴忓彿">
- <el-input-number v-model="searchForm.offset" :min="0" controls-position="right" style="width: 100px;"></el-input-number>
- </el-form-item>
- <el-form-item label="鏈�澶ф枃浠�">
- <el-input-number v-model="searchForm.limit" :min="1" :max="1000" controls-position="right" style="width: 100px;"></el-input-number>
- </el-form-item>
- <el-form-item>
- <el-button type="primary" icon="el-icon-download" @click="handleBatchDownload" :disabled="!canDownload">涓嬭浇</el-button>
- </el-form-item>
- </el-form>
- </div>
+ <section class="dl-panel dl-date-panel">
+ <div class="dl-panel-head">
+ <div class="dl-panel-title">鏃ユ湡瀵艰埅</div>
+ <div class="dl-panel-desc">鍏堥�夋棩蹇楁棩鏈燂紝鍐嶈繘鍏ヨ澶囬�夋嫨椤点��</div>
+ </div>
+ <div class="dl-panel-body">
+ <div class="dl-quick-days">
+ <button
+ v-for="day in recentDays"
+ :key="day.day"
+ type="button"
+ class="dl-day-chip"
+ :class="{ 'is-active': selectedDay === day.day }"
+ @click="handleRecentDayClick(day.day)"
+ >{{ day.label }}</button>
+ </div>
+ <div class="dl-tree-wrap">
+ <el-tree
+ ref="dateTree"
+ :data="dateTreeData"
+ :props="defaultProps"
+ node-key="id"
+ :default-expanded-keys="defaultExpandedKeys"
+ highlight-current
+ accordion
+ @node-click="handleNodeClick">
+ <span slot-scope="{ node, data }">
+ <i v-if="data.children" class="el-icon-folder"></i>
+ <i v-else class="el-icon-date"></i>
+ <span style="margin-left: 6px;">{{ node.label }}</span>
+ </span>
+ </el-tree>
+ </div>
+ </div>
+ </section>
+ </aside>
- <!-- Device List -->
- <el-card class="box-card">
- <div slot="header" class="clearfix">
- <span>璁惧鍒楄〃</span>
- <span style="float: right; color: #909399; font-size: 12px;">鍏� {{ filteredDeviceList.length }} 涓澶�</span>
- </div>
-
- <div v-if="loading" style="text-align: center; padding: 20px;">
- <i class="el-icon-loading" style="font-size: 24px;"></i>
- </div>
- <div v-else-if="filteredDeviceList.length === 0" style="text-align: center; color: #909399; padding: 50px;">
- <i class="el-icon-info" style="margin-right: 5px;"></i>鏆傛棤鏁版嵁锛岃鍏堥�夋嫨鏃ユ湡
- </div>
- <div v-else>
- <div v-for="(item, index) in filteredDeviceList" :key="index" class="device-item">
- <div class="device-card">
- <div class="device-info">
- <div>
- <span class="info-text"><b>璁惧缂栧彿:</b> {{ item.deviceNo }}</span>
- <span class="info-text tag-group"><b>绫诲瀷:</b> {{ item.types.join(', ') }}</span>
- <span class="info-text tag-group"><b>鏂囦欢鏁�:</b> {{ item.fileCount }}</span>
+ <section class="dl-picker-main">
+ <section class="dl-panel dl-picker-panel">
+ <div class="dl-panel-head">
+ <div class="dl-panel-title">璁惧閫夋嫨</div>
+ <div class="dl-panel-desc">{{ selectedDay ? ('鏃ュ織鏃ユ湡 ' + formatDayText(selectedDay) + '锛屽厛绛涢�夎澶囩被鍨嬶紝鍐嶇偣鍑讳竴鍙拌澶囪繘鍏ョ姸鎬佹煡鐪嬮〉銆�') : '鍏堜粠宸︿晶閫夋嫨涓�涓棩蹇楁棩鏈熴��' }}</div>
+ </div>
+ <div class="dl-panel-body">
+ <div class="dl-picker-toolbar">
+ <div class="dl-type-tabs">
+ <button
+ v-for="group in deviceGroups"
+ :key="group.type"
+ type="button"
+ class="dl-type-tab"
+ :class="{ 'is-active': activeType === group.type }"
+ @click="selectTypeGroup(group.type)">
+ <div class="dl-type-tab-label">{{ group.typeLabel }}</div>
+ <div class="dl-type-tab-meta">{{ group.deviceCount }} 鍙� / {{ group.totalFiles }} 鏂囦欢</div>
+ </button>
+ </div>
+ <div class="dl-picker-side-tools">
+ <div class="dl-picker-current-day">
+ 褰撳墠鏃ユ湡
+ <strong>{{ selectedDay ? formatDayText(selectedDay) : '鏈�夋嫨' }}</strong>
</div>
- <div>
- <template v-for="t in item.types">
- <el-button size="mini" icon="el-icon-download" @click="downloadLog(item.deviceNo, t)">涓嬭浇({{t}})</el-button>
- <el-button size="mini" type="success" icon="el-icon-view" @click="visualizeLog(item.deviceNo, t)">鍙鍖�({{t}})</el-button>
- </template>
+ <div class="dl-device-search" style="margin-bottom: 0;">
+ <el-input
+ v-model.trim="searchDeviceNo"
+ size="small"
+ clearable
+ placeholder="鎸夎澶囩紪鍙风瓫閫�">
+ <i slot="prefix" class="el-input__icon el-icon-search"></i>
+ </el-input>
</div>
</div>
</div>
+ <div class="dl-picker-device-grid">
+ <div v-if="summaryLoading" class="dl-loading dl-picker-span-all">
+ <i class="el-icon-loading"></i><span>姝e湪鍔犺浇璁惧鎽樿...</span>
+ </div>
+ <div v-else-if="!selectedDay" class="dl-empty dl-picker-span-all">鍏堜粠宸︿晶閫夋嫨涓�涓棩鏈熴��</div>
+ <div v-else-if="filteredDevices.length === 0" class="dl-empty dl-picker-span-all">褰撳墠鍒嗙粍涓嬫病鏈夊尮閰嶇殑璁惧銆�</div>
+ <button
+ v-else
+ v-for="device in filteredDevices"
+ :key="buildDeviceKey(device.type, device.deviceNo)"
+ type="button"
+ class="dl-device-item"
+ @click="selectDevice(device)">
+ <div class="dl-device-name">
+ <span>{{ device.deviceNo }} 鍙穥{ device.typeLabel }}</span>
+ <span class="dl-device-badge">{{ device.fileCount }} 鏂囦欢</span>
+ </div>
+ <div class="dl-device-meta">
+ <span>棣栨潯: {{ formatTimestamp(device.firstTime, false) }}</span>
+ <span>鏈潯: {{ formatTimestamp(device.lastTime, false) }}</span>
+ </div>
+ </button>
+ </div>
+ </div>
+ </section>
+ </section>
+ </template>
+
+ <section v-else class="dl-viewer-shell">
+ <section class="dl-panel dl-viewer-header">
+ <div class="dl-panel-body">
+ <div class="dl-viewer-header-main">
+ <button type="button" class="dl-btn is-ghost" @click="returnToSelector">杩斿洖绛涢��</button>
+ <div class="dl-viewer-copy">
+ <div class="dl-panel-title">{{ selectedDeviceSummary ? (selectedDeviceSummary.typeLabel + ' ' + selectedDeviceSummary.deviceNo + '鍙�') : '璁惧鐘舵�佹煡鐪�' }}</div>
+ <div class="dl-panel-desc">{{ selectedDay ? ('鏃ュ織鏃ユ湡 ' + formatDayText(selectedDay)) : '璇烽�夋嫨鏃ユ湡鍜岃澶�' }}</div>
+ <div v-if="selectedDeviceSummary" class="dl-viewer-meta">
+ <span class="dl-viewer-meta-item">绫诲瀷 <strong>{{ selectedDeviceSummary.typeLabel }}</strong></span>
+ <span class="dl-viewer-meta-item">鏂囦欢 <strong>{{ selectedDeviceSummary.fileCount }}</strong></span>
+ <span class="dl-viewer-meta-item">宸茶浇 <strong>{{ loadedSegmentCount }}</strong></span>
+ <span class="dl-viewer-meta-item">鑼冨洿 <strong>{{ timelineRangeText }}</strong></span>
+ </div>
+ </div>
+ </div>
+ <div class="dl-inline-actions">
+ <span class="dl-log-status" :class="'dl-tone-' + currentStatusTone">{{ currentStatusLabel }}</span>
+ <button type="button" class="dl-btn is-ghost" @click="loadPreviousSegment" :disabled="!canLoadPreviousSegment">鍔犺浇鏇存棭鐗囨</button>
+ <button type="button" class="dl-btn is-ghost" @click="loadNextSegment" :disabled="!canLoadNextSegment">鍔犺浇鏇存柊鐗囨</button>
+ <button type="button" class="dl-btn" @click="handleCurrentDeviceDownload" :disabled="!canDownload">涓嬭浇褰撴棩鏃ュ織</button>
</div>
</div>
- </el-card>
- </div>
+ </section>
+
+ <div class="dl-viewer-grid">
+ <section class="dl-viewer-main">
+ <section class="dl-panel dl-visual-card">
+ <div class="dl-panel-head">
+ <div class="dl-panel-title">鐘舵�佸彲瑙嗗寲</div>
+ <div class="dl-panel-desc">{{ selectedLogRow ? selectedLogRow._summary.detail : '鎷栧姩鏃堕棿杞存垨鐐瑰嚮鏃ュ織鍚庯紝鍦ㄨ繖閲屾煡鐪嬭鏃跺埢鐨勮澶囩姸鎬併��' }}</div>
+ </div>
+ <div class="dl-panel-body">
+ <component
+ v-if="visualComponentName"
+ :is="visualComponentName"
+ :key="activeDeviceKey"
+ :items="visualItems"
+ :param="visualParam"
+ :auto-refresh="false"
+ :read-only="true"></component>
+ <div v-else class="dl-empty">鍏堥�夋嫨涓�涓澶囷紝骞跺姞杞借嚦灏戜竴鏉℃棩蹇楄褰曘��</div>
+ </div>
+ </section>
+ </section>
+
+ <section class="dl-viewer-side">
+ <section class="dl-panel dl-timeline-panel">
+ <div class="dl-panel-head">
+ <div class="dl-panel-title">鏃堕棿杞翠笌鍥炴斁</div>
+ <div class="dl-panel-desc">鎷栧姩鏃堕棿杞存垨璺宠浆鏃堕棿鐐规煡鐪嬭璁惧鐨勭姸鎬佸彉鍖栥��</div>
+ </div>
+ <div class="dl-panel-body">
+ <div class="dl-timeline-toolbar">
+ <div class="dl-inline-actions">
+ <button type="button" class="dl-btn" v-if="!isPlaying" @click="play" :disabled="!canPlay">鎾斁</button>
+ <button type="button" class="dl-btn" v-else @click="pause">鏆傚仠</button>
+ <button type="button" class="dl-btn is-ghost" @click="resetPlayback" :disabled="!selectedDeviceSummary">閲嶇疆</button>
+ </div>
+ <div class="dl-timeline-range">
+ <el-slider
+ :value="sliderValue"
+ :max="sliderMax"
+ :disabled="!selectedDeviceSummary || sliderMax <= 0"
+ @input="handleSliderInput"
+ @change="handleSliderChange"
+ :format-tooltip="formatTooltip"></el-slider>
+ <div class="dl-time-readout">{{ currentTimeStr }}</div>
+ </div>
+ <div class="dl-timeline-side">
+ <el-popover
+ placement="bottom"
+ width="220"
+ trigger="click"
+ v-model="jumpVisible"
+ @show="initJumpTime">
+ <div style="display: flex; flex-direction: column; gap: 10px;">
+ <el-time-picker
+ v-model="jumpTime"
+ size="small"
+ placeholder="閫夋嫨鏃堕棿"
+ style="width: 100%;"
+ :picker-options="{ selectableRange: '00:00:00 - 23:59:59' }">
+ </el-time-picker>
+ <button type="button" class="dl-btn" style="width: 100%;" @click="confirmJump">璺宠浆</button>
+ </div>
+ <button slot="reference" type="button" class="dl-btn is-ghost" :disabled="!selectedDeviceSummary">璺宠浆</button>
+ </el-popover>
+ <el-select v-model="playbackSpeed" size="small" style="width: 92px;" :disabled="!selectedDeviceSummary">
+ <el-option :value="1" label="1x"></el-option>
+ <el-option :value="5" label="5x"></el-option>
+ <el-option :value="10" label="10x"></el-option>
+ <el-option :value="50" label="50x"></el-option>
+ <el-option :value="100" label="100x"></el-option>
+ <el-option :value="200" label="200x"></el-option>
+ </el-select>
+ </div>
+ </div>
+ <div class="dl-range-meta">
+ <span>瀹屾暣鑼冨洿: {{ timelineRangeText }}</span>
+ <span>宸插姞杞界墖娈�: {{ loadedSegmentCount }} / {{ timelineMeta.totalFiles || 0 }}</span>
+ <span v-if="selectedLogRow">褰撳墠璁板綍: {{ selectedLogRow._summary.title }}</span>
+ </div>
+ </div>
+ </section>
+
+ <section class="dl-panel dl-detail-panel">
+ <div class="dl-detail-toolbar">
+ <div>
+ <div class="dl-panel-title">{{ detailTab === 'raw' ? '鍘熷鏁版嵁' : '鏃ュ織鍒楄〃' }}</div>
+ <div v-if="detailTab === 'raw'" class="dl-panel-desc">{{ selectedLogRow ? '褰撳墠璁板綍鐨� wcsData JSON 宸叉牸寮忓寲灞曠ず锛屽繀瑕佹椂鍙垏鎹㈡煡鐪嬪師濮嬫枃鏈��' : '鍏堥�夋嫨涓�鏉℃棩蹇楋紝鍐嶆煡鐪嬪師濮嬫暟鎹��' }}</div>
+ </div>
+ <div class="dl-detail-tabs">
+ <button type="button" class="dl-tab-btn" :class="{ 'is-active': detailTab === 'logs' }" @click="detailTab = 'logs'">鏃ュ織鍒楄〃</button>
+ <button type="button" class="dl-tab-btn" :class="{ 'is-active': detailTab === 'raw' }" @click="detailTab = 'raw'" :disabled="!selectedLogRow">鍘熷鏁版嵁</button>
+ </div>
+ </div>
+ <div class="dl-detail-body">
+ <div v-show="detailTab === 'logs'" class="dl-log-list">
+ <div v-if="timelineLoading || logLoading" class="dl-loading">
+ <i class="el-icon-loading"></i><span>{{ timelineLoading ? '姝e湪鏋勫缓璁惧鏃堕棿杞�...' : '姝e湪鍔犺浇鏃ュ織鐗囨...' }}</span>
+ </div>
+ <div v-else-if="!selectedDeviceSummary" class="dl-empty">鍏堣繑鍥炵瓫閫夐〉閫夋嫨璁惧銆�</div>
+ <div v-else-if="logRows.length === 0" class="dl-empty">{{ logLoadError || '褰撳墠璁惧鏆傛棤鍙睍绀烘棩蹇椼��' }}</div>
+ <div
+ v-else
+ v-for="row in logRows"
+ :key="row._key"
+ :id="'log-row-' + row._key"
+ class="dl-log-row"
+ :class="{ 'is-active': row._key === selectedLogKey }"
+ @click="handleLogRowClick(row)">
+ <div class="dl-log-row-head">
+ <span class="dl-log-time">{{ formatTimestamp(row._ts, true) }}</span>
+ <span class="dl-log-status" :class="'dl-tone-' + row._summary.tone">{{ row._summary.statusLabel }}</span>
+ </div>
+ <div class="dl-log-title">{{ row._summary.title }}</div>
+ <div class="dl-log-meta-line">{{ buildLogMetaLine(row._summary) }}</div>
+ </div>
+ </div>
+ <div v-show="detailTab === 'raw'" class="dl-raw-content">
+ <template v-if="selectedLogRow">
+ <div class="dl-raw-toolbar">
+ <div class="dl-raw-meta">
+ <span>{{ formatTimestamp(selectedLogRow._ts, true) }}</span>
+ <span>{{ currentLogTitle }}</span>
+ </div>
+ <div class="dl-raw-tabs">
+ <button type="button" class="dl-tab-btn" :class="{ 'is-active': rawTab === 'wcs' }" @click="rawTab = 'wcs'">WCS JSON</button>
+ <button type="button" class="dl-tab-btn" :class="{ 'is-active': rawTab === 'origin' }" @click="rawTab = 'origin'">originData</button>
+ </div>
+ </div>
+ <div class="dl-json-box">
+ <pre class="dl-json-pre">{{ activeRawText }}</pre>
+ </div>
+ <div class="dl-json-note">{{ activeRawHint }}</div>
+ </template>
+ <div v-else class="dl-empty">鍏堝湪鏃ュ織鍒楄〃鐐瑰嚮涓�鏉¤褰曪紝鍐嶆煡鐪嬪師濮嬫暟鎹��</div>
+ </div>
+ </div>
+ </section>
+ </section>
+ </div>
+ </section>
</div>
- <!-- Visualization Dialog -->
- <el-dialog
- :title="visualizationTitle"
- :visible.sync="visualizationVisible"
- width="90%"
- top="5vh"
- :close-on-click-modal="false"
- @close="handleVisualizationClose">
-
- <div class="vis-control-panel">
- <el-button-group>
- <el-button type="primary" icon="el-icon-video-play" @click="play" v-if="!isPlaying" size="small">鎾斁</el-button>
- <el-button type="primary" icon="el-icon-video-pause" @click="pause" v-else size="small">鏆傚仠</el-button>
- <el-button type="warning" icon="el-icon-refresh-left" @click="reset" size="small">閲嶇疆</el-button>
- </el-button-group>
- <div style="margin-left: 20px; flex: 1; padding-right: 20px;">
- <el-slider v-model="sliderValue" :max="maxSliderValue" @change="sliderChange" @input="sliderInput" :format-tooltip="formatTooltip"></el-slider>
- </div>
- <div style="width: 210px; font-size: 14px; font-weight: bold; font-family: monospace; display: flex; align-items: center;">
- {{ currentTimeStr }}
- <el-popover
- placement="bottom"
- width="200"
- trigger="click"
- v-model="jumpVisible"
- @show="initJumpTime">
- <div style="text-align: center;">
- <el-time-picker
- v-model="jumpTime"
- size="small"
- placeholder="閫夋嫨鏃堕棿"
- style="width: 100%; margin-bottom: 10px;"
- :picker-options="{ selectableRange: '00:00:00 - 23:59:59' }">
- </el-time-picker>
- <el-button type="primary" size="mini" @click="confirmJump" style="width: 100%;">璺宠浆</el-button>
- </div>
- <el-button type="text" slot="reference" icon="el-icon-edit" style="margin-left: 5px; padding: 0;" title="璺宠浆鏃堕棿"></el-button>
- </el-popover>
- </div>
- <div style="margin-left: 10px;">
- <el-select v-model="playbackSpeed" style="width: 100px;" size="small" placeholder="鍊嶉��">
- <el-option :value="1" label="1x"></el-option>
- <el-option :value="5" label="5x"></el-option>
- <el-option :value="10" label="10x"></el-option>
- <el-option :value="50" label="50x"></el-option>
- <el-option :value="100" label="100x"></el-option>
- <el-option :value="200" label="200x"></el-option>
- <el-option :value="500" label="500x"></el-option>
- <el-option :value="1000" label="1000x"></el-option>
- </el-select>
- </div>
- </div>
-
- <div class="vis-container">
- <watch-crn-card v-if="visDeviceType === 'Crn'" ref="card" :auto-refresh="false" :read-only="true"></watch-crn-card>
- <watch-rgv-card v-else-if="visDeviceType === 'Rgv'" ref="card" :auto-refresh="false" :read-only="true"></watch-rgv-card>
- <watch-dual-crn-card v-else-if="visDeviceType === 'DualCrn'" ref="card" :auto-refresh="false" :read-only="true"></watch-dual-crn-card>
- <devp-card v-else-if="visDeviceType === 'Devp'" ref="card" :auto-refresh="false" :read-only="true"></devp-card>
- <div v-else style="text-align: center; padding: 50px; color: #909399;">
- 鏈煡璁惧绫诲瀷: {{ visDeviceType }}
- </div>
- </div>
- </el-dialog>
-
- <!-- Download Progress Dialog -->
<el-dialog :title="downloadDialogTitle" :visible.sync="downloadDialogVisible" width="400px" :close-on-click-modal="false" :show-close="false">
<div style="padding: 10px;">
<div style="margin-bottom: 5px; font-size: 14px;">鍘嬬缉鐢熸垚杩涘害</div>
@@ -206,11 +1360,11 @@
<script type="text/javascript" src="../../static/js/common.js" charset="utf-8"></script>
<script src="../../static/vue/js/vue.min.js"></script>
<script src="../../static/vue/element/element.js"></script>
-<script src="../../components/MonitorCardKit.js"></script>
-<script src="../../components/WatchCrnCard.js"></script>
-<script src="../../components/WatchRgvCard.js"></script>
-<script src="../../components/WatchDualCrnCard.js"></script>
-<script src="../../components/DevpCard.js"></script>
-<script type="text/javascript" src="../../static/js/deviceLogs/deviceLogs.js?v=20260309_i18n_pagefix1" charset="utf-8"></script>
+<script src="../../components/MonitorCardKit.js?v=20260318_monitor_v2"></script>
+<script src="../../components/WatchCrnCard.js?v=20260318_monitor_v2"></script>
+<script src="../../components/WatchRgvCard.js?v=20260318_monitor_v2"></script>
+<script src="../../components/WatchDualCrnCard.js?v=20260318_monitor_v2"></script>
+<script src="../../components/DevpCard.js?v=20260318_monitor_v2"></script>
+<script type="text/javascript" src="../../static/js/deviceLogs/deviceLogs.js?v=20260318_workbench_v9" charset="utf-8"></script>
</body>
</html>
--
Gitblit v1.9.1