feat(): 日到报率详情开发

master
李神峰 2025-02-12 17:49:39 +08:00
parent 95b642324e
commit 92b4c8e1bc
6 changed files with 460 additions and 135 deletions

View File

@ -12,6 +12,8 @@
"craco-less": "^3.0.1",
"crypto-js": "^4.2.0",
"dayjs": "^1.11.12",
"echarts": "^5.6.0",
"echarts-for-react": "^3.0.2",
"http-proxy-middleware": "^2.0.6",
"moment": "^2.30.1",
"ol": "^9.1.0",

View File

@ -0,0 +1,96 @@
export default function dblOption(sellist,selday) {
let itemStyle = {
normal: {
label: {
show: true,
formatter: '{c}%'
},
labelLine: {
show: true
}
}
}
const seriesData = [];
const serdata = {
type: 'bar',
itemStyle: itemStyle,
data: []
};
const yAxisData = [];
for (let si = 0; si < sellist.length; si++) {
let obj = sellist[si];
yAxisData.push(obj.adnm);
serdata.data.push(parseFloat((obj[selday]?.rate * 100).toFixed(2)));
}
if (serdata.data.length > 0) {
seriesData.push(serdata);
}
return {
title: {
text: '日到报率详情',
textStyle: {
fontSize: 16
},
left: 'center'
},
color: ['#1fbcd2', '#a23746'],
tooltip: {
formatter: '{b}, {c}%'
},
grid: {
left:'12%'
},
legend: { data: [] },
toolbox: {
show: true,
orient: 'vertical',
feature: {
dataZoom: {
show: true,
title: {
zoom: '区域缩放',
back: '区域缩放还原',
}
},
magicType: {
show: true,
title: {
line: '切换为折线图',
bar: '切换为柱状图',
},
type: ['line', 'bar']
},
restore: {
show: true,
title: '还原'
},
saveAsImage: {
show: true,
title: '保存为图片'
}
}
},
xAxis: {
type: 'value',
axisLabel: {
formatter: '{value}%'
},
axisLine: {
show: true,
},
axisTick: {
show: true,
}
},
yAxis: {
type: 'category',
data: yAxisData,
axisLabel: {
interval: 0
}
},
series: seriesData
}
}

View File

