Index.js 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173
  1. import React, { useEffect, useState, useRef, useMemo } from 'react';
  2. import { UnorderedListOutlined, PlusOutlined } from '@ant-design/icons';
  3. import { Button, Modal, message, Alert, Avatar, Spin, Select, Menu, Dropdown } from 'antd';
  4. import { connect } from 'dva';
  5. import styles from './Index.less';
  6. import LuckySheet from './LuckySheet';
  7. import router from 'umi/router';
  8. import AuditModal from './AuditModal';
  9. // import CommentDrawer from './CommentDrawer';
  10. import RightDrawer from './RightDrawer';
  11. import CommitModal from './CommitModal';
  12. import CompareModal from './CompareModal';
  13. import ExportModal from './ExportModal';
  14. import FlowModal from './FlowModal';
  15. import HistoryModal from './HistoryModal';
  16. import TimeNode from './TimeNode';
  17. import FilesModal from './FilesModal';
  18. import VersionModal from './VersionModal';
  19. import CommitAuditModal from './CommitAuditModal';
  20. import CommentContent from '@/components/CommentContent';
  21. import MergeModal from './MergeModal';
  22. import { GetTokenFromUrl, getToken } from '@/utils/utils';
  23. import { queryDetail, queryDingInstanceExecute } from '@/services/boom';
  24. import HistoryDrawer from './HistoryDrawer';
  25. import AuditFlow from './AuditFlow';
  26. import { getCurrentUser } from '@/utils/authority';
  27. import { async } from '@antv/x6/es/registry/marker/async';
  28. const LocalData = localStorage.luckysheet;
  29. const { Option } = Select;
  30. function Detail(props) {
  31. const {
  32. dispatch,
  33. comment,
  34. history,
  35. loading,
  36. currentUser,
  37. fileList,
  38. roleList,
  39. template,
  40. versionList,
  41. auditList,
  42. flowDetail,
  43. versionTree,
  44. match: { params },
  45. instanceDetail,
  46. } = props;
  47. const [versionTreeVisible, setVersionTreeVisible] = useState(false);
  48. const [commentVisible, setCommentVisible] = useState(false);
  49. const [mergeVisible, setMergeVisible] = useState(false);
  50. const [compareVisible, setCompareVisible] = useState(false);
  51. const [exportVisible, setExportVisible] = useState(false);
  52. const [commitVisible, setCommitVisible] = useState(false);
  53. const [auditVisible, setAuditVisible] = useState(false);
  54. const [flowVisible, setFlowVisible] = useState(false);
  55. const [versionVisible, setVersionVisible] = useState(false);
  56. const [commitAuditVisible, setCommitAuditVisible] = useState(false);
  57. const [sheet, setSheet] = useState({});
  58. const [compareList, setCompareList] = useState([]);
  59. const [edit, setEdit] = useState(false);
  60. const [isMerge, setIsMerge] = useState(false);
  61. const [version, setVersion] = useState({});
  62. const [user, setUser] = useState([]);
  63. const [updateCount, setUpdateCount] = useState({
  64. diff: 0,
  65. add: 0,
  66. });
  67. const [fileVisible, setFileVisible] = useState(false);
  68. const [exportDate, setExportData] = useState([]);
  69. const sheetRef = useRef();
  70. const sheetRef2 = useRef();
  71. const sheetRef3 = useRef();
  72. const fileRef = useRef();
  73. const userRef = useRef();
  74. const statusRef = useRef({
  75. edit: false,
  76. compare: false,
  77. });
  78. const cellPosition = useRef({});
  79. const projectId = parseInt(params.projectId);
  80. const templateId = parseInt(params.templateId);
  81. const auditDetail = useMemo(() => {
  82. let data = {
  83. processCode: '',
  84. deptId: '14169890',
  85. tasks: [],
  86. // userId: '16569001414345099',
  87. // deptId: currentUser.DingDepId || getCurrentUser()?.DingDepId,
  88. userId: currentUser.DingUserId || getCurrentUser()?.DingUserId,
  89. formComponentValues: [],
  90. activityId: '',
  91. cc_userids: [],
  92. status: version.audit_status,
  93. };
  94. if (version?.flow_id && instanceDetail?.tasks && instanceDetail.tasks?.length > 0) {
  95. let item = flowDetail.nodes.find(item => item.Id == version.template_node_id);
  96. if (!item) return data;
  97. const { tasks, form_component_values, cc_userids } = instanceDetail;
  98. data.processCode = item.process_code;
  99. data.activityId = tasks[tasks.length - 1]?.activity_id;
  100. data.tasks = tasks || [];
  101. data.cc_userids = cc_userids;
  102. data.formComponentValues = form_component_values?.filter(item => item.name);
  103. if (version.status == 1) data.status = -1;
  104. }
  105. return data;
  106. }, [instanceDetail, version]);
  107. const flow = useMemo(() => {
  108. let data = {
  109. active: 0,
  110. active_id: null,
  111. current: 0,
  112. currentNode: {},
  113. list: {
  114. FlowNodes: [],
  115. },
  116. };
  117. if (version?.flow_id && auditList?.length > 0) {
  118. let item = auditList.find(item => item.list.id == version.flow_id);
  119. if (!item) return data;
  120. // 查询当前节点
  121. let current = item.list.FlowNodes.findIndex(node => node.seq == item.active);
  122. item.current = current == -1 ? 0 : current;
  123. // 保存当前所处节点
  124. item.currentNode = item.list.FlowNodes[item.current];
  125. data = item;
  126. }
  127. return data;
  128. }, [auditList, version]);
  129. const active_audit = flow.active_audit;
  130. console.log(flow);
  131. const isAuditor = useMemo(() => {
  132. const getUserRole = () => {
  133. return flow.currentNode.auditor == currentUser.ID;
  134. // let roleID = flow.currentNode?.AuditRoleInfo?.ID;
  135. // let item = currentUser.roleList.find(role => role.ID == roleID);
  136. // if (item) return true;
  137. // return false;
  138. };
  139. return active_audit == 1 && getUserRole();
  140. }, [active_audit, flow, currentUser]);
  141. const onSave = () => {
  142. let sheet1 = compareList[0];
  143. Modal.confirm({
  144. title: '提示',
  145. content: `是否确认保存【${sheet1.version_name || sheet1.name}】`,
  146. okText: '确定',
  147. cancelText: '取消',
  148. onOk() {
  149. let sheetData = sheetRef3.current.getSheetJson().data;
  150. sheetData.forEach(sheet => {
  151. delete sheet.data;
  152. });
  153. let params = {
  154. ...sheet1,
  155. data: JSON.stringify(sheetData),
  156. };
  157. dispatch({
  158. type: 'detail/commitSheet',
  159. payload: params,
  160. callback: () => {
  161. onCompare(false);
  162. },
  163. });
  164. },
  165. });
  166. };
  167. const onCompare = async checkSheets => {
  168. if (checkSheets) {
  169. const [sheet1, sheet2] = checkSheets;
  170. sheet1.data = (
  171. await queryDetail({
  172. excel_id: sheet1.id,
  173. })
  174. ).data;
  175. sheet2.data = (
  176. await queryDetail({
  177. excel_id: sheet2.id,
  178. })
  179. ).data;
  180. setCompareList(checkSheets);
  181. statusRef.current.compare = true;
  182. } else {
  183. let index = compareList.findIndex(item => item.id == sheet.id);
  184. // 退出比对模式
  185. if (index == 0) {
  186. sheetRef3.current.toggleCompare(false);
  187. } else if (index == 1) {
  188. sheetRef2.current.toggleCompare(false);
  189. }
  190. setIsMerge(false);
  191. setCompareList([]);
  192. setSheet({
  193. ...sheet,
  194. });
  195. setUpdateCount({
  196. diff: 0,
  197. add: 0,
  198. });
  199. statusRef.current.compare = false;
  200. }
  201. setCommentVisible(false);
  202. };
  203. const renderSheetDom = (item, index) => {
  204. return (
  205. <div key={item?.id || 'temp'} className={styles.sheetItem}>
  206. <h3>{item.version_name || item?.name}</h3>
  207. <LuckySheet
  208. className={styles.sheet}
  209. ref={!index ? sheetRef3 : sheetRef2}
  210. data={item.data || null}
  211. // onClickCell={onClickCell}
  212. />
  213. </div>
  214. );
  215. };
  216. const onClickCell = (cell, position, s) => {
  217. console.log(cell);
  218. if (cell?.cid && !statusRef.current.edit) {
  219. let payload = {
  220. sheet_id: s.order || '0',
  221. excel_id: version.id,
  222. cid: cell.cid,
  223. };
  224. dispatch({
  225. type: 'detail/queryComment',
  226. payload,
  227. });
  228. cellPosition.current = {
  229. ...payload,
  230. sheet_index: (s.seq || 0) + '',
  231. };
  232. // setCommentVisible(true);
  233. }
  234. // 比对模式下双excl同步选中
  235. // if (statusRef.current.compare) {
  236. // sheetRef3.current.selectCell(position.r, position.c, s.order);
  237. // sheetRef2.current.selectCell(position.r, position.c, s.order);
  238. // }
  239. };
  240. const onCommit = (values, id) => {
  241. let currentNode = flowDetail.nodes.find?.(item => item.Id == version.template_node_id);
  242. let currentData = sheetRef.current.getSheetJson().data;
  243. let sheets = JSON.parse(JSON.stringify(currentData));
  244. if (!currentNode.muti_version) {
  245. // audit_status=4 表示为清单推进后留存的副本.不计入多清单计算
  246. let serviceVersion = versionList.find(
  247. item => item.audit_status != 4 && item.template_node_id == currentNode.Id
  248. );
  249. if (serviceVersion) {
  250. message.error(`新建清单失败!业务节点【${currentNode.label}】只能有一个清单!`);
  251. return;
  252. }
  253. }
  254. sheets.forEach(item => {
  255. delete item.data;
  256. });
  257. console.log(sheets);
  258. let params = {
  259. ...values,
  260. id: id,
  261. project_id: version.project_id,
  262. name: version.name,
  263. guid: version.guid,
  264. template_id: version.template_id,
  265. template_node_id: version.template_node_id,
  266. flow_id: version.flow_id,
  267. node_id: version.node_id,
  268. new_version: '0',
  269. audit_status: 0,
  270. data: JSON.stringify(sheets),
  271. base_id: version.id,
  272. };
  273. dispatch({
  274. type: 'detail/commitSheet',
  275. payload: params,
  276. callback: newVersion => {
  277. onCompare(false);
  278. setCommitVisible(false);
  279. setVersionVisible(false);
  280. changeVersion(newVersion);
  281. // 更新flow流程图
  282. dispatch({
  283. type: 'xflow/queryBoomFlowDetail',
  284. payload: {
  285. id: templateId,
  286. },
  287. });
  288. },
  289. });
  290. };
  291. const onUpdate = () => {
  292. if (flow.active != 0) return;
  293. let currentData = sheetRef.current.getSheetJson().data;
  294. let sheets = JSON.parse(JSON.stringify(currentData));
  295. sheets.forEach(item => {
  296. delete item.data;
  297. });
  298. console.log(sheets);
  299. let params = {
  300. ...version,
  301. data: JSON.stringify(sheets),
  302. };
  303. dispatch({
  304. type: 'detail/saveSheet',
  305. payload: params,
  306. });
  307. };
  308. const onAudit = ({ audit_comment }) => {
  309. const flowNode = flow.currentNode;
  310. dispatch({
  311. type: 'detail/approve',
  312. payload: {
  313. id: flow.active_id,
  314. project_id: projectId,
  315. audit_status: 2,
  316. flow_id: flowNode.flow_id,
  317. node_id: flowNode.seq,
  318. audit_comment,
  319. },
  320. callback: newVersion => {
  321. setAuditVisible(false);
  322. // 更新flow流程图
  323. dispatch({
  324. type: 'xflow/queryBoomFlowDetail',
  325. payload: {
  326. id: templateId,
  327. },
  328. });
  329. localStorage.excelId = newVersion.id;
  330. setVersion({
  331. ...version,
  332. flow_id: newVersion.flow_id,
  333. id: newVersion.id,
  334. });
  335. },
  336. });
  337. };
  338. const onApprove = flag => {
  339. if (!flag) {
  340. setAuditVisible(true);
  341. return;
  342. }
  343. let isSingle = false;
  344. let serviceNode;
  345. const flowNode = flow.currentNode;
  346. const getLastTemplateNodeId = data => {
  347. let result;
  348. const getFun = item => {
  349. if (item.flow_path?.length > 0) {
  350. getFun(item.flow_path[0]);
  351. } else {
  352. result = item.template_node_id;
  353. }
  354. };
  355. if (!data) return version.template_node_id;
  356. getFun(data[0]);
  357. return result;
  358. };
  359. let lastTemplateNodeId = version.template_node_id;
  360. if (version.flow_path) {
  361. //如果多节点审批 获取当前是否审批流程的最后一个审批节点
  362. let flowPathList = JSON.parse(version.flow_path);
  363. lastTemplateNodeId = getLastTemplateNodeId(flowPathList);
  364. }
  365. // 判断是否为最后一个审批节点
  366. if (
  367. lastTemplateNodeId == version.template_node_id &&
  368. flow.current == flow.list.FlowNodes.length - 1
  369. ) {
  370. serviceNode = flowDetail.nodes.find?.(item => item.Id == version.next_template_node_id);
  371. if (!serviceNode.muti_version) {
  372. //audit_status=4 表示为清单推进后留存的副本.不计入多清单计算
  373. isSingle = versionList.find(
  374. item => item.audit_status != 4 && item.template_node_id == serviceNode.Id
  375. );
  376. }
  377. }
  378. Modal.confirm({
  379. title: '提示',
  380. content: isSingle
  381. ? `节点【${serviceNode.label}】只能拥有一个清单,是否覆盖?`
  382. : `是否通过审批。`,
  383. okText: '确定',
  384. cancelText: '取消',
  385. onOk: () => {
  386. dispatch({
  387. type: 'detail/approve',
  388. payload: {
  389. id: flow.active_id,
  390. project_id: projectId,
  391. audit_status: 3,
  392. flow_id: flowNode.flow_id,
  393. node_id: flowNode.seq,
  394. },
  395. callback: newVersion => {
  396. // 更新flow流程图
  397. dispatch({
  398. type: 'xflow/queryBoomFlowDetail',
  399. payload: {
  400. id: templateId,
  401. },
  402. });
  403. // 更新审批流
  404. dispatch({
  405. type: 'detail/queryAuditList',
  406. payload: {
  407. template_id: version.template_id,
  408. template_node_id: version.template_node_id,
  409. flow_id: version.flow_id,
  410. version_id: version.version_id,
  411. },
  412. });
  413. if (flow.current == flow.list.FlowNodes.length - 1) {
  414. // 最后一个审核节点通过后 需要更新version id 不更不更,留在原地
  415. // localStorage.excelId = newVersion.id;
  416. // setVersion({
  417. // ...version,
  418. // flow_id: newVersion.flow_id,
  419. // id: newVersion.id,
  420. // });
  421. }
  422. },
  423. });
  424. },
  425. });
  426. };
  427. const onApprove1 = (flag, taskId) => {
  428. const callback = () => {
  429. // 更新flow流程图
  430. dispatch({
  431. type: 'xflow/queryBoomFlowDetail',
  432. payload: {
  433. id: templateId,
  434. },
  435. });
  436. // 更新审批流
  437. dispatch({
  438. type: 'detail/queryDingInstanceDetail',
  439. payload: {
  440. process_instance_id: version.ding_instance_id, //创建表单成功返回的id
  441. },
  442. });
  443. };
  444. const request = async () => {
  445. let param = {
  446. request: {
  447. process_instance_id: version.ding_instance_id,
  448. result: flag ? 'agree' : 'refuse',
  449. actioner_userid: currentUser.DingUserId || getCurrentUser()?.DingUserId,
  450. task_id: taskId,
  451. },
  452. };
  453. let res = await queryDingInstanceExecute(param);
  454. if (res?.data?.result) {
  455. callback();
  456. }
  457. };
  458. let tipText = '是否通过审批。';
  459. if (!flag) {
  460. tipText = '是否拒绝审批。';
  461. // request();
  462. // return;
  463. }
  464. let isSingle = false;
  465. let serviceNode;
  466. const flowNode = flow.currentNode;
  467. const getLastTemplateNodeId = data => {
  468. let result;
  469. const getFun = item => {
  470. if (item.flow_path?.length > 0) {
  471. getFun(item.flow_path[0]);
  472. } else {
  473. result = item.template_node_id;
  474. }
  475. };
  476. if (!data) return version.template_node_id;
  477. getFun(data[0]);
  478. return result;
  479. };
  480. let lastTemplateNodeId = version.template_node_id;
  481. if (version.flow_path) {
  482. //如果多节点审批 获取当前是否审批流程的最后一个审批节点
  483. let flowPathList = JSON.parse(version.flow_path);
  484. lastTemplateNodeId = getLastTemplateNodeId(flowPathList);
  485. }
  486. // 判断是否为最后一个审批节点
  487. if (
  488. lastTemplateNodeId == version.template_node_id &&
  489. flow.current == flow.list.FlowNodes.length - 1
  490. ) {
  491. serviceNode = flowDetail.nodes.find?.(item => item.Id == version.next_template_node_id);
  492. if (!serviceNode.muti_version) {
  493. //audit_status=4 表示为清单推进后留存的副本.不计入多清单计算
  494. isSingle = versionList.find(
  495. item => item.audit_status != 4 && item.template_node_id == serviceNode.Id
  496. );
  497. if (isSingle) tipText = `节点【${serviceNode.label}】只能拥有一个清单,是否覆盖?`;
  498. }
  499. }
  500. Modal.confirm({
  501. title: '提示',
  502. content: tipText,
  503. okText: '确定',
  504. cancelText: '取消',
  505. onOk: () => {
  506. request();
  507. },
  508. });
  509. };
  510. const onMerge = () => {
  511. const [sheet1, sheet2] = compareList;
  512. Modal.confirm({
  513. title: '提示',
  514. content: `是否确认将【${sheet2.version_name}】改动的内容同步至【${sheet1.version_name ||
  515. sheet1.name}】`,
  516. okText: '确定',
  517. cancelText: '取消',
  518. onOk() {
  519. // let sheet2Data = sheetRef2.current.getSheetJson()
  520. sheetRef3.current.mergeExcl(sheetRef2.current.updateCell);
  521. // setCompareList([...compareList]);
  522. // let currentData = sheetRef3.current.getSheetJson()
  523. // // 更新后重新比对
  524. // sheetRef2.current.toggleCompare(false);
  525. // sheetRef2.current.toggleCompare(true, currentData);
  526. },
  527. });
  528. };
  529. const onMergeVersion = async sheet2 => {
  530. // const [sheet1, sheet2] = checkSheets;
  531. const sheet1 = version;
  532. if (!sheet1.data) {
  533. sheet1.data = (
  534. await queryDetail({
  535. excel_id: sheet1.id,
  536. })
  537. ).data;
  538. }
  539. if (!sheet2.data) {
  540. sheet2.data = (
  541. await queryDetail({
  542. excel_id: sheet2.id,
  543. })
  544. ).data;
  545. }
  546. setIsMerge(true);
  547. setCompareList([sheet1, sheet2]);
  548. // setTimeout(() => {
  549. // sheetRef3.current.mergeExcl(sheet.data);
  550. // }, 400);
  551. };
  552. const handleClickFile = () => {
  553. fileRef.current.click();
  554. };
  555. const handleMenuClick = e => {
  556. console.log('click', e);
  557. switch (e.key) {
  558. case 'back':
  559. // 返回
  560. router.push(`/home`);
  561. break;
  562. // case 'version':
  563. // // 清单
  564. // queryHistory();
  565. // setCommentVisible(false);
  566. // setHistoryVisible(true);
  567. // break;
  568. case 'bomDetail':
  569. // 清单
  570. setCommentVisible(true);
  571. break;
  572. case 'export':
  573. // 导出
  574. handleExportClick();
  575. break;
  576. case 'commitAudit':
  577. // 提交流转
  578. setCommitAuditVisible(true);
  579. break;
  580. case 'flow':
  581. // 查看流程
  582. setFlowVisible(true);
  583. break;
  584. case 'compare':
  585. // 比对
  586. setCompareVisible(true);
  587. break;
  588. case 'template':
  589. // 模板
  590. handleClickFile();
  591. break;
  592. // case 'auditSuccess':
  593. // // 审核通过
  594. // onApprove(true);
  595. // break;
  596. // case 'auditFailed':
  597. // // 审核拒绝
  598. // onApprove(false);
  599. // break;
  600. // case 'edit':
  601. // // 编辑
  602. // handleEdit(true);
  603. case 'merge':
  604. // 同步清单
  605. setMergeVisible(true);
  606. break;
  607. case 'commit':
  608. // 提交
  609. // handleClickCommit();
  610. setCommitVisible(true);
  611. setCommentVisible(false);
  612. break;
  613. case 'attachment':
  614. // 附件
  615. setFileVisible(true);
  616. queryFiles();
  617. break;
  618. }
  619. };
  620. const renderBtns = () => {
  621. // 判断是否为比对模式
  622. if (compareList.length == 2) {
  623. // 判断是否为同步最新清单的比对
  624. if (isMerge) {
  625. return (
  626. <>
  627. <Button type="primary" onClick={() => onSave()}>
  628. 保存
  629. </Button>
  630. <Button onClick={() => onMerge()}>同步新增内容</Button>
  631. <Button onClick={() => onCompare(false)}>取消同步</Button>
  632. </>
  633. );
  634. } else {
  635. return <Button onClick={() => onCompare(false)}>取消比对</Button>;
  636. }
  637. }
  638. const menuList = [
  639. <Menu.Item key="back">返回</Menu.Item>,
  640. <Menu.Item key="bomDetail">详情</Menu.Item>,
  641. <Menu.Item key="export">导出</Menu.Item>,
  642. <Menu.Item key="compare">比对</Menu.Item>,
  643. <Menu.Item key="attachment">附件</Menu.Item>,
  644. ];
  645. // version.audit_status:4 为副本。不可操作
  646. if (version.audit_status != 4) {
  647. //判断权限配置,如果配置了,就指定权限的人可提交,没配置就全部人都可提交
  648. const getIsSubmit = () => {
  649. const nodeId = version.template_node_id;
  650. if (!flowDetail?.nodes || !nodeId) return;
  651. const node = flowDetail.nodes.find(item => item.Id == nodeId);
  652. if (!node || node.name == 'custom-circle') return;
  653. if (node?.role_list) {
  654. return node.role_list
  655. .split(',')
  656. .some(id => currentUser.roleList?.find(role => role.ID == id));
  657. }
  658. return true;
  659. };
  660. // console.log('是否有权限提交流转 ', getIsSubmit());
  661. if (getIsSubmit() && version.audit_status != 3)
  662. menuList.push(<Menu.Item key="commitAudit">提交流转</Menu.Item>);
  663. if (!isAuditor && canEdit() && !version.flow_id) {
  664. // menuList.push(<Menu.Item key="edit">编辑</Menu.Item>);
  665. menuList.push(<Menu.Item key="merge">同步</Menu.Item>);
  666. // menuList.push(<Menu.Item key="commit">提交</Menu.Item>);
  667. // if (history.list.length > 0) {
  668. // menuList.push(<Menu.Item key="approval">申请审批</Menu.Item>);
  669. // }
  670. }
  671. }
  672. return (
  673. <>
  674. <Dropdown overlay={<Menu onClick={handleMenuClick}>{menuList}</Menu>}>
  675. <UnorderedListOutlined style={{ fontSize: 30, cursor: 'pointer' }} />
  676. </Dropdown>
  677. </>
  678. );
  679. };
  680. const canEdit = () => {
  681. if (flow.list.FlowNodes.length - 1 == flow.current && active_audit == 3) return false;
  682. return active_audit != 1;
  683. };
  684. const renderAlert = () => {
  685. const audit_comment = history.list[0]?.audit_comment;
  686. let item = '';
  687. switch (active_audit) {
  688. case 0:
  689. if (!flow.list || flow.list.FlowNodes?.length == 0) return;
  690. item = <Alert message="审批拒绝" type="error" />;
  691. break;
  692. case 1:
  693. item = <Alert message="等待审核中" type="info" />;
  694. break;
  695. case 2:
  696. item = (
  697. <Alert
  698. message={`审批被拒绝${
  699. audit_comment ? `,拒绝原因:${audit_comment}` : ''
  700. }。请修改后重新提交`}
  701. type="error"
  702. />
  703. );
  704. break;
  705. case 3:
  706. item = <Alert message="审批通过" type="success" />;
  707. break;
  708. }
  709. return <div style={{ marginTop: 20 }}>{item}</div>;
  710. };
  711. const exportExcl = files => {
  712. sheetRef.current.uploadExcel(files, () => {
  713. fileRef.current.value = null;
  714. });
  715. };
  716. const getRowOneList = () => {
  717. const obj = sheetRef.current.getSheetJson();
  718. console.log(obj);
  719. const list = [];
  720. obj.data.forEach(item => {
  721. list.push(item.data[0]);
  722. });
  723. return list;
  724. };
  725. //点击导出弹出选择导出列弹框
  726. const handleExportClick = () => {
  727. setExportData(sheetRef.current.getSheetJson());
  728. setExportVisible(true);
  729. };
  730. const downloadExcel = checkValue => {
  731. sheetRef.current.downloadExcel(checkValue);
  732. setExportVisible(false);
  733. };
  734. const queryHistory = id => {
  735. return new Promise(reslove => {
  736. dispatch({
  737. type: 'detail/queryHistory',
  738. payload: {
  739. // excel_id: id || excelId,
  740. project_id: projectId,
  741. },
  742. callback: reslove,
  743. });
  744. });
  745. };
  746. const queryFiles = () => {
  747. dispatch({
  748. type: 'detail/queryFiles',
  749. payload: {
  750. // excel_id: id || excelId,
  751. excel_id: version.id,
  752. },
  753. });
  754. };
  755. const getUploadProps = () => {
  756. const token = getToken() || GetTokenFromUrl();
  757. const uploadProps = {
  758. name: 'file',
  759. showUploadList: false,
  760. action: `/api/v1/purchase/attachment/${version.id}`,
  761. headers: {
  762. 'JWT-TOKEN': token,
  763. },
  764. onChange(info) {
  765. if (info.file.status !== 'uploading') {
  766. console.log(info.file, info.fileList);
  767. }
  768. if (info.file.status === 'done') {
  769. message.success(`${info.file.name} 文件上传成功`);
  770. queryFiles();
  771. } else if (info.file.status === 'error') {
  772. message.error(`${info.file.name} 文件上传失败`);
  773. }
  774. },
  775. };
  776. return uploadProps;
  777. };
  778. const deleteFile = id => {
  779. dispatch({
  780. type: 'detail/deleteFiles',
  781. id: id,
  782. callback: () => {
  783. queryFiles();
  784. },
  785. });
  786. };
  787. const queryHistoryDetail = async item => {
  788. return new Promise(resolve => {
  789. dispatch({
  790. type: 'detail/queryHistoryDetail',
  791. payload: {
  792. excel_id: item.excel_id,
  793. history_id: item.id,
  794. },
  795. callback: sheet => {
  796. resolve(sheet);
  797. },
  798. });
  799. });
  800. };
  801. const getLoading = () => {
  802. let effects = loading.effects;
  803. return !loading.effects['detail/queryComment'] && loading.models.detail;
  804. };
  805. const getFilesLoading = () => {
  806. let effects = loading.effects;
  807. return loading.effects['detail/queryFiles'];
  808. };
  809. const downloadFile = record => {
  810. window.location.href = `${record.url}`;
  811. };
  812. const changeVersion = id => {
  813. let version;
  814. if (typeof id == 'object') {
  815. version = id;
  816. localStorage.excelId = version.id;
  817. localStorage.excelItem = JSON.stringify(version);
  818. } else {
  819. version = versionList.find(item => item.id == id);
  820. if (!version) return;
  821. localStorage.excelId = id;
  822. }
  823. setVersion(version);
  824. //请求历史版本
  825. dispatch({
  826. type: 'detail/queryVersionsTree',
  827. payload: {
  828. excel_id: version.id || localStorage.excelId,
  829. },
  830. });
  831. // 判断是否审批节点
  832. if (version.flow_id) {
  833. dispatch({
  834. type: 'detail/queryAuditList',
  835. payload: {
  836. template_id: version.template_id,
  837. template_node_id: version.template_node_id,
  838. flow_id: version.flow_id,
  839. version_id: version.version_id,
  840. },
  841. });
  842. }
  843. };
  844. const onSubmitNextNode = values => {
  845. dispatch({
  846. type: 'detail/submitNextNode',
  847. payload: values,
  848. callback: newVersion => {
  849. setCommitAuditVisible(false);
  850. // 更新version
  851. // localStorage.excelId = newVersion.id;
  852. // changeVersion({
  853. // ...version,
  854. // ...newVersion,
  855. // version_id: version.id
  856. // });
  857. // 更新flow流程图
  858. dispatch({
  859. type: 'xflow/queryBoomFlowDetail',
  860. payload: {
  861. id: templateId,
  862. },
  863. });
  864. },
  865. });
  866. };
  867. const getUser = newUser => {
  868. try {
  869. if (JSON.stringify(newUser) != JSON.stringify(userRef.current)) {
  870. userRef.current = newUser;
  871. setUser(newUser);
  872. }
  873. } catch (error) {}
  874. };
  875. const renderNode = () => {
  876. const nodeId = version.template_node_id;
  877. if (!flowDetail?.nodes || !nodeId) return;
  878. const node = flowDetail.nodes.find(item => item.Id == nodeId);
  879. // return `当前清单:${version.version_name || '-'}; 当前节点:${node?.label || '-'}`;
  880. return (
  881. <span className={styles.curTitle}>
  882. 当前清单: <span>{version.version_name || '-'}</span>当前节点:{' '}
  883. <span>{node?.label || '-'}</span>
  884. </span>
  885. );
  886. };
  887. const handleSubmitCell = (value, callback) => {
  888. if (!value) return;
  889. dispatch({
  890. type: 'detail/addComment',
  891. payload: {
  892. ...cellPosition.current,
  893. comment: value,
  894. },
  895. callback,
  896. });
  897. };
  898. useEffect(() => {
  899. dispatch({
  900. type: 'detail/queryProjectRecord',
  901. payload: {
  902. project_id: projectId,
  903. },
  904. });
  905. dispatch({
  906. type: 'xflow/queryBoomFlowDetail',
  907. payload: {
  908. id: templateId,
  909. },
  910. });
  911. dispatch({
  912. type: 'user/fetch',
  913. });
  914. dispatch({
  915. type: 'user/fetchDepV2',
  916. });
  917. dispatch({
  918. type: 'detail/queryListParentByUser',
  919. payload: {
  920. userid: currentUser.DingUserId || getCurrentUser()?.DingUserId,
  921. },
  922. });
  923. }, []);
  924. useEffect(() => {
  925. if (compareList.length == 2) {
  926. const callback = ({ diff, add }) => {
  927. setUpdateCount(updateCount => {
  928. return {
  929. diff: diff.length,
  930. add: updateCount.add + add.length,
  931. };
  932. });
  933. };
  934. var update1 = sheetRef3.current.toggleCompare(true, compareList[1].data, callback);
  935. var update2 = sheetRef2.current.toggleCompare(true, compareList[0].data, callback);
  936. }
  937. }, [compareList]);
  938. useEffect(() => {
  939. if (versionList.length == 0) return;
  940. if (!version.id) {
  941. const excelId = localStorage.excelItem
  942. ? JSON.parse(localStorage.excelItem)
  943. : localStorage.excelId;
  944. changeVersion(excelId);
  945. } else {
  946. changeVersion(version.id);
  947. }
  948. }, [versionList]);
  949. return (
  950. <Spin spinning={false}>
  951. <div className={styles.top}>
  952. <div>
  953. <Button type="primary" style={{ marginRight: 20 }} onClick={() => setFlowVisible(true)}>
  954. 查看流程
  955. </Button>
  956. {/* 非审批节点可以创建清单 */}
  957. {flow?.active == 0 && (
  958. <Button type="primary" onClick={() => setVersionVisible(true)}>
  959. 新建清单
  960. </Button>
  961. )}
  962. {renderNode()}
  963. </div>
  964. <div className={styles.btns}>
  965. <Button
  966. type="primary"
  967. style={{ marginRight: 20 }}
  968. onClick={() => setVersionTreeVisible(true)}
  969. >
  970. 历史版本
  971. </Button>
  972. <Avatar.Group style={{ marginRight: 20 }}>
  973. {user.map((item, id) => (
  974. <Avatar
  975. key={`${id}-${item.name}`}
  976. style={{ backgroundColor: '#008dff' }}
  977. size="large"
  978. >
  979. {item.Name}
  980. </Avatar>
  981. ))}
  982. </Avatar.Group>
  983. {renderBtns()}
  984. </div>
  985. <input
  986. type="file"
  987. ref={fileRef}
  988. style={{ display: 'none' }}
  989. onChange={e => exportExcl(e.target.files)}
  990. />
  991. </div>
  992. <TimeNode flow={flow} isAuditor={isAuditor} onApprove={onApprove}></TimeNode>
  993. {/* {version.flow_id ? (
  994. <AuditFlow {...auditDetail} canShowAudit={true} onApprove={onApprove} />
  995. ) : null} */}
  996. {/* {renderAlert()} */}
  997. {/* 判断是否为比对模式 */}
  998. {compareList.length == 2 ? (
  999. <>
  1000. <Alert
  1001. message={`比对结果:${updateCount.diff}项差异。${updateCount.add}项新增`}
  1002. type="info"
  1003. />
  1004. <div className={styles.sheetBox}>{compareList.map(renderSheetDom)}</div>
  1005. </>
  1006. ) : (
  1007. <div className={styles.sheetBox}>
  1008. {version.id && (
  1009. <LuckySheet
  1010. className={styles.sheet}
  1011. ref={sheetRef}
  1012. onClickCell={onClickCell}
  1013. version={version}
  1014. templateId={templateId}
  1015. getUser={getUser}
  1016. onUpdate={onUpdate}
  1017. />
  1018. )}
  1019. </div>
  1020. )}
  1021. <HistoryDrawer
  1022. versionTree={versionTree}
  1023. version={version}
  1024. visible={versionTreeVisible}
  1025. onChangeVersion={version => changeVersion(version)}
  1026. onClose={() => setVersionTreeVisible(false)}
  1027. />
  1028. <CommentContent
  1029. title="单元格沟通记录"
  1030. comment={comment}
  1031. onSubmit={handleSubmitCell}
  1032. loading={loading.effects['detail/queryComment'] || loading.effects['detail/addComment']}
  1033. />
  1034. <RightDrawer
  1035. version={version}
  1036. visible={commentVisible}
  1037. onClose={() => setCommentVisible(false)}
  1038. />
  1039. <CompareModal
  1040. visible={compareVisible}
  1041. version={version}
  1042. onClose={() => setCompareVisible(false)}
  1043. onOk={onCompare}
  1044. />
  1045. <MergeModal
  1046. visible={mergeVisible}
  1047. version={version}
  1048. onClose={() => setMergeVisible(false)}
  1049. onOk={onMergeVersion}
  1050. />
  1051. <ExportModal
  1052. visible={exportVisible}
  1053. sheet={exportDate.data}
  1054. onClose={() => setExportVisible(false)}
  1055. onOk={downloadExcel}
  1056. />
  1057. <FlowModal
  1058. flowDetail={flowDetail}
  1059. visible={flowVisible}
  1060. onClose={() => setFlowVisible(false)}
  1061. version={version}
  1062. onChangeVersion={version => changeVersion(version)}
  1063. />
  1064. <AuditModal
  1065. loading={getLoading()}
  1066. visible={auditVisible}
  1067. onClose={() => setAuditVisible(false)}
  1068. onOk={onAudit}
  1069. />
  1070. <FilesModal
  1071. loading={getFilesLoading()}
  1072. visible={fileVisible}
  1073. onClose={() => setFileVisible(false)}
  1074. uploadProps={getUploadProps()}
  1075. DeleteFile={deleteFile}
  1076. downloadFile={downloadFile}
  1077. data={fileList}
  1078. />
  1079. <VersionModal
  1080. loading={getLoading()}
  1081. visible={versionVisible}
  1082. onClose={() => setVersionVisible(false)}
  1083. onOk={values => onCommit(values)}
  1084. />
  1085. <CommitAuditModal
  1086. loading={getLoading()}
  1087. visible={commitAuditVisible}
  1088. version={version}
  1089. onClose={() => setCommitAuditVisible(false)}
  1090. onOk={onSubmitNextNode}
  1091. luckysheet={sheetRef}
  1092. />
  1093. </Spin>
  1094. );
  1095. }
  1096. export default connect(({ detail, user, xflow, loading }) => ({
  1097. flowDetail: xflow.flowDetail,
  1098. auditList: detail.auditList,
  1099. fileList: detail.fileList,
  1100. history: detail.history,
  1101. comment: detail.comment,
  1102. instanceDetail: detail.dingInstanceDetail,
  1103. currentUser: user.currentUser,
  1104. roleList: detail.roleList,
  1105. versionList: detail.versionList,
  1106. versionTree: detail.versionTree,
  1107. loading,
  1108. }))(Detail);