diff --git a/server/initialize/plugin_biz_v1.go b/server/initialize/plugin_biz_v1.go index 7366c65d..00214aac 100644 --- a/server/initialize/plugin_biz_v1.go +++ b/server/initialize/plugin_biz_v1.go @@ -2,8 +2,10 @@ package initialize import ( "fmt" + "github.com/flipped-aurora/gin-vue-admin/server/global" "github.com/flipped-aurora/gin-vue-admin/server/plugin/email" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/organization" "github.com/flipped-aurora/gin-vue-admin/server/utils/plugin" "github.com/gin-gonic/gin" ) @@ -30,5 +32,6 @@ func bizPluginV1(group ...*gin.RouterGroup) { global.GVA_CONFIG.Email.Port, global.GVA_CONFIG.Email.IsSSL, )) + PluginInit(public, organization.CreateOrganizationPlug()) holder(public, private) } diff --git a/server/plugin/organization/README.md b/server/plugin/organization/README.md new file mode 100644 index 00000000..cb98b25a --- /dev/null +++ b/server/plugin/organization/README.md @@ -0,0 +1,133 @@ +## GVA 组织管理功能 +#### 开发者:奇淼 +### 本插件支持自动安装 + +### 图例 + + + + + + + + + + + +### 手动安装方法 + + 1.解压zip获得organization文件夹 + 2.将 organization/web/plugin/organization 放置在web/plugin下 + 3.将 organization/server/plugin/organization 放置在server/plugin下 + +#### 执行如下注册方法 + +### 注册(手动自动都需要) + +#### 1. 前往GVA主程序下的initialize/router.go 在Routers 方法最末尾按照你需要的及安全模式添加本插件 + PluginInit(PublicGroup, organization.CreateOrganizationPlug()) + 到gva系统,角色管理,分配角色的菜单权限和api权限即可,插件会自动注册菜单和api,需要手动分配。 +### 2. 配置说明 + +#### 2-1 全局配置结构体说明 + + 无配置 + +#### 2-2 结构说明 + + // 组织基础结构 + type Organization struct { + global.GVA_MODEL + Name string `json:"name" form:"name" gorm:"column:name;comment:;"` + ParentID uint `json:"parentID" form:"parentID" gorm:"column:parent_id;comment:父节点ID;"` + } + + // 组织用户关系表 + type OrgUser struct { + Organization Organization `json:"organization"` + OrganizationID uint `json:"organizationID,omitempty" form:"organizationID" ` + SysUserID uint `json:"sysUserID,omitempty" form:"sysUserID"` + IsAdmin bool `json:"isAdmin" form:"isAdmin"` + SysUser system.SysUser `json:"sysUser"` + } + + // 组织内用户操作结构 + type OrgUserReq struct { + OrganizationID uint `json:"organizationID,omitempty"` + ToOrganizationID uint `json:"toOrganizationID,omitempty"` + SysUserIDS []uint `json:"sysUserIDS,omitempty"` + } + + + // 搜索组织功能(未使用) + type OrganizationSearch struct { + organization.Organization + request.PageInfo + } + + // 搜索组织内用户功能 + type OrgUserSearch struct { + organization.OrgUser + UserName string `json:"userName" form:"userName"` + request.PageInfo + } + + + +### 3. 方法API + + 无,后续维护会增加便捷查询当前用户所属组织,所属组织子组织以及成员等便捷方法,根据反馈添加。 + +### 4. 可直接调用的接口 + + POST /org/createOrganization // 新建Organization + 入参为 Organization 结构体 + + DELETE /org/deleteOrganization // 删除Organization + 入参为 ID int + + + DELETE /org/deleteOrganizationByIds // 批量删除Organization + 入参为 IDS:[]int + + + PUT /org/updateOrganization // 更新Organization + 入参为 Organization 结构体 + + + POST /org/createOrgUser // 人员入职 + 入参为 OrgUserReq 结构体 + + + PUT /org/setOrgUserAdmin // 管理员设置 + 入参为 { + sysUserID: 用户id, + isAdmin: 是否管理员 bool + } + + + GET /org/findOrganization // 根据ID获取Organization + 入参为 { ID: 组织id } + + + GET /org/getOrganizationList // 获取Organization列表 + 无需入参 + + + GET /org/findOrgUserAll // 获取当前组织下所有用户ID + 入参为 { organizationID: 组织id } + + + GET /org/findOrgUserList // 获取当前组织下所有用户(分页) + 入参为 OrgUserSearch 结构体 + + + DELETE /org/deleteOrgUser // 删除当前组织下选中用户 + 入参为 { sysUserIDS:用户id []int, organizationID: 当前组织id } + + + PUT /org/transferOrgUser // 用户转移组织 + 入参为 sysUserIDS: 需要操作的用户的ids []int, + organizationID: 原始组织ID int, + toOrganizationID: 目标组织ID int, + diff --git a/server/plugin/organization/api/api.go b/server/plugin/organization/api/api.go new file mode 100644 index 00000000..2939db3c --- /dev/null +++ b/server/plugin/organization/api/api.go @@ -0,0 +1,259 @@ +package api + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/request" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" + organization "github.com/flipped-aurora/gin-vue-admin/server/plugin/organization/model" + organizationReq "github.com/flipped-aurora/gin-vue-admin/server/plugin/organization/model/request" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/organization/service" + "github.com/gin-gonic/gin" + "go.uber.org/zap" +) + +type OrganizationApi struct { +} + +var orgService = service.ServiceGroupApp.OrganizationService + +// CreateOrganization 创建Organization +// @Tags Organization +// @Summary 创建Organization +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body organization.Organization true "创建Organization" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" +// @Router /org/createOrganization [post] +func (orgApi *OrganizationApi) CreateOrganization(c *gin.Context) { + var org organization.Organization + _ = c.ShouldBindJSON(&org) + if err := orgService.CreateOrganization(org); err != nil { + global.GVA_LOG.Error("创建失败!", zap.Error(err)) + response.FailWithMessage("创建失败", c) + } else { + response.OkWithMessage("创建成功", c) + } +} + +// DeleteOrganization 删除Organization +// @Tags Organization +// @Summary 删除Organization +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body organization.Organization true "删除Organization" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}" +// @Router /org/deleteOrganization [delete] +func (orgApi *OrganizationApi) DeleteOrganization(c *gin.Context) { + var org organization.Organization + _ = c.ShouldBindJSON(&org) + if err := orgService.DeleteOrganization(org); err != nil { + global.GVA_LOG.Error("删除失败!", zap.Error(err)) + response.FailWithMessage(err.Error(), c) + } else { + response.OkWithMessage("删除成功", c) + } +} + +// DeleteOrganizationByIds 批量删除Organization +// @Tags Organization +// @Summary 批量删除Organization +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body request.IdsReq true "批量删除Organization" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"批量删除成功"}" +// @Router /org/deleteOrganizationByIds [delete] +func (orgApi *OrganizationApi) DeleteOrganizationByIds(c *gin.Context) { + var IDS request.IdsReq + _ = c.ShouldBindJSON(&IDS) + if err := orgService.DeleteOrganizationByIds(IDS); err != nil { + global.GVA_LOG.Error("批量删除失败!", zap.Error(err)) + response.FailWithMessage("批量删除失败", c) + } else { + response.OkWithMessage("批量删除成功", c) + } +} + +// UpdateOrganization 更新Organization +// @Tags Organization +// @Summary 更新Organization +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data body organization.Organization true "更新Organization" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"更新成功"}" +// @Router /org/updateOrganization [put] +func (orgApi *OrganizationApi) UpdateOrganization(c *gin.Context) { + var org organization.Organization + _ = c.ShouldBindJSON(&org) + if err := orgService.UpdateOrganization(org); err != nil { + global.GVA_LOG.Error("更新失败!", zap.Error(err)) + response.FailWithMessage("更新失败", c) + } else { + response.OkWithMessage("更新成功", c) + } +} + +// FindOrganization 用id查询Organization +// @Tags Organization +// @Summary 用id查询Organization +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data query organization.Organization true "用id查询Organization" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}" +// @Router /org/findOrganization [get] +func (orgApi *OrganizationApi) FindOrganization(c *gin.Context) { + var org organization.Organization + _ = c.ShouldBindQuery(&org) + if reorg, err := orgService.GetOrganization(org.ID); err != nil { + global.GVA_LOG.Error("查询失败!", zap.Error(err)) + response.FailWithMessage("查询失败", c) + } else { + response.OkWithData(gin.H{"reorg": reorg}, c) + } +} + +// GetOrganizationList 分页获取Organization列表 +// @Tags Organization +// @Summary 分页获取Organization列表 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data query organizationReq.OrganizationSearch true "分页获取Organization列表" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" +// @Router /org/getOrganizationList [get] +func (orgApi *OrganizationApi) GetOrganizationList(c *gin.Context) { + var pageInfo organizationReq.OrganizationSearch + _ = c.ShouldBindQuery(&pageInfo) + if list, total, err := orgService.GetOrganizationInfoList(pageInfo); err != nil { + global.GVA_LOG.Error("获取失败!", zap.Error(err)) + response.FailWithMessage("获取失败", c) + } else { + response.OkWithDetailed(response.PageResult{ + List: list, + Total: total, + Page: pageInfo.Page, + PageSize: pageInfo.PageSize, + }, "获取成功", c) + } +} + +// GetOrganizationList 获取Organization树 +// @Tags Organization +// @Summary 获取Organization树 +// @Security ApiKeyAuth +// @accept application/json +// @Produce application/json +// @Param data query organizationReq.OrganizationSearch true "获取Organization树" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}" +// @Router /org/getOrganizationTree [get] +func (orgApi *OrganizationApi) GetOrganizationTree(c *gin.Context) { + if tree, err := orgService.GetOrganizationTree(); err != nil { + global.GVA_LOG.Error("获取失败!", zap.Error(err)) + response.FailWithMessage("获取失败", c) + } else { + response.OkWithDetailed(tree, "获取成功", c) + } +} + +func (orgApi *OrganizationApi) CreateOrgUser(c *gin.Context) { + var orgUser organization.OrgUserReq + c.ShouldBindJSON(&orgUser) + if err := orgService.CreateOrgUser(orgUser); err != nil { + global.GVA_LOG.Error("变更失败!", zap.Error(err)) + response.FailWithMessage("变更失败", c) + } else { + response.OkWithMessage("变更成功", c) + } +} + +func (orgApi *OrganizationApi) FindOrgUserAll(c *gin.Context) { + org := c.Query("organizationID") + if UserIds, err := orgService.FindOrgUserAll(org); err != nil { + global.GVA_LOG.Error("查询失败!", zap.Error(err)) + response.FailWithMessage("查询失败", c) + } else { + response.OkWithData(UserIds, c) + } +} + +func (orgApi *OrganizationApi) FindOrgUserList(c *gin.Context) { + var pageInfo organizationReq.OrgUserSearch + c.ShouldBindQuery(&pageInfo) + if list, total, err := orgService.GetOrgUserList(pageInfo); err != nil { + global.GVA_LOG.Error("获取失败!", zap.Error(err)) + response.FailWithMessage("获取失败", c) + } else { + response.OkWithDetailed(response.PageResult{ + List: list, + Total: total, + Page: pageInfo.Page, + PageSize: pageInfo.PageSize, + }, "获取成功", c) + } +} + +func (orgApi *OrganizationApi) SetOrgUserAdmin(c *gin.Context) { + var orgUser organization.OrgUser + c.ShouldBindJSON(&orgUser) + if err := orgService.SetOrgUserAdmin(orgUser.SysUserID, orgUser.IsAdmin); err != nil { + global.GVA_LOG.Error("设置失败!", zap.Error(err)) + response.FailWithMessage("设置失败", c) + } else { + response.OkWithMessage("设置成功", c) + } +} + +func (orgApi *OrganizationApi) SetOrgAuthority(c *gin.Context) { + var dataAuthority organization.DataAuthority + c.ShouldBindJSON(&dataAuthority) + if err := orgService.SetOrgAuthority(dataAuthority.AuthorityID, dataAuthority.AuthorityType); err != nil { + global.GVA_LOG.Error("设置失败!", zap.Error(err)) + response.FailWithMessage("设置失败", c) + } else { + response.OkWithMessage("设置成功", c) + } +} + +func (orgApi *OrganizationApi) SyncAuthority(c *gin.Context) { + if err := orgService.SyncAuthority(); err != nil { + global.GVA_LOG.Error("同步失败!", zap.Error(err)) + response.FailWithMessage(err.Error(), c) + } else { + response.OkWithMessage("同步成功", c) + } +} + +func (orgApi *OrganizationApi) GetAuthority(c *gin.Context) { + if authDataList, err := orgService.GetOrgAuthority(); err != nil { + global.GVA_LOG.Error("获取失败!", zap.Error(err)) + response.FailWithMessage("获取失败", c) + } else { + response.OkWithData(authDataList, c) + } +} + +func (orgApi *OrganizationApi) DeleteOrgUser(c *gin.Context) { + var orgUser organization.OrgUserReq + c.ShouldBindJSON(&orgUser) + if err := orgService.DeleteOrgUser(orgUser.SysUserIDS, orgUser.OrganizationID); err != nil { + global.GVA_LOG.Error("删除失败!", zap.Error(err)) + response.FailWithMessage("删除失败", c) + } else { + response.OkWithMessage("删除成功", c) + } +} + +func (orgApi *OrganizationApi) TransferOrgUser(c *gin.Context) { + var orgUser organization.OrgUserReq + c.ShouldBindJSON(&orgUser) + if err := orgService.TransferOrgUser(orgUser.SysUserIDS, orgUser.OrganizationID, orgUser.ToOrganizationID); err != nil { + global.GVA_LOG.Error("转移失败!", zap.Error(err)) + response.FailWithMessage("转移失败", c) + } else { + response.OkWithMessage("转移成功", c) + } +} diff --git a/server/plugin/organization/api/enter.go b/server/plugin/organization/api/enter.go new file mode 100644 index 00000000..c27fc67c --- /dev/null +++ b/server/plugin/organization/api/enter.go @@ -0,0 +1,7 @@ +package api + +type ApiGroup struct { + OrganizationApi +} + +var ApiGroupApp = new(ApiGroup) diff --git a/server/plugin/organization/config/config.go b/server/plugin/organization/config/config.go new file mode 100644 index 00000000..aeaaa917 --- /dev/null +++ b/server/plugin/organization/config/config.go @@ -0,0 +1 @@ +package config diff --git a/server/plugin/organization/global/global.go b/server/plugin/organization/global/global.go new file mode 100644 index 00000000..3941b9c5 --- /dev/null +++ b/server/plugin/organization/global/global.go @@ -0,0 +1 @@ +package global diff --git a/server/plugin/organization/main.go b/server/plugin/organization/main.go new file mode 100644 index 00000000..6cbdd2c7 --- /dev/null +++ b/server/plugin/organization/main.go @@ -0,0 +1,159 @@ +package organization + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/system" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/organization/model" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/organization/router" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/plugin-tool/utils" + "github.com/gin-gonic/gin" +) + +type OrganizationPlugin struct { +} + +func CreateOrganizationPlug() *OrganizationPlugin { + global.GVA_DB.AutoMigrate(model.Organization{}, model.OrgUser{}, model.DataAuthority{}) + utils.RegisterMenus( + system.SysBaseMenu{ + Path: "organizationGroup", + Name: "organizationGroup", + Hidden: false, + Component: "view/routerHolder.vue", + Sort: 1000, + Meta: system.Meta{ + Title: "组织管理", + Icon: "school", + }, + }, + system.SysBaseMenu{ + Path: "organization", + Name: "organization", + Hidden: false, + Component: "plugin/organization/view/index.vue", + Sort: 0, + Meta: system.Meta{ + Title: "组织管理", + Icon: "school", + }, + }, + system.SysBaseMenu{ + Path: "dataAuthority", + Name: "dataAuthority", + Hidden: false, + Component: "plugin/organization/view/dataAuthority.vue", + Sort: 1, + Meta: system.Meta{ + Title: "资源管理", + Icon: "money", + }, + }, + ) + utils.RegisterApis( + system.SysApi{ + Path: "/org/createOrganization", + Description: "创建组织", + ApiGroup: "组织管理", + Method: "POST", + }, + system.SysApi{ + Path: "/org/deleteOrganization", + Description: "删除组织", + ApiGroup: "组织管理", + Method: "DELETE", + }, + system.SysApi{ + Path: "/org/deleteOrganizationByIds", + Description: "批量删除组织", + ApiGroup: "组织管理", + Method: "DELETE", + }, + system.SysApi{ + Path: "/org/updateOrganization", + Description: "更新组织", + ApiGroup: "组织管理", + Method: "PUT", + }, + system.SysApi{ + Path: "/org/createOrgUser", + Description: "添加组织成员", + ApiGroup: "组织管理", + Method: "POST", + }, + system.SysApi{ + Path: "/org/setOrgUserAdmin", + Description: "设置管理员", + ApiGroup: "组织管理", + Method: "PUT", + }, + system.SysApi{ + Path: "/org/findOrganization", + Description: "获取所选组织", + ApiGroup: "组织管理", + Method: "GET", + }, + system.SysApi{ + Path: "/org/getOrganizationList", + Description: "获取组织列表", + ApiGroup: "组织管理", + Method: "GET", + }, + system.SysApi{ + Path: "/org/getOrganizationTree", + Description: "获取所有组织树", + ApiGroup: "组织管理", + Method: "GET", + }, + system.SysApi{ + Path: "/org/findOrgUserAll", + Description: "获取组织下所有用户", + ApiGroup: "组织管理", + Method: "GET", + }, + system.SysApi{ + Path: "/org/findOrgUserList", + Description: "获取组织下所有用户(分页)", + ApiGroup: "组织管理", + Method: "GET", + }, + system.SysApi{ + Path: "/org/deleteOrgUser", + Description: "删除当前组织下选中用户", + ApiGroup: "组织管理", + Method: "DELETE", + }, + system.SysApi{ + Path: "/org/transferOrgUser", + Description: "用户转移组织", + ApiGroup: "组织管理", + Method: "PUT", + }, + system.SysApi{ + Path: "/org/setDataAuthority", + Description: "设置资源权限", + ApiGroup: "组织管理", + Method: "PUT", + }, + system.SysApi{ + Path: "/org/syncAuthority", + Description: "同步角色数据", + ApiGroup: "组织管理", + Method: "POST", + }, + system.SysApi{ + Path: "/org/getAuthority", + Description: "获取所有资源权限", + ApiGroup: "组织管理", + Method: "GET", + }, + ) + return &OrganizationPlugin{} +} + +func (*OrganizationPlugin) Register(group *gin.RouterGroup) { + router.RouterGroupApp.InitOrganizationRouter(group) +} + +func (*OrganizationPlugin) RouterPath() string { + return "org" +} diff --git a/server/plugin/organization/model/model.go b/server/plugin/organization/model/model.go new file mode 100644 index 00000000..d606777f --- /dev/null +++ b/server/plugin/organization/model/model.go @@ -0,0 +1,45 @@ +package model + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/system" +) + +// Organization 结构体 +// 如果含有time.Time 请自行import time包 +type Organization struct { + global.GVA_MODEL + Name string `json:"name" form:"name" gorm:"column:name;comment:;"` + ParentID uint `json:"parentID" form:"parentID" gorm:"column:parent_id;comment:父节点ID;"` + Children []Organization `json:"children" gorm:"-"` +} + +// TableName Organization 表名 +func (Organization) TableName() string { + return "organization" +} + +type OrgUser struct { + Organization Organization `json:"organization"` + OrganizationID uint `json:"organizationID,omitempty" form:"organizationID" ` + SysUserID uint `json:"sysUserID,omitempty" form:"sysUserID"` + IsAdmin bool `json:"isAdmin" form:"isAdmin"` + SysUser system.SysUser `json:"sysUser"` +} + +type DataAuthority struct { + AuthorityID uint `json:"authorityID" gorm:"column:authority_id;comment:角色ID;"` + Authority system.SysAuthority `json:"authority"` + AuthorityType int `json:"authorityType" gorm:"column:authority_type;comment:角色权限标记;"` +} + +type OrgUserReq struct { + OrganizationID uint `json:"organizationID,omitempty"` + ToOrganizationID uint `json:"toOrganizationID,omitempty"` + SysUserIDS []uint `json:"sysUserIDS,omitempty"` +} + +// TableName Organization 表名 +func (OrgUser) TableName() string { + return "org_user" +} diff --git a/server/plugin/organization/model/request/main.go b/server/plugin/organization/model/request/main.go new file mode 100644 index 00000000..6ba9d1e1 --- /dev/null +++ b/server/plugin/organization/model/request/main.go @@ -0,0 +1,17 @@ +package request + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/model/common/request" + organization "github.com/flipped-aurora/gin-vue-admin/server/plugin/organization/model" +) + +type OrganizationSearch struct { + organization.Organization + request.PageInfo +} + +type OrgUserSearch struct { + organization.OrgUser + UserName string `json:"userName" form:"userName"` + request.PageInfo +} diff --git a/server/plugin/organization/router/enter.go b/server/plugin/organization/router/enter.go new file mode 100644 index 00000000..8320291c --- /dev/null +++ b/server/plugin/organization/router/enter.go @@ -0,0 +1,7 @@ +package router + +type RouterGroup struct { + OrganizationRouter +} + +var RouterGroupApp = new(RouterGroup) diff --git a/server/plugin/organization/router/router.go b/server/plugin/organization/router/router.go new file mode 100644 index 00000000..06e80dd1 --- /dev/null +++ b/server/plugin/organization/router/router.go @@ -0,0 +1,35 @@ +package router + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/middleware" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/organization/api" + "github.com/gin-gonic/gin" +) + +type OrganizationRouter struct{} + +func (s *OrganizationRouter) InitOrganizationRouter(Router *gin.RouterGroup) { + orgRouter := Router.Use(middleware.OperationRecord()) + orgRouterWithoutRecord := Router + var orgApi = api.ApiGroupApp.OrganizationApi + { + orgRouter.POST("createOrganization", orgApi.CreateOrganization) // 新建Organization + orgRouter.DELETE("deleteOrganization", orgApi.DeleteOrganization) // 删除Organization + orgRouter.DELETE("deleteOrganizationByIds", orgApi.DeleteOrganizationByIds) // 批量删除Organization + orgRouter.PUT("updateOrganization", orgApi.UpdateOrganization) // 更新Organization + orgRouter.POST("createOrgUser", orgApi.CreateOrgUser) // 人员入职 + orgRouter.PUT("setOrgUserAdmin", orgApi.SetOrgUserAdmin) // 管理员设置 + orgRouter.PUT("setDataAuthority", orgApi.SetOrgAuthority) // 设置资源权限 + orgRouter.POST("syncAuthority", orgApi.SyncAuthority) // 同步角色 + orgRouter.GET("getAuthority", orgApi.GetAuthority) // 获取资源权限 + } + { + orgRouterWithoutRecord.GET("findOrganization", orgApi.FindOrganization) // 根据ID获取Organization + orgRouterWithoutRecord.GET("getOrganizationList", orgApi.GetOrganizationList) // 获取Organization列表 + orgRouterWithoutRecord.GET("getOrganizationTree", orgApi.GetOrganizationTree) // 获取Organization树 + orgRouterWithoutRecord.GET("findOrgUserAll", orgApi.FindOrgUserAll) // 获取当前组织下所有用户ID + orgRouterWithoutRecord.GET("findOrgUserList", orgApi.FindOrgUserList) // 获取当前组织下所有用户(分页) + orgRouterWithoutRecord.DELETE("deleteOrgUser", orgApi.DeleteOrgUser) // 删除当前组织下选中用户 + orgRouterWithoutRecord.PUT("transferOrgUser", orgApi.TransferOrgUser) // 用户转移组织 + } +} diff --git a/server/plugin/organization/service/enter.go b/server/plugin/organization/service/enter.go new file mode 100644 index 00000000..4aaa7851 --- /dev/null +++ b/server/plugin/organization/service/enter.go @@ -0,0 +1,7 @@ +package service + +type ServiceGroup struct { + OrganizationService +} + +var ServiceGroupApp = new(ServiceGroup) diff --git a/server/plugin/organization/service/service.go b/server/plugin/organization/service/service.go new file mode 100644 index 00000000..21c0a012 --- /dev/null +++ b/server/plugin/organization/service/service.go @@ -0,0 +1,255 @@ +package service + +import ( + "errors" + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/request" + "github.com/flipped-aurora/gin-vue-admin/server/model/system" + organization "github.com/flipped-aurora/gin-vue-admin/server/plugin/organization/model" + organizationReq "github.com/flipped-aurora/gin-vue-admin/server/plugin/organization/model/request" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/organization/utils" + "gorm.io/gorm" +) + +type OrganizationService struct { +} + +// CreateOrganization 创建Organization记录 +// Author [piexlmax](https://github.com/piexlmax) +func (orgService *OrganizationService) CreateOrganization(org organization.Organization) (err error) { + err = global.GVA_DB.Create(&org).Error + return err +} + +// DeleteOrganization 删除Organization记录 +// Author [piexlmax](https://github.com/piexlmax) +func (orgService *OrganizationService) DeleteOrganization(org organization.Organization) (err error) { + err = global.GVA_DB.Where("parent_id = ?", org.ID).First(&organization.Organization{}).Error + if err == nil { + return errors.New("该组织有子组织,不能删除") + } + if errors.Is(err, gorm.ErrRecordNotFound) { + err = global.GVA_DB.Delete(&org, "id = ?", org.ID).Error + } + return err +} + +// DeleteOrganizationByIds 批量删除Organization记录 +// Author [piexlmax](https://github.com/piexlmax) +func (orgService *OrganizationService) DeleteOrganizationByIds(ids request.IdsReq) (err error) { + err = global.GVA_DB.Delete(&[]organization.Organization{}, "id in ?", ids.Ids).Error + return err +} + +// UpdateOrganization 更新Organization记录 +// Author [piexlmax](https://github.com/piexlmax) +func (orgService *OrganizationService) UpdateOrganization(org organization.Organization) (err error) { + var updatesmap = make(map[string]interface{}) + updatesmap["Name"] = org.Name + updatesmap["ParentID"] = org.ParentID + err = global.GVA_DB.Model(&organization.Organization{}).Where("id = ?", org.ID).Updates(updatesmap).Error + return err +} + +// GetOrganization 根据id获取Organization记录 +// Author [piexlmax](https://github.com/piexlmax) +func (orgService *OrganizationService) GetOrganization(id uint) (org organization.Organization, err error) { + err = global.GVA_DB.Where("id = ?", id).First(&org).Error + return +} + +// GetOrganizationInfoList 分页获取Organization记录 +// Author [piexlmax](https://github.com/piexlmax) +func (orgService *OrganizationService) GetOrganizationInfoList(info organizationReq.OrganizationSearch) (list interface{}, total int64, err error) { + // 创建db + db := global.GVA_DB.Model(&organization.Organization{}) + var orgs []organization.Organization + // 如果有条件搜索 下方会自动创建搜索语句 + db = db.Where("parent_id = ?", info.ParentID) + if info.Name != "" { + db = db.Where("name LIKE ?", "%"+info.Name+"%") + } + err = db.Count(&total).Error + if err != nil { + return + } + err = db.Find(&orgs).Error + return orgs, total, err +} + +func (orgService *OrganizationService) GetOrganizationTree() (OrgTree []organization.Organization, err error) { + var all []organization.Organization + err = global.GVA_DB.Find(&all).Error + if err != nil { + return + } + fmtOrgTree := orgService.fmtOrgTree(all, 0) + return fmtOrgTree, err +} + +func (orgService *OrganizationService) fmtOrgTree(orgs []organization.Organization, pid uint) (tree []organization.Organization) { + for i := range orgs { + if orgs[i].ParentID == pid { + orgs[i].Children = orgService.fmtOrgTree(orgs, orgs[i].ID) + tree = append(tree, orgs[i]) + } + } + return +} + +func (orgService *OrganizationService) CreateOrgUser(orgUser organization.OrgUserReq) error { + var Users []organization.OrgUser + var CUsers []organization.OrgUser + err := global.GVA_DB.Find(&Users, "organization_id = ?", orgUser.OrganizationID).Error + if err != nil { + return err + } + var UserIdMap = make(map[uint]bool) + for i := range Users { + UserIdMap[Users[i].SysUserID] = true + } + + for i := range orgUser.SysUserIDS { + if !UserIdMap[orgUser.SysUserIDS[i]] { + CUsers = append(CUsers, organization.OrgUser{SysUserID: orgUser.SysUserIDS[i], OrganizationID: orgUser.OrganizationID}) + } + } + err = global.GVA_DB.Create(&CUsers).Error + return err +} + +func (orgService *OrganizationService) FindOrgUserAll(orgID string) ([]uint, error) { + var Users []organization.OrgUser + var ids []uint + err := global.GVA_DB.Find(&Users, "organization_id = ?", orgID).Error + if err != nil { + return ids, err + } + for i := range Users { + ids = append(ids, Users[i].SysUserID) + } + return ids, err +} + +// GetOrganizationInfoList 分页获取当前组织下用户记录 +// Author [piexlmax](https://github.com/piexlmax) +func (orgService *OrganizationService) GetOrgUserList(info organizationReq.OrgUserSearch) (list interface{}, total int64, err error) { + limit := info.PageSize + offset := info.PageSize * (info.Page - 1) + // 创建db + db := global.GVA_DB.Model(&organization.OrgUser{}).Joins("SysUser").Preload("SysUser.Authority") + var orgs []organization.OrgUser + // 如果有条件搜索 下方会自动创建搜索语句 + db = db.Where("organization_id = ?", info.OrganizationID) + if info.UserName != "" { + db = db.Where("SysUser.nick_name LIKE ?", "%"+info.UserName+"%") + } + err = db.Count(&total).Error + if err != nil { + return + } + err = db.Limit(limit).Offset(offset).Find(&orgs).Error + return orgs, total, err +} + +func (orgService *OrganizationService) SetOrgUserAdmin(id uint, flag bool) (err error) { + return global.GVA_DB.Model(&organization.OrgUser{}).Where("sys_user_id = ?", id).Update("is_admin", flag).Error +} + +func (orgService *OrganizationService) SetOrgAuthority(authID uint, authorityType int) (err error) { + return global.GVA_DB.Model(&organization.DataAuthority{}).Where("authority_id = ?", authID).Update("authority_type", authorityType).Error +} + +func (orgService *OrganizationService) GetOrgAuthority() (authorityData []organization.DataAuthority, err error) { + err = global.GVA_DB.Preload("Authority").Find(&authorityData).Error + return authorityData, err +} + +func (orgService *OrganizationService) SyncAuthority() (err error) { + var authData []system.SysAuthority + var auth []organization.DataAuthority + return global.GVA_DB.Transaction(func(tx *gorm.DB) error { + var idMap = make(map[uint]*bool) + err := tx.Find(&authData).Error + if err != nil { + return err + } + for _, datum := range authData { + idMap[datum.AuthorityId] = utils.GetBoolPointer(true) + } + err = tx.Find(&auth).Error + if err != nil { + return err + } + for _, datum := range auth { + if idMap[datum.AuthorityID] != nil { + idMap[datum.AuthorityID] = utils.GetBoolPointer(false) + } else { + idMap[datum.AuthorityID] = nil + } + + } + var ayncData []organization.DataAuthority + var deleteAuth []organization.DataAuthority + + for k, _ := range idMap { + if idMap[k] != nil && *idMap[k] { + ayncData = append(ayncData, organization.DataAuthority{ + AuthorityID: k, + AuthorityType: 0, + }) + } + if idMap[k] == nil { + deleteAuth = append(deleteAuth, organization.DataAuthority{ + AuthorityID: k, + AuthorityType: 0, + }) + } + } + if len(ayncData) > 0 || len(deleteAuth) > 0 { + if len(ayncData) > 0 { + err := tx.Create(&ayncData).Error + + if err != nil { + return err + } + } + + if len(deleteAuth) > 0 { + var deleteAuthIds []uint + for i := range deleteAuth { + deleteAuthIds = append(deleteAuthIds, deleteAuth[i].AuthorityID) + } + err = tx.Delete(&deleteAuth, "authority_id in (?)", deleteAuthIds).Error + if err != nil { + return err + } + } + return nil + } else { + return errors.New("当前无需同步") + } + }) +} + +func (orgService *OrganizationService) DeleteOrgUser(ids []uint, orgID uint) (err error) { + return global.GVA_DB.Where("sys_user_id in (?) and organization_id = ?", ids, orgID).Delete(&[]organization.OrgUser{}).Error +} + +func (orgService *OrganizationService) TransferOrgUser(ids []uint, orgID, toOrgID uint) (err error) { + return global.GVA_DB.Transaction(func(tx *gorm.DB) error { + var CUsers []organization.OrgUser + err := global.GVA_DB.Where("sys_user_id in (?) and organization_id in (?)", ids, []uint{orgID, toOrgID}).Delete(&[]organization.OrgUser{}).Error + if err != nil { + return err + } + for i := range ids { + CUsers = append(CUsers, organization.OrgUser{SysUserID: ids[i], OrganizationID: toOrgID}) + } + err = global.GVA_DB.Create(&CUsers).Error + if err != nil { + return err + } + return nil + }) +} diff --git a/server/plugin/organization/utils/authFunc.go b/server/plugin/organization/utils/authFunc.go new file mode 100644 index 00000000..6f94afac --- /dev/null +++ b/server/plugin/organization/utils/authFunc.go @@ -0,0 +1,195 @@ +package utils + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/system" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/organization/model" + "github.com/flipped-aurora/gin-vue-admin/server/utils" + "github.com/gin-gonic/gin" +) + +// uint 去重方法 +func Uniq(array []uint) []uint { + var uintMap = make(map[uint]bool) + var res []uint + for _, u := range array { + if !uintMap[u] { + uintMap[u] = true + res = append(res, u) + } + } + return res +} + +const ( + Node = 0 // 无资源权限 + Self = 1 // 仅自己 + Current = 2 // 当前部门 + Deep = 3 // 当前部门及以下 + All = 4 // 所有 +) + +// 获取当前部门ID +func GetSelfOrg(id uint) []uint { + var orgUser []model.OrgUser + err := global.GVA_DB.Find(&orgUser, "sys_user_id = ?", id).Error + if err != nil { + return []uint{} + } + var orgId []uint + for _, m := range orgUser { + orgId = append(orgId, m.OrganizationID) + } + return Uniq(orgId) +} + +// 获取所有部门 +func GetAllOrg() []model.Organization { + var orgUser []model.Organization + err := global.GVA_DB.Find(&orgUser).Error + if err != nil { + return []model.Organization{} + } + return orgUser +} + +// 获取所有部门ID +func GetAllOrgID() []uint { + orgUser := GetAllOrg() + if len(orgUser) == 0 { + return []uint{} + } + var orgids []uint + for _, organization := range orgUser { + orgids = append(orgids, organization.ID) + } + return Uniq(orgids) +} + +// 获取当前部门及以下部门id +func GetDeepOrg(id uint) []uint { + orgId := GetSelfOrg(id) + if len(orgId) == 0 { + return []uint{} + } + orgs := GetAllOrg() + if len(orgs) == 0 { + return []uint{} + } + orgids := findChildren(orgId, orgs) + return Uniq(append(orgids, orgId...)) +} + +// 获取当前部门及以下部门的递归方法 +func findChildren(ids []uint, orgs []model.Organization) []uint { + var idsMap = make(map[uint]bool) + var resIDs []uint + for _, id := range ids { + idsMap[id] = true + } + for _, org := range orgs { + if idsMap[org.ParentID] { + resIDs = append(resIDs, org.ID) + } + } + if len(resIDs) == 0 { + resIDs = append(resIDs, ids...) + return resIDs + } + dids := findChildren(resIDs, orgs) + resIDs = append(resIDs, ids...) + resIDs = append(resIDs, dids...) + return resIDs +} + +// 获取当前部门的用户id +func GetCurrentUserIDs(id uint) []uint { + orgId := GetSelfOrg(id) + if len(orgId) == 0 { + return []uint{} + } + return GetUsersByOrgIds(orgId) +} + +// 获取当前部门及以下的用户id +func GetDeepUserIDs(id uint) []uint { + orgids := GetDeepOrg(id) + if len(orgids) == 0 { + return []uint{} + } + return GetUsersByOrgIds(orgids) +} + +// 根据部门获取部门下用户ID +func GetUsersByOrgIds(orgIds []uint) []uint { + var orgUser []model.OrgUser + err := global.GVA_DB.Find(&orgUser, "organization_id in (?)", orgIds).Error + if err != nil { + return []uint{} + } + var userIDS []uint + for _, m := range orgUser { + userIDS = append(userIDS, m.SysUserID) + } + return Uniq(userIDS) +} + +// 获取所有用户ID +func GetAllUserIDs() []uint { + var users []system.SysUser + err := global.GVA_DB.Find(&users).Error + if err != nil { + return []uint{} + } + var usersID []uint + for _, sysUser := range users { + usersID = append(usersID, sysUser.ID) + } + return Uniq(usersID) +} + +// 自动获取当前用户拥有的权限的用户ID +func GetUserIDS(c *gin.Context) []uint { + user := utils.GetUserInfo(c) + var data model.DataAuthority + err := global.GVA_DB.First(&data, "authority_id = ?", user.AuthorityId).Error + if err != nil { + return []uint{} + } + switch data.AuthorityType { + case Node: + return []uint{} + case Self: + return []uint{user.BaseClaims.ID} + case Current: + return GetCurrentUserIDs(user.BaseClaims.ID) + case Deep: + return GetDeepUserIDs(user.BaseClaims.ID) + case All: + return GetAllUserIDs() + } + return []uint{} +} + +// 自动获取当前用户拥有的权限的部门ID +func GetOrgIDS(c *gin.Context) []uint { + user := utils.GetUserInfo(c) + var data model.DataAuthority + err := global.GVA_DB.First(&data, "authority_id = ?", user.AuthorityId).Error + if err != nil { + return []uint{} + } + switch data.AuthorityType { + case Node: + return []uint{} + case Self: + return GetSelfOrg(user.BaseClaims.ID) + case Current: + return GetSelfOrg(user.BaseClaims.ID) + case Deep: + return GetDeepOrg(user.BaseClaims.ID) + case All: + return GetAllOrgID() + } + return []uint{} +} diff --git a/server/plugin/organization/utils/authFunc_test.go b/server/plugin/organization/utils/authFunc_test.go new file mode 100644 index 00000000..cab66927 --- /dev/null +++ b/server/plugin/organization/utils/authFunc_test.go @@ -0,0 +1,14 @@ +package utils + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/core" + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/initialize" + "testing" +) + +func TestGetAllUserIDs(t *testing.T) { + global.GVA_VP = core.Viper() // 初始化Viper + global.GVA_LOG = core.Zap() // 初始化zap日志库 + global.GVA_DB = initialize.Gorm() // gorm连接数据库 +} diff --git a/server/plugin/organization/utils/config.yaml b/server/plugin/organization/utils/config.yaml new file mode 100644 index 00000000..6b8c30ba --- /dev/null +++ b/server/plugin/organization/utils/config.yaml @@ -0,0 +1,160 @@ +aliyun-oss: + endpoint: yourEndpoint + access-key-id: yourAccessKeyId + access-key-secret: yourAccessKeySecret + bucket-name: yourBucketName + bucket-url: yourBucketUrl + base-path: yourBasePath +autocode: + transfer-restart: true + root: E:\gin-vue-admin + server: /server + server-api: /api/v1/%s + server-plug: /plugin/%s + server-initialize: /initialize + server-model: /model/%s + server-request: /model/%s/request/ + server-router: /router/%s + server-service: /service/%s + web: /web/src + web-api: /api + web-form: /view + web-table: /view +aws-s3: + bucket: xxxxx-10005608 + region: ap-shanghai + endpoint: "" + s3-force-path-style: false + disable-ssl: false + secret-id: xxxxxxxx + secret-key: xxxxxxxx + base-url: https://gin.vue.admin + path-prefix: github.com/flipped-aurora/gin-vue-admin/server +captcha: + key-long: 6 + img-width: 240 + img-height: 80 +cors: + mode: whitelist + whitelist: + - allow-origin: example1.com + allow-methods: GET, POST + allow-headers: content-type + expose-headers: Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, + Content-Type + allow-credentials: true + - allow-origin: example2.com + allow-methods: GET, POST + allow-headers: content-type + expose-headers: Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, + Content-Type + allow-credentials: true +db-list: +- disable: false + type: "" + alias-name: "" + path: "" + port: "" + config: "" + db-name: "" + username: "" + password: "" + max-idle-conns: 10 + max-open-conns: 100 + log-mode: "" + log-zap: false +email: + to: xxx@qq.com + port: 465 + from: xxx@163.com + host: smtp.163.com + is-ssl: true + secret: xxx + nickname: test +excel: + dir: ./resource/excel/ +hua-wei-obs: + path: you-path + bucket: you-bucket + endpoint: you-endpoint + access-key: you-access-key + secret-key: you-secret-key +jwt: + signing-key: 3a300b4a-3352-406d-ade3-0dac0b39cf60 + expires-time: 604800 + buffer-time: 86400 + issuer: qmPlus +local: + path: uploads/file + store-path: uploads/file +mysql: + path: 127.0.0.1 + port: "3306" + config: charset=utf8mb4&parseTime=True&loc=Local + db-name: gva + username: root + password: Aa@6447985 + max-idle-conns: 10 + max-open-conns: 100 + log-mode: error + log-zap: false +pgsql: + path: "" + port: "" + config: "" + db-name: "" + username: "" + password: "" + max-idle-conns: 10 + max-open-conns: 100 + log-mode: "" + log-zap: false +qiniu: + zone: ZoneHuaDong + bucket: "" + img-path: "" + use-https: falsee + access-key: "" + secret-key: "" + use-cdn-domains: false +redis: + db: 0 + addr: 127.0.0.1:6379 + password: "" +system: + env: public + addr: 8888 + db-type: mysql + oss-type: local + use-multipoint: false + use-redis: false + iplimit-count: 15000 + iplimit-time: 3600 +tencent-cos: + bucket: xxxxx-10005608 + region: ap-shanghai + secret-id: xxxxxxxx + secret-key: xxxxxxxx + base-url: https://gin.vue.admin + path-prefix: github.com/flipped-aurora/gin-vue-admin/server +timer: + start: true + spec: '@daily' + with_seconds: false + detail: + - tableName: sys_operation_records + compareField: created_at + interval: 2160h + - tableName: jwt_blacklists + compareField: created_at + interval: 168h +zap: + level: info + prefix: '[github.com/flipped-aurora/gin-vue-admin/server]' + format: console + director: log + encode-level: LowercaseColorLevelEncoder + stacktrace-key: stacktrace + max-age: 30 + show-line: true + log-in-console: true diff --git a/server/plugin/organization/utils/pointer.go b/server/plugin/organization/utils/pointer.go new file mode 100644 index 00000000..a89d6aa1 --- /dev/null +++ b/server/plugin/organization/utils/pointer.go @@ -0,0 +1,5 @@ +package utils + +func GetBoolPointer(t bool) *bool { + return &t +} diff --git a/web/src/config.json b/web/src/config.json index 08d2aebe..b5f6d498 100644 --- a/web/src/config.json +++ b/web/src/config.json @@ -1,10 +1,10 @@ {"weakness":false, - "grey":false, - "primaryColor":"#3b82f6", - "showTabs":true, - "darkMode":"auto", - "layout_side_width":200, - "layout_side_collapsed_width":60, - "layout_side_item_height":44, - "show_watermark":true, - "side_mode":"normal"} +"grey":false, +"primaryColor":"#3b82f6", +"showTabs":true, +"darkMode":"auto", +"layout_side_width":150, +"layout_side_collapsed_width":60, +"layout_side_item_height":44, +"show_watermark":true, +"side_mode":"combination"} diff --git a/web/src/plugin/organization/api/organization.js b/web/src/plugin/organization/api/organization.js new file mode 100644 index 00000000..af5810a3 --- /dev/null +++ b/web/src/plugin/organization/api/organization.js @@ -0,0 +1,127 @@ +import service from '@/utils/request' + +export const createOrganization = (data) => { + return service({ + url: '/org/createOrganization', + method: 'post', + data + }) +} + +export const findOrganization = (params) => { + return service({ + url: '/org/findOrganization', + method: 'get', + params + }) +} + +export const updateOrganization = (data) => { + return service({ + url: '/org/updateOrganization', + method: 'put', + data + }) +} + +export const deleteOrganizationByIds = (data) => { + return service({ + url: '/org/deleteOrganizationByIds', + method: 'delete', + data + }) +} + +export const deleteOrganization = (data) => { + return service({ + url: '/org/deleteOrganization', + method: 'delete', + data + }) +} + +export const getOrganizationList = (params) => { + return service({ + url: '/org/getOrganizationList', + method: 'get', + params + }) +} + +export const getOrganizationTree = () => { + return service({ + url: '/org/getOrganizationTree', + method: 'get', + }) +} + +export const findOrgUserAll = (params) => { + return service({ + url: '/org/findOrgUserAll', + method: 'get', + params + }) +} + +export const createOrgUser = (data) => { + return service({ + url: '/org/createOrgUser', + method: 'post', + data + }) +} + +export const findOrgUserList = (params) => { + return service({ + url: '/org/findOrgUserList', + method: 'get', + params + }) +} + +export const setOrgUserAdmin = (data) => { + return service({ + url: '/org/setOrgUserAdmin', + method: 'put', + data + }) +} + +export const deleteOrgUserApi = (data) => { + return service({ + url: '/org/deleteOrgUser', + method: 'delete', + data + }) +} + +export const transferOrgUserApi = (data) => { + return service({ + url: '/org/transferOrgUser', + method: 'put', + data + }) +} + +export const setDataAuthority = (data) => { + return service({ + url: '/org/setDataAuthority', + method: 'put', + data + }) +} + +export const syncAuthority = () => { + return service({ + url: '/org/syncAuthority', + method: 'post' + }) +} + +export const getAuthority = () => { + return service({ + url: '/org/getAuthority', + method: 'get' + }) +} + diff --git a/web/src/plugin/organization/view/dataAuthority.vue b/web/src/plugin/organization/view/dataAuthority.vue new file mode 100644 index 00000000..a3bce133 --- /dev/null +++ b/web/src/plugin/organization/view/dataAuthority.vue @@ -0,0 +1,71 @@ + + + + 资源权限管理 + + + + 同步角色数据 + + + + + + + {setDataAuthorityFun(row,val)}"> + + + + + + + + + + + + + + + + diff --git a/web/src/plugin/organization/view/index.vue b/web/src/plugin/organization/view/index.vue new file mode 100644 index 00000000..3f3727da --- /dev/null +++ b/web/src/plugin/organization/view/index.vue @@ -0,0 +1,662 @@ + + + + + + 新增组织 + + + + + + + {{ node.label }} + + + + + + + + + + + 新增子组织 + + + 编辑组织 + + + 删除组织 + + + + + + + + + + + + + + 搜索 + + + 更换组织 + item.sysUser.ID))" + >踢出组织 + 人员入职 + + + + + + + + + + + {{ row.isAdmin?"是":"否" }} + + + + + 更换组织 + 踢出组织 + 设置管理员 + 取消管理员 + + + + + + + + + + + + + + + + + + + + + 取消 + 确认 + + + + + + + + + + + + + + + + 取消 + 确定 + + + + + + + + + + + 取消 + 确定 + + + + + + + + + +