From 8937acee1336e4f0047d20a774345caa9971ffe6 Mon Sep 17 00:00:00 2001 From: ssongliu <73214554+ssongliu@users.noreply.github.com> Date: Sun, 28 Sep 2025 18:18:17 +0800 Subject: [PATCH] feat: Support manual stopping of shell-type tasks (#10524) Refs #10067 --- agent/app/api/v2/cronjob.go | 22 ++++++++++ agent/app/service/cronjob.go | 15 +++++++ agent/app/service/cronjob_helper.go | 3 +- agent/app/task/task.go | 7 +++- agent/global/global.go | 4 ++ agent/i18n/lang/en.yaml | 1 + agent/i18n/lang/es-ES.yaml | 2 + agent/i18n/lang/ko.yaml | 1 + agent/i18n/lang/ms.yaml | 1 + agent/i18n/lang/pt-BR.yaml | 1 + agent/i18n/lang/ru.yaml | 1 + agent/i18n/lang/tr.yaml | 1 + agent/i18n/lang/zh-Hant.yaml | 1 + agent/i18n/lang/zh.yaml | 1 + agent/router/ro_cronjob.go | 1 + agent/utils/cmd/cmdx.go | 29 +++++++++++--- frontend/src/api/modules/cronjob.ts | 4 ++ frontend/src/lang/modules/en.ts | 2 + frontend/src/lang/modules/es-es.ts | 2 + frontend/src/lang/modules/ja.ts | 2 + frontend/src/lang/modules/ko.ts | 2 + frontend/src/lang/modules/ms.ts | 2 + frontend/src/lang/modules/pt-br.ts | 2 + frontend/src/lang/modules/ru.ts | 2 + frontend/src/lang/modules/tr.ts | 2 + frontend/src/lang/modules/zh-Hant.ts | 2 + frontend/src/lang/modules/zh.ts | 2 + .../views/cronjob/cronjob/record/index.vue | 40 ++++++++++++++++--- 28 files changed, 141 insertions(+), 14 deletions(-) diff --git a/agent/app/api/v2/cronjob.go b/agent/app/api/v2/cronjob.go index 168620eb7..f9f7969af 100644 --- a/agent/app/api/v2/cronjob.go +++ b/agent/app/api/v2/cronjob.go @@ -226,6 +226,28 @@ func (b *BaseApi) CleanRecord(c *gin.Context) { helper.Success(c) } +// @Tags Cronjob +// @Summary Handle stop job +// @Accept json +// @Param request body dto.OperateByID true "request" +// @Success 200 +// @Security ApiKeyAuth +// @Security Timestamp +// @Router /cronjobs/stop [post] +func (b *BaseApi) StopCronJob(c *gin.Context) { + var req dto.OperateByID + if err := helper.CheckBindAndValidate(&req, c); err != nil { + return + } + + if err := cronjobService.HandleStop(req.ID); err != nil { + helper.InternalServer(c, err) + return + } + + helper.Success(c) +} + // @Tags Cronjob // @Summary Delete cronjob // @Accept json diff --git a/agent/app/service/cronjob.go b/agent/app/service/cronjob.go index 289363b52..96eea039c 100644 --- a/agent/app/service/cronjob.go +++ b/agent/app/service/cronjob.go @@ -36,6 +36,7 @@ type ICronjobService interface { Delete(req dto.CronjobBatchDelete) error StartJob(cronjob *model.Cronjob, isUpdate bool) (string, error) CleanRecord(req dto.CronjobClean) error + HandleStop(id uint) error Export(req dto.OperateByIDs) (string, error) Import(req []dto.CronjobTrans) error @@ -617,6 +618,20 @@ func (u *CronjobService) StartJob(cronjob *model.Cronjob, isUpdate bool) (string return strings.Join(ids, ","), nil } +func (u *CronjobService) HandleStop(id uint) error { + record, _ := cronjobRepo.GetRecord(repo.WithByID(id)) + if record.ID == 0 { + return buserr.New("ErrRecordNotFound") + } + if len(record.TaskID) == 0 { + return nil + } + if cancle, ok := global.TaskCtxMap[record.TaskID]; ok { + cancle() + } + return nil +} + func (u *CronjobService) Delete(req dto.CronjobBatchDelete) error { for _, id := range req.IDs { cronjob, _ := cronjobRepo.Get(repo.WithByID(id)) diff --git a/agent/app/service/cronjob_helper.go b/agent/app/service/cronjob_helper.go index 949fccbbb..cdcff709d 100644 --- a/agent/app/service/cronjob_helper.go +++ b/agent/app/service/cronjob_helper.go @@ -132,7 +132,8 @@ func (u *CronjobService) loadTask(cronjob *model.Cronjob, record *model.JobRecor } func (u *CronjobService) handleShell(cronjob model.Cronjob, taskItem *task.Task) { - cmdMgr := cmd.NewCommandMgr(cmd.WithTask(*taskItem)) + cmdMgr := cmd.NewCommandMgr(cmd.WithTask(*taskItem), cmd.WithContext(taskItem.TaskCtx)) + taskItem.AddSubTaskWithOps(i18n.GetWithName("HandleShell", cronjob.Name), func(t *task.Task) error { if len(cronjob.ContainerName) != 0 { scriptItem := cronjob.Script diff --git a/agent/app/task/task.go b/agent/app/task/task.go index 18b8b99f5..23b3e3087 100644 --- a/agent/app/task/task.go +++ b/agent/app/task/task.go @@ -25,6 +25,8 @@ type ActionFunc func(*Task) error type RollbackFunc func(*Task) type Task struct { + TaskCtx context.Context + Name string TaskID string Logger *log.Logger @@ -147,7 +149,9 @@ func NewTask(name, operate, taskScope, taskID string, resourceID uint) (*Task, e Operate: operate, } taskRepo := repo.NewITaskRepo() - task := &Task{Name: name, logFile: file, Logger: logger, taskRepo: taskRepo, Task: taskModel, Writer: writer} + ctx, cancle := context.WithCancel(context.Background()) + global.TaskCtxMap[taskID] = cancle + task := &Task{TaskCtx: ctx, Name: name, logFile: file, Logger: logger, taskRepo: taskRepo, Task: taskModel, Writer: writer} return task, nil } @@ -211,6 +215,7 @@ func (t *Task) AddSubTaskWithIgnoreErr(name string, action ActionFunc) { } func (s *SubTask) Execute() error { + defer delete(global.TaskCtxMap, s.RootTask.TaskID) subTaskName := s.Name if s.Name == "" { subTaskName = i18n.GetMsgByKey("SubTask") diff --git a/agent/global/global.go b/agent/global/global.go index 2935f1f80..8548e2c14 100644 --- a/agent/global/global.go +++ b/agent/global/global.go @@ -1,6 +1,8 @@ package global import ( + "context" + badger_db "github.com/1Panel-dev/1Panel/agent/init/cache/db" "github.com/go-playground/validator/v10" "github.com/nicksnyder/go-i18n/v2/i18n" @@ -34,4 +36,6 @@ var ( AlertBaseJobID cron.EntryID AlertResourceJobID cron.EntryID + + TaskCtxMap = make(map[string]context.CancelFunc) ) diff --git a/agent/i18n/lang/en.yaml b/agent/i18n/lang/en.yaml index 131c9b1ca..120f56ed5 100644 --- a/agent/i18n/lang/en.yaml +++ b/agent/i18n/lang/en.yaml @@ -14,6 +14,7 @@ ErrApiConfigKeyInvalid: 'API interface key error: {{ .detail }}' ErrApiConfigIPInvalid: 'The IP used to call the API interface is not in the whitelist: {{ .detail }}' ErrApiConfigDisable: 'This interface prohibits the use of API interface calls: {{ .detail }}' ErrApiConfigKeyTimeInvalid: 'API interface timestamp error: {{ .detail }}' +ErrShutDown: "Command manually terminated!" ErrMinQuickJump: "Please set at least one quick jump entry!" ErrMaxQuickJump: "You can set up to four quick jump entries!" diff --git a/agent/i18n/lang/es-ES.yaml b/agent/i18n/lang/es-ES.yaml index 1d00cf459..72a22d0c6 100644 --- a/agent/i18n/lang/es-ES.yaml +++ b/agent/i18n/lang/es-ES.yaml @@ -14,6 +14,8 @@ ErrApiConfigKeyInvalid: 'Error en la clave de la interfaz API: {{ .detail }}' ErrApiConfigIPInvalid: 'La IP usada para llamar a la API no está en la lista blanca: {{ .detail }}' ErrApiConfigDisable: 'Esta interfaz prohíbe el uso de llamadas a la API: {{ .detail }}' ErrApiConfigKeyTimeInvalid: 'Error en la marca de tiempo de la interfaz API: {{ .detail }}' +ErrShutDown: "¡Comando terminado manualmente!" + ErrMinQuickJump: "¡Por favor configure al menos una entrada de acceso rápido!" ErrMaxQuickJump: "¡Puede configurar hasta cuatro entradas de acceso rápido!" diff --git a/agent/i18n/lang/ko.yaml b/agent/i18n/lang/ko.yaml index 28748f5e4..5b493ba83 100644 --- a/agent/i18n/lang/ko.yaml +++ b/agent/i18n/lang/ko.yaml @@ -14,6 +14,7 @@ ErrApiConfigKeyInvalid: 'API 인터페이스 키 오류: {{ .detail }}' ErrApiConfigIPInvalid: 'API 인터페이스를 호출하는 데 사용된 IP가 허용 목록에 없습니다: {{ .detail }}' ErrApiConfigDisable: '이 인터페이스는 API 인터페이스 호출 사용을 금지합니다: {{ .detail }}' ErrApiConfigKeyTimeInvalid: 'API 인터페이스 타임스탬프 오류: {{ .detail }}' +ErrShutDown: "명령이 수동으로 종료되었습니다!" ErrMinQuickJump: "최소 하나의 빠른 점프 항목을 설정해 주세요!" ErrMaxQuickJump: "최대 네 개의 빠른 점프 항목을 설정할 수 있습니다!" diff --git a/agent/i18n/lang/ms.yaml b/agent/i18n/lang/ms.yaml index a65ab9eaa..984b24035 100644 --- a/agent/i18n/lang/ms.yaml +++ b/agent/i18n/lang/ms.yaml @@ -17,6 +17,7 @@ ErrApiConfigKeyTimeInvalid: 'Ralat cap masa antara muka API: {{ .detail }}' StartPushSSLToNode: "Mula menolak sijil ke nod" PushSSLToNodeFailed: "Gagal menolak sijil ke nod: {{ .err }}" PushSSLToNodeSuccess: "Berjaya menolak sijil ke nod" +ErrShutDown: "Arahan dihentikan secara manual!" ErrMinQuickJump: "Sila tetapkan sekurang-kurangnya satu entri lompat pantas!" ErrMaxQuickJump: "Anda boleh menetapkan sehingga empat entri lompat pantas!" diff --git a/agent/i18n/lang/pt-BR.yaml b/agent/i18n/lang/pt-BR.yaml index 703ed0ef9..4d805f7d5 100644 --- a/agent/i18n/lang/pt-BR.yaml +++ b/agent/i18n/lang/pt-BR.yaml @@ -17,6 +17,7 @@ ErrApiConfigKeyTimeInvalid: 'Erro de registro de data e hora da interface da API StartPushSSLToNode: "Iniciando o envio do certificado para o nó" PushSSLToNodeFailed: "Falha ao enviar o certificado para o nó: {{ .err }}" PushSSLToNodeSuccess: "Certificado enviado com sucesso para o nó" +ErrShutDown: "Comando terminado manualmente!" ErrMinQuickJump: "Defina pelo menos uma entrada de salto rápido!" ErrMaxQuickJump: "Você pode definir até quatro entradas de salto rápido!" diff --git a/agent/i18n/lang/ru.yaml b/agent/i18n/lang/ru.yaml index e2efee708..44ac9f421 100644 --- a/agent/i18n/lang/ru.yaml +++ b/agent/i18n/lang/ru.yaml @@ -17,6 +17,7 @@ ErrApiConfigKeyTimeInvalid: 'Ошибка временной метки инте StartPushSSLToNode: "Начало отправки сертификата на узел" PushSSLToNodeFailed: "Не удалось отправить сертификат на узел: {{ .err }}" PushSSLToNodeSuccess: "Сертификат успешно отправлен на узел" +ErrShutDown: "Команда завершена вручную!" ErrMinQuickJump: "Пожалуйста, установите хотя бы одну запись быстрого перехода!" ErrMaxQuickJump: "Можно установить до четырех записей быстрого перехода!" diff --git a/agent/i18n/lang/tr.yaml b/agent/i18n/lang/tr.yaml index 2358cc2fb..61c9a639c 100644 --- a/agent/i18n/lang/tr.yaml +++ b/agent/i18n/lang/tr.yaml @@ -17,6 +17,7 @@ ErrApiConfigKeyTimeInvalid: 'API arayüz zaman damgası hatası: {{ .detail }}' StartPushSSLToNode: "Sertifika düğüme gönderilmeye başlandı" PushSSLToNodeFailed: "Sertifika düğüme gönderilemedi: {{ .err }}" PushSSLToNodeSuccess: "Sertifika düğüme başarıyla gönderildi" +ErrShutDown: "Komut manuel olarak sonlandırıldı!" ErrMinQuickJump: "Lütfen en az bir hızlı atlama girişi ayarlayın!" ErrMaxQuickJump: "En fazla dört hızlı atlama girişi ayarlayabilirsiniz!" diff --git a/agent/i18n/lang/zh-Hant.yaml b/agent/i18n/lang/zh-Hant.yaml index d2a179d84..7d0f8ff2c 100644 --- a/agent/i18n/lang/zh-Hant.yaml +++ b/agent/i18n/lang/zh-Hant.yaml @@ -14,6 +14,7 @@ ErrApiConfigKeyInvalid: 'API 介面金鑰錯誤: {{ .detail }}' ErrApiConfigIPInvalid: '呼叫 API 介面 IP 不在白名單: {{ .detail }}' ErrApiConfigDisable: '此介面禁止使用 API 介面呼叫: {{ .detail }}' ErrApiConfigKeyTimeInvalid: 'API 介面時間戳記錯誤: {{ .detail }}' +ErrShutDown: "命令被手動結束!" ErrMinQuickJump: "請至少設定一個快速跳轉入口!" ErrMaxQuickJump: "最多可設定四個快速跳轉入口!" diff --git a/agent/i18n/lang/zh.yaml b/agent/i18n/lang/zh.yaml index 01a8ba24f..7a589109a 100644 --- a/agent/i18n/lang/zh.yaml +++ b/agent/i18n/lang/zh.yaml @@ -14,6 +14,7 @@ ErrApiConfigKeyInvalid: "API 接口密钥错误: {{ .detail }}" ErrApiConfigIPInvalid: "调用 API 接口 IP 不在白名单: {{ .detail }}" ErrApiConfigDisable: "此接口禁止使用 API 接口调用: {{ .detail }}" ErrApiConfigKeyTimeInvalid: "API 接口时间戳错误: {{ .detail }}" +ErrShutDown: "命令被手动结束!" ErrMinQuickJump: "请至少设置一个快速跳转入口!" ErrMaxQuickJump: "最多可设置四个快速跳转入口!" diff --git a/agent/router/ro_cronjob.go b/agent/router/ro_cronjob.go index 72bd8cbc9..576baa8ad 100644 --- a/agent/router/ro_cronjob.go +++ b/agent/router/ro_cronjob.go @@ -18,6 +18,7 @@ func (s *CronjobRouter) InitRouter(Router *gin.RouterGroup) { cmdRouter.POST("/load/info", baseApi.LoadCronjobInfo) cmdRouter.GET("/script/options", baseApi.LoadScriptOptions) cmdRouter.POST("/del", baseApi.DeleteCronjob) + cmdRouter.POST("/stop", baseApi.StopCronJob) cmdRouter.POST("/update", baseApi.UpdateCronjob) cmdRouter.POST("/group/update", baseApi.UpdateCronjobGroup) cmdRouter.POST("/status", baseApi.UpdateCronjobStatus) diff --git a/agent/utils/cmd/cmdx.go b/agent/utils/cmd/cmdx.go index acec8c76c..0c3779d48 100644 --- a/agent/utils/cmd/cmdx.go +++ b/agent/utils/cmd/cmdx.go @@ -18,6 +18,7 @@ import ( ) type CommandHelper struct { + context context.Context workDir string outputFile string scriptPath string @@ -95,15 +96,23 @@ func (c *CommandHelper) RunWithStdoutBashCf(command string, arg ...interface{}) func (c *CommandHelper) run(name string, arg ...string) (string, error) { var cmd *exec.Cmd - var ctx context.Context var cancel context.CancelFunc if c.timeout != 0 { - ctx, cancel = context.WithTimeout(context.Background(), c.timeout) - defer cancel() - cmd = exec.CommandContext(ctx, name, arg...) + if c.context == nil { + c.context, cancel = context.WithTimeout(context.Background(), c.timeout) + defer cancel() + } else { + c.context, cancel = context.WithTimeout(c.context, c.timeout) + defer cancel() + } + cmd = exec.CommandContext(c.context, name, arg...) } else { - cmd = exec.Command(name, arg...) + if c.context == nil { + cmd = exec.Command(name, arg...) + } else { + cmd = exec.CommandContext(c.context, name, arg...) + } } customWriter := &CustomWriter{taskItem: c.taskItem} @@ -141,16 +150,24 @@ func (c *CommandHelper) run(name string, arg ...string) (string, error) { customWriter.Flush() } if c.timeout != 0 { - if ctx != nil && errors.Is(ctx.Err(), context.DeadlineExceeded) { + if c.context != nil && errors.Is(c.context.Err(), context.DeadlineExceeded) { return "", buserr.New("ErrCmdTimeout") } } if err != nil { + if err.Error() == "signal: killed" { + return "", buserr.New("ErrShutDown") + } return handleErr(stdout, stderr, c.IgnoreExist1, err) } return stdout.String(), nil } +func WithContext(ctx context.Context) Option { + return func(s *CommandHelper) { + s.context = ctx + } +} func WithOutputFile(outputFile string) Option { return func(s *CommandHelper) { s.outputFile = outputFile diff --git a/frontend/src/api/modules/cronjob.ts b/frontend/src/api/modules/cronjob.ts index db289bcc5..2a0c6490a 100644 --- a/frontend/src/api/modules/cronjob.ts +++ b/frontend/src/api/modules/cronjob.ts @@ -50,6 +50,10 @@ export const searchRecords = (params: Cronjob.SearchRecord) => { return http.post>(`cronjobs/search/records`, params); }; +export const stopCronjob = (id: number) => { + return http.post(`cronjobs/stop`, { id: id }); +}; + export const cleanRecords = (id: number, cleanData: boolean, cleanRemoteData: boolean) => { return http.post(`cronjobs/records/clean`, { cronjobID: id, cleanData: cleanData, cleanRemoteData }); }; diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index 6f84880eb..c3c7b950d 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -1035,6 +1035,8 @@ const message = { record: 'Records', viewRecords: 'View records', shell: 'Shell', + stop: 'Manual Stop', + stopHelper: 'This operation will force stop the current task execution. Continue?', log: 'Backup logs', logHelper: 'Backup system log', ogHelper1: '1.1Panel System log ', diff --git a/frontend/src/lang/modules/es-es.ts b/frontend/src/lang/modules/es-es.ts index 0091536c5..7c6e5fc94 100644 --- a/frontend/src/lang/modules/es-es.ts +++ b/frontend/src/lang/modules/es-es.ts @@ -1033,6 +1033,8 @@ const message = { record: 'Registros', viewRecords: 'Ver registros', shell: 'Shell', + stop: 'Detención Manual', + stopHelper: 'Esta operación forzará la detención de la ejecución de la tarea actual. ¿Continuar?', log: 'Registros de respaldo', logHelper: 'Registro del sistema de copias de seguridad', ogHelper1: '1. Registro del sistema de 1Panel', diff --git a/frontend/src/lang/modules/ja.ts b/frontend/src/lang/modules/ja.ts index 52aaa507a..43018376f 100644 --- a/frontend/src/lang/modules/ja.ts +++ b/frontend/src/lang/modules/ja.ts @@ -1006,6 +1006,8 @@ const message = { record: '記録', viewRecords: '記録', shell: 'シェル', + stop: '手動終了', + stopHelper: 'この操作により現在のタスクの実行が強制停止されます。続行しますか?', log: 'バックアップログ', logHelper: 'バックアップシステムログ', ogHelper1: '1.1パネルシステムログ', diff --git a/frontend/src/lang/modules/ko.ts b/frontend/src/lang/modules/ko.ts index e33593e9c..7b8520b87 100644 --- a/frontend/src/lang/modules/ko.ts +++ b/frontend/src/lang/modules/ko.ts @@ -996,6 +996,8 @@ const message = { record: '레코드', viewRecords: '레코드 보기', shell: '셸', + stop: '수동 중지', + stopHelper: '이 작업은 현재 작업 실행을 강제로 중지합니다. 계속하시겠습니까?', log: '백업 로그', logHelper: '시스템 백업 로그', ogHelper1: '1. 1Panel 시스템 로그', diff --git a/frontend/src/lang/modules/ms.ts b/frontend/src/lang/modules/ms.ts index ec5a2a5df..ab75a47e8 100644 --- a/frontend/src/lang/modules/ms.ts +++ b/frontend/src/lang/modules/ms.ts @@ -1028,6 +1028,8 @@ const message = { record: 'Rekod', viewRecords: 'Rekod', shell: 'Shell', + stop: 'Hentikan Manual', + stopHelper: 'Operasi ini akan memaksa menghentikan pelaksanaan tugas semasa. Teruskan?', log: 'Log sandaran', logHelper: 'Log sistem sandaran', ogHelper1: '1. Log Sistem 1Panel ', diff --git a/frontend/src/lang/modules/pt-br.ts b/frontend/src/lang/modules/pt-br.ts index 3aa85ed45..385510ffb 100644 --- a/frontend/src/lang/modules/pt-br.ts +++ b/frontend/src/lang/modules/pt-br.ts @@ -1025,6 +1025,8 @@ const message = { record: 'Registros', viewRecords: 'Visualizar registros', shell: 'Shell', + stop: 'Parada Manual', + stopHelper: 'Esta operação forçará a parada da execução da tarefa atual. Continuar?', log: 'Logs de backup', logHelper: 'Backup do log do sistema', ogHelper1: '1. Log do sistema 1Panel', diff --git a/frontend/src/lang/modules/ru.ts b/frontend/src/lang/modules/ru.ts index 3ae11fcdd..bde73a4f9 100644 --- a/frontend/src/lang/modules/ru.ts +++ b/frontend/src/lang/modules/ru.ts @@ -1022,6 +1022,8 @@ const message = { record: 'Записи', viewRecords: 'Записи', shell: 'Shell', + stop: 'Ручная Остановка', + stopHelper: 'Эта операция принудительно остановит выполнение текущей задачи. Продолжить?', log: 'Логи резервного копирования', logHelper: 'Резервное копирование системного лога', ogHelper1: '1. Системный лог 1Panel', diff --git a/frontend/src/lang/modules/tr.ts b/frontend/src/lang/modules/tr.ts index 8d5e0766a..ee7011420 100644 --- a/frontend/src/lang/modules/tr.ts +++ b/frontend/src/lang/modules/tr.ts @@ -1047,6 +1047,8 @@ const message = { record: 'Kayıtlar', viewRecords: 'Kayıtları görüntüle', shell: 'Shell', + stop: 'Manuel Durdur', + stopHelper: 'Bu işlem mevcut görev yürütmesini zorla durduracaktır. Devam etmek istiyor musunuz?', log: 'Yedekleme logları', logHelper: 'Sistem logunu yedekle', ogHelper1: '1.1Panel Sistem logu ', diff --git a/frontend/src/lang/modules/zh-Hant.ts b/frontend/src/lang/modules/zh-Hant.ts index e87d22bb8..6c97e9b30 100644 --- a/frontend/src/lang/modules/zh-Hant.ts +++ b/frontend/src/lang/modules/zh-Hant.ts @@ -987,6 +987,8 @@ const message = { record: '報告', viewRecords: '查看報告', shell: 'Shell 腳本', + stop: '手動結束', + stopHelper: '該操作將強制停止該次任務執行,是否繼續?', log: '備份日誌', logHelper: '備份系統日誌', logHelper1: '1. 1Panel 系統日誌', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index ab483b7f3..8a67844e5 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -986,6 +986,8 @@ const message = { record: '报告', viewRecords: '查看报告', shell: 'Shell 脚本', + stop: '手动结束', + stopHelper: '该操作将强制停止该次任务执行,是否继续?', log: '备份日志', logHelper: '备份系统日志', logHelper1: '1. 1Panel 系统日志', diff --git a/frontend/src/views/cronjob/cronjob/record/index.vue b/frontend/src/views/cronjob/cronjob/record/index.vue index 62b972606..df0e80d68 100644 --- a/frontend/src/views/cronjob/cronjob/record/index.vue +++ b/frontend/src/views/cronjob/cronjob/record/index.vue @@ -91,14 +91,13 @@
- - + - + + + +
import { reactive, ref } from 'vue'; import { Cronjob } from '@/api/interface/cronjob'; -import { searchRecords, handleOnce, updateStatus, cleanRecords } from '@/api/modules/cronjob'; +import { searchRecords, handleOnce, updateStatus, cleanRecords, stopCronjob } from '@/api/modules/cronjob'; import { dateFormat } from '@/utils/util'; import LogFile from '@/components/log/file/index.vue'; import i18n from '@/lang'; @@ -374,7 +390,8 @@ const search = async (changeToLatest: boolean) => { } }; -const forDetail = async (row: Cronjob.Record) => { +const forDetail = (row: Cronjob.Record) => { + console.log('123'); currentRecord.value = row; }; @@ -400,6 +417,17 @@ const onClean = async () => { } }; +const onStop = async (id: number) => { + ElMessageBox.confirm(i18n.global.t('cronjob.stopHelper'), i18n.global.t('cronjob.stop'), { + confirmButtonText: i18n.global.t('commons.button.confirm'), + cancelButtonText: i18n.global.t('commons.button.cancel'), + type: 'warning', + }).then(async () => { + await stopCronjob(id); + MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); + }); +}; + const cleanRecord = async () => { delLoading.value = true; await cleanRecords(dialogData.value.rowData.id, cleanData.value, false)