Browse Source

bom改为协同模式

xujunjie 3 years ago
parent
commit
7eed5f1580
27 changed files with 2004 additions and 437 deletions
  1. 6 2
      config/router.config.js
  2. 0 0
      public/Luckysheet/luckysheet.umd.js
  3. 0 0
      public/Luckysheet/luckysheet.umd.js.map
  4. 14 21
      public/luckysheet.html
  5. 68 0
      src/components/CommentContent/index.js
  6. 32 14
      src/components/Flow/config-toolbar.ts
  7. 2 2
      src/components/Flow/node/circle/index.tsx
  8. 1 1
      src/components/Flow/node/rect/index.tsx
  9. 9 15
      src/pages/PurchaseAdmin/PurchaseList/Detail/FlowModal.js
  10. 234 128
      src/pages/PurchaseAdmin/PurchaseList/Detail/Index.js
  11. 4 3
      src/pages/PurchaseAdmin/PurchaseList/Detail/Index.less
  12. 96 30
      src/pages/PurchaseAdmin/PurchaseList/Detail/LuckySheet.js
  13. 23 85
      src/pages/PurchaseAdmin/PurchaseList/Detail/MergeModal.js
  14. 19 77
      src/pages/PurchaseAdmin/PurchaseList/Detail/RightDrawer.js
  15. 36 19
      src/pages/PurchaseAdmin/PurchaseList/Detail/TimeNode.js
  16. 15 0
      src/pages/PurchaseAdmin/PurchaseList/Detail/models/detail.js
  17. 2 2
      src/pages/PurchaseAdmin/PurchaseList/Flow/AuditNodeModal.js
  18. 11 7
      src/pages/PurchaseAdmin/PurchaseList/Flow/Flow.js
  19. 1188 0
      src/pages/PurchaseAdmin/PurchaseList/Flow/FlowDetail.json
  20. 50 0
      src/pages/PurchaseAdmin/PurchaseList/Flow/FlowModal.js
  21. 79 0
      src/pages/PurchaseAdmin/PurchaseList/Flow/List.js
  22. 33 0
      src/pages/PurchaseAdmin/PurchaseList/Flow/models/flow.js
  23. 2 2
      src/pages/PurchaseAdmin/PurchaseList/Index.js
  24. 19 16
      src/pages/PurchaseAdmin/PurchaseList/List/NewList.js
  25. 32 5
      src/pages/PurchaseAdmin/PurchaseList/List/models/newList.js
  26. 0 1
      src/pages/PurchaseAdmin/PurchaseList/Login/Login.less
  27. 29 7
      src/services/boom.js

+ 6 - 2
config/router.config.js

