From 2304516dbc9dfb94e39770cfd770f608704b5454 Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Wed, 6 May 2026 11:45:26 +0800 Subject: [PATCH] =?UTF-8?q?=E5=90=88=E5=B9=B6=E6=A8=A1=E5=9E=8B=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E5=88=86=E6=94=AF=EF=BC=8C=E4=BF=AE=E6=94=B9=E5=90=AF?= =?UTF-8?q?=E5=8A=A8=E8=84=9A=E6=9C=AC=EF=BC=8C=E5=A2=9E=E5=8A=A0=E4=BA=86?= =?UTF-8?q?managerd.bat=E5=92=8Cmanagerd.ps1=EF=BC=8C=E5=88=A0=E9=99=A4?= =?UTF-8?q?=E4=BA=86restart.bat?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Readme.md | 23 ++ internal/web/ui.go | 61 +++--- .../web/ui/templates/device_assignments.html | 16 +- internal/web/ui/templates/layout.html | 12 +- .../web/ui/templates/recognition_units.html | 26 +-- internal/web/ui/templates/resources.html | 2 +- .../web/ui/templates/scene_templates.html | 24 +-- internal/web/ui_test.go | 121 ++++++++++- scripts/managerd.bat | 5 + scripts/managerd.ps1 | 197 ++++++++++++++++++ scripts/restart.bat | 30 --- 11 files changed, 410 insertions(+), 107 deletions(-) create mode 100644 scripts/managerd.bat create mode 100644 scripts/managerd.ps1 delete mode 100644 scripts/restart.bat diff --git a/Readme.md b/Readme.md index 5990810..d2198b5 100644 --- a/Readme.md +++ b/Readme.md @@ -25,6 +25,29 @@ managerd managerd path/to/managerd.json ``` +在当前仓库的 Windows 开发环境中,推荐使用统一脚本入口: + +```bat +scripts\managerd.bat build +scripts\managerd.bat start +scripts\managerd.bat stop +scripts\managerd.bat restart +scripts\managerd.bat status +``` + +各动作说明: + +- `build`: 编译 `.\cmd\managerd`,生成根目录下的 `managerd.exe` +- `start`: 启动当前仓库中的 `managerd.exe`,并检查 `/health` +- `stop`: 停止当前仓库对应的 `managerd.exe` +- `restart`: 先停止再启动 +- `status`: 查看进程状态与健康检查结果 + +脚本实际入口文件: + +- `scripts/managerd.bat` +- `scripts/managerd.ps1` + 程序启动后: - `GET /` 会重定向到 `/ui` diff --git a/internal/web/ui.go b/internal/web/ui.go index 9a85810..cc37bf0 100644 --- a/internal/web/ui.go +++ b/internal/web/ui.go @@ -1190,7 +1190,7 @@ func (u *UI) pageTasks(w http.ResponseWriter, r *http.Request) { devices := u.registry.GetDevices() selectedIDs := filterSelectedDeviceIDs(devices, selectedIDsFromQuery(r.URL.Query()["selected"])) data := PageData{ - Title: "任务", + Title: "任务中心", Tasks: u.tasks.ListTasks(), Devices: devices, SelectedDeviceIDs: selectedIDs, @@ -1201,7 +1201,7 @@ func (u *UI) pageTasks(w http.ResponseWriter, r *http.Request) { } func (u *UI) taskPageData(task *models.Task) PageData { - data := PageData{Title: "任务详情", Task: task} + data := PageData{Title: "任务中心", Task: task} if task == nil { return data } @@ -1260,13 +1260,13 @@ func (u *UI) actionCreateTask(w http.ResponseWriter, r *http.Request) { } var payload any if err := json.Unmarshal([]byte(raw), &payload); err != nil { - u.render(w, r, "tasks", PageData{Title: "任务", Tasks: u.tasks.ListTasks(), Devices: u.registry.GetDevices(), Error: "高级参数 JSON 无效: " + err.Error(), RawJSON: raw, DeviceIDs: ids}) + u.render(w, r, "tasks", PageData{Title: "任务中心", Tasks: u.tasks.ListTasks(), Devices: u.registry.GetDevices(), Error: "高级参数 JSON 无效: " + err.Error(), RawJSON: raw, DeviceIDs: ids}) return } task, err := u.tasks.CreateTask(typeStr, deviceIDs, payload) if err != nil { - u.render(w, r, "tasks", PageData{Title: "任务", Tasks: u.tasks.ListTasks(), Devices: u.registry.GetDevices(), Error: err.Error(), RawJSON: raw, DeviceIDs: ids}) + u.render(w, r, "tasks", PageData{Title: "任务中心", Tasks: u.tasks.ListTasks(), Devices: u.registry.GetDevices(), Error: err.Error(), RawJSON: raw, DeviceIDs: ids}) return } http.Redirect(w, r, "/ui/tasks/"+task.ID, http.StatusFound) @@ -1301,6 +1301,7 @@ func (u *UI) pageTemplate(w http.ResponseWriter, r *http.Request) { } func (u *UI) pageModels(w http.ResponseWriter, r *http.Request) { + u.ensureDevicesLoaded() data := PageData{Title: "模型管理", Devices: u.registry.GetDevices()} for _, dev := range data.Devices { if dev == nil { @@ -1376,7 +1377,7 @@ func (u *UI) pageDiagnostics(w http.ResponseWriter, r *http.Request) { } func (u *UI) pageResources(w http.ResponseWriter, r *http.Request) { - u.render(w, r, "resources", PageData{Title: "资源管理", Devices: u.registry.GetDevices()}) + u.render(w, r, "resources", PageData{Title: "资源状态", Devices: u.registry.GetDevices()}) } func (u *UI) pageRecognition(w http.ResponseWriter, r *http.Request) { @@ -1570,7 +1571,7 @@ func (u *UI) pageAssetTemplateGraph(w http.ResponseWriter, r *http.Request) { data.Error = strings.TrimSpace(r.URL.Query().Get("error")) } if u.preview == nil { - data.Error = "基础配置服务未初始化" + data.Error = "配置中心服务未初始化" u.render(w, r, "asset_templates", data) return } @@ -1760,7 +1761,7 @@ func (u *UI) pageAssetTemplateExport(w http.ResponseWriter, r *http.Request) { func (u *UI) pagePlans(w http.ResponseWriter, r *http.Request) { data := u.assetPageData("") - data.Title = "场景模板" + data.Title = "场景" data.Message = strings.TrimSpace(r.URL.Query().Get("msg")) if data.Error == "" { data.Error = strings.TrimSpace(r.URL.Query().Get("error")) @@ -1821,7 +1822,7 @@ func (u *UI) pagePlan(w http.ResponseWriter, r *http.Request) { http.NotFound(w, r) return } - data.Title = "场景模板" + data.Title = "场景" data.AssetProfileEditing = strings.TrimSpace(r.URL.Query().Get("edit")) == "1" data.AssetProfileFormAction = "/ui/scene-templates/" + url.PathEscape(name) data.ActiveInstanceIndex = clampActiveInstanceIndex(len(data.AssetProfileEditor.Instances), activeInstanceIndexFromValues(r.URL.Query())) @@ -1856,7 +1857,7 @@ func (u *UI) actionPlanSaveWithName(w http.ResponseWriter, r *http.Request, name ) if strings.TrimSpace(name) == "" { data = u.assetPageData("") - data.Title = "场景模板" + data.Title = "场景" _ = r.ParseForm() editor = service.ConfigProfileEditor{ Name: strings.TrimSpace(r.FormValue("profile_name")), @@ -1883,14 +1884,14 @@ func (u *UI) actionPlanSaveWithName(w http.ResponseWriter, r *http.Request, name } if err := u.preview.SaveProfileEditor(editor); err != nil { data.Error = err.Error() - data.Title = "场景模板" + data.Title = "场景" data.AssetProfileEditing = true u.render(w, r, "scene_templates", data) return } - msg := "场景模板已保存" + msg := "场景已保存" if strings.TrimSpace(name) != "" && editor.Name != name { - msg = "场景模板已保存,名称已更新" + msg = "场景已保存,名称已更新" } http.Redirect(w, r, "/ui/scene-templates?msg="+urlQueryEscape(msg)+"&name="+url.PathEscape(editor.Name), http.StatusFound) } @@ -1912,7 +1913,7 @@ func (u *UI) actionPlanDelete(w http.ResponseWriter, r *http.Request) { for _, item := range items { deviceIDs = append(deviceIDs, item.DeviceID) } - msg := fmt.Sprintf("场景模板 %q 正被以下设备使用,无法删除:%s", name, strings.Join(deviceIDs, "、")) + msg := fmt.Sprintf("场景 %q 正被以下设备使用,无法删除:%s", name, strings.Join(deviceIDs, "、")) http.Redirect(w, r, "/ui/scene-templates?name="+url.PathEscape(name)+"&error="+urlQueryEscape(msg), http.StatusFound) return } @@ -1921,7 +1922,7 @@ func (u *UI) actionPlanDelete(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, "/ui/scene-templates?name="+url.PathEscape(name)+"&error="+urlQueryEscape(err.Error()), http.StatusFound) return } - http.Redirect(w, r, "/ui/scene-templates?msg="+urlQueryEscape("场景模板已删除"), http.StatusFound) + http.Redirect(w, r, "/ui/scene-templates?msg="+urlQueryEscape("场景已删除"), http.StatusFound) } func (u *UI) actionAssetProfileSave(w http.ResponseWriter, r *http.Request) { @@ -1958,7 +1959,7 @@ func (u *UI) redirectPlanToSceneTemplate(w http.ResponseWriter, r *http.Request) func (u *UI) pageRecognitionUnits(w http.ResponseWriter, r *http.Request) { data := u.assetPageData("") - data.Title = "识别单元" + data.Title = "视频通道" data.Message = strings.TrimSpace(r.URL.Query().Get("msg")) if data.Error == "" { data.Error = strings.TrimSpace(r.URL.Query().Get("error")) @@ -2006,7 +2007,7 @@ func (u *UI) actionRecognitionUnitSave(w http.ResponseWriter, r *http.Request) { originalRef := strings.TrimSpace(r.FormValue("original_ref")) if err := u.preview.SaveRecognitionUnit(asset, originalRef); err != nil { data := u.assetPageData("") - data.Title = "识别单元" + data.Title = "视频通道" data.Error = err.Error() data.RecognitionUnit = &asset data.RecognitionUnitEditing = true @@ -2015,7 +2016,7 @@ func (u *UI) actionRecognitionUnitSave(w http.ResponseWriter, r *http.Request) { return } ref := serviceRecognitionUnitRef(asset.SceneTemplateName, asset.Name) - http.Redirect(w, r, "/ui/recognition-units?msg="+urlQueryEscape("识别单元已保存")+"&ref="+url.QueryEscape(ref), http.StatusFound) + http.Redirect(w, r, "/ui/recognition-units?msg="+urlQueryEscape("视频通道已保存")+"&ref="+url.QueryEscape(ref), http.StatusFound) } func serviceRecognitionUnitRef(profileName string, unitName string) string { @@ -2028,12 +2029,12 @@ func (u *UI) actionRecognitionUnitDelete(w http.ResponseWriter, r *http.Request) http.Redirect(w, r, "/ui/recognition-units?error="+urlQueryEscape(err.Error())+"&ref="+url.QueryEscape(ref), http.StatusFound) return } - http.Redirect(w, r, "/ui/recognition-units?msg="+urlQueryEscape("识别单元已删除"), http.StatusFound) + http.Redirect(w, r, "/ui/recognition-units?msg="+urlQueryEscape("视频通道已删除"), http.StatusFound) } func (u *UI) pageDeviceAssignments(w http.ResponseWriter, r *http.Request) { data := u.assetPageData("") - data.Title = "设备分配" + data.Title = "通道部署" data.Message = strings.TrimSpace(r.URL.Query().Get("msg")) if data.Error == "" { data.Error = strings.TrimSpace(r.URL.Query().Get("error")) @@ -2063,7 +2064,7 @@ func (u *UI) actionDeviceAssignmentSave(w http.ResponseWriter, r *http.Request) assignments, err := parseDeviceAssignmentBoardState(strings.TrimSpace(r.FormValue("board_state_json"))) if err != nil { data := u.assetPageData("") - data.Title = "设备分配" + data.Title = "通道部署" u.ensureDevicesLoaded() data.Devices = deviceAssignmentBoardDevices(u.registry.GetDevices()) data.Error = err.Error() @@ -2087,7 +2088,7 @@ func (u *UI) actionDeviceAssignmentSave(w http.ResponseWriter, r *http.Request) http.Redirect(w, r, "/ui/device-assignments?error="+urlQueryEscape(err.Error()), http.StatusFound) return } - http.Redirect(w, r, "/ui/device-assignments?msg="+urlQueryEscape("设备分配已保存"), http.StatusFound) + http.Redirect(w, r, "/ui/device-assignments?msg="+urlQueryEscape("通道部署已保存"), http.StatusFound) } func deviceAssignmentBoardDevices(devices []*models.Device) []*models.Device { @@ -2136,12 +2137,12 @@ func (u *UI) actionDeviceAssignmentDelete(w http.ResponseWriter, r *http.Request http.Redirect(w, r, "/ui/device-assignments?error="+urlQueryEscape(err.Error())+"&device_id="+url.PathEscape(deviceID), http.StatusFound) return } - http.Redirect(w, r, "/ui/device-assignments?msg="+urlQueryEscape("设备分配已删除"), http.StatusFound) + http.Redirect(w, r, "/ui/device-assignments?msg="+urlQueryEscape("通道部署已删除"), http.StatusFound) } func (u *UI) pageAssetVideoSources(w http.ResponseWriter, r *http.Request) { data := u.assetPageData("video-sources") - data.Title = "基础配置" + data.Title = "配置中心" data.Message = strings.TrimSpace(r.URL.Query().Get("msg")) if data.Error == "" { data.Error = strings.TrimSpace(r.URL.Query().Get("error")) @@ -2196,7 +2197,7 @@ func (u *UI) actionAssetVideoSourceSave(w http.ResponseWriter, r *http.Request) } if err := u.preview.SaveVideoSourceAsset(asset); err != nil { data := u.assetPageData("video-sources") - data.Title = "基础配置" + data.Title = "配置中心" data.Error = err.Error() data.AssetVideoSource = &asset data.AssetVideoSource.SourceTypeLabel = serviceVideoSourceTypeLabel(asset.SourceType) @@ -2222,7 +2223,7 @@ func (u *UI) actionAssetVideoSourceDelete(w http.ResponseWriter, r *http.Request func (u *UI) pageAssetIntegrations(w http.ResponseWriter, r *http.Request) { data := u.assetPageData("integrations") - data.Title = "基础配置" + data.Title = "配置中心" data.Message = strings.TrimSpace(r.URL.Query().Get("msg")) if data.Error == "" { data.Error = strings.TrimSpace(r.URL.Query().Get("error")) @@ -2381,7 +2382,7 @@ func (u *UI) actionAssetOverlaySave(w http.ResponseWriter, r *http.Request) { raw := map[string]any{} if err := json.Unmarshal([]byte(rawText), &raw); err != nil { data := u.assetPageData("overlays") - data.Title = "基础配置" + data.Title = "配置中心" data.Error = "调试参数 JSON 格式不正确:" + err.Error() data.AssetOverlayEditing = true data.AssetOverlay = &service.ConfigOverlayAsset{Name: name, Description: description, Raw: raw} @@ -2392,7 +2393,7 @@ func (u *UI) actionAssetOverlaySave(w http.ResponseWriter, r *http.Request) { asset := service.ConfigOverlayAsset{Name: name, Description: description, Raw: raw} if err := u.preview.SaveOverlayAsset(asset, raw); err != nil { data := u.assetPageData("overlays") - data.Title = "基础配置" + data.Title = "配置中心" data.Error = err.Error() data.AssetOverlayEditing = true data.AssetOverlay = &asset @@ -2418,11 +2419,11 @@ func (u *UI) actionAssetOverlayDelete(w http.ResponseWriter, r *http.Request) { func (u *UI) assetPageData(tab string) PageData { data := PageData{ - Title: "基础配置", + Title: "配置中心", AssetTab: tab, } if u.preview == nil { - data.Error = "基础配置服务未初始化" + data.Error = "配置中心服务未初始化" return data } sources, err := u.preview.ListSources() @@ -3055,7 +3056,7 @@ func (u *UI) actionDevicePlanApply(w http.ResponseWriter, r *http.Request) { } data := u.deviceDetailPageData(dev) if data.DeviceAssignment == nil { - data.Error = "请先到设备分配中为该设备指定识别单元" + data.Error = "请先到通道部署中为该设备指定视频通道" u.render(w, r, "device", data) return } diff --git a/internal/web/ui/templates/device_assignments.html b/internal/web/ui/templates/device_assignments.html index 855e395..a6f904c 100644 --- a/internal/web/ui/templates/device_assignments.html +++ b/internal/web/ui/templates/device_assignments.html @@ -2,14 +2,14 @@
-

{{icon "devices"}}设备分配

+

{{icon "devices"}}通道部署

{{if .DeviceAssignmentBoard}}
- 识别单元 + 视频通道 {{.DeviceAssignmentBoard.Stats.TotalUnits}}
@@ -45,7 +45,7 @@
- +
@@ -75,7 +75,7 @@ {{if lt .AssignedCount .MaxUnits}}
@@ -87,7 +87,7 @@
-

未分配识别单元

+

未部署视频通道

@@ -273,7 +273,7 @@ const addControls = card.refs.length < state.max ? `
@@ -358,7 +358,7 @@ document.getElementById('auto-assign-btn').addEventListener('click', autoAssign); document.getElementById('clear-assign-btn').addEventListener('click', () => { clearAssignments(); - feedback.textContent = '已清空当前页面中的设备分配。'; + feedback.textContent = '已清空当前页面中的通道部署。'; render(); }); form.addEventListener('submit', syncHiddenState); @@ -366,7 +366,7 @@ })(); {{else}} -
暂无可用的设备分配数据
+
暂无可用的通道部署数据
{{end}}
{{end}} diff --git a/internal/web/ui/templates/layout.html b/internal/web/ui/templates/layout.html index 916ee98..e5d29f8 100644 --- a/internal/web/ui/templates/layout.html +++ b/internal/web/ui/templates/layout.html @@ -22,11 +22,11 @@ {{icon "overview"}}总览 {{icon "devices"}}设备 - {{icon "profile"}}场景模板 - {{icon "device"}}识别单元 - {{icon "apply"}}设备分配 - {{icon "assets"}}基础配置 - {{icon "task"}}任务 + {{icon "profile"}}场景 + {{icon "device"}}视频通道 + {{icon "apply"}}通道部署 + {{icon "assets"}}配置中心 + {{icon "task"}}任务中心