fix: 调整主机分组逻辑、增加默认分组

This commit is contained in:
ssongliu 2023-03-06 17:18:13 +08:00 committed by ssongliu
parent a31e2ec913
commit 9bbad95c04
26 changed files with 406 additions and 712 deletions

View File

@ -12,13 +12,13 @@ import (
// @Summary Create group // @Summary Create group
// @Description 创建系统组 // @Description 创建系统组
// @Accept json // @Accept json
// @Param request body dto.GroupOperate true "request" // @Param request body dto.GroupCreate true "request"
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /hosts/group [post] // @Router /hosts/group [post]
// @x-panel-log {"bodyKeys":["name","type"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"创建组 [name][type]","formatEN":"create group [name][type]"} // @x-panel-log {"bodyKeys":["name","type"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"创建组 [name][type]","formatEN":"create group [name][type]"}
func (b *BaseApi) CreateGroup(c *gin.Context) { func (b *BaseApi) CreateGroup(c *gin.Context) {
var req dto.GroupOperate var req dto.GroupCreate
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return return
@ -42,7 +42,7 @@ func (b *BaseApi) CreateGroup(c *gin.Context) {
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /hosts/group/del [post] // @Router /hosts/group/del [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"groups","output_colume":"name","output_value":"name"}],"formatZH":"删除组 [name]","formatEN":"delete group [name]"} // @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"groups","output_colume":"name","output_value":"name"},{"input_colume":"id","input_value":"id","isList":false,"db":"groups","output_colume":"type","output_value":"type"}],"formatZH":"删除组 [type][name]","formatEN":"delete group [type][name]"}
func (b *BaseApi) DeleteGroup(c *gin.Context) { func (b *BaseApi) DeleteGroup(c *gin.Context) {
var req dto.OperateByID var req dto.OperateByID
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
@ -65,13 +65,13 @@ func (b *BaseApi) DeleteGroup(c *gin.Context) {
// @Summary Update group // @Summary Update group
// @Description 更新系统组 // @Description 更新系统组
// @Accept json // @Accept json
// @Param request body dto.GroupOperate true "request" // @Param request body dto.GroupUpdate true "request"
// @Success 200 // @Success 200
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /hosts/group/update [post] // @Router /hosts/group/update [post]
// @x-panel-log {"bodyKeys":["name","type"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"更新组 [name][type]","formatEN":"update group [name][type]"} // @x-panel-log {"bodyKeys":["name","type"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"更新组 [name][type]","formatEN":"update group [name][type]"}
func (b *BaseApi) UpdateGroup(c *gin.Context) { func (b *BaseApi) UpdateGroup(c *gin.Context) {
var req dto.GroupOperate var req dto.GroupUpdate
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return return
@ -80,8 +80,7 @@ func (b *BaseApi) UpdateGroup(c *gin.Context) {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return return
} }
if err := groupService.Update(req); err != nil {
if err := groupService.Update(req.ID, req.Name); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return return
} }

View File

@ -215,7 +215,7 @@ func (b *BaseApi) UpdateHost(c *gin.Context) {
upMap := make(map[string]interface{}) upMap := make(map[string]interface{})
upMap["name"] = req.Name upMap["name"] = req.Name
upMap["group_belong"] = req.GroupBelong upMap["group_id"] = req.GroupID
upMap["addr"] = req.Addr upMap["addr"] = req.Addr
upMap["port"] = req.Port upMap["port"] = req.Port
upMap["user"] = req.User upMap["user"] = req.User
@ -251,7 +251,7 @@ func (b *BaseApi) UpdateHostGroup(c *gin.Context) {
} }
upMap := make(map[string]interface{}) upMap := make(map[string]interface{})
upMap["group_belong"] = req.Group upMap["group_id"] = req.GroupID
if err := hostService.Update(req.ID, upMap); err != nil { if err := hostService.Update(req.ID, upMap); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return return

View File

@ -1,6 +1,6 @@
package dto package dto
type GroupOperate struct { type GroupCreate struct {
ID uint `json:"id"` ID uint `json:"id"`
Name string `json:"name" validate:"required"` Name string `json:"name" validate:"required"`
Type string `json:"type" validate:"required"` Type string `json:"type" validate:"required"`
@ -10,8 +10,15 @@ type GroupSearch struct {
Type string `json:"type" validate:"required"` Type string `json:"type" validate:"required"`
} }
type GroupUpdate struct {
ID uint `json:"id"`
Name string `json:"name"`
IsDefault bool `json:"isDefault"`
}
type GroupInfo struct { type GroupInfo struct {
ID uint `json:"id"` ID uint `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Type string `json:"type"` Type string `json:"type"`
IsDefault bool `json:"isDefault"`
} }

View File

@ -6,7 +6,7 @@ import (
type HostOperate struct { type HostOperate struct {
ID uint `json:"id"` ID uint `json:"id"`
GroupBelong string `json:"groupBelong" validate:"required"` GroupID uint `json:"groupID"`
Name string `json:"name"` Name string `json:"name"`
Addr string `json:"addr" validate:"required,ip"` Addr string `json:"addr" validate:"required,ip"`
Port uint `json:"port" validate:"required,number,max=65535,min=1"` Port uint `json:"port" validate:"required,number,max=65535,min=1"`
@ -29,7 +29,7 @@ type HostConnTest struct {
type SearchHostWithPage struct { type SearchHostWithPage struct {
PageInfo PageInfo
Group string `json:"group"` GroupID uint `json:"groupID"`
Info string `json:"info"` Info string `json:"info"`
} }
@ -39,12 +39,13 @@ type SearchForTree struct {
type ChangeHostGroup struct { type ChangeHostGroup struct {
ID uint `json:"id" validate:"required"` ID uint `json:"id" validate:"required"`
Group string `json:"group" validate:"required"` GroupID uint `json:"groupID" validate:"required"`
} }
type HostInfo struct { type HostInfo struct {
ID uint `json:"id"` ID uint `json:"id"`
CreatedAt time.Time `json:"createdAt"` CreatedAt time.Time `json:"createdAt"`
GroupID uint `json:"groupID"`
GroupBelong string `json:"groupBelong"` GroupBelong string `json:"groupBelong"`
Name string `json:"name"` Name string `json:"name"`
Addr string `json:"addr"` Addr string `json:"addr"`

View File

@ -2,6 +2,7 @@ package model
type Group struct { type Group struct {
BaseModel BaseModel
IsDefault bool `json:"isDefault"`
Name string `gorm:"type:varchar(64);not null" json:"name"` Name string `gorm:"type:varchar(64);not null" json:"name"`
Type string `gorm:"type:varchar(16);not null" json:"type"` Type string `gorm:"type:varchar(16);not null" json:"type"`
} }

View File

@ -2,10 +2,10 @@ package model
type Host struct { type Host struct {
BaseModel BaseModel
GroupBelong string `gorm:"type:varchar(64);not null" json:"groupBelong"` GroupID uint `gorm:"type:decimal;not null" json:"group_id"`
Name string `gorm:"type:varchar(64);not null" json:"name"` Name string `gorm:"type:varchar(64);not null" json:"name"`
Addr string `gorm:"type:varchar(16);not null" json:"addr"` Addr string `gorm:"type:varchar(16);not null" json:"addr"`
Port int `gorm:"type:varchar(5);not null" json:"port"` Port int `gorm:"type:decimal;not null" json:"port"`
User string `gorm:"type:varchar(64);not null" json:"user"` User string `gorm:"type:varchar(64);not null" json:"user"`
AuthMode string `gorm:"type:varchar(16);not null" json:"authMode"` AuthMode string `gorm:"type:varchar(16);not null" json:"authMode"`
Password string `gorm:"type:varchar(64)" json:"password"` Password string `gorm:"type:varchar(64)" json:"password"`

View File

@ -16,6 +16,7 @@ type ICommonRepo interface {
WithByName(name string) DBOption WithByName(name string) DBOption
WithByType(tp string) DBOption WithByType(tp string) DBOption
WithOrderBy(orderStr string) DBOption WithOrderBy(orderStr string) DBOption
WithByGroupID(groupID uint) DBOption
WithLikeName(name string) DBOption WithLikeName(name string) DBOption
WithIdsIn(ids []uint) DBOption WithIdsIn(ids []uint) DBOption
WithByDate(startTime, endTime time.Time) DBOption WithByDate(startTime, endTime time.Time) DBOption
@ -54,6 +55,15 @@ func (c *CommonRepo) WithByType(tp string) DBOption {
} }
} }
func (c *CommonRepo) WithByGroupID(groupID uint) DBOption {
return func(g *gorm.DB) *gorm.DB {
if groupID == 0 {
return g
}
return g.Where("group_id = ?", groupID)
}
}
func (c *CommonRepo) WithByStatus(status string) DBOption { func (c *CommonRepo) WithByStatus(status string) DBOption {
return func(g *gorm.DB) *gorm.DB { return func(g *gorm.DB) *gorm.DB {
if len(status) == 0 { if len(status) == 0 {

View File

@ -11,10 +11,11 @@ type GroupRepo struct{}
type IGroupRepo interface { type IGroupRepo interface {
Get(opts ...DBOption) (model.Group, error) Get(opts ...DBOption) (model.Group, error)
GetList(opts ...DBOption) ([]model.Group, error) GetList(opts ...DBOption) ([]model.Group, error)
WithByType(groupType string) DBOption
Create(group *model.Group) error Create(group *model.Group) error
Update(id uint, vars map[string]interface{}) error Update(id uint, vars map[string]interface{}) error
Delete(opts ...DBOption) error Delete(opts ...DBOption) error
CancelDefault() error
WithByIsDefault(isDefault bool) DBOption
} }
func NewIGroupRepo() IGroupRepo { func NewIGroupRepo() IGroupRepo {
@ -41,12 +42,6 @@ func (u *GroupRepo) GetList(opts ...DBOption) ([]model.Group, error) {
return groups, err 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 { func (u *GroupRepo) Create(group *model.Group) error {
return global.DB.Create(group).Error return global.DB.Create(group).Error
} }
@ -55,6 +50,12 @@ func (u *GroupRepo) Update(id uint, vars map[string]interface{}) error {
return global.DB.Model(&model.Group{}).Where("id = ?", id).Updates(vars).Error return global.DB.Model(&model.Group{}).Where("id = ?", id).Updates(vars).Error
} }
func (u *GroupRepo) WithByIsDefault(isDefault bool) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("is_default = ?", isDefault)
}
}
func (u *GroupRepo) Delete(opts ...DBOption) error { func (u *GroupRepo) Delete(opts ...DBOption) error {
db := global.DB db := global.DB
for _, opt := range opts { for _, opt := range opts {
@ -62,3 +63,7 @@ func (u *GroupRepo) Delete(opts ...DBOption) error {
} }
return db.Delete(&model.Group{}).Error return db.Delete(&model.Group{}).Error
} }
func (w GroupRepo) CancelDefault() error {
return global.DB.Model(&model.Group{}).Where("`is_default` = 1").Updates(map[string]interface{}{"is_default": 0}).Error
}

View File

@ -16,9 +16,7 @@ type IHostRepo interface {
WithByPort(port uint) DBOption WithByPort(port uint) DBOption
WithByUser(user string) DBOption WithByUser(user string) DBOption
WithByAddr(addr string) DBOption WithByAddr(addr string) DBOption
WithByGroup(group string) DBOption
Create(host *model.Host) error Create(host *model.Host) error
ChangeGroup(oldGroup, newGroup string) error
Update(id uint, vars map[string]interface{}) error Update(id uint, vars map[string]interface{}) error
Delete(opts ...DBOption) error Delete(opts ...DBOption) error
} }
@ -97,10 +95,6 @@ func (u *HostRepo) Create(host *model.Host) error {
return global.DB.Create(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 { func (u *HostRepo) Update(id uint, vars map[string]interface{}) error {
return global.DB.Model(&model.Host{}).Where("id = ?", id).Updates(vars).Error return global.DB.Model(&model.Host{}).Where("id = ?", id).Updates(vars).Error
} }

View File

@ -2,6 +2,7 @@ package service
import ( import (
"github.com/1Panel-dev/1Panel/backend/app/dto" "github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/constant" "github.com/1Panel-dev/1Panel/backend/constant"
"github.com/jinzhu/copier" "github.com/jinzhu/copier"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -11,8 +12,8 @@ type GroupService struct{}
type IGroupService interface { type IGroupService interface {
List(req dto.GroupSearch) ([]dto.GroupInfo, error) List(req dto.GroupSearch) ([]dto.GroupInfo, error)
Create(groupDto dto.GroupOperate) error Create(req dto.GroupCreate) error
Update(id uint, name string) error Update(req dto.GroupUpdate) error
Delete(id uint) error Delete(id uint) error
} }
@ -21,7 +22,7 @@ func NewIGroupService() IGroupService {
} }
func (u *GroupService) List(req dto.GroupSearch) ([]dto.GroupInfo, error) { func (u *GroupService) List(req dto.GroupSearch) ([]dto.GroupInfo, error) {
groups, err := groupRepo.GetList(groupRepo.WithByType(req.Type)) groups, err := groupRepo.GetList(commonRepo.WithByType(req.Type))
if err != nil { if err != nil {
return nil, constant.ErrRecordNotFound return nil, constant.ErrRecordNotFound
} }
@ -36,12 +37,12 @@ func (u *GroupService) List(req dto.GroupSearch) ([]dto.GroupInfo, error) {
return dtoUsers, err return dtoUsers, err
} }
func (u *GroupService) Create(groupDto dto.GroupOperate) error { func (u *GroupService) Create(req dto.GroupCreate) error {
group, _ := groupRepo.Get(commonRepo.WithByName(groupDto.Name), commonRepo.WithByName(groupDto.Name)) group, _ := groupRepo.Get(commonRepo.WithByName(req.Name), commonRepo.WithByName(req.Name))
if group.ID != 0 { if group.ID != 0 {
return constant.ErrRecordExist return constant.ErrRecordExist
} }
if err := copier.Copy(&group, &groupDto); err != nil { if err := copier.Copy(&group, &req); err != nil {
return errors.WithMessage(constant.ErrStructTransform, err.Error()) return errors.WithMessage(constant.ErrStructTransform, err.Error())
} }
if err := groupRepo.Create(&group); err != nil { if err := groupRepo.Create(&group); err != nil {
@ -55,22 +56,30 @@ func (u *GroupService) Delete(id uint) error {
if group.ID == 0 { if group.ID == 0 {
return constant.ErrRecordNotFound return constant.ErrRecordNotFound
} }
if err := hostRepo.ChangeGroup(group.Name, "default"); err != nil { switch group.Type {
return err case "website":
websites, _ := websiteRepo.GetBy(commonRepo.WithByGroupID(id))
if len(websites) > 0 {
return buserr.New(constant.ErrGroupIsUsed)
}
case "host":
hosts, _ := hostRepo.GetList(commonRepo.WithByGroupID(id))
if len(hosts) > 0 {
return buserr.New(constant.ErrGroupIsUsed)
}
} }
return groupRepo.Delete(commonRepo.WithByID(id)) return groupRepo.Delete(commonRepo.WithByID(id))
} }
func (u *GroupService) Update(id uint, name string) error { func (u *GroupService) Update(req dto.GroupUpdate) error {
group, _ := groupRepo.Get(commonRepo.WithByID(id)) if req.IsDefault {
if group.ID == 0 { if err := groupRepo.CancelDefault(); err != nil {
return constant.ErrRecordNotFound
}
upMap := make(map[string]interface{})
upMap["name"] = name
if err := hostRepo.ChangeGroup(group.Name, name); err != nil {
return err return err
} }
return groupRepo.Update(id, upMap) }
upMap := make(map[string]interface{})
upMap["name"] = req.Name
upMap["is_default"] = req.IsDefault
return groupRepo.Update(req.ID, upMap)
} }

View File

@ -65,7 +65,7 @@ func (u *HostService) GetHostInfo(id uint) (*model.Host, error) {
} }
func (u *HostService) SearchWithPage(search dto.SearchHostWithPage) (int64, interface{}, error) { func (u *HostService) SearchWithPage(search dto.SearchHostWithPage) (int64, interface{}, error) {
total, hosts, err := hostRepo.Page(search.Page, search.PageSize, hostRepo.WithByInfo(search.Info), hostRepo.WithByGroup(search.Group)) total, hosts, err := hostRepo.Page(search.Page, search.PageSize, hostRepo.WithByInfo(search.Info), commonRepo.WithByGroupID(search.GroupID))
if err != nil { if err != nil {
return 0, nil, err return 0, nil, err
} }
@ -75,6 +75,8 @@ func (u *HostService) SearchWithPage(search dto.SearchHostWithPage) (int64, inte
if err := copier.Copy(&item, &host); err != nil { if err := copier.Copy(&item, &host); err != nil {
return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error()) return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
} }
group, _ := groupRepo.Get(commonRepo.WithByID(host.GroupID))
item.GroupBelong = group.Name
dtoHosts = append(dtoHosts, item) dtoHosts = append(dtoHosts, item)
} }
return total, dtoHosts, err return total, dtoHosts, err
@ -85,7 +87,7 @@ func (u *HostService) SearchForTree(search dto.SearchForTree) ([]dto.HostTree, e
if err != nil { if err != nil {
return nil, err return nil, err
} }
groups, err := groupRepo.GetList() groups, err := groupRepo.GetList(commonRepo.WithByType("host"))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -99,12 +101,14 @@ func (u *HostService) SearchForTree(search dto.SearchForTree) ([]dto.HostTree, e
if len(host.Name) != 0 { if len(host.Name) != 0 {
label = fmt.Sprintf("%s - %s@%s:%d", host.Name, host.User, host.Addr, host.Port) label = fmt.Sprintf("%s - %s@%s:%d", host.Name, host.User, host.Addr, host.Port)
} }
if host.GroupBelong == group.Name { if host.GroupID == group.ID {
data.Children = append(data.Children, dto.TreeChild{ID: host.ID, Label: label}) data.Children = append(data.Children, dto.TreeChild{ID: host.ID, Label: label})
} }
} }
if len(data.Children) != 0 {
datas = append(datas, data) datas = append(datas, data)
} }
}
return datas, err return datas, err
} }
@ -113,6 +117,14 @@ func (u *HostService) Create(req dto.HostOperate) (*dto.HostInfo, error) {
if err := copier.Copy(&host, &req); err != nil { if err := copier.Copy(&host, &req); err != nil {
return nil, errors.WithMessage(constant.ErrStructTransform, err.Error()) return nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
} }
if req.GroupID == 0 {
group, err := groupRepo.Get(groupRepo.WithByIsDefault(true))
if err != nil {
return nil, errors.New("get default group failed")
}
host.GroupID = group.ID
req.GroupID = group.ID
}
var sameHostID uint var sameHostID uint
if req.Addr == "127.0.0.1" { if req.Addr == "127.0.0.1" {
hostSame, _ := hostRepo.Get(hostRepo.WithByAddr(req.Addr)) hostSame, _ := hostRepo.Get(hostRepo.WithByAddr(req.Addr))
@ -125,7 +137,7 @@ func (u *HostService) Create(req dto.HostOperate) (*dto.HostInfo, error) {
host.ID = sameHostID host.ID = sameHostID
upMap := make(map[string]interface{}) upMap := make(map[string]interface{})
upMap["name"] = req.Name upMap["name"] = req.Name
upMap["group_belong"] = req.GroupBelong upMap["group_id"] = req.GroupID
upMap["addr"] = req.Addr upMap["addr"] = req.Addr
upMap["port"] = req.Port upMap["port"] = req.Port
upMap["user"] = req.User upMap["user"] = req.User

View File

@ -39,7 +39,7 @@ var AddTableHost = &gormigrate.Migration{
return err return err
} }
host := model.Host{ host := model.Host{
Name: "localhost", Addr: "127.0.0.1", User: "root", Port: 22, GroupBelong: "default", Name: "localhost", Addr: "127.0.0.1", User: "root", Port: 22, GroupID: group.ID,
} }
if err := tx.Create(&host).Error; err != nil { if err := tx.Create(&host).Error; err != nil {
return err return err

View File

@ -164,39 +164,6 @@ var doc = `{
} }
} }
}, },
"/apps/installed": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "获取已安装应用列表",
"consumes": [
"application/json"
],
"tags": [
"App"
],
"summary": "List app installed",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/request.AppInstalledSearch"
}
}
],
"responses": {
"200": {
"description": ""
}
}
}
},
"/apps/installed/:appInstallId/versions": { "/apps/installed/:appInstallId/versions": {
"get": { "get": {
"security": [ "security": [
@ -231,93 +198,6 @@ var doc = `{
} }
} }
}, },
"/apps/installed/backups": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "查询已安装备份列表分页",
"consumes": [
"application/json"
],
"tags": [
"App"
],
"summary": "Page installed backups",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/request.AppBackupSearch"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.PageResult"
}
}
}
}
},
"/apps/installed/backups/del": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "删除应用备份记录",
"consumes": [
"application/json"
],
"tags": [
"App"
],
"summary": "Delete app backup record",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/request.AppBackupDelete"
}
}
],
"responses": {
"200": {
"description": ""
}
},
"x-panel-log": {
"BeforeFuntions": [
{
"db": "app_install_backups",
"input_colume": "id",
"input_value": "ids",
"isList": true,
"output_colume": "name",
"output_value": "names"
}
],
"bodyKeys": [
"ids"
],
"formatEN": "Deleting an Application Backup [names]",
"formatZH": "删除应用备份 [names]",
"paramKeys": []
}
}
},
"/apps/installed/check/:key": { "/apps/installed/check/:key": {
"get": { "get": {
"security": [ "security": [
@ -634,6 +514,39 @@ var doc = `{
} }
} }
}, },
"/apps/installed/search": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "获取已安装应用列表",
"consumes": [
"application/json"
],
"tags": [
"App"
],
"summary": "List app installed",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/request.AppInstalledSearch"
}
}
],
"responses": {
"200": {
"description": ""
}
}
}
},
"/apps/installed/sync": { "/apps/installed/sync": {
"post": { "post": {
"security": [ "security": [
@ -4946,7 +4859,7 @@ var doc = `{
"in": "body", "in": "body",
"required": true, "required": true,
"schema": { "schema": {
"$ref": "#/definitions/dto.GroupOperate" "$ref": "#/definitions/dto.GroupCreate"
} }
} }
], ],
@ -5007,13 +4920,21 @@ var doc = `{
"isList": false, "isList": false,
"output_colume": "name", "output_colume": "name",
"output_value": "name" "output_value": "name"
},
{
"db": "groups",
"input_colume": "id",
"input_value": "id",
"isList": false,
"output_colume": "type",
"output_value": "type"
} }
], ],
"bodyKeys": [ "bodyKeys": [
"id" "id"
], ],
"formatEN": "delete group [name]", "formatEN": "delete group [type][name]",
"formatZH": "删除组 [name]", "formatZH": "删除组 [type][name]",
"paramKeys": [] "paramKeys": []
} }
} }
@ -5076,7 +4997,7 @@ var doc = `{
"in": "body", "in": "body",
"required": true, "required": true,
"schema": { "schema": {
"$ref": "#/definitions/dto.GroupOperate" "$ref": "#/definitions/dto.GroupUpdate"
} }
} }
], ],
@ -8549,12 +8470,12 @@ var doc = `{
"dto.ChangeHostGroup": { "dto.ChangeHostGroup": {
"type": "object", "type": "object",
"required": [ "required": [
"group", "groupID",
"id" "id"
], ],
"properties": { "properties": {
"group": { "groupID": {
"type": "string" "type": "integer"
}, },
"id": { "id": {
"type": "integer" "type": "integer"
@ -9353,7 +9274,7 @@ var doc = `{
} }
} }
}, },
"dto.GroupOperate": { "dto.GroupCreate": {
"type": "object", "type": "object",
"required": [ "required": [
"name", "name",
@ -9382,6 +9303,20 @@ var doc = `{
} }
} }
}, },
"dto.GroupUpdate": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"isDefault": {
"type": "boolean"
},
"name": {
"type": "string"
}
}
},
"dto.HostConnTest": { "dto.HostConnTest": {
"type": "object", "type": "object",
"required": [ "required": [
@ -9434,6 +9369,9 @@ var doc = `{
"groupBelong": { "groupBelong": {
"type": "string" "type": "string"
}, },
"groupID": {
"type": "integer"
},
"id": { "id": {
"type": "integer" "type": "integer"
}, },
@ -9452,7 +9390,6 @@ var doc = `{
"type": "object", "type": "object",
"required": [ "required": [
"addr", "addr",
"groupBelong",
"port", "port",
"user" "user"
], ],
@ -9470,8 +9407,8 @@ var doc = `{
"description": { "description": {
"type": "string" "type": "string"
}, },
"groupBelong": { "groupID": {
"type": "string" "type": "integer"
}, },
"id": { "id": {
"type": "integer" "type": "integer"
@ -10270,8 +10207,8 @@ var doc = `{
"pageSize" "pageSize"
], ],
"properties": { "properties": {
"group": { "groupID": {
"type": "string" "type": "integer"
}, },
"info": { "info": {
"type": "string" "type": "string"
@ -10744,12 +10681,6 @@ var doc = `{
"appId": { "appId": {
"type": "integer" "type": "integer"
}, },
"backups": {
"type": "array",
"items": {
"$ref": "#/definitions/model.AppInstallBackup"
}
},
"containerName": { "containerName": {
"type": "string" "type": "string"
}, },
@ -10797,35 +10728,6 @@ var doc = `{
} }
} }
}, },
"model.AppInstallBackup": {
"type": "object",
"properties": {
"app_detail_id": {
"type": "integer"
},
"app_install_id": {
"type": "integer"
},
"createdAt": {
"type": "string"
},
"id": {
"type": "integer"
},
"name": {
"type": "string"
},
"param": {
"type": "string"
},
"path": {
"type": "string"
},
"updatedAt": {
"type": "string"
}
}
},
"model.Tag": { "model.Tag": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -11019,35 +10921,6 @@ var doc = `{
} }
} }
}, },
"request.AppBackupDelete": {
"type": "object",
"properties": {
"ids": {
"type": "array",
"items": {
"type": "integer"
}
}
}
},
"request.AppBackupSearch": {
"type": "object",
"required": [
"page",
"pageSize"
],
"properties": {
"appInstallID": {
"type": "integer"
},
"page": {
"type": "integer"
},
"pageSize": {
"type": "integer"
}
}
},
"request.AppInstallCreate": { "request.AppInstallCreate": {
"type": "object", "type": "object",
"required": [ "required": [

View File

@ -150,39 +150,6 @@
} }
} }
}, },
"/apps/installed": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "获取已安装应用列表",
"consumes": [
"application/json"
],
"tags": [
"App"
],
"summary": "List app installed",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/request.AppInstalledSearch"
}
}
],
"responses": {
"200": {
"description": ""
}
}
}
},
"/apps/installed/:appInstallId/versions": { "/apps/installed/:appInstallId/versions": {
"get": { "get": {
"security": [ "security": [
@ -217,93 +184,6 @@
} }
} }
}, },
"/apps/installed/backups": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "查询已安装备份列表分页",
"consumes": [
"application/json"
],
"tags": [
"App"
],
"summary": "Page installed backups",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/request.AppBackupSearch"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.PageResult"
}
}
}
}
},
"/apps/installed/backups/del": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "删除应用备份记录",
"consumes": [
"application/json"
],
"tags": [
"App"
],
"summary": "Delete app backup record",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/request.AppBackupDelete"
}
}
],
"responses": {
"200": {
"description": ""
}
},
"x-panel-log": {
"BeforeFuntions": [
{
"db": "app_install_backups",
"input_colume": "id",
"input_value": "ids",
"isList": true,
"output_colume": "name",
"output_value": "names"
}
],
"bodyKeys": [
"ids"
],
"formatEN": "Deleting an Application Backup [names]",
"formatZH": "删除应用备份 [names]",
"paramKeys": []
}
}
},
"/apps/installed/check/:key": { "/apps/installed/check/:key": {
"get": { "get": {
"security": [ "security": [
@ -620,6 +500,39 @@
} }
} }
}, },
"/apps/installed/search": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "获取已安装应用列表",
"consumes": [
"application/json"
],
"tags": [
"App"
],
"summary": "List app installed",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/request.AppInstalledSearch"
}
}
],
"responses": {
"200": {
"description": ""
}
}
}
},
"/apps/installed/sync": { "/apps/installed/sync": {
"post": { "post": {
"security": [ "security": [
@ -4932,7 +4845,7 @@
"in": "body", "in": "body",
"required": true, "required": true,
"schema": { "schema": {
"$ref": "#/definitions/dto.GroupOperate" "$ref": "#/definitions/dto.GroupCreate"
} }
} }
], ],
@ -4993,13 +4906,21 @@
"isList": false, "isList": false,
"output_colume": "name", "output_colume": "name",
"output_value": "name" "output_value": "name"
},
{
"db": "groups",
"input_colume": "id",
"input_value": "id",
"isList": false,
"output_colume": "type",
"output_value": "type"
} }
], ],
"bodyKeys": [ "bodyKeys": [
"id" "id"
], ],
"formatEN": "delete group [name]", "formatEN": "delete group [type][name]",
"formatZH": "删除组 [name]", "formatZH": "删除组 [type][name]",
"paramKeys": [] "paramKeys": []
} }
} }
@ -5062,7 +4983,7 @@
"in": "body", "in": "body",
"required": true, "required": true,
"schema": { "schema": {
"$ref": "#/definitions/dto.GroupOperate" "$ref": "#/definitions/dto.GroupUpdate"
} }
} }
], ],
@ -8535,12 +8456,12 @@
"dto.ChangeHostGroup": { "dto.ChangeHostGroup": {
"type": "object", "type": "object",
"required": [ "required": [
"group", "groupID",
"id" "id"
], ],
"properties": { "properties": {
"group": { "groupID": {
"type": "string" "type": "integer"
}, },
"id": { "id": {
"type": "integer" "type": "integer"
@ -9339,7 +9260,7 @@
} }
} }
}, },
"dto.GroupOperate": { "dto.GroupCreate": {
"type": "object", "type": "object",
"required": [ "required": [
"name", "name",
@ -9368,6 +9289,20 @@
} }
} }
}, },
"dto.GroupUpdate": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"isDefault": {
"type": "boolean"
},
"name": {
"type": "string"
}
}
},
"dto.HostConnTest": { "dto.HostConnTest": {
"type": "object", "type": "object",
"required": [ "required": [
@ -9420,6 +9355,9 @@
"groupBelong": { "groupBelong": {
"type": "string" "type": "string"
}, },
"groupID": {
"type": "integer"
},
"id": { "id": {
"type": "integer" "type": "integer"
}, },
@ -9438,7 +9376,6 @@
"type": "object", "type": "object",
"required": [ "required": [
"addr", "addr",
"groupBelong",
"port", "port",
"user" "user"
], ],
@ -9456,8 +9393,8 @@
"description": { "description": {
"type": "string" "type": "string"
}, },
"groupBelong": { "groupID": {
"type": "string" "type": "integer"
}, },
"id": { "id": {
"type": "integer" "type": "integer"
@ -10256,8 +10193,8 @@
"pageSize" "pageSize"
], ],
"properties": { "properties": {
"group": { "groupID": {
"type": "string" "type": "integer"
}, },
"info": { "info": {
"type": "string" "type": "string"
@ -10730,12 +10667,6 @@
"appId": { "appId": {
"type": "integer" "type": "integer"
}, },
"backups": {
"type": "array",
"items": {
"$ref": "#/definitions/model.AppInstallBackup"
}
},
"containerName": { "containerName": {
"type": "string" "type": "string"
}, },
@ -10783,35 +10714,6 @@
} }
} }
}, },
"model.AppInstallBackup": {
"type": "object",
"properties": {
"app_detail_id": {
"type": "integer"
},
"app_install_id": {
"type": "integer"
},
"createdAt": {
"type": "string"
},
"id": {
"type": "integer"
},
"name": {
"type": "string"
},
"param": {
"type": "string"
},
"path": {
"type": "string"
},
"updatedAt": {
"type": "string"
}
}
},
"model.Tag": { "model.Tag": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -11005,35 +10907,6 @@
} }
} }
}, },
"request.AppBackupDelete": {
"type": "object",
"properties": {
"ids": {
"type": "array",
"items": {
"type": "integer"
}
}
}
},
"request.AppBackupSearch": {
"type": "object",
"required": [
"page",
"pageSize"
],
"properties": {
"appInstallID": {
"type": "integer"
},
"page": {
"type": "integer"
},
"pageSize": {
"type": "integer"
}
}
},
"request.AppInstallCreate": { "request.AppInstallCreate": {
"type": "object", "type": "object",
"required": [ "required": [

View File

@ -61,12 +61,12 @@ definitions:
type: object type: object
dto.ChangeHostGroup: dto.ChangeHostGroup:
properties: properties:
group: groupID:
type: string type: integer
id: id:
type: integer type: integer
required: required:
- group - groupID
- id - id
type: object type: object
dto.CleanLog: dto.CleanLog:
@ -608,7 +608,7 @@ definitions:
- type - type
- vars - vars
type: object type: object
dto.GroupOperate: dto.GroupCreate:
properties: properties:
id: id:
type: integer type: integer
@ -627,6 +627,15 @@ definitions:
required: required:
- type - type
type: object type: object
dto.GroupUpdate:
properties:
id:
type: integer
isDefault:
type: boolean
name:
type: string
type: object
dto.HostConnTest: dto.HostConnTest:
properties: properties:
addr: addr:
@ -663,6 +672,8 @@ definitions:
type: string type: string
groupBelong: groupBelong:
type: string type: string
groupID:
type: integer
id: id:
type: integer type: integer
name: name:
@ -683,8 +694,8 @@ definitions:
type: string type: string
description: description:
type: string type: string
groupBelong: groupID:
type: string type: integer
id: id:
type: integer type: integer
name: name:
@ -701,7 +712,6 @@ definitions:
type: string type: string
required: required:
- addr - addr
- groupBelong
- port - port
- user - user
type: object type: object
@ -1217,8 +1227,8 @@ definitions:
type: object type: object
dto.SearchHostWithPage: dto.SearchHostWithPage:
properties: properties:
group: groupID:
type: string type: integer
info: info:
type: string type: string
page: page:
@ -1532,10 +1542,6 @@ definitions:
type: integer type: integer
appId: appId:
type: integer type: integer
backups:
items:
$ref: '#/definitions/model.AppInstallBackup'
type: array
containerName: containerName:
type: string type: string
createdAt: createdAt:
@ -1567,25 +1573,6 @@ definitions:
version: version:
type: string type: string
type: object type: object
model.AppInstallBackup:
properties:
app_detail_id:
type: integer
app_install_id:
type: integer
createdAt:
type: string
id:
type: integer
name:
type: string
param:
type: string
path:
type: string
updatedAt:
type: string
type: object
model.Tag: model.Tag:
properties: properties:
createdAt: createdAt:
@ -1713,25 +1700,6 @@ definitions:
$ref: '#/definitions/model.Website' $ref: '#/definitions/model.Website'
type: array type: array
type: object type: object
request.AppBackupDelete:
properties:
ids:
items:
type: integer
type: array
type: object
request.AppBackupSearch:
properties:
appInstallID:
type: integer
page:
type: integer
pageSize:
type: integer
required:
- page
- pageSize
type: object
request.AppInstallCreate: request.AppInstallCreate:
properties: properties:
appDetailId: appDetailId:
@ -2721,26 +2689,6 @@ paths:
formatEN: Install app [appKey]-[name] formatEN: Install app [appKey]-[name]
formatZH: 安装应用 [appKey]-[name] formatZH: 安装应用 [appKey]-[name]
paramKeys: [] paramKeys: []
/apps/installed:
post:
consumes:
- application/json
description: 获取已安装应用列表
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/request.AppInstalledSearch'
responses:
"200":
description: ""
security:
- ApiKeyAuth: []
summary: List app installed
tags:
- App
/apps/installed/:appInstallId/versions: /apps/installed/:appInstallId/versions:
get: get:
consumes: consumes:
@ -2762,61 +2710,6 @@ paths:
summary: Search app update version by install id summary: Search app update version by install id
tags: tags:
- App - App
/apps/installed/backups:
post:
consumes:
- application/json
description: 查询已安装备份列表分页
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/request.AppBackupSearch'
responses:
"200":
description: OK
schema:
$ref: '#/definitions/dto.PageResult'
security:
- ApiKeyAuth: []
summary: Page installed backups
tags:
- App
/apps/installed/backups/del:
post:
consumes:
- application/json
description: 删除应用备份记录
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/request.AppBackupDelete'
responses:
"200":
description: ""
security:
- ApiKeyAuth: []
summary: Delete app backup record
tags:
- App
x-panel-log:
BeforeFuntions:
- db: app_install_backups
input_colume: id
input_value: ids
isList: true
output_colume: name
output_value: names
bodyKeys:
- ids
formatEN: Deleting an Application Backup [names]
formatZH: 删除应用备份 [names]
paramKeys: []
/apps/installed/check/:key: /apps/installed/check/:key:
get: get:
consumes: consumes:
@ -3018,6 +2911,26 @@ paths:
formatEN: Application port update [key]-[name] => [port] formatEN: Application port update [key]-[name] => [port]
formatZH: 应用端口修改 [key]-[name] => [port] formatZH: 应用端口修改 [key]-[name] => [port]
paramKeys: [] paramKeys: []
/apps/installed/search:
post:
consumes:
- application/json
description: 获取已安装应用列表
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/request.AppInstalledSearch'
responses:
"200":
description: ""
security:
- ApiKeyAuth: []
summary: List app installed
tags:
- App
/apps/installed/sync: /apps/installed/sync:
post: post:
description: 同步已安装应用列表 description: 同步已安装应用列表
@ -5758,7 +5671,7 @@ paths:
name: request name: request
required: true required: true
schema: schema:
$ref: '#/definitions/dto.GroupOperate' $ref: '#/definitions/dto.GroupCreate'
responses: responses:
"200": "200":
description: "" description: ""
@ -5803,10 +5716,16 @@ paths:
isList: false isList: false
output_colume: name output_colume: name
output_value: name output_value: name
- db: groups
input_colume: id
input_value: id
isList: false
output_colume: type
output_value: type
bodyKeys: bodyKeys:
- id - id
formatEN: delete group [name] formatEN: delete group [type][name]
formatZH: 删除组 [name] formatZH: 删除组 [type][name]
paramKeys: [] paramKeys: []
/hosts/group/search: /hosts/group/search:
post: post:
@ -5841,7 +5760,7 @@ paths:
name: request name: request
required: true required: true
schema: schema:
$ref: '#/definitions/dto.GroupOperate' $ref: '#/definitions/dto.GroupUpdate'
responses: responses:
"200": "200":
description: "" description: ""

View File

@ -73,6 +73,8 @@ declare module 'vue' {
FileList: typeof import('./src/components/file-list/index.vue')['default'] FileList: typeof import('./src/components/file-list/index.vue')['default']
FileRole: typeof import('./src/components/file-role/index.vue')['default'] FileRole: typeof import('./src/components/file-role/index.vue')['default']
Footer: typeof import('./src/components/app-layout/footer/index.vue')['default'] Footer: typeof import('./src/components/app-layout/footer/index.vue')['default']
Gropu: typeof import('./src/components/gropu/index.vue')['default']
Group: typeof import('./src/components/group/index.vue')['default']
InfiniteScroll: typeof import('element-plus/es')['ElInfiniteScroll'] InfiniteScroll: typeof import('element-plus/es')['ElInfiniteScroll']
Loading: typeof import('element-plus/es')['ElLoadingDirective'] Loading: typeof import('element-plus/es')['ElLoadingDirective']
Logo: typeof import('./src/components/app-layout/menu/components/Logo.vue')['default'] Logo: typeof import('./src/components/app-layout/menu/components/Logo.vue')['default']

View File

@ -3,12 +3,18 @@ export namespace Group {
id: number; id: number;
name: string; name: string;
type: string; type: string;
isDefault: boolean;
} }
export interface GroupOperate { export interface GroupCreate {
id: number; id: number;
name: string; name: string;
type: string; type: string;
} }
export interface GroupUpdate {
id: number;
name: string;
isDefault: boolean;
}
export interface GroupSearch { export interface GroupSearch {
type: string; type: string;
} }

View File

@ -12,6 +12,7 @@ export namespace Host {
} }
export interface Host extends CommonModel { export interface Host extends CommonModel {
name: string; name: string;
groupID: number;
groupBelong: string; groupBelong: string;
addr: string; addr: string;
port: number; port: number;
@ -22,7 +23,7 @@ export namespace Host {
export interface HostOperate { export interface HostOperate {
id: number; id: number;
name: string; name: string;
groupBelong: string; groupID: number;
addr: string; addr: string;
port: number; port: number;
user: string; user: string;
@ -42,13 +43,13 @@ export namespace Host {
} }
export interface GroupChange { export interface GroupChange {
id: number; id: number;
group: string; groupID: number;
} }
export interface ReqSearch { export interface ReqSearch {
info?: string; info?: string;
} }
export interface SearchWithPage extends ReqPage { export interface SearchWithPage extends ReqPage {
group: string; groupID: number;
info?: string; info?: string;
} }
} }

View File

@ -33,16 +33,16 @@ export const deleteHost = (params: { ids: number[] }) => {
}; };
// group // group
export const getGroupList = (params: Group.GroupSearch) => { export const GetGroupList = (params: Group.GroupSearch) => {
return http.post<Array<Group.GroupInfo>>(`/hosts/group/search`, params); return http.post<Array<Group.GroupInfo>>(`/hosts/group/search`, params);
}; };
export const addGroup = (params: Group.GroupOperate) => { export const CreateGroup = (params: Group.GroupCreate) => {
return http.post<Group.GroupOperate>(`/hosts/group`, params); return http.post<Group.GroupCreate>(`/hosts/group`, params);
}; };
export const editGroup = (params: Group.GroupOperate) => { export const UpdateGroup = (params: Group.GroupUpdate) => {
return http.post(`/hosts/group/update`, params); return http.post(`/hosts/group/update`, params);
}; };
export const deleteGroup = (id: number) => { export const DeleteGroup = (id: number) => {
return http.post(`/hosts/group/del`, { id: id }); return http.post(`/hosts/group/del`, { id: id });
}; };

View File

@ -108,7 +108,6 @@ const onBackup = async () => {
loading.value = true; loading.value = true;
await handleBackup(params) await handleBackup(params)
.then(() => { .then(() => {
console.log(loading.value);
loading.value = false; loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
search(); search();

View File

@ -1,10 +1,10 @@
<template> <template>
<el-drawer :close-on-click-modal="false" v-model="drawerVisiable" size="50%" :before-close="handleClose"> <el-drawer :close-on-click-modal="false" v-model="open" size="50%" :before-close="handleClose">
<template #header> <template #header>
<Header :header="$t('website.group')" :back="handleClose"></Header> <Header :header="$t('website.group')" :back="handleClose"></Header>
</template> </template>
<ComplexTable v-loading="loading" :data="data" @search="search()"> <ComplexTable :data="data" @search="search()">
<template #toolbar> <template #toolbar>
<el-button type="primary" @click="openCreate">{{ $t('website.createGroup') }}</el-button> <el-button type="primary" @click="openCreate">{{ $t('website.createGroup') }}</el-button>
</template> </template>
@ -12,6 +12,7 @@
<template #default="{ row }"> <template #default="{ row }">
<span v-if="!row.edit"> <span v-if="!row.edit">
{{ row.name }} {{ row.name }}
<span v-if="row.isDefault">({{ $t('website.default') }})</span>
</span> </span>
<el-input v-if="row.edit" v-model="row.name"></el-input> <el-input v-if="row.edit" v-model="row.name"></el-input>
</template> </template>
@ -19,30 +20,27 @@
<el-table-column :label="$t('commons.table.operate')"> <el-table-column :label="$t('commons.table.operate')">
<template #default="{ row, $index }"> <template #default="{ row, $index }">
<div> <div>
<el-button link v-if="row.edit" type="primary" @click="onSaveGroup(row)"> <el-button link v-if="row.edit" type="primary" @click="saveGroup(row, false)">
{{ $t('commons.button.save') }} {{ $t('commons.button.save') }}
</el-button> </el-button>
<el-button <el-button link v-if="!row.edit" type="primary" @click="editGroup($index)">
link
v-if="!row.edit"
:disabled="row.name === 'default'"
type="primary"
@click="onEditGroup($index)"
>
{{ $t('commons.button.edit') }} {{ $t('commons.button.edit') }}
</el-button> </el-button>
<el-button <el-button
link link
v-if="!row.edit" v-if="!row.edit"
:disabled="row.name === 'default'" :disabled="row.isDefault"
type="primary" type="primary"
@click="onDeleteGroup($index)" @click="deleteGroup($index)"
> >
{{ $t('commons.button.delete') }} {{ $t('commons.button.delete') }}
</el-button> </el-button>
<el-button link v-if="row.edit" type="primary" @click="cancelEdit($index)"> <el-button link v-if="row.edit" type="primary" @click="cancelEdit($index)">
{{ $t('commons.button.cancel') }} {{ $t('commons.button.cancel') }}
</el-button> </el-button>
<el-button link v-if="!row.edit && !row.isDefault" type="primary" @click="saveGroup(row, true)">
{{ $t('website.setDefault') }}
</el-button>
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
@ -53,33 +51,38 @@
import { ref } from 'vue'; import { ref } from 'vue';
import i18n from '@/lang'; import i18n from '@/lang';
import ComplexTable from '@/components/complex-table/index.vue'; import ComplexTable from '@/components/complex-table/index.vue';
import { CreateGroup, DeleteGroup, GetGroupList, UpdateGroup } from '@/api/modules/host';
import Header from '@/components/drawer-header/index.vue'; import Header from '@/components/drawer-header/index.vue';
import { MsgSuccess } from '@/utils/message'; import { MsgSuccess } from '@/utils/message';
import { addGroup, deleteGroup, editGroup, getGroupList } from '@/api/modules/host'; import { Group } from '@/api/interface/group';
import { useDeleteData } from '@/hooks/use-delete-data';
const loading = ref(); const open = ref(false);
const type = ref();
interface groupData { const data = ref();
id: number;
name: string;
}
let drawerVisiable = ref(false);
let data = ref();
const handleClose = () => { const handleClose = () => {
drawerVisiable.value = false; open.value = false;
data.value = []; data.value = [];
emit('search'); emit('search');
}; };
interface DialogProps {
type: string;
}
const acceptParams = (params: DialogProps): void => {
type.value = params.type;
open.value = true;
search();
};
const emit = defineEmits<{ (e: 'search'): void }>();
const search = () => { const search = () => {
data.value = []; data.value = [];
getGroupList({ type: 'host' }).then((res) => { GetGroupList({ type: type.value }).then((res) => {
for (const d of res.data) { for (const d of res.data) {
const g = { const g = {
id: d.id, id: d.id,
name: d.name, name: d.name,
isDefault: d.isDefault,
edit: false, edit: false,
}; };
data.value.push(g); data.value.push(g);
@ -87,42 +90,22 @@ const search = () => {
}); });
}; };
const onSaveGroup = (create: groupData) => { const saveGroup = (group: Group.GroupInfo, isDefault: boolean) => {
const group = { group.type = type.value;
id: create.id,
type: 'host',
name: create.name,
};
loading.value = true;
if (group.id == 0) { if (group.id == 0) {
addGroup(group) CreateGroup(group).then(() => {
.then(() => {
loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.createSuccess')); MsgSuccess(i18n.global.t('commons.msg.createSuccess'));
search(); search();
})
.catch(() => {
loading.value = false;
}); });
} else { } else {
editGroup(group) group.isDefault = isDefault;
.then(() => { UpdateGroup(group).then(() => {
loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.updateSuccess')); MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));
search(); search();
})
.catch(() => {
loading.value = false;
}); });
} }
}; };
const acceptParams = async () => {
drawerVisiable.value = true;
search();
};
const emit = defineEmits<{ (e: 'search'): void }>();
const openCreate = () => { const openCreate = () => {
for (const d of data.value) { for (const d of data.value) {
if (d.name == '') { if (d.name == '') {
@ -134,30 +117,27 @@ const openCreate = () => {
} }
const g = { const g = {
id: 0, id: 0,
type: 'host',
name: '', name: '',
isDefault: false,
edit: true, edit: true,
}; };
data.value.push(g); data.value.push(g);
}; };
const onDeleteGroup = async (index: number) => { const deleteGroup = (index: number) => {
const group = data.value[index]; const group = data.value[index];
if (group.id > 0) { if (group.id > 0) {
await useDeleteData(deleteGroup, group.id, 'terminal.groupDeleteHelper') DeleteGroup(group.id).then(() => {
.then(() => { data.value.splice(index, 1);
loading.value = false; MsgSuccess(i18n.global.t('commons.msg.deleteSuccess'));
search();
})
.catch(() => {
loading.value = false;
}); });
} else { } else {
data.value.splice(index, 1); data.value.splice(index, 1);
} }
}; };
const onEditGroup = (index: number) => { const editGroup = (index: number) => {
for (const i in data.value) { for (const i in data.value) {
const d = data.value[i]; const d = data.value[i];
if (d.name == '') { if (d.name == '') {
@ -176,7 +156,6 @@ const cancelEdit = (index: number) => {
} else { } else {
data.value[index].edit = false; data.value[index].edit = false;
} }
search();
}; };
defineExpose({ acceptParams }); defineExpose({ acceptParams });

View File

@ -8,12 +8,12 @@
<el-col :span="22"> <el-col :span="22">
<el-form ref="hostInfoRef" label-position="top" :model="dialogData" :rules="rules"> <el-form ref="hostInfoRef" label-position="top" :model="dialogData" :rules="rules">
<el-form-item :label="$t('commons.table.group')" prop="group"> <el-form-item :label="$t('commons.table.group')" prop="group">
<el-select filterable v-model="dialogData.group" clearable style="width: 100%"> <el-select filterable v-model="dialogData.groupID" clearable style="width: 100%">
<el-option <el-option
v-for="item in groupList" v-for="item in groupList"
:key="item.id" :key="item.id"
:label="item.name" :label="item.name"
:value="item.name" :value="item.id"
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
@ -36,7 +36,7 @@
import { ref, reactive } from 'vue'; import { ref, reactive } from 'vue';
import type { ElForm } from 'element-plus'; import type { ElForm } from 'element-plus';
import { Rules } from '@/global/form-rules'; import { Rules } from '@/global/form-rules';
import { editHostGroup, getGroupList } from '@/api/modules/host'; import { editHostGroup, GetGroupList } from '@/api/modules/host';
import i18n from '@/lang'; import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message'; import { MsgSuccess } from '@/utils/message';
@ -46,16 +46,16 @@ interface DialogProps {
group: string; group: string;
} }
const drawerVisiable = ref(false); const drawerVisiable = ref(false);
const dialogData = ref<DialogProps>({ const dialogData = ref({
id: 0, id: 0,
group: '', groupID: 0,
}); });
const groupList = ref(); const groupList = ref();
const acceptParams = (params: DialogProps): void => { const acceptParams = (params: DialogProps): void => {
dialogData.value = params; dialogData.value.id = params.id;
loadGroups(params.group);
drawerVisiable.value = true; drawerVisiable.value = true;
loadGroups();
}; };
const emit = defineEmits<{ (e: 'search'): void }>(); const emit = defineEmits<{ (e: 'search'): void }>();
@ -66,12 +66,18 @@ const handleClose = () => {
type FormInstance = InstanceType<typeof ElForm>; type FormInstance = InstanceType<typeof ElForm>;
const hostInfoRef = ref<FormInstance>(); const hostInfoRef = ref<FormInstance>();
const rules = reactive({ const rules = reactive({
group: [Rules.requiredSelect], groupID: [Rules.requiredSelect],
}); });
const loadGroups = async () => { const loadGroups = async (groupName: string) => {
const res = await getGroupList({ type: 'host' }); const res = await GetGroupList({ type: 'host' });
groupList.value = res.data; groupList.value = res.data;
for (const group of groupList.value) {
if (group.name === groupName) {
dialogData.value.groupID = group.id;
break;
}
}
}; };
const onSubmit = (formEl: FormInstance | undefined) => { const onSubmit = (formEl: FormInstance | undefined) => {

View File

@ -32,7 +32,7 @@
<template #search> <template #search>
<el-select v-model="group" @change="search()" clearable> <el-select v-model="group" @change="search()" clearable>
<template #prefix>{{ $t('terminal.group') }}</template> <template #prefix>{{ $t('terminal.group') }}</template>
<el-option v-for="item in groupList" :key="item.name" :value="item.name" :label="item.name" /> <el-option v-for="item in groupList" :key="item.name" :value="item.id" :label="item.name" />
</el-select> </el-select>
</template> </template>
<template #main> <template #main>
@ -71,11 +71,11 @@
<script setup lang="ts"> <script setup lang="ts">
import LayoutContent from '@/layout/layout-content.vue'; import LayoutContent from '@/layout/layout-content.vue';
import GroupDialog from '@/views/host/terminal/host/group/index.vue'; import GroupDialog from '@/components/group/index.vue';
import GroupChangeDialog from '@/views/host/terminal/host/change-group/index.vue'; import GroupChangeDialog from '@/views/host/terminal/host/change-group/index.vue';
import OperateDialog from '@/views/host/terminal/host/operate/index.vue'; import OperateDialog from '@/views/host/terminal/host/operate/index.vue';
import ComplexTable from '@/components/complex-table/index.vue'; import ComplexTable from '@/components/complex-table/index.vue';
import { deleteHost, getGroupList, searchHosts } from '@/api/modules/host'; import { deleteHost, GetGroupList, searchHosts } from '@/api/modules/host';
import { reactive, ref } from 'vue'; import { reactive, ref } from 'vue';
import i18n from '@/lang'; import i18n from '@/lang';
import { Host } from '@/api/interface/host'; import { Host } from '@/api/interface/host';
@ -105,7 +105,6 @@ const dialogRef = ref();
const onOpenDialog = async ( const onOpenDialog = async (
title: string, title: string,
rowData: Partial<Host.Host> = { rowData: Partial<Host.Host> = {
groupBelong: 'default',
port: 22, port: 22,
user: 'root', user: 'root',
authMode: 'password', authMode: 'password',
@ -120,7 +119,7 @@ const onOpenDialog = async (
const dialogGroupRef = ref(); const dialogGroupRef = ref();
const onOpenGroupDialog = () => { const onOpenGroupDialog = () => {
dialogGroupRef.value!.acceptParams(); dialogGroupRef.value!.acceptParams({ type: 'host' });
}; };
const onBatchDelete = async (row: Host.Host | null) => { const onBatchDelete = async (row: Host.Host | null) => {
@ -137,7 +136,7 @@ const onBatchDelete = async (row: Host.Host | null) => {
}; };
const loadGroups = async () => { const loadGroups = async () => {
const res = await getGroupList({ type: 'host' }); const res = await GetGroupList({ type: 'host' });
groupList.value = res.data; groupList.value = res.data;
}; };
@ -172,7 +171,7 @@ const search = async () => {
let params = { let params = {
page: paginationConfig.currentPage, page: paginationConfig.currentPage,
pageSize: paginationConfig.pageSize, pageSize: paginationConfig.pageSize,
group: group.value, groupID: Number(group.value),
info: info.value, info: info.value,
}; };
loadGroups(); loadGroups();

View File

@ -39,18 +39,13 @@
<el-form-item :label="$t('terminal.port')" prop="port"> <el-form-item :label="$t('terminal.port')" prop="port">
<el-input clearable v-model.number="dialogData.rowData!.port" /> <el-input clearable v-model.number="dialogData.rowData!.port" />
</el-form-item> </el-form-item>
<el-form-item :label="$t('commons.table.group')" prop="groupBelong"> <el-form-item :label="$t('commons.table.group')" prop="groupID">
<el-select <el-select filterable v-model="dialogData.rowData!.groupID" clearable style="width: 100%">
filterable
v-model="dialogData.rowData!.groupBelong"
clearable
style="width: 100%"
>
<el-option <el-option
v-for="item in groupList" v-for="item in groupList"
:key="item.id" :key="item.id"
:label="item.name" :label="item.name"
:value="item.name" :value="item.id"
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
@ -82,7 +77,7 @@
import { ref, reactive } from 'vue'; import { ref, reactive } from 'vue';
import type { ElForm } from 'element-plus'; import type { ElForm } from 'element-plus';
import { Rules } from '@/global/form-rules'; import { Rules } from '@/global/form-rules';
import { addHost, editHost, testByInfo, getGroupList } from '@/api/modules/host'; import { addHost, editHost, testByInfo, GetGroupList } from '@/api/modules/host';
import i18n from '@/lang'; import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message'; import { MsgSuccess } from '@/utils/message';
@ -113,7 +108,7 @@ const handleClose = () => {
type FormInstance = InstanceType<typeof ElForm>; type FormInstance = InstanceType<typeof ElForm>;
const hostInfoRef = ref<FormInstance>(); const hostInfoRef = ref<FormInstance>();
const rules = reactive({ const rules = reactive({
groupBelong: [Rules.requiredSelect], groupID: [Rules.requiredSelect],
addr: [Rules.requiredInput], addr: [Rules.requiredInput],
port: [Rules.requiredInput, Rules.port], port: [Rules.requiredInput, Rules.port],
user: [Rules.requiredInput], user: [Rules.requiredInput],
@ -123,7 +118,7 @@ const rules = reactive({
}); });
const loadGroups = async () => { const loadGroups = async () => {
const res = await getGroupList({ type: 'host' }); const res = await GetGroupList({ type: 'host' });
groupList.value = res.data; groupList.value = res.data;
}; };

View File

@ -84,7 +84,7 @@ const hostRef = ref<FormInstance>();
let hostInfo = reactive<Host.HostOperate>({ let hostInfo = reactive<Host.HostOperate>({
id: 0, id: 0,
name: '', name: '',
groupBelong: '', groupID: 0,
addr: '', addr: '',
port: 22, port: 22,
user: '', user: '',
@ -110,7 +110,7 @@ interface DialogProps {
const acceptParams = (props: DialogProps) => { const acceptParams = (props: DialogProps) => {
hostInfo.addr = ''; hostInfo.addr = '';
hostInfo.name = ''; hostInfo.name = '';
hostInfo.groupBelong = 'default'; hostInfo.groupID = 0;
hostInfo.addr = ''; hostInfo.addr = '';
hostInfo.port = 22; hostInfo.port = 22;
hostInfo.user = ''; hostInfo.user = '';
@ -136,7 +136,7 @@ const submitAddHost = (formEl: FormInstance | undefined, ops: string) => {
if (!formEl) return; if (!formEl) return;
formEl.validate(async (valid) => { formEl.validate(async (valid) => {
if (!valid) return; if (!valid) return;
hostInfo.groupBelong = 'default'; hostInfo.groupID = 0;
switch (ops) { switch (ops) {
case 'testConn': case 'testConn':
await testByInfo(hostInfo).then((res) => { await testByInfo(hostInfo).then((res) => {

View File

@ -173,13 +173,17 @@ const acceptParams = async () => {
timer = setInterval(() => { timer = setInterval(() => {
syncTerminal(); syncTerminal();
}, 1000 * 5); }, 1000 * 5);
for (const item of hostTree.value) { for (let gIndex = 0; gIndex < hostTree.value.length; gIndex++) {
if (!item.children) { if (!hostTree.value[gIndex].children) {
continue; continue;
} }
for (const host of item.children) { for (let i = 0; i < hostTree.value[gIndex].children.length; i++) {
if (host.label.indexOf('127.0.0.1') !== -1) { if (hostTree.value[gIndex].children[i].label.indexOf('@127.0.0.1:') !== -1) {
localHostID.value = host.id; localHostID.value = hostTree.value[gIndex].children[i].id;
hostTree.value[gIndex].children.splice(i, 1);
if (hostTree.value[gIndex].children.length === 0) {
hostTree.value.splice(gIndex, 1);
}
if (terminalTabs.value.length !== 0) { if (terminalTabs.value.length !== 0) {
return; return;
} }