diff --git a/client/containers/Project/Interface/InterfaceList/Run.js b/client/containers/Project/Interface/InterfaceList/Run.js index 3aa0a3ae..b71db814 100644 --- a/client/containers/Project/Interface/InterfaceList/Run.js +++ b/client/containers/Project/Interface/InterfaceList/Run.js @@ -1,5 +1,511 @@ -import React from 'react' +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import { connect } from 'react-redux' +import { Button, Input, Select, Card, Alert, Spin, Icon, message } from 'antd' +import { autobind } from 'core-decorators'; +import crossRequest from 'cross-request'; +import { withRouter } from 'react-router'; +import axios from 'axios'; +import URL from 'url'; -export default () => { - return

接口Run

-} \ No newline at end of file +// import { +// } from '../../../reducer/modules/group.js' + +import './Run.scss' + +const { TextArea } = Input; +const InputGroup = Input.Group; +const Option = Select.Option; + +@connect( + state => ({ + reqParams: state.addInterface.reqParams, + method: state.addInterface.method, + url: state.addInterface.url, + seqGroup: state.addInterface.seqGroup, + interfaceName: state.addInterface.interfaceName, + interfaceProject: state.addInterface.project + }) +) +@withRouter +export default class InterfaceTest extends Component { + + static propTypes = { + reqParams: PropTypes.string, + method: PropTypes.string, + url: PropTypes.string, + interfaceName: PropTypes.string, + seqGroup: PropTypes.array, + match: PropTypes.object, + interfaceProject: PropTypes.object + } + + state = { + res: '', + method: 'GET', + domains: [], + pathname: '', + query: {}, + params: {}, + paramsNotJson: false, + headers: {}, + currDomain: '', + paramsType: 'from' + } + + constructor(props) { + super(props) + } + + componentWillMount() { + this.getInterfaceState() + } + + componentWillReceiveProps(nextProps) { + this.getInterfaceState(nextProps) + } + + @autobind + getInterfaceState(nextProps) { + const props = nextProps || this.props; + const { method, url, seqGroup, interfaceProject } = props; + const { prd_host, basepath, protocol, env } = interfaceProject; + const pathname = (basepath + url).replace(/\/+/g, '/'); + + const domains = {prd: protocol + '://' + prd_host}; + env.forEach(item => { + domains[item.name] = item.domain; + }) + + const query = []; + let params = []; + let reqParams = this.props.reqParams ? this.props.reqParams : '{}'; + let paramsNotJson = false; + try { + reqParams = JSON.parse(reqParams); + // paramsNotJson = false; + } catch (e) { + // paramsNotJson = true; + reqParams = {}; + message.error('请求参数不是 JSON 格式'); + } + if (method === 'GET') { + Object.keys(reqParams).forEach(key => { + const value = typeof reqParams[key] === 'object' ? JSON.stringify(reqParams[key]) : reqParams[key].toString(); + query.push({key, value}) + }) + } else if (method === 'POST') { + // params = reqParams; + Object.keys(reqParams).forEach(key => { + const value = typeof reqParams[key] === 'object' ? JSON.stringify(reqParams[key]) : reqParams[key].toString(); + query.push({key, value, type: 'text'}) + }) + } + + const headers = [] + let contentTypeIndex = -1; + seqGroup.forEach((headerItem, index) => { + if (headerItem.name) { + // TODO 'Content-Type' 排除大小写不同格式影响 + if (headerItem.name === 'Content-Type'){ + contentTypeIndex = index; + headerItem.value = headerItem.value || 'application/x-www-form-urlencoded'; + } + headers.push({name: headerItem.name, value: headerItem.value}); + } + }) + if (contentTypeIndex === -1) { + headers.push({name: 'Content-Type', value: 'application/x-www-form-urlencoded'}); + } + + this.setState({ + method, + domains, + pathname, + query, + params, + paramsNotJson, + headers, + currDomain: domains.prd, + loading: false, + paramsType: 'form' + }); + } + + @autobind + requestInterface() { + const { headers, params, currDomain, method, pathname, query } = this.state; + const urlObj = URL.parse(currDomain); + + const href = URL.format({ + protocol: urlObj.protocol || 'http', + host: urlObj.host, + pathname, + query: this.getQueryObj(query) + }); + + this.setState({ loading: true }) + + crossRequest({ + url: href, + method, + headers: this.getHeadersObj(headers), + data: this.arrToObj(params), + files: this.getFiles(params), + success: (res) => { + try { + res = JSON.parse(res) + } catch (e) { + null + } + this.setState({res}) + this.setState({ loading: false }) + }, + error: (err) => { + this.setState({res: err || '请求失败'}) + this.setState({ loading: false }) + } + }) + } + + @autobind + changeDomain(value) { + this.setState({ currDomain: value }); + } + + @autobind + selectDomain(value) { + this.setState({ currDomain: value }); + } + + @autobind + changeHeader(e, index, isName) { + const headers = JSON.parse(JSON.stringify(this.state.headers)); + const v = e.target.value; + if (isName) { + headers[index].name = v; + } else { + headers[index].value = v; + } + this.setState({ headers }); + } + @autobind + addHeader() { + const { headers } = this.state; + this.setState({headers: headers.concat([{name: '', value: ''}])}) + } + @autobind + deleteHeader(index) { + const { headers } = this.state; + this.setState({headers: headers.filter((item, i) => +index !== +i)}); + } + @autobind + setContentType(type) { + const headersObj = this.getHeadersObj(this.state.headers); + headersObj['Content-Type'] = type; + this.setState({headers: this.objToArr(headersObj, 'name')}) + } + + @autobind + changeQuery(e, index, isKey) { + const query = JSON.parse(JSON.stringify(this.state.query)); + const v = e.target.value; + if (isKey) { + query[index].key = v; + } else { + query[index].value = v; + } + this.setState({ query }); + } + @autobind + addQuery() { + const { query } = this.state; + this.setState({query: query.concat([{key: '', value: ''}])}) + } + @autobind + deleteQuery(index) { + const { query } = this.state; + this.setState({query: query.filter((item, i) => +index !== +i)}); + } + + @autobind + changeParams(e, index, type) { + const params = JSON.parse(JSON.stringify(this.state.params)); + switch (type) { + case 'key': + params[index].key = e.target.value + break; + case 'type': + params[index].type = e + break; + case 'value': + if (params[index].type === 'file') { + params[index].value = e.target.id + } else { + params[index].value = e.target.value + } + break; + default: + break; + } + if (type === 'type' && e === 'file') { + this.setContentType('multipart/form-data') + } + this.setState({ params }); + } + @autobind + addParams() { + const { params } = this.state; + this.setState({params: params.concat([{key: '', value: '', type: 'text'}])}) + } + @autobind + deleteParams(index) { + const { params } = this.state; + this.setState({params: params.filter((item, i) => +index !== +i)}); + } + + @autobind + changeMethod(value) { + this.setState({ method: value }); + } + + @autobind + changePath(e) { + const path = e.target.value; + const urlObj = URL.parse(path, true); + this.setState({ + query: this.objToArr(urlObj.query), + pathname: urlObj.pathname + }) + } + + @autobind + changeParamsType(value) { + this.setState({paramsType: value}) + } + + hasCrossRequestPlugin() { + const dom = document.getElementById('y-request'); + return dom.getAttribute('key') === 'yapi'; + } + + objToArr(obj, key, value) { + const keyName = key || 'key'; + const valueName = value || 'value'; + const arr = [] + Object.keys(obj).forEach((_key) => { + if (_key) { + arr.push({[keyName]: _key, [valueName]: obj[_key]}); + } + }) + return arr; + } + arrToObj(arr) { + const obj = {}; + arr.forEach(item => { + if (item.key && item.type !== 'file') { + obj[item.key] = item.value || ''; + } + }) + return obj; + } + getFiles(params) { + const files = {}; + params.forEach(item => { + if (item.key && item.type === 'file') { + files[item.key] = item.value + } + }) + return files; + } + getQueryObj(query) { + const queryObj = {}; + query.forEach(item => { + if (item.key) { + queryObj[item.key] = item.value || ''; + } + }) + return queryObj; + } + getHeadersObj(headers) { + const headersObj = {}; + headers.forEach(item => { + if (item.name && item.value) { + headersObj[item.name] = item.value; + } + }) + return headersObj; + } + + @autobind + fileChange(e, index) { + console.log(e) + console.log(index) + } + + render () { + + const { interfaceName } = this.props; + const { method, domains, pathname, query, headers, params, currDomain, paramsType } = this.state; + const hasPlugin = this.hasCrossRequestPlugin(); + const search = decodeURIComponent(URL.format({query: this.getQueryObj(query)})); + + console.log(axios) + window.axios = axios + + + return ( +
+
+ { hasPlugin ? '' : + + 温馨提示:当前正在使用接口测试服务,请安装我们为您免费提供的  + + 测试增强插件 [点击获取]! + +
+ } + type="warning" + /> + } +
+
{interfaceName}
+ + {/* url */} +
+
+ + + + + + + + + + + + (请求测试真实接口) +
+ + + { + query.map((item, index) => { + return ( +
+ this.changeQuery(e, index, true)} style={{display: 'inline-block', width: 200, margin: 10}} />{' = '} + this.changeQuery(e, index)} style={{display: 'inline-block', width: 200, margin: 10}} /> + this.deleteQuery(index)} /> +
+ ) + }) + } + +
+ +
+ { + headers.map((item, index) => { + return ( +
+ this.changeHeader(e, index, true)} style={{display: 'inline-block', width: 200, margin: 10}} />{' = '} + this.changeHeader(e, index)} style={{display: 'inline-block', width: 200, margin: 10}} /> + this.deleteHeader(index)} /> +
+ ) + }) + } + +
+
+ +
+
+ +
+ { method === 'POST' && paramsType !== 'form' && paramsType !== 'file' && +
+ +
{paramsType}
+
+ } + { + method === 'POST' && paramsType === 'form' && ( +
+ { + params.map((item, index) => { + return ( +
+ this.changeParams(e, index, 'key')} style={{display: 'inline-block', width: 200, margin: 10}} /> + []{' = '} + {item.type === 'file' ? + this.changeParams(e, index, 'value')} multiple style={{display: 'inline-block', width: 200, margin: 10}} /> : + this.changeParams(e, index, 'value')} style={{display: 'inline-block', width: 200, margin: 10}} /> + } +
+ ) + }) + } + +
+ ) + } + { + method === 'POST' && paramsType === 'file' && ( +
+ +
+ ) + } + { + method !== 'POST' && ( +
GET 请求没有 Body。
+ ) + } +
+
+
+ + +
+
+ +
+
+
+
+ + ) + } +} diff --git a/client/containers/Project/Interface/InterfaceList/Run.scss b/client/containers/Project/Interface/InterfaceList/Run.scss new file mode 100644 index 00000000..a5f2179f --- /dev/null +++ b/client/containers/Project/Interface/InterfaceList/Run.scss @@ -0,0 +1,18 @@ +.interface-test { + .interface-name { + font-size: 24px; + margin-bottom: 24px; + } + .icon-btn { + cursor: pointer; + margin-left: 6px; + } + .icon-btn:hover { + color: #108ee9; + } +} +.floatfix:after{ + content:""; + display:table; + clear:both; +}