feat: support group management for cronjob (#9735)

Refs #9258
This commit is contained in:
ssongliu 2025-07-30 12:08:50 +08:00 committed by GitHub
parent 951ab2502e
commit a801140a82
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 219 additions and 25 deletions

View file

@ -270,6 +270,28 @@ func (b *BaseApi) UpdateCronjob(c *gin.Context) {
helper.Success(c) helper.Success(c)
} }
// @Tags Cronjob
// @Summary Update cronjob group
// @Accept json
// @Param request body dto.ChangeGroup true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /cronjobs/group/update [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"cronjobs","output_column":"name","output_value":"name"}],"formatZH":"更新计划任务分组 [name]","formatEN":"update cronjob group [name]"}
func (b *BaseApi) UpdateCronjobGroup(c *gin.Context) {
var req dto.ChangeGroup
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := cronjobService.UpdateGroup(req); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags Cronjob // @Tags Cronjob
// @Summary Update cronjob status // @Summary Update cronjob status
// @Accept json // @Accept json

View file

@ -79,3 +79,8 @@ type ForceDelete struct {
IDs []uint `json:"ids"` IDs []uint `json:"ids"`
ForceDelete bool `json:"forceDelete"` ForceDelete bool `json:"forceDelete"`
} }
type ChangeGroup struct {
ID uint `json:"id" validate:"required"`
GroupID uint `json:"groupID" validate:"required"`
}

View file

