mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-09-07 23:25:30 +08:00
feat: 完成主机管理界面
This commit is contained in:
parent
89432bc1b8
commit
0fdf519808
34 changed files with 651 additions and 157 deletions
|
@ -9,7 +9,7 @@ import (
|
|||
)
|
||||
|
||||
func (b *BaseApi) CreateGroup(c *gin.Context) {
|
||||
var req dto.GroupCreate
|
||||
var req dto.GroupOperate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
|
@ -26,17 +26,13 @@ func (b *BaseApi) CreateGroup(c *gin.Context) {
|
|||
}
|
||||
|
||||
func (b *BaseApi) DeleteGroup(c *gin.Context) {
|
||||
var req dto.DeleteByName
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := global.VALID.Struct(req); err != nil {
|
||||
id, err := helper.GetParamID(c)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := groupService.Delete(req.Name); err != nil {
|
||||
if err := groupService.Delete(id); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
@ -44,7 +40,7 @@ func (b *BaseApi) DeleteGroup(c *gin.Context) {
|
|||
}
|
||||
|
||||
func (b *BaseApi) UpdateGroup(c *gin.Context) {
|
||||
var req dto.GroupUpdate
|
||||
var req dto.GroupOperate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
|
@ -59,17 +55,39 @@ func (b *BaseApi) UpdateGroup(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
upMap := make(map[string]interface{})
|
||||
upMap["name"] = req.Name
|
||||
if err := groupService.Update(id, upMap); err != nil {
|
||||
if err := groupService.Update(id, req.Name); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
func (b *BaseApi) GetGroupInfo(c *gin.Context) {
|
||||
id, err := helper.GetParamID(c)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
group, err := groupService.GetGroupInfo(id)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, group)
|
||||
}
|
||||
|
||||
func (b *BaseApi) ListGroup(c *gin.Context) {
|
||||
list, err := groupService.Search()
|
||||
var req dto.GroupSearch
|
||||
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
|
||||
}
|
||||
|
||||
list, err := groupService.List(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
|
|
|
@ -5,11 +5,12 @@ import (
|
|||
"github.com/1Panel-dev/1Panel/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/constant"
|
||||
"github.com/1Panel-dev/1Panel/global"
|
||||
"github.com/1Panel-dev/1Panel/utils/copier"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func (b *BaseApi) CreateHost(c *gin.Context) {
|
||||
var req dto.HostCreate
|
||||
var req dto.HostOperate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
|
@ -42,6 +43,25 @@ func (b *BaseApi) HostTree(c *gin.Context) {
|
|||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
func (b *BaseApi) GetHostInfo(c *gin.Context) {
|
||||
id, err := helper.GetParamID(c)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
host, err := hostService.GetHostInfo(id)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
var hostDto dto.HostInfo
|
||||
if err := copier.Copy(&hostDto, host); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, hostDto)
|
||||
}
|
||||
|
||||
func (b *BaseApi) DeleteHost(c *gin.Context) {
|
||||
var req dto.BatchDeleteReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
|
@ -61,7 +81,7 @@ func (b *BaseApi) DeleteHost(c *gin.Context) {
|
|||
}
|
||||
|
||||
func (b *BaseApi) UpdateHost(c *gin.Context) {
|
||||
var req dto.HostUpdate
|
||||
var req dto.HostOperate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
|
@ -78,7 +98,7 @@ func (b *BaseApi) UpdateHost(c *gin.Context) {
|
|||
|
||||
upMap := make(map[string]interface{})
|
||||
upMap["name"] = req.Name
|
||||
upMap["group"] = req.Group
|
||||
upMap["group_belong"] = req.GroupBelong
|
||||
upMap["addr"] = req.Addr
|
||||
upMap["port"] = req.Port
|
||||
upMap["user"] = req.User
|
||||
|
|
|
@ -32,7 +32,7 @@ func (b *BaseApi) WsSsh(c *gin.Context) {
|
|||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
host, err := hostService.GetConnInfo(uint(id))
|
||||
host, err := hostService.GetHostInfo(uint(id))
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
|
|
|
@ -10,6 +10,7 @@ type CommandUpdate struct {
|
|||
}
|
||||
|
||||
type CommandInfo struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Command string `json:"command"`
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ package dto
|
|||
|
||||
type SearchWithPage struct {
|
||||
PageInfo
|
||||
Name string `json:"name" validate:"required"`
|
||||
Info string `json:"info" validate:"required"`
|
||||
}
|
||||
|
||||
type PageInfo struct {
|
||||
|
|
|
@ -1,10 +1,16 @@
|
|||
package dto
|
||||
|
||||
type GroupCreate struct {
|
||||
type GroupOperate struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
Type string `json:"type" validate:"required"`
|
||||
}
|
||||
|
||||
type GroupUpdate struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
type GroupSearch struct {
|
||||
Type string `json:"type" validate:"required"`
|
||||
}
|
||||
|
||||
type GroupInfo struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
|
|
@ -4,15 +4,15 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
type HostCreate struct {
|
||||
Group string `json:"group" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Addr string `json:"addr" validate:"required,ip"`
|
||||
Port uint `json:"port" validate:"required,number,max=65535,min=1"`
|
||||
User string `json:"user" validate:"required"`
|
||||
AuthMode string `json:"authMode" validate:"oneof=password key"`
|
||||
PrivateKey string `json:"privateKey"`
|
||||
Password string `json:"password"`
|
||||
type HostOperate struct {
|
||||
GroupBelong string `json:"groupBelong" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Addr string `json:"addr" validate:"required,ip"`
|
||||
Port uint `json:"port" validate:"required,number,max=65535,min=1"`
|
||||
User string `json:"user" validate:"required"`
|
||||
AuthMode string `json:"authMode" validate:"oneof=password key"`
|
||||
PrivateKey string `json:"privateKey"`
|
||||
Password string `json:"password"`
|
||||
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
@ -22,36 +22,25 @@ type SearchForTree struct {
|
|||
}
|
||||
|
||||
type HostInfo struct {
|
||||
ID uint `json:"id"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
Group string `json:"group"`
|
||||
Name string `json:"name"`
|
||||
Addr string `json:"addr"`
|
||||
Port uint `json:"port"`
|
||||
User string `json:"user"`
|
||||
AuthMode string `json:"authMode"`
|
||||
ID uint `json:"id"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
GroupBelong string `json:"groupBelong"`
|
||||
Name string `json:"name"`
|
||||
Addr string `json:"addr"`
|
||||
Port uint `json:"port"`
|
||||
User string `json:"user"`
|
||||
AuthMode string `json:"authMode"`
|
||||
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type HostTree struct {
|
||||
ID uint `json:"id"`
|
||||
Label string `json:"label"`
|
||||
Children []TreeChild `json:"children"`
|
||||
}
|
||||
|
||||
type TreeChild struct {
|
||||
ID uint `json:"id"`
|
||||
Label string `json:"label"`
|
||||
}
|
||||
|
||||
type HostUpdate struct {
|
||||
Group string `json:"group" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Addr string `json:"addr" validate:"required,ip"`
|
||||
Port uint `json:"port" validate:"required,number,max=65535,min=1"`
|
||||
User string `json:"user" validate:"required"`
|
||||
AuthMode string `json:"authMode" validate:"oneof=password key"`
|
||||
PrivateKey string `json:"privateKey"`
|
||||
Password string `json:"password"`
|
||||
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
|
|
@ -4,6 +4,6 @@ import "gorm.io/gorm"
|
|||
|
||||
type Command struct {
|
||||
gorm.Model
|
||||
Name string `gorm:"type:varchar(64));unique;not null" json:"name"`
|
||||
Command string `gorm:"type:varchar(256);unique;not null" json:"command"`
|
||||
Name string `gorm:"type:varchar(64);unique;not null" json:"name"`
|
||||
Command string `gorm:"type:varchar(256);not null" json:"command"`
|
||||
}
|
||||
|
|
|
@ -5,5 +5,5 @@ import "gorm.io/gorm"
|
|||
type Group struct {
|
||||
gorm.Model
|
||||
Name string `gorm:"type:varchar(64);not null" json:"name"`
|
||||
Type string `gorm:"type:varchar(16);unique;not null" json:"type"`
|
||||
Type string `gorm:"type:varchar(16);not null" json:"type"`
|
||||
}
|
||||
|
|
|
@ -4,14 +4,14 @@ import "gorm.io/gorm"
|
|||
|
||||
type Host struct {
|
||||
gorm.Model
|
||||
Group string `gorm:"type:varchar(64);not null" json:"group"`
|
||||
Name string `gorm:"type:varchar(64);unique;not null" json:"name"`
|
||||
Addr string `gorm:"type:varchar(16);unique;not null" json:"addr"`
|
||||
Port int `gorm:"type:varchar(5);not null" json:"port"`
|
||||
User string `gorm:"type:varchar(64);not null" json:"user"`
|
||||
AuthMode string `gorm:"type:varchar(16);not null" json:"authMode"`
|
||||
Password string `gorm:"type:varchar(64)" json:"password"`
|
||||
PrivateKey string `gorm:"type:varchar(256)" json:"privateKey"`
|
||||
GroupBelong string `gorm:"type:varchar(64);not null" json:"groupBelong"`
|
||||
Name string `gorm:"type:varchar(64);unique;not null" json:"name"`
|
||||
Addr string `gorm:"type:varchar(16);unique;not null" json:"addr"`
|
||||
Port int `gorm:"type:varchar(5);not null" json:"port"`
|
||||
User string `gorm:"type:varchar(64);not null" json:"user"`
|
||||
AuthMode string `gorm:"type:varchar(16);not null" json:"authMode"`
|
||||
Password string `gorm:"type:varchar(64)" json:"password"`
|
||||
PrivateKey string `gorm:"type:varchar(256)" json:"privateKey"`
|
||||
|
||||
Description string `gorm:"type:varchar(256)" json:"description"`
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package repo
|
|||
import (
|
||||
"github.com/1Panel-dev/1Panel/app/model"
|
||||
"github.com/1Panel-dev/1Panel/global"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type GroupRepo struct{}
|
||||
|
@ -10,6 +11,7 @@ type GroupRepo struct{}
|
|||
type IGroupRepo interface {
|
||||
Get(opts ...DBOption) (model.Group, error)
|
||||
GetList(opts ...DBOption) ([]model.Group, error)
|
||||
WithByType(groupType string) DBOption
|
||||
Create(group *model.Group) error
|
||||
Update(id uint, vars map[string]interface{}) error
|
||||
Delete(opts ...DBOption) error
|
||||
|
@ -39,6 +41,12 @@ func (u *GroupRepo) GetList(opts ...DBOption) ([]model.Group, error) {
|
|||
return groups, err
|
||||
}
|
||||
|
||||
func (c *GroupRepo) WithByType(groupType string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("type = ?", groupType)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *GroupRepo) Create(group *model.Group) error {
|
||||
return global.DB.Create(group).Error
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ type IHostRepo interface {
|
|||
GetList(opts ...DBOption) ([]model.Host, error)
|
||||
WithByInfo(info string) DBOption
|
||||
Create(host *model.Host) error
|
||||
ChangeGroup(oldGroup, newGroup string) error
|
||||
Update(id uint, vars map[string]interface{}) error
|
||||
Delete(opts ...DBOption) error
|
||||
}
|
||||
|
@ -55,6 +56,10 @@ func (u *HostRepo) Create(host *model.Host) error {
|
|||
return global.DB.Create(host).Error
|
||||
}
|
||||
|
||||
func (u *HostRepo) ChangeGroup(oldGroup, newGroup string) error {
|
||||
return global.DB.Model(&model.Host{}).Where("group_belong = ?", oldGroup).Updates(map[string]interface{}{"group_belong": newGroup}).Error
|
||||
}
|
||||
|
||||
func (u *HostRepo) Update(id uint, vars map[string]interface{}) error {
|
||||
return global.DB.Model(&model.Host{}).Where("id = ?", id).Updates(vars).Error
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ func (u *CommandService) Search() ([]model.Command, error) {
|
|||
}
|
||||
|
||||
func (u *CommandService) SearchWithPage(search dto.SearchWithPage) (int64, interface{}, error) {
|
||||
total, commands, err := commandRepo.Page(search.Page, search.PageSize, commonRepo.WithLikeName(search.Name))
|
||||
total, commands, err := commandRepo.Page(search.Page, search.PageSize, commonRepo.WithLikeName(search.Info))
|
||||
var dtoCommands []dto.CommandInfo
|
||||
for _, command := range commands {
|
||||
var item dto.CommandInfo
|
||||
|
|
|
@ -2,7 +2,6 @@ package service
|
|||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/app/model"
|
||||
"github.com/1Panel-dev/1Panel/constant"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/pkg/errors"
|
||||
|
@ -11,25 +10,46 @@ import (
|
|||
type GroupService struct{}
|
||||
|
||||
type IGroupService interface {
|
||||
Search() ([]model.Group, error)
|
||||
Create(groupDto dto.GroupCreate) error
|
||||
Update(id uint, upMap map[string]interface{}) error
|
||||
Delete(name string) error
|
||||
GetGroupInfo(id uint) (*dto.GroupInfo, error)
|
||||
List(req dto.GroupSearch) ([]dto.GroupInfo, error)
|
||||
Create(groupDto dto.GroupOperate) error
|
||||
Update(id uint, name string) error
|
||||
Delete(id uint) error
|
||||
}
|
||||
|
||||
func NewIGroupService() IGroupService {
|
||||
return &GroupService{}
|
||||
}
|
||||
|
||||
func (u *GroupService) Search() ([]model.Group, error) {
|
||||
groups, err := groupRepo.GetList()
|
||||
func (u *GroupService) GetGroupInfo(id uint) (*dto.GroupInfo, error) {
|
||||
group, err := groupRepo.Get(commonRepo.WithByID(id))
|
||||
if err != nil {
|
||||
return nil, constant.ErrRecordNotFound
|
||||
}
|
||||
return groups, err
|
||||
var dtoGroup dto.GroupInfo
|
||||
if err := copier.Copy(&dtoGroup, &group); err != nil {
|
||||
return nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
return &dtoGroup, err
|
||||
}
|
||||
|
||||
func (u *GroupService) Create(groupDto dto.GroupCreate) error {
|
||||
func (u *GroupService) List(req dto.GroupSearch) ([]dto.GroupInfo, error) {
|
||||
groups, err := groupRepo.GetList(groupRepo.WithByType(req.Type))
|
||||
if err != nil {
|
||||
return nil, constant.ErrRecordNotFound
|
||||
}
|
||||
var dtoUsers []dto.GroupInfo
|
||||
for _, group := range groups {
|
||||
var item dto.GroupInfo
|
||||
if err := copier.Copy(&item, &group); err != nil {
|
||||
return nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
dtoUsers = append(dtoUsers, item)
|
||||
}
|
||||
return dtoUsers, err
|
||||
}
|
||||
|
||||
func (u *GroupService) Create(groupDto dto.GroupOperate) error {
|
||||
group, _ := groupRepo.Get(commonRepo.WithByName(groupDto.Name), commonRepo.WithByName(groupDto.Name))
|
||||
if group.ID != 0 {
|
||||
return constant.ErrRecordExist
|
||||
|
@ -43,14 +63,27 @@ func (u *GroupService) Create(groupDto dto.GroupCreate) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (u *GroupService) Delete(name string) error {
|
||||
group, _ := groupRepo.Get(commonRepo.WithByName(name))
|
||||
func (u *GroupService) Delete(id uint) error {
|
||||
group, _ := groupRepo.Get(commonRepo.WithByID(id))
|
||||
if group.ID == 0 {
|
||||
return constant.ErrRecordNotFound
|
||||
}
|
||||
return groupRepo.Delete(commonRepo.WithByID(group.ID))
|
||||
if err := hostRepo.ChangeGroup(group.Name, "default"); err != nil {
|
||||
return err
|
||||
}
|
||||
return groupRepo.Delete(commonRepo.WithByID(id))
|
||||
}
|
||||
|
||||
func (u *GroupService) Update(id uint, upMap map[string]interface{}) error {
|
||||
func (u *GroupService) Update(id uint, name string) error {
|
||||
group, _ := groupRepo.Get(commonRepo.WithByID(id))
|
||||
if group.ID == 0 {
|
||||
return constant.ErrRecordNotFound
|
||||
}
|
||||
|
||||
upMap := make(map[string]interface{})
|
||||
upMap["name"] = name
|
||||
if err := hostRepo.ChangeGroup(group.Name, name); err != nil {
|
||||
return err
|
||||
}
|
||||
return groupRepo.Update(id, upMap)
|
||||
}
|
||||
|
|
|
@ -13,9 +13,9 @@ import (
|
|||
type HostService struct{}
|
||||
|
||||
type IHostService interface {
|
||||
GetConnInfo(id uint) (*model.Host, error)
|
||||
GetHostInfo(id uint) (*model.Host, error)
|
||||
SearchForTree(search dto.SearchForTree) ([]dto.HostTree, error)
|
||||
Create(hostDto dto.HostCreate) (*dto.HostInfo, error)
|
||||
Create(hostDto dto.HostOperate) (*dto.HostInfo, error)
|
||||
Update(id uint, upMap map[string]interface{}) error
|
||||
BatchDelete(ids []uint) error
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ func NewIHostService() IHostService {
|
|||
return &HostService{}
|
||||
}
|
||||
|
||||
func (u *HostService) GetConnInfo(id uint) (*model.Host, error) {
|
||||
func (u *HostService) GetHostInfo(id uint) (*model.Host, error) {
|
||||
host, err := hostRepo.Get(commonRepo.WithByID(id))
|
||||
if err != nil {
|
||||
return nil, constant.ErrRecordNotFound
|
||||
|
@ -34,26 +34,30 @@ func (u *HostService) GetConnInfo(id uint) (*model.Host, error) {
|
|||
|
||||
func (u *HostService) SearchForTree(search dto.SearchForTree) ([]dto.HostTree, error) {
|
||||
hosts, err := hostRepo.GetList(hostRepo.WithByInfo(search.Info))
|
||||
distinctMap := make(map[string][]string)
|
||||
for _, host := range hosts {
|
||||
if _, ok := distinctMap[host.Group]; !ok {
|
||||
distinctMap[host.Group] = []string{fmt.Sprintf("%s@%s:%d", host.User, host.Addr, host.Port)}
|
||||
} else {
|
||||
distinctMap[host.Group] = append(distinctMap[host.Group], fmt.Sprintf("%s@%s:%d", host.User, host.Addr, host.Port))
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var data []dto.HostTree
|
||||
for key, value := range distinctMap {
|
||||
var children []dto.TreeChild
|
||||
for _, label := range value {
|
||||
children = append(children, dto.TreeChild{Label: label})
|
||||
}
|
||||
data = append(data, dto.HostTree{Label: key, Children: children})
|
||||
groups, err := groupRepo.GetList()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data, err
|
||||
var datas []dto.HostTree
|
||||
for _, group := range groups {
|
||||
var data dto.HostTree
|
||||
data.ID = group.ID + 10000
|
||||
data.Label = group.Name
|
||||
for _, host := range hosts {
|
||||
label := fmt.Sprintf("%s@%s:%d", host.User, host.Addr, host.Port)
|
||||
if host.GroupBelong == group.Name {
|
||||
data.Children = append(data.Children, dto.TreeChild{ID: host.ID, Label: label})
|
||||
}
|
||||
}
|
||||
datas = append(datas, data)
|
||||
}
|
||||
return datas, err
|
||||
}
|
||||
|
||||
func (u *HostService) Create(hostDto dto.HostCreate) (*dto.HostInfo, error) {
|
||||
func (u *HostService) Create(hostDto dto.HostOperate) (*dto.HostInfo, error) {
|
||||
host, _ := hostRepo.Get(commonRepo.WithByName(hostDto.Name))
|
||||
if host.ID != 0 {
|
||||
return nil, constant.ErrRecordExist
|
||||
|
|
|
@ -44,7 +44,7 @@ func (u *UserService) Get(id uint) (*dto.UserInfo, error) {
|
|||
}
|
||||
|
||||
func (u *UserService) Page(search dto.SearchWithPage) (int64, interface{}, error) {
|
||||
total, users, err := userRepo.Page(search.Page, search.PageSize, commonRepo.WithLikeName(search.Name))
|
||||
total, users, err := userRepo.Page(search.Page, search.PageSize, commonRepo.WithLikeName(search.Info))
|
||||
var dtoUsers []dto.UserInfo
|
||||
for _, user := range users {
|
||||
var item dto.UserInfo
|
||||
|
|
|
@ -35,6 +35,15 @@ var AddTableOperationLog = &gormigrate.Migration{
|
|||
var AddTableHost = &gormigrate.Migration{
|
||||
ID: "20200818-add-table-host",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
return tx.AutoMigrate(&model.Host{})
|
||||
if err := tx.AutoMigrate(&model.Host{}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.AutoMigrate(&model.Group{}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.AutoMigrate(&model.Command{}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
|
|
@ -40,6 +40,8 @@ func Routers() *gin.Engine {
|
|||
systemRouter.InitBaseRouter(PrivateGroup)
|
||||
systemRouter.InitUserRouter(PrivateGroup)
|
||||
systemRouter.InitHostRouter(PrivateGroup)
|
||||
systemRouter.InitGroupRouter(PrivateGroup)
|
||||
systemRouter.InitCommandRouter(PrivateGroup)
|
||||
systemRouter.InitTerminalRouter(PrivateGroup)
|
||||
systemRouter.InitOperationLogRouter(PrivateGroup)
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@ type RouterGroup struct {
|
|||
BaseRouter
|
||||
UserRouter
|
||||
HostRouter
|
||||
GroupRouter
|
||||
CommandRouter
|
||||
OperationLogRouter
|
||||
}
|
||||
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
package router
|
||||
|
||||
import (
|
||||
v1 "github.com/1Panel-dev/1Panel/app/api/v1"
|
||||
"github.com/1Panel-dev/1Panel/middleware"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type GroupRouter struct{}
|
||||
|
||||
func (s *GroupRouter) InitGroupRouter(Router *gin.RouterGroup) {
|
||||
userRouter := Router.Group("group")
|
||||
userRouter.Use(middleware.JwtAuth()).Use(middleware.SessionAuth())
|
||||
withRecordRouter := userRouter.Use(middleware.OperationRecord())
|
||||
baseApi := v1.ApiGroupApp.BaseApi
|
||||
{
|
||||
withRecordRouter.POST("", baseApi.CreateGroup)
|
||||
withRecordRouter.POST("/del", baseApi.DeleteGroup)
|
||||
userRouter.GET("", baseApi.ListGroup)
|
||||
userRouter.PUT(":id", baseApi.UpdateGroup)
|
||||
}
|
||||
}
|
|
@ -17,8 +17,8 @@ func (s *CommandRouter) InitCommandRouter(Router *gin.RouterGroup) {
|
|||
{
|
||||
withRecordRouter.POST("", baseApi.CreateCommand)
|
||||
withRecordRouter.POST("/del", baseApi.DeleteCommand)
|
||||
withRecordRouter.PUT(":id", baseApi.UpdateCommand)
|
||||
userRouter.POST("/search", baseApi.SearchCommand)
|
||||
userRouter.GET("", baseApi.ListCommand)
|
||||
userRouter.PUT(":id", baseApi.UpdateCommand)
|
||||
}
|
||||
}
|
23
backend/router/ro_group.go
Normal file
23
backend/router/ro_group.go
Normal file
|
@ -0,0 +1,23 @@
|
|||
package router
|
||||
|
||||
import (
|
||||
v1 "github.com/1Panel-dev/1Panel/app/api/v1"
|
||||
"github.com/1Panel-dev/1Panel/middleware"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type GroupRouter struct{}
|
||||
|
||||
func (s *GroupRouter) InitGroupRouter(Router *gin.RouterGroup) {
|
||||
userRouter := Router.Group("groups").Use(middleware.JwtAuth()).Use(middleware.SessionAuth())
|
||||
withRecordRouter := Router.Group("groups").Use(middleware.JwtAuth()).Use(middleware.SessionAuth()).Use(middleware.OperationRecord())
|
||||
baseApi := v1.ApiGroupApp.BaseApi
|
||||
{
|
||||
withRecordRouter.POST("", baseApi.CreateGroup)
|
||||
withRecordRouter.DELETE(":id", baseApi.DeleteGroup)
|
||||
userRouter.POST("/search", baseApi.ListGroup)
|
||||
userRouter.GET(":id", baseApi.GetGroupInfo)
|
||||
userRouter.PUT(":id", baseApi.UpdateGroup)
|
||||
}
|
||||
}
|
|
@ -10,14 +10,14 @@ import (
|
|||
type HostRouter struct{}
|
||||
|
||||
func (s *HostRouter) InitHostRouter(Router *gin.RouterGroup) {
|
||||
userRouter := Router.Group("hosts")
|
||||
userRouter.Use(middleware.JwtAuth()).Use(middleware.SessionAuth())
|
||||
withRecordRouter := userRouter.Use(middleware.OperationRecord())
|
||||
hostRouter := Router.Group("hosts").Use(middleware.JwtAuth()).Use(middleware.SessionAuth())
|
||||
withRecordRouter := Router.Group("hosts").Use(middleware.JwtAuth()).Use(middleware.SessionAuth()).Use(middleware.OperationRecord())
|
||||
baseApi := v1.ApiGroupApp.BaseApi
|
||||
{
|
||||
withRecordRouter.POST("", baseApi.CreateHost)
|
||||
withRecordRouter.POST("/del", baseApi.DeleteHost)
|
||||
userRouter.POST("/search", baseApi.HostTree)
|
||||
userRouter.PUT(":id", baseApi.UpdateHost)
|
||||
withRecordRouter.DELETE(":id", baseApi.DeleteHost)
|
||||
hostRouter.POST("/search", baseApi.HostTree)
|
||||
hostRouter.GET(":id", baseApi.GetHostInfo)
|
||||
hostRouter.PUT(":id", baseApi.UpdateHost)
|
||||
}
|
||||
}
|
||||
|
|
17
frontend/src/api/interface/command.ts
Normal file
17
frontend/src/api/interface/command.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { ReqPage } from '.';
|
||||
|
||||
export namespace Command {
|
||||
export interface CommandInfo {
|
||||
id: number;
|
||||
name: string;
|
||||
command: string;
|
||||
}
|
||||
export interface CommandOperate {
|
||||
id: number;
|
||||
name: string;
|
||||
command: string;
|
||||
}
|
||||
export interface CommandSearch extends ReqPage {
|
||||
info: string;
|
||||
}
|
||||
}
|
15
frontend/src/api/interface/group.ts
Normal file
15
frontend/src/api/interface/group.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
export namespace Group {
|
||||
export interface GroupInfo {
|
||||
id: number;
|
||||
name: string;
|
||||
type: string;
|
||||
}
|
||||
export interface GroupOperate {
|
||||
id: number;
|
||||
name: string;
|
||||
type: string;
|
||||
}
|
||||
export interface GroupSearch {
|
||||
type: string;
|
||||
}
|
||||
}
|
|
@ -2,11 +2,17 @@ import { CommonModel } from '.';
|
|||
|
||||
export namespace Host {
|
||||
export interface HostTree {
|
||||
id: number;
|
||||
label: string;
|
||||
children: Array<TreeNode>;
|
||||
}
|
||||
export interface TreeNode {
|
||||
id: number;
|
||||
label: string;
|
||||
children: Array<string>;
|
||||
}
|
||||
export interface Host extends CommonModel {
|
||||
name: string;
|
||||
groupBelong: string;
|
||||
addr: string;
|
||||
port: number;
|
||||
user: string;
|
||||
|
@ -16,6 +22,7 @@ export namespace Host {
|
|||
export interface HostOperate {
|
||||
id: number;
|
||||
name: string;
|
||||
groupBelong: string;
|
||||
addr: string;
|
||||
port: number;
|
||||
user: string;
|
||||
|
|
|
@ -12,7 +12,7 @@ export namespace User {
|
|||
}
|
||||
|
||||
export interface ReqGetUserParams extends ReqPage {
|
||||
name?: string;
|
||||
info?: string;
|
||||
email?: string;
|
||||
}
|
||||
}
|
||||
|
|
24
frontend/src/api/modules/command.ts
Normal file
24
frontend/src/api/modules/command.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
import http from '@/api';
|
||||
import { ResPage } from '../interface';
|
||||
import { Command } from '../interface/command';
|
||||
|
||||
export const getCommandList = () => {
|
||||
return http.get<Array<Command.CommandInfo>>(`/commands`, {});
|
||||
};
|
||||
|
||||
export const getCommandPage = (params: Command.CommandSearch) => {
|
||||
return http.post<ResPage<Command.CommandInfo>>(`/commands/search`, params);
|
||||
};
|
||||
|
||||
export const addCommand = (params: Command.CommandOperate) => {
|
||||
return http.post<Command.CommandOperate>(`/commands`, params);
|
||||
};
|
||||
|
||||
export const editCommand = (params: Command.CommandOperate) => {
|
||||
console.log(params.id);
|
||||
return http.put(`/commands/${params.id}`, params);
|
||||
};
|
||||
|
||||
export const deleteCommand = (params: { ids: number[] }) => {
|
||||
return http.post(`/commands/del`, params);
|
||||
};
|
18
frontend/src/api/modules/group.ts
Normal file
18
frontend/src/api/modules/group.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import http from '@/api';
|
||||
import { Group } from '../interface/group';
|
||||
|
||||
export const getGroupList = (params: Group.GroupSearch) => {
|
||||
return http.post<Array<Group.GroupInfo>>(`/groups/search`, params);
|
||||
};
|
||||
|
||||
export const addGroup = (params: Group.GroupOperate) => {
|
||||
return http.post<Group.GroupOperate>(`/groups`, params);
|
||||
};
|
||||
|
||||
export const editGroup = (params: Group.GroupOperate) => {
|
||||
return http.put(`/groups/` + params.id, params);
|
||||
};
|
||||
|
||||
export const deleteGroup = (id: number) => {
|
||||
return http.delete(`/groups/` + id);
|
||||
};
|
|
@ -5,6 +5,10 @@ export const getHostList = (params: Host.ReqSearch) => {
|
|||
return http.post<Array<Host.HostTree>>(`/hosts/search`, params);
|
||||
};
|
||||
|
||||
export const getHostInfo = (id: number) => {
|
||||
return http.get<Host.Host>(`/hosts/` + id);
|
||||
};
|
||||
|
||||
export const addHost = (params: Host.HostOperate) => {
|
||||
return http.post<Host.HostOperate>(`/hosts`, params);
|
||||
};
|
||||
|
@ -14,6 +18,6 @@ export const editHost = (params: Host.HostOperate) => {
|
|||
return http.put(`/hosts/` + params.id, params);
|
||||
};
|
||||
|
||||
export const deleteHost = (params: { ids: number[] }) => {
|
||||
return http.post(`/hosts/del`, params);
|
||||
export const deleteHost = (id: number) => {
|
||||
return http.delete(`/hosts/` + id);
|
||||
};
|
||||
|
|
|
@ -13,11 +13,13 @@ export default {
|
|||
},
|
||||
table: {
|
||||
name: '名称',
|
||||
group: '组',
|
||||
createdAt: '创建时间',
|
||||
date: '时间',
|
||||
updatedAt: '更新时间',
|
||||
operate: '操作',
|
||||
message: '信息',
|
||||
description: '描述信息',
|
||||
},
|
||||
msg: {
|
||||
delete: '此操作不可回滚,是否继续',
|
||||
|
@ -94,6 +96,7 @@ export default {
|
|||
conn: '连接',
|
||||
hostList: '主机信息',
|
||||
quickCmd: '快捷命令',
|
||||
command: '命令',
|
||||
addHost: '添加主机',
|
||||
localhost: '本地服务器',
|
||||
name: '名称',
|
||||
|
@ -110,6 +113,8 @@ export default {
|
|||
detail: {
|
||||
users: '用户',
|
||||
hosts: '主机',
|
||||
groups: '组',
|
||||
command: '快捷命令',
|
||||
auth: '用户',
|
||||
post: '创建',
|
||||
put: '更新',
|
||||
|
|
|
@ -92,7 +92,7 @@ const search = async () => {
|
|||
};
|
||||
|
||||
const fmtOperation = (row: ResOperationLog) => {
|
||||
if (row.method.toLocaleLowerCase() !== 'put') {
|
||||
if (row.method.toLocaleLowerCase() === 'post') {
|
||||
if (row.source == '' && row.action == '') {
|
||||
return (
|
||||
i18n.global.t('operations.detail.' + row.group.toLocaleLowerCase()) +
|
||||
|
@ -118,7 +118,6 @@ const fmtOperation = (row: ResOperationLog) => {
|
|||
i18n.global.t('operations.detail.' + row.source.toLocaleLowerCase())
|
||||
);
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
const fmtBody = (value: string) => {
|
||||
|
|
154
frontend/src/views/terminal/command/index.vue
Normal file
154
frontend/src/views/terminal/command/index.vue
Normal file
|
@ -0,0 +1,154 @@
|
|||
<template>
|
||||
<div>
|
||||
<ComplexTable :pagination-config="paginationConfig" v-model:selects="selects" :data="data" @search="search">
|
||||
<template #toolbar>
|
||||
<el-button @click="onCreate()">{{ $t('commons.button.create') }}</el-button>
|
||||
<el-button type="danger" plain :disabled="selects.length === 0" @click="batchDelete(null)">{{
|
||||
$t('commons.button.delete')
|
||||
}}</el-button>
|
||||
</template>
|
||||
<el-table-column type="selection" fix />
|
||||
<el-table-column :label="$t('commons.table.name')" min-width="100" prop="name" fix />
|
||||
<el-table-column :label="$t('terminal.command')" min-width="300" show-overflow-tooltip prop="command" />
|
||||
<fu-table-operations type="icon" :buttons="buttons" :label="$t('commons.table.operate')" fix />
|
||||
</ComplexTable>
|
||||
|
||||
<el-dialog v-model="cmdVisiable" :title="$t('terminal.addHost')" width="30%">
|
||||
<el-form ref="commandInfoRef" label-width="100px" label-position="left" :model="commandInfo" :rules="rules">
|
||||
<el-form-item :label="$t('commons.table.name')" prop="name">
|
||||
<el-input clearable v-model="commandInfo.name" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('terminal.command')" prop="command">
|
||||
<el-input type="textarea" clearable v-model="commandInfo.command" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="cmdVisiable = false">{{ $t('commons.button.cancel') }}</el-button>
|
||||
<el-button type="primary" @click="submitAddCommand(commandInfoRef)">
|
||||
{{ $t('commons.button.confirm') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import ComplexTable from '@/components/complex-table/index.vue';
|
||||
import { Command } from '@/api/interface/command';
|
||||
import { addCommand, editCommand, deleteCommand, getCommandPage } from '@/api/modules/command';
|
||||
import { onMounted, reactive, ref } from '@vue/runtime-core';
|
||||
import { useDeleteData } from '@/hooks/use-delete-data';
|
||||
import type { ElForm } from 'element-plus';
|
||||
import { Rules } from '@/global/form-rues';
|
||||
import i18n from '@/lang';
|
||||
import { ElMessage } from 'element-plus';
|
||||
|
||||
const data = ref();
|
||||
const selects = ref<any>([]);
|
||||
const paginationConfig = reactive({
|
||||
page: 1,
|
||||
pageSize: 5,
|
||||
total: 0,
|
||||
});
|
||||
const commandSearch = reactive({
|
||||
page: 1,
|
||||
pageSize: 5,
|
||||
info: '',
|
||||
});
|
||||
type FormInstance = InstanceType<typeof ElForm>;
|
||||
const commandInfoRef = ref<FormInstance>();
|
||||
const rules = reactive({
|
||||
name: [Rules.requiredInput],
|
||||
command: [Rules.requiredInput],
|
||||
});
|
||||
let operate = ref<string>('create');
|
||||
|
||||
let commandInfo = reactive<Command.CommandOperate>({
|
||||
id: 0,
|
||||
name: '',
|
||||
command: '',
|
||||
});
|
||||
|
||||
const cmdVisiable = ref<boolean>(false);
|
||||
|
||||
const onCreate = async () => {
|
||||
restcommandForm();
|
||||
operate.value = 'create';
|
||||
cmdVisiable.value = true;
|
||||
};
|
||||
|
||||
const submitAddCommand = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return;
|
||||
formEl.validate(async (valid) => {
|
||||
if (!valid) return;
|
||||
console.log(commandInfo.id);
|
||||
if (operate.value === 'create') {
|
||||
await addCommand(commandInfo);
|
||||
} else {
|
||||
await editCommand(commandInfo);
|
||||
}
|
||||
cmdVisiable.value = false;
|
||||
search();
|
||||
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
||||
});
|
||||
};
|
||||
|
||||
const onEdit = async (row: Command.CommandInfo | null) => {
|
||||
if (row !== null) {
|
||||
console.log(row.id);
|
||||
// commandInfo.id = row.id;
|
||||
// commandInfo.name = row.name;
|
||||
// commandInfo.command = row.command;
|
||||
// operate.value = 'edit';
|
||||
// cmdVisiable.value = true;
|
||||
}
|
||||
};
|
||||
|
||||
const batchDelete = async (row: Command.CommandInfo | null) => {
|
||||
let ids: Array<number> = [];
|
||||
if (row === null) {
|
||||
selects.value.forEach((item: Command.CommandInfo) => {
|
||||
ids.push(item.id);
|
||||
});
|
||||
} else {
|
||||
ids.push(row.id);
|
||||
}
|
||||
await useDeleteData(deleteCommand, { ids: ids }, 'commons.msg.delete');
|
||||
search();
|
||||
};
|
||||
|
||||
function restcommandForm() {
|
||||
if (commandInfoRef.value) {
|
||||
commandInfoRef.value.resetFields();
|
||||
}
|
||||
}
|
||||
const buttons = [
|
||||
{
|
||||
label: i18n.global.t('commons.button.edit'),
|
||||
icon: 'Edit',
|
||||
click: onEdit,
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('commons.button.delete'),
|
||||
icon: 'Delete',
|
||||
click: batchDelete,
|
||||
},
|
||||
];
|
||||
|
||||
const search = async () => {
|
||||
commandSearch.page = paginationConfig.page;
|
||||
commandSearch.pageSize = paginationConfig.pageSize;
|
||||
const res = await getCommandPage(commandSearch);
|
||||
data.value = res.data.items;
|
||||
for (const d of data.value) {
|
||||
d.id = d.id + '';
|
||||
}
|
||||
paginationConfig.total = res.data.total;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
search();
|
||||
});
|
||||
</script>
|
|
@ -2,22 +2,64 @@
|
|||
<el-row style="margin: 20px; margin-left: 20px" class="row-box" :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-card class="el-card">
|
||||
<el-button icon="Plus" @click="readyForCreate" size="small" />
|
||||
<el-button icon="FolderAdd" @click="(folderCreate = true), (newGroupName = '')" size="small" />
|
||||
<el-button icon="Expand" @click="setTreeStatus(true)" size="small" />
|
||||
<el-button icon="Fold" @click="setTreeStatus(false)" size="small" />
|
||||
<el-input size="small" @input="loadHost" clearable style="margin-top: 5px" v-model="searcConfig.info">
|
||||
<template #append><el-button icon="search" @click="loadHost" /></template>
|
||||
<el-tooltip class="box-item" effect="dark" content="创建连接" placement="top-start">
|
||||
<el-button icon="Plus" @click="restHostForm" size="small" />
|
||||
</el-tooltip>
|
||||
<el-tooltip class="box-item" effect="dark" content="创建分组" placement="top-start">
|
||||
<el-button icon="FolderAdd" @click="onGroupCreate" size="small" />
|
||||
</el-tooltip>
|
||||
<el-tooltip class="box-item" effect="dark" content="展开" placement="top-start">
|
||||
<el-button icon="Expand" @click="setTreeStatus(true)" size="small" />
|
||||
</el-tooltip>
|
||||
<el-tooltip class="box-item" effect="dark" content="收缩" placement="top-start">
|
||||
<el-button icon="Fold" @click="setTreeStatus(false)" size="small" />
|
||||
</el-tooltip>
|
||||
<el-input
|
||||
size="small"
|
||||
@input="loadHostTree"
|
||||
clearable
|
||||
style="margin-top: 5px"
|
||||
v-model="searcConfig.info"
|
||||
>
|
||||
<template #append><el-button icon="search" @click="loadHostTree" /></template>
|
||||
</el-input>
|
||||
<el-input size="small" v-if="folderCreate" clearable style="margin-top: 5px" v-model="newGroupName">
|
||||
<el-input
|
||||
size="small"
|
||||
v-if="groupInputShow"
|
||||
clearable
|
||||
style="margin-top: 5px"
|
||||
v-model="groupInputValue"
|
||||
>
|
||||
<template #append>
|
||||
<el-button-group>
|
||||
<el-button icon="Check" @click="loadHost" />
|
||||
<el-button icon="Close" @click="folderCreate = false" />
|
||||
<el-button icon="Check" @click="onCreateGroup" />
|
||||
<el-button icon="Close" @click="groupInputShow = false" />
|
||||
</el-button-group>
|
||||
</template>
|
||||
</el-input>
|
||||
<el-tree ref="tree" :default-expand-all="true" :data="hostTree" :props="defaultProps" />
|
||||
<el-tree
|
||||
ref="tree"
|
||||
:expand-on-click-node="false"
|
||||
node-key="id"
|
||||
:default-expand-all="true"
|
||||
:data="hostTree"
|
||||
:props="defaultProps"
|
||||
draggable
|
||||
>
|
||||
<template #default="{ node, data }">
|
||||
<span class="custom-tree-node" @mouseover="hover = data.id" @mouseleave="hover = null">
|
||||
<span>
|
||||
<a @click="onEdit(node, data)">{{ node.label }}</a>
|
||||
</span>
|
||||
<el-button-group
|
||||
v-if="!(node.level === 1 && data.label === 'default') && data.id === hover"
|
||||
>
|
||||
<el-button icon="Edit" size="small" @click="onEdit(node, data)" />
|
||||
<el-button icon="Delete" size="small" @click="onDelete(node, data)" />
|
||||
</el-button-group>
|
||||
</span>
|
||||
</template>
|
||||
</el-tree>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="16">
|
||||
|
@ -26,6 +68,11 @@
|
|||
<el-form-item :label="$t('commons.table.name')" prop="name">
|
||||
<el-input clearable v-model="hostInfo.name" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('commons.table.group')" prop="groupBelong">
|
||||
<el-select v-model="hostInfo.groupBelong" clearable style="width: 100%">
|
||||
<el-option v-for="item in groupList" :key="item.id" :label="item.name" :value="item.name" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="IP" prop="addr">
|
||||
<el-input clearable v-model="hostInfo.addr" />
|
||||
</el-form-item>
|
||||
|
@ -51,10 +98,19 @@
|
|||
<el-form-item :label="$t('terminal.key')" v-if="hostInfo.authMode === 'key'" prop="privateKey">
|
||||
<el-input clearable type="textarea" v-model="hostInfo.privateKey" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('commons.table.description')" prop="description">
|
||||
<el-input clearable type="textarea" v-model="hostInfo.description" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="submitAddHost(hostInfoRef)">
|
||||
<el-button @click="restHostForm">
|
||||
{{ $t('commons.button.reset') }}
|
||||
</el-button>
|
||||
<el-button v-if="hostOperation === 'create'" type="primary" @click="submitAddHost(hostInfoRef)">
|
||||
{{ $t('commons.button.create') }}
|
||||
</el-button>
|
||||
<el-button v-if="hostOperation === 'edit'" type="primary" @click="submitAddHost(hostInfoRef)">
|
||||
{{ $t('commons.button.confirm') }}
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
@ -67,14 +123,19 @@ import { ref, reactive, onMounted } from 'vue';
|
|||
import type { ElForm } from 'element-plus';
|
||||
import { Rules } from '@/global/form-rues';
|
||||
import { Host } from '@/api/interface/host';
|
||||
import { getHostList, addHost } from '@/api/modules/host';
|
||||
import { Group } from '@/api/interface/group';
|
||||
import { getHostList, getHostInfo, addHost, editHost, deleteHost } from '@/api/modules/host';
|
||||
import { getGroupList, addGroup, editGroup, deleteGroup } from '@/api/modules/group';
|
||||
import { useDeleteData } from '@/hooks/use-delete-data';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import i18n from '@/lang';
|
||||
import type Node from 'element-plus/es/components/tree/src/model/node';
|
||||
|
||||
type FormInstance = InstanceType<typeof ElForm>;
|
||||
const hostInfoRef = ref<FormInstance>();
|
||||
const rules = reactive({
|
||||
name: [Rules.requiredInput, Rules.name],
|
||||
group: [Rules.requiredSelect],
|
||||
addr: [Rules.requiredInput, Rules.ip],
|
||||
port: [Rules.requiredInput, Rules.port],
|
||||
user: [Rules.requiredInput],
|
||||
|
@ -82,10 +143,11 @@ const rules = reactive({
|
|||
password: [Rules.requiredInput],
|
||||
privateKey: [Rules.requiredInput],
|
||||
});
|
||||
|
||||
let hostOperation = ref<string>('create');
|
||||
let hostInfo = reactive<Host.HostOperate>({
|
||||
id: 0,
|
||||
name: '',
|
||||
groupBelong: 'default',
|
||||
addr: '',
|
||||
port: 22,
|
||||
user: '',
|
||||
|
@ -95,30 +157,47 @@ let hostInfo = reactive<Host.HostOperate>({
|
|||
description: '',
|
||||
});
|
||||
|
||||
interface Tree {
|
||||
id: number;
|
||||
label: string;
|
||||
children?: Tree[];
|
||||
}
|
||||
|
||||
let searcConfig = reactive<Host.ReqSearch>({
|
||||
info: '',
|
||||
});
|
||||
const tree = ref<any>(null);
|
||||
const hover = ref();
|
||||
const hostTree = ref<Array<Host.HostTree>>();
|
||||
const defaultProps = {
|
||||
children: 'children',
|
||||
label: 'label',
|
||||
children: 'children',
|
||||
};
|
||||
const newGroupName = ref();
|
||||
const folderCreate = ref<boolean>(false);
|
||||
|
||||
const loadHost = async () => {
|
||||
const groupList = ref<Array<Group.GroupInfo>>();
|
||||
|
||||
let groupInputValue = ref();
|
||||
let currentGroupID = ref();
|
||||
let groupOperation = ref<string>('create');
|
||||
let groupInputShow = ref<boolean>(false);
|
||||
|
||||
const loadHostTree = async () => {
|
||||
const res = await getHostList(searcConfig);
|
||||
hostTree.value = res.data;
|
||||
};
|
||||
|
||||
const loadGroups = async () => {
|
||||
const res = await getGroupList({ type: 'host' });
|
||||
groupList.value = res.data;
|
||||
};
|
||||
|
||||
function setTreeStatus(expend: boolean) {
|
||||
for (let i = 0; i < tree.value.store._getAllNodes().length; i++) {
|
||||
tree.value.store._getAllNodes()[i].expanded = expend;
|
||||
}
|
||||
}
|
||||
|
||||
function readyForCreate() {
|
||||
function restHostForm() {
|
||||
if (hostInfoRef.value) {
|
||||
hostInfoRef.value.resetFields();
|
||||
}
|
||||
|
@ -128,17 +207,92 @@ const submitAddHost = (formEl: FormInstance | undefined) => {
|
|||
if (!formEl) return;
|
||||
formEl.validate(async (valid) => {
|
||||
if (!valid) return;
|
||||
try {
|
||||
if (hostOperation.value === 'create') {
|
||||
await addHost(hostInfo);
|
||||
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
||||
// loadHost();
|
||||
} catch (error) {
|
||||
ElMessage.success(i18n.global.t('commons.msg.loginSuccess') + ':' + error);
|
||||
} else {
|
||||
await editHost(hostInfo);
|
||||
}
|
||||
restHostForm();
|
||||
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
||||
loadHostTree();
|
||||
});
|
||||
};
|
||||
|
||||
const onGroupCreate = () => {
|
||||
groupInputShow.value = true;
|
||||
groupInputValue.value = '';
|
||||
groupOperation.value = 'create';
|
||||
};
|
||||
const onCreateGroup = async () => {
|
||||
console.log(groupOperation.value);
|
||||
if (groupOperation.value === 'create') {
|
||||
let group = { id: 0, name: groupInputValue.value, type: 'host' };
|
||||
await addGroup(group);
|
||||
groupOperation.value = '';
|
||||
groupInputShow.value = false;
|
||||
} else {
|
||||
let group = { id: currentGroupID.value, name: groupInputValue.value, type: 'host' };
|
||||
await editGroup(group);
|
||||
}
|
||||
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
||||
groupOperation.value = '';
|
||||
groupInputShow.value = false;
|
||||
loadHostTree();
|
||||
loadGroups();
|
||||
};
|
||||
|
||||
const onDelete = async (node: Node, data: Tree) => {
|
||||
if (node.level === 1 && data.label === 'default') {
|
||||
return;
|
||||
}
|
||||
if (node.level === 1) {
|
||||
await useDeleteData(deleteGroup, data.id - 10000, '移除组后,组内所有连接将迁移到 default 组内,是否确认?');
|
||||
loadGroups();
|
||||
} else {
|
||||
await useDeleteData(deleteHost, data.id, 'commons.msg.delete');
|
||||
}
|
||||
loadHostTree();
|
||||
loadGroups();
|
||||
};
|
||||
|
||||
const onEdit = async (node: Node, data: Tree) => {
|
||||
if (node.level === 1 && data.label === 'default') {
|
||||
return;
|
||||
}
|
||||
console.log(node.level === 1);
|
||||
if (node.level === 1) {
|
||||
groupInputShow.value = true;
|
||||
groupInputValue.value = data.label;
|
||||
currentGroupID.value = data.id - 10000;
|
||||
groupOperation.value = 'edit';
|
||||
console.log(groupOperation.value);
|
||||
return;
|
||||
} else {
|
||||
const res = await getHostInfo(data.id);
|
||||
hostInfo.id = res.data.id;
|
||||
hostInfo.name = res.data.name;
|
||||
hostInfo.groupBelong = res.data.groupBelong;
|
||||
hostInfo.addr = res.data.addr;
|
||||
hostInfo.port = res.data.port;
|
||||
hostInfo.user = res.data.user;
|
||||
hostInfo.description = res.data.description;
|
||||
hostOperation.value = 'edit';
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
loadHost();
|
||||
loadHostTree();
|
||||
loadGroups();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.custom-tree-node {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 14px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
</style>
|
||||
|
|
Loading…
Add table
Reference in a new issue