This commit is contained in:
喻希里 2017-09-29 15:42:57 +08:00
commit fe00959a29
14 changed files with 268 additions and 45 deletions

View File

@ -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((

View File

@ -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"

View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -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>
)
}

View File

@ -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)
}
}
}

View File

@ -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>
)
}

View File

@ -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:

View File

@ -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;
}

View File

@ -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) {

View File

@ -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

View File

@ -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,

View File

@ -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",