diff --git a/dashboard/app/(dashboard)/skill-definition/page.tsx b/dashboard/app/(dashboard)/skill-definition/page.tsx index 6b184f7..f00042a 100644 --- a/dashboard/app/(dashboard)/skill-definition/page.tsx +++ b/dashboard/app/(dashboard)/skill-definition/page.tsx @@ -7,6 +7,7 @@ import { MoreHorizontalIcon, PlusIcon, RefreshCwIcon, + RotateCcwIcon, SearchIcon, Trash2Icon, } from "lucide-react" @@ -37,6 +38,7 @@ import { createSkillDefinition, deleteSkillDefinition, fetchSkillDefinitions, + restoreSkillDefinition, updateSkillDefinition, updateSkillDefinitionStatus, type CreateSkillDefinitionPayload, @@ -64,6 +66,7 @@ type SkillRowProps = { openDebugDialog: (item: SkillDefinition) => void handleToggleStatus: (item: SkillDefinition) => void handleDelete: (item: SkillDefinition) => void + handleRestore: (item: SkillDefinition) => void } function SkillRow({ @@ -73,10 +76,17 @@ function SkillRow({ openDebugDialog, handleToggleStatus, handleDelete, + handleRestore, }: SkillRowProps) { + const isDeleted = item.status === Status.Deleted + const statusBadgeVariant = isDeleted + ? "destructive" + : item.status === Status.Ok + ? "default" + : "outline" return ( - +
@@ -113,11 +123,11 @@ function SkillRow({
void handleToggleStatus(item)} aria-label={`${item.name} 状态切换`} /> - + {getEnumLabel(StatusLabels, item.status as keyof typeof StatusLabels)}
@@ -147,14 +157,24 @@ function SkillRow({ - void handleDelete(item)} - className="text-destructive focus:text-destructive" - > - - {actionLoadingId === item.id ? "删除中..." : "删除"} - + {isDeleted ? ( + void handleRestore(item)} + > + + {actionLoadingId === item.id ? "恢复中..." : "恢复"} + + ) : ( + void handleDelete(item)} + className="text-destructive focus:text-destructive" + > + + {actionLoadingId === item.id ? "删除中..." : "删除"} + + )} @@ -288,6 +308,10 @@ export default function DashboardSkillsPage() { } async function handleToggleStatus(item: SkillDefinition) { + if (item.status === Status.Deleted) { + return + } + const nextStatus = item.status === Status.Ok ? Status.Disabled : Status.Ok setActionLoadingId(item.id) @@ -303,6 +327,10 @@ export default function DashboardSkillsPage() { } async function handleDelete(item: SkillDefinition) { + if (item.status === Status.Deleted) { + return + } + setActionLoadingId(item.id) try { await deleteSkillDefinition(item.id) @@ -315,6 +343,23 @@ export default function DashboardSkillsPage() { } } + async function handleRestore(item: SkillDefinition) { + if (item.status !== Status.Deleted) { + return + } + + setActionLoadingId(item.id) + try { + await restoreSkillDefinition(item.id) + toast.success(`已恢复 Skill:${item.name}`) + await loadData() + } catch (error) { + toast.error(error instanceof Error ? error.message : "恢复 Skill 失败") + } finally { + setActionLoadingId(null) + } + } + return ( <>
@@ -388,6 +433,7 @@ export default function DashboardSkillsPage() { openDebugDialog={openDebugDialog} handleToggleStatus={handleToggleStatus} handleDelete={handleDelete} + handleRestore={handleRestore} /> ))} diff --git a/dashboard/lib/api/admin.ts b/dashboard/lib/api/admin.ts index 668c4c3..cac1f8f 100644 --- a/dashboard/lib/api/admin.ts +++ b/dashboard/lib/api/admin.ts @@ -913,6 +913,13 @@ export function deleteSkillDefinition(id: number) { }) } +export function restoreSkillDefinition(id: number) { + return request("/api/dashboard/skill-definition/restore", { + method: "POST", + body: JSON.stringify({ id }), + }) +} + export function debugRunSkillDefinition(payload: SkillDebugRunPayload) { return request("/api/dashboard/skill-definition/debug_run", { method: "POST", diff --git a/internal/controllers/dashboard/skill_definition_controller.go b/internal/controllers/dashboard/skill_definition_controller.go index 906958a..0773349 100644 --- a/internal/controllers/dashboard/skill_definition_controller.go +++ b/internal/controllers/dashboard/skill_definition_controller.go @@ -2,6 +2,7 @@ package dashboard import ( "context" + "strings" "time" "cs-agent/internal/builders" @@ -30,6 +31,9 @@ func (c *SkillDefinitionController) AnyList() *web.JsonResult { params.QueryFilter{ParamName: "name", Op: params.Like}, params.QueryFilter{ParamName: "code", Op: params.Like}, ).Desc("id") + if _, ok := params.Get(c.Ctx, "status"); !ok { + cnd.Where("status <> ?", enums.StatusDeleted) + } list, paging := services.SkillDefinitionService.FindPageByCnd(cnd) results := make([]response.SkillDefinitionResponse, 0, len(list)) for _, item := range list { @@ -43,9 +47,13 @@ func (c *SkillDefinitionController) GetList_all() *web.JsonResult { return web.JsonError(err) } - list := services.SkillDefinitionService.Find(params.NewSqlCnd(c.Ctx, + cnd := params.NewSqlCnd(c.Ctx, params.QueryFilter{ParamName: "status"}, - ).Desc("id")) + ).Desc("id") + if status, ok := params.Get(c.Ctx, "status"); !ok || strings.TrimSpace(status) == "" { + cnd.Where("status <> ?", enums.StatusDeleted) + } + list := services.SkillDefinitionService.Find(cnd) results := make([]response.SkillDefinitionResponse, 0, len(list)) for _, item := range list { results = append(results, builders.BuildSkillDefinitionResponse(&item)) @@ -114,9 +122,16 @@ func (c *SkillDefinitionController) PostUpdate_status() *web.JsonResult { if !enums.IsValidStatus(req.Status) { return web.JsonErrorMsg("状态值不合法") } - if services.SkillDefinitionService.Get(req.ID) == nil { + item := services.SkillDefinitionService.Get(req.ID) + if item == nil { return web.JsonErrorMsg("Skill 不存在") } + if item.Status == enums.StatusDeleted { + return web.JsonErrorMsg("已删除的 Skill 不能直接修改状态,请先恢复") + } + if req.Status == int(enums.StatusDeleted) { + return web.JsonErrorMsg("请使用删除接口处理删除状态") + } if err := services.SkillDefinitionService.Updates(req.ID, map[string]any{ "status": req.Status, @@ -156,6 +171,39 @@ func (c *SkillDefinitionController) PostDelete() *web.JsonResult { return web.JsonSuccess() } +func (c *SkillDefinitionController) PostRestore() *web.JsonResult { + operator, err := services.AuthService.RequirePermission(c.Ctx, constants.PermissionSkillDefinitionDelete) + if err != nil { + return web.JsonError(err) + } + + req := request.RestoreSkillDefinitionRequest{} + if err := params.ReadJSON(c.Ctx, &req); err != nil { + return web.JsonError(err) + } + if req.ID <= 0 { + return web.JsonErrorMsg("Skill ID 不合法") + } + + item := services.SkillDefinitionService.Get(req.ID) + if item == nil { + return web.JsonErrorMsg("Skill 不存在") + } + if item.Status != enums.StatusDeleted { + return web.JsonErrorMsg("仅已删除的 Skill 支持恢复") + } + + if err := services.SkillDefinitionService.Updates(req.ID, map[string]any{ + "status": enums.StatusDisabled, + "update_user_id": operator.UserID, + "update_user_name": operator.Username, + "updated_at": time.Now(), + }); err != nil { + return web.JsonError(err) + } + return web.JsonSuccess() +} + func (c *SkillDefinitionController) PostDebug_run() *web.JsonResult { if _, err := services.AuthService.RequirePermission(c.Ctx, constants.PermissionSkillDefinitionView); err != nil { return web.JsonError(err) diff --git a/internal/pkg/dto/request/skill_request.go b/internal/pkg/dto/request/skill_request.go index 132a4ae..ccde269 100644 --- a/internal/pkg/dto/request/skill_request.go +++ b/internal/pkg/dto/request/skill_request.go @@ -25,6 +25,10 @@ type DeleteSkillDefinitionRequest struct { ID int64 `json:"id"` } +type RestoreSkillDefinitionRequest struct { + ID int64 `json:"id"` +} + type UpdateSkillDefinitionStatusRequest struct { ID int64 `json:"id"` Status int `json:"status"`