@ -6,9 +6,10 @@ import (
type PageCronjob struct { type PageCronjob struct {
PageInfo PageInfo
Info string `json:"info"` Info string `json:"info"`
OrderBy string `json:"orderBy" validate:"required,oneof=name status createdAt"` GroupIDs []uint `json:"groupIDs"`
Order string `json:"order" validate:"required,oneof=null ascending descending"` OrderBy string `json:"orderBy" validate:"required,oneof=name status createdAt"`
Order string `json:"order" validate:"required,oneof=null ascending descending"`
} }
type CronjobSpec struct { type CronjobSpec struct {
@ -19,6 +20,7 @@ type CronjobOperate struct {
ID uint `json:"id"` ID uint `json:"id"`
Name string `json:"name" validate:"required"` Name string `json:"name" validate:"required"`
Type string `json:"type" validate:"required"` Type string `json:"type" validate:"required"`
GroupID uint `json:"groupID"`
SpecCustom bool `json:"specCustom"` SpecCustom bool `json:"specCustom"`
Spec string `json:"spec" validate:"required"` Spec string `json:"spec" validate:"required"`
@ -85,6 +87,7 @@ type CronjobInfo struct {
ID uint `json:"id"` ID uint `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Type string `json:"type"` Type string `json:"type"`
GroupID uint `json:"groupID"`
SpecCustom bool `json:"specCustom"` SpecCustom bool `json:"specCustom"`
Spec string `json:"spec"` Spec string `json:"spec"`
@ -129,6 +132,7 @@ type CronjobImport struct {
type CronjobTrans struct { type CronjobTrans struct {
Name string `json:"name"` Name string `json:"name"`
Type string `json:"type"` Type string `json:"type"`
GroupID uint `json:"groupID"`
SpecCustom bool `json:"specCustom"` SpecCustom bool `json:"specCustom"`
Spec string `json:"spec"` Spec string `json:"spec"`

View file

@ -11,6 +11,7 @@ type Cronjob struct {
Name string `gorm:"not null" json:"name"` Name string `gorm:"not null" json:"name"`
Type string `gorm:"not null" json:"type"` Type string `gorm:"not null" json:"type"`
GroupID uint `json:"groupID"`
SpecCustom bool `json:"specCustom"` SpecCustom bool `json:"specCustom"`
Spec string `gorm:"not null" json:"spec"` Spec string `gorm:"not null" json:"spec"`

View file

@ -104,6 +104,15 @@ func WithByDate(startTime, endTime time.Time) DBOption {
} }
} }
func WithByGroups(groupIDs []uint) DBOption {
return func(g *gorm.DB) *gorm.DB {
if len(groupIDs) == 0 {
return g
}
return g.Where("group_id in (?)", groupIDs)
}
}
func WithByCreatedAt(startTime, endTime time.Time) DBOption { func WithByCreatedAt(startTime, endTime time.Time) DBOption {
return func(g *gorm.DB) *gorm.DB { return func(g *gorm.DB) *gorm.DB {
return g.Where("created_at > ? AND created_at < ?", startTime, endTime) return g.Where("created_at > ? AND created_at < ?", startTime, endTime)

View file

@ -32,6 +32,7 @@ type ICronjobService interface {
HandleOnce(id uint) error HandleOnce(id uint) error
Update(id uint, req dto.CronjobOperate) error Update(id uint, req dto.CronjobOperate) error
UpdateStatus(id uint, status string) error UpdateStatus(id uint, status string) error
UpdateGroup(req dto.ChangeGroup) error
Delete(req dto.CronjobBatchDelete) error Delete(req dto.CronjobBatchDelete) error
Download(down dto.CronjobDownload) (string, error) Download(down dto.CronjobDownload) (string, error)
StartJob(cronjob *model.Cronjob, isUpdate bool) (string, error) StartJob(cronjob *model.Cronjob, isUpdate bool) (string, error)
@ -50,7 +51,11 @@ func NewICronjobService() ICronjobService {
} }
func (u *CronjobService) SearchWithPage(search dto.PageCronjob) (int64, interface{}, error) { func (u *CronjobService) SearchWithPage(search dto.PageCronjob) (int64, interface{}, error) {
total, cronjobs, err := cronjobRepo.Page(search.Page, search.PageSize, repo.WithByLikeName(search.Info), repo.WithOrderRuleBy(search.OrderBy, search.Order)) total, cronjobs, err := cronjobRepo.Page(search.Page,
search.PageSize,
repo.WithByGroups(search.GroupIDs),
repo.WithByLikeName(search.Info),
repo.WithOrderRuleBy(search.OrderBy, search.Order))
var dtoCronjobs []dto.CronjobInfo var dtoCronjobs []dto.CronjobInfo
for _, cronjob := range cronjobs { for _, cronjob := range cronjobs {
var item dto.CronjobInfo var item dto.CronjobInfo
@ -116,6 +121,7 @@ func (u *CronjobService) Export(req dto.OperateByIDs) (string, error) {
item := dto.CronjobTrans{ item := dto.CronjobTrans{
Name: cronjob.Name, Name: cronjob.Name,
Type: cronjob.Type, Type: cronjob.Type,
GroupID: cronjob.GroupID,
SpecCustom: cronjob.SpecCustom, SpecCustom: cronjob.SpecCustom,
Spec: cronjob.Spec, Spec: cronjob.Spec,
Executor: cronjob.Executor, Executor: cronjob.Executor,
@ -211,6 +217,7 @@ func (u *CronjobService) Import(req []dto.CronjobTrans) error {
cronjob := model.Cronjob{ cronjob := model.Cronjob{
Name: item.Name, Name: item.Name,
Type: item.Type, Type: item.Type,
GroupID: item.GroupID,
SpecCustom: item.SpecCustom, SpecCustom: item.SpecCustom,
Spec: item.Spec, Spec: item.Spec,
Executor: item.Executor, Executor: item.Executor,
@ -774,6 +781,14 @@ func (u *CronjobService) UpdateStatus(id uint, status string) error {
return cronjobRepo.Update(cronjob.ID, map[string]interface{}{"status": status, "entry_ids": entryIDs}) return cronjobRepo.Update(cronjob.ID, map[string]interface{}{"status": status, "entry_ids": entryIDs})
} }
func (u *CronjobService) UpdateGroup(req dto.ChangeGroup) error {
cronjob, _ := cronjobRepo.Get(repo.WithByID(req.ID))
if cronjob.ID == 0 {
return buserr.New("ErrRecordNotFound")
}
return cronjobRepo.Update(cronjob.ID, map[string]interface{}{"group_id": req.GroupID})
}
func (u *CronjobService) AddCronJob(cronjob *model.Cronjob) (int, error) { func (u *CronjobService) AddCronJob(cronjob *model.Cronjob) (int, error) {
addFunc := func() { addFunc := func() {
u.HandleJob(cronjob) u.HandleJob(cronjob)

View file

@ -19,7 +19,7 @@ import (
) )
var AddTable = &gormigrate.Migration{ var AddTable = &gormigrate.Migration{
ID: "20250507-add-table", ID: "20250729-add-table",
Migrate: func(tx *gorm.DB) error { Migrate: func(tx *gorm.DB) error {
return tx.AutoMigrate( return tx.AutoMigrate(
&model.AppDetail{}, &model.AppDetail{},

View file

@ -19,6 +19,7 @@ func (s *CronjobRouter) InitRouter(Router *gin.RouterGroup) {
cmdRouter.GET("/script/options", baseApi.LoadScriptOptions) cmdRouter.GET("/script/options", baseApi.LoadScriptOptions)
cmdRouter.POST("/del", baseApi.DeleteCronjob) cmdRouter.POST("/del", baseApi.DeleteCronjob)
cmdRouter.POST("/update", baseApi.UpdateCronjob) cmdRouter.POST("/update", baseApi.UpdateCronjob)
cmdRouter.POST("/group/update", baseApi.UpdateCronjobGroup)
cmdRouter.POST("/status", baseApi.UpdateCronjobStatus) cmdRouter.POST("/status", baseApi.UpdateCronjobStatus)
cmdRouter.POST("/handle", baseApi.HandleOnce) cmdRouter.POST("/handle", baseApi.HandleOnce)
cmdRouter.POST("/download", baseApi.TargetDownload) cmdRouter.POST("/download", baseApi.TargetDownload)

View file

@ -116,8 +116,6 @@ func (u *GroupService) Delete(id uint) error {
if err := xpack.UpdateGroup("node", id, defaultGroup.ID); err != nil { if err := xpack.UpdateGroup("node", id, defaultGroup.ID); err != nil {
return err return err
} }
default:
return buserr.New("ErrNotSupportType")
} }
if err != nil { if err != nil {
return err return err

View file

@ -22,6 +22,7 @@ func Init() {
migrations.UpdateOnedrive, migrations.UpdateOnedrive,
migrations.AddClusterMenu, migrations.AddClusterMenu,
migrations.DeleteXpackHideMenu, migrations.DeleteXpackHideMenu,
migrations.AddCronjobGroup,
}) })
if err := m.Migrate(); err != nil { if err := m.Migrate(); err != nil {
global.LOG.Error(err) global.LOG.Error(err)

View file

@ -531,3 +531,13 @@ var DeleteXpackHideMenu = &gormigrate.Migration{
return tx.Model(&model.Setting{}).Where("key = ?", "HideMenu").Update("value", string(updatedJSON)).Error return tx.Model(&model.Setting{}).Where("key = ?", "HideMenu").Update("value", string(updatedJSON)).Error
}, },
} }
var AddCronjobGroup = &gormigrate.Migration{
ID: "20250729-add-cronjob-group",
Migrate: func(tx *gorm.DB) error {
if err := tx.Create(&model.Group{Name: "Default", Type: "cronjob", IsDefault: true}).Error; err != nil {
return err
}
return nil
},
}

View file

@ -5,6 +5,7 @@ export namespace Cronjob {
id: number; id: number;
name: string; name: string;
type: string; type: string;
groupID: number;
specCustom: boolean; specCustom: boolean;
spec: string; spec: string;
specs: Array<string>; specs: Array<string>;

View file

@ -20,6 +20,7 @@ export interface ReqPage {
} }
export interface SearchWithPage { export interface SearchWithPage {
info: string; info: string;
groupIDs: Array<number>;
page: number; page: number;
pageSize: number; pageSize: number;
orderBy?: string; orderBy?: string;

View file

@ -3,7 +3,7 @@ import { ResPage, SearchWithPage } from '../interface';
import { Cronjob } from '../interface/cronjob'; import { Cronjob } from '../interface/cronjob';
import { TimeoutEnum } from '@/enums/http-enum'; import { TimeoutEnum } from '@/enums/http-enum';
export const getCronjobPage = (params: SearchWithPage) => { export const searchCronjobPage = (params: SearchWithPage) => {
return http.post<ResPage<Cronjob.CronjobInfo>>(`/cronjobs/search`, params); return http.post<ResPage<Cronjob.CronjobInfo>>(`/cronjobs/search`, params);
}; };
@ -11,6 +11,10 @@ export const loadNextHandle = (spec: string) => {
return http.post<Array<String>>(`/cronjobs/next`, { spec: spec }); return http.post<Array<String>>(`/cronjobs/next`, { spec: spec });
}; };
export const editCronjobGroup = (id: number, groupID: number) => {
return http.post(`/cronjobs/group/update`, { id: id, groupID: groupID });
};
export const importCronjob = (trans: Array<Cronjob.CronjobTrans>) => { export const importCronjob = (trans: Array<Cronjob.CronjobTrans>) => {
return http.post('cronjobs/import', { cronjobs: trans }, TimeoutEnum.T_60S); return http.post('cronjobs/import', { cronjobs: trans }, TimeoutEnum.T_60S);
}; };

View file

@ -5,7 +5,10 @@
<el-button type="primary" @click="onOpenDialog('')"> <el-button type="primary" @click="onOpenDialog('')">
{{ $t('commons.button.create') }}{{ $t('menu.cronjob') }} {{ $t('commons.button.create') }}{{ $t('menu.cronjob') }}
</el-button> </el-button>
<el-button-group class="ml-4"> <el-button @click="onOpenGroupDialog()">
{{ $t('commons.table.group') }}
</el-button>
<el-button-group>
<el-button plain :disabled="selects.length === 0" @click="onBatchChangeStatus('enable')"> <el-button plain :disabled="selects.length === 0" @click="onBatchChangeStatus('enable')">
{{ $t('commons.button.enable') }} {{ $t('commons.button.enable') }}
</el-button> </el-button>
@ -17,7 +20,7 @@
</el-button> </el-button>
</el-button-group> </el-button-group>
<el-button-group class="ml-4"> <el-button-group>
<el-button @click="onImport"> <el-button @click="onImport">
{{ $t('commons.button.import') }} {{ $t('commons.button.import') }}
</el-button> </el-button>
@ -27,6 +30,17 @@
</el-button-group> </el-button-group>
</template> </template>
<template #rightToolBar> <template #rightToolBar>
<el-select v-model="searchGroupID" @change="search()" clearable class="p-w-200">
<template #prefix>{{ $t('commons.table.group') }}</template>
<div v-for="item in groupOptions" :key="item.id">
<el-option
v-if="item.name === 'Default'"
:label="$t('commons.table.default')"
:value="item.id"
/>
<el-option v-else :label="item.name" :value="item.id" />
</div>
</el-select>
<TableSearch @search="search()" v-model:searchName="searchName" /> <TableSearch @search="search()" v-model:searchName="searchName" />
<TableRefresh @search="search()" /> <TableRefresh @search="search()" />
<TableSetting title="cronjob-refresh" @search="search()" /> <TableSetting title="cronjob-refresh" @search="search()" />
@ -54,6 +68,23 @@
</el-text> </el-text>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column :label="$t('commons.table.group')" min-width="120" prop="group">
<template #default="{ row }">
<fu-select-rw-switch v-model="row.groupID" @change="updateGroup(row)">
<template #read>
{{ row.groupBelong === 'Default' ? $t('commons.table.default') : row.groupBelong }}
</template>
<div v-for="item in groupOptions" :key="item.id">
<el-option
v-if="item.name === 'Default'"
:label="$t('commons.table.default')"
:value="item.id"
/>
<el-option v-else :label="item.name" :value="item.id" />
</div>
</fu-select-rw-switch>
</template>
</el-table-column>
<el-table-column :label="$t('commons.table.status')" :min-width="80" prop="status" sortable> <el-table-column :label="$t('commons.table.status')" :min-width="80" prop="status" sortable>
<template #default="{ row }"> <template #default="{ row }">
<Status <Status
@ -177,6 +208,7 @@
</template> </template>
</OpDialog> </OpDialog>
<OpDialog ref="opExportRef" @search="search" @submit="onSubmitExport()" /> <OpDialog ref="opExportRef" @search="search" @submit="onSubmitExport()" />
<GroupDialog @search="loadGroups" ref="dialogGroupRef" />
<Records @search="search" ref="dialogRecordRef" /> <Records @search="search" ref="dialogRecordRef" />
<Import @search="search" ref="dialogImportRef" /> <Import @search="search" ref="dialogImportRef" />
<Backups @search="search" ref="dialogBackupRef" /> <Backups @search="search" ref="dialogBackupRef" />
@ -188,15 +220,24 @@ import Records from '@/views/cronjob/cronjob/record/index.vue';
import Backups from '@/views/cronjob/cronjob/backup/index.vue'; import Backups from '@/views/cronjob/cronjob/backup/index.vue';
import Import from '@/views/cronjob/cronjob/import/index.vue'; import Import from '@/views/cronjob/cronjob/import/index.vue';
import { computed, onMounted, reactive, ref } from 'vue'; import { computed, onMounted, reactive, ref } from 'vue';
import { deleteCronjob, exportCronjob, getCronjobPage, handleOnce, updateStatus } from '@/api/modules/cronjob'; import {
deleteCronjob,
editCronjobGroup,
exportCronjob,
searchCronjobPage,
handleOnce,
updateStatus,
} from '@/api/modules/cronjob';
import i18n from '@/lang'; import i18n from '@/lang';
import { Cronjob } from '@/api/interface/cronjob'; import { Cronjob } from '@/api/interface/cronjob';
import GroupDialog from '@/components/group/index.vue';
import { ElMessageBox } from 'element-plus'; import { ElMessageBox } from 'element-plus';
import { MsgSuccess } from '@/utils/message'; import { MsgSuccess } from '@/utils/message';
import { hasBackup, transSpecToStr } from './helper'; import { hasBackup, transSpecToStr } from './helper';
import { GlobalStore } from '@/store'; import { GlobalStore } from '@/store';
import router from '@/routers'; import router from '@/routers';
import { getCurrentDateFormatted } from '@/utils/util'; import { getCurrentDateFormatted } from '@/utils/util';
import { getGroupList } from '@/api/modules/group';
const globalStore = GlobalStore(); const globalStore = GlobalStore();
const mobile = computed(() => { const mobile = computed(() => {
@ -226,21 +267,32 @@ const paginationConfig = reactive({
}); });
const searchName = ref(); const searchName = ref();
const defaultGroupID = ref<number>();
const searchGroupID = ref<number>();
const groupOptions = ref();
const dialogGroupRef = ref();
const search = async (column?: any) => { const search = async (column?: any) => {
paginationConfig.orderBy = column?.order ? column.prop : paginationConfig.orderBy; paginationConfig.orderBy = column?.order ? column.prop : paginationConfig.orderBy;
paginationConfig.order = column?.order ? column.order : paginationConfig.order; paginationConfig.order = column?.order ? column.order : paginationConfig.order;
let groupIDs;
if (searchGroupID.value) {
groupIDs = searchGroupID.value === defaultGroupID.value ? [searchGroupID.value, 0] : [searchGroupID.value];
}
let params = { let params = {
info: searchName.value, info: searchName.value,
groupIDs: groupIDs,
page: paginationConfig.currentPage, page: paginationConfig.currentPage,
pageSize: paginationConfig.pageSize, pageSize: paginationConfig.pageSize,
orderBy: paginationConfig.orderBy, orderBy: paginationConfig.orderBy,
order: paginationConfig.order, order: paginationConfig.order,
}; };
loading.value = true; loading.value = true;
await getCronjobPage(params) await searchCronjobPage(params)
.then((res) => { .then((res) => {
loading.value = false; loading.value = false;
data.value = res.data.items || []; data.value = res.data.items || [];
loadGroups();
paginationConfig.total = res.data.total; paginationConfig.total = res.data.total;
}) })
.catch(() => { .catch(() => {
@ -343,6 +395,41 @@ const onSubmitExport = async () => {
}); });
}; };
const loadGroups = async () => {
const res = await getGroupList('cronjob');
groupOptions.value = res.data || [];
for (const item of data.value) {
if (item.groupID === 0) {
item.groupBelong = 'Default';
continue;
}
let hasGroup = false;
for (const group of groupOptions.value) {
if (group.name === 'Default') {
defaultGroupID.value = group.id;
}
if (item.groupID === group.id) {
hasGroup = true;
item.groupBelong = group.name;
}
}
if (!hasGroup) {
item.groupID = null;
item.groupBelong = '-';
}
}
};
const updateGroup = async (row: any) => {
await editCronjobGroup(row.id, row.groupID);
search();
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
};
const onOpenGroupDialog = () => {
dialogGroupRef.value!.acceptParams({ type: 'cronjob', hideDefaultButton: true });
};
const onChangeStatus = async (id: number, status: string) => { const onChangeStatus = async (id: number, status: string) => {
ElMessageBox.confirm(i18n.global.t('cronjob.' + status + 'Msg'), i18n.global.t('cronjob.changeStatus'), { ElMessageBox.confirm(i18n.global.t('cronjob.' + status + 'Msg'), i18n.global.t('cronjob.changeStatus'), {
confirmButtonText: i18n.global.t('commons.button.confirm'), confirmButtonText: i18n.global.t('commons.button.confirm'),

View file

@ -70,14 +70,27 @@
</el-link> </el-link>
</span> </span>
</el-form-item> </el-form-item>
<el-form-item :label="$t('cronjob.taskName')" prop="name"> <el-row :gutter="20">
<el-input <LayoutCol>
class="mini-form-item" <el-form-item :label="$t('cronjob.taskName')" prop="name">
:disabled="!isCreate" <el-input :disabled="!isCreate" clearable v-model.trim="form.name" />
clearable </el-form-item>
v-model.trim="form.name" </LayoutCol>
/> <LayoutCol>
</el-form-item> <el-form-item :label="$t('commons.table.group')" prop="groupID">
<el-select filterable v-model="form.groupID" clearable>
<div v-for="item in groupOptions" :key="item.id">
<el-option
v-if="item.name === 'Default'"
:label="$t('commons.table.default')"
:value="item.id"
/>
<el-option v-else :label="item.name" :value="item.id" />
</div>
</el-select>
</el-form-item>
</LayoutCol>
</el-row>
</el-card> </el-card>
<el-card class="mt-5"> <el-card class="mt-5">
@ -593,10 +606,11 @@
<el-input-number <el-input-number
class="selectClass" class="selectClass"
:min="1" :min="1"
:precision="0"
step-strictly step-strictly
:step="1" :step="1"
v-model.number="form.retainCopies" v-model.number="form.retainCopies"
></el-input-number> />
<span v-if="isBackup()" class="input-help"> <span v-if="isBackup()" class="input-help">
{{ $t('cronjob.retainCopiesHelper1') }} {{ $t('cronjob.retainCopiesHelper1') }}
</span> </span>
@ -629,7 +643,9 @@
</span> </span>
</el-form-item> </el-form-item>
</LayoutCol> </LayoutCol>
<LayoutCol :span="6"> </el-row>
<el-row :gutter="20">
<LayoutCol>
<el-form-item <el-form-item
:label="$t('xpack.alert.alertMethod')" :label="$t('xpack.alert.alertMethod')"
v-if="form.hasAlert" v-if="form.hasAlert"
@ -651,7 +667,7 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
</LayoutCol> </LayoutCol>
<LayoutCol :span="6"> <LayoutCol>
<el-form-item <el-form-item
prop="alertCount" prop="alertCount"
v-if="form.hasAlert" v-if="form.hasAlert"
@ -660,10 +676,11 @@
<el-input-number <el-input-number
class="selectClass" class="selectClass"
:min="1" :min="1"
:precision="0"
step-strictly step-strictly
:step="1" :step="1"
v-model.number="form.alertCount" v-model.number="form.alertCount"
></el-input-number> />
<span class="input-help">{{ $t('xpack.alert.alertCountHelper') }}</span> <span class="input-help">{{ $t('xpack.alert.alertCountHelper') }}</span>
</el-form-item> </el-form-item>
</LayoutCol> </LayoutCol>
@ -696,10 +713,11 @@
<el-input-number <el-input-number
class="selectClass" class="selectClass"
:min="0" :min="0"
:precision="0"
step-strictly step-strictly
:step="1" :step="1"
v-model.number="form.retryTimes" v-model.number="form.retryTimes"
></el-input-number> />
<span class="input-help">{{ $t('cronjob.retryTimesHelper') }}</span> <span class="input-help">{{ $t('cronjob.retryTimesHelper') }}</span>
</el-form-item> </el-form-item>
</LayoutCol> </LayoutCol>
@ -756,6 +774,7 @@ import { storeToRefs } from 'pinia';
import { GlobalStore } from '@/store'; import { GlobalStore } from '@/store';
import LicenseImport from '@/components/license-import/index.vue'; import LicenseImport from '@/components/license-import/index.vue';
import { transferTimeToSecond } from '@/utils/util'; import { transferTimeToSecond } from '@/utils/util';
import { getGroupList } from '@/api/modules/group';
const router = useRouter(); const router = useRouter();
const globalStore = GlobalStore(); const globalStore = GlobalStore();
@ -769,6 +788,7 @@ const form = reactive<Cronjob.CronjobInfo>({
id: 0, id: 0,
name: '', name: '',
type: 'shell', type: 'shell',
groupID: null,
specCustom: false, specCustom: false,
spec: '', spec: '',
specs: [], specs: [],
@ -943,6 +963,7 @@ const accountOptions = ref([]);
const appOptions = ref([]); const appOptions = ref([]);
const userOptions = ref([]); const userOptions = ref([]);
const scriptOptions = ref([]); const scriptOptions = ref([]);
const groupOptions = ref([]);
const dbInfo = reactive({ const dbInfo = reactive({
isExist: false, isExist: false,
@ -1121,6 +1142,19 @@ const loadScriptDir = async (path: string) => {
form.script = path; form.script = path;
}; };
const loadGroups = async () => {
const res = await getGroupList('cronjob');
groupOptions.value = res.data || [];
if (isCreate.value) {
for (const item of groupOptions.value) {
if (item.isDefault) {
form.groupID = item.id;
break;
}
}
}
};
const goBack = () => { const goBack = () => {
router.push({ name: 'CronjobItem' }); router.push({ name: 'CronjobItem' });
}; };
@ -1411,6 +1445,7 @@ onMounted(() => {
} else { } else {
isCreate.value = true; isCreate.value = true;
} }
loadGroups();
search(); search();
}); });
</script> </script>