mirror of
https://github.com/YMFE/yapi.git
synced 2024-12-15 05:10:47 +08:00
Merge branch 'dev' of http://gitlab.corp.qunar.com/mfe/yapi into dev
This commit is contained in:
commit
fe00959a29
@ -49,6 +49,8 @@ export default class App extends Component {
|
||||
} else {
|
||||
r = (
|
||||
<Router getUserConfirmation={(msg, callback) => {
|
||||
// 自定义 window.confirm
|
||||
// http://reacttraining.cn/web/api/BrowserRouter/getUserConfirmation-func
|
||||
let container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
ReactDOM.render((
|
||||
|
@ -196,7 +196,7 @@ export default class HeaderCom extends Component {
|
||||
</div>
|
||||
</Link>
|
||||
<Breadcrumb />
|
||||
<div className="user-toolbar">
|
||||
<div className="user-toolbar" style={{ position: 'relative', zIndex: this.props.studyTip === 1 ? 3 : 1}}>
|
||||
{login?
|
||||
<Popover
|
||||
overlayClassName="popover-index"
|
||||
|
@ -552,7 +552,7 @@ export default class Run extends Component {
|
||||
<div>
|
||||
<a
|
||||
target="blank"
|
||||
href="/attachment/cross-request-v2.0.1.zip"
|
||||
href="/api/interface/download_crx"
|
||||
> [手动下载] </a>
|
||||
<span> zip 文件解压后将 crx 文件拖入到 chrome://extensions/ </span>
|
||||
<a
|
||||
|
@ -278,9 +278,9 @@ export default class GroupList extends Component {
|
||||
menu = null;
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<div className="m-group">
|
||||
{!this.props.study ? <div className="study-mask"></div> : null}
|
||||
<div className="group-bar">
|
||||
<div className="curr-group">
|
||||
<div className="curr-group-name">
|
||||
@ -303,14 +303,14 @@ export default class GroupList extends Component {
|
||||
>
|
||||
{this.state.groupList.map((group) => {
|
||||
if(group.type === 'private') {
|
||||
return <Menu.Item key={`${group._id}`} className="group-item">
|
||||
return <Menu.Item key={`${group._id}`} className="group-item" style={{zIndex: this.props.studyTip === 0 ? 3 : 1}}>
|
||||
<Icon type="user" />
|
||||
<Popover
|
||||
overlayClassName="popover-index"
|
||||
content={<GuideBtns/>}
|
||||
title={tip}
|
||||
placement="right"
|
||||
visible={(this.props.studyTip === 0 && !this.props.study) ? true : false}
|
||||
visible={(this.props.studyTip === 0) && !this.props.study}
|
||||
>
|
||||
{group.group_name}
|
||||
</Popover>
|
||||
|
@ -132,7 +132,7 @@ class ProjectList extends Component {
|
||||
placement="right"
|
||||
visible={(this.props.studyTip === 2 && !this.props.study) ? true : false}
|
||||
>
|
||||
<Button type="primary" ><Link to="/add-project">添加项目</Link></Button>
|
||||
<Button type="primary" style={{ position: 'relative', boxShadow: this.props.studyTip === 2 ? '0 0 2px 3px #fff' : '' , zIndex: this.props.studyTip === 2 ? 3 : 1}}><Link to="/add-project">添加项目</Link></Button>
|
||||
</Popover> :
|
||||
<Tooltip title="您没有权限,请联系该分组组长或管理员">
|
||||
<Button type="primary" disabled >添加项目</Button>
|
||||
|
@ -1,7 +1,8 @@
|
||||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
// import { connect } from 'react-redux'
|
||||
// import { Table } from 'antd'
|
||||
import { Table } from 'antd'
|
||||
import variable from '../../../../constants/variable';
|
||||
// import { fetchInterfaceList } from '../../../../reducer/modules/interface.js';
|
||||
|
||||
// @connect(
|
||||
@ -19,22 +20,128 @@ export default class ImportInterface extends Component {
|
||||
super(props)
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
// colId: PropTypes.number,
|
||||
// fetchInterfaceList: PropTypes.func,
|
||||
// projectId: PropTypes.number,
|
||||
list: PropTypes.array
|
||||
state = {
|
||||
selectedRowKeys: [],
|
||||
categoryCount: {}
|
||||
}
|
||||
|
||||
// componentWillMount() {
|
||||
// this.props.fetchInterfaceList(this.props.projectId);
|
||||
// }
|
||||
static propTypes = {
|
||||
list: PropTypes.array,
|
||||
onChange: PropTypes.func
|
||||
}
|
||||
|
||||
render() {
|
||||
console.log(this.props.list)
|
||||
const { list } = this.props;
|
||||
// const { selectedRowKeys } = this.state;
|
||||
const data = list.map(item => {
|
||||
return {
|
||||
key: 'category_' + item._id,
|
||||
title: item.name,
|
||||
isCategory: true,
|
||||
children: item.list ? item.list.map(e => {
|
||||
e.key = e._id
|
||||
e.categoryKey = 'category_' + item._id
|
||||
e.categoryLength = item.list.length
|
||||
return e
|
||||
}) : []
|
||||
}
|
||||
});
|
||||
const self = this;
|
||||
const rowSelection = {
|
||||
// onChange: (selectedRowKeys) => {
|
||||
// console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
|
||||
// if (selectedRows.isCategory) {
|
||||
// const selectedRowKeys = selectedRows.children.map(item => item._id)
|
||||
// this.setState({ selectedRowKeys })
|
||||
// }
|
||||
// this.props.onChange(selectedRowKeys.filter(id => ('' + id).indexOf('category') === -1));
|
||||
// },
|
||||
onSelect: (record, selected) => {
|
||||
// console.log(record, selected, selectedRows);
|
||||
const oldSelecteds = self.state.selectedRowKeys;
|
||||
const categoryCount = self.state.categoryCount;
|
||||
const categoryKey = record.categoryKey;
|
||||
const categoryLength = record.categoryLength;
|
||||
let selectedRowKeys = [];
|
||||
if (record.isCategory) {
|
||||
selectedRowKeys = record.children.map(item => item._id).concat(record.key)
|
||||
if (selected) {
|
||||
selectedRowKeys = selectedRowKeys.filter(id => oldSelecteds.indexOf(id) === -1).concat(oldSelecteds)
|
||||
categoryCount[categoryKey] = categoryLength;
|
||||
} else {
|
||||
selectedRowKeys = oldSelecteds.filter(id => selectedRowKeys.indexOf(id) === -1)
|
||||
categoryCount[categoryKey] = 0;
|
||||
}
|
||||
} else {
|
||||
if (selected) {
|
||||
selectedRowKeys = oldSelecteds.concat(record._id)
|
||||
if (categoryCount[categoryKey]) {
|
||||
categoryCount[categoryKey] += 1;
|
||||
} else {
|
||||
categoryCount[categoryKey] = 1;
|
||||
}
|
||||
if (categoryCount[categoryKey] === record.categoryLength) {
|
||||
selectedRowKeys.push(categoryKey)
|
||||
}
|
||||
} else {
|
||||
selectedRowKeys = oldSelecteds.filter(id => id !== record._id)
|
||||
if (categoryCount[categoryKey]) {
|
||||
categoryCount[categoryKey] -= 1;
|
||||
}
|
||||
selectedRowKeys = selectedRowKeys.filter(id => id !== categoryKey)
|
||||
}
|
||||
}
|
||||
self.setState({ selectedRowKeys, categoryCount })
|
||||
self.props.onChange(selectedRowKeys.filter(id => ('' + id).indexOf('category') === -1));
|
||||
},
|
||||
onSelectAll: (selected) => {
|
||||
// console.log(selected, selectedRows, changeRows);
|
||||
let selectedRowKeys = [];
|
||||
let categoryCount = self.state.categoryCount;
|
||||
if (selected) {
|
||||
data.forEach(item => {
|
||||
if(item.children) {
|
||||
categoryCount['category_' + item._id] = item.children.length;
|
||||
selectedRowKeys = selectedRowKeys.concat(item.children.map(item => item._id))
|
||||
}
|
||||
});
|
||||
selectedRowKeys = selectedRowKeys.concat(data.map(item => item.key))
|
||||
} else {
|
||||
categoryCount = {};
|
||||
selectedRowKeys = [];
|
||||
}
|
||||
self.setState({ selectedRowKeys, categoryCount })
|
||||
self.props.onChange(selectedRowKeys.filter(id => ('' + id).indexOf('category') === -1));
|
||||
},
|
||||
selectedRowKeys: self.state.selectedRowKeys
|
||||
};
|
||||
|
||||
const columns = [{
|
||||
title: '接口名称',
|
||||
dataIndex: 'title',
|
||||
width: '30%'
|
||||
}, {
|
||||
title: '接口路径',
|
||||
dataIndex: 'path',
|
||||
width: '40%'
|
||||
}, {
|
||||
title: '请求方法',
|
||||
dataIndex: 'method',
|
||||
render: (item) => {
|
||||
let methodColor = variable.METHOD_COLOR[item ? item.toLowerCase() : 'get'];
|
||||
return <span style={{color: methodColor.color, backgroundColor: methodColor.bac, borderRadius: 4}} className="colValue">{item}</span>
|
||||
}
|
||||
}, {
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
render: (text) => {
|
||||
return text && (text === 'done' ? <span className="tag-status done">已完成</span> : <span className="tag-status undone">未完成</span>)
|
||||
}
|
||||
}];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>{this.props.list}</div>
|
||||
<Table columns={columns} rowSelection={rowSelection} dataSource={data} pagination={false} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -297,12 +297,11 @@ class InterfaceColContent extends Component {
|
||||
if (!interfaceColList.find(item => +item._id === +newColId)) {
|
||||
this.props.history.push('/project/' + id + '/interface/col/' + interfaceColList[0]._id)
|
||||
} else if (oldColId !== newColId) {
|
||||
if (newColId && newColId != 0) {
|
||||
if ((newColId && newColId != 0) || interfaceColList !== this.props.interfaceColList) {
|
||||
await this.props.fetchCaseList(newColId);
|
||||
this.props.setColData({ currColId: +newColId, isShowCol: true })
|
||||
this.handleColdata(this.props.currCaseList)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,9 +2,8 @@ import React, { Component } from 'react'
|
||||
import { connect } from 'react-redux';
|
||||
import { withRouter } from 'react-router'
|
||||
import PropTypes from 'prop-types'
|
||||
import { fetchInterfaceColList, fetchInterfaceCaseList, setColData } from '../../../../reducer/modules/interfaceCol'
|
||||
import { fetchInterfaceColList, fetchInterfaceCaseList, setColData, fetchCaseList } 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'
|
||||
@ -56,6 +55,7 @@ const ColModalForm = Form.create()((props) => {
|
||||
fetchInterfaceColList,
|
||||
fetchInterfaceCaseList,
|
||||
fetchInterfaceList,
|
||||
fetchCaseList,
|
||||
setColData
|
||||
}
|
||||
)
|
||||
@ -68,6 +68,7 @@ export default class InterfaceColMenu extends Component {
|
||||
fetchInterfaceColList: PropTypes.func,
|
||||
fetchInterfaceCaseList: PropTypes.func,
|
||||
fetchInterfaceList: PropTypes.func,
|
||||
fetchCaseList: PropTypes.func,
|
||||
setColData: PropTypes.func,
|
||||
history: PropTypes.object,
|
||||
currColId: PropTypes.number,
|
||||
@ -82,8 +83,9 @@ export default class InterfaceColMenu extends Component {
|
||||
colModalVisible: false,
|
||||
editColId: 0,
|
||||
filterValue: '',
|
||||
// importInterVisible: false,
|
||||
importInterIds: []
|
||||
importInterVisible: false,
|
||||
importInterIds: [],
|
||||
importColId: 0
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
@ -106,8 +108,7 @@ export default class InterfaceColMenu extends Component {
|
||||
this.setState({expandedKeys})
|
||||
}
|
||||
|
||||
@autobind
|
||||
async addorEditCol() {
|
||||
addorEditCol = async () => {
|
||||
const { colName: name, colDesc: desc } = this.form.getFieldsValue();
|
||||
const { colModalType, editColId: col_id } = this.state;
|
||||
const project_id = this.props.match.params.id;
|
||||
@ -198,22 +199,36 @@ export default class InterfaceColMenu extends Component {
|
||||
}
|
||||
|
||||
selectInterface = (importInterIds) => {
|
||||
// console.log(importInterIds)
|
||||
this.setState({ importInterIds })
|
||||
}
|
||||
|
||||
showImportInterface = async (colId) => {
|
||||
showImportInterfaceModal = 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');
|
||||
}
|
||||
});
|
||||
await this.props.fetchInterfaceList(projectId)
|
||||
this.setState({ importInterVisible: true, importColId: colId })
|
||||
}
|
||||
handleImportOk = async () => {
|
||||
const project_id = this.props.match.params.id;
|
||||
const { importColId, importInterIds } = this.state;
|
||||
const res = await axios.post('/api/col/add_case_list', {
|
||||
interface_list: importInterIds,
|
||||
col_id: importColId,
|
||||
project_id
|
||||
})
|
||||
if (!res.data.errcode) {
|
||||
this.setState({ importInterVisible: false })
|
||||
message.success('导入集合成功');
|
||||
await this.props.fetchInterfaceColList(project_id);
|
||||
// if (this.props.isShowCol) {
|
||||
// await this.props.fetchCaseList(this.props.currColId);
|
||||
// }
|
||||
} else {
|
||||
message.error(res.data.errmsg);
|
||||
}
|
||||
}
|
||||
handleImportCancel = () => {
|
||||
this.setState({ importInterVisible: false })
|
||||
}
|
||||
|
||||
filterCol = (e) => {
|
||||
@ -223,7 +238,7 @@ export default class InterfaceColMenu extends Component {
|
||||
|
||||
render() {
|
||||
const { currColId, currCaseId, isShowCol } = this.props;
|
||||
const { colModalType, colModalVisible, filterValue } = this.state;
|
||||
const { colModalType, colModalVisible, filterValue, importInterVisible } = this.state;
|
||||
|
||||
// const menu = (col) => {
|
||||
// return (
|
||||
@ -282,7 +297,7 @@ export default class InterfaceColMenu extends Component {
|
||||
<div className="btns">
|
||||
<Icon type='delete' className="interface-delete-icon" onClick={() => {this.showDelColConfirm(col._id)}} />
|
||||
<Icon type='edit' className="interface-delete-icon" onClick={() => {this.showColModal('edit', col)}} />
|
||||
<Icon type='plus' className="interface-delete-icon" onClick={() => this.showImportInterface(col._id)} />
|
||||
<Icon type='plus' className="interface-delete-icon" onClick={() => this.showImportInterfaceModal(col._id)} />
|
||||
</div>
|
||||
{/*<Dropdown overlay={menu(col)} trigger={['click']} onClick={e => e.stopPropagation()}>
|
||||
<Icon className="opts-icon" type='ellipsis'/>
|
||||
@ -320,6 +335,15 @@ export default class InterfaceColMenu extends Component {
|
||||
onCancel={() => { this.setState({ colModalVisible: false }) }}
|
||||
onCreate={this.addorEditCol}
|
||||
></ColModalForm>
|
||||
<Modal
|
||||
title="导入接口到集合"
|
||||
visible={importInterVisible}
|
||||
onOk={this.handleImportOk}
|
||||
onCancel={this.handleImportCancel}
|
||||
width={800}
|
||||
>
|
||||
<ImportInterface onChange={this.selectInterface} list={this.props.list} />
|
||||
</Modal>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -31,7 +31,8 @@ const initialState = {
|
||||
// name: '当前页面'
|
||||
// }]
|
||||
breadcrumb: [],
|
||||
studyTip: 0
|
||||
studyTip: 0,
|
||||
study: false
|
||||
};
|
||||
|
||||
export default (state = initialState, action) => {
|
||||
@ -88,7 +89,8 @@ export default (state = initialState, action) => {
|
||||
loginState: MEMBER_STATUS,
|
||||
uid: action.payload.data.data.uid,
|
||||
userName: action.payload.data.data.username,
|
||||
type: action.payload.data.data.type
|
||||
type: action.payload.data.data.type,
|
||||
study: action.payload.data.data ? action.payload.data.data.study : false
|
||||
};
|
||||
}
|
||||
case SET_BREADCRUMB: {
|
||||
@ -106,7 +108,8 @@ export default (state = initialState, action) => {
|
||||
case FINISH_STUDY: {
|
||||
return {
|
||||
...state,
|
||||
study: true
|
||||
study: true,
|
||||
studyTip: 0
|
||||
};
|
||||
}
|
||||
default:
|
||||
|
@ -190,3 +190,13 @@ em {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.study-mask {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0,0,0,.35);
|
||||
z-index: 2;
|
||||
}
|
||||
|
@ -255,6 +255,14 @@ class interfaceController extends baseController {
|
||||
}
|
||||
}
|
||||
|
||||
async downloadCrx(ctx){
|
||||
let filename = 'crossRequest.zip';
|
||||
let dataBuffer = yapi.fs.readFileSync(yapi.path.join(yapi.WEBROOT, 'static/attachment/cross-request-v2.0.1.zip'));
|
||||
ctx.set('Content-disposition', 'attachment; filename=' + filename);
|
||||
ctx.set('Content-Type', 'application/zip');
|
||||
ctx.body = dataBuffer;
|
||||
}
|
||||
|
||||
async listByCat(ctx) {
|
||||
let catid = ctx.request.query.catid;
|
||||
if (!catid) {
|
||||
|
@ -236,6 +236,66 @@ class interfaceColController extends baseController{
|
||||
}
|
||||
}
|
||||
|
||||
async addCaseList(ctx){
|
||||
try{
|
||||
let params = ctx.request.body;
|
||||
params = yapi.commons.handleParams(params, {
|
||||
project_id: 'number',
|
||||
col_id: 'number'
|
||||
});
|
||||
if(!params.interface_list || !Array.isArray(params.interface_list)){
|
||||
return ctx.body = yapi.commons.resReturn(null, 400, 'interface_list 参数有误');
|
||||
}
|
||||
|
||||
if (!params.project_id) {
|
||||
return ctx.body = yapi.commons.resReturn(null, 400, '项目id不能为空');
|
||||
}
|
||||
|
||||
let auth = await this.checkAuth(params.project_id, 'project', 'edit');
|
||||
if (!auth) {
|
||||
return ctx.body = yapi.commons.resReturn(null, 400, '没有权限');
|
||||
}
|
||||
|
||||
|
||||
if (!params.col_id) {
|
||||
return ctx.body = yapi.commons.resReturn(null, 400, '接口集id不能为空');
|
||||
}
|
||||
|
||||
let data = {
|
||||
uid: this.getUid(),
|
||||
index: 0,
|
||||
add_time: yapi.commons.time(),
|
||||
up_time: yapi.commons.time(),
|
||||
project_id: params.project_id,
|
||||
col_id: params.col_id
|
||||
}
|
||||
|
||||
for(let i=0; i<params.interface_list.length; i++){
|
||||
let interfaceData = await this.interfaceModel.getBaseinfo(params.interface_list[i]);
|
||||
data.interface_id = params.interface_list[i];
|
||||
data.casename = interfaceData.title;
|
||||
await this.caseModel.save(data);
|
||||
let username = this.getUsername();
|
||||
this.colModel.get(params.col_id).then((col)=>{
|
||||
yapi.commons.saveLog({
|
||||
content: `用户 "${username}" 在接口集 "${col.name}" 下导入了接口 ${data.casename}`,
|
||||
type: 'project',
|
||||
uid: this.getUid(),
|
||||
username: username,
|
||||
typeid: params.project_id
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
this.projectModel.up(params.project_id,{up_time: new Date().getTime()}).then();
|
||||
|
||||
ctx.body = yapi.commons.resReturn('ok');
|
||||
|
||||
}catch(e){
|
||||
ctx.body = yapi.commons.resReturn(null, 402, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新一个接口用例
|
||||
* @interface /col/up_case
|
||||
|
@ -306,8 +306,7 @@ class userController extends baseController {
|
||||
role: 'member',
|
||||
add_time: yapi.commons.time(),
|
||||
up_time: yapi.commons.time(),
|
||||
type: "site",
|
||||
study: false
|
||||
type: "site"
|
||||
};
|
||||
|
||||
if (!data.username) {
|
||||
@ -326,7 +325,8 @@ class userController extends baseController {
|
||||
add_time: user.add_time,
|
||||
up_time: user.up_time,
|
||||
role: 'member',
|
||||
type: user.type
|
||||
type: user.type,
|
||||
study: false
|
||||
});
|
||||
yapi.commons.sendMail({
|
||||
to: user.email,
|
||||
|
@ -244,6 +244,11 @@ let routerConfig = {
|
||||
"path": "add",
|
||||
"method": "post"
|
||||
},
|
||||
{
|
||||
"action": "downloadCrx",
|
||||
"path" : "download_crx",
|
||||
"method": "get"
|
||||
},
|
||||
{
|
||||
"action": "getCatMenu",
|
||||
"path": "getCatMenu",
|
||||
@ -320,6 +325,11 @@ let routerConfig = {
|
||||
action: "addCol",
|
||||
path: "add_col",
|
||||
method: "post"
|
||||
},{
|
||||
action: 'addCaseList',
|
||||
path: 'add_case_list',
|
||||
method: 'post'
|
||||
|
||||
}, {
|
||||
action: "list",
|
||||
path: "list",
|
||||
|
Loading…
Reference in New Issue
Block a user