浏览代码

feat: 新增安全巡检审批用弹窗

lanjianrong 4 年之前
父节点
当前提交
5023b98e4a

+ 8 - 0
src/assets/css/common.scss

@@ -484,3 +484,11 @@
     border-bottom-width: 2px;
   }
 }
+
+.pi-warning {
+  padding: 0.75rem 1.25rem;
+  margin: 0.5rem 0 1rem 0;
+  color: #856404;
+  background-color: #fff3cd;
+  border-color: #ffeeba;
+}

+ 50 - 47
src/components/AuditContent/index.tsx

@@ -48,7 +48,7 @@ const GroupItem: React.FC<iGroupItem> = props => {
 
 interface iAuditContentProps {
   onSelect: (type: string, item: iUserInfo) => void
-  onDelete: (id: string) => void
+  onDelete: (id: string, progress: string) => void
   latest: iLatestAuditorState
   auditors: iAuditor[]
   auditHistory: iAuditHistoryState[][]
@@ -56,8 +56,6 @@ interface iAuditContentProps {
 }
 
 const Index: React.FC<iAuditContentProps> = props => {
-  console.log(typeof props.auditHistory)
-
   const { onSelect, auditors, onDelete, status, auditHistory } = props
   const [ visible, setVisible ] = useState({
     check: false,
@@ -118,7 +116,7 @@ const Index: React.FC<iAuditContentProps> = props => {
                       </p>
                       <small className="text-muted">{item.company}</small>
                     </div>
-                    <span className="pi-red pi-pointer" onClick={() => onDelete(item.audit_id)}>
+                    <span className="pi-red pi-pointer" onClick={() => onDelete(item.audit_id, "0")}>
                       移除
                     </span>
                   </li>
@@ -136,7 +134,7 @@ const Index: React.FC<iAuditContentProps> = props => {
                       </p>
                       <small className="text-muted">{item.company}</small>
                     </div>
-                    <span className="pi-red pi-pointer" onClick={() => onDelete(item.audit_id)}>
+                    <span className="pi-red pi-pointer" onClick={() => onDelete(item.audit_id, "2")}>
                       移除
                     </span>
                   </li>
@@ -183,7 +181,7 @@ const Index: React.FC<iAuditContentProps> = props => {
   const renderStatusEle = (status: number, progress: string) => {
     let text = ''
     let textClass = 'pi-color-green'
-    if (progress === "0") {
+    if (progress === '0') {
       switch (status) {
         case 1:
           text = '审批中'
@@ -257,63 +255,62 @@ const Index: React.FC<iAuditContentProps> = props => {
               <td width="30%">
                 <table className="table table-bordered">
                   <tbody>
-                    {auditors.map((item, idx) => {
-                      if (!item.progress) {
-                        return (
+                    <tr>
+                      <td className="pi-text-center">检查人</td>
+                      <td>
+                        <SvgIcon type="xxh-play-circle"></SvgIcon>
+                        <span className="pi-mg-left-3">{auditors[0]?.name}</span>
+                        <small className="text-muted pi-mg-left-3">{auditors[0]?.position}</small>
+                      </td>
+                    </tr>
+                    {auditors
+                      .filter(item => item.progress === '0')
+                      .map((item, idx) => {
+                        return idx === 0 ? (
                           <tr key={item.audit_id}>
-                            <td className="pi-text-center">检查人</td>
-                            <td>
-                              <SvgIcon type="xxh-play-circle"></SvgIcon> {item.name} <small className="text-muted">{item.position}</small>
+                            <td rowSpan={auditors.filter(item => item.progress === '0').length} className="pi-text-center">
+                              审批
                             </td>
-                          </tr>
-                        )
-                      } else if (item.progress === '0') {
-                        return auditors[idx - 1].progress === '0' ? (
-                          <tr key={item.audit_id}>
                             <td>
-                              <SvgIcon type={item.status === 0 ? 'xxh-stop-circle' : 'xxh-chevron-circle-down'}></SvgIcon> {item.name}{' '}
-                              <small className="text-muted">{item.position}</small>
+                              <SvgIcon type={item.status === 0 ? 'xxh-stop-circle' : 'xxh-chevron-circle-down'}></SvgIcon>
+                              <span className="pi-mg-left-3">{item.name}</span>
+                              <small className="text-muted pi-mg-left-3">{item.position}</small>
                             </td>
                           </tr>
                         ) : (
                           <tr key={item.audit_id}>
-                            <td rowSpan={2} className="pi-text-center">
-                              审批
-                            </td>
                             <td>
-                              <SvgIcon type={item.status === 0 ? 'xxh-stop-circle' : 'xxh-chevron-circle-down'}></SvgIcon> {item.name}{' '}
-                              <small className="text-muted">{item.position}</small>
+                              <SvgIcon type={item.status === 0 ? 'xxh-stop-circle' : 'xxh-chevron-circle-down'}></SvgIcon>
+                              <span className="pi-mg-left-3">{item.name}</span>
+                              <small className="text-muted pi-mg-left-3">{item.position}</small>
                             </td>
                           </tr>
                         )
-                      } else if (item.progress === '1' || !auditors.find(item => item.progress === '1')) {
-                        return !auditors.find(item => item.progress === '1') ? (
+                      })}
+                    {auditors
+                      .filter(item => item.progress === '2')
+                      .map((item, idx) => {
+                        return idx === 0 ? (
                           <tr key={item.audit_id}>
-                            <td className="pi-text-center">整改</td>
-                            <td>
-                              <SvgIcon type={item.status === 0 ? 'xxh-stop-circle' : 'xxh-chevron-circle-down'}></SvgIcon> 由 {auditors[idx - 1].name} 指派
+                            <td rowSpan={auditors.filter(item => item.progress === '2').length} className="pi-text-center">
+                              复查
                             </td>
-                          </tr>
-                        ) : (
-                          <tr key={item.audit_id}>
-                            <td className="pi-text-center">整改</td>
                             <td>
-                              <SvgIcon type={item.status === 0 ? 'xxh-stop-circle' : 'xxh-chevron-circle-down'}></SvgIcon> {item.name}{' '}
-                              <small className="text-muted">{item.position}</small>
+                              <SvgIcon type={item.status === 0 ? 'xxh-stop-circle' : 'xxh-chevron-circle-down'}></SvgIcon>
+                              <span className="pi-mg-left-3">{item.name}</span>
+                              <small className="text-muted pi-mg-left-3">{item.position}</small>
                             </td>
                           </tr>
-                        )
-                      } else {
-                        return (
+                        ) : (
                           <tr key={item.audit_id}>
-                            <td className="pi-text-center">复查</td>
                             <td>
-                              <SvgIcon type="xxh-stop-circle"></SvgIcon> {item.name} <small className="text-muted">{item.position}</small>
+                              <SvgIcon type={item.status === 0 ? 'xxh-stop-circle' : 'xxh-chevron-circle-down'}></SvgIcon>
+                              <span className="pi-mg-left-3">{item.name}</span>
+                              <small className="text-muted pi-mg-left-3">{item.position}</small>
                             </td>
                           </tr>
                         )
-                      }
-                    })}
+                      })}
                   </tbody>
                 </table>
               </td>
@@ -374,7 +371,7 @@ const Index: React.FC<iAuditContentProps> = props => {
                 <div className="pi-flex-sub pi-mg-right-15 pi-mg-top-5 pi-mg-left-5">
                   <table className="table table-bordered pi-width-100P">
                     <tbody>
-                      {auditors.map((item, idx) => {
+                      {auditors?.map((item, idx) => {
                         if (!item.progress) {
                           return (
                             <tr key={item.audit_id}>
@@ -443,10 +440,10 @@ const Index: React.FC<iAuditContentProps> = props => {
                   </table>
                 </div>
                 <div className="pi-flex-treble pi-mg-left-15">
-                  {auditHistory.map((item, idx) => {
+                  {auditHistory?.map((item, idx) => {
                     return (
                       <ul className="timeline-list" key={idx}>
-                        {item.map((auditor, idx) => (
+                        {item?.map((auditor, idx) => (
                           <li className="timeline-list-item" key={auditor.id}>
                             <div className="timeline-item-date pi-flex-column" dangerouslySetInnerHTML={{ __html: formatDate(auditor.create_time) }}></div>
                             <div className={item.length - 1 === idx ? '' : 'timeline-item-tail'}></div>
@@ -456,11 +453,17 @@ const Index: React.FC<iAuditContentProps> = props => {
                                 <div className="card-content">
                                   <div className="pi-justify-between label">
                                     <span>{auditor.name}</span>
-                                    <span className={renderStatusEle(auditor.status, auditor.progress).textClass}>{idx === 0 ? '上报审批' : renderStatusEle(auditor.status, auditor.progress).text}</span>
+                                    <span className={renderStatusEle(auditor.status, auditor.progress).textClass}>
+                                      {idx === 0 ? '上报审批' : renderStatusEle(auditor.status, auditor.progress).text}
+                                    </span>
                                   </div>
                                   <div className="text-muted">{auditor.position}</div>
                                 </div>
-                                {auditor.opinion ? <div className="textarea"><span>{auditor.opinion}</span></div> : null}
+                                {auditor.opinion ? (
+                                  <div className="textarea">
+                                    <span>{auditor.opinion}</span>
+                                  </div>
+                                ) : null}
                               </div>
                             </div>
                           </li>

+ 1 - 1
src/pages/Contract/Content/Income/components/Modal/index.tsx

@@ -88,7 +88,7 @@ const ContractModal: React.FC<iModalCommonProps> = ({ modalObj: { type, visible,
     <Modal
       getContainer={false}
       visible={visible}
-      title={modalObj[type]?.title}
+      title={modalObj[type].title}
       onCancel={onCancel}
       footer={<div className="pi-justify-end">
         {type === 'update' ? <Button type="primary" key="delete" size="small" danger onClick={() => reload('del')}>删除合同</Button> : ''}

+ 12 - 0
src/pages/Safe/Content/Info/Detail/api.ts

@@ -17,3 +17,15 @@ export async function apiGetSafeDetail(id: string) {
   data.data.auditHistory = auditHistory
   return data
 }
+
+// 安全巡检审批
+export async function apiResfulSafeAudit(type: string, payload: object) {
+  if (type === 'delete') {
+    // 删除巡检记录
+    const { data } = await request.del('/api/safe', payload)
+    return data
+  } else {
+    const { data } = await request.post(`/api/safe_audit/${type}`, payload)
+    return data
+  }
+}

+ 117 - 0
src/pages/Safe/Content/Info/Detail/components/Modal/index.tsx

@@ -0,0 +1,117 @@
+import { ZhAuditBackButton, ZhCloseButton, ZhSubmitButton } from '@/components/Button'
+import { Button, Form, Input, Modal } from 'antd'
+import React from 'react'
+
+interface iAuditModalProps{
+  visible: boolean
+  onCancel: () => void
+  type: string
+  onCreate: () => void
+}
+const AuditModal: React.FC<iAuditModalProps> = props => {
+  const [ form ] = Form.useForm()
+  const { visible, type, onCancel, onCreate } = props
+  const textObj = {
+    start: {
+      title: '提交审批',
+      okText: '确认提交'
+    },
+    delete: {
+      title: '删除巡检',
+      okText: '确认删除'
+    },
+    close: {
+      title: '审批关闭',
+      okText: '确认关闭'
+    },
+    back: {
+      title: '审批退回',
+      okText: '确认退回'
+    },
+    pass: {
+      title: '审批通过',
+      okText: '确认通过'
+    }
+  }
+  const comfirmBtnClick = () => {
+    form.validateFields().then(values => {
+      form.resetFields()
+      onCreate()
+    })
+  }
+  const renderOkBtn = (type: string) => {
+    if (type === 'start' || type === 'pass') {
+      return (
+        <ZhSubmitButton size="small" onClick={comfirmBtnClick}>
+          {textObj[type]?.okText}
+        </ZhSubmitButton>
+      )
+    } else if (type === 'delete' || type === 'close') {
+      return (
+        <Button danger size="small" onClick={comfirmBtnClick}>
+          {textObj[type]?.okText}
+        </Button>
+      )
+    } else if (type === 'back') {
+      return <ZhAuditBackButton size="small" onClick={comfirmBtnClick}>{textObj[type]?.okText}</ZhAuditBackButton>
+    }
+  }
+  return (
+    <Modal
+      visible={visible}
+      title={textObj[type]?.title}
+      onCancel={onCancel}
+      getContainer={false}
+      footer={
+        <div className="pi-justify-end">
+          <ZhCloseButton size="small" onClick={onCancel} className="pi-mg-right-5">
+            关闭
+          </ZhCloseButton>
+          {renderOkBtn(type)}
+        </div>
+      }>
+      <Form form={form}>
+        {type === 'delete' ? (
+          <>
+            <p className="mb-2">删除后,数据无法恢复,请谨慎操作。</p>
+            <p className="mb-2">
+              请在下方文本框输入文本「<span className="pi-red">确认删除本次巡检</span>」,以此确认删除操作。
+            </p>
+            <Form.Item
+              name="warningText"
+              rules={[
+                () => ({
+                  validator(rule, value) {
+                    if (!value || value !== '确认删除本次巡检') {
+                      return Promise.reject('请按照提示信息进行删除操作!')
+                    }
+                    return Promise.resolve()
+                  }
+                })
+              ]}>
+              <Input placeholder="输入文本, 确认删除"></Input>
+            </Form.Item>
+          </>
+        ) : null}
+      {
+        type === 'start'? (
+          <p>请确认审批流程及信息无误。</p>
+        ) : null
+      }
+      {
+        type === 'close' ? (
+          <>
+          <Form.Item name="opinion" label="审批意见">
+            <Input.TextArea rows={5}>
+            </Input.TextArea>
+          </Form.Item>
+          <p className="pi-warning">审批关闭,将直接停止该巡检流程。</p>
+          </>
+        ) : null
+      }
+      </Form>
+    </Modal>
+  )
+}
+
+export default AuditModal

+ 74 - 17
src/pages/Safe/Content/Info/Detail/index.tsx

@@ -12,12 +12,13 @@ import { iUserInfo } from '@/types/setting'
 import { apiDelFile, apiGetFileList, apiSaveFileInfo } from '@/utils/common/api'
 import consts from '@/utils/consts'
 import { dayjsFormat } from '@/utils/util'
-import { Button, Input, Pagination, Tooltip } from 'antd'
+import { Button, Input, message, Pagination, Tooltip } from 'antd'
 import locale from 'antd/es/date-picker/locale/zh_CN'
 import dayjs from 'dayjs'
 import React, { useEffect, useState } from 'react'
 import { RouteComponentProps } from 'react-router'
-import { apiGetSafeDetail } from './api'
+import { apiGetSafeDetail, apiResfulSafeAudit } from './api'
+import AuditModal from './components/Modal'
 import styles from './index.module.scss'
 const { TextArea } = Input
 interface iDetailState {
@@ -49,9 +50,19 @@ interface iFileState {
   fileList: any[]
   total: number
 }
+
+interface iModalObj {
+  ossModal: boolean
+  auditModal: boolean
+  auditType: string
+}
 const Detail: React.FC<RouteComponentProps> = props => {
   const { saveId = '' } = props.location.state as any
-  const [ visible, setVisible ] = useState<boolean>(false)
+  const [ visible, setVisible ] = useState<iModalObj>({
+    ossModal: false,
+    auditModal: false,
+    auditType: ''
+  })
   const [ detail, setDetail ] = useState<iDetailState>({
     auditName: '',
     auditors: [],
@@ -79,7 +90,6 @@ const Detail: React.FC<RouteComponentProps> = props => {
       progress: ''
     }
   })
-
   useEffect(() => {
     initData()
   }, [ saveId ])
@@ -98,10 +108,10 @@ const Detail: React.FC<RouteComponentProps> = props => {
         })
       )
       setDetail({ ...detail, file: { ...detail.file, fileList: newFiles } })
-      setVisible(false)
+      setVisible({ ...visible, ossModal: false })
     }
   }
-  const onShow = (show: boolean) => setVisible(show)
+  const onOssModalShow = (show: boolean) => setVisible({ ...visible, ossModal: show })
   const fileListChange = async (pageNo: number = 1, pageSize: number = 10) => {
     const { code = -1, data } = await apiGetFileList(consts.DATA_TYPE.SAFE, detail.id, pageNo, pageSize)
     if (code === consts.RET_CODE.SUCCESS) {
@@ -118,6 +128,9 @@ const Detail: React.FC<RouteComponentProps> = props => {
   }
 
   const addAuditor = (type: string, user: iUserInfo) => {
+    if (detail.auditors.find(item => item.progress === (type === 'check' ? '0' : '2') && item.audit_id === user.id)) {
+      return message.error('该审批组下已存在该审批人,请勿重复添加!')
+    }
     if (type === "check") {
       const newAuditors = detail.auditors
       const len = detail.auditors.filter((item: iAuditor) => item.progress === "0").length
@@ -131,36 +144,69 @@ const Detail: React.FC<RouteComponentProps> = props => {
     }
   }
 
-  const delAuditor = (id: string) => {
-    const newAuditors = detail.auditors.filter(item => item.audit_id !== id)
+  const delAuditor = (id: string, progress: string) => {
+    const newAuditors = detail.auditors.filter(item => item.progress !== progress)
+    const auditor = detail.auditors.find(item => item.progress === progress && item.audit_id !== id)
+
+    if (auditor) {
+      newAuditors.push(auditor)
+    }
     setDetail({ ...detail, auditors: newAuditors })
   }
 
+  const btnClick = (type: string) => {
+    setVisible({ ...visible, auditType: type, auditModal: true })
+  }
+  const onModalConfirm = () => {
+    let payload: any = { safe_id: detail.id, bidsection_id: detail.bidsectionId }
+    if (visible.auditType === 'start') {
+      payload.auditors = detail.auditors.filter(item => item.progress === '0').map(item => item.audit_id)
+      payload.reAuditors = detail.auditors.filter(item => item.progress === '2').map(item => item.audit_id)
+      if (!payload.auditors.length || !payload.reAuditors.length) {
+        return message.error('审批人或整改人不能为空!')
+      }
+    }
+    if (visible.auditType === 'delete') {
+      payload = { id: detail.id }
+    }
+    apiResful(visible.auditType, payload)
+  }
+  const apiResful = async (type: string, payload: any) => {
+    const { code } = await apiResfulSafeAudit(type, payload)
+    if (code === consts.RET_CODE.SUCCESS) {
+      if (type === 'delete') {
+        props.history.goBack()
+      } else {
+        initData()
+      }
+    }
+  }
   const renderHeaderBtn = (status: number) => {
     if (!status) {
       return (
         <div className="pi-flex-row pi-align-center">
-          <ZhCloseButton size="small">删除巡检</ZhCloseButton>
-          <ZhSubmitButton size="small" className="pi-mg-left-5">提交审批</ZhSubmitButton>
+          <ZhCloseButton size="small" onClick={() => btnClick('delete')}>删除巡检</ZhCloseButton>
+          <ZhSubmitButton size="small" className="pi-mg-left-5" onClick={() => btnClick('start')}>提交审批</ZhSubmitButton>
         </div>
       )
     } else if(status === 1) {
       return (
         <div className="pi-flex-row pi-align-center">
-          <Button type="primary" danger size="small">关闭</Button>
-          <ZhAuditBackButton size="small" className="pi-mg-left-5">审批退回</ZhAuditBackButton>
-          <ZhSubmitButton size="small" className="pi-mg-left-5">审批通过</ZhSubmitButton>
+          <Button type="primary" danger size="small" onClick={() => btnClick('sclose')}>关闭</Button>
+          <ZhAuditBackButton size="small" className="pi-mg-left-5" onClick={() => btnClick('back')}>审批退回</ZhAuditBackButton>
+          <ZhSubmitButton size="small" className="pi-mg-left-5" onClick={() => btnClick('pass')}>审批通过</ZhSubmitButton>
         </div>
       )
     } else {
       return (
         <div className="pi-flex-row pi-align-center">
-          <ZhAuditBackButton size="small">审批退回</ZhAuditBackButton>
-          <ZhSubmitButton size="small" className="pi-mg-left-5">整改</ZhSubmitButton>
+          <ZhAuditBackButton size="small" onClick={() => btnClick('back')}>审批退回</ZhAuditBackButton>
+          <ZhSubmitButton size="small" className="pi-mg-left-5" onClick={() => btnClick('pass')}>整改完成</ZhSubmitButton>
         </div>
       )
     }
   }
+
   return (
     <div className="wrap-contaniner">
       <Header title="安全巡检">
@@ -266,7 +312,7 @@ const Detail: React.FC<RouteComponentProps> = props => {
             <tbody>
               <tr>
                 <td colSpan={5}>
-                  <ZhUploadButton size="small" icon={<SvgIcon type="xxh-cloud-upload" />} onClick={() => setVisible(true)}>
+                  <ZhUploadButton size="small" icon={<SvgIcon type="xxh-cloud-upload" />} onClick={() => setVisible({ ...visible, ossModal: true })}>
                     上传附件
                   </ZhUploadButton>
                 </td>
@@ -318,7 +364,18 @@ const Detail: React.FC<RouteComponentProps> = props => {
             ></AuditContent>
         </div>
       </div>
-      <OssUploadModal visible={visible} onCancel={() => setVisible(false)} onCreate={onCreate} onShow={onShow}></OssUploadModal>
+      <OssUploadModal
+        visible={visible.ossModal}
+        onCancel={() => setVisible({ ...visible, ossModal: false })}
+        onCreate={onCreate}
+        onShow={onOssModalShow}>
+      </OssUploadModal>
+      <AuditModal
+        type={visible.auditType}
+        visible={visible.auditModal}
+        onCancel={() => setVisible({ ...visible, auditModal: false })}
+        onCreate={onModalConfirm}
+        ></AuditModal>
     </div>
   )
 }