mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-12-09 03:45:51 +08:00
feat: Support the selection of scripts from the script library for cr… (#8552)
This commit is contained in:
parent
91843382c3
commit
64a14a6b2a
31 changed files with 407 additions and 191 deletions
|
|
@ -53,6 +53,16 @@ func (b *BaseApi) LoadCronjobInfo(c *gin.Context) {
|
|||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
// @Tags Cronjob
|
||||
// @Summary Load script options
|
||||
// @Success 200 {array} dto.ScriptOptions
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /cronjobs/script/options [get]
|
||||
func (b *BaseApi) LoadScriptOptions(c *gin.Context) {
|
||||
helper.SuccessWithData(c, cronjobService.LoadScriptOptions())
|
||||
}
|
||||
|
||||
// @Tags Cronjob
|
||||
// @Summary Load cronjob spec time
|
||||
// @Accept json
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ type CronjobOperate struct {
|
|||
ContainerName string `json:"containerName"`
|
||||
User string `json:"user"`
|
||||
|
||||
ScriptID uint `json:"scriptID"`
|
||||
AppID string `json:"appID"`
|
||||
Website string `json:"website"`
|
||||
ExclusionRules string `json:"exclusionRules"`
|
||||
|
|
@ -84,6 +85,7 @@ type CronjobInfo struct {
|
|||
ContainerName string `json:"containerName"`
|
||||
User string `json:"user"`
|
||||
|
||||
ScriptID uint `json:"scriptID"`
|
||||
AppID string `json:"appID"`
|
||||
Website string `json:"website"`
|
||||
ExclusionRules string `json:"exclusionRules"`
|
||||
|
|
@ -109,6 +111,11 @@ type CronjobInfo struct {
|
|||
AlertCount uint `json:"alertCount"`
|
||||
}
|
||||
|
||||
type ScriptOptions struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type SearchRecord struct {
|
||||
PageInfo
|
||||
CronjobID int `json:"cronjobID"`
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ type Cronjob struct {
|
|||
Script string `json:"script"`
|
||||
User string `json:"user"`
|
||||
|
||||
ScriptID uint `json:"scriptID"`
|
||||
Website string `json:"website"`
|
||||
AppID string `json:"appID"`
|
||||
DBType string `json:"dbType"`
|
||||
|
|
@ -55,3 +56,9 @@ type JobRecords struct {
|
|||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type ScriptLibrary struct {
|
||||
BaseModel
|
||||
Name string `json:"name" gorm:"not null;"`
|
||||
Script string `json:"script" gorm:"not null;"`
|
||||
}
|
||||
|
|
|
|||
76
agent/app/repo/script.go
Normal file
76
agent/app/repo/script.go
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
package repo
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
)
|
||||
|
||||
type ScriptRepo struct{}
|
||||
|
||||
type IScriptRepo interface {
|
||||
Get(opts ...DBOption) (model.ScriptLibrary, error)
|
||||
List(opts ...DBOption) ([]model.ScriptLibrary, error)
|
||||
|
||||
SyncAll(data []model.ScriptLibrary) error
|
||||
}
|
||||
|
||||
func NewIScriptRepo() IScriptRepo {
|
||||
return &ScriptRepo{}
|
||||
}
|
||||
|
||||
func (u *ScriptRepo) Get(opts ...DBOption) (model.ScriptLibrary, error) {
|
||||
var script model.ScriptLibrary
|
||||
db := global.DB
|
||||
if global.IsMaster {
|
||||
db = global.CoreDB
|
||||
}
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
err := db.First(&script).Error
|
||||
return script, err
|
||||
}
|
||||
|
||||
func (u *ScriptRepo) List(opts ...DBOption) ([]model.ScriptLibrary, error) {
|
||||
var ops []model.ScriptLibrary
|
||||
itemDB := global.DB
|
||||
if global.IsMaster {
|
||||
itemDB = global.CoreDB
|
||||
}
|
||||
db := itemDB.Model(&model.ScriptLibrary{})
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
err := db.Find(&ops).Error
|
||||
return ops, err
|
||||
}
|
||||
|
||||
func (u *ScriptRepo) SyncAll(data []model.ScriptLibrary) error {
|
||||
tx := global.DB.Begin()
|
||||
var oldScripts []model.ScriptLibrary
|
||||
_ = tx.Where("1 = 1").Find(&oldScripts).Error
|
||||
oldScriptMap := make(map[string]uint)
|
||||
for _, item := range oldScripts {
|
||||
oldScriptMap[item.Name] = item.ID
|
||||
}
|
||||
for _, item := range data {
|
||||
if val, ok := oldScriptMap[item.Name]; ok {
|
||||
item.ID = val
|
||||
delete(oldScriptMap, item.Name)
|
||||
} else {
|
||||
item.ID = 0
|
||||
}
|
||||
if err := tx.Model(model.ScriptLibrary{}).Where("id = ?", item.ID).Save(&item).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, val := range oldScriptMap {
|
||||
if err := tx.Where("id = ?", val).Delete(&model.ScriptLibrary{}).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
}
|
||||
tx.Commit()
|
||||
return nil
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package service
|
|||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
|
|
@ -37,6 +38,8 @@ type ICronjobService interface {
|
|||
StartJob(cronjob *model.Cronjob, isUpdate bool) (string, error)
|
||||
CleanRecord(req dto.CronjobClean) error
|
||||
|
||||
LoadScriptOptions() []dto.ScriptOptions
|
||||
|
||||
LoadInfo(req dto.OperateByID) (*dto.CronjobOperate, error)
|
||||
LoadRecordLog(req dto.OperateByID) string
|
||||
}
|
||||
|
|
@ -95,6 +98,27 @@ func (u *CronjobService) LoadInfo(req dto.OperateByID) (*dto.CronjobOperate, err
|
|||
return &item, err
|
||||
}
|
||||
|
||||
func (u *CronjobService) LoadScriptOptions() []dto.ScriptOptions {
|
||||
scripts, err := scriptRepo.List()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
var options []dto.ScriptOptions
|
||||
for _, script := range scripts {
|
||||
var item dto.ScriptOptions
|
||||
item.ID = script.ID
|
||||
var translations = make(map[string]string)
|
||||
_ = json.Unmarshal([]byte(script.Name), &translations)
|
||||
if name, ok := translations["en"]; ok {
|
||||
item.Name = strings.ReplaceAll(name, " ", "_")
|
||||
} else {
|
||||
item.Name = strings.ReplaceAll(script.Name, " ", "_")
|
||||
}
|
||||
options = append(options, item)
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
func (u *CronjobService) SearchRecords(search dto.SearchRecord) (int64, interface{}, error) {
|
||||
total, records, err := cronjobRepo.PageRecords(
|
||||
search.Page,
|
||||
|
|
@ -362,6 +386,8 @@ func (u *CronjobService) Update(id uint, req dto.CronjobOperate) error {
|
|||
upMap["container_name"] = req.ContainerName
|
||||
upMap["executor"] = req.Executor
|
||||
upMap["user"] = req.User
|
||||
|
||||
upMap["script_id"] = req.ScriptID
|
||||
upMap["app_id"] = req.AppID
|
||||
upMap["website"] = req.Website
|
||||
upMap["exclusion_rules"] = req.ExclusionRules
|
||||
|
|
|
|||
|
|
@ -79,6 +79,14 @@ func (u *CronjobService) handleJob(cronjob *model.Cronjob, record model.JobRecor
|
|||
)
|
||||
switch cronjob.Type {
|
||||
case "shell":
|
||||
if cronjob.ScriptMode == "library" {
|
||||
scriptItem, _ := scriptRepo.Get(repo.WithByID(cronjob.ScriptID))
|
||||
if scriptItem.ID == 0 {
|
||||
return nil, fmt.Errorf("load script from db failed, err: %v", err)
|
||||
}
|
||||
cronjob.Script = scriptItem.Script
|
||||
cronjob.ScriptMode = "input"
|
||||
}
|
||||
if len(cronjob.Script) == 0 {
|
||||
return nil, fmt.Errorf("the script content is empty and is skipped")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ var (
|
|||
imageRepoRepo = repo.NewIImageRepoRepo()
|
||||
composeRepo = repo.NewIComposeTemplateRepo()
|
||||
|
||||
scriptRepo = repo.NewIScriptRepo()
|
||||
cronjobRepo = repo.NewICronjobRepo()
|
||||
|
||||
hostRepo = repo.NewIHostRepo()
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import (
|
|||
)
|
||||
|
||||
var AddTable = &gormigrate.Migration{
|
||||
ID: "20250413-add-table",
|
||||
ID: "20250507-add-table",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
return tx.AutoMigrate(
|
||||
&model.AppDetail{},
|
||||
|
|
@ -43,6 +43,7 @@ var AddTable = &gormigrate.Migration{
|
|||
&model.Firewall{},
|
||||
&model.Ftp{},
|
||||
&model.ImageRepo{},
|
||||
&model.ScriptLibrary{},
|
||||
&model.JobRecords{},
|
||||
&model.MonitorBase{},
|
||||
&model.MonitorIO{},
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ func (s *CronjobRouter) InitRouter(Router *gin.RouterGroup) {
|
|||
cmdRouter.POST("", baseApi.CreateCronjob)
|
||||
cmdRouter.POST("/next", baseApi.LoadNextHandle)
|
||||
cmdRouter.POST("/load/info", baseApi.LoadCronjobInfo)
|
||||
cmdRouter.GET("/script/options", baseApi.LoadScriptOptions)
|
||||
cmdRouter.POST("/del", baseApi.DeleteCronjob)
|
||||
cmdRouter.POST("/update", baseApi.UpdateCronjob)
|
||||
cmdRouter.POST("/status", baseApi.UpdateCronjobStatus)
|
||||
|
|
|
|||
|
|
@ -3,21 +3,23 @@ package dto
|
|||
import "time"
|
||||
|
||||
type ScriptInfo struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Lable string `json:"lable"`
|
||||
Script string `json:"script"`
|
||||
GroupList []uint `json:"groupList"`
|
||||
GroupBelong []string `json:"groupBelong"`
|
||||
IsSystem bool `json:"isSystem"`
|
||||
Description string `json:"description"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
IsInteractive bool `json:"isInteractive"`
|
||||
Lable string `json:"lable"`
|
||||
Script string `json:"script"`
|
||||
GroupList []uint `json:"groupList"`
|
||||
GroupBelong []string `json:"groupBelong"`
|
||||
IsSystem bool `json:"isSystem"`
|
||||
Description string `json:"description"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
}
|
||||
|
||||
type ScriptOperate struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Script string `json:"script"`
|
||||
Groups string `json:"groups"`
|
||||
Description string `json:"description"`
|
||||
ID uint `json:"id"`
|
||||
IsInteractive bool `json:"isInteractive"`
|
||||
Name string `json:"name"`
|
||||
Script string `json:"script"`
|
||||
Groups string `json:"groups"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,10 @@ package model
|
|||
|
||||
type ScriptLibrary struct {
|
||||
BaseModel
|
||||
Name string `json:"name" gorm:"not null;"`
|
||||
Script string `json:"script" gorm:"not null;"`
|
||||
Groups string `json:"groups"`
|
||||
IsSystem bool `json:"isSystem"`
|
||||
Description string `json:"description"`
|
||||
Name string `json:"name" gorm:"not null;"`
|
||||
IsInteractive bool `json:"isInteractive"`
|
||||
Script string `json:"script" gorm:"not null;"`
|
||||
Groups string `json:"groups"`
|
||||
IsSystem bool `json:"isSystem"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import (
|
|||
"github.com/1Panel-dev/1Panel/core/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/files"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/req_helper"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/xpack"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/jinzhu/copier"
|
||||
"gopkg.in/yaml.v2"
|
||||
|
|
@ -57,7 +58,7 @@ func (u *ScriptService) Search(ctx *gin.Context, req dto.SearchPageWithGroup) (i
|
|||
for _, itemData := range list {
|
||||
var item dto.ScriptInfo
|
||||
if err := copier.Copy(&item, &itemData); err != nil {
|
||||
global.LOG.Errorf("copy backup account to dto backup info failed, err: %v", err)
|
||||
global.LOG.Errorf("copy scripts to dto backup info failed, err: %v", err)
|
||||
}
|
||||
if item.IsSystem {
|
||||
lang := strings.ToLower(common.GetLang(ctx))
|
||||
|
|
@ -117,6 +118,14 @@ func (u *ScriptService) Create(req dto.ScriptOperate) error {
|
|||
if err := scriptRepo.Create(&itemData); err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
if req.IsInteractive {
|
||||
return
|
||||
}
|
||||
if err := xpack.Sync(constant.SyncScripts); err != nil {
|
||||
global.LOG.Errorf("sync scripts to node failed, err: %v", err)
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -130,6 +139,11 @@ func (u *ScriptService) Delete(req dto.OperateByIDs) error {
|
|||
return err
|
||||
}
|
||||
}
|
||||
go func() {
|
||||
if err := xpack.Sync(constant.SyncScripts); err != nil {
|
||||
global.LOG.Errorf("sync scripts to node failed, err: %v", err)
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -142,10 +156,16 @@ func (u *ScriptService) Update(req dto.ScriptOperate) error {
|
|||
updateMap["name"] = req.Name
|
||||
updateMap["script"] = req.Script
|
||||
updateMap["groups"] = req.Groups
|
||||
updateMap["is_interactive"] = req.IsInteractive
|
||||
updateMap["description"] = req.Description
|
||||
if err := scriptRepo.Update(req.ID, updateMap); err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
if err := xpack.Sync(constant.SyncScripts); err != nil {
|
||||
global.LOG.Errorf("sync scripts to node failed, err: %v", err)
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -179,6 +199,7 @@ func (u *ScriptService) Sync() error {
|
|||
if err != nil {
|
||||
return fmt.Errorf("load scripts data.yaml from remote failed, err: %v", err)
|
||||
}
|
||||
fmt.Println(string(dataRes))
|
||||
|
||||
syncTask.Log("download successful!")
|
||||
var scripts Scripts
|
||||
|
|
@ -205,10 +226,11 @@ func (u *ScriptService) Sync() error {
|
|||
itemDescription, _ := json.Marshal(item.Description)
|
||||
shell, _ := os.ReadFile(fmt.Sprintf("%s/scripts/sh/%s.sh", tmpDir, item.Key))
|
||||
scriptItem := model.ScriptLibrary{
|
||||
Name: string(itemName),
|
||||
IsSystem: true,
|
||||
Script: string(shell),
|
||||
Description: string(itemDescription),
|
||||
Name: string(itemName),
|
||||
IsInteractive: item.Interactive,
|
||||
IsSystem: true,
|
||||
Script: string(shell),
|
||||
Description: string(itemDescription),
|
||||
}
|
||||
scriptsForDB = append(scriptsForDB, scriptItem)
|
||||
}
|
||||
|
|
@ -243,5 +265,6 @@ type ScriptHelper struct {
|
|||
Sort uint `json:"sort"`
|
||||
Groups string `json:"groups"`
|
||||
Name map[string]string `json:"name"`
|
||||
Interactive bool `json:"interactive"`
|
||||
Description map[string]string `json:"description"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -141,6 +141,12 @@ func (u *SettingService) Update(key, value string) error {
|
|||
}
|
||||
case "UserName", "Password":
|
||||
_ = global.SESSION.Clean()
|
||||
case "Language":
|
||||
go func() {
|
||||
if err := xpack.Sync(constant.SyncLanguage); err != nil {
|
||||
global.LOG.Errorf("sync language to node failed, err: %v", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -44,9 +44,11 @@ const (
|
|||
|
||||
const (
|
||||
SyncSystemProxy = "SyncSystemProxy"
|
||||
SyncScripts = "SyncScripts"
|
||||
SyncBackupAccounts = "SyncBackupAccounts"
|
||||
SyncAlertSetting = "SyncAlertSetting"
|
||||
SyncCustomApp = "SyncCustomApp"
|
||||
SyncLanguage = "SyncLanguage"
|
||||
)
|
||||
|
||||
var WebUrlMap = map[string]struct{}{
|
||||
|
|
|
|||
|
|
@ -176,7 +176,3 @@ func GetMsgWithMapForCmd(key string, maps map[string]interface{}) string {
|
|||
return content
|
||||
}
|
||||
}
|
||||
|
||||
func GetLanguageFromDB() {
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import (
|
|||
)
|
||||
|
||||
var AddTable = &gormigrate.Migration{
|
||||
ID: "20240109-add-table",
|
||||
ID: "20240506-add-table",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
return tx.AutoMigrate(
|
||||
&model.OperationLog{},
|
||||
|
|
@ -27,6 +27,7 @@ var AddTable = &gormigrate.Migration{
|
|||
&model.Host{},
|
||||
&model.Command{},
|
||||
&model.UpgradeLog{},
|
||||
&model.ScriptLibrary{},
|
||||
)
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -105,6 +105,11 @@ export namespace Cronjob {
|
|||
recordID: number;
|
||||
backupAccountID: number;
|
||||
}
|
||||
export interface ScriptOptions {
|
||||
id: number;
|
||||
name: string;
|
||||
script: string;
|
||||
}
|
||||
export interface SearchRecord extends ReqPage {
|
||||
cronjobID: number;
|
||||
startTime: Date;
|
||||
|
|
|
|||
|
|
@ -15,6 +15,10 @@ export const loadCronjobInfo = (id: number) => {
|
|||
return http.post<Cronjob.CronjobOperate>(`/cronjobs/load/info`, { id: id });
|
||||
};
|
||||
|
||||
export const loadScriptOptions = () => {
|
||||
return http.get<Cronjob.ScriptOptions>(`/cronjobs/script/options`);
|
||||
};
|
||||
|
||||
export const getRecordLog = (id: number) => {
|
||||
return http.post<string>(`/cronjobs/records/log`, { id: id });
|
||||
};
|
||||
|
|
|
|||
16
frontend/src/components/layout-col/form.vue
Normal file
16
frontend/src/components/layout-col/form.vue
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<template>
|
||||
<el-col :xs="24" :sm="span" :md="span" :lg="span" :xl="span">
|
||||
<slot />
|
||||
</el-col>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
span: {
|
||||
type: Number,
|
||||
default: 10,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -1069,6 +1069,9 @@ const message = {
|
|||
alertTitle: 'Planned Task - {0} 「{1}」 Task Failure Alert',
|
||||
library: {
|
||||
script: 'Script',
|
||||
isInteractive: 'Interactive',
|
||||
interactive: 'Interactive script',
|
||||
interactiveHelper: 'Requires user input during execution and cannot be used in scheduled tasks.',
|
||||
library: 'Script Library',
|
||||
create: 'Add Script',
|
||||
edit: 'Edit Script',
|
||||
|
|
|
|||
|
|
@ -1029,6 +1029,9 @@ const message = {
|
|||
alertTitle: '計画タスク - {0}「{1}」タスク障害アラート',
|
||||
library: {
|
||||
script: 'スクリプト',
|
||||
isInteractive: '対話型',
|
||||
interactive: '対話型スクリプト',
|
||||
interactiveHelper: '実行中にユーザー入力が必要で、スケジュールタスクでは使用できません。',
|
||||
library: 'スクリプトライブラリ',
|
||||
create: 'スクリプトを追加',
|
||||
edit: 'スクリプトを編集',
|
||||
|
|
|
|||
|
|
@ -1023,6 +1023,9 @@ const message = {
|
|||
alertTitle: '예정된 작업 - {0} 「{1}」 작업 실패 경고',
|
||||
library: {
|
||||
script: '스크립트',
|
||||
isInteractive: '대화형',
|
||||
interactive: '대화형 스크립트',
|
||||
interactiveHelper: '실행 중 사용자 입력이 필요하며 예약 작업에서는 사용할 수 없습니다.',
|
||||
library: '스크립트 라이브러리',
|
||||
create: '스크립트 추가',
|
||||
edit: '스크립트 수정',
|
||||
|
|
|
|||
|
|
@ -1059,6 +1059,10 @@ const message = {
|
|||
alertTitle: 'Tugas Terancang - {0} 「{1}」 Amaran Kegagalan Tugas',
|
||||
library: {
|
||||
script: 'Skrip',
|
||||
isInteractive: 'Interaktif',
|
||||
interactive: 'Skrip interaktif',
|
||||
interactiveHelper:
|
||||
'Memerlukan input pengguna semasa pelaksanaan dan tidak boleh digunakan dalam tugas terjadual.',
|
||||
library: 'Perpustakaan Skrip',
|
||||
create: 'Tambah Skrip',
|
||||
edit: 'Sunting Skrip',
|
||||
|
|
|
|||
|
|
@ -1048,6 +1048,10 @@ const message = {
|
|||
alertTitle: 'Tarefa Planejada - {0} 「{1}」 Alerta de Falha na Tarefa',
|
||||
library: {
|
||||
script: 'Script',
|
||||
isInteractive: 'Interativo',
|
||||
interactive: 'Script interativo',
|
||||
interactiveHelper:
|
||||
'Requer entrada do usuário durante a execução e não pode ser usado em tarefas agendadas.',
|
||||
library: 'Biblioteca de Scripts',
|
||||
create: 'Adicionar Script',
|
||||
edit: 'Editar Script',
|
||||
|
|
|
|||
|
|
@ -1053,6 +1053,10 @@ const message = {
|
|||
alertTitle: 'Плановая задача - {0} «{1}» Оповещение о сбое задачи',
|
||||
library: {
|
||||
script: 'Скрипт',
|
||||
isInteractive: 'Интерактивный',
|
||||
interactive: 'Интерактивный скрипт',
|
||||
interactiveHelper:
|
||||
'Требует ввода пользователя во время выполнения и не может использоваться в запланированных задачах.',
|
||||
library: 'Библиотека скриптов',
|
||||
create: 'Добавить скрипт',
|
||||
edit: 'Редактировать скрипт',
|
||||
|
|
|
|||
|
|
@ -1017,6 +1017,9 @@ const message = {
|
|||
alertTitle: '計畫任務-{0}「{1}」任務失敗告警',
|
||||
library: {
|
||||
script: '腳本',
|
||||
isInteractive: '交互式',
|
||||
interactive: '交互式腳本',
|
||||
interactiveHelper: '在腳本執行過程中需要用戶輸入參數或做出選擇,且無法用於計劃任務中。',
|
||||
library: '腳本庫',
|
||||
create: '添加腳本',
|
||||
edit: '修改腳本',
|
||||
|
|
|
|||
|
|
@ -1015,6 +1015,9 @@ const message = {
|
|||
alertTitle: '计划任务-{0}「 {1} 」任务失败告警',
|
||||
library: {
|
||||
script: '脚本',
|
||||
isInteractive: '交互式',
|
||||
interactive: '交互式脚本',
|
||||
interactiveHelper: '在脚本执行过程中需要用户输入参数或做出选择,且无法用于计划任务中。',
|
||||
library: '脚本库',
|
||||
create: '添加脚本',
|
||||
edit: '修改脚本',
|
||||
|
|
|
|||
|
|
@ -2,70 +2,25 @@
|
|||
<div v-loading="loading">
|
||||
<docker-status v-model:isActive="isActive" v-model:isExist="isExist" @search="search" />
|
||||
|
||||
<div class="card-interval" v-if="isExist && isActive">
|
||||
<el-tag @click="searchWithStatus('all')" v-if="countItem.all" effect="plain" size="large">
|
||||
{{ $t('commons.table.all') }} * {{ countItem.all }}
|
||||
</el-tag>
|
||||
<el-tag
|
||||
@click="searchWithStatus('running')"
|
||||
v-if="countItem.running"
|
||||
effect="plain"
|
||||
size="large"
|
||||
class="ml-2"
|
||||
>
|
||||
{{ $t('commons.status.running') }} * {{ countItem.running }}
|
||||
</el-tag>
|
||||
<el-tag
|
||||
@click="searchWithStatus('created')"
|
||||
v-if="countItem.created"
|
||||
effect="plain"
|
||||
size="large"
|
||||
class="ml-2"
|
||||
>
|
||||
{{ $t('commons.status.created') }} * {{ countItem.created }}
|
||||
</el-tag>
|
||||
<el-tag
|
||||
@click="searchWithStatus('paused')"
|
||||
v-if="countItem.paused"
|
||||
effect="plain"
|
||||
size="large"
|
||||
class="ml-2"
|
||||
>
|
||||
{{ $t('commons.status.paused') }} * {{ countItem.paused }}
|
||||
</el-tag>
|
||||
<el-tag
|
||||
@click="searchWithStatus('restarting')"
|
||||
v-if="countItem.restarting"
|
||||
effect="plain"
|
||||
size="large"
|
||||
class="ml-2"
|
||||
>
|
||||
{{ $t('commons.status.restarting') }} * {{ countItem.restarting }}
|
||||
</el-tag>
|
||||
<el-tag
|
||||
@click="searchWithStatus('removing')"
|
||||
v-if="countItem.removing"
|
||||
effect="plain"
|
||||
size="large"
|
||||
class="ml-2"
|
||||
>
|
||||
{{ $t('commons.status.removing') }} * {{ countItem.removing }}
|
||||
</el-tag>
|
||||
<el-tag
|
||||
@click="searchWithStatus('exited')"
|
||||
v-if="countItem.exited"
|
||||
effect="plain"
|
||||
size="large"
|
||||
class="ml-2"
|
||||
>
|
||||
{{ $t('commons.status.exited') }} * {{ countItem.exited }}
|
||||
</el-tag>
|
||||
<el-tag @click="searchWithStatus('dead')" v-if="countItem.dead" effect="plain" size="large" class="ml-2">
|
||||
{{ $t('commons.status.dead') }} * {{ countItem.dead }}
|
||||
</el-tag>
|
||||
</div>
|
||||
|
||||
<LayoutContent :title="$t('menu.container')" v-if="isExist" :class="{ mask: !isActive }">
|
||||
<template #search>
|
||||
<div class="card-interval" v-if="isExist && isActive">
|
||||
<div v-for="item in tags" :key="item.key" class="inline">
|
||||
<el-button
|
||||
v-if="item.count"
|
||||
class="tag-button"
|
||||
:class="activeTag === item.key ? '' : 'no-active'"
|
||||
@click="searchWithStatus(item.key)"
|
||||
:type="activeTag === item.key ? 'primary' : ''"
|
||||
:plain="activeTag !== item.key"
|
||||
>
|
||||
{{ item.key === 'all' ? $t('commons.table.all') : $t('commons.status.' + item.key) }} *
|
||||
{{ item.count }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #leftToolBar>
|
||||
<el-button type="primary" @click="onContainerOperate('')">
|
||||
{{ $t('container.create') }}
|
||||
|
|
@ -102,7 +57,11 @@
|
|||
<el-tooltip
|
||||
:content="includeAppStore ? $t('container.includeAppstore') : $t('container.excludeAppstore')"
|
||||
>
|
||||
<el-button @click="searchWithAppShow(!includeAppStore)" :icon="includeAppStore ? 'View' : 'Hide'" />
|
||||
<el-button
|
||||
:type="includeAppStore ? '' : 'primary'"
|
||||
@click="searchWithAppShow(!includeAppStore)"
|
||||
:icon="includeAppStore ? 'View' : 'Hide'"
|
||||
/>
|
||||
</el-tooltip>
|
||||
<TableRefresh @search="search()" />
|
||||
<TableSetting title="container-refresh" @search="refresh()" />
|
||||
|
|
@ -455,16 +414,8 @@ const opRef = ref();
|
|||
const includeAppStore = ref();
|
||||
const columns = ref([]);
|
||||
|
||||
const countItem = reactive({
|
||||
all: 0,
|
||||
created: 0,
|
||||
running: 0,
|
||||
paused: 0,
|
||||
restarting: 0,
|
||||
removing: 0,
|
||||
exited: 0,
|
||||
dead: 0,
|
||||
});
|
||||
const tags = ref([]);
|
||||
const activeTag = ref('all');
|
||||
|
||||
const goDashboard = async (port: any) => {
|
||||
if (port.indexOf('127.0.0.1') !== -1) {
|
||||
|
|
@ -527,8 +478,9 @@ const search = async (column?: any) => {
|
|||
});
|
||||
};
|
||||
|
||||
const searchWithStatus = (item: any) => {
|
||||
paginationConfig.state = item;
|
||||
const searchWithStatus = (item: string) => {
|
||||
activeTag.value = item;
|
||||
paginationConfig.state = activeTag.value;
|
||||
search();
|
||||
};
|
||||
|
||||
|
|
@ -539,14 +491,15 @@ const searchWithAppShow = (item: any) => {
|
|||
|
||||
const loadContainerCount = async () => {
|
||||
await loadContainerStatus().then((res) => {
|
||||
countItem.all = res.data.all;
|
||||
countItem.running = res.data.running;
|
||||
countItem.paused = res.data.paused;
|
||||
countItem.restarting = res.data.restarting;
|
||||
countItem.removing = res.data.removing;
|
||||
countItem.created = res.data.created;
|
||||
countItem.dead = res.data.dead;
|
||||
countItem.exited = res.data.exited;
|
||||
tags.value = [];
|
||||
tags.value.push({ key: 'all', count: res.data.all });
|
||||
tags.value.push({ key: 'running', count: res.data.running });
|
||||
tags.value.push({ key: 'paused', count: res.data.paused });
|
||||
tags.value.push({ key: 'restarting', count: res.data.restarting });
|
||||
tags.value.push({ key: 'removing', count: res.data.removing });
|
||||
tags.value.push({ key: 'created', count: res.data.created });
|
||||
tags.value.push({ key: 'dead', count: res.data.dead });
|
||||
tags.value.push({ key: 'exited', count: res.data.exited });
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -810,4 +763,12 @@ onMounted(() => {
|
|||
position: relative !important;
|
||||
z-index: 1 !important;
|
||||
}
|
||||
.tag-button {
|
||||
margin-top: -5px;
|
||||
margin-right: 10px;
|
||||
&.no-active {
|
||||
background: none;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -214,7 +214,7 @@
|
|||
|
||||
<el-card class="mt-5">
|
||||
<el-row :gutter="20">
|
||||
<el-col :xs="24" :sm="10" :md="10" :lg="10" :xl="10" v-if="isWebsite()">
|
||||
<LayoutCol v-if="isWebsite()">
|
||||
<el-form-item
|
||||
:label="form.type === 'website' ? $t('cronjob.website') : $t('menu.website')"
|
||||
prop="website"
|
||||
|
|
@ -241,8 +241,8 @@
|
|||
{{ $t('cronjob.cutWebsiteLogHelper') }}
|
||||
</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="10" :md="10" :lg="10" :xl="10" v-if="form.type === 'app'">
|
||||
</LayoutCol>
|
||||
<LayoutCol v-if="form.type === 'app'">
|
||||
<el-form-item :label="$t('cronjob.app')" prop="appID">
|
||||
<el-select clearable v-model="form.appID">
|
||||
<el-option
|
||||
|
|
@ -260,8 +260,8 @@
|
|||
</div>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="10" :md="10" :lg="10" :xl="10" v-if="form.type === 'database'">
|
||||
</LayoutCol>
|
||||
<LayoutCol v-if="form.type === 'database'">
|
||||
<el-form-item :label="$t('cronjob.database')">
|
||||
<el-select v-model="form.dbType" @change="loadDatabases">
|
||||
<el-option label="MySQL" value="mysql" />
|
||||
|
|
@ -269,8 +269,8 @@
|
|||
<el-option label="PostgreSQL" value="postgresql" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="10" :md="10" :lg="10" :xl="10" v-if="form.type === 'database'">
|
||||
</LayoutCol>
|
||||
<LayoutCol v-if="form.type === 'database'">
|
||||
<el-form-item :label="$t('cronjob.database')" prop="dbName">
|
||||
<el-select clearable v-model="form.dbName">
|
||||
<el-option
|
||||
|
|
@ -298,34 +298,32 @@
|
|||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="10" :md="10" :lg="10" :xl="10" v-if="form.type === 'directory'">
|
||||
</LayoutCol>
|
||||
<LayoutCol v-if="form.type === 'directory'">
|
||||
<el-form-item :label="$t('cronjob.backupContent')">
|
||||
<el-radio-group v-model="form.isDir">
|
||||
<el-radio-group v-model="form.isDir" class="w-full">
|
||||
<el-radio :value="true">{{ $t('file.dir') }}</el-radio>
|
||||
<el-radio :value="false">{{ $t('menu.files') }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="10" :md="10" :lg="10" :xl="10" v-if="form.type === 'curl'">
|
||||
</LayoutCol>
|
||||
<LayoutCol v-if="form.type === 'curl'">
|
||||
<el-form-item :label="$t('cronjob.url')" prop="url">
|
||||
<el-input clearable v-model.trim="form.url" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</LayoutCol>
|
||||
</el-row>
|
||||
|
||||
<div v-if="hasScript()" class="w-full">
|
||||
<el-row :gutter="20">
|
||||
<el-col :xs="24" :sm="10" :md="10" :lg="10" :xl="10">
|
||||
<el-form-item>
|
||||
<el-checkbox v-model="form.inContainer">
|
||||
{{ $t('cronjob.containerCheckBox') }}
|
||||
</el-checkbox>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24" v-if="hasScript()">
|
||||
<el-form-item>
|
||||
<el-checkbox v-model="form.inContainer">
|
||||
{{ $t('cronjob.containerCheckBox') }}
|
||||
</el-checkbox>
|
||||
</el-form-item>
|
||||
|
||||
<el-row :gutter="20" v-if="form.inContainer">
|
||||
<el-col :xs="24" :sm="10" :md="10" :lg="10" :xl="10">
|
||||
<LayoutCol>
|
||||
<el-form-item :label="$t('cronjob.containerName')" prop="containerName">
|
||||
<el-select v-model="form.containerName">
|
||||
<el-option
|
||||
|
|
@ -336,8 +334,8 @@
|
|||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="10" :md="10" :lg="10" :xl="10">
|
||||
</LayoutCol>
|
||||
<LayoutCol>
|
||||
<el-form-item
|
||||
:label="$t('container.command')"
|
||||
prop="command"
|
||||
|
|
@ -364,10 +362,10 @@
|
|||
v-model="form.command"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</LayoutCol>
|
||||
</el-row>
|
||||
<el-row :gutter="20" v-if="!form.inContainer">
|
||||
<el-col :xs="24" :sm="10" :md="10" :lg="10" :xl="10">
|
||||
<LayoutCol>
|
||||
<el-form-item :label="$t('commons.table.user')" prop="user">
|
||||
<el-select filterable v-model="form.user">
|
||||
<div v-for="item in userOptions" :key="item">
|
||||
|
|
@ -375,8 +373,8 @@
|
|||
</div>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="10" :md="10" :lg="10" :xl="10">
|
||||
</LayoutCol>
|
||||
<LayoutCol>
|
||||
<el-form-item :label="$t('cronjob.executor')" prop="executor">
|
||||
<el-checkbox border v-model="form.isCustom">
|
||||
{{ $t('container.custom') }}
|
||||
|
|
@ -397,39 +395,51 @@
|
|||
v-model="form.executor"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</LayoutCol>
|
||||
</el-row>
|
||||
|
||||
<el-form-item :label="$t('cronjob.shellContent')" prop="script" class="mt-5">
|
||||
<el-form-item :label="$t('cronjob.shellContent')" prop="scriptMode">
|
||||
<el-radio-group @change="form.script = ''" v-model="form.scriptMode">
|
||||
<el-radio value="input">{{ $t('commons.button.edit') }}</el-radio>
|
||||
<el-radio value="library">{{ $t('cronjob.library.library') }}</el-radio>
|
||||
<el-radio value="select">{{ $t('container.pathSelect') }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
<CodemirrorPro
|
||||
v-if="form.scriptMode === 'input'"
|
||||
v-model="form.script"
|
||||
placeholder="#Define or paste the content of your shell file here"
|
||||
mode="javascript"
|
||||
:heightDiff="400"
|
||||
class="mb-5"
|
||||
/>
|
||||
<el-row :gutter="20" v-if="form.scriptMode === 'select'">
|
||||
<el-col :xs="24" :sm="10" :md="10" :lg="10" :xl="10">
|
||||
<el-input
|
||||
:placeholder="$t('commons.example') + '/tmp/test.sh'"
|
||||
v-model="form.script"
|
||||
>
|
||||
<template #prepend>
|
||||
<FileList @choose="loadScriptDir" :dir="false"></FileList>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-col>
|
||||
<el-form-item class="-mt-4" v-if="form.scriptMode === 'input'" prop="script">
|
||||
<CodemirrorPro
|
||||
v-model="form.script"
|
||||
placeholder="#Define or paste the content of your shell file here"
|
||||
mode="javascript"
|
||||
:heightDiff="400"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-row :gutter="20" class="-mt-4">
|
||||
<LayoutCol>
|
||||
<el-form-item prop="scriptID" v-if="form.scriptMode === 'library'">
|
||||
<el-select filterable v-model="form.scriptID">
|
||||
<el-option
|
||||
v-for="item in scriptOptions"
|
||||
:key="item.id"
|
||||
:value="item.id"
|
||||
:label="item.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item prop="script" v-if="form.scriptMode === 'select'">
|
||||
<el-input
|
||||
:placeholder="$t('commons.example') + '/tmp/test.sh'"
|
||||
v-model="form.script"
|
||||
>
|
||||
<template #prepend>
|
||||
<FileList @choose="loadScriptDir" :dir="false"></FileList>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</LayoutCol>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<el-col :xs="24" :sm="10" :md="10" :lg="10" :xl="10" v-if="isDir() && form.isDir">
|
||||
<LayoutCol v-if="isDir() && form.isDir">
|
||||
<el-form-item prop="sourceDir">
|
||||
<el-input v-model="form.sourceDir">
|
||||
<template #prepend>
|
||||
|
|
@ -437,8 +447,8 @@
|
|||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="10" :md="10" :lg="10" :xl="10" v-if="isDir() && !form.isDir">
|
||||
</LayoutCol>
|
||||
<LayoutCol v-if="isDir() && !form.isDir">
|
||||
<el-input class="mb-5">
|
||||
<template #prepend>
|
||||
<FileList @choose="loadFile" :dir="false" />
|
||||
|
|
@ -462,11 +472,11 @@
|
|||
</ComplexTable>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</LayoutCol>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :xs="24" :sm="10" :md="10" :lg="10" :xl="10" v-if="isBackup()">
|
||||
<LayoutCol v-if="isBackup()">
|
||||
<el-form-item :label="$t('setting.backupAccount')" prop="sourceAccountItems">
|
||||
<el-select multiple v-model="form.sourceAccountItems" @change="changeAccount">
|
||||
<div v-for="item in backupOptions" :key="item.id">
|
||||
|
|
@ -493,8 +503,8 @@
|
|||
</el-link>
|
||||
</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="10" :md="10" :lg="10" :xl="10" v-if="isBackup()">
|
||||
</LayoutCol>
|
||||
<LayoutCol v-if="isBackup()">
|
||||
<el-form-item :label="$t('cronjob.default_download_path')" prop="downloadAccountID">
|
||||
<el-select v-model="form.downloadAccountID">
|
||||
<div v-for="item in accountOptions" :key="item.id">
|
||||
|
|
@ -510,16 +520,16 @@
|
|||
</div>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</LayoutCol>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :xs="24" :sm="10" :md="10" :lg="10" :xl="10" v-if="isBackup() && !isDatabase()">
|
||||
<LayoutCol v-if="isBackup() && !isDatabase()">
|
||||
<el-form-item :label="$t('setting.compressPassword')" prop="secret">
|
||||
<el-input v-model="form.secret" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="10" :md="10" :lg="10" :xl="10">
|
||||
</LayoutCol>
|
||||
<LayoutCol>
|
||||
<el-form-item :label="$t('cronjob.retainCopies')" prop="retainCopies">
|
||||
<el-input-number
|
||||
class="selectClass"
|
||||
|
|
@ -533,10 +543,10 @@
|
|||
</span>
|
||||
<span v-else class="input-help">{{ $t('cronjob.retainCopiesHelper') }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</LayoutCol>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :xs="24" :sm="20" :md="20" :lg="20" :xl="20" v-if="hasExclusionRules()">
|
||||
<LayoutCol :span="20" v-if="hasExclusionRules()">
|
||||
<el-form-item :label="$t('cronjob.exclusionRules')" prop="exclusionRules">
|
||||
<el-input
|
||||
:placeholder="$t('cronjob.rulesHelper')"
|
||||
|
|
@ -545,14 +555,14 @@
|
|||
/>
|
||||
<span class="input-help">{{ $t('cronjob.exclusionRulesHelper') }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</LayoutCol>
|
||||
</el-row>
|
||||
</el-card>
|
||||
|
||||
<el-card class="mt-5">
|
||||
<div v-if="!globalStore.isIntl">
|
||||
<el-row :gutter="20">
|
||||
<el-col :xs="24" :sm="10" :md="10" :lg="10" :xl="10">
|
||||
<LayoutCol>
|
||||
<el-form-item prop="hasAlert">
|
||||
<el-checkbox v-model="form.hasAlert" :label="$t('xpack.alert.isAlert')" />
|
||||
<span class="input-help">{{ $t('xpack.alert.cronJobHelper') }}</span>
|
||||
|
|
@ -564,8 +574,8 @@
|
|||
</el-link>
|
||||
</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="10" :md="10" :lg="10" :xl="10">
|
||||
</LayoutCol>
|
||||
<LayoutCol>
|
||||
<el-form-item
|
||||
prop="alertCount"
|
||||
v-if="form.hasAlert && isProductPro"
|
||||
|
|
@ -580,12 +590,12 @@
|
|||
></el-input-number>
|
||||
<span class="input-help">{{ $t('xpack.alert.alertCountHelper') }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</LayoutCol>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :xs="24" :sm="10" :md="10" :lg="10" :xl="10">
|
||||
<LayoutCol>
|
||||
<el-form-item :label="$t('cronjob.timeout')" prop="timeoutItem">
|
||||
<el-input type="number" class="selectClass" v-model.number="form.timeoutItem">
|
||||
<template #append>
|
||||
|
|
@ -597,8 +607,8 @@
|
|||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="10" :md="10" :lg="10" :xl="10">
|
||||
</LayoutCol>
|
||||
<LayoutCol>
|
||||
<el-form-item :label="$t('cronjob.retryTimes')" prop="retryTimes">
|
||||
<el-input-number
|
||||
class="selectClass"
|
||||
|
|
@ -609,7 +619,7 @@
|
|||
></el-input-number>
|
||||
<span class="input-help">{{ $t('cronjob.retryTimesHelper') }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</LayoutCol>
|
||||
</el-row>
|
||||
</el-card>
|
||||
|
||||
|
|
@ -638,8 +648,9 @@ import { listBackupOptions } from '@/api/modules/backup';
|
|||
import i18n from '@/lang';
|
||||
import { ElForm } from 'element-plus';
|
||||
import { Cronjob } from '@/api/interface/cronjob';
|
||||
import { addCronjob, editCronjob, loadCronjobInfo, loadNextHandle } from '@/api/modules/cronjob';
|
||||
import { addCronjob, editCronjob, loadCronjobInfo, loadNextHandle, loadScriptOptions } from '@/api/modules/cronjob';
|
||||
import CodemirrorPro from '@/components/codemirror-pro/index.vue';
|
||||
import LayoutCol from '@/components/layout-col/form.vue';
|
||||
import { listDbItems } from '@/api/modules/database';
|
||||
import { getWebsiteOptions } from '@/api/modules/website';
|
||||
import { MsgError, MsgSuccess } from '@/utils/message';
|
||||
|
|
@ -802,6 +813,7 @@ const search = async () => {
|
|||
loadShellUsers();
|
||||
loadWebsites();
|
||||
loadContainers();
|
||||
loadScripts();
|
||||
if (form.dbType) {
|
||||
loadDatabases(form.dbType);
|
||||
} else {
|
||||
|
|
@ -819,6 +831,7 @@ const backupOptions = ref([]);
|
|||
const accountOptions = ref([]);
|
||||
const appOptions = ref([]);
|
||||
const userOptions = ref([]);
|
||||
const scriptOptions = ref([]);
|
||||
|
||||
const dbInfo = reactive({
|
||||
isExist: false,
|
||||
|
|
@ -968,6 +981,7 @@ const rules = reactive({
|
|||
],
|
||||
|
||||
script: [{ validator: verifyScript, trigger: 'blur', required: true }],
|
||||
scriptID: [Rules.requiredSelect],
|
||||
containerName: [Rules.requiredSelect],
|
||||
appID: [Rules.requiredSelect],
|
||||
website: [Rules.requiredSelect],
|
||||
|
|
@ -1040,6 +1054,11 @@ const loadNext = async (spec: any) => {
|
|||
nextTimes.value = data.data || [];
|
||||
};
|
||||
|
||||
const loadScripts = async () => {
|
||||
const res = await loadScriptOptions();
|
||||
scriptOptions.value = res.data || [];
|
||||
};
|
||||
|
||||
const loadDatabases = async (dbType: string) => {
|
||||
const data = await listDbItems(dbType);
|
||||
dbInfo.dbs = data.data || [];
|
||||
|
|
@ -1288,6 +1307,7 @@ onMounted(() => {
|
|||
}
|
||||
.selectClass {
|
||||
width: 100%;
|
||||
padding-left: 0px;
|
||||
}
|
||||
.tagClass {
|
||||
float: right;
|
||||
|
|
|
|||
|
|
@ -47,6 +47,14 @@
|
|||
</el-text>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('cronjob.library.isInteractive')" prop="isInteractive" min-width="60">
|
||||
<template #default="{ row }">
|
||||
<div class="-mb-1">
|
||||
<el-icon v-if="row.isInteractive"><Check /></el-icon>
|
||||
<el-icon v-else><Minus /></el-icon>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('commons.table.group')" min-width="120" prop="group">
|
||||
<template #default="{ row }">
|
||||
<el-button class="mr-3" size="small" v-if="row.isSystem">{{ $t('menu.system') }}</el-button>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,14 @@
|
|||
>
|
||||
<el-form ref="formRef" v-loading="loading" label-position="top" :model="dialogData.rowData" :rules="rules">
|
||||
<el-form-item :label="$t('commons.table.name')" prop="name">
|
||||
<el-input clearable v-model="dialogData.rowData!.name" />
|
||||
<el-tag v-if="dialogData.title === 'edit'">{{ dialogData.rowData!.name }}</el-tag>
|
||||
<el-input v-else v-model="dialogData.rowData!.name" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="isInteractive">
|
||||
<el-checkbox v-model="dialogData.rowData!.isInteractive">
|
||||
{{ $t('cronjob.library.interactive') }}
|
||||
</el-checkbox>
|
||||
<span class="input-help">{{ $t('cronjob.library.interactiveHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('commons.table.group')" prop="groupList">
|
||||
<el-select filterable v-model="dialogData.rowData!.groupList" multiple>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue