lanjianrong 3 gadi atpakaļ
vecāks
revīzija
0be67600b4

+ 1 - 1
package.json

@@ -56,7 +56,7 @@
     "antd": "^4.14.0",
     "classnames": "^2.2.6",
     "dayjs": "^1.10.7",
-    "form-render": "^1.8.4-beta.3",
+    "form-render": "1.7.3",
     "fr-generator": "^2.6.1",
     "lodash": "^4.17.11",
     "moment": "^2.25.3",

+ 3 - 0
src/components/Flow/index.ts

@@ -0,0 +1,3 @@
+import ApprovalFlow from './src'
+
+export default ApprovalFlow

+ 81 - 15
src/components/Flow/src/components/Edge/Edge.tsx

@@ -1,11 +1,61 @@
-import { PlusOutlined } from '@ant-design/icons'
-import { Button } from 'antd'
-import React from 'react'
+import { buildUUID } from '@/utils/uuid'
+import { ApartmentOutlined, ClusterOutlined, PlusOutlined } from '@ant-design/icons'
+import { Button, Dropdown, Menu, Popover } from 'antd'
+import React, { useState, useContext } from 'react'
 import { getBezierPath, getEdgeCenter, getMarkerEnd } from 'react-flow-renderer'
+import { Actions, FlowContext } from '../../context'
 import styles from './index.less'
 
 const foreignObjectSize = 40
 
