Files
2024-03-01 20:28:14 +08:00

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);