@@ -17,7 +17,7 @@ export default [
             // component: './PurchaseAdmin/PurchaseList/List/List',
           },
           {
-            path: '/home/detail/:projectId',
+            path: '/home/detail/:projectId/:templateId',
             component: './PurchaseAdmin/PurchaseList/Detail/Index',
           },
           {
@@ -49,13 +49,17 @@ export default [
             component: './PurchaseAdmin/PurchaseList/Approval/Auth',
           },
           {
-            path: '/home/flow',
+            path: '/home/flow/:flowId',
             component: './PurchaseAdmin/PurchaseList/Flow/Flow',
           },
           {
             path: '/home/audit',
             component: './PurchaseAdmin/PurchaseList/Flow/Audit',
           },
+          {
+            path: '/home/flow-list',
+            component: './PurchaseAdmin/PurchaseList/Flow/List',
+          },
         ],
       },
       {

File diff suppressed because it is too large
+ 0 - 0
public/Luckysheet/luckysheet.umd.js


File diff suppressed because it is too large
+ 0 - 0
public/Luckysheet/luckysheet.umd.js.map


+ 14 - 21
public/luckysheet.html

@@ -6,28 +6,21 @@
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
     <title>Document</title>
 
-    <!-- 本地开发使用路径 -->
-    <!-- <link rel="stylesheet" href="/Luckysheet/plugins/css/pluginsCss.css" />
-    <link rel="stylesheet" href="/Luckysheet/plugins/plugins.css" />
-    <link rel="stylesheet" href="/Luckysheet/css/luckysheet.css" />
-    <link rel="stylesheet" href="/Luckysheet/assets/iconfont/iconfont.css" />
-    <script src="/Luckysheet/plugins/js/plugin.js"></script>
-    <script src="/Luckysheet/luckysheet.umd.js"></script> -->
+   <!-- 线上发布使用路径 -->
+   <!-- <link rel="stylesheet" href="/bom/Luckysheet/plugins/css/pluginsCss.css" />
+   <link rel="stylesheet" href="/bom/Luckysheet/plugins/plugins.css" />
+   <link rel="stylesheet" href="/bom/Luckysheet/css/luckysheet.css" />
+   <link rel="stylesheet" href="/bom/Luckysheet/assets/iconfont/iconfont.css" />
+   <script src="/bom/Luckysheet/plugins/js/plugin.js"></script>
+   <script src="/bom/Luckysheet/luckysheet.umd.js"></script> -->
 
-    <!-- 线上发布使用路径 -->
-    <!-- <link rel="stylesheet" href="/bom/Luckysheet/plugins/css/pluginsCss.css" />
-    <link rel="stylesheet" href="/bom/Luckysheet/plugins/plugins.css" />
-    <link rel="stylesheet" href="/bom/Luckysheet/css/luckysheet.css" />
-    <link rel="stylesheet" href="/bom/Luckysheet/assets/iconfont/iconfont.css" />
-    <script src="/bom/Luckysheet/plugins/js/plugin.js"></script>
-    <script src="/bom/Luckysheet/luckysheet.umd.js"></script> -->
-
-    <link rel='stylesheet' href='http://localhost:3000/plugins/css/pluginsCss.css' />
-    <link rel='stylesheet' href='http://localhost:3000/plugins/plugins.css' />
-    <link rel='stylesheet' href='http://localhost:3000/css/luckysheet.css' />
-    <link rel='stylesheet' href='http://localhost:3000/assets/iconfont/iconfont.css' />
-    <script src="http://localhost:3000/plugins/js/plugin.js"></script>
-    <script src="http://localhost:3000/luckysheet.umd.js"></script>
+   <!-- 本地开发使用路径 -->
+   <link rel='stylesheet' href='http://localhost:3000/plugins/css/pluginsCss.css' />
+   <link rel='stylesheet' href='http://localhost:3000/plugins/plugins.css' />
+   <link rel='stylesheet' href='http://localhost:3000/css/luckysheet.css' />
+   <link rel='stylesheet' href='http://localhost:3000/assets/iconfont/iconfont.css' />
+   <script src="http://localhost:3000/plugins/js/plugin.js"></script>
+   <script src="http://localhost:3000/luckysheet.umd.js"></script>
   </head>
   <body>
     <div

+ 68 - 0
src/components/CommentContent/index.js

@@ -0,0 +1,68 @@
+import React, { useState } from 'react';
+import { UserOutlined } from '@ant-design/icons';
+import { Form } from '@ant-design/compatible';
+import '@ant-design/compatible/assets/index.css';
+import { Button, Comment, Tooltip, Avatar, List, Card, Input } from 'antd';
+import moment from 'moment';
+const { TextArea } = Input;
+
+function CommentContent(props) {
+  const {
+    comment: { list },
+    loading,
+    onSubmit,
+    title,
+  } = props;
+  const [value, setValue] = useState('');
+
+  return (
+    <Card title={title} type="inner" style={{ marginTop: 20 }}>
+      <List
+        className="comment-list"
+        itemLayout="horizontal"
+        dataSource={list}
+        loading={loading}
+        renderItem={item => (
+          <li>
+            <Comment
+              author={<a>{item.AuthorUser.CName}</a>}
+              avatar={<Avatar icon={<UserOutlined />} alt={item.author} />}
+              content={<p>{item.comment}</p>}
+              datetime={
+                <Tooltip title={moment(item.c_time).format('YYYY-MM-DD')}>
+                  <span>{moment(item.c_time).fromNow()}</span>
+                </Tooltip>
+              }
+            />
+          </li>
+        )}
+      />
+      <Comment
+        avatar={<Avatar icon={<UserOutlined />} />}
+        content={
+          <>
+            <Form.Item>
+              <TextArea rows={4} onChange={e => setValue(e.target.value)} value={value} />
+            </Form.Item>
+            <Form.Item>
+              <Button
+                htmlType="submit"
+                loading={loading}
+                type="primary"
+                onClick={() =>
+                  onSubmit(value, () => {
+                    setValue('');
+                  })
+                }
+              >
+                添加评论
+              </Button>
+            </Form.Item>
+          </>
+        }
+      />
+    </Card>
+  );
+}
+
+export default CommentContent;

+ 32 - 14
src/components/Flow/config-toolbar.ts

@@ -196,25 +196,43 @@ namespace NSToolbarConfig {
             saveGraphDataService: (meta, graphData) => {
               let data = JSON.parse(JSON.stringify(graphData))
               data.nodes = data.nodes.map(item => {
-                delete item.incomingEdges;
-                delete item.originData;
-                delete item.outgoingEdges;
-                delete item.ports.groups
-                return item;
+                // delete item.incomingEdges;
+                // delete item.originData;
+                // delete item.outgoingEdges;
+                // delete item.ports.groups
+                return {
+                  id: item.id,
+                  renderKey: item.renderKey,
+                  name: item.name,
+                  label: item.label,
+                  width: item.width,
+                  height: item.height,
+                  ports: item.ports,
+                  isCustom: item.isCustom,
+                  parentKey: item.parentKey,
+                  x: item.x,
+                  y: item.y,
+                  zIndex: item.zIndex,
+                  count: item.count,
+                };
               });
               // graphData.edges = []
               data.edges = data.edges.map(item => {
-                delete item.data;
-                delete item.attrs;
-                delete item.sourcePort;
-                delete item.sourcePortId;
-                delete item.targetPort;
-                delete item.targetPortId;
-                delete item.zIndex;
-                return item;
+                // delete item.data;
+                // delete item.attrs;
+                // delete item.sourcePort;
+                // delete item.sourcePortId;
+                // delete item.targetPort;
+                // delete item.targetPortId;
+                // delete item.zIndex;
+                return {
+                  id: item.id,
+                  source: item.source,
+                  target: item.target,
+                };
               });
               console.log(data);
-              // localStorage.graphData = JSON.stringify(data);
+              localStorage.graphData = JSON.stringify(data);
               return null;
             },
           }

+ 2 - 2
src/components/Flow/node/circle/index.tsx

@@ -18,7 +18,7 @@ export default function CustomRect(props) {
     // message.success(`${XFlowNodeCommands.SELECT_NODE.label}: 命令执行成功`);
   };
   return (
-    <Badge count={data.version?.length} offset={[-10, 10]}>
+    // <Badge count={data.versions} offset={[-10, 10]}>
       <div
         className="container"
         onClick={handleClick}
@@ -37,6 +37,6 @@ export default function CustomRect(props) {
       >
         <span>{label}</span>
       </div>
-    </Badge>
+    // </Badge>
   );
 }

+ 1 - 1
src/components/Flow/node/rect/index.tsx

@@ -21,7 +21,7 @@ export default function CustomRect(props) {
   };
 
   return (
-    <Badge count={data.version?.length}>
+    <Badge count={data.versions}>
       <div
         style={{
           width,

+ 9 - 15
src/pages/PurchaseAdmin/PurchaseList/Detail/FlowModal.js

@@ -5,22 +5,15 @@ import { connect } from 'dva';
 import { GetTokenFromUrl, getToken } from '@/utils/utils';
 import { MODELS, useXFlowApp, useModelAsync } from '@antv/xflow';
 import { StarOutlined } from '@ant-design/icons';
+import { queryVserionByNode } from '@/services/boom';
+import { async } from '@antv/x6/lib/registry/marker/async';
 
 const { TextArea } = Input;
 const localData = JSON.parse(localStorage.ggDetaiData || '{}');
 
 // 提交
 function FlowModal(props) {
-  const {
-    visible,
-    version,
-    onClose,
-    onChangeVersion,
-    form,
-    loading,
-    flowDetail,
-    versionList,
-  } = props;
+  const { visible, version, onClose, onChangeVersion, form, loading, flowDetail } = props;
   const [data, setData] = useState([]);
   // const app = useXFlowApp();
   // const appRef = userRef()
@@ -29,19 +22,21 @@ function FlowModal(props) {
     let nodes = flowDetail.nodes.map(item => ({
       ...item,
       isCheck: item.Id == version.template_node_id,
-      version: versionList.filter(version => version.template_node_id == item.Id) || [],
     }));
     return {
       nodes,
       edges: flowDetail.edges,
     };
-  }, [flowDetail, versionList, version.template_node_id]);
+  }, [flowDetail, version.template_node_id]);
 
-  const handleSelectNode = args => {
+  const handleSelectNode = async args => {
     const id = args.nodeId || args.nodeIds[0];
     if (!id) return;
     let node = graphData.nodes.find(item => item.id == id);
-    setData(node.version);
+    // setData(node.version);
+    let res = await queryVserionByNode({ template_node_id: node.Id });
+    console.log(res);
+    setData(res.data.excel_version)
   };
 
   return (
@@ -94,5 +89,4 @@ function FlowModal(props) {
 
 export default connect(({ xflow, detail }) => ({
   flowDetail: xflow.flowDetail,
-  versionList: detail.versionList,
 }))(FlowModal);

+ 234 - 128
src/pages/PurchaseAdmin/PurchaseList/Detail/Index.js

@@ -1,5 +1,5 @@
 import React, { useEffect, useState, useRef, useMemo } from 'react';
-import { UnorderedListOutlined } from '@ant-design/icons';
+import { UnorderedListOutlined, PlusOutlined } from '@ant-design/icons';
 import { Button, Modal, message, Alert, Avatar, Spin, Select, Menu, Dropdown } from 'antd';
 import { connect } from 'dva';
 import styles from './Index.less';
@@ -17,6 +17,8 @@ import TimeNode from './TimeNode';
 import FilesModal from './FilesModal';
 import VersionModal from './VersionModal';
 import CommitAuditModal from './CommitAuditModal';
+import CommentContent from '@/components/CommentContent';
+import MergeModal from './MergeModal';
 import { GetTokenFromUrl, getToken } from '@/utils/utils';
 import { queryDetail } from '@/services/boom';
 const LocalData = localStorage.luckysheet;
@@ -36,10 +38,12 @@ function Detail(props) {
     template,
     versionList,
     auditList,
+    flowDetail,
     match: { params },
   } = props;
   // const audit_status = 0
   const [commentVisible, setCommentVisible] = useState(false);
+  const [mergeVisible, setMergeVisible] = useState(false);
   const [compareVisible, setCompareVisible] = useState(false);
   const [exportVisible, setExportVisible] = useState(false);
   const [commitVisible, setCommitVisible] = useState(false);
@@ -74,6 +78,7 @@ function Detail(props) {
   // const [excelId, setExcelId] = useState(parseInt(params.excelId));
   // const excelId = parseInt(params.excelId);
   const projectId = parseInt(params.projectId);
+  const templateId = parseInt(params.templateId);
 
   const flow = useMemo(() => {
     let data = {
@@ -106,22 +111,42 @@ function Detail(props) {
     return data;
   }, [auditList, version]);
   const audit_status = flow.active_audit;
+
+  const isAuditor = useMemo(() => {
+    const getUserRole = () => {
+      let item = flow.currentNode?.user?.find?.(item => item.ID == currentUser.ID);
+      if (item) return true;
+      return false;
+    };
+    return audit_status == 1 && getUserRole();
+  }, [audit_status, flow, currentUser]);
   // const audit_status = 1;
 
   const onSave = () => {
-    setEdit(false);
-    statusRef.current.edit = false;
-    sheetRef.current.toggleEdit(false);
-    let jsonData = sheetRef.current.getSheetJson();
-    console.log(jsonData);
-    let newExcl = {
-      ...sheet,
-      excel_id: version.id,
-      title: '',
-      data: jsonData.data,
-    };
-    setSheet(newExcl);
-    localStorage.luckysheet = JSON.stringify(newExcl);
+    let sheet1 = compareList[0];
+    Modal.confirm({
+      title: '提示',
+      content: `是否确认保存【${sheet1.version_name || sheet1.name}】`,
+      okText: '确定',
+      cancelText: '取消',
+      onOk() {
+        let sheetData = sheetRef3.current.getSheetJson().data;
+        sheetData.forEach(sheet => {
+          delete sheet.data;
+        });
+        let params = {
+          ...sheet1,
+          data: JSON.stringify(sheetData),
+        };
+        dispatch({
+          type: 'detail/commitSheet',
+          payload: params,
+          callback: () => {
+            onCompare(false);
+          },
+        });
+      },
+    });
   };
 
   const onCompare = async checkSheets => {
@@ -189,7 +214,7 @@ function Detail(props) {
           className={styles.sheet}
           ref={!index ? sheetRef3 : sheetRef2}
           data={item.data || null}
-          onClickCell={onClickCell}
+          // onClickCell={onClickCell}
         />
       </div>
     );
@@ -211,7 +236,7 @@ function Detail(props) {
         ...payload,
         sheet_index: (s.seq || 0) + '',
       };
-      setCommentVisible(true);
+      // setCommentVisible(true);
     }
     // 比对模式下双excl同步选中
     // if (statusRef.current.compare) {
@@ -254,6 +279,24 @@ function Detail(props) {
       },
     });
   };
+
+  const onUpdate = () => {
+    let currentData = sheetRef.current.getSheetJson().data;
+    let sheets = JSON.parse(JSON.stringify(currentData));
+    sheets.forEach(item => {
+      delete item.data;
+    });
+    console.log(sheets);
+    let params = {
+      ...version,
+      data: JSON.stringify(sheets),
+    };
+    dispatch({
+      type: 'detail/saveSheet',
+      payload: params,
+    });
+  };
+
   // const onCommit = values => {
   //   var flowNode = flow.currentNode;
 
@@ -356,6 +399,13 @@ function Detail(props) {
             node_id: flowNode.seq,
           },
           callback: newVersion => {
+            // 更新flow流程图
+            dispatch({
+              type: 'xflow/queryBoomFlowDetail',
+              payload: {
+                id: templateId,
+              },
+            });
             if (flow.current == flow.list.FlowNodes.length - 1) {
               // 最后一个审核节点通过后  需要更新version id
               localStorage.excelId = newVersion.id;
@@ -371,15 +421,18 @@ function Detail(props) {
   };
 
   const onMerge = () => {
+    const [sheet1, sheet2] = compareList;
     Modal.confirm({
       title: '提示',
-      content: '是否确认同步最新版本新增项',
+      content: `是否确认将【${sheet2.version_name}】改动的内容同步至【${sheet1.version_name ||
+        sheet1.name}】`,
       okText: '确定',
       cancelText: '取消',
       onOk() {
-        sheetRef.current.mergeExcl(compareList[1].data);
-        setCompareList([...compareList]);
-        // let currentData = sheetRef.current.getSheetJson()
+        // let sheet2Data = sheetRef2.current.getSheetJson()
+        sheetRef3.current.mergeExcl(sheetRef2.current.updateCell);
+        // setCompareList([...compareList]);
+        // let currentData = sheetRef3.current.getSheetJson()
         // // 更新后重新比对
         // sheetRef2.current.toggleCompare(false);
         // sheetRef2.current.toggleCompare(true, currentData);
@@ -387,76 +440,85 @@ function Detail(props) {
     });
   };
 
-  const handleClickCommit = async () => {
-    const { list } = await queryHistory();
-    let lastCommit = list[0];
-    // 判断当前版本是否为最新版本
-    if (lastCommit && sheet.guid != lastCommit.guid) {
-      Modal.confirm({
-        title: '提示',
-        content: '当前版本与最新版本不匹配,将进入比对模式确认版本差异。',
-        okText: '比对',
-        cancelText: '返回',
-        onOk() {
-          dispatch({
-            type: 'detail/queryHistoryDetail',
-            payload: {
-              excel_id: lastCommit.excel_id,
-              history_id: lastCommit.id,
-            },
-            callback: sheets => {
-              setIsMerge(true);
-              let currentSheet = {
-                ...sheet,
-                name: '当前版本',
-              };
-              let lastSheet = {
-                ...lastCommit,
-                name: `线上最新版本(${lastCommit.version_name})`,
-                data: sheets,
-              };
-              onCompare([currentSheet, lastSheet]);
-            },
-          });
-        },
-      });
-    } else {
-      setCommitVisible(true);
+  const onMergeVersion = async sheet2 => {
+    // const [sheet1, sheet2] = checkSheets;
+    const sheet1 = version;
+    if (!sheet1.data) {
+      sheet1.data = (
+        await queryDetail({
+          excel_id: sheet1.id,
+        })
+      ).data;
+    }
+    if (!sheet2.data) {
+      sheet2.data = (
+        await queryDetail({
+          excel_id: sheet2.id,
+        })
+      ).data;
     }
 
-    setCommentVisible(false);
+    setIsMerge(true);
+    setCompareList([sheet1, sheet2]);
+    // setTimeout(() => {
+    //   sheetRef3.current.mergeExcl(sheet.data);
+    // }, 400);
   };
+  // const handleClickCommit = async () => {
+  //   const { list } = await queryHistory();
+  //   let lastCommit = list[0];
+  //   // 判断当前版本是否为最新版本
+  //   if (lastCommit && sheet.guid != lastCommit.guid) {
+  //     Modal.confirm({
+  //       title: '提示',
+  //       content: '当前版本与最新版本不匹配,将进入比对模式确认版本差异。',
+  //       okText: '比对',
+  //       cancelText: '返回',
+  //       onOk() {
+  //         dispatch({
+  //           type: 'detail/queryHistoryDetail',
+  //           payload: {
+  //             excel_id: lastCommit.excel_id,
+  //             history_id: lastCommit.id,
+  //           },
+  //           callback: sheets => {
+  //             setIsMerge(true);
+  //             let currentSheet = {
+  //               ...sheet,
+  //               name: '当前版本',
+  //             };
+  //             let lastSheet = {
+  //               ...lastCommit,
+  //               name: `线上最新版本(${lastCommit.version_name})`,
+  //               data: sheets,
+  //             };
+  //             onCompare([currentSheet, lastSheet]);
+  //           },
+  //         });
+  //       },
+  //     });
+  //   } else {
+  //     setCommitVisible(true);
+  //   }
 
-  const handleEdit = status => {
-    if (!status) {
-      // 还原数据
-      sheetRef.current.renderSheet(sheet.data);
-    }
-    setCommentVisible(false);
-    setEdit(status);
-    statusRef.current.edit = status;
-    sheetRef.current.toggleEdit(status);
-  };
+  //   setCommentVisible(false);
+  // };
+
+  // const handleEdit = status => {
+  //   if (!status) {
+  //     // 还原数据
+  //     sheetRef.current.renderSheet(sheet.data);
+  //   }
+  //   setCommentVisible(false);
+  //   setEdit(status);
+  //   statusRef.current.edit = status;
+  //   sheetRef.current.toggleEdit(status);
+  // };
 
   const handleClickFile = () => {
     fileRef.current.click();
   };
 
-  const getUserRole = () => {
-    // if (currentUser.ID == flow.currentNode?.auditor) return true;
-    let item = flow.currentNode?.user?.find?.(item => item.ID == currentUser.ID);
-    if (item) return true;
-    // let roleId = Number(flow.currentNode?.audit_role);
-    // console.log(roleId);
-    // if (roleId) {
-    //   let role = roleList.find(item => item.ID == roleId);
-    //   if (role) {
-    //     return role.User.find(item => item.ID == currentUser.ID);
-    //   }
-    // }
-    return false;
-  };
-
   const handleMenuClick = e => {
     console.log('click', e);
     switch (e.key) {
@@ -470,6 +532,10 @@ function Detail(props) {
       //   setCommentVisible(false);
       //   setHistoryVisible(true);
       //   break;
+      case 'bomDetail':
+        // 版本
+        setCommentVisible(true);
+        break;
       case 'export':
         // 导出
         handleExportClick();
@@ -503,7 +569,7 @@ function Detail(props) {
       //   handleEdit(true);
       case 'merge':
         // 同步版本
-
+        setMergeVisible(true);
         break;
       case 'commit':
         // 提交
@@ -524,27 +590,27 @@ function Detail(props) {
   };
 
   const renderBtns = () => {
-    if (edit) {
-      return (
-        <>
-          <Button type="primary" onClick={onSave}>
-            保存
-          </Button>
-          <Button onClick={() => handleEdit(false)}>取消</Button>
-        </>
-      );
-    }
+    // if (edit) {
+    //   return (
+    //     <>
+    //       <Button type="primary" onClick={onSave}>
+    //         保存
+    //       </Button>
+    //       <Button onClick={() => handleEdit(false)}>取消</Button>
+    //     </>
+    //   );
+    // }
     // 判断是否为比对模式
     if (compareList.length == 2) {
       // 判断是否为同步最新版本的比对
       if (isMerge) {
         return (
           <>
-            {/* <Button type="success" onClick={() => onMerge()}>
-              同步新增内容
-            </Button>*/}
-            {/* <Button onClick={() => handleEdit(true)}>编辑</Button> */}
-            <Button onClick={() => setCommitVisible(true)}>确认提交</Button>
+            <Button type="primary" onClick={() => onSave()}>
+              保存
+            </Button>
+            <Button onClick={() => onMerge()}>同步新增内容</Button>
+            {/* <Button onClick={() => setCommitVisible(true)}>确认提交</Button> */}
             <Button onClick={() => onCompare(false)}>取消比对</Button>
           </>
         );
@@ -553,9 +619,9 @@ function Detail(props) {
       }
     }
 
-    let isAuditor = audit_status == 1 && getUserRole();
     const menuList = [
       <Menu.Item key="back">返回</Menu.Item>,
+      <Menu.Item key="bomDetail">详情</Menu.Item>,
       <Menu.Item key="export">导出</Menu.Item>,
       <Menu.Item key="commitAudit">提交流转</Menu.Item>,
       <Menu.Item key="flow">查看流程</Menu.Item>,
@@ -567,25 +633,15 @@ function Detail(props) {
     //   // menuList.push(<Menu.Item key="template">模板</Menu.Item>);
     // }
     if (!isAuditor && canEdit()) {
-      menuList.push(<Menu.Item key="edit">编辑</Menu.Item>);
-      menuList.push(<Menu.Item key="merge">合并</Menu.Item>);
-      menuList.push(<Menu.Item key="commit">提交</Menu.Item>);
-      if (history.list.length > 0) {
-        menuList.push(<Menu.Item key="approval">申请审批</Menu.Item>);
-      }
+      // menuList.push(<Menu.Item key="edit">编辑</Menu.Item>);
+      menuList.push(<Menu.Item key="merge">同步</Menu.Item>);
+      // menuList.push(<Menu.Item key="commit">提交</Menu.Item>);
+      // if (history.list.length > 0) {
+      //   menuList.push(<Menu.Item key="approval">申请审批</Menu.Item>);
+      // }
     }
     return (
       <>
-        {isAuditor && (
-          <>
-            <Button type="primary" onClick={() => onApprove(true)}>
-              审批通过
-            </Button>
-            <Button onClick={() => onApprove(false)} danger>
-              审批拒绝
-            </Button>
-          </>
-        )}
         <Dropdown overlay={<Menu onClick={handleMenuClick}>{menuList}</Menu>}>
           {/* <Button type="primary">
             其他操作 <DownOutlined />
@@ -797,6 +853,25 @@ function Detail(props) {
     setUser(user);
   };
 
+  const renderNode = () => {
+    const nodeId = version.template_node_id;
+    if (!flowDetail?.nodes || !nodeId) return;
+    const node = flowDetail.nodes.find(item => item.Id == nodeId);
+    return `当前节点:${node?.label || '-'}`;
+  };
+
+  const handleSubmitCell = (value, callback) => {
+    if (!value) return;
+    dispatch({
+      type: 'detail/addComment',
+      payload: {
+        ...cellPosition.current,
+        comment: value,
+      },
+      callback,
+    });
+  };
+
   useEffect(() => {
     dispatch({
       type: 'detail/queryProjectRecord',
@@ -807,9 +882,12 @@ function Detail(props) {
     dispatch({
       type: 'xflow/queryBoomFlowDetail',
       payload: {
-        id: 1,
+        id: templateId,
       },
     });
+    dispatch({
+      type: 'user/fetchUserList',
+    });
     // 审批流程
     // dispatch({
     //   type: 'detail/queryAuditList',
@@ -883,13 +961,11 @@ function Detail(props) {
   return (
     <Spin spinning={false}>
       {/* <Spin spinning={getLoading()}> */}
-
       <div className={styles.top}>
-        <div style={{ minWidth: 700, marginTop: 20 }}>
+        <div>
           {/* 当前节点: {version.template_node_id}
           <br />
           当前状态:{version.audit_status} */}
-          <TimeNode nodeId={version.template_node_id} flow={flow}></TimeNode>
           <Select
             style={{ width: 140, marginLeft: 10 }}
             value={version.id}
@@ -901,14 +977,27 @@ function Detail(props) {
               </Option>
             ))}
           </Select>
-          <Button type="primary" onClick={() => setVersionVisible(true)}>
-            创建版本
-          </Button>
+          {flow?.active == 0 && (
+            <Button
+              type="primary"
+              icon={<PlusOutlined />}
+              onClick={() => setVersionVisible(true)}
+            ></Button>
+          )}
+          <span style={{ marginLeft: 20 }}>{renderNode()}</span>
         </div>
         <div className={styles.btns}>
-          {user.map(item => (
-            <Avatar size="large">{item.Name}</Avatar>
-          ))}
+          <Avatar.Group style={{ marginRight: 20 }}>
+            {user.map(item => (
+              <Avatar
+                // style={{ backgroundColor: item.id == currentUser.ID ? '#008dff' : '' }}
+                size="large"
+              >
+                {item.Name}
+              </Avatar>
+            ))}
+          </Avatar.Group>
+
           {renderBtns()}
         </div>
         <input
@@ -918,6 +1007,8 @@ function Detail(props) {
           onChange={e => exportExcl(e.target.files)}
         />
       </div>
+      <TimeNode flow={flow} isAuditor={isAuditor} onApprove={onApprove}></TimeNode>
+
       {renderAlert()}
       {/* 判断是否为比对模式 */}
       {compareList.length == 2 ? (
@@ -936,17 +1027,25 @@ function Detail(props) {
               ref={sheetRef}
               onClickCell={onClickCell}
               version={version}
+              templateId={templateId}
               getUser={getUser}
+              onUpdate={onUpdate}
             />
           )}
         </div>
       )}
 
+      <CommentContent
+        title="单元格沟通记录"
+        comment={comment}
+        onSubmit={handleSubmitCell}
+        loading={loading.effects['detail/queryComment'] || loading.effects['detail/addComment']}
+      />
+
       <RightDrawer
         version={version}
         visible={commentVisible}
         onClose={() => setCommentVisible(false)}
-        cellPosition={cellPosition}
       />
       {/* <HistoryModal
         visible={historyVisible}
@@ -961,19 +1060,25 @@ function Detail(props) {
         onClose={() => setCompareVisible(false)}
         onOk={onCompare}
       />
+      <MergeModal
+        visible={mergeVisible}
+        version={version}
+        onClose={() => setMergeVisible(false)}
+        onOk={onMergeVersion}
+      />
       <ExportModal
         visible={exportVisible}
         sheet={exportDate.data}
         onClose={() => setExportVisible(false)}
         onOk={downloadExcel}
       />
-      <CommitModal
+      {/* <CommitModal
         loading={getLoading()}
         visible={commitVisible}
         version={version}
         onClose={() => setCommitVisible(false)}
         onOk={values => onCommit(values, version.id)}
-      />
+      /> */}
       <FlowModal
         visible={flowVisible}
         onClose={() => setFlowVisible(false)}
@@ -1013,8 +1118,9 @@ function Detail(props) {
   );
 }
 
-export default connect(({ detail, user, loading }) => ({
+export default connect(({ detail, user, xflow, loading }) => ({
   flow: detail.flow,
+  flowDetail: xflow.flowDetail,
   auditList: detail.auditList,
   fileList: detail.fileList,
   history: detail.history,

+ 4 - 3
src/pages/PurchaseAdmin/PurchaseList/Detail/Index.less

@@ -26,15 +26,16 @@
   display: flex;
   justify-content: space-between;
   align-items: center;
+  margin-bottom: 20px;
 }
 .btns {
-  margin: 20px 0;
+  margin: 5px 0;
   position: relative;
   display: flex;
   align-items: center;
   :global {
-    .ant-avatar {
-      margin-right: 10px;
+    .ant-btn {
+      margin-left: 10px;
     }
   }
 }

+ 96 - 30
src/pages/PurchaseAdmin/PurchaseList/Detail/LuckySheet.js

@@ -20,6 +20,16 @@ class LuckySheet extends React.Component {
     this.renderTimer = null;
     this.chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
   }
+  componentWillUnmount() {
+    this.luckysheet?.destroy();
+  }
+  componentDidUpdate(prveProps) {
+    if (prveProps.version?.id != this.props.version?.id) {
+      console.log(prveProps.version);
+      this.renderSheet();
+    }
+  }
+
   getUUID(len = 8, radix = 16) {
     var chars = this.chars;
     var uuid = [],
@@ -49,13 +59,13 @@ class LuckySheet extends React.Component {
 
     return uuid.join('');
   }
-  renderSheet(sheets) {
-    // if (!sheets) return;
-    const { onClickCell, version, getUser, data } = this.props;
+  renderSheet(currentData) {
+    const { onClickCell, version, getUser, onUpdate, templateId } = this.props;
+    const data = currentData || this.props.data;
     if (!this.luckysheet) {
       clearTimeout(this.renderTimer);
       this.renderTimer = setTimeout(() => {
-        this.renderSheet(sheets);
+        this.renderSheet(currentData);
       }, 300);
       return;
     }
@@ -76,6 +86,10 @@ class LuckySheet extends React.Component {
             delete cell.bg;
           }
         },
+        updated(operate) {
+          console.log(operate);
+          onUpdate && onUpdate();
+        },
       },
     };
     if (version) {
@@ -83,9 +97,21 @@ class LuckySheet extends React.Component {
         ...option,
         allowUpdate: true,
         gridKey: version.id,
+        templateId: templateId,
         loadUrl: `/api/v1/purchase/record/sheet?gridKey=${version.id}&JWT-TOKEN=${token}`,
-        updateUrl: `ws://47.96.12.136:8896/api/v1/ws?id=${version.id}&sid=1&JWT-TOKEN=${token}`,
+        updateUrl: `ws://47.96.12.136:8896/api/v1/ws?id=${version.id}&sid=${templateId}&JWT-TOKEN=${token}`,
+        authorityUrl: `/api/v1/purchase/bom/user/excel/col?depId=${localStorage.depId}&JWT-TOKEN=${token}`,
         getUser,
+        // workbookCreateBefore(luckysheet) {
+        //   console.log('===============================', luckysheet);
+        //   let oldConfig = JSON.parse(JSON.stringify(luckysheet.getConfig()));
+        //   setTimeout(() => {
+        //     luckysheet.setConfig({
+        //       ...oldConfig,
+        //       authority: { sheet: true, hintText },
+        //     });
+        //   }, 300);
+        // },
       };
     } else if (data && data.length > 0) {
       option.data = JSON.parse(JSON.stringify(data));
@@ -111,7 +137,7 @@ class LuckySheet extends React.Component {
         },
       ];
     }
-    // this.luckysheet.destroy();
+    this.luckysheet.destroy();
     this.luckysheet.create(option);
   }
 
@@ -123,11 +149,14 @@ class LuckySheet extends React.Component {
   // }
 
   handleLoad() {
-    const { onLoad } = this.props;
+    const { version } = this.props;
     let contentWindow = this.sheetRef.current.contentWindow;
     this.luckysheet = contentWindow.luckysheet;
     // this.luckysheet = this.luckysheet;
-    this.renderSheet();
+    // version存在  则需调用render
+    if(version) {
+      this.renderSheet();
+    }
     // onLoad && onLoad();
   }
 
@@ -249,30 +278,67 @@ class LuckySheet extends React.Component {
   isEmpty(item) {
     return (item?.v?.v ?? '') === '' && !item?.v?.ct?.s;
   }
-  mergeExcl(mergeData = []) {
-    let currentData = this.luckysheet.toJson();
+  mergeExcl(updateCell = {}) {
+    const { diff = [], add = [] } = updateCell;
+    let currentData = this.luckysheet.toJson().data;
     let luckysheet = this.luckysheet;
-    // let add = [];
-    currentData.data.forEach((sheet, index) => {
-      if (!mergeData[index]) return;
-      let celldata1 = sheet.celldata;
-      let celldata2 = mergeData[index].celldata;
-      celldata2.forEach(item => {
-        var d2Item = celldata1.find(item2 => item2.v.cid == item.v.cid);
-        if (!d2Item) {
-          delete item.v.v.bg;
-          // 将新增项添加至当前文档
-          luckysheet.setCellValue(item.r, item.c, item.v);
-          // 记录新增
-          // add.push(item);
-        }
-      });
+    console.log(updateCell);
+    diff.forEach(item => {
+      let sheet = currentData[item.sheetOrder];
+      let d1Item = sheet.celldata.find(item2 => item2.v.cid == item.v.cid);
+      // 将差异项覆盖至当前文档
+      d1Item.v = {
+        ...item.v,
+        bg: undefined,
+      };
     });
-    // let celldata1 = currentData.data[0].celldata;
-    // let celldata2 = mergeData.data[0].celldata;
-
-    // this.add = add;
-    // return add;
+    add.forEach(item => {
+      // 将新增项添加至当前文档
+      let sheet = currentData[item.sheetOrder];
+      let d1Item = sheet.celldata.find(item2 => item2.r == item.r && item2.c == item.c);
+      if (d1Item) {
+        d1Item.v = {
+          ...item.v,
+          bg: undefined,
+        };
+      } else {
+        sheet.celldata.push({
+          ...item,
+          sheetOrder: undefined,
+        });
+      }
+    });
+    currentData.forEach(sheet => {
+      delete sheet.data
+    })
+    this.renderSheet(currentData);
+    // currentData.data.forEach((sheet, index) => {
+    //   if (!mergeData[index]) return;
+    //   let celldata1 = sheet.celldata;
+    //   let celldata2 = mergeData[index].celldata;
+    //   celldata2.forEach(item => {
+    //     let bg = item.v?.bg;
+    //     let d1Item;
+    //     if (bg == DIFF_COLOR) {
+    //       delete item.v.bg;
+    //       d1Item = celldata1.find(item2 => item2.v.cid == item.v.cid);
+    //       // 将差异项覆盖至当前文档
+    //       d1Item.v = item.v;
+    //       // luckysheet.setCellValue(d1Item.r, d1Item.c, item.v);
+    //     } else if (bg == ADD_COLOR) {
+    //       delete item.v.bg;
+    //       // 将新增项添加至当前文档
+    //       // luckysheet.setCellValue(item.r, item.c, item.v);
+    //       d1Item = celldata1.find(item2 => item2.r == item.r && item2.c == item.c);
+    //       if (d1Item) {
+    //         d1Item.v = item.v;
+    //       } else {
+    //         celldata1.push(item);
+    //       }
+    //     }
+    //   });
+    // });
+    // this.renderSheet(currentData);
   }
 
   /**

+ 23 - 85
src/pages/PurchaseAdmin/PurchaseList/Detail/MergeModal.js

@@ -1,112 +1,50 @@
 import React, { useEffect, useState, useRef } from 'react';
 import { Form } from '@ant-design/compatible';
 import '@ant-design/compatible/assets/index.css';
-import { Modal, Checkbox, Row, Col, message, Tabs } from 'antd';
+import { Modal, Radio, Row, Col, message, Tabs } from 'antd';
 import { connect } from 'dva';
 
 const { TabPane } = Tabs;
 
 // 选择比对版本
-function CompareModal(props) {
-  const { visible, versionList, onClose, onOk, historyList, dispatch, version } = props;
+function MergeModal(props) {
+  const { visible, versionList, onClose, onOk, dispatch, version } = props;
 
   const [checkValue, setCheckValue] = useState([]);
-  const [tabList, setTabList] = useState([]);
-  const [active, setActive] = useState(version.id);
 
-  const onChange = check => {
-    if (check.length <= 2) {
-      setCheckValue(check);
-    } else {
-      message.error('只能比对两个文件');
-    }
-  };
-
-  const changeAcitve = versionId => {
-    setActive(versionId);
-    if (!versionId || historyList[versionId]) return;
-    // 查询历史提交
-    dispatch({
-      type: 'detail/queryHistoryList',
-      payload: {
-        version_id: versionId,
-      },
-    });
+  const onChange = e => {
+    setCheckValue(e.target.value);
   };
 
   const handleOk = () => {
-    if (checkValue.length != 2) {
-      message.error('请选择要比对的两个文件');
+    if (!checkValue) {
+      message.error('请选择要合并的版本');
     } else {
-      let checkSheet = [];
-
-      Object.values(historyList).forEach(verList => {
-        if (checkSheet.length >= 2) return;
-        verList.forEach(item => {
-          if (checkValue.indexOf(item.id) != -1) {
-            checkSheet.push(item);
-          }
-        });
-      });
-      // console.log(checkSheet);
+      let checkSheet = versionList.find(item => item.id == checkValue);
       onOk(checkSheet);
       onClose();
-      setCheckValue([]);
+      setCheckValue();
     }
   };
 
-  useEffect(() => {
-    if (version.id) {
-      setActive(version.id + '');
-    }
-  }, [version.id]);
-
-  // useEffect(() => {
-  //   let obj = {};
-  //   list.forEach(item => {
-  //     let nodeId = item.NodeInfo.id;
-  //     if (!obj[nodeId])
-  //       obj[nodeId] = {
-  //         list: [],
-  //         id: nodeId + '',
-  //         name: item.NodeInfo.node,
-  //       };
-  //     obj[nodeId].list.push(item);
-  //   });
-  //   let arr = Object.values(obj).map(item => item);
-  //   setTabList(arr);
-  //   setActive(arr[0]?.id);
-  // }, [list]);
-
   return (
-    <Modal
-      title="选择合并的版本"
-      visible={visible}
-      onCancel={onClose}
-      onOk={handleOk}
-      bodyStyle={{ paddingTop: 0 }}
-    >
-      <Checkbox.Group value={checkValue} style={{ width: '100%' }} onChange={onChange}>
-        <Tabs activeKey={active} onChange={changeAcitve}>
-          {/* {tabList.map(tab => ( */}
-          {versionList.map(version => (
-            <TabPane tab={version.version_name} key={version.id}>
-              <Row>
-                {(historyList[version.id] || []).map(item => (
-                  <Col span={8} key={item.id}>
-                    <Checkbox value={item.id}>{item.version_name}</Checkbox>
-                  </Col>
-                ))}
-              </Row>
-            </TabPane>
-          ))}
-        </Tabs>
-      </Checkbox.Group>
+    <Modal title="选择比对文件" visible={visible} onCancel={onClose} onOk={handleOk}>
+      <Radio.Group value={checkValue} style={{ width: '100%' }} onChange={onChange}>
+        <Row gutter={16}>
+          {versionList.map(v => {
+            if (v.id == version.id) return null;
+            return (
+              <Col span={8} key={v.id}>
+                <Radio value={v.id}>{v.version_name}</Radio>
+              </Col>
+            );
+          })}
+        </Row>
+      </Radio.Group>
     </Modal>
   );
 }
 
 export default connect(({ detail }) => ({
-  historyList: detail.historyList,
   versionList: detail.versionList,
-}))(CompareModal);
+}))(MergeModal);

+ 19 - 77
src/pages/PurchaseAdmin/PurchaseList/Detail/RightDrawer.js

@@ -2,11 +2,10 @@ import React, { useEffect, useState, useRef } from 'react';
 import { UserOutlined } from '@ant-design/icons';
 import { Form } from '@ant-design/compatible';
 import '@ant-design/compatible/assets/index.css';
-import { Button, Drawer, Comment, Tooltip, Avatar, List, Card, Input, Descriptions } from 'antd';
+import { Drawer, Descriptions, Card } from 'antd';
 import moment from 'moment';
 import { connect } from 'dva';
-
-const { TextArea } = Input;
+import CommentContent from '@/components/CommentContent';
 
 // 评论
 function RightDrawer(props) {
@@ -19,6 +18,7 @@ function RightDrawer(props) {
     version,
     loading,
     dispatch,
+    userList,
     cellPosition,
   } = props;
   // const [commentList, setCommentList] = useState([]);
@@ -57,104 +57,46 @@ function RightDrawer(props) {
   return (
     <Drawer
       width={600}
-      title="评论列表"
+      title="版本"
       mask={false}
       placement="right"
       onClose={onClose}
       visible={visible}
     >
-      <BomContetn />
+      <BomContetn version={version} userList={userList} />
       <CommentContent
-        title="BOM沟通记录"
+        title="版本沟通记录"
         comment={bomComment}
         onSubmit={handleSubmitBom}
         loading={
           loading.effects['detail/queryBomComment'] || loading.effects['detail/addBomComment']
         }
       />
-      <CommentContent
-        title="单元格沟通记录"
-        comment={comment}
-        onSubmit={handleSubmitCell}
-        loading={loading.effects['detail/queryComment'] || loading.effects['detail/addComment']}
-      />
+
     </Drawer>
   );
 }
 function BomContetn(props) {
+  const { version, userList } = props;
   return (
-    <Card title="BOM属性" type="inner">
+    <Card title="版本详情" type="inner">
       <Descriptions column={2}>
-        <Descriptions.Item label="版本名称">XXXXX</Descriptions.Item>
-        <Descriptions.Item label="创建人">XXXXX</Descriptions.Item>
-        <Descriptions.Item label="创建时间">XXXXX</Descriptions.Item>
-        <Descriptions.Item label="概述">XXXXX</Descriptions.Item>
+        <Descriptions.Item label="版本名称">{version.version_name}</Descriptions.Item>
+        <Descriptions.Item label="创建人">
+          {userList.find(item => item.Id == version.author)?.Name}
+        </Descriptions.Item>
+        <Descriptions.Item label="创建时间">
+          {moment(version.c_time).format('YYYY-MM-DD')}
+        </Descriptions.Item>
+        <Descriptions.Item label="概述">{version.desc}</Descriptions.Item>
       </Descriptions>
     </Card>
   );
 }
 
-function CommentContent(props) {
-  const {
-    comment: { list },
-    loading,
-    onSubmit,
-    title,
-  } = props;
-  const [value, setValue] = useState('');
-
-  return (
-    <Card title={title} type="inner" style={{ marginTop: 20 }}>
-      <List
-        className="comment-list"
-        itemLayout="horizontal"
-        dataSource={list}
-        loading={loading}
-        renderItem={item => (
-          <li>
-            <Comment
-              author={<a>{item.AuthorUser.CName}</a>}
-              avatar={<Avatar icon={<UserOutlined />} alt={item.author} />}
-              content={<p>{item.comment}</p>}
-              datetime={
-                <Tooltip title={moment(item.c_time).format('YYYY-MM-DD')}>
-                  <span>{moment(item.c_time).fromNow()}</span>
-                </Tooltip>
-              }
-            />
-          </li>
-        )}
-      />
-      <Comment
-        avatar={<Avatar icon={<UserOutlined />} />}
-        content={
-          <>
-            <Form.Item>
-              <TextArea rows={4} onChange={e => setValue(e.target.value)} value={value} />
-            </Form.Item>
-            <Form.Item>
-              <Button
-                htmlType="submit"
-                loading={loading}
-                type="primary"
-                onClick={() =>
-                  onSubmit(value, () => {
-                    setValue('');
-                  })
-                }
-              >
-                添加评论
-              </Button>
-            </Form.Item>
-          </>
-        }
-      />
-    </Card>
-  );
-}
-
-export default connect(({ detail, loading }) => ({
+export default connect(({ detail, user, loading }) => ({
   comment: detail.comment,
+  userList: user.userList,
   bomComment: detail.bomComment,
   loading: loading,
 }))(RightDrawer);

+ 36 - 19
src/pages/PurchaseAdmin/PurchaseList/Detail/TimeNode.js

@@ -2,36 +2,53 @@ import React, { useEffect, useState, useRef } from 'react';
 import { Form } from '@ant-design/compatible';
 import '@ant-design/compatible/assets/index.css';
 import { connect } from 'dva';
-import { Steps } from 'antd';
+import { Steps, Button } from 'antd';
+import styles from './Index.less';
 
 const { Step } = Steps;
 // 时间节点
 function TimeNode(props) {
   const {
     flow: { current, list, active },
-    flowDetail,
-    nodeId,
+    isAuditor,
+    onApprove,
   } = props;
 
   if (active !== 0) {
     return (
-      <Steps current={current}>
-        {list.FlowNodes.map(item => (
-          <Step
-            key={item.id}
-            title={item.node}
-            description={`审批人:${item?.user.map(item => item.CName).join('、') || '-'}`}
-          />
-        ))}
-      </Steps>
+      <div className={styles.top}>
+        <Steps current={current}>
+          {list.FlowNodes.map(item => (
+            <Step
+              key={item.id}
+              title={item.node}
+              description={`审批人:${item?.user
+                .map(user => user.CName)
+                .join(item.seq_relate == 1 ? '或' : '且') || '-'}`}
+            />
+          ))}
+        </Steps>
+        <div className={styles.btns} style={{ marginLeft: 80 }}>
+          {isAuditor && (
+            <>
+              <Button type="primary" onClick={() => onApprove(true)}>
+                审批通过
+              </Button>
+              <Button onClick={() => onApprove(false)} danger>
+                审批拒绝
+              </Button>
+            </>
+          )}
+        </div>
+      </div>
     );
-  } else {
-    if (!flowDetail?.nodes) return;
-    const node = flowDetail.nodes.find(item => item.Id == nodeId);
-    return `当前节点:${node?.label || '-'}`;
   }
+  // else {
+  //   if (!flowDetail?.nodes) return;
+  //   const node = flowDetail.nodes.find(item => item.Id == nodeId);
+  //   return `当前节点:${node?.label || '-'}`;
+  // }
+  return null;
 }
 
-export default connect(({ xflow }) => ({
-  flowDetail: xflow.flowDetail,
-}))(TimeNode);
+export default TimeNode;

+ 15 - 0
src/pages/PurchaseAdmin/PurchaseList/Detail/models/detail.js

@@ -27,6 +27,7 @@ import {
   addBomComment,
   queryBomComment,
   approve,
+  queryAuthority,
 } from '@/services/boom';
 import { queryRole } from '@/services/SysAdmin';
 import { setCurrentUser } from '@/utils/authority';
@@ -66,9 +67,20 @@ export default {
     bomComment: { list: [] },
     fileList: [],
     roleList: [],
+    authority: []
   },
 
   effects: {
+    *queryAuthority({ payload, callback }, { call, put }) {
+      const response = yield call(queryAuthority, payload);
+      if (response) {
+        yield put({
+          type: 'save',
+          payload: { authority: response.data },
+        });
+       
+      }
+    },
     *queryProjectRecord({ payload, callback }, { call, put }) {
       const response = yield call(queryProjectRecord, payload);
       if (response) {
@@ -156,6 +168,9 @@ export default {
         });
       }
     },
+    *saveSheet({ payload }, { call }) {
+      yield call(commitSheet, payload);
+    },
     *queryBomComment({ payload }, { call, put }) {
       const response = yield call(queryBomComment, payload);
       if (response) {

+ 2 - 2
src/pages/PurchaseAdmin/PurchaseList/Flow/AuditNodeModal.js

@@ -60,8 +60,8 @@ function AuditModal(props) {
         </Form.Item>
         <Form.Item initialValue={data?.seq_relate || 1} label="审批关系" name="seq_relate">
           <Radio.Group>
-            <Radio value={1}></Radio>
-            <Radio value={2}></Radio>
+            <Radio value={1}></Radio>
+            <Radio value={2}></Radio>
           </Radio.Group>
         </Form.Item>
       </Form>

+ 11 - 7
src/pages/PurchaseAdmin/PurchaseList/Flow/Flow.js

@@ -3,7 +3,7 @@ import { connect } from 'dva';
 import React, { useEffect } from 'react';
 import { UnityAction } from '@/utils/utils';
 import { Button } from 'antd';
-import Link from 'umi/link';
+import router from 'umi/router';
 
 @connect(({ xflow }) => ({ flowDetail: xflow.flowDetail }))
 class FlowPage extends React.PureComponent {
@@ -31,14 +31,19 @@ class FlowPage extends React.PureComponent {
   }
 
   componentDidMount() {
-    const { dispatch } = this.props;
+    const {
+      dispatch,
+      match: {
+        params: { flowId },
+      },
+    } = this.props;
     dispatch({
       type: 'xflow/queryOSSData',
     });
     dispatch({
       type: 'xflow/queryBoomFlowDetail',
       payload: {
-        id: 1,
+        id: flowId,
       },
     });
     dispatch({
@@ -57,10 +62,9 @@ class FlowPage extends React.PureComponent {
     return (
       <div>
         {/* <Form></Form> */}
-        <Link to="/home/audit" style={{ marginBottom: 20 }}>
-          <Button style={{ marginBottom: 20 }}>审批流管理</Button>
-        </Link>
-
+        <Button style={{ marginBottom: 20 }} onClick={() => router.go(-1)}>
+          返回
+        </Button>
         <Flow
           meta={{ type: 'edit', flowId: 1 }}
           flowDetail={flowDetail}

+ 1188 - 0
src/pages/PurchaseAdmin/PurchaseList/Flow/FlowDetail.json

@@ -0,0 +1,1188 @@
+{
+  "start_node": "41561012",
+  "nodes": [
+    {
+      "id": "41561012",
+      "renderKey": "custom-rect",
+      "name": "custom-rect",
+      "label": "BOM-1",
+      "width": 120,
+      "height": 50,
+      "ports": {
+        "items": [
+          { "group": "top", "id": "293a90b5" },
+          { "group": "right", "id": "92334433" },
+          { "group": "bottom", "id": "c2ab5849" },
+          { "group": "left", "id": "6079a903" }
+        ],
+        "groups": {
+          "top": {
+            "position": { "name": "top" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          },
+          "right": {
+            "position": { "name": "right" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          },
+          "bottom": {
+            "position": { "name": "bottom" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          },
+          "left": {
+            "position": { "name": "left" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          }
+        }
+      },
+      "isCustom": true,
+      "parentKey": "1",
+      "x": -580,
+      "y": -150,
+      "zIndex": 10
+    },
+    {
+      "id": "975bf288",
+      "renderKey": "custom-circle",
+      "name": "custom-circle",
+      "label": "三级审批",
+      "width": 90,
+      "height": 90,
+      "ports": {
+        "items": [
+          { "group": "top", "id": "a61170c3" },
+          { "group": "right", "id": "821f59c0" },
+          { "group": "bottom", "id": "17360bc4" },
+          { "group": "left", "id": "15d1b217" }
+        ],
+        "groups": {
+          "top": {
+            "position": { "name": "top" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          },
+          "right": {
+            "position": { "name": "right" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          },
+          "bottom": {
+            "position": { "name": "bottom" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          },
+          "left": {
+            "position": { "name": "left" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          }
+        }
+      },
+      "isCustom": true,
+      "parentKey": "1",
+      "x": -369,
+      "y": -170,
+      "zIndex": 10
+    },
+    {
+      "id": "5764f3ce",
+      "renderKey": "custom-rect",
+      "name": "custom-rect",
+      "label": "投标清单",
+      "width": 120,
+      "height": 50,
+      "ports": {
+        "items": [
+          { "group": "top", "id": "4b4d9fa6" },
+          { "group": "right", "id": "ce88d7e2" },
+          { "group": "bottom", "id": "e69d8709" },
+          { "group": "left", "id": "c29d7b43" }
+        ],
+        "groups": {
+          "top": {
+            "position": { "name": "top" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          },
+          "right": {
+            "position": { "name": "right" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          },
+          "bottom": {
+            "position": { "name": "bottom" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          },
+          "left": {
+            "position": { "name": "left" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          }
+        }
+      },
+      "isCustom": true,
+      "parentKey": "1",
+      "x": -162,
+      "y": -150,
+      "zIndex": 10
+    },
+    {
+      "id": "5359e23c",
+      "renderKey": "custom-circle",
+      "name": "custom-circle",
+      "label": "三级审批",
+      "width": 90,
+      "height": 90,
+      "ports": {
+        "items": [
+          { "group": "top", "id": "331cd291" },
+          { "group": "right", "id": "ff6724ee" },
+          { "group": "bottom", "id": "16b4df46" },
+          { "group": "left", "id": "34c9dbc6" }
+        ],
+        "groups": {
+          "top": {
+            "position": { "name": "top" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          },
+          "right": {
+            "position": { "name": "right" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          },
+          "bottom": {
+            "position": { "name": "bottom" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          },
+          "left": {
+            "position": { "name": "left" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          }
+        }
+      },
+      "isCustom": true,
+      "parentKey": "1",
+      "x": -369,
+      "y": 6,
+      "zIndex": 10
+    },
+    {
+      "id": "c28a18d3",
+      "renderKey": "custom-rect",
+      "name": "custom-rect",
+      "label": "采购执行",
+      "width": 120,
+      "height": 50,
+      "ports": {
+        "items": [
+          { "group": "top", "id": "f71ce55b" },
+          { "group": "right", "id": "e67dc19c" },
+          { "group": "bottom", "id": "a06aba2c" },
+          { "group": "left", "id": "b578cc26" }
+        ],
+        "groups": {
+          "top": {
+            "position": { "name": "top" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          },
+          "right": {
+            "position": { "name": "right" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          },
+          "bottom": {
+            "position": { "name": "bottom" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          },
+          "left": {
+            "position": { "name": "left" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          }
+        }
+      },
+      "isCustom": true,
+      "parentKey": "1",
+      "x": -162,
+      "y": 26,
+      "zIndex": 10
+    },
+    {
+      "id": "5216c5dc",
+      "renderKey": "custom-rect",
+      "name": "custom-rect",
+      "label": "采购合同",
+      "width": 120,
+      "height": 50,
+      "ports": {
+        "items": [
+          { "group": "top", "id": "0fc44196" },
+          { "group": "right", "id": "d2030f1b" },
+          { "group": "bottom", "id": "188c9b68" },
+          { "group": "left", "id": "4e9ce7ad" }
+        ],
+        "groups": {
+          "top": {
+            "position": { "name": "top" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          },
+          "right": {
+            "position": { "name": "right" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          },
+          "bottom": {
+            "position": { "name": "bottom" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          },
+          "left": {
+            "position": { "name": "left" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          }
+        }
+      },
+      "isCustom": true,
+      "parentKey": "1",
+      "x": -580,
+      "y": 26,
+      "zIndex": 10
+    },
+    {
+      "id": "8c1f18d0",
+      "renderKey": "custom-rect",
+      "name": "custom-rect",
+      "label": "预算",
+      "width": 120,
+      "height": 50,
+      "ports": {
+        "items": [
+          { "group": "top", "id": "b58731c5" },
+          { "group": "right", "id": "b3dfbc16" },
+          { "group": "bottom", "id": "89c0bc16" },
+          { "group": "left", "id": "a3adcac9" }
+        ],
+        "groups": {
+          "top": {
+            "position": { "name": "top" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          },
+          "right": {
+            "position": { "name": "right" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          },
+          "bottom": {
+            "position": { "name": "bottom" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          },
+          "left": {
+            "position": { "name": "left" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          }
+        }
+      },
+      "isCustom": true,
+      "parentKey": "1",
+      "x": -384,
+      "y": -304,
+      "zIndex": 10
+    },
+    {
+      "id": "1aed14d1",
+      "renderKey": "custom-rect",
+      "name": "custom-rect",
+      "label": "初版PSR",
+      "width": 120,
+      "height": 50,
+      "ports": {
+        "items": [
+          { "group": "top", "id": "7f8dc65b" },
+          { "group": "right", "id": "9a699e3f" },
+          { "group": "bottom", "id": "d348b56a" },
+          { "group": "left", "id": "47317157" }
+        ],
+        "groups": {
+          "top": {
+            "position": { "name": "top" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          },
+          "right": {
+            "position": { "name": "right" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          },
+          "bottom": {
+            "position": { "name": "bottom" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          },
+          "left": {
+            "position": { "name": "left" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          }
+        }
+      },
+      "isCustom": true,
+      "parentKey": "1",
+      "x": -162,
+      "y": -304,
+      "zIndex": 10
+    },
+    {
+      "id": "4651130e",
+      "renderKey": "custom-circle",
+      "name": "custom-circle",
+      "label": "PSR审批",
+      "width": 90,
+      "height": 90,
+      "ports": {
+        "items": [
+          { "group": "top", "id": "cf1c4df0" },
+          { "group": "right", "id": "1eb352b0" },
+          { "group": "bottom", "id": "83b59198" },
+          { "group": "left", "id": "94f485b5" }
+        ],
+        "groups": {
+          "top": {
+            "position": { "name": "top" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          },
+          "right": {
+            "position": { "name": "right" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          },
+          "bottom": {
+            "position": { "name": "bottom" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          },
+          "left": {
+            "position": { "name": "left" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          }
+        }
+      },
+      "isCustom": true,
+      "parentKey": "1",
+      "x": 213,
+      "y": -324,
+      "zIndex": 10
+    },
+    {
+      "id": "a48131e0",
+      "renderKey": "custom-rect",
+      "name": "custom-rect",
+      "label": "合同清单",
+      "width": 120,
+      "height": 50,
+      "ports": {
+        "items": [
+          { "group": "top", "id": "3ca1320c" },
+          { "group": "right", "id": "4dee75d9" },
+          { "group": "bottom", "id": "0f72f2ba" },
+          { "group": "left", "id": "bac7962b" }
+        ],
+        "groups": {
+          "top": {
+            "position": { "name": "top" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          },
+          "right": {
+            "position": { "name": "right" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          },
+          "bottom": {
+            "position": { "name": "bottom" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          },
+          "left": {
+            "position": { "name": "left" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          }
+        }
+      },
+      "isCustom": true,
+      "parentKey": "1",
+      "x": 198,
+      "y": -150,
+      "zIndex": 10
+    },
+    {
+      "id": "b57b57c8",
+      "renderKey": "custom-rect",
+      "name": "custom-rect",
+      "label": "终版PSR",
+      "width": 120,
+      "height": 50,
+      "ports": {
+        "items": [
+          { "group": "top", "id": "04c81e99" },
+          { "group": "right", "id": "0d594eef" },
+          { "group": "bottom", "id": "17ff5fe6" },
+          { "group": "left", "id": "73307680" }
+        ],
+        "groups": {
+          "top": {
+            "position": { "name": "top" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          },
+          "right": {
+            "position": { "name": "right" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          },
+          "bottom": {
+            "position": { "name": "bottom" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          },
+          "left": {
+            "position": { "name": "left" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          }
+        }
+      },
+      "isCustom": true,
+      "parentKey": "1",
+      "x": 386,
+      "y": -150,
+      "zIndex": 10
+    },
+    {
+      "id": "3631eae9",
+      "renderKey": "custom-rect",
+      "name": "custom-rect",
+      "label": "采购清单",
+      "width": 120,
+      "height": 50,
+      "ports": {
+        "items": [
+          { "group": "top", "id": "2aae2a71" },
+          { "group": "right", "id": "e3a42bda" },
+          { "group": "bottom", "id": "0f06668a" },
+          { "group": "left", "id": "8e0bff55" }
+        ],
+        "groups": {
+          "top": {
+            "position": { "name": "top" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          },
+          "right": {
+            "position": { "name": "right" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          },
+          "bottom": {
+            "position": { "name": "bottom" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          },
+          "left": {
+            "position": { "name": "left" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          }
+        }
+      },
+      "isCustom": true,
+      "parentKey": "1",
+      "x": 198,
+      "y": 26,
+      "zIndex": 10
+    },
+    {
+      "id": "3fb8d302",
+      "renderKey": "custom-circle",
+      "name": "custom-circle",
+      "label": "三级审批",
+      "width": 90,
+      "height": 90,
+      "ports": {
+        "items": [
+          { "group": "top", "id": "8ab6c3f6" },
+          { "group": "right", "id": "205f1437" },
+          { "group": "bottom", "id": "761ad2b5" },
+          { "group": "left", "id": "22d16375" }
+        ],
+        "groups": {
+          "top": {
+            "position": { "name": "top" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          },
+          "right": {
+            "position": { "name": "right" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          },
+          "bottom": {
+            "position": { "name": "bottom" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          },
+          "left": {
+            "position": { "name": "left" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          }
+        }
+      },
+      "isCustom": true,
+      "parentKey": "1",
+      "x": 42,
+      "y": -170,
+      "zIndex": 10
+    },
+    {
+      "id": "399bddb7",
+      "renderKey": "custom-circle",
+      "name": "custom-circle",
+      "label": "三级审批",
+      "width": 90,
+      "height": 90,
+      "ports": {
+        "items": [
+          { "group": "top", "id": "99f69f24" },
+          { "group": "right", "id": "0bccd839" },
+          { "group": "bottom", "id": "42c0d925" },
+          { "group": "left", "id": "58f52f2c" }
+        ],
+        "groups": {
+          "top": {
+            "position": { "name": "top" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          },
+          "right": {
+            "position": { "name": "right" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          },
+          "bottom": {
+            "position": { "name": "bottom" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          },
+          "left": {
+            "position": { "name": "left" },
+            "attrs": {
+              "circle": {
+                "r": 4,
+                "magnet": true,
+                "stroke": "#31d0c6",
+                "strokeWidth": 2,
+                "fill": "#fff",
+                "style": { "visibility": "hidden" }
+              }
+            },
+            "zIndex": 10
+          }
+        }
+      },
+      "isCustom": true,
+      "parentKey": "1",
+      "x": 42,
+      "y": 6,
+      "zIndex": 10
+    }
+  ],
+  "edges": [
+    {
+      "id": "41561012:92334433-975bf288:15d1b217",
+      "source": { "cell": "41561012", "port": "92334433" },
+      "target": { "cell": "975bf288", "port": "15d1b217" }
+    },
+    {
+      "id": "975bf288:821f59c0-5764f3ce:c29d7b43",
+      "source": { "cell": "975bf288", "port": "821f59c0" },
+      "target": { "cell": "5764f3ce", "port": "c29d7b43" }
+    },
+    {
+      "id": "975bf288:a61170c3-8c1f18d0:89c0bc16",
+      "source": { "cell": "975bf288", "port": "a61170c3" },
+      "target": { "cell": "8c1f18d0", "port": "89c0bc16" }
+    },
+    {
+      "id": "8c1f18d0:a3adcac9-41561012:293a90b5",
+      "source": { "cell": "8c1f18d0", "port": "a3adcac9" },
+      "target": { "cell": "41561012", "port": "293a90b5" }
+    },
+    {
+      "id": "5764f3ce:4b4d9fa6-1aed14d1:d348b56a",
+      "source": { "cell": "5764f3ce", "port": "4b4d9fa6" },
+      "target": { "cell": "1aed14d1", "port": "d348b56a" }
+    },
+    {
+      "id": "1aed14d1:9a699e3f-4651130e:94f485b5",
+      "source": { "cell": "1aed14d1", "port": "9a699e3f" },
+      "target": { "cell": "4651130e", "port": "94f485b5" }
+    },
+    {
+      "id": "5764f3ce:ce88d7e2-3fb8d302:22d16375",
+      "source": { "cell": "5764f3ce", "port": "ce88d7e2" },
+      "target": { "cell": "3fb8d302", "port": "22d16375" }
+    },
+    {
+      "id": "3fb8d302:205f1437-a48131e0:bac7962b",
+      "source": { "cell": "3fb8d302", "port": "205f1437" },
+      "target": { "cell": "a48131e0", "port": "bac7962b" }
+    },
+    {
+      "id": "a48131e0:4dee75d9-b57b57c8:73307680",
+      "source": { "cell": "a48131e0", "port": "4dee75d9" },
+      "target": { "cell": "b57b57c8", "port": "73307680" }
+    },
+    {
+      "id": "a48131e0:0f72f2ba-3631eae9:2aae2a71",
+      "source": { "cell": "a48131e0", "port": "0f72f2ba" },
+      "target": { "cell": "3631eae9", "port": "2aae2a71" }
+    },
+    {
+      "id": "3631eae9:8e0bff55-399bddb7:0bccd839",
+      "source": { "cell": "3631eae9", "port": "8e0bff55" },
+      "target": { "cell": "399bddb7", "port": "0bccd839" }
+    },
+    {
+      "id": "399bddb7:58f52f2c-c28a18d3:e67dc19c",
+      "source": { "cell": "399bddb7", "port": "58f52f2c" },
+      "target": { "cell": "c28a18d3", "port": "e67dc19c" }
+    },
+    {
+      "id": "c28a18d3:b578cc26-5359e23c:ff6724ee",
+      "source": { "cell": "c28a18d3", "port": "b578cc26" },
+      "target": { "cell": "5359e23c", "port": "ff6724ee" }
+    },
+    {
+      "id": "5359e23c:34c9dbc6-5216c5dc:d2030f1b",
+      "source": { "cell": "5359e23c", "port": "34c9dbc6" },
+      "target": { "cell": "5216c5dc", "port": "d2030f1b" }
+    },
+    {
+      "id": "b57b57c8:04c81e99-4651130e:1eb352b0",
+      "source": { "cell": "b57b57c8", "port": "04c81e99" },
+      "target": { "cell": "4651130e", "port": "1eb352b0" }
+    }
+  ]
+}

+ 50 - 0
src/pages/PurchaseAdmin/PurchaseList/Flow/FlowModal.js

@@ -0,0 +1,50 @@
+import React, { useEffect } from 'react';
+import { Modal, Input, Table, Select, Form, Radio } from 'antd';
+import FlowDetail from './FlowDetail.json';
+
+const { Option } = Select;
+
+function FlowModal(props) {
+  const { visible, onCancel, onOk, projectList, loading } = props;
+  const [form] = Form.useForm();
+  const formLayout = { labelCol: { span: 4 }, wrapperCol: { span: 14 } };
+
+  const handleOk = async () => {
+    let fieldsValue = await form.validateFields();
+    onOk({
+      ...fieldsValue,
+      ...FlowDetail,
+      project_id: Number(fieldsValue.project_id)
+    });
+  };
+
+  useEffect(() => {
+    if (visible) form.resetFields();
+  }, [visible]);
+
+  return (
+    <Modal
+      confirmLoading={loading}
+      destroyOnClose
+      title="流程"
+      visible={visible}
+      onCancel={onCancel}
+      onOk={handleOk}
+    >
+      <Form {...formLayout} form={form}>
+        <Form.Item label="流程名称" name="name">
+          <Input />
+        </Form.Item>
+        <Form.Item label="所属项目" name="project_id">
+          <Select>
+            {projectList.map(item => (
+              <Option key={item.ID}>{item.Name}</Option>
+            ))}
+          </Select>
+        </Form.Item>
+      </Form>
+    </Modal>
+  );
+}
+
+export default FlowModal;

+ 79 - 0
src/pages/PurchaseAdmin/PurchaseList/Flow/List.js

@@ -0,0 +1,79 @@
+import React, { useState, useEffect } from 'react';
+import { Form, Select, Button, Table, Input, Checkbox, Divider } from 'antd';
+import { connect } from 'dva';
+import FlowModal from './FlowModal';
+import router from 'umi/router';
+import Link from 'umi/link';
+
+const { Option } = Select;
+
+function List(props) {
+  const { userList, list, dispatch, projectList } = props;
+  const [visible, setVisible] = useState(false);
+  const columns = [
+    {
+      title: '工作流',
+      dataIndex: 'Name',
+    },
+    {
+      title: '所属项目',
+      dataIndex: ['Project', 'Name'],
+    },
+    {
+      title: '操作',
+      render: (item, index) => (
+        <>
+          <a onClick={() => router.push(`/home/flow/${item.Id}`)}>查看</a>
+        </>
+      ),
+    },
+  ];
+
+  const onOk = values => {
+    console.log(values);
+    dispatch({
+      type: 'flow/addFlow',
+      payload: values,
+      callback: () => {
+        setVisible(false);
+      },
+    });
+  };
+
+  useEffect(() => {
+    dispatch({
+      type: 'flow/queryFlowList',
+    });
+    dispatch({
+      type: 'flow/queryProject',
+    });
+  }, []);
+
+  return (
+    <div>
+      <div style={{ marginBottom: 20 }}>
+        <Link to="/home/audit" style={{ marginRight: 20 }}>
+          <Button>审批流管理</Button>
+        </Link>
+        <Button type="primary" onClick={() => setVisible(true)}>
+          新增工作流
+        </Button>
+      </div>
+
+      <Table rowKey="Id" dataSource={list} columns={columns} />
+
+      <FlowModal
+        visible={visible}
+        projectList={projectList}
+        onCancel={() => setVisible(false)}
+        onOk={onOk}
+      />
+    </div>
+  );
+}
+export default connect(({ user, flow, loading }) => ({
+  userList: user.list,
+  list: flow.flowList,
+  projectList: flow.projectList,
+  loading: loading.models.purchaseList2,
+}))(List);

+ 33 - 0
src/pages/PurchaseAdmin/PurchaseList/Flow/models/flow.js

@@ -2,9 +2,12 @@ import {
   queryAuditList,
   addAudit,
   addAuditNode,
+  addFlow,
   queryBoomFlowDetail,
   updateNode,
+  queryFlowList,
 } from '@/services/boom';
+import { queryProject } from '@/services/PurchaseList';
 import { message } from 'antd';
 
 export default {
@@ -12,9 +15,21 @@ export default {
   state: {
     flowDetail: { nodes: [], edges: [] },
     auditList: [],
+    flowList: [],
+    projectList: [],
   },
 
   effects: {
+    *addFlow({ payload, callback }, { call, put }) {
+      const res = yield call(addFlow, payload);
+      if (res) {
+        message.success('添加成功');
+        callback && callback();
+        yield put({
+          type: 'queryFlowList',
+        });
+      }
+    },
     *queryBoomFlowDetail({ payload }, { call, put }) {
       const data = yield call(queryBoomFlowDetail, payload);
       console.log(data);
@@ -23,6 +38,24 @@ export default {
         payload: { flowDetail: data },
       });
     },
+    *queryProject({ callback }, { call, put }) {
+      const response = yield call(queryProject);
+      if (response) {
+        yield put({
+          type: 'save',
+          payload: {
+            projectList: response.data.list,
+          },
+        });
+      }
+    },
+    *queryFlowList({ payload }, { call, put }) {
+      const res = yield call(queryFlowList, payload);
+      yield put({
+        type: 'save',
+        payload: { flowList: res.data },
+      });
+    },
     *updateNode({ payload }, { call, put }) {
       const data = yield call(updateNode, payload);
       console.log(data);

+ 2 - 2
src/pages/PurchaseAdmin/PurchaseList/Index.js

@@ -80,8 +80,8 @@ function LayoutDetail(props) {
                   <Link to="/home">采购清单</Link>
                 </Menu.Item>
               {/* )} */}
-              <Menu.Item key="/home/flow">
-                <Link to="/home/flow">流程图</Link>
+              <Menu.Item key="/home/flow-list">
+                <Link to="/home/flow-list">流程图</Link>
               </Menu.Item>
             </Menu>
           </div>

+ 19 - 16
src/pages/PurchaseAdmin/PurchaseList/List/NewList.js

@@ -4,38 +4,37 @@ import { connect } from 'dva';
 import router from 'umi/router';
 
 function List(props) {
-  const { excel, loading, dispatch } = props;
+  const { excel, loading, project, dispatch } = props;
 
   const columns = [
     {
       title: '名称',
-      dataIndex: 'name',
+      width: '35%',
+      render: item => item.version_name || item.name,
     },
-    // {
-    //   title: '所属项目',
-    //   dataIndex: 'project_id',
-    //   render: id => {
-    //     return project.list.find(item => item.ID == id)?.Name;
-    //   },
-    // },
     {
-      title: '版本名称',
-      dataIndex: 'version_name',
+      title: '所属项目',
+      width: '35%',
+      render: item => {
+        if (!item.is_parent) return '';
+        return project.list.find(p => p.ID == item.project_id)?.Name;
+      },
     },
     {
       title: '操作',
-      render: record => (
-        <>
+      render: record => {
+        if (record.is_parent) return null;
+        return (
           <a
             onClick={() => {
               localStorage.excelId = record.id;
-              router.push(`/home/detail/${record.project_id}`);
+              router.push(`/home/detail/${record.project_id}/${record.template_id}`);
             }}
           >
             查看
           </a>
-        </>
-      ),
+        );
+      },
     },
   ];
   const queryList = page => {
@@ -56,6 +55,9 @@ function List(props) {
         pageSize: 20,
       },
     });
+    dispatch({
+      type: 'newList/queryProject',
+    });
   }, []);
 
   return (
@@ -74,5 +76,6 @@ function List(props) {
 
 export default connect(({ newList, loading }) => ({
   excel: newList.excel,
+  project: newList.project,
   loading: loading.models.newList,
 }))(List);

+ 32 - 5
src/pages/PurchaseAdmin/PurchaseList/List/models/newList.js

@@ -1,5 +1,7 @@
 import { queryProjectRecord } from '@/services/boom';
-
+import {
+  queryProject,
+} from '@/services/PurchaseList';
 import { message } from 'antd';
 
 export default {
@@ -9,17 +11,42 @@ export default {
       list: [],
       pagination: {},
     },
+    project: {
+      list: [],
+      pagination: false,
+    },
   },
 
   effects: {
     *queryProjectRecord({ payload = {}, callback }, { call, put }) {
-      const response = yield call(queryProjectRecord, payload);
+      const { data } = yield call(queryProjectRecord, payload);
+      let temp = {};
+      data.list.forEach(item => {
+        if (!temp[item.template_id]) {
+          temp[item.template_id] = {
+            id: `template-${item.template_id}`,
+            name: item.name,
+            project_id: item.project_id,
+            is_parent: true,
+            children: [],
+          };
+        }
+        temp[item.template_id].children.push(item);
+      });
+      data.list = Object.values(temp);
+      yield put({
+        type: 'save',
+        payload: { excel: data },
+      });
+    },
+    *queryProject({ callback }, { call, put }) {
+      const response = yield call(queryProject);
       if (response) {
-        console.log(response);
-        const flow = response.data.list[0];
         yield put({
           type: 'save',
-          payload: { excel: response.data },
+          payload: {
+            project: response.data,
+          },
         });
       }
     },

+ 0 - 1
src/pages/PurchaseAdmin/PurchaseList/Login/Login.less

@@ -82,7 +82,6 @@
       outline-style: none;
       border-bottom: 1px solid #D8D8D8;
       border-radius: 0px;
-      padding-left: 38px !important;
     }
     i {
       color: #42a0e0;

+ 29 - 7
src/services/boom.js

@@ -11,23 +11,39 @@ import { stringify } from 'qs';
 export async function queryRecord(params) {
   return request(`/purchase/record?${stringify(params)}`);
 }
+// 查询全部工作流
+export async function queryFlowList(params) {
+  return request(`/purchase/bom/flows?${stringify(params)}`);
+}
+
+// 根据节点id查询所有version
+export async function queryVserionByNode(params) {
+  return request(`/purchase/bom/flow/node?${stringify(params)}`);
+}
 
 export async function commitSheet(params) {
   return request(`/purchase/record`, {
     method: 'POST',
-    body: params
+    body: params,
   });
 }
 export async function approve(params) {
   return request(`/purchase/audit/status`, {
     method: 'POST',
-    body: params
+    body: params,
+  });
+}
+export async function queryAuthority(params) {
+  const depId = localStorage.depId;
+  return request(`/purchase/bom/user/excel/col?depId=${depId}`, {
+    method: 'POST',
+    body: params,
   });
 }
 export async function addBomComment(params) {
   return request(`/purchase/comment`, {
     method: 'POST',
-    body: params
+    body: params,
   });
 }
 export async function queryBomComment(params) {
@@ -49,12 +65,12 @@ export async function queryBomComment(params) {
 export async function submitNextNode(params) {
   return request(`/purchase/next/node/submit`, {
     method: 'POST',
-    body: params
+    body: params,
   });
 }
 
 export async function queryDetail(params) {
-  let response =await request(`/purchase/record?${stringify(params)}`);
+  let response = await request(`/purchase/record?${stringify(params)}`);
   let sheet = response.data;
   sheet.data = JSON.parse(sheet.data || '[]');
   sheet.data.forEach(item => {
@@ -62,7 +78,7 @@ export async function queryDetail(params) {
     item.celldata = JSON.parse(item.cell_data || '[]');
     delete item.cell_data;
   });
-  return sheet
+  return sheet;
 }
 export async function queryHistoryDetail(params) {
   return request(`/purchase/record/history/detail?${stringify(params)}`);
@@ -76,7 +92,7 @@ export async function queryBoomFlowList(params) {
 }
 /**
  * 查看项目流程列表
- * project_id 
+ * project_id
  */
 export async function queryProjectRecord(params) {
   return request(`/purchase/bom/project/record?${stringify(params)}`);
@@ -216,6 +232,12 @@ export async function addAudit(data) {
     body: data,
   });
 }
+export async function addFlow(data) {
+  return request(`/purchase/bom/flow/info`, {
+    method: 'POST',
+    body: data,
+  });
+}
 /**
  *  [
       {

Some files were not shown because too many files changed in this diff