mirror of
https://github.com/YMFE/yapi.git
synced 2024-12-09 05:00:30 +08:00
feat: 添加分组可以添加多个组长
This commit is contained in:
parent
b2102a9753
commit
1bfdeff722
@ -21,7 +21,7 @@ module.exports = {
|
||||
"rules": {
|
||||
"indent": [
|
||||
"error",
|
||||
2,
|
||||
4,
|
||||
{
|
||||
"SwitchCase": 1
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { AutoComplete } from 'antd';
|
||||
import { Select } from 'antd';
|
||||
import axios from 'axios';
|
||||
|
||||
const Option = Select.Option;
|
||||
|
||||
/**
|
||||
* 用户名输入框自动完成组件
|
||||
*
|
||||
@ -12,7 +14,7 @@ import axios from 'axios';
|
||||
* * 用户名输入框自动完成组件
|
||||
* * 用户名输入框自动完成组件
|
||||
*
|
||||
*
|
||||
*s
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -37,12 +39,14 @@ import axios from 'axios';
|
||||
class UsernameAutoComplete extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
dataSource: [],
|
||||
uid: 0,
|
||||
username: '',
|
||||
changeName: ''
|
||||
}
|
||||
}
|
||||
|
||||
state = {
|
||||
dataSource: []
|
||||
// value: []
|
||||
// uid: 0,
|
||||
// username: ''
|
||||
// changeName: ''
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
@ -50,27 +54,38 @@ class UsernameAutoComplete extends Component {
|
||||
}
|
||||
|
||||
// 改变本组件 state,并回调给父组件
|
||||
changeState = (uid, username) => {
|
||||
// 设置本组件 state
|
||||
this.setState({ uid, username });
|
||||
// 回调 将当前选中的uid和username回调给父组件
|
||||
this.props.callbackState({ uid, username });
|
||||
}
|
||||
// changeState = (value) => {
|
||||
// // 设置本组件 state
|
||||
// // this.setState({ value });
|
||||
// // 回调 将当前选中的uid和username回调给父组件
|
||||
// this.props.callbackState({ value });
|
||||
// }
|
||||
|
||||
// 输入框中的值改变时
|
||||
onChange = (userName) => {
|
||||
this.setState({
|
||||
changeName: userName
|
||||
});
|
||||
}
|
||||
// onChange = (userName) => {
|
||||
// this.setState({
|
||||
// changeName: userName
|
||||
// });
|
||||
// }
|
||||
|
||||
// process(value) {
|
||||
// return value.map(item => {
|
||||
// const index = item.index('_')
|
||||
// const id = item.substring(0, index);
|
||||
// const username = item.substring(index + 1);
|
||||
// return { id, username }
|
||||
// });
|
||||
// }
|
||||
|
||||
// 选中候选词时
|
||||
onSelect = (userName) => {
|
||||
this.state.dataSource.forEach((item) => {
|
||||
if (item.username === userName) {
|
||||
this.changeState(item.id, item.username);
|
||||
}
|
||||
});
|
||||
handleChange = (value) => {
|
||||
// this.state.dataSource.forEach((item) => {
|
||||
// if (item.username === userName) {
|
||||
// this.changeState(item.id, item.username);
|
||||
// }
|
||||
// });
|
||||
// this.changeState({ value: this.process(value) })
|
||||
this.props.callbackState(value);
|
||||
}
|
||||
|
||||
// 搜索回调
|
||||
@ -90,28 +105,45 @@ class UsernameAutoComplete extends Component {
|
||||
this.setState({
|
||||
dataSource: userList
|
||||
});
|
||||
if (userList.length) {
|
||||
// 每次取回搜索值后,没选择时默认选择第一位
|
||||
this.changeState(userList[0].id, userList[0].username);
|
||||
} else {
|
||||
// 如果没有搜索结果,则清空候选 uid 和 username
|
||||
this.changeState(-1, '');
|
||||
}
|
||||
// if (userList.length) {
|
||||
// // 每次取回搜索值后,没选择时默认选择第一位
|
||||
// // this.changeState(userList[0].id, userList[0].username);
|
||||
// } else {
|
||||
// // 如果没有搜索结果,则清空候选 uid 和 username
|
||||
// // this.changeState(-1, '');
|
||||
// this.changeState([]);
|
||||
// }
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
render () {
|
||||
|
||||
const { dataSource } = this.state;
|
||||
|
||||
return (
|
||||
<AutoComplete
|
||||
dataSource={this.state.dataSource.map(i => i.username)}
|
||||
// <AutoComplete
|
||||
// dataSource={this.state.dataSource.map(i => i.username)}
|
||||
// style={{ width: '100%' }}
|
||||
// onChange={this.onChange}
|
||||
// onSelect={this.onSelect}
|
||||
// onSearch={this.handleSearch}
|
||||
// placeholder="请输入用户名"
|
||||
// size="large"
|
||||
// />
|
||||
<Select
|
||||
mode="multiple"
|
||||
style={{ width: '100%' }}
|
||||
onChange={this.onChange}
|
||||
onSelect={this.onSelect}
|
||||
onSearch={this.handleSearch}
|
||||
placeholder="请输入用户名"
|
||||
onSearch={this.handleSearch}
|
||||
onChange={this.handleChange}
|
||||
onBlur={() => this.setState({dataSource: []})}
|
||||
size="large"
|
||||
/>
|
||||
>
|
||||
{dataSource.map((item, index) => (
|
||||
<Option key={index} value={'' + item.id}>{item.username}</Option>
|
||||
))}
|
||||
</Select>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ export default class GroupList extends Component {
|
||||
currGroupName: '',
|
||||
currGroupDesc: '',
|
||||
groupList: [],
|
||||
owner_uid: 0
|
||||
owner_uids: []
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
@ -103,20 +103,20 @@ export default class GroupList extends Component {
|
||||
this.setState({
|
||||
newGroupName: '',
|
||||
group_name: '',
|
||||
owner_uid: 0,
|
||||
owner_uids: [],
|
||||
addGroupModalVisible: false
|
||||
});
|
||||
}
|
||||
}
|
||||
@autobind
|
||||
async addGroup() {
|
||||
const { newGroupName: group_name, newGroupDesc: group_desc, owner_uid } = this.state;
|
||||
const res = await axios.post('/api/group/add', { group_name, group_desc, owner_uid })
|
||||
const { newGroupName: group_name, newGroupDesc: group_desc, owner_uids } = this.state;
|
||||
const res = await axios.post('/api/group/add', { group_name, group_desc, owner_uids })
|
||||
if (!res.data.errcode) {
|
||||
this.setState({
|
||||
newGroupName: '',
|
||||
group_name: '',
|
||||
owner_uid: 0,
|
||||
owner_uids: [],
|
||||
addGroupModalVisible: false
|
||||
});
|
||||
await this.props.fetchGroupList();
|
||||
@ -168,9 +168,9 @@ export default class GroupList extends Component {
|
||||
}
|
||||
|
||||
@autobind
|
||||
onUserSelect(childState) {
|
||||
onUserSelect(uids) {
|
||||
this.setState({
|
||||
owner_uid: childState.uid
|
||||
owner_uids: uids
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,7 @@ class MemberList extends Component {
|
||||
role: '',
|
||||
visible: false,
|
||||
dataSource: [],
|
||||
inputUid: 0,
|
||||
inputUids: [],
|
||||
inputRole: 'dev'
|
||||
}
|
||||
}
|
||||
@ -82,7 +82,7 @@ class MemberList extends Component {
|
||||
handleOk() {
|
||||
this.props.addMember({
|
||||
id: this.props.currGroup._id,
|
||||
member_uid: this.state.inputUid,
|
||||
member_uids: this.state.inputUids,
|
||||
role: this.state.inputRole
|
||||
}).then((res) => {
|
||||
if (!res.payload.data.errcode) {
|
||||
@ -169,9 +169,9 @@ class MemberList extends Component {
|
||||
}
|
||||
|
||||
@autobind
|
||||
onUserSelect(childState) {
|
||||
onUserSelect(uids) {
|
||||
this.setState({
|
||||
inputUid: childState.uid
|
||||
inputUids: uids
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,41 @@
|
||||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
// import { connect } from 'react-redux'
|
||||
// import { Table } from 'antd'
|
||||
// import { fetchInterfaceList } from '../../../../reducer/modules/interface.js';
|
||||
|
||||
// @connect(
|
||||
// state => {
|
||||
// return {
|
||||
// list: state.inter.list
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// fetchInterfaceList
|
||||
// }
|
||||
// )
|
||||
export default class ImportInterface extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
// colId: PropTypes.number,
|
||||
// fetchInterfaceList: PropTypes.func,
|
||||
// projectId: PropTypes.number,
|
||||
list: PropTypes.array
|
||||
}
|
||||
|
||||
// componentWillMount() {
|
||||
// this.props.fetchInterfaceList(this.props.projectId);
|
||||
// }
|
||||
|
||||
render() {
|
||||
console.log(this.props.list)
|
||||
return (
|
||||
<div>
|
||||
<div>{this.props.list}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
@ -3,9 +3,11 @@ import { connect } from 'react-redux';
|
||||
import { withRouter } from 'react-router'
|
||||
import PropTypes from 'prop-types'
|
||||
import { fetchInterfaceColList, fetchInterfaceCaseList, setColData } from '../../../../reducer/modules/interfaceCol'
|
||||
import { fetchInterfaceList } from '../../../../reducer/modules/interface.js';
|
||||
import { autobind } from 'core-decorators';
|
||||
import axios from 'axios';
|
||||
import { Input, Icon, Button, Modal, message, Tooltip, Tree, Dropdown, Menu, Form } from 'antd';
|
||||
import ImportInterface from './ImportInterface'
|
||||
|
||||
const TreeNode = Tree.TreeNode;
|
||||
const FormItem = Form.Item;
|
||||
@ -45,12 +47,14 @@ const ColModalForm = Form.create()((props) => {
|
||||
interfaceColList: state.interfaceCol.interfaceColList,
|
||||
currColId: state.interfaceCol.currColId,
|
||||
currCaseId: state.interfaceCol.currCaseId,
|
||||
isShowCol: state.interfaceCol.isShowCol
|
||||
isShowCol: state.interfaceCol.isShowCol,
|
||||
list: state.inter.list
|
||||
}
|
||||
},
|
||||
{
|
||||
fetchInterfaceColList,
|
||||
fetchInterfaceCaseList,
|
||||
fetchInterfaceList,
|
||||
setColData
|
||||
}
|
||||
)
|
||||
@ -62,11 +66,13 @@ export default class InterfaceColMenu extends Component {
|
||||
interfaceColList: PropTypes.array,
|
||||
fetchInterfaceColList: PropTypes.func,
|
||||
fetchInterfaceCaseList: PropTypes.func,
|
||||
fetchInterfaceList: PropTypes.func,
|
||||
setColData: PropTypes.func,
|
||||
history: PropTypes.object,
|
||||
currColId: PropTypes.number,
|
||||
currCaseId: PropTypes.number,
|
||||
isShowCol: PropTypes.bool
|
||||
isShowCol: PropTypes.bool,
|
||||
list: PropTypes.array
|
||||
}
|
||||
|
||||
state = {
|
||||
@ -74,7 +80,9 @@ export default class InterfaceColMenu extends Component {
|
||||
colModalType: '',
|
||||
colModalVisible: false,
|
||||
editColId: 0,
|
||||
filterValue: ''
|
||||
filterValue: '',
|
||||
// importInterVisible: false,
|
||||
importInterIds: []
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
@ -188,6 +196,25 @@ export default class InterfaceColMenu extends Component {
|
||||
this.form = form;
|
||||
}
|
||||
|
||||
selectInterface = (importInterIds) => {
|
||||
this.setState({ importInterIds })
|
||||
}
|
||||
|
||||
showImportInterface = async (colId) => {
|
||||
const projectId = this.props.match.params.id;
|
||||
await fetchInterfaceList(projectId)
|
||||
confirm({
|
||||
title: '请选择添加到集合的接口',
|
||||
content: <ImportInterface onSelect={this.selectInterface} list={this.props.list} />,
|
||||
onOk() {
|
||||
console.log(colId);
|
||||
},
|
||||
onCancel() {
|
||||
console.log('Cancel');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
filterCol = (e) => {
|
||||
const value = e.target.value;
|
||||
this.setState({filterValue: value})
|
||||
@ -208,6 +235,9 @@ export default class InterfaceColMenu extends Component {
|
||||
this.showDelColConfirm(col._id)
|
||||
}}>删除集合</span>
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
<span onClick={() => this.showImportInterface(col._id)}>导入接口</span>
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
)
|
||||
};
|
||||
|
@ -49,7 +49,7 @@ class ProjectMember extends Component {
|
||||
role: '',
|
||||
visible: false,
|
||||
dataSource: [],
|
||||
inputUid: 0,
|
||||
inputUids: [],
|
||||
inputRole: 'dev'
|
||||
}
|
||||
}
|
||||
@ -90,7 +90,7 @@ class ProjectMember extends Component {
|
||||
handleOk() {
|
||||
this.props.addMember({
|
||||
id: this.props.match.params.id,
|
||||
member_uid: this.state.inputUid,
|
||||
member_uids: this.state.inputUids,
|
||||
role: this.state.inputRole
|
||||
}).then((res) => {
|
||||
if (!res.payload.data.errcode) {
|
||||
@ -144,9 +144,9 @@ class ProjectMember extends Component {
|
||||
}
|
||||
|
||||
@autobind
|
||||
onUserSelect(childState) {
|
||||
onUserSelect(uids) {
|
||||
this.setState({
|
||||
inputUid: childState.uid
|
||||
inputUids: uids
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,7 @@ class groupController extends baseController {
|
||||
* @foldnumber 10
|
||||
* @param {String} group_name 项目分组名称,不能为空
|
||||
* @param {String} [group_desc] 项目分组描述
|
||||
* @param {String} owner_uid 组长uid
|
||||
* @param {String} [owner_uids] 组长[uid]
|
||||
* @returns {Object}
|
||||
* @example ./api/group/add.json
|
||||
*/
|
||||
@ -67,17 +67,22 @@ class groupController extends baseController {
|
||||
return ctx.body = yapi.commons.resReturn(null, 400, '项目分组名不能为空');
|
||||
}
|
||||
|
||||
// if (!params.owner_uid) {
|
||||
// return ctx.body = yapi.commons.resReturn(null, 400, '项目分组必须添加一个组长');
|
||||
// }
|
||||
if (!params.owner_uids || !params.owner_uids.length) {
|
||||
return ctx.body = yapi.commons.resReturn(null, 400, '项目分组必须添加一个组长');
|
||||
}
|
||||
|
||||
let groupUserdata = null;
|
||||
if (params.owner_uid) {
|
||||
groupUserdata = await this.getUserdata(params.owner_uid, 'owner');
|
||||
if (groupUserdata === null) {
|
||||
return ctx.body = yapi.commons.resReturn(null, 400, '组长uid不存在')
|
||||
let owners = [];
|
||||
for(let i = 0, len = params.owner_uids.length; i < len; i++) {
|
||||
let id = params.owner_uids[i]
|
||||
let groupUserdata = await this.getUserdata(id, 'owner');
|
||||
if (groupUserdata) {
|
||||
owners.push(groupUserdata)
|
||||
}
|
||||
}
|
||||
// groupUserdata = await this.getUserdata(params.owner_uid, 'owner');
|
||||
if (!owners.length) {
|
||||
return ctx.body = yapi.commons.resReturn(null, 400, '组长uid不存在')
|
||||
}
|
||||
|
||||
let groupInst = yapi.getInst(groupModel);
|
||||
|
||||
@ -93,7 +98,7 @@ class groupController extends baseController {
|
||||
uid: this.getUid(),
|
||||
add_time: yapi.commons.time(),
|
||||
up_time: yapi.commons.time(),
|
||||
members: groupUserdata ? [groupUserdata] : []
|
||||
members: owners
|
||||
};
|
||||
|
||||
try {
|
||||
@ -130,7 +135,7 @@ class groupController extends baseController {
|
||||
* @category group
|
||||
* @foldnumber 10
|
||||
* @param {String} id 项目分组id
|
||||
* @param {String} member_uid 项目分组成员uid
|
||||
* @param {String} member_uids 项目分组成员[uid]
|
||||
* @param {String} role 成员角色,owner or dev or guest
|
||||
* @returns {Object}
|
||||
* @example
|
||||
@ -142,7 +147,7 @@ class groupController extends baseController {
|
||||
|
||||
let params = ctx.request.body;
|
||||
let groupInst = yapi.getInst(groupModel);
|
||||
if (!params.member_uid) {
|
||||
if (!params.member_uids || !params.member_uids.length) {
|
||||
return ctx.body = yapi.commons.resReturn(null, 400, '分组成员uid不能为空');
|
||||
}
|
||||
if (!params.id) {
|
||||
@ -150,22 +155,46 @@ class groupController extends baseController {
|
||||
}
|
||||
|
||||
params.role = ['owner', 'dev', 'guest'].find(v => v === params.role) || 'dev';
|
||||
let add_members = [];
|
||||
let exist_members = [];
|
||||
let no_members = []
|
||||
for(let i = 0, len = params.member_uids.length; i < len; i++) {
|
||||
let id = params.member_uids[i];
|
||||
let check = await groupInst.checkMemberRepeat(params.id, id);
|
||||
let userdata = await this.getUserdata(id, params.role);
|
||||
console.log(userdata)
|
||||
if (check > 0) {
|
||||
exist_members.push(userdata)
|
||||
} else if (!userdata) {
|
||||
no_members.push(id)
|
||||
} else {
|
||||
userdata.role !== 'admin' && add_members.push(userdata);
|
||||
delete userdata._role;
|
||||
}
|
||||
}
|
||||
|
||||
var check = await groupInst.checkMemberRepeat(params.id, params.member_uid);
|
||||
if (check > 0) {
|
||||
return ctx.body = yapi.commons.resReturn(null, 400, '成员已存在');
|
||||
}
|
||||
let groupUserdata = await this.getUserdata(params.member_uid, params.role);
|
||||
if (groupUserdata === null) {
|
||||
return ctx.body = yapi.commons.resReturn(null, 400, '组长uid不存在')
|
||||
}
|
||||
if (groupUserdata._role === 'admin') {
|
||||
return ctx.body = yapi.commons.resReturn(null, 400, '不能邀请管理员')
|
||||
}
|
||||
delete groupUserdata._role;
|
||||
// params.role = ['owner', 'dev', 'guest'].find(v => v === params.role) || 'dev';
|
||||
|
||||
// var check = await groupInst.checkMemberRepeat(params.id, params.member_uid);
|
||||
// if (check > 0) {
|
||||
// return ctx.body = yapi.commons.resReturn(null, 400, '成员已存在');
|
||||
// }
|
||||
// let groupUserdata = await this.getUserdata(params.member_uid, params.role);
|
||||
// if (groupUserdata === null) {
|
||||
// return ctx.body = yapi.commons.resReturn(null, 400, '组长uid不存在')
|
||||
// }
|
||||
// if (groupUserdata._role === 'admin') {
|
||||
// return ctx.body = yapi.commons.resReturn(null, 400, '不能邀请管理员')
|
||||
// }
|
||||
// delete groupUserdata._role;
|
||||
try {
|
||||
let result = await groupInst.addMember(params.id, groupUserdata);
|
||||
ctx.body = yapi.commons.resReturn(result);
|
||||
let result = await groupInst.addMember(params.id, add_members);
|
||||
ctx.body = yapi.commons.resReturn({
|
||||
result,
|
||||
add_members,
|
||||
exist_members,
|
||||
no_members
|
||||
});
|
||||
} catch (e) {
|
||||
ctx.body = yapi.commons.resReturn(null, 402, e.message);
|
||||
}
|
||||
|
@ -159,7 +159,7 @@ class projectController extends baseController {
|
||||
*/
|
||||
async addMember(ctx) {
|
||||
let params = ctx.request.body;
|
||||
if (!params.member_uid) {
|
||||
if (!params.member_uids || !params.member_uids.length) {
|
||||
return ctx.body = yapi.commons.resReturn(null, 400, '项目成员uid不能为空');
|
||||
}
|
||||
if (!params.id) {
|
||||
@ -170,30 +170,52 @@ class projectController extends baseController {
|
||||
return ctx.body = yapi.commons.resReturn(null, 405, '没有权限');
|
||||
}
|
||||
|
||||
var check = await this.Model.checkMemberRepeat(params.id, params.member_uid);
|
||||
if (check > 0) {
|
||||
return ctx.body = yapi.commons.resReturn(null, 400, '项目成员已存在');
|
||||
}
|
||||
|
||||
params.role = ['owner', 'dev', 'guest'].find(v => v === params.role) || 'dev';
|
||||
let add_members = [];
|
||||
let exist_members = [];
|
||||
let no_members = []
|
||||
params.member_uids.forEach(async id => {
|
||||
let check = await this.Model.checkMemberRepeat(params.id, id);
|
||||
let userdata = await this.getUserdata(id, params.role);
|
||||
if (!check) {
|
||||
exist_members.push(userdata)
|
||||
} else if (!userdata) {
|
||||
no_members.push(id)
|
||||
} else {
|
||||
add_members.push(userdata)
|
||||
}
|
||||
});
|
||||
|
||||
let userdata = await this.getUserdata(params.member_uid, params.role);
|
||||
if (userdata === null) {
|
||||
return ctx.body = yapi.commons.resReturn(null, 400, '成员uid不存在')
|
||||
}
|
||||
// var check = await this.Model.checkMemberRepeat(params.id, params.member_uids);
|
||||
// if (check > 0) {
|
||||
// // return ctx.body = yapi.commons.resReturn(null, 400, '项目成员已存在');
|
||||
// }
|
||||
|
||||
// params.role = ['owner', 'dev', 'guest'].find(v => v === params.role) || 'dev';
|
||||
|
||||
// let userdata = await this.getUserdata(params.member_uid, params.role);
|
||||
// if (userdata === null) {
|
||||
// // return ctx.body = yapi.commons.resReturn(null, 400, '成员uid不存在')
|
||||
// }
|
||||
|
||||
|
||||
try {
|
||||
let result = await this.Model.addMember(params.id, userdata);
|
||||
let result = await this.Model.addMember(params.id, add_members);
|
||||
let username = this.getUsername();
|
||||
yapi.commons.saveLog({
|
||||
content: `用户 "${username}" 添加了项目成员 "${userdata.username}"`,
|
||||
content: `用户 "${username}" 添加了项目成员 "${add_members.reduce((str, item) => (str ? str + '、' : '') + item.username, '')}"`,
|
||||
type: 'project',
|
||||
uid: this.getUid(),
|
||||
username: username,
|
||||
typeid: params.id
|
||||
});
|
||||
ctx.body = yapi.commons.resReturn(result);
|
||||
ctx.body = yapi.commons.resReturn({
|
||||
result,
|
||||
add_members,
|
||||
exist_members,
|
||||
no_members
|
||||
});
|
||||
} catch (e) {
|
||||
ctx.body = yapi.commons.resReturn(null, 402, e.message);
|
||||
}
|
||||
|
@ -52,7 +52,8 @@ class groupModel extends baseModel {
|
||||
{
|
||||
_id: id
|
||||
}, {
|
||||
$push: { members: data }
|
||||
// $push: { members: data },
|
||||
$push: { members: { $each: data } }
|
||||
}
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user