瀏覽代碼

refactor: Flow组件ContextProvider相关逻辑

lanjianrong 3 年之前
父節點
當前提交
942c3a6021

+ 1 - 1
package.json

@@ -46,7 +46,7 @@
     "@umijs/max": "4.0.19",
     "@umijs/plugins": "4.0.19",
     "@umijs/route-utils": "^2.2.0",
-    "ahooks": "^3.0.0",
+    "ahooks": "^3.7.1",
     "antd": "4.23.0",
     "array-move": "^4.0.0",
     "classnames": "^2.2.6",

+ 8 - 11
src/pages/Business/Step/components/Flow/components/Graph/index.tsx

@@ -10,16 +10,13 @@ import { Actions, FlowContext } from '../../context'
 import type { NodeTypesType, EdgeTypesType } from 'react-flow-renderer'
 import { BaseNode, ConditionNode, ConditionButton, ConditionHanderNode, InputNode, OutputNode } from '../Node'
 import { SmoothStepEdge } from '../Edge'
-import { FlowElements } from '@/components/Flow/src/type'
 import { NodeType } from '@/components/Flow/src/enum/index'
 
-type FlowGrophProps<T> = {
-  defaultElements?: FlowElements<T>
-  elements?: FlowElements<T>
-}
+function FlowGroph() {
+  const { flowStore, dispatch } = useContext(FlowContext)
+
+  const elements = useMemo(() => flowStore.elements, [flowStore.elements])
 
-function FlowGroph<T>({ defaultElements, elements }: FlowGrophProps<T>) {
-  const { dispatch } = useContext(FlowContext)
   const nodeTypes: NodeTypesType = useMemo(
     () => ({
       [NodeType.INPUT]: InputNode,
@@ -46,9 +43,9 @@ function FlowGroph<T>({ defaultElements, elements }: FlowGrophProps<T>) {
     // snapToGrid: true,
     onLoad: (flowInstance: OnLoadParams) => {
       const payload = {}
-      if (defaultElements?.length || elements?.length) {
-        payload.elements = defaultElements || elements
-      }
+      // if (defaultElements?.length || elements?.length) {
+      //   payload.elements = defaultElements || elements
+      // }
       payload.flowInstance = flowInstance
       dispatch({
         type: Actions.INIT_FLOW_CONTEXT,
@@ -70,7 +67,7 @@ function FlowGroph<T>({ defaultElements, elements }: FlowGrophProps<T>) {
     <ReactFlow
       {...defaultOptions}
       selectNodesOnDrag={false}
-      elements={defaultElements}
+      elements={elements}
       nodeTypes={nodeTypes}
       edgeTypes={edgeTypes}>
       <MiniMap />

+ 7 - 29
src/pages/Business/Step/components/Flow/context/index.tsx

@@ -1,43 +1,21 @@
 import React, { createContext, useReducer, Dispatch } from 'react'
 import reducer from './reducer'
 import { Actions } from '../enum'
-
 import type { Elements, OnLoadParams } from 'react-flow-renderer'
-import { ArrowHeadType } from 'react-flow-renderer'
-import { createUid } from '@/utils/util'
 
 export type FlowContextState = {
   elements: Elements
   flowInstance?: OnLoadParams
 }
-
-const FlowContext = createContext<{ flowStore: FlowContextState; dispatch: Dispatch<Actions> }>({})
-
-export const initialElements = [
-  {
-    id: 'input',
-    type: 'input',
-    position: { x: 0, y: 0 }
-  },
-  {
-    id: 'output',
-    type: 'output',
-    position: { x: 0, y: 0 }
-  },
-  {
-    id: createUid(),
-    source: 'input',
-    target: 'output',
-    arrowHeadType: ArrowHeadType.Arrow
-  }
-]
-const initialState: FlowContextState = {
-  elements: initialElements
+type FlowContextProviderProps = {
+  children: React.ReactNode
+  initialElements: Elements
 }
+const FlowContext = createContext<{ flowStore: FlowContextState; dispatch: Dispatch<Actions> }>({})
 
-const FlowContextProvider = props => {
-  const { children } = props
-  const [flowStore, dispatch] = useReducer(reducer, initialState)
+const FlowContextProvider: React.FC<FlowContextProviderProps> = props => {
+  const { children, initialElements = [] } = props
+  const [flowStore, dispatch] = useReducer(reducer, { elements: initialElements })
   return <FlowContext.Provider value={{ flowStore, dispatch }}>{children}</FlowContext.Provider>
 }
 

+ 2 - 2
src/pages/Business/Step/components/Flow/index.tsx

@@ -11,12 +11,12 @@ type ApprovalFlowProps<T> = {
 function ApprovalFlow<T>({ defaultElements }: ApprovalFlowProps<T>) {
   return (
     <div className="flow-container h-full w-full">
-      <FlowContextProvider>
+      <FlowContextProvider initialElements={defaultElements}>
         <ReactFlowProvider>
           {/** 顶部工具栏 */}
           {/* <ToolBar dataId={props.dataID} closeAnimateContent={props.closeAnimateContent} /> */}
           <div className="h-full w-full">
-            <FlowGroph<T> defaultElements={defaultElements} />
+            <FlowGroph<T> />
           </div>
         </ReactFlowProvider>
       </FlowContextProvider>

+ 0 - 226
src/pages/Business/Step/components/Flow/utils.ts

@@ -1,226 +0,0 @@
-// 根据传入的elements、node节点、edge节点对elements元素进行再计算
-
-import { ArrowHeadType, isEdge, isNode, Position } from 'react-flow-renderer'
-import { buildUUID } from '@/utils/uuid'
-
-import type { Edge, Elements, Node, XYPosition } from 'react-flow-renderer'
-import { ParticipantMode } from './enum'
-
-export enum genreateElementEnum {
-  ADD = 'add',
-  DEL = 'del'
-}
-
-/** 通过传入的新的node节点以及当前操作的edge构建新的elements */
-function buildWithNewNode(elements: Elements, newNode?: Node | null, oldEdge?: Edge | null) {
-  if (!newNode || !oldEdge) return elements
-  // 取出当前操作edge的数据
-  const { id: edgeId, source: edgeSid, target: edgeTid } = oldEdge
-  // 原来node节点中的sort
-  const sort = elements.find(item => item.id === edgeTid)?.data?.sort
-  // console.log(sort)
-
-  let oldIdx
-  const newElements: Elements = [...elements].map((item, idx) => {
-    if (isNode(item)) {
-      if (item.data?.sort >= sort) {
-        return { ...item, data: { ...item.data, sort: item.data.sort + 1 } }
-      }
-      if (item.id === edgeTid) {
-        oldIdx = idx
-      }
-      return item
-    } else if (isEdge(item) && item.id === edgeId) {
-      // 修改原来edge的source
-      const edge = { ...item, source: newNode.id }
-      return edge
-    }
-
-    return item
-  })
-  // 添加新的node和edge
-  // TODO: 在当前oldEdge target指向的节点前插入node
-  newElements.splice(oldIdx + 1, 0, {
-    ...newNode,
-    position: { x: 500, y: sort * 200 },
-    type: 'common',
-    data: { sort }
-  })
-
-  newElements.push({
-    id: buildUUID(),
-    source: edgeSid,
-    target: newNode.id,
-    type: 'common',
-    arrowHeadType: ArrowHeadType.Arrow
-  })
-  return newElements.map(item => {
-    if (isNode(item)) {
-      return { ...item, position: { ...item.position, y: item.data?.sort * 200 } }
-    }
-    return item
-  })
-}
-
-/** 传入需要删除的node构建新的elements */
-function buildWithDeletedNode(elements: Elements, deletedNode?: Node | null) {
-  if (!deletedNode) return elements
-  const {
-    id,
-    data: { sort }
-  } = deletedNode
-  // 1.先找到node关联的的那两条线
-  const upEdge: Edge = elements.find(item => isEdge(item) && item.target === id)
-  const downEdge: Edge = elements.find(item => isEdge(item) && item.source === id)
-  // 2. 删除对应的edge以及node以及重写sort
-  return elements
-    .filter(item => item.id !== id && item.id !== downEdge.id)
-    .map(item => {
-      if (isNode(item) && item.data?.sort > sort)
-        return {
-          ...item,
-          data: { sort: item.data?.sort - 1 },
-          position: { ...item.position, y: (item.data?.sort - 1) * 200 }
-        }
-      if (isEdge(item) && item.id === upEdge.id) {
-        return { ...item, target: downEdge.target }
-      }
-      return item
-    })
-}
-export const generateElements = (opreate: genreateElementEnum, elements: Elements) =>
-  opreate === genreateElementEnum.ADD
-    ? (newNode?: Node | null, oldEdge?: Edge | null) => buildWithNewNode(elements, newNode, oldEdge)
-    : (deletedNode?: Node | null) => buildWithDeletedNode(elements, deletedNode)
-
-// export function generateElements(elements: Elements, newNode?: Node | null, oldEdge?: Edge | null) {
-
-// }
-
-// this helper function returns the intersection point
-// of the line between the center of the intersectionNode and the target node
-function getNodeIntersection(intersectionNode: Node, targetNode: Node): XYPosition {
-  // https://math.stackexchange.com/questions/1724792/an-algorithm-for-finding-the-intersection-point-between-a-center-of-vision-and-a
-  const {
-    width: intersectionNodeWidth,
-    height: intersectionNodeHeight,
-    position: intersectionNodePosition
-  } = intersectionNode.__rf
-  const targetPosition = targetNode.__rf.position
-
-  const w = intersectionNodeWidth / 2
-  const h = intersectionNodeHeight / 2
-
-  const x2 = intersectionNodePosition.x + w
-  const y2 = intersectionNodePosition.y + h
-  const x1 = targetPosition.x + w
-  const y1 = targetPosition.y + h
-
-  const xx1 = (x1 - x2) / (2 * w) - (y1 - y2) / (2 * h)
-  const yy1 = (x1 - x2) / (2 * w) + (y1 - y2) / (2 * h)
-  const a = 1 / (Math.abs(xx1) + Math.abs(yy1))
-  const xx3 = a * xx1
-  const yy3 = a * yy1
-  const x = w * (xx3 + yy3) + x2
-  const y = h * (-xx3 + yy3) + y2
-
-  return { x, y }
-}
-
-// returns the position (top,right,bottom or right) passed node compared to the intersection point
-function getEdgePosition(node: Node, intersectionPoint: XYPosition) {
-  const n = { ...node.__rf.position, ...node.__rf }
-  const nx = Math.round(n.x)
-  const ny = Math.round(n.y)
-  const px = Math.round(intersectionPoint.x)
-  const py = Math.round(intersectionPoint.y)
-
-  if (px <= nx + 1) {
-    return Position.Left
-  }
-  if (px >= nx + n.width - 1) {
-    return Position.Right
-  }
-  if (py <= ny + 1) {
-    return Position.Top
-  }
-  if (py >= n.y + n.height - 1) {
-    return Position.Bottom
-  }
-
-  return Position.Top
-}
-
-// returns the parameters (sx, sy, tx, ty, sourcePos, targetPos) you need to create an edge
-export function getEdgeParams(source: Node, target: Node) {
-  const sourceIntersectionPoint = getNodeIntersection(source, target)
-  const targetIntersectionPoint = getNodeIntersection(target, source)
-
-  const sourcePos = getEdgePosition(source, sourceIntersectionPoint)
-  const targetPos = getEdgePosition(target, targetIntersectionPoint)
-
-  return {
-    sx: sourceIntersectionPoint.x,
-    sy: sourceIntersectionPoint.y,
-    tx: targetIntersectionPoint.x,
-    ty: targetIntersectionPoint.y,
-    sourcePos,
-    targetPos
-  }
-}
-
-export const participantSchema = {
-  type: 'object',
-  properties: {
-    participantMode: {
-      type: 'string',
-      title: '',
-      required: true,
-      'x-decorator': 'FormItem',
-      'x-component': 'Select',
-      default: ParticipantMode.ACCOUNT,
-      enum: [
-        { label: '仅用户模式', value: ParticipantMode.ACCOUNT }
-        // { label: '角色模式', value: ParticipantMode.ROLE }
-      ],
-      'x-index': 1
-    },
-    name: {
-      type: 'string',
-      title: '',
-      required: true,
-      'x-decorator': 'FormItem',
-      'x-component': 'Input',
-      'x-display': 'none',
-      'x-index': 4
-    },
-    institutionID: {
-      type: 'string',
-      title: '',
-      required: true,
-      'x-decorator': 'FormItem',
-      'x-component': 'Select',
-      'x-component-props': {
-        placeholder: '请选择单位',
-        showSearch: true,
-        virtual: true,
-        optionFilterProp: 'label'
-      },
-      'x-reactions': ['{{useAsyncDataSource(loadInstitutionList)}}'],
-      'x-index': 3
-    },
-    ID: {
-      type: 'string',
-      title: '',
-      required: true,
-      'x-decorator': 'FormItem',
-      'x-component': 'Select',
-      'x-component-props': {
-        placeholder: '请选择用户',
-        showSearch: true,
-        optionFilterProp: 'label'
-      },
-      'x-index': 4
-    }
-  }
-}