mock平台

CaseDesModal.js 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. import React, { Component } from 'react';
  2. import PropTypes from 'prop-types';
  3. import {
  4. Form,
  5. Select,
  6. InputNumber,
  7. Switch,
  8. Col,
  9. message,
  10. Row,
  11. Input,
  12. Button,
  13. Icon,
  14. AutoComplete,
  15. Modal
  16. } from 'antd';
  17. const Option = Select.Option;
  18. const FormItem = Form.Item;
  19. import { safeAssign } from 'client/common.js';
  20. import AceEditor from 'client/components/AceEditor/AceEditor';
  21. import constants from 'client/constants/variable.js';
  22. import { httpCodes } from '../index.js';
  23. import './CaseDesModal.scss';
  24. import { connect } from 'react-redux';
  25. import json5 from 'json5';
  26. const formItemLayout = {
  27. labelCol: { span: 5 },
  28. wrapperCol: { span: 12 }
  29. };
  30. const formItemLayoutWithOutLabel = {
  31. wrapperCol: { span: 12, offset: 5 }
  32. };
  33. @connect(state => {
  34. return {
  35. currInterface: state.inter.curdata
  36. };
  37. })
  38. class CaseDesForm extends Component {
  39. static propTypes = {
  40. form: PropTypes.object,
  41. caseData: PropTypes.object,
  42. currInterface: PropTypes.object,
  43. onOk: PropTypes.func,
  44. onCancel: PropTypes.func,
  45. isAdd: PropTypes.bool,
  46. visible: PropTypes.bool
  47. };
  48. // 初始化输入数据
  49. preProcess = caseData => {
  50. try {
  51. caseData = JSON.parse(JSON.stringify(caseData));
  52. } catch (error) {
  53. console.log(error);
  54. }
  55. const initCaseData = {
  56. ip: '',
  57. ip_enable: false,
  58. name: '',
  59. code: '200',
  60. delay: 0,
  61. headers: [{ name: '', value: '' }],
  62. paramsArr: [{ name: '', value: '' }],
  63. params: {},
  64. res_body: '',
  65. paramsForm: 'form'
  66. };
  67. caseData.params = caseData.params || {};
  68. const paramsArr = Object.keys(caseData.params).length
  69. ? Object.keys(caseData.params)
  70. .map(key => {
  71. return { name: key, value: caseData.params[key] };
  72. })
  73. .filter(item => {
  74. if (typeof item.value === 'object') {
  75. // this.setState({ paramsForm: 'json' })
  76. caseData.paramsForm = 'json';
  77. }
  78. return typeof item.value !== 'object';
  79. })
  80. : [{ name: '', value: '' }];
  81. const headers =
  82. caseData.headers && caseData.headers.length ? caseData.headers : [{ name: '', value: '' }];
  83. caseData.code = '' + caseData.code;
  84. caseData.params = JSON.stringify(caseData.params, null, 2);
  85. caseData = safeAssign(initCaseData, { ...caseData, headers, paramsArr });
  86. return caseData;
  87. };
  88. constructor(props) {
  89. super(props);
  90. const { caseData } = this.props;
  91. this.state = this.preProcess(caseData);
  92. }
  93. // 处理request_body编译器
  94. handleRequestBody = d => {
  95. this.setState({ res_body: d.text });
  96. };
  97. // 处理参数编译器
  98. handleParams = d => {
  99. this.setState({ params: d.text });
  100. };
  101. // 增加参数信息
  102. addValues = key => {
  103. const { getFieldValue } = this.props.form;
  104. let values = getFieldValue(key);
  105. values = values.concat({ name: '', value: '' });
  106. this.setState({ [key]: values });
  107. };
  108. // 删除参数信息
  109. removeValues = (key, index) => {
  110. const { setFieldsValue, getFieldValue } = this.props.form;
  111. let values = getFieldValue(key);
  112. values = values.filter((val, index2) => index !== index2);
  113. setFieldsValue({ [key]: values });
  114. this.setState({ [key]: values });
  115. };
  116. // 处理参数
  117. getParamsKey = () => {
  118. let {
  119. req_query,
  120. req_body_form,
  121. req_body_type,
  122. method,
  123. req_body_other,
  124. req_body_is_json_schema,
  125. req_params
  126. } = this.props.currInterface;
  127. let keys = [];
  128. req_query &&
  129. Array.isArray(req_query) &&
  130. req_query.forEach(item => {
  131. keys.push(item.name);
  132. });
  133. req_params &&
  134. Array.isArray(req_params) &&
  135. req_params.forEach(item => {
  136. keys.push(item.name);
  137. });
  138. if (constants.HTTP_METHOD[method.toUpperCase()].request_body && req_body_type === 'form') {
  139. req_body_form &&
  140. Array.isArray(req_body_form) &&
  141. req_body_form.forEach(item => {
  142. keys.push(item.name);
  143. });
  144. } else if (
  145. constants.HTTP_METHOD[method.toUpperCase()].request_body &&
  146. req_body_type === 'json' &&
  147. req_body_other
  148. ) {
  149. let bodyObj;
  150. try {
  151. // 针对json-schema的处理
  152. if (req_body_is_json_schema) {
  153. bodyObj = json5.parse(this.props.caseData.req_body_other);
  154. } else {
  155. bodyObj = json5.parse(req_body_other);
  156. }
  157. keys = keys.concat(Object.keys(bodyObj));
  158. } catch (error) {
  159. console.log(error);
  160. }
  161. }
  162. return keys;
  163. };
  164. endProcess = caseData => {
  165. const headers = [];
  166. const params = {};
  167. const { paramsForm } = this.state;
  168. caseData.headers &&
  169. Array.isArray(caseData.headers) &&
  170. caseData.headers.forEach(item => {
  171. if (item.name) {
  172. headers.push({
  173. name: item.name,
  174. value: item.value
  175. });
  176. }
  177. });
  178. caseData.paramsArr &&
  179. Array.isArray(caseData.paramsArr) &&
  180. caseData.paramsArr.forEach(item => {
  181. if (item.name) {
  182. params[item.name] = item.value;
  183. }
  184. });
  185. caseData.headers = headers;
  186. if (paramsForm === 'form') {
  187. caseData.params = params;
  188. } else {
  189. try {
  190. caseData.params = json5.parse(caseData.params);
  191. } catch (error) {
  192. console.log(error);
  193. message.error('请求参数 json 格式有误,请修改');
  194. return false;
  195. }
  196. }
  197. delete caseData.paramsArr;
  198. return caseData;
  199. };
  200. handleOk = () => {
  201. const form = this.props.form;
  202. form.validateFieldsAndScroll((err, values) => {
  203. if (!err) {
  204. values.res_body = this.state.res_body;
  205. values.params = this.state.params;
  206. this.props.onOk(this.endProcess(values));
  207. }
  208. });
  209. };
  210. render() {
  211. const { getFieldDecorator, getFieldValue } = this.props.form;
  212. const { isAdd, visible, onCancel } = this.props;
  213. const {
  214. name,
  215. code,
  216. headers,
  217. ip,
  218. ip_enable,
  219. params,
  220. paramsArr,
  221. paramsForm,
  222. res_body,
  223. delay
  224. } = this.state;
  225. this.props.form.initialValue;
  226. const valuesTpl = (values, title) => {
  227. const dataSource = this.getParamsKey();
  228. const display = paramsForm === 'json' ? 'none' : '';
  229. return values.map((item, index) => (
  230. <div key={index} className="paramsArr" style={{ display }}>
  231. <FormItem
  232. {...(index === 0 ? formItemLayout : formItemLayoutWithOutLabel)}
  233. wrapperCol={index === 0 ? { span: 19 } : { span: 19, offset: 5 }}
  234. label={index ? '' : title}
  235. >
  236. <Row gutter={8}>
  237. <Col span={10}>
  238. <FormItem>
  239. {getFieldDecorator(`paramsArr[${index}].name`, { initialValue: item.name })(
  240. <AutoComplete
  241. dataSource={dataSource}
  242. placeholder="参数名称"
  243. filterOption={(inputValue, option) =>
  244. option.props.children.toUpperCase().indexOf(inputValue.toUpperCase()) !== -1
  245. }
  246. />
  247. )}
  248. </FormItem>
  249. </Col>
  250. <Col span={10}>
  251. <FormItem>
  252. {getFieldDecorator(`paramsArr[${index}].value`, { initialValue: item.value })(
  253. <Input placeholder="参数值" />
  254. )}
  255. </FormItem>
  256. </Col>
  257. <Col span={4}>
  258. {values.length > 1 ? (
  259. <Icon
  260. className="dynamic-delete-button"
  261. type="minus-circle-o"
  262. onClick={() => this.removeValues('paramsArr', index)}
  263. />
  264. ) : null}
  265. </Col>
  266. </Row>
  267. </FormItem>
  268. </div>
  269. ));
  270. };
  271. const headersTpl = (values, title) => {
  272. const dataSource = constants.HTTP_REQUEST_HEADER;
  273. return values.map((item, index) => (
  274. <div key={index} className="headers">
  275. <FormItem
  276. {...(index === 0 ? formItemLayout : formItemLayoutWithOutLabel)}
  277. wrapperCol={index === 0 ? { span: 19 } : { span: 19, offset: 5 }}
  278. label={index ? '' : title}
  279. >
  280. <Row gutter={8}>
  281. <Col span={10}>
  282. <FormItem>
  283. {getFieldDecorator(`headers[${index}].name`, { initialValue: item.name })(
  284. <AutoComplete
  285. dataSource={dataSource}
  286. placeholder="参数名称"
  287. filterOption={(inputValue, option) =>
  288. option.props.children.toUpperCase().indexOf(inputValue.toUpperCase()) !== -1
  289. }
  290. />
  291. )}
  292. </FormItem>
  293. </Col>
  294. <Col span={10}>
  295. <FormItem>
  296. {getFieldDecorator(`headers[${index}].value`, { initialValue: item.value })(
  297. <Input placeholder="参数值" />
  298. )}
  299. </FormItem>
  300. </Col>
  301. <Col span={4}>
  302. {values.length > 1 ? (
  303. <Icon
  304. className="dynamic-delete-button"
  305. type="minus-circle-o"
  306. onClick={() => this.removeValues('headers', index)}
  307. />
  308. ) : null}
  309. </Col>
  310. </Row>
  311. </FormItem>
  312. </div>
  313. ));
  314. };
  315. return (
  316. <Modal
  317. title={isAdd ? '添加期望' : '编辑期望'}
  318. visible={visible}
  319. maskClosable={false}
  320. onOk={this.handleOk}
  321. width={780}
  322. onCancel={() => onCancel()}
  323. afterClose={() => this.setState({ paramsForm: 'form' })}
  324. className="case-des-modal"
  325. >
  326. <Form onSubmit={this.handleOk}>
  327. <h2 className="sub-title" style={{ marginTop: 0 }}>
  328. 基本信息
  329. </h2>
  330. <FormItem {...formItemLayout} label="期望名称">
  331. {getFieldDecorator('name', {
  332. initialValue: name,
  333. rules: [{ required: true, message: '请输入期望名称!' }]
  334. })(<Input placeholder="请输入期望名称" />)}
  335. </FormItem>
  336. <FormItem {...formItemLayout} label="IP 过滤" className="ip-filter">
  337. <Col span={6} className="ip-switch">
  338. <FormItem>
  339. {getFieldDecorator('ip_enable', {
  340. initialValue: ip_enable,
  341. valuePropName: 'checked',
  342. rules: [{ type: 'boolean' }]
  343. })(<Switch />)}
  344. </FormItem>
  345. </Col>
  346. <Col span={18}>
  347. <div style={{ display: getFieldValue('ip_enable') ? '' : 'none' }} className="ip">
  348. <FormItem>
  349. {getFieldDecorator(
  350. 'ip',
  351. getFieldValue('ip_enable')
  352. ? {
  353. initialValue: ip,
  354. rules: [
  355. {
  356. pattern: constants.IP_REGEXP,
  357. message: '请填写正确的 IP 地址',
  358. required: true
  359. }
  360. ]
  361. }
  362. : {}
  363. )(<Input placeholder="请输入过滤的 IP 地址" />)}
  364. </FormItem>
  365. </div>
  366. </Col>
  367. </FormItem>
  368. <Row className="params-form" style={{ marginBottom: 8 }}>
  369. <Col {...{ span: 12, offset: 5 }}>
  370. <Switch
  371. size="small"
  372. checkedChildren="JSON"
  373. unCheckedChildren="JSON"
  374. checked={paramsForm === 'json'}
  375. onChange={bool => {
  376. this.setState({ paramsForm: bool ? 'json' : 'form' });
  377. }}
  378. />
  379. </Col>
  380. </Row>
  381. {valuesTpl(paramsArr, '参数过滤')}
  382. <FormItem
  383. wrapperCol={{ span: 6, offset: 5 }}
  384. style={{ display: paramsForm === 'form' ? '' : 'none' }}
  385. >
  386. <Button
  387. size="default"
  388. type="primary"
  389. onClick={() => this.addValues('paramsArr')}
  390. style={{ width: '100%' }}
  391. >
  392. <Icon type="plus" /> 添加参数
  393. </Button>
  394. </FormItem>
  395. <FormItem
  396. {...formItemLayout}
  397. wrapperCol={{ span: 17 }}
  398. label="参数过滤"
  399. style={{ display: paramsForm === 'form' ? 'none' : '' }}
  400. >
  401. <AceEditor className="pretty-editor" data={params} onChange={this.handleParams} />
  402. <FormItem>
  403. {getFieldDecorator(
  404. 'params',
  405. paramsForm === 'json'
  406. ? {
  407. rules: [
  408. { validator: this.jsonValidator, message: '请输入正确的 JSON 字符串!' }
  409. ]
  410. }
  411. : {}
  412. )(<Input style={{ display: 'none' }} />)}
  413. </FormItem>
  414. </FormItem>
  415. <h2 className="sub-title">响应</h2>
  416. <FormItem {...formItemLayout} required label="HTTP Code">
  417. {getFieldDecorator('code', {
  418. initialValue: code
  419. })(
  420. <Select showSearch>
  421. {httpCodes.map(code => (
  422. <Option key={'' + code} value={'' + code}>
  423. {'' + code}
  424. </Option>
  425. ))}
  426. </Select>
  427. )}
  428. </FormItem>
  429. <FormItem {...formItemLayout} label="延时">
  430. {getFieldDecorator('delay', {
  431. initialValue: delay,
  432. rules: [{ required: true, message: '请输入延时时间!', type: 'integer' }]
  433. })(<InputNumber placeholder="请输入延时时间" min={0} />)}
  434. <span>ms</span>
  435. </FormItem>
  436. {headersTpl(headers, 'HTTP 头')}
  437. <FormItem wrapperCol={{ span: 6, offset: 5 }}>
  438. <Button
  439. size="default"
  440. type="primary"
  441. onClick={() => this.addValues('headers')}
  442. style={{ width: '100%' }}
  443. >
  444. <Icon type="plus" /> 添加 HTTP 头
  445. </Button>
  446. </FormItem>
  447. <FormItem {...formItemLayout} wrapperCol={{ span: 17 }} label="Body" required>
  448. <FormItem>
  449. <AceEditor
  450. className="pretty-editor"
  451. data={res_body}
  452. mode={this.props.currInterface.res_body_type === 'json' ? null : 'text'}
  453. onChange={this.handleRequestBody}
  454. />
  455. </FormItem>
  456. </FormItem>
  457. </Form>
  458. </Modal>
  459. );
  460. }
  461. }
  462. const CaseDesModal = Form.create()(CaseDesForm);
  463. export default CaseDesModal;