ソースを参照

feat: 项目-审批流程增删改

lanjianrong 3 年 前
コミット
2811d8700f

+ 2 - 1
src/components/AnimateContent/index.tsx

@@ -26,7 +26,8 @@ const AnimateContent: FC<PropsWithChildren<AnimateContentProps>> = ({
         contentWrapperStyle={{
           height: `calc(100vh - 96px ${!disableBreadcrumb ? '- 50px' : ''})`
         }}
-        closeIcon={<Button onClick={() => onVisibleChange(false)}>返回</Button>}>
+        // closeIcon={<Button onClick={() => onVisibleChange(false)}>返回</Button>}>
+      >
         {children}
       </Drawer>
     </div>

+ 1 - 0
src/components/Flow/src/components/Drawer/index.tsx

@@ -126,6 +126,7 @@ const FlowDrawer = () => {
           </div>
           <Select
             showSearch
+            disabled={state.staffOptions.length}
             value={null}
             onChange={triggerChange}
             filterOption={false}

+ 25 - 12
src/components/Flow/src/components/Graph/index.tsx

@@ -1,6 +1,6 @@
 import React, { useContext, useEffect } from 'react'
 import ReactFlow, { Background, MiniMap, Controls } from 'react-flow-renderer'
-import { Actions, FlowContext } from '../../context'
+import { Actions, FlowContext, initialElements } from '../../context'
 
 import type { Elements, NodeTypesType, EdgeTypesType } from 'react-flow-renderer'
 import { CommonNode, InputNode, OutputNode } from '../Node'
@@ -24,23 +24,36 @@ type flowProcessDataItem = {
 }
 
 type FlowGrophProps = {
+  id: string
   flowProcess?: Elements
   flowProcessData?: Record<string, flowProcessDataItem>
 }
-const FlowGroph: React.FC<FlowGrophProps> = ({ flowProcess, flowProcessData }) => {
+const FlowGroph: React.FC<FlowGrophProps> = ({ id, flowProcess, flowProcessData }) => {
   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(Object.entries(flowProcessData))
-      })
+    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
+        })
+
+        dispatch({
+          type: Actions.SET_FLOW_NODE,
+          payload: new Map()
+        })
+      }
     }
