修改仓库设置的协作者页面
This commit is contained in:
parent
ac39faceca
commit
f21ad1199f
File diff suppressed because one or more lines are too long
|
|
@ -1,208 +1,423 @@
|
||||||
import React , { Component } from 'react';
|
import React, { Component } from "react";
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from "react-router-dom";
|
||||||
import { Input , AutoComplete , Dropdown , Menu , Icon , Spin , Pagination } from 'antd';
|
import {
|
||||||
import axios from 'axios';
|
Input,
|
||||||
import { getImageUrl } from 'educoder';
|
AutoComplete,
|
||||||
|
Dropdown,
|
||||||
|
Menu,
|
||||||
|
Icon,
|
||||||
|
Spin,
|
||||||
|
Pagination,
|
||||||
|
Button,
|
||||||
|
Table,
|
||||||
|
Tooltip
|
||||||
|
} from "antd";
|
||||||
|
import NoneData from "../Nodata";
|
||||||
|
import axios from "axios";
|
||||||
|
import { getImageUrl } from "educoder";
|
||||||
|
const { Search } = Input;
|
||||||
|
|
||||||
const { Option } = AutoComplete;
|
const { Option } = AutoComplete;
|
||||||
const MENU_LIST = [{
|
const MENU_LIST = [
|
||||||
id:"Manager",
|
{
|
||||||
name:'管理员'
|
id: "Manager",
|
||||||
},{
|
name: "管理员",
|
||||||
id:"Developer",
|
},
|
||||||
name:'可写权限'
|
{
|
||||||
},{
|
id: "Developer",
|
||||||
id:"Reporter",
|
name: "开发者",
|
||||||
name:'可读权限'
|
},
|
||||||
}];
|
{
|
||||||
|
id: "Reporter",
|
||||||
|
name: "报告者",
|
||||||
|
},
|
||||||
|
];
|
||||||
const LIMIT = 15;
|
const LIMIT = 15;
|
||||||
class Collaborator extends Component{
|
class Collaborator extends Component {
|
||||||
constructor(props){
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state={
|
this.state = {
|
||||||
listData:undefined,
|
listData: undefined,
|
||||||
user:undefined,
|
user: undefined,
|
||||||
user_id:undefined,
|
user_id: undefined,
|
||||||
userDataSource:undefined,
|
userDataSource: undefined,
|
||||||
page:1,
|
page: 1,
|
||||||
total_count:undefined,
|
total_count: undefined,
|
||||||
isSpin:true,
|
isSpin: true,
|
||||||
searchKey: undefined
|
searchKey: undefined,
|
||||||
}
|
search: undefined,
|
||||||
|
role: undefined,
|
||||||
|
otherSpin: false,
|
||||||
|
searchSpin: false,
|
||||||
|
roleName: undefined,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount=()=>{
|
componentDidMount = () => {
|
||||||
if(this.props.project_id){
|
if (this.props.project_id) {
|
||||||
const { page } = this.state;
|
this.getMember();
|
||||||
this.getMember(this.props.project_id,page);
|
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
componentDidUpdate=(prevState)=>{
|
componentDidUpdate = (prevState) => {
|
||||||
if(this.props.project_id && this.props.project_id !== prevState.project_id){
|
if (
|
||||||
const { page } = this.state;
|
this.props.project_id &&
|
||||||
this.getMember(this.props.project_id,page);
|
this.props.project_id !== prevState.project_id
|
||||||
|
) {
|
||||||
|
this.getMember();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
// 获取项目协作者
|
// 获取项目协作者
|
||||||
getMember=(project_id,page)=>{
|
getMember = () => {
|
||||||
const url = `/projects/${project_id}/members.json`;
|
const { page, search, role } = this.state;
|
||||||
axios.get(url,{
|
console.log("search", search);
|
||||||
params:{
|
console.log("role", role);
|
||||||
page,limit:LIMIT
|
const url = `/projects/${this.props.project_id}/members.json`;
|
||||||
}
|
axios
|
||||||
}).then(result=>{
|
.get(url, {
|
||||||
if(result){
|
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({
|
this.setState({
|
||||||
listData:result.data.members,
|
isSpin: false,
|
||||||
isSpin:false,
|
otherSpin: false,
|
||||||
total_count:result.data.total_count
|
searchSpin: false,
|
||||||
})
|
});
|
||||||
}
|
console.log(error);
|
||||||
}).catch(error=>{
|
});
|
||||||
console.log(error);
|
};
|
||||||
})
|
|
||||||
}
|
|
||||||
// 输入用户
|
// 输入用户
|
||||||
changeInputUser=(e)=>{
|
changeInputUser = (e) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
searchKey: e
|
searchKey: e,
|
||||||
})
|
});
|
||||||
this.getUserList(e);
|
this.getUserList(e);
|
||||||
}
|
};
|
||||||
// 选择用户
|
searchMember = (e) => {
|
||||||
selectInputUser=(e,option)=>{
|
this.state.search = e;
|
||||||
this.setState({
|
this.setState({
|
||||||
user_id:e,
|
searchSpin: true,
|
||||||
searchKey: option.props.searchValue
|
});
|
||||||
|
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);
|
this.getUserList(option.props.searchValue);
|
||||||
}
|
};
|
||||||
getUserList=(e)=>{
|
getUserList = (e) => {
|
||||||
const url = `/users/list.json`;
|
const url = `/users/list.json`;
|
||||||
axios.get(url,{
|
axios
|
||||||
params:{
|
.get(url, {
|
||||||
search:e
|
params: {
|
||||||
}
|
search: e,
|
||||||
}).then(result=>{
|
},
|
||||||
if(result){
|
})
|
||||||
this.setState({
|
.then((result) => {
|
||||||
userDataSource:result.data.users
|
if (result) {
|
||||||
})
|
this.setState({
|
||||||
}
|
userDataSource: result.data.users,
|
||||||
}).catch(error=>{
|
});
|
||||||
console.log(error);
|
}
|
||||||
})
|
})
|
||||||
}
|
.catch((error) => {
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// 增加协作者
|
// 增加协作者
|
||||||
addCollaborator=()=>{
|
addCollaborator = () => {
|
||||||
const { project_id } = this.props;
|
// const { project_id } = this.props;
|
||||||
|
this.setState({
|
||||||
|
otherSpin: true,
|
||||||
|
});
|
||||||
const { user_id } = this.state;
|
const { user_id } = this.state;
|
||||||
const url = `/projects/${project_id}/members.json`;
|
const url = `/projects/${this.props.project_id}/members.json`;
|
||||||
axios.post(url,{
|
axios
|
||||||
user_id
|
.post(url, {
|
||||||
}).then(result=>{
|
user_id,
|
||||||
if(result){
|
})
|
||||||
this.setState({
|
.then((result) => {
|
||||||
isSpin:true
|
if (result) {
|
||||||
})
|
this.setState({
|
||||||
this.getMember(project_id);
|
isSpin: true,
|
||||||
}
|
otherSpin: false,
|
||||||
}).catch(error=>{
|
});
|
||||||
console.log(error);
|
this.getMember();
|
||||||
})
|
}
|
||||||
}
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// 修改权限
|
// 修改权限
|
||||||
changeOperaiton=(e,id)=>{
|
changeOperaiton = (e, id) => {
|
||||||
const { project_id , page } = this.props;
|
// const { project_id, page } = this.state;
|
||||||
this.setState({
|
this.setState({
|
||||||
isSpin:true
|
isSpin: true,
|
||||||
})
|
});
|
||||||
const url = `/projects/${project_id}/members/change_role.json`;
|
const url = `/projects/${this.props.project_id}/members/change_role.json`;
|
||||||
axios.put(url,{
|
axios
|
||||||
user_id:id,
|
.put(url, {
|
||||||
role:e.key
|
user_id: id,
|
||||||
}).then(result=>{
|
role: e.key,
|
||||||
if(result){
|
})
|
||||||
this.setState({
|
.then((result) => {
|
||||||
isSpin:true
|
if (result) {
|
||||||
})
|
this.setState({
|
||||||
this.props.showNotification('权限修改成功!');
|
isSpin: true,
|
||||||
this.getMember(project_id,page);
|
});
|
||||||
}
|
this.props.showNotification("权限修改成功!");
|
||||||
}).catch(error=>{
|
this.getMember();
|
||||||
console.log(error);
|
}
|
||||||
})
|
})
|
||||||
}
|
.catch((error) => {
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// 删除协作者
|
// 删除协作者
|
||||||
deleteUser=(id)=>{
|
deleteUser = (id) => {
|
||||||
const { page } = this.state;
|
const { page } = this.state;
|
||||||
this.props.confirm({
|
this.props.confirm({
|
||||||
content:"确认将此成员从项目中移除?",
|
content: "确认将此成员从项目中移除?",
|
||||||
onOk:()=>{
|
onOk: () => {
|
||||||
const { project_id } = this.props;
|
const { project_id } = this.props;
|
||||||
const url = `/projects/${project_id}/members/remove.json`;
|
const url = `/projects/${project_id}/members/remove.json`;
|
||||||
axios.delete(url,{ data: {
|
axios
|
||||||
user_id:id
|
.delete(url, {
|
||||||
}
|
data: {
|
||||||
}).then(result=>{
|
user_id: id,
|
||||||
if(result){
|
},
|
||||||
this.setState({
|
})
|
||||||
isSpin:true
|
.then((result) => {
|
||||||
})
|
if (result) {
|
||||||
this.props.showNotification("成员删除成功!");
|
this.setState({
|
||||||
this.getMember(project_id,page);
|
isSpin: true,
|
||||||
}
|
});
|
||||||
}).catch(error=>{
|
this.props.showNotification("成员删除成功!");
|
||||||
console.log(error);
|
this.getMember();
|
||||||
})
|
}
|
||||||
}
|
})
|
||||||
})
|
.catch((error) => {
|
||||||
}
|
console.log(error);
|
||||||
changePage=(page)=>{
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
changePage = (page) => {
|
||||||
|
this.state.page = page;
|
||||||
this.setState({
|
this.setState({
|
||||||
page
|
isSpin: true,
|
||||||
})
|
});
|
||||||
const { project_id } = this.props;
|
this.getMember();
|
||||||
this.getMember(project_id,page);
|
};
|
||||||
}
|
|
||||||
render(){
|
render() {
|
||||||
const { userDataSource , listData , isSpin , page , total_count,searchKey } = this.state;
|
const {
|
||||||
|
userDataSource,
|
||||||
|
listData,
|
||||||
|
isSpin,
|
||||||
|
page,
|
||||||
|
total_count,
|
||||||
|
searchKey,
|
||||||
|
otherSpin,
|
||||||
|
searchSpin,
|
||||||
|
roleName,
|
||||||
|
} = this.state;
|
||||||
// 获取当前项目的拥有者
|
// 获取当前项目的拥有者
|
||||||
const { author } = this.props;
|
const { author } = this.props;
|
||||||
const menu =(id)=> (
|
const get_color = (role) => {
|
||||||
<Menu>
|
if (role === "Manager") {
|
||||||
{
|
return "text-green";
|
||||||
MENU_LIST.map((item,key)=>{
|
} else if (role === "Developer") {
|
||||||
return(
|
return "text-primary";
|
||||||
<Menu.Item key={item.id} value={item.id} onClick={(e)=>this.changeOperaiton(e,id)}>{item.name}</Menu.Item>
|
} else {
|
||||||
)
|
return "text-yellow";
|
||||||
})
|
}
|
||||||
}
|
};
|
||||||
</Menu>
|
const member_roles = (item) => {
|
||||||
)
|
const operation = MENU_LIST.filter((i) => i.id === item.role);
|
||||||
|
return (
|
||||||
const source = userDataSource && userDataSource.map((item,key)=>{
|
<span>
|
||||||
return(
|
{author && author.login === item.login ? (
|
||||||
<Option key={key} value={`${item.user_id}`} searchValue={`${item.username}`}>
|
<label className={get_color(item.role)}>
|
||||||
<img className="user_img radius" width="28" height="28" src={getImageUrl(`images/${item && item.image_url}`)} alt=""/>
|
{operation && operation[0].name}
|
||||||
<span className="ml10" style={{'vertical-align':'middle'}}>
|
</label>
|
||||||
{item.username}
|
) : (
|
||||||
<span className="color-grey ml10">({item.login})</span>
|
<Dropdown overlay={setRoles(`${item.id}`)} placement={"bottomCenter"}>
|
||||||
</span>
|
<span className={get_color(item.role)}>
|
||||||
</Option>
|
{operation && operation[0].name}
|
||||||
)
|
<Icon type="caret-down" className="ml2" size="13" />
|
||||||
})
|
</span>
|
||||||
return(
|
</Dropdown>
|
||||||
<div>
|
)}
|
||||||
<div className="normalBox">
|
</span>
|
||||||
<div className="normalBox-title font-16">
|
);
|
||||||
协作者
|
};
|
||||||
|
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>
|
</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}/projects`}
|
||||||
|
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}/projects`} 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 && (
|
||||||
|
<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 (
|
||||||
|
<div>
|
||||||
|
<div className="flex-a-center baseForm bbr">
|
||||||
|
<span className="font-18 fwb text-black">协作者管理</span>
|
||||||
<div className="addPanel">
|
<div className="addPanel">
|
||||||
<AutoComplete
|
<AutoComplete
|
||||||
dataSource={source}
|
dataSource={source}
|
||||||
|
|
@ -210,63 +425,64 @@ class Collaborator extends Component{
|
||||||
style={{ width: 300 }}
|
style={{ width: 300 }}
|
||||||
onChange={this.changeInputUser}
|
onChange={this.changeInputUser}
|
||||||
onSelect={this.selectInputUser}
|
onSelect={this.selectInputUser}
|
||||||
placeholder="用户名或邮箱搜索..."
|
placeholder="搜索需要添加的用户..."
|
||||||
/>
|
/>
|
||||||
<a className="small_submitBtn ml20" onClick={this.addCollaborator}>增加协作者</a>
|
<Button
|
||||||
|
type="primary"
|
||||||
|
ghost
|
||||||
|
onClick={this.addCollaborator}
|
||||||
|
className="ml15"
|
||||||
|
loading={otherSpin}
|
||||||
|
>
|
||||||
|
<Icon type="plus" size="16"></Icon>
|
||||||
|
添加成员
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<Spin spinning={isSpin}>
|
|
||||||
<div className="collaboratorList">
|
|
||||||
{
|
|
||||||
listData && listData.map((item,key)=>{
|
|
||||||
const operation = MENU_LIST.filter(i=>i.id === item.role);
|
|
||||||
return(
|
|
||||||
<div className="collaboratorItem">
|
|
||||||
<span>
|
|
||||||
<Link to={`/users/${item && item.login}/projects`} className="show-user-link">
|
|
||||||
<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.name}
|
|
||||||
<span className="color-grey ml10">({item.login})</span>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
</Link>
|
|
||||||
</span>
|
|
||||||
<span>
|
|
||||||
{
|
|
||||||
author && author.login === item.login ?
|
|
||||||
<label>{operation && operation[0].name}</label>
|
|
||||||
:
|
|
||||||
<Dropdown overlay={menu(`${item.id}`)} placement={"bottomCenter"}>
|
|
||||||
<span>{operation && operation[0].name}<Icon type="down" /></span>
|
|
||||||
</Dropdown>
|
|
||||||
}
|
|
||||||
|
|
||||||
</span>
|
|
||||||
<span style={{justifyContent:"center"}}>
|
|
||||||
{
|
|
||||||
author && author.login !== item.login &&
|
|
||||||
<a className="red_btn" onClick={()=>this.deleteUser(item.id)}>删除</a>
|
|
||||||
}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</Spin>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{
|
<div className="grid-item-left baseForm">
|
||||||
total_count && total_count > LIMIT ?
|
<Search
|
||||||
<div className="edu-txt-center mt20 mb20">
|
placeholder="搜索项目成员..."
|
||||||
<Pagination showQuickJumper pageSize={LIMIT} current={page} total={total_count} onChange={this.changePage}></Pagination>
|
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 ? (
|
||||||
|
<Table
|
||||||
|
pagination={false}
|
||||||
|
columns={columns}
|
||||||
|
dataSource={listData}
|
||||||
|
rowKey={(record) => record.id}
|
||||||
|
></Table>
|
||||||
|
) : (
|
||||||
|
<NoneData _html="暂时还没有相关数据哦!" />
|
||||||
|
)}
|
||||||
</div>
|
</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>
|
||||||
|
) : (
|
||||||
|
""
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export default Collaborator;
|
export default Collaborator;
|
||||||
|
|
|
||||||
|
|
@ -1,67 +1,124 @@
|
||||||
import React , { Component } from 'react';
|
import React, { Component } from "react";
|
||||||
import { Link , Route , Switch } from 'react-router-dom';
|
import { Link, Route, Switch } from "react-router-dom";
|
||||||
|
|
||||||
import '../css/index.css';
|
import "../css/index.css";
|
||||||
import './setting.css';
|
import "./setting.css";
|
||||||
|
|
||||||
import Loadable from 'react-loadable';
|
import Loadable from "react-loadable";
|
||||||
import Loading from '../../Loading';
|
import Loading from "../../Loading";
|
||||||
|
|
||||||
const Branch = Loadable({
|
const Branch = Loadable({
|
||||||
loader: () => import('./Branch'),
|
loader: () => import("./Branch"),
|
||||||
loading: Loading,
|
|
||||||
})
|
|
||||||
const Setting = Loadable({
|
|
||||||
loader: () => import('./Setting'),
|
|
||||||
loading: Loading,
|
|
||||||
})
|
|
||||||
const Collaborator = Loadable({
|
|
||||||
loader: () => import('./Collaborator'),
|
|
||||||
loading: Loading,
|
|
||||||
})
|
|
||||||
const Tags = Loadable({
|
|
||||||
loader: () => import('./new_tags'),
|
|
||||||
loading: Loading,
|
loading: Loading,
|
||||||
})
|
});
|
||||||
class Index extends Component{
|
const Setting = Loadable({
|
||||||
render(){
|
loader: () => import("./Setting"),
|
||||||
|
loading: Loading,
|
||||||
|
});
|
||||||
|
const Collaborator = Loadable({
|
||||||
|
loader: () => import("./Collaborator"),
|
||||||
|
loading: Loading,
|
||||||
|
});
|
||||||
|
const Tags = Loadable({
|
||||||
|
loader: () => import("./new_tags"),
|
||||||
|
loading: Loading,
|
||||||
|
});
|
||||||
|
class Index extends Component {
|
||||||
|
render() {
|
||||||
const { projectsId } = this.props.match.params;
|
const { projectsId } = this.props.match.params;
|
||||||
const { pathname } = this.props.history.location;
|
const { pathname } = this.props.history.location;
|
||||||
|
|
||||||
const flag = (pathname === `/projects/${projectsId}/setting`);
|
const flag = pathname === `/projects/${projectsId}/setting`;
|
||||||
return(
|
return (
|
||||||
<div>
|
<div className="ProjectListIndex">
|
||||||
<ul className="settingNav">
|
<div className="list-left">
|
||||||
<li className={flag?"active":""}><Link to={`/projects/${projectsId}/setting`}>仓库</Link></li>
|
<ul className="list-l-Menu">
|
||||||
<li className={pathname.indexOf('setting/collaborator')>-1?"active":""}><Link to={`/projects/${projectsId}/setting/collaborator`}>协作者</Link></li>
|
<li className={flag ? "active" : ""}>
|
||||||
<li className={pathname.indexOf('setting/tags')>-1?"active":""}><Link to={`/projects/${projectsId}/setting/tags`}>项目标签</Link></li>
|
<p>
|
||||||
{/* <li className={pathname.indexOf('setting/branch')>-1?"active":""}><Link to={`/projects/${projectsId}/setting/branch`}>分支列表</Link></li> */}
|
<Link to={`/projects/${projectsId}/setting`} className="w-100">
|
||||||
</ul>
|
|
||||||
<div className="main">
|
|
||||||
<Switch {...this.props}>
|
|
||||||
{/* 协作者 */}
|
|
||||||
<Route path="/projects/:projectsId/setting/collaborator"
|
|
||||||
render={
|
|
||||||
(props) => (<Collaborator {...this.props} {...props} {...this.state}/>)
|
|
||||||
}
|
|
||||||
></Route>
|
|
||||||
{/* 修改仓库信息 */}
|
|
||||||
<Route path="/projects/:projectsId/setting/tags"
|
|
||||||
render={
|
|
||||||
(props) => (<Tags {...this.props} {...props} {...this.state}/>)
|
|
||||||
}
|
|
||||||
></Route>
|
|
||||||
{/* 修改仓库信息 */}
|
|
||||||
<Route path="/projects/:projectsId/setting"
|
|
||||||
render={
|
|
||||||
(props) => (<Setting {...this.props} {...props} {...this.state}/>)
|
|
||||||
}
|
|
||||||
></Route>
|
|
||||||
|
|
||||||
</Switch>
|
<i className="iconfont icon-huabanfuben font-18 mr10"></i>基本设置
|
||||||
|
</Link>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
className={
|
||||||
|
pathname.indexOf("setting/collaborator") > -1 ? "active" : ""
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
<Link to={`/projects/${projectsId}/setting/collaborator`} className="w-100">
|
||||||
|
<i className="iconfont icon-chengyuan font-18 mr10"></i>
|
||||||
|
协作者管理
|
||||||
|
</Link>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
className={
|
||||||
|
pathname.indexOf("setting/branch") > -1 ? "active" : ""
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
<Link to={`/projects/${projectsId}/setting/branch`} className="w-100">
|
||||||
|
<i className="iconfont icon-fenzhi font-20 mr10"></i>
|
||||||
|
分支设置
|
||||||
|
</Link>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
className={pathname.indexOf("setting/tags") > -1 ? "active" : ""}
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
<Link to={`/projects/${projectsId}/setting/tags`} className="w-100">
|
||||||
|
<i className="iconfont icon-biaoqian3 font-18 mr10"></i>
|
||||||
|
项目标签
|
||||||
|
</Link>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li
|
||||||
|
className={
|
||||||
|
pathname.indexOf("setting/hooks") > -1 ? "active" : ""
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
<Link to={`/projects/${projectsId}/setting/hooks`} className="w-100">
|
||||||
|
<i className="iconfont icon-zhongqingdianxinicon10 font-18 mr10"></i>
|
||||||
|
管理Web钩子
|
||||||
|
</Link>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div className="list-right">
|
||||||
|
<div>
|
||||||
|
<Switch {...this.props}>
|
||||||
|
{/* 协作者 */}
|
||||||
|
<Route
|
||||||
|
path="/projects/:projectsId/setting/collaborator"
|
||||||
|
render={(props) => (
|
||||||
|
<Collaborator {...this.props} {...props} {...this.state} />
|
||||||
|
)}
|
||||||
|
></Route>
|
||||||
|
{/* 修改仓库信息 */}
|
||||||
|
<Route
|
||||||
|
path="/projects/:projectsId/setting/tags"
|
||||||
|
render={(props) => (
|
||||||
|
<Tags {...this.props} {...props} {...this.state} />
|
||||||
|
)}
|
||||||
|
></Route>
|
||||||
|
{/* 修改仓库信息 */}
|
||||||
|
<Route
|
||||||
|
path="/projects/:projectsId/setting"
|
||||||
|
render={(props) => (
|
||||||
|
<Setting {...this.props} {...props} {...this.state} />
|
||||||
|
)}
|
||||||
|
></Route>
|
||||||
|
</Switch>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export default Index;
|
export default Index;
|
||||||
|
|
@ -1,196 +1,212 @@
|
||||||
import React , { Component } from 'react';
|
import React, { Component } from "react";
|
||||||
import { Form , Input , Checkbox , Select } from 'antd';
|
import { Form, Input, Checkbox, Select } from "antd";
|
||||||
|
|
||||||
import axios from 'axios';
|
import axios from "axios";
|
||||||
const { TextArea } = Input;
|
const { TextArea } = Input;
|
||||||
const { Option } = Select;
|
const { Option } = Select;
|
||||||
class Setting extends Component{
|
class Setting extends Component {
|
||||||
constructor(props){
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state={
|
this.state = {
|
||||||
CategoryList:undefined,
|
CategoryList: undefined,
|
||||||
LanguageList:undefined,
|
LanguageList: undefined,
|
||||||
private_check:undefined
|
private_check: undefined,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount=()=>{
|
componentDidMount = () => {
|
||||||
this.getCategory();
|
this.getCategory();
|
||||||
this.getLanguage();
|
this.getLanguage();
|
||||||
this.getInfo();
|
this.getInfo();
|
||||||
}
|
};
|
||||||
getLanguage=()=>{
|
getLanguage = () => {
|
||||||
const url = `/project_languages.json`
|
const url = `/project_languages.json`;
|
||||||
axios.get(url).then((result)=>{
|
axios
|
||||||
if(result){
|
.get(url)
|
||||||
let LanguageList = this.setOptionsList(result.data.project_languages)
|
.then((result) => {
|
||||||
this.setState({
|
if (result) {
|
||||||
LanguageList
|
let LanguageList = this.setOptionsList(result.data.project_languages);
|
||||||
})
|
this.setState({
|
||||||
}
|
LanguageList,
|
||||||
}).catch((error)=>{})
|
});
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {});
|
||||||
|
};
|
||||||
|
|
||||||
getInfo=()=>{
|
getInfo = () => {
|
||||||
const { projectsId } = this.props.match.params;
|
const { projectsId } = this.props.match.params;
|
||||||
const url = `/repositories/${projectsId}/edit.json`;
|
const url = `/repositories/${projectsId}/edit.json`;
|
||||||
axios.get(url).then(result=>{
|
axios
|
||||||
if(result){
|
.get(url)
|
||||||
this.props.form.setFieldsValue({
|
.then((result) => {
|
||||||
...result.data
|
if (result) {
|
||||||
})
|
this.props.form.setFieldsValue({
|
||||||
this.setState({
|
...result.data,
|
||||||
private_check:result.data.private
|
});
|
||||||
})
|
this.setState({
|
||||||
}
|
private_check: result.data.private,
|
||||||
}).catch(error=>{
|
});
|
||||||
console.log(error);
|
}
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
getCategory=()=>{
|
|
||||||
const url = `/project_categories.json`
|
|
||||||
axios.get(url).then((result)=>{
|
|
||||||
if(result){
|
|
||||||
let CategoryList = this.setOptionsList(result.data.project_categories)
|
|
||||||
this.setState({
|
|
||||||
CategoryList
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}).catch((error)=>{})
|
|
||||||
}
|
|
||||||
|
|
||||||
setOptionsList = (data) =>{
|
|
||||||
let list = undefined;
|
|
||||||
if(data && data.length > 0){
|
|
||||||
list = data.map((item,key)=>{
|
|
||||||
return(
|
|
||||||
<Option key={item.id} value={item.id}>{item.name}</Option>
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
getCategory = () => {
|
||||||
|
const url = `/project_categories.json`;
|
||||||
|
axios
|
||||||
|
.get(url)
|
||||||
|
.then((result) => {
|
||||||
|
if (result) {
|
||||||
|
let CategoryList = this.setOptionsList(
|
||||||
|
result.data.project_categories
|
||||||
|
);
|
||||||
|
this.setState({
|
||||||
|
CategoryList,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {});
|
||||||
|
};
|
||||||
|
|
||||||
|
setOptionsList = (data) => {
|
||||||
|
let list = undefined;
|
||||||
|
if (data && data.length > 0) {
|
||||||
|
list = data.map((item, key) => {
|
||||||
|
return (
|
||||||
|
<Option key={item.id} value={item.id}>
|
||||||
|
{item.name}
|
||||||
|
</Option>
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
}
|
};
|
||||||
|
|
||||||
// 更新仓库设置
|
// 更新仓库设置
|
||||||
resetSetting=()=>{
|
resetSetting = () => {
|
||||||
this.props.form.validateFields((err,values)=>{
|
this.props.form.validateFields((err, values) => {
|
||||||
if(!err){
|
if (!err) {
|
||||||
const { project_id } = this.props;
|
const { project_id } = this.props;
|
||||||
const { private_check } = this.state;
|
const { private_check } = this.state;
|
||||||
const url = `/projects/${project_id}.json`;
|
const url = `/projects/${project_id}.json`;
|
||||||
axios.put(url,{
|
axios
|
||||||
name:values.project_name,
|
.put(url, {
|
||||||
description:values.project_description,
|
name: values.project_name,
|
||||||
private:private_check,
|
description: values.project_description,
|
||||||
...values
|
private: private_check,
|
||||||
}).then(result=>{
|
...values,
|
||||||
if(result){
|
})
|
||||||
this.props.showNotification(`仓库信息修改成功!`);
|
.then((result) => {
|
||||||
const { getDetail } = this.props;
|
if (result) {
|
||||||
getDetail && getDetail();
|
this.props.showNotification(`仓库信息修改成功!`);
|
||||||
}
|
const { getDetail } = this.props;
|
||||||
}).catch(error=>{
|
getDetail && getDetail();
|
||||||
console.log(error);
|
}
|
||||||
})
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
// 删除本仓库
|
// 删除本仓库
|
||||||
deleteProject=()=>{
|
deleteProject = () => {
|
||||||
this.props.confirm({
|
this.props.confirm({
|
||||||
content:"删除后无法恢复,是否确认删除本仓库?",
|
content: "删除后无法恢复,是否确认删除本仓库?",
|
||||||
onOk:()=>{
|
onOk: () => {
|
||||||
const { project_id } = this.props;
|
const { project_id } = this.props;
|
||||||
const url = `/projects/${project_id}.json`;
|
const url = `/projects/${project_id}.json`;
|
||||||
axios.delete(url).then(result=>{
|
axios
|
||||||
this.props.showNotification("仓库删除成功!");
|
.delete(url)
|
||||||
this.props.history.push("/projects");
|
.then((result) => {
|
||||||
}).catch(error=>{
|
this.props.showNotification("仓库删除成功!");
|
||||||
console.log(error);
|
this.props.history.push("/projects");
|
||||||
})
|
})
|
||||||
}
|
.catch((error) => {
|
||||||
})
|
console.log(error);
|
||||||
}
|
});
|
||||||
changePrivate=(e)=>{
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
changePrivate = (e) => {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
this.setState({
|
this.setState({
|
||||||
private_check:e.target.checked
|
private_check: e.target.checked,
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
render(){
|
render() {
|
||||||
const { getFieldDecorator } = this.props.form;
|
const { getFieldDecorator } = this.props.form;
|
||||||
|
|
||||||
const { CategoryList , LanguageList , private_check } = this.state;
|
const { CategoryList, LanguageList, private_check } = this.state;
|
||||||
return(
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="normalBox">
|
<div className="normalBox">
|
||||||
<div className="normalBox-title font-16">
|
<div className="normalBox-title font-16">基本设置</div>
|
||||||
基本设置
|
|
||||||
</div>
|
|
||||||
<Form className="baseForm">
|
<Form className="baseForm">
|
||||||
<Form.Item
|
<Form.Item label="项目名称">
|
||||||
label="项目名称"
|
{getFieldDecorator("project_name", {
|
||||||
>
|
rules: [
|
||||||
{getFieldDecorator('project_name', {
|
{
|
||||||
rules: [{
|
required: true,
|
||||||
required: true, message: '请输入项目名称'
|
message: "请输入项目名称",
|
||||||
}],
|
},
|
||||||
})(
|
],
|
||||||
<Input placeholder="请输入项目名称"/>
|
})(<Input placeholder="请输入项目名称" />)}
|
||||||
)}
|
</Form.Item>
|
||||||
</Form.Item >
|
<div className="df" style={{ alignItems: "center" }}>
|
||||||
<div className="df" style={{alignItems:"center"}}>
|
|
||||||
<span className="mr20 mb15 font-16">可见性</span>
|
<span className="mr20 mb15 font-16">可见性</span>
|
||||||
<Form.Item
|
<Form.Item label="">
|
||||||
label=""
|
{getFieldDecorator("private", {
|
||||||
>
|
|
||||||
{getFieldDecorator('private', {
|
|
||||||
rules: [],
|
rules: [],
|
||||||
})(
|
})(
|
||||||
<Checkbox checked={private_check} onChange={this.changePrivate}>将仓库设为私有</Checkbox>
|
<Checkbox
|
||||||
|
checked={private_check}
|
||||||
|
onChange={this.changePrivate}
|
||||||
|
>
|
||||||
|
将仓库设为私有
|
||||||
|
</Checkbox>
|
||||||
)}
|
)}
|
||||||
</Form.Item >
|
</Form.Item>
|
||||||
</div>
|
</div>
|
||||||
<Form.Item
|
<Form.Item label="仓库描述">
|
||||||
label="仓库描述"
|
{getFieldDecorator("project_description", {
|
||||||
>
|
rules: [],
|
||||||
{getFieldDecorator('project_description', {
|
|
||||||
rules: [],
|
|
||||||
})(
|
|
||||||
<TextArea placeholder="请输入仓库描述" style={{height:"80px"}}/>
|
|
||||||
)}
|
|
||||||
</Form.Item >
|
|
||||||
<Form.Item
|
|
||||||
label="项目类别"
|
|
||||||
>
|
|
||||||
{getFieldDecorator('project_category_id', {
|
|
||||||
rules: [{
|
|
||||||
required: true, message: '请选择大类别'
|
|
||||||
}],
|
|
||||||
})(
|
})(
|
||||||
<Select>
|
<TextArea
|
||||||
{CategoryList}
|
placeholder="请输入仓库描述"
|
||||||
</Select>
|
style={{ height: "80px" }}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item label="项目类别">
|
||||||
label="项目语言"
|
{getFieldDecorator("project_category_id", {
|
||||||
>
|
rules: [
|
||||||
{getFieldDecorator('project_language_id', {
|
{
|
||||||
rules: [{
|
required: true,
|
||||||
required: true, message: '请选择项目语言'
|
message: "请选择大类别",
|
||||||
}],
|
},
|
||||||
})(
|
],
|
||||||
<Select>
|
})(<Select>{CategoryList}</Select>)}
|
||||||
{LanguageList}
|
</Form.Item>
|
||||||
</Select>
|
<Form.Item label="项目语言">
|
||||||
)}
|
{getFieldDecorator("project_language_id", {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: "请选择项目语言",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})(<Select>{LanguageList}</Select>)}
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<p className="clearfix">
|
<p className="clearfix">
|
||||||
<a className="submitBtn" onClick={this.resetSetting}>更新仓库设置</a>
|
<a className="submitBtn" onClick={this.resetSetting}>
|
||||||
|
更新仓库设置
|
||||||
|
</a>
|
||||||
</p>
|
</p>
|
||||||
</Form>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -199,14 +215,19 @@ class Setting extends Component{
|
||||||
<div className="flex-a-center padding15-10">
|
<div className="flex-a-center padding15-10">
|
||||||
<div>
|
<div>
|
||||||
<p className="font-bd font-16">删除本仓库</p>
|
<p className="font-bd font-16">删除本仓库</p>
|
||||||
<p className="mt10">删除仓库是永久性的, 无法撤消,且删除后,与仓库关联的项目/任务/合并请求/版本发布等,均会被删除</p>
|
<p className="mt10">
|
||||||
|
删除仓库是永久性的,
|
||||||
|
无法撤消,且删除后,与仓库关联的项目/任务/合并请求/版本发布等,均会被删除
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<a onClick={this.deleteProject} className="red_deleteBtn">删除本仓库</a>
|
<a onClick={this.deleteProject} className="red_deleteBtn">
|
||||||
|
删除本仓库
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const WrappedSettingIndexForm = Form.create({ name: 'settingForm' })(Setting);
|
const WrappedSettingIndexForm = Form.create({ name: "settingForm" })(Setting);
|
||||||
export default WrappedSettingIndexForm;
|
export default WrappedSettingIndexForm;
|
||||||
|
|
@ -49,7 +49,7 @@
|
||||||
}
|
}
|
||||||
.addPanel{
|
.addPanel{
|
||||||
display: flex;
|
display: flex;
|
||||||
padding:15px;
|
/* padding:15px; */
|
||||||
}
|
}
|
||||||
.red_btn{
|
.red_btn{
|
||||||
display: block;
|
display: block;
|
||||||
|
|
@ -102,3 +102,16 @@
|
||||||
.padding15-10{
|
.padding15-10{
|
||||||
padding:15px 10px;
|
padding:15px 10px;
|
||||||
}
|
}
|
||||||
|
.w-100{width: 100%;}
|
||||||
|
.fwb{font-weight: 600;}
|
||||||
|
.text-black{color: #333;}
|
||||||
|
.bbr{border-bottom: 1px solid #f6f6f6;}
|
||||||
|
.ant-input-group-addon{border: 0 !important;}
|
||||||
|
.grid-item-left{display: grid; align-items: center; grid-template-columns: 1fr max-content;}
|
||||||
|
.text-primary{color: #1890ff !important;}
|
||||||
|
.ant-dropdown-menu{min-width: 90px ; text-align: center !important;}
|
||||||
|
.text-green{color: #28BD6C !important;}
|
||||||
|
.text-yellow{color: #FF6E21 !important;}
|
||||||
|
.text-delete{color: #BBBBBB; }
|
||||||
|
.text-delete:hover{color: #db2828; }
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue