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

fix: 解决计划任务部分 bug

This commit is contained in:
ssongliu 2023-03-10 00:27:41 +08:00 committed by ssongliu
parent 6da43d7eb0
commit 015baec864
18 changed files with 365 additions and 333 deletions

1
.gitignore vendored
View File

@ -16,7 +16,6 @@ build/1panel
# Dependency directories (remove the comment below to include it) # Dependency directories (remove the comment below to include it)
# vendor/ # vendor/
/build
/pkg/ /pkg/
backend/__debug_bin backend/__debug_bin
cmd/server/__debug_bin cmd/server/__debug_bin

View File

@ -86,7 +86,7 @@ func (u *CronjobRepo) Page(page, size int, opts ...DBOption) (int64, []model.Cro
func (u *CronjobRepo) RecordFirst(id uint) (model.JobRecords, error) { func (u *CronjobRepo) RecordFirst(id uint) (model.JobRecords, error) {
var record model.JobRecords var record model.JobRecords
err := global.DB.Order("created_at desc").First(&record).Error err := global.DB.Where("cronjob_id = ?", id).Order("created_at desc").First(&record).Error
return record, err return record, err
} }

View File

@ -5,7 +5,6 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"os" "os"
"os/exec"
"strings" "strings"
"github.com/1Panel-dev/1Panel/backend/app/dto" "github.com/1Panel-dev/1Panel/backend/app/dto"
@ -13,6 +12,7 @@ import (
"github.com/1Panel-dev/1Panel/backend/constant" "github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global" "github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/cloud_storage" "github.com/1Panel-dev/1Panel/backend/utils/cloud_storage"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
"github.com/jinzhu/copier" "github.com/jinzhu/copier"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -232,6 +232,9 @@ func (u *BackupService) Update(req dto.BackupOperate) error {
if backup.Type == "LOCAL" { if backup.Type == "LOCAL" {
if dir, ok := varMap["dir"]; ok { if dir, ok := varMap["dir"]; ok {
if dirStr, isStr := dir.(string); isStr { if dirStr, isStr := dir.(string); isStr {
if strings.HasSuffix(dirStr, "/") {
dirStr = dirStr[:strings.LastIndex(dirStr, "/")]
}
if err := updateBackupDir(dirStr); err != nil { if err := updateBackupDir(dirStr); err != nil {
upMap["vars"] = backup.Vars upMap["vars"] = backup.Vars
_ = backupRepo.Update(req.ID, upMap) _ = backupRepo.Update(req.ID, upMap)
@ -316,8 +319,7 @@ func updateBackupDir(dir string) error {
if strings.HasSuffix(oldDir, "/") { if strings.HasSuffix(oldDir, "/") {
oldDir = oldDir[:strings.LastIndex(oldDir, "/")] oldDir = oldDir[:strings.LastIndex(oldDir, "/")]
} }
cmd := exec.Command("cp", "-r", oldDir+"/*", dir) stdout, err := cmd.Execf("cp -r %s/* %s", oldDir, dir)
stdout, err := cmd.CombinedOutput()
if err != nil { if err != nil {
return errors.New(string(stdout)) return errors.New(string(stdout))
} }

View File

@ -151,7 +151,7 @@ func (u *CronjobService) Download(down dto.CronjobDownload) (string, error) {
} }
return fmt.Sprintf("%v/database/mysql/%s/%s/db_%s_%s.sql.gz", varMap["dir"], mysqlInfo.Name, cronjob.DBName, cronjob.DBName, record.StartTime.Format("20060102150405")), nil return fmt.Sprintf("%v/database/mysql/%s/%s/db_%s_%s.sql.gz", varMap["dir"], mysqlInfo.Name, cronjob.DBName, cronjob.DBName, record.StartTime.Format("20060102150405")), nil
case "directory": case "directory":
return fmt.Sprintf("%v/%s/%s/%s.tar.gz", varMap["dir"], cronjob.Type, cronjob.Name, record.StartTime.Format("20060102150405")), nil return fmt.Sprintf("%v/%s/%s/directory%s_%s.tar.gz", varMap["dir"], cronjob.Type, cronjob.Name, strings.ReplaceAll(cronjob.SourceDir, "/", "_"), record.StartTime.Format("20060102150405")), nil
default: default:
return "", fmt.Errorf("not support type %s", cronjob.Type) return "", fmt.Errorf("not support type %s", cronjob.Type)
} }
@ -220,6 +220,11 @@ func (u *CronjobService) Update(id uint, req dto.CronjobUpdate) error {
if err := copier.Copy(&cronjob, &req); err != nil { if err := copier.Copy(&cronjob, &req); err != nil {
return errors.WithMessage(constant.ErrStructTransform, err.Error()) return errors.WithMessage(constant.ErrStructTransform, err.Error())
} }
cronModel, err := cronjobRepo.Get(commonRepo.WithByID(id))
if err != nil {
return constant.ErrRecordNotFound
}
cronjob.EntryID = cronModel.EntryID
cronjob.Spec = loadSpec(cronjob) cronjob.Spec = loadSpec(cronjob)
if err := u.StartJob(&cronjob); err != nil { if err := u.StartJob(&cronjob); err != nil {
return err return err

View File

@ -114,7 +114,7 @@ func (u *CronjobService) HandleBackup(cronjob *model.Cronjob, startTime time.Tim
fileName = fmt.Sprintf("directory%s_%s.tar.gz", strings.ReplaceAll(cronjob.SourceDir, "/", "_"), startTime.Format("20060102150405")) fileName = fmt.Sprintf("directory%s_%s.tar.gz", strings.ReplaceAll(cronjob.SourceDir, "/", "_"), startTime.Format("20060102150405"))
backupDir = fmt.Sprintf("%s/%s/%s", localDir, cronjob.Type, cronjob.Name) backupDir = fmt.Sprintf("%s/%s/%s", localDir, cronjob.Type, cronjob.Name)
global.LOG.Infof("handle tar %s to %s", backupDir, fileName) global.LOG.Infof("handle tar %s to %s", backupDir, fileName)
if err := handleTar(cronjob.SourceDir, localDir+"/"+backupDir, fileName, cronjob.ExclusionRules); err != nil { if err := handleTar(cronjob.SourceDir, backupDir, fileName, cronjob.ExclusionRules); err != nil {
return "", err return "", err
} }
} }
@ -197,10 +197,20 @@ func (u *CronjobService) HandleRmExpired(backType, backupDir string, cronjob *mo
if len(files) == 0 { if len(files) == 0 {
return return
} }
if cronjob.Type == "database" {
prefix := ""
switch cronjob.Type {
case "database":
prefix = "db_"
case "website":
prefix = "website_"
case "directory":
prefix = "directory_"
}
dbCopies := uint64(0) dbCopies := uint64(0)
for i := len(files) - 1; i >= 0; i-- { for i := len(files) - 1; i >= 0; i-- {
if strings.HasPrefix(files[i].Name(), "db_") { if strings.HasPrefix(files[i].Name(), prefix) {
dbCopies++ dbCopies++
if dbCopies > cronjob.RetainCopies { if dbCopies > cronjob.RetainCopies {
_ = os.Remove(backupDir + "/" + files[i].Name()) _ = os.Remove(backupDir + "/" + files[i].Name())
@ -208,11 +218,6 @@ func (u *CronjobService) HandleRmExpired(backType, backupDir string, cronjob *mo
} }
} }
} }
} else {
for i := 0; i < len(files)-int(cronjob.RetainCopies); i++ {
_ = os.Remove(backupDir + "/" + files[i].Name())
}
}
records, _ := cronjobRepo.ListRecord(cronjobRepo.WithByJobID(int(cronjob.ID))) records, _ := cronjobRepo.ListRecord(cronjobRepo.WithByJobID(int(cronjob.ID)))
if len(records) > int(cronjob.RetainCopies) { if len(records) > int(cronjob.RetainCopies) {
for i := int(cronjob.RetainCopies); i < len(records); i++ { for i := int(cronjob.RetainCopies); i < len(records); i++ {

View File

@ -84,7 +84,7 @@ const checkImageName = (rule: any, value: any, callback: any) => {
if (value === '' || typeof value === 'undefined' || value == null) { if (value === '' || typeof value === 'undefined' || value == null) {
callback(new Error(i18n.global.t('commons.rule.imageName'))); callback(new Error(i18n.global.t('commons.rule.imageName')));
} else { } else {
const reg = /^[a-zA-Z0-9\u4e00-\u9fa5]{1}[a-z:A-Z0-9_.\u4e00-\u9fa5-]{0,30}$/; const reg = /^[a-zA-Z0-9]{1}[a-z:A-Z0-9_/.-]{0,150}$/;
if (!reg.test(value) && value !== '') { if (!reg.test(value) && value !== '') {
callback(new Error(i18n.global.t('commons.rule.imageName'))); callback(new Error(i18n.global.t('commons.rule.imageName')));
} else { } else {

View File

@ -129,7 +129,7 @@ export default {
userName: 'Support English, Chinese, numbers and _ length 3-30', userName: 'Support English, Chinese, numbers and _ length 3-30',
simpleName: 'Support English, numbers and _ length 1-30', simpleName: 'Support English, numbers and _ length 1-30',
dbName: 'Support English, Chinese, numbers, .-, and _ length 1-16', dbName: 'Support English, Chinese, numbers, .-, and _ length 1-16',
imageName: 'Support English, Chinese, numbers, :.-_, length 1-30', imageName: 'Support English, numbers, :/.-_, length 1-150',
volumeName: 'Support English, numbers, .-_, length 1-30', volumeName: 'Support English, numbers, .-_, length 1-30',
complexityPassword: complexityPassword:
'Enter a password that is longer than eight characters and contains at least two letters, digits, and special characters', 'Enter a password that is longer than eight characters and contains at least two letters, digits, and special characters',

View File

@ -133,7 +133,7 @@ export default {
userName: '支持英文中文数字和_,长度3-30', userName: '支持英文中文数字和_,长度3-30',
simpleName: '支持英文数字_,长度1-30', simpleName: '支持英文数字_,长度1-30',
dbName: '支持英文中文数字.-_,长度1-16', dbName: '支持英文中文数字.-_,长度1-16',
imageName: '支持英文中文数字:.-_,长度1-30', imageName: '支持英文数字:/.-_,长度1-150',
volumeName: '支持英文数字.-和_,长度1-30', volumeName: '支持英文数字.-和_,长度1-30',
complexityPassword: '请输入长度大于 8 位且包含字母数字特殊字符至少两项的密码组合', complexityPassword: '请输入长度大于 8 位且包含字母数字特殊字符至少两项的密码组合',
commonPassword: '请输入 6 位以上长度密码', commonPassword: '请输入 6 位以上长度密码',

View File

@ -279,3 +279,10 @@
.middle-center { .middle-center {
vertical-align: middle; vertical-align: middle;
} }
.status-count {
font-size: 24px;
}
.status-label {
font-size: 14px;
color: #646a73;
}

View File

@ -25,7 +25,7 @@
<el-option v-for="item in repos" :key="item.id" :value="item.id" :label="item.name" /> <el-option v-for="item in repos" :key="item.id" :value="item.id" :label="item.name" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item :label="$t('container.imageName')" :rules="Rules.requiredInput" prop="imageName"> <el-form-item :label="$t('container.imageName')" :rules="Rules.imageName" prop="imageName">
<el-input v-model.trim="form.imageName"> <el-input v-model.trim="form.imageName">
<template v-if="form.fromRepo" #prepend>{{ loadDetailInfo(form.repoID) }}/</template> <template v-if="form.fromRepo" #prepend>{{ loadDetailInfo(form.repoID) }}/</template>
</el-input> </el-input>

View File

@ -27,7 +27,7 @@
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item :label="$t('container.image')" :rules="Rules.requiredInput" prop="name"> <el-form-item :label="$t('container.image')" :rules="Rules.imageName" prop="name">
<el-input v-model.trim="form.name"> <el-input v-model.trim="form.name">
<template #prepend>{{ loadDetailInfo(form.repoID) }}/</template> <template #prepend>{{ loadDetailInfo(form.repoID) }}/</template>
</el-input> </el-input>

View File

@ -45,7 +45,14 @@
<el-table-column type="selection" fix /> <el-table-column type="selection" fix />
<el-table-column :label="$t('cronjob.taskName')" :min-width="120" prop="name"> <el-table-column :label="$t('cronjob.taskName')" :min-width="120" prop="name">
<template #default="{ row }"> <template #default="{ row }">
<el-link @click="loadDetail(row)" type="primary">{{ row.name }}</el-link> <el-tooltip effect="dark" :content="row.name" v-if="row.name.length > 12" placement="top">
<el-link @click="loadDetail(row)" type="primary">
{{ row.name.substring(0, 15) }}...
</el-link>
</el-tooltip>
<el-link v-else @click="loadDetail(row)" type="primary">
{{ row.name }}
</el-link>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column :label="$t('commons.table.status')" :min-width="80" prop="status"> <el-table-column :label="$t('commons.table.status')" :min-width="80" prop="status">

View File

@ -7,13 +7,19 @@
<el-row type="flex" justify="center"> <el-row type="flex" justify="center">
<el-col :span="22"> <el-col :span="22">
<el-form-item :label="$t('cronjob.taskType')" prop="type"> <el-form-item :label="$t('cronjob.taskType')" prop="type">
<el-select style="width: 100%" @change="changeType" v-model="dialogData.rowData!.type"> <el-select
v-if="dialogData.title === 'create'"
style="width: 100%"
@change="changeType"
v-model="dialogData.rowData!.type"
>
<el-option value="shell" :label="$t('cronjob.shell')" /> <el-option value="shell" :label="$t('cronjob.shell')" />
<el-option value="website" :label="$t('cronjob.website')" /> <el-option value="website" :label="$t('cronjob.website')" />
<el-option value="database" :label="$t('cronjob.database')" /> <el-option value="database" :label="$t('cronjob.database')" />
<el-option value="directory" :label="$t('cronjob.directory')" /> <el-option value="directory" :label="$t('cronjob.directory')" />
<el-option value="curl" :label="$t('cronjob.curl')" /> <el-option value="curl" :label="$t('cronjob.curl')" />
</el-select> </el-select>
<el-tag v-else>{{ dialogData.rowData!.type }}</el-tag>
</el-form-item> </el-form-item>
<el-form-item :label="$t('cronjob.taskName')" prop="name"> <el-form-item :label="$t('cronjob.taskName')" prop="name">
@ -120,6 +126,8 @@
<el-input-number <el-input-number
:min="1" :min="1"
:max="30" :max="30"
step-strictly
:step="1"
v-model.number="dialogData.rowData!.retainCopies" v-model.number="dialogData.rowData!.retainCopies"
></el-input-number> ></el-input-number>
</el-form-item> </el-form-item>
@ -184,7 +192,9 @@ const dialogData = ref<DialogProps>({
}); });
const acceptParams = (params: DialogProps): void => { const acceptParams = (params: DialogProps): void => {
dialogData.value = params; dialogData.value = params;
if (dialogData.value.title === 'create') {
changeType(); changeType();
}
title.value = i18n.global.t('commons.button.' + dialogData.value.title); title.value = i18n.global.t('commons.button.' + dialogData.value.title);
drawerVisiable.value = true; drawerVisiable.value = true;
checkMysqlInstalled(); checkMysqlInstalled();

View File

@ -126,59 +126,13 @@
</el-card> </el-card>
</el-col> </el-col>
<el-col :span="16"> <el-col :span="16">
<el-card style="height: 362px"> <el-form label-position="top">
<el-form> <el-row type="flex" justify="center">
<el-row v-if="hasScript()"> <el-form-item class="descriptionWide" v-if="isBackup()">
<span>{{ $t('cronjob.shellContent') }}</span> <template #label>
<codemirror <span class="status-label">{{ $t('cronjob.target') }}</span>
ref="mymirror"
:autofocus="true"
placeholder="None data"
:indent-with-tab="true"
:tabSize="4"
style="height: 100px; width: 100%; margin-top: 5px"
:lineWrapping="true"
:matchBrackets="true"
theme="cobalt"
:styleActiveLine="true"
:extensions="extensions"
v-model="dialogData.rowData!.script"
:disabled="true"
/>
</el-row>
<el-row>
<el-col :span="8" v-if="dialogData.rowData!.type === 'website'">
<el-form-item :label="$t('cronjob.website')">
{{ dialogData.rowData!.website }}
</el-form-item>
</el-col>
<el-col :span="8" v-if="dialogData.rowData!.type === 'database'">
<el-form-item :label="$t('cronjob.database')">
{{ dialogData.rowData!.dbName }}
</el-form-item>
</el-col>
<el-col :span="8" v-if="dialogData.rowData!.type === 'directory'">
<el-form-item :label="$t('cronjob.directory')">
<span v-if="dialogData.rowData!.sourceDir.length <= 20">
{{ dialogData.rowData!.sourceDir }}
</span>
<div v-else>
<el-popover
placement="top-start"
trigger="hover"
width="250"
:content="dialogData.rowData!.sourceDir"
>
<template #reference>
{{ dialogData.rowData!.sourceDir.substring(0, 20) }}...
</template> </template>
</el-popover> <span class="status-count">{{ dialogData.rowData!.targetDir }}</span>
</div>
</el-form-item>
</el-col>
<el-col :span="8" v-if="isBackup()">
<el-form-item :label="$t('cronjob.target')">
{{ dialogData.rowData!.targetDir }}
<el-button <el-button
v-if="currentRecord?.status! !== 'Failed'" v-if="currentRecord?.status! !== 'Failed'"
type="primary" type="primary"
@ -190,52 +144,83 @@
{{ $t('file.download') }} {{ $t('file.download') }}
</el-button> </el-button>
</el-form-item> </el-form-item>
</el-col> <el-form-item class="description" v-if="dialogData.rowData!.type === 'website'">
<el-col :span="8" v-if="isBackup()"> <template #label>
<el-form-item :label="$t('cronjob.retainCopies')"> <span class="status-label">{{ $t('cronjob.website') }}</span>
{{ dialogData.rowData!.retainCopies }} </template>
<span class="status-count">{{ dialogData.rowData!.website }}</span>
</el-form-item> </el-form-item>
</el-col> <el-form-item class="description" v-if="dialogData.rowData!.type === 'database'">
<el-col :span="8" v-if="dialogData.rowData!.type === 'curl'"> <template #label>
<el-form-item :label="$t('cronjob.url')"> <span class="status-label">{{ $t('cronjob.database') }}</span>
{{ dialogData.rowData!.url }} </template>
<span class="status-count">{{ dialogData.rowData!.dbName }}</span>
</el-form-item> </el-form-item>
</el-col> <el-form-item class="description" v-if="dialogData.rowData!.type === 'directory'">
<el-col <template #label>
:span="8" <span class="status-label">{{ $t('cronjob.directory') }}</span>
</template>
<span v-if="dialogData.rowData!.sourceDir.length <= 12" class="status-count">
{{ dialogData.rowData!.sourceDir }}
</span>
<div v-else>
<el-popover
placement="top-start"
trigger="hover"
width="250"
:content="dialogData.rowData!.sourceDir"
>
<template #reference>
<span class="status-count">
{{ dialogData.rowData!.sourceDir.substring(0, 12) }}...
</span>
</template>
</el-popover>
</div>
</el-form-item>
<el-form-item class="description" v-if="isBackup()">
<template #label>
<span class="status-label">{{ $t('cronjob.retainCopies') }}</span>
</template>
<span class="status-count">{{ dialogData.rowData!.retainCopies }}</span>
</el-form-item>
</el-row>
<el-form-item
class="description"
v-if="dialogData.rowData!.type === 'website' || dialogData.rowData!.type === 'directory'" v-if="dialogData.rowData!.type === 'website' || dialogData.rowData!.type === 'directory'"
> >
<el-form-item :label="$t('cronjob.exclusionRules')"> <template #label>
<span class="status-label">{{ $t('cronjob.exclusionRules') }}</span>
</template>
<div v-if="dialogData.rowData!.exclusionRules"> <div v-if="dialogData.rowData!.exclusionRules">
<div <div v-for="item in dialogData.rowData!.exclusionRules.split(';')" :key="item">
v-for="item in dialogData.rowData!.exclusionRules.split(';')"
:key="item"
>
<el-tag>{{ item }}</el-tag> <el-tag>{{ item }}</el-tag>
</div> </div>
</div> </div>
<span v-else>-</span> <span class="status-count" v-else>-</span>
</el-form-item> </el-form-item>
</el-col> <el-row type="flex" justify="center">
</el-row> <el-form-item class="descriptionWide">
<el-row> <template #label>
<el-col :span="8"> <span class="status-label">{{ $t('commons.search.timeStart') }}</span>
<el-form-item :label="$t('commons.search.timeStart')"> </template>
{{ dateFormat(0, 0, currentRecord?.startTime) }} <span class="status-count">{{ dateFormat(0, 0, currentRecord?.startTime) }}</span>
</el-form-item> </el-form-item>
</el-col> <el-form-item class="description">
<el-col :span="8"> <template #label>
<el-form-item :label="$t('commons.table.interval')"> <span class="status-label">{{ $t('commons.table.interval') }}</span>
<span v-if="currentRecord?.interval! <= 1000"> </template>
<span class="status-count" v-if="currentRecord?.interval! <= 1000">
{{ currentRecord?.interval }} ms {{ currentRecord?.interval }} ms
</span> </span>
<span v-if="currentRecord?.interval! > 1000"> <span class="status-count" v-if="currentRecord?.interval! > 1000">
{{ currentRecord?.interval! / 1000 }} s {{ currentRecord?.interval! / 1000 }} s
</span> </span>
</el-form-item> </el-form-item>
</el-col> <el-form-item class="description">
<el-col :span="8"> <template #label>
<el-form-item :label="$t('commons.table.status')"> <span class="status-label">{{ $t('commons.table.status') }}</span>
</template>
<el-tooltip <el-tooltip
v-if="currentRecord?.status === 'Failed'" v-if="currentRecord?.status === 'Failed'"
class="box-item" class="box-item"
@ -251,7 +236,6 @@
{{ $t('commons.table.statusWaiting') }} {{ $t('commons.table.statusWaiting') }}
</el-tag> </el-tag>
</el-form-item> </el-form-item>
</el-col>
</el-row> </el-row>
<el-row v-if="currentRecord?.records"> <el-row v-if="currentRecord?.records">
<span>{{ $t('commons.table.records') }}</span> <span>{{ $t('commons.table.records') }}</span>
@ -261,7 +245,7 @@
:placeholder="$t('cronjob.noLogs')" :placeholder="$t('cronjob.noLogs')"
:indent-with-tab="true" :indent-with-tab="true"
:tabSize="4" :tabSize="4"
style="height: 130px; width: 100%; margin-top: 5px" style="height: calc(100vh - 484px); width: 100%; margin-top: 5px"
:lineWrapping="true" :lineWrapping="true"
:matchBrackets="true" :matchBrackets="true"
theme="cobalt" theme="cobalt"
@ -272,7 +256,6 @@
/> />
</el-row> </el-row>
</el-form> </el-form>
</el-card>
</el-col> </el-col>
</el-row> </el-row>
<div class="app-warn" v-if="!hasRecords"> <div class="app-warn" v-if="!hasRecords">
@ -416,6 +399,8 @@ const onHandle = async (row: Cronjob.CronjobInfo) => {
.then(() => { .then(() => {
loading.value = false; loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
searchInfo.page = 1;
records.value = [];
search(); search();
}) })
.catch(() => { .catch(() => {
@ -451,13 +436,14 @@ const search = async () => {
endTime: searchInfo.endTime, endTime: searchInfo.endTime,
status: searchInfo.status, status: searchInfo.status,
}; };
records.value = [];
const res = await searchRecords(params); const res = await searchRecords(params);
if (!res.data.items) { if (searchInfo.page === 1 && !res.data.items) {
hasRecords.value = false; hasRecords.value = false;
return; return;
} }
records.value = res.data.items; for (const item of res.data.items) {
records.value.push(item);
}
hasRecords.value = true; hasRecords.value = true;
currentRecord.value = records.value[0]; currentRecord.value = records.value[0];
currentRecordIndex.value = 0; currentRecordIndex.value = 0;
@ -490,7 +476,7 @@ const nextPage = async () => {
if (searchInfo.pageSize >= searchInfo.recordTotal) { if (searchInfo.pageSize >= searchInfo.recordTotal) {
return; return;
} }
searchInfo.pageSize = searchInfo.pageSize + 5; searchInfo.page = searchInfo.page + 1;
search(); search();
}; };
const forDetail = async (row: Cronjob.Record, index: number) => { const forDetail = async (row: Cronjob.Record, index: number) => {
@ -515,9 +501,6 @@ function isBackup() {
dialogData.value.rowData!.type === 'directory' dialogData.value.rowData!.type === 'directory'
); );
} }
function hasScript() {
return dialogData.value.rowData!.type === 'shell' || dialogData.value.rowData!.type === 'sync';
}
function loadWeek(i: number) { function loadWeek(i: number) {
for (const week of weekOptions) { for (const week of weekOptions) {
if (week.value === i) { if (week.value === i) {
@ -534,7 +517,7 @@ defineExpose({
<style lang="scss" scoped> <style lang="scss" scoped>
.infinite-list { .infinite-list {
height: 310px; height: calc(100vh - 435px);
padding: 0; padding: 0;
margin: 0; margin: 0;
} }
@ -593,4 +576,10 @@ defineExpose({
height: 300px; height: 300px;
} }
} }
.descriptionWide {
width: 40%;
}
.description {
width: 30%;
}
</style> </style>

View File

@ -213,13 +213,6 @@ defineExpose({
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.status-count {
font-size: 24px;
}
.status-label {
font-size: 14px;
color: #646a73;
}
.devider { .devider {
display: block; display: block;
height: 1px; height: 1px;

View File

@ -169,13 +169,6 @@ defineExpose({
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.status-count {
font-size: 24px;
}
.status-label {
font-size: 14px;
color: #646a73;
}
.devider { .devider {
display: block; display: block;
height: 1px; height: 1px;

View File

@ -1,9 +1,16 @@
<template> <template>
<div v-loading="loading">
<el-drawer v-model="drawerVisiable" :destroy-on-close="true" :close-on-click-modal="false" size="50%"> <el-drawer v-model="drawerVisiable" :destroy-on-close="true" :close-on-click-modal="false" size="50%">
<template #header> <template #header>
<DrawerHeader :header="title + $t('setting.backupAccount')" :back="handleClose" /> <DrawerHeader :header="title + $t('setting.backupAccount')" :back="handleClose" />
</template> </template>
<el-form ref="formRef" v-loading="loading" label-position="top" :model="dialogData.rowData" label-width="120px"> <el-form
ref="formRef"
v-loading="loading"
label-position="top"
:model="dialogData.rowData"
label-width="120px"
>
<el-row type="flex" justify="center"> <el-row type="flex" justify="center">
<el-col :span="22"> <el-col :span="22">
<el-form-item :label="$t('commons.table.type')" prop="type" :rules="Rules.requiredSelect"> <el-form-item :label="$t('commons.table.type')" prop="type" :rules="Rules.requiredSelect">
@ -73,7 +80,11 @@
label="Bucket" label="Bucket"
prop="bucket" prop="bucket"
> >
<el-select style="width: 80%" @change="errBuckets = false" v-model="dialogData.rowData!.bucket"> <el-select
style="width: 80%"
@change="errBuckets = false"
v-model="dialogData.rowData!.bucket"
>
<el-option v-for="item in buckets" :key="item" :value="item" /> <el-option v-for="item in buckets" :key="item" :value="item" />
</el-select> </el-select>
<el-button style="width: 20%" plain @click="getBuckets(formRef)"> <el-button style="width: 20%" plain @click="getBuckets(formRef)">
@ -92,10 +103,18 @@
v-model.number="dialogData.rowData!.varsJson['port']" v-model.number="dialogData.rowData!.varsJson['port']"
/> />
</el-form-item> </el-form-item>
<el-form-item :label="$t('setting.username')" prop="accessKey" :rules="[Rules.requiredInput]"> <el-form-item
:label="$t('setting.username')"
prop="accessKey"
:rules="[Rules.requiredInput]"
>
<el-input v-model="dialogData.rowData!.accessKey" /> <el-input v-model="dialogData.rowData!.accessKey" />
</el-form-item> </el-form-item>
<el-form-item :label="$t('setting.password')" prop="credential" :rules="[Rules.requiredInput]"> <el-form-item
:label="$t('setting.password')"
prop="credential"
:rules="[Rules.requiredInput]"
>
<el-input <el-input
type="password" type="password"
clearable clearable
@ -121,6 +140,7 @@
</span> </span>
</template> </template>
</el-drawer> </el-drawer>
</div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@ -213,7 +233,7 @@ const getBuckets = async (formEl: FormInstance | undefined) => {
}; };
const onSubmit = async (formEl: FormInstance | undefined) => { const onSubmit = async (formEl: FormInstance | undefined) => {
if (!dialogData.value.rowData.bucket) { if (hasBucket(dialogData.value.rowData.type) && !dialogData.value.rowData.bucket) {
errBuckets.value = true; errBuckets.value = true;
return; return;
} }
@ -228,16 +248,29 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
dialogData.value.rowData!.varsJson['endpointItem'] = undefined; dialogData.value.rowData!.varsJson['endpointItem'] = undefined;
} }
dialogData.value.rowData.vars = JSON.stringify(dialogData.value.rowData!.varsJson); dialogData.value.rowData.vars = JSON.stringify(dialogData.value.rowData!.varsJson);
loading.value = true;
if (dialogData.value.title === 'create') { if (dialogData.value.title === 'create') {
await addBackup(dialogData.value.rowData); await addBackup(dialogData.value.rowData)
} .then(() => {
if (dialogData.value.title === 'edit') { loading.value = false;
await editBackup(dialogData.value.rowData);
}
MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
emit('search'); emit('search');
drawerVisiable.value = false; drawerVisiable.value = false;
})
.catch(() => {
loading.value = false;
});
}
await editBackup(dialogData.value.rowData)
.then(() => {
loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
emit('search');
drawerVisiable.value = false;
})
.catch(() => {
loading.value = false;
});
}); });
}; };

View File

@ -85,14 +85,3 @@ onMounted(() => {
get(); get();
}); });
</script> </script>
<style lang="scss" scoped>
.status-count {
font-size: 24px;
}
.status-label {
font-size: 14px;
color: #646a73;
}
</style>