Procházet zdrojové kódy

feat: 增加项目管理员页面以及项目角色权限

lanjianrong před 3 roky
rodič
revize
75a0e513ba

+ 11 - 6
config/routes.ts

@@ -35,17 +35,22 @@
     routes: [
       {
         path: '/project',
-        redirect: '/project/management/list'
+        redirect: '/project/management'
       },
       {
-        path: '/project/management/list',
-        name: 'management-list',
+        path: '/project/management',
+        name: 'management',
         component: './Project/Management'
       },
+      // {
+      //   path: '/project/verification',
+      //   name: 'verification',
+      //   component: './Project/Verification'
+      // },
       {
-        path: '/project/verification',
-        name: 'verification',
-        component: './Project/Verification'
+        path: '/project/created',
+        name: 'created',
+        component: './Project/Created'
       }
     ]
   },

+ 34 - 34
src/app.tsx

@@ -3,7 +3,7 @@ import { notification } from 'antd'
 import { PageLoading } from '@ant-design/pro-layout'
 import type { RequestConfig, RunTimeLayoutConfig } from 'umi'
 import { Link } from 'umi'
-import { getIntl, getLocale, history } from 'umi'
+import { history } from 'umi'
 import type { RequestOptionsInit, ResponseError } from 'umi-request'
 import RightContent from '@/components/RightContent'
 // import { currentUser as queryCurrentUser } from './services/ant-design-pro/api'
