feat():ai告警页面更改

master
李神峰 2025-11-13 16:58:52 +08:00
parent 09ee59d002
commit e767f8648a
5 changed files with 137 additions and 93 deletions

View File

@ -2,6 +2,7 @@ import { FC, useEffect, useRef, useState } from 'react'
// import styles from './index.module.less'
import { message, Spin } from "antd";
import EZUIKit from 'ezuikit-js';
import moment from 'moment';
/**
* 海康视频H5插件视频播放
@ -23,10 +24,16 @@ const HFivePlayer = ({ wsUrl, playerID, size }) => {
const initVideo = () => {
const hasReplayRange = wsUrl?.beginTime && wsUrl?.endTime && wsUrl?.indexCode;
const beginStr = hasReplayRange ? moment(wsUrl.beginTime).format('YYYYMMDDHHmmss') : '';
const endStr = hasReplayRange ? moment(wsUrl.endTime).format('YYYYMMDDHHmmss') : '';
const ezUrl = hasReplayRange
? `ezopen://open.ys7.com/${wsUrl.indexCode}/1.rec?begin=${beginStr}&end=${endStr}`
: `ezopen://open.ys7.com/${wsUrl.indexCode}/1.live`;
playerYsy.current = new EZUIKit.EZUIKitPlayer({
id: 'player' + playerID, // 视频容器ID
accessToken: wsUrl.src,
url: `ezopen://open.ys7.com/${wsUrl.indexCode}/1.live`,
url: ezUrl,
// plugin: ["talk"], // 加载插件talk-对讲
width: parentRef.current?.offsetWidth,
height: parentRef.current?.offsetHeight,
@ -133,6 +140,13 @@ const HFivePlayer = ({ wsUrl, playerID, size }) => {
if (wsUrl?.src) {
setIsLoading(true) //开始加载
let preUrl = wsUrl?.src // 播放地址
// 支持回放时间范围(海康取流地址若后端支持时间参数则追加)
if (wsUrl?.beginTime && wsUrl?.endTime) {
const begin = moment(wsUrl.beginTime).subtract(1,'hours').format('YYYY-MM-DD HH:mm:ss');
const end = moment(wsUrl.endTime).format('YYYY-MM-DD HH:mm:ss');
const sep = preUrl.includes('?') ? '&' : '?';
preUrl = `${preUrl}${sep}beginTime=${begin}&endTime=${end}`;
}
console.log(preUrl);
const param = {
playURL: preUrl,

View File

@ -121,7 +121,7 @@ const Page = ({ mySetTms }) => {
{ key==='位移告警'?<Table_wy data={dataObj.shiftWarn} onCancel={()=>setOpen(false)}/>:null }
{ key==='渗压告警'?<Table_sy data={dataObj.pressWarn} onCancel={()=>setOpen(false)}/>:null }
{ key==='渗流告警'?<Table_sl data={dataObj.flowWarn} onCancel={()=>setOpen(false)}/>:null }
{key === 'AI告警' ? <Table_AI /> : null}
{key === 'AI告警' ? <Table_AI tms={tms} /> : null}
{ key==='白蚁告警'?<Table_by data={dataObj.byWarn} onCancel={()=>setOpen(false)}/>:null }
</div>
</Modal>

View File

@ -10,7 +10,7 @@ import apiurl from "../../../../service/apiurl";
import moment from 'moment';
const Page = () => {
const Page = ({ tms }) => {
// const { tableProps, search, refresh } = usePageTable(createCrudService('/gunshiApp/tsg/rescue/goods/page/query').find_noCode,{});
@ -23,7 +23,7 @@ const Page = () => {
return (
<div className="ant-card-body" style={{padding:"0 10px",height:'600px',overflowY:'auto'}}>
<AiWarn/>
<AiWarn tm={tms}/>
{/* <div>时间:{moment().format('YYYY-MM-DD HH:mm:ss')} 至 {moment().format('YYYY-MM-DD HH:mm:ss')}</div> */}
</div>
)

View File

