From 8666589e792db833f6706ee102d1de2babda0231 Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Wed, 29 Apr 2026 02:10:21 +0800 Subject: [PATCH] Refine admin UI themes --- docs/theme-preview-dark.html | 191 ++++++++++++++++++ docs/theme-preview-gold.html | 190 ++++++++++++++++++ docs/theme-preview-light.html | 190 ++++++++++++++++++ internal/web/ui.go | 23 +-- internal/web/ui/assets/graph_editor.css | 30 +-- internal/web/ui/assets/style.css | 244 ++++++++++++++++------- internal/web/ui/templates/assets.html | 11 - internal/web/ui/templates/dashboard.html | 45 ----- internal/web/ui/templates/layout.html | 102 +++++++++- internal/web/ui_test.go | 119 ++++++++++- 10 files changed, 962 insertions(+), 183 deletions(-) create mode 100644 docs/theme-preview-dark.html create mode 100644 docs/theme-preview-gold.html create mode 100644 docs/theme-preview-light.html diff --git a/docs/theme-preview-dark.html b/docs/theme-preview-dark.html new file mode 100644 index 0000000..3883e18 --- /dev/null +++ b/docs/theme-preview-dark.html @@ -0,0 +1,191 @@ + + + + + + 3588 管理后台深色主题预览 + + + +
+ +
+
+
多设备视觉识别运维平台

总览

+
石墨金色 切换浅色主题
+
+
+
+

Fleet 运行态势

只保留需要运维立即判断的信息:在线率、异常节点、执行中任务、最近告警。
+
批量操作 进入设备列表
+
+
+
设备总数
12
3 个分组 / 2 个车间
+
在线率
10 / 12
83.3%,较昨日 -1
+
离线节点
2
需要检查网络或 agent
+
执行中任务
3
配置下发 2,重启 1
+
+
+
+
异常设备全部设备任务结果
+
+ + + + + + + + +
节点状态识别配置管理地址最后心跳
OPI-3588-A03
edge-a03
离线车间全功能识别10.0.0.81:1808118 分钟前
OPI-3588-B02
edge-b02
告警劳保鞋检测10.0.0.92:1808137 秒前
OPI-3588-C01
edge-c01
在线人脸识别视频流10.0.0.77:180819 秒前
OPI-3588-C04
edge-c04
任务中服务测试流10.0.0.79:1808112 秒前
+
+
+
+

识别配置资产

模板 5
+
+
+
+
13/12车间全功能识别标准
+
7/6人脸识别视频流用户
+
10/9劳保鞋检测标准
+
+
+
+
+
+

最近任务

选中行用高亮背景和描边表示,不再依赖低对比浅灰
+
+ + + + + + + +
任务标识操作状态节点数耗时
task-0428-0018下发识别配置执行中401:42
task-0428-0017重启视频分析服务失败100:19
task-0428-0016导出诊断日志成功200:43
+
+
+
+
+
+ + diff --git a/docs/theme-preview-gold.html b/docs/theme-preview-gold.html new file mode 100644 index 0000000..341cf92 --- /dev/null +++ b/docs/theme-preview-gold.html @@ -0,0 +1,190 @@ + + + + + + 3588 管理后台石墨金色主题预览 + + + +
+ +
+
+
多设备视觉识别运维平台

总览

+
蓝黑主题 浅色主题
+
+
+
+

Fleet 运行态势

石墨灰作为主体,金色只用于标题、关键数字和选中态,保持工业控制台的克制感。
+
批量操作 进入设备列表
+
+
+
设备总数
12
3 个分组 / 2 个车间
+
在线率
10 / 12
83.3%,较昨日 -1
+
离线节点
2
需要检查网络或 agent
+
执行中任务
3
配置下发 2,重启 1
+
+
+
+
异常设备全部设备任务结果
+
+ + + + + + + + +
节点状态识别配置管理地址最后心跳
OPI-3588-A03
edge-a03
离线车间全功能识别10.0.0.81:1808118 分钟前
OPI-3588-B02
edge-b02
告警劳保鞋检测10.0.0.92:1808137 秒前
OPI-3588-C01
edge-c01
在线人脸识别视频流10.0.0.77:180819 秒前
OPI-3588-C04
edge-c04
任务中服务测试流10.0.0.79:1808112 秒前
+
+
+
+

识别配置资产

模板 5
+
+
+
+
13/12车间全功能识别标准
+
7/6人脸识别视频流用户
+
10/9劳保鞋检测标准
+
+
+
+
+
+

最近任务

金色强调更有质感,但大面积使用会降低告警色的辨识度
+
+ + + + + + + +
任务标识操作状态节点数耗时
task-0428-0018下发识别配置执行中401:42
task-0428-0017重启视频分析服务失败100:19
task-0428-0016导出诊断日志成功200:43
+
+
+
+
+
+ + diff --git a/docs/theme-preview-light.html b/docs/theme-preview-light.html new file mode 100644 index 0000000..89c3456 --- /dev/null +++ b/docs/theme-preview-light.html @@ -0,0 +1,190 @@ + + + + + + 3588 管理后台浅色主题预览 + + + +
+ +
+
+
多设备视觉识别运维平台

总览

+ 切换深色主题 +
+
+
+

Fleet 运行态势

