1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-01-19 08:19:15 +08:00

feat: 服务重启重新拉起定时任务

This commit is contained in:
ssongliu 2022-09-27 17:26:01 +08:00 committed by ssongliu
parent fdcfdd7963
commit 7d14d37057
12 changed files with 221 additions and 80 deletions

View File

@ -111,6 +111,24 @@ func (b *BaseApi) UpdateCronjob(c *gin.Context) {
helper.SuccessWithData(c, nil)
}
func (b *BaseApi) UpdateCronjobStatus(c *gin.Context) {
var req dto.CronjobUpdateStatus
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := global.VALID.Struct(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := cronjobService.UpdateStatus(req.ID, req.Status); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
func (b *BaseApi) LoadRecordDetail(c *gin.Context) {
var req dto.DetailFile
if err := c.ShouldBindJSON(&req); err != nil {

View File

@ -37,8 +37,11 @@ type CronjobUpdate struct {
SourceDir string `json:"sourceDir"`
TargetDirID int `json:"targetDirID"`
RetainCopies int `json:"retainCopies" validate:"number,min=1"`
}
Status string `json:"status"`
type CronjobUpdateStatus struct {
ID uint `json:"id" validate:"required"`
Status string `json:"status" validate:"required"`
}
type DetailFile struct {

View File

@ -13,6 +13,7 @@ type CronjobRepo struct{}
type ICronjobRepo interface {
Get(opts ...DBOption) (model.Cronjob, error)
List(opts ...DBOption) ([]model.Cronjob, error)
Page(limit, offset int, opts ...DBOption) (int64, []model.Cronjob, error)
Create(cronjob *model.Cronjob) error
WithByDate(startTime, endTime time.Time) DBOption
@ -20,6 +21,7 @@ type ICronjobRepo interface {
Save(id uint, cronjob model.Cronjob) error
Update(id uint, vars map[string]interface{}) error
Delete(opts ...DBOption) error
DeleteRecord(jobID uint) error
StartRecords(cronjobID uint, targetPath string) model.JobRecords
EndRecords(record model.JobRecords, status, message, records string)
}
@ -38,28 +40,40 @@ func (u *CronjobRepo) Get(opts ...DBOption) (model.Cronjob, error) {
return cronjob, err
}
func (u *CronjobRepo) Page(page, size int, opts ...DBOption) (int64, []model.Cronjob, error) {
var users []model.Cronjob
func (u *CronjobRepo) List(opts ...DBOption) ([]model.Cronjob, error) {
var cronjobs []model.Cronjob
db := global.DB.Model(&model.Cronjob{})
for _, opt := range opts {
db = opt(db)
}
count := int64(0)
db = db.Count(&count)
err := db.Limit(size).Offset(size * (page - 1)).Find(&users).Error
return count, users, err
err := db.Find(&cronjobs).Error
return cronjobs, err
}
func (u *CronjobRepo) Page(page, size int, opts ...DBOption) (int64, []model.Cronjob, error) {
var cronjobs []model.Cronjob
db := global.DB.Model(&model.Cronjob{})
for _, opt := range opts {
db = opt(db)
}
count := int64(0)
db = db.Count(&count)
err := db.Order("created_at").Limit(size).Offset(size * (page - 1)).Find(&cronjobs).Error
return count, cronjobs, err
}
func (u *CronjobRepo) PageRecords(page, size int, opts ...DBOption) (int64, []model.JobRecords, error) {
var users []model.JobRecords
var cronjobs []model.JobRecords
db := global.DB.Model(&model.JobRecords{})
for _, opt := range opts {
db = opt(db)
}
count := int64(0)
db = db.Count(&count)
err := db.Limit(size).Offset(size * (page - 1)).Find(&users).Error
return count, users, err
err := db.Order("created_at").Limit(size).Offset(size * (page - 1)).Find(&cronjobs).Error
return count, cronjobs, err
}
func (u *CronjobRepo) Create(cronjob *model.Cronjob) error {
@ -112,3 +126,6 @@ func (u *CronjobRepo) Delete(opts ...DBOption) error {
}
return db.Delete(&model.Cronjob{}).Error
}
func (u *CronjobRepo) DeleteRecord(jobID uint) error {
return global.DB.Where("cronjob_id = ?", jobID).Delete(&model.JobRecords{}).Error
}

View File

@ -19,11 +19,13 @@ import (
"github.com/1Panel-dev/1Panel/utils/cloud_storage"
"github.com/jinzhu/copier"
"github.com/pkg/errors"
"github.com/robfig/cron/v3"
)
const (
errRecord = "errRecord"
errHandle = "errHandle"
noRecord = "noRecord"
)
type CronjobService struct{}
@ -33,6 +35,7 @@ type ICronjobService interface {
SearchRecords(search dto.SearchRecord) (int64, interface{}, error)
Create(cronjobDto dto.CronjobCreate) error
Save(id uint, req dto.CronjobUpdate) error
UpdateStatus(id uint, status string) error
Delete(ids []uint) error
}
@ -93,19 +96,32 @@ func (u *CronjobService) Create(cronjobDto dto.CronjobCreate) error {
if err := cronjobRepo.Create(&cronjob); err != nil {
return err
}
if err := u.StartJob(&cronjob); err != nil {
return err
}
return nil
}
func (u *CronjobService) StartJob(cronjob *model.Cronjob) error {
global.Cron.Remove(cron.EntryID(cronjob.EntryID))
var (
entryID int
err error
)
switch cronjobDto.Type {
switch cronjob.Type {
case "shell":
entryID, err = u.AddShellJob(&cronjob)
entryID, err = u.AddShellJob(cronjob)
case "curl":
entryID, err = u.AddCurlJob(&cronjob)
entryID, err = u.AddCurlJob(cronjob)
case "directory":
entryID, err = u.AddDirectoryJob(&cronjob)
entryID, err = u.AddDirectoryJob(cronjob)
case "website":
entryID, err = u.AddWebSiteJob(cronjob)
case "database":
entryID, err = u.AddDatabaseJob(cronjob)
default:
entryID, err = u.AddShellJob(cronjob)
}
if err != nil {
return err
}
@ -119,8 +135,25 @@ func (u *CronjobService) Delete(ids []uint) error {
if cronjob.ID == 0 {
return constant.ErrRecordNotFound
}
global.Cron.Remove(cron.EntryID(cronjob.EntryID))
_ = cronjobRepo.DeleteRecord(ids[0])
if err := os.RemoveAll(fmt.Sprintf("%s/%s/%s-%v", constant.TaskDir, cronjob.Type, cronjob.Name, cronjob.ID)); err != nil {
global.LOG.Errorf("rm file %s/%s/%s-%v failed, err: %v", constant.TaskDir, cronjob.Type, cronjob.Name, cronjob.ID, err)
}
return cronjobRepo.Delete(commonRepo.WithByID(ids[0]))
}
cronjobs, err := cronjobRepo.List(commonRepo.WithIdsIn(ids))
if err != nil {
return err
}
for i := range cronjobs {
global.Cron.Remove(cron.EntryID(cronjobs[i].EntryID))
_ = cronjobRepo.DeleteRecord(cronjobs[i].ID)
if err := os.RemoveAll(fmt.Sprintf("%s/%s/%s-%v", constant.TaskDir, cronjobs[i].Type, cronjobs[i].Name, cronjobs[i].ID)); err != nil {
global.LOG.Errorf("rm file %s/%s/%s-%v failed, err: %v", constant.TaskDir, cronjobs[i].Type, cronjobs[i].Name, cronjobs[i].ID, err)
}
}
return cronjobRepo.Delete(commonRepo.WithIdsIn(ids))
}
@ -129,15 +162,33 @@ func (u *CronjobService) Save(id uint, req dto.CronjobUpdate) error {
if err := copier.Copy(&cronjob, &req); err != nil {
return errors.WithMessage(constant.ErrStructTransform, err.Error())
}
if err := u.StartJob(&cronjob); err != nil {
return err
}
return cronjobRepo.Save(id, cronjob)
}
func (u *CronjobService) UpdateStatus(id uint, status string) error {
cronjob, _ := cronjobRepo.Get(commonRepo.WithByID(id))
if cronjob.ID == 0 {
return errors.WithMessage(constant.ErrRecordNotFound, "record not found")
}
if status == constant.StatusEnable {
if err := u.StartJob(&cronjob); err != nil {
return err
}
} else {
global.Cron.Remove(cron.EntryID(cronjob.EntryID))
}
return cronjobRepo.Update(cronjob.ID, map[string]interface{}{"status": status})
}
func (u *CronjobService) AddShellJob(cronjob *model.Cronjob) (int, error) {
addFunc := func() {
record := cronjobRepo.StartRecords(cronjob.ID, "")
cmd := exec.Command(cronjob.Script)
stdout, err := cmd.Output()
stdout, err := cmd.CombinedOutput()
if err != nil {
record.Records = errHandle
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), errHandle)
@ -150,6 +201,7 @@ func (u *CronjobService) AddShellJob(cronjob *model.Cronjob) (int, error) {
}
cronjobRepo.EndRecords(record, constant.StatusSuccess, "", record.Records)
}
global.LOG.Infof("add %s job %s successful", cronjob.Type, cronjob.Name)
entryID, err := global.Cron.AddFunc(cronjob.Spec, addFunc)
if err != nil {
return 0, err
@ -183,6 +235,7 @@ func (u *CronjobService) AddCurlJob(cronjob *model.Cronjob) (int, error) {
}
cronjobRepo.EndRecords(record, constant.StatusSuccess, "", record.Records)
}
global.LOG.Infof("add %s job %s successful", cronjob.Type, cronjob.Name)
entryID, err := global.Cron.AddFunc(cronjob.Spec, addFunc)
if err != nil {
return 0, err
@ -209,6 +262,7 @@ func (u *CronjobService) AddDirectoryJob(cronjob *model.Cronjob) (int, error) {
}
cronjobRepo.EndRecords(record, constant.StatusSuccess, "", record.Records)
}
global.LOG.Infof("add %s job %s successful", cronjob.Type, cronjob.Name)
entryID, err := global.Cron.AddFunc(cronjob.Spec, addFunc)
if err != nil {
return 0, err
@ -228,6 +282,11 @@ func (u *CronjobService) AddWebSiteJob(cronjob *model.Cronjob) (int, error) {
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), errHandle)
return
}
if len(message) == 0 {
record.Records = noRecord
cronjobRepo.EndRecords(record, constant.StatusSuccess, "", record.Records)
return
}
record.Records, err = mkdirAndWriteFile(cronjob, record.StartTime, message)
if err != nil {
record.Records = errRecord
@ -235,6 +294,34 @@ func (u *CronjobService) AddWebSiteJob(cronjob *model.Cronjob) (int, error) {
}
cronjobRepo.EndRecords(record, constant.StatusSuccess, "", record.Records)
}
global.LOG.Infof("add %s job %s successful", cronjob.Type, cronjob.Name)
entryID, err := global.Cron.AddFunc(cronjob.Spec, addFunc)
if err != nil {
return 0, err
}
return int(entryID), nil
}
func (u *CronjobService) AddDatabaseJob(cronjob *model.Cronjob) (int, error) {
addFunc := func() {
record := cronjobRepo.StartRecords(cronjob.ID, "")
if len(cronjob.URL) == 0 {
return
}
message, err := tarWithExclude(cronjob, record.StartTime)
if err != nil {
record.Records = errHandle
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), errHandle)
return
}
record.Records, err = mkdirAndWriteFile(cronjob, record.StartTime, message)
if err != nil {
record.Records = errRecord
global.LOG.Errorf("save file %s failed, err: %v", record.Records, err)
}
cronjobRepo.EndRecords(record, constant.StatusSuccess, "", record.Records)
}
global.LOG.Infof("add %s job %s successful", cronjob.Type, cronjob.Name)
entryID, err := global.Cron.AddFunc(cronjob.Spec, addFunc)
if err != nil {
return 0, err
@ -268,18 +355,27 @@ func tarWithExclude(cronjob *model.Cronjob, startTime time.Time) ([]byte, error)
return nil, fmt.Errorf("load target dir failed, err: %v", err)
}
excludes := strings.Split(cronjob.ExclusionRules, ";")
name := fmt.Sprintf("%s/%s.tar.gz", targetdir, startTime.Format("20060102150405"))
exStr := []string{"-zcvPf", name}
for _, exclude := range excludes {
exStr = append(exStr, "--exclude")
exStr = append(exStr, exclude)
exStr := []string{}
name := ""
if cronjob.Type == "database" {
exStr = append(exStr, "-zvPf")
name = fmt.Sprintf("%s/%s.gz", targetdir, startTime.Format("20060102150405"))
exStr = append(exStr, name)
} else {
exStr = append(exStr, "-zcvPf")
name = fmt.Sprintf("%s/%s.tar.gz", targetdir, startTime.Format("20060102150405"))
exStr = append(exStr, name)
excludes := strings.Split(cronjob.ExclusionRules, ";")
for _, exclude := range excludes {
exStr = append(exStr, "--exclude")
exStr = append(exStr, exclude)
}
}
exStr = append(exStr, cronjob.SourceDir)
cmd := exec.Command("tar", exStr...)
stdout, err := cmd.Output()
stdout, err := cmd.CombinedOutput()
if err != nil {
return nil, fmt.Errorf("tar zcvPf failed, err: %v", err)
return nil, fmt.Errorf("tar zcPf failed, err: %v", err)
}
if varMaps["type"] != "LOCAL" {
@ -291,6 +387,9 @@ func tarWithExclude(cronjob *model.Cronjob, startTime time.Time) ([]byte, error)
if !isOK {
return nil, fmt.Errorf("cloud storage upload failed, err: %v", err)
}
if err := os.RemoveAll(fmt.Sprintf("%s/%s/%s-%v", constant.TmpDir, cronjob.Type, cronjob.Name, cronjob.ID)); err != nil {
global.LOG.Errorf("rm file %s/%s/%s-%v failed, err: %v", constant.TaskDir, cronjob.Type, cronjob.Name, cronjob.ID, err)
}
}
return stdout, nil
}
@ -305,8 +404,8 @@ func loadTargetInfo(cronjob *model.Cronjob) (map[string]interface{}, string, err
return nil, "", err
}
dir := ""
varMap["type"] = backup.Type
if backup.Type != "LOCAL" {
varMap["type"] = backup.Type
varMap["bucket"] = backup.Bucket
switch backup.Type {
case constant.Sftp:

View File

@ -3,6 +3,9 @@ package cron
import (
"time"
"github.com/1Panel-dev/1Panel/app/model"
"github.com/1Panel-dev/1Panel/app/service"
"github.com/1Panel-dev/1Panel/constant"
"github.com/1Panel-dev/1Panel/cron/job"
"github.com/1Panel-dev/1Panel/global"
"github.com/robfig/cron/v3"
@ -10,15 +13,7 @@ import (
func Run() {
nyc, _ := time.LoadLocation("Asia/Shanghai")
Cron := cron.New(cron.WithLocation(nyc))
// var Cronjobs []model.Cronjob
// if err := global.DB.Where("status = ?", constant.StatusEnable).Find(&Cronjobs).Error; err != nil {
// global.LOG.Errorf("start my cronjob failed, err: %v", err)
// }
// for _, cronjob := range Cronjobs {
// switch cronjob.Type {}
// }
Cron := cron.New(cron.WithLocation(nyc), cron.WithChain(cron.Recover(cron.DefaultLogger)), cron.WithChain(cron.DelayIfStillRunning(cron.DefaultLogger)))
_, err := Cron.AddJob("@every 1m", job.NewMonitorJob())
if err != nil {
global.LOG.Errorf("can not add corn job: %s", err.Error())
@ -26,4 +21,14 @@ func Run() {
Cron.Start()
global.Cron = Cron
var Cronjobs []model.Cronjob
if err := global.DB.Where("status = ?", constant.StatusEnable).Find(&Cronjobs).Error; err != nil {
global.LOG.Errorf("start my cronjob failed, err: %v", err)
}
for _, cronjob := range Cronjobs {
if err := service.ServiceGroupApp.StartJob(&cronjob); err != nil {
global.LOG.Errorf("start %s job %s failed, err: %v", cronjob.Type, cronjob.Name, err)
}
}
}

View File

@ -17,6 +17,7 @@ func (s *CronjobRouter) InitCronjobRouter(Router *gin.RouterGroup) {
withRecordRouter.POST("", baseApi.CreateCronjob)
withRecordRouter.POST("/del", baseApi.DeleteCronjob)
withRecordRouter.PUT(":id", baseApi.UpdateCronjob)
withRecordRouter.POST("/status", baseApi.UpdateCronjobStatus)
cmdRouter.POST("/search", baseApi.SearchCronjob)
cmdRouter.POST("/search/records", baseApi.SearchJobRecords)
cmdRouter.POST("/search/detail", baseApi.LoadRecordDetail)

View File

@ -56,6 +56,9 @@ export namespace Cronjob {
sourceDir: string;
targetDirID: number;
retainCopies: number;
}
export interface UpdateStatus {
id: number;
status: string;
}
export interface SearchRecord extends ReqPage {

View File

@ -25,3 +25,7 @@ export const searchRecords = (params: Cronjob.SearchRecord) => {
export const getRecordDetail = (params: string) => {
return http.post<string>(`cronjobs/search/detail`, { path: params });
};
export const updateStatus = (params: Cronjob.UpdateStatus) => {
return http.post(`cronjobs/status`, params);
};

View File

@ -183,6 +183,9 @@ export default {
saturday: '周六',
sunday: '周日',
shellContent: '脚本内容',
errRecord: '错误的日志记录',
errHandle: '任务执行失败',
noRecord: '执行未产生任何日志',
},
monitor: {
avgLoad: '平均负载',

View File

@ -72,7 +72,7 @@ import RecordDialog from '@/views/cronjob/record/index.vue';
import { loadZero } from '@/views/cronjob/options';
import { onMounted, reactive, ref } from 'vue';
import { loadBackupName } from '@/views/setting/helper';
import { deleteCronjob, editCronjob, getCronjobPage } from '@/api/modules/cronjob';
import { deleteCronjob, getCronjobPage, updateStatus } from '@/api/modules/cronjob';
import { loadWeek } from './options';
import i18n from '@/lang';
import { Cronjob } from '@/api/interface/cronjob';
@ -149,7 +149,7 @@ const beforeChangeStatus = () => {
};
const onChangeStatus = async (row: Cronjob.CronjobInfo) => {
if (switchState.value) {
await editCronjob(row);
await updateStatus({ id: row.id, status: row.status });
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
search();
}

View File

@ -8,7 +8,7 @@
<el-form ref="formRef" :model="dialogData.rowData" label-position="left" :rules="rules" label-width="120px">
<el-form-item :label="$t('cronjob.taskType')" prop="type">
<el-select
@change="changeName(true, dialogData.rowData!.type, dialogData.rowData!.website)"
@change="changeName(true, dialogData.rowData!.type)"
style="width: 100%"
v-model="dialogData.rowData!.type"
>
@ -17,12 +17,7 @@
</el-form-item>
<el-form-item :label="$t('cronjob.taskName')" prop="name">
<el-input
:disabled="dialogData.rowData!.type === 'website' || dialogData.rowData!.type === 'database'"
style="width: 100%"
clearable
v-model="dialogData.rowData!.name"
/>
<el-input style="width: 100%" clearable v-model="dialogData.rowData!.name" />
</el-form-item>
<el-form-item :label="$t('cronjob.cronSpec')" prop="spec">
@ -56,12 +51,18 @@
</el-form-item>
<el-form-item v-if="hasScript()" :label="$t('cronjob.shellContent')" prop="script">
<el-input style="width: 100%" clearable type="textarea" v-model="dialogData.rowData!.script" />
<el-input
style="width: 100%"
clearable
type="textarea"
:autosize="{ minRows: 3, maxRows: 6 }"
v-model="dialogData.rowData!.script"
/>
</el-form-item>
<el-form-item v-if="dialogData.rowData!.type === 'website'" :label="$t('cronjob.website')" prop="website">
<el-select
@change="changeName(false, dialogData.rowData!.type, dialogData.rowData!.website)"
@change="changeName(false, dialogData.rowData!.type)"
style="width: 100%"
v-model="dialogData.rowData!.website"
>
@ -86,7 +87,7 @@
prop="sourceDir"
>
<el-input
@input="changeName(false, dialogData.rowData!.type, dialogData.rowData!.website)"
@input="changeName(false, dialogData.rowData!.type)"
style="width: 100%"
clearable
v-model="dialogData.rowData!.sourceDir"
@ -120,7 +121,13 @@
:label="$t('cronjob.exclusionRules')"
prop="exclusionRules"
>
<el-input style="width: 100%" type="textarea" clearable v-model="dialogData.rowData!.exclusionRules" />
<el-input
style="width: 100%"
type="textarea"
:autosize="{ minRows: 3, maxRows: 6 }"
clearable
v-model="dialogData.rowData!.exclusionRules"
/>
</el-form-item>
</el-form>
<template #footer>
@ -216,7 +223,7 @@ const varifySpec = (rule: any, value: any, callback: any) => {
callback();
};
const rules = reactive({
name: [Rules.requiredInput],
name: [Rules.requiredInput, Rules.name],
type: [Rules.requiredSelect],
specType: [Rules.requiredSelect],
spec: [
@ -232,7 +239,7 @@ const rules = reactive({
website: [Rules.requiredSelect],
database: [Rules.requiredSelect],
url: [Rules.requiredInput],
sourceDir: [Rules.requiredInput],
sourceDir: [Rules.requiredSelect],
targetDirID: [Rules.requiredSelect, Rules.number],
retainCopies: [Rules.number],
});
@ -261,42 +268,15 @@ function isBackup() {
function hasScript() {
return dialogData.value.rowData!.type === 'shell' || dialogData.value.rowData!.type === 'sync';
}
function changeName(isChangeType: boolean, type: string, targetName: string) {
function changeName(isChangeType: boolean, type: string) {
if (isChangeType) {
targetName = '';
if (isBackup()) {
if (backupOptions.value.length === 0) {
ElMessage.error(i18n.global.t('cronjob.missBackupAccount'));
}
}
}
switch (type) {
case 'website':
targetName = targetName ? targetName : i18n.global.t('cronjob.all');
dialogData.value.rowData!.name = `${i18n.global.t('cronjob.website')} [ ${targetName} ]`;
break;
case 'database':
targetName = targetName ? targetName : i18n.global.t('cronjob.all');
dialogData.value.rowData!.name = `${i18n.global.t('cronjob.database')} [ ${targetName} ]`;
break;
case 'directory':
targetName = targetName ? targetName : '/etc/1panel';
dialogData.value.rowData!.name = `${i18n.global.t('cronjob.directory')} [ ${targetName} ]`;
break;
case 'sync':
dialogData.value.rowData!.name = i18n.global.t('cronjob.syncDateName');
break;
case 'release':
dialogData.value.rowData!.name = i18n.global.t('cronjob.releaseMemory');
break;
case 'curl':
dialogData.value.rowData!.name = i18n.global.t('cronjob.curl');
dialogData.value.rowData!.url = 'http://';
break;
default:
dialogData.value.rowData!.name = '';
break;
}
dialogData.value.rowData!.name = `${type}-test`;
}
function restForm() {
if (formRef.value) {

View File

@ -95,7 +95,7 @@
trigger="click"
style="white-space: pre-wrap"
>
<div style="margin-left: 20px; height: 400px; overflow: auto">
<div style="margin-left: 20px; max-height: 400px; overflow: auto">
<span style="white-space: pre-wrap">{{ dialogData.rowData!.script }}</span>
</div>
<template #reference>
@ -121,7 +121,7 @@
</el-col>
<el-col :span="8" v-if="isBackup()">
<el-form-item :label="$t('cronjob.target')">
{{ dialogData.rowData!.targetDirID }}
{{ loadBackupName(dialogData.rowData!.targetDir) }}
</el-form-item>
</el-col>
<el-col :span="8" v-if="isBackup()">
@ -139,7 +139,9 @@
v-if="dialogData.rowData!.type === 'website' || dialogData.rowData!.type === 'directory'"
>
<el-form-item :label="$t('cronjob.exclusionRules')">
{{ dialogData.rowData!.exclusionRules }}
<div v-for="item in dialogData.rowData!.exclusionRules.split(';')" :key="item">
<el-tag>{{ item }}</el-tag>
</div>
</el-form-item>
</el-col>
</el-row>
@ -176,13 +178,18 @@
<el-row>
<el-col :span="24">
<el-form-item :label="$t('commons.table.records')">
<span
v-if="currentRecord?.records! === 'errRecord' || currentRecord?.records! === 'errHandle'|| currentRecord?.records! === 'noRecord'"
>
{{ $t('cronjob.' + currentRecord?.records!) }}
</span>
<el-popover
placement="right"
:width="600"
trigger="click"
style="white-space: pre-wrap"
>
<div style="margin-left: 20px; height: 400px; overflow: auto">
<div style="margin-left: 20px; max-height: 400px; overflow: auto">
<span style="white-space: pre-wrap">
{{ currentRecordDetail }}
</span>
@ -212,6 +219,7 @@
import { reactive, ref } from 'vue';
import { Cronjob } from '@/api/interface/cronjob';
import { loadZero, loadWeek } from '@/views/cronjob/options';
import { loadBackupName } from '@/views/setting/helper';
import { searchRecords, getRecordDetail } from '@/api/modules/cronjob';
import { dateFromat } from '@/utils/util';
import i18n from '@/lang';