This commit is contained in:
caishi 2021-02-04 14:54:26 +08:00
parent 26d2e0f541
commit 0c5dd61fdc
9 changed files with 439 additions and 524 deletions

View File

@ -0,0 +1,84 @@
import React, { useEffect, useState } from 'react';
import { AutoComplete , Button , Icon } from 'antd';
import axios from 'axios';
const { Option } = AutoComplete;
function AddGroup({organizeId,getGroupID}){
const [ id , setID ] = useState(undefined);
const [ source , setSource ] = useState(undefined);
const [ searchKey , setSearchKey ] = useState(undefined);
useEffect(()=>{
getUserList();
},[searchKey])
function getUserList(e){
const url = `/organizations/${organizeId}/teams.json`;
axios.get(url, {
params: {
search: searchKey,
},
}).then((result) => {
if (result) {
sourceOptions(result.data.teams);
}
})
.catch((error) => {
console.log(error);
});
};
function sourceOptions(userDataSource){
const s = userDataSource && userDataSource.map((item, key) => {
return (
<Option
key={key}
value={`${item.id}`}
name={item.name}
>
{item.name}
</Option>
);
});
setSource(s);
}
function changeInputUser(e){
setSearchKey(e);
};
//
function selectInputUser(e, option){
setID(e);
setSearchKey(option.props.name);
};
function addCollaborator(){
getGroupID && getGroupID(id);
setSearchKey(undefined);
}
return(
<div className="addPanel">
<AutoComplete
dataSource={source}
value={searchKey}
style={{ width: 300 }}
onChange={changeInputUser}
onSelect={selectInputUser}
placeholder="搜索需要添加的团队..."
allowClear
/>
<Button
type="primary"
ghost
onClick={addCollaborator}
className="ml15"
>
<Icon type="plus" size="16"></Icon>
添加团队
</Button>
</div>
)
}
export default AddGroup;

View File

