From 45470d09086f540a6f71f734a446f254e59a0c04 Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Mon, 27 Jul 2026 18:35:08 +0800 Subject: [PATCH] refactor: move overlay selection from console to device detail page --- internal/web/ui.go | 70 ++++++++++--------------- internal/web/ui/templates/console.html | 14 +---- internal/web/ui/templates/device.html | 28 ++++++++++ safesightd-linux-arm64 | Bin 15270050 -> 15270050 bytes 4 files changed, 56 insertions(+), 56 deletions(-) diff --git a/internal/web/ui.go b/internal/web/ui.go index 02547cf..910ae0d 100644 --- a/internal/web/ui.go +++ b/internal/web/ui.go @@ -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 diff --git a/internal/web/ui/templates/console.html b/internal/web/ui/templates/console.html index 4281b3a..1fc9cbd 100644 --- a/internal/web/ui/templates/console.html +++ b/internal/web/ui/templates/console.html @@ -68,19 +68,7 @@ {{end}} - {{if $cd.Overlays}} -
| - | {{$o.Name}} | -
4&NeUsCkr@?%`L$5@VPx#!`Dix7r)#>*^et|lC1}AbyMDDg^2^F}mfB5RMzB@AHVXXGY>i$crII=MQi%^f
z`9iy&*Jte;X?Qx2XQ)}X5L*5U`0w}STVGLjS*){lCjUYqnAlg`K!~XUlbk?>3qZKZ
zmC6<>p(9GU$$vctn@C5Oh%;dEr6@PG%9lEQYGGnqYo)iE>6M%!i+f3pVj6A`Iw`
z;f8$fcte1oL#%+qs(c7I`l3M^jCXrj?jr;oin>XsM^9ia#MEgm;IMM3Ivh8{^;sGK
z_}XFMJsmvkA&wi^`U{mI+j6Cq@4zI@SMX2P)<7IznfQd|iYGQ=K{R)T@|G{&8_k;2
zM4@5I_mbGcL0OvKP+C%#H
jP?c;6z4;3lzx`)}oAF
zy|g&!JCxA_WX8PN1>)R4CsIcrP?IA9e0lfM;#kAbwp^h3tmDBiUFQOEtchq_E)WXT
z@k!#hhaz1PAB+?2ENF_HcFkV
zVdu`}*sJC;>_|G~1b1lQpgR~iiss&-VVB;)vA1*Z5NdOR{qE%8lsg4SsBWmDU#+k1
zyj)z}ZS>~56|g1<>B>}jr;w?a>G++(Tm&h1@vmYnIH(1_*}@zqp!{qhG K5GH$7dbZ7Og!7
KFYyJtvgAdh|4e$Yc?v{c#^@#lV&@VAe7%l9J
z>l1NuEPBab;+nnw3aj^`@K42Skwe5)sO4M*W;as#r(&`fcrwtzPsMTghY`{EnOGkT
zo(zA@1A6cHXW}2gvwP9eL*h#Oi}0fC!#qXKRk$kgqLk0Yts3PMpNk_x&^E5Z%SJD(
z9DQOMWMbmb8MPa9)H}WqzxT$t_KynQR^0aG?my1QwVgEYTX^Xo` IrlM@F4A4VB;Yw5WW&>#0au
zS0^e@&G^KIvU(}GUmy1CJjK3%InYt^U+d#E@2!_^<0UC5sB*b=I{ljTxZ|BjTDwCU
z;g~*@cI}YnhI|xh96{6vj;DPL8dLx(+1_HOvj_*2eoJ~M+Ydd6`i+e8BIyh1bHf%K)BQrKF=S7mRgF+D
z7i4v3qqIe@hno9Rdc(x1bRU!AEu2a%sHiFbmTvHg&XF+tv+r&xm#a_BuwOb25AP|m
z!t?A~-Mp$_^?Y
Df*4XBfKZSvsg
zoCIY|C_f?iRWl^J60LG2$``KlYCDO8QBUG~8=6?WULI^{o8s<%$YMYXU?2CbLNcu?91?z
zIt`DfNAU9&2%1m18lAKuNCKyfp~PTm=6ooj@1L_Y?~yJezMFDchR%f&+C#aF2f7-b
zaXrQDxX&u?dK9-A-^TvFIP*}j^u6etef=d7;K$Q8m6DVs(s#*{2;(8hOE7a3Nq9g}
zo+{mA^?{G*1JVBIhZcx@?t_V(0al_js^|l&dI@HxCb4aA4CU$49fH?&Dauzsp459l
zra3(&O$m`W;ItRRp%97np)oWiT!O)E3`Isrl^4F#&Ik#1U$|&RO2h0)-Z23-YvI@^
z*evu=W{h12K8FoqHJXygML;yT%-zS#h?ceq65C>=$F1!lKhO4%pEomA#7cSH3OyMs
z?O<9q$4OtLqs&s+%vw<<%EB1Lb>1jfe;nn(E`jpuc~67KmF;{$2D%cH2fP$rQVf^B
z-z7nn1+sMs()&|I1#Blac0)GL03xh3yCZU6KqSochq1lKiXYFy{!sh`Awa%)fb_gT
zo-^>`$ahrze@8wuQF;^V;ute%u+(3)eS