feat: rewire primary UI navigation
This commit is contained in:
parent
ca004e07a7
commit
661b0b0afd
@ -299,7 +299,9 @@ func tablerIconSVG(name string) string {
|
||||
"preview": `<svg xmlns="http://www.w3.org/2000/svg" class="icon ui-icon" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 5c-7.633 0 -9 7 -9 7s1.367 7 9 7s9 -7 9 -7s-1.367 -7 -9 -7"/><path d="M12 12m-3 0a3 3 0 1 0 6 0a3 3 0 1 0 -6 0"/></svg>`,
|
||||
"apply": `<svg xmlns="http://www.w3.org/2000/svg" class="icon ui-icon" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M7 12l3 3l7 -7"/><path d="M21 12c0 4.97 -4.03 9 -9 9s-9 -4.03 -9 -9s4.03 -9 9 -9"/></svg>`,
|
||||
"service": `<svg xmlns="http://www.w3.org/2000/svg" class="icon ui-icon" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M4 13h5"/><path d="M12 16v5"/><path d="M16 4l0 5"/><path d="M20 8h-5"/><path d="M4 9h1a2 2 0 0 1 2 2v4a2 2 0 0 1 -2 2h-1"/><path d="M9 4h1a2 2 0 0 1 2 2v1a2 2 0 0 1 -2 2h-1"/><path d="M15 15h1a2 2 0 0 1 2 2v1a2 2 0 0 1 -2 2h-1"/><path d="M9 13h1a2 2 0 0 1 2 2v1a2 2 0 0 1 -2 2h-1"/><path d="M15 4h1a2 2 0 0 1 2 2v4a2 2 0 0 1 -2 2h-1"/></svg>`,
|
||||
"task": `<svg xmlns="http://www.w3.org/2000/svg" class="icon ui-icon" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M9 11l3 3l8 -8"/><path d="M20 12v7a1 1 0 0 1 -1 1h-14a1 1 0 0 1 -1 -1v-14a1 1 0 0 1 1 -1h9"/></svg>`,
|
||||
"result": `<svg xmlns="http://www.w3.org/2000/svg" class="icon ui-icon" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M4 19l16 0"/><path d="M4 15l4 -6l4 2l4 -5l4 9"/></svg>`,
|
||||
"logs": `<svg xmlns="http://www.w3.org/2000/svg" class="icon ui-icon" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M4 19l16 0"/><path d="M4 15l4 -6l4 2l4 -5l4 9"/></svg>`,
|
||||
"meta": `<svg xmlns="http://www.w3.org/2000/svg" class="icon ui-icon" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M5 4m0 2a2 2 0 1 0 4 0a2 2 0 1 0 -4 0"/><path d="M3 17m0 2a2 2 0 1 0 4 0a2 2 0 1 0 -4 0"/><path d="M17 17m0 2a2 2 0 1 0 4 0a2 2 0 1 0 -4 0"/><path d="M7 6h10"/><path d="M5 8v9"/><path d="M7 19h10"/><path d="M17 8v9"/></svg>`,
|
||||
"template": `<svg xmlns="http://www.w3.org/2000/svg" class="icon ui-icon" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M6 4h12a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2v-12a2 2 0 0 1 2 -2"/><path d="M9 8h6"/><path d="M9 12h6"/><path d="M9 16h4"/></svg>`,
|
||||
"profile": `<svg xmlns="http://www.w3.org/2000/svg" class="icon ui-icon" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 12m-3 0a3 3 0 1 0 6 0a3 3 0 1 0 -6 0"/><path d="M12 3c2.755 0 5.455 .638 7.407 1.758a2 2 0 0 1 1.002 1.737v11.01a2 2 0 0 1 -1.002 1.737c-1.952 1.12 -4.652 1.758 -7.407 1.758s-5.455 -.638 -7.407 -1.758a2 2 0 0 1 -1.002 -1.737v-11.01a2 2 0 0 1 1.002 -1.737c1.952 -1.12 4.652 -1.758 7.407 -1.758z"/></svg>`,
|
||||
@ -335,12 +337,10 @@ func (u *UI) Routes() (chi.Router, error) {
|
||||
}))
|
||||
|
||||
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Redirect(w, r, "/ui/devices", http.StatusFound)
|
||||
http.Redirect(w, r, "/ui/dashboard", http.StatusFound)
|
||||
})
|
||||
|
||||
r.Get("/dashboard", func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Redirect(w, r, "/ui/devices", http.StatusFound)
|
||||
})
|
||||
r.Get("/dashboard", u.pageDashboard)
|
||||
r.Get("/devices", u.pageDevices)
|
||||
r.Get("/devices/{id}/control", u.pageDeviceControl)
|
||||
r.Get("/assets", u.pageAssets)
|
||||
@ -354,6 +354,7 @@ func (u *UI) Routes() (chi.Router, error) {
|
||||
r.Get("/audit", u.pageAudit)
|
||||
r.Get("/system", u.pageSystem)
|
||||
r.Get("/device-config", u.pageDeviceConfig)
|
||||
r.Get("/device-config/{id}", u.pageDeviceConfigDetail)
|
||||
r.Get("/devices-add", u.pageDeviceAdd)
|
||||
r.Post("/devices-add", u.actionDeviceAdd)
|
||||
r.Post("/devices/batch-action", u.actionDevicesBatchAction)
|
||||
@ -442,7 +443,31 @@ func (u *UI) ensureDevicesLoaded() {
|
||||
}
|
||||
|
||||
func (u *UI) pageDashboard(w http.ResponseWriter, r *http.Request) {
|
||||
http.Redirect(w, r, "/ui/devices", http.StatusFound)
|
||||
data := u.deviceOverviewPageData(r, nil, "")
|
||||
if u.tasks != nil {
|
||||
for _, task := range u.tasks.ListTasks() {
|
||||
switch task.Status {
|
||||
case models.TaskRunning:
|
||||
data.RunningTaskCount++
|
||||
case models.TaskFailed:
|
||||
data.FailedTaskCount++
|
||||
case models.TaskSuccess:
|
||||
data.SuccessTaskCount++
|
||||
}
|
||||
}
|
||||
}
|
||||
data.Title = "总览"
|
||||
data.Tasks = nil
|
||||
if u.tasks != nil {
|
||||
data.Tasks = u.tasks.ListTasks()
|
||||
}
|
||||
data.AttentionDevices = nil
|
||||
for _, dev := range data.Devices {
|
||||
if dev != nil && !dev.Online {
|
||||
data.AttentionDevices = append(data.AttentionDevices, dev)
|
||||
}
|
||||
}
|
||||
u.render(w, r, "dashboard", data)
|
||||
}
|
||||
|
||||
func (u *UI) pageDevices(w http.ResponseWriter, r *http.Request) {
|
||||
@ -454,7 +479,21 @@ func (u *UI) pageDeviceAdd(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func (u *UI) pageDeviceConfig(w http.ResponseWriter, r *http.Request) {
|
||||
http.Redirect(w, r, "/ui/assets", http.StatusFound)
|
||||
u.ensureDevicesLoaded()
|
||||
u.render(w, r, "device_config", PageData{
|
||||
Title: "配置管理",
|
||||
Devices: u.registry.GetDevices(),
|
||||
})
|
||||
}
|
||||
|
||||
func (u *UI) pageDeviceConfigDetail(w http.ResponseWriter, r *http.Request) {
|
||||
id := chi.URLParam(r, "id")
|
||||
dev, ok := u.findDevice(id)
|
||||
if !ok {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
u.render(w, r, "device_control", u.deviceConfigWorkspacePageData(dev))
|
||||
}
|
||||
|
||||
func (u *UI) actionDeviceAdd(w http.ResponseWriter, r *http.Request) {
|
||||
@ -647,12 +686,7 @@ func (u *UI) deviceDetailPageData(dev *models.Device) PageData {
|
||||
|
||||
func (u *UI) pageDeviceControl(w http.ResponseWriter, r *http.Request) {
|
||||
id := chi.URLParam(r, "id")
|
||||
dev, ok := u.findDevice(id)
|
||||
if !ok {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
u.render(w, r, "device_control", u.deviceControlPageData(dev))
|
||||
http.Redirect(w, r, "/ui/device-config/"+url.PathEscape(id), http.StatusFound)
|
||||
}
|
||||
|
||||
func (u *UI) actionDeviceAction(w http.ResponseWriter, r *http.Request) {
|
||||
@ -692,8 +726,8 @@ func (u *UI) actionDeviceAction(w http.ResponseWriter, r *http.Request) {
|
||||
body, code, err := u.agent.Do(method, dev.IP, dev.AgentPort, path, nil)
|
||||
msg := fmt.Sprintf("%s %s -> %d", method, path, code)
|
||||
returnTo := strings.TrimSpace(r.FormValue("return_to"))
|
||||
if returnTo == "control" {
|
||||
data := u.deviceControlPageData(dev)
|
||||
if returnTo == "control" || returnTo == "config" {
|
||||
data := u.deviceConfigWorkspacePageData(dev)
|
||||
data.Message = msg
|
||||
data.RawText = string(body)
|
||||
data.ResultTitle = "执行结果摘要"
|
||||
@ -1016,7 +1050,7 @@ func (u *UI) pageModels(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func (u *UI) pageDiagnostics(w http.ResponseWriter, r *http.Request) {
|
||||
u.render(w, r, "diagnostics", PageData{Title: "日志分析", Devices: u.registry.GetDevices()})
|
||||
u.render(w, r, "diagnostics", PageData{Title: "诊断", Devices: u.registry.GetDevices()})
|
||||
}
|
||||
|
||||
func (u *UI) pageRecognition(w http.ResponseWriter, r *http.Request) {
|
||||
@ -1024,7 +1058,7 @@ func (u *UI) pageRecognition(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func (u *UI) pageLogs(w http.ResponseWriter, r *http.Request) {
|
||||
http.Redirect(w, r, "/ui/audit", http.StatusFound)
|
||||
http.Redirect(w, r, "/ui/diagnostics", http.StatusFound)
|
||||
}
|
||||
|
||||
func (u *UI) pageAPIConsole(w http.ResponseWriter, r *http.Request) {
|
||||
@ -1038,6 +1072,19 @@ func (u *UI) pageAssets(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
func (u *UI) pageAssetTemplates(w http.ResponseWriter, r *http.Request) {
|
||||
data := u.assetPageData("templates")
|
||||
if name := strings.TrimSpace(r.URL.Query().Get("name")); name != "" {
|
||||
if item, err := u.preview.GetTemplateAsset(name); err == nil {
|
||||
data.AssetTemplate = item
|
||||
} else if data.Error == "" {
|
||||
data.Error = err.Error()
|
||||
}
|
||||
} else if len(data.AssetTemplates) > 0 {
|
||||
if item, err := u.preview.GetTemplateAsset(data.AssetTemplates[0].Name); err == nil {
|
||||
data.AssetTemplate = item
|
||||
} else if data.Error == "" {
|
||||
data.Error = err.Error()
|
||||
}
|
||||
}
|
||||
u.render(w, r, "asset_templates", data)
|
||||
}
|
||||
|
||||
@ -1049,13 +1096,28 @@ func (u *UI) pageAssetTemplate(w http.ResponseWriter, r *http.Request) {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
data.Title = "模板详情"
|
||||
data.AssetTemplate = item
|
||||
u.render(w, r, "asset_template", data)
|
||||
u.render(w, r, "asset_templates", data)
|
||||
}
|
||||
|
||||
func (u *UI) pageAssetProfiles(w http.ResponseWriter, r *http.Request) {
|
||||
data := u.assetPageData("profiles")
|
||||
selected := strings.TrimSpace(r.URL.Query().Get("name"))
|
||||
if selected == "" && len(data.AssetProfiles) > 0 {
|
||||
selected = data.AssetProfiles[0].Name
|
||||
}
|
||||
if selected != "" {
|
||||
editor, err := u.preview.GetProfileEditor(selected)
|
||||
if err == nil {
|
||||
data.AssetProfileEditor = editor
|
||||
data.SelectedProfile = editor.Name
|
||||
if len(editor.Instances) > 0 && editor.Instances[0].Template != "" {
|
||||
data.SelectedTemplate = editor.Instances[0].Template
|
||||
}
|
||||
} else if data.Error == "" {
|
||||
data.Error = err.Error()
|
||||
}
|
||||
}
|
||||
u.render(w, r, "asset_profiles", data)
|
||||
}
|
||||
|
||||
@ -1066,8 +1128,8 @@ func (u *UI) pageAssetProfile(w http.ResponseWriter, r *http.Request) {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
data.Title = "业务配置编辑"
|
||||
u.render(w, r, "asset_profile", data)
|
||||
data.Title = "识别配置"
|
||||
u.render(w, r, "asset_profiles", data)
|
||||
}
|
||||
|
||||
func (u *UI) actionAssetProfileSave(w http.ResponseWriter, r *http.Request) {
|
||||
@ -1079,7 +1141,7 @@ func (u *UI) actionAssetProfileSave(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
if err := u.preview.SaveProfileEditor(editor); err != nil {
|
||||
data.Error = err.Error()
|
||||
u.render(w, r, "asset_profile", data)
|
||||
u.render(w, r, "asset_profiles", data)
|
||||
return
|
||||
}
|
||||
if editor.Name != name {
|
||||
@ -1087,11 +1149,24 @@ func (u *UI) actionAssetProfileSave(w http.ResponseWriter, r *http.Request) {
|
||||
} else {
|
||||
data.Message = "业务配置已保存"
|
||||
}
|
||||
u.render(w, r, "asset_profile", data)
|
||||
u.render(w, r, "asset_profiles", data)
|
||||
}
|
||||
|
||||
func (u *UI) pageAssetOverlays(w http.ResponseWriter, r *http.Request) {
|
||||
data := u.assetPageData("overlays")
|
||||
if name := strings.TrimSpace(r.URL.Query().Get("name")); name != "" {
|
||||
if item, err := u.preview.GetOverlayAsset(name); err == nil {
|
||||
data.AssetOverlay = item
|
||||
} else if data.Error == "" {
|
||||
data.Error = err.Error()
|
||||
}
|
||||
} else if len(data.AssetOverlays) > 0 {
|
||||
if item, err := u.preview.GetOverlayAsset(data.AssetOverlays[0].Name); err == nil {
|
||||
data.AssetOverlay = item
|
||||
} else if data.Error == "" {
|
||||
data.Error = err.Error()
|
||||
}
|
||||
}
|
||||
u.render(w, r, "asset_overlays", data)
|
||||
}
|
||||
|
||||
@ -1103,14 +1178,13 @@ func (u *UI) pageAssetOverlay(w http.ResponseWriter, r *http.Request) {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
data.Title = "配置叠加项详情"
|
||||
data.AssetOverlay = item
|
||||
u.render(w, r, "asset_overlay", data)
|
||||
u.render(w, r, "asset_overlays", data)
|
||||
}
|
||||
|
||||
func (u *UI) assetPageData(tab string) PageData {
|
||||
data := PageData{
|
||||
Title: "配置资产",
|
||||
Title: "识别配置",
|
||||
AssetTab: tab,
|
||||
}
|
||||
if u.preview == nil {
|
||||
@ -1388,8 +1462,8 @@ func (u *UI) actionDeviceConfigCandidateApply(w http.ResponseWriter, r *http.Req
|
||||
}
|
||||
returnTo := strings.TrimSpace(r.FormValue("return_to"))
|
||||
var data PageData
|
||||
if returnTo == "control" {
|
||||
data = u.deviceControlPageData(dev)
|
||||
if returnTo == "control" || returnTo == "config" {
|
||||
data = u.deviceConfigWorkspacePageData(dev)
|
||||
} else {
|
||||
data = u.configPreviewPageData(dev)
|
||||
}
|
||||
@ -1401,11 +1475,7 @@ func (u *UI) actionDeviceConfigCandidateApply(w http.ResponseWriter, r *http.Req
|
||||
body, code, err := u.agent.Do("POST", dev.IP, dev.AgentPort, "/v1/config/candidate/apply", []byte(`{}`))
|
||||
data.Message = fmt.Sprintf("POST /v1/config/candidate/apply -> %d", code)
|
||||
data.RawText = prettyJSON(body)
|
||||
if returnTo == "control" {
|
||||
data.ResultTitle = "应用候选配置结果"
|
||||
} else {
|
||||
data.ResultTitle = "应用候选配置结果"
|
||||
}
|
||||
data.ResultTitle = "应用候选配置结果"
|
||||
if err != nil {
|
||||
data.Error = err.Error()
|
||||
} else {
|
||||
@ -1417,7 +1487,7 @@ func (u *UI) actionDeviceConfigCandidateApply(w http.ResponseWriter, r *http.Req
|
||||
data.ConfigStatusErr = ""
|
||||
}
|
||||
}
|
||||
if returnTo == "control" {
|
||||
if returnTo == "control" || returnTo == "config" {
|
||||
u.render(w, r, "device_control", data)
|
||||
return
|
||||
}
|
||||
@ -1460,6 +1530,12 @@ func (u *UI) deviceControlPageData(dev *models.Device) PageData {
|
||||
return data
|
||||
}
|
||||
|
||||
func (u *UI) deviceConfigWorkspacePageData(dev *models.Device) PageData {
|
||||
data := u.deviceControlPageData(dev)
|
||||
data.Title = "配置管理"
|
||||
return data
|
||||
}
|
||||
|
||||
func (u *UI) listTemplatesSafe() ([]service.Template, error) {
|
||||
if u.templates == nil {
|
||||
return nil, nil
|
||||
|
||||
@ -2,17 +2,17 @@
|
||||
<div class="card">
|
||||
<div class="section-title">
|
||||
<div>
|
||||
<h2>视觉节点态势</h2>
|
||||
<div class="muted small">面向多台视觉识别边缘节点的运行总览。</div>
|
||||
<h2>全局 KPI</h2>
|
||||
<div class="muted small">只看 fleet 级运行态势、最近任务和需要关注的异常设备。</div>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<a class="btn ghost" href="/ui/devices">设备列表</a>
|
||||
<a class="btn ghost" href="/ui/device-config">配置管理</a>
|
||||
<a class="btn ghost" href="/ui/tasks">任务</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stats">
|
||||
<div class="stat"><div class="k">设备总数</div><div class="v">{{.DeviceCount}}</div><div class="hint">已纳管的边缘设备</div></div>
|
||||
<div class="stat"><div class="k">在线节点</div><div class="v">{{.OnlineCount}}</div><div class="hint">可执行识别任务</div></div>
|
||||
<div class="stat"><div class="k">在线率</div><div class="v">{{.OnlineCount}} / {{.DeviceCount}}</div><div class="hint">在线设备占比</div></div>
|
||||
<div class="stat"><div class="k">离线节点</div><div class="v">{{.OfflineCount}}</div><div class="hint">需要检查网络或设备服务</div></div>
|
||||
<div class="stat"><div class="k">执行中任务</div><div class="v">{{.RunningTaskCount}}</div><div class="hint">正在下发或控制节点</div></div>
|
||||
</div>
|
||||
@ -33,9 +33,9 @@
|
||||
<div class="muted small">按常见现场操作顺序进入对应页面。</div>
|
||||
<div class="actions" style="margin-top:8px">
|
||||
<a class="btn ghost" href="/ui/devices">查看设备列表</a>
|
||||
<a class="btn ghost" href="/ui/device-config">配置管理</a>
|
||||
<a class="btn ghost" href="/ui/recognition">配置识别方案</a>
|
||||
<a class="btn ghost" href="/ui/logs">日志分析</a>
|
||||
<a class="btn ghost" href="/ui/tasks">批量任务</a>
|
||||
<a class="btn ghost" href="/ui/diagnostics">诊断</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -59,7 +59,7 @@
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>需要关注的节点</h2>
|
||||
<h2>异常设备</h2>
|
||||
<div class="table-wrap" style="margin-top:10px">
|
||||
<table>
|
||||
<thead>
|
||||
|
||||
@ -3,12 +3,18 @@
|
||||
<div class="section-title">
|
||||
<div>
|
||||
<h2>诊断工作台</h2>
|
||||
<div class="muted small">按设备进入诊断日志、运行指标和高级接口排查。</div>
|
||||
<div class="muted small">诊断域集中承载日志分析、系统状态、审计记录和高级排障入口。</div>
|
||||
</div>
|
||||
<a class="btn ghost" href="/ui/api">高级调试</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stats">
|
||||
<div class="stat"><div class="k">日志分析</div><div class="v">Logs</div><div class="hint">按设备查看诊断日志和运行指标</div></div>
|
||||
<div class="stat"><div class="k">系统状态</div><div class="v">System</div><div class="hint">查看发现、健康和接口状态</div></div>
|
||||
<div class="stat"><div class="k">审计记录</div><div class="v">Audit</div><div class="hint">追踪任务与关键操作</div></div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>日志分析</h2>
|
||||
<div class="table-wrap" style="margin-top:10px">
|
||||
@ -40,4 +46,21 @@
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="card">
|
||||
<h2>系统状态</h2>
|
||||
<div class="muted small">查看平台健康、接口和发现能力。</div>
|
||||
<div class="actions" style="margin-top:12px">
|
||||
<a class="btn ghost" href="/ui/system">进入系统状态</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h2>审计记录</h2>
|
||||
<div class="muted small">统一查看任务执行和关键操作留痕。</div>
|
||||
<div class="actions" style="margin-top:12px">
|
||||
<a class="btn ghost" href="/ui/audit">进入审计记录</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
@ -20,13 +20,14 @@
|
||||
</div>
|
||||
<nav class="side-nav" aria-label="主导航">
|
||||
<div class="nav-section">主模块</div>
|
||||
<a href="/ui/dashboard"><span class="nav-icon">{{icon "overview"}}</span><span>总览</span></a>
|
||||
<a href="/ui/devices"><span class="nav-icon">{{icon "devices"}}</span><span>设备</span></a>
|
||||
<a href="/ui/assets"><span class="nav-icon">{{icon "assets"}}</span><span>配置管理</span></a>
|
||||
<a href="/ui/system"><span class="nav-icon">{{icon "system"}}</span><span>系统状态</span></a>
|
||||
<a href="/ui/audit"><span class="nav-icon">{{icon "audit"}}</span><span>操作审计</span></a>
|
||||
<a href="/ui/assets"><span class="nav-icon">{{icon "assets"}}</span><span>识别配置</span></a>
|
||||
<a href="/ui/tasks"><span class="nav-icon">{{icon "task"}}</span><span>任务</span></a>
|
||||
<a href="/ui/diagnostics"><span class="nav-icon">{{icon "logs"}}</span><span>诊断</span></a>
|
||||
</nav>
|
||||
<div class="sidebar-footer">
|
||||
<div class="sidebar-note">统一设备管理、配置管理与操作留痕</div>
|
||||
<div class="sidebar-note">总览 fleet 状态,进入设备工作台,统一查看任务与诊断</div>
|
||||
</div>
|
||||
</aside>
|
||||
<div class="workspace">
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
{{define "tasks"}}
|
||||
<div class="card">
|
||||
<h2>创建任务</h2>
|
||||
<h2>批量操作</h2>
|
||||
<div class="muted small">任务域负责批量下发、批量重启、批量回滚和执行历史。</div>
|
||||
<form method="post" action="/ui/tasks" style="margin-top:10px">
|
||||
<div class="row">
|
||||
<div>
|
||||
@ -30,7 +31,7 @@
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>任务列表</h2>
|
||||
<h2>执行历史</h2>
|
||||
<div class="table-wrap" style="margin-top:10px">
|
||||
<table>
|
||||
<thead>
|
||||
@ -58,7 +59,12 @@
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>高级入口</h2>
|
||||
<div><a href="/ui/api">高级调试</a></div>
|
||||
<h2>常用动作</h2>
|
||||
<div class="actions" style="margin-top:8px">
|
||||
<span class="pill">批量下发</span>
|
||||
<span class="pill">批量重启</span>
|
||||
<span class="pill">批量回滚</span>
|
||||
<a class="btn ghost" href="/ui/api">高级调试</a>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user