index.tsx 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. import AuditContent from '@/components/AuditContent'
  2. import { ZhAuditBackButton, ZhCloseButton, ZhSubmitButton, ZhUploadButton } from '@/components/Button'
  3. import DatePicker from '@/components/DatePicker'
  4. import Header from '@/components/Header'
  5. import Slot from '@/components/Header/slot'
  6. import OssUploadModal from '@/components/OssUpload'
  7. import SvgIcon from '@/components/SvgIcon'
  8. import { userStore } from '@/store/mobx'
  9. import { iDetailState } from '@/types/auditDetail'
  10. import { iFile } from '@/types/file'
  11. import { iAuditor } from '@/types/safe'
  12. import { iUserInfo } from '@/types/setting'
  13. import { apiDelFile, apiGetFileList, apiSaveFileInfo } from '@/utils/common/api'
  14. import { auditProgress } from '@/utils/common/constStatus'
  15. import consts from '@/utils/consts'
  16. import { dayjsFormat } from '@/utils/util'
  17. import { Button, Input, message, Pagination, Tooltip } from 'antd'
  18. import locale from 'antd/es/date-picker/locale/zh_CN'
  19. import dayjs from 'dayjs'
  20. import React, { useEffect, useState, useMemo } from 'react'
  21. import { RouteComponentProps } from 'react-router'
  22. import { apiGetSafeDetail, apiResfulSafeAudit } from './api'
  23. import AuditModal from './components/Modal'
  24. import styles from './index.module.scss'
  25. const { TextArea } = Input
  26. interface iModalObj {
  27. ossModal: boolean
  28. auditModal: boolean
  29. auditType: string
  30. curPage: number
  31. }
  32. const Detail: React.FC<RouteComponentProps> = props => {
  33. const { saveId = '' } = props.location.state as any
  34. const [ visible, setVisible ] = useState<iModalObj>({
  35. ossModal: false,
  36. auditModal: false,
  37. auditType: '',
  38. curPage: 1
  39. })
  40. const [ detail, setDetail ] = useState<iDetailState>({
  41. auditName: '',
  42. uid: '',
  43. auditors: [],
  44. bidsectionId: '',
  45. code: '',
  46. createTime: new Date().toDateString(),
  47. demand: '',
  48. file: { fileList: [], total: 0 },
  49. id: '',
  50. inspection: '',
  51. inspectionDetail: '',
  52. position: '',
  53. status: 0,
  54. checkOrder: { // 整改单
  55. name: '',
  56. status: 0,
  57. opinion: '',
  58. create_time: new Date().toDateString()
  59. },
  60. auditHistory: [],
  61. rectifiedInfo: [],
  62. latestAuditor: {
  63. audit_id: '',
  64. audit_order: 0,
  65. bidsection_id: '',
  66. data_id: '',
  67. data_type: 0,
  68. id: '',
  69. progress: '',
  70. project_id: '',
  71. status: 0
  72. },
  73. times: 0
  74. })
  75. const isEdited = useMemo(() => {
  76. return !!detail.status
  77. }, [ detail.status ])
  78. useEffect(() => {
  79. initData()
  80. }, [ saveId ])
  81. const initData = async () => {
  82. const { code = -1, data = {} } = await apiGetSafeDetail(saveId)
  83. if (code === consts.RET_CODE.SUCCESS) {
  84. setDetail({ ...detail, ...data })
  85. }
  86. if (!userStore.groupList.length) {
  87. userStore.getGroupList()
  88. }
  89. }
  90. const onCreate = async (fileList: iFile[]) => {
  91. const { code = -1 } = await apiSaveFileInfo(fileList, consts.DATA_TYPE.SAFE, detail.id)
  92. if (code === consts.RET_CODE.SUCCESS) {
  93. const newFiles = detail.file.fileList.concat(
  94. fileList.map(file => {
  95. return { ...file, accountName: userStore.userInfo.name }
  96. })
  97. )
  98. setDetail({ ...detail, file: { ...detail.file, total: newFiles.length } })
  99. await fileListChange(visible.curPage)
  100. }
  101. }
  102. const onOssModalShow = (show: boolean) => setVisible({ ...visible, ossModal: show })
  103. const fileListChange = async (pageNo: number = 1, pageSize: number = 10) => {
  104. const { code = -1, data, total } = await apiGetFileList(consts.DATA_TYPE.SAFE, detail.id, pageNo, pageSize)
  105. if (code === consts.RET_CODE.SUCCESS) {
  106. setVisible({ ...visible, curPage: pageNo, ossModal: false })
  107. setDetail({ ...detail, file: { ...detail.file, fileList: data, total } })
  108. }
  109. }
  110. const delFile = async (id: string, isLast: boolean) => {
  111. const { code = -1 } = await apiDelFile(id)
  112. if (code === consts.RET_CODE.SUCCESS) {
  113. console.log(visible.curPage - 1)
  114. await fileListChange(isLast ? visible.curPage - 1 : visible.curPage)
  115. isLast && (setVisible({ ...visible, curPage: visible.curPage - 1 }))
  116. }
  117. }
  118. const addAuditor = (type: string, user: iUserInfo) => {
  119. if (detail.auditors.find(item => item.progress === (type === 'check' ? '0' : '2') && item.audit_id === user.id)) {
  120. return message.error('该审批组下已存在该审批人,请勿重复添加!')
  121. }
  122. if (type === "check") {
  123. const newAuditors = detail.auditors
  124. const len = detail.auditors.filter((item: iAuditor) => item.progress === "0").length
  125. newAuditors.push({ id: '', mobile:'', audit_id: user.id, audit_order: len + 1,position: user.position, progress: "0", name: user.name,accountGroup: user.accountGroup, company: user.company, status: 0 })
  126. setDetail({ ...detail, auditors: newAuditors })
  127. } else {
  128. const newAuditors = detail.auditors
  129. const len = detail.auditors.filter((item: iAuditor) => item.progress === "2").length
  130. newAuditors.push({ id: '', audit_id: user.id, mobile:'', audit_order: len + 1,position: user.position, progress: "2", name: user.name, accountGroup: user.accountGroup, company: user.company, status: 0 })
  131. setDetail({ ...detail, auditors: newAuditors })
  132. }
  133. }
  134. const delAuditor = (id: string, progress: string) => {
  135. const newAuditors = detail.auditors.filter(item => item.progress !== progress)
  136. const auditor = detail.auditors.find(item => item.progress === progress && item.audit_id !== id)
  137. if (auditor) {
  138. newAuditors.push(auditor)
  139. }
  140. setDetail({ ...detail, auditors: newAuditors })
  141. }
  142. const btnClick = (type: string) => {
  143. setVisible({ ...visible, auditType: type, auditModal: true })
  144. }
  145. const onModalConfirm = (values?: object) => {
  146. let payload: any = { safe_id: detail.id, bidsection_id: detail.bidsectionId, ...values }
  147. if (visible.auditType === 'start') {
  148. payload.auditors = detail.auditors.filter(item => item.progress === '0').map(item => item.audit_id)
  149. payload.reAuditors = detail.auditors.filter(item => item.progress === '2').map(item => item.audit_id)
  150. payload.times = detail.times
  151. if (!payload.auditors.length || !payload.reAuditors.length) {
  152. return message.error('审批人或复查人不能为空!')
  153. }
  154. }
  155. if (visible.auditType === 'delete') {
  156. payload = { id: detail.id }
  157. }
  158. if (visible.auditType === 'pass' || visible.auditType === 'back') {
  159. payload.id = detail.latestAuditor.id
  160. if (detail.latestAuditor.progress === '1') {
  161. if (!detail.checkOrder.opinion) {
  162. return message.error("请填写整改单!")
  163. } else {
  164. payload.rectifiedInfo = detail.checkOrder.opinion
  165. }
  166. }
  167. }
  168. if (visible.auditType === 'close') {
  169. payload.id = detail.latestAuditor.id
  170. }
  171. apiResful(visible.auditType, payload)
  172. }
  173. const apiResful = async (type: string, payload: any) => {
  174. const { code } = await apiResfulSafeAudit(type, payload)
  175. if (code === consts.RET_CODE.SUCCESS) {
  176. setVisible({ ...visible, auditModal: false })
  177. if (type === 'delete') {
  178. props.history.goBack()
  179. } else {
  180. initData()
  181. }
  182. }
  183. }
  184. const renderHeaderBtn = (status: number) => {
  185. if (!detail.latestAuditor.audit_id && userStore.userInfo.id !== detail.uid) return null
  186. if (detail.latestAuditor.audit_id && userStore.userInfo.id !== detail.latestAuditor.audit_id) return null
  187. if (!status) {
  188. return (
  189. <div className="pi-flex-row pi-align-center">
  190. <ZhCloseButton size="small" onClick={() => btnClick('delete')}>删除巡检</ZhCloseButton>
  191. <ZhSubmitButton size="small" className="pi-mg-left-5" onClick={() => btnClick('start')}>提交审批</ZhSubmitButton>
  192. </div>
  193. )
  194. } else if(status === auditProgress.checking || status === auditProgress.checkNo) {
  195. return (
  196. <div className="pi-flex-row pi-align-center">
  197. <Button type="primary" danger size="small" onClick={() => btnClick('close')}>关闭</Button>
  198. <ZhAuditBackButton size="small" className="pi-mg-left-5" onClick={() => btnClick('back')}>审批退回</ZhAuditBackButton>
  199. <ZhSubmitButton size="small" className="pi-mg-left-5" onClick={() => btnClick('pass')}>审批通过</ZhSubmitButton>
  200. </div>
  201. )
  202. } else if (status === auditProgress.checked) {
  203. return (
  204. <div className="pi-flex-row pi-align-center">
  205. <ZhAuditBackButton size="small" onClick={() => btnClick('back')}>审批退回</ZhAuditBackButton>
  206. <ZhSubmitButton size="small" className="pi-mg-left-5" onClick={() => btnClick('pass')}>整改完成</ZhSubmitButton>
  207. </div>
  208. )
  209. }
  210. }
  211. return (
  212. <div className="wrap-contaniner">
  213. <Header title="安全巡检">
  214. <Slot position="right">
  215. {renderHeaderBtn(detail.status)}
  216. </Slot>
  217. </Header>
  218. <div className={styles.detailContainer}>
  219. <div className={styles.content}>
  220. <h4 className={styles.header}>{detail.code}</h4>
  221. <table className="pi-table pi-bordered">
  222. <thead>
  223. <tr>
  224. <th colSpan={2} className="pi-text-center">
  225. 安全巡检单
  226. </th>
  227. </tr>
  228. </thead>
  229. <tbody>
  230. <tr>
  231. <th style={{ width: '150px' }}>检查项目</th>
  232. <td>
  233. {
  234. isEdited ? <span>{detail.inspection}</span> : <TextArea value={detail.inspection} ></TextArea>
  235. }
  236. </td>
  237. </tr>
  238. <tr>
  239. <th style={{ width: '150px' }}>现场检查情况</th>
  240. <td>
  241. {
  242. isEdited ? <span>{detail.inspectionDetail}</span> : <TextArea value={detail.inspectionDetail} ></TextArea>
  243. }
  244. </td>
  245. </tr>
  246. <tr>
  247. <th style={{ width: '150px' }}>处理要求及措施</th>
  248. <td>
  249. {
  250. isEdited ? <span>{detail.demand}</span> : <TextArea value={detail.demand}></TextArea>
  251. }
  252. </td>
  253. </tr>
  254. <tr>
  255. <th style={{ width: '150px' }}>检查日期</th>
  256. <td>
  257. {
  258. isEdited ? <span>{detail.createTime && dayjsFormat(detail.createTime, 'YYYY-MM-DD')}</span> :
  259. <DatePicker
  260. size="small"
  261. locale={locale}
  262. allowClear={false}
  263. value={dayjs(detail.createTime)}
  264. onChange={value => setDetail({ ...detail, createTime: value?.format() })}></DatePicker>
  265. }
  266. </td>
  267. </tr>
  268. <tr>
  269. <th style={{ width: '150px' }}>质检员</th>
  270. <td>{detail.auditName}</td>
  271. </tr>
  272. </tbody>
  273. </table>
  274. {
  275. detail.status === auditProgress.checked && detail.latestAuditor.progress === '1'?
  276. <table className="pi-table pi-bordered">
  277. <thead>
  278. <tr>
  279. <th colSpan={2} className="pi-text-center">
  280. 整改单
  281. </th>
  282. </tr>
  283. </thead>
  284. <tbody>
  285. <tr>
  286. <th style={{ width: '150px' }}>整改情况</th>
  287. <td>
  288. <TextArea value={detail.checkOrder.opinion} onChange={(e) => setDetail({ ...detail, checkOrder: { ...detail.checkOrder, opinion: e.currentTarget.value } })}></TextArea>
  289. </td>
  290. </tr>
  291. <tr>
  292. <th style={{ width: '150px' }}>整改日期</th>
  293. <td>
  294. <DatePicker
  295. size="small"
  296. locale={locale}
  297. allowClear={false}
  298. value={dayjs(detail.checkOrder.create_time)}
  299. onChange={value => setDetail({ ...detail, checkOrder: { ...detail.checkOrder, create_time: value?.format() } })}></DatePicker>
  300. </td>
  301. </tr>
  302. <tr>
  303. <th style={{ width: '150px' }}>整改人</th>
  304. <td>{detail.auditors.find(item => item.progress === '1')?.name}</td>
  305. </tr>
  306. </tbody>
  307. </table>
  308. : detail.rectifiedInfo.map(item => (
  309. <table className="pi-table pi-bordered" key={item.create_time}>
  310. <thead>
  311. <tr>
  312. <th colSpan={2} className="pi-text-center">
  313. 整改单
  314. </th>
  315. </tr>
  316. </thead>
  317. <tbody>
  318. <tr>
  319. <th style={{ width: '150px' }}>整改情况</th>
  320. <td>{item.opinion}</td>
  321. </tr>
  322. <tr>
  323. <th style={{ width: '150px' }}>整改日期</th>
  324. <td>{dayjsFormat(item.create_time, 'YYYY-MM-DD')}</td>
  325. </tr>
  326. <tr>
  327. <th style={{ width: '150px' }}>整改人</th>
  328. <td>{item.name}</td>
  329. </tr>
  330. </tbody>
  331. </table>
  332. ))
  333. }
  334. <table className="pi-table pi-bordered mt-3">
  335. <thead>
  336. <tr>
  337. <th></th>
  338. <th className="pi-text-center">附件</th>
  339. <th className="pi-text-center">上传者</th>
  340. <th className="pi-text-center" style={{ width: 200 }}>
  341. 上传时间
  342. </th>
  343. <th className="pi-text-center">操作</th>
  344. </tr>
  345. </thead>
  346. <tbody>
  347. <tr>
  348. <td colSpan={5}>
  349. <ZhUploadButton size="small" icon={<SvgIcon type="xxh-cloud-upload" />} onClick={() => setVisible({ ...visible, ossModal: true })}>
  350. 上传附件
  351. </ZhUploadButton>
  352. </td>
  353. </tr>
  354. {detail.file.fileList?.map((file, idx) => (
  355. <tr key={idx}>
  356. <td className="pi-width-70">{idx + 1}</td>
  357. <td style={{ width: 383, maxWidth: 383, overflow: 'hidden', whiteSpace: 'nowrap', textOverflow: 'ellipsis' }}>
  358. <a href={consts.OSS_PATH.REVIEW + file.filepath} target="_blank" rel="noopener noreferrer">
  359. {file.filename}
  360. </a>
  361. </td>
  362. <td className="pi-text-center">{file.accountName}</td>
  363. <td className="pi-text-center">{dayjsFormat(file.createTime)}</td>
  364. <td className="pi-text-center pi-width-90">
  365. <Tooltip title="移除">
  366. <Button size="small" type="text" icon={<SvgIcon type="xxh-times-circle1" />} style={{ color: '#df3f45' }} onClick={() => delFile(file.id, !idx)}></Button>
  367. </Tooltip>
  368. </td>
  369. </tr>
  370. ))}
  371. {detail.file.total > consts.PAGE_SIZE ? (
  372. <tr>
  373. <td colSpan={5} className="pi-text-right">
  374. <Pagination
  375. current={visible.curPage}
  376. size="small"
  377. pageSize={consts.PAGE_SIZE}
  378. hideOnSinglePage={true}
  379. total={detail.file.total}
  380. onChange={(page, pageSize) => fileListChange(page, pageSize)}></Pagination>
  381. </td>
  382. </tr>
  383. ) : null}
  384. </tbody>
  385. </table>
  386. <AuditContent
  387. auditors={detail.auditors}
  388. onSelect={addAuditor}
  389. onDelete={delAuditor}
  390. latest={detail.latestAuditor}
  391. auditHistory={detail.auditHistory}
  392. status={detail.status}
  393. uName={detail.auditName}
  394. ></AuditContent>
  395. </div>
  396. </div>
  397. <OssUploadModal
  398. visible={visible.ossModal}
  399. onCancel={() => setVisible({ ...visible, ossModal: false })}
  400. onCreate={onCreate}
  401. onShow={onOssModalShow}>
  402. </OssUploadModal>
  403. <AuditModal
  404. type={visible.auditType}
  405. visible={visible.auditModal}
  406. onCancel={() => setVisible({ ...visible, auditModal: false })}
  407. onCreate={onModalConfirm}
  408. auditors={detail.auditors}
  409. curAuditor={detail.latestAuditor}
  410. ></AuditModal>
  411. </div>
  412. )
  413. }
  414. export default Detail