Browse Source

feat: 完善审批流程功能交互逻辑

lanjianrong 3 năm trước cách đây
mục cha
commit
9cee36b7f5

+ 5 - 5
config/routes.ts

@@ -59,11 +59,11 @@
           }
         ]
       },
-      // {
-      //   path: '/project/verification',
-      //   name: 'verification',
-      //   component: './Project/Verification'
-      // },
+      {
+        path: '/project/verification',
+        name: 'verification',
+        component: './Project/Verification'
+      },
       {
         path: '/project/created',
         name: 'created',

+ 0 - 5
src/pages/Institutions/Company/Detail/components/Staff.tsx

@@ -14,7 +14,6 @@ import { generateColumns } from '@/utils/util'
 import { genderEum } from '@/pages/Institutions/Staff'
 import AnimateContent from '@/components/AnimateContent'
 import { ModalType } from '@/utils/enum'
-import { UserOutlined } from '@ant-design/icons'
 import IconFont from '@/components/IconFont'
 
 type ListProps = ConnectProps & {
@@ -65,7 +64,6 @@ const Staff: React.FC<ListProps> = ({
       dataIndex: 'account',
       title: '帐号',
       width: 116,
-      align: 'center',
       onHeaderCell: () => ({ style: { textAlign: 'center' } })
     },
     {
@@ -80,7 +78,6 @@ const Staff: React.FC<ListProps> = ({
         </div>
       ),
       width: 86,
-      align: 'center',
       onHeaderCell: () => ({ style: { textAlign: 'center' } })
     },
     {
@@ -113,7 +110,6 @@ const Staff: React.FC<ListProps> = ({
       key: 'organizationalStructureID',
       title: '组织架构',
       width: 146,
-      align: 'center',
       onHeaderCell: () => ({ style: { textAlign: 'center' } }),
       renderText: (_, record) => record.organizationalStructure?.name
     },
@@ -122,7 +118,6 @@ const Staff: React.FC<ListProps> = ({
       key: 'institutionID',
       title: '所属企事业单位',
       width: 286,
-      align: 'center',
       onHeaderCell: () => ({ style: { textAlign: 'center' } }),
       renderText: (_, record) => record.institution.name
     },

+ 0 - 1
src/pages/Institutions/Company/List/index.tsx

@@ -62,7 +62,6 @@ const CompanyList = () => {
       title: '人员账号',
       renderText: (text, record) => `${text}/${record.memberTotal}`,
       width: 96,
-      align: 'center',
       onHeaderCell: () => ({ style: { textAlign: 'center' } })
     },
     {

+ 0 - 4
src/pages/Institutions/Staff/index.tsx

@@ -50,7 +50,6 @@ const CompanyList: React.FC<ListProps> = ({ dispatch, accountTypeList }) => {
       key: 'account',
       title: '账号',
       width: 116,
-      align: 'center',
       onHeaderCell: () => ({ style: { textAlign: 'center' } }),
       render: (name, record) => (
         <div
@@ -73,7 +72,6 @@ const CompanyList: React.FC<ListProps> = ({ dispatch, accountTypeList }) => {
       key: 'name',
       title: '名称',
       width: 86,
-      align: 'center',
       onHeaderCell: () => ({ style: { textAlign: 'center' } })
     },
     {
@@ -106,7 +104,6 @@ const CompanyList: React.FC<ListProps> = ({ dispatch, accountTypeList }) => {
       key: 'organizationalStructureID',
       title: '组织架构',
       width: 146,
-      align: 'center',
       onHeaderCell: () => ({ style: { textAlign: 'center' } }),
       renderText: (_, record) => record.organizationalStructure?.name
     },
@@ -115,7 +112,6 @@ const CompanyList: React.FC<ListProps> = ({ dispatch, accountTypeList }) => {
       key: 'institutionID',
       title: '所属企事业单位',
       width: 286,
-      align: 'center',
       onHeaderCell: () => ({ style: { textAlign: 'center' } }),
       renderText: (_, record) => record.institution.name
     },

+ 1 - 2
src/pages/Project/Management/Detail/index.tsx

@@ -70,8 +70,7 @@ const ProjectDetail: React.FC<RouteComponentProps> = ({ location }) => {
           // { tab: menuMap[TabEnum.AUDIT_ROLE], key: TabEnum.AUDIT_ROLE },
           { tab: menuMap[TabEnum.STAFF_ROLE], key: TabEnum.STAFF_ROLE }
         ]}
-        onTabChange={key => setState({ ...state, selectKey: key })}
-      >
+        onTabChange={key => setState({ ...state, selectKey: key })}>
         {renderChildren()}
       </PageContainer>
     )

+ 160 - 0
src/pages/Project/Verification/Detail/Flow/components/Drawer/ParticipantCard.tsx

@@ -0,0 +1,160 @@
+import React, { useEffect, useState } from 'react'
+import { ParticipantMode } from '../../enum'
+import { ConfigureType } from '../../enum'
+import { queryAccountList } from '@/services/api/institution'
+import { Col, Collapse, Row, Select, Switch } from 'antd'
+import { useRequest } from 'umi'
+
+type ParticipantCardProps = {
+  defaultData: {
+    participantMode: ParticipantMode
+    ID: string | null
+    name: string | null
+    institutionID: string | null
+    configure: ConfigureType[]
+  }
+  institutionList: { label: string; value: string }[]
+  updateAction: (values: Recordable<string>) => void
+}
+
+const configureArr = [
+  // ConfigureType.ADDSIGN,
+  ConfigureType.ASSISTAUDIT,
+  ConfigureType.RETURN
+  // ConfigureType.REVOKE,
+  // ConfigureType.SKIP
+]
+
+const configureItemTitle = {
+  [ConfigureType.ADDSIGN]: {
+    name: '允许加签',
+    short: '签'
+  },
+  [ConfigureType.ASSISTAUDIT]: {
+    name: '允许协审',
+    short: '协'
+  },
+  [ConfigureType.RETURN]: {
+    name: '允许退回',
+    short: '退'
+  },
+  [ConfigureType.REVOKE]: {
+    name: '允许撤回',
+    short: '撤'
+  },
+  [ConfigureType.SKIP]: {
+    name: '允许跳过',
+    short: '跳'
+  }
+}
+const ParticipantCard: React.FC<ParticipantCardProps> = ({
+  updateAction,
+  defaultData,
+  institutionList
+}) => {
+  const [staffList, setStaffList] = useState([])
+  const { run: tryUpdateStaffList } = useRequest(
+    dataID => queryAccountList({ current: 1, pageSize: 214000, dataID }),
+    {
+      manual: true,
+      onSuccess(result) {
+        setStaffList(result.items.map(item => ({ label: item.name, value: item.ID })))
+      }
+    }
+  )
+
+  useEffect(() => {
+    if (defaultData.ID && defaultData.institutionID && !staffList?.length) {
+      tryUpdateStaffList(defaultData.institutionID)
+    }
+  }, [])
+
+  if (defaultData.ID && !staffList?.length) return null
+  return (
+    <>
+      <div className="children:mb-4">
+        <div>
+          <Select
+            className="w-full"
+            defaultValue={defaultData.participantMode}
+            options={[
+              { label: '仅用户模式', value: ParticipantMode.ACCOUNT }
+              // { label: '角色模式', value: ParticipantMode.ROLE }
+            ]}
+            onChange={value => {
+              updateAction({ ...defaultData, participantMode: value })
+            }}
+          />
+        </div>
+        <div>
+          <Select
+            className="w-full"
+            defaultValue={defaultData.institutionID}
+            options={institutionList}
+            onChange={value => {
+              if (value) tryUpdateStaffList(value)
+              updateAction({ ...defaultData, institutionID: value })
+            }}
+          />
+        </div>
+        <div>
+          <Select
+            className="w-full"
+            defaultValue={defaultData.ID}
+            options={staffList}
+            onChange={(_, options) => {
+              updateAction({ ...defaultData, ID: options?.value, name: options?.label })
+            }}
+          />
+        </div>
+      </div>
+      <Collapse
+        defaultActiveKey={['1']}
+        ghost
+        expandIcon={({ isActive }) => (
+          <span className="text-primary font-medium">{isActive ? '收叠' : '展开'}</span>
+        )}
+        expandIconPosition="right">
+        <Collapse.Panel
+          header={
+            <div className="flex flex-row justify-between items-center">
+              <div className="text-md font-midium h-24px leading-24px">配置信息</div>
+              <div className="children:mx-1 flex flex-row ml-1">
+                {defaultData.configure.map(item => (
+                  <div
+                    key={item}
+                    className="w-30px text-center rounded-lg text-primary border border-hex-0089ff bg-hex-e9f5ff">
+                    {configureItemTitle[item]?.short}
+                  </div>
+                ))}
+              </div>
+            </div>
+          }
+          key="1">
+          <Row>
+            {configureArr.map(item => (
+              <Col span={8} key={item}>
+                <div className="py-2 flex flex-row items-center ">
+                  <div className="mr-2">{configureItemTitle[item]?.name}</div>
+                  <Switch
+                    checked={defaultData.configure.includes(item)}
+                    onChange={checked => {
+                      updateAction({
+                        ...defaultData,
+                        configure: checked
+                          ? [...defaultData.configure, item]
+                          : defaultData.configure.filter(conf => conf !== item)
+                      })
+                    }}
+                  />
+                </div>
+              </Col>
+            ))}
+          </Row>
+        </Collapse.Panel>
+      </Collapse>
+    </>
+  )
+}
+
+export default ParticipantCard

+ 87 - 184
src/pages/Project/Verification/Detail/Flow/components/Drawer/index.tsx

@@ -1,64 +1,54 @@
-import { useState, useEffect, useContext, useMemo, useCallback } from 'react'
-import { Button, Drawer, Radio, Tabs, Tooltip, Row, Col, Switch, Collapse } from 'antd'
+import { useState, useEffect, useContext, useMemo } from 'react'
+import { Button, Drawer, Radio, Tabs, Tooltip } from 'antd'
 import { Actions, FlowContext } from '../../context'
 import type { RadioChangeEvent } from 'antd'
 import { ApprovalWay, ConfigureType, ParticipantMode } from '../../enum'
 import { QuestionCircleFilled } from '@ant-design/icons'
-import { createForm, onFieldMount, onFieldReact, onFormValuesChange } from '@formily/core'
-import { createSchemaField, FormProvider } from '@formily/react'
-import { FormItem, Select } from '@formily/antd'
-import { participantSchema } from '../../utils'
-import { queryAccountList, queryInstitutionList } from '@/services/api/institution'
-import consts from '@/utils/consts'
-import { action } from '@formily/reactive'
+import { queryInstitutionList } from '@/services/api/institution'
 import { buildUUID } from '@/utils/uuid'
 import styles from './index.less'
-import { useDebounceFn } from 'ahooks'
-const configureItemTitle = {
-  [ConfigureType.ADDSIGN]: {
-    name: '允许加签',
-    short: '签'
-  },
-  [ConfigureType.ASSISTAUDIT]: {
-    name: '允许协审',
-    short: '协'
-  },
-  [ConfigureType.RETURN]: {
-    name: '允许退回',
-    short: '退'
-  },
-  [ConfigureType.REVOKE]: {
-    name: '允许撤回',
-    short: '撤'
-  },
-  [ConfigureType.SKIP]: {
-    name: '允许跳过',
-    short: '跳'
-  }
-}
+import ParticipantCard from './ParticipantCard'
+import { useRequest } from 'umi'
+import { saveApprovalParticipant } from '@/services/api/project'
+import consts from '@/utils/consts'
 
-const configureArr = [
-  // ConfigureType.ADDSIGN,
-  ConfigureType.ASSISTAUDIT,
-  ConfigureType.RETURN
-  // ConfigureType.REVOKE,
-  // ConfigureType.SKIP
-]
 const FlowDrawer = () => {
   const { flowState, dispatch } = useContext(FlowContext)
-  const { drawerConfig } = flowState
-
+  const { modalConfig: { nodeID = null, visible = false } = {}, dataID } = flowState
   const [state, setState] = useState({
+    institutionList: [],
     approvalWay: ApprovalWay.ACCOUNT,
-    accounts: {}
+    accounts: []
   })
+
   useEffect(() => {
-    if (drawerConfig.nodeId && drawerConfig.visible) {
-      const defaultStaffIds = flowState.flowData.get(drawerConfig.nodeId)?.approvalAccounts || []
-      setState({ ...state, staffOptions: defaultStaffIds })
+    if (nodeID && visible) {
+      console.log(flowState.flowData.get(nodeID))
+
+      const defaultConfig = flowState.flowData.get(nodeID) || {
+        approvalWay: ApprovalWay.ACCOUNT,
+        accounts: []
+      }
+      setState({ ...state, ...defaultConfig })
+    }
+  }, [nodeID])
+
+  useRequest(() => queryInstitutionList({ current: 1, pageSize: 214000 }), {
+    onSuccess(result) {
+      setState({
+        ...state,
+        institutionList: result.items.map(item => ({ label: item.name, value: item.ID }))
+      })
     }
-  }, [drawerConfig])
+  })
 
+  // 会签、或签按钮不禁用
+  const btnStatus = useMemo(() => {
+    if (state.accounts.length >= 1) {
+      return ![ApprovalWay.JOINTLYSIGN, ApprovalWay.ORSIGN].includes(state.approvalWay)
+    }
+    return false
+  }, [state.approvalWay, state.accounts])
   const handleCancel = () => dispatch({ type: Actions.CLOSE_MODAL })
 
   const plainOptions = [{ label: '指定用户', value: ApprovalWay.ACCOUNT }]
@@ -69,120 +59,70 @@ const FlowDrawer = () => {
       setState({ ...state, approvalWay: target.value })
     }
   }
-  const handleOnOk = () => {
+  const handleOnOk = async () => {
+    const { approvalWay, accounts } = state
     const payload = {
-      id: drawerConfig.nodeId,
+      id: nodeID,
       node: {
-        approvalType: state.approvalType
+        approvalWay,
+        accounts
       }
     }
-    dispatch({
-      type: Actions.SET_FLOW_NODE,
-      payload
+    const participantInfo: API.ParticipantInfo = {
+      approvalWay: state.approvalWay,
+      accounts: state.accounts
+    }
+    const { code = -1 } = await saveApprovalParticipant({
+      ID: dataID,
+      segmentID: nodeID,
+      participantInfo
     })
-    handleCancel()
+    if (code === consts.RET_CODE.SUCCESS) {
+      dispatch({
+        type: Actions.SET_FLOW_NODE,
+        payload
+      })
+      handleCancel()
+    }
   }
 
   // 添加用户-参与者卡片
   const addParticipantInfo = () => {
     setState({
       ...state,
-      accounts: {
+      accounts: [
         ...state.accounts,
-        [buildUUID()]: {
+        {
+          uid: buildUUID(),
           participantMode: ParticipantMode.ACCOUNT,
           ID: null,
           name: null,
           institutionID: null,
           configure: [ConfigureType.ASSISTAUDIT, ConfigureType.RETURN]
         }
-      }
+      ]
     })
   }
 
-  const { run: trySaveCardData } = useDebounceFn((targetID, formData) => {
-    console.log(1)
-
+  const saveCardData = (targetID, formData) => {
     setState({
       ...state,
-      accounts: { ...state.accounts, [targetID]: { ...state.accounts[targetID], ...formData } }
-    })
-  })
-  const renderCard = () => {
-    const keys = Object.keys(state.accounts)
-    return keys.map(key => {
-      const participant = state.accounts?.[key]
-      if (!participant) return null
-      const formInstance = createForm({
-        initialValues: {
-          ID: participant.ID,
-          participantMode: participant.participantMode,
-          institutionID: participant.institutionID,
-          name: participant.name
-        },
-        effects() {
-          onFormValuesChange(form => {
-            trySaveCardData(key, form.getValuesIn())
-          })
-          onFieldMount('ID', (field, form) => {
-            field.setComponentProps({
-              onSelect: (_, option: { label: string; value: string }) => {
-                option && form.setValuesIn('name', option?.label)
-              }
-            })
-          })
-          onFieldReact('ID', async field => {
-            const dataID = field.query('institutionID').get('value')
-            if (dataID) {
-              const { code = -1, data: { items = [] } = {} } = await queryAccountList({
-                current: 1,
-                pageSize: 214000,
-                dataID
-              })
-              if (code === consts.RET_CODE.SUCCESS) {
-                field.dataSource = items.map(item => ({ label: item.name, value: item.ID }))
-              }
-            }
-          })
-        }
-      })
-      const useAsyncDataSource = service => field => {
-        field.loading = true
-        service(field).then(
-          action.bound(data => {
-            field.dataSource = data
-            field.loading = false
-          })
-        )
-      }
-      const loadInstitutionList = async () => {
-        const { code = -1, data: { items = [] } = {} } = await queryInstitutionList({
-          current: 1,
-          pageSize: 214000
-        })
-        if (code === consts.RET_CODE.SUCCESS) {
-          return items.map(item => ({ label: item.name, value: item.ID }))
-        }
-        return []
-      }
-      const SchemaField = createSchemaField({
-        components: {
-          FormItem,
-          Select
+      accounts: state.accounts.map(item => {
+        if (item.uid === targetID) {
+          const newItem = { ...item, ...formData }
+          return newItem
         }
+        return item
       })
+    })
+  }
 
-      const toggleCheck = (checked, target) => {
-        participant.configure = checked
-          ? [...participant.configure, target]
-          : participant.configure.filter(item => item !== target)
-        setState({ ...state, accounts: { ...state.accounts, [key]: participant } })
-      }
+  const renderCard = () => {
+    return state.accounts.map(participant => {
+      if (!participant) return null
 
       const delCardItem = id => {
-        const newAccount = { ...state.accounts }
-        delete newAccount[id]
-        setState({ ...state, accounts: newAccount })
+        setState({ ...state, accounts: state.accounts.filter(item => item.uid !== id) })
       }
       return (
         <div
@@ -190,66 +130,25 @@ const FlowDrawer = () => {
             'shadow-base border rounded-md flex flex-col justify-between px-4 py-2 mt-6',
             styles.participantCard
           ].join(' ')}
-          key={key}>
+          key={participant.uid}>
           <div className="flex flex-row justify-between">
             <div className="text-base font-medium ">参与者</div>
-            <div className="text-primary cursor-pointer" onClick={() => delCardItem(key)}>
+            <div
+              className="text-primary cursor-pointer"
+              onClick={() => delCardItem(participant.uid)}>
               删除
             </div>
           </div>
           <div className="mt-3">
-            <FormProvider form={formInstance}>
-              <SchemaField
-                schema={participantSchema}
-                scope={{
-                  useAsyncDataSource,
-                  loadInstitutionList
-                }}
-              />
-            </FormProvider>
+            <ParticipantCard
+              institutionList={state.institutionList}
+              defaultData={participant}
+              updateAction={formData => saveCardData(participant.uid, formData)}
+            />
           </div>
           {/* <Button onClick={() => console.log(formOptions.instance.getState().values)}>
                 11111
               </Button> */}
-
-          <Collapse
-            defaultActiveKey={['1']}
-            ghost
-            expandIcon={({ isActive }) => (
-              <span className="text-primary font-medium">{isActive ? '收叠' : '展开'}</span>
-            )}
-            expandIconPosition="right">
-            <Collapse.Panel
-              header={
-                <div className="flex flex-row justify-between items-center">
-                  <div className="text-md font-midium h-24px leading-24px">配置信息</div>
-                  <div className="children:mx-1 flex flex-row ml-1">
-                    {participant.configure.map(item => (
-                      <div
-                        key={item}
-                        className="w-30px text-center rounded-lg text-primary border border-hex-0089ff bg-hex-e9f5ff">
-                        {configureItemTitle[item]?.short}
-                      </div>
-                    ))}
-                  </div>
-                </div>
-              }
-              key="1">
-              <Row>
-                {configureArr.map(item => (
-                  <Col span={8} key={item}>
-                    <div className="py-2 flex flex-row items-center ">
-                      <div className="mr-2">{configureItemTitle[item]?.name}</div>
-                      <Switch
-                        checked={participant.configure.includes(item)}
-                        onChange={checked => toggleCheck(checked, item)}
-                      />
-                    </div>
-                  </Col>
-                ))}
-              </Row>
-            </Collapse.Panel>
-          </Collapse>
         </div>
       )
     })
@@ -257,7 +156,7 @@ const FlowDrawer = () => {
 
   return (
     <Drawer
-      visible={drawerConfig.visible}
+      visible={visible}
       onClose={handleCancel}
       destroyOnClose
       footer={
@@ -302,7 +201,11 @@ const FlowDrawer = () => {
             />
           </div>
           <div className="mt-20px">
-            <Button type="primary" size="middle" onClick={() => addParticipantInfo()}>
+            <Button
+              type="primary"
+              size="middle"
+              disabled={btnStatus}
+              onClick={() => addParticipantInfo()}>
               添加用户
             </Button>
           </div>

+ 44 - 17
src/pages/Project/Verification/Detail/Flow/components/Edge/index.tsx

@@ -1,15 +1,22 @@
 import { buildUUID } from '@/utils/uuid'
-import { BranchesOutlined, PlusOutlined, SolutionOutlined } from '@ant-design/icons'
+import { PlusOutlined, SolutionOutlined } from '@ant-design/icons'
 import { Button, Popover } from 'antd'
 import React, { useMemo, useState, useContext } from 'react'
 import { getBezierPath, getEdgeCenter, getMarkerEnd, useStoreState } from 'react-flow-renderer'
 import { Actions, FlowContext } from '../../context'
 import { generateElements, genreateElementEnum, getEdgeParams } from '../../utils'
+import { addApprovalNode } from '@/services/api/project'
 import styles from './index.less'
 import 'antd/lib/button/style/css'
+import consts from '@/utils/consts'
 const foreignObjectSize = 50
 
-const renderMenu = ({ dispatch, elements, togglePopver, id, flowInstance }) => {
+export enum SectorType {
+  APPROVAL = 'approval',
+  CONDITION = 'condition'
+}
+
+const renderMenu = ({ dataID, source, dispatch, elements, togglePopver, id, flowInstance }) => {
   const setElements = els => {
     dispatch({
       type: Actions.SET_ELEMENTS,
@@ -33,28 +40,40 @@ const renderMenu = ({ dispatch, elements, togglePopver, id, flowInstance }) => {
     })
   }
 
-  const addAuditor = () => {
-    // console.log(buildUUID())
+  const addAuditor = async () => {
     const uid = buildUUID()
     const newNode = {
       id: uid,
       type: 'input',
-      // position: { x: 400, y: 0 },
       data: {}
     }
     const currentEdge = elements.find(item => item.id === id)
-    setElements(generateElements(genreateElementEnum.ADD, elements)(newNode, currentEdge))
-    dispatch({
-      type: Actions.SET_FLOW_NODE,
-      payload: {
-        id: uid,
-        node: null
+    const newElements = generateElements(genreateElementEnum.ADD, elements)(newNode, currentEdge)
+
+    const { code = -1 } = await addApprovalNode({
+      ID: dataID,
+      segmentBrotherID: source,
+      flowProcess: newElements,
+      segment: {
+        ID: uid,
+        name: '审批人',
+        sectorType: SectorType.APPROVAL
       }
     })
-    togglePopver(false)
-    setTimeout(() => {
-      flowInstance?.fitView()
-    }, 80)
+    if (code === consts.RET_CODE.SUCCESS) {
+      setElements(newElements)
+      dispatch({
+        type: Actions.SET_FLOW_NODE,
+        payload: {
+          id: uid,
+          node: null
+        }
+      })
+      togglePopver(false)
+      setTimeout(() => {
+        flowInstance?.fitView()
+      }, 80)
+    }
   }
 
   return (
@@ -109,7 +128,7 @@ export function CommonEdge(props) {
 
   const [visible, setVisible] = useState(false)
   const { flowState, dispatch } = useContext(FlowContext)
-  const { elements, flowInstance } = flowState
+  const { elements, flowInstance, dataID } = flowState
   const nodes = useStoreState(store => store.nodes)
   const markerEnd = getMarkerEnd(arrowHeadType, markerEndId)
 
@@ -157,7 +176,15 @@ export function CommonEdge(props) {
         y={edgeCenterY - foreignObjectSize / 2}>
         <div className={styles.addIcon}>
           <Popover
-            content={renderMenu({ elements, dispatch, togglePopver, id, flowInstance })}
+            content={renderMenu({
+              elements,
+              dispatch,
+              source,
+              togglePopver,
+              id,
+              flowInstance,
+              dataID
+            })}
             trigger="click"
             placement="right"
             visible={visible}

+ 41 - 30
src/pages/Project/Verification/Detail/Flow/components/Graph/index.tsx

@@ -17,43 +17,50 @@ const edgeTypes: EdgeTypesType = {
   common: CommonEdge
 }
 
-type flowProcessDataItem = {
-  approvalType: ApprovalType
-  approvalMethod: ApprovalMethod
-  approvalAccounts: { ID: string; name: string }[]
-}
+// type flowProcessDataItem = {
+//   approvalType: ApprovalType
+//   approvalMethod: ApprovalMethod
+//   approvalAccounts: { ID: string; name: string }[]
+// }
 
 type FlowGrophProps = {
-  id: string
-  flowProcess?: Elements
-  flowProcessData?: Record<string, flowProcessDataItem>
+  dataID: string
+  flowProcess: Elements
+  flowProcessData: API.ApprovalProcessItem[]
 }
-const FlowGroph: React.FC<FlowGrophProps> = ({ id, flowProcess, flowProcessData }) => {
+const FlowGroph: React.FC<FlowGrophProps> = ({ dataID, flowProcess, flowProcessData }) => {
   const { flowState, dispatch } = useContext(FlowContext)
   useEffect(() => {
-    if (id) {
-      if (flowProcess?.length) {
-        dispatch({
-          type: Actions.SET_ELEMENTS,
-          payload: flowProcess
-        })
-        dispatch({
-          type: Actions.SET_FLOW_NODE,
-          payload: new Map(Object.entries(flowProcessData))
-        })
-      } else {
-        dispatch({
-          type: Actions.SET_ELEMENTS,
-          payload: initialElements
-        })
+    if (flowProcess?.length) {
+      dispatch({
+        type: Actions.SET_ELEMENTS,
+        payload: flowProcess
+      })
+      dispatch({
+        type: Actions.SET_FLOW_NODE,
+        payload: new Map(
+          Object.entries(
+            flowProcessData.reduce((prev, curr) => {
+              if (curr.ID) {
+                prev[curr.ID] = curr.participantInfo
+              }
+              return prev
+            }, {})
+          )
+        )
+      })
+    } else {
+      dispatch({
+        type: Actions.SET_ELEMENTS,
+        payload: initialElements
+      })
 
-        dispatch({
-          type: Actions.SET_FLOW_NODE,
-          payload: new Map()
-        })
-      }
+      dispatch({
+        type: Actions.SET_FLOW_NODE,
+        payload: new Map()
+      })
     }
-  }, [id])
+  }, [])
   const { elements } = flowState
 
   const defaultOptions = {
@@ -66,6 +73,10 @@ const FlowGroph: React.FC<FlowGrophProps> = ({ id, flowProcess, flowProcessData
         type: Actions.SET_FLOW_INSTANCE,
         payload: reactFlowInstance
       })
+      dispatch({
+        type: Actions.SET_FOLW_DATAID,
+        payload: dataID
+      })
       setTimeout(() => {
         reactFlowInstance?.fitView()
       }, 0)

+ 24 - 12
src/pages/Project/Verification/Detail/Flow/components/Node/index.tsx

@@ -2,6 +2,8 @@ import { Handle } from 'react-flow-renderer'
 import { Actions, FlowContext } from '../../context'
 import { useContext } from 'react'
 import { CloseOutlined, RightOutlined } from '@ant-design/icons'
+import { removeApprovalNode } from '@/services/api/project'
+import consts from '@/utils/consts'
 export const InputNode = () => {
   return (
     <>
@@ -35,17 +37,20 @@ export const CommonNode = props => {
 
   const { flowState, dispatch } = useContext(FlowContext)
   const { flowInstance, flowData } = flowState
-  const removeNode = () => {
-    dispatch({
-      type: Actions.REMOVE_FLOW_NODE,
-      payload: {
-        id,
-        data
-      }
-    })
-    setTimeout(() => {
-      flowInstance?.fitView()
-    }, 80)
+  const removeNode = async () => {
+    const { code = -1 } = await removeApprovalNode({ ID: dataID, segmentID: id })
+    if (code === consts.RET_CODE.SUCCESS) {
+      dispatch({
+        type: Actions.REMOVE_FLOW_NODE,
+        payload: {
+          id,
+          data
+        }
+      })
+      setTimeout(() => {
+        flowInstance?.fitView()
+      }, 80)
+    }
   }
   const openDrawer = () => {
     dispatch({
@@ -57,6 +62,7 @@ export const CommonNode = props => {
   }
 
   const auditor = flowData.get(id)
+  console.log(auditor)
 
   return (
     <>
@@ -89,7 +95,13 @@ export const CommonNode = props => {
         <div
           className="text-14px px-4px py-5px leading-22px text-black text-opacity-85 bg-white rounded-4px h-32px flex justify-between cursor-pointer"
           onClick={openDrawer}>
-          <div>{auditor ? auditor.approvalAccounts?.[0]?.name : '请选择审批人'}</div>
+          <div>
+            {auditor ? (
+              auditor.accounts?.[0]?.name
+            ) : (
+              <span className="text-hex-999999">发起人自选</span>
+            )}
+          </div>
           <div>
             <RightOutlined />
           </div>

+ 1 - 1
src/pages/Project/Verification/Detail/Flow/context/index.tsx

@@ -45,7 +45,7 @@ const initialState: InitialState = {
   // node节点或者是edge
   elements: initialElements,
   flowData: new Map(),
-  drawerConfig: {
+  modalConfig: {
     visible: false,
     nodeType: '',
     nodeId: ''

+ 27 - 9
src/pages/Project/Verification/Detail/Flow/context/reducer.ts

@@ -1,6 +1,8 @@
 // context/reducer.js
 // import { removeElements } from 'react-flow-renderer'
 
+import { removeApprovalNode } from '@/services/api/project'
+import consts from '@/utils/consts'
 import type { Elements } from 'react-flow-renderer'
 import { Actions } from '../enum'
 import { generateElements, genreateElementEnum } from '../utils'
@@ -33,25 +35,40 @@ const setFlowInstance = (state, flowInstance) => {
   return state
 }
 
-const removeFlowNode = (state, node) => {
+const removeFlowNode = async (state, node) => {
   const { id } = node
   const { flowData, elements } = state
   const res = { ...state }
   if (flowData.get(id)) {
-    res.elements = generateElements(genreateElementEnum.DEL, elements)(node)
-    flowData.delete(id)
+    const newElements = generateElements(genreateElementEnum.DEL, elements)(node)
+    const { code = -1 } = await removeApprovalNode({
+      ID: state.dataID,
+      segmentID: id,
+      flowProcess: newElements
+    })
+    if (code === consts.RET_CODE.SUCCESS) {
+      res.elements = newElements
+      flowData.delete(id)
+    }
   }
   return res
 }
 
+const setFlowDataId = (state, dataID) => {
+  if (dataID) {
+    return { ...state, dataID }
+  }
+  return state
+}
+
 const openModal = (state, node) => {
   return node?.id
     ? {
         ...state,
-        drawerConfig: {
+        modalConfig: {
           visible: true,
           nodeType: node.type,
-          nodeId: node.id
+          nodeID: node.id
         }
       }
     : state
@@ -59,10 +76,10 @@ const openModal = (state, node) => {
 
 const closeModal = state => ({
   ...state,
-  drawerConfig: {
+  modalConfig: {
     visible: false,
-    nodeType: '',
-    nodeId: ''
+    nodeType: null,
+    nodeID: null
   }
 })
 
@@ -73,7 +90,8 @@ const handlerMap = {
   [Actions.OPEN_MODAL]: openModal,
   [Actions.CLOSE_MODAL]: closeModal,
   [Actions.SET_ELEMENTS]: setElements,
-  [Actions.SET_FLOW_INSTANCE]: setFlowInstance
+  [Actions.SET_FLOW_INSTANCE]: setFlowInstance,
+  [Actions.SET_FOLW_DATAID]: setFlowDataId
 }
 
 const reducer = (state, action) => {

+ 1 - 0
src/pages/Project/Verification/Detail/Flow/enum/index.ts

@@ -2,6 +2,7 @@ export enum Actions {
   SET_ELEMENTS = 'set_elements',
   SET_FLOW_NODE = 'set_flow_node',
   SET_FLOW_INSTANCE = 'set_flow_instance',
+  SET_FOLW_DATAID = 'set_flow_dataId',
   REMOVE_FLOW_NODE = 'remove_flow_node',
   OPEN_MODAL = 'open_modal',
   CLOSE_MODAL = 'close_modal'

+ 32 - 8
src/pages/Project/Verification/Detail/Flow/index.tsx

@@ -1,22 +1,46 @@
-import React from 'react'
+import React, { useState, useEffect } from 'react'
 import { ReactFlowProvider } from 'react-flow-renderer'
-import type { FC } from 'react'
-
-import './index.less'
 import { FlowContextProvider } from './context'
-import ToolBar from './components/Toolbar'
 import FlowGroph from './components/Graph'
 import FlowDrawer from './components/Drawer'
+import { useRequest } from 'umi'
+import { queryApprovalDetail } from '@/services/api/project'
+import './index.less'
+
+type ApprovalFlowProps = {
+  dataID: string
+}
+
+const ApprovalFlow: React.FC<ApprovalFlowProps> = ({ dataID }) => {
+  const [state, setState] = useState({
+    flowProcess: null,
+    flowProcessData: null
+  })
+  const { run: tryGetDetail } = useRequest(queryApprovalDetail, {
+    manual: true,
+    onSuccess: result => {
+      setState({ ...state, flowProcess: result.flowProcess, flowProcessData: result.process })
+    }
+  })
+  useEffect(() => {
+    if (dataID) tryGetDetail({ ID: dataID })
+  }, [dataID])
 
-const ApprovalFlow: FC<ApprovalFlowProps> = props => {
+  const { flowProcess, flowProcessData } = state
   return (
     <div className="flow-container h-full w-full">
       <FlowContextProvider>
         <ReactFlowProvider>
           {/** 顶部工具栏 */}
-          <ToolBar dataId={props.id} closeAnimateContent={props.closeAnimateContent} />
+          {/* <ToolBar dataId={props.dataID} closeAnimateContent={props.closeAnimateContent} /> */}
           <div className="h-full w-full">
-            <FlowGroph {...props} />
+            {flowProcess ? (
+              <FlowGroph
+                dataID={dataID}
+                flowProcess={flowProcess}
+                flowProcessData={flowProcessData}
+              />
+            ) : null}
           </div>
           <FlowDrawer />
         </ReactFlowProvider>

+ 4 - 3
src/pages/Project/Verification/Detail/index.tsx

@@ -1,13 +1,14 @@
 import ApprovalFlow from '@/pages/Project/Verification/Detail/Flow'
 import type { FC } from 'react'
 type ApprovalDetailProps = {
-  id: string
+  ID: string
+  name: string
   onVisibleChange: (visible: boolean) => void
 }
-const ApprovalDetail: FC<ApprovalDetailProps> = props => {
+const ApprovalDetail: FC<ApprovalDetailProps> = ({ ID, name, onVisibleChange }) => {
   return (
     <div className="w-full h-full">
-      <ApprovalFlow {...props.data} closeAnimateContent={props.onVisibleChange} />
+      <ApprovalFlow dataID={ID} name={name} closeAnimateContent={onVisibleChange} />
     </div>
   )
 }

+ 55 - 45
src/pages/Project/Verification/index.tsx

@@ -2,10 +2,10 @@ import AnimateContent from '@/components/AnimateContent'
 import consts from '@/utils/consts'
 import { PageContainer } from '@ant-design/pro-layout'
 import ProTable from '@ant-design/pro-table'
-import { Button, message, Popconfirm } from 'antd'
+import { Button, message } from 'antd'
 import { useRef, useState } from 'react'
 import { useRequest } from 'umi'
-import { delApproval, getApprovalList, addApproval, updateApproval } from '@/services/api/project'
+import { getApprovalList, saveApproval } from '@/services/api/project'
 import type { ActionType } from '@ant-design/pro-table'
 
 import ApprovalDetail from './Detail'
@@ -25,19 +25,19 @@ const FlowList = () => {
     }
   })
 
-  const { run: tryDel } = useRequest(delApproval, {
-    manual: true,
-    onSuccess: () => {
-      tRef.current?.reload()
-    }
-  })
-  const { run: tryAdd } = useRequest(addApproval, {
-    manual: true,
-    onSuccess: () => {
-      tRef.current?.reload()
-    }
-  })
-  const { run: tryUpdate } = useRequest(updateApproval, {
+  // const { run: tryDel } = useRequest(delApproval, {
+  //   manual: true,
+  //   onSuccess: () => {
+  //     tRef.current?.reload()
+  //   }
+  // })
+  // const { run: tryAdd } = useRequest(addApproval, {
+  //   manual: true,
+  //   onSuccess: () => {
+  //     tRef.current?.reload()
+  //   }
+  // })
+  const { run: tryUpdate } = useRequest(saveApproval, {
     manual: true,
     onSuccess: () => {
       tRef.current?.reload()
@@ -49,9 +49,24 @@ const FlowList = () => {
       title: '名称',
       onHeaderCell: () => ({ style: { textAlign: 'center' } })
     },
-    // {
-    //   dataIndex: ''
-    // }
+    {
+      dataIndex: 'approval',
+      title: '审批名称',
+      onHeaderCell: () => ({ style: { textAlign: 'center' } }),
+      renderText: (_, record) => (
+        <span
+          className="text-primary hover:cursor-pointer hover:text-blue-600"
+          onClick={() => {
+            setState({
+              ...state,
+              visible: true,
+              current: record.approval
+            })
+          }}>
+          {record.approval?.name}
+        </span>
+      )
+    },
     {
       dataIndex: 'opreate',
       title: '操作',
@@ -63,33 +78,23 @@ const FlowList = () => {
             className="pr-2 text-primary cursor-pointer hover:text-hex-967bbd"
             onClick={() => {
               setState({ ...state, modalType: 'update', modalVisible: true })
-              formRef.current?.setFieldsValue({ ID: record.ID, name: record.name })
+
+              setTimeout(() => {
+                formRef.current?.setFieldsValue({ ID: record.ID, name: record.approval?.name })
+              }, 80)
             }}>
-            编辑
+            编辑流程名称
           </span>
-          <span
-            className="px-2 text-primary cursor-pointer hover:text-hex-967bbd"
-            onClick={() => {
-              setState({
-                ...state,
-                visible: true,
-                current: {
-                  id: record.ID,
-                  name: record.name,
-                  flowProcess: record.flowProcess,
-                  flowProcessData: record.flowProcessData
-                }
-              })
-            }}>
-            设置审批人
+          <span className="pl-2 text-primary hover:cursor-pointer hover:text-blue-600">
+            查看流程图
           </span>
-          <Popconfirm
+          {/* <Popconfirm
             title={`确认删除${record.name}吗?`}
             okText="确认"
             cancelText="取消"
             onConfirm={() => tryDel({ ID: record.ID })}>
-            <span className="text-hex-F5222D pl-2 cursor-pointer hover:text-hex-967bbd">删除</span>
-          </Popconfirm>
+            <span className="text-red-500 pl-2 cursor-pointer hover:text-red-600">删除</span>
+          </Popconfirm> */}
         </div>
       )
     }
@@ -119,12 +124,12 @@ const FlowList = () => {
             onSearch: value => setState({ ...state, params: { ...state.params, search: value } })
           },
           actions: [
-            <Button
-              type="primary"
-              onClick={() => setState({ ...state, modalType: 'add', modalVisible: true })}
-              key="add_flow_btn">
-              新建流程
-            </Button>
+            // <Button
+            //   type="primary"
+            //   onClick={() => setState({ ...state, modalType: 'add', modalVisible: true })}
+            //   key="add_flow_btn">
+            //   新建流程
+            // </Button>
           ]
         }}
       />
@@ -133,12 +138,16 @@ const FlowList = () => {
         visible={state.visible}
         onVisibleChange={visible => setState({ ...state, visible })}>
         <ApprovalDetail
-          data={state.current}
+          {...state.current}
           onVisibleChange={visible => setState({ ...state, visible })}
         />
       </AnimateContent>
       <ModalForm
         visible={state.modalVisible}
+        isKeyPressSubmit
+        modalProps={{
+          width: '30%'
+        }}
         onVisibleChange={visible => setState({ ...state, modalVisible: visible })}
         title={`${state.modalType === 'add' ? '新建' : '更新'}审批流程`}
         formRef={formRef}
@@ -150,6 +159,7 @@ const FlowList = () => {
               await tryUpdate(values)
             }
             message.success(`${state.modalType === 'add' ? '新增' : '更新'}成功`)
+            tRef.current?.reload()
             return true
           } catch (error) {
             message.error(error)

+ 54 - 2
src/services/api/project.ts

@@ -1,3 +1,4 @@
+import type { SectorType } from '@/pages/Project/Verification/Detail/Flow/components/Edge'
 import { request } from 'umi'
 
 /** 获取项目列表 POST /api/project/list */
@@ -88,8 +89,8 @@ export async function addApproval(params: { name: string }) {
 }
 
 /** 更新审批流程 */
-export async function updateApproval(params: { ID: string; name: string }) {
-  return request('/approval/update', {
+export async function saveApproval(params: { ID: string; name: string }) {
+  return request('/approval/save/name', {
     method: 'POST',
     data: params
   })
@@ -106,3 +107,54 @@ export async function setProjectPersonor(params: {
     data: params
   })
 }
+
+/** 获取审批流程详情 */
+export async function queryApprovalDetail(params: { ID: string }) {
+  return request('/approval/detail', {
+    params
+  })
+}
+
+/** 审批流程-新增节点 */
+export async function addApprovalNode(params: {
+  ID: string
+  segmentBrotherID: string
+  segment: {
+    ID: string
+    name: string
+    sectorType: SectorType
+  }[]
+}) {
+  return request('/approval/add/segment', {
+    method: 'POST',
+    data: params
+  })
+}
+
+/** 审批流程-移除节点 */
+export async function removeApprovalNode(params: { ID: string; segmentID: string }) {
+  return request('/approval/delete/segment', {
+    method: 'POST',
+    data: params
+  })
+}
+
+/** 审批流程-保存可视化节点数据 */
+export async function saveApprovalProcess(params: { ID: string; flowProcess: string }) {
+  return request('/approval/update/process', {
+    method: 'POST',
+    data: params
+  })
+}
+
+/** 审批流程-保存环节下参与人信息 */
+export async function saveApprovalParticipant(params: {
+  ID: string
+  segmentID: string
+  participantInfo: API.ParticipantInfo
+}) {
+  return request('/approval/set/participant', {
+    method: 'POST',
+    data: params
+  })
+}

+ 18 - 3
src/services/api/typings.d.ts

@@ -1,6 +1,3 @@
-// @ts-ignore
-/* eslint-disable */
-
 declare namespace API {
   type BasicPageParams = {
     /** 页码 */
@@ -281,4 +278,22 @@ declare namespace API {
     backstageName: string
     backstageLogo: string
   }
+
+  type ParticipantInfo = {
+    approvalWay: string
+    accounts: {
+      participantMode: string
+      ID: string
+      name: string
+      institutionID: string
+      configure: string[]
+    }[]
+  }
+
+  type ApprovalProcessItem = {
+    ID: string
+    name: string
+    participantInfo: ParticipantInfo
+    sectorType: 'approval' | 'condition'
+  }
 }