Merge branch 'master' of http://120.79.193.119:9090/r/gzzfw/frontEnd/gzDyh into master
| | |
| | | * @Description: 来访登记步骤条 |
| | | */ |
| | | |
| | | import React, { useEffect, useState } from 'react'; |
| | | import { Tooltip, Space } from 'antd'; |
| | | import Icon, { PaperClipOutlined, RollbackOutlined, EllipsisOutlined } from '@ant-design/icons'; |
| | | import React, { Fragment, useEffect, useState } from 'react'; |
| | | import { Tooltip } from 'antd'; |
| | | import { ledger_8, ledger_7, ledger_12, ledger_13 } from '../../assets/images'; |
| | | import NameCard from '../NameCard'; |
| | | import * as $$ from '../../utils/utility'; |
| | | import FilesDrawer from '../FilesDrawer'; |
| | | import './index.less'; |
| | | |
| | | /** |
| | | * progressData:'', // 流程数据 |
| | | * progressData: { |
| | | * assistCaseFlowList: [],//配合部门流程数组 |
| | | * handleCaseFlowList: [],//承办部门流程数组 |
| | | * }, // 流程数据 |
| | | * hasTab: false,//是否有部门切换 |
| | | */ |
| | | const ProgressStep = ({ progressData }) => { |
| | | const ProgressStep = ({ progressData, hasTab }) => { |
| | | const [proType, setProType] = useState('handleCaseFlowList') |
| | | const iconMap = { |
| | | 1: ledger_8, |
| | | 2: ledger_7, |
| | | 3: ledger_12, |
| | | 4: ledger_13 |
| | | 1: ledger_7,//完成 |
| | | 2: ledger_12,//回退 |
| | | 3: ledger_13//上报 |
| | | } |
| | | const btnList = [ |
| | | { |
| | | value: 'handleCaseFlowList', |
| | | label: '承办部门' |
| | | }, |
| | | { |
| | | value: 'assistCaseFlowList', |
| | | label: '配合部门' |
| | | }, |
| | | ] |
| | | |
| | | // 已处理列表第一个元素 |
| | | const dom1 = (x) => ( |
| | | useEffect(() => { |
| | | setProType('handleCaseFlowList') |
| | | }, [progressData]) |
| | | |
| | | return ( |
| | | <Fragment> |
| | | {hasTab && |
| | | <div className='tabBtn'> |
| | | {btnList.map((item, index) => { |
| | | return <div |
| | | className={`tabBtn-btn ${proType === item.value ? 'tabBtn-active' : ''}`} |
| | | key={item.value} |
| | | onClick={() => { setProType(item.value) }} |
| | | style={{ marginRight: (index + 1) !== btnList.length ? '20px' : '0' }} |
| | | >{item.label}</div> |
| | | })} |
| | | |
| | | </div> |
| | | } |
| | | {progressData[proType] && progressData[proType].length !== 0 |
| | | ? progressData[proType].map((x, t) => { |
| | | return ( |
| | | <div key={t + 1}> |
| | | <div className="myStep-item"> |
| | | {t === progressData[proType].length - 1 ? null : <div className={`${!x.handleContent ? 'myStep-item-divider' : x.fileInfoList?.length > 0 ? 'myStep-item-divider2' : "myStep-item-divider1"} ${x.status === '2' && 'myStep-item-divider-success'}`} />} |
| | | <div className={`myStep-item-icon1 myStep-item-${x.status === 1 ? 'noStarted1' : 'success1'}`}> |
| | | <div className="myStep-item-title">{x.nodeShowName || '事件流转'}</div> |
| | | <img className='myStep-item-img' src={x.status === 1 ? ledger_8 : iconMap[x.taskType]} alt="" /> |
| | | </div> |
| | | <div className="myStep-item-right"> |
| | | {t === progressData[proType].length - 1 ? |
| | | <div> |
| | | <div className="myStep-item-p" style={{ color: 'rgba(0,0,0,0.50)' }}> |
| | | {x.processName} |
| | | </div> |
| | | <div className="myStep-item-p"> |
| | | <span>{x.handleUnitName || '-'}</span> |
| | | </div> |
| | | </div> |
| | | : |
| | | <div> |
| | | <div className="myStep-item-p"> |
| | | <span>{x.handlerUserName || '-'}</span> |
| | | <span>{x.handleUnitName || '-'}</span> |
| | | </div> |
| | | {x.handleUserName && |
| | | <div className="myStep-item-p" style={{ color: 'rgba(0,0,0,0.50)' }}> |
| | | <span>操作人:</span> |
| | | <span>{x.operationName}</span> |
| | | </div> |
| | | <div className="myStep-item-p" style={{ color: 'rgba(0,0,0,0.50)' }}> |
| | | <span>经办时间:</span> |
| | | <span>{$$.timeFormat(x.finishTime)}</span> |
| | | </div> |
| | | </div> |
| | | ); |
| | | // 已处理列表后面 元素 |
| | | const dom2 = (x) => ( |
| | | <div> |
| | | <div className="myStep-item-p"> |
| | | <span>{x.handlerUserName || '-'}</span> |
| | | </div> |
| | | {x.operationName && |
| | | <div className="myStep-item-p" style={{ color: 'rgba(0,0,0,0.50)' }}> |
| | | <span>操作人:</span> |
| | | <span>{x.operationName}</span> |
| | | <span>{x.handleUserName}</span> |
| | | </div> |
| | | } |
| | | <div className="myStep-item-p" style={{ color: 'rgba(0,0,0,0.50)' }}> |
| | | <span>操作时间:</span> |
| | | <span>{$$.timeFormat(x.finishTime)}</span> |
| | | <span>{$$.timeFormat(x.handleTime)}</span> |
| | | </div> |
| | | { |
| | | x.handleContent && |
| | | x.handleNotes && |
| | | <div className='myStep-item-p-yy' style={{ width: '200px' }}> |
| | | <span className='myStep-item-p-yy-l'> |
| | | {x.handleResult == '2' ? |
| | | <span>退回<Tooltip placement="top" title={x.handleContent || ''}><span>{`(${x.handleContent})` || '-'}</span></Tooltip></span> |
| | | : x.taskNode == 'F22_00019-3' && x.handleResult == '1' ? |
| | | // 22_00025-1:调解成功,22_00025-2:调解不成功 |
| | | <span>调解结果:<Tooltip placement="top" title={x.mediResult == '22_00025-1' ? '调解成功' : x.mediResult == '22_00025-2' ? '调解不成功' : '-'}><span>{`${x.mediResult == '22_00025-1' ? '调解成功' : x.mediResult == '22_00025-2' ? '调解不成功' : '-'}` || '-'}</span></Tooltip></span> |
| | | : <Tooltip placement="top" title={x.handleContent || ''}><span>{x.handleContent || '-'}</span></Tooltip>} |
| | | <span><Tooltip placement="top" title={x.handleNotes || ''}><span>{x.handleNotes || '-'}</span></Tooltip></span> |
| | | </span> |
| | | { |
| | | x.fileInfoList?.length > 0 && |
| | | <div style={{ marginTop: '4px' }}> |
| | | <FilesDrawer name={<> |
| | | <PaperClipOutlined style={{ marginRight: '4px' }} /> |
| | | <span>{x.taskNode == 'F22_00019-3' && x.handleResult == '1' ? '调解协议书' : '处理附件'}</span> |
| | | </>} filesData={x.fileInfoList} title="经办附件" /> |
| | | </div> |
| | | } |
| | | </div> |
| | | } |
| | | </div> |
| | | ); |
| | | |
| | | // 办理中 |
| | | const dom3 = (x) => ( |
| | | <div> |
| | | <div className="myStep-item-p"> |
| | | <span style={{ color: 'rgba(0,0,0,0.50)' }}>待受理</span> |
| | | </div> |
| | | <div className="myStep-item-p"> |
| | | <span>{x.handlerUserName}</span> |
| | | </div> |
| | | </div> |
| | | ); |
| | | |
| | | return ( |
| | | <> |
| | | {progressData.length !== 0 |
| | | ? progressData.map((x, t) => { |
| | | return ( |
| | | <div key={t + 1}> |
| | | <div className="myStep-item"> |
| | | {t === progressData.length - 1 ? null : <div className={`${!x.handleContent ? 'myStep-item-divider' : x.fileInfoList?.length > 0 ? 'myStep-item-divider2' : "myStep-item-divider1"} ${x.status === '2' && 'myStep-item-divider-success'}`} />} |
| | | <div className={`myStep-item-icon1 myStep-item-${x.status === '1' ? 'noStarted1' : 'success1'}`}> |
| | | <div className="myStep-item-title">{x.taskNodeName}</div> |
| | | <img className='myStep-item-img' src={iconMap[x.status]} alt="" /> |
| | | </div> |
| | | <div className="myStep-item-right"> |
| | | { |
| | | t === 0 && dom1(x || {}) |
| | | } |
| | | { |
| | | t !== 0 && <> |
| | | { |
| | | x.status === '1' ? dom3(x || {}) : dom2(x || {}) |
| | | } |
| | | </> |
| | | } |
| | | </div> |
| | | </div> |
| | |
| | | ); |
| | | }) |
| | | : <div style={{ padding: '100px 0' }}>{$$.MyEmpty()}</div>} |
| | | </> |
| | | </Fragment> |
| | | ); |
| | | }; |
| | | |
| | |
| | | border-color: #bfbfbf; |
| | | } |
| | | } |
| | | |
| | | &-success1 { |
| | | background-color: @main-color; |
| | | |
| | |
| | | margin-top: 4px; |
| | | display: flex; |
| | | align-items: flex-start; |
| | | |
| | | &-flex { |
| | | display: flex; |
| | | flex-direction: column; |
| | | justify-content: center; |
| | | align-items: center; |
| | | margin-top: 8px; |
| | | |
| | | &-title { |
| | | font-size: 12px; |
| | | line-height: 20px; |
| | | color: @text-color-secondary; |
| | | } |
| | | } |
| | | |
| | | &-yy { |
| | | display: flex; |
| | | flex-direction: column; |
| | |
| | | border-radius: 5px; |
| | | padding: 4px 8px; |
| | | color: rgba(0, 0, 0, 0.5); |
| | | |
| | | &-l { |
| | | overflow: hidden; |
| | | white-space: nowrap; |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | .tabBtn { |
| | | display: flex; |
| | | margin-bottom: 12px; |
| | | |
| | | &-btn { |
| | | height: 32px; |
| | | background: #ffffff; |
| | | border: 1px solid #e5e6eb; |
| | | border-radius: 4px; |
| | | color: #e5e6eb; |
| | | padding: 4px 8px; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | &-active { |
| | | color: #1a6fb8; |
| | | border-color: #1a6fb8; |
| | | } |
| | | } |
| | |
| | | |
| | | // 获取人员,组织,部门数据 |
| | | function getDataApi(type, searchData) { |
| | | const url = type === 'person' ? 'ctUser/userChoose' : type === 'unit' ? 'ctUser/unitList' : 'ctUser/deptList'; |
| | | let url |
| | | switch (type) { |
| | | case 'dept': |
| | | url = `ctUnit/unitChoose` |
| | | break; |
| | | case 'person': |
| | | url = 'ctUser/userChoose' |
| | | break |
| | | case 'unit': |
| | | url = 'ctUser/unitList' |
| | | break |
| | | default: |
| | | break; |
| | | } |
| | | return $$.ax.request({ url, type: 'get', data: searchData, service: 'cust' }); |
| | | } |
| | | |
| | |
| | | * onClose, // 关闭 |
| | | * onOk, // 点击确定的回调 |
| | | */ |
| | | const SelectObjModal = ({ visible = false, checkKeys = [], type = 'person', isCheckbox = false, searchData = {}, onClose, onOk, }) => { |
| | | const SelectObjModal = ({ visible = false, checkKeys = [], type = 'dept', isCheckbox = false, searchData = {}, onClose, onOk, }) => { |
| | | const nameStr = type === 'person' ? '人员' : type === 'unit' ? '组织' : '部门'; |
| | | // 默认调解员查询'22_00024-4' |
| | | const searchRole = type === 'person' ? { roleCode: '22_00024-4' } : {}; |
| | | const [data, setData] = useState([]); |
| | | |
| | | const [checkedKeys, setCheckedKeys] = useState({ keys: [], items: [] }); |
| | | |
| | | const [expandedKeys, setExpandedKeys] = useState([]); |
| | | |
| | | const [searchValue, setSearchValue] = useState(''); |
| | | |
| | | const [autoExpandParent, setAutoExpandParent] = useState(true); |
| | | |
| | | const [dataList, setDataList] = useState([]); |
| | | |
| | | // tree复选框选择 |
| | | function handleCheck(checkedKeys, e) { |
| | | if (!isCheckbox && checkedKeys.checked.length > 1) { |
| | | $$.info({ type: 'warning', content: '当前选择只可单选' }); |
| | | return; |
| | | useEffect(() => { |
| | | if (!visible) return; |
| | | // 获取数据 |
| | | async function getData() { |
| | | // global.setSpinning(true); |
| | | const res = await getDataApi(type, { ...searchRole, ...searchData }); |
| | | // global.setSpinning(false); |
| | | if (res.type) { |
| | | setData(res.data || []); |
| | | } |
| | | let checkedNodes = e.checkedNodes; |
| | | checkedNodes.forEach((x) => delete x.children); |
| | | setCheckedKeys({ keys: checkedKeys.checked, items: checkedNodes }); |
| | | } |
| | | |
| | | // 删除选项 |
| | | function handleDelete(t) { |
| | | checkedKeys.keys.splice(t, 1); |
| | | checkedKeys.items.splice(t, 1); |
| | | setCheckedKeys({ ...checkedKeys }); |
| | | if (checkKeys.length !== 0) { |
| | | let keys = []; |
| | | checkKeys.forEach((x) => keys.push(x.value)); |
| | | setCheckedKeys({ keys, items: checkKeys }); |
| | | } else { |
| | | setCheckedKeys({ keys: [], items: [] }); |
| | | } |
| | | getData(); |
| | | }, [type, visible]); |
| | | |
| | | // 搜索 |
| | | useEffect(() => { |
| | |
| | | setDataList(arr); |
| | | handleSearch('', arr); |
| | | }, [data]); |
| | | |
| | | const getParentKey = (key, tree) => { |
| | | let parentKey; |
| | | for (let i = 0; i < tree.length; i++) { |
| | | const node = tree[i]; |
| | | if (node.children) { |
| | | if (node.children.some((item) => item.value === key)) { |
| | | parentKey = node.value; |
| | | } else if (getParentKey(key, node.children)) { |
| | | parentKey = getParentKey(key, node.children); |
| | | } |
| | | } |
| | | } |
| | | return parentKey; |
| | | }; |
| | | |
| | | function handleSearch(value, dataList) { |
| | | const newExpandedKeys = dataList |
| | | .map((item) => { |
| | | if (item.label.indexOf(value) > -1) { |
| | | return getParentKey(item.value, data); |
| | | } |
| | | return null; |
| | | }) |
| | | .filter((item, i, self) => item && self.indexOf(item) === i); |
| | | setExpandedKeys(newExpandedKeys); |
| | | setSearchValue(value); |
| | | setAutoExpandParent(true); |
| | | } |
| | | |
| | | const treeData = useMemo(() => { |
| | | const loop = (data) => |
| | |
| | | return loop(data); |
| | | }, [searchValue, data]); |
| | | |
| | | // 默认调解员查询'22_00024-4' |
| | | const searchRole = type === 'person' ? { roleCode: '22_00024-4' } : {}; |
| | | // tree复选框选择 |
| | | function handleCheck(checkedKeys, e) { |
| | | if (!isCheckbox && checkedKeys.checked.length > 1) { |
| | | $$.info({ type: 'warning', content: '当前选择只可单选' }); |
| | | return; |
| | | } |
| | | let checkedNodes = e.checkedNodes; |
| | | checkedNodes.forEach((x) => delete x.children); |
| | | setCheckedKeys({ keys: checkedKeys.checked, items: checkedNodes }); |
| | | } |
| | | |
| | | console.log(checkedKeys.keys,'checkedKeys.keyscheckedKeys.keys') |
| | | useEffect(() => { |
| | | if (!visible) return; |
| | | // 获取数据 |
| | | async function getData() { |
| | | global.setSpinning(true); |
| | | const res = await getDataApi(type, { ...searchRole, ...searchData }); |
| | | global.setSpinning(false); |
| | | if (res.type) { |
| | | setData(res.data || []); |
| | | // 删除选项 |
| | | function handleDelete(t) { |
| | | checkedKeys.keys.splice(t, 1); |
| | | checkedKeys.items.splice(t, 1); |
| | | setCheckedKeys({ ...checkedKeys }); |
| | | } |
| | | } |
| | | if (checkKeys.length !== 0) { |
| | | let keys = []; |
| | | checkKeys.forEach((x) => keys.push(x.value)); |
| | | setCheckedKeys({ keys, items: checkKeys }); |
| | | } else { |
| | | setCheckedKeys({ keys: [], items: [] }); |
| | | } |
| | | getData(); |
| | | }, [type, visible]); |
| | | |
| | | const nameStr = type === 'person' ? '人员' : type === 'unit' ? '组织' : '部门'; |
| | | const getParentKey = (key, tree) => { |
| | | let parentKey; |
| | | for (let i = 0; i < tree.length; i++) { |
| | | const node = tree[i]; |
| | | if (node.children) { |
| | | if (node.children.some((item) => item.value === key)) { |
| | | parentKey = node.value; |
| | | } else if (getParentKey(key, node.children)) { |
| | | parentKey = getParentKey(key, node.children); |
| | | } |
| | | } |
| | | } |
| | | return parentKey; |
| | | }; |
| | | |
| | | function handleSearch(value, dataList) { |
| | | const newExpandedKeys = dataList |
| | | .map((item) => { |
| | | if (item.label.indexOf(value) > -1) { |
| | | return getParentKey(item.value, data); |
| | | } |
| | | return null; |
| | | }) |
| | | .filter((item, i, self) => item && self.indexOf(item) === i); |
| | | setExpandedKeys(newExpandedKeys); |
| | | setSearchValue(value); |
| | | setAutoExpandParent(true); |
| | | } |
| | | |
| | | return ( |
| | | <Modal |
| | |
| | | <Route path="judicialOverview" element={<JudicialOverview />} /> |
| | | {/* 来访登记*/} |
| | | <Route path="visit/:id?" element={<Visit />} /> |
| | | <Route path="visit/eventFlow" element={<EventFlow />} /> |
| | | <Route path="visit/eventFlow/:caseTaskId?/:caseId?" element={<EventFlow />} /> |
| | | <Route path="visit/handleFeedback" element={<HandleFeedback />} /> |
| | | <Route path="visit/fileMessage" element={<FileMessage />} /> |
| | | <Route path="visit/closingReview" element={<ClosingReview />}/> |
| | |
| | | import React, { useRef, useState } from 'react' |
| | | import React, { useRef, useState, useEffect } from 'react' |
| | | import { Row, Col } from 'antd'; |
| | | import { Form, Input, Button, Select } from '@arco-design/web-react'; |
| | | import ArcoUpload from '@/components/ArcoUpload'; |
| | | import { Scrollbars } from "react-custom-scrollbars"; |
| | | import SelectObjModal from '@/components/SelectObjModal/selectPerson'; |
| | | import { useParams } from 'react-router-dom'; |
| | | import * as $$ from '@/utils/utility'; |
| | | |
| | | const FormItem = Form.Item; |
| | | const TextArea = Input.TextArea; |
| | | const Option = Select.Option; |
| | | const options = ['Beijing', 'Shanghai', 'Guangzhou', 'Shenzhen', 'Chengdu', 'Wuhan']; |
| | | const appUrl = $$.appUrl; |
| | | |
| | | function assign(data) { |
| | | return $$.ax.request({ url: `caseTask/assign`, type: 'post', service: 'mediate', data }); |
| | | } |
| | | |
| | | function delFile(id) { |
| | | return $$.ax.request({ url: `fileInfo/deleteFileById`, type: 'get', service: 'sys', data: { id } }); |
| | | } |
| | | |
| | | export default function BackModel(props) { |
| | | const routeData = useParams(); |
| | | const formRef = useRef(); |
| | | const [isModalVisible, setIsModalVisible] = useState(false); |
| | | const [wantUser, setWantUser] = useState({}); |
| | | const [mainDept, setMainDept] = useState('handleUnit');//判断打开弹窗的是承办部门还是配合部门 |
| | | const [selectOptions, setSelectOptions] = useState({});//两个部门选择的options |
| | | |
| | | useEffect(() => { |
| | | }, []) |
| | | |
| | | |
| | | const handleSubmit = () => { |
| | | formRef.current.validate(undefined, (errors, values) => { |
| | | console.log(errors, values); |
| | | if (!errors) { |
| | | handleAssign({ |
| | | caseTaskId: routeData.caseTaskId, |
| | | assignContent: values.assignContent, |
| | | handleUnitId: wantUser['handleUnit'][0].value, |
| | | handleUnitName: wantUser['handleUnit'][0].name, |
| | | assistUnitList: wantUser['assistUnit'] && wantUser['assistUnit'].map(item => { |
| | | return { |
| | | uitId: item.value, |
| | | uitName: item.name |
| | | } |
| | | }) |
| | | }) |
| | | } |
| | | }) |
| | | } |
| | | |
| | | const handleTemplate = (type) => { |
| | | if (type === 1) { |
| | | formRef.current.setFieldValue('trueName', '本事项经初步核实,认为属于贵部门职责范围内的矛盾纠纷化解工作。请组织专门人员负责此事,尽快查明事实真相,依法依规进行处理,并在处理过程中充分考虑当事人的合理诉求,确保公平公正,维护当事人的合法权益。同时,请务必保持与当事人的沟通畅通,及时反馈办理进展,以增强矛盾纠纷化解工作的透明度和公信力。') |
| | | formRef.current.setFieldValue('assignContent', '本事项经初步核实,认为属于贵部门职责范围内的矛盾纠纷化解工作。请组织专门人员负责此事,尽快查明事实真相,依法依规进行处理,并在处理过程中充分考虑当事人的合理诉求,确保公平公正,维护当事人的合法权益。同时,请务必保持与当事人的沟通畅通,及时反馈办理进展,以增强矛盾纠纷化解工作的透明度和公信力。') |
| | | } else { |
| | | formRef.current.setFieldValue('trueName', '') |
| | | formRef.current.setFieldValue('assignContent', '') |
| | | } |
| | | } |
| | | |
| | | const handleFocus = (e) => { |
| | | e.stopPropagation() |
| | | setIsModalVisible(true) |
| | | //交办请求 |
| | | const handleAssign = async (data) => { |
| | | const res = await assign(data) |
| | | if (res.type) { |
| | | $$.infoSuccess({ content: '交办成功!' }); |
| | | props.onCancel() |
| | | } |
| | | } |
| | | |
| | | //删除文件 |
| | | const handleDelFile = async (id) => { |
| | | const res = await delFile(id) |
| | | if (res.type) { |
| | | $$.infoSuccess({ content: '删除成功!' }); |
| | | } |
| | | } |
| | | |
| | | //form数据同步要提交的数据 |
| | | const handleSync = (field, value) => { |
| | | wantUser[field] = wantUser[field].filter(item => value.indexOf(item.value) != -1) |
| | | setWantUser(wantUser); |
| | | } |
| | | |
| | | return ( |
| | |
| | | <Col span={24}> |
| | | <FormItem |
| | | label={(<div style={{ display: 'flex' }}>承办部门<div className="must">必填</div></div>)} |
| | | field='bumen' |
| | | field='handleUnit' |
| | | rules={[{ required: true, message: '请选择承办部门' }]} |
| | | > |
| | | <Select |
| | | mode='multiple' |
| | | placeholder='请选择承办部门' |
| | | allowClear |
| | | onFocus={handleFocus} |
| | | onFocus={(e) => { |
| | | e.stopPropagation() |
| | | setIsModalVisible(true) |
| | | setMainDept('handleUnit') |
| | | }} |
| | | options={selectOptions['handleUnit']} |
| | | onChange={(v) => { handleSync('handleUnit', v) }} |
| | | > |
| | | </Select> |
| | | </FormItem> |
| | |
| | | <Col span={24}> |
| | | <FormItem |
| | | label={(<div style={{ display: 'flex' }}>配合部门<div style={{ color: '#86909C' }}>(可多选)</div></div>)} |
| | | field='peihe' |
| | | field='assistUnit' |
| | | > |
| | | <Select |
| | | mode='multiple' |
| | | placeholder='请选择配合部门' |
| | | allowClear |
| | | onFocus={(e) => { |
| | | e.stopPropagation() |
| | | setIsModalVisible(true) |
| | | setMainDept('assistUnit') |
| | | }} |
| | | options={selectOptions['assistUnit']} |
| | | onChange={(v) => { handleSync('assistUnit', v) }} |
| | | > |
| | | {options.map((option) => ( |
| | | <Option key={option} value={option}> |
| | | {option} |
| | | </Option> |
| | | ))} |
| | | </Select> |
| | | </FormItem> |
| | | </Col> |
| | |
| | | </div> |
| | | <FormItem |
| | | label={(<div style={{ display: 'flex' }}>交办意见<div className="must">必填</div></div>)} |
| | | field='trueName' |
| | | field='assignContent' |
| | | rules={[{ required: true, message: '请选择回退理由' }]} |
| | | > |
| | | <TextArea |
| | |
| | | <Col span={24} className="doubleFile"> |
| | | <ArcoUpload |
| | | params={{ |
| | | action: ``, |
| | | action: `${appUrl.fileUrl}/${appUrl.sys}/api/web/fileInfo/upload?mainId=${props.caseId}&ownerId=${routeData.caseTaskId}&ownerType=22_00018-501`, |
| | | }} |
| | | field='file' |
| | | label='附件材料' |
| | | handleDelFile={handleDelFile} |
| | | /> |
| | | </Col> |
| | | </Row> |
| | |
| | | </div> |
| | | <SelectObjModal |
| | | visible={isModalVisible} |
| | | checkKeys={wantUser.wantUserId ? [{ label: wantUser.wantUserName, value: wantUser.wantUserId }] : undefined} |
| | | checkKeys={wantUser[mainDept]} |
| | | onOk={(value) => { |
| | | console.log(value); |
| | | setIsModalVisible(false); |
| | | setWantUser({ wantUserId: value.keys[0], wantUserName: value.items[0].name }); |
| | | wantUser[mainDept] = value.items |
| | | setWantUser(wantUser); |
| | | selectOptions[mainDept] = value.items.map(item => ({ |
| | | label: item.name, |
| | | value: item.value |
| | | })) |
| | | setSelectOptions(selectOptions) |
| | | formRef.current.setFieldValue(mainDept, value.items.map(item => item.value)) |
| | | |
| | | }} |
| | | onClose={() => setIsModalVisible(false)} |
| | | type='dept' |
| | | isCheckbox={mainDept === 'handleUnit' ? false : true} |
| | | /> |
| | | </div> |
| | | ) |
| | |
| | | import React, { useRef } from 'react' |
| | | import React, { useEffect, useRef, useState } from 'react' |
| | | import { Row, Col } from 'antd'; |
| | | import { Form, Input, Button, Radio } from '@arco-design/web-react'; |
| | | import ArcoUpload from '@/components/ArcoUpload'; |
| | | import { Scrollbars } from "react-custom-scrollbars"; |
| | | import * as $$ from '@/utils/utility'; |
| | | |
| | | const RadioGroup = Radio.Group; |
| | | const FormItem = Form.Item; |
| | | const TextArea = Input.TextArea; |
| | | const appUrl = $$.appUrl; |
| | | |
| | | function getId() { |
| | | return $$.ax.request({ url: `caseUtils/getNewTimeId`, type: 'get', service: 'utils' }); |
| | | } |
| | | |
| | | function delFile(id) { |
| | | return $$.ax.request({ url: `fileInfo/deleteFileById`, type: 'get', service: 'sys', data: { id } }); |
| | | } |
| | | |
| | | function returnApply(data) { |
| | | return $$.ax.request({ url: `caseTask/returnApply`, type: 'post', service: 'mediate', data }); |
| | | } |
| | | |
| | | export default function BackModel(props) { |
| | | const formRef = useRef(); |
| | | const [id, setId] = useState(); |
| | | const options = [ |
| | | { |
| | | label: '不属于本部门的职能范围', |
| | |
| | | value: 5 |
| | | }, |
| | | ] |
| | | const handleSubmit = () => { |
| | | |
| | | useEffect(() => { |
| | | getAppId() |
| | | }, []) |
| | | |
| | | //获取id |
| | | const getAppId = async () => { |
| | | const res = await getId() |
| | | if (res.type) { |
| | | setId(res.data) |
| | | } |
| | | } |
| | | |
| | | const handleSubmit = () => { |
| | | if (formRef.current) { |
| | | formRef.current.validate(undefined, (errors, values) => { |
| | | if (!errors) { |
| | | handleReturn({ |
| | | id, |
| | | caseId: props.caseId, |
| | | returnContent: values.returnContent |
| | | }) |
| | | } |
| | | }) |
| | | } |
| | | } |
| | | |
| | | //删除文件 |
| | | const handleDelFile = async (id) => { |
| | | const res = await delFile(id) |
| | | if (res.type) { |
| | | $$.infoSuccess({ content: '删除成功!' }); |
| | | } |
| | | } |
| | | |
| | | //回退请求 |
| | | const handleReturn = async (data) => { |
| | | const res = await returnApply(data) |
| | | if (res.type) { |
| | | $$.infoSuccess({ content: '提交申请成功!' }); |
| | | props.onCancel() |
| | | } |
| | | } |
| | | |
| | | return ( |
| | | <div> |
| | | <Scrollbars style={{ height: '550px' }} autoHide> |
| | |
| | | <Col span={24}> |
| | | <FormItem |
| | | label={(<div style={{ display: 'flex' }}>回退理由<div className="must">必填</div></div>)} |
| | | field='trueName' |
| | | field='backType' |
| | | rules={[{ required: true, message: '请选择回退理由' }]} |
| | | > |
| | | <RadioGroup direction='vertical' options={options}> |
| | | </RadioGroup> |
| | | <RadioGroup |
| | | direction='vertical' |
| | | options={options} |
| | | onChange={(value) => { |
| | | const obj = options.find(item => item.value === value) |
| | | formRef.current.setFieldValue('returnContent', obj.label) |
| | | }} |
| | | /> |
| | | </FormItem> |
| | | </Col> |
| | | <Col span={24}> |
| | | <FormItem |
| | | label=' ' |
| | | field='luyou' |
| | | field='returnContent' |
| | | rules={[{ required: true, message: '回退理由不能为空' }]} |
| | | > |
| | | <TextArea |
| | |
| | | <Col span={24} className="doubleFile"> |
| | | <ArcoUpload |
| | | params={{ |
| | | action: ``, |
| | | action: `${appUrl.fileUrl}/${appUrl.sys}/api/web/fileInfo/upload?mainId=${props.caseId}&ownerId=${id}&ownerType=22_00018-520`, |
| | | }} |
| | | field='file' |
| | | label='附件材料' |
| | | handleDelFile={handleDelFile} |
| | | /> |
| | | </Col> |
| | | </Row> |
| | |
| | | import React, { useRef } from 'react' |
| | | import React, { useEffect, useRef, useState } from 'react' |
| | | import { Row, Col } from 'antd'; |
| | | import { Form, Input, Button } from '@arco-design/web-react'; |
| | | import ArcoUpload from '@/components/ArcoUpload'; |
| | | import { Scrollbars } from "react-custom-scrollbars"; |
| | | import { escalation } from '@/assets/images/icon'; |
| | | import * as $$ from '@/utils/utility'; |
| | | |
| | | const FormItem = Form.Item; |
| | | const TextArea = Input.TextArea; |
| | | const appUrl = $$.appUrl; |
| | | |
| | | function getId() { |
| | | return $$.ax.request({ url: `caseUtils/getNewTimeId`, type: 'get', service: 'utils' }); |
| | | } |
| | | |
| | | function delFile(id) { |
| | | return $$.ax.request({ url: `fileInfo/deleteFileById`, type: 'get', service: 'sys', data: { id } }); |
| | | } |
| | | |
| | | function appearApply(data) { |
| | | return $$.ax.request({ url: `caseTask/appearApply`, type: 'post', service: 'mediate', data }); |
| | | } |
| | | |
| | | export default function BackModel(props) { |
| | | const formRef = useRef(); |
| | | const [id, setId] = useState() |
| | | |
| | | useEffect(() => { |
| | | getAppId() |
| | | }, []) |
| | | |
| | | //获取id |
| | | const getAppId = async () => { |
| | | const res = await getId() |
| | | if (res.type) { |
| | | setId(res.data) |
| | | } |
| | | } |
| | | |
| | | const handleSubmit = () => { |
| | | formRef.current.validate(undefined, (errors, values) => { |
| | | console.log(errors, values); |
| | | if (formRef.current) { |
| | | formRef.current.validate(undefined, (errors, values) => { |
| | | if (!errors) { |
| | | handleEscala({ |
| | | id, |
| | | caseId: props.caseId, |
| | | returnContent: values.returnContent |
| | | }) |
| | | } |
| | | }) |
| | | } |
| | | }) |
| | | } |
| | | |
| | | const handleTemplate = (type) => { |
| | | if(type === 1) { |
| | | formRef.current.setFieldValue('trueName', '经初步核查,该事项较为复杂,且涉及多个相关部门的协调配合,为确保能够高效、妥善地解决当事人的问题,特此请求上级给予指导和支持。') |
| | | formRef.current.setFieldValue('returnContent', '经初步核查,该事项较为复杂,且涉及多个相关部门的协调配合,为确保能够高效、妥善地解决当事人的问题,特此请求上级给予指导和支持。') |
| | | } else { |
| | | formRef.current.setFieldValue('trueName', '') |
| | | formRef.current.setFieldValue('returnContent', '') |
| | | } |
| | | } |
| | | |
| | | //上报请求 |
| | | const handleEscala = async (data) => { |
| | | const res = await appearApply(data) |
| | | if (res.type) { |
| | | $$.infoSuccess({ content: '提交申请成功!' }); |
| | | props.onCancel() |
| | | } |
| | | } |
| | | |
| | | //删除文件 |
| | | const handleDelFile = async (id) => { |
| | | const res = await delFile(id) |
| | | if (res.type) { |
| | | $$.infoSuccess({ content: '删除成功!' }); |
| | | } |
| | | } |
| | | |
| | |
| | | </div> |
| | | <FormItem |
| | | label={(<div style={{ display: 'flex' }}>上报意见<div className="must">必填</div></div>)} |
| | | field='trueName' |
| | | field='returnContent' |
| | | rules={[{ required: true, message: '请选择回退理由' }]} |
| | | > |
| | | |
| | | <TextArea |
| | | autoSize={{ minRows: 4, maxRows: 8 }} |
| | | placeholder='请填写回退的具体理由' |
| | |
| | | <Col span={24} className="doubleFile"> |
| | | <ArcoUpload |
| | | params={{ |
| | | action: ``, |
| | | action: `${appUrl.fileUrl}/${appUrl.sys}/api/web/fileInfo/upload?mainId=${props.caseId}&ownerId=${id}&ownerType=22_00018-512`, |
| | | }} |
| | | field='file' |
| | | label='附件材料' |
| | | handleDelFile={handleDelFile} |
| | | /> |
| | | </Col> |
| | | </Row> |
| | |
| | | import AssignedModel from './AssignedModel'; |
| | | import MatterDetail from '../../matterDetail'; |
| | | import SupervisingView from "../../matterDetail/Supervising"; |
| | | import * as $$ from '@/utils/utility'; |
| | | |
| | | const TabPane = Tabs.TabPane; |
| | | const Step = Steps.Step; |
| | | |
| | | const fakeData = [ |
| | | { |
| | | handlerUserName: '天河区棠下街综治中心', |
| | | finishTime: new Date().getTime() - 24 * 60 * 60 * 1000, // 一天前的时间 |
| | | handleResult: '1', |
| | | status: '2', |
| | | taskNodeName: '来访登记', |
| | | mediResult: '22_00025-1', |
| | | handleContent: '调解成功,双方达成一致意见。', |
| | | operationName: '李晓明' |
| | | }, |
| | | { |
| | | handlerUserName: '系统派单', |
| | | finishTime: new Date().getTime() - 12 * 60 * 60 * 1000, // 半天前的时间 |
| | | handleResult: '1', |
| | | status: '2', |
| | | taskNodeName: '事件流转', |
| | | mediResult: '22_00025-1', |
| | | handleContent: '派单至:白云区新市街市场监管所', |
| | | }, |
| | | { |
| | | handlerUserName: '白云区新市街市场监管所', |
| | | finishTime: new Date().getTime() - 11 * 60 * 60 * 1000, // 半天前的时间 |
| | | handleResult: '1', |
| | | status: '2', |
| | | taskNodeName: '事件流转', |
| | | mediResult: '22_00025-1', |
| | | handleContent: '已签收', |
| | | operationName: '赵菲菲' |
| | | }, |
| | | { |
| | | handlerUserName: '白云区新市街市场监管所', |
| | | finishTime: new Date().getTime() - 10 * 60 * 60 * 1000, // 半天前的时间 |
| | | handleResult: '1', |
| | | status: '3', |
| | | taskNodeName: '事件回退', |
| | | mediResult: '22_00025-1', |
| | | // handleContent: '已签收', |
| | | operationName: '赵菲菲' |
| | | }, |
| | | { |
| | | handlerUserName: '白云区新市街综治中心', |
| | | finishTime: new Date().getTime() - 9 * 60 * 60 * 1000, // 半天前的时间 |
| | | handleResult: '1', |
| | | status: '2', |
| | | taskNodeName: '回退审核', |
| | | mediResult: '22_00025-1', |
| | | handleContent: '通过', |
| | | operationName: '赵菲菲' |
| | | }, |
| | | { |
| | | handlerUserName: '天河区棠下街综治中心', |
| | | finishTime: new Date().getTime() - 6 * 60 * 60 * 1000, // 6小时前的时间 |
| | | handleResult: '2', |
| | | status: '1', |
| | | taskNodeName: '事件流转', |
| | | mediResult: '22_00025-1', |
| | | handleContent: '案件已被签收,准备开始调解。', |
| | | operationName: '李晓明' |
| | | }, |
| | | ]; |
| | | function getListCaseFlow(data) { |
| | | return $$.ax.request({ url: `caseTask/listCaseFlow`, type: 'get', service: 'mediate', data }); |
| | | } |
| | | |
| | | export default function EventFlow(props) { |
| | | const myButton = [ |
| | | { |
| | | label: '受理', |
| | | type: 'primary', |
| | | click: () => { }, |
| | | key: 'sl', |
| | | }, |
| | | { |
| | | label: '提交', |
| | | type: 'primary', |
| | | click: () => { }, |
| | | key: 'tj', |
| | | }, |
| | | { |
| | | label: '自行受理', |
| | | type: 'primary', |
| | | click: () => { }, |
| | | key: 'zxsl', |
| | | }, |
| | | { |
| | | label: '回退', |
| | | type: 'outline', |
| | | click: () => setBackVisible(true), |
| | | key: 'ht', |
| | | status: 'danger' |
| | | }, |
| | | { |
| | | label: '交办', |
| | | type: 'outline', |
| | | click: () => setAssignedVisible(true), |
| | | key: 'jb', |
| | | }, |
| | | { |
| | | label: '上报', |
| | | type: 'outline', |
| | | click: () => setEscalationVisible(true), |
| | | key: 'sb', |
| | | }, |
| | | ] |
| | | const scrollRef = useRef(null) |
| | | const [backVisible, setBackVisible] = useState(false) |
| | | const [height, setHeight] = useState(500) |
| | | const [escalationVisible, setEscalationVisible] = useState(false) |
| | | const [assignedVisible, setAssignedVisible] = useState(false) |
| | | const [staticButtonList, setStaticButtonList] = useState([]) |
| | | const [progressData, setProgressData] = useState({}) |
| | | |
| | | useEffect(() => { |
| | | if (props.authorData) { |
| | | const { buttonList } = props.authorData; |
| | | setStaticButtonList(myButton.filter(item => { |
| | | const flag = buttonList.some(result => { |
| | | if (result.id === item.key) { |
| | | return true |
| | | } |
| | | }) |
| | | return flag |
| | | })) |
| | | } |
| | | }, [props.authorData]) |
| | | |
| | | useEffect(() => { |
| | | getData() |
| | | onWindowResize() |
| | | window.addEventListener("resize", onWindowResize); |
| | | // 返回一个函数,该函数会在组件卸载前执行 |
| | | return () => { |
| | | // 组件销毁时执行 |
| | | window.removeEventListener("resize", onWindowResize); |
| | | }; |
| | | }, []) |
| | | |
| | | const onWindowResize = () => { |
| | |
| | | } |
| | | setHeight(getSize().windowH - offsetTop - 16) |
| | | }; |
| | | |
| | | //获取流程信息 |
| | | const getData = async () => { |
| | | const res = await getListCaseFlow({ |
| | | caseId: props.caseId |
| | | }) |
| | | if(res.type) { |
| | | setProgressData(res.data) |
| | | } |
| | | } |
| | | |
| | | return ( |
| | | <Fragment> |
| | |
| | | ref={scrollRef} |
| | | autoHide |
| | | > |
| | | <MatterDetail /> |
| | | <MatterDetail caseId={props.caseId}/> |
| | | <div className='dataSync-hasTabPage' style={{ marginTop: '-8px' }}> |
| | | <Tabs defaultActiveTab='1' > |
| | | <TabPane |
| | |
| | | </span> |
| | | } |
| | | > |
| | | <ProgressStep progressData={fakeData} /> |
| | | <ProgressStep progressData={progressData} /> |
| | | </TabPane> |
| | | <TabPane |
| | | key='2' |
| | |
| | | visible={backVisible} |
| | | onOk={() => setBackVisible(false)} |
| | | onCancel={() => { setBackVisible(false) }} |
| | | autoFocus={false} |
| | | focusLock={true} |
| | | footer={null} |
| | | unmountOnExit={true} |
| | | maskClosable={false} |
| | | > |
| | | <BackModel /> |
| | | <BackModel caseId={props.caseId} onCancel={() => { setBackVisible(false) }}/> |
| | | </Modal> |
| | | <Modal |
| | | title='上报' |
| | | visible={escalationVisible} |
| | | onOk={() => setEscalationVisible(false)} |
| | | onCancel={() => { setEscalationVisible(false) }} |
| | | autoFocus={false} |
| | | focusLock={true} |
| | | footer={null} |
| | | unmountOnExit={true} |
| | | maskClosable={false} |
| | | > |
| | | <EscalationModel /> |
| | | <EscalationModel caseId={props.caseId} onCancel={() => { setEscalationVisible(false) }}/> |
| | | </Modal> |
| | | <Modal |
| | | title='交办' |
| | |
| | | footer={null} |
| | | unmountOnExit={true} |
| | | maskClosable={false} |
| | | autoFocus={false} |
| | | focusLock={false} |
| | | > |
| | | <AssignedModel /> |
| | | <AssignedModel caseId={props.caseId} onCancel={() => { setAssignedVisible(false) }}/> |
| | | </Modal> |
| | | <div className="dataSync-excel"> |
| | | <Space size="large" style={{ margin: '4px 14px' }}> |
| | | <Button type="primary" >受理</Button> |
| | | <Button type="primary" >提交</Button> |
| | | <Button type="primary" >自行受理</Button> |
| | | <Button type='outline' status='danger' onClick={() => setBackVisible(true)}>回退</Button> |
| | | <Button type='outline' onClick={() => setAssignedVisible(true)}>交办</Button> |
| | | <Button type='outline' onClick={() => setEscalationVisible(true)}>上报</Button> |
| | | {staticButtonList?.map(item => { |
| | | const { label, key, click, ...rest } = item; |
| | | return <Button key={key} onClick={click} {...rest} >{label}</Button> |
| | | })} |
| | | <Button type='secondary' >返回上级页面</Button> |
| | | </Space> |
| | | </div> |
| | |
| | | * @Description: 来访登记 |
| | | */ |
| | | |
| | | import React, { useState, useRef, Fragment } from "react"; |
| | | import React, { useState, useRef, Fragment, useEffect } from "react"; |
| | | import NewPage from '@/components/NewPage'; |
| | | import * as $$ from '@/utils/utility'; |
| | | import "@arco-themes/react-gzzz/css/arco.css"; |
| | |
| | | import EventFlow from './component/EventFlow'; |
| | | import Examine from "./component/Examine"; |
| | | import ApplyInfo from "../matterDetail/ApplyInfo"; |
| | | import { useParams } from 'react-router-dom'; |
| | | |
| | | const Step = Steps.Step; |
| | | const TabPane = Tabs.TabPane; |
| | | |
| | | function getTabButton(data) { |
| | | return $$.ax.request({ url: `caseTask/getTabButton`, type: 'get', service: 'mediate', data }); |
| | | } |
| | | |
| | | const Organization = () => { |
| | | const [current, setCurrent] = useState(2); |
| | | const [tabsActive, setTabsActive] = useState('1'); |
| | | const [tabsList, setTabList] = useState([ |
| | | const myTab = [ |
| | | { |
| | | img: Matter, |
| | | label: '详情', |
| | | key: '1' |
| | | key: 'dslxq', |
| | | }, |
| | | { |
| | | img: Matter, |
| | | label: '事项详情', |
| | | key: 'sxxq', |
| | | }, |
| | | { |
| | | img: applyRecord, |
| | | label: '申请记录', |
| | | key: '2', |
| | | key: 'sqjl', |
| | | }, |
| | | { |
| | | img: examine, |
| | | label: '审核', |
| | | key: '3', |
| | | label: '回退审核', |
| | | key: 'htsh', |
| | | }, |
| | | ]) |
| | | const [disTab, setDisTab] = useState(true) |
| | | { |
| | | img: examine, |
| | | label: '上报审核', |
| | | key: 'sbsh', |
| | | }, |
| | | { |
| | | img: examine, |
| | | label: '结案审核', |
| | | key: 'jash', |
| | | }, |
| | | { |
| | | img: examine, |
| | | label: '联合处置申请审核', |
| | | key: 'lhczsh', |
| | | }, |
| | | ] |
| | | const Organization = () => { |
| | | const routeData = useParams(); |
| | | const [authorData, setAuthorData] = useState({}); |
| | | const [tabsList, setTabsList] = useState([]); |
| | | const [tabsActive, setTabsActive] = useState(); |
| | | const [disTab, setDisTab] = useState(true); |
| | | |
| | | useEffect(() => { |
| | | getAuthor() |
| | | }, []) |
| | | |
| | | //获取权限tab和按钮权限 |
| | | const getAuthor = async () => { |
| | | const res = await getTabButton({ |
| | | caseTaskId: routeData.caseTaskId |
| | | }) |
| | | if (res.type) { |
| | | const { tabList } = res.data |
| | | setAuthorData(res.data) |
| | | if (tabList.length === 0) { |
| | | //没有tab就不展示 |
| | | setDisTab(false) |
| | | } else { |
| | | setTabsList(myTab.filter(item => { |
| | | const flag = tabList.some(result => { |
| | | if (result.id === item.key) { |
| | | return true |
| | | } |
| | | }) |
| | | return flag |
| | | })) |
| | | setTabsActive(tabList[0].id) |
| | | } |
| | | } |
| | | } |
| | | |
| | | //根据id定义组件 |
| | | const getTypeDom = (key) => { |
| | | if (key === 'dslxq' || key === 'sxxq') { |
| | | return <EventFlow authorData={authorData} caseId={routeData.caseId} /> |
| | | } |
| | | if (key === 'sqjl') { |
| | | return <ApplyInfo /> |
| | | } |
| | | if (key === 'htsh' || key === 'sbsh' || key === 'jash' || key === 'lhczsh') { |
| | | return <Examine type={key} /> |
| | | } |
| | | } |
| | | |
| | | return ( |
| | | <div style={{ position: 'relative' }}> |
| | |
| | | } |
| | | > |
| | | {disTab ? <Tabs |
| | | defaultActiveTab='1' |
| | | onChange={(v) => setTabsActive(v)} |
| | | className='myTabContent' |
| | | activeTab={tabsActive} |
| | | > |
| | | {tabsList?.map(item => { |
| | | return <TabPane |
| | |
| | | </span> |
| | | } |
| | | > |
| | | {tabsActive === '1' && <EventFlow />} |
| | | {tabsActive === '2' && <ApplyInfo />} |
| | | {tabsActive === '3' && <Examine />} |
| | | {getTypeDom(item.key)} |
| | | </TabPane> |
| | | })} |
| | | </Tabs> : <EventFlow /> |
| | | </Tabs> : <EventFlow authorData={authorData} /> |
| | | } |
| | | </NewPage> |
| | | </div> |
| | |
| | | // 更多数据... |
| | | ]; |
| | | |
| | | useEffect(() => { |
| | | console.log(props); |
| | | getCaseInfo(props.caseId) |
| | | }, [props.caseId]) |
| | | |
| | | //获取id |
| | | const getCaseInfo = async () => { |
| | | const res = await getCaseInfoApi('24083010062110001') |
| | | const getCaseInfo = async (id) => { |
| | | const res = await getCaseInfoApi(id) |
| | | |
| | | if (res.type) { |
| | | let data = res.data |
| | |
| | | } |
| | | |
| | | } |
| | | |
| | | |
| | | // useEffect(() => { |
| | | // getCaseInfo() |
| | | // }, []) |
| | | |
| | | return ( |
| | | <div style={{ position: 'relative' }}> |