642 lines
20 KiB
JavaScript
642 lines
20 KiB
JavaScript
import React, { PureComponent as Component } from 'react';
|
|
import { connect } from 'react-redux';
|
|
import PropTypes from 'prop-types';
|
|
import {
|
|
fetchInterfaceListMenu,
|
|
fetchInterfaceList,
|
|
fetchInterfaceCatList,
|
|
fetchInterfaceData,
|
|
deleteInterfaceData,
|
|
deleteInterfaceCatData,
|
|
initInterface
|
|
} from '../../../../reducer/modules/interface.js';
|
|
import { getProject } from '../../../../reducer/modules/project.js';
|
|
import { Input, Icon, Button, Modal, message, Tree, Tooltip } from 'antd';
|
|
import AddInterfaceForm from './AddInterfaceForm';
|
|
import AddInterfaceCatForm from './AddInterfaceCatForm';
|
|
import axios from 'axios';
|
|
import { Link, withRouter } from 'react-router-dom';
|
|
import produce from 'immer';
|
|
import { arrayChangeIndex } from '../../../../common.js';
|
|
|
|
import './interfaceMenu.scss';
|
|
|
|
const confirm = Modal.confirm;
|
|
const TreeNode = Tree.TreeNode;
|
|
const headHeight = 240; // menu顶部到网页顶部部分的高度
|
|
|
|
@connect(
|
|
state => {
|
|
return {
|
|
list: state.inter.list,
|
|
inter: state.inter.curdata,
|
|
curProject: state.project.currProject,
|
|
expands: []
|
|
};
|
|
},
|
|
{
|
|
fetchInterfaceListMenu,
|
|
fetchInterfaceData,
|
|
deleteInterfaceCatData,
|
|
deleteInterfaceData,
|
|
initInterface,
|
|
getProject,
|
|
fetchInterfaceCatList,
|
|
fetchInterfaceList
|
|
}
|
|
)
|
|
class InterfaceMenu extends Component {
|
|
static propTypes = {
|
|
match: PropTypes.object,
|
|
inter: PropTypes.object,
|
|
projectId: PropTypes.string,
|
|
list: PropTypes.array,
|
|
fetchInterfaceListMenu: PropTypes.func,
|
|
curProject: PropTypes.object,
|
|
fetchInterfaceData: PropTypes.func,
|
|
addInterfaceData: PropTypes.func,
|
|
deleteInterfaceData: PropTypes.func,
|
|
initInterface: PropTypes.func,
|
|
history: PropTypes.object,
|
|
router: PropTypes.object,
|
|
getProject: PropTypes.func,
|
|
fetchInterfaceCatList: PropTypes.func,
|
|
fetchInterfaceList: PropTypes.func
|
|
};
|
|
|
|
/**
|
|
* @param {String} key
|
|
*/
|
|
changeModal = (key, status) => {
|
|
//visible add_cat_modal_visible change_cat_modal_visible del_cat_modal_visible
|
|
let newState = {};
|
|
newState[key] = status;
|
|
this.setState(newState);
|
|
};
|
|
|
|
handleCancel = () => {
|
|
this.setState({
|
|
visible: false
|
|
});
|
|
};
|
|
|
|
constructor(props) {
|
|
super(props);
|
|
this.state = {
|
|
curKey: null,
|
|
visible: false,
|
|
delIcon: null,
|
|
curCatid: null,
|
|
add_cat_modal_visible: false,
|
|
change_cat_modal_visible: false,
|
|
del_cat_modal_visible: false,
|
|
curCatdata: {},
|
|
expands: null,
|
|
list: []
|
|
};
|
|
}
|
|
|
|
handleRequest() {
|
|
this.props.initInterface();
|
|
this.getList();
|
|
}
|
|
|
|
async getList() {
|
|
let r = await this.props.fetchInterfaceListMenu(this.props.projectId);
|
|
this.setState({
|
|
list: r.payload.data.data
|
|
});
|
|
}
|
|
|
|
UNSAFE_componentWillMount() {
|
|
this.handleRequest();
|
|
}
|
|
|
|
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
if (this.props.list !== nextProps.list) {
|
|
// console.log('next', nextProps.list)
|
|
this.setState({
|
|
list: nextProps.list
|
|
});
|
|
}
|
|
}
|
|
|
|
onSelect = selectedKeys => {
|
|
const { history, match } = this.props;
|
|
let curkey = selectedKeys[0];
|
|
|
|
if (!curkey || !selectedKeys) {
|
|
return false;
|
|
}
|
|
let basepath = '/project/' + match.params.id + '/interface/api';
|
|
if (curkey === 'root') {
|
|
history.push(basepath);
|
|
} else {
|
|
history.push(basepath + '/' + curkey);
|
|
}
|
|
this.setState({
|
|
expands: null
|
|
});
|
|
};
|
|
|
|
changeExpands = () => {
|
|
this.setState({
|
|
expands: null
|
|
});
|
|
};
|
|
|
|
handleAddInterface = (data, cb) => {
|
|
data.project_id = this.props.projectId;
|
|
axios.post('/api/interface/add', data).then(res => {
|
|
if (res.data.errcode !== 0) {
|
|
return message.error(res.data.errmsg);
|
|
}
|
|
message.success('接口添加成功');
|
|
let interfaceId = res.data.data._id;
|
|
this.props.history.push('/project/' + this.props.projectId + '/interface/api/' + interfaceId);
|
|
this.getList();
|
|
this.setState({
|
|
visible: false
|
|
});
|
|
if (cb) {
|
|
cb();
|
|
}
|
|
});
|
|
};
|
|
|
|
handleAddInterfaceCat = data => {
|
|
data.project_id = this.props.projectId;
|
|
axios.post('/api/interface/add_cat', data).then(res => {
|
|
if (res.data.errcode !== 0) {
|
|
return message.error(res.data.errmsg);
|
|
}
|
|
message.success('接口分类添加成功');
|
|
this.getList();
|
|
this.props.getProject(data.project_id);
|
|
this.setState({
|
|
add_cat_modal_visible: false
|
|
});
|
|
});
|
|
};
|
|
|
|
handleChangeInterfaceCat = data => {
|
|
data.project_id = this.props.projectId;
|
|
|
|
let params = {
|
|
catid: this.state.curCatdata._id,
|
|
name: data.name,
|
|
desc: data.desc
|
|
};
|
|
|
|
axios.post('/api/interface/up_cat', params).then(res => {
|
|
if (res.data.errcode !== 0) {
|
|
return message.error(res.data.errmsg);
|
|
}
|
|
message.success('接口分类更新成功');
|
|
this.getList();
|
|
this.props.getProject(data.project_id);
|
|
this.setState({
|
|
change_cat_modal_visible: false
|
|
});
|
|
});
|
|
};
|
|
|
|
showConfirm = data => {
|
|
let that = this;
|
|
let id = data._id;
|
|
let catid = data.catid;
|
|
const ref = confirm({
|
|
title: '您确认删除此接口????',
|
|
content: '温馨提示:接口删除后,无法恢复',
|
|
okText: '确认',
|
|
cancelText: '取消',
|
|
async onOk() {
|
|
await that.props.deleteInterfaceData(id, that.props.projectId);
|
|
await that.getList();
|
|
await that.props.fetchInterfaceCatList({ catid });
|
|
ref.destroy();
|
|
that.props.history.push(
|
|
'/project/' + that.props.match.params.id + '/interface/api/cat_' + catid
|
|
);
|
|
},
|
|
onCancel() {
|
|
ref.destroy();
|
|
}
|
|
});
|
|
};
|
|
|
|
showDelCatConfirm = catid => {
|
|
let that = this;
|
|
const ref = confirm({
|
|
title: '确定删除此接口分类吗?',
|
|
content: '温馨提示:该操作会删除该分类下所有接口,接口删除后无法恢复',
|
|
okText: '确认',
|
|
cancelText: '取消',
|
|
async onOk() {
|
|
await that.props.deleteInterfaceCatData(catid, that.props.projectId);
|
|
await that.getList();
|
|
// await that.props.getProject(that.props.projectId)
|
|
await that.props.fetchInterfaceList({ project_id: that.props.projectId });
|
|
that.props.history.push('/project/' + that.props.match.params.id + '/interface/api');
|
|
ref.destroy();
|
|
},
|
|
onCancel() {}
|
|
});
|
|
};
|
|
|
|
copyInterface = async id => {
|
|
let interfaceData = await this.props.fetchInterfaceData(id);
|
|
// let data = JSON.parse(JSON.stringify(interfaceData.payload.data.data));
|
|
// data.title = data.title + '_copy';
|
|
// data.path = data.path + '_' + Date.now();
|
|
let data = interfaceData.payload.data.data;
|
|
let newData = produce(data, draftData => {
|
|
draftData.title = draftData.title + '_copy';
|
|
draftData.path = draftData.path + '_' + Date.now();
|
|
});
|
|
|
|
axios.post('/api/interface/add', newData).then(async res => {
|
|
if (res.data.errcode !== 0) {
|
|
return message.error(res.data.errmsg);
|
|
}
|
|
message.success('接口添加成功');
|
|
let interfaceId = res.data.data._id;
|
|
await this.getList();
|
|
this.props.history.push('/project/' + this.props.projectId + '/interface/api/' + interfaceId);
|
|
this.setState({
|
|
visible: false
|
|
});
|
|
});
|
|
};
|
|
|
|
enterItem = id => {
|
|
this.setState({ delIcon: id });
|
|
};
|
|
|
|
leaveItem = () => {
|
|
this.setState({ delIcon: null });
|
|
};
|
|
|
|
onFilter = e => {
|
|
this.setState({
|
|
filter: e.target.value,
|
|
list: JSON.parse(JSON.stringify(this.props.list))
|
|
});
|
|
};
|
|
|
|
onExpand = e => {
|
|
this.setState({
|
|
expands: e
|
|
});
|
|
};
|
|
|
|
onDrop = async e => {
|
|
const dropCatIndex = e.node.props.pos.split('-')[1] - 1;
|
|
const dragCatIndex = e.dragNode.props.pos.split('-')[1] - 1;
|
|
if (dropCatIndex < 0 || dragCatIndex < 0) {
|
|
return;
|
|
}
|
|
const { list } = this.props;
|
|
const dropCatId = this.props.list[dropCatIndex]._id;
|
|
const id = e.dragNode.props.eventKey;
|
|
const dragCatId = this.props.list[dragCatIndex]._id;
|
|
|
|
const dropPos = e.node.props.pos.split('-');
|
|
const dropIndex = Number(dropPos[dropPos.length - 1]);
|
|
const dragPos = e.dragNode.props.pos.split('-');
|
|
const dragIndex = Number(dragPos[dragPos.length - 1]);
|
|
|
|
if (id.indexOf('cat') === -1) {
|
|
if (dropCatId === dragCatId) {
|
|
// 同一个分类下的接口交换顺序
|
|
let colList = list[dropCatIndex].list;
|
|
let changes = arrayChangeIndex(colList, dragIndex, dropIndex);
|
|
axios.post('/api/interface/up_index', changes).then();
|
|
} else {
|
|
await axios.post('/api/interface/up', { id, catid: dropCatId });
|
|
}
|
|
const { projectId, router } = this.props;
|
|
this.props.fetchInterfaceListMenu(projectId);
|
|
this.props.fetchInterfaceList({ project_id: projectId });
|
|
if (router && isNaN(router.params.actionId)) {
|
|
// 更新分类list下的数据
|
|
let catid = router.params.actionId.substr(4);
|
|
this.props.fetchInterfaceCatList({ catid });
|
|
}
|
|
} else {
|
|
// 分类之间拖动
|
|
let changes = arrayChangeIndex(list, dragIndex - 1, dropIndex - 1);
|
|
axios.post('/api/interface/up_cat_index', changes).then();
|
|
this.props.fetchInterfaceListMenu(this.props.projectId);
|
|
}
|
|
};
|
|
// 数据过滤
|
|
filterList = list => {
|
|
let that = this;
|
|
let arr = [];
|
|
let menuList = produce(list, draftList => {
|
|
draftList.filter(item => {
|
|
let interfaceFilter = false;
|
|
// arr = [];
|
|
if (item.name.indexOf(that.state.filter) === -1) {
|
|
item.list = item.list.filter(inter => {
|
|
if (
|
|
inter.title.indexOf(that.state.filter) === -1 &&
|
|
inter.path.indexOf(that.state.filter) === -1
|
|
) {
|
|
return false;
|
|
}
|
|
//arr.push('cat_' + inter.catid)
|
|
interfaceFilter = true;
|
|
return true;
|
|
});
|
|
arr.push('cat_' + item._id);
|
|
return interfaceFilter === true;
|
|
}
|
|
arr.push('cat_' + item._id);
|
|
return true;
|
|
});
|
|
});
|
|
|
|
return { menuList, arr };
|
|
};
|
|
|
|
render() {
|
|
const matchParams = this.props.match.params;
|
|
// let menuList = this.state.list;
|
|
const searchBox = (
|
|
<div className="interface-filter">
|
|
<Input onChange={this.onFilter} value={this.state.filter} placeholder="搜索接口" />
|
|
<Button
|
|
type="primary"
|
|
onClick={() => this.changeModal('add_cat_modal_visible', true)}
|
|
className="btn-filter"
|
|
>
|
|
添加分类
|
|
</Button>
|
|
{this.state.visible ? (
|
|
<Modal
|
|
title="添加接口"
|
|
visible={this.state.visible}
|
|
onCancel={() => this.changeModal('visible', false)}
|
|
footer={null}
|
|
className="addcatmodal"
|
|
>
|
|
<AddInterfaceForm
|
|
catdata={this.props.curProject.cat}
|
|
catid={this.state.curCatid}
|
|
onCancel={() => this.changeModal('visible', false)}
|
|
onSubmit={this.handleAddInterface}
|
|
/>
|
|
</Modal>
|
|
) : (
|
|
''
|
|
)}
|
|
|
|
{this.state.add_cat_modal_visible ? (
|
|
<Modal
|
|
title="添加分类"
|
|
visible={this.state.add_cat_modal_visible}
|
|
onCancel={() => this.changeModal('add_cat_modal_visible', false)}
|
|
footer={null}
|
|
className="addcatmodal"
|
|
>
|
|
<AddInterfaceCatForm
|
|
onCancel={() => this.changeModal('add_cat_modal_visible', false)}
|
|
onSubmit={this.handleAddInterfaceCat}
|
|
/>
|
|
</Modal>
|
|
) : (
|
|
''
|
|
)}
|
|
|
|
{this.state.change_cat_modal_visible ? (
|
|
<Modal
|
|
title="修改分类"
|
|
visible={this.state.change_cat_modal_visible}
|
|
onCancel={() => this.changeModal('change_cat_modal_visible', false)}
|
|
footer={null}
|
|
className="addcatmodal"
|
|
>
|
|
<AddInterfaceCatForm
|
|
catdata={this.state.curCatdata}
|
|
onCancel={() => this.changeModal('change_cat_modal_visible', false)}
|
|
onSubmit={this.handleChangeInterfaceCat}
|
|
/>
|
|
</Modal>
|
|
) : (
|
|
''
|
|
)}
|
|
</div>
|
|
);
|
|
const defaultExpandedKeys = () => {
|
|
const { router, inter, list } = this.props,
|
|
rNull = { expands: [], selects: [] };
|
|
if (list.length === 0) {
|
|
return rNull;
|
|
}
|
|
if (router) {
|
|
if (!isNaN(router.params.actionId)) {
|
|
if (!inter || !inter._id) {
|
|
return rNull;
|
|
}
|
|
return {
|
|
expands: this.state.expands ? this.state.expands : ['cat_' + inter.catid],
|
|
selects: [inter._id + '']
|
|
};
|
|
} else {
|
|
let catid = router.params.actionId.substr(4);
|
|
return {
|
|
expands: this.state.expands ? this.state.expands : ['cat_' + catid],
|
|
selects: ['cat_' + catid]
|
|
};
|
|
}
|
|
} else {
|
|
return {
|
|
expands: this.state.expands ? this.state.expands : ['cat_' + list[0]._id],
|
|
selects: ['root']
|
|
};
|
|
}
|
|
};
|
|
|
|
const itemInterfaceCreate = item => {
|
|
return (
|
|
<TreeNode
|
|
title={
|
|
<div
|
|
className="container-title"
|
|
onMouseEnter={() => this.enterItem(item._id)}
|
|
onMouseLeave={this.leaveItem}
|
|
>
|
|
<Tooltip title= {item.title}>
|
|
<Link
|
|
className="interface-item"
|
|
onClick={e => e.stopPropagation()}
|
|
to={'/project/' + matchParams.id + '/interface/api/' + item._id}
|
|
>
|
|
{item.title}
|
|
</Link>
|
|
</Tooltip>
|
|
<div className="btns">
|
|
<Tooltip title="删除接口">
|
|
<Icon
|
|
type="delete"
|
|
className="interface-delete-icon"
|
|
onClick={e => {
|
|
e.stopPropagation();
|
|
this.showConfirm(item);
|
|
}}
|
|
style={{ display: this.state.delIcon == item._id ? 'block' : 'none' }}
|
|
/>
|
|
</Tooltip>
|
|
<Tooltip title="复制接口">
|
|
<Icon
|
|
type="copy"
|
|
className="interface-delete-icon"
|
|
onClick={e => {
|
|
e.stopPropagation();
|
|
this.copyInterface(item._id);
|
|
}}
|
|
style={{ display: this.state.delIcon == item._id ? 'block' : 'none' }}
|
|
/>
|
|
</Tooltip>
|
|
</div>
|
|
{/*<Dropdown overlay={menu(item)} trigger={['click']} onClick={e => e.stopPropagation()}>
|
|
<Icon type='ellipsis' className="interface-delete-icon" style={{ opacity: this.state.delIcon == item._id ? 1 : 0 }}/>
|
|
</Dropdown>*/}
|
|
</div>
|
|
}
|
|
key={'' + item._id}
|
|
/>
|
|
);
|
|
};
|
|
|
|
let currentKes = defaultExpandedKeys();
|
|
let menuList;
|
|
if (this.state.filter) {
|
|
let res = this.filterList(this.state.list);
|
|
menuList = res.menuList;
|
|
currentKes.expands = res.arr;
|
|
} else {
|
|
menuList = this.state.list;
|
|
}
|
|
|
|
return (
|
|
<div>
|
|
{searchBox}
|
|
{menuList.length > 0 ? (
|
|
<div
|
|
className="tree-wrappper"
|
|
style={{ maxHeight: parseInt(document.body.clientHeight) - headHeight + 'px' }}
|
|
>
|
|
<Tree
|
|
className="interface-list"
|
|
defaultExpandedKeys={currentKes.expands}
|
|
defaultSelectedKeys={currentKes.selects}
|
|
expandedKeys={currentKes.expands}
|
|
selectedKeys={currentKes.selects}
|
|
onSelect={this.onSelect}
|
|
onExpand={this.onExpand}
|
|
draggable
|
|
onDrop={this.onDrop}
|
|
>
|
|
<TreeNode
|
|
className="item-all-interface"
|
|
title={
|
|
<Link
|
|
onClick={e => {
|
|
e.stopPropagation();
|
|
this.changeExpands();
|
|
}}
|
|
to={'/project/' + matchParams.id + '/interface/api'}
|
|
>
|
|
<Icon type="folder" style={{ marginRight: 5 }} />
|
|
全部接口
|
|
</Link>
|
|
}
|
|
key="root"
|
|
/>
|
|
{menuList.map(item => {
|
|
return (
|
|
<TreeNode
|
|
title={
|
|
<div
|
|
className="container-title"
|
|
onMouseEnter={() => this.enterItem(item._id)}
|
|
onMouseLeave={this.leaveItem}
|
|
>
|
|
<Link
|
|
className="interface-item"
|
|
onClick={e => {
|
|
e.stopPropagation();
|
|
this.changeExpands();
|
|
}}
|
|
to={'/project/' + matchParams.id + '/interface/api/cat_' + item._id}
|
|
>
|
|
<Icon type="folder-open" style={{ marginRight: 5 }} />
|
|
{item.name}
|
|
</Link>
|
|
<div className="btns">
|
|
<Tooltip title="删除分类">
|
|
<Icon
|
|
type="delete"
|
|
className="interface-delete-icon"
|
|
onClick={e => {
|
|
e.stopPropagation();
|
|
this.showDelCatConfirm(item._id);
|
|
}}
|
|
style={{ display: this.state.delIcon == item._id ? 'block' : 'none' }}
|
|
/>
|
|
</Tooltip>
|
|
<Tooltip title="修改分类">
|
|
<Icon
|
|
type="edit"
|
|
className="interface-delete-icon"
|
|
style={{ display: this.state.delIcon == item._id ? 'block' : 'none' }}
|
|
onClick={e => {
|
|
e.stopPropagation();
|
|
this.changeModal('change_cat_modal_visible', true);
|
|
this.setState({
|
|
curCatdata: item
|
|
});
|
|
}}
|
|
/>
|
|
</Tooltip>
|
|
<Tooltip title="添加接口">
|
|
<Icon
|
|
type="plus"
|
|
className="interface-delete-icon"
|
|
style={{ display: this.state.delIcon == item._id ? 'block' : 'none' }}
|
|
onClick={e => {
|
|
e.stopPropagation();
|
|
this.changeModal('visible', true);
|
|
this.setState({
|
|
curCatid: item._id
|
|
});
|
|
}}
|
|
/>
|
|
</Tooltip>
|
|
</div>
|
|
|
|
{/*<Dropdown overlay={menu(item)} trigger={['click']} onClick={e => e.stopPropagation()}>
|
|
<Icon type='ellipsis' className="interface-delete-icon" />
|
|
</Dropdown>*/}
|
|
</div>
|
|
}
|
|
key={'cat_' + item._id}
|
|
className={`interface-item-nav ${item.list.length ? '' : 'cat_switch_hidden'}`}
|
|
>
|
|
{item.list.map(itemInterfaceCreate)}
|
|
</TreeNode>
|
|
);
|
|
})}
|
|
</Tree>
|
|
</div>
|
|
) : null}
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
|
|
export default withRouter(InterfaceMenu);
|