lanjianrong 3 年之前
父节点
当前提交
2ef0588567

+ 18 - 1
config/routes.ts

@@ -40,7 +40,24 @@
       {
         path: '/project/management',
         name: 'management',
-        component: './Project/Management'
+        routes: [
+          {
+            path: '/project/management',
+            redirect: '/project/management/list'
+          },
+          {
+            path: '/project/management/list',
+            name: 'list',
+            hideInMenu: true,
+            component: './Project/Management/List'
+          },
+          {
+            path: '/project/management/detail',
+            name: 'detail',
+            hideInMenu: true,
+            component: './Project/Management/Detail'
+          }
+        ]
       },
       {
         path: '/project/verification',

+ 2 - 3
src/components/Flow/src/components/Drawer/index.tsx

@@ -3,7 +3,7 @@ import { Button, Drawer, Radio, Tag } from 'antd'
 import { Actions, FlowContext } from '../../context'
 import type { RadioChangeEvent } from 'antd'
 import { ApprovalMethod, ApprovalType } from '../../enum'
-import TreeNodeSelect from '@/pages/Project/Management/components/TreeNodeSelect'
+import TreeNodeSelect from '@/pages/Project/Management/List/components/TreeNodeSelect'
 
 const FlowDrawer = () => {
   const { flowState, dispatch } = useContext(FlowContext)
@@ -79,8 +79,7 @@ const FlowDrawer = () => {
         </div>
       }
       title={<span className="font-medium">设置审批人</span>}
-      width="30%"
-    >
+      width="30%">
       <div>
         <Radio.Group
           options={plainOptions}

+ 2 - 0
src/locales/zh-CN/menu.ts

@@ -17,6 +17,8 @@ export default {
   'menu.account.logout': '退出登录',
   'menu.project': '项目',
   'menu.project.management': '项目管理',
+  'menu.project.management.list': '项目列表',
+  'menu.project.management.detail': '项目详情',
   'menu.project.created': '项目创建人',
   'menu.project.verification': '审批流程',
   'menu.institutions': '企事业单位',

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

@@ -3,7 +3,7 @@ import ProTable from '@ant-design/pro-table'
 import consts from '@/utils/consts'
 import { Button } from 'antd'
 import { useRef, useState, useEffect } from 'react'
-import { connect, history, Link } from 'umi'
+import { connect, history } from 'umi'
 import { queryInstitutionList } from '@/services/api/institution'
 import type { ProColumnType, ActionType } from '@ant-design/pro-table'
 import type { ConnectProps } from 'umi'

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

@@ -12,7 +12,7 @@ import { Button, Form, message, Table } from 'antd'
 import { saveCreator } from '@/services/api/user'
 import { useRequest } from 'umi'
 import { getProjectList } from '@/services/api/project'
-import TreeNodeSelect from '../Management/components/TreeNodeSelect'
+import TreeNodeSelect from '../Management/List/components/TreeNodeSelect'
 
 enum CreatedModalType {
   ADD = '1',

+ 91 - 0
src/pages/Project/Management/Detail/components/ProjectInfo.tsx

@@ -0,0 +1,91 @@
+import { createForm, onFieldMount } from '@formily/core'
+import { createSchemaField, Field, FormProvider } from '@formily/react'
+import { FormLayout, FormItem, Input, Select } from '@formily/antd'
+import { BaseMenuEnum, SchemaEnum } from '@/pages/Schema/Base'
+import type { ConnectProps } from 'umi'
+import { connect } from 'umi'
+import { useEffect } from 'react'
+import { connectSchema } from '@/utils/schema'
+import { Card } from 'antd'
+
+type ProjectInfoProps = ConnectProps & {
+  projectSchema: Recordable<string>
+  pTypeList: { label: string; value: string }[]
+  defaultFormData: API.ProjectListItem
+}
+
+const ProjectInfo: React.FC<ProjectInfoProps> = ({
+  dispatch,
+  pTypeList,
+  projectSchema,
+  defaultFormData
+}) => {
+  useEffect(() => {
+    if (!pTypeList.length) {
+      dispatch({
+        type: 'project/queryProjectTypeList'
+      })
+    }
+    if (!projectSchema) {
+      dispatch({
+        type: 'schemaBase/querySchema',
+        payload: {
+          columnType: BaseMenuEnum.PROJECT
+        }
+      })
+    }
+  }, [])
+
+  const formInstance = createForm({
+    validateFirst: true,
+    initialValues: defaultFormData,
+    readPretty: true,
+    effects() {
+      onFieldMount('projectTypeID', field => {
+        field.dataSource = pTypeList
+      })
+    }
+  })
+
+  const SchemaField = createSchemaField({
+    components: {
+      FormLayout,
+      FormItem,
+      Input,
+      Select
+    }
+  })
+  return (
+    <Card>
+      <div className="max-w-560px ">
+        <FormProvider form={formInstance}>
+          <SchemaField
+            schema={connectSchema(SchemaEnum?.[BaseMenuEnum.PROJECT], projectSchema)}
+            // scope={{ useAsyncDataSource }}
+          />
+          {/* <Field
+            name="created"
+            title="创建人"
+            initialValue={defaultFormData.created ? `${defaultFormData.created?.name}(${defaultFormData.created?.institution.name})` : null}
+            component={[Input]}
+            decorator={[FormItem, { labelCol: 6, wrapperCol: 8 }]}
+          />
+          <Field
+            name="reportAccount"
+            title="编辑者"
+            initialValue={'111'}
+            component={[Input]}
+            decorator={[FormItem, { labelCol: 6, wrapperCol: 8 }]}
+          /> */}
+        </FormProvider>
+      </div>
+    </Card>
+  )
+}
+
+export default connect(
+  ({ project, schemaBase }: { project: ProjectModelState; schemaBase: SchemaBaseModelState }) => ({
+    pTypeList: project.projectTypeList.map(item => ({ label: item.name, value: item.ID })),
+    projectSchema: schemaBase.base[BaseMenuEnum.PROJECT]
+  })
+)(ProjectInfo)

+ 51 - 0
src/pages/Project/Management/Detail/components/RolePerm.tsx

@@ -0,0 +1,51 @@
+import ProForm from '@ant-design/pro-form'
+import type { ProFormInstance } from '@ant-design/pro-form'
+import React, { useEffect, useRef } from 'react'
+import { useRequest } from 'umi'
+import { setProjectPersonor } from '@/services/api/project'
+import { Card, Form, message } from 'antd'
+import TreeNodeSelect from '../../List/components/TreeNodeSelect'
+
+type RolePermProps = {
+  dataID: string
+  defaultFormData: {
+    createdID: string
+    reportAccount: string[]
+  }
+}
+export default function RolePerm(props: RolePermProps) {
+  const { dataID, defaultFormData } = props
+  const ref = useRef<ProFormInstance>(null)
+  const { run: trySetProjectPersonor } = useRequest(setProjectPersonor, {
+    manual: true,
+    onSuccess: () => {
+      message.success('提交成功')
+    }
+  })
+  useEffect(() => {
+    if (dataID) {
+      ref.current?.setFieldsValue({ ...defaultFormData })
+    }
+  }, [dataID])
+
+  return (
+    <Card>
+      <div className="max-w-320px">
+        <ProForm
+          formRef={ref}
+          onFinish={formData => trySetProjectPersonor({ ...formData, ID: dataID })}>
+          <Form.Item
+            tooltip="创建项目、管理编辑者、上传项目数据、编制项目、添加批注"
+            label="创建人"
+            name="createdID"
+            rules={[{ required: true, message: '请选择创建人' }]}>
+            <TreeNodeSelect params={{ isCreated: 1 }} />
+          </Form.Item>
+          <Form.Item label="编辑者" name="reportAccount" tooltip="编制项目、上传项目数据、添加批注">
+            <TreeNodeSelect multiple />
+          </Form.Item>
+        </ProForm>
+      </div>
+    </Card>
+  )
+}

+ 74 - 0
src/pages/Project/Management/Detail/index.tsx

@@ -0,0 +1,74 @@
+import { getProject } from '@/services/api/project'
+import { PageContainer } from '@ant-design/pro-layout'
+import React, { useEffect, useState } from 'react'
+import { useRequest } from 'umi'
+import ProjectInfo from './components/ProjectInfo'
+import type { RouteComponentProps } from 'umi/node_modules/@types/react-router'
+import RolePerm from './components/RolePerm'
+
+export enum TabEnum {
+  INFO = '0',
+  UPLOAD_ROLE = '1',
+  STAFF_ROLE = '2'
+}
+
+const menuMap: Record<string, string> = {
+  [TabEnum.INFO]: '项目信息',
+  // [TabEnum.UPLOAD_ROLE]: '上传权限',
+  [TabEnum.STAFF_ROLE]: '角色权限'
+}
+
+type ProjectDetailStateProps = {
+  projectInfo: Nullable<API.ProjectListItem>
+  selectKey: TabEnum
+}
+const ProjectDetail: React.FC<RouteComponentProps> = ({ location }) => {
+  const { dataID } = location.state
+  const [state, setState] = useState<ProjectDetailStateProps>({
+    projectInfo: null,
+    selectKey: TabEnum.INFO
+  })
+  const { run: tryGetProjectInfo } = useRequest(getProject, {
+    manual: true,
+    onSuccess: (result: API.ProjectListItem) => setState({ ...state, projectInfo: result })
+  })
+  useEffect(() => {
+    if (dataID) {
+      tryGetProjectInfo({ ID: dataID })
+    }
+  }, [dataID])
+
+  const renderChildren = () => {
+    switch (state.selectKey) {
+      case TabEnum.INFO:
+        return <ProjectInfo defaultFormData={state.projectInfo} />
+        break
+      case TabEnum.STAFF_ROLE:
+        return (
+          <RolePerm
+            dataID={state.projectInfo?.ID}
+            defaultFormData={{
+              createdID: state.projectInfo?.created?.ID,
+              reportAccount: state.projectInfo?.reportAccount.map(item => item.ID)
+            }}
+          />
+        )
+      default:
+        break
+    }
+  }
+  return (
+    <PageContainer
+      title={state.projectInfo?.name}
+      fixedHeader
+      tabList={[
+        { tab: menuMap[TabEnum.INFO], key: TabEnum.INFO },
+        // { tab: menuMap[TabEnum.UPLOAD_ROLE], key: TabEnum.UPLOAD_ROLE },
+        { tab: menuMap[TabEnum.STAFF_ROLE], key: TabEnum.STAFF_ROLE }
+      ]}
+      onTabChange={key => setState({ ...state, selectKey: key })}>
+      {renderChildren()}
+    </PageContainer>
+  )
+}
+export default ProjectDetail

+ 19 - 73
src/pages/Project/Management/components/Detail.tsx

@@ -1,23 +1,18 @@
 import { connect, useRequest } from 'umi'
 import { useEffect, useRef, useState } from 'react'
 import { Form, message, Tabs } from 'antd'
-import {
-  getApprovalList,
-  getProject,
-  setApproval,
-  setProjectPersonor
-} from '@/services/api/project'
-import { delay } from '@/utils/util'
-import FormRender, { useForm } from 'form-render'
-import { BaseMenuEnum } from '@/pages/Schema/Base'
+import { FormLayout, FormItem, Input, Select } from '@formily/antd'
+import { getApprovalList, setApproval, setProjectPersonor } from '@/services/api/project'
+import { BaseMenuEnum, SchemaEnum } from '@/pages/Schema/Base'
 import type { SchemaBaseModelState } from '@/pages/Schema/Base/model'
 import type { ConnectProps } from 'umi'
-import type { ProjectModelState } from '../../model'
 import ProForm from '@ant-design/pro-form'
 // import TreeNodeSelect from './TreeNodeSelect'
-import { ModalType } from './ProjectModal'
-import consts from '@/utils/consts'
+import type { ModalType } from './ProjectModal'
 import TreeNodeSelect from './TreeNodeSelect'
+import { connectSchema } from '@/utils/schema'
+import { createForm, onFieldMount, onFormReact } from '@formily/core'
+import { createSchemaField, Field, FormProvider } from '@formily/react'
 
 enum FormType {
   UPLOAD_PERM = '1',
@@ -26,25 +21,21 @@ enum FormType {
 
 type ProjectModalProps = ConnectProps & {
   visible: boolean
-  onVisibleChange: (visible: boolean) => void
   type: ModalType
   defaultFormData?: {
     dataID: string
   }
   reload: () => void
-  schema?: Record<string, any> | null
+  projectSchema: Record<string, any> | null
+  pTypeList: { label: string; value: string }[]
 }
 const DetailModal: React.FC<ProjectModalProps> = ({
   visible,
-  // onVisibleChange,
-  dispatch,
-  schema,
-  type,
+  projectSchema,
   defaultFormData,
   pTypeList,
   reload
 }) => {
-  const form = useForm()
   const ref = useRef<FormInstance>(null)
   const ref2 = useRef<FormInstance>(null)
   const { TabPane } = Tabs
@@ -79,14 +70,6 @@ const DetailModal: React.FC<ProjectModalProps> = ({
   })
 
   useEffect(() => {
-    if (visible && !schema) {
-      dispatch({
-        type: 'schemaBase/querySchema',
-        payload: {
-          columnType: BaseMenuEnum.PROJECT
-        }
-      })
-    }
     if (state.activeKey === '2') {
       const TabFormData = {
         accountID: state.account.reportAccount?.ID,
@@ -100,39 +83,6 @@ const DetailModal: React.FC<ProjectModalProps> = ({
     }
   }, [visible, state.activeKey])
 
-  const onMount = async () => {
-    const { dataID } = defaultFormData
-    const { code = -1, data } = await getProject({ ID: dataID })
-    if (code === consts.RET_CODE.SUCCESS) {
-      const {
-        created: { ID: createdID },
-        reportAccount,
-        ...currentFormData
-      } = { ...data }
-
-      setState({
-        ...state,
-        account: currentFormData,
-        perm: { createdID, reportAccount: reportAccount.map(item => item.ID) }
-      })
-      const keys = Object.keys(currentFormData)
-      keys.forEach(key => {
-        if (currentFormData[key] instanceof Object) {
-          const targetMap = currentFormData[key]
-          delete currentFormData[key]
-          currentFormData[`${key}ID`] = targetMap.ID
-        }
-      })
-      form.setValues({ ...currentFormData })
-    }
-    delay(80).then(() => {
-      form.setSchemaByPath('projectTypeID', {
-        enum: pTypeList.map(item => item.value),
-        enumNames: pTypeList.map(item => item.label)
-      })
-    })
-  }
-
   const onChange = key => {
     setState({ ...state, activeKey: key })
     if (key === '2') {
@@ -172,14 +122,13 @@ const DetailModal: React.FC<ProjectModalProps> = ({
   return (
     <Tabs onChange={onChange}>
       <TabPane tab="项目信息" key="1">
-        {schema && (
-          <FormRender
-            form={form}
-            schema={schema}
-            onMount={onMount}
-            readOnly={type === ModalType.PREVIEW ? true : false}
+        <FormProvider form={formInstance}>
+          <SchemaField
+            schema={connectSchema(SchemaEnum?.[BaseMenuEnum.PROJECT], projectSchema)}
+            // scope={{ useAsyncDataSource }}
           />
-        )}
+          <Field name="input" component={[Input, { placeholder: 'Please Input' }]} />
+        </FormProvider>
       </TabPane>
       {/* <TabPane tab="上传权限" key="2">
         <ProForm
@@ -232,9 +181,6 @@ const DetailModal: React.FC<ProjectModalProps> = ({
   )
 }
 
-export default connect(
-  ({ project, schemaBase }: { project: ProjectModelState; schemaBase: SchemaBaseModelState }) => ({
-    pTypeList: project.projectTypeList.map(item => ({ label: item.name, value: item.ID })),
-    schema: schemaBase.base[BaseMenuEnum.PROJECT]?.schema
-  })
-)(DetailModal)
+export default connect(({ schemaBase }: { schemaBase: SchemaBaseModelState }) => ({
+  projectSchema: schemaBase.base[BaseMenuEnum.PROJECT]
+}))(DetailModal)

+ 9 - 14
src/pages/Project/Management/components/ProjectModal.tsx

@@ -1,14 +1,7 @@
 import { connect, useRequest } from 'umi'
-import { useEffect } from 'react'
-import { Button, message } from 'antd'
-import { addProject, getProject, updateProject } from '@/services/api/project'
-import { delay } from '@/utils/util'
-import FormRender, { useForm } from 'form-render'
+import { message } from 'antd'
+import { addProject, updateProject } from '@/services/api/project'
 import { BaseMenuEnum, SchemaEnum } from '@/pages/Schema/Base'
-import type { SchemaBaseModelState } from '@/pages/Schema/Base/model'
-import type { ConnectProps } from 'umi'
-import type { ProjectModelState } from '../../model'
-import consts from '@/utils/consts'
 import { createForm, onFieldMount, onFormInit } from '@formily/core'
 import {
   Form,
@@ -23,6 +16,11 @@ import {
 import { createSchemaField } from '@formily/react'
 import { connectSchema } from '@/utils/schema'
 
+import type { SchemaBaseModelState } from '@/pages/Schema/Base/model'
+import type { ConnectProps } from 'umi'
+import type { ProjectModelState } from '../../../model'
+import { useEffect } from 'react'
+
 export enum ModalType {
   ADD = 0,
   UPDATE = 1,
@@ -49,7 +47,7 @@ const ProjectModal: React.FC<ProjectModalProps> = ({
 }) => {
   const formInstance = createForm({
     validateFirst: true,
-    initialValues: type === ModalType.ADD ? null : defaultFormData,
+    initialValues: defaultFormData,
     effects() {
       onFieldMount('projectTypeID', field => (field.dataSource = pTypeList))
     }
@@ -95,10 +93,7 @@ const ProjectModal: React.FC<ProjectModalProps> = ({
   return (
     <div className="max-w-800px mt-20px">
       <Form form={formInstance} labelCol={6} wrapperCol={8}>
-        <SchemaField
-          schema={connectSchema(SchemaEnum?.[BaseMenuEnum.PROJECT], projectSchema)}
-          // scope={{ useAsyncDataSource }}
-        />
+        <SchemaField schema={connectSchema(SchemaEnum?.[BaseMenuEnum.PROJECT], projectSchema)} />
         <FormButtonGroup.Sticky>
           <FormButtonGroup.FormItem>
             <Submit onSubmit={onFinish}>提交</Submit>

src/pages/Project/Management/components/TreeNodeSelect.tsx → src/pages/Project/Management/List/components/TreeNodeSelect.tsx


+ 27 - 33
src/pages/Project/Management/index.tsx

@@ -3,7 +3,7 @@ import ProTable from '@ant-design/pro-table'
 import { Button, Popconfirm, Tag } from 'antd'
 import consts from '@/utils/consts'
 import { useRef, useState, useEffect } from 'react'
-import { connect, useRequest } from 'umi'
+import { connect, history, useRequest } from 'umi'
 import { DeleteOutlined } from '@ant-design/icons'
 import ProjectModal, { ModalType } from './components/ProjectModal'
 import dayjs from 'dayjs'
@@ -14,7 +14,7 @@ import AnimateContent from '@/components/AnimateContent'
 import { transformToSchemaData } from '@/utils/schema'
 import type { ProColumnType, ActionType } from '@ant-design/pro-table'
 import type { ConnectProps } from 'umi'
-import type { ProjectModelState } from '../model'
+import type { ProjectModelState } from '../../model'
 import type { SchemaBaseModelState } from '@/pages/Schema/Base/model'
 
 type ListProps = ConnectProps & {
@@ -53,27 +53,31 @@ const List: React.FC<ListProps> = ({ schema, dispatch, pTypeList }) => {
     manual: true,
     onSuccess: () => tRef.current?.reload()
   })
+
+  const goToDetail = params => {
+    // return history.push(`/institutions/company/companyname`)
+    return history.push({
+      pathname: '/project/management/detail',
+      state: params
+    })
+  }
+
   const columns: ProColumnType<API.ProjectListItem>[] = [
     {
-      dataIndex: 'ID',
-      hideInTable: true
-    },
-    {
       dataIndex: 'name',
       title: '项目名称',
       render: (text, record) => (
         <span
           className="text-primary cursor-pointer hover:text-hex-967bbd"
           onClick={() => {
-            setState({
-              ...state,
-              visible: true,
-              currentModalType: ModalType.PREVIEW,
-              title: record.name,
-              defaultFormData: {
-                dataID: record.ID
-              }
-            })
+            goToDetail({ dataID: record.ID })
+            // setState({
+            //   ...state,
+            //   visible: true,
+            //   currentModalType: ModalType.PREVIEW,
+            //   title: record.name,
+            //   defaultFormData: record
+            // })
           }}>
           {text}
         </span>
@@ -203,24 +207,14 @@ const List: React.FC<ListProps> = ({ schema, dispatch, pTypeList }) => {
         }
         visible={state.visible}
         onVisibleChange={onAnimateChange}>
-        {state.currentModalType === ModalType.PREVIEW ? (
-          <Detail
-            defaultFormData={state.defaultFormData}
-            pTypeList={pTypeList}
-            reload={() => tRef.current?.reload()}
-            onVisibleChange={onAnimateChange}
-            type={state.currentModalType}
-          />
-        ) : (
-          <ProjectModal
-            type={state.currentModalType}
-            defaultFormData={state.defaultFormData}
-            pTypeList={pTypeList}
-            visible={state.visible}
-            reload={() => tRef.current?.reload()}
-            setVisible={onAnimateChange}
-          />
-        )}
+        <ProjectModal
+          type={state.currentModalType}
+          defaultFormData={state.defaultFormData}
+          pTypeList={pTypeList}
+          visible={state.visible}
+          reload={() => tRef.current?.reload()}
+          setVisible={onAnimateChange}
+        />
       </AnimateContent>
     </PageContainer>
   )

+ 10 - 2
src/services/api/typings.d.ts

@@ -19,10 +19,18 @@ declare namespace API {
   type ProjectListItem = {
     createdTime: string
     name: string
-    created: string
+    created: Nullable<{
+      ID: string
+      name: string
+      institution: {
+        ID: string
+        name: string
+      }
+    }>
     createdID: string
-    projectType: ProjectTypeListItem
+    projectTypeID: string
     ID: string
+    reportAccount: { ID: string; name: string }[]
   }
 
   type ProjectListParams = BasicPageParams & {

+ 3 - 0
src/typings.d.ts

@@ -21,3 +21,6 @@ declare module 'bizcharts-plugin-slider'
 declare let ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: 'site' | undefined
 
 declare const REACT_APP_ENV: 'test' | 'dev' | 'pre' | false
+declare type Nullable<T> = T | null
+declare type NonNullable<T> = T extends null | undefined ? never : T
+declare type Recordable<T = any> = Record<string, T>

+ 0 - 12
src/utils/schema.ts

@@ -1,14 +1,10 @@
 import { getAccountTypeList } from '@/services/api/institution'
 import { getProjectTypeList } from '@/services/api/project'
-import { Link, useDispatch } from 'umi'
 import { action } from '@formily/reactive'
-import type { GeneralField } from '@formily/core'
 import consts from './consts'
-import { useEffect, useState } from 'react'
 import type { BaseMenuEnum } from '@/pages/Schema/Base'
 import { SchemaEnum } from '@/pages/Schema/Base'
 import type { ISchema } from '@formily/react'
-import { isDef, isUnDef } from './is'
 
 // 项目schema
 export const projectSchema = {
@@ -23,14 +19,6 @@ export const projectSchema = {
         layout: 'horizontal'
       },
       properties: {
-        ID: {
-          type: 'string',
-          title: 'ID',
-          'x-display': 'none',
-          'x-decorator': 'FormItem',
-          'x-component': 'Input',
-          'x-index': 0
-        },
         name: {
           type: 'string',
           title: '名称',