From 15f6f91fb6579fb2cf62cbf395625952ccd1268e Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Tue, 12 May 2026 14:26:10 +0800 Subject: [PATCH] feat: task timestamps, dirty state, confirmation dialog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Task 模型增加 CreatedAt/CompletedAt,DB 读取和快照同步 - 总览页最近任务显示时间,限制5条,增加"更多 →"链接 - 任务中心增加创建时间/完成时间列 - 管控台"保存并下发"按钮:修改功能或视频源后才激活 - 下发前弹出变更摘要确认对话框 - 下发后回到管控台并显示任务ID --- internal/models/task.go | 18 ++-- internal/service/task.go | 16 ++-- internal/storage/tasks_repo.go | 17 ++-- internal/web/ui.go | 11 ++- internal/web/ui/templates/console.html | 104 ++++++++++++++++++++++- internal/web/ui/templates/dashboard.html | 22 +++-- internal/web/ui/templates/tasks.html | 6 +- internal/web/ui_test.go | 8 +- 8 files changed, 162 insertions(+), 40 deletions(-) diff --git a/internal/models/task.go b/internal/models/task.go index 9845ed6..b80bf19 100644 --- a/internal/models/task.go +++ b/internal/models/task.go @@ -2,6 +2,7 @@ package models import ( "sync" + "time" ) type TaskStatus string @@ -21,13 +22,15 @@ type DeviceTaskStatus struct { } type Task struct { - ID string `json:"task_id"` - Type string `json:"type"` - DeviceIDs []string `json:"device_ids"` - Payload interface{} `json:"payload"` - Status TaskStatus `json:"status"` - Devices map[string]*DeviceTaskStatus `json:"devices"` - Mu sync.RWMutex `json:"-"` + ID string `json:"task_id"` + Type string `json:"type"` + DeviceIDs []string `json:"device_ids"` + Payload interface{} `json:"payload"` + Status TaskStatus `json:"status"` + Devices map[string]*DeviceTaskStatus `json:"devices"` + CreatedAt string `json:"created_at"` + CompletedAt string `json:"completed_at"` + Mu sync.RWMutex `json:"-"` } func NewTask(id, t string, deviceIDs []string, payload interface{}) *Task { @@ -45,5 +48,6 @@ func NewTask(id, t string, deviceIDs []string, payload interface{}) *Task { Payload: payload, Status: TaskPending, Devices: devices, + CreatedAt: time.Now().Format("2006-01-02 15:04:05"), } } diff --git a/internal/service/task.go b/internal/service/task.go index a6e1b74..38553a0 100644 --- a/internal/service/task.go +++ b/internal/service/task.go @@ -98,12 +98,14 @@ func (s *TaskService) ListTasks() []models.Task { t.Mu.RLock() snap := models.Task{ - ID: t.ID, - Type: t.Type, - DeviceIDs: append([]string(nil), t.DeviceIDs...), - Payload: t.Payload, - Status: t.Status, - Devices: make(map[string]*models.DeviceTaskStatus, len(t.Devices)), + ID: t.ID, + Type: t.Type, + DeviceIDs: append([]string(nil), t.DeviceIDs...), + Payload: t.Payload, + Status: t.Status, + CreatedAt: t.CreatedAt, + CompletedAt: t.CompletedAt, + Devices: make(map[string]*models.DeviceTaskStatus, len(t.Devices)), } for did, ds := range t.Devices { snap.Devices[did] = &models.DeviceTaskStatus{ @@ -149,6 +151,8 @@ func (s *TaskService) LoadPersistedTasks() error { item := items[i] s.tasks[item.ID] = models.NewTask(item.ID, item.Type, append([]string(nil), item.DeviceIDs...), item.Payload) s.tasks[item.ID].Status = item.Status + s.tasks[item.ID].CreatedAt = item.CreatedAt + s.tasks[item.ID].CompletedAt = item.CompletedAt for did, ds := range item.Devices { if ds == nil { continue diff --git a/internal/storage/tasks_repo.go b/internal/storage/tasks_repo.go index 16a4ce5..49da5a2 100644 --- a/internal/storage/tasks_repo.go +++ b/internal/storage/tasks_repo.go @@ -87,7 +87,7 @@ func (r *TasksRepo) List() ([]models.Task, error) { return nil, nil } rows, err := r.db.Query(` -SELECT task_id, type, payload_json, status +SELECT task_id, type, payload_json, status, COALESCE(created_at,''), COALESCE(finished_at,'') FROM tasks ORDER BY created_at DESC, task_id DESC `) @@ -101,7 +101,8 @@ ORDER BY created_at DESC, task_id DESC var ( id, tType, payloadJSON, status string ) - if err := rows.Scan(&id, &tType, &payloadJSON, &status); err != nil { + var createdAt, finishedAt string + if err := rows.Scan(&id, &tType, &payloadJSON, &status, &createdAt, &finishedAt); err != nil { return nil, err } var payload any @@ -111,11 +112,13 @@ ORDER BY created_at DESC, task_id DESC } } task := models.Task{ - ID: id, - Type: tType, - Payload: payload, - Status: models.TaskStatus(status), - Devices: map[string]*models.DeviceTaskStatus{}, + ID: id, + Type: tType, + Payload: payload, + Status: models.TaskStatus(status), + CreatedAt: createdAt, + CompletedAt: finishedAt, + Devices: map[string]*models.DeviceTaskStatus{}, } deviceRows, err := r.db.Query(` diff --git a/internal/web/ui.go b/internal/web/ui.go index 44cabb2..ba78cd5 100644 --- a/internal/web/ui.go +++ b/internal/web/ui.go @@ -485,6 +485,12 @@ func NewUI(discovery *service.DiscoveryService, registry *service.RegistryServic } return a / b }, + "shortID": func(v string) string { + if len(v) > 8 { + return v[:8] + } + return v + }, }).ParseFS(uiFS, "ui/templates/*.html") if err != nil { return nil, err @@ -1186,12 +1192,11 @@ func (u *UI) actionConsoleSave(w http.ResponseWriter, r *http.Request) { } if len(taskIDs) == 1 { - http.Redirect(w, r, "/ui/tasks/"+taskIDs[0], http.StatusFound) + http.Redirect(w, r, "/ui/console?task="+taskIDs[0], http.StatusFound) return } if len(taskIDs) > 1 { - // Multiple devices: redirect to tasks list. - http.Redirect(w, r, "/ui/tasks?msg="+url.QueryEscape(fmt.Sprintf("已为 %d 台设备创建配置下发任务", len(taskIDs))), http.StatusFound) + http.Redirect(w, r, "/ui/console?task="+taskIDs[0]+"&msg="+url.QueryEscape(fmt.Sprintf("已为 %d 台设备创建下发任务", len(taskIDs))), http.StatusFound) return } if len(errs) > 0 { diff --git a/internal/web/ui/templates/console.html b/internal/web/ui/templates/console.html index ac8516b..73e7c65 100644 --- a/internal/web/ui/templates/console.html +++ b/internal/web/ui/templates/console.html @@ -14,9 +14,9 @@ 🎥 视频视图