Przeglądaj źródła

feat: 新增useDrawer替代AnimateContent

lanjianrong 3 lat temu
rodzic
commit
a02a15b9f9

+ 1 - 1
src/components/AnimateContent/index.less

@@ -8,6 +8,6 @@
     max-width: 80vw;
   }
   :global(.ant-drawer-body) {
-    padding: 0 24px 0;
+    padding: 0 24px;
   }
 }

+ 13 - 0
src/components/Drawer/index.less

@@ -0,0 +1,13 @@
+.pageContainer {
+  :global(.ant-drawer-header-title) {
+    display: flex;
+    flex-direction: row-reverse;
+  }
+  :global(.ant-page-header) {
+    padding: 0;
+    max-width: 80vw;
+  }
+  :global(.ant-drawer-body) {
+    padding: 0 24px;
+  }
+}

+ 132 - 0
src/components/Drawer/index.tsx

@@ -0,0 +1,132 @@
+import React, {
+  useRef,
+  useMemo,
+  memo,
+  forwardRef,
+  useCallback,
+  useState,
+  useImperativeHandle,
+  useId
+} from 'react'
+import { Button, Drawer, PageHeader } from 'antd'
+import { useModel } from '@umijs/max'
+import type { DrawerProps } from 'antd'
+
+import styles from './index.less'
+
+const DrawerHoc = memo(
+  forwardRef((prop: any, ref) => {
+    const {
+      initialState: { setting: { collapsed } = { collapsed: false } }
+    } = useModel('@@initialState')
+
+    const [chidlren, setDrawerChildren] = useState<React.ReactElement>(null)
+    const [drawerProps, setDrawerProps] = useState<DrawerProps>({
+      visible: false
+    })
+
+    // 关闭当前Modal
+    const onClose = useCallback(() => {
+      setDrawerProps(source => ({
+        ...source,
+        visible: false
+      }))
+    }, [])
+
+    // 打开当前Modal
+    const onOpen = useCallback(() => {
+      setDrawerProps(source => ({
+        ...source,
+        visible: true
+      }))
+    }, [])
+
+    useImperativeHandle(
+      ref,
+      () => ({
+        // 注入Modal的子组件
+        injectChildren: element => {
+          setDrawerChildren(element)
+        },
+        // 注入Modal参数
+        injectDrawerProps: props => {
+          setDrawerProps(source => {
+            return {
+              ...source,
+              ...props
+            }
+          })
+        },
+        // 打开Modal
+        open: () => {
+          onOpen()
+        },
+        // 关闭Modal
+        close: () => {
+          onClose()
+        }
+      }),
+      []
+    )
+    const wrapHeight = document.querySelector('.ant-pro-page-container-warp')?.clientHeight || 0
+
+    // 这里的Modal是ant.design中的Modal
+    return (
+      <Drawer
+        {...drawerProps}
+        className={styles.pageContainer}
+        title={<PageHeader title={drawerProps.title} onBack={onClose} />}
+        mask={false}
+        width={`calc(100vw - ${collapsed ? 48 : 256}px - 48px)`}
+        style={{ right: '24px', top: `${48 + wrapHeight}px` }}
+        contentWrapperStyle={{
+          height: `calc(100vh - 72px - ${wrapHeight}px)`
+        }}
+        closeIcon={<Button onClick={onClose}>返回</Button>}>
+        {chidlren}
+      </Drawer>
+    )
+  })
+)
+
+interface DrawerRefType {
+  open: () => void
+  close: () => void
+  injectChildren: (child: React.ReactElement) => void
+  injectDrawerProps: (
+    props: Omit<DrawerProps, 'mask' | 'width' | 'style' | 'contentWrapperStyle' | 'closeIcon'>
+  ) => void
+}
+
+interface Options
+  extends Omit<DrawerProps, 'mask' | 'width' | 'style' | 'contentWrapperStyle' | 'closeIcon'> {
+  children?: React.ReactElement
+}
+
+/**
+ * 适用于单弹窗模式
+ */
+const useDrawer = () => {
+  const drawerRef = useRef<DrawerRefType>()
+  const handle = useMemo(() => {
+    return {
+      open: ({ children, ...rest }: Options) => {
+        drawerRef.current.injectChildren(children) // 注入子组件
+        drawerRef.current.injectDrawerProps(rest) // 注入Modal的参数
+        drawerRef.current.open()
+      },
+      close: () => {
+        modalRef.current.close()
+      }
+    }
+  }, [])
+
+  return [handle, <DrawerHoc ref={drawerRef} key={useId()} />] as const
+}
+
+export type DrawerAction = {
+  open: (args: Options) => void
+  close: () => void
+}
+
+export default useDrawer

