完成资产隔离

This commit is contained in:
dushixiang 2021-01-17 23:54:05 +08:00
parent a0610b8ce0
commit 28d17accd2
16 changed files with 1072 additions and 63 deletions

View File

@ -33,9 +33,11 @@ func AssetPagingEndpoint(c echo.Context) error {
name := c.QueryParam("name") name := c.QueryParam("name")
protocol := c.QueryParam("protocol") protocol := c.QueryParam("protocol")
tags := c.QueryParam("tags") tags := c.QueryParam("tags")
owner := c.QueryParam("owner")
sharer := c.QueryParam("sharer")
account, _ := GetCurrentAccount(c) account, _ := GetCurrentAccount(c)
items, total, _ := model.FindPageAsset(pageIndex, pageSize, name, protocol, tags, account) items, total, _ := model.FindPageAsset(pageIndex, pageSize, name, protocol, tags, account, owner, sharer)
return Success(c, H{ return Success(c, H{
"total": total, "total": total,

View File

@ -6,6 +6,12 @@ import (
"strings" "strings"
) )
type RU struct {
UserId string `json:"userId"`
ResourceType string `json:"resourceType"`
ResourceIds []string `json:"resourceIds"`
}
func ResourceGetAssignEndPoint(c echo.Context) error { func ResourceGetAssignEndPoint(c echo.Context) error {
resourceId := c.Param("id") resourceId := c.Param("id")
userIds, err := model.FindUserIdsByResourceId(resourceId) userIds, err := model.FindUserIdsByResourceId(resourceId)
@ -25,3 +31,29 @@ func ResourceOverwriteAssignEndPoint(c echo.Context) error {
return Success(c, "") return Success(c, "")
} }
func ResourceRemoveByUserIdAssignEndPoint(c echo.Context) error {
var ru RU
if err := c.Bind(&ru); err != nil {
return err
}
if err := model.DeleteByUserIdAndResourceTypeAndResourceIdIn(ru.UserId, ru.ResourceType, ru.ResourceIds); err != nil {
return err
}
return Success(c, "")
}
func ResourceAddByUserIdAssignEndPoint(c echo.Context) error {
var ru RU
if err := c.Bind(&ru); err != nil {
return err
}
if err := model.AddSharerResources(ru.UserId, ru.ResourceType, ru.ResourceIds); err != nil {
return err
}
return Success(c, "")
}

View File

@ -58,8 +58,8 @@ func SetupRoutes() *echo.Echo {
userGroups.PUT("/:id", UserGroupUpdateEndpoint) userGroups.PUT("/:id", UserGroupUpdateEndpoint)
userGroups.DELETE("/:id", UserGroupDeleteEndpoint) userGroups.DELETE("/:id", UserGroupDeleteEndpoint)
userGroups.GET("/:id", UserGroupGetEndpoint) userGroups.GET("/:id", UserGroupGetEndpoint)
userGroups.POST("/:id/members", UserGroupAddMembersEndpoint) //userGroups.POST("/:id/members", UserGroupAddMembersEndpoint)
userGroups.DELETE("/:id/members/:memberId", UserGroupDelMembersEndpoint) //userGroups.DELETE("/:id/members/:memberId", UserGroupDelMembersEndpoint)
} }
assets := e.Group("/assets", Auth) assets := e.Group("/assets", Auth)
@ -119,6 +119,8 @@ func SetupRoutes() *echo.Echo {
{ {
resources.GET("/:id/assign", ResourceGetAssignEndPoint) resources.GET("/:id/assign", ResourceGetAssignEndPoint)
resources.POST("/:id/assign", ResourceOverwriteAssignEndPoint) resources.POST("/:id/assign", ResourceOverwriteAssignEndPoint)
resources.POST("/remove", ResourceRemoveByUserIdAssignEndPoint)
resources.POST("/add", ResourceAddByUserIdAssignEndPoint)
} }
e.GET("/properties", PropertyGetEndpoint) e.GET("/properties", PropertyGetEndpoint)

View File

@ -10,6 +10,7 @@ import (
) )
type UserGroup struct { type UserGroup struct {
Id string `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Members []string `json:"members"` Members []string `json:"members"`
} }
@ -52,12 +53,17 @@ func UserGroupPagingEndpoint(c echo.Context) error {
func UserGroupUpdateEndpoint(c echo.Context) error { func UserGroupUpdateEndpoint(c echo.Context) error {
id := c.Param("id") id := c.Param("id")
var item model.UserGroup var item UserGroup
if err := c.Bind(&item); err != nil { if err := c.Bind(&item); err != nil {
return err return err
} }
userGroup := model.UserGroup{
Name: item.Name,
}
model.UpdateUserGroupById(&item, id) if err := model.UpdateUserGroupById(&userGroup, item.Members, id); err != nil {
return err
}
return Success(c, nil) return Success(c, nil)
} }
@ -81,7 +87,18 @@ func UserGroupGetEndpoint(c echo.Context) error {
return err return err
} }
return Success(c, item) members, err := model.FindUserGroupMembersByUserGroupId(id)
if err != nil {
return err
}
userGroup := UserGroup{
Id: item.ID,
Name: item.Name,
Members: members,
}
return Success(c, userGroup)
} }
func UserGroupAddMembersEndpoint(c echo.Context) error { func UserGroupAddMembersEndpoint(c echo.Context) error {

View File

@ -58,7 +58,7 @@ func FindAssetByConditions(protocol string, account User) (o []Asset, err error)
return return
} }
func FindPageAsset(pageIndex, pageSize int, name, protocol, tags string, account User) (o []AssetVo, total int64, err error) { func FindPageAsset(pageIndex, pageSize int, name, protocol, tags string, account User, owner, sharer string) (o []AssetVo, total int64, 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")
dbCounter := global.DB.Table("assets").Select("DISTINCT assets.id").Joins("left join resources on assets.id = resources.resource_id") dbCounter := global.DB.Table("assets").Select("DISTINCT assets.id").Joins("left join resources on assets.id = resources.resource_id")
@ -66,6 +66,15 @@ func FindPageAsset(pageIndex, pageSize int, name, protocol, tags string, account
owner := account.ID owner := account.ID
db = db.Where("assets.owner = ? or resources.user_id = ?", owner, owner) db = db.Where("assets.owner = ? or resources.user_id = ?", owner, owner)
dbCounter = dbCounter.Where("assets.owner = ? or resources.user_id = ?", owner, owner) dbCounter = dbCounter.Where("assets.owner = ? or resources.user_id = ?", owner, owner)
} else {
if len(owner) > 0 {
db = db.Where("assets.owner = ?", owner)
dbCounter = dbCounter.Where("assets.owner = ?", owner)
}
if len(sharer) > 0 {
db = db.Where("resources.user_id = ?", sharer)
dbCounter = dbCounter.Where("resources.user_id = ?", sharer)
}
} }
if len(name) > 0 { if len(name) > 0 {

View File

@ -1,6 +1,7 @@
package model package model
import ( import (
"gorm.io/gorm"
"next-terminal/pkg/global" "next-terminal/pkg/global"
"next-terminal/pkg/utils" "next-terminal/pkg/utils"
) )
@ -45,3 +46,28 @@ func OverwriteUserIdsByResourceId(resourceId, resourceType string, userIds []str
} }
db.Commit() db.Commit()
} }
func DeleteByUserIdAndResourceTypeAndResourceIdIn(userId, resourceType string, resourceIds []string) error {
return global.DB.Where("user_id = ? and resource_type = ? and resource_id in ?", userId, resourceType, resourceIds).Delete(&Resource{}).Error
}
func AddSharerResources(userId, resourceType string, resourceIds []string) error {
return global.DB.Transaction(func(tx *gorm.DB) (err error) {
for i := range resourceIds {
resourceId := resourceIds[i]
id := utils.Sign([]string{resourceId, resourceType, userId})
resource := &Resource{
ID: id,
ResourceId: resourceId,
ResourceType: resourceType,
UserId: userId,
}
err = tx.Create(resource).Error
if err != nil {
return err
}
}
return nil
})
}

View File

@ -1,5 +1,7 @@
package model package model
import "next-terminal/pkg/global"
type UserGroupMember struct { type UserGroupMember struct {
ID string `gorm:"primary_key" json:"name"` ID string `gorm:"primary_key" json:"name"`
UserId string `json:"userId"` UserId string `json:"userId"`
@ -9,3 +11,8 @@ type UserGroupMember struct {
func (r *UserGroupMember) TableName() string { func (r *UserGroupMember) TableName() string {
return "user_group_members" return "user_group_members"
} }
func FindUserGroupMembersByUserGroupId(id string) (o []string, err error) {
err = global.DB.Table("user_group_members").Select("user_id").Where("user_group_id = ?", id).Find(&o).Error
return
}

View File

@ -86,9 +86,28 @@ func FindUserGroupById(id string) (o UserGroup, err error) {
return return
} }
func UpdateUserGroupById(o *UserGroup, id string) { func UpdateUserGroupById(o *UserGroup, members []string, id string) error {
o.ID = id return global.DB.Transaction(func(tx *gorm.DB) error {
global.DB.Updates(o) o.ID = id
err := tx.Updates(o).Error
if err != nil {
return err
}
err = tx.Where("user_group_id = ?", id).Delete(&UserGroupMember{}).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 DeleteUserGroupById(id string) { func DeleteUserGroupById(id string) {

View File

@ -23,6 +23,18 @@ type User struct {
Type string `json:"type"` Type string `json:"type"`
} }
type UserVo struct {
ID string `gorm:"primary_key" json:"id"`
Username string `json:"username"`
Nickname string `json:"nickname"`
Online bool `json:"online"`
Enabled bool `json:"enabled"`
Created utils.JsonTime `json:"created"`
Type string `json:"type"`
//OwnerAssetCount int64 `json:"ownerAssetCount"`
SharerAssetCount int64 `json:"sharerAssetCount"`
}
func (r *User) TableName() string { func (r *User) TableName() string {
return "users" return "users"
} }
@ -38,19 +50,27 @@ func FindAllUser() (o []User) {
return return
} }
func FindPageUser(pageIndex, pageSize int, username, nickname string) (o []User, total int64, err error) { func FindPageUser(pageIndex, pageSize int, username, nickname string) (o []UserVo, total int64, err error) {
db := global.DB db := global.DB.Table("users").Select("users.id,users.username,users.nickname,users.online,users.enabled,users.created,users.type, count(resources.user_id) as sharer_asset_count").Joins("left join resources on users.id = resources.user_id and resources.resource_type = 'asset'").Group("users.id")
dbCounter := global.DB.Table("users")
if len(username) > 0 { if len(username) > 0 {
db = db.Where("username like ?", "%"+username+"%") db = db.Where("users.username like ?", "%"+username+"%")
dbCounter = dbCounter.Where("username like ?", "%"+username+"%")
} }
if len(nickname) > 0 { if len(nickname) > 0 {
db = db.Where("nickname like ?", "%"+nickname+"%") db = db.Where("users.nickname like ?", "%"+nickname+"%")
dbCounter = dbCounter.Where("nickname like ?", "%"+nickname+"%")
} }
err = db.Order("created desc").Find(&o).Offset((pageIndex - 1) * pageSize).Limit(pageSize).Count(&total).Error err = dbCounter.Count(&total).Error
if err != nil {
return nil, 0, err
}
err = db.Order("users.created desc").Find(&o).Offset((pageIndex - 1) * pageSize).Limit(pageSize).Error
if o == nil { if o == nil {
o = make([]User, 0) o = make([]UserVo, 0)
} }
return return
} }

View File

@ -88,6 +88,7 @@ class Asset extends Component {
}; };
async componentDidMount() { async componentDidMount() {
this.loadTableData(); this.loadTableData();
let result = await request.get('/tags'); let result = await request.get('/tags');

View File

@ -21,17 +21,9 @@ import qs from "qs";
import UserModal from "./UserModal"; import UserModal from "./UserModal";
import request from "../../common/request"; import request from "../../common/request";
import {message} from "antd/es"; import {message} from "antd/es";
import { import {DeleteOutlined, ExclamationCircleOutlined, PlusOutlined, SyncOutlined, UndoOutlined} from '@ant-design/icons';
DeleteOutlined,
DownOutlined,
ExclamationCircleOutlined,
IssuesCloseOutlined,
PlusOutlined,
StopOutlined,
SyncOutlined,
UndoOutlined
} from '@ant-design/icons';
import Logout from "./Logout"; import Logout from "./Logout";
import UserShareAsset from "./UserShareAsset";
const confirm = Modal.confirm; const confirm = Modal.confirm;
const {Search} = Input; const {Search} = Input;
@ -68,6 +60,7 @@ class User extends Component {
model: null, model: null,
selectedRowKeys: [], selectedRowKeys: [],
delBtnLoading: false, delBtnLoading: false,
assetVisible: false
}; };
componentDidMount() { componentDidMount() {
@ -230,14 +223,14 @@ class User extends Component {
}) })
try { try {
let result = await request.delete('/users/' + this.state.selectedRowKeys.join(',')); let result = await request.delete('/users/' + this.state.selectedRowKeys.join(','));
if (result.code === 1) { if (result['code'] === 1) {
message.success('操作成功', 3); message.success('操作成功', 3);
this.setState({ this.setState({
selectedRowKeys: [] selectedRowKeys: []
}) })
await this.loadTableData(this.state.queryParams); await this.loadTableData(this.state.queryParams);
} else { } else {
message.error('删除失败 :( ' + result.message, 10); message.error(result['message'], 10);
} }
} finally { } finally {
this.setState({ this.setState({
@ -246,6 +239,13 @@ class User extends Component {
} }
} }
handleAssetCancel = () => {
this.loadTableData()
this.setState({
assetVisible: false
})
}
render() { render() {
const columns = [{ const columns = [{
@ -293,6 +293,18 @@ class User extends Component {
return (<Badge status="default" text="离线"/>); return (<Badge status="default" text="离线"/>);
} }
} }
}, {
title: '共享资产',
dataIndex: 'sharerAssetCount',
key: 'sharerAssetCount',
render: (text, record, index) => {
return <Button type='link' onClick={async () => {
this.setState({
assetVisible: true,
sharer: record['id']
})
}}>{text}</Button>
}
}, { }, {
title: '创建日期', title: '创建日期',
dataIndex: 'created', dataIndex: 'created',
@ -483,15 +495,39 @@ class User extends Component {
loading={this.state.loading} loading={this.state.loading}
/> />
<UserModal {/* 为了屏蔽ant modal 关闭后数据仍然遗留的问题*/}
visible={this.state.modalVisible} {
title={this.state.modalTitle} this.state.modalVisible ?
handleOk={this.handleOk} <UserModal
handleCancel={this.handleCancelModal} visible={this.state.modalVisible}
confirmLoading={this.state.modalConfirmLoading} title={this.state.modalTitle}
model={this.state.model} handleOk={this.handleOk}
handleCancel={this.handleCancelModal}
confirmLoading={this.state.modalConfirmLoading}
model={this.state.model}
>
</UserModal> : undefined
}
<Modal
width={window.innerWidth * 0.8}
title='已共享资产'
visible={this.state.assetVisible}
maskClosable={false}
destroyOnClose={true}
onOk={() => {
}}
onCancel={this.handleAssetCancel}
okText='确定'
cancelText='取消'
footer={null}
> >
</UserModal> <UserShareAsset
sharer={this.state.sharer}
owner={this.state.owner}
/>
</Modal>
</Content> </Content>
</> </>
); );

View File

@ -40,11 +40,10 @@ class UserGroup extends Component {
modalVisible: false, modalVisible: false,
modalTitle: '', modalTitle: '',
modalConfirmLoading: false, modalConfirmLoading: false,
model: null, model: undefined,
selectedRowKeys: [], selectedRowKeys: [],
delBtnLoading: false, delBtnLoading: false,
users: [], users: [],
members: []
}; };
componentDidMount() { componentDidMount() {
@ -125,11 +124,35 @@ class UserGroup extends Component {
}); });
}; };
showModal(title, user = {}) { showModal = async (title, id, index) => {
this.handleSearchByNickname(''); let items = this.state.items;
let model = {}
if (id) {
items[index].updateBtnLoading = true;
this.setState({
items: items
});
let result = await request.get('/user-groups/' + id);
if (result['code'] !== 1) {
message.error(result['message']);
items[index].updateBtnLoading = false;
this.setState({
items: items
});
return;
}
items[index].updateBtnLoading = false;
model = result['data']
}
await this.handleSearchByNickname('');
console.log(model)
this.setState({ this.setState({
model: user, model: model,
modalVisible: true, modalVisible: true,
modalTitle: title modalTitle: title
}); });
@ -138,7 +161,8 @@ class UserGroup extends Component {
handleCancelModal = e => { handleCancelModal = e => {
this.setState({ this.setState({
modalVisible: false, modalVisible: false,
modalTitle: '' modalTitle: '',
model: undefined
}); });
}; };
@ -243,9 +267,7 @@ class UserGroup extends Component {
dataIndex: 'memberCount', dataIndex: 'memberCount',
key: 'memberCount', key: 'memberCount',
render: (text, record, index) => { render: (text, record, index) => {
return <Button type='link' onClick={async () => { return <Button type='link' onClick={() => this.showModal('更新用户组', record['id'], index)}>{text}</Button>
await this.handleShowSharer(record, true);
}}>{text}</Button>
} }
}, { }, {
title: '创建日期', title: '创建日期',
@ -255,13 +277,12 @@ class UserGroup extends Component {
{ {
title: '操作', title: '操作',
key: 'action', key: 'action',
render: (text, record) => { render: (text, record, index) => {
return ( return (
<div> <div>
<Button type="link" size='small' <Button type="link" size='small'
onClick={() => this.showModal('更新用户组', record)}>编辑</Button> loading={this.state.items[index].updateBtnLoading}
onClick={() => this.showModal('更新用户组', record['id'], index)}>编辑</Button>
<Button type="link" size='small' <Button type="link" size='small'
onClick={() => this.showDeleteConfirm(record.id, record.name)}>删除</Button> onClick={() => this.showDeleteConfirm(record.id, record.name)}>删除</Button>
</div> </div>
@ -325,7 +346,7 @@ class UserGroup extends Component {
<Tooltip title="新增"> <Tooltip title="新增">
<Button type="dashed" icon={<PlusOutlined/>} <Button type="dashed" icon={<PlusOutlined/>}
onClick={() => this.showModal('新增用户组', {})}> onClick={() => this.showModal('新增用户组')}>
</Button> </Button>
</Tooltip> </Tooltip>
@ -382,17 +403,20 @@ class UserGroup extends Component {
loading={this.state.loading} loading={this.state.loading}
/> />
<UserGroupModal {/* 为了屏蔽ant modal 关闭后数据仍然遗留的问题*/}
visible={this.state.modalVisible} {this.state.modalVisible ?
title={this.state.modalTitle} <UserGroupModal
handleOk={this.handleOk} visible={this.state.modalVisible}
handleCancel={this.handleCancelModal} title={this.state.modalTitle}
confirmLoading={this.state.modalConfirmLoading} handleOk={this.handleOk}
model={this.state.model} handleCancel={this.handleCancelModal}
users={this.state.users} confirmLoading={this.state.modalConfirmLoading}
members={this.state.members} model={this.state.model}
> users={this.state.users}
</UserGroupModal> >
</UserGroupModal> : undefined
}
</Content> </Content>
</> </>

View File

@ -1,4 +1,4 @@
import React, {useState} from 'react'; import React from 'react';
import {Form, Input, Modal, Select} from "antd/lib/index"; import {Form, Input, Modal, Select} from "antd/lib/index";
const UserGroupModal = ({ const UserGroupModal = ({
@ -9,7 +9,6 @@ const UserGroupModal = ({
confirmLoading, confirmLoading,
model, model,
users, users,
members,
}) => { }) => {
const [form] = Form.useForm(); const [form] = Form.useForm();
@ -24,6 +23,7 @@ const UserGroupModal = ({
title={title} title={title}
visible={visible} visible={visible}
maskClosable={false} maskClosable={false}
destroyOnClose={true}
onOk={() => { onOk={() => {
form form
.validateFields() .validateFields()

View File

@ -0,0 +1,392 @@
import React, {Component} from 'react';
import {
Badge,
Button,
Col,
Divider,
Drawer,
Input,
Layout,
Modal,
Row,
Select,
Space,
Table,
Tag,
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 {PROTOCOL_COLORS} from "../../common/constants";
import UserShareChooseAsset from "./UserShareSelectAsset";
const confirm = Modal.confirm;
const {Search} = Input;
const {Content} = Layout;
const {Title, Text} = Typography;
class UserShareAsset extends Component {
inputRefOfName = React.createRef();
changeOwnerFormRef = React.createRef();
state = {
items: [],
total: 0,
queryParams: {
pageIndex: 1,
pageSize: 10,
protocol: ''
},
loading: false,
tags: [],
model: {},
selectedRowKeys: [],
delBtnLoading: false,
changeOwnerModalVisible: false,
changeSharerModalVisible: false,
changeOwnerConfirmLoading: false,
changeSharerConfirmLoading: false,
users: [],
selected: {},
selectedSharers: [],
chooseAssetVisible: false
};
async componentDidMount() {
let sharer = this.props.sharer;
this.loadTableData({sharer: sharer});
let result = await request.get('/tags');
if (result['code'] === 1) {
this.setState({
tags: result['data']
})
}
}
async loadTableData(queryParams) {
this.setState({
loading: true
});
queryParams = queryParams || this.state.queryParams;
// queryParams
let paramsStr = qs.stringify(queryParams);
let data = {
items: [],
total: 0
};
try {
let result = await request.get('/assets/paging?' + paramsStr);
if (result['code'] === 1) {
data = result['data'];
} else {
message.error(result['message']);
}
} 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 = async (pageIndex, pageSize) => {
let queryParams = this.state.queryParams;
queryParams.pageIndex = pageIndex;
queryParams.pageSize = pageSize;
this.setState({
queryParams: queryParams
});
await this.loadTableData(queryParams)
};
handleSearchByName = name => {
let query = {
...this.state.queryParams,
'pageIndex': 1,
'pageSize': this.state.queryParams.pageSize,
'name': name,
}
this.loadTableData(query);
};
handleTagsChange = tags => {
console.log(tags)
// this.setState({
// tags: tags
// })
let query = {
...this.state.queryParams,
'pageIndex': 1,
'pageSize': this.state.queryParams.pageSize,
'tags': tags.join(','),
}
this.loadTableData(query);
}
handleSearchByProtocol = protocol => {
let query = {
...this.state.queryParams,
'pageIndex': 1,
'pageSize': this.state.queryParams.pageSize,
'protocol': protocol,
}
this.loadTableData(query);
}
render() {
const columns = [{
title: '序号',
dataIndex: 'id',
key: 'id',
render: (id, record, index) => {
return index + 1;
}
}, {
title: '资产名称',
dataIndex: 'name',
key: 'name',
render: (name, record) => {
let short = name;
if (short && short.length > 20) {
short = short.substring(0, 20) + " ...";
}
return (
<Tooltip placement="topLeft" title={name}>
{short}
</Tooltip>
);
}
}, {
title: '网络',
dataIndex: 'ip',
key: 'ip',
render: (text, record) => {
return record['ip'] + ':' + record['port'];
}
}, {
title: '连接协议',
dataIndex: 'protocol',
key: 'protocol',
render: (text, record) => {
return (<Tag color={PROTOCOL_COLORS[text]}>{text}</Tag>);
}
}, {
title: '状态',
dataIndex: 'active',
key: 'active',
render: text => {
if (text) {
return (<Badge status="processing" text="运行中"/>);
} else {
return (<Badge status="error" text="不可用"/>);
}
}
}, {
title: '所有者',
dataIndex: 'ownerName',
key: 'ownerName'
}, {
title: '创建日期',
dataIndex: 'created',
key: 'created'
}
];
const selectedRowKeys = this.state.selectedRowKeys;
const rowSelection = {
selectedRowKeys: this.state.selectedRowKeys,
onChange: (selectedRowKeys, selectedRows) => {
this.setState({selectedRowKeys});
},
};
const hasSelected = selectedRowKeys.length > 0;
return (
<>
<Content key='page-content' className="site-layout-background">
<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}
/>
<Select mode="multiple"
allowClear
placeholder="资产标签" onChange={this.handleTagsChange}
style={{minWidth: 150}}>
{this.state.tags.map(tag => {
if (tag === '-') {
return undefined;
}
return (<Select.Option key={tag}>{tag}</Select.Option>)
})}
</Select>
<Select onChange={this.handleSearchByProtocol}
value={this.state.queryParams.protocol ? this.state.queryParams.protocol : ''}
style={{width: 100}}>
<Select.Option value="">全部协议</Select.Option>
<Select.Option value="rdp">rdp</Select.Option>
<Select.Option value="ssh">ssh</Select.Option>
<Select.Option value="vnc">vnc</Select.Option>
<Select.Option value="telnet">telnet</Select.Option>
</Select>
<Tooltip title='重置查询'>
<Button icon={<UndoOutlined/>} onClick={() => {
this.inputRefOfName.current.setValue('');
this.loadTableData({
...this.state.queryParams,
pageIndex: 1,
pageSize: 10,
protocol: ''
})
}}>
</Button>
</Tooltip>
<Divider type="vertical"/>
<Tooltip title="新增">
<Button type="dashed" icon={<PlusOutlined/>}
onClick={() => {
this.setState({
chooseAssetVisible: true
})
}}>
</Button>
</Tooltip>
<Tooltip title="刷新列表">
<Button icon={<SyncOutlined/>} onClick={() => {
this.loadTableData(this.state.queryParams)
}}>
</Button>
</Tooltip>
<Tooltip title="移除共享">
<Button type="dashed" 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: async () => {
let userId = this.state.queryParams.sharer;
let result = await request.post(`/resources/remove`, {
userId: userId,
resourceType: 'asset',
resourceIds: this.state.selectedRowKeys
});
if (result['code'] === 1) {
message.success('操作成功', 3);
this.setState({
selectedRowKeys: []
})
await this.loadTableData();
} else {
message.error(result['message'], 10);
}
},
onCancel() {
},
});
}}>
</Button>
</Tooltip>
</Space>
</Col>
</Row>
</div>
<Table key='assets-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}
/>
{this.state.chooseAssetVisible ?
<Drawer
title="添加资产"
placement="right"
closable={true}
onClose={() => {
this.loadTableData()
this.setState({
chooseAssetVisible: false
})
}}
visible={this.state.chooseAssetVisible}
width={window.innerWidth * 0.8}
>
<UserShareChooseAsset
sharer={this.state.queryParams.sharer}
>
</UserShareChooseAsset>
</Drawer> : undefined
}
</Content>
</>
);
}
}
export default UserShareAsset;

View File

@ -0,0 +1,416 @@
import React, {Component} from 'react';
import {
Badge,
Button,
Col,
Divider,
Input,
Layout,
Modal,
Row,
Select,
Space,
Table,
Tag,
Tooltip,
Typography
} from "antd";
import qs from "qs";
import request from "../../common/request";
import {message} from "antd/es";
import {PlusOutlined, SyncOutlined, UndoOutlined} from '@ant-design/icons';
import {PROTOCOL_COLORS} from "../../common/constants";
const confirm = Modal.confirm;
const {Search} = Input;
const {Content} = Layout;
const {Title, Text} = Typography;
class UserShareAsset extends Component {
inputRefOfName = React.createRef();
changeOwnerFormRef = React.createRef();
state = {
items: [],
total: 0,
queryParams: {
pageIndex: 1,
pageSize: 10,
protocol: ''
},
loading: false,
tags: [],
model: {},
selectedRowKeys: [],
selectedRows: [],
delBtnLoading: false,
changeOwnerModalVisible: false,
changeSharerModalVisible: false,
changeOwnerConfirmLoading: false,
changeSharerConfirmLoading: false,
users: [],
selected: {},
totalSelectedRows: [],
sharer: ''
};
async componentDidMount() {
this.setState({
sharer: this.props.sharer
})
let r2 = await request.get('/assets/paging?pageIndex=1&pageSize=1000&sharer=' + this.props.sharer);
if (r2['code'] === 1) {
let items = r2['data']['items'];
this.setState({
totalSelectedRows: items
})
}
this.loadTableData();
let result = await request.get('/tags');
if (result['code'] === 1) {
this.setState({
tags: result['data']
})
}
}
async loadTableData(queryParams) {
this.setState({
loading: true
});
queryParams = queryParams || this.state.queryParams;
// queryParams
let paramsStr = qs.stringify(queryParams);
let data = {
items: [],
total: 0
};
try {
let result = await request.get('/assets/paging?' + paramsStr);
if (result['code'] === 1) {
data = result['data'];
} else {
message.error(result['message']);
}
} catch (e) {
} finally {
const items = data.items.map(item => {
return {'key': item['id'], ...item}
})
let totalSelectedRows = this.state.totalSelectedRows;
let selectedRowKeys = totalSelectedRows.map(item => item['id']);
this.setState({
items: items,
total: data.total,
queryParams: queryParams,
loading: false,
selectedRowKeys: selectedRowKeys
});
}
}
handleChangPage = async (pageIndex, pageSize) => {
let queryParams = this.state.queryParams;
queryParams.pageIndex = pageIndex;
queryParams.pageSize = pageSize;
this.setState({
queryParams: queryParams
});
await this.loadTableData(queryParams)
};
handleSearchByName = name => {
let query = {
...this.state.queryParams,
'pageIndex': 1,
'pageSize': this.state.queryParams.pageSize,
'name': name,
}
this.loadTableData(query);
};
handleTagsChange = tags => {
console.log(tags)
// this.setState({
// tags: tags
// })
let query = {
...this.state.queryParams,
'pageIndex': 1,
'pageSize': this.state.queryParams.pageSize,
'tags': tags.join(','),
}
this.loadTableData(query);
}
handleSearchByProtocol = protocol => {
let query = {
...this.state.queryParams,
'pageIndex': 1,
'pageSize': this.state.queryParams.pageSize,
'protocol': protocol,
}
this.loadTableData(query);
}
unSelectRow = async (assetId) => {
let userId = this.state.sharer;
let result = await request.post(`/resources/remove`, {
userId: userId,
resourceType: 'asset',
resourceIds: [assetId]
});
if (result['code'] === 1) {
message.success('操作成功', 3);
} else {
message.error(result['message'], 10);
}
const selectedRowKeys = this.state.selectedRowKeys.filter(key => key !== assetId);
const totalSelectedRows = this.state.totalSelectedRows.filter(item => item['id'] !== assetId);
this.setState({
selectedRowKeys: selectedRowKeys,
totalSelectedRows: totalSelectedRows
})
}
render() {
const columns = [{
title: '序号',
dataIndex: 'id',
key: 'id',
render: (id, record, index) => {
return index + 1;
}
}, {
title: '资产名称',
dataIndex: 'name',
key: 'name',
render: (name, record) => {
let short = name;
if (short && short.length > 20) {
short = short.substring(0, 20) + " ...";
}
return (
<Tooltip placement="topLeft" title={name}>
{short}
</Tooltip>
);
}
}, {
title: '网络',
dataIndex: 'ip',
key: 'ip',
render: (text, record) => {
return record['ip'] + ':' + record['port'];
}
}, {
title: '连接协议',
dataIndex: 'protocol',
key: 'protocol',
render: (text, record) => {
return (<Tag color={PROTOCOL_COLORS[text]}>{text}</Tag>);
}
}, {
title: '状态',
dataIndex: 'active',
key: 'active',
render: text => {
if (text) {
return (<Badge status="processing" text="运行中"/>);
} else {
return (<Badge status="error" text="不可用"/>);
}
}
}, {
title: '所有者',
dataIndex: 'ownerName',
key: 'ownerName'
}, {
title: '创建日期',
dataIndex: 'created',
key: 'created'
}
];
const selectedRowKeys = this.state.selectedRowKeys;
const rowSelection = {
selectedRowKeys: this.state.selectedRowKeys,
onChange: (selectedRowKeys, selectedRows) => {
this.setState({selectedRowKeys, selectedRows});
},
};
let hasSelected = false;
if (selectedRowKeys.length > 0) {
let totalSelectedRows = this.state.totalSelectedRows;
let allSelectedRowKeys = totalSelectedRows.map(item => item['id']);
for (let i = 0; i < selectedRowKeys.length; i++) {
let selectedRowKey = selectedRowKeys[i];
if (!allSelectedRowKeys.includes(selectedRowKey)) {
hasSelected = true;
break;
}
}
}
return (
<>
<Title level={3}>共享资产列表</Title>
<div>
{
this.state.totalSelectedRows.map(item => {
return <Tag color={PROTOCOL_COLORS[item['protocol']]} closable
onClose={() => this.unSelectRow(item['id'])}
key={item['id']}>{item['name']}</Tag>
})
}
</div>
<Divider/>
<Content key='page-content' className="site-layout-background">
<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}
/>
<Select mode="multiple"
allowClear
placeholder="资产标签" onChange={this.handleTagsChange}
style={{minWidth: 150}}>
{this.state.tags.map(tag => {
if (tag === '-') {
return undefined;
}
return (<Select.Option key={tag}>{tag}</Select.Option>)
})}
</Select>
<Select onChange={this.handleSearchByProtocol}
value={this.state.queryParams.protocol ? this.state.queryParams.protocol : ''}
style={{width: 100}}>
<Select.Option value="">全部协议</Select.Option>
<Select.Option value="rdp">rdp</Select.Option>
<Select.Option value="ssh">ssh</Select.Option>
<Select.Option value="vnc">vnc</Select.Option>
<Select.Option value="telnet">telnet</Select.Option>
</Select>
<Tooltip title='重置查询'>
<Button icon={<UndoOutlined/>} onClick={() => {
this.inputRefOfName.current.setValue('');
this.loadTableData({
...this.state.queryParams,
pageIndex: 1,
pageSize: 10,
protocol: ''
})
}}>
</Button>
</Tooltip>
<Divider type="vertical"/>
<Tooltip title="刷新列表">
<Button icon={<SyncOutlined/>} onClick={() => {
this.loadTableData(this.state.queryParams)
}}>
</Button>
</Tooltip>
<Tooltip title="添加共享">
<Button type="primary" disabled={!hasSelected} icon={<PlusOutlined/>}
onClick={async () => {
console.log(this.state.selectedRows)
let totalSelectedRows = this.state.totalSelectedRows;
let totalSelectedRowKeys = totalSelectedRows.map(item => item['id']);
let selectedRows = this.state.selectedRows;
let newRowKeys = []
for (let i = 0; i < selectedRows.length; i++) {
let selectedRow = selectedRows[i];
if (totalSelectedRowKeys.includes(selectedRow['id'])) {
continue;
}
totalSelectedRows.push(selectedRow);
newRowKeys.push(selectedRow['id']);
}
let userId = this.state.sharer;
let result = await request.post(`/resources/add`, {
userId: userId,
resourceType: 'asset',
resourceIds: newRowKeys
});
if (result['code'] === 1) {
message.success('操作成功', 3);
this.setState({
totalSelectedRows: totalSelectedRows
})
await this.loadTableData();
} else {
message.error(result['message'], 10);
}
}}>
</Button>
</Tooltip>
</Space>
</Col>
</Row>
</div>
<Table key='assets-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}
/>
</Content>
</>
);
}
}
export default UserShareAsset;

View File

@ -171,3 +171,9 @@ export function compare(p) {
return 0; return 0;
} }
} }
export function difference(a, b) {
let aSet = new Set(a)
let bSet = new Set(b)
return Array.from(new Set(a.concat(b).filter(v => !aSet.has(v) || !bSet.has(v))))
}