Sfoglia il codice sorgente

Merge branch 'dev' of http://192.168.1.41:3000/lanjianrong/financial_audit_console into dev

outaozhen 3 anni fa
parent
commit
2f49de69b2

+ 39 - 15
src/pages/Project/Verification/Detail/Flow/components/Drawer/ParticipantCard.tsx

@@ -1,9 +1,11 @@
-import React, { useEffect, useState } from 'react'
+import React, { useContext, useEffect, useMemo, 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'
+import { eId, FlowContext } from '../../context'
+import { isEdge, isNode } from 'react-flow-renderer'
 
 type ParticipantCardProps = {
   defaultData: {
@@ -17,14 +19,6 @@ type ParticipantCardProps = {
   updateAction: (values: Recordable<string>) => void
 }
 
-const configureArr = [
-  // ConfigureType.ADDSIGN,
-  ConfigureType.ASSISTAUDIT,
-  ConfigureType.RETURN
-  // ConfigureType.REVOKE,
-  // ConfigureType.SKIP
-]
-
 const configureItemTitle = {
   [ConfigureType.ADDSIGN]: {
     name: '允许加签',
@@ -45,6 +39,10 @@ const configureItemTitle = {
   [ConfigureType.SKIP]: {
     name: '允许跳过',
     short: '跳'
+  },
+  [ConfigureType.NEXTSECTOR]: {
+    name: '允许添加下一环节',
+    short: '环'
   }
 }
 const ParticipantCard: React.FC<ParticipantCardProps> = ({
@@ -52,24 +50,50 @@ const ParticipantCard: React.FC<ParticipantCardProps> = ({
   defaultData,
   institutionList
 }) => {
-  const [staffList, setStaffList] = useState([])
+  const { flowState } = useContext(FlowContext)
+  const { modalConfig: { nodeID = null } = {}, elements } = flowState
+  const curNode = elements.filter(item => isNode(item)).find(item => item.id === nodeID)
+  const lastEdge = elements.filter(item => isEdge(item)).find(item => item.target === eId)
+
+  const [state, setState] = useState({
+    configureArr: [
+      // ConfigureType.ADDSIGN,
+      ConfigureType.ASSISTAUDIT,
+      ConfigureType.RETURN
+      // ConfigureType.REVOKE,
+      // ConfigureType.SKIP
+    ],
+    staffList: []
+  })
   const { run: tryUpdateStaffList } = useRequest(
     dataID => queryAccountList({ current: 1, pageSize: 214000, dataID, enable: ['true'] }),
     {
       manual: true,
       onSuccess(result) {
-        setStaffList(result.items.map(item => ({ label: item.name, value: item.ID })))
+        setState({
+          ...state,
+          staffList: result.items.map(item => ({ label: item.name, value: item.ID }))
+        })
       }
     }
   )
 
   useEffect(() => {
-    if (defaultData.ID && defaultData.institutionID && !staffList?.length) {
+    if (defaultData.ID && defaultData.institutionID && !state.staffList?.length) {
       tryUpdateStaffList(defaultData.institutionID)
     }
   }, [])
 
-  if (defaultData.ID && !staffList?.length) return null
+  useEffect(() => {
+    if ([ParticipantMode.ACCOUNT].includes(defaultData.participantMode)) {
+      // 当前的node元素
+      // 最后一个edge及诶单
+      if (lastEdge && curNode && lastEdge.source === curNode.id) {
+        setState({ ...state, configureArr: [...state.configureArr, ConfigureType.NEXTSECTOR] })
+      }
+    }
+  }, [defaultData.participantMode, curNode, lastEdge])
+  if (defaultData.ID && !state.staffList?.length) return null
   return (
     <>
       <div className="children:mb-4">
@@ -101,7 +125,7 @@ const ParticipantCard: React.FC<ParticipantCardProps> = ({
           <Select
             className="w-full"
             defaultValue={defaultData.ID}
-            options={staffList}
+            options={state.staffList}
             onChange={(_, options) => {
               updateAction({ ...defaultData, ID: options?.value, name: options?.label })
             }}
@@ -132,7 +156,7 @@ const ParticipantCard: React.FC<ParticipantCardProps> = ({
           }
           key="1">
           <Row>
-            {configureArr.map(item => (
+            {state.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>

+ 14 - 7
src/pages/Project/Verification/Detail/Flow/components/Drawer/index.tsx

@@ -11,11 +11,13 @@ import ParticipantCard from './ParticipantCard'
 import { useRequest } from 'umi'
 import { saveApprovalParticipant } from '@/services/api/project'
 import consts from '@/utils/consts'
+import classNames from 'classnames'
 
 const FlowDrawer = () => {
   const { flowState, dispatch } = useContext(FlowContext)
   const { modalConfig: { nodeID = null, visible = false } = {}, dataID } = flowState
   const [state, setState] = useState({
+    loading: false,
     institutionList: [],
     approvalWay: ApprovalWay.ACCOUNT,
     accounts: [],
@@ -61,6 +63,7 @@ const FlowDrawer = () => {
   }
 
   const handleOnOk = async () => {
+    setState({ ...state, loading: true })
     const { approvalWay, accounts } = state
     if (!accounts.every(item => item.institutionID && item.ID)) {
       setState({
@@ -92,12 +95,15 @@ const FlowDrawer = () => {
     })
     if (code === consts.RET_CODE.SUCCESS) {
       message.success('保存成功')
-      dispatch({
-        type: Actions.SET_FLOW_NODE,
-        payload
-      })
+      setTimeout(() => {
+        dispatch({
+          type: Actions.SET_FLOW_NODE,
+          payload
+        })
+      }, 80)
       handleCancel()
     }
+    setState({ ...state, loading: false })
   }
 
   // 添加用户-参与者卡片
@@ -145,11 +151,11 @@ const FlowDrawer = () => {
       }
       return (
         <div
-          className={[
+          className={classNames(
             'shadow-base border rounded-md flex flex-col justify-between px-4 py-2 mt-6',
             styles.participantCard,
-            state.validityIDs.includes(participant.uid) ? 'border-red-500' : ''
-          ].join(' ')}
+            { 'border-red-500': state.validityIDs.includes(participant.uid) }
+          )}
           key={participant.uid}>
           <div className="flex flex-row justify-between">
             <div className="text-base font-medium">参与者</div>
@@ -182,6 +188,7 @@ const FlowDrawer = () => {
           <Button
             className="ml-8px"
             type="primary"
+            loading={state.loading}
             disabled={!state.accounts.length}
             onClick={handleOnOk}>
             确定

+ 2 - 0
src/pages/Project/Verification/Detail/Flow/components/Edge/index.less

@@ -8,9 +8,11 @@
   justify-content: center;
   padding: 10px;
   :global(.ant-btn) {
+    // color: #e4e4e7;
     // box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.1);
     transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
     &:hover {
+      // color: #40a9ff;
       // box-shadow: 0 13px 27px 0 rgba(0, 0, 0, 0.1);
       transform: scale(1.3);
     }

+ 82 - 121
src/pages/Project/Verification/Detail/Flow/components/Edge/index.tsx

@@ -1,9 +1,11 @@
 import { buildUUID } from '@/utils/uuid'
-import { PlusOutlined, SolutionOutlined } from '@ant-design/icons'
+import { SolutionOutlined } from '@ant-design/icons'
 import { Button, Popover } from 'antd'
 import React, { useMemo, useState, useContext } from 'react'
+import type { EdgeProps } from 'react-flow-renderer'
+import { isNode } from 'react-flow-renderer'
 import { getBezierPath, getEdgeCenter, getMarkerEnd, useStoreState } from 'react-flow-renderer'
-import { Actions, FlowContext } from '../../context'
+import { Actions, eId, FlowContext } from '../../context'
 import { generateElements, genreateElementEnum, getEdgeParams } from '../../utils'
 import { addApprovalNode } from '@/services/api/project'
 import styles from './index.less'
@@ -11,6 +13,7 @@ import 'antd/lib/button/style/css'
 import consts from '@/utils/consts'
 import useLoading from '../../hooks/useLoading'
 import IconFont from '@/components/IconFont'
+import { ConfigureType } from '../../enum'
 const foreignObjectSize = 50
 
 export enum SectorType {
@@ -18,105 +21,7 @@ export enum SectorType {
   CONDITION = 'condition'
 }
 
-const RenderMenu = ({ dataID, source, dispatch, elements, togglePopver, id, flowInstance }) => {
-  const { run: showLoading } = useLoading()
-  const setElements = els => {
-    dispatch({
-      type: Actions.SET_ELEMENTS,
-      payload: els
-    })
-  }
-  // const edit = () => {
-  // togglePopver(false)
-  // dispatch({
-  //   type: Actions.OPEN_MODAL,
-  //   payload: {
-  //     id: node.id,
-  //     type: 'relation'
-  //   }
-  // })
-  // }
-  // const remove = () => {
-  //   dispatch({
-  //     type: Actions.REMOVE_FLOW_NODE,
-  //     payload: node
-  //   })
-  // }
-
-  const addAuditor = async () => {
-    const uid = buildUUID()
-    const newNode = {
-      id: uid,
-      type: 'input',
-      data: {}
-    }
-    const currentEdge = elements.find(item => item.id === id)
-    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
-      }
-    })
-    if (code === consts.RET_CODE.SUCCESS) {
-      setElements(newElements)
-      dispatch({
-        type: Actions.SET_FLOW_NODE,
-        payload: {
-          id: uid,
-          node: null
-        }
-      })
-      togglePopver(false)
-      setTimeout(() => {
-        showLoading()
-        flowInstance?.fitView()
-      }, 80)
-    }
-  }
-
-  return (
-    // <Menu>
-    //   <Menu.Item key="1" onClick={addAuditor}>
-    //     <ClusterOutlined />
-    //     <span className="ml-1">审批人</span>
-    //   </Menu.Item>
-    //   {/* <Menu.Item key="2" onClick={remove}>
-    //     <ApartmentOutlined />
-    //     <span className="ml-1">条件分支</span>
-    //   </Menu.Item> */}
-    // </Menu>
-    <ul className="m-0 p-0 ">
-      <li>
-        <Button type="dashed" onClick={addAuditor} icon={<SolutionOutlined />}>
-          审批人
-        </Button>
-      </li>
-      {/* <li className="mt-2 condition">
-        <Button type="dashed" onClick={addAuditor} icon={<BranchesOutlined />}>
-          条件分支
-        </Button>
-      </li> */}
-    </ul>
-    //   <ul className="m-0 p-0">
-    //   <li className="rounded-2px border border-hex-d9d9d9 border-dashed text-hex-1890ff px-17px py-9px cursor-pointer">
-    //     <SolutionOutlined />
-    //     <span className="ml-9px">审批人</span>
-    //   </li>
-    //   <li className="mt-2 rounded-2px border border-hex-d9d9d9 border-dashed text-hex-52c41a px-17px py-9px cursor-pointer">
-    //     <BranchesOutlined />
-    //     <span className="ml-9px">条件分支</span>
-    //   </li>
-    // </ul>
-  )
-}
-
-export function CommonEdge(props) {
+export function CommonEdge(props: EdgeProps) {
   const {
     id,
     sourceX,
@@ -132,7 +37,21 @@ export function CommonEdge(props) {
 
   const [visible, setVisible] = useState(false)
   const { flowState, dispatch } = useContext(FlowContext)
-  const { elements, flowInstance, dataID, readPretty } = flowState
+  const { elements, flowInstance, flowData, dataID, readPretty } = flowState
+  const { run: showLoading } = useLoading()
+  const setElements = els => {
+    dispatch({
+      type: Actions.SET_ELEMENTS,
+      payload: els
+    })
+  }
+  const showBtn =
+    flowData.get(source) && target === eId
+      ? flowData
+          .get(source)
+          ?.accounts.every(item => !item.configure.includes(ConfigureType.NEXTSECTOR))
+      : true
+
   const nodes = useStoreState(store => store.nodes)
   const markerEnd = getMarkerEnd(arrowHeadType, markerEndId)
 
@@ -170,26 +89,68 @@ export function CommonEdge(props) {
     // alert(`remove ${id}`)
   }
 
+  const addAuditor = async () => {
+    const uid = buildUUID()
+    const newNode = {
+      id: uid,
+      type: 'input',
+      data: {}
+    }
+    const currentEdge = elements.find(item => item.id === id)
+    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
+      }
+    })
+    if (code === consts.RET_CODE.SUCCESS) {
+      setElements(newElements)
+      dispatch({
+        type: Actions.SET_FLOW_NODE,
+        payload: {
+          id: uid,
+          node: null
+        }
+      })
+      togglePopver(false)
+      setTimeout(() => {
+        showLoading()
+        flowInstance?.fitView()
+      }, 80)
+    }
+  }
+
   return (
     <>
       <path id={id} style={style} className={styles.flowPath} d={d} markerEnd={markerEnd} />
-      <foreignObject
-        width={foreignObjectSize}
-        height={foreignObjectSize}
-        x={edgeCenterX - foreignObjectSize / 2}
-        y={edgeCenterY - foreignObjectSize / 2}>
-        <div className={styles.addIcon}>
-          {!readPretty ? (
+      {showBtn && !readPretty && (
+        <foreignObject
+          width={foreignObjectSize}
+          height={foreignObjectSize}
+          x={edgeCenterX - foreignObjectSize / 2}
+          y={edgeCenterY - foreignObjectSize / 2}>
+          <div className={styles.addIcon}>
             <Popover
-              content={RenderMenu({
-                elements,
-                dispatch,
-                source,
-                togglePopver,
-                id,
-                flowInstance,
-                dataID
-              })}
+              content={
+                <ul className="m-0 p-0">
+                  <li>
+                    <Button type="dashed" onClick={addAuditor} icon={<SolutionOutlined />}>
+                      审批人
+                    </Button>
+                  </li>
+                  {/* <li className="mt-2 condition">
+        <Button type="dashed" onClick={addAuditor} icon={<BranchesOutlined />}>
+          条件分支
+        </Button>
+      </li> */}
+                </ul>
+              }
               trigger="click"
               placement="right"
               visible={visible}
@@ -203,9 +164,9 @@ export function CommonEdge(props) {
                 onClick={event => onEdgeClick(event, id)}
               />
             </Popover>
-          ) : null}
-        </div>
-      </foreignObject>
+          </div>
+        </foreignObject>
+      )}
     </>
   )
 }

+ 29 - 46
src/pages/Project/Verification/Detail/Flow/components/Graph/index.tsx

@@ -1,4 +1,4 @@
-import React, { useContext, useEffect } from 'react'
+import React, { useContext, useEffect, useMemo } from 'react'
 import ReactFlow, { Background, MiniMap, Controls } from 'react-flow-renderer'
 import { Actions, FlowContext, initialElements } from '../../context'
 
@@ -6,12 +6,6 @@ import type { Elements, NodeTypesType, EdgeTypesType } from 'react-flow-renderer
 import { CommonNode, InputNode, OutputNode } from '../Node'
 import { CommonEdge } from '../Edge'
 
-const nodeTypes: NodeTypesType = {
-  start: InputNode,
-  end: OutputNode,
-  common: CommonNode
-}
-
 const edgeTypes: EdgeTypesType = {
   common: CommonEdge
 }
@@ -35,15 +29,27 @@ const FlowGroph: React.FC<FlowGrophProps> = ({
   readPretty
 }) => {
   const { flowState, dispatch } = useContext(FlowContext)
-  useEffect(() => {
-    if (flowProcess?.length) {
-      dispatch({
-        type: Actions.SET_ELEMENTS,
-        payload: flowProcess
-      })
-      dispatch({
-        type: Actions.SET_FLOW_NODE,
-        payload: new Map(
+  const nodeTypes: NodeTypesType = useMemo(
+    () => ({
+      start: InputNode,
+      end: OutputNode,
+      common: CommonNode
+    }),
+    []
+  )
+
+  const { elements } = flowState
+
+  const defaultOptions = {
+    nodesDraggable: false, // 不可拖拽
+    // zoomOnScroll: false, // 使用鼠标滚轮或触控板放大和缩小图形
+    zoomOnPinch: false, // 使用捏合放大和缩小图形
+    // snapToGrid: true,
+    onLoad: reactFlowInstance => {
+      const payload = {}
+      if (flowProcess?.length) {
+        payload.elements = flowProcess
+        payload.flowData = new Map(
           Object.entries(
             flowProcessData.reduce((prev, curr) => {
               if (curr.ID) {
@@ -53,39 +59,15 @@ const FlowGroph: React.FC<FlowGrophProps> = ({
             }, {})
           )
         )
-      })
-    } else {
-      dispatch({
-        type: Actions.SET_ELEMENTS,
-        payload: initialElements
-      })
-
-      dispatch({
-        type: Actions.SET_FLOW_NODE,
-        payload: new Map()
-      })
-    }
-  }, [])
-  const { elements } = flowState
+      }
+      payload.flowInstance = reactFlowInstance
+      payload.dataID = dataID
+      payload.readPretty = readPretty
 
-  const defaultOptions = {
-    nodesDraggable: false, // 不可拖拽
-    // zoomOnScroll: false, // 使用鼠标滚轮或触控板放大和缩小图形
-    zoomOnPinch: false, // 使用捏合放大和缩小图形
-    // snapToGrid: true,
-    onLoad: reactFlowInstance => {
       dispatch({
-        type: Actions.SET_FLOW_INSTANCE,
-        payload: reactFlowInstance
+        type: Actions.INIT_FLOW_CONTEXT,
+        payload
       })
-      dispatch({
-        type: Actions.SET_FOLW_PROPS,
-        payload: {
-          dataID,
-          readPretty
-        }
-      })
-
       setTimeout(() => {
         reactFlowInstance?.fitView()
       }, 0)
@@ -97,6 +79,7 @@ const FlowGroph: React.FC<FlowGrophProps> = ({
     showFitView: false,
     showInteractive: false
   }
+
   return (
     <ReactFlow {...defaultOptions} elements={elements} nodeTypes={nodeTypes} edgeTypes={edgeTypes}>
       <MiniMap />

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

@@ -29,14 +29,12 @@ export const initialElements = [
     position: { x: 500, y: 0 },
     data: { sort: 0 }
   },
-
   {
     id: '83282d24-22d9-4bd8-a6bf-71d6df94f29e',
     type: 'end',
     position: { x: 500, y: 200 },
     data: { sort: 1 }
   },
-
   {
     id: '29b4469f-9522-4688-8a35-a57ca4875a7a',
     source: '89e43a46-88da-4243-ad0b-3c701819ff63',
@@ -59,4 +57,6 @@ const FlowContextProvider = props => {
   return <FlowContext.Provider value={{ flowState, dispatch }}>{children}</FlowContext.Provider>
 }
 
+export const sId = '89e43a46-88da-4243-ad0b-3c701819ff63'
+export const eId = '83282d24-22d9-4bd8-a6bf-71d6df94f29e'
 export { FlowContext, FlowContextProvider, Actions }

+ 6 - 1
src/pages/Project/Verification/Detail/Flow/context/reducer.ts

@@ -70,6 +70,10 @@ const closeModal = state => ({
   }
 })
 
+const onStoreLoad = (state, payload) => ({
+  ...state,
+  ...payload
+})
 // 管理所有处理函数
 const handlerMap = {
   [Actions.SET_FLOW_NODE]: setFlowNode,
@@ -78,7 +82,8 @@ const handlerMap = {
   [Actions.CLOSE_MODAL]: closeModal,
   [Actions.SET_ELEMENTS]: setElements,
   [Actions.SET_FLOW_INSTANCE]: setFlowInstance,
-  [Actions.SET_FOLW_PROPS]: setFlowProps
+  [Actions.SET_FOLW_PROPS]: setFlowProps,
+  [Actions.INIT_FLOW_CONTEXT]: onStoreLoad
 }
 
 const reducer = (state: InitialState, action: Actions) => {

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

@@ -1,4 +1,5 @@
 export enum Actions {
+  INIT_FLOW_CONTEXT = 'init_flow_context',
   SET_ELEMENTS = 'set_elements',
   SET_FLOW_NODE = 'set_flow_node',
   SET_FLOW_INSTANCE = 'set_flow_instance',
@@ -35,5 +36,6 @@ export enum ConfigureType {
   RETURN = 'return',
   REVOKE = 'revoke',
   ASSISTAUDIT = 'assistAudit',
-  ADDSIGN = 'addSign'
+  ADDSIGN = 'addSign',
+  NEXTSECTOR = 'nextSector'
 }

+ 7 - 3
src/pages/Project/Verification/Detail/Flow/hooks/useLoading.tsx

@@ -1,6 +1,6 @@
 import { isDef } from '@/utils/is'
 import { useDebounceFn } from 'ahooks'
-import { useContext } from 'react'
+import { useContext, useEffect } from 'react'
 import { FlowContext } from '../context'
 import { Actions } from '../enum'
 type LoadingOptions = {
@@ -17,8 +17,12 @@ const useLoading = (opt: LoadingOptions = { delay: 500 }) => {
       }
     })
   }
-  const { run: resetLoading } = useDebounceFn(cancel, { wait: opt.delay })
-
+  const { run: resetLoading, cancel: tryCancelDebounceFn } = useDebounceFn(cancel, {
+    wait: opt.delay
+  })
+  useEffect(() => {
+    return () => tryCancelDebounceFn()
+  }, [])
   const run = () => {
     // 若当前loading为true停止执行
     if (isDef(flowState.canvasLoading) && flowState.canvasLoading) {

+ 9 - 7
src/pages/Project/Verification/Detail/Flow/index.less

@@ -11,11 +11,18 @@
   .node_content {
     cursor: default;
     .remove_node {
+      transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
       position: absolute;
       top: -13px;
       right: -16px;
-      display: none;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      border: 1px solid #ff4d4f;
+      border-radius: 99px 99px 99px 99px;
+      box-shadow: 0 2px 0 1px rgba(0, 0, 0, 0.0430000014603138);
       cursor: pointer;
+      opacity: 0;
     }
     .operate_node {
       cursor: pointer;
@@ -24,12 +31,7 @@
       border-color: blue;
     }
     &:hover .remove_node {
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      border: 1px solid #ff4d4f;
-      border-radius: 99px 99px 99px 99px;
-      box-shadow: 0 2px 0 1px rgba(0, 0, 0, 0.0430000014603138);
+      opacity: 1;
     }
   }
   & .react-flow__controls {