From 6c5326dc04bcd297a7f877d4a65421a325a0cef9 Mon Sep 17 00:00:00 2001 From: engigu Date: Tue, 23 Dec 2025 23:41:32 +0800 Subject: [PATCH] feat: add task workdir option --- internal/controllers/task_controller.go | 32 +++++++++++++++++++++++-- web/src/components/DirTreeSelect.vue | 29 +++++++++++++++++++--- 2 files changed, 56 insertions(+), 5 deletions(-) diff --git a/internal/controllers/task_controller.go b/internal/controllers/task_controller.go index 705001d..8b196cc 100644 --- a/internal/controllers/task_controller.go +++ b/internal/controllers/task_controller.go @@ -1,8 +1,10 @@ package controllers import ( + "path/filepath" "strconv" + "baihu/internal/constant" "baihu/internal/services" "baihu/internal/utils" @@ -21,6 +23,29 @@ func NewTaskController(taskService *services.TaskService, cronService *services. } } +// resolveWorkDir 将相对路径转换为绝对路径 +func resolveWorkDir(workDir string) string { + if workDir == "" { + // 空则使用默认 scripts 目录 + absPath, err := filepath.Abs(constant.ScriptsWorkDir) + if err != nil { + return constant.ScriptsWorkDir + } + return absPath + } + // 如果已经是绝对路径,直接返回 + if filepath.IsAbs(workDir) { + return workDir + } + // 相对路径,基于 scripts 目录 + fullPath := filepath.Join(constant.ScriptsWorkDir, workDir) + absPath, err := filepath.Abs(fullPath) + if err != nil { + return fullPath + } + return absPath +} + func (tc *TaskController) CreateTask(c *gin.Context) { var req struct { Name string `json:"name" binding:"required"` @@ -42,7 +67,10 @@ func (tc *TaskController) CreateTask(c *gin.Context) { return } - task := tc.taskService.CreateTask(req.Name, req.Command, req.Schedule, req.Timeout, req.WorkDir, req.CleanConfig, req.Envs) + // 转换为绝对路径 + workDir := resolveWorkDir(req.WorkDir) + + task := tc.taskService.CreateTask(req.Name, req.Command, req.Schedule, req.Timeout, workDir, req.CleanConfig, req.Envs) tc.cronService.AddTask(task) utils.Success(c, task) @@ -102,7 +130,7 @@ func (tc *TaskController) UpdateTask(c *gin.Context) { } } - task := tc.taskService.UpdateTask(id, req.Name, req.Command, req.Schedule, req.Timeout, req.WorkDir, req.CleanConfig, req.Envs, req.Enabled) + task := tc.taskService.UpdateTask(id, req.Name, req.Command, req.Schedule, req.Timeout, resolveWorkDir(req.WorkDir), req.CleanConfig, req.Envs, req.Enabled) if task == nil { utils.NotFound(c, "任务不存在") return diff --git a/web/src/components/DirTreeSelect.vue b/web/src/components/DirTreeSelect.vue index eea53fa..ac6087c 100644 --- a/web/src/components/DirTreeSelect.vue +++ b/web/src/components/DirTreeSelect.vue @@ -78,13 +78,36 @@ function selectRoot() { open.value = false } +// 检查是否选中(支持绝对路径匹配) +function isSelected(dirPath: string): boolean { + if (!props.modelValue) return false + // 直接匹配相对路径 + if (props.modelValue === dirPath) return true + // 绝对路径以相对路径结尾 + if (props.modelValue.endsWith('/' + dirPath)) return true + return false +} + +// 检查是否是默认目录 +function isDefaultSelected(): boolean { + if (!props.modelValue) return true + // 绝对路径以 /scripts 结尾且没有子目录 + if (props.modelValue.endsWith('/scripts') || props.modelValue.endsWith('/data/scripts')) return true + return false +} + watch(open, (val) => { if (val) loadTree() }) const displayValue = computed(() => { if (!props.modelValue) return props.placeholder || 'scripts (默认)' - return props.modelValue + // 如果是绝对路径,只显示最后部分 + const parts = props.modelValue.split('/') + const lastPart = parts[parts.length - 1] + // 如果是 scripts 目录本身 + if (lastPart === 'scripts') return 'scripts (默认)' + return lastPart || props.modelValue }) @@ -103,7 +126,7 @@ const displayValue = computed(() => {
@@ -117,7 +140,7 @@ const displayValue = computed(() => { :key="dir.path" :class="[ 'flex items-center gap-1 py-1 px-2 rounded cursor-pointer text-sm', - modelValue === dir.path ? 'bg-primary/10 text-primary' : 'hover:bg-muted' + isSelected(dir.path) ? 'bg-primary/10 text-primary' : 'hover:bg-muted' ]" :style="{ paddingLeft: (dir.depth * 12 + 8) + 'px' }" @click="selectDir(dir.path)"