+ 9 - 8
src/pages/Business/Inventory/index.tsx

@@ -1,6 +1,6 @@
 import useModal from '@/components/Modal'
 import { PageContainer } from '@ant-design/pro-layout'
-import { Button, Table } from 'antd'
+import { Button } from 'antd'
 import { ColumnsType } from 'antd/lib/table'
 import { useRowScript } from './hooks/useRowScript'
 import {
@@ -15,6 +15,7 @@ import {
   FileTextOutlined
 } from '@ant-design/icons'
 import LeftMenu from '../RuleCode/components/LeftMenu'
+import ProTable from '@ant-design/pro-table'
 
 export enum TemplateMode {
   PAPER = 'paper',
@@ -24,7 +25,6 @@ export enum TemplateMode {
 const Inventory = () => {
   const contentHeight = document.body.clientHeight - 122
   const [modal, ModalDOM] = useModal()
-
   const {
     query,
     loading,
@@ -104,12 +104,13 @@ const Inventory = () => {
     <PageContainer title={false}>
       <div className="h-full w-full flex flex-row">
         <LeftMenu title="业务列表" onChange={key => handleMenuOnChange(key)} />
-        <div style={{ height: `${contentHeight}px` }} className="bg-white w-6/7 ml-8">
-          <Table
+        <div style={{ height: `${contentHeight}px` }} className="bg-white w-6/7 ml-8 rounded-20px">
+          <ProTable
+            search={false}
             rowKey="ID"
-            title={() => (
-              <div className="ml-2 flex flex-nowrap justify-start items-center">
-                <div className="children:mx-1 px-1 py-2 ml-5">
+            headerTitle={
+              <div className="flex flex-nowrap justify-start items-center">
+                <div className="children:mx-1 px-1 py-2 ">
                   <Button icon={<FolderAddOutlined />} size="small" type="primary" ghost onClick={addFolder}>
                     新建目录
                   </Button>
@@ -171,7 +172,7 @@ const Inventory = () => {
                   </Button>
                 </div>
               </div>
-            )}
+            }
             loading={loading}
             columns={columns}
             dataSource={list}

+ 1 - 1
src/pages/Business/Process/index.tsx

@@ -84,7 +84,7 @@ const Process = () => {
     <PageContainer title={false}>
       <div className="h-full w-full flex flex-row">
         <LeftMenu title="业务列表" onChange={key => handleMenuOnChange(key)} />
-        <div className="w-6/7 ml-8 bg-white">
+        <div className="w-6/7 ml-8 bg-white rounded-20px">
           <ProTable
             toolbar={{
               actions: [

+ 0 - 2
src/pages/Business/RuleCode/components/LeftMenu/index.tsx

@@ -8,9 +8,7 @@ import classNames from 'classnames'
 
 type LeftMenuProps = {
   title?: string
-  options: { label: string; value: string }[]
   onChange: (key: string) => void
-  value: string
 }
 
 export const businessOptions = [

+ 1 - 1
src/pages/Business/RuleCode/index.tsx

@@ -203,7 +203,7 @@ const RulesCode = () => {
       <div className="h-full w-full flex flex-row">
         <LeftMenu title="业务主体列表" onChange={key => handleMenuChange(key)} />
 
-        <div className="w-6/7 ml-8 p-4 pt-1 bg-white">
+        <div className="w-6/7 ml-8 p-4 pt-1 bg-white rounded-20px">
           <Tabs
             defaultActiveKey={BusinessType.UNDO}
             tabBarExtraContent={renderExtraTabContent()}

+ 30 - 41
src/pages/Institutions/Company/Detail/components/Staff.tsx

@@ -1,5 +1,4 @@
 import ProTable from '@ant-design/pro-table'
-import type { ProColumnType } from '@ant-design/pro-table'
 import React, { useState, useEffect, useRef, useMemo } from 'react'
 import { Button, Tooltip } from 'antd'
 import consts from '@/utils/consts'
@@ -7,15 +6,16 @@ import { connect, useAccess } from '@umijs/max'
 import dayjs from 'dayjs'
 import { queryAccountList } from '@/services/api/institution'
 import StaffDetail from '@/pages/Institutions/Staff/components/StaffDetail'
-import type { SchemaBaseModelState } from '@/pages/Schema/Base/model'
 import { BaseMenuEnum } from '@/pages/Schema/Base'
 import { generateColumns } from '@/utils/util'
-import AnimateContent from '@/components/AnimateContent'
 import { ModalType } from '@/utils/enum'
-import type { ColumnsStateType } from '@ant-design/pro-table/lib/typing'
 import { BackstagePermission } from '@/enums/access'
 import { UserOutlined } from '@ant-design/icons'
 import classNames from 'classnames'
+import useDrawer from '@/components/Drawer'
+import type { SchemaBaseModelState } from '@/pages/Schema/Base/model'
+import type { ColumnsStateType } from '@ant-design/pro-table/lib/typing'
+import type { ProColumnType } from '@ant-design/pro-table'
 
 type ListProps = ConnectProps & {
   accountType: API.AccountType[]
@@ -29,12 +29,12 @@ const Staff: React.FC<ListProps> = ({
   dataID,
   companyName,
   dispatch,
-  accountTypeList,
   memberTotal = 3,
   managerID
 }) => {
   const tRef = useRef<ActionType>(null)
   const { validatePerm } = useAccess()
+  const [drawer, drawerDom] = useDrawer()
   const [state, setState] = useState({
     params: {
       search: null,
@@ -176,12 +176,15 @@ const Staff: React.FC<ListProps> = ({
             )}
             onClick={() => {
               validatePerm(BackstagePermission.EDIT_INSTITUTION_STAFF) &&
-                setState({
-                  ...state,
-                  visible: true,
-                  currentModalType: ModalType.UPDATE,
-                  title: record.name,
-                  defaultFormData: record
+                drawer.open({
+                  title: `编辑账号(${record.name})`,
+                  children: (
+                    <StaffDetail
+                      type={ModalType.UPDATE}
+                      defaultFormData={record}
+                      reload={() => tRef.current?.reload()}
+                    />
+                  )
                 })
             }}>
             编辑
@@ -243,19 +246,21 @@ const Staff: React.FC<ListProps> = ({
                 type="primary"
                 disabled={!validatePerm(BackstagePermission.ADD_INSTITUTION_STAFF)}
                 onClick={() => {
-                  setState({
-                    ...state,
-                    visible: true,
-                    currentModalType: ModalType.ADD,
-                    institutionDisable: true,
-                    title: '',
-                    defaultFormData: {
-                      institutionID: dataID,
-                      institution: {
-                        ID: dataID,
-                        name: companyName
-                      }
-                    }
+                  drawer.open({
+                    title: '添加人员',
+                    children: (
+                      <StaffDetail
+                        type={ModalType.ADD}
+                        defaultFormData={{
+                          institutionID: dataID,
+                          institution: {
+                            ID: dataID,
+                            name: companyName
+                          }
+                        }}
+                        reload={() => tRef.current?.reload()}
+                      />
+                    )
                   })
                 }}>
                 添加人员
@@ -264,23 +269,7 @@ const Staff: React.FC<ListProps> = ({
           ]
         }}
       />
-      <AnimateContent
-        title={
-          <>
-            {state.currentModalType === ModalType.ADD ? '新增账号' : null}
-            {state.currentModalType === ModalType.UPDATE ? `编辑账号(${state.title})` : null}
-          </>
-        }
-        visible={state.visible}
-        onVisibleChange={visible => setState({ ...state, visible })}>
-        <StaffDetail
-          onVisibleChange={(visible: boolean) => setState({ ...state, visible })}
-          type={state.currentModalType}
-          defaultFormData={{ ...state.defaultFormData }}
-          accountTypeList={accountTypeList}
-          reload={() => tRef.current?.reload()}
-        />
-      </AnimateContent>
+      {drawerDom}
     </div>
   )
 }

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

@@ -5,18 +5,14 @@ import { Button } from 'antd'
 import { useMemo, useRef, useState } from 'react'
 import { Link, useAccess } from '@umijs/max'
 import { queryInstitutionList } from '@/services/api/institution'
-import type { ProColumnType, ActionType } from '@ant-design/pro-table'
 import { PageContainer } from '@ant-design/pro-layout'
 import CompanyDrawer from './components/CompanyDrawer'
 import { ModalType } from '@/utils/enum'
-import AnimateContent from '@/components/AnimateContent'
-import type { ColumnsStateType } from '@ant-design/pro-table/lib/typing'
 import { BackstagePermission } from '@/enums/access'
 import classNames from 'classnames'
-
-export type InstitutionDetailProps = {
-  dataID: string
-}
+import useDrawer from '@/components/Drawer'
+import type { ProColumnType, ActionType } from '@ant-design/pro-table'
+import type { ColumnsStateType } from '@ant-design/pro-table/lib/typing'
 
 const CompanyList = () => {
   const tRef = useRef<ActionType>(null)
@@ -24,11 +20,10 @@ const CompanyList = () => {
     params: {
       search: null
     },
-    visible: false,
-    currentModalType: ModalType.ADD,
     defaultFormData: null
   })
 
+  const [drawer, DrawerDom] = useDrawer()
   const { validatePerm } = useAccess()
   const columns: ProColumnType<API.InstitutionListItem>[] = [
     {
@@ -86,11 +81,15 @@ const CompanyList = () => {
             )}
             onClick={() => {
               validatePerm(BackstagePermission.EDIT_INSTITUTION) &&
-                setState({
-                  ...state,
-                  visible: true,
-                  currentModalType: ModalType.UPDATE,
-                  defaultFormData: record
+                drawer.open({
+                  title: '编辑企事业单位',
+                  children: (
+                    <CompanyDrawer
+                      type={ModalType.UPDATE}
+                      defaultFormData={record}
+                      reload={() => tRef.current?.reload()}
+                    />
+                  )
                 })
             }}>
             编辑
@@ -140,24 +139,25 @@ const CompanyList = () => {
               key="add"
               type="primary"
               disabled={!validatePerm(BackstagePermission.ADD_INSTITUTION)}
-              onClick={() => setState({ ...state, visible: true, currentModalType: ModalType.ADD })}>
+              onClick={() => {
+                drawer.open({
+                  title: '新增企事业单位',
+                  children: (
+                    <CompanyDrawer
+                      type={ModalType.ADD}
+                      defaultFormData={state.defaultFormData}
+                      reload={() => tRef.current?.reload()}
+                    />
+                  )
+                })
+              }}>
               新建企事业单位
             </Button>
           ]
         }}
         search={false}
       />
-      <AnimateContent
-        title={state.currentModalType === ModalType.ADD ? '新增企事业单位' : '编辑企事业单位'}
-        visible={state.visible}
-        onVisibleChange={visible => setState({ ...state, visible })}>
-        <CompanyDrawer
-          type={state.currentModalType}
-          defaultFormData={state.defaultFormData}
-          reload={() => tRef.current?.reload()}
-          setVisible={(visible: boolean) => setState({ ...state, visible })}
-        />
-      </AnimateContent>
+      {DrawerDom}
     </PageContainer>
   )
 }

+ 12 - 7
src/pages/Institutions/Staff/components/StaffDetail.tsx

@@ -174,7 +174,12 @@ const StaffDrawer: React.FC<StaffModalProps> = ({
   })
 
   return (
-    <Form form={normalForm} labelCol={6} wrapperCol={8} className="max-w-500px mt-6">
+    <Form
+      form={normalForm}
+      labelCol={6}
+      wrapperCol={8}
+      className="max-w-500px mt-6"
+      previewTextPlaceholder="-">
       <SchemaField schema={staffSchema} />
       <FormButtonGroup.Sticky>
         <FormButtonGroup.FormItem>
@@ -200,13 +205,13 @@ const StaffDrawer: React.FC<StaffModalProps> = ({
                   { required: true, message: '请输入新密码' },
                   () => ({
                     validator(_, value) {
-                      if (!value) {
-                        return Promise.resolve()
-                      }
-                      if (!/(?=.*[a-zA-Z])(?=.*\d).{8,16}/.test(value)) {
-                        return Promise.reject(new Error('密码长度最小8位且是数字和英文的组合'))
+                      if (value.length >= 5 && value.length <= 15) {
+                        if (/^[A-Za-z0-9][A-Za-z0-9_.]{5,14}$/.test(value)) {
+                          return Promise.resolve()
+                        }
+                        return Promise.reject('密码只能由英文字母数字以及_.组成')
                       }
-                      return Promise.resolve()
+                      return Promise.reject('密码至少由5-14位字符组成')
                     }
                   })
                 ]}

+ 23 - 38
src/pages/Institutions/Staff/index.tsx

@@ -1,5 +1,4 @@
 import ProTable from '@ant-design/pro-table'
-import type { ProColumnType, ActionType } from '@ant-design/pro-table'
 import { PageContainer } from '@ant-design/pro-layout'
 import consts from '@/utils/consts'
 import { useMemo, useRef, useState } from 'react'
@@ -7,11 +6,12 @@ import { ConnectProps, useAccess } from '@umijs/max'
 import { queryAccountList } from '@/services/api/institution'
 import dayjs from 'dayjs'
 import StaffDetail from './components/StaffDetail'
-import AnimateContent from '@/components/AnimateContent'
 import { ModalType } from '@/utils/enum'
-import type { ColumnsStateType } from '@ant-design/pro-table/lib/typing'
 import { BackstagePermission } from '@/enums/access'
 import classNames from 'classnames'
+import useDrawer from '@/components/Drawer'
+import type { ColumnsStateType } from '@ant-design/pro-table/lib/typing'
+import type { ProColumnType, ActionType } from '@ant-design/pro-table'
 
 type ListProps = ConnectProps & {
   accountTypeList: { label: string; value: string }[]
@@ -25,6 +25,7 @@ export const genderEum = {
 const CompanyList: React.FC<ListProps> = () => {
   const tRef = useRef<ActionType>(null)
   const { validatePerm } = useAccess()
+  const [drawer, DrawerDom] = useDrawer()
   const [state, setState] = useState({
     params: {
       search: null,
@@ -47,12 +48,15 @@ const CompanyList: React.FC<ListProps> = () => {
         <div
           className="text-primary cursor-pointer hover:text-hex-967bbd"
           onClick={() => {
-            setState({
-              ...state,
-              visible: true,
-              currentModalType: ModalType.PREVIEW,
-              title: record.name,
-              defaultFormData: record
+            drawer.open({
+              title: `员工详情(${record.name})`,
+              children: (
+                <StaffDetail
+                  type={ModalType.PREVIEW}
+                  defaultFormData={record}
+                  reload={() => tRef.current?.reload()}
+                />
+              )
             })
           }}>
           {name}
@@ -156,12 +160,15 @@ const CompanyList: React.FC<ListProps> = () => {
             )}
             onClick={() => {
               validatePerm(BackstagePermission.EDIT_INSTITUTION_STAFF) &&
-                setState({
-                  ...state,
-                  visible: true,
-                  currentModalType: ModalType.UPDATE,
-                  title: record.name,
-                  defaultFormData: record
+                drawer.open({
+                  title: `编辑账号(${record.name})`,
+                  children: (
+                    <StaffDetail
+                      type={ModalType.UPDATE}
+                      defaultFormData={record}
+                      reload={() => tRef.current?.reload()}
+                    />
+                  )
                 })
             }}>
             编辑
@@ -209,31 +216,9 @@ const CompanyList: React.FC<ListProps> = () => {
         }}
         search={false}
       />
-      <AnimateContent
-        title={
-          <>
-            {state.currentModalType === ModalType.UPDATE ? `编辑账号(${state.title})` : null}
-            {state.currentModalType === ModalType.PREVIEW ? `员工详情(${state.title})` : null}
-          </>
-        }
-        visible={state.visible}
-        onVisibleChange={visible => setState({ ...state, visible })}>
-        <StaffDetail
-          type={state.currentModalType}
-          defaultFormData={state.defaultFormData}
-          visible={state.visible}
-          reload={() => tRef.current?.reload()}
-          onVisibleChange={(visible: boolean) => setState({ ...state, visible })}
-        />
-      </AnimateContent>
+      {DrawerDom}
     </PageContainer>
   )
 }
 
-// export default connect(({ institutions }: { institutions: InstitutionsModelState }) => ({
-//   accountTypeList: institutions.accountType.map(item => ({
-//     label: item.name,
-//     value: item.value
-//   }))
-// }))(CompanyList)
 export default CompanyList

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

@@ -24,7 +24,7 @@ const Detail: React.FC<DetailProps> = ({ dispatch, base }) => {
   }, [columnType])
   return (
     <PageContainer title={false}>
-      <div className="h-full w-full bg-white px-4 pb-4 rounded-20px">
+      <div className="h-full w-full bg-white px-4 pb-4 rounded-2px">
         <Designable title={menuOptions.find(item => item.value === columnType)?.label + '数据模型'} />
       </div>
     </PageContainer>

+ 6 - 4
src/pages/Schema/Budget/index.tsx

@@ -93,7 +93,7 @@ const Budget: React.FC = () => {
       </Form>
     )
   }
-
+  const contentHeight = document.body.clientHeight - 122
   return (
     <PageContainer title={false}>
       <div className="h-full w-full flex flex-row">
@@ -106,8 +106,10 @@ const Budget: React.FC = () => {
           />
         )}
 
-        <div className="w-6/7 ml-8 bg-white p-4  rounded-20px shadow-hex-3e2c5a relative">
-          <div className="flex justify-end">
+        <div
+          className="w-6/7 ml-8 bg-white p-4  rounded-20px shadow-hex-3e2c5a relative overflow-y-auto"
+          style={{ height: contentHeight }}>
+          <div className="text-right">
             {state.activeKey && (
               <Button
                 type="primary"
@@ -117,7 +119,7 @@ const Budget: React.FC = () => {
               </Button>
             )}
           </div>
-          <div className="w-full overflow-y-auto max-h-700px">{renderForm()}</div>
+          <div className="w-full ">{renderForm()}</div>
         </div>
       </div>
     </PageContainer>