@ -4,7 +4,7 @@ import axios from 'axios';
import { getImageUrl } from 'educoder';
const { Option } = AutoComplete;
function AddMember({getID}){
function AddMember({getID,login}){
const [ id , setID ] = useState(undefined);
const [ source , setSource ] = useState(undefined);
const [ searchKey , setSearchKey ] = useState(undefined);
@ -34,8 +34,9 @@ function AddMember({getID}){
return (
<Option
key={key}
value={`${item.login}`}
searchValue={`${item.username}`}
value={`${item.user_id}`}
login={`${item.login}`}
name={item.username}
>
<img
className="user_img radius"
@ -60,12 +61,13 @@ function AddMember({getID}){
//
function selectInputUser(e, option){
setID(e);
setSearchKey(option.props.searchValue);
setID(login ? e : option.props.login);
setSearchKey(option.props.name);
};
function addCollaborator(){
getID && getID(id);
setSearchKey(undefined);
}
return(
@ -77,6 +79,7 @@ function AddMember({getID}){
onChange={changeInputUser}
onSelect={selectInputUser}
placeholder="搜索需要添加的用户..."
allowClear
/>
<Button
type="primary"

View File

@ -32,7 +32,6 @@ export default ({ getUser })=>{
}
function selectInputUser(id, option){
console.log(id,option);
setSearchKey(option.props.value);
getUserList(option.props.value);
getUser && getUser(id);

View File

@ -394,7 +394,7 @@ class Detail extends Component {
<div className="f-wrap-between pb15" style={{ position: "relative" }}>
<p className="font-22 df flex-1 lineH2 mt15" style={{ alignItems: "center" }}>
{project && project.author &&
<Link to={`/users/${project.author.login}`} className="show-user-link">
<Link to={`${project.author.type ==="Organization" ? "/organize":'/users'}/${project.author.login}`} className="show-user-link">
{project.author.name}
</Link>
}

View File

@ -1,522 +1,47 @@
import React, { Component } from "react";
import { Link } from "react-router-dom";
import {
Input,
AutoComplete,
Dropdown,
Menu,
Icon,
Spin,
Pagination,
Button,
Table,
Tooltip
} from "antd";
import NoneData from "../Nodata";
import axios from "axios";
import { getImageUrl } from "educoder";
import React, { useState } from "react";
import {WhiteBack} from '../Component/layout';
import AddMember from '../Component/AddMember';
import AddGroup from '../Component/AddGroup';
import Member from './CollaboratorMember';
import Group from './CollaboratorGroup';
const { Search } = Input;
function Collaborator(props){
const [ nav , setNav] = useState("1");
const [ newId , setNewId] = useState(undefined);
const [ newGroupId , setNewGroupId] = useState(undefined);
const {projectsId ,owner} = props.match.params;
const { Option } = AutoComplete;
const MENU_LIST = [
{
id: "Manager",
name: "管理员",
},
{
id: "Developer",
name: "开发者",
},
{
id: "Reporter",
name: "报告者",
},
];
const LIMIT = 15;
class Collaborator extends Component {
constructor(props) {
super(props);
this.state = {
listData: undefined,
user: undefined,
user_id: undefined,
userDataSource: undefined,
page: 1,
total_count: undefined,
isSpin: true,
searchKey: undefined,
search: undefined,
role: undefined,
otherSpin: false,
searchSpin: false,
roleName: undefined,
};
function getID(id){
setNewId(id);
}
componentDidUpdate=(prevPros)=>{
if(prevPros && this.props && !this.props.checkIfLogin()){
this.props.history.push("/403")
return
}
function getGroupID(id){
setNewGroupId(id);
}
componentDidMount = () => {
// this.check_is_login()
if (this.props.project_id) {
this.getMember();
}
};
// check_is_login =() =>{
// if(!this.props.checkIfLogin()){
// this.props.history.push("/403")
// return
// }
// };
componentDidUpdate = (prevState) => {
if (
this.props.project_id &&
this.props.project_id !== prevState.project_id
) {
this.getMember();
}
};
// 获取项目协作者
getMember = () => {
const { page, search, role } = this.state;
const {projectsId ,owner} = this.props.match.params;
const url = `/${owner}/${projectsId}/collaborators.json`;
axios
.get(url, {
params: {
page,
search: search,
role: role,
limit: LIMIT,
},
})
.then((result) => {
if (result) {
this.setState({
listData: result.data.members,
isSpin: false,
otherSpin: false,
searchSpin: false,
total_count: result.data.total_count,
});
}
})
.catch((error) => {
this.setState({
isSpin: false,
otherSpin: false,
searchSpin: false,
});
console.log(error);
});
};
// 输入用户
changeInputUser = (e) => {
this.setState({
searchKey: e,
});
this.getUserList(e);
};
searchMember = (e) => {
this.state.search = e;
this.setState({
searchSpin: true,
});
this.getMember();
};
orderMember = (id, name) => {
this.setState({
isSpin: true
})
this.state.role = id;
this.state.roleName = name;
this.getMember();
};
// 选择用户
selectInputUser = (e, option) => {
this.setState({
user_id: e,
searchKey: option.props.searchValue,
});
this.getUserList(option.props.searchValue);
};
getUserList = (e) => {
const url = `/users/list.json`;
axios
.get(url, {
params: {
search: e,
},
})
.then((result) => {
if (result) {
this.setState({
userDataSource: result.data.users,
});
}
})
.catch((error) => {
console.log(error);
});
};
// 增加协作者
addCollaborator = () => {
// const { project_id } = this.props;
const { user_id } = this.state;
if(user_id){
this.setState({
otherSpin: true,
});
const {projectsId ,owner} = this.props.match.params;
const url = `/${owner}/${projectsId}/collaborators.json`;
axios.post(url, {
user_id,
})
.then((result) => {
if (result) {
this.setState({
isSpin: true,
otherSpin: false,
});
this.getMember();
}
})
.catch((error) => {
this.setState({
isSpin: false,
otherSpin: false,
});
console.log(error);
});
}
};
// 修改权限
changeOperaiton = (e, id) => {
// const { project_id, page } = this.state;
this.setState({
isSpin: true,
});
const {projectsId ,owner} = this.props.match.params;
const url = `/${owner}/${projectsId}/collaborators/change_role.json`;
axios
.put(url, {
user_id: id,
role: e.key,
})
.then((result) => {
if (result) {
this.setState({
isSpin: true,
});
this.props.showNotification("权限修改成功!");
this.getMember();
}
})
.catch((error) => {
this.setState({
isSpin: false,
});
console.log(error);
});
};
// 删除协作者
deleteUser = (id) => {
const { page } = this.state;
const {projectsId ,owner} = this.props.match.params;
this.props.confirm({
content: "确认将此成员从项目中移除?",
onOk: () => {
const { project_id } = this.props;
const url = `/${owner}/${projectsId}/collaborators/remove.json`;
axios
.delete(url, {
data: {
user_id: id,
},
})
.then((result) => {
if (result) {
this.setState({
isSpin: true,
});
this.props.showNotification("成员删除成功!");
this.getMember();
}
})
.catch((error) => {
this.setState({
isSpin: false,
});
console.log(error);
});
},
});
};
changePage = (page) => {
this.state.page = page;
this.setState({
isSpin: true,
});
this.getMember();
};
render() {
const {
userDataSource,
listData,
isSpin,
page,
total_count,
searchKey,
otherSpin,
searchSpin,
roleName,
} = this.state;
// 获取当前项目的拥有者
const { author } = this.props;
const get_color = (role) => {
if (role === "Manager") {
return "text-green";
} else if (role === "Developer") {
return "text-primary";
} else {
return "text-yellow";
}
};
const member_roles = (item) => {
const operation = MENU_LIST.filter((i) => i.id === item.role);
return (
return (
<WhiteBack>
<div className="flex-a-center baseForm bbr">
<span>
{author && author.login === item.login ? (
<label className={get_color(item.role)}>
{operation && operation[0].name}
</label>
) : (
<Dropdown overlay={setRoles(`${item.id}`)} placement={"bottomCenter"}>
<span className={get_color(item.role)}>
{operation && operation[0].name}
<Icon type="caret-down" className="ml2" size="13" />
</span>
</Dropdown>
)}
<span style={{cursor:"pointer"}} className={nav === "1" ? "font-18 text-black color-blue":"font-18 text-black"} onClick={()=>setNav("1")}>协作者管理</span>
<span style={{cursor:"pointer"}} className={nav === "2" ? "font-18 text-black ml30 color-blue":"font-18 text-black ml30"} onClick={()=>setNav("2")}>团队管理</span>
</span>
);
};
const roleTitle = (
<div><span className="mr3">角色</span>
<Tooltip placement='bottom' title={<div>
<div className="mb3">管理员拥有仓库设置功能代码库读写操作</div>
<div className="mb3">开发人员只拥有代码库读写操作</div>
<div className="mb3">报告者只拥有代码库读操作</div>
</div>
}>
<Icon type="question-circle"></Icon>
</Tooltip>
</div>
);
const columns = [
{
title: "头像",
dataIndex: "image_url",
render: (text, item) => (
<span className="f-wrap-alignCenter">
<Link
to={`/users/${item.login}`}
className="show-user-link"
>
<img
src={getImageUrl(`images/${text}`)}
alt=""
width="32px"
height="32px"
className="mr3 radius"
/>
</Link>
</span>
),
},
{
title: "用户名",
dataIndex: "name",
render: (text, item) => (
<Link to={`/users/${item.login}`} className="show-user-link">
{text}
</Link>
),
},
{
title: "邮箱",
dataIndex: "email",
render: (text) => <span>{text}</span>,
},
{
title: "Token值",
dataIndex: "token",
render: (text) => <span>{text}</span>,
},
{
title: roleTitle,
dataIndex: "role_name",
render: (text, item) => member_roles(item),
},
{
title: "操作",
dataIndex: "action",
render: (text, item) => (
<span style={{ justifyContent: "center" }}>
{author && author.login !== item.login && (
<a
className="text-delete"
onClick={() => this.deleteUser(item.id)}
>
删除
</a>
)}
</span>
),
},
];
const roles = (id) => (
<Menu>
<Menu.Item
key={0}
value={undefined}
onClick={(e) => this.orderMember(undefined, "角色筛选")}
>
全部
</Menu.Item>
{MENU_LIST.map((item, key) => {
return (
<Menu.Item
key={item.id}
value={item.id}
onClick={(e) => this.orderMember(item.id, item.name)}
>
{item.name}
</Menu.Item>
);
})}
</Menu>
);
const setRoles = (id) => (
<Menu>
{MENU_LIST.map((item, key) => {
return (
<Menu.Item
key={item.id}
value={item.id}
onClick={(e) => this.changeOperaiton(e,id)}
>
{item.name}
</Menu.Item>
);
})}
</Menu>
);
const source =
userDataSource &&
userDataSource.map((item, key) => {
return (
<Option
key={key}
value={`${item.user_id}`}
searchValue={`${item.username}`}
>
<img
className="user_img radius"
width="28"
height="28"
src={getImageUrl(`images/${item && item.image_url}`)}
alt=""
/>
<span className="ml10" style={{ "vertical-align": "middle" }}>
{item.username}
<span className="color-grey ml10">({item.login})</span>
</span>
</Option>
);
});
return (
<WhiteBack>
<div className="flex-a-center baseForm bbr">
<span className="font-18 text-black">协作者管理</span>
<div className="addPanel">
<AutoComplete
dataSource={source}
value={searchKey}
style={{ width: 300 }}
onChange={this.changeInputUser}
onSelect={this.selectInputUser}
placeholder="搜索需要添加的用户..."
/>
<Button
type="primary"
ghost
onClick={this.addCollaborator}
className="ml15"
loading={otherSpin}
>
<Icon type="plus" size="16"></Icon>
添加成员
</Button>
</div>
</div>
<div className="grid-item-left baseForm">
<Search
placeholder="搜索项目成员..."
enterButton="搜索"
loading={searchSpin}
onSearch={(value) => this.searchMember(value)}
/>
<Dropdown overlay={roles} placement={"bottomCenter"}>
<a className="ml180 text-primary">
{roleName ? roleName : "角色筛选"}
<Icon type="caret-down" size="16"></Icon>
</a>
</Dropdown>
</div>
<Spin spinning={isSpin}>
<div className="collaboratorList baseForm">
{listData && listData.length>0 ? (
<Table
pagination={false}
columns={columns}
dataSource={listData}
rowKey={(record) => record.id}
></Table>
) : (
<NoneData _html="暂时还没有相关数据!" />
)}
</div>
</Spin>
{total_count && total_count > LIMIT ? (
<div className="edu-txt-center mt20 mb20">
<Pagination
showQuickJumper
pageSize={LIMIT}
current={page}
total={total_count}
onChange={this.changePage}
></Pagination>
</div>
) : (
""
)}
</WhiteBack>
);
}
{
nav === "1" ?
<AddMember getID={getID} login/>
:
<AddGroup getGroupID={getGroupID} organizeId={owner}/>
}
</div>
<div>
{
nav === "1" ?
<Member newId={newId} projectsId={projectsId} owner={owner} project_id={props.project_id} author={props.author} showNotification={props.showNotification}/>
:
<Group {...props} newGroupId={newGroupId}/>
}
</div>
</WhiteBack>
);
}
export default Collaborator;

View File

@ -0,0 +1,18 @@
import React, { useEffect, useState } from 'react';
function CollaboratorGroup(props,{newGroupId}){
useEffect(()=>{
if(newGroupId){
addGroup(newGroupId);
}
},[newGroupId])
function addGroup(id){
}
return(
<div>团队管理</div>
)
}
export default CollaboratorGroup;

View File

@ -0,0 +1,278 @@
import React, { useEffect, useState } from 'react';
import { Dropdown, Menu, Icon , Input , Spin , Table , Tooltip , Pagination , Popconfirm } from "antd";
import axios from 'axios';
import NoneData from "../Nodata";
import { Link } from "react-router-dom";
import { getImageUrl } from "educoder";
const { Search } = Input;
const LIMIT = 15;
const MENU_LIST = [
{id: "Manager",name: "管理员"},
{id: "Developer",name: "开发者"},
{id: "Reporter",name: "报告者"}
];
function CollaboratorMember({projectsId,owner,project_id,author,showNotification,newId}){
const [ roleName , setRoleName ] = useState(undefined);
const [ search , setSearch ] = useState(undefined);
const [ page , setPage ] = useState(undefined);
const [ isSpin , setIsSpin ] = useState(false);
const [ role , setRole ] = useState(undefined);
const [ listData , setListData ] = useState(undefined);
const [ total , setTotal ] = useState(0);
useEffect(()=>{
if(newId){
addCollaborator(newId);
}
},[newId])
//
function addCollaborator(id){
if(id){
const url = `/${owner}/${projectsId}/collaborators.json`;
axios.post(url, {
user_id:id,
})
.then((result) => {
if (result) {
getMember();
}
})
.catch((error) => {});
}
};
useEffect(()=>{
if(project_id && projectsId && owner) {
getMember();
}
},[project_id,search,page,role])
//
function getMember(){
setIsSpin(true);
const url = `/${owner}/${projectsId}/collaborators.json`;
axios.get(url, {
params: {
page,search,role,limit: LIMIT,
},
})
.then((result) => {
if (result) {
setListData(result.data.members);
setTotal(result.data.total_count);
setIsSpin(false);
}
})
.catch((error) => {setIsSpin(false);});
};
function orderMember(id, name){
setRole(id);
setRoleName(name);
};
const roles = (id) => (
<Menu>
<Menu.Item
key={0}
value={undefined}
onClick={(e) => orderMember(undefined, "角色筛选")}
>
全部
</Menu.Item>
{MENU_LIST.map((item, key) => {
return (
<Menu.Item
key={item.id}
value={item.id}
onClick={(e) => orderMember(item.id, item.name)}
>
{item.name}
</Menu.Item>
);
})}
</Menu>
);
//
function changeOperaiton(e, id){
const url = `/${owner}/${projectsId}/collaborators/change_role.json`;
axios.put(url, {
user_id: id,
role: e.key,
}).then((result) => {
if (result) {
showNotification("权限修改成功!");
getMember();
}
})
.catch((error) => {});
};
//
function deleteUser(id){
const url = `/${owner}/${projectsId}/collaborators/remove.json`;
axios.delete(url, {
data: {user_id: id,},
})
.then((result) => {
if (result) {
showNotification("成员删除成功!");
getMember();
}
}).catch((error) => {});
};
const roleTitle = (
<div><span className="mr3">角色</span>
<Tooltip placement='bottom' title={<div>
<div className="mb3">管理员拥有仓库设置功能代码库读写操作</div>
<div className="mb3">开发人员只拥有代码库读写操作</div>
<div className="mb3">报告者只拥有代码库读操作</div>
</div>
}>
<Icon type="question-circle"></Icon>
</Tooltip>
</div>
);
const get_color = (role) => {
if (role === "Manager") {
return "text-green";
} else if (role === "Developer") {
return "text-primary";
} else {
return "text-yellow";
}
};
const setRoles = (id) => (
<Menu>
{MENU_LIST.map((item, key) => {
return (
<Menu.Item
key={item.id}
value={item.id}
onClick={(e) => changeOperaiton(e,id)}
>
{item.name}
</Menu.Item>
);
})}
</Menu>
);
const member_roles = (item) => {
const operation = MENU_LIST.filter((i) => i.id === item.role);
return (
<span>
{author && author.login === item.login ? (
<label className={get_color(item.role)}>
{operation && operation[0].name}
</label>
) : (
<Dropdown overlay={setRoles(`${item.id}`)} placement={"bottomCenter"}>
<span className={get_color(item.role)}>
{operation && operation[0].name}
<Icon type="caret-down" className="ml2" size="13" />
</span>
</Dropdown>
)}
</span>
);
};
const columns = [
{
title: "头像",
dataIndex: "image_url",
render: (text, item) => (
<span className="f-wrap-alignCenter">
<Link
to={`/users/${item.login}`}
className="show-user-link"
>
<img
src={getImageUrl(`images/${text}`)}
alt=""
width="32px"
height="32px"
className="mr3 radius"
/>
</Link>
</span>
),
},
{
title: "用户名",
dataIndex: "name",
render: (text, item) => (
<Link to={`/users/${item.login}`} className="show-user-link">
{text}
</Link>
),
},
{
title: "邮箱",
dataIndex: "email",
render: (text) => <span>{text}</span>,
},
{
title: roleTitle,
dataIndex: "role_name",
render: (text, item) => member_roles(item),
},
{
title: "操作",
dataIndex: "action",
render: (text, item) => (
<span style={{ justifyContent: "center" }}>
{author && author.login !== item.login && (
<Popconfirm title="确认将此成员从项目中移除?" okText="是" cancelText="否" onOk={() => deleteUser(item.id)}>
<a className="text-delete">删除</a>
</Popconfirm>
)}
</span>
),
},
];
return(
<React.Fragment>
<div className="baseForm" style={{display:'flex',justifyContent:"space-between",alignItems:"center"}}>
<Dropdown overlay={roles} placement={"bottomCenter"}>
<a className="text-primary">
{roleName ? roleName : "角色筛选"}
<Icon type="caret-down" size="16"></Icon>
</a>
</Dropdown>
<Search
placeholder="搜索项目成员..."
enterButton="搜索"
onSearch={setSearch}
style={{width:300}}
/>
</div>
<Spin spinning={isSpin}>
<div className="collaboratorList baseForm">
{listData && listData.length>0 ? (
<Table
pagination={false}
columns={columns}
dataSource={listData}
rowKey={(record) => record.id}
></Table>
) : (
<NoneData _html="暂时还没有相关数据!" />
)}
</div>
</Spin>
{total > LIMIT ?
<div className="edu-txt-center mt20 mb20">
<Pagination
showQuickJumper
pageSize={LIMIT}
current={page}
total={total}
onChange={()=>setPage(page)}
></Pagination>
</div>
:""}
</React.Fragment>
)
}
export default CollaboratorMember;

View File

@ -112,8 +112,9 @@ function GroupProjectSetting(props) {
let s = value || "";
setSearch(s);
}
function selectInput(value,e){
function selectInput(value){
setRepoName(value);
setSearch(value);
}
return (

View File

@ -7,6 +7,7 @@ import axios from 'axios';
export default Form.create()(
forwardRef(({ form , showNotification , history })=>{
const [ image , setImage ] = useState(undefined);
const [ imageFlag , setImageFlag] = useState(false);
const { getFieldDecorator, validateFields } = form;
const radioStyle = {
@ -29,12 +30,17 @@ export default Form.create()(
//
function getImage(image){
setImage(image);
setImageFlag(false);
}
//
function createOrganize(){
validateFields((error,values)=>{
if(!error){
if(!image){
setImageFlag(true);
return;
}
const url = `/organizations.json`;
axios.post(url,{
...values,image
@ -74,7 +80,6 @@ export default Form.create()(
'所在地区',
"location",
[],
// <Cascader placeholder="" options={locData}/>,false
<Input placeholder="请输入地址"/>,false
)}
{helper(
@ -87,9 +92,11 @@ export default Form.create()(
<Radio style={radioStyle} value="privacy" key={3}>私有<span className="color-grey-8">仅对组织成员可见</span></Radio>
</Radio.Group>
)}
<p className="font-16">选择头像</p>
<p className="font-16 lables must">选择头像</p>
<UploadImage getImage={getImage}/>
{imageFlag && <p className="color-red" style={{marginTop:"-10px"}}>请上传头像</p> }
{helper(
'权限',
"repo_admin_change_team_access",