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 styles from './index.module.less'
import { message, Spin } from "antd"; import { message, Spin } from "antd";
import EZUIKit from 'ezuikit-js'; import EZUIKit from 'ezuikit-js';
import moment from 'moment';
/** /**
* 海康视频H5插件视频播放 * 海康视频H5插件视频播放
@ -23,10 +24,16 @@ const HFivePlayer = ({ wsUrl, playerID, size }) => {
const initVideo = () => { 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({ playerYsy.current = new EZUIKit.EZUIKitPlayer({
id: 'player' + playerID, // 视频容器ID id: 'player' + playerID, // 视频容器ID
accessToken: wsUrl.src, accessToken: wsUrl.src,
url: `ezopen://open.ys7.com/${wsUrl.indexCode}/1.live`, url: ezUrl,
// plugin: ["talk"], // 加载插件talk-对讲 // plugin: ["talk"], // 加载插件talk-对讲
width: parentRef.current?.offsetWidth, width: parentRef.current?.offsetWidth,
height: parentRef.current?.offsetHeight, height: parentRef.current?.offsetHeight,
@ -133,6 +140,13 @@ const HFivePlayer = ({ wsUrl, playerID, size }) => {
if (wsUrl?.src) { if (wsUrl?.src) {
setIsLoading(true) //开始加载 setIsLoading(true) //开始加载
let preUrl = wsUrl?.src // 播放地址 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); console.log(preUrl);
const param = { const param = {
playURL: preUrl, playURL: preUrl,

View File

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

View File

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

View File

@ -1,78 +1,128 @@
import React, { Fragment, useRef, useMemo,useEffect,useState } from 'react'; import React, { Fragment, useRef, useMemo, useEffect, useState } from 'react';
import { Table, Card,Modal,Form,Input,Button,Row,Pagination,message } from 'antd'; import { Table, Card, Modal, Form, Input, Button, Row, Pagination, message } from 'antd';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import ToolBar from './toolbar'; import ToolBar from './toolbar';
import apiurl from '../../../service/apiurl'; import apiurl from '../../../service/apiurl';
import usePageTable from '../../../components/crud/usePageTable3'; import usePageTable from '../../../components/crud/usePageTable3';
import { createCrudService } from '../../../components/crud/_'; import { createCrudService } from '../../../components/crud/_';
import { httpget2,httppost2 } from '../../../utils/request';
import HFivePlayer from '../../../components/video1Plary';
import CardShow from "./Card" import CardShow from "./Card"
import "./index.less" import "./index.less"
const Page = () => { import moment from 'moment';
const role = useSelector(state => state.auth.role); const Page = (props) => {
const tm = props?.tm;
const [searchVal, setSearchVal] = useState(false) const statusObj = {
const { tableProps, search, refresh } = usePageTable(createCrudService(apiurl.spjk1.aiWarn.page1).find_noCode); 0: "未处理",
const onchange = (page, pageSize) => { 1: '已处理'
const obj = { };
pageNumber: page, const levelStatus = {
pageSize: pageSize, 1: '低',
} 2: '中',
const searchParams = { 3: '高'
...obj,
// search: {
// ...searchVal,
// }
}
search(searchParams)
} }
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 width = useMemo(() => columns.reduce((total, cur) => total + (cur.width), 0), [columns]);
const replay = (r) => {
setReplayOpen(true);
setReplayItem(r)
getVideoSrc(r)
}
const getVideoSrc = async(data) => {
try {
// 仅获取播放地址,回放时间由播放器拼接
const res = await httpget2(`${apiurl.gsxl.zfzl.videosrc}${data.resIndexCode}`)
setVideoSrc(res.data)
} catch (error) {
console.log(error);
}
}
// 当外部传入 tm[start,end])时,初始化查询条件
useEffect(() => { useEffect(() => {
if (searchVal) { if (tm && Array.isArray(tm) && tm[0]) {
const params = { 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(() => {
const params = {
search: { search: {
...searchVal, ...searchVal,
} }
}; };
search(params) // 避免初始化时触发空查询造成二次请求闪烁
if (searchVal !== false) {
search(params)
} }
}, [searchVal]) }, [searchVal])
return ( 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%" }}> <div className='lf CrudAdcdTreeTableBox' style={{ width: "100%" }}>
<Card className='nonebox'> <Card className='nonebox'>
<ToolBar <ToolBar
setSearchVal={setSearchVal} setSearchVal={setSearchVal}
role={role} role={role}
tm={tm}
/> />
</Card> </Card>
<div className="ant-card-body" style={{padding:"20px 0 0 0",display:"flex",flexWrap:"wrap",maxHeight:650,overflowY:"auto"}}> <div className="ant-card-body" style={{ padding: "20px 0 0 0" }}>
{ <Table columns={columns} rowKey="inx" {...tableProps} scroll={{ x: width, y: "calc( 100vh - 500px )" }} />
tableProps.dataSource.length > 0 ?
tableProps.dataSource.map((item,i) => {
return (
<div key={i} >
<CardShow
record={item}
/>
</div>
)
}) : null
}
</div> </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>
</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> </div>
</> </>
); );

View File

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