-  }, [flowProcess, flowProcessData])
+  }, [id])
   const { elements } = flowState
 
   const defaultOptions = {

+ 7 - 2
src/components/Flow/src/components/Toolbar/index.tsx

@@ -7,7 +7,11 @@ import consts from '@/utils/consts'
 import { isNode } from 'react-flow-renderer'
 import type { Elements } from 'react-flow-renderer'
 
-export default function ToolBar() {
+export default function ToolBar(props: {
+  dataId: string
+  closeAnimateContent: (visible: boolean) => void
+}) {
+  const { dataId, closeAnimateContent } = props
   const { flowState } = useContext(FlowContext)
 
   const validate = (elements: Elements) => {
@@ -27,12 +31,13 @@ export default function ToolBar() {
       cancelText: '取消',
       onOk: async () => {
         const { code = -1 } = await addApprovalFlow({
-          ID: 'a905f20b-ce73-48c7-9173-8520ca9ebe66',
+          ID: dataId,
           flowProcess: flowState.elements,
           flowProcessData: Object.fromEntries(flowState.flowData.entries())
         })
         if (code === consts.RET_CODE.SUCCESS) {
           message.success('发布成功')
+          closeAnimateContent(false)
         }
       }
     })

+ 25 - 23
src/components/Flow/src/context/index.tsx

@@ -17,31 +17,33 @@ type InitialState = {
     nodeType: string
   }
 }
+
+export const initialElements = [
+  {
+    id: '89e43a46-88da-4243-ad0b-3c701819ff63',
+    type: 'start',
+    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',
+    target: '83282d24-22d9-4bd8-a6bf-71d6df94f29e',
+    type: 'common',
+    arrowHeadType: ArrowHeadType.Arrow
+  }
+]
 const initialState: InitialState = {
   // node节点或者是edge
-  elements: [
-    {
-      id: '89e43a46-88da-4243-ad0b-3c701819ff63',
-      type: 'start',
-      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',
-      target: '83282d24-22d9-4bd8-a6bf-71d6df94f29e',
-      type: 'common',
-      arrowHeadType: ArrowHeadType.Arrow
-    }
-  ],
+  elements: initialElements,
   flowData: new Map(),
   drawerConfig: {
     visible: false,

+ 5 - 5
src/components/Flow/src/index.tsx

@@ -1,21 +1,21 @@
-import React, { useContext } from 'react'
+import React from 'react'
 import { ReactFlowProvider } from 'react-flow-renderer'
 import type { FC } from 'react'
 
 import './index.less'
-import { FlowContext, FlowContextProvider } from './context'
+import { FlowContextProvider } from './context'
 import ToolBar from './components/Toolbar'
 import FlowGroph from './components/Graph'
 import FlowDrawer from './components/Drawer'
 
-const ApprovalFlow: FC<ApprovalFlowProps> = () => {
+const ApprovalFlow: FC<ApprovalFlowProps> = props => {
   return (
     <div className="flow-container h-full w-full">
       <FlowContextProvider>
         <ReactFlowProvider>
           {/** 顶部工具栏 */}
-          <ToolBar />
-          <FlowGroph />
+          <ToolBar dataId={props.id} closeAnimateContent={props.closeAnimateContent} />
+          <FlowGroph {...props} />
           <FlowDrawer />
         </ReactFlowProvider>
       </FlowContextProvider>

+ 3 - 2
src/pages/Project/Verification/Detail.tsx

@@ -2,11 +2,12 @@ import ApprovalFlow from '@/components/Flow'
 import type { FC } from 'react'
 type ApprovalDetailProps = {
   id: string
+  onVisibleChange: (visible: boolean) => void
 }
-const ApprovalDetail: FC<ApprovalDetailProps> = ({ id }) => {
+const ApprovalDetail: FC<ApprovalDetailProps> = props => {
   return (
     <div className="w-full h-full">
-      <ApprovalFlow></ApprovalFlow>
+      <ApprovalFlow {...props.data} closeAnimateContent={props.onVisibleChange} />
     </div>
   )
 }

+ 83 - 8
src/pages/Project/Verification/index.tsx

@@ -1,19 +1,47 @@
 import AnimateContent from '@/components/AnimateContent'
-import { getApprovalList } from '@/services/api/project'
 import consts from '@/utils/consts'
 import { PageContainer } from '@ant-design/pro-layout'
 import ProTable from '@ant-design/pro-table'
-import { Button } from 'antd'
-import { useState } from 'react'
+import { Button, message, Popconfirm } from 'antd'
+import { useRef, useState } from 'react'
+import { useRequest } from 'umi'
+import { delApproval, getApprovalList, addApproval, updateApproval } from '@/services/api/project'
+import type { ActionType } from '@ant-design/pro-table'
+
 import ApprovalDetail from './Detail'
+import { ModalForm, ProFormText } from '@ant-design/pro-form'
+import type { ProFormInstance } from '@ant-design/pro-form'
 const FlowList = () => {
+  const tRef = useRef<ActionType>(null)
+  const formRef = useRef<ProFormInstance>(null)
   const [state, setState] = useState({
     params: {},
     visible: false,
+    modalType: 'add',
+    modalVisible: false,
     current: {
       id: null
     }
   })
+
+  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, {
+    manual: true,
+    onSuccess: () => {
+      tRef.current?.reload()
+    }
+  })
   const columns = [
     {
       dataIndex: 'name',
@@ -30,12 +58,33 @@ const FlowList = () => {
           <span
             className="pr-2 text-primary cursor-pointer hover:text-hex-967bbd"
             onClick={() => {
-              setState({ ...state, visible: true, current: { ...state.current, id: record.ID } })
+              setState({ ...state, modalType: 'update', modalVisible: true })
+              formRef.current?.setFieldsValue({ ID: record.ID, name: record.name })
             }}>
             编辑
           </span>
-          <span className="px-2 text-primary cursor-pointer hover:text-hex-967bbd">设置审批人</span>
-          <span className="text-hex-F5222D pl-2 cursor-pointer hover:text-hex-967bbd">删除</span>
+          <span
+            className="px-2 text-primary cursor-pointer hover:text-hex-967bbd"
+            onClick={() => {
+              setState({
+                ...state,
+                visible: true,
+                current: {
+                  id: record.ID,
+                  flowProcess: record.flowProcess,
+                  flowProcessData: record.flowProcessData
+                }
+              })
+            }}>
+            设置审批人
+          </span>
+          <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>
         </div>
       )
     }
@@ -45,6 +94,7 @@ const FlowList = () => {
       <ProTable
         columns={columns}
         rowKey="ID"
+        actionRef={tRef}
         params={state.params}
         request={async (params, filters, sorter) => {
           const {
@@ -65,7 +115,7 @@ const FlowList = () => {
           actions: [
             <Button
               type="primary"
-              onClick={() => setState({ ...state, visible: true })}
+              onClick={() => setState({ ...state, modalType: 'add', modalVisible: true })}
               key="add_flow_btn">
               新建流程
             </Button>
@@ -75,8 +125,33 @@ const FlowList = () => {
       <AnimateContent
         visible={state.visible}
         onVisibleChange={visible => setState({ ...state, visible })}>
-        <ApprovalDetail {...state.current}></ApprovalDetail>
+        <ApprovalDetail
+          data={state.current}
+          onVisibleChange={visible => setState({ ...state, visible })}
+        />
       </AnimateContent>
+      <ModalForm
+        visible={state.modalVisible}
+        onVisibleChange={visible => setState({ ...state, modalVisible: visible })}
+        title={`${state.modalType === 'add' ? '新建' : '更新'}审批流程`}
+        formRef={formRef}
+        onFinish={async values => {
+          try {
+            if (state.modalType === 'add') {
+              await tryAdd(values)
+            } else {
+              await tryUpdate(values)
+            }
+            message.success(`${state.modalType === 'add' ? '新增' : '更新'}成功`)
+            return true
+          } catch (error) {
+            message.error(error)
+            return false
+          }
+        }}>
+        <ProFormText name="ID" label="名称" hidden />
+        <ProFormText name="name" label="名称" />
+      </ModalForm>
     </PageContainer>
   )
 }

+ 0 - 166
src/services/api/pet.ts

@@ -1,166 +0,0 @@
-// @ts-ignore
-/* eslint-disable */
-import { request } from 'umi'
-
-/** Update an existing pet PUT /pet */
-export async function updatePet(body: API.Pet, options?: { [key: string]: any }) {
-  return request<any>('/pet', {
-    method: 'PUT',
-    headers: {
-      'Content-Type': 'application/json'
-    },
-    data: body,
-    ...(options || {})
-  })
-}
-
-/** Add a new pet to the store POST /pet */
-export async function addPet(body: API.Pet, options?: { [key: string]: any }) {
-  return request<any>('/pet', {
-    method: 'POST',
-    headers: {
-      'Content-Type': 'application/json'
-    },
-    data: body,
-    ...(options || {})
-  })
-}
-
-/** Finds Pets by status Multiple status values can be provided with comma separated strings GET /pet/findByStatus */
-export async function findPetsByStatus(
-  params: {
-    // query
-    /** Status values that need to be considered for filter */
-    status: 'available' | 'pending' | 'sold'[]
-  },
-  options?: { [key: string]: any }
-) {
-  return request<API.Pet[]>('/pet/findByStatus', {
-    method: 'GET',
-    params: {
-      ...params
-    },
-
-    ...(options || {})
-  })
-}
-
-/** Finds Pets by tags Muliple tags can be provided with comma separated strings. Use         tag1, tag2, tag3 for testing. GET /pet/findByTags */
-export async function findPetsByTags(
-  params: {
-    // query
-    /** Tags to filter by */
-    tags: string[]
-  },
-  options?: { [key: string]: any }
-) {
-  return request<API.Pet[]>('/pet/findByTags', {
-    method: 'GET',
-    params: {
-      ...params
-    },
-
-    ...(options || {})
-  })
-}
-
-/** Find pet by ID Returns a single pet GET /pet/${param0} */
-export async function getPetById(
-  params: {
-    // path
-    /** ID of pet to return */
-    petId: number
-  },
-  options?: { [key: string]: any }
-) {
-  const { petId: param0 } = params
-  return request<API.Pet>(`/pet/${param0}`, {
-    method: 'GET',
-    params: { ...params },
-
-    ...(options || {})
-  })
-}
-
-/** Updates a pet in the store with form data POST /pet/${param0} */
-export async function updatePetWithForm(
-  params: {
-    // path
-    /** ID of pet that needs to be updated */
-    petId: number
-  },
-  body: { name?: string; status?: string },
-  options?: { [key: string]: any }
-) {
-  const { petId: param0 } = params
-  const formData = new FormData()
-
-  Object.keys(body).forEach(ele => {
-    const item = (body as any)[ele]
-
-    if (item !== undefined && item !== null) {
-      formData.append(ele, typeof item === 'object' ? JSON.stringify(item) : item)
-    }
-  })
-
-  return request<any>(`/pet/${param0}`, {
-    method: 'POST',
-    headers: {
-      'Content-Type': 'application/x-www-form-urlencoded'
-    },
-    params: { ...params },
-    data: formData,
-    ...(options || {})
-  })
-}
-
-/** Deletes a pet DELETE /pet/${param0} */
-export async function deletePet(
-  params: {
-    // header
-    api_key?: string
-    // path
-    /** Pet id to delete */
-    petId: number
-  },
-  options?: { [key: string]: any }
-) {
-  const { petId: param0 } = params
-  return request<any>(`/pet/${param0}`, {
-    method: 'DELETE',
-    params: { ...params },
-    ...(options || {})
-  })
-}
-
-/** uploads an image POST /pet/${param0}/uploadImage */
-export async function uploadFile(
-  params: {
-    // path
-    /** ID of pet to update */
-    petId: number
-  },
-  body: { additionalMetadata?: string; file?: string },
-  options?: { [key: string]: any }
-) {
-  const { petId: param0 } = params
-  const formData = new FormData()
-
-  Object.keys(body).forEach(ele => {
-    const item = (body as any)[ele]
-
-    if (item !== undefined && item !== null) {
-      formData.append(ele, typeof item === 'object' ? JSON.stringify(item) : item)
-    }
-  })
-
-  return request<API.ApiResponse>(`/pet/${param0}/uploadImage`, {
-    method: 'POST',
-    headers: {
-      'Content-Type': 'multipart/form-data'
-    },
-    params: { ...params },
-    data: formData,
-    ...(options || {})
-  })
-}

+ 23 - 0
src/services/api/project.ts

@@ -63,3 +63,26 @@ export async function setApproval(params: API.ApprovalSetParams) {
     data: params
   })
 }
+
+/** 删除审批流程 */
+export async function delApproval(params: { ID: string }) {
+  return request('/approval/delete', {
+    method: 'POST',
+    data: params
+  })
+}
+/** 新增审批流程 */
+export async function addApproval(params: { name: string }) {
+  return request('/approval/add', {
+    method: 'POST',
+    data: params
+  })
+}
+
+/** 更新审批流程 */
+export async function updateApproval(params: { ID: string; name: string }) {
+  return request('/approval/update', {
+    method: 'POST',
+    data: params
+  })
+}