소스 검색

Squashed commit of the following:
合并Oa审批相关修改
commit 5ebec549350be80379fa3a62b9e84436fddce9f9
Author: Renxy <18510891294@163.com>
Date: Tue Jun 13 17:27:17 2023 +0800

删除log

commit f4dc257905af71235bba619ed4d80820e07efb5b
Author: Renxy <18510891294@163.com>
Date: Tue Jun 13 17:24:50 2023 +0800

审批节点支持选择多人依次审批

commit f87dbf6acfc4f5feff9c913272a2d381bdb4f080
Author: Renxy <18510891294@163.com>
Date: Tue Jun 13 13:44:42 2023 +0800

Squashed commit of the following:
合并develop
commit bc3dfed8bc8dfbe4e5b45676fe92b01b6de35b43
Author: Renxy <18510891294@163.com>
Date: Tue Jun 13 13:41:02 2023 +0800

合同管理兼容旧审批流程

commit ea7c41df6b110fef53edc8e5cea0001f5cb3be1e
Author: Renxy <18510891294@163.com>
Date: Tue Jun 13 11:14:22 2023 +0800

抄送人修改

Renxy 2 년 전
부모
커밋
07d9ec5bad

+ 9 - 7
src/components/Flow/node/auditNode/index.tsx

@@ -68,8 +68,10 @@ const CustomRect = (props: any) => {
             return roleList.find((item: any) => item.ID == audits[0].value)
               ?.Name;
           case IDTYPE.USER:
-            return userList.find((item: any) => item.ID == audits[0].value)
-              ?.CName;
+            const nameList = audits.map((item: any) => {
+              return userList.find((user: any) => user.ID == item.value)?.CName;
+            });
+            return nameList.join('、');
           case IDTYPE.LEADER:
             return '部门主管';
         }
