增加用户组功能
This commit is contained in:
6
main.go
6
main.go
@ -135,6 +135,12 @@ func Run() error {
|
|||||||
if err := global.DB.AutoMigrate(&model.Resource{}); err != nil {
|
if err := global.DB.AutoMigrate(&model.Resource{}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := global.DB.AutoMigrate(&model.UserGroup{}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := global.DB.AutoMigrate(&model.UserGroupMember{}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if err := global.DB.AutoMigrate(&model.Num{}); err != nil {
|
if err := global.DB.AutoMigrate(&model.Num{}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,17 @@ func SetupRoutes() *echo.Echo {
|
|||||||
users.GET("/:id", UserGetEndpoint)
|
users.GET("/:id", UserGetEndpoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
userGroups := e.Group("/user-groups")
|
||||||
|
{
|
||||||
|
userGroups.POST("", UserGroupCreateEndpoint)
|
||||||
|
userGroups.GET("/paging", UserGroupPagingEndpoint)
|
||||||
|
userGroups.PUT("/:id", UserGroupUpdateEndpoint)
|
||||||
|
userGroups.DELETE("/:id", UserGroupDeleteEndpoint)
|
||||||
|
userGroups.GET("/:id", UserGroupGetEndpoint)
|
||||||
|
userGroups.POST("/:id/members", UserGroupAddMembersEndpoint)
|
||||||
|
userGroups.DELETE("/:id/members/:memberId", UserGroupDelMembersEndpoint)
|
||||||
|
}
|
||||||
|
|
||||||
assets := e.Group("/assets", Auth)
|
assets := e.Group("/assets", Auth)
|
||||||
{
|
{
|
||||||
assets.GET("", AssetAllEndpoint)
|
assets.GET("", AssetAllEndpoint)
|
||||||
|
114
pkg/api/user-group.go
Normal file
114
pkg/api/user-group.go
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/labstack/echo/v4"
|
||||||
|
"next-terminal/pkg/global"
|
||||||
|
"next-terminal/pkg/model"
|
||||||
|
"next-terminal/pkg/utils"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserGroup struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Members []string `json:"members"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func UserGroupCreateEndpoint(c echo.Context) error {
|
||||||
|
var item UserGroup
|
||||||
|
if err := c.Bind(&item); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
userGroup := model.UserGroup{
|
||||||
|
ID: utils.UUID(),
|
||||||
|
Created: utils.NowJsonTime(),
|
||||||
|
Name: item.Name,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := model.CreateNewUserGroup(&userGroup, item.Members); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return Success(c, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
func UserGroupPagingEndpoint(c echo.Context) error {
|
||||||
|
pageIndex, _ := strconv.Atoi(c.QueryParam("pageIndex"))
|
||||||
|
pageSize, _ := strconv.Atoi(c.QueryParam("pageSize"))
|
||||||
|
name := c.QueryParam("name")
|
||||||
|
|
||||||
|
items, total, err := model.FindPageUserGroup(pageIndex, pageSize, name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return Success(c, H{
|
||||||
|
"total": total,
|
||||||
|
"items": items,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func UserGroupUpdateEndpoint(c echo.Context) error {
|
||||||
|
id := c.Param("id")
|
||||||
|
|
||||||
|
var item model.UserGroup
|
||||||
|
if err := c.Bind(&item); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
model.UpdateUserGroupById(&item, id)
|
||||||
|
|
||||||
|
return Success(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func UserGroupDeleteEndpoint(c echo.Context) error {
|
||||||
|
ids := c.Param("id")
|
||||||
|
split := strings.Split(ids, ",")
|
||||||
|
for i := range split {
|
||||||
|
userId := split[i]
|
||||||
|
model.DeleteUserGroupById(userId)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Success(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func UserGroupGetEndpoint(c echo.Context) error {
|
||||||
|
id := c.Param("id")
|
||||||
|
|
||||||
|
item, err := model.FindUserGroupById(id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return Success(c, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
func UserGroupAddMembersEndpoint(c echo.Context) error {
|
||||||
|
id := c.Param("id")
|
||||||
|
|
||||||
|
var items []string
|
||||||
|
if err := c.Bind(&items); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := model.AddUserGroupMembers(global.DB, items, id); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return Success(c, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func UserGroupDelMembersEndpoint(c echo.Context) (err error) {
|
||||||
|
id := c.Param("id")
|
||||||
|
memberIdsStr := c.Param("memberId")
|
||||||
|
memberIds := strings.Split(memberIdsStr, ",")
|
||||||
|
for i := range memberIds {
|
||||||
|
memberId := memberIds[i]
|
||||||
|
err = global.DB.Where("user_group_id = ? and user_id = ?", id, memberId).Delete(&model.UserGroupMember{}).Error
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Success(c, "")
|
||||||
|
}
|
1
pkg/model/asset-group.go
Normal file
1
pkg/model/asset-group.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package model
|
@ -43,11 +43,6 @@ func (r *Asset) TableName() string {
|
|||||||
return "assets"
|
return "assets"
|
||||||
}
|
}
|
||||||
|
|
||||||
func FindAllAsset() (o []Asset, err error) {
|
|
||||||
err = global.DB.Find(&o).Error
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func FindAssetByConditions(protocol string, account User) (o []Asset, err error) {
|
func FindAssetByConditions(protocol string, account User) (o []Asset, err error) {
|
||||||
db := global.DB.Table("assets").Select("assets.id,assets.name,assets.ip,assets.port,assets.protocol,assets.active,assets.owner,assets.created, users.nickname as owner_name,COUNT(resources.user_id) as sharer_count").Joins("left join users on assets.owner = users.id").Joins("left join resources on assets.id = resources.resource_id").Group("assets.id")
|
db := global.DB.Table("assets").Select("assets.id,assets.name,assets.ip,assets.port,assets.protocol,assets.active,assets.owner,assets.created, users.nickname as owner_name,COUNT(resources.user_id) as sharer_count").Joins("left join users on assets.owner = users.id").Joins("left join resources on assets.id = resources.resource_id").Group("assets.id")
|
||||||
|
|
||||||
|
11
pkg/model/user-group-member.go
Normal file
11
pkg/model/user-group-member.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
type UserGroupMember struct {
|
||||||
|
ID string `gorm:"primary_key" json:"name"`
|
||||||
|
UserId string `json:"userId"`
|
||||||
|
UserGroupId string `json:"userGroupId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *UserGroupMember) TableName() string {
|
||||||
|
return "user_group_members"
|
||||||
|
}
|
97
pkg/model/user-group.go
Normal file
97
pkg/model/user-group.go
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gorm.io/gorm"
|
||||||
|
"next-terminal/pkg/global"
|
||||||
|
"next-terminal/pkg/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserGroup struct {
|
||||||
|
ID string `gorm:"primary_key" json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Created utils.JsonTime `json:"created"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserGroupVo struct {
|
||||||
|
ID string `gorm:"primary_key" json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Created utils.JsonTime `json:"created"`
|
||||||
|
MemberCount int64 `json:"memberCount"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *UserGroup) TableName() string {
|
||||||
|
return "user_groups"
|
||||||
|
}
|
||||||
|
|
||||||
|
func FindPageUserGroup(pageIndex, pageSize int, name string) (o []UserGroupVo, total int64, err error) {
|
||||||
|
db := global.DB.Table("user_groups").Select("user_groups.id, user_groups.name, user_groups.created, count(user_group_members.user_id) as member_count").Joins("left join user_group_members on user_groups.id = user_group_members.user_group_id").Group("user_groups.id")
|
||||||
|
dbCounter := global.DB.Table("user_groups")
|
||||||
|
if len(name) > 0 {
|
||||||
|
db = db.Where("user_groups.name like ?", "%"+name+"%")
|
||||||
|
dbCounter = dbCounter.Where("name like ?", "%"+name+"%")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = dbCounter.Count(&total).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
err = db.Order("user_groups.created desc").Find(&o).Offset((pageIndex - 1) * pageSize).Limit(pageSize).Error
|
||||||
|
if o == nil {
|
||||||
|
o = make([]UserGroupVo, 0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateNewUserGroup(o *UserGroup, members []string) (err error) {
|
||||||
|
return global.DB.Transaction(func(tx *gorm.DB) error {
|
||||||
|
err = tx.Create(o).Error
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if members != nil {
|
||||||
|
userGroupId := o.ID
|
||||||
|
err = AddUserGroupMembers(tx, members, userGroupId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddUserGroupMembers(tx *gorm.DB, userIds []string, userGroupId string) error {
|
||||||
|
for i := range userIds {
|
||||||
|
userId := userIds[i]
|
||||||
|
_, err := FindUserById(userId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
userGroupMember := UserGroupMember{
|
||||||
|
ID: utils.Sign([]string{userGroupId, userId}),
|
||||||
|
UserId: userId,
|
||||||
|
UserGroupId: userGroupId,
|
||||||
|
}
|
||||||
|
err = tx.Create(&userGroupMember).Error
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func FindUserGroupById(id string) (o UserGroup, err error) {
|
||||||
|
err = global.DB.Where("id = ?", id).First(&o).Error
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateUserGroupById(o *UserGroup, id string) {
|
||||||
|
o.ID = id
|
||||||
|
global.DB.Updates(o)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteUserGroupById(id string) {
|
||||||
|
global.DB.Where("id = ?", id).Delete(&UserGroup{})
|
||||||
|
global.DB.Where("user_group_id = ?", id).Delete(&UserGroupMember{})
|
||||||
|
}
|
@ -35,6 +35,7 @@ import Setting from "./components/setting/Setting";
|
|||||||
import BatchCommand from "./components/command/BatchCommand";
|
import BatchCommand from "./components/command/BatchCommand";
|
||||||
import {NT_PACKAGE} from "./utils/utils";
|
import {NT_PACKAGE} from "./utils/utils";
|
||||||
import {isAdmin} from "./service/permission";
|
import {isAdmin} from "./service/permission";
|
||||||
|
import UserGroup from "./components/user/UserGroup";
|
||||||
|
|
||||||
const {Footer, Sider} = Layout;
|
const {Footer, Sider} = Layout;
|
||||||
|
|
||||||
@ -188,25 +189,18 @@ class App extends Component {
|
|||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
</SubMenu>
|
</SubMenu>
|
||||||
|
|
||||||
{/*<SubMenu key='user-team' title='用户管理' icon={<UserSwitchOutlined />}>*/}
|
<SubMenu key='user-group' title='用户管理' icon={<UserSwitchOutlined />}>
|
||||||
{/* <Menu.Item key="team" icon={<TeamOutlined />}>*/}
|
<Menu.Item key="user" icon={<UserOutlined/>}>
|
||||||
{/* <Link to={'/team'}>*/}
|
<Link to={'/user'}>
|
||||||
{/* 用户组管理*/}
|
用户管理
|
||||||
{/* </Link>*/}
|
</Link>
|
||||||
{/* </Menu.Item>*/}
|
</Menu.Item>
|
||||||
|
<Menu.Item key="user-group" icon={<TeamOutlined />}>
|
||||||
{/* <Menu.Item key="user" icon={<UserOutlined/>}>*/}
|
<Link to={'/user-group'}>
|
||||||
{/* <Link to={'/user'}>*/}
|
用户组管理
|
||||||
{/* 用户管理*/}
|
</Link>
|
||||||
{/* </Link>*/}
|
</Menu.Item>
|
||||||
{/* </Menu.Item>*/}
|
</SubMenu>
|
||||||
{/*</SubMenu>*/}
|
|
||||||
|
|
||||||
<Menu.Item key="user" icon={<UserOutlined/>}>
|
|
||||||
<Link to={'/user'}>
|
|
||||||
用户管理
|
|
||||||
</Link>
|
|
||||||
</Menu.Item>
|
|
||||||
|
|
||||||
</> : undefined
|
</> : undefined
|
||||||
}
|
}
|
||||||
@ -246,6 +240,7 @@ class App extends Component {
|
|||||||
|
|
||||||
<Route path="/" exact component={Dashboard}/>
|
<Route path="/" exact component={Dashboard}/>
|
||||||
<Route path="/user" component={User}/>
|
<Route path="/user" component={User}/>
|
||||||
|
<Route path="/user-group" component={UserGroup}/>
|
||||||
<Route path="/asset" component={Asset}/>
|
<Route path="/asset" component={Asset}/>
|
||||||
<Route path="/credential" component={Credential}/>
|
<Route path="/credential" component={Credential}/>
|
||||||
<Route path="/dynamic-command" component={DynamicCommand}/>
|
<Route path="/dynamic-command" component={DynamicCommand}/>
|
||||||
|
@ -466,7 +466,7 @@ class Asset extends Component {
|
|||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
|
|
||||||
{isAdmin() ?
|
{isAdmin() ?
|
||||||
<Menu.Item key="1">
|
<Menu.Item key="3">
|
||||||
<Button type="text" size='small'
|
<Button type="text" size='small'
|
||||||
disabled={!hasPermission(record['owner'])}
|
disabled={!hasPermission(record['owner'])}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@ -488,7 +488,7 @@ class Asset extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
<Menu.Item key="2">
|
<Menu.Item key="4">
|
||||||
<Button type="text" size='small'
|
<Button type="text" size='small'
|
||||||
disabled={!hasPermission(record['owner'])}
|
disabled={!hasPermission(record['owner'])}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
@ -497,7 +497,7 @@ class Asset extends Component {
|
|||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
|
|
||||||
<Menu.Divider/>
|
<Menu.Divider/>
|
||||||
<Menu.Item key="3">
|
<Menu.Item key="5">
|
||||||
<Button type="text" size='small' danger
|
<Button type="text" size='small' danger
|
||||||
disabled={!hasPermission(record['owner'])}
|
disabled={!hasPermission(record['owner'])}
|
||||||
onClick={() => this.showDeleteConfirm(record.id, record.name)}>删除</Button>
|
onClick={() => this.showDeleteConfirm(record.id, record.name)}>删除</Button>
|
||||||
|
@ -165,7 +165,7 @@ class OfflineSession extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleSearchByNickname = async nickname => {
|
handleSearchByNickname = async nickname => {
|
||||||
const result = await request.get(`/users/paging?pageIndex=1&pageSize=100&nickname=${nickname}`);
|
const result = await request.get(`/users/paging?pageIndex=1&pageSize=1000&nickname=${nickname}`);
|
||||||
if (result.code !== 1) {
|
if (result.code !== 1) {
|
||||||
message.error(result.message, 10);
|
message.error(result.message, 10);
|
||||||
return;
|
return;
|
||||||
|
@ -369,7 +369,7 @@ class User extends Component {
|
|||||||
<Button icon={<UndoOutlined/>} onClick={() => {
|
<Button icon={<UndoOutlined/>} onClick={() => {
|
||||||
this.inputRefOfUsername.current.setValue('');
|
this.inputRefOfUsername.current.setValue('');
|
||||||
this.inputRefOfNickname.current.setValue('');
|
this.inputRefOfNickname.current.setValue('');
|
||||||
this.loadTableData({pageIndex: 1, pageSize: 10, protocol: ''})
|
this.loadTableData({pageIndex: 1, pageSize: 10})
|
||||||
}}>
|
}}>
|
||||||
|
|
||||||
</Button>
|
</Button>
|
||||||
@ -483,21 +483,15 @@ class User extends Component {
|
|||||||
loading={this.state.loading}
|
loading={this.state.loading}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{
|
<UserModal
|
||||||
this.state.modalVisible ?
|
visible={this.state.modalVisible}
|
||||||
<UserModal
|
title={this.state.modalTitle}
|
||||||
visible={this.state.modalVisible}
|
handleOk={this.handleOk}
|
||||||
title={this.state.modalTitle}
|
handleCancel={this.handleCancelModal}
|
||||||
handleOk={this.handleOk}
|
confirmLoading={this.state.modalConfirmLoading}
|
||||||
handleCancel={this.handleCancelModal}
|
model={this.state.model}
|
||||||
confirmLoading={this.state.modalConfirmLoading}
|
>
|
||||||
model={this.state.model}
|
</UserModal>
|
||||||
>
|
|
||||||
</UserModal>
|
|
||||||
: null
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
</Content>
|
</Content>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
403
web/src/components/user/UserGroup.js
Normal file
403
web/src/components/user/UserGroup.js
Normal file
@ -0,0 +1,403 @@
|
|||||||
|
import React, {Component} from 'react';
|
||||||
|
import {itemRender} from '../../utils/utils'
|
||||||
|
|
||||||
|
import {Button, Col, Divider, Input, Layout, Modal, PageHeader, Row, Space, Table, Tooltip, Typography,} from "antd";
|
||||||
|
import qs from "qs";
|
||||||
|
import request from "../../common/request";
|
||||||
|
import {message} from "antd/es";
|
||||||
|
import {DeleteOutlined, ExclamationCircleOutlined, PlusOutlined, SyncOutlined, UndoOutlined} from '@ant-design/icons';
|
||||||
|
import Logout from "./Logout";
|
||||||
|
import UserGroupModal from "./UserGroupModal";
|
||||||
|
|
||||||
|
const confirm = Modal.confirm;
|
||||||
|
const {Search} = Input;
|
||||||
|
const {Title, Text} = Typography;
|
||||||
|
const {Content} = Layout;
|
||||||
|
|
||||||
|
const routes = [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
breadcrumbName: '首页',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'user',
|
||||||
|
breadcrumbName: '用户组',
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
class UserGroup extends Component {
|
||||||
|
|
||||||
|
inputRefOfName = React.createRef();
|
||||||
|
|
||||||
|
state = {
|
||||||
|
items: [],
|
||||||
|
total: 0,
|
||||||
|
queryParams: {
|
||||||
|
pageIndex: 1,
|
||||||
|
pageSize: 10
|
||||||
|
},
|
||||||
|
loading: false,
|
||||||
|
modalVisible: false,
|
||||||
|
modalTitle: '',
|
||||||
|
modalConfirmLoading: false,
|
||||||
|
model: null,
|
||||||
|
selectedRowKeys: [],
|
||||||
|
delBtnLoading: false,
|
||||||
|
users: [],
|
||||||
|
members: []
|
||||||
|
};
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.loadTableData();
|
||||||
|
}
|
||||||
|
|
||||||
|
async delete(id) {
|
||||||
|
let result = await request.delete('/user-groups/' + id);
|
||||||
|
if (result.code === 1) {
|
||||||
|
message.success('操作成功', 3);
|
||||||
|
await this.loadTableData(this.state.queryParams);
|
||||||
|
} else {
|
||||||
|
message.error('删除失败 :( ' + result.message, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async loadTableData(queryParams) {
|
||||||
|
this.setState({
|
||||||
|
loading: true
|
||||||
|
});
|
||||||
|
|
||||||
|
queryParams = queryParams || this.state.queryParams;
|
||||||
|
|
||||||
|
let paramsStr = qs.stringify(queryParams);
|
||||||
|
|
||||||
|
let data = {
|
||||||
|
items: [],
|
||||||
|
total: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
let result = await request.get('/user-groups/paging?' + paramsStr);
|
||||||
|
if (result.code === 1) {
|
||||||
|
data = result.data;
|
||||||
|
} else {
|
||||||
|
message.error(result.message, 10);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
const items = data.items.map(item => {
|
||||||
|
return {'key': item['id'], ...item}
|
||||||
|
})
|
||||||
|
this.setState({
|
||||||
|
items: items,
|
||||||
|
total: data.total,
|
||||||
|
queryParams: queryParams,
|
||||||
|
loading: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
handleChangPage = (pageIndex, pageSize) => {
|
||||||
|
let queryParams = this.state.queryParams;
|
||||||
|
queryParams.pageIndex = pageIndex;
|
||||||
|
queryParams.pageSize = pageSize;
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
queryParams: queryParams
|
||||||
|
});
|
||||||
|
|
||||||
|
this.loadTableData(queryParams).then(r => {
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
showDeleteConfirm(id, content) {
|
||||||
|
let self = this;
|
||||||
|
confirm({
|
||||||
|
title: '您确定要删除此用户组吗?',
|
||||||
|
content: content,
|
||||||
|
okText: '确定',
|
||||||
|
okType: 'danger',
|
||||||
|
cancelText: '取消',
|
||||||
|
onOk() {
|
||||||
|
self.delete(id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
showModal(title, user = {}) {
|
||||||
|
|
||||||
|
this.handleSearchByNickname('');
|
||||||
|
this.setState({
|
||||||
|
model: user,
|
||||||
|
modalVisible: true,
|
||||||
|
modalTitle: title
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
handleCancelModal = e => {
|
||||||
|
this.setState({
|
||||||
|
modalVisible: false,
|
||||||
|
modalTitle: ''
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
handleOk = async (formData) => {
|
||||||
|
// 弹窗 form 传来的数据
|
||||||
|
this.setState({
|
||||||
|
modalConfirmLoading: true
|
||||||
|
});
|
||||||
|
|
||||||
|
if (formData.id) {
|
||||||
|
// 向后台提交数据
|
||||||
|
const result = await request.put('/user-groups/' + formData.id, formData);
|
||||||
|
if (result.code === 1) {
|
||||||
|
message.success('操作成功', 3);
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
modalVisible: false
|
||||||
|
});
|
||||||
|
await this.loadTableData(this.state.queryParams);
|
||||||
|
} else {
|
||||||
|
message.error('操作失败 :( ' + result.message, 10);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 向后台提交数据
|
||||||
|
const result = await request.post('/user-groups', formData);
|
||||||
|
if (result.code === 1) {
|
||||||
|
message.success('操作成功', 3);
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
modalVisible: false
|
||||||
|
});
|
||||||
|
await this.loadTableData(this.state.queryParams);
|
||||||
|
} else {
|
||||||
|
message.error('操作失败 :( ' + result.message, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
modalConfirmLoading: false
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
handleSearchByName = name => {
|
||||||
|
let query = {
|
||||||
|
...this.state.queryParams,
|
||||||
|
'pageIndex': 1,
|
||||||
|
'pageSize': this.state.queryParams.pageSize,
|
||||||
|
'name': name,
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loadTableData(query);
|
||||||
|
};
|
||||||
|
|
||||||
|
batchDelete = async () => {
|
||||||
|
this.setState({
|
||||||
|
delBtnLoading: true
|
||||||
|
})
|
||||||
|
try {
|
||||||
|
let result = await request.delete('/user-groups/' + this.state.selectedRowKeys.join(','));
|
||||||
|
if (result.code === 1) {
|
||||||
|
message.success('操作成功', 3);
|
||||||
|
this.setState({
|
||||||
|
selectedRowKeys: []
|
||||||
|
})
|
||||||
|
await this.loadTableData(this.state.queryParams);
|
||||||
|
} else {
|
||||||
|
message.error('删除失败 :( ' + result.message, 10);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
this.setState({
|
||||||
|
delBtnLoading: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSearchByNickname = async nickname => {
|
||||||
|
const result = await request.get(`/users/paging?pageIndex=1&pageSize=1000&nickname=${nickname}`);
|
||||||
|
if (result.code !== 1) {
|
||||||
|
message.error(result.message, 10);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
users: result.data.items
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
|
||||||
|
const columns = [{
|
||||||
|
title: '序号',
|
||||||
|
dataIndex: 'id',
|
||||||
|
key: 'id',
|
||||||
|
render: (id, record, index) => {
|
||||||
|
return index + 1;
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
title: '名称',
|
||||||
|
dataIndex: 'name',
|
||||||
|
}, {
|
||||||
|
title: '成员人数',
|
||||||
|
dataIndex: 'memberCount',
|
||||||
|
key: 'memberCount',
|
||||||
|
render: (text, record, index) => {
|
||||||
|
return <Button type='link' onClick={async () => {
|
||||||
|
await this.handleShowSharer(record, true);
|
||||||
|
}}>{text}</Button>
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
title: '创建日期',
|
||||||
|
dataIndex: 'created',
|
||||||
|
key: 'created'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
key: 'action',
|
||||||
|
render: (text, record) => {
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Button type="link" size='small'
|
||||||
|
onClick={() => this.showModal('更新用户组', record)}>编辑</Button>
|
||||||
|
<Button type="link" size='small'
|
||||||
|
onClick={() => this.showDeleteConfirm(record.id, record.name)}>删除</Button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const selectedRowKeys = this.state.selectedRowKeys;
|
||||||
|
const rowSelection = {
|
||||||
|
selectedRowKeys: this.state.selectedRowKeys,
|
||||||
|
onChange: (selectedRowKeys, selectedRows) => {
|
||||||
|
this.setState({selectedRowKeys});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const hasSelected = selectedRowKeys.length > 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<PageHeader
|
||||||
|
className="site-page-header-ghost-wrapper page-herder"
|
||||||
|
title="用户组管理"
|
||||||
|
breadcrumb={{
|
||||||
|
routes: routes,
|
||||||
|
itemRender: itemRender
|
||||||
|
}}
|
||||||
|
extra={[
|
||||||
|
<Logout key='logout'/>
|
||||||
|
]}
|
||||||
|
subTitle="平台用户管理"
|
||||||
|
>
|
||||||
|
</PageHeader>
|
||||||
|
|
||||||
|
<Content className="site-layout-background page-content">
|
||||||
|
<div style={{marginBottom: 20}}>
|
||||||
|
<Row justify="space-around" align="middle" gutter={24}>
|
||||||
|
<Col span={8} key={1}>
|
||||||
|
<Title level={3}>用户组列表</Title>
|
||||||
|
</Col>
|
||||||
|
<Col span={16} key={2} style={{textAlign: 'right'}}>
|
||||||
|
<Space>
|
||||||
|
|
||||||
|
<Search
|
||||||
|
ref={this.inputRefOfName}
|
||||||
|
placeholder="名称"
|
||||||
|
allowClear
|
||||||
|
onSearch={this.handleSearchByName}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Tooltip title='重置查询'>
|
||||||
|
|
||||||
|
<Button icon={<UndoOutlined/>} onClick={() => {
|
||||||
|
this.inputRefOfName.current.setValue('');
|
||||||
|
this.loadTableData({pageIndex: 1, pageSize: 10})
|
||||||
|
}}>
|
||||||
|
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
<Divider type="vertical"/>
|
||||||
|
|
||||||
|
<Tooltip title="新增">
|
||||||
|
<Button type="dashed" icon={<PlusOutlined/>}
|
||||||
|
onClick={() => this.showModal('新增用户组', {})}>
|
||||||
|
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
<Tooltip title="刷新列表">
|
||||||
|
<Button icon={<SyncOutlined/>} onClick={() => {
|
||||||
|
this.loadTableData(this.state.queryParams)
|
||||||
|
}}>
|
||||||
|
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
<Tooltip title="批量删除">
|
||||||
|
<Button type="primary" danger disabled={!hasSelected} icon={<DeleteOutlined/>}
|
||||||
|
loading={this.state.delBtnLoading}
|
||||||
|
onClick={() => {
|
||||||
|
const content = <div>
|
||||||
|
您确定要删除选中的<Text style={{color: '#1890FF'}}
|
||||||
|
strong>{this.state.selectedRowKeys.length}</Text>条记录吗?
|
||||||
|
</div>;
|
||||||
|
confirm({
|
||||||
|
icon: <ExclamationCircleOutlined/>,
|
||||||
|
content: content,
|
||||||
|
onOk: () => {
|
||||||
|
this.batchDelete()
|
||||||
|
},
|
||||||
|
onCancel() {
|
||||||
|
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}}>
|
||||||
|
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
</Space>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Table rowSelection={rowSelection}
|
||||||
|
dataSource={this.state.items}
|
||||||
|
columns={columns}
|
||||||
|
position={'both'}
|
||||||
|
pagination={{
|
||||||
|
showSizeChanger: true,
|
||||||
|
current: this.state.queryParams.pageIndex,
|
||||||
|
pageSize: this.state.queryParams.pageSize,
|
||||||
|
onChange: this.handleChangPage,
|
||||||
|
onShowSizeChange: this.handleChangPage,
|
||||||
|
total: this.state.total,
|
||||||
|
showTotal: total => `总计 ${total} 条`
|
||||||
|
}}
|
||||||
|
loading={this.state.loading}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<UserGroupModal
|
||||||
|
visible={this.state.modalVisible}
|
||||||
|
title={this.state.modalTitle}
|
||||||
|
handleOk={this.handleOk}
|
||||||
|
handleCancel={this.handleCancelModal}
|
||||||
|
confirmLoading={this.state.modalConfirmLoading}
|
||||||
|
model={this.state.model}
|
||||||
|
users={this.state.users}
|
||||||
|
members={this.state.members}
|
||||||
|
>
|
||||||
|
</UserGroupModal>
|
||||||
|
|
||||||
|
</Content>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UserGroup;
|
69
web/src/components/user/UserGroupModal.js
Normal file
69
web/src/components/user/UserGroupModal.js
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import React, {useState} from 'react';
|
||||||
|
import {Form, Input, Modal, Select} from "antd/lib/index";
|
||||||
|
|
||||||
|
const UserGroupModal = ({
|
||||||
|
title,
|
||||||
|
visible,
|
||||||
|
handleOk,
|
||||||
|
handleCancel,
|
||||||
|
confirmLoading,
|
||||||
|
model,
|
||||||
|
users,
|
||||||
|
members,
|
||||||
|
}) => {
|
||||||
|
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
|
||||||
|
const formItemLayout = {
|
||||||
|
labelCol: {span: 6},
|
||||||
|
wrapperCol: {span: 14},
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
title={title}
|
||||||
|
visible={visible}
|
||||||
|
maskClosable={false}
|
||||||
|
onOk={() => {
|
||||||
|
form
|
||||||
|
.validateFields()
|
||||||
|
.then(values => {
|
||||||
|
form.resetFields();
|
||||||
|
handleOk(values);
|
||||||
|
})
|
||||||
|
.catch(info => {
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
onCancel={handleCancel}
|
||||||
|
confirmLoading={confirmLoading}
|
||||||
|
okText='确定'
|
||||||
|
cancelText='取消'
|
||||||
|
>
|
||||||
|
|
||||||
|
<Form form={form} {...formItemLayout} initialValues={model}>
|
||||||
|
<Form.Item name='id' noStyle>
|
||||||
|
<Input hidden={true}/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item label="名称" name='name' rules={[{required: true, message: '请输入用户组名称'}]}>
|
||||||
|
<Input autoComplete="off" placeholder="请输入用户组名称"/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item label="用户组成员" name='members'>
|
||||||
|
<Select
|
||||||
|
// showSearch
|
||||||
|
mode="multiple"
|
||||||
|
allowClear
|
||||||
|
placeholder='用户组成员'
|
||||||
|
filterOption={false}
|
||||||
|
>
|
||||||
|
{users.map(d => <Select.Option key={d.id}
|
||||||
|
value={d.id}>{d.nickname}</Select.Option>)}
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UserGroupModal;
|
@ -19,7 +19,7 @@ const UserModal = ({title, visible, handleOk, handleCancel, confirmLoading, mode
|
|||||||
form
|
form
|
||||||
.validateFields()
|
.validateFields()
|
||||||
.then(values => {
|
.then(values => {
|
||||||
// form.resetFields();
|
form.resetFields();
|
||||||
handleOk(values);
|
handleOk(values);
|
||||||
})
|
})
|
||||||
.catch(info => {
|
.catch(info => {
|
||||||
|
Reference in New Issue
Block a user