CommitAuditModal.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562
  1. import React, { useEffect, useState, useRef, useMemo, useCallback } from 'react';
  2. import '@ant-design/compatible/assets/index.css';
  3. import {
  4. Modal,
  5. Input,
  6. Select,
  7. message,
  8. Cascader,
  9. Form,
  10. Tabs,
  11. Row,
  12. Col,
  13. Empty,
  14. Button,
  15. Steps,
  16. Popover,
  17. } from 'antd';
  18. import { PlusOutlined } from '@ant-design/icons';
  19. import { connect } from 'dva';
  20. import { isArray, result, set } from 'lodash';
  21. import { useForm } from 'rc-field-form';
  22. import { async } from '@antv/x6/lib/registry/marker/async';
  23. import AuditDetailed from './AuditDetailed';
  24. import AuditFlow from './AuditFlow';
  25. import { queryDingSchema, queryProcessFlows } from '@/services/boom';
  26. import { Form as Form3x } from '@ant-design/compatible';
  27. import { getCurrentUser } from '@/utils/authority';
  28. import DDCode from '@/components/DDComponents/DDCode';
  29. import { uploadFile, queryUserListByRoleID } from '@/services/boom';
  30. import ApprovalProcess from './ApprovalProcess';
  31. const { TextArea } = Input;
  32. const { Option } = Select;
  33. const { TabPane } = Tabs;
  34. const { Step } = Steps;
  35. // 提交
  36. function CommitAuditModal(props) {
  37. const {
  38. dispatch,
  39. visible,
  40. onClose,
  41. onOk,
  42. loading,
  43. version,
  44. versionList,
  45. flowDetail,
  46. currentUser,
  47. luckysheet,
  48. userList,
  49. } = props;
  50. const [auditId, setAuditId] = useState();
  51. const [data, setData] = useState([]);
  52. const [length, setLength] = useState(1);
  53. const [formData, setFromData] = useState({});
  54. const [auditList, setAuditList] = useState([]); //用于创建Tabs表单
  55. const [formComponentValues, setFormComponentValues] = useState({}); //用于创建Tabs表单
  56. const [form] = Form.useForm();
  57. const [approvalProcess, setApprovalProcess] = useState({});
  58. const [selectUserList, setSelectUserList] = useState([]);
  59. const [curNodeIdx, setCurNodeIdx] = useState(-1);
  60. useEffect(() => {
  61. if (!visible) return;
  62. const { edges, nodes } = flowDetail;
  63. let Id = version.template_node_id;
  64. const currentId = flowDetail.nodes.find?.(item => item.Id == Id)?.node_id;
  65. const data = treeData(currentId);
  66. if (data.length <= 0) setAuditId(currentId);
  67. setData(data);
  68. }, [auditId, version.template_node_id, visible]);
  69. useEffect(() => {
  70. form.resetFields();
  71. setAuditList([]);
  72. }, [visible]);
  73. const treeData = currentId => {
  74. const list = getNextNodes(currentId, 'custom-circle');
  75. const fun = nodes => {
  76. const re = nodes?.forEach((item, idx) => {
  77. const data = getNextNodes(item.Id, 'custom-circle');
  78. if (data || data.length > 0) list.push(...data);
  79. fun(data);
  80. });
  81. };
  82. fun(list);
  83. const fun2 = list => {
  84. const parents = list.filter(item => list.findIndex(node => node.Id == item.parentId) == -1);
  85. let translator = (parents, children) => {
  86. setLength(length + 1);
  87. parents.forEach(parent => {
  88. children.forEach((current, index) => {
  89. if (current.parentId === parent.Id) {
  90. let temp = JSON.parse(JSON.stringify(children));
  91. temp.splice(index, 1);
  92. translator([current], temp);
  93. if (!parent.children.find(item => item.Id == current.Id))
  94. parent.children.push(current);
  95. }
  96. });
  97. });
  98. };
  99. translator(parents, list);
  100. return parents;
  101. };
  102. return fun2(list);
  103. };
  104. const currentNodeId = useMemo(() => {
  105. let Id = version.template_node_id;
  106. setAuditId(currentNodeId);
  107. return flowDetail.nodes.find?.(item => item.Id == Id)?.node_id;
  108. }, [flowDetail, version]);
  109. /**
  110. *
  111. * @param {*} currentId 当前节点
  112. * @param {*} type 下一个节点的类型 custom-circle: 审批节点 custom-rect: 业务节点
  113. * @returns
  114. */
  115. const getNextNodes = (currentId, type) => {
  116. const { edges, nodes } = flowDetail;
  117. if (!currentId) return [];
  118. //删除虚线通向的节点
  119. // let targetIds = edges
  120. // .filter(edge => {
  121. // let line = edge.attrs?.line?.strokeDasharray?.split(' ');
  122. // return edge.source.cell == currentId && line && line[0] == '0';
  123. // })
  124. // .map(item => item.target.cell);
  125. let targetIds = edges
  126. .filter(edge => edge.source.cell == currentId)
  127. .map(item => item.target.cell);
  128. edges.filter(edge => edge.source.cell == currentId);
  129. let auditNodes = nodes.filter(node => {
  130. if (type && node.name != type) {
  131. return false;
  132. }
  133. return targetIds.indexOf(node.id) != -1;
  134. });
  135. const result = auditNodes.map(item => {
  136. return {
  137. label: item.label,
  138. value: item.Id,
  139. Id: item.node_id,
  140. parentId: currentId,
  141. children: [],
  142. };
  143. });
  144. return result || [];
  145. };
  146. const changeAudit = id => {
  147. let node = flowDetail.nodes.find?.(item => item.Id == id);
  148. setAuditId(node.node_id);
  149. };
  150. const onChange = value => {
  151. changeAudit(value[value.length - 1]);
  152. setAuditListFun();
  153. };
  154. const getReComputeAudit = (items, changedValues) => {
  155. const id = Object.keys(changedValues)[0];
  156. const formItem = items?.find(item => item.props.id == id);
  157. if (formItem && formItem.props?.required) return true;
  158. return false;
  159. };
  160. //填写表单实时计算审批流程
  161. const advanceSubmit = async () => {
  162. console.log('重重新计算审批流程');
  163. var fieldsValue = await form.validateFields();
  164. let hasFlowId = true; //是否都绑定审批节点
  165. let result = Object.values(fieldsValue)
  166. .map(item => {
  167. if (item && Array.isArray(item)) return item;
  168. })
  169. .filter(item => item);
  170. const formList = await getFromData(result);
  171. let params = {
  172. desc: fieldsValue.desc,
  173. // 审核流程id
  174. flow_id: 0,
  175. node_level_id: 0,
  176. id: version.id,
  177. project_id: version.project_id,
  178. cur_template_node_id: version.template_node_id * 1, // 当前节点
  179. next_template_node_id: 0, // 审核完成后的业务节点
  180. template_node_id: null, // 将要流转的节点审批节点
  181. flow_path: null, //审批节点数组
  182. // 模板id.一致就行
  183. template_id: version.template_id,
  184. cur_template_id: version.template_id,
  185. next_template_id: version.template_id,
  186. form_list: formList,
  187. };
  188. const ddd = {
  189. '778': [
  190. { type: 'user', value: 448, origin: 0 },
  191. { type: 'role', value: 82, origin: 0 },
  192. { type: 'role', value: 61, origin: 0 },
  193. ],
  194. '791': [
  195. { type: 'user', value: 448, origin: 0 },
  196. { type: 'role', value: 82, origin: 0 },
  197. { type: 'role', value: 61, origin: 0 },
  198. ],
  199. '792': [
  200. { type: 'user', value: 448, origin: 0 },
  201. { type: 'role', value: 82, origin: 0 },
  202. { type: 'role', value: 29, origin: 0 },
  203. ],
  204. };
  205. dispatch({
  206. type: 'detail/advanceSubmitNextNode',
  207. payload: params, //values,
  208. callback: data => {
  209. if (data) {
  210. setApprovalProcess(data);
  211. }
  212. console.log('=======================================', data);
  213. },
  214. });
  215. };
  216. //处理tabs页
  217. const setAuditListFun = async () => {
  218. var fieldsValue = await form.validateFields();
  219. let addAuditList = [];
  220. let result = Object.values(fieldsValue)
  221. .map(item => {
  222. if (item && Array.isArray(item)) return item;
  223. })
  224. .filter(item => item)
  225. .flat(Infinity);
  226. let nodeList = [...new Set(result)]
  227. .map(Id => {
  228. return flowDetail.nodes.find?.(item => item.Id == Id);
  229. })
  230. .filter(item => item);
  231. let flowIds = [...new Set(nodeList.map(item => item.flow_id))].join(',');
  232. let data = await queryProcessFlows({ ids: flowIds });
  233. if (data && data?.length > 0) {
  234. let newlist = nodeList.map(node => {
  235. let curData = data.find(item => item.id == node.flow_id);
  236. let newItem = {
  237. name: curData?.name,
  238. nodeId: node.Id,
  239. items: JSON.parse(curData.form_json),
  240. };
  241. return newItem;
  242. });
  243. addAuditList = [...addAuditList, ...newlist];
  244. }
  245. addAuditList.forEach((item, index) => {
  246. let Components = Form3x.create({
  247. onValuesChange: (props, changedValues, allValues) => {
  248. const { items } = props;
  249. formComponentValues[item.nodeId] = items
  250. .map(item => {
  251. const itemProps = item.props;
  252. let val = allValues[itemProps.id];
  253. if (!itemProps.label || val === '') return;
  254. if (val instanceof Object) {
  255. return {
  256. name: itemProps.label,
  257. id: itemProps.id,
  258. value: [...val],
  259. };
  260. } else if (allValues[itemProps.id]) {
  261. return {
  262. name: itemProps.label,
  263. id: itemProps.id,
  264. value: [allValues[itemProps.id]] || undefined,
  265. };
  266. }
  267. })
  268. .filter(item => item);
  269. if (getReComputeAudit(items, changedValues)) advanceSubmit();
  270. console.log('==================================', { ...formComponentValues });
  271. setFormComponentValues({ ...formComponentValues });
  272. },
  273. })(AuditDetailed);
  274. item.FormComponents = <Components items={item.items} />;
  275. });
  276. setAuditList(addAuditList);
  277. };
  278. const getFromData = async idList => {
  279. const data = formComponentValues;
  280. const result = [];
  281. //获取流转节点的层级关系
  282. let len = 0;
  283. let list = [];
  284. idList.forEach(item => {
  285. if (len < item.length) len = item.length;
  286. });
  287. for (let i = 0; i < len; i++) {
  288. idList.forEach(item => {
  289. if (item && item[i]) list.push(item[i]);
  290. });
  291. }
  292. let firstList = [...new Set(list)];
  293. // let attachment = await upload();
  294. firstList.forEach(id => {
  295. let approvalNode = flowDetail.nodes.find?.(item => item.Id == id);
  296. let values = data[approvalNode.Id] || [];
  297. const audit_list = approvalProcess[approvalNode.Id]?.map(item => {
  298. if (item[0].type == 'role') return item[0].nowValue;
  299. return item[0].value;
  300. });
  301. const formItem = {
  302. flow_id: approvalNode.flow_id,
  303. template_node_id: approvalNode.Id,
  304. formComponentValues: [...values], //{ name: '附件', value: JSON.stringify(attachment) }
  305. audit_list: audit_list || [],
  306. };
  307. result.push(JSON.stringify(formItem));
  308. });
  309. return result;
  310. };
  311. const getFlowPath = node => {
  312. //[134, 135]
  313. let itemData = {};
  314. const Function = (curId, index) => {
  315. if (!curId) return;
  316. let data = {};
  317. let approvalNode = flowDetail.nodes.find?.(item => item.Id == curId);
  318. data.template_id = version.template_id;
  319. data.flow_id = approvalNode?.flow_id || 0;
  320. data.node_level_id = approvalNode?.flow_id ? 1 : 0;
  321. data.template_node_id = approvalNode?.Id;
  322. index++;
  323. if (approvalNode?.Id) {
  324. if (!approvalNode?.flow_id) {
  325. hasFlowId = false;
  326. }
  327. }
  328. const res = Function(node[index], index);
  329. if (res) {
  330. data.flow_path = [res];
  331. }
  332. return data;
  333. };
  334. itemData = Function(node[0], 0);
  335. return itemData;
  336. };
  337. const onFinish = async () => {
  338. console.log(approvalProcess);
  339. const isOk = Object.values(approvalProcess).every(item => {
  340. console.log(item);
  341. return item.every(cur => {
  342. if (cur[0].type == 'role') return cur[0].nowValue;
  343. return true;
  344. });
  345. });
  346. if (!isOk) {
  347. message.error('请选择审批人。');
  348. return;
  349. }
  350. var fieldsValue = await form.validateFields();
  351. let hasFlowId = true; //是否都绑定审批节点
  352. const getFlowPath = node => {
  353. //[134, 135]
  354. let itemData = {};
  355. const Function = (curId, index) => {
  356. if (!curId) return;
  357. let data = {};
  358. let approvalNode = flowDetail.nodes.find?.(item => item.Id == curId);
  359. data.template_id = version.template_id;
  360. data.flow_id = approvalNode?.flow_id || 0;
  361. data.node_level_id = approvalNode?.flow_id ? 1 : 0;
  362. data.template_node_id = approvalNode?.Id;
  363. index++;
  364. if (approvalNode?.Id) {
  365. if (!approvalNode?.flow_id) {
  366. hasFlowId = false;
  367. }
  368. }
  369. const res = Function(node[index], index);
  370. if (res) {
  371. data.flow_path = [res];
  372. }
  373. return data;
  374. };
  375. itemData = Function(node[0], 0);
  376. return itemData;
  377. };
  378. let result = Object.values(fieldsValue)
  379. .map(item => {
  380. if (item && Array.isArray(item)) return item;
  381. })
  382. .filter(item => item);
  383. let serviceNode = flowDetail.nodes.find?.(item => item.Id == fieldsValue.next_template_node_id);
  384. if (!serviceNode) {
  385. message.error('请选择需要流转的业务节点。');
  386. return;
  387. }
  388. const flowPath = result.map(item => getFlowPath(item));
  389. const formList = await getFromData(result);
  390. let params = {
  391. desc: fieldsValue.desc,
  392. // 审核流程id
  393. // flow_id: approvalNode?.flow_id || 0,
  394. // node_level_id: approvalNode?.flow_id ? 1 : 0,
  395. id: version.id,
  396. project_id: version.project_id,
  397. cur_template_node_id: version.template_node_id * 1, // 当前节点
  398. next_template_node_id: serviceNode.Id * 1, // 审核完成后的业务节点
  399. // template_node_id: result[0][0], // 将要流转的节点审批节点
  400. // flow_path:flow_path, //审批节点数组
  401. // 模板id.一致就行
  402. template_id: version.template_id,
  403. cur_template_id: version.template_id,
  404. next_template_id: version.template_id,
  405. };
  406. if (result.length <= 0) {
  407. //直接走业务节点
  408. } else if (result.length <= 1 && result[0]?.length <= 1) {
  409. //单个审批节点
  410. let approvalNode = flowDetail.nodes.find?.(item => item.Id == result[0][0]);
  411. params.flow_id = approvalNode?.flow_id || 0;
  412. params.node_level_id = approvalNode?.flow_id ? 1 : 0;
  413. params.template_node_id = result[0][0]; // 将要流转的节点审批节点
  414. params.form_list = formList; //创建钉钉表单所需数据
  415. if (approvalNode?.Id) {
  416. if (!approvalNode?.flow_id) {
  417. hasFlowId = false;
  418. }
  419. }
  420. } else {
  421. //多节点审批
  422. params.template_node_id = result[0][0]; // 将要流转的节点审批节点
  423. params.flow_path = flowPath;
  424. params.form_list = formList; //创建钉钉表单所需数据
  425. }
  426. if (!hasFlowId) {
  427. message.error('当前存在审批节点未绑定审批流程!请联系管理员。');
  428. return;
  429. }
  430. onOk(params);
  431. };
  432. const CascaderNode = index => {
  433. return (
  434. <Form.Item
  435. labelCol={{ span: 7 }}
  436. wrapperCol={{ span: 15 }}
  437. label={`审批节点${index + 1}`}
  438. name={`circle${index}`}
  439. key={`circle${index}`}
  440. >
  441. <Cascader style={{ width: '100%' }} options={data} onChange={onChange} />
  442. </Form.Item>
  443. );
  444. };
  445. const upload = async () => {
  446. let blob = await luckysheet.current.getExcelBolb();
  447. let formData = new FormData();
  448. formData.append('userid', currentUser.DingUserId);
  449. formData.append('file', new File([blob], `${version.version_name}_${version.version_no}.xlsx`));
  450. try {
  451. let res = await uploadFile(formData);
  452. let data = JSON.parse(res.dentry);
  453. return [
  454. {
  455. spaceId: String(data.spaceId),
  456. fileName: data.name,
  457. fileSize: String(data.spaceId),
  458. fileType: data.extension,
  459. fileId: data.id,
  460. },
  461. ];
  462. } catch (error) {
  463. message.error('附件上传失败');
  464. }
  465. };
  466. return (
  467. <Modal
  468. confirmLoading={loading}
  469. destroyOnClose
  470. title="提交流转目标"
  471. width={1000}
  472. visible={visible}
  473. onCancel={() => {
  474. setAuditId();
  475. onClose();
  476. }}
  477. onOk={onFinish}
  478. >
  479. <Form form={form}>
  480. {data.map((item, idx) => (data.length == 1 ? CascaderNode('') : CascaderNode(idx)))}
  481. <Form.Item
  482. labelCol={{ span: 7 }}
  483. wrapperCol={{ span: 15 }}
  484. label="业务节点"
  485. name="next_template_node_id"
  486. >
  487. <Select style={{ width: '100%' }}>
  488. {getNextNodes(data.length < 0 ? currentNodeId : auditId, 'custom-rect').map(item => (
  489. <Option key={item.value}>{item.label}</Option>
  490. ))}
  491. </Select>
  492. </Form.Item>
  493. <Form.Item labelCol={{ span: 7 }} wrapperCol={{ span: 15 }} label="备注信息" name="desc">
  494. <Input.TextArea />
  495. </Form.Item>
  496. {/* <Form.Item labelCol={{ span: 7 }} wrapperCol={{ span: 15 }} label="授权码" name="ddCode">
  497. <DDCode />
  498. <Button onClick={upload}>上传文件</Button>
  499. </Form.Item> */}
  500. </Form>
  501. <Tabs defaultActiveKey="1">
  502. {auditList.map((item, idx) => (
  503. <TabPane tab={item.name} key={`${idx}_${item.title}`}>
  504. <Row>
  505. <Col span={17}>{item.FormComponents}</Col>
  506. <Col offset={1} span={4}>
  507. {!formComponentValues[item.nodeId] || !approvalProcess[item.nodeId] ? (
  508. <Empty description="请先填写表单" />
  509. ) : (
  510. <ApprovalProcess
  511. id={item.nodeId}
  512. approvalProcess={approvalProcess}
  513. onChange={setApprovalProcess}
  514. />
  515. )
  516. // <AuditFlow
  517. // processCode={item.formCode}
  518. // formComponentValues={formComponentValues[item.nodeId]}
  519. // direction={'vertical'}
  520. // deptId={'14237557'}
  521. // userId={currentUser.DingUserId || getCurrentUser()?.DingUserId}
  522. // />
  523. }
  524. </Col>
  525. </Row>
  526. </TabPane>
  527. ))}
  528. </Tabs>
  529. </Modal>
  530. );
  531. }
  532. export default connect(({ xflow, detail, user }) => ({
  533. flowDetail: xflow.flowDetail,
  534. versionList: detail.versionList,
  535. currentUser: user.currentUser,
  536. userList: user.list,
  537. }))(CommitAuditModal);