Files
fe_service_ebtp_frontend/src/pages/MonitorScreen/Home/index.tsx
jl-zhoujl2 2a69d4531d 8.25
2022-08-25 14:08:06 +08:00

620 lines
25 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Col, Descriptions, Radio, Row, Table } from 'antd';
import React, { useEffect, useRef, useState, useMemo } from 'react';
import './style.less';
import './china'
import * as echarts from 'echarts';
import warn_icon_01 from '@/assets/screen/warn_icon_01.png'
import warn_icon_02 from '@/assets/screen/warn_icon_02.png'
import crown_01 from '@/assets/screen/crown_01.png'
import crown_02 from '@/assets/screen/crown_02.png'
import crown_03 from '@/assets/screen/crown_03.png'
import moment from 'moment';
import { getApplicationData, getMonitorSample, getRoomProjectData, getTodayExpert, getTotalMapData, getWarnData } from './service';
import { debounce } from 'lodash';
import { RightCircleOutlined } from '@ant-design/icons';
import { history } from 'umi';
import ScreenVideoPlay from '@/components/Screen/ScreenVideoPlay';
export const onCell = (_: any, rowIndex: any) => ({ className: rowIndex % 2 == 0 ? "screen-table-odd-content" : "screen-table-even-content", });
export const onHeaderCell = () => ({ className: "screen-table-header", });
export const roomStatusMap = ["已预约", "评标中", "评标结束"];
const method = ["公开比选", "公开询价", "公开招募", "竞争性谈判", "单一来源"]
const zeroProvince = ["香港", "澳门", "台湾", "南海诸岛"];
export const proviceEnum = {
"0011": "北京",
"0012": "天津",
"0013": "河北",
"0014": "山西",
"0015": "内蒙古",
"0021": "辽宁",
"0022": "吉林",
"0023": "黑龙江",
"0031": "上海",
"0032": "江苏",
"0033": "浙江",
"0034": "安徽",
"0035": "福建",
"0036": "江西",
"0037": "山东",
"0041": "河南",
"0042": "湖北",
"0043": "湖南",
"0044": "广东",
"0045": "广西",
"0046": "海南",
"0050": "重庆",
"0051": "四川",
"0052": "贵州",
"0053": "云南",
"0054": "西藏",
"0061": "陕西",
"0062": "甘肃",
"0063": "青海",
"0064": "宁夏",
"0065": "新疆",
"001000": "集团"
}
const evalColumn: any[] = [
{
title: '省分',
dataIndex: 'provinceDictId',
key: 'provinceDictId',
align: 'center',
ellipsis: true,
onCell,
onHeaderCell,
render: (_: any, record: any) => proviceEnum[_],
},
{
title: '项目名称',
dataIndex: 'projectName',
key: 'projectName',
ellipsis: true,
onCell,
onHeaderCell,
},
{
title: '专家人数',
dataIndex: 'userNumber',
key: 'userNumber',
align: 'center',
onCell,
onHeaderCell,
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
align: 'center',
onCell,
onHeaderCell,
render: (_: any, record: any) => roomStatusMap[_],
},
];
/**
* 当前时间
* @returns
*/
export const LocalTime = () => {
const [time, setTime] = useState<string>('');
useEffect(() => {
const interval = setInterval(function () {
const date = moment().format("YYYY-MM-DD HH:mm:ss");
setTime(date);
}, 1000);
return () => {
clearInterval(interval)
};
}, [])
return (
<span>{time}</span>
)
}
const projectClick = () => {
history.push("/MonitorScreen/MonitorRoom");
}
const GraphChart = (props: { type: string, chartData: any[] }) => {
const { type, chartData } = props;
const random = Math.random().toString();
useEffect(() => {
type EChartsOption = echarts.EChartsOption;
const chartDom = document.getElementById(random)!;
const myChart = echarts.init(chartDom);
const categoryOption: EChartsOption = {
legend: { top: '3%', textStyle: { color: '#fff' } },
tooltip: {},
grid: { left: '8%', right: '4%', top: '16%', bottom: 60 },
dataset: {
source: chartData
},
xAxis: { type: 'category', axisLabel: { interval: 0, color: '#fff' } },
yAxis: { axisLabel: { color: '#fff' } },
// Declare several bar series, each will be mapped
// to a column of dataset.source by default.
series: [{ type: 'bar' }, { type: 'bar' }]
};
const pieOption: EChartsOption = {
legend: {
orient: 'vertical',
right: '3%',
top: 'middle',
textStyle: { color: '#fff' }
},
tooltip: {},
dataset: {
source: chartData
},
series: [
{
type: 'pie',
radius: ['40%', '70%'],
center: ['15%', '50%'],
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: true,
fontSize: '20px',
fontWeight: 'bold',
color: '#fff'
}
},
encode: {
itemName: 'product',
value: 'reserve'
}
},
{
type: 'pie',
radius: ['40%', '70%'],
center: ['45%', '50%'],
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: true,
fontSize: '20px',
fontWeight: 'bold',
color: '#fff'
}
},
encode: {
itemName: 'product',
value: 'ing'
}
},
{
type: 'pie',
radius: ['40%', '70%'],
center: ['75%', '50%'],
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: true,
fontSize: '20px',
fontWeight: 'bold',
color: '#fff'
}
},
encode: {
itemName: 'product',
value: 'end'
}
}
]
};
const mapOption: EChartsOption = {
tooltip: {
trigger: 'item',
showDelay: 0,
transitionDuration: 0.2,
padding: [8, 12],
formatter: (params: any) => {
return `<div style="display:flex;justify-content:flex-start;align-items:center;font-weight:600">
<div style="width:8px;height:8px;border-radius:8px;background:#FFCC00;margin-right:8px"></div>${params.data?.name}
</div>
<div style="margin-top:8px;margin-left:16px;">
评标室间数:${params.data?.placeNumber}
</div>
<div style="margin-top:8px;margin-left:16px;">
正在评标:${params.data?.openingNumber}
</div>
<div style="margin-top:8px;margin-left:16px;">
评标专家:${params.data?.expertNumber}
</div>
<div style="margin-top:8px;margin-left:16px;">
累计评标次数:${params.data?.cumulativePlaceNumber}
</div>`
}
},
visualMap: {
show: false,
min: 0,
max: 1000,
inRange: {
color: [
'#ECF3F4',
'#E3F4FF',
'#D1ECFD',
'#A0D6F8',
'#5DCAF2',
'#09A6F7',
'#0081C4',
'#26519E',
'#052D74',
'#001F53',
]
},
},
series: [
{
name: '累计评标次数',
type: 'map',
roam: false,
zoom: 1.2,
left: "20%",
map: 'china',
emphasis: {
label: {
show: false,
},
itemStyle: {
areaColor: '#FFCC00'
}
},
select: {
disabled: true,
},
data: chartData,
}
]
};
const option: EChartsOption = type == "pie" ? pieOption : type == "map" ? mapOption : categoryOption;
myChart.setOption(option);
const resize = () => {
myChart && myChart.resize();
};
window.addEventListener("resize", debounce(() => resize(), 100));
return () => {
window.removeEventListener("resize", debounce(() => resize(), 100));
}
}, [chartData])
return (
<div id={random} style={{ width: '100%', height: '100%', zIndex: 99 }}></div>
)
}
//异常预警
export const EarlyWarn = ({ img, name, num }: { img: string, name: string, num: string | number }) => {
return (
<div className='screen-warn-content'>
<img src={img} className='screen-warn-img' />
<div className='screen-warn-content'>
<div className='screen-warn-num'>{num}</div>
<div>{name}</div>
</div>
</div>
)
}
export default () => {
//中心地图及周边数据
const [centerMapData, setCenterMapData] = useState<any>();
//异常预警数据
const [earlyWarnData, setEarlyWarnData] = useState<any>();
//评标室项目数据
const [bidProjectData, setBidProjectData] = useState<any[]>([]);
//今日评标专家数量数据
const [todayExpertNum, setTodayExpertNum] = useState<any[]>([]);
//评标室应用情况数据
const [evalApplData, setEvalApplData] = useState<any[]>([]);
//当前播放的设备参数
const [cameraParams, setCameraParams] = useState<any>();
//当月&年转换
const [radioSelect, setRadioSelect] = useState<string>("1");
//评标室监控示例数据
const [sampleData, setSampleData] = useState<any>();
//监控视频Ref
const videoRef = useRef<any>();
//定时刷新间隔
const _time = 10000;
const categoryChart = useMemo(() => {
return todayExpertNum.length > 0 && <GraphChart type="category" chartData={todayExpertNum} />
}, [todayExpertNum])
const pieChart = useMemo(() => {
return evalApplData.length > 0 && <GraphChart type="pie" chartData={evalApplData} />
}, [evalApplData])
const mapChart = useMemo(() => {
return centerMapData?.list && <GraphChart type="map" chartData={centerMapData?.list} />
}, [centerMapData])
//星期&年切换
const onRadioChange = (e: any) => {
const type = e.target.value;
setRadioSelect(type);
getEvalApplData(type);
}
//获取中间地图数据
const getMapList = () => {
getTotalMapData({ pageNo: 1, pageSize: 10 }).then(res => {
if (res?.code == 200) {
const data = res?.data;
for (const ite of data.list) {
ite["name"] = ite.provinceDictName;
ite["value"] = ite.placeNumber;
}
for (const name of zeroProvince) {//有些省市字典没有默认放0
data.list.push({ name: name, value: 0, cumulativePlaceNumber: 0, expertNumber: 0, openingNumber: 0, placeNumber: 0 });
}
setCenterMapData(data);
}
})
}
//获取异常预警数据
const getWarnInfo = () => {
getWarnData().then(res => {
if (res?.code == 200) {
const data = res?.data;
setEarlyWarnData(data);
}
})
}
//获取评标室项目情况
const getEvaRoomData = () => {
getRoomProjectData({ pageNo: 1, pageSize: 20 }).then(res => {
if (res?.code == 200) {
const data = res?.data;
setBidProjectData(data);
}
})
}
//获取今日评标专家数量
const getExpertNumber = () => {
getTodayExpert().then(res => {
if (res?.code == 200) {
const data = res?.data;
let formatData = [['product', '专家人数', '专家签到数量'], ['招标项目', data[0].number + data[1].number, data[0].signNumber + data[1].signNumber]];
for (const ite of data) {
if (method.includes(ite.bidMethod)) {
formatData.push([ite.bidMethod, ite.number, ite.signNumber]);
}
}
setTodayExpertNum(formatData);
}
})
}
//获取评标室应用情况
const getEvalApplData = (type: string) => {
getApplicationData({ type }).then(res => {
if (res?.code == 200) {
const data = res?.data;
let formatData = [['product', 'reserve', 'ing', 'end'], ['招标项目', String(Number(data.reserveNumber[0].number) + Number(data.reserveNumber[1].number)), String(Number(data.ingNumber[0].number) + Number(data.ingNumber[1].number)), String(Number(data.endNumber[0].number) + Number(data.endNumber[1].number))]];
let comp = ['公开比选'];
let inqu = ['公开询价'];
let rect = ['公开招募'];
let nego = ['竞争性谈判'];
let only = ['单一来源'];
for (const ite of data.reserveNumber) {
if (ite.bidMethod == method[0]) {
comp.push(ite.number)
} else if (ite.bidMethod == method[1]) {
inqu.push(ite.number)
} else if (ite.bidMethod == method[2]) {
rect.push(ite.number)
} else if (ite.bidMethod == method[3]) {
nego.push(ite.number)
} else if (ite.bidMethod == method[4]) {
only.push(ite.number)
}
}
for (const ite of data.ingNumber) {
if (ite.bidMethod == method[0]) {
comp.push(ite.number)
} else if (ite.bidMethod == method[1]) {
inqu.push(ite.number)
} else if (ite.bidMethod == method[2]) {
rect.push(ite.number)
} else if (ite.bidMethod == method[3]) {
nego.push(ite.number)
} else if (ite.bidMethod == method[4]) {
only.push(ite.number)
}
}
for (const ite of data.endNumber) {
if (ite.bidMethod == method[0]) {
comp.push(ite.number)
} else if (ite.bidMethod == method[1]) {
inqu.push(ite.number)
} else if (ite.bidMethod == method[2]) {
rect.push(ite.number)
} else if (ite.bidMethod == method[3]) {
nego.push(ite.number)
} else if (ite.bidMethod == method[4]) {
only.push(ite.number)
}
}
formatData.push(comp);
formatData.push(inqu);
formatData.push(rect);
formatData.push(nego);
formatData.push(only);
setEvalApplData(formatData);
}
})
}
//获取首页评标室监控示例
const getSampleData = () => {
getMonitorSample().then(res => {
if (res?.code == 200) {
const data = res?.data;
if (data) {
setSampleData(data);
setCameraParams(data?.devicePageVOList[0].platform);
setTimeout(() => {
videoRef.current?.play(data?.devicePageVOList[0].deviceCode);
}, 4000);
}
}
})
}
useEffect(() => {
getMapList();
getWarnInfo();
getEvaRoomData();
getExpertNumber();
getEvalApplData("1");
getSampleData();
}, [])
//定时器
useEffect(() => {
const interval = setInterval(function () {
getWarnInfo();
getEvaRoomData();
}, _time);
return () => {
clearInterval(interval)
};
}, [])
return (
<div className="screen-bg">
<div className='top-block'>
</div>
<div className='top'>
<Row>
<Col span={6}>
<div className='top-left'>
</div>
</Col>
<Col span={12}>
<div className='top-title'>
<span></span>
</div>
</Col>
<Col span={6}>
<div className='top-right'>
<LocalTime />
</div>
</Col>
</Row>
</div>
<Row className="screen-content">
<Col span={18}>
<Row>
<Col span={8} className='screen-right4'>
<div className='screen-card'>
<div className='card-title'>
<span></span>
</div>
{categoryChart}
</div>
<div className='screen-card screen-top8'>
<div className='card-title'>
<span></span>
</div>
{
sampleData && <>
<div className='card-project-content'>
<p>{sampleData?.projectName}</p>
<p>{sampleData?.sectionName}</p>
<p>{sampleData?.devicePageVOList[0].deviceName}</p>
<p><span>{sampleData?.areaName}</span><span></span></p>
</div>
<div className='card-carema-c'>
{cameraParams && <ScreenVideoPlay videoRef={videoRef} cameraParams={cameraParams} status={0} />}
</div>
</>
}
</div>
</Col>
<Col span={16} className='screen-left4 screen-right4'>
<div className='middle-c'>
<div className='map-bg'>
{mapChart}
<div className='map-total-num'>
<span>{centerMapData?.number}</span>
</div>
<div className='map-grand'>
<div className='map-grand-title'></div>
<div>
<img src={crown_01} className='map-grand-img' />
<span>{centerMapData?.list?.[0].name}{centerMapData?.list?.[0].cumulativePlaceNumber}</span>
</div>
<div className='map-grand-content'>
<img src={crown_02} className='map-grand-img' />
<span>{centerMapData?.list?.[1].name}{centerMapData?.list?.[1].cumulativePlaceNumber}</span>
</div>
<div className='map-grand-content'>
<img src={crown_03} className='map-grand-img' />
<span>{centerMapData?.list?.[2].name}{centerMapData?.list?.[2].cumulativePlaceNumber}</span>
</div>
</div>
</div>
</div>
</Col>
<Col span={24} className='screen-top8 screen-right4'>
<div className='screen-card'>
<div className='screen-graph-top'>
<div className='screen-graph-left-title'>
</div>
{evalApplData.length > 0 && <Radio.Group buttonStyle="solid" size='small' value={radioSelect} onChange={onRadioChange}>
<Radio.Button value="1"></Radio.Button>
<Radio.Button value="2"> </Radio.Button>
</Radio.Group>}
</div>
<div className='screen-graph-chart'>
{pieChart}
</div>
<div className='screen-graph-bottom'>
<span className='screen-graph-title'></span>
<span className='screen-graph-title'></span>
<span className='screen-graph-title'></span>
</div>
{/* <div className='screen-graph-end'>
<span>&gt;&gt;项目列表</span>
</div> */}
</div>
</Col>
</Row>
</Col>
<Col span={6} className='screen-left4'>
<div className='screen-card'>
<div className='card-title'>
<span></span>
</div>
<div className='screen-warn'>
<EarlyWarn name="陌生人预警" img={warn_icon_01} num={earlyWarnData?.strangerCount ? earlyWarnData?.strangerCount : 0} />
<EarlyWarn name="评标室人数预警" img={warn_icon_02} num={earlyWarnData?.numberCount ? earlyWarnData?.numberCount : 0} />
</div>
</div>
<div className='screen-card screen-card-double screen-top8'>
<div className='card-title'>
<span><RightCircleOutlined style={{ marginLeft: "0.5rem", cursor: 'pointer' }} onClick={() => projectClick()} /></span>
</div>
{bidProjectData.length > 0 && <Table
pagination={false}
className="screen-table"
rowKey="id"
size="small"
dataSource={bidProjectData}
columns={evalColumn}
/>}
</div>
</Col>
</Row>
</div>
);
};;