FlowModal.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586
  1. import React, { useEffect, useState, useRef, useMemo, memo } from 'react';
  2. import {
  3. Modal,
  4. Input,
  5. Select,
  6. List,
  7. Row,
  8. Col,
  9. Table,
  10. message,
  11. Steps,
  12. Space,
  13. Button,
  14. Popover,
  15. Cascader,
  16. AutoComplete,
  17. } from 'antd';
  18. import Flow from '@/components/Flow/index';
  19. import { connect } from 'dva';
  20. import { GetTokenFromUrl, getToken } from '@/utils/utils';
  21. import { MODELS, useXFlowApp, useModelAsync } from '@antv/xflow';
  22. import { CheckOutlined } from '@ant-design/icons';
  23. import {
  24. queryDelPurchaseExcel,
  25. queryDingInstanceDetail,
  26. queryRecordSheet,
  27. queryVserionByNode,
  28. } from '@/services/boom';
  29. import { async } from '@antv/x6/lib/registry/marker/async';
  30. import VersionModal from './VersionModal';
  31. import AuditFlow from './AuditFlow';
  32. const { Option } = Select;
  33. const { Step } = Steps;
  34. const { TextArea } = Input;
  35. const localData = JSON.parse(localStorage.ggDetaiData || '{}');
  36. const PAGE_SIZE = 8;
  37. // 提交
  38. function FlowModal(props) {
  39. let token = getToken();
  40. const SELECT_TYPE = {
  41. NAME: '0',
  42. TYPE: '1',
  43. CREATOR: '2',
  44. };
  45. const {
  46. visible,
  47. version,
  48. onClose,
  49. onChangeVersion,
  50. onDelVersion,
  51. form,
  52. loading,
  53. flowDetail,
  54. dispatch,
  55. isOut,
  56. onCommit,
  57. commitLoading,
  58. currentUser,
  59. typeOptions,
  60. userList,
  61. } = props;
  62. const [data, setData] = useState([]);
  63. const [showData, setShowData] = useState([]);
  64. const [nodeLoading, setNodeLoading] = useState(false);
  65. const [pageSize, setPageSize] = useState(PAGE_SIZE);
  66. const [stepsData, setStepsData] = useState([]);
  67. const [versionVisible, setVersionVisible] = useState(false);
  68. const [selectType, setSelectType] = useState(SELECT_TYPE.NAME);
  69. const [inputValue, setInputValue] = useState('');
  70. const graphData = useMemo(() => {
  71. if (!flowDetail) return;
  72. let nodes = flowDetail.nodes?.map(item => ({
  73. ...item,
  74. isCheck: item.Id == version.template_node_id,
  75. }));
  76. return {
  77. nodes,
  78. edges: flowDetail.edges,
  79. };
  80. }, [flowDetail, version.template_node_id]);
  81. useEffect(() => {
  82. if (!visible) updateSteps([]);
  83. }, [visible, version]);
  84. useEffect(() => {
  85. if (stepsData.length <= 0) {
  86. setPageSize(PAGE_SIZE);
  87. } else {
  88. setPageSize(PAGE_SIZE - stepsData.length);
  89. }
  90. }, [stepsData]);
  91. const handleSelectNode = async args => {
  92. let res;
  93. const id = args.nodeId || args.nodeIds[0];
  94. if (!id) return;
  95. let node = graphData.nodes.find(item => item.id == id);
  96. setNodeLoading(true);
  97. try {
  98. res = await queryVserionByNode({ template_node_id: node.Id });
  99. let data = [];
  100. if (!res.data.excel_version_tree) setData([]);
  101. res.data.excel_version_tree?.map(arr => {
  102. if (res.data.flow_id) {
  103. data = [...data, { ...arr, flow_id: res.data.flow_id }];
  104. } else {
  105. data = [...data, arr];
  106. }
  107. });
  108. data.sort((a, b) => b.id - a.id);
  109. data.forEach((item, id) => {
  110. //解决key报错问题
  111. data[id].key = `${id}-${item.name}`;
  112. item.isParent = true;
  113. });
  114. console.log(data);
  115. setData(data);
  116. } catch (error) {
  117. console.log(error);
  118. }
  119. setNodeLoading(false);
  120. updateSteps([]);
  121. };
  122. const updateSteps = (data, curNodeId) => {
  123. let newData = [];
  124. let set = new Set();
  125. data.forEach(item => set.add(item.template_node_id));
  126. let list = [...set];
  127. if (set.has(curNodeId)) {
  128. set.delete(curNodeId);
  129. list = [curNodeId, ...set];
  130. }
  131. let dataList = list.map(template_node_id => {
  132. let itemDataList = data.filter(item => item.template_node_id == template_node_id);
  133. let curid = 3;
  134. let status = 'process';
  135. itemDataList.forEach(item => {
  136. if (item.audit_status != 3 && item.node_id <= curid) curid = item.node_id - 1;
  137. if (item.audit_status == 2) status = 'error';
  138. });
  139. let curNode = flowDetail.nodes.find(item => item.Id == itemDataList[0].template_node_id);
  140. const seqList = itemDataList[0].FlowInfo.FlowNodes.filter(
  141. item => item.template_node_id == template_node_id
  142. ).sort((a, b) => a.seq - b.seq);
  143. let obj = {
  144. status,
  145. current: curid,
  146. list: seqList,
  147. name: curNode?.label || itemDataList[0].FlowInfo.name,
  148. };
  149. // itemDataList.forEach((itemData, idx) => {
  150. // if (idx >= obj.list.length) return;
  151. // obj.list[idx].auditor = itemData.AuthorInfo.CName;
  152. // });
  153. return obj;
  154. });
  155. setStepsData(dataList);
  156. };
  157. // const updateSteps = async (data, curNodeId) => {
  158. // const dataList = [];
  159. // for (let i = 0; i < data.length; i++) {
  160. // let curNode = flowDetail.nodes.find(item => item.Id == data[i].template_node_id);
  161. // console.log(curNode);
  162. // const response = await queryDingInstanceDetail({
  163. // process_instance_id: data[i].ding_instance_id, //创建表单成功返回的id
  164. // });
  165. // if (response) {
  166. // const processInstance = response.data?.process_instance;
  167. // let data = {
  168. // processCode: '',
  169. // deptId: '14169890',
  170. // tasks: [],
  171. // // userId: '16569001414345099',
  172. // // deptId: currentUser.DingDepId || getCurrentUser()?.DingDepId,
  173. // userId: currentUser.DingUserId || getCurrentUser()?.DingUserId,
  174. // formComponentValues: [],
  175. // activityId: '',
  176. // cc_userids: [],
  177. // status: 'undefined',
  178. // name: curNode?.label || '未知节点',
  179. // };
  180. // if (processInstance?.tasks && processInstance.tasks?.length > 0) {
  181. // // let item = flowDetail.nodes.find(item => item.Id == version.template_node_id);
  182. // // if (!item) return data;
  183. // const { tasks, form_component_values, cc_userids } = processInstance;
  184. // data.processCode = curNode.process_code;
  185. // data.activityId = tasks[tasks.length - 1]?.activity_id;
  186. // data.tasks = tasks || [];
  187. // data.cc_userids = cc_userids;
  188. // data.formComponentValues = form_component_values?.filter(curNode => curNode.name);
  189. // }
  190. // dataList.push(data);
  191. // }
  192. // }
  193. // console.log(dataList);
  194. // setStepsData(dataList);
  195. // };
  196. const handleChangeClick = item => {
  197. let file = isOut ? 'newList' : 'detail';
  198. let type = item.flow_id ? '/queryAuditRecord' : '/queryAuditExcel';
  199. console.log(`${file}${type}`, item);
  200. dispatch({
  201. type: `${file}${type}`,
  202. payload: {
  203. excel_id: item.id,
  204. pageSize: 100,
  205. },
  206. callback: res => {
  207. updateSteps(res, item.template_node_id);
  208. },
  209. });
  210. };
  211. const columns = useMemo(() => {
  212. return [
  213. {
  214. title: '名称',
  215. width: '25%',
  216. render: item => (
  217. <span style={{ color: item.audit_status != 0 ? '#9b9b9b' : '' }}>
  218. {item.id == version.id && !item.isParent && (
  219. <CheckOutlined style={{ marginRight: 10 }} />
  220. )}
  221. {item.version_no && !item.children?.length
  222. ? `${item.version_name}.${item.version_no}`
  223. : item.version_name}
  224. </span>
  225. ),
  226. },
  227. {
  228. title: '创建人',
  229. width: '15%',
  230. render: item => {
  231. return (
  232. item.isParent && (
  233. <span>{userList.find(cur => cur.ID == item.author)?.CName || '-'}</span>
  234. )
  235. );
  236. },
  237. },
  238. {
  239. title: '分类',
  240. width: '15%',
  241. render: item => {
  242. return (
  243. item.isParent && (
  244. <span>{typeOptions.find(cur => cur.id == item.classify_id)?.name}</span>
  245. )
  246. );
  247. },
  248. },
  249. {
  250. title: '状态',
  251. width: '15%',
  252. render: item => {
  253. if (!item.flow_id && item.isParent) return;
  254. let style = { color: getColor(item) };
  255. let txt = '';
  256. let dom = '';
  257. switch (item.audit_status) {
  258. case 0:
  259. txt = '未提交';
  260. break;
  261. case 1:
  262. txt = '待审批';
  263. break;
  264. case 2:
  265. txt = '已拒绝';
  266. break;
  267. case 3:
  268. txt = '已通过';
  269. break;
  270. case 4:
  271. txt = '已提交';
  272. break;
  273. }
  274. if (item.status == 1) txt = '已失效';
  275. // 显示拒绝原因
  276. if (item.audit_comment) {
  277. dom = (
  278. <Popover content={item.audit_comment} title="原因">
  279. {txt}
  280. </Popover>
  281. );
  282. } else {
  283. dom = txt;
  284. }
  285. return item.audit_status != 0 ? (
  286. <Button onClick={() => onDelVersion(item)}>{dom}</Button>
  287. ) : (
  288. <span style={style}>{dom}</span>
  289. );
  290. },
  291. },
  292. {
  293. title: '操作',
  294. width: '20%',
  295. render: item =>
  296. (item.flow_id || !item.isParent) &&
  297. item.id != version.id && (
  298. <Space>
  299. <a
  300. onClick={() => {
  301. console.log(item);
  302. onChangeVersion(item);
  303. onClose();
  304. }}
  305. >
  306. 加载
  307. </a>
  308. {item.audit_status == 0 &&
  309. item.author == currentUser.ID && ( //自己创建的&&未提交的清单自己可以删除
  310. <a
  311. onClick={() => {
  312. onDelVersion({ excel_id: item.id });
  313. }}
  314. >
  315. 删除
  316. </a>
  317. )}
  318. </Space>
  319. ),
  320. },
  321. ];
  322. }, [version]);
  323. const childColumns = useMemo(() => {
  324. return [
  325. {
  326. title: '名称',
  327. width: '50%',
  328. render: item => (
  329. <span style={{ color: item.audit_status != 0 ? '#9b9b9b' : '' }}>
  330. {item.id == version.id && !item.isParent && (
  331. <CheckOutlined style={{ marginRight: 10 }} />
  332. )}
  333. {item.version_no && !item.children?.length
  334. ? `${item.version_name}.${item.version_no}`
  335. : item.version_name}
  336. </span>
  337. ),
  338. },
  339. {
  340. title: '状态',
  341. render: item => {
  342. if (!item.flow_id && item.isParent) return;
  343. let style = { color: getColor(item) };
  344. let txt = '';
  345. let dom = '';
  346. switch (item.audit_status) {
  347. case 0:
  348. txt = '未提交';
  349. break;
  350. case 1:
  351. txt = '待审批';
  352. break;
  353. case 2:
  354. txt = '已拒绝';
  355. break;
  356. case 3:
  357. txt = '已通过';
  358. break;
  359. case 4:
  360. txt = '已提交';
  361. break;
  362. }
  363. if (item.status == 1) txt = '已失效';
  364. // 显示拒绝原因
  365. if (item.audit_comment) {
  366. dom = (
  367. <Popover content={item.audit_comment} title="原因">
  368. {txt}
  369. </Popover>
  370. );
  371. } else {
  372. dom = txt;
  373. }
  374. return item.audit_status != 0 ? (
  375. <Button onClick={() => onDelVersion(item)}>{dom}</Button>
  376. ) : (
  377. <span style={style}>{dom}</span>
  378. );
  379. },
  380. },
  381. {
  382. title: '操作',
  383. width: '30%',
  384. render: item =>
  385. (item.flow_id || !item.isParent) &&
  386. item.id != version.id && (
  387. <Space>
  388. <a
  389. onClick={() => {
  390. console.log(item);
  391. onChangeVersion(item);
  392. onClose();
  393. }}
  394. >
  395. 加载
  396. </a>
  397. {item.audit_status == 0 &&
  398. item.author == currentUser.ID && ( //自己创建的&&未提交的清单自己可以删除
  399. <a
  400. onClick={() => {
  401. onDelVersion({ excel_id: item.id });
  402. }}
  403. >
  404. 删除
  405. </a>
  406. )}
  407. </Space>
  408. ),
  409. },
  410. ];
  411. }, [version]);
  412. const onChange = value => {
  413. updateSteps([]);
  414. };
  415. useEffect(() => {
  416. if (!inputValue) {
  417. setShowData(data);
  418. return;
  419. }
  420. let resultData = [...data];
  421. switch (selectType) {
  422. case SELECT_TYPE.NAME:
  423. resultData = data.filter(item => item.version_name.includes(inputValue));
  424. break;
  425. case SELECT_TYPE.TYPE:
  426. const classify = typeOptions.find(item => item.name.includes(inputValue));
  427. if (classify) {
  428. resultData = data.filter(item => item.classify_id == classify.id);
  429. }
  430. break;
  431. case SELECT_TYPE.CREATOR:
  432. resultData = data.filter(item => item.AuthorInfo?.CName.includes(inputValue));
  433. break;
  434. }
  435. setShowData(resultData);
  436. }, [inputValue, data]);
  437. return (
  438. <>
  439. <Modal
  440. confirmLoading={loading}
  441. destroyOnClose
  442. title="流程图"
  443. visible={visible}
  444. onCancel={onClose}
  445. footer={false}
  446. width="98%"
  447. >
  448. <Row gutter={8}>
  449. <Col span={14}>
  450. <Flow meta={{ type: 'view' }} flowDetail={graphData} onSelectNode={handleSelectNode} />
  451. </Col>
  452. <Col span={10}>
  453. <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
  454. <div style={{ fontSize: '16px' }}>清单列表</div>
  455. {isOut && (
  456. <Button
  457. type="primary"
  458. style={{ float: 'right', marginBottom: '10px' }}
  459. onClick={() => setVersionVisible(true)}
  460. >
  461. 新建清单
  462. </Button>
  463. )}
  464. </div>
  465. <div style={{ display: 'flex' }}>
  466. <Select
  467. defaultValue={selectType}
  468. style={{ width: '30%' }}
  469. onChange={value => {
  470. setSelectType(value);
  471. setInputValue('');
  472. }}
  473. >
  474. <Option value={SELECT_TYPE.NAME}>名称:</Option>
  475. <Option value={SELECT_TYPE.TYPE}>分类:</Option>
  476. <Option value={SELECT_TYPE.CREATOR}>创建人:</Option>
  477. </Select>
  478. <Input
  479. placeholder="请输入"
  480. value={inputValue}
  481. onChange={e => setInputValue(e.target.value)}
  482. />
  483. <Button type="primary" style={{ marginLeft: '10px' }}>
  484. 搜索
  485. </Button>
  486. </div>
  487. <div style={{ width: '100%', marginTop: '10px' }}>
  488. <Table
  489. style={{ maxHeight: '80%' }}
  490. columns={columns}
  491. dataSource={showData} //data
  492. loading={nodeLoading}
  493. bordered={false}
  494. pagination={{ position: ['none', 'none'], pageSize: 999, onChange }}
  495. scroll={{ y: 65 * pageSize }}
  496. // childrenColumnName="none"
  497. // expandable={{
  498. // expandedRowRender: record => (
  499. // <Table
  500. // columns={columns}
  501. // dataSource={record.children}
  502. // pagination={{ position: ['none', 'none'] }}
  503. // />
  504. // ),
  505. // rowExpandable: record => record.children?.length > 0,
  506. // }}
  507. />
  508. </div>
  509. {stepsData.map((item, idx) => (
  510. <div key={`${item.name}_${idx}`} style={{ marginBottom: '20px' }}>
  511. <div style={{ marginBottom: '4px' }}>{item.name}</div>
  512. <Steps size="small" current={item.current} status={item.status}>
  513. {item.list.map(node => (
  514. <Step
  515. key={`${node.id}_${node.node}`}
  516. title={node.node}
  517. description={`审批人:${node.AuditorUser?.CName || '-'}`}
  518. />
  519. ))}
  520. </Steps>
  521. </div>
  522. ))}
  523. </Col>
  524. </Row>
  525. </Modal>
  526. <VersionModal
  527. typeOptions={typeOptions}
  528. visible={versionVisible}
  529. onClose={() => setVersionVisible(false)}
  530. onOk={values => {
  531. onCommit?.(values, null, () => {
  532. setVersionVisible(false);
  533. });
  534. }}
  535. loading={commitLoading}
  536. />
  537. </>
  538. );
  539. }
  540. const getColor = item => {
  541. let color = '';
  542. switch (item.audit_status) {
  543. case 2:
  544. // 审批拒绝
  545. color = '#f5222d';
  546. break;
  547. case 3:
  548. // 审批成功
  549. color = '#7cb305';
  550. break;
  551. case 4:
  552. // 历史清单
  553. color = '#9b9b9b';
  554. break;
  555. default:
  556. break;
  557. }
  558. return color;
  559. };
  560. export default connect(({ loading, user }) => ({
  561. loading,
  562. currentUser: user.currentUser,
  563. userList: user.list,
  564. }))(FlowModal);
  565. // export default FlowModal;