+const renderMenu = ({ dispatch, elements, togglePopver, ...node }) => {
+  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 = () => {
+    const newNode = {
+      id: buildUUID(),
+      tpye: 'input',
+      position: { x: 400, y: 0 },
+      data: {}
+    }
+    setElements(elements.concat(newNode))
+  }
+
+  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>
+  )
+}
+
 export function ButtonEdge({
   id,
   sourceX,
@@ -15,10 +65,12 @@ export function ButtonEdge({
   sourcePosition,
   targetPosition,
   style = {},
-  data,
   arrowHeadType,
   markerEndId
 }) {
+  const [visible, setVisible] = useState(false)
+  const { state, dispatch } = useContext(FlowContext)
+  const { elements } = state
   const edgePath = getBezierPath({
     sourceX,
     sourceY,
@@ -35,9 +87,16 @@ export function ButtonEdge({
     targetY
   })
 
+  const togglePopver = (isShow: boolean) => {
+    console.log(isShow)
+
+    setVisible(isShow)
+  }
+
   const onEdgeClick = (evt, id) => {
-    evt.stopPropagation()
-    alert(`remove ${id}`)
+    togglePopver(true)
+    // evt.stopPropagation()
+    // alert(`remove ${id}`)
   }
 
   return (
@@ -47,16 +106,23 @@ export function ButtonEdge({
         width={foreignObjectSize}
         height={foreignObjectSize}
         x={edgeCenterX - foreignObjectSize / 2}
-        y={edgeCenterY - foreignObjectSize / 2}
-        className="edgebutton-foreignobject"
-        requiredExtensions="http://www.w3.org/1999/xhtml">
+        y={edgeCenterY - foreignObjectSize / 2}>
         <div className={styles.addIcon}>
-          <Button
-            icon={<PlusOutlined />}
-            shape="circle"
-            size="small"
-            onClick={event => onEdgeClick(event, id)}
-          />
+          <Popover
+            content={renderMenu({ elements, dispatch, togglePopver })}
+            trigger="click"
+            placement="right"
+            // visible={visible}
+            // destroyTooltipOnHide
+            // onVisibleChange={togglePopver}
+            overlayClassName="flow-popover">
+            <Button
+              icon={<PlusOutlined />}
+              shape="circle"
+              size="small"
+              onClick={event => onEdgeClick(event, id)}
+            />
+          </Popover>
         </div>
       </foreignObject>
     </>

src/components/Flow/src/components/index.less → src/components/Flow/src/components/Edge/index.less


+ 33 - 0
src/components/Flow/src/components/Graph/index.tsx

@@ -0,0 +1,33 @@
+import { useContext } from 'react'
+import ReactFlow, { Background, Controls, MiniMap } from 'react-flow-renderer'
+import { FlowContext } from '../../context'
+
+import type { NodeTypesType, EdgeTypesType } from 'react-flow-renderer'
+import { InputNode, OutputNode } from '../Node'
+import { ButtonEdge } from '../Edge/Edge'
+
+const nodeTypes: NodeTypesType = {
+  start: InputNode,
+  end: OutputNode
+}
+
+const edgeTypes: EdgeTypesType = {
+  add: ButtonEdge
+}
+
+export default function FlowGroph() {
+  const { state } = useContext(FlowContext)
+  const { elements } = state
+
+  const defaultOptions = {
+    nodesDraggable: false, // 不可拖拽
+    zoomOnScroll: false, // 使用鼠标滚轮或触控板放大和缩小图形
+    zoomOnPinch: false // 使用捏合放大和缩小图形
+  }
+  return (
+    <ReactFlow {...defaultOptions} elements={elements} nodeTypes={nodeTypes} edgeTypes={edgeTypes}>
+      <MiniMap />
+      <Background />
+    </ReactFlow>
+  )
+}

+ 3 - 3
src/components/Flow/src/components/Node/index.tsx

@@ -3,11 +3,11 @@ import { Handle } from 'react-flow-renderer'
 export const InputNode = () => {
   return (
     <>
-      <div className="rounded-md shadow-card border min-h-18 w-52">
-        <div className="bg-hex-15388B text-light-500 leading-6 h-6 px-4 rounded-t-md text-12px">
+      <div className="shadow-card min-h-18 w-52 cursor-default">
+        <div className="bg-hex-15388B text-light-500 leading-6 h-6 px-4 text-12px rounded-t-sm">
           发起人
         </div>
-        <div className="text-14px p-4">具有新建权限的员工</div>
+        <div className="text-14px p-4 bg-white rounded-b-sm">具有新建权限的员工</div>
       </div>
       <Handle type="source" id="e-1" position="bottom" />
     </>

+ 21 - 2
src/components/Flow/src/context/index.tsx

@@ -1,6 +1,6 @@
 import React, { createContext, useReducer } from 'react'
 import reducer from './reducer'
-import * as Actions from './actions'
+import { Actions } from './reducer'
 
 import type { Elements } from 'react-flow-renderer'
 
@@ -9,10 +9,29 @@ const FlowContext = createContext()
 type InitialState = {
   elements: Elements
   flowData: Map<any, any>
+  modalConfig: {
+    visible: boolean
+    nodeType: string
+    nodeType: string
+  }
 }
 const initialState: InitialState = {
   // node节点或者是edge
-  elements: [],
+  elements: [
+    {
+      id: 'ewb-1',
+      type: 'start',
+      position: { x: 250, y: 0 }
+    },
+    { id: 'ewb-2', type: 'end', position: { x: 250, y: 300 } },
+
+    {
+      id: 'edge-1-2',
+      source: 'ewb-1',
+      target: 'ewb-2',
+      type: 'add'
+    }
+  ],
   flowData: new Map(),
   modalConfig: {
     visible: false,

+ 0 - 0
src/components/Flow/src/context/reducer.js


+ 75 - 0
src/components/Flow/src/context/reducer.ts

@@ -0,0 +1,75 @@
+// context/reducer.js
+import { removeElements } from 'react-flow-renderer'
+
+import type { Elements } from 'react-flow-renderer'
+
+export enum Actions {
+  SET_ELEMENTS = 'set_elements',
+  SET_FLOW_NODE = 'set_flow_node',
+  REMOVE_FLOW_NODE = 'remove_flow_node',
+  OPEN_MODAL = 'open_modal',
+  CLOSE_MODAL = 'close_modal'
+}
+const setElements = (state, elements: Elements) => ({
+  ...state,
+  elements: Array.isArray(elements) ? elements : []
+})
+
+const setFlowNode = (state, node) => {
+  if (!node || !node.id) return state
+  const nodeId = node.id
+  const res = { ...state }
+  res.flowData.set(nodeId, node)
+  return res
+}
+
+const removeFlowNode = (state, node) => {
+  const { id } = node
+  const { flowData } = state
+  const res = { ...state }
+
+  if (flowData.get(id)) {
+    flowData.delete(id)
+    res.elements = removeElements([node], state.elements)
+  }
+  return res
+}
+
+const openModal = (state, node) =>
+  node?.id
+    ? {
+        ...state,
+        modalConfig: {
+          visible: true,
+          nodeType: node.type,
+          nodeId: node.id
+        }
+      }
+    : state
+
+const closeModal = state => ({
+  ...state,
+  modalConfig: {
+    visible: false,
+    nodeType: '',
+    nodeId: ''
+  }
+})
+
+// 管理所有处理函数
+const handlerMap = {
+  [Actions.SET_FLOW_NODE]: setFlowNode,
+  [Actions.REMOVE_FLOW_NODE]: removeFlowNode,
+  [Actions.OPEN_MODAL]: openModal,
+  [Actions.CLOSE_MODAL]: closeModal,
+  [Actions.SET_ELEMENTS]: setElements
+}
+
+const reducer = (state, action) => {
+  const { type, payload } = action
+  const handler = handlerMap[type]
+  const res = typeof handler === 'function' && handler(state, payload)
+  return res || state
+}
+
+export default reducer

+ 35 - 65
src/components/Flow/src/index.tsx

@@ -1,72 +1,42 @@
-import React, { useState, useReducer, useContext } from 'react'
-import ReactFlow, {
-  removeElements,
-  addEdge,
-  MiniMap,
-  Controls,
-  Background
-} from 'react-flow-renderer'
+import React, { useContext } from 'react'
+import { ReactFlowProvider } from 'react-flow-renderer'
 import type { FC } from 'react'
-import type { Elements, NodeTypesType, EdgeTypesType } from 'react-flow-renderer'
 
 import './index.less'
-import { InputNode, OutputNode } from './components/Node'
-import { ButtonEdge } from './components/Edge/Edge'
-
-type ApprovalFlowProps<T = any> = {
-  elements: Elements<T>
-}
-
-const nodeTypes: NodeTypesType = {
-  start: InputNode,
-  end: OutputNode
-}
-
-const edgeTypes: EdgeTypesType = {
-  add: ButtonEdge
-}
-
-const flowContext = React.createContext()
-
-function reducer(state, action) {
-  switch (action.type) {
-    case 'reset':
-      return initialState
-    case 'increment':
-      return { count: state.count + 1 }
-    case 'decrement':
-      return { count: state.count - 1 }
-    default:
-      return state
-  }
-}
-
-const initialElements = [
-  {
-    id: 'ewb-1',
-    type: 'start',
-    position: { x: 250, y: 0 }
-  },
-  { id: 'ewb-2', type: 'end', position: { x: 250, y: 300 } },
-
-  {
-    id: 'edge-1-2',
-    source: 'ewb-1',
-    target: 'ewb-2',
-    type: 'add'
-  }
-]
-
-const ApprovalFlow: FC<ApprovalFlowProps> = ({ elements }) => {
-  // const [elements, setElements] = useState<Elements>(initialElements);
-  const [elements, dispatch] = useReducer<Elements>(reducer, initialElements)
+import { FlowContext, FlowContextProvider } from './context'
+import ToolBar from './components/Toolbar'
+import FlowGroph from './components/Graph'
+
+// type ApprovalFlowProps<T = any> = {
+//   elements: Elements<T>
+// }
+
+// const initialElements = [
+//   {
+//     id: 'ewb-1',
+//     type: 'start',
+//     position: { x: 250, y: 0 }
+//   },
+//   { id: 'ewb-2', type: 'end', position: { x: 250, y: 300 } },
+
+//   {
+//     id: 'edge-1-2',
+//     source: 'ewb-1',
+//     target: 'ewb-2',
+//     type: 'add'
+//   }
+// ]
+
+const ApprovalFlow: FC<ApprovalFlowProps> = () => {
   return (
-    <div className="w-full h-full">
-      <ReactFlow elements={elements} nodeTypes={nodeTypes} edgeTypes={edgeTypes}>
-        <MiniMap />
-        <Controls />
-        <Background />
-      </ReactFlow>
+    <div className="flow-container h-full w-full">
+      <FlowContextProvider>
+        <ReactFlowProvider>
+          {/** 顶部工具栏 */}
+          {/* <ToolBar /> */}
+          <FlowGroph />
+        </ReactFlowProvider>
+      </FlowContextProvider>
     </div>
   )
 }

+ 3 - 0
src/components/Flow/src/util.ts

@@ -0,0 +1,3 @@
+// 根据传入的elements 以及新的node节点对elements元素进行再计算
+
+export function generateElements(elements, node) {}

+ 10 - 0
src/global.less

@@ -83,3 +83,13 @@ ol {
     @apply text-white bg-primary rounded-4px;
   }
 }
+
+.flow-popover {
+  & .ant-popover-inner-content {
+    padding: 0 2px !important;
+  }
+  & .ant-menu-vertical > .ant-menu-item {
+    height: 25px;
+    line-height: 25px;
+  }
+}

+ 1 - 1
src/pages/Dashboard/index.tsx

@@ -3,7 +3,7 @@ import { PageContainer } from '@ant-design/pro-layout'
 const Dashboard = () => {
   return (
     <PageContainer>
-      <div>这里是首页</div>
+      <div className="w-20 h-20 rounded-3xl border"></div>
     </PageContainer>
   )
 }

+ 1 - 3
src/pages/Institutions/Company/List/components/CompanyDrawer.tsx

@@ -85,9 +85,7 @@ const CompanyDrawer: React.FC<CompanyModalProps> = ({
         setVisible(false)
       }}
       title={type === ModalType.ADD ? '新增企事业单位' : '编辑企事业单位'}>
-      {schema && (
-        <FormRender form={form} schema={JSON.parse(schema)} onFinish={onFinish} onMount={onMount} />
-      )}
+      {schema && <FormRender form={form} schema={schema} onFinish={onFinish} onMount={onMount} />}
       <div className="ml-120px">
         <Button onClick={form.submit}>提交</Button>
       </div>

+ 4 - 1
src/pages/Institutions/Staff/components/DebounceSelect.tsx

@@ -53,8 +53,11 @@ function DebounceSelect<
     if (v) {
       onChange(v)
     }
+    setTimeout(() => {
+      console.log(state.options)
+    }, 500)
   }
-  // console.log(state)
+  // console.log(state.options)
 
   return (
     <Select<ValueType>

+ 11 - 9
src/pages/Institutions/Staff/components/StaffDrawer.tsx

@@ -55,7 +55,7 @@ const StaffDrawer: React.FC<StaffModalProps> = ({
         })
       }
     }
-    if (!organizationList?.length) {
+    if (defaultFormData?.dataID && !organizationList?.length) {
       dispatch({
         type: 'institutions/queryOrganizationList',
         payload: {
@@ -110,16 +110,15 @@ const StaffDrawer: React.FC<StaffModalProps> = ({
     }
     return []
   }
-  const { institution } = defaultFormData || {}
 
   const SiteInput = props => {
-    // console.log('widget props:', props)
+    console.log('widget props:', props)
     return (
       <DebounceSelect
         className="w-full"
         fetchOptions={queryInstitutionOptions}
         showSearch
-        defaultOptions={institution && [{ label: institution.name, value: institution.ID }]}
+        // options={institution?.map(item => ({ label: item.name, value: item.ID }))}
         {...props}
       />
     )
@@ -129,15 +128,18 @@ const StaffDrawer: React.FC<StaffModalProps> = ({
     form.setValues({ ...defaultFormData })
     delay(80).then(() => {
       form.setSchemaByPath('accountType', {
-        enum: accountTypeList.map(item => item.value),
-        enumNames: accountTypeList.map(item => item.label)
+        type: 'string',
+        widget: 'select',
+        props: {
+          options: accountTypeList
+        }
+        // enum: accountTypeList.map(item => item.value),
+        // enumNames: accountTypeList.map(item => item.label)
       })
       form.setSchemaByPath('institutionID', {
         type: 'string',
         widget: 'site'
       })
-    })
-    delay(80).then(() => {
       form.setSchemaByPath('organizationalStructureID', {
         type: 'string',
         widget: 'treeSelect',
@@ -205,7 +207,7 @@ const StaffDrawer: React.FC<StaffModalProps> = ({
       {schema && (
         <FormRender
           form={form}
-          schema={JSON.parse(schema)}
+          schema={schema}
           onFinish={onFinish}
           onMount={onMount}
           widgets={{ site: SiteInput }}

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

@@ -143,6 +143,7 @@ const CompanyList: React.FC<ListProps> = ({ schema, dispatch, accountTypeList })
       )
     }
   ]
+
   return (
     <div>
       <ProTable<API.AccountListItem>

+ 2 - 0
src/pages/Institutions/model.ts

@@ -50,6 +50,8 @@ const InstitutionsModel: InstitutionsModelType = {
 
       const response = yield call(getAccountTypeList)
       if (response?.code === consts.RET_CODE.SUCCESS) {
+        console.log(response.data)
+
         yield put({
           type: 'save',
           payload: {

+ 1 - 3
src/pages/Project/Management/components/ProjectModal.tsx

@@ -98,9 +98,7 @@ const ProjectModal: React.FC<ProjectModalProps> = ({
         setVisible(false)
       }}
       title={type === ModalType.ADD ? '新增项目' : '编辑项目'}>
-      {schema && (
-        <FormRender form={form} schema={JSON.parse(schema)} onFinish={onFinish} onMount={onMount} />
-      )}
+      {schema && <FormRender form={form} schema={schema} onFinish={onFinish} onMount={onMount} />}
       <div className="ml-120px">
         {/** 重置会导致下拉框的options丢失 */}
         {/* <Button onClick={() => form.setValues({})}>重置</Button> */}

+ 3 - 0
src/pages/Project/Management/index.tsx

@@ -50,6 +50,9 @@ const List: React.FC<ListProps> = ({ schema, dispatch, pTypeList }) => {
   })
   const columns: ProColumnType<API.ProjectListItem>[] = [
     {
+      dataIndex: 'ID'
+    },
+    {
       dataIndex: 'name',
       title: '项目名称'
     },

+ 1 - 1
src/pages/Project/Verification/index.tsx

@@ -1,3 +1,4 @@
+import ApprovalFlow from '@/components/Flow'
 import React, { useState } from 'react'
 
 import ReactFlow, {
@@ -9,7 +10,6 @@ import ReactFlow, {
 } from 'react-flow-renderer'
 
 // import ButtonEdge from './components/Flow/components/Edge'
-import ApprovalFlow from '../../../components/Flow'
 
 const onLoad = reactFlowInstance => reactFlowInstance.fitView()
 

+ 1 - 1
src/pages/Schema/Base/detail.tsx

@@ -54,7 +54,7 @@ const Detail: React.FC<DetailProps> = ({ dispatch, base, location }) => {
       <div className="bg-white shadow-card p-4" style={{ height: 'calc(100vh - 196px)' }}>
         <Generator
           ref={genRef}
-          defaultValue={(base?.[columnType] && JSON.parse(base?.[columnType]?.schema)) || {}}
+          defaultValue={base?.[columnType]?.schema || {}}
           controlButtons={[true, true]}
           extraButtons={[
             true,

+ 1 - 3
src/pages/Schema/Base/index.tsx

@@ -61,9 +61,7 @@ const Index: React.FC<BaseProps> = ({ base, dispatch }) => {
           <div className="text-right mb-4">
             <Button onClick={() => gotoDetail(state.activeKey, currentSchema?.ID)}>编辑</Button>
           </div>
-          <div>
-            {currentSchema && <FormRender form={form} schema={JSON.parse(currentSchema?.schema)} />}
-          </div>
+          <div>{currentSchema && <FormRender form={form} schema={currentSchema?.schema} />}</div>
         </div>
       </div>
     </PageContainer>

+ 1 - 1
src/utils/util.ts

@@ -5,7 +5,7 @@ export function generateColumns(c, s) {
   // 新的列
   const nC = [...c]
   if (s) {
-    const properties = JSON.parse(s).properties
+    const { properties } = s
     const keys = Object.keys(properties)
     keys.forEach(item => {
       const isExist = c.some(column => column.dataIndex === item)

+ 28 - 0
src/utils/uuid.ts

@@ -0,0 +1,28 @@
+const hexList: string[] = []
+for (let i = 0; i <= 15; i++) {
+  hexList[i] = i.toString(16)
+}
+
+export function buildUUID(): string {
+  let uuid = ''
+  for (let i = 1; i <= 36; i++) {
+    if (i === 9 || i === 14 || i === 19 || i === 24) {
+      uuid += '-'
+    } else if (i === 15) {
+      uuid += 4
+    } else if (i === 20) {
+      uuid += hexList[(Math.random() * 4) | 8]
+    } else {
+      uuid += hexList[(Math.random() * 16) | 0]
+    }
+  }
+  return uuid.replace(/-/g, '')
+}
+
+let unique = 0
+export function buildShortUUID(prefix = ''): string {
+  const time = Date.now()
+  const random = Math.floor(Math.random() * 1000000000)
+  unique++
+  return prefix + '_' + random + unique + String(time)
+}