CommitAuditModal.js 14 KB

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