mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-31 14:08:06 +08:00
feat: 计划任务增删改查实现
This commit is contained in:
parent
6f4e811b7d
commit
f0a7ce855a
@ -79,6 +79,21 @@ func (b *BaseApi) UpdateCronjob(c *gin.Context) {
|
||||
}
|
||||
|
||||
upMap := make(map[string]interface{})
|
||||
upMap["name"] = req.Name
|
||||
upMap["spec_type"] = req.SpecType
|
||||
upMap["week"] = req.Week
|
||||
upMap["day"] = req.Day
|
||||
upMap["hour"] = req.Hour
|
||||
upMap["minute"] = req.Minute
|
||||
|
||||
upMap["script"] = req.Script
|
||||
upMap["website"] = req.Website
|
||||
upMap["database"] = req.Database
|
||||
upMap["source_dir"] = req.SourceDir
|
||||
upMap["target_dir_id"] = req.TargetDirID
|
||||
upMap["exclusion_rules"] = req.ExclusionRules
|
||||
upMap["retain_copies"] = req.RetainCopies
|
||||
upMap["status"] = req.Status
|
||||
if err := cronjobService.Update(id, upMap); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
|
@ -14,25 +14,26 @@ type CronjobCreate struct {
|
||||
ExclusionRules string `json:"exclusionRules"`
|
||||
Database string `json:"database"`
|
||||
URL string `json:"url"`
|
||||
SourceDir string `json:"sourceDir"`
|
||||
TargetDirID int `json:"targetDirID"`
|
||||
RetainCopies int `json:"retainCopies" validate:"number,min=1"`
|
||||
}
|
||||
|
||||
type CronjobUpdate struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
Type string `json:"type" validate:"required"`
|
||||
SpecType string `json:"specType" validate:"required"`
|
||||
Week int `json:"week" validate:"number,max=7,min=1"`
|
||||
Day int `json:"day" validate:"number,max=31,min=1"`
|
||||
Hour int `json:"hour" validate:"number,max=23,min=0"`
|
||||
Minute int `json:"minute" validate:"number,max=60,min=1"`
|
||||
Minute int `json:"minute" validate:"number,max=59,min=0"`
|
||||
|
||||
Script string `json:"script"`
|
||||
Website string `json:"website"`
|
||||
ExclusionRules string `json:"exclusionRules"`
|
||||
Database string `json:"database"`
|
||||
URL string `json:"url"`
|
||||
TargetDirID int `json:"targetDirID" validate:"number,min=1"`
|
||||
SourceDir string `json:"sourceDir"`
|
||||
TargetDirID int `json:"targetDirID"`
|
||||
RetainCopies int `json:"retainCopies" validate:"number,min=1"`
|
||||
|
||||
Status string `json:"status"`
|
||||
@ -53,6 +54,7 @@ type CronjobInfo struct {
|
||||
ExclusionRules string `json:"exclusionRules"`
|
||||
Database string `json:"database"`
|
||||
URL string `json:"url"`
|
||||
SourceDir string `json:"sourceDir"`
|
||||
TargetDir string `json:"targetDir"`
|
||||
TargetDirID int `json:"targetDirID"`
|
||||
RetainCopies int `json:"retainCopies"`
|
||||
|
@ -14,6 +14,7 @@ export namespace Cronjob {
|
||||
exclusionRules: string;
|
||||
database: string;
|
||||
url: string;
|
||||
sourceDir: string;
|
||||
targetDirID: number;
|
||||
targetDir: string;
|
||||
retainCopies: number;
|
||||
@ -33,6 +34,7 @@ export namespace Cronjob {
|
||||
exclusionRules: string;
|
||||
database: string;
|
||||
url: string;
|
||||
sourceDir: string;
|
||||
targetDirID: number;
|
||||
retainCopies: number;
|
||||
}
|
||||
@ -49,6 +51,7 @@ export namespace Cronjob {
|
||||
exclusionRules: string;
|
||||
database: string;
|
||||
url: string;
|
||||
sourceDir: string;
|
||||
targetDirID: number;
|
||||
retainCopies: number;
|
||||
status: string;
|
||||
|
@ -17,6 +17,7 @@ export default {
|
||||
clean: '清空',
|
||||
login: '登录',
|
||||
close: '关闭',
|
||||
view: '详情',
|
||||
saveAndEnable: '保存并启用',
|
||||
},
|
||||
search: {
|
||||
@ -134,7 +135,7 @@ export default {
|
||||
logout: '退出登录',
|
||||
},
|
||||
cronjob: {
|
||||
createCronTask: '创建计划任务',
|
||||
cronTask: '计划任务',
|
||||
taskType: '任务类型',
|
||||
shell: 'Shell 脚本',
|
||||
website: '备份网站',
|
||||
|
@ -8,8 +8,8 @@
|
||||
:data="data"
|
||||
>
|
||||
<template #toolbar>
|
||||
<el-button type="primary" @click="onCreate()">{{ $t('commons.button.create') }}</el-button>
|
||||
<el-button type="danger" plain :disabled="selects.length === 0" @click="onBatchDelete()">
|
||||
<el-button type="primary" @click="onOpenDialog('create')">{{ $t('commons.button.create') }}</el-button>
|
||||
<el-button type="danger" plain :disabled="selects.length === 0" @click="onBatchDelete(null)">
|
||||
{{ $t('commons.button.delete') }}
|
||||
</el-button>
|
||||
</template>
|
||||
@ -29,12 +29,16 @@
|
||||
<el-table-column :label="$t('commons.table.status')" prop="status">
|
||||
<template #default="{ row }">
|
||||
<el-switch
|
||||
@change="onChangeStatus(row)"
|
||||
:before-change="beforeChangeStatus"
|
||||
v-model="row.status"
|
||||
active-text="running"
|
||||
inactive-text="stoped"
|
||||
inline-prompt
|
||||
style="--el-switch-on-color: #13ce66; --el-switch-off-color: #ff4949"
|
||||
active-text="Y"
|
||||
inactive-text="N"
|
||||
active-value="running"
|
||||
inactive-value="stoped"
|
||||
></el-switch>
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('cronjob.cronSpec')">
|
||||
@ -66,165 +70,23 @@
|
||||
<fu-table-operations type="icon" :buttons="buttons" :label="$t('commons.table.operate')" fix />
|
||||
</ComplexTable>
|
||||
|
||||
<el-dialog @close="search" v-model="cronjobVisiable" width="50%">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>{{ $t('cronjob.createCronTask') }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-form :model="form" ref="formRef" label-position="left" :rules="rules" label-width="120px">
|
||||
<el-form-item :label="$t('cronjob.taskType')" prop="type">
|
||||
<el-select
|
||||
@change="changeName(true, form.type, form.website)"
|
||||
style="width: 100%"
|
||||
v-model="form.type"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in typeOptions"
|
||||
:key="item.label"
|
||||
:value="item.value"
|
||||
:label="item.label"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="$t('cronjob.taskName')" prop="name">
|
||||
<el-input
|
||||
:disabled="form.type === 'website' || form.type === 'database'"
|
||||
style="width: 100%"
|
||||
clearable
|
||||
v-model="form.name"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="$t('cronjob.cronSpec')" prop="spec">
|
||||
<el-select style="width: 15%" v-model="form.specType">
|
||||
<el-option
|
||||
v-for="item in specOptions"
|
||||
:key="item.label"
|
||||
:value="item.value"
|
||||
:label="item.label"
|
||||
/>
|
||||
</el-select>
|
||||
<el-select
|
||||
v-if="form.specType === 'perWeek'"
|
||||
style="width: 12%; margin-left: 20px"
|
||||
v-model="form.week"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in weekOptions"
|
||||
:key="item.label"
|
||||
:value="item.value"
|
||||
:label="item.label"
|
||||
/>
|
||||
</el-select>
|
||||
<el-input
|
||||
v-if="form.specType === 'perMonth' || form.specType === 'perNDay'"
|
||||
style="width: 20%; margin-left: 20px"
|
||||
v-model.number="form.day"
|
||||
>
|
||||
<template #append>{{ $t('cronjob.day') }}</template>
|
||||
</el-input>
|
||||
<el-input
|
||||
v-if="form.specType !== 'perHour' && form.specType !== 'perNMinute'"
|
||||
style="width: 20%; margin-left: 20px"
|
||||
v-model.number="form.hour"
|
||||
>
|
||||
<template #append>{{ $t('cronjob.hour') }}</template>
|
||||
</el-input>
|
||||
<el-input style="width: 20%; margin-left: 20px" v-model.number="form.minute">
|
||||
<template #append>{{ $t('cronjob.minute') }}</template>
|
||||
</el-input>
|
||||
</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="form.script" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="form.type === 'website'" :label="$t('cronjob.website')" prop="website">
|
||||
<el-select
|
||||
@change="changeName(false, form.type, form.website)"
|
||||
style="width: 100%"
|
||||
v-model="form.website"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in websiteOptions"
|
||||
:key="item.label"
|
||||
:value="item.value"
|
||||
:label="item.label"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.type === 'database'" :label="$t('cronjob.database')" prop="database">
|
||||
<el-input style="width: 100%" clearable v-model="form.database" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.type === 'directory'" :label="$t('cronjob.sourceDir')" prop="sourceDir">
|
||||
<el-input
|
||||
@input="changeName(false, form.type, form.website)"
|
||||
style="width: 100%"
|
||||
clearable
|
||||
v-model="form.sourceDir"
|
||||
>
|
||||
<template #append>
|
||||
<FileList @choose="loadDir" :dir="true"></FileList>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="isBackup()" :label="$t('cronjob.target')" prop="targetDirID">
|
||||
<el-select style="width: 100%" v-model="form.targetDirID">
|
||||
<el-option
|
||||
v-for="item in backupOptions"
|
||||
:key="item.label"
|
||||
:value="item.value"
|
||||
:label="item.label"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="isBackup()" :label="$t('cronjob.retainCopies')" prop="retainCopies">
|
||||
<el-input-number :min="1" v-model.number="form.retainCopies"></el-input-number>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="form.type === 'curl'" :label="$t('cronjob.url') + 'URL'" prop="url">
|
||||
<el-input style="width: 100%" clearable v-model="form.url" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item
|
||||
v-if="form.type === 'website' || form.type === 'directory'"
|
||||
:label="$t('cronjob.exclusionRules')"
|
||||
prop="exclusionRules"
|
||||
>
|
||||
<el-input style="width: 100%" type="textarea" clearable v-model="form.exclusionRules" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="cronjobVisiable = false">{{ $t('commons.button.cancel') }}</el-button>
|
||||
<el-button type="primary" @click="onSubmit(formRef)">
|
||||
{{ $t('commons.button.confirm') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<OperatrDialog @search="search" ref="dialogRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import ComplexTable from '@/components/complex-table/index.vue';
|
||||
import OperatrDialog from '@/views/cronjob/operate/index.vue';
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { getBackupList } from '@/api/modules/backup';
|
||||
import { getCronjobPage, addCronjob, editCronjob } from '@/api/modules/cronjob';
|
||||
import { ElForm, ElMessage } from 'element-plus';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
import { typeOptions, specOptions, weekOptions, loadWeek } from './options';
|
||||
import FileList from '@/components/file-list/index.vue';
|
||||
import { loadBackupName } from '@/views/setting/helper';
|
||||
import { deleteCronjob, editCronjob, getCronjobPage } from '@/api/modules/cronjob';
|
||||
import { loadWeek } from './options';
|
||||
import i18n from '@/lang';
|
||||
|
||||
const cronjobVisiable = ref<boolean>(false);
|
||||
const operation = ref<string>('create');
|
||||
type FormInstance = InstanceType<typeof ElForm>;
|
||||
const formRef = ref<FormInstance>();
|
||||
import { Cronjob } from '@/api/interface/cronjob';
|
||||
import { useDeleteData } from '@/hooks/use-delete-data';
|
||||
import { ElMessage } from 'element-plus';
|
||||
const selects = ref<any>([]);
|
||||
const switchState = ref<boolean>(false);
|
||||
|
||||
const data = ref();
|
||||
const paginationConfig = reactive({
|
||||
@ -238,86 +100,6 @@ const logSearch = reactive({
|
||||
pageSize: 5,
|
||||
});
|
||||
|
||||
const varifySpec = (rule: any, value: any, callback: any) => {
|
||||
switch (form.specType) {
|
||||
case 'perMonth':
|
||||
case 'perNDay':
|
||||
if (!(Number.isInteger(form.day) && Number.isInteger(form.hour) && Number.isInteger(form.minute))) {
|
||||
callback(new Error(i18n.global.t('cronjob.cronSpecRule')));
|
||||
}
|
||||
break;
|
||||
case 'perWeek':
|
||||
if (!(Number.isInteger(form.week) && Number.isInteger(form.hour) && Number.isInteger(form.minute))) {
|
||||
callback(new Error(i18n.global.t('cronjob.cronSpecRule')));
|
||||
}
|
||||
break;
|
||||
case 'perNHour':
|
||||
if (!(Number.isInteger(form.hour) && Number.isInteger(form.minute))) {
|
||||
callback(new Error(i18n.global.t('cronjob.cronSpecRule')));
|
||||
}
|
||||
break;
|
||||
case 'perHour':
|
||||
case 'perNMinute':
|
||||
if (!Number.isInteger(form.minute)) {
|
||||
callback(new Error(i18n.global.t('cronjob.cronSpecRule')));
|
||||
}
|
||||
break;
|
||||
}
|
||||
callback();
|
||||
};
|
||||
|
||||
const rules = reactive({
|
||||
name: [Rules.requiredInput],
|
||||
type: [Rules.requiredSelect],
|
||||
specType: [Rules.requiredSelect],
|
||||
spec: [
|
||||
{ validator: varifySpec, trigger: 'blur', required: true },
|
||||
{ validator: varifySpec, trigger: 'change', required: true },
|
||||
],
|
||||
week: [Rules.requiredSelect, Rules.number],
|
||||
day: [Rules.number, { max: 31, min: 1 }],
|
||||
hour: [Rules.number, { max: 23, min: 0 }],
|
||||
minute: [Rules.number, { max: 60, min: 1 }],
|
||||
|
||||
script: [Rules.requiredInput],
|
||||
website: [Rules.requiredSelect],
|
||||
database: [Rules.requiredSelect],
|
||||
url: [Rules.requiredInput],
|
||||
sourceDir: [Rules.requiredInput],
|
||||
targetDirID: [Rules.requiredSelect, Rules.number],
|
||||
retainCopies: [Rules.number],
|
||||
});
|
||||
|
||||
const form = reactive({
|
||||
id: 0,
|
||||
name: '',
|
||||
type: '',
|
||||
specType: 'perMonth',
|
||||
spec: '',
|
||||
week: 1,
|
||||
day: 1,
|
||||
hour: 2,
|
||||
minute: 3,
|
||||
|
||||
script: '',
|
||||
website: '',
|
||||
exclusionRules: '',
|
||||
database: '',
|
||||
url: '',
|
||||
sourceDir: '',
|
||||
targetDirID: 0,
|
||||
retainCopies: 3,
|
||||
status: '',
|
||||
});
|
||||
|
||||
const websiteOptions = ref([
|
||||
{ label: '所有', value: 'all' },
|
||||
{ label: '网站1', value: 'web1' },
|
||||
{ label: '网站2', value: 'web2' },
|
||||
]);
|
||||
|
||||
const backupOptions = ref();
|
||||
|
||||
const search = async () => {
|
||||
logSearch.page = paginationConfig.currentPage;
|
||||
logSearch.pageSize = paginationConfig.pageSize;
|
||||
@ -331,142 +113,83 @@ const search = async () => {
|
||||
paginationConfig.total = res.data.total;
|
||||
};
|
||||
|
||||
const onCreate = async () => {
|
||||
operation.value = 'create';
|
||||
form.id = 0;
|
||||
form.name = '';
|
||||
form.type = '';
|
||||
form.specType = 'perMonth';
|
||||
form.spec = '';
|
||||
form.week = 1;
|
||||
form.day = 1;
|
||||
form.hour = 2;
|
||||
form.minute = 3;
|
||||
form.script = '';
|
||||
form.website = '';
|
||||
form.exclusionRules = '';
|
||||
form.database = '';
|
||||
form.url = '';
|
||||
form.sourceDir = '';
|
||||
form.targetDirID = backupOptions.value.length === 0 ? 0 : backupOptions.value[0].value;
|
||||
form.retainCopies = 3;
|
||||
cronjobVisiable.value = true;
|
||||
};
|
||||
const onEdit = async () => {
|
||||
cronjobVisiable.value = true;
|
||||
};
|
||||
const onBatchDelete = async () => {};
|
||||
|
||||
const onSubmit = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return;
|
||||
formEl.validate(async (valid) => {
|
||||
if (!valid) return;
|
||||
if (form.id !== 0 && operation.value === 'edit') {
|
||||
await editCronjob(form);
|
||||
} else if (form.id === 0 && operation.value === 'create') {
|
||||
await addCronjob(form);
|
||||
} else {
|
||||
ElMessage.success(i18n.global.t('commons.msg.notSupportOperation'));
|
||||
return;
|
||||
}
|
||||
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
||||
restForm();
|
||||
search();
|
||||
cronjobVisiable.value = false;
|
||||
});
|
||||
};
|
||||
function restForm() {
|
||||
if (formRef.value) {
|
||||
formRef.value.resetFields();
|
||||
}
|
||||
interface DialogExpose {
|
||||
acceptParams: (params: any) => void;
|
||||
}
|
||||
const dialogRef = ref<DialogExpose>();
|
||||
const onOpenDialog = async (
|
||||
title: string,
|
||||
rowData: Partial<Cronjob.CronjobInfo> = {
|
||||
specType: 'perMonth',
|
||||
week: 1,
|
||||
day: 1,
|
||||
hour: 2,
|
||||
minute: 3,
|
||||
retainCopies: 3,
|
||||
},
|
||||
) => {
|
||||
let params = {
|
||||
title,
|
||||
rowData: { ...rowData },
|
||||
isView: title === 'view',
|
||||
};
|
||||
dialogRef.value!.acceptParams(params);
|
||||
};
|
||||
|
||||
const loadBackups = async () => {
|
||||
const res = await getBackupList();
|
||||
backupOptions.value = [];
|
||||
for (const item of res.data) {
|
||||
backupOptions.value.push({ label: loadBackupName(item.type), value: item.id });
|
||||
const onBatchDelete = async (row: Cronjob.CronjobInfo | null) => {
|
||||
let ids: Array<number> = [];
|
||||
if (row) {
|
||||
ids.push(row.id);
|
||||
} else {
|
||||
selects.value.forEach((item: Cronjob.CronjobInfo) => {
|
||||
ids.push(item.id);
|
||||
});
|
||||
}
|
||||
await useDeleteData(deleteCronjob, { ids: ids }, 'commons.msg.delete', true);
|
||||
search();
|
||||
};
|
||||
const beforeChangeStatus = () => {
|
||||
switchState.value = true;
|
||||
return switchState.value;
|
||||
};
|
||||
const onChangeStatus = async (row: Cronjob.CronjobInfo) => {
|
||||
if (switchState.value) {
|
||||
console.log(row.status);
|
||||
await editCronjob(row);
|
||||
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
||||
search();
|
||||
}
|
||||
};
|
||||
|
||||
const buttons = [
|
||||
{
|
||||
label: i18n.global.t('commons.button.edit'),
|
||||
icon: 'Edit',
|
||||
click: onEdit,
|
||||
click: (row: Cronjob.CronjobInfo) => {
|
||||
onOpenDialog('edit', row);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('commons.button.view'),
|
||||
icon: 'View',
|
||||
click: (row: Cronjob.CronjobInfo) => {
|
||||
onOpenDialog('view', row);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('commons.button.delete'),
|
||||
icon: 'Delete',
|
||||
click: onBatchDelete,
|
||||
click: (row: Cronjob.CronjobInfo) => {
|
||||
onBatchDelete(row);
|
||||
},
|
||||
},
|
||||
];
|
||||
const loadDir = async (path: string) => {
|
||||
form.sourceDir = path;
|
||||
};
|
||||
|
||||
function isBackup() {
|
||||
return form.type === 'website' || form.type === 'database' || form.type === 'directory';
|
||||
}
|
||||
function hasScript() {
|
||||
return form.type === 'shell' || form.type === 'sync';
|
||||
}
|
||||
function changeName(isChangeType: boolean, type: string, targetName: 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');
|
||||
form.name = `${i18n.global.t('cronjob.website')} [ ${targetName} ]`;
|
||||
break;
|
||||
case 'database':
|
||||
targetName = targetName ? targetName : i18n.global.t('cronjob.all');
|
||||
form.name = `${i18n.global.t('cronjob.database')} [ ${targetName} ]`;
|
||||
break;
|
||||
case 'directory':
|
||||
targetName = targetName ? targetName : '/etc/1panel';
|
||||
form.name = `${i18n.global.t('cronjob.directory')} [ ${targetName} ]`;
|
||||
break;
|
||||
case 'sync':
|
||||
form.name = i18n.global.t('cronjob.syncDateName');
|
||||
break;
|
||||
case 'release':
|
||||
form.name = i18n.global.t('cronjob.releaseMemory');
|
||||
break;
|
||||
case 'curl':
|
||||
form.name = i18n.global.t('cronjob.curl');
|
||||
form.url = 'http://';
|
||||
break;
|
||||
default:
|
||||
form.name = '';
|
||||
break;
|
||||
}
|
||||
}
|
||||
function loadZero(i: number) {
|
||||
return i < 10 ? '0' + i : '' + i;
|
||||
}
|
||||
const loadBackupName = (type: string) => {
|
||||
switch (type) {
|
||||
case 'OSS':
|
||||
return i18n.global.t('setting.OSS');
|
||||
break;
|
||||
case 'S3':
|
||||
return i18n.global.t('setting.S3');
|
||||
break;
|
||||
case 'LOCAL':
|
||||
return i18n.global.t('setting.serverDisk');
|
||||
break;
|
||||
default:
|
||||
return type;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
search();
|
||||
loadBackups();
|
||||
});
|
||||
</script>
|
||||
|
341
frontend/src/views/cronjob/operate/index.vue
Normal file
341
frontend/src/views/cronjob/operate/index.vue
Normal file
@ -0,0 +1,341 @@
|
||||
<template>
|
||||
<el-dialog v-model="cronjobVisiable" :destroy-on-close="true" :close-on-click-modal="false" width="50%">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>{{ title }}{{ $t('cronjob.cronTask') }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-form
|
||||
:disabled="dialogData.isView"
|
||||
ref="formRef"
|
||||
:model="dialogData.rowData"
|
||||
label-position="left"
|
||||
:rules="rules"
|
||||
label-width="120px"
|
||||
:hide-required-asterisk="dialogData.isView"
|
||||
>
|
||||
<el-form-item :label="$t('cronjob.taskType')" prop="type">
|
||||
<el-select
|
||||
@change="changeName(true, dialogData.rowData!.type, dialogData.rowData!.website)"
|
||||
style="width: 100%"
|
||||
v-model="dialogData.rowData!.type"
|
||||
>
|
||||
<el-option v-for="item in typeOptions" :key="item.label" :value="item.value" :label="item.label" />
|
||||
</el-select>
|
||||
</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-form-item>
|
||||
|
||||
<el-form-item :label="$t('cronjob.cronSpec')" prop="spec">
|
||||
<el-select style="width: 15%" v-model="dialogData.rowData!.specType">
|
||||
<el-option v-for="item in specOptions" :key="item.label" :value="item.value" :label="item.label" />
|
||||
</el-select>
|
||||
<el-select
|
||||
v-if="dialogData.rowData!.specType === 'perWeek'"
|
||||
style="width: 12%; margin-left: 20px"
|
||||
v-model="dialogData.rowData!.week"
|
||||
>
|
||||
<el-option v-for="item in weekOptions" :key="item.label" :value="item.value" :label="item.label" />
|
||||
</el-select>
|
||||
<el-input
|
||||
v-if="dialogData.rowData!.specType === 'perMonth' || dialogData.rowData!.specType === 'perNDay'"
|
||||
style="width: 20%; margin-left: 20px"
|
||||
v-model.number="dialogData.rowData!.day"
|
||||
>
|
||||
<template #append>{{ $t('cronjob.day') }}</template>
|
||||
</el-input>
|
||||
<el-input
|
||||
v-if="dialogData.rowData!.specType !== 'perHour' && dialogData.rowData!.specType !== 'perNMinute'"
|
||||
style="width: 20%; margin-left: 20px"
|
||||
v-model.number="dialogData.rowData!.hour"
|
||||
>
|
||||
<template #append>{{ $t('cronjob.hour') }}</template>
|
||||
</el-input>
|
||||
<el-input style="width: 20%; margin-left: 20px" v-model.number="dialogData.rowData!.minute">
|
||||
<template #append>{{ $t('cronjob.minute') }}</template>
|
||||
</el-input>
|
||||
</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-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)"
|
||||
style="width: 100%"
|
||||
v-model="dialogData.rowData!.website"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in websiteOptions"
|
||||
:key="item.label"
|
||||
:value="item.value"
|
||||
:label="item.label"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="dialogData.rowData!.type === 'database'"
|
||||
:label="$t('cronjob.database')"
|
||||
prop="database"
|
||||
>
|
||||
<el-input style="width: 100%" clearable v-model="dialogData.rowData!.database" />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="dialogData.rowData!.type === 'directory'"
|
||||
:label="$t('cronjob.sourceDir')"
|
||||
prop="sourceDir"
|
||||
>
|
||||
<el-input
|
||||
@input="changeName(false, dialogData.rowData!.type, dialogData.rowData!.website)"
|
||||
style="width: 100%"
|
||||
clearable
|
||||
v-model="dialogData.rowData!.sourceDir"
|
||||
>
|
||||
<template #append>
|
||||
<FileList @choose="loadDir" :dir="true"></FileList>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="isBackup()" :label="$t('cronjob.target')" prop="targetDirID">
|
||||
<el-select style="width: 100%" v-model="dialogData.rowData!.targetDirID">
|
||||
<el-option
|
||||
v-for="item in backupOptions"
|
||||
:key="item.label"
|
||||
:value="item.value"
|
||||
:label="item.label"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="isBackup()" :label="$t('cronjob.retainCopies')" prop="retainCopies">
|
||||
<el-input-number :min="1" v-model.number="dialogData.rowData!.retainCopies"></el-input-number>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="dialogData.rowData!.type === 'curl'" :label="$t('cronjob.url') + 'URL'" prop="url">
|
||||
<el-input style="width: 100%" clearable v-model="dialogData.rowData!.url" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item
|
||||
v-if="dialogData.rowData!.type === 'website' || dialogData.rowData!.type === 'directory'"
|
||||
:label="$t('cronjob.exclusionRules')"
|
||||
prop="exclusionRules"
|
||||
>
|
||||
<el-input style="width: 100%" type="textarea" clearable v-model="dialogData.rowData!.exclusionRules" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="cronjobVisiable = false">{{ $t('commons.button.cancel') }}</el-button>
|
||||
<el-button v-show="!dialogData.isView" type="primary" @click="onSubmit(formRef)">
|
||||
{{ $t('commons.button.confirm') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
import { loadBackupName } from '@/views/setting/helper';
|
||||
import { typeOptions, specOptions, weekOptions } from '../options';
|
||||
import FileList from '@/components/file-list/index.vue';
|
||||
import { getBackupList } from '@/api/modules/backup';
|
||||
import i18n from '@/lang';
|
||||
import { ElForm, ElMessage } from 'element-plus';
|
||||
import { Cronjob } from '@/api/interface/cronjob';
|
||||
import { addCronjob, editCronjob } from '@/api/modules/cronjob';
|
||||
|
||||
interface DialogProps {
|
||||
title: string;
|
||||
isView: boolean;
|
||||
rowData?: Cronjob.CronjobInfo;
|
||||
getTableList?: () => Promise<any>;
|
||||
}
|
||||
const title = ref<string>('');
|
||||
const cronjobVisiable = ref(false);
|
||||
const dialogData = ref<DialogProps>({
|
||||
isView: false,
|
||||
title: '',
|
||||
});
|
||||
const acceptParams = (params: DialogProps): void => {
|
||||
dialogData.value = params;
|
||||
title.value = i18n.global.t('commons.button.' + dialogData.value.title);
|
||||
cronjobVisiable.value = true;
|
||||
};
|
||||
|
||||
const websiteOptions = ref([
|
||||
{ label: '所有', value: 'all' },
|
||||
{ label: '网站1', value: 'web1' },
|
||||
{ label: '网站2', value: 'web2' },
|
||||
]);
|
||||
const backupOptions = ref();
|
||||
|
||||
const emit = defineEmits<{ (e: 'search'): void }>();
|
||||
|
||||
const varifySpec = (rule: any, value: any, callback: any) => {
|
||||
switch (dialogData.value.rowData!.specType) {
|
||||
case 'perMonth':
|
||||
case 'perNDay':
|
||||
if (
|
||||
!(
|
||||
Number.isInteger(dialogData.value.rowData!.day) &&
|
||||
Number.isInteger(dialogData.value.rowData!.hour) &&
|
||||
Number.isInteger(dialogData.value.rowData!.minute)
|
||||
)
|
||||
) {
|
||||
callback(new Error(i18n.global.t('cronjob.cronSpecRule')));
|
||||
}
|
||||
break;
|
||||
case 'perWeek':
|
||||
if (
|
||||
!(
|
||||
Number.isInteger(dialogData.value.rowData!.week) &&
|
||||
Number.isInteger(dialogData.value.rowData!.hour) &&
|
||||
Number.isInteger(dialogData.value.rowData!.minute)
|
||||
)
|
||||
) {
|
||||
callback(new Error(i18n.global.t('cronjob.cronSpecRule')));
|
||||
}
|
||||
break;
|
||||
case 'perNHour':
|
||||
if (
|
||||
!(
|
||||
Number.isInteger(dialogData.value.rowData!.hour) &&
|
||||
Number.isInteger(dialogData.value.rowData!.minute)
|
||||
)
|
||||
) {
|
||||
callback(new Error(i18n.global.t('cronjob.cronSpecRule')));
|
||||
}
|
||||
break;
|
||||
case 'perHour':
|
||||
case 'perNMinute':
|
||||
if (!Number.isInteger(dialogData.value.rowData!.minute)) {
|
||||
callback(new Error(i18n.global.t('cronjob.cronSpecRule')));
|
||||
}
|
||||
break;
|
||||
}
|
||||
callback();
|
||||
};
|
||||
const rules = reactive({
|
||||
name: [Rules.requiredInput],
|
||||
type: [Rules.requiredSelect],
|
||||
specType: [Rules.requiredSelect],
|
||||
spec: [
|
||||
{ validator: varifySpec, trigger: 'blur', required: true },
|
||||
{ validator: varifySpec, trigger: 'change', required: true },
|
||||
],
|
||||
week: [Rules.requiredSelect, Rules.number],
|
||||
day: [Rules.number, { max: 31, min: 1 }],
|
||||
hour: [Rules.number, { max: 23, min: 0 }],
|
||||
minute: [Rules.number, { max: 60, min: 1 }],
|
||||
|
||||
script: [Rules.requiredInput],
|
||||
website: [Rules.requiredSelect],
|
||||
database: [Rules.requiredSelect],
|
||||
url: [Rules.requiredInput],
|
||||
sourceDir: [Rules.requiredInput],
|
||||
targetDirID: [Rules.requiredSelect, Rules.number],
|
||||
retainCopies: [Rules.number],
|
||||
});
|
||||
|
||||
type FormInstance = InstanceType<typeof ElForm>;
|
||||
const formRef = ref<FormInstance>();
|
||||
|
||||
const loadDir = async (path: string) => {
|
||||
dialogData.value.rowData!.sourceDir = path;
|
||||
};
|
||||
|
||||
const loadBackups = async () => {
|
||||
const res = await getBackupList();
|
||||
backupOptions.value = [];
|
||||
for (const item of res.data) {
|
||||
backupOptions.value.push({ label: loadBackupName(item.type), value: item.id });
|
||||
}
|
||||
};
|
||||
function isBackup() {
|
||||
return (
|
||||
dialogData.value.rowData!.type === 'website' ||
|
||||
dialogData.value.rowData!.type === 'database' ||
|
||||
dialogData.value.rowData!.type === 'directory'
|
||||
);
|
||||
}
|
||||
function hasScript() {
|
||||
return dialogData.value.rowData!.type === 'shell' || dialogData.value.rowData!.type === 'sync';
|
||||
}
|
||||
function changeName(isChangeType: boolean, type: string, targetName: 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;
|
||||
}
|
||||
}
|
||||
function restForm() {
|
||||
if (formRef.value) {
|
||||
formRef.value.resetFields();
|
||||
}
|
||||
}
|
||||
const onSubmit = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return;
|
||||
formEl.validate(async (valid) => {
|
||||
if (!valid) return;
|
||||
if (!dialogData.value.rowData) return;
|
||||
if (dialogData.value.title === 'create') {
|
||||
await addCronjob(dialogData.value.rowData);
|
||||
}
|
||||
if (dialogData.value.title === 'edit') {
|
||||
await editCronjob(dialogData.value.rowData);
|
||||
}
|
||||
|
||||
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
||||
restForm();
|
||||
emit('search');
|
||||
cronjobVisiable.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
loadBackups();
|
||||
});
|
||||
defineExpose({
|
||||
acceptParams,
|
||||
});
|
||||
</script>
|
17
frontend/src/views/setting/helper.ts
Normal file
17
frontend/src/views/setting/helper.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import i18n from '@/lang';
|
||||
|
||||
export const loadBackupName = (type: string) => {
|
||||
switch (type) {
|
||||
case 'OSS':
|
||||
return i18n.global.t('setting.OSS');
|
||||
break;
|
||||
case 'S3':
|
||||
return i18n.global.t('setting.S3');
|
||||
break;
|
||||
case 'LOCAL':
|
||||
return i18n.global.t('setting.serverDisk');
|
||||
break;
|
||||
default:
|
||||
return type;
|
||||
}
|
||||
};
|
@ -167,6 +167,7 @@
|
||||
<script setup lang="ts">
|
||||
import { dateFromat } from '@/utils/util';
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { loadBackupName } from '@/views/setting/helper';
|
||||
import { getBackupList, addBackup, editBackup, listBucket, deleteBackup } from '@/api/modules/backup';
|
||||
import { Backup } from '@/api/interface/backup';
|
||||
import i18n from '@/lang';
|
||||
@ -315,22 +316,6 @@ const loadIconName = (type: string) => {
|
||||
break;
|
||||
}
|
||||
};
|
||||
const loadBackupName = (type: string) => {
|
||||
switch (type) {
|
||||
case 'OSS':
|
||||
return i18n.global.t('setting.OSS');
|
||||
break;
|
||||
case 'S3':
|
||||
return i18n.global.t('setting.S3');
|
||||
break;
|
||||
case 'LOCAL':
|
||||
return i18n.global.t('setting.serverDisk');
|
||||
break;
|
||||
default:
|
||||
return type;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
search();
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user