feat: add task running status #88
This commit is contained in:
@@ -99,6 +99,13 @@ type Task struct {
|
|||||||
UpdatedAt LocalTime `json:"updated_at"`
|
UpdatedAt LocalTime `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Task) IsRunning() bool {
|
||||||
|
if string(t.RunningGo) == "" || string(t.RunningGo) == "[]" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (Task) TableName() string {
|
func (Task) TableName() string {
|
||||||
return constant.TablePrefix + "tasks"
|
return constant.TablePrefix + "tasks"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ type TaskVO struct {
|
|||||||
NextRun *models.LocalTime `json:"next_run"`
|
NextRun *models.LocalTime `json:"next_run"`
|
||||||
CreatedAt models.LocalTime `json:"created_at"`
|
CreatedAt models.LocalTime `json:"created_at"`
|
||||||
UpdatedAt models.LocalTime `json:"updated_at"`
|
UpdatedAt models.LocalTime `json:"updated_at"`
|
||||||
|
RunningStatus string `json:"running_status"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToTaskVO 将 Task 模型转换为 TaskVO
|
// ToTaskVO 将 Task 模型转换为 TaskVO
|
||||||
@@ -64,6 +65,12 @@ func ToTaskVO(task *models.Task) *TaskVO {
|
|||||||
NextRun: task.NextRun,
|
NextRun: task.NextRun,
|
||||||
CreatedAt: task.CreatedAt,
|
CreatedAt: task.CreatedAt,
|
||||||
UpdatedAt: task.UpdatedAt,
|
UpdatedAt: task.UpdatedAt,
|
||||||
|
RunningStatus: func() string {
|
||||||
|
if task.IsRunning() {
|
||||||
|
return "running"
|
||||||
|
}
|
||||||
|
return "idle"
|
||||||
|
}(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+51
-31
@@ -5,45 +5,65 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
defaultShell string
|
||||||
|
defaultArgs []string
|
||||||
|
shellOnce sync.Once
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
// GetShell 返回当前操作系统的 shell 和参数
|
// GetShell 返回当前操作系统的 shell 和参数
|
||||||
func GetShell() (shell string, args []string) {
|
func GetShell() (shell string, args []string) {
|
||||||
if runtime.GOOS == "windows" {
|
shellOnce.Do(func() {
|
||||||
return "cmd", []string{}
|
if runtime.GOOS == "windows" {
|
||||||
}
|
defaultShell = "cmd"
|
||||||
|
defaultArgs = []string{}
|
||||||
// 优先使用环境变量中的 SHELL
|
return
|
||||||
if envShell := os.Getenv("SHELL"); envShell != "" {
|
|
||||||
if _, err := os.Stat(envShell); err == nil {
|
|
||||||
return envShell, []string{}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// 尝试在 PATH 中查找 bash
|
// 1. 优先在 PATH 中查找 bash
|
||||||
if path, err := exec.LookPath("bash"); err == nil {
|
if path, err := exec.LookPath("bash"); err == nil {
|
||||||
return path, []string{}
|
defaultShell = path
|
||||||
}
|
defaultArgs = []string{}
|
||||||
|
return
|
||||||
// 尝试在 PATH 中查找 zsh
|
|
||||||
if path, err := exec.LookPath("zsh"); err == nil {
|
|
||||||
return path, []string{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 尝试在 PATH 中查找 sh
|
|
||||||
if path, err := exec.LookPath("sh"); err == nil {
|
|
||||||
return path, []string{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 最后回退到最常见的硬编码路径
|
|
||||||
shells := []string{"/bin/bash", "/usr/bin/bash", "/bin/sh"}
|
|
||||||
for _, sh := range shells {
|
|
||||||
if _, err := os.Stat(sh); err == nil {
|
|
||||||
return sh, []string{}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return "sh", []string{}
|
// 2. 其次使用环境变量中的 SHELL
|
||||||
|
if envShell := os.Getenv("SHELL"); envShell != "" {
|
||||||
|
if _, err := os.Stat(envShell); err == nil {
|
||||||
|
defaultShell = envShell
|
||||||
|
defaultArgs = []string{}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 尝试在 PATH 中查找 zsh 或 sh
|
||||||
|
for _, s := range []string{"zsh", "sh"} {
|
||||||
|
if path, err := exec.LookPath(s); err == nil {
|
||||||
|
defaultShell = path
|
||||||
|
defaultArgs = []string{}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 最后回退到硬编码路径
|
||||||
|
shells := []string{"/usr/bin/bash", "/bin/bash", "/usr/bin/sh", "/bin/sh"}
|
||||||
|
for _, sh := range shells {
|
||||||
|
if _, err := os.Stat(sh); err == nil {
|
||||||
|
defaultShell = sh
|
||||||
|
defaultArgs = []string{}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultShell = "sh"
|
||||||
|
defaultArgs = []string{}
|
||||||
|
})
|
||||||
|
|
||||||
|
return defaultShell, defaultArgs
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetShellCommand 返回执行命令的 shell 和参数
|
// GetShellCommand 返回执行命令的 shell 和参数
|
||||||
|
|||||||
@@ -362,6 +362,7 @@ export interface Task {
|
|||||||
enabled: boolean
|
enabled: boolean
|
||||||
last_run: string
|
last_run: string
|
||||||
next_run: string
|
next_run: string
|
||||||
|
running_status?: string
|
||||||
created_at?: string
|
created_at?: string
|
||||||
updated_at?: string
|
updated_at?: string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -570,8 +570,10 @@ watch(() => route.query.agent_id, (newVal: any) => {
|
|||||||
<!-- 列表 -->
|
<!-- 列表 -->
|
||||||
<div class="divide-y text-sm">
|
<div class="divide-y text-sm">
|
||||||
<div v-for="(task, index) in tasks" :key="`large-${task.id}`"
|
<div v-for="(task, index) in tasks" :key="`large-${task.id}`"
|
||||||
class="flex items-center gap-4 px-4 py-1.5 hover:bg-muted/30 transition-colors">
|
class="flex items-center gap-2 px-4 py-1.5 hover:bg-muted/30 transition-colors">
|
||||||
<div class="w-12 shrink-0 pl-1 text-muted-foreground tabular-nums">#{{ total - (currentPage - 1) * pageSize - index }}</div>
|
<div v-if="task.running_status === 'running'" class="h-2 w-2 rounded-full bg-amber-500 animate-pulse shadow-[0_0_8px_rgba(245,158,11,0.5)] shrink-0" title="运行中" />
|
||||||
|
<div v-else class="h-1.5 w-1.5 rounded-full bg-muted-foreground/20 shrink-0" />
|
||||||
|
<div class="w-12 shrink-0 text-muted-foreground tabular-nums">#{{ total - (currentPage - 1) * pageSize - index }}</div>
|
||||||
<span class="w-8 shrink-0 flex justify-center" :title="getTaskTypeTitle(task.type || 'task')">
|
<span class="w-8 shrink-0 flex justify-center" :title="getTaskTypeTitle(task.type || 'task')">
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<GitBranch v-if="task.type === TASK_TYPE.REPO" class="h-4 w-4 text-primary" />
|
<GitBranch v-if="task.type === TASK_TYPE.REPO" class="h-4 w-4 text-primary" />
|
||||||
@@ -664,8 +666,10 @@ watch(() => route.query.agent_id, (newVal: any) => {
|
|||||||
<!-- 列表 -->
|
<!-- 列表 -->
|
||||||
<div class="divide-y text-sm">
|
<div class="divide-y text-sm">
|
||||||
<div v-for="(task, index) in tasks" :key="`medium-${task.id}`"
|
<div v-for="(task, index) in tasks" :key="`medium-${task.id}`"
|
||||||
class="flex items-center gap-4 px-4 py-2.5 hover:bg-muted/30 transition-colors">
|
class="flex items-center gap-2 px-4 py-2.5 hover:bg-muted/30 transition-colors">
|
||||||
<div class="w-12 shrink-0 pl-1 text-muted-foreground tabular-nums text-xs">#{{ total - (currentPage - 1) * pageSize - index }}</div>
|
<div v-if="task.running_status === 'running'" class="h-1.5 w-1.5 rounded-full bg-amber-500 animate-pulse shadow-[0_0_8px_rgba(245,158,11,0.5)] shrink-0" />
|
||||||
|
<div v-else class="h-1 w-1 rounded-full bg-muted-foreground/20 shrink-0" />
|
||||||
|
<div class="w-12 shrink-0 text-muted-foreground tabular-nums text-xs">#{{ total - (currentPage - 1) * pageSize - index }}</div>
|
||||||
<div class="w-48 shrink-0 flex items-center gap-2 overflow-hidden">
|
<div class="w-48 shrink-0 flex items-center gap-2 overflow-hidden">
|
||||||
<span class="shrink-0" :title="getTaskTypeTitle(task.type || 'task')">
|
<span class="shrink-0" :title="getTaskTypeTitle(task.type || 'task')">
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
@@ -732,6 +736,8 @@ watch(() => route.query.agent_id, (newVal: any) => {
|
|||||||
<div v-for="(task, index) in tasks" :key="`small-${task.id}`" class="p-3 hover:bg-muted/50 transition-colors">
|
<div v-for="(task, index) in tasks" :key="`small-${task.id}`" class="p-3 hover:bg-muted/50 transition-colors">
|
||||||
<div class="flex items-start justify-between mb-3 border-b border-border/40 pb-2">
|
<div class="flex items-start justify-between mb-3 border-b border-border/40 pb-2">
|
||||||
<div class="flex items-center gap-2 flex-1 min-w-0 pr-2">
|
<div class="flex items-center gap-2 flex-1 min-w-0 pr-2">
|
||||||
|
<div v-if="task.running_status === 'running'" class="h-1.5 w-1.5 rounded-full bg-amber-500 animate-pulse shadow-[0_0_8px_rgba(245,158,11,0.5)] shrink-0" />
|
||||||
|
<div v-else class="h-1 w-1 rounded-full bg-muted-foreground/20 shrink-0" />
|
||||||
<span class="text-xs text-muted-foreground tabular-nums flex-shrink-0">#{{ total - (currentPage - 1) * pageSize - index }}</span>
|
<span class="text-xs text-muted-foreground tabular-nums flex-shrink-0">#{{ total - (currentPage - 1) * pageSize - index }}</span>
|
||||||
<span class="shrink-0">
|
<span class="shrink-0">
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
|
|||||||
Reference in New Issue
Block a user