123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501 |
- import { iAuditHistoryState, iAuditor, iLatestAuditorState } from '@/types/safe'
- import { iAccountGroupItem, iUserInfo } from '@/types/setting'
- import { auditConsts } from '@/utils/common/constStatus'
- import { getUserGroup } from '@/utils/common/user'
- import { formatDate } from '@/utils/util'
- import { Input, Popover } from 'antd'
- import QueueAnim from 'rc-queue-anim'
- import React, { ChangeEvent, useEffect, useState } from 'react'
- import { ExpandButton, ZhButton } from '../Button'
- import SvgIcon from '../SvgIcon'
- import './index.scss'
- interface iGroupItem extends iAccountGroupItem {
- onSelect: (item: iUserInfo) => void
- }
- export const GroupItem: React.FC<iGroupItem> = props => {
- const { onSelect } = props
- const [ visible, setVisible ] = useState<boolean>(true)
- const changeVisible = () => {
- setVisible(!visible)
- }
- return props.children.length ? (
- <div>
- <div className="group-item-label-name pi-align-center">
- <ExpandButton expanded={visible} onExpand={() => setVisible(!visible)}></ExpandButton>
- <span onClick={changeVisible} className="pi-mg-left-5">
- {props.value}
- </span>
- </div>
- <QueueAnim>
- {visible
- ? props.children.map((account: iUserInfo) => {
- return (
- <div key={account.id} className={[ 'group-item-content', 'pi-justify-between', 'item-border-bottom' ].join(' ')} onClick={() => onSelect(account)}>
- <div className="pi-flex-column">
- <span className="pi-blue">{account.name}</span>
- <span className="pi-gray">{account.position}</span>
- </div>
- <span>{account.mobile}</span>
- </div>
- )
- })
- : null}
- </QueueAnim>
- </div>
- ) : null
- }
- interface iAuditContentProps {
- onSelect: (type: string, item: iUserInfo) => void
- onDelete: (id: string, progress: string) => void
- latest: iLatestAuditorState
- auditors: iAuditor[]
- auditHistory: iAuditHistoryState[][]
- status: number
- uName: string
- }
- const Index: React.FC<iAuditContentProps> = props => {
- const { onSelect, auditors, onDelete, status, auditHistory } = props
- const [ visible, setVisible ] = useState({
- check: false,
- reCheck: false
- })
- const showPopover = (type: string) => {
- setVisible({ ...visible, [type]: true })
- }
- const [ searchValue, setSearchValue ] = useState<string>('')
- const [ groups, setGroups ] = useState<Array<iAccountGroupItem>>([])
- useEffect(() => {
- if (visible.check || visible.reCheck) {
- initGroupList()
- }
- }, [ visible.check, visible.reCheck ])
- const initGroupList = async (serach?: string) => {
- const data = await getUserGroup(serach)
- setGroups(data)
- }
- const handleVisibleChange = (type: string, isShow: boolean) => {
- setVisible({ ...visible, [type]: isShow })
- }
- const search = (value: string) => {
- if (value != searchValue) {
- setSearchValue(value)
- initGroupList(value)
- }
- }
- const change = (e: ChangeEvent) => {
- e.persist()
- const target = e.target as HTMLTextAreaElement
- if (!target.value) {
- initGroupList()
- }
- }
- const itemSelectHandler = (type: string, item: iUserInfo) => {
- onSelect(type, item)
- setVisible({ ...visible, [type]: false })
- }
- const renderAuditorSelectItem = (type = 'check') => {
- return (
- <ul className="card-content">
- {type === 'check'
- ? auditors
- .filter(item => item.progress === '0')
- .map((item, idx) => {
- return (
- <li key={item.audit_id}>
- <div className="pi-flex-column">
- <p>
- <span>{idx + 1}</span> {item.name}
- <small className="text-muted pi-mg-left-5">{item.position}</small>
- </p>
- <small className="text-muted">{item.company}</small>
- </div>
- <span className="pi-red pi-pointer" onClick={() => onDelete(item.audit_id, '0')}>
- 移除
- </span>
- </li>
- )
- })
- : auditors
- .filter(item => item.progress === '2')
- .map((item, idx) => {
- return (
- <li key={item.audit_id}>
- <div className="pi-flex-column">
- <p>
- <span>{idx + 1}</span> {item.name}
- <small className="text-muted pi-mg-left-5">{item.position}</small>
- </p>
- <small className="text-muted">{item.company}</small>
- </div>
- <span className="pi-red pi-pointer" onClick={() => onDelete(item.audit_id, '2')}>
- 移除
- </span>
- </li>
- )
- })}
- </ul>
- )
- }
- const renderStatusIcon = (status: number, isEnd: boolean) => {
- let bgColor: string = ''
- let iconType: string = ''
- switch (status) {
- case 0:
- bgColor = 'pi-bg-green'
- iconType = 'xxh-check'
- break
- case 1:
- bgColor = 'pi-bg-yellow'
- iconType = 'xxh-reply'
- break
- case 2:
- bgColor = 'pi-bg-red'
- iconType = 'xxh-minus'
- break
- default:
- break
- }
- if (isEnd) {
- iconType = 'xxh-caret-down1'
- }
- return <div className={[ 'timeline-item-icon', 'pi-justify-center', 'pi-align-center', bgColor ].join(' ')}>{iconType ? <SvgIcon type={iconType}></SvgIcon> : null}</div>
- }
- const renderStatusEle = (status: number, progress: string) => {
- let text = ''
- let textClass = 'pi-green'
- if (progress === '0') {
- switch (status) {
- case 0:
- text = '上报审批'
- textClass = 'pi-green'
- break
- case 1:
- text = '审批退回'
- textClass = 'pi-yellow'
- break
- case 2:
- text = '关闭'
- textClass = 'pi-red'
- break
- default:
- break
- }
- } else if (progress === '1' || progress === '3') {
- switch (status) {
- case 0:
- text = '审批通过'
- textClass = 'pi-green'
- break
- case 1:
- text = '审批退回'
- textClass = 'pi-yellow'
- break
- case 2:
- text = '关闭'
- textClass = 'pi-red'
- break
- default:
- break
- }
- } else if (progress === '2') {
- switch (status) {
- case 0:
- text = '整改完成'
- textClass = 'pi-green'
- break
- case 1:
- text = '审批退回'
- textClass = 'pi-yellow'
- break
- case 2:
- text = '关闭'
- textClass = 'pi-red'
- break
- default:
- break
- }
- }
- return { text, textClass }
- }
- const renderLeftStatus = (status: number) => {
- let text = ''
- let textClass = ''
- switch (status) {
- case 1:
- text = '进行中'
- textClass = 'pi-yellow'
- break
- case 2:
- text = '完成'
- textClass = 'pi-green'
- break
- case 3:
- text = '关闭'
- textClass = 'pi-red'
- break
- default:
- break
- }
- return { text, textClass }
- }
- const renderLeftAuditors = (status: number) => {
- // 整改人所需信息
- const len = auditors.filter(item => item.progress === '0').length
- return (
- <tbody>
- <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>
- {status !== auditConsts.uncheck ? (
- <td>
- <span className={renderLeftStatus(auditors.length > 1 ? 2 : 0).textClass}>{renderLeftStatus(auditors.length > 1 ? 2 : 0).text}</span>
- </td>
- ) : null}
- </tr>
- {auditors
- .filter(item => item.progress === '0')
- .map((item, idx) => {
- return idx === 0 ? (
- <tr key={item.audit_id}>
- <td rowSpan={auditors.filter(item => item.progress === '0').length} className="pi-text-center">
- 审批
- </td>
- <td>
- <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>
- {status !== auditConsts.uncheck ? (
- <td>
- <span className={renderLeftStatus(item.status).textClass}>{renderLeftStatus(item.status).text}</span>
- </td>
- ) : null}
- </tr>
- ) : (
- <tr key={item.audit_id}>
- <td>
- <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>
- {status !== auditConsts.uncheck ? (
- <td>
- <span className={renderLeftStatus(item.status).textClass}>{renderLeftStatus(item.status).text}</span>
- </td>
- ) : null}
- </tr>
- )
- })}
- <tr>
- <td className="pi-text-center">整改人</td>
- <td>
- <SvgIcon type={status === 0 ? 'xxh-stop-circle' : 'xxh-chevron-circle-down'}></SvgIcon>
- {status ? (
- auditors.find(item => item.progress === '1') ? (
- <>
- <span className="pi-mg-left-3">{auditors.find(item => item.progress === '1')?.name}</span>
- <small className="text-muted pi-mg-left-3">{auditors.find(item => item.progress === '1')?.position}</small>
- </>
- ) : (
- <span className="pi-mg-left-3">由 {auditors.filter(item => item.progress === '0')[len - 1]?.name} 指派</span>
- )
- ) : (
- <span className="pi-mg-left-3">
- 由
- <span className="pi-mg-3">
- {auditors.filter(item => item.progress === '0').length ? auditors.filter(item => item.progress === '0')[len - 1]?.name : '最后一个审批人'}
- </span>
- 指派
- </span>
- )}
- </td>
- {status !== auditConsts.uncheck ? (
- <td>
- <span className={renderLeftStatus(auditors.find(item => item.progress === '1')?.status || 0).textClass}>
- {renderLeftStatus(auditors.find(item => item.progress === '1')?.status || 0).text}
- </span>
- </td>
- ) : null}
- </tr>
- {auditors
- .filter(item => item.progress === '2')
- .map((item, idx) => {
- return idx === 0 ? (
- <tr key={item.audit_id}>
- <td rowSpan={auditors.filter(item => item.progress === '2').length} className="pi-text-center">
- 复查
- </td>
- <td>
- <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>
- {status !== auditConsts.uncheck ? (
- <td>
- <span className={renderLeftStatus(item.status).textClass}>{renderLeftStatus(item.status).text}</span>
- </td>
- ) : null}
- </tr>
- ) : (
- <tr key={item.audit_id}>
- <td>
- <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>
- {status !== auditConsts.uncheck ? (
- <td>
- <span className={renderLeftStatus(item.status).textClass}>{renderLeftStatus(item.status).text}</span>
- </td>
- ) : null}
- </tr>
- )
- })}
- </tbody>
- )
- }
- const renderHistory = () => {
- return (
- <>
- {auditHistory?.map((item, index) => {
- return (
- <ul className="timeline-list" key={index}>
- {item?.map((auditor, idx) => (
- <div key={idx}>
- <li className="timeline-list-item">
- <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>
- {renderStatusIcon(auditor.status, idx === item.length - 1)}
- <div className="timeline-item-content">
- <div className="card-container">
- <div className="card-content">
- <div className="pi-justify-between label">
- <span>{auditor.name}</span>
- <span className={renderStatusEle(auditor.status, auditor.progress).textClass}>{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}
- </div>
- </div>
- </li>
- </div>
- ))}
- </ul>
- )
- })}
- </>
- )
- }
- return (
- <table className="pi-table pi-bordered mt-3">
- <thead>
- <tr>
- <th colSpan={2} className="pi-text-center">
- 审批流程
- </th>
- </tr>
- </thead>
- <tbody>
- <tr>
- {!status ? (
- <>
- <td width="30%">
- <table className="table table-bordered">{renderLeftAuditors(status)}</table>
- </td>
- <td width="70%">
- <div className="pi-justify-end">
- <Popover
- title={<Input.Search size="small" placeholder="姓名/手机 检索" onSearch={search} onChange={e => change(e)}></Input.Search>}
- content={groups.map(item => (
- <GroupItem {...item} key={item.value} onSelect={(item: iUserInfo) => itemSelectHandler('check', item)}></GroupItem>
- ))}
- overlayClassName="popover-card"
- trigger="click"
- visible={visible.check}
- onVisibleChange={visible => handleVisibleChange('check', visible)}
- placement="bottomRight">
- <ZhButton size="small" onClick={() => showPopover('check')}>
- 添加审批流程
- </ZhButton>
- </Popover>
- </div>
- <div className="audit-card">
- <div className="card-header">审批流程</div>
- {renderAuditorSelectItem('check')}
- </div>
- <div className="audit-card">
- <div className="card-header">整改流程</div>
- <ul className="card-content">
- <li>
- <span>整改人由最后一位审批人指派</span>
- </li>
- </ul>
- </div>
- <div className="pi-justify-end">
- <Popover
- title={<Input.Search size="small" placeholder="姓名/手机 检索" onSearch={search} onChange={e => change(e)}></Input.Search>}
- content={groups.map(item => (
- <GroupItem {...item} key={item.value} onSelect={(item: iUserInfo) => itemSelectHandler('reCheck', item)}></GroupItem>
- ))}
- overlayClassName="popover-card"
- trigger="click"
- visible={visible.reCheck}
- onVisibleChange={visible => handleVisibleChange('reCheck', visible)}
- placement="bottomRight">
- <ZhButton size="small" onClick={() => showPopover('reCheck')}>
- 添加审批流程
- </ZhButton>
- </Popover>
- </div>
- <div className="audit-card">
- <div className="card-header">复查流程</div>
- {renderAuditorSelectItem('reCheck')}
- </div>
- </td>
- </>
- ) : (
- <td>
- <div className="pi-flex-row">
- <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">{renderLeftAuditors(status)}</table>
- </div>
- <div className="pi-flex-twice pi-mg-left-15">{renderHistory()}</div>
- </div>
- </td>
- )}
- </tr>
- </tbody>
- </table>
- )
- }
- export default Index
|