@@ -82,26 +84,26 @@ const CustomRect = (props: any) => {
   // const type: TYPE = 0;
   const titleDom = () => {
     let color = COLOR.AUDIT;
-    let text = label == '动作节点' ? TITLETEXT.AUDIT : label;
+    let text = label;
     switch (type) {
       case TYPE.AUDIT:
         color = COLOR.AUDIT;
-        text = TITLETEXT.AUDIT;
+        text = label !== '动作节点' ? label : TITLETEXT.AUDIT;
         break;
       case TYPE.INITIATOR:
         color = COLOR.INITIATOR;
-        text = TITLETEXT.INITIATOR;
+        text = label !== '动作节点' ? label : TITLETEXT.INITIATOR;
         break;
       case TYPE.COPYMAN:
         color = COLOR.COPYMAN;
-        text = TITLETEXT.COPYMAN;
+        text = label !== '动作节点' ? label : TITLETEXT.COPYMAN;
         break;
     }
     return (
       <div
         style={{
           width: '100%',
-          height: `${fontSize + 16}px`,
+          height: '22px',
           paddingLeft: '6px',
           backgroundColor: color,
           color: 'white',

+ 75 - 13
src/components/Flow/node/auditNode/mapServe.tsx

@@ -51,6 +51,7 @@ export interface IConfig {
   process_code?: string;
   type: TYPE;
   initiator: {
+    label: string; //描述
     type: string;
     value: number;
     origin?: string | number;
@@ -58,10 +59,12 @@ export interface IConfig {
   }[];
   //审批人目前只支持单选角色或者单选人
   audits: {
+    label: string; //描述
     type: string;
     value: number;
     origin?: string | number;
     name?: string;
+    is_cc: number;
   }[];
 }
 
@@ -123,7 +126,6 @@ const Component = (props: any) => {
     console.log(bool);
     setIsRole(bool);
   }, []);
-  console.log('===================', nodeConfig);
 
   const handleTreeChange = (values: (string | number)[]) => {
     const newValues = values.map((cur) => {
@@ -134,7 +136,7 @@ const Component = (props: any) => {
           origin: cur,
         };
       } else {
-        return { type: IDTYPE.DEP, value: cur, origin: cur };
+        return { type: IDTYPE.DEP, value: cur, origin: cur, is_cc: 1 };
       }
     });
     onNodeConfigChange('initiator', newValues);
@@ -159,12 +161,21 @@ const Component = (props: any) => {
             <label>审批人</label>
             <Select
               showSearch
+              mode="multiple"
               style={{ width: '100%' }}
               value={nodeConfig.audits?.map((item) => item.value)}
               onChange={(value) => {
-                onNodeConfigChange('audits', [
-                  { type: IDTYPE.USER, value: Number(value) },
-                ]);
+                const list = value.map((id) => {
+                  return {
+                    type: IDTYPE.USER,
+                    label:
+                      nodeConfig.label == '动作节点'
+                        ? '抄送人'
+                        : nodeConfig.label,
+                    value: Number(id),
+                  };
+                });
+                onNodeConfigChange('audits', list);
               }}
               filterOption={(input, option) =>
                 option.props.children.indexOf(input) >= 0
@@ -191,7 +202,14 @@ const Component = (props: any) => {
               }
               onChange={(value) => {
                 onNodeConfigChange('audits', [
-                  { type: IDTYPE.ROLE, value: Number(value) },
+                  {
+                    type: IDTYPE.ROLE,
+                    label:
+                      nodeConfig.label == '动作节点'
+                        ? '审批人'
+                        : nodeConfig.label,
+                    value: Number(value),
+                  },
                 ]);
               }}
             >
@@ -215,7 +233,14 @@ const Component = (props: any) => {
               }
               onChange={(value) => {
                 onNodeConfigChange('audits', [
-                  { type: IDTYPE.LEADER, value: Number(value) },
+                  {
+                    type: IDTYPE.LEADER,
+                    label:
+                      nodeConfig.label == '动作节点'
+                        ? '审批人'
+                        : nodeConfig.label,
+                    value: Number(value),
+                  },
                 ]);
               }}
             />
@@ -232,7 +257,13 @@ const Component = (props: any) => {
           label="标题"
           value={nodeConfig.label}
           onChange={(value) => {
-            onNodeConfigChange('label', value);
+            onNodeConfigChange('', {
+              label: value,
+              audits:
+                nodeConfig?.audits?.length > 0
+                  ? [{ ...(nodeConfig?.audits[0] || []), label: value }]
+                  : [],
+            });
           }}
         />
         <SelectField
@@ -297,12 +328,9 @@ const Component = (props: any) => {
             }}
           />
         </div>
-        {nodeConfig.type != TYPE.AUDIT && (
+        {nodeConfig.type == TYPE.INITIATOR && (
           <div className="group">
-            <label>
-              {typeOption.find((item) => item.value == nodeConfig.type)
-                ?.label || '-'}
-            </label>
+            <label>发起人</label>
             <TreeSelect
               showSearch
               multiple
@@ -337,6 +365,40 @@ const Component = (props: any) => {
           </div>
         )}
       </div>
+      {nodeConfig.type == TYPE.COPYMAN && (
+        <div className="group">
+          <label>抄送人:</label>
+          <Select
+            showSearch
+            mode="multiple"
+            style={{ width: '80%' }}
+            value={nodeConfig.audits?.map((item) => item.value)}
+            onChange={(value) => {
+              const list = value.map((id) => {
+                return {
+                  type: IDTYPE.USER,
+                  label:
+                    nodeConfig.label == '动作节点'
+                      ? '抄送人'
+                      : nodeConfig.label,
+                  value: Number(id),
+                  is_cc: 1,
+                };
+              });
+              onNodeConfigChange('audits', list);
+            }}
+            filterOption={(input, option) =>
+              option?.props?.children.indexOf(input) >= 0
+            }
+          >
+            {(userList || []).map((item) => (
+              <Option key={item.ID} value={item.ID}>
+                {item.CName}
+              </Option>
+            ))}
+          </Select>
+        </div>
+      )}
 
       {/* <Button style={{ marginTop: 20 }} type="primary" onClick={onSave}>
         保存

+ 1 - 0
src/pages/Flow/AuditModal.js

@@ -2,6 +2,7 @@ import React, { useEffect } from 'react';
 import { Modal, Input, Form, Select } from 'antd';
 
 const flowTypeList = [
+  { value: 0, label: 'BOM审批' },
   { value: 1, label: '文档审批' },
   { value: 2, label: 'OA审批' },
   { value: 3, label: '合同归档审批' },

+ 1 - 1
src/pages/Flow/OaDetail.js

@@ -115,7 +115,7 @@ const OaDetail = () => {
       createRun({
         flow_id: Number(oaId),
         form: JSON.stringify(formData),
-        audit_list,
+        audit_list: audit_list.flat(),
         cc_list,
         files: files.join(','),
       });

+ 102 - 52
src/pages/Flow/components/ApprovalProcess.tsx

@@ -1,9 +1,23 @@
 import React, { CSSProperties, useEffect, useMemo, useState } from 'react';
 import { queryUserListByRoleID, queryLeader } from '@/services/boom';
 import { connect } from 'umi';
-import { CheckOutlined, PlusOutlined } from '@ant-design/icons';
-import { Popover, Radio, RadioChangeEvent, Spin, Steps } from 'antd';
+import {
+  CheckOutlined,
+  PlusOutlined,
+  PlusSquareOutlined,
+} from '@ant-design/icons';
+import {
+  Popover,
+  Radio,
+  RadioChangeEvent,
+  Space,
+  Spin,
+  Steps,
+  Timeline,
+  Checkbox,
+} from 'antd';
 import { useModel, useRequest } from '@umijs/max';
+import { CheckboxValueType } from 'antd/es/checkbox/Group';
 
 const { Step } = Steps;
 
@@ -19,6 +33,7 @@ interface AuditNode {
   type: (typeof TYPE)[keyof typeof TYPE];
   value: number[];
   checkValue: [];
+  label: string;
 }
 
 const ApprovalProcess = (props: any) => {
@@ -30,7 +45,7 @@ const ApprovalProcess = (props: any) => {
     roleList = [],
   } = props;
   const { userList, run } = useModel('userList');
-  const [checkValue, setCheckValue] = useState<number[]>([]);
+  const [checkValue, setCheckValue] = useState<number[][]>([]);
 
   const { auditList, ccList } = useMemo<{
     auditList: AuditNode[];
@@ -45,6 +60,7 @@ const ApprovalProcess = (props: any) => {
         type: item[0].type,
         checkValue: [],
         seq: index,
+        label: item[0].label,
         value: item.map((node: any) => node.value),
       };
 
@@ -57,7 +73,7 @@ const ApprovalProcess = (props: any) => {
     return { auditList, ccList };
   }, [approvalProcess]);
 
-  const onCheckValue = (value: number, index: number) => {
+  const onCheckValue = (value: number[], index: number) => {
     let values = [...checkValue];
     values[index] = value;
     setCheckValue(values);
@@ -76,10 +92,10 @@ const ApprovalProcess = (props: any) => {
     fontSize: 16,
   };
 
-  return (
-    <div>
-      <div style={rowStyle}>
-        {auditList.map((item: AuditNode, index: number) => (
+  const renderAudits = (list: AuditNode[]) => {
+    return list.map((item: AuditNode, index: number) => {
+      return {
+        children: (
           <AuditNodeStep
             key={`${item.type}-${item.value.join('.')}-${index}`}
             leaderData={leaderData}
@@ -87,26 +103,18 @@ const ApprovalProcess = (props: any) => {
             roleList={roleList}
             userList={userList}
             value={checkValue[item.seq]}
-            onChange={(value: number) => onCheckValue(value, item.seq)}
+            onChange={(value: number[]) => onCheckValue(value, item.seq)}
           />
-        ))}
-      </div>
-      {ccList.length > 0 && (
-        <div style={rowStyle}>
-          <h3 style={{ margin: "20px 0", fontWeight: 'bold' }}>抄送人</h3>
-          {ccList.map((item: AuditNode, index: number) => (
-            <AuditNodeStep
-              key={`${item.type}-${item.value.join('.')}-${index}`}
-              leaderData={leaderData}
-              item={item}
-              roleList={roleList}
-              userList={userList}
-              value={checkValue[item.seq]}
-              onChange={(value: number) => onCheckValue(value, item.seq)}
-            />
-          ))}
-        </div>
-      )}
+        ),
+      };
+    });
+  };
+
+  return (
+    <div>
+      <Timeline items={renderAudits(auditList)}></Timeline>
+
+      {ccList.length > 0 && <Timeline items={renderAudits(ccList)}></Timeline>}
     </div>
   );
 };
@@ -116,8 +124,8 @@ interface AuditNodeStepProps {
   leaderData: any;
   roleList: any;
   userList: any;
-  value: number;
-  onChange: (value: number) => void;
+  value: number[];
+  onChange: (value: number[]) => void;
 }
 
 const AuditNodeStep = (props: AuditNodeStepProps) => {
@@ -152,19 +160,29 @@ const AuditNodeStep = (props: AuditNodeStepProps) => {
     setLoading(false);
   };
 
-  const selectedUserId = ({ target: { value } }: RadioChangeEvent) => {
-    onChange(Number(value));
+  const selectedUserId = (list: CheckboxValueType[]) => {
+    onChange(list as number[]);
   };
 
   const content = (
     <Spin spinning={loading}>
-      <Radio.Group onChange={selectedUserId} value={value}>
-        {selectUserList.map((item: any) => (
-          <Radio.Button value={Number(item.user_id)}>
-            {item.c_name}
-          </Radio.Button>
-        ))}
-      </Radio.Group>
+      <Checkbox.Group
+        style={{ display: 'block' }}
+        onChange={selectedUserId}
+        value={value as any}
+      >
+        {selectUserList
+          .filter((item: any) => item.c_name)
+          .map((item: any) => (
+            <Checkbox
+              style={{ whiteSpace: 'nowrap' }}
+              key={item.user_id}
+              value={Number(item.user_id)}
+            >
+              {item.c_name}
+            </Checkbox>
+          ))}
+      </Checkbox.Group>
     </Spin>
   );
 
@@ -184,18 +202,31 @@ const AuditNodeStep = (props: AuditNodeStepProps) => {
     );
   }
   if (item.type == TYPE.ROLE) {
-    let title = '';
-    if (value) {
-      title = userList.find((cur: any) => cur.ID == value)?.CName;
-    } else {
-      const names = item.value.map((id) => {
-        const role = roleList.find((cur: any) => cur.ID == id);
-        return role?.Name;
-      });
-      title = `从${names.join('、')}选择`;
+    let label = item.label || '';
+    const names = item.value.map((id) => {
+      const role = roleList.find((cur: any) => cur.ID == id);
+      return role?.Name;
+    });
+    let title = `从${names.join('、')}选择`;
+    let userNames = [];
+    if (value?.length > 0) {
+      userNames = value.map(
+        (id: number) => userList.find((cur: any) => cur.ID == id)?.CName,
+      );
     }
     return (
-      <div style={{ marginBottom: 20, cursor: 'pointer' }}>
+      <div
+        style={{
+          marginBottom: 20,
+          cursor: 'pointer',
+          display: 'flex',
+          justifyContent: 'space-between',
+        }}
+      >
+        <div>
+          <div style={{ fontSize: '18px' }}>{label}</div>
+          <div style={{ fontSize: '14px', color: 'gray' }}>{title}</div>
+        </div>
         <Popover
           placement="bottomLeft"
           title={'选择审批人'}
@@ -203,21 +234,40 @@ const AuditNodeStep = (props: AuditNodeStepProps) => {
           trigger="click"
           overlayStyle={{ width: '300px' }}
           onOpenChange={handleOpen}
-          style={{ marginBottom: 20 }}
+          style={{ marginBottom: 20, overflow: 'auto' }}
         >
-          <PlusOutlined style={{ marginRight: 20 }} /> {title}
+          <Space>
+            {userNames.join('、')}
+            <PlusSquareOutlined style={{ fontSize: '36px', color: 'gray' }} />
+          </Space>
         </Popover>
       </div>
     );
   }
+
+  let label = item.label || '';
+
+  let title = item.is_cc
+    ? `抄送${item.value.length}人`
+    : `${item.value.length}人审批`;
   const names = item.value.map((id) => {
     const user = userList.find((cur: any) => cur.ID == id);
     return user?.CName;
   });
 
   return (
-    <div style={{ marginBottom: 20 }}>
-      <CheckOutlined style={{ marginRight: 20 }} /> {names.join('、')}
+    <div
+      style={{
+        marginBottom: 20,
+        display: 'flex',
+        justifyContent: 'space-between',
+      }}
+    >
+      <div>
+        <div style={{ fontSize: '18px' }}>{label}</div>
+        <div style={{ fontSize: '14px', color: 'gray' }}>{title}</div>
+      </div>
+      {names.join('、')}
     </div>
   );
 };

+ 2 - 2
src/pages/Flow/index.js

@@ -27,7 +27,7 @@ function Audit(props) {
     {
       title: '分类',
       dataIndex: 'classify_id',
-      render: (id) => classify?.find((item) => item.id == id)?.name || '-',
+      render: (id) => classify?.find((item) => item.id == id)?.name || 'BOM',
     },
     {
       title: '操作',
@@ -140,7 +140,7 @@ function Audit(props) {
     dispatch({
       type: 'flow/queryAuditList',
       payload: {
-        flow_type: 1,
+        flow_type: 'all',
       },
     });
     dispatch({