mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-09-18 12:35:33 +08:00
feat: 完成数据库前端页面
This commit is contained in:
parent
b2653c2aef
commit
2c529e8fa3
22 changed files with 790 additions and 52 deletions
63
backend/app/api/v1/databse_mysql.go
Normal file
63
backend/app/api/v1/databse_mysql.go
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/global"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (b *BaseApi) CreateMysql(c *gin.Context) {
|
||||||
|
var req dto.MysqlDBCreate
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := global.VALID.Struct(req); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := mysqlService.Create(req); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseApi) SearchMysql(c *gin.Context) {
|
||||||
|
var req dto.SearchWithPage
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
total, list, err := mysqlService.SearchWithPage(req)
|
||||||
|
if err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
helper.SuccessWithData(c, dto.PageResult{
|
||||||
|
Items: list,
|
||||||
|
Total: total,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseApi) DeleteMysql(c *gin.Context) {
|
||||||
|
var req dto.BatchDeleteReq
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := global.VALID.Struct(req); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := mysqlService.Delete(req.Ids); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, nil)
|
||||||
|
}
|
|
@ -9,18 +9,26 @@ type ApiGroup struct {
|
||||||
var ApiGroupApp = new(ApiGroup)
|
var ApiGroupApp = new(ApiGroup)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
authService = service.ServiceGroupApp.AuthService
|
authService = service.ServiceGroupApp.AuthService
|
||||||
hostService = service.ServiceGroupApp.HostService
|
|
||||||
backupService = service.ServiceGroupApp.BackupService
|
appService = service.ServiceGroupApp.AppService
|
||||||
groupService = service.ServiceGroupApp.GroupService
|
|
||||||
containerService = service.ServiceGroupApp.ContainerService
|
containerService = service.ServiceGroupApp.ContainerService
|
||||||
composeTemplateService = service.ServiceGroupApp.ComposeTemplateService
|
composeTemplateService = service.ServiceGroupApp.ComposeTemplateService
|
||||||
imageRepoService = service.ServiceGroupApp.ImageRepoService
|
imageRepoService = service.ServiceGroupApp.ImageRepoService
|
||||||
imageService = service.ServiceGroupApp.ImageService
|
imageService = service.ServiceGroupApp.ImageService
|
||||||
commandService = service.ServiceGroupApp.CommandService
|
|
||||||
operationService = service.ServiceGroupApp.OperationService
|
mysqlService = service.ServiceGroupApp.MysqlService
|
||||||
fileService = service.ServiceGroupApp.FileService
|
|
||||||
cronjobService = service.ServiceGroupApp.CronjobService
|
cronjobService = service.ServiceGroupApp.CronjobService
|
||||||
settingService = service.ServiceGroupApp.SettingService
|
|
||||||
appService = service.ServiceGroupApp.AppService
|
hostService = service.ServiceGroupApp.HostService
|
||||||
|
groupService = service.ServiceGroupApp.GroupService
|
||||||
|
commandService = service.ServiceGroupApp.CommandService
|
||||||
|
fileService = service.ServiceGroupApp.FileService
|
||||||
|
|
||||||
|
settingService = service.ServiceGroupApp.SettingService
|
||||||
|
backupService = service.ServiceGroupApp.BackupService
|
||||||
|
|
||||||
|
operationService = service.ServiceGroupApp.OperationService
|
||||||
)
|
)
|
||||||
|
|
24
backend/app/dto/database.go
Normal file
24
backend/app/dto/database.go
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
package dto
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type MysqlDBInfo struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Format string `json:"format"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
Permission string `json:"permission"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MysqlDBCreate struct {
|
||||||
|
Name string `json:"name" validate:"required"`
|
||||||
|
Format string `json:"format" validate:"required,oneof=utf8mb4 utf-8 gbk big5"`
|
||||||
|
Username string `json:"username" validate:"required"`
|
||||||
|
Password string `json:"password" validate:"required"`
|
||||||
|
Permission string `json:"permission" validate:"required,oneof=local all ip"`
|
||||||
|
PermissionIPs string `json:"permissionIPs"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
}
|
12
backend/app/model/database_mysql.go
Normal file
12
backend/app/model/database_mysql.go
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package model
|
||||||
|
|
||||||
|
type DatabaseMysql struct {
|
||||||
|
BaseModel
|
||||||
|
Name string `json:"name" gorm:"type:varchar(256);not null"`
|
||||||
|
Format string `json:"format" gorm:"type:varchar(64);not null"`
|
||||||
|
Username string `json:"username" gorm:"type:varchar(256);not null"`
|
||||||
|
Password string `json:"password" gorm:"type:varchar(256);not null"`
|
||||||
|
Permission string `json:"permission" gorm:"type:varchar(256);not null"`
|
||||||
|
PermissionIPs string `json:"permissionIPs" gorm:"type:varchar(256)"`
|
||||||
|
Description string `json:"description" gorm:"type:varchar(256);"`
|
||||||
|
}
|
52
backend/app/repo/databse_mysql.go
Normal file
52
backend/app/repo/databse_mysql.go
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
package repo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/global"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MysqlRepo struct{}
|
||||||
|
|
||||||
|
type IMysqlRepo interface {
|
||||||
|
Page(limit, offset int, opts ...DBOption) (int64, []model.DatabaseMysql, error)
|
||||||
|
Create(mysql *model.DatabaseMysql) error
|
||||||
|
Delete(opts ...DBOption) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewIMysqlRepo() IMysqlRepo {
|
||||||
|
return &MysqlRepo{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *MysqlRepo) Get(opts ...DBOption) (model.DatabaseMysql, error) {
|
||||||
|
var mysql model.DatabaseMysql
|
||||||
|
db := global.DB
|
||||||
|
for _, opt := range opts {
|
||||||
|
db = opt(db)
|
||||||
|
}
|
||||||
|
err := db.First(&mysql).Error
|
||||||
|
return mysql, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *MysqlRepo) Page(page, size int, opts ...DBOption) (int64, []model.DatabaseMysql, error) {
|
||||||
|
var users []model.DatabaseMysql
|
||||||
|
db := global.DB.Model(&model.DatabaseMysql{})
|
||||||
|
for _, opt := range opts {
|
||||||
|
db = opt(db)
|
||||||
|
}
|
||||||
|
count := int64(0)
|
||||||
|
db = db.Count(&count)
|
||||||
|
err := db.Limit(size).Offset(size * (page - 1)).Find(&users).Error
|
||||||
|
return count, users, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *MysqlRepo) Create(mysql *model.DatabaseMysql) error {
|
||||||
|
return global.DB.Create(mysql).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *MysqlRepo) Delete(opts ...DBOption) error {
|
||||||
|
db := global.DB
|
||||||
|
for _, opt := range opts {
|
||||||
|
db = opt(db)
|
||||||
|
}
|
||||||
|
return db.Delete(&model.DatabaseMysql{}).Error
|
||||||
|
}
|
|
@ -1,16 +1,8 @@
|
||||||
package repo
|
package repo
|
||||||
|
|
||||||
type RepoGroup struct {
|
type RepoGroup struct {
|
||||||
HostRepo
|
|
||||||
BackupRepo
|
|
||||||
GroupRepo
|
|
||||||
ImageRepoRepo
|
|
||||||
ComposeTemplateRepo
|
|
||||||
CommandRepo
|
|
||||||
OperationRepo
|
|
||||||
CommonRepo
|
CommonRepo
|
||||||
CronjobRepo
|
|
||||||
SettingRepo
|
|
||||||
AppRepo
|
AppRepo
|
||||||
AppTagRepo
|
AppTagRepo
|
||||||
TagRepo
|
TagRepo
|
||||||
|
@ -19,6 +11,22 @@ type RepoGroup struct {
|
||||||
AppInstallResourceRpo
|
AppInstallResourceRpo
|
||||||
DatabaseRepo
|
DatabaseRepo
|
||||||
AppInstallBackupRepo
|
AppInstallBackupRepo
|
||||||
|
|
||||||
|
ImageRepoRepo
|
||||||
|
ComposeTemplateRepo
|
||||||
|
|
||||||
|
MysqlRepo
|
||||||
|
|
||||||
|
CronjobRepo
|
||||||
|
|
||||||
|
HostRepo
|
||||||
|
CommandRepo
|
||||||
|
GroupRepo
|
||||||
|
|
||||||
|
SettingRepo
|
||||||
|
BackupRepo
|
||||||
|
|
||||||
|
OperationRepo
|
||||||
}
|
}
|
||||||
|
|
||||||
var RepoGroupApp = new(RepoGroup)
|
var RepoGroupApp = new(RepoGroup)
|
||||||
|
|
58
backend/app/service/database_mysql.go
Normal file
58
backend/app/service/database_mysql.go
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||||
|
"github.com/jinzhu/copier"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MysqlService struct{}
|
||||||
|
|
||||||
|
type IMysqlService interface {
|
||||||
|
SearchWithPage(search dto.SearchWithPage) (int64, interface{}, error)
|
||||||
|
Create(mysqlDto dto.MysqlDBCreate) error
|
||||||
|
Delete(ids []uint) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewIMysqlService() IMysqlService {
|
||||||
|
return &MysqlService{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *MysqlService) SearchWithPage(search dto.SearchWithPage) (int64, interface{}, error) {
|
||||||
|
total, mysqls, err := mysqlRepo.Page(search.Page, search.PageSize, commonRepo.WithLikeName(search.Info))
|
||||||
|
var dtoMysqls []dto.MysqlDBInfo
|
||||||
|
for _, mysql := range mysqls {
|
||||||
|
var item dto.MysqlDBInfo
|
||||||
|
if err := copier.Copy(&item, &mysql); err != nil {
|
||||||
|
return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||||
|
}
|
||||||
|
dtoMysqls = append(dtoMysqls, item)
|
||||||
|
}
|
||||||
|
return total, dtoMysqls, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *MysqlService) Create(mysqlDto dto.MysqlDBCreate) error {
|
||||||
|
mysql, _ := mysqlRepo.Get(commonRepo.WithByName(mysqlDto.Name))
|
||||||
|
if mysql.ID != 0 {
|
||||||
|
return constant.ErrRecordExist
|
||||||
|
}
|
||||||
|
if err := copier.Copy(&mysql, &mysqlDto); err != nil {
|
||||||
|
return errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||||
|
}
|
||||||
|
if err := mysqlRepo.Create(&mysql); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *MysqlService) Delete(ids []uint) error {
|
||||||
|
if len(ids) == 1 {
|
||||||
|
mysql, _ := mysqlRepo.Get(commonRepo.WithByID(ids[0]))
|
||||||
|
if mysql.ID == 0 {
|
||||||
|
return constant.ErrRecordNotFound
|
||||||
|
}
|
||||||
|
return mysqlRepo.Delete(commonRepo.WithByID(ids[0]))
|
||||||
|
}
|
||||||
|
return mysqlRepo.Delete(commonRepo.WithIdsIn(ids))
|
||||||
|
}
|
|
@ -4,34 +4,35 @@ import "github.com/1Panel-dev/1Panel/backend/app/repo"
|
||||||
|
|
||||||
type ServiceGroup struct {
|
type ServiceGroup struct {
|
||||||
AuthService
|
AuthService
|
||||||
HostService
|
|
||||||
BackupService
|
|
||||||
GroupService
|
|
||||||
ImageService
|
|
||||||
ComposeTemplateService
|
|
||||||
ImageRepoService
|
|
||||||
ContainerService
|
|
||||||
CommandService
|
|
||||||
OperationService
|
|
||||||
FileService
|
|
||||||
CronjobService
|
|
||||||
SettingService
|
|
||||||
AppService
|
AppService
|
||||||
|
|
||||||
|
ContainerService
|
||||||
|
ImageService
|
||||||
|
ImageRepoService
|
||||||
|
ComposeTemplateService
|
||||||
|
|
||||||
|
MysqlService
|
||||||
|
|
||||||
|
CronjobService
|
||||||
|
|
||||||
|
HostService
|
||||||
|
GroupService
|
||||||
|
CommandService
|
||||||
|
FileService
|
||||||
|
|
||||||
|
SettingService
|
||||||
|
BackupService
|
||||||
|
|
||||||
|
OperationService
|
||||||
}
|
}
|
||||||
|
|
||||||
var ServiceGroupApp = new(ServiceGroup)
|
var ServiceGroupApp = new(ServiceGroup)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
hostRepo = repo.RepoGroupApp.HostRepo
|
commonRepo = repo.RepoGroupApp.CommonRepo
|
||||||
backupRepo = repo.RepoGroupApp.BackupRepo
|
|
||||||
groupRepo = repo.RepoGroupApp.GroupRepo
|
appInstallBackupRepo = repo.RepoGroupApp.AppInstallBackupRepo
|
||||||
commandRepo = repo.RepoGroupApp.CommandRepo
|
|
||||||
operationRepo = repo.RepoGroupApp.OperationRepo
|
|
||||||
commonRepo = repo.RepoGroupApp.CommonRepo
|
|
||||||
imageRepoRepo = repo.RepoGroupApp.ImageRepoRepo
|
|
||||||
composeRepo = repo.RepoGroupApp.ComposeTemplateRepo
|
|
||||||
cronjobRepo = repo.RepoGroupApp.CronjobRepo
|
|
||||||
settingRepo = repo.RepoGroupApp.SettingRepo
|
|
||||||
appRepo = repo.RepoGroupApp.AppRepo
|
appRepo = repo.RepoGroupApp.AppRepo
|
||||||
appTagRepo = repo.RepoGroupApp.AppTagRepo
|
appTagRepo = repo.RepoGroupApp.AppTagRepo
|
||||||
appDetailRepo = repo.RepoGroupApp.AppDetailRepo
|
appDetailRepo = repo.RepoGroupApp.AppDetailRepo
|
||||||
|
@ -39,5 +40,20 @@ var (
|
||||||
appInstallRepo = repo.RepoGroupApp.AppInstallRepo
|
appInstallRepo = repo.RepoGroupApp.AppInstallRepo
|
||||||
appInstallResourceRepo = repo.RepoGroupApp.AppInstallResourceRpo
|
appInstallResourceRepo = repo.RepoGroupApp.AppInstallResourceRpo
|
||||||
dataBaseRepo = repo.RepoGroupApp.DatabaseRepo
|
dataBaseRepo = repo.RepoGroupApp.DatabaseRepo
|
||||||
appInstallBackupRepo = repo.RepoGroupApp.AppInstallBackupRepo
|
|
||||||
|
mysqlRepo = repo.RepoGroupApp.MysqlRepo
|
||||||
|
|
||||||
|
imageRepoRepo = repo.RepoGroupApp.ImageRepoRepo
|
||||||
|
composeRepo = repo.RepoGroupApp.ComposeTemplateRepo
|
||||||
|
|
||||||
|
cronjobRepo = repo.RepoGroupApp.CronjobRepo
|
||||||
|
|
||||||
|
hostRepo = repo.RepoGroupApp.HostRepo
|
||||||
|
groupRepo = repo.RepoGroupApp.GroupRepo
|
||||||
|
commandRepo = repo.RepoGroupApp.CommandRepo
|
||||||
|
|
||||||
|
settingRepo = repo.RepoGroupApp.SettingRepo
|
||||||
|
backupRepo = repo.RepoGroupApp.BackupRepo
|
||||||
|
|
||||||
|
operationRepo = repo.RepoGroupApp.OperationRepo
|
||||||
)
|
)
|
||||||
|
|
|
@ -17,6 +17,7 @@ func Init() {
|
||||||
migrations.AddTableCronjob,
|
migrations.AddTableCronjob,
|
||||||
migrations.AddTableApp,
|
migrations.AddTableApp,
|
||||||
migrations.AddTableImageRepo,
|
migrations.AddTableImageRepo,
|
||||||
|
migrations.AddTableDatabaseMysql,
|
||||||
})
|
})
|
||||||
if err := m.Migrate(); err != nil {
|
if err := m.Migrate(); err != nil {
|
||||||
global.LOG.Error(err)
|
global.LOG.Error(err)
|
||||||
|
|
|
@ -170,3 +170,10 @@ var AddTableImageRepo = &gormigrate.Migration{
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var AddTableDatabaseMysql = &gormigrate.Migration{
|
||||||
|
ID: "20201020-add-table-database_mysql",
|
||||||
|
Migrate: func(tx *gorm.DB) error {
|
||||||
|
return tx.AutoMigrate(&model.DatabaseMysql{})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
package router
|
package router
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"html/template"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
v1 "github.com/1Panel-dev/1Panel/backend/app/api/v1"
|
v1 "github.com/1Panel-dev/1Panel/backend/app/api/v1"
|
||||||
"github.com/1Panel-dev/1Panel/backend/docs"
|
"github.com/1Panel-dev/1Panel/backend/docs"
|
||||||
"github.com/1Panel-dev/1Panel/backend/i18n"
|
"github.com/1Panel-dev/1Panel/backend/i18n"
|
||||||
|
@ -11,8 +14,6 @@ import (
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
swaggerfiles "github.com/swaggo/files"
|
swaggerfiles "github.com/swaggo/files"
|
||||||
ginSwagger "github.com/swaggo/gin-swagger"
|
ginSwagger "github.com/swaggo/gin-swagger"
|
||||||
"html/template"
|
|
||||||
"net/http"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func setWebStatic(rootRouter *gin.Engine) {
|
func setWebStatic(rootRouter *gin.Engine) {
|
||||||
|
@ -78,6 +79,7 @@ func Routers() *gin.Engine {
|
||||||
systemRouter.InitCronjobRouter(PrivateGroup)
|
systemRouter.InitCronjobRouter(PrivateGroup)
|
||||||
systemRouter.InitSettingRouter(PrivateGroup)
|
systemRouter.InitSettingRouter(PrivateGroup)
|
||||||
systemRouter.InitAppRouter(PrivateGroup)
|
systemRouter.InitAppRouter(PrivateGroup)
|
||||||
|
systemRouter.InitDatabaseRouter(PrivateGroup)
|
||||||
}
|
}
|
||||||
|
|
||||||
return Router
|
return Router
|
||||||
|
|
|
@ -14,6 +14,7 @@ type RouterGroup struct {
|
||||||
CronjobRouter
|
CronjobRouter
|
||||||
SettingRouter
|
SettingRouter
|
||||||
AppRouter
|
AppRouter
|
||||||
|
DatabaseRouter
|
||||||
}
|
}
|
||||||
|
|
||||||
var RouterGroupApp = new(RouterGroup)
|
var RouterGroupApp = new(RouterGroup)
|
||||||
|
|
28
backend/router/ro_database.go
Normal file
28
backend/router/ro_database.go
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
v1 "github.com/1Panel-dev/1Panel/backend/app/api/v1"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/middleware"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DatabaseRouter struct{}
|
||||||
|
|
||||||
|
func (s *DatabaseRouter) InitDatabaseRouter(Router *gin.RouterGroup) {
|
||||||
|
cmdRouter := Router.Group("databases").
|
||||||
|
Use(middleware.JwtAuth()).
|
||||||
|
Use(middleware.SessionAuth()).
|
||||||
|
Use(middleware.PasswordExpired())
|
||||||
|
withRecordRouter := Router.Group("databases").
|
||||||
|
Use(middleware.JwtAuth()).
|
||||||
|
Use(middleware.SessionAuth()).
|
||||||
|
Use(middleware.PasswordExpired()).
|
||||||
|
Use(middleware.OperationRecord())
|
||||||
|
baseApi := v1.ApiGroupApp.BaseApi
|
||||||
|
{
|
||||||
|
withRecordRouter.POST("", baseApi.CreateMysql)
|
||||||
|
withRecordRouter.POST("/del", baseApi.DeleteMysql)
|
||||||
|
cmdRouter.POST("/search", baseApi.SearchMysql)
|
||||||
|
}
|
||||||
|
}
|
22
frontend/src/api/interface/database.ts
Normal file
22
frontend/src/api/interface/database.ts
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
export namespace Database {
|
||||||
|
export interface MysqlDBInfo {
|
||||||
|
id: number;
|
||||||
|
createdAt: Date;
|
||||||
|
name: string;
|
||||||
|
format: string;
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
permission: string;
|
||||||
|
permissionIPs: string;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
export interface MysqlDBCreate {
|
||||||
|
name: string;
|
||||||
|
format: string;
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
permission: string;
|
||||||
|
permissionIPs: string;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
}
|
15
frontend/src/api/modules/database.ts
Normal file
15
frontend/src/api/modules/database.ts
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import http from '@/api';
|
||||||
|
import { ResPage, ReqPage } from '../interface';
|
||||||
|
import { Database } from '../interface/database';
|
||||||
|
|
||||||
|
export const searchMysqlDBs = (params: ReqPage) => {
|
||||||
|
return http.post<ResPage<Database.MysqlDBInfo>>(`databases/search`, params);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const addMysqlDB = (params: Database.MysqlDBCreate) => {
|
||||||
|
return http.post(`/databases`, params);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteMysqlDB = (params: { ids: number[] }) => {
|
||||||
|
return http.post(`/databases/del`, params);
|
||||||
|
};
|
|
@ -150,6 +150,16 @@ export default {
|
||||||
header: {
|
header: {
|
||||||
logout: '退出登录',
|
logout: '退出登录',
|
||||||
},
|
},
|
||||||
|
database: {
|
||||||
|
permission: '权限',
|
||||||
|
permissionLocal: '本地服务器',
|
||||||
|
permissionForIP: '指定 IP',
|
||||||
|
permissionAll: '所有人(不安全)',
|
||||||
|
rootPassword: 'root 密码',
|
||||||
|
backupList: '备份列表',
|
||||||
|
loadBackup: '导入备份',
|
||||||
|
setting: '数据库设置',
|
||||||
|
},
|
||||||
container: {
|
container: {
|
||||||
operatorHelper: '将对选中容器进行 {0} 操作,是否继续?',
|
operatorHelper: '将对选中容器进行 {0} 操作,是否继续?',
|
||||||
start: '启动',
|
start: '启动',
|
||||||
|
|
|
@ -2,19 +2,31 @@ import { Layout } from '@/routers/constant';
|
||||||
|
|
||||||
const databaseRouter = {
|
const databaseRouter = {
|
||||||
sort: 4,
|
sort: 4,
|
||||||
path: '/database',
|
path: '/databases',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
redirect: '/database',
|
redirect: '/databases',
|
||||||
meta: {
|
meta: {
|
||||||
icon: 'p-database',
|
icon: 'p-database',
|
||||||
title: 'menu.database',
|
title: 'menu.database',
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '/database',
|
path: '',
|
||||||
name: 'Database',
|
name: 'Mysql',
|
||||||
component: () => import('@/views/database/index.vue'),
|
component: () => import('@/views/database/mysql/index.vue'),
|
||||||
meta: {},
|
hidden: true,
|
||||||
|
meta: {
|
||||||
|
activeMenu: '/databases',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/redis',
|
||||||
|
name: 'Redis',
|
||||||
|
component: () => import('@/views/database/redis/index.vue'),
|
||||||
|
hidden: true,
|
||||||
|
meta: {
|
||||||
|
activeMenu: '/databases',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,70 @@
|
||||||
<template>
|
<template>
|
||||||
<LayoutContent></LayoutContent>
|
<div>
|
||||||
|
<el-card class="topCard">
|
||||||
|
<el-radio-group v-model="active">
|
||||||
|
<el-radio-button class="topButton" size="large" @click="routerTo('/databases')" label="mysql">
|
||||||
|
Mysql
|
||||||
|
</el-radio-button>
|
||||||
|
<el-radio-button class="topButton" size="large" @click="routerTo('/databases/redis')" label="redis">
|
||||||
|
Redis
|
||||||
|
</el-radio-button>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import LayoutContent from '@/layout/layout-content.vue';
|
import { onMounted, ref } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
const router = useRouter();
|
||||||
|
interface MenuProps {
|
||||||
|
activeName: string;
|
||||||
|
}
|
||||||
|
const props = withDefaults(defineProps<MenuProps>(), {
|
||||||
|
activeName: 'mysql',
|
||||||
|
});
|
||||||
|
|
||||||
|
const active = ref('mysql');
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (props.activeName) {
|
||||||
|
active.value = props.activeName;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const routerTo = (path: string) => {
|
||||||
|
router.push({ path: path });
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.topCard {
|
||||||
|
--el-card-border-color: var(--el-border-color-light);
|
||||||
|
--el-card-border-radius: 4px;
|
||||||
|
--el-card-padding: 0px;
|
||||||
|
--el-card-bg-color: var(--el-fill-color-blank);
|
||||||
|
}
|
||||||
|
.topButton .el-radio-button__inner {
|
||||||
|
display: inline-block;
|
||||||
|
line-height: 1;
|
||||||
|
white-space: nowrap;
|
||||||
|
vertical-align: middle;
|
||||||
|
background: var(--el-button-bg-color, var(--el-fill-color-blank));
|
||||||
|
border: 0;
|
||||||
|
font-weight: 350;
|
||||||
|
border-left: 0;
|
||||||
|
color: var(--el-button-text-color, var(--el-text-color-regular));
|
||||||
|
text-align: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
outline: 0;
|
||||||
|
margin: 0;
|
||||||
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: var(--el-transition-all);
|
||||||
|
-webkit-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
padding: 8px 15px;
|
||||||
|
font-size: var(--el-font-size-base);
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
107
frontend/src/views/database/mysql/create/index.vue
Normal file
107
frontend/src/views/database/mysql/create/index.vue
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
<template>
|
||||||
|
<el-dialog v-model="createVisiable" :destroy-on-close="true" :close-on-click-modal="false" width="30%">
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span>创建数据库</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form ref="formRef" :model="form" :rules="rules" label-width="80px">
|
||||||
|
<el-form-item :label="$t('commons.table.name')" prop="name">
|
||||||
|
<el-input clearable v-model="form.name">
|
||||||
|
<template #append>
|
||||||
|
<el-select v-model="form.format" style="width: 125px">
|
||||||
|
<el-option label="utf8mb4" value="utf8mb4" />
|
||||||
|
<el-option label="utf-8" value="utf-8" />
|
||||||
|
<el-option label="gbk" value="gbk" />
|
||||||
|
<el-option label="big5" value="big5" />
|
||||||
|
</el-select>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('auth.username')" prop="username">
|
||||||
|
<el-input clearable v-model="form.username" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('auth.password')" prop="password">
|
||||||
|
<el-input type="password" clearable show-password v-model="form.password" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item :label="$t('database.permission')" prop="permission">
|
||||||
|
<el-select style="width: 100%" v-model="form.permission">
|
||||||
|
<el-option value="local" :label="$t('database.permissionLocal')" />
|
||||||
|
<el-option value="all" :label="$t('database.permissionAll')" />
|
||||||
|
<el-option value="ip" :label="$t('database.permissionForIP')" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="form.permission === 'ip'" prop="permissionIPs">
|
||||||
|
<el-input type="password" clearable v-model="form.permissionIPs" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('commons.table.description')" prop="description">
|
||||||
|
<el-input type="textarea" clearable v-model="form.description" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="createVisiable = false">{{ $t('commons.button.cancel') }}</el-button>
|
||||||
|
<el-button type="primary" @click="onSubmit(formRef)">
|
||||||
|
{{ $t('commons.button.confirm') }}
|
||||||
|
</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { reactive, ref } from 'vue';
|
||||||
|
import { Rules } from '@/global/form-rules';
|
||||||
|
import i18n from '@/lang';
|
||||||
|
import { ElForm, ElMessage } from 'element-plus';
|
||||||
|
import { addMysqlDB } from '@/api/modules/database';
|
||||||
|
|
||||||
|
const createVisiable = ref(false);
|
||||||
|
const form = reactive({
|
||||||
|
name: '',
|
||||||
|
format: '',
|
||||||
|
username: '',
|
||||||
|
password: '',
|
||||||
|
permission: '',
|
||||||
|
permissionIPs: '',
|
||||||
|
description: '',
|
||||||
|
});
|
||||||
|
const rules = reactive({
|
||||||
|
name: [Rules.requiredInput, Rules.name],
|
||||||
|
username: [Rules.requiredInput, Rules.name],
|
||||||
|
password: [Rules.requiredInput],
|
||||||
|
permission: [Rules.requiredSelect],
|
||||||
|
permissionIPs: [Rules.requiredInput],
|
||||||
|
});
|
||||||
|
type FormInstance = InstanceType<typeof ElForm>;
|
||||||
|
const formRef = ref<FormInstance>();
|
||||||
|
|
||||||
|
const acceptParams = (): void => {
|
||||||
|
form.name = '';
|
||||||
|
form.format = 'utf8mb4';
|
||||||
|
form.username = '';
|
||||||
|
form.password = '';
|
||||||
|
form.permission = 'local';
|
||||||
|
form.permissionIPs = '';
|
||||||
|
form.description = '';
|
||||||
|
createVisiable.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const emit = defineEmits<{ (e: 'search'): void }>();
|
||||||
|
const onSubmit = async (formEl: FormInstance | undefined) => {
|
||||||
|
if (!formEl) return;
|
||||||
|
formEl.validate(async (valid) => {
|
||||||
|
if (!valid) return;
|
||||||
|
await addMysqlDB(form);
|
||||||
|
|
||||||
|
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
||||||
|
emit('search');
|
||||||
|
createVisiable.value = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
acceptParams,
|
||||||
|
});
|
||||||
|
</script>
|
133
frontend/src/views/database/mysql/index.vue
Normal file
133
frontend/src/views/database/mysql/index.vue
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<Submenu activeName="mysql" />
|
||||||
|
<el-dropdown size="large" split-button style="margin-top: 20px; margin-bottom: 5px">
|
||||||
|
Mysql 版本 {{ version }}
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu v-model="version">
|
||||||
|
<el-dropdown-item @click="version = '5.7.39'">5.7.39</el-dropdown-item>
|
||||||
|
<el-dropdown-item @click="version = '8.0.30'">8.0.30</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
<el-button style="margin-top: 20px; margin-left: 10px" size="large" icon="Setting" @click="onOpenDialog()">
|
||||||
|
{{ $t('database.setting') }}
|
||||||
|
</el-button>
|
||||||
|
<el-card>
|
||||||
|
<ComplexTable :pagination-config="paginationConfig" v-model:selects="selects" @search="search" :data="data">
|
||||||
|
<template #toolbar>
|
||||||
|
<el-button type="primary" @click="onOpenDialog()">{{ $t('commons.button.create') }}</el-button>
|
||||||
|
<el-button @click="onOpenDialog()">{{ $t('database.rootPassword') }}</el-button>
|
||||||
|
<el-button @click="onOpenDialog()">phpMyAdmin</el-button>
|
||||||
|
<el-button type="danger" plain :disabled="selects.length === 0" @click="onBatchDelete(null)">
|
||||||
|
{{ $t('commons.button.delete') }}
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
<el-table-column type="selection" fix />
|
||||||
|
<el-table-column :label="$t('commons.table.name')" prop="name" />
|
||||||
|
<el-table-column :label="$t('auth.username')" prop="username" />
|
||||||
|
<el-table-column :label="$t('auth.password')" prop="password" />
|
||||||
|
<el-table-column :label="$t('commons.table.description')" prop="description" />
|
||||||
|
<el-table-column
|
||||||
|
prop="createdAt"
|
||||||
|
:label="$t('commons.table.date')"
|
||||||
|
:formatter="dateFromat"
|
||||||
|
show-overflow-tooltip
|
||||||
|
/>
|
||||||
|
<fu-table-operations
|
||||||
|
width="300px"
|
||||||
|
:buttons="buttons"
|
||||||
|
:ellipsis="10"
|
||||||
|
:label="$t('commons.table.operate')"
|
||||||
|
fix
|
||||||
|
/>
|
||||||
|
</ComplexTable>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<OperatrDialog @search="search" ref="dialogRef" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import ComplexTable from '@/components/complex-table/index.vue';
|
||||||
|
import OperatrDialog from '@/views/database/mysql/create/index.vue';
|
||||||
|
import Submenu from '@/views/database/index.vue';
|
||||||
|
import { dateFromat } from '@/utils/util';
|
||||||
|
import { onMounted, reactive, ref } from 'vue';
|
||||||
|
import { deleteMysqlDB, searchMysqlDBs } from '@/api/modules/database';
|
||||||
|
import i18n from '@/lang';
|
||||||
|
import { Cronjob } from '@/api/interface/cronjob';
|
||||||
|
import { useDeleteData } from '@/hooks/use-delete-data';
|
||||||
|
|
||||||
|
const selects = ref<any>([]);
|
||||||
|
const version = ref<string>('5.7.39');
|
||||||
|
|
||||||
|
const data = ref();
|
||||||
|
const paginationConfig = reactive({
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
const dialogRef = ref();
|
||||||
|
const onOpenDialog = async () => {
|
||||||
|
dialogRef.value!.acceptParams();
|
||||||
|
};
|
||||||
|
|
||||||
|
const search = async () => {
|
||||||
|
let params = {
|
||||||
|
page: paginationConfig.currentPage,
|
||||||
|
pageSize: paginationConfig.pageSize,
|
||||||
|
};
|
||||||
|
const res = await searchMysqlDBs(params);
|
||||||
|
data.value = res.data.items || [];
|
||||||
|
paginationConfig.total = res.data.total;
|
||||||
|
};
|
||||||
|
|
||||||
|
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(deleteMysqlDB, { ids: ids }, 'commons.msg.delete', true);
|
||||||
|
search();
|
||||||
|
};
|
||||||
|
const buttons = [
|
||||||
|
{
|
||||||
|
label: i18n.global.t('commons.button.edit'),
|
||||||
|
icon: 'Delete',
|
||||||
|
click: (row: Cronjob.CronjobInfo) => {
|
||||||
|
onBatchDelete(row);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.global.t('database.backupList') + '(1)',
|
||||||
|
icon: 'Delete',
|
||||||
|
click: (row: Cronjob.CronjobInfo) => {
|
||||||
|
onBatchDelete(row);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.global.t('database.loadBackup'),
|
||||||
|
icon: 'Delete',
|
||||||
|
click: (row: Cronjob.CronjobInfo) => {
|
||||||
|
onBatchDelete(row);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.global.t('commons.button.delete'),
|
||||||
|
icon: 'Delete',
|
||||||
|
click: (row: Cronjob.CronjobInfo) => {
|
||||||
|
onBatchDelete(row);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
search();
|
||||||
|
});
|
||||||
|
</script>
|
96
frontend/src/views/database/redis/index.vue
Normal file
96
frontend/src/views/database/redis/index.vue
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<Submenu activeName="redis" />
|
||||||
|
<ComplexTable
|
||||||
|
:pagination-config="paginationConfig"
|
||||||
|
v-model:selects="selects"
|
||||||
|
@search="search"
|
||||||
|
style="margin-top: 20px"
|
||||||
|
:data="data"
|
||||||
|
>
|
||||||
|
<template #toolbar>
|
||||||
|
<el-button type="primary" @click="onOpenDialog()">{{ $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>
|
||||||
|
<el-table-column type="selection" fix />
|
||||||
|
<el-table-column :label="$t('commons.table.name')" prop="name" />
|
||||||
|
<el-table-column :label="$t('auth.username')" prop="username" />
|
||||||
|
<el-table-column :label="$t('auth.password')" prop="password" />
|
||||||
|
<el-table-column :label="$t('commons.table.description')" prop="description" />
|
||||||
|
<el-table-column
|
||||||
|
prop="createdAt"
|
||||||
|
:label="$t('commons.table.date')"
|
||||||
|
:formatter="dateFromat"
|
||||||
|
show-overflow-tooltip
|
||||||
|
/>
|
||||||
|
<fu-table-operations type="icon" :buttons="buttons" :label="$t('commons.table.operate')" fix />
|
||||||
|
</ComplexTable>
|
||||||
|
|
||||||
|
<OperatrDialog @search="search" ref="dialogRef" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import ComplexTable from '@/components/complex-table/index.vue';
|
||||||
|
import OperatrDialog from '@/views/database/create/index.vue';
|
||||||
|
import Submenu from '@/views/database/index.vue';
|
||||||
|
import { dateFromat } from '@/utils/util';
|
||||||
|
import { onMounted, reactive, ref } from 'vue';
|
||||||
|
import { deleteMysqlDB, searchMysqlDBs } from '@/api/modules/database';
|
||||||
|
import i18n from '@/lang';
|
||||||
|
import { Cronjob } from '@/api/interface/cronjob';
|
||||||
|
import { useDeleteData } from '@/hooks/use-delete-data';
|
||||||
|
|
||||||
|
const selects = ref<any>([]);
|
||||||
|
|
||||||
|
const data = ref();
|
||||||
|
const paginationConfig = reactive({
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
const dialogRef = ref();
|
||||||
|
const onOpenDialog = async () => {
|
||||||
|
dialogRef.value!.acceptParams();
|
||||||
|
};
|
||||||
|
|
||||||
|
const search = async () => {
|
||||||
|
let params = {
|
||||||
|
page: paginationConfig.currentPage,
|
||||||
|
pageSize: paginationConfig.pageSize,
|
||||||
|
};
|
||||||
|
const res = await searchMysqlDBs(params);
|
||||||
|
data.value = res.data.items || [];
|
||||||
|
paginationConfig.total = res.data.total;
|
||||||
|
};
|
||||||
|
|
||||||
|
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(deleteMysqlDB, { ids: ids }, 'commons.msg.delete', true);
|
||||||
|
search();
|
||||||
|
};
|
||||||
|
|
||||||
|
const buttons = [
|
||||||
|
{
|
||||||
|
label: i18n.global.t('commons.button.delete'),
|
||||||
|
icon: 'Delete',
|
||||||
|
click: (row: Cronjob.CronjobInfo) => {
|
||||||
|
onBatchDelete(row);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
search();
|
||||||
|
});
|
||||||
|
</script>
|
Loading…
Add table
Reference in a new issue