浅色主题统一为蓝灰工业面板,与蓝黑深色主题保持同一套色相。
+
批量操作 进入设备列表
+
+
+
设备总数
12
3 个分组 / 2 个车间
+
在线率
10 / 12
83.3%,较昨日 -1
+
离线节点
2
需要检查网络或 agent
+
执行中任务
3
配置下发 2,重启 1
+
+
+
+
异常设备全部设备任务结果
+
+ + + + + + + + +
节点状态识别配置管理地址最后心跳
OPI-3588-A03
edge-a03
离线车间全功能识别10.0.0.81:1808118 分钟前
OPI-3588-B02
edge-b02
告警劳保鞋检测10.0.0.92:1808137 秒前
OPI-3588-C01
edge-c01
在线人脸识别视频流10.0.0.77:180819 秒前
OPI-3588-C04
edge-c04
任务中服务测试流10.0.0.79:1808112 秒前
+
+
+
+

识别配置资产

模板 5
+
+
+
+
13/12车间全功能识别标准
+
7/6人脸识别视频流用户
+
10/9劳保鞋检测标准
+
+
+
+
+
+

最近任务

浅色主题仍保留明显选中行,不回到低对比白灰表格
+
+ + + + + + + +
任务标识操作状态节点数耗时
task-0428-0018下发识别配置执行中401:42
task-0428-0017重启视频分析服务失败100:19
task-0428-0016导出诊断日志成功200:43
+
+
+
+
+
+ + diff --git a/internal/web/ui.go b/internal/web/ui.go index 826b94f..b927b8a 100644 --- a/internal/web/ui.go +++ b/internal/web/ui.go @@ -373,6 +373,8 @@ func tablerIconSVG(name string) string { "assets": ``, "audit": ``, "system": ``, + "theme": ``, + "bell": ``, "online": ``, "detail": ``, "control": ``, @@ -432,7 +434,6 @@ func (u *UI) Routes() (chi.Router, error) { r.Get("/devices", u.pageDevices) r.Get("/devices/{id}/control", u.pageDeviceControl) r.Get("/assets", u.pageAssets) - r.Post("/assets/import", u.actionAssetsImport) r.Get("/assets/templates", u.pageAssetTemplates) r.Post("/assets/templates/create", u.actionAssetTemplateCreate) r.Get("/assets/templates/{name}", u.pageAssetTemplate) @@ -506,6 +507,8 @@ func (u *UI) render(w http.ResponseWriter, r *http.Request, content string, data data.ContentHTML = template.HTML(buf.String()) w.Header().Set("Content-Type", "text/html; charset=utf-8") + w.Header().Set("Cache-Control", "no-store, max-age=0") + w.Header().Set("Pragma", "no-cache") if err := u.tpl.ExecuteTemplate(w, "layout", data); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return @@ -1179,24 +1182,6 @@ func (u *UI) pageAssets(w http.ResponseWriter, r *http.Request) { u.render(w, r, "assets", data) } -func (u *UI) actionAssetsImport(w http.ResponseWriter, r *http.Request) { - data := u.assetPageData("overview") - if u.preview == nil { - data.Error = "配置资产服务未初始化" - u.render(w, r, "assets", data) - return - } - result, err := u.preview.ImportAssetsFromMediaRepo() - if err != nil { - data.Error = err.Error() - u.render(w, r, "assets", data) - return - } - data = u.assetPageData("overview") - data.Message = fmt.Sprintf("已导入 %d 个业务配置、%d 个叠加项。标准模板保持目录只读,不写入数据库。", result.Profiles, result.Overlays) - u.render(w, r, "assets", data) -} - func (u *UI) pageAssetTemplates(w http.ResponseWriter, r *http.Request) { data := u.assetPageData("templates") data.Message = strings.TrimSpace(r.URL.Query().Get("msg")) diff --git a/internal/web/ui/assets/graph_editor.css b/internal/web/ui/assets/graph_editor.css index 62b2fab..7ee167c 100644 --- a/internal/web/ui/assets/graph_editor.css +++ b/internal/web/ui/assets/graph_editor.css @@ -1,38 +1,38 @@ .graph-editor{display:grid;grid-template-columns:210px minmax(0,1fr) 280px;gap:12px;min-height:760px} -.graph-sidebar,.graph-inspector{border:1px solid var(--border);border-radius:8px;background:var(--surface-soft);padding:12px} +.graph-sidebar,.graph-inspector{border:1px solid var(--border);border-radius:var(--radius);background:var(--surface-soft);padding:12px} .graph-sidebar h3,.graph-inspector h3{font-size:13px;margin:0 0 10px;font-weight:600} .graph-node-palette-list{display:flex;flex-direction:column;gap:10px;max-height:720px;overflow:auto;padding-right:2px} .graph-node-palette-category{font-size:11px;color:var(--muted);margin:0 0 6px} -.graph-node-palette{width:100%;padding:7px 8px;border:1px solid var(--border);border-radius:7px;background:#fff;margin:0 0 6px;cursor:pointer;font-size:12px;text-align:left;display:flex;align-items:center;justify-content:flex-start;gap:8px} -.graph-node-palette:hover{border-color:var(--border-strong);background:#f8fafc} -.graph-node-palette-icon{display:inline-flex;align-items:center;justify-content:center;width:25px;height:25px;border-radius:7px;background:#e0f2fe;color:#075985;font-size:10px;letter-spacing:0;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;flex:0 0 auto} +.graph-node-palette{width:100%;padding:7px 8px;border:1px solid var(--border);border-radius:var(--radius);background:var(--surface);color:var(--text);margin:0 0 6px;cursor:pointer;font-size:12px;text-align:left;display:flex;align-items:center;justify-content:flex-start;gap:8px} +.graph-node-palette:hover{border-color:var(--border-strong);background:var(--surface-strong)} +.graph-node-palette-icon{display:inline-flex;align-items:center;justify-content:center;width:25px;height:25px;border-radius:var(--radius);background:var(--surface-strong);color:var(--primary);font-size:10px;letter-spacing:0;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;flex:0 0 auto} .graph-node-palette-text{display:flex;flex-direction:column;align-items:flex-start;min-width:0;gap:1px} .graph-node-palette-text span{white-space:nowrap;overflow:hidden;text-overflow:ellipsis} .graph-node-palette-text small{font-size:10px;color:var(--muted);font-weight:400;white-space:nowrap;overflow:hidden;text-overflow:ellipsis} -.graph-canvas-wrap{position:relative;border:1px solid var(--border);border-radius:8px;background:#fff;overflow:auto;height:min(80vh,980px);min-height:760px} +.graph-canvas-wrap{position:relative;border:1px solid var(--border);border-radius:var(--radius);background:var(--surface);overflow:auto;height:min(80vh,980px);min-height:760px} .graph-canvas-toolbar{position:absolute;top:10px;right:10px;z-index:2;display:flex;gap:8px} -.graph-canvas-toolbar .btn{background:rgba(255,255,255,.92);font-size:12px;padding:6px 10px} +.graph-canvas-toolbar .btn{background:var(--button-soft);font-size:12px;padding:6px 10px} .graph-canvas{display:block;min-width:100%;min-height:760px} -.graph-node rect{fill:#fff;stroke:#cbd5e1;stroke-width:1.4} +.graph-node rect{fill:var(--surface);stroke:var(--border-strong);stroke-width:1.4} .graph-node{cursor:grab} -.graph-node text{font-size:12px;fill:#111827;pointer-events:none} -.graph-node .graph-node-icon{font-size:10px;fill:#0284c7;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace} -.graph-node .graph-node-type{font-size:11px;fill:#6b7280} -.graph-node.selected rect{stroke:#2563eb;stroke-width:2} -.graph-edge{stroke:#94a3b8;stroke-width:1.8;fill:none;cursor:pointer} +.graph-node text{font-size:12px;fill:var(--text);pointer-events:none} +.graph-node .graph-node-icon{font-size:10px;fill:var(--primary);font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace} +.graph-node .graph-node-type{font-size:11px;fill:var(--muted)} +.graph-node.selected rect{stroke:var(--primary);stroke-width:2} +.graph-edge{stroke:var(--border-strong);stroke-width:1.8;fill:none;cursor:pointer} .graph-edge-hit{stroke:transparent;stroke-width:16;fill:none;cursor:pointer} -.graph-edge.selected{stroke:#2563eb;stroke-width:2.2} +.graph-edge.selected{stroke:var(--primary);stroke-width:2.2} .graph-empty-inspector{font-size:12px;color:var(--muted)} .graph-node-form,.graph-edge-form{display:flex;flex-direction:column;gap:10px} .graph-node-form label,.graph-edge-form label{display:block;width:100%} .graph-node-form label span,.graph-edge-form label span{display:block;font-size:11px;color:var(--muted);margin-bottom:4px} .graph-node-form textarea,.graph-edge-form textarea{display:block;width:100%;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px;line-height:1.45;resize:vertical;min-height:96px} -.graph-inspector-title{font-size:12px;color:#0f172a;margin-top:2px} +.graph-inspector-title{font-size:12px;color:var(--text);margin-top:2px} .graph-form-hint{font-size:11px;color:var(--muted);line-height:1.45} .graph-typed-param-fields{display:flex;flex-direction:column;gap:10px} .graph-typed-param-fields:empty::before{content:"此节点暂无常用参数";display:block;font-size:11px;color:var(--muted);padding:8px 0} .graph-advanced-json{border-top:1px solid var(--border);padding-top:8px} .graph-advanced-json label{display:block;width:100%} -.graph-advanced-json summary{cursor:pointer;font-size:12px;color:#0f172a;margin-bottom:8px} +.graph-advanced-json summary{cursor:pointer;font-size:12px;color:var(--text);margin-bottom:8px} .graph-save-form{display:inline-flex;align-items:center;gap:8px;flex-wrap:wrap} .graph-save-form input[type="text"]{min-width:180px;max-width:240px} diff --git a/internal/web/ui/assets/style.css b/internal/web/ui/assets/style.css index bd176d4..2183c4f 100644 --- a/internal/web/ui/assets/style.css +++ b/internal/web/ui/assets/style.css @@ -1,28 +1,105 @@ *{box-sizing:border-box} :root{ - --bg:#f5f7fa; - --surface:#ffffff; - --surface-soft:#fafbfc; - --sidebar:#1f2937; - --sidebar-hover:#2b3646; - --border:#e5e7eb; - --border-strong:#d1d5db; - --text:#111827; - --muted:#6b7280; - --primary:#475569; - --primary-strong:#334155; - --primary-strong-hover:#273444; - --button-soft:#e5e7eb; - --button-soft-hover:#dbe1e8; - --button-soft-text:#374151; - --danger-soft:#f3e8e8; - --danger-soft-hover:#eadbdb; - --danger-soft-text:#7c4a4a; - --teal:#0f766e; - --green:#15803d; - --amber:#b45309; - --red:#b91c1c; - --shadow:0 10px 30px rgba(15,23,42,.06); + --radius:4px; + --bg:#090909; + --surface:#121212; + --surface-soft:#1a1a1a; + --surface-strong:#242424; + --topbar:#0e0e0e; + --sidebar:#070707; + --sidebar-hover:#171717; + --sidebar-text:#eeeeee; + --sidebar-muted:#8d8d8d; + --border:#303030; + --border-strong:#565656; + --text:#f4f4f4; + --muted:#bcbcbc; + --table-text:#c8c8c8; + --table-link:#f4f4f4; + --primary:#dddddd; + --primary-strong:#2b2b2b; + --primary-strong-hover:#363636; + --button-soft:#1d1d1d; + --button-soft-hover:#292929; + --button-soft-text:#f4f4f4; + --input-bg:#101010; + --selected-row:#303030; + --danger-soft:#242424; + --danger-soft-hover:#303030; + --danger-soft-text:#dddddd; + --teal:#dddddd; + --green:#66c98f; + --amber:#d8a657; + --red:#e46f72; + --shadow:0 18px 44px rgba(0,0,0,.32); +} + +body[data-theme="blue-light"]{ + --bg:#dfe7f0; + --surface:#eef4fa; + --surface-soft:#e5edf6; + --surface-strong:#d5e1ef; + --topbar:#e8eef6; + --sidebar:#0d1520; + --sidebar-hover:#1a2a43; + --sidebar-text:#d7e2ee; + --sidebar-muted:#728299; + --border:#b5c4d6; + --border-strong:#8499b3; + --text:#172333; + --muted:#4f6177; + --table-text:#172333; + --table-link:#2d6fb5; + --primary:#2d6fb5; + --primary-strong:#245f9f; + --primary-strong-hover:#1f528a; + --button-soft:#d5e1ef; + --button-soft-hover:#c8d8eb; + --button-soft-text:#172333; + --input-bg:#eef4fa; + --selected-row:#c7d9ee; + --danger-soft:#f0dada; + --danger-soft-hover:#e8caca; + --danger-soft-text:#a43f3f; + --teal:#2d6fb5; + --green:#245f9f; + --amber:#9b650e; + --red:#a43f3f; + --shadow:0 14px 34px rgba(35,50,60,.12); +} + +body[data-theme="graphite-gold"]{ + --bg:#171717; + --surface:#202020; + --surface-soft:#262626; + --surface-strong:#303030; + --topbar:#1b1b1b; + --sidebar:#121212; + --sidebar-hover:#2a271f; + --sidebar-text:#d1cbc1; + --sidebar-muted:#7f796e; + --border:#3c3c3c; + --border-strong:#5a5447; + --text:#ece7da; + --muted:#c9c0b3; + --table-text:#eee5d6; + --table-link:#d3a84f; + --primary:#d3a84f; + --primary-strong:#5d431e; + --primary-strong-hover:#745426; + --button-soft:#303030; + --button-soft-hover:#39352d; + --button-soft-text:#ece7da; + --input-bg:#1b1b1b; + --selected-row:#332d20; + --danger-soft:#301d1c; + --danger-soft-hover:#3b2423; + --danger-soft-text:#d66a63; + --teal:#d3a84f; + --green:#f0cf7a; + --amber:#d79a3b; + --red:#d66a63; + --shadow:0 18px 44px rgba(0,0,0,.3); } body{margin:0;background:var(--bg);color:var(--text);font:13px/1.5 var(--tblr-font-sans-serif,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif)} @@ -31,41 +108,47 @@ a:hover{text-decoration:none} code,.mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace} .app-shell{display:grid;grid-template-columns:240px minmax(0,1fr);min-height:100vh} -.sidebar{background:var(--sidebar);color:#f3f4f6;display:flex;flex-direction:column;padding:18px 14px;position:sticky;top:0;height:100vh} +.sidebar{background:var(--sidebar);color:var(--sidebar-text);display:flex;flex-direction:column;padding:18px 14px;position:sticky;top:0;height:100vh} .brand-block{display:flex;gap:12px;align-items:center;padding:4px 8px 20px} -.brand-mark{width:40px;height:40px;border-radius:8px;background:#111827;border:1px solid rgba(255,255,255,.08);display:grid;place-items:center;font-size:13px;font-weight:700} +.brand-mark{width:38px;height:38px;border-radius:var(--radius);background:rgba(0,0,0,.28);border:1px solid rgba(255,255,255,.12);display:grid;place-items:center;font-size:13px;font-weight:600;color:var(--primary)} .brand-title{font-size:14px;font-weight:600} -.brand-subtitle{font-size:11px;color:#9ca3af;margin-top:2px} .side-nav{display:flex;flex-direction:column;gap:4px} -.nav-section{padding:14px 10px 6px;font-size:11px;color:#9ca3af;text-transform:uppercase;letter-spacing:.06em} -.side-nav a{display:flex;align-items:center;gap:10px;padding:9px 10px;border-radius:8px;color:#e5e7eb;font-size:12px;font-weight:500} +.nav-section{padding:14px 10px 6px;font-size:11px;color:var(--sidebar-muted);text-transform:uppercase;letter-spacing:.06em} +.side-nav a{display:flex;align-items:center;gap:10px;padding:9px 10px;border-radius:var(--radius);color:var(--sidebar-text);font-size:13px;font-weight:500} .side-nav a:hover{background:var(--sidebar-hover)} -.nav-icon{width:28px;height:24px;border-radius:6px;border:1px solid rgba(255,255,255,.1);display:grid;place-items:center;font-size:10px;color:#d1d5db} +.nav-icon{width:28px;height:24px;border-radius:3px;border:1px solid rgba(255,255,255,.12);display:grid;place-items:center;font-size:10px;color:var(--primary)} .nav-icon .ui-icon{width:14px;height:14px;stroke-width:1.75} -.sidebar-footer{margin-top:auto;padding:14px 8px 0;border-top:1px solid rgba(255,255,255,.08)} -.sidebar-note{font-size:12px;color:#9ca3af;line-height:1.5} .workspace{min-width:0} -.topbar{height:72px;background:rgba(255,255,255,.92);backdrop-filter:blur(10px);border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;padding:0 28px;position:sticky;top:0;z-index:10} -.topbar h1{margin:4px 0 0;font-size:18px;font-weight:600} +.topbar{height:60px;background:var(--topbar);border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;padding:0 28px;position:sticky;top:0;z-index:100} +.topbar h1{margin:0;font-size:18px;font-weight:600} .crumb,.eyebrow{font-size:11px;color:var(--muted);text-transform:uppercase;letter-spacing:.08em} -.topbar-actions{display:flex;gap:8px} +.topbar-actions{display:flex;align-items:center;gap:8px} +.topbar-icon-btn{position:relative;display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;min-height:32px;padding:0;border-radius:var(--radius);border:1px solid var(--border-strong);background:var(--button-soft);color:var(--button-soft-text)} +.topbar-icon-btn:hover{background:var(--button-soft-hover);border-color:var(--primary);color:var(--text)} +.topbar-icon-btn .ui-icon{width:16px;height:16px} +.topbar-dot{position:absolute;right:6px;top:6px;width:6px;height:6px;border-radius:999px;background:var(--red)} +.theme-menu{position:relative} +.theme-menu-panel{position:fixed;right:28px;top:54px;min-width:132px;padding:6px;border:1px solid var(--border-strong);border-radius:var(--radius);background:var(--surface);box-shadow:var(--shadow);z-index:9999} +.theme-menu-panel button{display:flex;width:100%;justify-content:flex-start;min-height:28px;border-color:transparent;background:transparent;color:var(--text)} +.theme-menu-panel button:hover,.theme-menu-panel button[aria-pressed="true"]{background:var(--surface-strong);border-color:var(--border)} main{padding:24px 28px 36px;max-width:1440px} .hero-band{display:flex;align-items:flex-end;justify-content:space-between;gap:18px;margin-bottom:18px;padding:8px 2px} .hero-band h2{margin:4px 0 6px;font-size:21px;font-weight:600;line-height:1.25} -.card{background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:18px 20px;margin:16px 0;box-shadow:var(--shadow)} -.card h2{margin:0 0 8px;font-size:16px;font-weight:600} -.card h3{margin:0 0 6px;font-size:14px;font-weight:600} +.card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:16px 18px;margin:14px 0;box-shadow:var(--shadow);color:var(--text)} +.card h2{margin:0 0 8px;font-size:15px;font-weight:600;color:var(--text)} +.card h3{margin:0 0 6px;font-size:13px;font-weight:600;color:var(--text)} .section-title{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;margin-bottom:12px} .title-with-icon{display:flex;align-items:center;gap:8px} -.title-with-icon .ui-icon{width:16px;height:16px;color:#475569} +.title-with-icon .ui-icon{width:16px;height:16px;color:var(--primary)} .muted{color:var(--muted)} .small{font-size:12px} +.form-hint{color:var(--muted);font-size:12px;line-height:1.35} .btn,button,input,select,textarea{font:inherit} -.btn,button{display:inline-flex;align-items:center;justify-content:center;gap:5px;padding:5px 10px;border-radius:7px;border:1px solid var(--border-strong);background:var(--button-soft);color:var(--button-soft-text);cursor:pointer;font-size:11px;font-weight:500;line-height:1.15;min-height:32px;transition:background-color .16s ease,border-color .16s ease,color .16s ease,box-shadow .16s ease} +.btn,button{display:inline-flex;align-items:center;justify-content:center;gap:5px;padding:5px 10px;border-radius:var(--radius);border:1px solid var(--border-strong);background:var(--button-soft);color:var(--button-soft-text);cursor:pointer;font-size:11px;font-weight:500;line-height:1.15;min-height:30px;transition:background-color .16s ease,border-color .16s ease,color .16s ease,box-shadow .16s ease} .btn:hover,button:hover{background:var(--button-soft-hover);border-color:#c3ccd6} .btn.primary,button.primary{background:var(--primary-strong);border-color:var(--primary-strong);color:#f8fafc} .btn.primary:hover,button.primary:hover{background:var(--primary-strong-hover);border-color:var(--primary-strong-hover)} @@ -74,36 +157,39 @@ main{padding:24px 28px 36px;max-width:1440px} .btn.danger,button.danger{background:var(--danger-soft);border-color:#e7d5d5;color:var(--danger-soft-text)} .btn.danger:hover,button.danger:hover{background:var(--danger-soft-hover);border-color:#dbc4c4} button:disabled,.btn:disabled{opacity:.55;cursor:not-allowed} -input,select,textarea{width:100%;padding:8px 10px;border-radius:8px;border:1px solid var(--border-strong);background:#fff;color:var(--text);font-size:12px} +input,select,textarea{width:100%;padding:8px 10px;border-radius:var(--radius);border:1px solid var(--border-strong);background:var(--input-bg);color:var(--text);font-size:12px} textarea{min-height:140px;line-height:1.55} -.msg,.error{padding:12px 14px;border-radius:8px;margin:12px 0;white-space:pre-wrap} -.msg{background:#ecfdf5;border:1px solid #bbf7d0;color:#166534} -.error{background:#fef2f2;border:1px solid #fecaca;color:#991b1b} +.msg,.error{padding:12px 14px;border-radius:var(--radius);margin:12px 0;white-space:pre-wrap} +.msg{background:var(--surface-soft);border:1px solid var(--border-strong);color:var(--green)} +.error{background:var(--danger-soft);border:1px solid var(--danger-soft-hover);color:var(--danger-soft-text)} .stats{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:12px} -.stat{padding:16px;border:1px solid var(--border);border-radius:8px;background:var(--surface);box-shadow:var(--shadow)} +.stat{padding:14px;border:1px solid var(--border);border-radius:var(--radius);background:var(--surface);box-shadow:var(--shadow)} .stat .k{font-size:12px;color:var(--muted)} .metric-label{display:flex;align-items:center;gap:6px} .metric-label .ui-icon{width:14px;height:14px} -.stat .v{margin-top:8px;font-size:22px;font-weight:700;line-height:1} +.stat .v{margin-top:8px;font-size:22px;font-weight:400;line-height:1;color:var(--text)} .stat .hint{margin-top:6px;font-size:12px;color:var(--muted)} .accent-teal .v{color:var(--teal)} .accent-green .v{color:var(--green)} -.accent-slate .v{color:#475569} +.accent-slate .v{color:var(--muted)} .accent-amber .v{color:var(--amber)} .table-tools{display:flex;gap:8px;align-items:center} -.table-wrap{overflow:auto;border:1px solid var(--border);border-radius:8px;background:#fff} +.table-wrap{overflow:auto;border:1px solid var(--border);border-radius:var(--radius);background:var(--surface)} table{width:100%;border-collapse:collapse;min-width:900px} -th,td{padding:13px 12px;border-bottom:1px solid var(--border);text-align:left;vertical-align:top} -th{background:var(--surface-soft);font-size:12px;font-weight:600;color:#4b5563} -tbody tr:hover{background:#f9fafb} +th,td{padding:8px 12px;border-bottom:1px solid var(--border);text-align:left;vertical-align:middle;line-height:1.25;color:var(--table-text)} +th{background:var(--surface-soft);font-size:12px;font-weight:600;color:var(--muted)} +.table-wrap td a{color:var(--table-link)} +.table-wrap td a:hover{color:var(--text)} +tbody tr:hover{background:var(--surface-soft)} +tbody tr.selected{background:var(--selected-row);outline:1px solid var(--primary);outline-offset:-1px} .device-cell{display:flex;align-items:center;gap:12px;min-width:220px} -.device-avatar{width:34px;height:34px;border-radius:8px;background:#e5eefc;color:#1d4ed8;display:grid;place-items:center} +.device-avatar{width:32px;height:32px;border-radius:var(--radius);background:var(--surface-strong);color:var(--primary);display:grid;place-items:center} .device-avatar .ui-icon{width:18px;height:18px} -.device-name{font-size:13px;font-weight:600} +.device-name{font-size:13px;font-weight:400;color:var(--table-link)} .device-meta-line{display:flex;flex-wrap:wrap;gap:8px;margin-top:3px;font-size:11px;color:var(--muted)} .status-dot{display:inline-block;width:8px;height:8px;border-radius:999px;margin-right:6px;vertical-align:middle} .status-dot.ok{background:var(--green)} @@ -114,11 +200,11 @@ tbody tr:hover{background:#f9fafb} .state-stack{display:flex;flex-direction:column;gap:6px} .state-row{display:flex;align-items:center;flex-wrap:wrap;gap:8px} -.pill{display:inline-flex;align-items:center;padding:3px 8px;border-radius:999px;border:1px solid var(--border);background:#f3f4f6;color:#374151;font-size:11px;font-weight:600} -.pill.ok{background:#ecfdf5;border-color:#bbf7d0;color:#166534} -.pill.bad{background:#fef2f2;border-color:#fecaca;color:#991b1b} -.pill.run{background:#eff6ff;border-color:#bfdbfe;color:#1d4ed8} -.pill.warn{background:#fffbeb;border-color:#fde68a;color:#92400e} +.pill{display:inline-flex;align-items:center;min-height:20px;padding:1px 6px;border-radius:3px;border:1px solid var(--border);background:var(--surface-strong);color:var(--button-soft-text);font-size:11px;font-weight:500;line-height:1.1} +.pill.ok{background:var(--surface-soft);border-color:var(--border-strong);color:var(--green)} +.pill.bad{background:var(--danger-soft);border-color:var(--danger-soft-hover);color:var(--danger-soft-text)} +.pill.run{background:var(--surface-soft);border-color:var(--border-strong);color:var(--primary)} +.pill.warn{background:var(--surface-soft);border-color:var(--border-strong);color:var(--amber)} .actions{display:flex;flex-wrap:wrap;gap:8px} .actions.compact{gap:6px} @@ -126,7 +212,7 @@ tbody tr:hover{background:#f9fafb} .btn .ui-icon{width:14px;height:14px} .stack{flex-direction:column;align-items:flex-start} .device-context-head{display:flex;align-items:center;gap:12px} -.device-context-icon{width:34px;height:34px;border-radius:8px;background:#e5eefc;color:#1d4ed8;display:grid;place-items:center} +.device-context-icon{width:32px;height:32px;border-radius:var(--radius);background:var(--surface-strong);color:var(--primary);display:grid;place-items:center} .device-context-icon .ui-icon{width:18px;height:18px} .device-tab-wrap{margin-top:16px} .asset-tab-wrap{margin-top:0} @@ -144,71 +230,76 @@ tbody tr:hover{background:#f9fafb} .quad-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px} .control-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px} .selector-card .actions{margin-top:auto} -.panel-block{border:1px solid var(--border);border-radius:8px;background:var(--surface-soft);padding:16px} +.panel-block{border:1px solid var(--border);border-radius:var(--radius);background:var(--surface-soft);padding:14px} .panel-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;margin-bottom:14px} .field-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin-bottom:14px} .field-grid label span{display:block;margin-bottom:6px;font-size:12px;color:var(--muted)} .field-grid .full{grid-column:1/-1} -.field-grid input,.field-grid select,.field-grid textarea{width:100%;padding:9px 10px;border:1px solid var(--border);border-radius:8px;background:#fff;color:var(--text);font:inherit} +.field-grid input,.field-grid select,.field-grid textarea{width:100%;padding:9px 10px;border:1px solid var(--border);border-radius:var(--radius);background:var(--input-bg);color:var(--text);font:inherit} .field-grid textarea{resize:vertical;min-height:120px} .code-input{font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:12px;line-height:1.5} .info-list{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px} -.info-list>div{padding:10px 12px;border:1px solid var(--border);border-radius:8px;background:var(--surface-soft)} +.info-list>div{padding:10px 12px;border:1px solid var(--border);border-radius:var(--radius);background:var(--surface-soft)} .info-list span{display:block;margin-bottom:5px;font-size:12px;color:var(--muted)} -.info-list strong{display:block;font-size:13px;font-weight:600;line-height:1.45} +.info-list strong{display:block;font-size:13px;font-weight:400;line-height:1.45} .editable-line{display:flex;align-items:center;justify-content:space-between;gap:8px} .editable-line strong{margin:0;flex:1 1 auto} .icon-only{display:inline-flex;align-items:center;justify-content:center;padding:0;min-width:28px;width:28px;height:28px} .icon-only .ui-icon{width:14px;height:14px;margin:0 auto} -.btn.ghost.icon-only{background:transparent;border-color:transparent;color:#64748b} -.btn.ghost.icon-only:hover{background:#eef2f7;border-color:#dbe1e8;color:#334155} +.btn.ghost.icon-only{background:transparent;border-color:transparent;color:var(--muted)} +.btn.ghost.icon-only:hover{background:var(--surface-strong);border-color:var(--border);color:var(--text)} .inline-edit-form{display:flex;align-items:center;gap:8px} .inline-edit-form input{flex:1 1 auto;min-width:0} .compact-list{grid-template-columns:1fr} .summary-strip{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px} .control-summary{margin-bottom:16px} -.summary-chip{padding:12px;border:1px solid var(--border);border-radius:8px;background:var(--surface-soft)} +.summary-chip{padding:12px;border:1px solid var(--border);border-radius:var(--radius);background:var(--surface-soft)} .summary-chip-label{font-size:12px;color:var(--muted)} .summary-chip-value{margin-top:6px;font-size:13px;font-weight:600} -.asset-stat{margin-top:12px;font-size:20px;font-weight:700;color:#111827} +.asset-stat{margin-top:12px;font-size:20px;font-weight:400;color:var(--text)} .asset-list{margin-top:14px;border-top:1px solid var(--border)} .asset-row{display:flex;justify-content:space-between;gap:12px;padding:10px 0;border-bottom:1px solid var(--border)} .asset-link{color:inherit} -.asset-link:hover{background:#f8fafc} +.asset-link>span:first-child{color:var(--table-link)} +.asset-link:hover{background:var(--surface-soft)} .truncate-cell{max-width:260px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis} .muted-row{opacity:.62} -.asset-tabs .nav-link{font-size:12px;font-weight:500;color:#64748b} -.asset-tabs .nav-link:hover{color:#334155} -.asset-tabs .nav-link.active{color:#111827} +.asset-tabs .nav-link{font-size:12px;font-weight:500;color:var(--muted)} +.asset-tabs .nav-link:hover{color:var(--text)} +.asset-tabs .nav-link.active{color:var(--text)} +.nav-tabs{border-color:var(--border)} +.nav-tabs .nav-link{border-color:var(--border)!important;border-radius:var(--radius) var(--radius) 0 0;background:var(--surface-soft)!important;color:var(--muted)!important} +.nav-tabs .nav-link:hover{border-color:var(--border-strong)!important;background:var(--surface-strong)!important;color:var(--text)!important} +.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{border-color:var(--border)!important;background:var(--surface)!important;color:var(--text)!important} .profile-editor-tabs .tab-content>.card{margin-top:-1px} .nested-section{margin-top:14px;padding:14px} .nested-section pre{margin-top:10px} .profile-instance-editor .field-grid{margin-top:14px} -.empty-state{padding:18px;border:1px dashed var(--border-strong);border-radius:8px;background:var(--surface-soft)} +.empty-state{padding:18px;border:1px dashed var(--border-strong);border-radius:var(--radius);background:var(--surface-soft)} .empty-state.compact{padding:14px} .empty-title{font-size:13px;font-weight:600;margin-bottom:6px} .collapsible summary,.collapsible-inline summary{cursor:pointer;font-weight:600} -pre{margin-top:12px;padding:12px;border-radius:8px;border:1px solid #1f2937;background:#111827;color:#e5e7eb;overflow:auto;white-space:pre-wrap} +pre{margin-top:12px;padding:12px;border-radius:var(--radius);border:1px solid var(--border);background:var(--sidebar);color:var(--sidebar-text);overflow:auto;white-space:pre-wrap} .row{display:flex;gap:12px;flex-wrap:wrap} .row>*{flex:1 1 220px} .subnav{display:flex;gap:8px;flex-wrap:wrap} .device-tab-wrap{margin-top:16px} -.device-tabs .nav-link{font-size:12px;font-weight:500;color:#64748b} -.device-tabs .nav-link:hover{color:#334155} -.device-tabs .nav-link.active{color:#111827} +.device-tabs .nav-link{font-size:12px;font-weight:500;color:var(--muted)} +.device-tabs .nav-link:hover{color:var(--text)} +.device-tabs .nav-link.active{color:var(--text)} .device-tab-card{margin-top:0} .device-panel-body{padding:16px} .device-panel-body>.card,.device-panel-body>details.card{margin:0 0 16px} .device-panel-body>.card:last-child,.device-panel-body>details.card:last-child{margin-bottom:0} .ui-icon{display:block;flex:0 0 auto} -.batch-toolbar{display:flex;align-items:flex-start;justify-content:space-between;gap:14px;padding:14px 16px;border:1px solid var(--border);border-radius:8px;background:var(--surface-soft);margin:0 0 12px} +.batch-toolbar{display:flex;align-items:flex-start;justify-content:space-between;gap:14px;padding:14px 16px;border:1px solid var(--border);border-radius:var(--radius);background:var(--surface-soft);margin:0 0 12px} .batch-toolbar-count{font-size:13px;font-weight:600} .batch-toolbar .actions{justify-content:flex-end} .batch-toolbar .actions .btn,.batch-toolbar .actions button{white-space:nowrap} @@ -219,6 +310,7 @@ pre{margin-top:12px;padding:12px;border-radius:8px;border:1px solid #1f2937;back .app-shell{grid-template-columns:1fr} .sidebar{position:relative;height:auto} .topbar{position:relative;height:auto;padding:18px;flex-direction:column;align-items:flex-start;gap:12px} + .theme-menu{display:flex;flex-direction:column;align-items:flex-start} main{padding:18px} .stats,.detail-grid,.device-selector-grid,.quad-grid,.control-grid,.summary-strip,.info-list,.field-grid{grid-template-columns:1fr} .hero-band{flex-direction:column;align-items:flex-start} diff --git a/internal/web/ui/templates/assets.html b/internal/web/ui/templates/assets.html index 532f76e..9f1da93 100644 --- a/internal/web/ui/templates/assets.html +++ b/internal/web/ui/templates/assets.html @@ -51,17 +51,6 @@ -
-
-
-

{{icon "assets"}}资产操作

-
-
- -
-
-
-
diff --git a/internal/web/ui/templates/dashboard.html b/internal/web/ui/templates/dashboard.html index b044ec2..57cf4bb 100644 --- a/internal/web/ui/templates/dashboard.html +++ b/internal/web/ui/templates/dashboard.html @@ -3,11 +3,6 @@

全局 KPI

-
只看 fleet 级运行态势、最近任务和需要关注的异常设备。
-
-
@@ -18,46 +13,6 @@
-
-
-

视频分析概览

-
当前版本基于节点在线状态和视频分析服务查询入口汇总。
- -
-
-

运维工作流

-
按常见现场操作顺序进入对应页面。
- -
-
- -
-
-

快速入口

- -
-
-

任务健康

-
-
执行中任务
{{.RunningTaskCount}}
-
失败任务
{{.FailedTaskCount}}
-
成功任务
{{.SuccessTaskCount}}
-
-
-
-

异常设备

diff --git a/internal/web/ui/templates/layout.html b/internal/web/ui/templates/layout.html index 9b94a25..7b57b19 100644 --- a/internal/web/ui/templates/layout.html +++ b/internal/web/ui/templates/layout.html @@ -6,17 +6,16 @@ {{.Title}} - - + + - +
-
-
多设备视觉识别运维平台
+

{{.Title}}

+
+
+ + +
+ + {{icon "bell"}} + + + + {{icon "system"}} + +
{{if .Error}}
{{.Error}}
{{end}} @@ -48,6 +62,62 @@