@@ -87,17 +87,16 @@ const authHeaderInterceptor = (url: string, options: RequestOptionsInit) => {
  */
 export const request: RequestConfig = {
   errorHandler: (error: ResponseError) => {
-    const { messages } = getIntl(getLocale())
+    // const { messages } = getIntl(getLocale())
     const { response } = error
-
-    if (response && response.status) {
-      const { status, statusText, url } = response
-      const requestErrorMessage = messages['app.request.error']
-      const errorMessage = `${requestErrorMessage} ${status}: ${url}`
-      const errorDescription = messages[`app.request.${status}`] || statusText
+    if (response && response.msg) {
+      // const { status, statusText, url } = response
+      // const requestErrorMessage = messages['app.request.error']
+      // const errorMessage = `${requestErrorMessage} ${status}: ${url}`
+      // const errorDescription = messages[`app.request.${status}`] || statusText
       notification.error({
-        message: errorMessage,
-        description: errorDescription
+        message: '请求参数错误',
+        description: response.msg
       })
     }
 
@@ -109,30 +108,30 @@ export const request: RequestConfig = {
     }
     throw error
   },
-  middlewares: [
-    async (ctx, next) => {
-      await next()
-      const { req, res } = ctx
-      // @ts-ignore
-      if (req.options?.skipErrorHandler) {
-        return
-      }
-      const errorAdaptor = req.options?.errorConfig.adaptor || (resData => resData)
-      const { options } = req
-      const { getResponse } = options
-      const resData = getResponse ? res.data : res
-      const errorInfo = errorAdaptor(resData, ctx)
-      if (resData.code !== consts.RET_CODE.SUCCESS) {
-        // 抛出错误到 errorHandler 中处理
-        const error = new Error(errorInfo.errorMessage)
-        error.name = 'BizError'
-        error.data = resData
-        error.info = errorInfo
-        error.response = res
-        throw error
-      }
-    }
-  ],
+  // middlewares: [
+  //   async (ctx, next) => {
+  //     await next()
+  //     const { req, res } = ctx
+  //     // @ts-ignore
+  //     if (req.options?.skipErrorHandler) {
+  //       return
+  //     }
+  //     const errorAdaptor = req.options?.errorConfig.adaptor || (resData => resData)
+  //     const { options } = req
+  //     const { getResponse } = options
+  //     const resData = getResponse ? res.data : res
+  //     const errorInfo = errorAdaptor(resData, ctx)
+  //     if (resData.code !== consts.RET_CODE.SUCCESS) {
+  //       // 抛出错误到 errorHandler 中处理
+  //       const error = new Error(errorInfo.errorMessage)
+  //       error.name = 'BizError'
+  //       error.data = resData
+  //       error.info = errorInfo
+  //       error.response = res
+  //       throw error
+  //     }
+  //   }
+  // ],
   prefix: '/api',
   errorConfig: {
     adaptor: resData => {
@@ -159,6 +158,7 @@ export const request: RequestConfig = {
         error.data = resData
         error.info = errorInfo
         error.response = res
+        console.log('1111', error)
         throw error
       }
       return response

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

@@ -16,7 +16,8 @@ export default {
   'menu.account.trigger': '触发报错',
   'menu.account.logout': '退出登录',
   'menu.project': '项目',
-  'menu.project.management-list': '项目管理',
+  'menu.project.management': '项目管理',
+  'menu.project.created': '项目管理员',
   'menu.project.verification': '审批流程',
   'menu.institutions': '企事业单位',
   'menu.institutions.company': '单位管理',

+ 169 - 0
src/pages/Project/Created/index.tsx

@@ -0,0 +1,169 @@
+import { PageContainer } from '@ant-design/pro-layout'
+import { useState, useEffect, useRef } from 'react'
+import ProTable from '@ant-design/pro-table'
+import type { ActionType } from '@ant-design/pro-table'
+import { ModalForm, ProFormSelect } from '@ant-design/pro-form'
+import type { ProFormColumnsType } from '@ant-design/pro-form'
+import { DeleteOutlined } from '@ant-design/icons'
+import { queryAcountList } from '@/services/api/institution'
+import consts from '@/utils/consts'
+import { Button } from 'antd'
+import { saveCreator } from '@/services/api/user'
+import { useRequest } from 'umi'
+import { getProjectList } from '@/services/api/project'
+
+enum CreatedModalType {
+  ADD = '1',
+  DEL = '2'
+}
+
+const Created = () => {
+  const tRef = useRef<ActionType>(null)
+  const [state, setState] = useState({
+    dataID: null,
+    modalType: CreatedModalType.ADD,
+    options: [],
+    projectList: [],
+    modalVisible: false
+  })
+  const { run: tryQueryProjectList } = useRequest(
+    createdID => getProjectList({ current: 1, pageSize: 21300, createdID }),
+    {
+      onSuccess: ({ items }) => {
+        setState({ ...state, projectList: items })
+      }
+    }
+  )
+  useEffect(() => {
+    async function querySelectOptions() {
+      const {
+        code = -1,
+        data: { items }
+      } = await queryAcountList({ current: 1, pageSize: 21400, isCreated: 0 })
+      setState({
+        ...state,
+        options:
+          code === consts.RET_CODE.SUCCESS
+            ? items.map(item => ({ label: item.name, value: item.ID }))
+            : []
+      })
+    }
+    state.modalVisible && querySelectOptions()
+  }, [state.modalVisible])
+
+  useEffect(() => {
+    if (state.dataID && state.modalType === CreatedModalType.DEL) {
+      tryQueryProjectList(state.dataID)
+    }
+  }, [state.dataID, state.modalType])
+  const columns: ProFormColumnsType<AccountListItem>[] = [
+    {
+      dataIndex: 'name',
+      title: '名称'
+    },
+    {
+      dataIndex: 'institutionID',
+      title: '企事业单位',
+      renderText: (_, record) => record?.institution?.name
+    },
+    {
+      dataIndex: 'organizationalStructureID',
+      title: '组织架构',
+      renderText: (_, record) => record?.organizationalStructure?.name
+    },
+    {
+      dataIndex: 'opreate',
+      title: '操作',
+      render: (_, record) => (
+        <div
+          className="text-hex-fd3995 cursor-pointer hover:text-hex-e7026e"
+          onClick={() =>
+            setState({
+              ...state,
+              dataID: record.ID,
+              modalType: CreatedModalType.DEL,
+              modalVisible: true
+            })
+          }
+        >
+          <DeleteOutlined />
+        </div>
+      )
+    }
+  ]
+
+  const onFinish = async formData => {
+    try {
+      if (state.modalType === CreatedModalType.ADD) {
+        await saveCreator({ ...formData, isCreated: '1' })
+      } else {
+        await saveCreator({ ID: state.dataID, isCreated: '0' })
+      }
+      setState({ ...state, modalVisible: false })
+      tRef?.current?.reload()
+      return true
+    } catch (error) {
+      return false
+    }
+  }
+  return (
+    <PageContainer title={false}>
+      <ProTable
+        rowKey="ID"
+        actionRef={tRef}
+        columns={columns}
+        search={false}
+        toolbar={{
+          actions: [
+            <Button
+              type="primary"
+              onClick={() =>
+                setState({ ...state, modalVisible: true, modalType: CreatedModalType.ADD })
+              }
+            >
+              添加创建人
+            </Button>
+          ]
+        }}
+        scroll={{ y: document.body.clientHeight - 313 }}
+        request={async (params, filter, sort) => {
+          const {
+            code = -1,
+            data: { items, total }
+          } = await queryAcountList({ ...params, ...filter, ...sort, isCreate: 1 })
+          return {
+            success: code === consts.RET_CODE.SUCCESS,
+            data: items,
+            total
+          }
+        }}
+      />
+      <ModalForm
+        title={state.modalType === CreatedModalType.ADD ? '添加创建人' : '移除创建人'}
+        onFinish={onFinish}
+        visible={state.modalVisible}
+        onVisibleChange={visible => setState({ ...state, modalVisible: visible })}
+      >
+        {state.modalType === CreatedModalType.ADD ? (
+          <>
+            <p className="mb-4">创建人可在前台创建项目、上传项目数据、编制项目等</p>
+            <ProFormSelect name="ID" options={state.options} />
+          </>
+        ) : (
+          <>
+            <p className="text-red-500">
+              当前创建人已经管理以下项目,移除后,这些项目对应创建人也将被移除。
+            </p>
+            <div className="max-h-42 overflow-y-auto">
+              {state.projectList?.map(item => (
+                <p key={item.ID}>{item.name}</p>
+              ))}
+            </div>
+          </>
+        )}
+      </ModalForm>
+    </PageContainer>
+  )
+}
+
+export default Created

+ 84 - 26
src/pages/Project/Management/components/Detail.tsx

@@ -1,17 +1,28 @@
 import { connect, useRequest } from 'umi'
 import { useEffect, useRef, useState } from 'react'
-import { message, Tabs, Form } from 'antd'
-import { getApprovalList, getProject, setApproval } from '@/services/api/project'
+import { 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 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 ProForm, { ProFormDependency, ProFormSelect } from '@ant-design/pro-form'
+// import TreeNodeSelect from './TreeNodeSelect'
 import { ModalType } from './ProjectModal'
 import consts from '@/utils/consts'
+import { queryAcountList } from '@/services/api/institution'
+
+enum FormType {
+  UPLOAD_PERM = '1',
+  ROLE_PERM = '2'
+}
 
 type ProjectModalProps = ConnectProps & {
   visible: boolean
@@ -33,16 +44,17 @@ const DetailModal: React.FC<ProjectModalProps> = ({
   pTypeList,
   reload
 }) => {
-  console.log(type)
-
   const form = useForm()
   const ref = useRef<FormInstance>(null)
+  const ref2 = useRef<FormInstance>(null)
   const { TabPane } = Tabs
   const [state, setState] = useState({
     acountInstitutionList: [],
     approvalList: [],
+    accountList: [],
     activeKey: '',
-    account: null
+    account: null,
+    perm: null
   })
 
   const { run: tryApprovalList } = useRequest(() => getApprovalList(), {
@@ -59,7 +71,28 @@ const DetailModal: React.FC<ProjectModalProps> = ({
     }
   })
 
+  const { run: trySetProjectPersonor } = useRequest(setProjectPersonor, {
+    manual: true,
+    onSuccess: () => {
+      message.success('提交成功')
+    }
+  })
+
   useEffect(() => {
+    async function querySelectOptions() {
+      const {
+        code = -1,
+        data: { items }
+      } = await queryAcountList({ current: 1, pageSize: 21400, isCreated: 0 })
+
+      setState({
+        ...state,
+        accountList:
+          code === consts.RET_CODE.SUCCESS
+            ? items.map(item => ({ label: item.name, value: item.ID }))
+            : []
+      })
+    }
     if (visible && !schema) {
       dispatch({
         type: 'schemaBase/querySchema',
@@ -75,6 +108,11 @@ const DetailModal: React.FC<ProjectModalProps> = ({
       }
       ref.current?.setFieldsValue({ ...TabFormData })
     }
+
+    if (state.activeKey === '3') {
+      querySelectOptions()
+      ref2?.current?.setFieldsValue({ ID: defaultFormData?.dataID })
+    }
   }, [visible, state.activeKey])
 
   const onMount = async () => {
@@ -113,17 +151,21 @@ const DetailModal: React.FC<ProjectModalProps> = ({
     }
   }
 
-  const onFinish = async formData => {
+  const onFinish = async (t, formData) => {
     try {
-      await trySetApproval({ ...formData, ID: defaultFormData.dataID })
-      setState({
-        ...state,
-        account: {
+      if (t === FormType.UPLOAD_PERM) {
+        await trySetApproval({ ...formData, ID: defaultFormData.dataID })
+        setState({
           ...state,
-          approval: { ID: formData.approvalID },
-          reportAccount: { ID: formData.accountID }
-        }
-      })
+          account: {
+            ...state,
+            approval: { ID: formData.approvalID },
+            reportAccount: { ID: formData.accountID }
+          }
+        })
+      } else {
+        await trySetProjectPersonor({ ...formData, ID: defaultFormData.dataID })
+      }
       reload()
       return true
     } catch (error) {
@@ -131,7 +173,6 @@ const DetailModal: React.FC<ProjectModalProps> = ({
       return false
     }
   }
-  console.log(type === ModalType.PREVIEW)
 
   return (
     <Tabs onChange={onChange}>
@@ -145,7 +186,7 @@ const DetailModal: React.FC<ProjectModalProps> = ({
           />
         )}
       </TabPane>
-      <TabPane tab="上传权限" key="2">
+      {/* <TabPane tab="上传权限" key="2">
         <ProForm
           formRef={ref}
           submitter={{ resetButtonProps: { style: { display: 'none' } } }}
@@ -153,14 +194,8 @@ const DetailModal: React.FC<ProjectModalProps> = ({
         >
           <Form.Item label="指定人员" name="accountID">
             <TreeNodeSelect />
-            {/* <TreeSelect
-              style={{ width: '100%' }}
-              placeholder="请选择上报人"
-              treeDefaultExpandAll
-              treeData={state.acountInstitutionList}
-            /> */}
           </Form.Item>
-          {/* <ProFormSelect
+          <ProFormSelect
             name="approvalID"
             label={'审批流程'}
             placeholder="请选择审批流程"
@@ -168,7 +203,30 @@ const DetailModal: React.FC<ProjectModalProps> = ({
               label: item.name,
               value: item.ID
             }))}
-          /> */}
+          />
+        </ProForm>
+      </TabPane> */}
+      <TabPane tab="角色权限" key="3">
+        <ProForm formRef={ref2} onFinish={formData => onFinish(FormType.ROLE_PERM, formData)}>
+          <ProFormSelect
+            name="createdID"
+            label="创建人:创建项目、管理编辑者、上传项目数据、编制项目、添加批注"
+            options={state.accountList}
+          />
+          <ProFormDependency name={['createdID']}>
+            {({ createdID }) => {
+              return (
+                <ProFormSelect
+                  name="reportAccount"
+                  label="编辑者:编制项目、上传项目数据、添加批注"
+                  fieldProps={{
+                    mode: 'tags'
+                  }}
+                  options={state.accountList.filter(item => item.value !== createdID)}
+                />
+              )
+            }}
+          </ProFormDependency>
         </ProForm>
       </TabPane>
     </Tabs>

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

@@ -20,7 +20,8 @@ const FlowList = () => {
     modalType: 'add',
     modalVisible: false,
     current: {
-      id: null
+      id: null,
+      name: null
     }
   })
 
@@ -72,6 +73,7 @@ const FlowList = () => {
                 visible: true,
                 current: {
                   id: record.ID,
+                  name: record.name,
                   flowProcess: record.flowProcess,
                   flowProcessData: record.flowProcessData
                 }
@@ -128,6 +130,7 @@ const FlowList = () => {
         }}
       />
       <AnimateContent
+        title={state.current.name}
         visible={state.visible}
         onVisibleChange={visible => setState({ ...state, visible })}
       >

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

@@ -94,3 +94,15 @@ export async function updateApproval(params: { ID: string; name: string }) {
     data: params
   })
 }
+
+/** 设置项目人员 */
+export async function setProjectPersonor(params: {
+  ID: string
+  createdID: string
+  reportAccount: string
+}) {
+  return request('/project/set/personnel', {
+    method: 'POST',
+    data: params
+  })
+}

+ 7 - 0
src/services/api/user.ts

@@ -112,3 +112,10 @@ export async function deleteUser(
     ...(options || {})
   })
 }
+
+// 报错创建人
+export async function saveCreator(params: { ID: string; isCreated?: '1' | '0' }) {
+  return request('/account/save/create', {
+    body: params
+  })
+}