mock平台

View.js 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586
  1. import './View.scss';
  2. import React, { PureComponent as Component } from 'react';
  3. import { connect } from 'react-redux';
  4. import PropTypes from 'prop-types';
  5. import { Table, Icon, Row, Col, Tooltip, message } from 'antd';
  6. import { Link } from 'react-router-dom';
  7. import AceEditor from 'client/components/AceEditor/AceEditor';
  8. import { formatTime, safeArray } from '../../../../common.js';
  9. import ErrMsg from '../../../../components/ErrMsg/ErrMsg.js';
  10. import variable from '../../../../constants/variable';
  11. import constants from '../../../../constants/variable.js';
  12. import copy from 'copy-to-clipboard';
  13. import SchemaTable from '../../../../components/SchemaTable/SchemaTable.js';
  14. const HTTP_METHOD = constants.HTTP_METHOD;
  15. @connect(state => {
  16. return {
  17. curData: state.inter.curdata,
  18. custom_field: state.group.field,
  19. currProject: state.project.currProject
  20. };
  21. })
  22. class View extends Component {
  23. constructor(props) {
  24. super(props);
  25. this.state = {
  26. init: true,
  27. enter: false
  28. };
  29. }
  30. static propTypes = {
  31. curData: PropTypes.object,
  32. currProject: PropTypes.object,
  33. custom_field: PropTypes.object
  34. };
  35. req_body_form(req_body_type, req_body_form) {
  36. if (req_body_type === 'form') {
  37. const columns = [
  38. {
  39. title: '参数名称',
  40. dataIndex: 'name',
  41. key: 'name',
  42. width: 140
  43. },
  44. {
  45. title: '参数类型',
  46. dataIndex: 'type',
  47. key: 'type',
  48. width: 100,
  49. render: text => {
  50. text = text || '';
  51. return text.toLowerCase() === 'text' ? (
  52. <span>
  53. <i className="query-icon text">T</i>文本
  54. </span>
  55. ) : (
  56. <span>
  57. <Icon type="file" className="query-icon" />文件
  58. </span>
  59. );
  60. }
  61. },
  62. {
  63. title: '是否必须',
  64. dataIndex: 'required',
  65. key: 'required',
  66. width: 100
  67. },
  68. {
  69. title: '示例',
  70. dataIndex: 'example',
  71. key: 'example',
  72. width: 80,
  73. render(_, item) {
  74. return <p style={{ whiteSpace: 'pre-wrap' }}>{item.example}</p>;
  75. }
  76. },
  77. {
  78. title: '备注',
  79. dataIndex: 'value',
  80. key: 'value',
  81. render(_, item) {
  82. return <p style={{ whiteSpace: 'pre-wrap' }}>{item.value}</p>;
  83. }
  84. }
  85. ];
  86. const dataSource = [];
  87. if (req_body_form && req_body_form.length) {
  88. req_body_form.map((item, i) => {
  89. dataSource.push({
  90. key: i,
  91. name: item.name,
  92. value: item.desc,
  93. example: item.example,
  94. required: item.required == 0 ? '否' : '是',
  95. type: item.type
  96. });
  97. });
  98. }
  99. return (
  100. <div style={{ display: dataSource.length ? '' : 'none' }} className="colBody">
  101. <Table
  102. bordered
  103. size="small"
  104. pagination={false}
  105. columns={columns}
  106. dataSource={dataSource}
  107. />
  108. </div>
  109. );
  110. }
  111. }
  112. res_body(res_body_type, res_body, res_body_is_json_schema) {
  113. if (res_body_type === 'json') {
  114. if (res_body_is_json_schema) {
  115. return <SchemaTable dataSource={res_body} />;
  116. } else {
  117. return (
  118. <div className="colBody">
  119. {/* <div id="vres_body_json" style={{ minHeight: h * 16 + 100 }}></div> */}
  120. <AceEditor data={res_body} readOnly={true} style={{ minHeight: 600 }} />
  121. </div>
  122. );
  123. }
  124. } else if (res_body_type === 'raw') {
  125. return (
  126. <div className="colBody">
  127. <AceEditor data={res_body} readOnly={true} mode="text" style={{ minHeight: 300 }} />
  128. </div>
  129. );
  130. }
  131. }
  132. req_body(req_body_type, req_body_other, req_body_is_json_schema) {
  133. if (req_body_other) {
  134. if (req_body_is_json_schema && req_body_type === 'json') {
  135. return <SchemaTable dataSource={req_body_other} />;
  136. } else {
  137. return (
  138. <div className="colBody">
  139. <AceEditor
  140. data={req_body_other}
  141. readOnly={true}
  142. style={{ minHeight: 300 }}
  143. mode={req_body_type === 'json' ? 'javascript' : 'text'}
  144. />
  145. </div>
  146. );
  147. }
  148. }
  149. }
  150. req_query(query) {
  151. const columns = [
  152. {
  153. title: '参数名称',
  154. dataIndex: 'name',
  155. width: 140,
  156. key: 'name'
  157. },
  158. {
  159. title: '是否必须',
  160. width: 100,
  161. dataIndex: 'required',
  162. key: 'required'
  163. },
  164. {
  165. title: '示例',
  166. dataIndex: 'example',
  167. key: 'example',
  168. width: 80,
  169. render(_, item) {
  170. return <p style={{ whiteSpace: 'pre-wrap' }}>{item.example}</p>;
  171. }
  172. },
  173. {
  174. title: '备注',
  175. dataIndex: 'value',
  176. key: 'value',
  177. render(_, item) {
  178. return <p style={{ whiteSpace: 'pre-wrap' }}>{item.value}</p>;
  179. }
  180. }
  181. ];
  182. const dataSource = [];
  183. if (query && query.length) {
  184. query.map((item, i) => {
  185. dataSource.push({
  186. key: i,
  187. name: item.name,
  188. value: item.desc,
  189. example: item.example,
  190. required: item.required == 0 ? '否' : '是'
  191. });
  192. });
  193. }
  194. return (
  195. <Table bordered size="small" pagination={false} columns={columns} dataSource={dataSource} />
  196. );
  197. }
  198. countEnter(str) {
  199. let i = 0;
  200. let c = 0;
  201. if (!str || !str.indexOf) {
  202. return 0;
  203. }
  204. while (str.indexOf('\n', i) > -1) {
  205. i = str.indexOf('\n', i) + 2;
  206. c++;
  207. }
  208. return c;
  209. }
  210. componentDidMount() {
  211. if (!this.props.curData.title && this.state.init) {
  212. this.setState({ init: false });
  213. }
  214. }
  215. enterItem = () => {
  216. this.setState({
  217. enter: true
  218. });
  219. };
  220. leaveItem = () => {
  221. this.setState({
  222. enter: false
  223. });
  224. };
  225. copyUrl = url => {
  226. copy(url);
  227. message.success('已经成功复制到剪切板');
  228. };
  229. flagMsg = (mock, strice) => {
  230. if (mock && strice) {
  231. return <span>( 全局mock & 严格模式 )</span>;
  232. } else if (!mock && strice) {
  233. return <span>( 严格模式 )</span>;
  234. } else if (mock && !strice) {
  235. return <span>( 全局mock )</span>;
  236. } else {
  237. return;
  238. }
  239. };
  240. render() {
  241. const dataSource = [];
  242. if (this.props.curData.req_headers && this.props.curData.req_headers.length) {
  243. this.props.curData.req_headers.map((item, i) => {
  244. dataSource.push({
  245. key: i,
  246. name: item.name,
  247. required: item.required == 0 ? '否' : '是',
  248. value: item.value,
  249. example: item.example,
  250. desc: item.desc
  251. });
  252. });
  253. }
  254. const req_dataSource = [];
  255. if (this.props.curData.req_params && this.props.curData.req_params.length) {
  256. this.props.curData.req_params.map((item, i) => {
  257. req_dataSource.push({
  258. key: i,
  259. name: item.name,
  260. desc: item.desc,
  261. example: item.example
  262. });
  263. });
  264. }
  265. const req_params_columns = [
  266. {
  267. title: '参数名称',
  268. dataIndex: 'name',
  269. key: 'name',
  270. width: 140
  271. },
  272. {
  273. title: '示例',
  274. dataIndex: 'example',
  275. key: 'example',
  276. width: 80,
  277. render(_, item) {
  278. return <p style={{ whiteSpace: 'pre-wrap' }}>{item.example}</p>;
  279. }
  280. },
  281. {
  282. title: '备注',
  283. dataIndex: 'desc',
  284. key: 'desc',
  285. render(_, item) {
  286. return <p style={{ whiteSpace: 'pre-wrap' }}>{item.desc}</p>;
  287. }
  288. }
  289. ];
  290. const columns = [
  291. {
  292. title: '参数名称',
  293. dataIndex: 'name',
  294. key: 'name',
  295. width: '200px'
  296. },
  297. {
  298. title: '参数值',
  299. dataIndex: 'value',
  300. key: 'value',
  301. width: '300px'
  302. },
  303. {
  304. title: '是否必须',
  305. dataIndex: 'required',
  306. key: 'required',
  307. width: '100px'
  308. },
  309. {
  310. title: '示例',
  311. dataIndex: 'example',
  312. key: 'example',
  313. width: '80px',
  314. render(_, item) {
  315. return <p style={{ whiteSpace: 'pre-wrap' }}>{item.example}</p>;
  316. }
  317. },
  318. {
  319. title: '备注',
  320. dataIndex: 'desc',
  321. key: 'desc',
  322. render(_, item) {
  323. return <p style={{ whiteSpace: 'pre-wrap' }}>{item.desc}</p>;
  324. }
  325. }
  326. ];
  327. let status = {
  328. undone: '未完成',
  329. done: '已完成'
  330. };
  331. let bodyShow =
  332. this.props.curData.req_body_other ||
  333. (this.props.curData.req_body_type === 'form' &&
  334. this.props.curData.req_body_form &&
  335. this.props.curData.req_body_form.length);
  336. let requestShow =
  337. (dataSource && dataSource.length) ||
  338. (req_dataSource && req_dataSource.length) ||
  339. (this.props.curData.req_query && this.props.curData.req_query.length) ||
  340. bodyShow;
  341. let methodColor =
  342. variable.METHOD_COLOR[
  343. this.props.curData.method ? this.props.curData.method.toLowerCase() : 'get'
  344. ];
  345. // statusColor = statusColor[this.props.curData.status?this.props.curData.status.toLowerCase():"undone"];
  346. // const aceEditor = <div style={{ display: this.props.curData.req_body_other && (this.props.curData.req_body_type !== "form") ? "block" : "none" }} className="colBody">
  347. // <AceEditor data={this.props.curData.req_body_other} readOnly={true} style={{ minHeight: 300 }} mode={this.props.curData.req_body_type === 'json' ? 'javascript' : 'text'} />
  348. // </div>
  349. if (!methodColor) {
  350. methodColor = 'get';
  351. }
  352. const { tag, up_time, title, uid, username } = this.props.curData;
  353. let res = (
  354. <div className="caseContainer">
  355. <h2 className="interface-title" style={{ marginTop: 0 }}>
  356. 基本信息
  357. </h2>
  358. <div className="panel-view">
  359. <Row className="row">
  360. <Col span={4} className="colKey">
  361. 接口名称:
  362. </Col>
  363. <Col span={8} className="colName">
  364. {title}
  365. </Col>
  366. <Col span={4} className="colKey">
  367. 创&ensp;建&ensp;人:
  368. </Col>
  369. <Col span={8} className="colValue">
  370. <Link className="user-name" to={'/user/profile/' + uid}>
  371. <img src={'/api/user/avatar?uid=' + uid} className="user-img" />
  372. {username}
  373. </Link>
  374. </Col>
  375. </Row>
  376. <Row className="row">
  377. <Col span={4} className="colKey">
  378. 状&emsp;&emsp;态:
  379. </Col>
  380. <Col span={8} className={'tag-status ' + this.props.curData.status}>
  381. {status[this.props.curData.status]}
  382. </Col>
  383. <Col span={4} className="colKey">
  384. 更新时间:
  385. </Col>
  386. <Col span={8}>{formatTime(up_time)}</Col>
  387. </Row>
  388. {safeArray(tag) &&
  389. safeArray(tag).length > 0 && (
  390. <Row className="row remark">
  391. <Col span={4} className="colKey">
  392. Tag :
  393. </Col>
  394. <Col span={18} className="colValue">
  395. {tag.join(' , ')}
  396. </Col>
  397. </Row>
  398. )}
  399. <Row className="row">
  400. <Col span={4} className="colKey">
  401. 接口路径:
  402. </Col>
  403. <Col
  404. span={18}
  405. className="colValue"
  406. onMouseEnter={this.enterItem}
  407. onMouseLeave={this.leaveItem}
  408. >
  409. <span
  410. style={{ color: methodColor.color, backgroundColor: methodColor.bac }}
  411. className="colValue tag-method"
  412. >
  413. {this.props.curData.method}
  414. </span>
  415. <span className="colValue">
  416. {this.props.currProject.basepath}
  417. {this.props.curData.path}
  418. </span>
  419. <Tooltip title="复制路径">
  420. <Icon
  421. type="copy"
  422. className="interface-url-icon"
  423. onClick={() => this.copyUrl(this.props.currProject.basepath + this.props.curData.path)}
  424. style={{ display: this.state.enter ? 'inline-block' : 'none' }}
  425. />
  426. </Tooltip>
  427. </Col>
  428. </Row>
  429. <Row className="row">
  430. <Col span={4} className="colKey">
  431. Mock地址:
  432. </Col>
  433. <Col span={18} className="colValue">
  434. {this.flagMsg(this.props.currProject.is_mock_open, this.props.currProject.strice)}
  435. <span
  436. className="href"
  437. onClick={() =>
  438. window.open(
  439. location.protocol +
  440. '//' +
  441. location.hostname +
  442. (location.port !== '' ? ':' + location.port : '') +
  443. `/mock/${this.props.currProject._id}${this.props.currProject.basepath}${
  444. this.props.curData.path
  445. }`,
  446. '_blank'
  447. )
  448. }
  449. >
  450. {location.protocol +
  451. '//' +
  452. location.hostname +
  453. (location.port !== '' ? ':' + location.port : '') +
  454. `/mock/${this.props.currProject._id}${this.props.currProject.basepath}${
  455. this.props.curData.path
  456. }`}
  457. </span>
  458. </Col>
  459. </Row>
  460. {this.props.curData.custom_field_value &&
  461. this.props.custom_field.enable && (
  462. <Row className="row remark">
  463. <Col span={4} className="colKey">
  464. {this.props.custom_field.name}:
  465. </Col>
  466. <Col span={18} className="colValue">
  467. {this.props.curData.custom_field_value}
  468. </Col>
  469. </Row>
  470. )}
  471. </div>
  472. {this.props.curData.desc && <h2 className="interface-title">备注</h2>}
  473. {this.props.curData.desc && (
  474. <div
  475. className="tui-editor-contents"
  476. style={{ margin: '0px', padding: '0px 20px', float: 'none' }}
  477. dangerouslySetInnerHTML={{ __html: this.props.curData.desc }}
  478. />
  479. )}
  480. <h2 className="interface-title" style={{ display: requestShow ? '' : 'none' }}>
  481. 请求参数
  482. </h2>
  483. {req_dataSource.length ? (
  484. <div className="colHeader">
  485. <h3 className="col-title">路径参数:</h3>
  486. <Table
  487. bordered
  488. size="small"
  489. pagination={false}
  490. columns={req_params_columns}
  491. dataSource={req_dataSource}
  492. />
  493. </div>
  494. ) : (
  495. ''
  496. )}
  497. {dataSource.length ? (
  498. <div className="colHeader">
  499. <h3 className="col-title">Headers:</h3>
  500. <Table
  501. bordered
  502. size="small"
  503. pagination={false}
  504. columns={columns}
  505. dataSource={dataSource}
  506. />
  507. </div>
  508. ) : (
  509. ''
  510. )}
  511. {this.props.curData.req_query && this.props.curData.req_query.length ? (
  512. <div className="colQuery">
  513. <h3 className="col-title">Query:</h3>
  514. {this.req_query(this.props.curData.req_query)}
  515. </div>
  516. ) : (
  517. ''
  518. )}
  519. <div
  520. style={{
  521. display:
  522. this.props.curData.method &&
  523. HTTP_METHOD[this.props.curData.method.toUpperCase()].request_body
  524. ? ''
  525. : 'none'
  526. }}
  527. >
  528. <h3 style={{ display: bodyShow ? '' : 'none' }} className="col-title">
  529. Body:
  530. </h3>
  531. {this.props.curData.req_body_type === 'form'
  532. ? this.req_body_form(this.props.curData.req_body_type, this.props.curData.req_body_form)
  533. : this.req_body(
  534. this.props.curData.req_body_type,
  535. this.props.curData.req_body_other,
  536. this.props.curData.req_body_is_json_schema
  537. )}
  538. </div>
  539. <h2 className="interface-title">返回数据</h2>
  540. {this.res_body(
  541. this.props.curData.res_body_type,
  542. this.props.curData.res_body,
  543. this.props.curData.res_body_is_json_schema
  544. )}
  545. </div>
  546. );
  547. if (!this.props.curData.title) {
  548. if (this.state.init) {
  549. res = <div />;
  550. } else {
  551. res = <ErrMsg type="noData" />;
  552. }
  553. }
  554. return res;
  555. }
  556. }
  557. export default View;