refactor: move overlay selection from console to device detail page
This commit is contained in:
parent
8d8daf1b7f
commit
45470d0908
@ -837,6 +837,7 @@ func (u *UI) Routes() (chi.Router, error) {
|
||||
r.Post("/devices/batch-config", u.actionDeviceBatchConfig)
|
||||
r.Post("/discovery/search", u.actionDiscoverySearch)
|
||||
r.Get("/devices/{id}", u.pageDevice)
|
||||
r.Post("/devices/{id}/overlays", u.actionDeviceOverlaysSave)
|
||||
r.Post("/devices/{id}/alias", u.actionDeviceAliasSave)
|
||||
r.Get("/devices/{id}/config-versions", u.pageDeviceConfigVersions)
|
||||
r.Get("/devices/{id}/config-versions/{versionID}", u.apiDeviceConfigVersion)
|
||||
@ -1204,30 +1205,6 @@ func (u *UI) pageConsole(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// Build console device data
|
||||
var allOverlays []ConsoleOverlay
|
||||
if u.preview != nil {
|
||||
if overlays, err := u.preview.ListOverlayAssets(); err == nil {
|
||||
for _, o := range overlays {
|
||||
allOverlays = append(allOverlays, ConsoleOverlay{Name: o.Name, Description: o.Description})
|
||||
}
|
||||
}
|
||||
}
|
||||
// Pre-load device overlay selections
|
||||
deviceOverlaySelections := map[string]map[string]bool{}
|
||||
if u.preview != nil {
|
||||
for _, dev := range devices {
|
||||
if dev == nil {
|
||||
continue
|
||||
}
|
||||
if names, err := u.preview.GetDeviceOverlays(dev.DeviceID); err == nil {
|
||||
m := map[string]bool{}
|
||||
for _, n := range names {
|
||||
m[n] = true
|
||||
}
|
||||
deviceOverlaySelections[dev.DeviceID] = m
|
||||
}
|
||||
}
|
||||
}
|
||||
var consoleDevices []ConsoleDeviceData
|
||||
for _, dev := range devices {
|
||||
if dev == nil {
|
||||
@ -1293,14 +1270,6 @@ func (u *UI) pageConsole(w http.ResponseWriter, r *http.Request) {
|
||||
diskUsage = m.Disk
|
||||
temp = m.Temperature
|
||||
}
|
||||
// Mark which overlays are enabled for this device
|
||||
deviceOverlays := make([]ConsoleOverlay, len(allOverlays))
|
||||
copy(deviceOverlays, allOverlays)
|
||||
if sel, ok := deviceOverlaySelections[dev.DeviceID]; ok {
|
||||
for i := range deviceOverlays {
|
||||
deviceOverlays[i].Enabled = sel[deviceOverlays[i].Name]
|
||||
}
|
||||
}
|
||||
consoleDevices = append(consoleDevices, ConsoleDeviceData{
|
||||
Device: dev,
|
||||
CPUUsage: cpuUsage,
|
||||
@ -1311,7 +1280,6 @@ func (u *UI) pageConsole(w http.ResponseWriter, r *http.Request) {
|
||||
MaxUnits: maxUnits,
|
||||
Channels: channels,
|
||||
Features: features,
|
||||
Overlays: deviceOverlays,
|
||||
Cameras: cameras,
|
||||
})
|
||||
}
|
||||
@ -1406,7 +1374,6 @@ func (u *UI) actionConsoleSave(w http.ResponseWriter, r *http.Request) {
|
||||
DeviceID: dev.DeviceID,
|
||||
Features: features,
|
||||
SourceNames: sources,
|
||||
Overlays: cleanFormList(r.Form["device_"+dev.DeviceID+"_overlay"]),
|
||||
})
|
||||
}
|
||||
|
||||
@ -1418,7 +1385,6 @@ func (u *UI) actionConsoleSave(w http.ResponseWriter, r *http.Request) {
|
||||
// Step 3: save features to DB BEFORE creating tasks (avoids SQLITE_BUSY race).
|
||||
for _, req := range requests {
|
||||
_ = u.preview.SaveDeviceFeatures(req.DeviceID, req.Features)
|
||||
_ = u.preview.SaveDeviceOverlays(req.DeviceID, req.Overlays)
|
||||
}
|
||||
|
||||
// Step 4: run pipeline (creates tasks asynchronously).
|
||||
@ -1964,12 +1930,37 @@ func (u *UI) pageDevice(w http.ResponseWriter, r *http.Request) {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
u.render(w, r, "device", u.deviceDetailPageData(dev))
|
||||
data := u.deviceDetailPageData(dev)
|
||||
data.Message = strings.TrimSpace(r.URL.Query().Get("msg"))
|
||||
data.Error = strings.TrimSpace(r.URL.Query().Get("error"))
|
||||
u.render(w, r, "device", data)
|
||||
}
|
||||
|
||||
func (u *UI) actionDeviceOverlaysSave(w http.ResponseWriter, r *http.Request) {
|
||||
id := chi.URLParam(r, "id")
|
||||
if err := r.ParseForm(); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
overlays := cleanFormList(r.Form["overlay"])
|
||||
if u.preview != nil {
|
||||
_ = u.preview.SaveDeviceOverlays(id, overlays)
|
||||
}
|
||||
http.Redirect(w, r, "/devices/"+url.PathEscape(id)+"?msg="+url.QueryEscape("覆盖参数已保存"), http.StatusFound)
|
||||
}
|
||||
|
||||
func (u *UI) deviceDetailPageData(dev *models.Device) PageData {
|
||||
data := u.deviceControlPageData(dev)
|
||||
data.Title = "设备详情"
|
||||
// Load overlays for device-specific override selection
|
||||
if u.preview != nil {
|
||||
if overlays, err := u.preview.ListOverlayAssets(); err == nil {
|
||||
data.AssetOverlays = overlays
|
||||
if selected, err := u.preview.GetDeviceOverlays(dev.DeviceID); err == nil {
|
||||
data.SelectedOverlays = selected
|
||||
}
|
||||
}
|
||||
}
|
||||
if data.ConfigStatus == nil && u.stateRepo != nil && dev != nil {
|
||||
if state, err := u.stateRepo.Get(dev.DeviceID); err == nil && state != nil {
|
||||
data.PersistedConfig = state
|
||||
@ -6377,18 +6368,11 @@ type ConsoleDeviceData struct {
|
||||
MaxUnits int
|
||||
Channels []ConsoleChannel
|
||||
Features []ConsoleFeature
|
||||
Overlays []ConsoleOverlay
|
||||
Cameras []string
|
||||
Busy bool
|
||||
BusyMsg string
|
||||
}
|
||||
|
||||
type ConsoleOverlay struct {
|
||||
Name string
|
||||
Description string
|
||||
Enabled bool
|
||||
}
|
||||
|
||||
type ConsoleChannel struct {
|
||||
Name string
|
||||
Display string
|
||||
|
||||
@ -68,19 +68,7 @@
|
||||
{{end}}
|
||||
</table>
|
||||
</div>
|
||||
{{if $cd.Overlays}}
|
||||
<div class="console-section">
|
||||
<div class="console-section-title">告警覆盖层</div>
|
||||
<table class="console-feature-table">
|
||||
{{range $o := $cd.Overlays}}
|
||||
<tr>
|
||||
<td class="console-feature-check"><input type="checkbox" name="device_{{$cd.Device.DeviceID}}_overlay" value="{{$o.Name}}"{{if $o.Enabled}} checked{{end}}></td>
|
||||
<td class="console-feature-label" title="{{$o.Description}}">{{$o.Name}}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</table>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<div class="console-section">
|
||||
<div class="console-section-title">设备负载</div>
|
||||
<div class="console-metrics">
|
||||
|
||||
@ -161,6 +161,34 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="panel-block" id="device-overlays">
|
||||
<div class="panel-head">
|
||||
<div>
|
||||
<h3 class="title-with-icon">{{icon "overlay"}}<span>调试参数覆盖</span></h3>
|
||||
</div>
|
||||
</div>
|
||||
{{if .AssetOverlays}}
|
||||
<form method="POST" action="/devices/{{.Device.DeviceID}}/overlays" style="margin-top:8px">
|
||||
<div class="info-list compact-list">
|
||||
{{range .AssetOverlays}}
|
||||
<label style="display:flex;align-items:center;gap:8px;cursor:pointer">
|
||||
<input type="checkbox" name="overlay" value="{{.Name}}" {{if hasString $.SelectedOverlays .Name}}checked{{end}}>
|
||||
<span class="mono">{{.Name}}</span>
|
||||
{{if .Description}}<span class="muted small">{{.Description}}</span>{{end}}
|
||||
</label>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="actions" style="margin-top:10px">
|
||||
<button type="submit" class="btn primary">保存覆盖参数</button>
|
||||
</div>
|
||||
</form>
|
||||
{{else}}
|
||||
<div class="empty-state compact">
|
||||
<div class="muted">暂无可用的调试参数覆盖。在 <a href="/assets/overlays">调试参数管理</a> 中创建。</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</section>
|
||||
|
||||
<section class="panel-block" id="device-models">
|
||||
<div class="panel-head">
|
||||
<div>
|
||||
|
||||
Binary file not shown.
Loading…
Reference in New Issue
Block a user