Detail.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869
  1. import ThresholdDetail from '@/components/ThresholdDetail';
  2. import ThresholdModal from '@/components/ThresholdDetail/ThresholdModal';
  3. import { changeRecordStatus, getDumuDetail } from '@/services/eqSelfInspection';
  4. import { UnityAction } from '@/utils/utils';
  5. import { connect, useRequest } from '@umijs/max';
  6. import {
  7. Col,
  8. DatePicker,
  9. Form,
  10. Input,
  11. Modal,
  12. Row,
  13. Select,
  14. Spin,
  15. Table,
  16. Tabs,
  17. message,
  18. } from 'antd';
  19. import dayjs from 'dayjs';
  20. import { useEffect, useMemo, useState } from 'react';
  21. import ReactZmage from 'react-zmage';
  22. import styles from './PatrolReportDetail.less';
  23. function Detail(props) {
  24. const { data, userList, projectId, dispatch, loading = false } = props;
  25. const [select, setSelect] = useState();
  26. const [dumuList, setDumuList] = useState([]);
  27. const sendMessageToUnity = (select, data) => {
  28. setSelect(select);
  29. if (window.HightlightEquipment) {
  30. window.HightlightEquipment(data);
  31. }
  32. };
  33. const getTextColor = (status) => {
  34. switch (status) {
  35. case '警告':
  36. return '#FFE26D';
  37. case '异常':
  38. return '#FE5850';
  39. case '正常':
  40. return '#12CEB3';
  41. default:
  42. return '#12CEB3';
  43. }
  44. };
  45. const { run: detailRun } = useRequest(getDumuDetail, {
  46. manual: true,
  47. fetchKey: (id) => id,
  48. onSuccess: (data) => {
  49. var item = dumuList?.find((child) => child.id === data.id);
  50. if (item) {
  51. item.url = base64ToImageUrl(data.event_bg);
  52. }
  53. setDumuList([...dumuList]);
  54. },
  55. });
  56. const result = useMemo(() => {
  57. var resultArr = [];
  58. var tempResult = data?.PatrolResult;
  59. var tempArr = tempResult?.split(';');
  60. if (tempArr?.length > 0) {
  61. tempArr?.forEach((item, index) => {
  62. var tempItem = item;
  63. var itemSplit = tempItem.split(':');
  64. if (itemSplit?.length > 1) {
  65. var label = '';
  66. var value = itemSplit[1];
  67. if (index === 0) label = '设备自检';
  68. else if (index === 1) label = '工艺自检';
  69. else {
  70. label = '安全隐患';
  71. value = data?.secureStatus === 0 ? '正常' : '异常';
  72. }
  73. resultArr.push({
  74. label,
  75. value,
  76. color: getTextColor(value),
  77. });
  78. }
  79. });
  80. }
  81. return resultArr;
  82. }, [data]);
  83. useEffect(() => {
  84. dispatch({
  85. type: 'eqSelfInspection/fetchUserList',
  86. payload: {
  87. projectId,
  88. },
  89. });
  90. }, []);
  91. useEffect(() => {
  92. setDumuList(data?.dumuList);
  93. data?.dumuList?.map((item) => {
  94. detailRun(item.id);
  95. });
  96. }, [data?.dumuList]);
  97. return (
  98. <Spin spinning={loading} wrapperClassName="card-box">
  99. <div className={styles.card}>
  100. <Row>
  101. <Col span={24} className={styles.cardText}>
  102. 自检时间:{data?.CreatedTime}
  103. </Col>
  104. </Row>
  105. <Row>
  106. <Col span={8} className={styles.cardText}>
  107. 自检路线:{data?.RouteInfo?.Name}
  108. </Col>
  109. <Col span={8} className={styles.cardText}>
  110. 工艺段:{data?.RouteInfo?.GroupID}
  111. </Col>
  112. </Row>
  113. <Row>
  114. {result?.map((item) => {
  115. return (
  116. <Col span={8} className={styles.cardText}>
  117. {item?.label}:
  118. <span style={{ color: item.color, fontWeight: 'bold' }}>
  119. {item?.value}
  120. </span>
  121. </Col>
  122. );
  123. })}
  124. </Row>
  125. </div>
  126. <div style={{ padding: 20, background: '#fff' }}>
  127. {/* 设备自检报告 */}
  128. <ReportCom
  129. sendMessageToUnity={sendMessageToUnity}
  130. select={select}
  131. waringData={data?.extendWarningData}
  132. allData={data?.extendWarningAllData}
  133. key="extend"
  134. type={'extend'}
  135. userList={userList}
  136. title={
  137. <div>
  138. <div className={styles.tableTitle}>设备自检报告</div>
  139. </div>
  140. }
  141. ></ReportCom>
  142. {/* 工艺自检报告"> */}
  143. <div className={styles.content}>
  144. <div className={styles.tableStatus}>
  145. 异常({data?.FaultAnalysis?.length || 0})
  146. </div>
  147. <div className={styles.tableTitle2}>工艺自检报告</div>
  148. <AalysisTable
  149. onClickItem={sendMessageToUnity}
  150. select={select}
  151. data={data}
  152. />
  153. </div>
  154. {/* 安全隐患自检报告"> */}
  155. <div className={styles.content}>
  156. <div className={styles.tableTitle2}>安全隐患自检报告</div>
  157. {/* 环境异常 */}
  158. <ReportCom
  159. sendMessageToUnity={sendMessageToUnity}
  160. select={select}
  161. waringData={data?.sensorWaringData}
  162. allData={data?.sensor}
  163. data={data?.sensorWaringData}
  164. key="sensor"
  165. type={'sensor'}
  166. userList={userList}
  167. title={
  168. <div style={{ color: '#1677ff', fontSize: 22 }}>环境异常</div>
  169. }
  170. ></ReportCom>
  171. {/* 安防检测异常 */}
  172. <ReportDumCom
  173. data={dumuList}
  174. title={
  175. <div style={{ color: '#1677ff', fontSize: 22 }}>安防检测异常</div>
  176. }
  177. />
  178. {/* 电器检测异常 */}
  179. <ReportCom
  180. sendMessageToUnity={sendMessageToUnity}
  181. select={select}
  182. waringData={[]}
  183. allData={[]}
  184. key="extend"
  185. type={'extend'}
  186. userList={userList}
  187. title={
  188. <div style={{ color: '#1677ff', fontSize: 22 }}>电气检测异常</div>
  189. }
  190. ></ReportCom>
  191. {/* 密闭空间检测异常 */}
  192. <ReportCom
  193. sendMessageToUnity={sendMessageToUnity}
  194. select={select}
  195. waringData={[]}
  196. allData={[]}
  197. key="extend"
  198. type={'extend'}
  199. userList={userList}
  200. title={
  201. <div style={{ color: '#1677ff', fontSize: 22 }}>
  202. 密闭空间检测异常
  203. </div>
  204. }
  205. ></ReportCom>
  206. </div>
  207. </div>
  208. {/* </Card> */}
  209. </Spin>
  210. );
  211. }
  212. export function DeviceTable(props) {
  213. const {
  214. onClickItem,
  215. data = {},
  216. items,
  217. onErrorHandle,
  218. select,
  219. userList,
  220. type,
  221. } = props;
  222. const { ProjectId, Id } = data;
  223. const [loading, setLoading] = useState(false);
  224. const [visible, setVisible] = useState(false);
  225. const [errVisible, setErrVisible] = useState(false);
  226. const [currentItem, setCurrentItem] = useState({});
  227. const isSensor = type == 'sensor';
  228. const onClickThreshold = (record) => {
  229. setCurrentItem(record);
  230. setVisible(true);
  231. };
  232. const onClickError = (record) => {
  233. setCurrentItem(record);
  234. setErrVisible(true);
  235. };
  236. const handleError = async (values) => {
  237. setLoading(true);
  238. var res = await changeRecordStatus({
  239. ...values,
  240. Id: currentItem.Id,
  241. DeviceCode: currentItem.DeviceCode,
  242. DeviceName: currentItem.DeviceName,
  243. RecordId: data.Id,
  244. RepairMan: values.RepairMan * 1,
  245. });
  246. setLoading(false);
  247. if (res) {
  248. message.success('操作成功');
  249. setErrVisible(false);
  250. }
  251. };
  252. const columns = [
  253. {
  254. title: '设备名称',
  255. width: '25%',
  256. dataIndex: 'DeviceName',
  257. },
  258. {
  259. title: '巡检项',
  260. width: '20%',
  261. dataIndex: 'TemplateItem.Name',
  262. },
  263. // {
  264. // title: '设备位号',
  265. // width: '16%',
  266. // dataIndex: 'DeviceCode',
  267. // },
  268. {
  269. title: '设定值范围',
  270. width: '30%',
  271. render: (record) => (
  272. <ThresholdDetail
  273. current={record.Value || 0}
  274. data={record || {}}
  275. // onClick={() => onClickThreshold(record)}
  276. />
  277. ),
  278. },
  279. {
  280. title: '状态',
  281. width: '13%',
  282. dataIndex: 'Status',
  283. render: (Status) => {
  284. switch (Status) {
  285. case -1:
  286. case 0:
  287. return (
  288. <div>
  289. <i
  290. className={styles.iconStatus}
  291. style={{ background: '#12CEB3' }}
  292. ></i>
  293. 正常
  294. </div>
  295. );
  296. case 1:
  297. return (
  298. <div>
  299. <i
  300. className={styles.iconStatus}
  301. style={{ background: '#FE5850' }}
  302. ></i>
  303. 异常
  304. </div>
  305. );
  306. case 2:
  307. return (
  308. <div>
  309. <i
  310. className={styles.iconStatus}
  311. style={{ background: '#FFE26D' }}
  312. ></i>
  313. 警告
  314. </div>
  315. );
  316. }
  317. },
  318. },
  319. ];
  320. const handleClickItem = (data) => {
  321. if (!isSensor) {
  322. onClickItem(`DeviceTable-${data.Id}`, {
  323. type: data.Status,
  324. deviceCode: data.DeviceCode,
  325. });
  326. } else {
  327. onClickItem(`DeviceTable-${data.Id}`, {
  328. // type: data.Status,
  329. deviceCode: data.DeviceCode,
  330. value: Number(data.Value || 0),
  331. threshold: data.JsonNumThreshold,
  332. });
  333. UnityAction.sendMsg('SinglePowerEnvironFromWeb', JSON.stringify(data));
  334. }
  335. };
  336. useEffect(() => {
  337. console.log('温控', items);
  338. if (isSensor)
  339. UnityAction.sendMsg('PowerEnvironsFromWeb', JSON.stringify(items));
  340. }, [items]);
  341. if (!isSensor) {
  342. columns.push({
  343. title: '操作',
  344. width: '12%',
  345. render: (record) =>
  346. record.Status == 1 && (
  347. <a style={{ color: '#7BFFFB' }} onClick={() => onClickError(record)}>
  348. 异常处理
  349. </a>
  350. ),
  351. });
  352. }
  353. return (
  354. <div>
  355. <Table
  356. columns={columns}
  357. dataSource={items}
  358. rowKey="Id"
  359. locale={{
  360. emptyText: <Empty />,
  361. }}
  362. />
  363. <ThresholdModal
  364. open={visible}
  365. data={currentItem.JsonNumThreshold}
  366. onClose={() => setVisible(false)}
  367. />
  368. <ErrorHandleModal
  369. open={errVisible}
  370. userList={userList}
  371. onCancel={() => setErrVisible(false)}
  372. onOk={handleError}
  373. />
  374. </div>
  375. );
  376. }
  377. function AalysisTable(props) {
  378. const { onClickItem, data = {}, select } = props;
  379. const { FaultAnalysis } = data;
  380. const columns = [
  381. {
  382. title: '异常名称',
  383. width: '18%',
  384. dataIndex: 'device_name',
  385. },
  386. // {
  387. // title: '位号',
  388. // width: '15%',
  389. // dataIndex: 'device_code',
  390. // },
  391. {
  392. title: '可能原因',
  393. width: '30%',
  394. render: (record) => record.reason,
  395. },
  396. {
  397. title: '解决方案',
  398. width: '52%',
  399. render: (record) => {
  400. if (record.fix_plan instanceof Array) {
  401. return (
  402. <div>
  403. {record.fix_plan.map((item) => (
  404. <div>
  405. {item.content}
  406. <br />
  407. </div>
  408. ))}
  409. </div>
  410. );
  411. } else {
  412. return record.fix_plan;
  413. }
  414. },
  415. },
  416. ];
  417. return (
  418. <div>
  419. <Table
  420. columns={columns}
  421. dataSource={FaultAnalysis}
  422. rowKey="device_code"
  423. locale={{
  424. emptyText: <Empty />,
  425. }}
  426. />
  427. </div>
  428. );
  429. }
  430. function ErrorHandleModal(props) {
  431. const { visible, onCancel, onOk, userList } = props;
  432. const [form] = Form.useForm();
  433. const status = form.getFieldValue('Status');
  434. const handleOk = () => {
  435. form.validateFields((error, values) => {
  436. if (error) return;
  437. onOk({
  438. ...values,
  439. PlanTime: values?.PlanTime?.format('YYYY-MM-DD HH:mm:ss'),
  440. });
  441. });
  442. };
  443. return (
  444. <Modal
  445. title="异常处理"
  446. open={visible}
  447. onCancel={onCancel}
  448. onOk={handleOk}
  449. destroyOnClose
  450. >
  451. <Form labelCol={{ span: 7 }} wrapperCol={{ span: 16 }}>
  452. <Form.Item label="异常处理备注" name="ExceptionHandling">
  453. <Input.TextArea />
  454. </Form.Item>
  455. <Form.Item
  456. label="审核状态"
  457. name="Status"
  458. rules={[{ required: true, message: '请选择验收状态' }]}
  459. >
  460. <Select style={{ width: '100%' }} placeholder="请选择验收状态">
  461. <Select.Option value={1}>已派遣</Select.Option>
  462. <Select.Option value={2}>已通过</Select.Option>
  463. </Select>
  464. </Form.Item>
  465. <Form.Item
  466. label="维修人"
  467. name="RepairMan"
  468. rules={[{ required: true, message: '请选择维修人' }]}
  469. >
  470. <Select
  471. showSearch
  472. placeholder="请选择维修人"
  473. filterOption={(input, option) =>
  474. option.props.children.indexOf(input) >= 0
  475. }
  476. style={{ width: '100%' }}
  477. >
  478. {userList?.map((item) => (
  479. <Select.Option key={item.ID}>{item.CName}</Select.Option>
  480. ))}
  481. </Select>
  482. </Form.Item>
  483. {status == 1 && (
  484. <>
  485. <Form.Item
  486. label="难度级别"
  487. name="DifficultyLevel"
  488. rules={[{ required: true, message: '请选择难度级别' }]}
  489. >
  490. <Select placeholder="请选择难度级别" style={{ width: '100%' }}>
  491. <Select.Option value={0}>大修</Select.Option>
  492. <Select.Option value={1}>项目维修</Select.Option>
  493. <Select.Option value={2}>小修</Select.Option>
  494. </Select>
  495. </Form.Item>
  496. <Form.Item
  497. label="维修方式"
  498. name="RepairType"
  499. rules={[{ required: true, message: '请选择维修方式' }]}
  500. >
  501. <Select placeholder="请选择维修方式" style={{ width: '100%' }}>
  502. <Select.Option value={0}>自维</Select.Option>
  503. <Select.Option value={1}>外委</Select.Option>
  504. </Select>
  505. </Form.Item>
  506. <Form.Item
  507. label="计划完成日期"
  508. name="PlanTime"
  509. rules={[{ required: true, message: '请选择计划完成日期' }]}
  510. >
  511. <DatePicker />
  512. </Form.Item>
  513. </>
  514. )}
  515. </Form>
  516. </Modal>
  517. );
  518. }
  519. export function WarningTable(props) {
  520. const {
  521. onClickItem,
  522. data = {},
  523. onErrorHandle,
  524. select,
  525. userList,
  526. type,
  527. items,
  528. } = props;
  529. const { ProjectId, Id } = data;
  530. const [loading, setLoading] = useState(false);
  531. const [visible, setVisible] = useState(false);
  532. const [errVisible, setErrVisible] = useState(false);
  533. const [currentItem, setCurrentItem] = useState({});
  534. const isSensor = type == 'sensor';
  535. const onClickThreshold = (record) => {
  536. setCurrentItem(record);
  537. setVisible(true);
  538. };
  539. const onClickError = (record) => {
  540. setCurrentItem(record);
  541. setErrVisible(true);
  542. };
  543. const handleError = async (values) => {
  544. setLoading(true);
  545. var res = await changeRecordStatus({
  546. ...values,
  547. Id: currentItem.Id,
  548. DeviceCode: currentItem.DeviceCode,
  549. DeviceName: currentItem.DeviceName,
  550. RecordId: data.Id,
  551. RepairMan: values.RepairMan * 1,
  552. });
  553. setLoading(false);
  554. if (res) {
  555. message.success('操作成功');
  556. setErrVisible(false);
  557. }
  558. };
  559. const columns = [
  560. {
  561. title: '设备名称',
  562. width: '25%',
  563. dataIndex: 'DeviceName',
  564. },
  565. {
  566. title: '巡检项',
  567. width: '20%',
  568. dataIndex: 'TemplateItem.Name',
  569. },
  570. // {
  571. // title: '设备位号',
  572. // width: '16%',
  573. // dataIndex: 'DeviceCode',
  574. // },
  575. {
  576. title: '设定值范围',
  577. width: '30%',
  578. render: (record) => (
  579. <ThresholdDetail
  580. current={record.Value || 0}
  581. data={record || {}}
  582. // onClick={() => onClickThreshold(record)}
  583. />
  584. ),
  585. },
  586. {
  587. title: '状态',
  588. width: '13%',
  589. dataIndex: 'Status',
  590. render: (Status) => {
  591. switch (Status) {
  592. case -1:
  593. case 0:
  594. return (
  595. <div>
  596. <i
  597. className={styles.iconStatus}
  598. style={{ background: '#12CEB3' }}
  599. ></i>
  600. 正常
  601. </div>
  602. );
  603. case 1:
  604. return (
  605. <div>
  606. <i
  607. className={styles.iconStatus}
  608. style={{ background: '#FE5850' }}
  609. ></i>
  610. 异常
  611. </div>
  612. );
  613. case 2:
  614. return (
  615. <div>
  616. <i
  617. className={styles.iconStatus}
  618. style={{ background: '#FFE26D' }}
  619. ></i>
  620. 警告
  621. </div>
  622. );
  623. }
  624. },
  625. },
  626. ];
  627. const handleClickItem = (data) => {
  628. if (!isSensor) {
  629. onClickItem(`DeviceTable-${data.Id}`, {
  630. type: data.Status,
  631. deviceCode: data.DeviceCode,
  632. });
  633. } else {
  634. onClickItem(`DeviceTable-${data.Id}`, {
  635. // type: data.Status,
  636. deviceCode: data.DeviceCode,
  637. value: Number(data.Value || 0),
  638. threshold: data.JsonNumThreshold,
  639. });
  640. UnityAction.sendMsg('SinglePowerEnvironFromWeb', JSON.stringify(data));
  641. }
  642. };
  643. if (!isSensor) {
  644. columns.push({
  645. title: '操作',
  646. width: '12%',
  647. render: (record) =>
  648. record.Status == 1 && (
  649. <a style={{ color: '#7BFFFB' }} onClick={() => onClickError(record)}>
  650. 异常处理
  651. </a>
  652. ),
  653. });
  654. }
  655. useEffect(() => {
  656. if (isSensor)
  657. UnityAction.sendMsg('PowerEnvironsFromWeb', JSON.stringify(items));
  658. }, [items]);
  659. return (
  660. <div>
  661. <Table
  662. columns={columns}
  663. dataSource={items}
  664. rowKey="Id"
  665. locale={{
  666. emptyText: <Empty />,
  667. }}
  668. />
  669. <ThresholdModal
  670. open={visible}
  671. data={currentItem.JsonNumThreshold}
  672. onClose={() => setVisible(false)}
  673. />
  674. <ErrorHandleModal
  675. open={errVisible}
  676. userList={userList}
  677. onCancel={() => setErrVisible(false)}
  678. onOk={handleError}
  679. />
  680. </div>
  681. );
  682. }
  683. function ReportCom(props) {
  684. const {
  685. sendMessageToUnity,
  686. select,
  687. waringData = [],
  688. allData = [],
  689. userList,
  690. type,
  691. title,
  692. data,
  693. } = props;
  694. const [activeKey, setActiveKey] = useState('1');
  695. const handleTabsChange = (activeKey) => {
  696. setActiveKey(activeKey);
  697. };
  698. return (
  699. <div className={styles.detailCard}>
  700. <div className={styles.tableTop}>
  701. {title}
  702. <Tabs
  703. style={{ float: 'right' }}
  704. defaultActiveKey="1"
  705. onChange={handleTabsChange}
  706. >
  707. <Tabs.TabPane
  708. tab={`异常/警告(${waringData.length || 0})`}
  709. key="1"
  710. ></Tabs.TabPane>
  711. <Tabs.TabPane
  712. tab={`全部(${allData.length || 0})`}
  713. key="2"
  714. ></Tabs.TabPane>
  715. </Tabs>
  716. </div>
  717. {activeKey == '1' && (
  718. <WarningTable
  719. onClickItem={sendMessageToUnity}
  720. select={select}
  721. items={waringData}
  722. key={type}
  723. data={data}
  724. type={type}
  725. userList={userList}
  726. />
  727. )}
  728. {activeKey == '2' && (
  729. <DeviceTable
  730. onClickItem={sendMessageToUnity}
  731. select={select}
  732. items={allData}
  733. data={data}
  734. key={type}
  735. type={type}
  736. userList={userList}
  737. />
  738. )}
  739. </div>
  740. );
  741. }
  742. function ReportDumCom(props) {
  743. const { data = [], title } = props;
  744. const columns = [
  745. {
  746. title: '报警时间',
  747. dataIndex: 'event_time',
  748. render: (time) => dayjs(time).format('YYYY-MM-DD HH:mm:ss'),
  749. },
  750. {
  751. title: '设备名称',
  752. dataIndex: 'device_name',
  753. },
  754. {
  755. title: '报警类型',
  756. dataIndex: 'event_type',
  757. // render: type => alarmType[type],
  758. },
  759. {
  760. title: '报警图片',
  761. render: (item) => (
  762. <ReactZmage
  763. controller={{
  764. // 关闭按钮
  765. close: true,
  766. // 旋转按钮
  767. rotate: true,
  768. // 缩放按钮
  769. zoom: false,
  770. // 下载按钮
  771. download: false,
  772. // 翻页按钮
  773. flip: false,
  774. // 多页指示
  775. pagination: false,
  776. }}
  777. backdrop="rgba(255,255,255,0.5)"
  778. style={{ height: '90px' }}
  779. src={item.url}
  780. />
  781. ),
  782. },
  783. ];
  784. return (
  785. <div>
  786. <div className={styles.tabBarExtraContent}>
  787. <div className={styles.text} style={{ height: 52, width: '60%' }}>
  788. <>
  789. <div>{title}</div>
  790. </>
  791. </div>
  792. <div className={styles.abnormal}>
  793. <div className={styles.text} style={{ float: 'right' }}>
  794. 异常({data?.length || 0})
  795. </div>
  796. </div>
  797. </div>
  798. <Table
  799. bordered
  800. rowKey="event_time"
  801. columns={columns}
  802. dataSource={data}
  803. locale={{
  804. emptyText: <Empty />,
  805. }}
  806. />
  807. </div>
  808. );
  809. }
  810. function base64ToImageUrl(base64String) {
  811. const byteCharacters = atob(base64String);
  812. const byteArrays = [];
  813. for (let i = 0; i < byteCharacters.length; i++) {
  814. byteArrays.push(byteCharacters.charCodeAt(i));
  815. }
  816. const byteArray = new Uint8Array(byteArrays);
  817. const blob = new Blob([byteArray], { type: 'image/png' });
  818. const imageUrl = URL.createObjectURL(blob);
  819. return imageUrl;
  820. }
  821. function Empty() {
  822. return (
  823. <div>
  824. <img
  825. src={require('@/assets/self-empty.png')}
  826. style={{ margin: '20px 0' }}
  827. />
  828. <p style={{ textAlign: 'center', color: '#555' }}>自检正常</p>
  829. </div>
  830. );
  831. }
  832. export default connect(({ eqSelfInspection }) => ({
  833. userList: eqSelfInspection.userList,
  834. mandateInfo: eqSelfInspection.mandateInfo,
  835. }))(Detail);