@ -1,78 +1,128 @@
import React, { Fragment, useRef, useMemo,useEffect,useState } from 'react';
import { Table, Card,Modal,Form,Input,Button,Row,Pagination,message } from 'antd';
import React, { Fragment, useRef, useMemo, useEffect, useState } from 'react';
import { Table, Card, Modal, Form, Input, Button, Row, Pagination, message } from 'antd';
import { useSelector } from 'react-redux';
import ToolBar from './toolbar';
import apiurl from '../../../service/apiurl';
import usePageTable from '../../../components/crud/usePageTable3';
import { createCrudService } from '../../../components/crud/_';
import { httpget2,httppost2 } from '../../../utils/request';
import HFivePlayer from '../../../components/video1Plary';
import CardShow from "./Card"
import "./index.less"
const Page = () => {
import moment from 'moment';
const Page = (props) => {
const tm = props?.tm;
const statusObj = {
0: "未处理",
1: '已处理'
};
const levelStatus = {
1: '低',
2: '中',
3: '高'
}
const role = useSelector(state => state.auth.role);
const columns = [
{ title: '序号', key: 'inx', dataIndex: 'inx', width: 60, align: "center" },
{ title: '事件源名称', key: 'resName', dataIndex: 'resName', width: 150, ellipsis: true },
{
title: '事件类型名称', key: 'eventTypeName', dataIndex: 'eventTypeName', width: 150, ellipsis: true
},
{ title: '事件处理状态', key: 'handleStatus', dataIndex: 'handleStatus', width: 120, render: (v) => <span>{statusObj[v]}</span> },
{ title: '事件等级', key: 'eventLevel', dataIndex: 'eventLevel', width: 100, render: (v) => <span>{levelStatus[v]}</span> },
{ title: '事件开始时间', key: 'startTime', dataIndex: 'startTime', width: 150, render: (v) => <span>{v ? moment(v).format("YYYY-MM-DD HH:mm:ss") : ''}</span> },
{ title: '事件结束时间', key: 'endTime', dataIndex: 'endTime', width: 150, render: (v) => <span>{v ? moment(v).format("YYYY-MM-DD HH:mm:ss") : ''}</span> },
// {
// title: '操作', key: 'opr', dataIndex: 'opr', width: 100, align: "center",
// render:(v,r)=><Button type="link" onClick={()=>replay(r)}>回放</Button>
// },
];
const [searchVal, setSearchVal] = useState(false)
const [replayOpen, setReplayOpen] = useState(false)
const [replayItem, setReplayItem] = useState({})
const [videoSrc, setVideoSrc] = useState('')
const { tableProps, search, refresh } = usePageTable(createCrudService(apiurl.spjk1.aiWarn.page1).find_noCode);
const onchange = (page, pageSize) => {
const obj = {
pageNumber: page,
pageSize: pageSize,
const width = useMemo(() => columns.reduce((total, cur) => total + (cur.width), 0), [columns]);
const replay = (r) => {
setReplayOpen(true);
setReplayItem(r)
getVideoSrc(r)
}
const searchParams = {
...obj,
// search: {
// ...searchVal,
// }
const getVideoSrc = async(data) => {
try {
// 仅获取播放地址,回放时间由播放器拼接
const res = await httpget2(`${apiurl.gsxl.zfzl.videosrc}${data.resIndexCode}`)
setVideoSrc(res.data)
} catch (error) {
console.log(error);
}
search(searchParams)
}
// 当外部传入 tm[start,end])时,初始化查询条件
useEffect(() => {
if (tm && Array.isArray(tm) && tm[0]) {
setSearchVal({
startTime: moment(tm[0]).format('YYYY-MM-DD HH:mm:ss'),
})
}
}, [tm])
// 路由直接进入 AI 告警页(没有 tm初始化一次默认查询
useEffect(() => {
if (!tm) {
setSearchVal({})
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
useEffect(() => {
if (searchVal) {
const params = {
search: {
...searchVal,
}
};
// 避免初始化时触发空查询造成二次请求闪烁
if (searchVal !== false) {
search(params)
}
}, [searchVal])
return (
<>
<div className='content-root clearFloat xybm' style={{paddingRight:"0",paddingBottom:"0"}}>
<div className='content-root clearFloat xybm' style={{ paddingRight: "0", paddingBottom: "0" }}>
<div className='lf CrudAdcdTreeTableBox' style={{ width: "100%" }}>
<Card className='nonebox'>
<ToolBar
setSearchVal={setSearchVal}
role={role}
tm={tm}
/>
</Card>
<div className="ant-card-body" style={{padding:"20px 0 0 0",display:"flex",flexWrap:"wrap",maxHeight:650,overflowY:"auto"}}>
{
tableProps.dataSource.length > 0 ?
tableProps.dataSource.map((item,i) => {
return (
<div key={i} >
<CardShow
record={item}
/>
</div>
)
}) : null
}
</div>
<div style={{textAlign: "right", marginTop: "25px",marginRight:40}}>
<Pagination
current={tableProps.pagination.current}
total={tableProps.pagination.total}
showTotal={tableProps.pagination.showTotal}
pageSize={tableProps.pagination.pageSize}
showSizeChanger
showQuickJumper
pageSizeOptions={[4, 8, 12, 16]}
onChange={onchange}
/>
<div className="ant-card-body" style={{ padding: "20px 0 0 0" }}>
<Table columns={columns} rowKey="inx" {...tableProps} scroll={{ x: width, y: "calc( 100vh - 500px )" }} />
</div>
</div>
<Modal
open={replayOpen}
width={800}
title={replayItem?.resName}
onCancel={() => setReplayOpen(false)}
footer={null}
destroyOnClose
>
<div style={{ width: "100%", height: 500 }}>
<HFivePlayer
size={1}
wsUrl={{
src: videoSrc,
indexCode: replayItem?.resIndexCode,
beginTime: replayItem?.startTime,
endTime: replayItem?.endTime,
}}
playerID={replayItem?.id} />
</div>
</Modal>
</div>
</>
);

View File

@ -5,26 +5,22 @@ import moment from 'moment';
import { httppost2 } from '../../../utils/request';
import apiurl from '../../../service/apiurl';
const { RangePicker } = DatePicker;
const ToolBar = ({ setSearchVal, onSave, storeData, role }) => {
const ToolBar = ({ setSearchVal, onSave, storeData, role, tm }) => {
const searchBtn = role?.rule?.find(item => item.menuName == "查询")|| true;
const warnTypes = [
{
label: "人员闯入",
label: "",
value:1
},
{
label: "工程车辆识别",
label: "",
value:2
},
{
label: "漂浮物识别",
label: "",
value:3
},
{
label: "游泳识别",
value:4
},
}
]
const [form] = Form.useForm();
const [codeList, setCodeList] = useState([])
@ -38,46 +34,30 @@ const ToolBar = ({ setSearchVal, onSave, storeData, role }) => {
}
}
const onFinish = (values) => {
let dateSo;
if (values.tm) {
dateSo = {
start: moment(values.tm[0]).format('YYYY-MM-DD 00:00:00'),
end: moment(values.tm[1]).format('YYYY-MM-DD 23:59:59')
if (values.startTime) {
values.startTime = moment(values.startTime).format('YYYY-MM-DD HH:mm:ss');
}
setSearchVal({...values});
}
delete values.tm
setSearchVal({...values, dateTimeRangeSo:dateSo});
}
// 预填开始时间(来自外部 tm
useEffect(() => {
getStationCode()
let time = [moment().subtract(1,"weeks"),moment()]
let dateSo = {
start:moment(time[0]).format('YYYY-MM-DD 00:00:00'),
end:moment(time[1]).format('YYYY-MM-DD 23:59:59'),
if (tm && Array.isArray(tm) && tm[0]) {
form.setFieldsValue({ startTime: moment(tm[0]) })
}
form.setFieldValue("tm",time)
setSearchVal({dateTimeRangeSo:dateSo})
}, [])
}, [tm])
return (
<>
<div style={{display:'flex',justifyContent:'space-between'}}>
<Form form={form} className='toolbarBox' layout="inline" onFinish={onFinish} >
<Form.Item label="告警日期" name="tm">
<RangePicker
<Form.Item label="事件开始时间" name="startTime">
<DatePicker
allowClear
style={{ width: "350px" }}
format="YYYY-MM-DD"
style={{ width: "200px" }}
showTime
placeholder='请选择时间'
/>
</Form.Item>
<Form.Item label="告警点位" name="indexCode">
<NormalSelect
allowClear
style={{ width: "150px" }}
options={codeList}
/>
</Form.Item>
<Form.Item label="告警类型" name="type">
<Form.Item label="事件等级" name="eventLevel">
<NormalSelect
allowClear
style={{ width: "150px" }}