@ -5,6 +5,9 @@ import ToolBottom from './toolBottom.js';
import apiurl from '../../../models/apiurl'
import { xyt_httpget2 } from "../../../utils/request"
import { resJson } from './res.js';
import ReactEcharts from 'echarts-for-react';
import qsOption from './qsOption.js'
import dblOption from './dblOption.js'
import moment from 'moment';
export default function Czrz() {
const [newColumns, setNewColumns] = useState([])
@ -15,6 +18,25 @@ export default function Czrz() {
const [loading, setLoading] = useState(false)
const [SearchBottom, setSearchBottom] = useState(false)
const [adcdList, setAdcdList] = useState([])
const [dayArr, setDayArr] = useState([])
const [hbobj, setHbobj] = useState()
const [selectedObject, setSelectObject] = useState()
const [subTableData, setSubTableData] = useState([])
const qsoptions = useMemo(() => {
if (dayArr.length > 0 && selectedObject) {
return qsOption(dayArr, selectedObject, hbobj)
} else {
return {}
}
}, [dayArr, selectedObject, hbobj])
const dblOptions = useMemo(() => {
if (subTableData.length > 0 && SearchBottom) {
return dblOption(subTableData, SearchBottom.operTime)
} else {
return {}
}
}, [subTableData, SearchBottom])
const columns = [
{
title: '序号',
@ -47,6 +69,16 @@ export default function Czrz() {
},
]
const detailsColumns = [
{
title: '序号',
dataIndex: 'inx',
key: 'inx',
width: 80,
align: "center",
render: (text, record, index) => {
return <span>{index + 1}</span>
}
},
{
title: '行政区划',
dataIndex: 'adnm',
@ -64,10 +96,10 @@ export default function Czrz() {
width: 100,
align: "center",
render: (text, record) => (
<span>
{record[SearchBottom?.operTime]?.ontpcnt?.pp}
</span>
)
<span>
{record[SearchBottom?.operTime]?.ontpcnt?.pp}
</span>
)
},
{
title: '离线',
@ -76,10 +108,10 @@ export default function Czrz() {
width: 100,
align: "center",
render: (text, record) => (
<span>
{record[SearchBottom?.operTime]?.offtpcnt?.pp}
</span>
)
<span>
{record[SearchBottom?.operTime]?.offtpcnt?.pp}
</span>
)
},
]
},
@ -93,10 +125,10 @@ export default function Czrz() {
width: 100,
align: "center",
render: (text, record) => (
<span>
{record[SearchBottom?.operTime]?.ontpcnt?.zz}
</span>
)
<span>
{record[SearchBottom?.operTime]?.ontpcnt?.zz}
</span>
)
},
{
title: '离线',
@ -105,10 +137,10 @@ export default function Czrz() {
width: 100,
align: "center",
render: (text, record) => (
<span>
{record[SearchBottom?.operTime]?.offtpcnt?.zz}
</span>
)
<span>
{record[SearchBottom?.operTime]?.offtpcnt?.zz}
</span>
)
},
]
},
@ -122,10 +154,10 @@ export default function Czrz() {
width: 100,
align: "center",
render: (text, record) => (
<span>
{record[SearchBottom?.operTime]?.ontpcnt?.wf}
</span>
)
<span>
{record[SearchBottom?.operTime]?.ontpcnt?.wf}
</span>
)
},
{
title: '离线',
@ -134,10 +166,10 @@ export default function Czrz() {
width: 100,
align: "center",
render: (text, record) => (
<span>
{record[SearchBottom?.operTime]?.offtpcnt?.wf}
</span>
)
<span>
{record[SearchBottom?.operTime]?.offtpcnt?.wf}
</span>
)
},
]
},
@ -148,10 +180,10 @@ export default function Czrz() {
width: 100,
align: "center",
render: (text, record) => (
<span>
{(record[SearchBottom?.operTime].rate * 100).toFixed(2)}%
</span>
)
<span>
{(record[SearchBottom?.operTime]?.rate * 100).toFixed(2)}%
</span>
)
},
{
title: '总在线率',
@ -160,12 +192,12 @@ export default function Czrz() {
width: 150,
align: "center",
render: (text, record) => {
return <span>
{ record.ratelen > 0 ? ( record.ratesum * 100 / record.ratelen ).toFixed(2) : '-'}%
</span>
return <span>
{record.ratelen > 0 ? (record.ratesum * 100 / record.ratelen).toFixed(2) : '-'}%
</span>
}
},
]
const newDetailsColumns = useMemo(() => {
@ -178,9 +210,9 @@ export default function Czrz() {
align: "center",
render: (v, r, i) => <span>{i + 1}</span>
}
return [tmObj,...detailsColumns]
return [tmObj, ...detailsColumns]
}
},[SearchBottom])
}, [SearchBottom])
const [searchVal, setSearchVal] = useState(false)
const width = useMemo(() => newColumns.reduce((total, cur) => total + (cur.width), 0), [newColumns]);
@ -189,14 +221,15 @@ export default function Czrz() {
try {
const res = await xyt_httpget2(apiurl.ptjs.czdbl, params)
if (res.code == 200) {
res.data['429000000000'] = { ...res.data["429021000000"], adcd: '429000000000' }
proccess(res.data)
}
} catch (error) {
console.log(error);
}
}
const [hbobj, setHbobj] = useState()
const proccess = (results) => {
const dayarr = [];
const adlist = [];
@ -216,7 +249,7 @@ export default function Czrz() {
adlist.push(adobj);
if (/00000000$/.test(adcd) || adcd === '429021000000') {
if (/00000000$/.test(adcd)) {
for (let k in adobj) {
if (!/^[0-9]{4}/.test(k)) {
continue;
@ -290,6 +323,7 @@ export default function Czrz() {
}
});
console.log("dayarr", dayarr);
setDayArr(dayarr)
const dayarrColumns = dayarr.map(it => ({
title: it,
dataIndex: it,
@ -328,14 +362,15 @@ export default function Czrz() {
obj.ratesum += obj[day].rate;
obj.ratelen += 1;
}
if (reg.test(adcd) || adcd === '429021000000') {
if (reg.test(adcd)) {
list.push(obj);
}
}
if (list.length > 0) {
if (list.length > 0) {
setLoading(false)
}
console.log("list", list);
console.log("newhbobj", newhbobj);
setTableList([newhbobj, ...list]);
}
@ -389,51 +424,52 @@ export default function Czrz() {
console.log(error);
}
}
const onSelect = (keys,arr) => {
console.log(keys,arr);
const onSelect = (keys, arr) => {
console.log(keys, arr);
setSelectedKeys(keys)
setAdcd(keys[0])
}
// 子表格数据
const [subTableData, setSubTableData] = useState([])
const filterSondata = (selectedObj, type, adlist, selday) => {
// 子表格数据
const filterSondata = (selectedObj, type, adlist, selday) => {
const sellist = [];
if (selectedObj && type != 'province') {
let adreg = new RegExp('^' + selectedObj.adcd.substring(0, 4));
for (let si = 0; si < adlist.length; si++) {
let obj = adlist[si];
if ((adreg.test(obj.adcd) && obj.adcd != selectedObj.adcd) ||
(selectedObj.adcd === '429021000000' && obj.adcd === '429021000000')) {
sellist.push(obj);
let obj = adlist[si];
if (selectedObj.isBl) {
if ((adreg.test(obj.adcd) && obj.adcd == selectedObj.adcd)) {
sellist.push(obj);
}
} else if ((adreg.test(obj.adcd) && obj.adcd != selectedObj.adcd)) {
sellist.push(obj);
}
}
}
if (selectedObj && type == 'province') {
let adreg = /00000000$/;
}
}
for (let si = 0; si < adlist.length; si++) {
let obj = adlist[si];
if (selectedObj && type == 'province') {
let adreg = /00000000$/;
if ((adreg.test(obj.adcd) || obj.adcd === '429021000000') && obj.adcd != selectedObj.adcd) {
sellist.push(obj);
}
}
}
for (let si = 0; si < adlist.length; si++) {
let obj = adlist[si];
sellist.sort(function (o1, o2) {
if (o1[selday].rate > o2[selday].rate) {
return 1;
} else {
return -1;
}
if ((adreg.test(obj.adcd)) && obj.adcd != selectedObj.adcd) {
sellist.push(obj);
}
}
}
sellist.sort(function (o1, o2) {
if (o1[selday]?.rate > o2[selday]?.rate) {
return 1;
} else {
return -1;
}
});
console.log("sellist",sellist);
console.log("sellist", sellist);
setSubTableData(sellist)
}
}
useEffect(() => {
if (searchVal) {
const params = {
@ -456,64 +492,88 @@ export default function Czrz() {
return item
}
})
const type = selectObj?.adcd == '420000000000' ? 'province':'city'
selectObj.isBl = selectObj?.adcd.substring(4) != '00000000' ? true : false
const type = selectObj?.adcd == '420000000000' ? 'province' : 'city'
// const type = 'province';
console.log("adcd",subStrAdcd);
filterSondata(selectObj,type,adcdList,SearchBottom.operTime)
console.log("adcd", subStrAdcd);
setSelectObject(selectObj)
filterSondata(selectObj, type, adcdList, SearchBottom.operTime)
}
}, [adcd,SearchBottom,adcdList,hbobj])
}, [adcd, SearchBottom, adcdList, hbobj])
return (
<div className='page'>
<div className='left'>
<Card>
{
treeList.length > 0 ?
<Tree
treeData={treeList}
fieldNames={{ title: 'adnm', key: "adcd" }}
loadData={onLoadData}
defaultExpandedKeys={["420000000000000"]}
<Tree
treeData={treeList}
fieldNames={{ title: 'adnm', key: "adcd" }}
loadData={onLoadData}
defaultExpandedKeys={["420000000000000"]}
selectedKeys={selectedKeys}
onSelect={onSelect}
/>:
<Spin size='large' style={{ margin: '7rem' }}/>
/> :
<Spin size='large' style={{ margin: '7rem' }} />
}
</Card>
</div>
<div className='right'>
<Card style={{ display: 'flex', flexDirection: 'column' }}>
<div className='flex' style={{ alignItems: 'center', marginRight: 10,marginBottom:20 }}>
<img src={require('../../../assets/images/panelTitle.png')} style={{ marginRight: 5 }}></img>
<span style={{fontSize:16,fontWeight:'bold'}}>测站到报率</span>
</div>
<ToolBar
setToolVal={setSearchVal}
/>
<div style={{ display: 'flex', justifyContent: 'center', marginTop: 20 }}>
{
!loading ?
<Table
columns={newColumns}
dataSource={tableList}
pagination={false}
scroll={{ x: width, y: "calc( 100vh - 600px )" }}
rowKey="inx"
/> : <Spin size="large" style={{ marginTop: '10rem' }} />}
</div>
<div className='bottom' style={{marginTop:20,width:'100%'}}>
<ToolBottom setToolVal={setSearchBottom} />
<div style={{ display: 'flex', marginTop: 20,width:'100%' }}>
{
newDetailsColumns &&
<Table
columns={newDetailsColumns}
dataSource={subTableData}
pagination={false}
scroll={{ x: 1200}}
rowKey="inx"
/>}
</div>
<div style={{ display: 'flex', columnGap: 20 }}>
<div style={{ marginTop: 20, width: '50%' }}>
{
!loading ?
<>
<div style={{ width: '100%', height: 400 }}>
<ReactEcharts option={qsoptions} style={{ width: "100%", height: '100%' }} notMerge={true} />
</div>
<Table
columns={newColumns}
dataSource={tableList}
pagination={false}
scroll={{ x: width, y: "calc( 100vh - 500px )" }}
rowKey="inx"
/>
</>
: null}
</div>
<div className='bottom' style={{ marginTop: 20, width: '50%', position: "relative" }}>
{!loading && <div style={{ position: 'absolute', right: 40, top: 20, zIndex: 10 }}>
<ToolBottom setToolVal={setSearchBottom} searchVal={searchVal} />
</div>}
<div style={{ marginTop: 0, width: '100%' }}>
{
!loading ?
<>
<div style={{ width: '100%', height: 400 }}>
<ReactEcharts option={dblOptions} style={{ width: "100%", height: '100%' }} notMerge={true} />
</div>
<Table
columns={detailsColumns}
dataSource={subTableData}
pagination={false}
scroll={{ x: 1200, y: "calc( 100vh - 600px )" }}
rowKey="inx"
/>
</> :
<Spin size="large" style={{ marginTop: '10rem' }} />
}
</div>
</div>
</div>
</Card>
</div>
</div>

View File

@ -0,0 +1,153 @@
import moment from "moment"
export default function qsOption(timeList, listObj, hbobj) {
const chartTimeList = JSON.parse(JSON.stringify(timeList));
let itemStyle = {
normal: {
label: {
show: true,
formatter: '{c}%'
},
labelLine: {
show: true
}
}
}
let minY = 100;
let hbSeriseData = [];
const totalSerise = [
{
name: "湖北省",
type: 'line',
itemStyle: itemStyle,
data: hbSeriseData
}
]
const legendName = ['湖北省'];
let addSerise = {
name: "",
type: 'line',
itemStyle: itemStyle,
data: []
}
// 湖北省
for (let d = chartTimeList.length - 1; d > -1; d--) {
let yval = parseFloat((hbobj[chartTimeList[d]]?.rate * 100).toFixed(2));
hbSeriseData.push(yval);
}
// 市
for (let d = chartTimeList.length - 1; d > -1; d--) {
let yval = parseFloat((listObj[chartTimeList[d]]?.rate * 100).toFixed(2));
addSerise.name = listObj?.adnm;
addSerise.data.push(yval);
if (yval < minY) {
minY = yval;
}
minY = Math.ceil(minY) - 5;
minY = minY < 0 ? 0 : minY;
}
if (addSerise.name && addSerise.name != '湖北省') {
totalSerise.push(addSerise)
legendName.push(addSerise.name);
}
chartTimeList.sort(function (o1, o2) {
if (moment(o1).isBefore(moment(o2))) {
return -1;
} else {
return 1;
}
});
chartTimeList.forEach(function (str, idx) {
chartTimeList[idx] = str.substring(5, 10);
});
return {
title: {
text: '到报率变化趋势',
textStyle: {
fontSize: 16
},
left: 'center'
},
tooltip: {
trigger: 'axis',
formatter: '{a}, {b}, {c}%'
},
legend: {
data: legendName,
top:30
},
toolbox: {
show: true,
orient: 'vertical',
feature: {
dataZoom: {
show: true,
title: {
zoom: '区域缩放',
back: '区域缩放还原',
}
},
magicType: {
show: true,
title: {
line: '切换为折线图',
bar: '切换为柱状图',
},
type: ['line', 'bar']
},
restore: {
show: true,
title: '还原'
},
saveAsImage: {
show: true,
title: '保存为图片'
}
}
},
// dataZoom: [{
// type: 'inside',
// start: 0,
// end: 100
// }, {
// start: 0,
// end: 10,
// handleIcon: 'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z',
// handleSize: '80%',
// handleStyle: {
// color: '#fff',
// shadowBlur: 3,
// shadowColor: 'rgba(0, 0, 0, 0.6)',
// shadowOffsetX: 2,
// shadowOffsetY: 2
// }
// }],
xAxis: {
type: 'category',
data: chartTimeList,
axisLabel: {
interval: 0
}
},
yAxis: {
type: 'value',
axisLabel: {
formatter: '{value}%'
},
min: minY,
axisLine: {
show: true,
},
axisTick: {
show: true,
}
},
series:totalSerise
}
}

View File

@ -2,35 +2,49 @@ import React, { useEffect,useState } from 'react';
import { Form, Input, Button, DatePicker } from 'antd';
import NormalSelect from '../../../components/Form/NormalSelect';
import dayjs from 'dayjs';
const ToolBottom = ({ setToolVal }) => {
const [form] = Form.useForm();
const onFinish = (values) => {
if (values.operTime) {
values.operTime = values.operTime ? dayjs(values.operTime).format('YYYY-MM-DD') : "";
import moment from 'moment';
const ToolBottom = ({ setToolVal, searchVal }) => {
console.log("searchVal",searchVal);
const [form] = Form.useForm();
const [timeOptions, setTimeOptions] = useState([])
const onValuesChange = (changedValues, allValues) => {
setToolVal(changedValues)
}
const generateDateRange = (startDate, endDate) => {
let dates = [];
let currentDate = moment(startDate);
let end = moment(endDate);
while (currentDate.isSameOrBefore(end)) {
dates.push(currentDate.format('YYYY-MM-DD'));
currentDate.add(1, 'days');
}
setToolVal(values);
}
useEffect(() => {
const defaultValue = dayjs()
form.setFieldsValue({ operTime: defaultValue })
setToolVal({operTime:defaultValue.format('YYYY-MM-DD')})
}, []);
return dates.map(item => ({
label: item,
value: item,
}));
}
// useEffect(() => {
// const defaultValue = dayjs().subtract(1,'days').format('YYYY-MM-DD')
// form.setFieldsValue({ operTime: defaultValue })
// setToolVal({operTime:defaultValue})
// }, []);
useEffect(() => {
if (searchVal) {
const timeOptions = generateDateRange(searchVal.stm, searchVal.etm)
setTimeOptions(timeOptions)
form.setFieldsValue({ operTime: searchVal.etm })
setToolVal({operTime:searchVal.etm})
}
}, [searchVal])
return (
<div className='pageToolBar'>
<Form form={form} onFinish={onFinish} size='Default' layout="inline">
<Form.Item label="监测日期" name="operTime">
<DatePicker
format='YYYY-MM-DD'
style={{ width: 150 }}
/>
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit" size='Default'>查询</Button>
</Form.Item>
<Form.Item>
<Button size='Default' onClick={()=>form.resetFields()}>重置</Button>
<Form form={form} size='Default' layout="inline" onValuesChange={onValuesChange}>
<Form.Item label="" name="operTime">
<NormalSelect options={timeOptions} style={{ width: '130px' }} />
</Form.Item>
</Form>

View File

@ -19,8 +19,8 @@ const ToolBar = ({ setToolVal }) => {
useEffect(() => {
const defaultValue = [
dayjs().subtract(7, 'days'),
dayjs()
dayjs().subtract(10, 'days'),
dayjs().subtract(1,'days')
]
form.setFieldsValue({ operTime: defaultValue })
setToolVal({stm:defaultValue[0].format('YYYY-MM-DD'), etm:defaultValue[1].format('YYYY-MM-DD')})