index.jsx 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926
  1. import React, { useEffect, useRef, useState } from 'react';
  2. import { Form, Row, Col, Card, Modal, Input, Select, Upload, Button, message, Tag, Tooltip, Image } from 'antd';
  3. import { connect } from 'umi';
  4. import styles from './index.less';
  5. import {
  6. ForkOutlined,
  7. PlusOutlined,
  8. ExclamationCircleFilled,
  9. UploadOutlined,
  10. EditOutlined,
  11. EyeOutlined,
  12. ShareAltOutlined,
  13. LeftOutlined,
  14. RightOutlined,
  15. } from '@ant-design/icons';
  16. const { Search } = Input;
  17. import * as echarts from 'echarts';
  18. import { recurrent as bug_recurrent, severity, bug_categories } from './const';
  19. import { timeToString, getBase64 } from '@/utils/common';
  20. import BugGuideTree from '../BugGuideTree';
  21. const formItemLayout = {
  22. labelCol: {
  23. span: 8,
  24. },
  25. wrapperCol: {
  26. span: 19,
  27. },
  28. };
  29. const modalFormItemLayout = {
  30. labelCol: {
  31. span: 5,
  32. },
  33. wrapperCol: {
  34. span: 19,
  35. },
  36. };
  37. const uploadButton = (
  38. <div>
  39. <PlusOutlined />
  40. <div style={{ marginTop: 8 }}>点击上传</div>
  41. </div>
  42. );
  43. const Step2 = (props) => {
  44. const [reportForm] = Form.useForm();
  45. const [addCaseForm] = Form.useForm();
  46. const [addBugForm] = Form.useForm();
  47. const [editReportForm] = Form.useForm();
  48. const {
  49. dispatch, reportCommonInfo, osType,collaborative_type,
  50. testCaseList, caseBugList, categories, pathInfo,commonId
  51. } = props;
  52. const [showTaskRecommendModal, setTaskRecommendModal] = useState(false);
  53. const [showAddTestCaseModal, setAddTestCaseModal] = useState(false);
  54. const [showAddBugModal, setAddTestBugModal] = useState(false);
  55. const [showEditReportModal, setEditReportModal] = useState(false);
  56. const currentTestCaseRef = useRef({});
  57. const [currBugDetail,setCurrBugDetail] = useState({});
  58. const [isAddCaseStatus, setIsAddCaseStatus] = useState(true);
  59. // const [currActiveTestCase, setCurrActiveTestCase] = useState({});
  60. //上传图片廊需要的字段
  61. const [fileList, setFileList] = useState([]);
  62. const [previewTitle, setPreviewTitle] = useState('');
  63. const [previewImage, setPreviewImage] = useState('');
  64. const [previewVisible, setPreviewVisible] = useState(false);
  65. const [page2List, setPage2List] = useState([]);
  66. const [page3List, setPage3List] = useState([]);
  67. const [bugList, setBugList] = useState([]);
  68. const forkStatus = useRef(false);
  69. const currForkId = useRef('');
  70. const forkInput = useRef('');
  71. const handleEditReportInfo = () => {
  72. editReportForm.validateFields().then((res) => {
  73. let formData = new FormData();
  74. formData.append('name', res.name);
  75. formData.append('report_id', reportCommonInfo.id);
  76. formData.append('worker_id', commonId.userId);
  77. formData.append('case_take_id', commonId.case_take_id);
  78. formData.append('device_model', res.device_model);
  79. formData.append('device_brand', res.device_brand);
  80. formData.append('device_os', res.device_os);
  81. dispatch({
  82. type: 'editReport/updateReportCommonDetail',
  83. payload: { formData },
  84. });
  85. });
  86. setEditReportModal(false);
  87. };
  88. const handleAddOrEditTestCase = () => {
  89. addCaseForm.validateFields().then((res) => {
  90. let formData = new FormData();
  91. // formData.append("id", values.reportName);
  92. formData.append('report_id', reportCommonInfo.id);
  93. formData.append('name', res.name);
  94. formData.append('front', res.front);
  95. formData.append('behind', res.behind);
  96. formData.append('description', res.description);
  97. if (!isAddCaseStatus) {
  98. //处理编辑用例
  99. formData.append('id', currentTestCaseRef.current.id);
  100. dispatch({
  101. type: 'editReport/updateTestCase',
  102. payload: {
  103. formData,
  104. report_id: reportCommonInfo.id,
  105. },
  106. }).then((res) => {
  107. if (res && res.status === 200) {
  108. message.success('修改成功!');
  109. }
  110. });
  111. } else {
  112. //处理添加用例
  113. dispatch({
  114. type: 'editReport/createTestCase',
  115. payload: {
  116. formData,
  117. report_id: reportCommonInfo.id,
  118. },
  119. }).then(({ res, testCaseList }) => {
  120. if (res && res.id) {
  121. message.success('添加成功!');
  122. //添加成功后要把currCase指向新的
  123. const currCase = testCaseList.filter(item =>
  124. item.id === res.id,
  125. );
  126. handleClickTestCase(currCase[0]);
  127. }
  128. });
  129. }
  130. setAddTestCaseModal(false);
  131. });
  132. };
  133. const handleAddBug = () => {
  134. addBugForm.validateFields().then((res) => {
  135. let formData = new FormData();
  136. formData.append('report_id', reportCommonInfo.id);
  137. formData.append('title', res.title);
  138. formData.append('description', res.description);
  139. formData.append('bug_category', res.bug_category);
  140. formData.append('severity', res.severity);
  141. formData.append('recurrent', res.recurrent);
  142. formData.append('parent', forkStatus.current?currForkId.current+'-2':null);
  143. formData.append('useCase', currentTestCaseRef.current.id);
  144. formData.append('case_id', commonId.caseId);
  145. formData.append('case_take_id', commonId.case_take_id);
  146. formData.append('worker_id', commonId.userId);
  147. formData.append('page', `${res.page1}-${res.page2}-${res.page3}`);
  148. if (fileList.length) {
  149. let str = '';
  150. fileList.map(item => {
  151. str += (item.response + ',');
  152. });
  153. str = str.substring(0, str.length - 1);
  154. formData.append('img_url', str);
  155. }
  156. //新建bug
  157. //提交bug之前要做相似度判断,超过80%就不让提交了
  158. dispatch({
  159. type: 'editReport/bugSimilarity',
  160. payload: {
  161. case_take_id:commonId.case_take_id,
  162. type:res.title,
  163. content:res.description
  164. },
  165. }).then(res=>{
  166. if(res){
  167. const { scores } = res;
  168. const s = scores.some(item=>item>=80);
  169. if(!s){
  170. dispatch({
  171. type: 'editReport/createCaseBug',
  172. payload: {
  173. formData,
  174. useCase: currentTestCaseRef.current.id,
  175. },
  176. }).then(res => {
  177. addBugForm.resetFields();
  178. setPage2List([]);
  179. setPage3List([]);
  180. setFileList([]);
  181. forkStatus.current=false;
  182. setAddTestBugModal(false);
  183. });
  184. }else{
  185. message.error('当前缺陷与已有缺陷相似度超过80%,请修改标题和内容后重试');
  186. }
  187. }
  188. })
  189. });
  190. };
  191. const handleCancelTestBug = ()=>{
  192. setAddTestBugModal(false);
  193. setFileList([]);
  194. forkInput.current.state.value = '';
  195. forkStatus.current = false;
  196. addBugForm.resetFields();
  197. setPage2List([]);
  198. setPage3List([]);
  199. }
  200. const handleClickTestCase = (caseItem) => {
  201. // setCurrActiveTestCase(caseItem);
  202. currentTestCaseRef.current = caseItem;
  203. dispatch({
  204. type: 'editReport/getCaseBugList',
  205. payload: caseItem.id,
  206. });
  207. };
  208. const handleEditTestCase = (item) => {
  209. setIsAddCaseStatus(false);
  210. currentTestCaseRef.current = item;
  211. addCaseForm.setFieldsValue(currentTestCaseRef.current);
  212. setAddTestCaseModal(true);
  213. };
  214. const handleClickAddCase = () => {
  215. addCaseForm.resetFields();
  216. setAddTestCaseModal(true);
  217. };
  218. const handleClickAddBug = () => {
  219. //current目前只在点击edit cease的时候会有用
  220. addBugForm.resetFields();
  221. forkInput.current.state.value = '';
  222. setAddTestBugModal(true);
  223. };
  224. const handleClickRecommendBtn = () => {
  225. dispatch({
  226. type: 'editReport/getPathInfo',
  227. payload: {
  228. case_take_id: commonId.case_take_id,
  229. report_id: reportCommonInfo.id,
  230. },
  231. });
  232. dispatch({
  233. type: 'editReport/getBugRecommendPath',
  234. payload: {
  235. case_take_id: commonId.case_take_id,
  236. report_id: reportCommonInfo.id,
  237. },
  238. });
  239. dispatch({
  240. type: 'editReport/getBugRecommendList',
  241. payload: {
  242. case_take_id: commonId.case_take_id,
  243. report_id: reportCommonInfo.id,
  244. },
  245. });
  246. setTaskRecommendModal(true);
  247. };
  248. const handleSelectPage1 = (val) => {
  249. if (val !== page1) {
  250. addBugForm.setFieldsValue({page1:val,page2:'',page3:''})
  251. let item = categories.find(x => x.item === val);
  252. setPage2List(item.children);
  253. }
  254. };
  255. const handleSelectPage2 = (val) => {
  256. if (val !== page2) {
  257. addBugForm.setFieldsValue({page2:val,page3:''})
  258. let item = page2List.find(x => x.item === val);
  259. setPage3List(item.children);
  260. }
  261. };
  262. // const handlePage3Change = val => {
  263. // // setPage3(val);
  264. // };
  265. const handlePreview = async file => {
  266. if (!file.url && !file.preview) {
  267. file.preview = await getBase64(file.originFileObj);
  268. }
  269. setPreviewImage(file.url || file.preview);
  270. setPreviewVisible(true);
  271. setPreviewTitle(file.name || file.url.substring(file.url.lastIndexOf('/') + 1));
  272. };
  273. const handleCancel = () => setPreviewVisible(false);
  274. const handleChange = ({ fileList }) => {
  275. setFileList(fileList);
  276. };
  277. const handleChangeImgToLeft = (item) => {
  278. console.log(item);
  279. item.left--;
  280. item.right--;
  281. item['imgArr'] = item['originArr'].slice(item['left'], item['right']);
  282. let newList = [...bugList];
  283. let index = newList.findIndex(i => i.id === item.id);
  284. newList.splice(index, 1, item);
  285. setBugList(newList);
  286. };
  287. const handleChangeImgToRight = (item) => {
  288. console.log(item);
  289. item.left++;
  290. item.right++;
  291. item['imgArr'] = item['originArr'].slice(item['left'], item['right']);
  292. let newList = [...bugList];
  293. let index = newList.findIndex(i => i.id === item.id);
  294. newList.splice(index, 1, item);
  295. setBugList(newList);
  296. };
  297. const searchBugToFork = value =>{
  298. value = value.trim();
  299. forkStatus.current = true;
  300. currForkId.current = value;
  301. dispatch({
  302. type: 'editReport/getBugDetail',
  303. payload: { id: value},
  304. }).then(res=>{
  305. let detail = res.detail;
  306. // detail['recurrent'] = bug_recurrent[res.detail.recurrent-1];
  307. // console.log(bug_recurrent[res.detail.recurrent-1])
  308. setCurrBugDetail(detail);
  309. const {bug_page, bug_category, severity, recurrent} = res.detail;
  310. const pages = bug_page.split("-");
  311. const page1 = pages[0];
  312. const page2 = pages[1];
  313. const page3 = pages[2];
  314. dispatch({
  315. type: 'editReport/forkReport',
  316. payload: {
  317. page1, page2, page3, bug_category, severity, recurrent
  318. },
  319. })
  320. })
  321. }
  322. const handleInitThreePages = ()=>{
  323. let detail = { ...currBugDetail };
  324. detail['recurrent'] = bug_recurrent[currBugDetail.recurrent-1];
  325. detail['severity'] = severity[currBugDetail.severity-1];
  326. if(JSON.stringify(detail)!=='{}') {
  327. let pages = detail.bug_page.split("-");
  328. detail.page1 = pages[0];
  329. detail.page2 = pages[1];
  330. detail.page3 = pages[2];
  331. if (page2List && page2List.length) {
  332. handleSelectPage2(pages[1]);
  333. // console.log(detail)
  334. addBugForm.setFieldsValue(detail)
  335. // console.log(addBugForm.getFieldsValue())
  336. }else{
  337. handleSelectPage1(pages[0]);
  338. }
  339. }
  340. }
  341. useEffect(()=>{
  342. handleInitThreePages();
  343. },[currBugDetail,page2List])
  344. useEffect(() => {
  345. //能到第二步,说明是有报告信息的
  346. //有报告,获取对应信息。没有就直接转去了第一步
  347. dispatch({
  348. type: 'editReport/getTestCaseList',
  349. payload: { report_id: reportCommonInfo.id },
  350. }).then((res) => {
  351. if (res && res.length && commonId) {
  352. currentTestCaseRef.current = res[0];
  353. dispatch({
  354. type: 'editReport/getCaseBugList',
  355. payload: currentTestCaseRef.current.id,
  356. });
  357. }
  358. });
  359. dispatch({
  360. type: 'editReport/getCategories',
  361. payload: { examId: commonId.examId },
  362. });
  363. }, [reportCommonInfo,commonId.userId,commonId.examId,commonId.case_take_id]);
  364. useEffect(() => {
  365. let bugs = caseBugList.map(item => item.detail);
  366. bugs.map(item => {
  367. item['originArr'] = item.img_url ? item.img_url.split(',') : [];
  368. if (item['originArr'].length >= 3) {
  369. //通过更改左右的标记值来更改显示的图片
  370. item['left'] = 0;
  371. item['right'] = 3;
  372. item['imgArr'] = item['originArr'].slice(item['left'], item['right']);
  373. } else if (item['originArr'].length > 0 && item['originArr'].length < 3) {
  374. item['imgArr'] = item['originArr'];
  375. item['left'] = 0;
  376. item['right'] = item['originArr'].length;
  377. }
  378. });
  379. setBugList(bugs);
  380. }, [caseBugList]);
  381. return (
  382. <div id="main">
  383. <Row gutter={10} className={styles.reportInfoContainer}>
  384. <Col span={23}>
  385. <Form
  386. {...formItemLayout}
  387. form={reportForm}
  388. className={styles.stepForm}
  389. hideRequiredMark
  390. >
  391. <Row gutter={10}>
  392. <Col span={5}>
  393. <Form.Item label="创建日期" rules={[{ required: true, message: '请输入报告名称' }]}>
  394. {timeToString(reportCommonInfo.create_time_millis)}
  395. </Form.Item>
  396. </Col>
  397. <Col span={5}>
  398. <Form.Item label="报告名称" rules={[{ required: true, message: '请输入报告名称' }]}>
  399. {reportCommonInfo.name}
  400. </Form.Item>
  401. </Col>
  402. <Col span={5}>
  403. <Form.Item label="设备名称" required>
  404. {reportCommonInfo.device_brand}
  405. </Form.Item>
  406. </Col>
  407. <Col span={5}>
  408. <Form.Item label="设备品牌" required>
  409. {reportCommonInfo.device_model}
  410. </Form.Item>
  411. </Col>
  412. <Col span={4}>
  413. <Form.Item label="操作系统" required>
  414. {reportCommonInfo.device_os}
  415. </Form.Item>
  416. </Col>
  417. </Row>
  418. </Form>
  419. </Col>
  420. <Col span={1}>
  421. <EditOutlined className={styles.editReportInfoIcon}
  422. onClick={() => setEditReportModal(true)}
  423. />
  424. </Col>
  425. </Row>
  426. <Card>
  427. <div className={styles.reportContainer}>
  428. <Row gutter={10}>
  429. <Col span={8}>
  430. <div gutter={10} className={styles.testCaseTitle}>
  431. <h3>测试用例列表</h3>
  432. <Button size="small" type="primary"
  433. onClick={() => {
  434. handleClickAddCase();
  435. }}>
  436. <PlusOutlined className={styles.addIcon} />
  437. 用例
  438. </Button>
  439. </div>
  440. <div className={styles.testCaseList}>
  441. {testCaseList && testCaseList.length ? testCaseList.map((item) => {
  442. return (
  443. <div
  444. className={`${styles.testCaseItem} ${currentTestCaseRef.current.id === item.id ? styles.activeCase : ''}`}
  445. key={item.id}
  446. onClick={() => {
  447. handleClickTestCase(item);
  448. }}>
  449. <Row gutter={10}>
  450. <Col span={6}>
  451. {<span
  452. className={`${styles.testCaseItemNo} ${styles.testCaseItemTitle}`}>{`编号.${((item.id).substring((item.id.length) - 6)).toUpperCase()}`}</span>}
  453. </Col>
  454. <Col span={15} className={styles.testCaseItemTitle}>
  455. <Tooltip placement="topLeft" title={item.name}>
  456. {item.name}
  457. </Tooltip>
  458. </Col>
  459. <Col span={3}>
  460. <EditOutlined className={styles.editTestBug}
  461. onClick={() => handleEditTestCase(item)} />
  462. </Col>
  463. </Row>
  464. </div>);
  465. }) : <div>快来创建你的第一个测试用例吧~</div>}
  466. </div>
  467. </Col>
  468. <Col span={16}>
  469. <div gutter={10} className={styles.testBugTitle}>
  470. <h3>缺陷列表</h3>
  471. <div>
  472. <Button size="small" type="primary"
  473. disabled={JSON.stringify(currentTestCaseRef.current) === '{}'}
  474. onClick={() => handleClickAddBug()}>
  475. <PlusOutlined className={styles.addIcon} />
  476. 缺陷
  477. </Button>
  478. {
  479. collaborative_type === '0' ?
  480. <Button size="small"
  481. className={styles.recommendBtn}
  482. disabled={JSON.stringify(currentTestCaseRef.current) === '{}'}
  483. onClick={() => handleClickRecommendBtn()}>
  484. <ShareAltOutlined className={styles.addIcon}/>
  485. 推荐
  486. </Button> : null
  487. }
  488. </div>
  489. </div>
  490. <div className={styles.testBugList}>
  491. {bugList && bugList.length ? bugList.map((item) => {
  492. return (
  493. <div className={styles.testBugItem} key={item.id}>
  494. <Row gutter={10}>
  495. <Col span={12}>
  496. <div>
  497. <div className={styles.testBugItemTitleBlock}>
  498. <Tooltip placement="topLeft" title={item.title}>
  499. <span className={styles.testBugItemTitleDetail}>标题:{item.title}</span>
  500. </Tooltip>
  501. </div>
  502. <div className={styles.testBugItemTitleBlock}>
  503. <span className={styles.testBugItemTitle}>特征:</span>
  504. <Tag color="cyan">{bug_recurrent[item.recurrent - 1]}</Tag>
  505. <Tag color="red">{severity[item.severity - 1]}</Tag>
  506. <Tag color="geekblue">{item.bug_category}</Tag>
  507. </div>
  508. <div className={styles.testBugItemTitleBlock}>
  509. <span className={styles.testBugItemTitle}>标识:</span>
  510. {item.id}
  511. </div>
  512. <div className={styles.testBugItemTitleBlock}>
  513. <span className={styles.testBugItemTitle}>路径:</span>
  514. {item.bug_page}
  515. </div>
  516. <div className={styles.testBugItemTitleBlock}>
  517. <Tooltip placement="topLeft" title={item.description}>
  518. <span className={styles.testBugItemDesc}>描述:{item.description}</span>
  519. </Tooltip>
  520. </div>
  521. </div>
  522. </Col>
  523. {
  524. item.img_url ? (
  525. <Col span={12} className={styles.bugImgList}>
  526. {item.left > 0 ? <LeftOutlined
  527. onClick={() => {
  528. handleChangeImgToLeft(item);
  529. }} /> : <div className={styles.switchImgBtn}></div>}
  530. {item.imgArr.map(img => {
  531. return <Image src={img} key={img} />;})}
  532. {item.right < item.originArr.length ? <RightOutlined onClick={() => {
  533. handleChangeImgToRight(item);
  534. }} /> : <div className={styles.switchImgBtn}></div>}
  535. </Col>)
  536. : null}
  537. </Row>
  538. </div>
  539. );
  540. }) : <div>当前用例暂无提交记录,快去创建第一个BUG吧~</div>}
  541. </div>
  542. </Col>
  543. </Row>
  544. </div>
  545. </Card>
  546. <Modal title={isAddCaseStatus ? '添加测试用例' : '编辑测试用例'} width={720}
  547. maskClosable={false}
  548. visible={showAddTestCaseModal}
  549. forceRender={true}
  550. className="addModal"
  551. footer={[
  552. <Button key='submit' type="primary" htmlType="submit" onClick={handleAddOrEditTestCase}>确定</Button>,
  553. <Button key='cancel' htmlType="button" style={{ marginLeft: '10px' }}
  554. onClick={() => {
  555. setAddTestCaseModal(false);
  556. }}>取消</Button>]}
  557. onCancel={() => {
  558. setAddTestCaseModal(false);
  559. }}
  560. >
  561. <ExclamationCircleFilled className={styles.addModalInfo} />为了评分准确,请勿提交重复测试用例
  562. <Form
  563. {...modalFormItemLayout}
  564. form={addCaseForm}
  565. layout="horizontal"
  566. className={styles.stepForm}
  567. >
  568. <Form.Item
  569. label="用例名称"
  570. name="name"
  571. rules={[
  572. {
  573. required: true,
  574. message: '请输入用例名称!',
  575. },
  576. ]}
  577. >
  578. <Input />
  579. </Form.Item>
  580. <Form.Item
  581. label="前置条件"
  582. name="front"
  583. rules={[
  584. {
  585. required: true,
  586. message: '请输入前置条件!',
  587. },
  588. ]}
  589. >
  590. <Input.TextArea autoSize={{ minRows: 3, maxRows: 999 }} />
  591. </Form.Item>
  592. <Form.Item
  593. label="测试步骤"
  594. name="behind"
  595. rules={[
  596. {
  597. required: true,
  598. message: '请输入测试步骤!',
  599. },
  600. ]}
  601. >
  602. <Input.TextArea autoSize={{ minRows: 3, maxRows: 999 }} />
  603. </Form.Item>
  604. <Form.Item
  605. label="预期结果"
  606. name="description"
  607. rules={[
  608. {
  609. required: true,
  610. message: '请输入预期结果!',
  611. },
  612. ]}
  613. >
  614. <Input.TextArea autoSize={{ minRows: 3, maxRows: 999 }} />
  615. </Form.Item>
  616. </Form>
  617. </Modal>
  618. <Modal visible={showAddBugModal} width={720}
  619. forceRender={true}
  620. maskClosable={false}
  621. title={[
  622. <span className={styles.modalTitle} key="title">添加用例缺陷</span>,
  623. <Search
  624. placeholder="输入Bug标识进行fork"
  625. enterButton="fork"
  626. onSearch={searchBugToFork}
  627. className={styles.forkBtn}
  628. key="search"
  629. ref={forkInput}
  630. />
  631. ]}
  632. footer={[
  633. <Button key='submit' type="primary" htmlType="submit" onClick={()=>handleAddBug()}>确定</Button>,
  634. <Button key='cancel' htmlType="button" style={{ marginLeft: '10px' }}
  635. onClick={() => {
  636. handleCancelTestBug()
  637. }}>取消</Button>]}
  638. onCancel={() => {
  639. handleCancelTestBug();
  640. }}
  641. className={styles.bugForm}
  642. >
  643. <ExclamationCircleFilled className={styles.addModalInfo} />为了评分准确,请勿提交重复缺陷Bug
  644. <Form
  645. form={addBugForm}
  646. {...modalFormItemLayout}
  647. layout="horizontal"
  648. className={styles.stepForm}
  649. >
  650. <Form.Item
  651. label="缺陷标题"
  652. name="title"
  653. rules={[
  654. {
  655. required: true,
  656. message: '请输入测试标题!',
  657. },
  658. ]}
  659. >
  660. <Input.TextArea autoSize={{ minRows: 1, maxRows: 999 }} />
  661. </Form.Item>
  662. <Form.Item
  663. label="缺陷描述"
  664. name="description"
  665. rules={[
  666. {
  667. required: true,
  668. message: '请输入题目描述!',
  669. },
  670. ]}
  671. >
  672. <Input.TextArea autoSize={{ minRows: 2, maxRows: 999 }} />
  673. </Form.Item>
  674. <Form.Item
  675. label="三级页面"
  676. required={true}
  677. >
  678. <Row gutter={5} className={styles.pageSelect}>
  679. <Col span={8}>
  680. <Form.Item
  681. name="page1"
  682. rules={[
  683. {
  684. required: true,
  685. message: '请输入一级页面',
  686. },
  687. ]}
  688. >
  689. <Select value={addBugForm.getFieldValue('page1')}
  690. disabled={forkStatus.current}
  691. onSelect={(val) => {
  692. handleSelectPage1(val);
  693. }}>
  694. {categories.map((item) => {
  695. return <Select.Option value={item.item} key={item.item}>{item.item}
  696. </Select.Option>;
  697. })}
  698. </Select>
  699. </Form.Item>
  700. </Col>
  701. <Col span={8}>
  702. <Form.Item
  703. name="page2"
  704. rules={[
  705. {
  706. required: true,
  707. message: '请输入二级页面',
  708. },
  709. ]}
  710. >
  711. <Select value={addBugForm.getFieldValue('page2')}
  712. disabled={(!page2List.length&& !addBugForm.getFieldValue('page2'))||forkStatus.current}
  713. onSelect={(val) => {
  714. handleSelectPage2(val);
  715. }}>
  716. {page2List.map((item) => {
  717. return <Select.Option value={item.item} key={item.item}>{item.item}</Select.Option>;
  718. })}
  719. </Select>
  720. </Form.Item>
  721. </Col>
  722. <Col span={8}>
  723. <Form.Item
  724. name="page3"
  725. rules={[
  726. {
  727. required: true,
  728. message: '请输入三级页面',
  729. },
  730. ]}
  731. >
  732. <Select value={addBugForm.getFieldValue('page3')}
  733. disabled={(!page3List.length&& !addBugForm.getFieldValue('page3'))||forkStatus.current}>
  734. {page3List.map((item) => {
  735. return <Select.Option value={item.item} key={item.item}>{item.item}</Select.Option>;
  736. })}
  737. </Select>
  738. </Form.Item>
  739. </Col>
  740. </Row>
  741. </Form.Item>
  742. <Form.Item
  743. label="漏洞分类"
  744. name="bug_category"
  745. rules={[
  746. {
  747. required: true,
  748. message: '请选择漏洞分类!',
  749. },
  750. ]}
  751. >
  752. <Select>
  753. {bug_categories.map((item) => {
  754. return <Select.Option value={item} key={item}>{item}</Select.Option>;
  755. })}
  756. </Select>
  757. </Form.Item>
  758. <Form.Item
  759. label="严重等级"
  760. name="severity"
  761. rules={[
  762. {
  763. required: true,
  764. message: '请选择严重等级!',
  765. },
  766. ]}
  767. >
  768. <Select>
  769. {severity.map((item) => {
  770. return <Select.Option value={item} key={item}>{item}</Select.Option>;
  771. })}
  772. </Select>
  773. </Form.Item>
  774. <Form.Item
  775. label="复现程度"
  776. name="recurrent"
  777. rules={[
  778. {
  779. required: true,
  780. message: '请选择复现程度!',
  781. },
  782. ]}
  783. >
  784. <Select>
  785. {bug_recurrent.map((item) => {
  786. return <Select.Option value={item} key={item}>{item}</Select.Option>;
  787. })}
  788. </Select>
  789. </Form.Item>
  790. <Form.Item
  791. label="上传截图"
  792. name="testName"
  793. >
  794. <Upload
  795. action='/Bug/api/upload/image'
  796. data={file => ({ // data里存放的是接口的请求参数
  797. file,
  798. caseId: currentTestCaseRef.current.id,
  799. work_id: '2',
  800. })}
  801. listType="picture-card"
  802. fileList={fileList}
  803. onPreview={handlePreview}
  804. onChange={handleChange}
  805. >
  806. {uploadButton}
  807. </Upload>
  808. <Modal
  809. visible={previewVisible}
  810. title={previewTitle}
  811. footer={null}
  812. onCancel={() => handleCancel()}
  813. >
  814. <img alt="example" style={{ width: '100%' }} src={previewImage} />
  815. </Modal>
  816. </Form.Item>
  817. </Form>
  818. </Modal>
  819. <Modal title="修改报告基础信息" visible={showEditReportModal}
  820. footer={[
  821. <Button key='submit' type="primary" htmlType="submit"
  822. onClick={handleEditReportInfo}>确定</Button>,
  823. <Button key='cancel' htmlType="button" style={{ marginLeft: '10px' }}
  824. onClick={() => {
  825. setEditReportModal(false);
  826. }}>取消</Button>]}
  827. onCancel={() => {
  828. setEditReportModal(false);
  829. }}>
  830. <div>
  831. <Form
  832. {...modalFormItemLayout}
  833. form={editReportForm}
  834. layout="horizontal"
  835. initialValues={reportCommonInfo}
  836. >
  837. <Form.Item label="报告名称" name="name" rules={[{ required: true, message: '请输入报告名称' }]}>
  838. <Input placeholder="请输入报告名称" />
  839. </Form.Item>
  840. <Form.Item label="设备名称" name="device_model" rules={[{ required: true, message: '请输入设备品牌' }]}>
  841. <Input placeholder="请输入设备名称" />
  842. </Form.Item>
  843. <Form.Item label="设备品牌" name="device_brand" rules={[{ required: true, message: '请输入设备品牌' }]}>
  844. <Input placeholder="请输入设备品牌" />
  845. </Form.Item>
  846. <Form.Item label="操作系统" name="device_os" rules={[{ required: true, message: '请输入操作系统' }]}>
  847. <Select placeholder="请选择操作系统">
  848. {osType.map((option) => {
  849. return <Select.Option value={option} key={option}>{option}</Select.Option>;
  850. })}
  851. </Select>
  852. </Form.Item>
  853. </Form>
  854. </div>
  855. </Modal>
  856. <Modal title="任务推荐"
  857. visible={showTaskRecommendModal}
  858. destroyOnClose width={1000}
  859. onOk={() => setTaskRecommendModal(false)}
  860. onCancel={() => {
  861. setTaskRecommendModal(false);
  862. }
  863. }
  864. className={styles.recommendModal}>
  865. <div>
  866. <BugGuideTree />
  867. </div>
  868. </Modal>
  869. </div>
  870. );
  871. };
  872. export default connect(({ editReport, loading ,report}) => ({
  873. submitting: loading.effects['editReport/submitStepForm'],
  874. reportCommonInfo: editReport.reportCommonInfo,
  875. testCaseList: editReport.testCaseList,
  876. caseBugList: editReport.caseBugList,
  877. categories: editReport.categories,
  878. pathInfo: editReport.pathInfo,
  879. osType: editReport.osType,
  880. commonId: editReport.commonId,
  881. collaborative_type: report.collaborative_type,
  882. }))(Step2);