文章目录
react实现对表格某列数据实现总览(表格总览热力图)
如图左侧,一张普通的数据表,其中有一项为每行数据的最终判定结果,当有很多条数据时,可以选择在右侧生成一个热力图,将左侧的表中的判定结果映射到右侧,形成一个总览,我们可以直接通过右侧的颜色直接定位到那些是异常,哪些为正常,并实现通过点击右侧区域,左边表格定位到右侧热力图对应的位置!
实现原理
颜色效果:通过css3中的样式控制:background: linear-gradient(${colors})
其本质是将一个空的div中
<div
id="scrollDiv"
style={{ height: '55vh', overflow: 'hidden', background: `linear-gradient(${colors})` }}
onClick={(event) => scrollRow(event)}/>
其中的colors为拼接的字符串
//定义一个颜色数组字符串的默认值
const colorsInnitial = (data: any) => {
let _colors = ''
data.map((item: any, index: number) => {
if (item.furNo == "1") {
_colors = _colors + ',' + 'rgb(172, 81, 99)'
} else if (item.furNo == "2") {
_colors = _colors + ',' + 'rgb(75, 152, 110)'
} else if (item.furNo == "3") {
_colors = _colors + ',' + 'rgb(34, 25, 129)'
} else {
_colors = _colors + ',' + 'white'
}
})
//'to bottom'渐变的方向从上到下
_colors = 'to bottom' + _colors
return _colors
}
点击效果:通过点击位置的高度百分比,去定位左侧表格中的滚动条的百分比
//拿到整个点击区域
let scrollDiv = document.getElementById("scrollDiv")
//定义点击事件
const scrollRow = (event: any) => {
let scrollPercentage = 0
if (scrollDiv) {
scrollPercentage = (event.clientY - scrollDiv.getBoundingClientRect().top) / scrollDiv.offsetHeight
}
if (AreaAlarmMsg.length) {
handleScroll(scrollPercentage)
}
}
// 获取左侧表格 通过传入的点击区域的百分比参数 确定左侧表格的导航条滚动位置
const handleScroll = (percent: number) => {
let scrolls = document.getElementsByClassName("ant-table-body")
scrolls[0].scrollTop = (percent * scrolls[0].scrollHeight)
}
//完整代码
import React, { useState, useEffect } from 'react'
import { SearchOutlined, ReloadOutlined, ExportOutlined } from '@ant-design/icons'
import { Tag, Modal, Button, message, Table, Breadcrumb, Row, Col, BackTop } from 'antd'
import { connect } from 'dva'
import lodash from 'lodash'
import moment from 'moment'
import "./style.less"
import { SearchCondition } from './fragments/searchCondition'
import { EmbaraseSection } from './fragments/embaraseSection'
import DispatchEvent from "../tools/dispatchEvent"
import PublicEvent from './services/publicEvent'
import axios from 'axios'
import ReactEcharts from 'echarts-for-react'
import { areaheatMapData } from './data/heatMap'
import { LinechartGroupModel } from '../automaticJudgement/models/linechartGroupModel'
let startDay: string = moment(new Date()).add(-1, "days").format("YYYY-MM-DD")
let startTime: string = startDay + ' 00:00:00'
let endedTime: string = startDay + ' 24:00:00'
const dispatcher: DispatchEvent = new DispatchEvent()
const waitList: string[] = ["StatisticsEmbaraseThemeView"]
let waitIndex: number = 0
const Index = (props: any) => {
const thisViewName: string = "StatisticsMainView"
const [searchConditionDisplay, setSearchConditionDisplay] = useState("none")
const [isLoading, setIsLoading] = useState(false)
let isSearchShow = false;
const [AreaAlarmMsg, setAreaAlarmMsg] = useState([])
const [AlarmMesByArea, setAlarmMesByArea] = useState([])
const [isShowDetails, setShowDetails] = useState(false)
const [colors, setColors] = useState('')
//分页
const [tableBasicPagination, setTableBasicPagination] = useState({
total: 0,
showSizeChanger: true,
showQuickJumper: true,
showTotal: (total: any) => {
return "共 " + total + " 项"
},
defaultPageSize: 200
})
const [searchRow, setSearchRow] = useState<any>({
coiltype: "",
thickMin: 0,
thickMax: 0,
orderNo: "",
group: "",
widthMin: 0,
widthMax: 0,
matId: "",
theme: "",
startTime: startTime,
endedTime: endedTime,
})
//前一天的区域报警归类统计信息列
const columns = [
{
title: '序号',
key: 'sort',
width: 50,
ellipsis: true,
render: (text: any, row: any, index: any) => {
return <div>{index + 1}</div>
}
},
{
title: '卷号',
dataIndex: 'coilNo',
key: 'coilNo',
width: 100,
ellipsis: true,
},
{
title: '班组',
// dataIndex:'group',
key: 'group',
width: 80,
ellipsis: true,
render: (text: any, record: any) => {
let group = "甲组"
switch (record.group) {
case "1": group = "甲组"
break;
case "2": group = "乙组"
break;
case "3": group = "丙组"
break;
case "4": group = "丁组"
break;
}
return <div>{group}</div>
}
},
{
title: '班次',
// dataIndex:'shift',
key: 'shift',
width: 80,
ellipsis: true,
render: (text: any, record: any) => {
let shift = "白班"
switch (record.shift) {
case "2": shift = "小夜班"
break;
case "3": shift = "大夜班"
break;
case "1": shift = "白班"
break;
}
return <div>{shift}</div>
}
},
{
title: '区域报警详情',
children: [
{
title: 'FUC1报警',
key: 'FUC1',
width: 80,
ellipsis: true,
render: (row: any, rocord: any) => {
if (rocord.furNo == 1) {
return <Tag style={{ fontSize: 18 }} color='rgb(172, 81, 99)'>{"1号炉 : " + rocord.fuc}</Tag>
} else {
return <div>{'非1号炉轧制'}</div>
}
}
},
{
title: 'FUC2报警',
// dataIndex:'FUC2',
key: 'FUC2',
width: 80,
ellipsis: true,
render: (row: any, rocord: any) => {
if (rocord.furNo == 2) {
return <Tag style={{ fontSize: 18 }} color="rgb(75, 152, 110)">{"2号炉 : " + rocord.fuc}</Tag>
} else {
return <div>{'非2号炉轧制'}</div>
}
}
},
{
title: 'FUC3报警',
// dataIndex:'FUC3',
key: 'FUC3',
width: 80,
ellipsis: true,
render: (row: any, rocord: any) => {
if (rocord.furNo == 3) {
return <Tag style={{ fontSize: 18 }} color="rgb(34, 25, 129)">{"3号炉 : " + rocord.fuc}</Tag>
} else {
return <div>{'非3号炉轧制'}</div>
}
}
},
{
title: 'R1报警',
dataIndex: 'r1',
key: 'r1',
width: 80,
ellipsis: true,
},
{
title: 'R2报警',
dataIndex: 'r2',
key: 'R2',
width: 80,
ellipsis: true,
},
{
title: '精轧区域',
dataIndex: 'fm',
key: 'fm',
width: 80,
ellipsis: true,
},
{
title: '层冷卷取区域',
dataIndex: 'ctc',
key: 'ctc',
width: 80,
ellipsis: true,
},
{
title: '总报警',
key: 'total',
width: 80,
ellipsis: true,
render : (record: any)=>{
let count = 0
count = record.fuc*1 + record.r1 + record.r2 + record.fm + record.ctc
return <div>{count}</div>
},
},
]
},
{
title: '操作',
dataIndex: 'opeator',
key: 'opeator',
width: 80,
ellipsis: true,
render: (row: any, record: any) => {
return <div>
<Button
onClick={() => {
setShowDetails(true)
let coilNo = record.coilNo
getAlarmMesByArea(coilNo)
}}
type="default"
style={{ background: 'rgb(75, 152, 110)', width: '80%' }}>查看</Button>
</div>
}
},
{
title: '生产时间',
// dataIndex:'date',
key: 'date',
width: 120,
ellipsis: true,
render: (record: any) => {
return <div>{moment(record.date).format('YYYY:MM:DD HH:mm:ss')}</div>
}
},
]
//单卷的报警详细情况
const coilColumns = [
{
title: '序号',
key: 'sort',
width: 50,
ellipsis: true,
render: (text: any, row: any, index: any) => {
return <div>{index + 1}</div>
}
},
{
title: '区域名称',
key: 'area',
dataIndex: 'area',
width: 80,
ellipsis: true,
},
{
title: '规则名称',
key: 'ruleName',
dataIndex: 'ruleName',
width: 120,
ellipsis: true,
},
{
title: '钢种',
key: 'steelGrade',
dataIndex: 'steelGrade',
width: 100,
ellipsis: true,
},
{
title: '报警ID',
key: 'alarmId',
// dataIndex:'alarmId',
width: 100,
ellipsis: true,
render: (record: any) => {
if (record.alarmId == null || record.alarmId == 0) {
record.alarmId = '暂未配置'
}
return <div>{record.alarmId}</div>
}
},
{
title: '报警等级',
key: 'level',
// dataIndex:'level',
width: 80,
ellipsis: true,
render: (record: any) => {
switch (record.level) {
case 0:
record.level = '正常'
break
case 1:
record.level = '轻微'
break
case 2:
record.level = '中等'
break
case 3:
record.level = '严重'
break
}
return <div>{record.level}</div>
}
},
{
title: '报警建议',
key: 'advice',
// dataIndex:'advice',
width: 120,
ellipsis: true,
render: (record: any) => {
let advice = '暂无'
if (record.advice != null && record.advice != 0) {
advice = record.advice
}
return <div>{advice}</div>
}
},
{
title: '报警时间',
key: 'date',
// dataIndex:'date',
width: 200,
ellipsis: true,
render: (record: any) => {
return <div>{moment(record.date).format('YYYY:MM:DD HH:mm:ss')}</div>
}
},
]
//定义一个颜色数组字符串的默认值
const colorsInnitial = (data: any) => {
let _colors = ''
data.map((item: any, index: number) => {
if (item.furNo == "1") {
_colors = _colors + ',' + 'rgb(172, 81, 99)'
} else if (item.furNo == "2") {
_colors = _colors + ',' + 'rgb(75, 152, 110)'
} else if (item.furNo == "3") {
_colors = _colors + ',' + 'rgb(34, 25, 129)'
} else {
_colors = _colors + ',' + 'white'
}
})
_colors = 'to bottom' + _colors
return _colors
}
const getAreaAlarmMsg = async (startTime: any, endedTime: any) => {
setIsLoading(true)
axios.post('http://170.0.35.145:9876/alarmResultService/getAreaAlarmMsg', { begin: startTime, end: endedTime }).then((response) => {
const data = response.data.data
tableBasicPagination.total = response.data.data.length
setColors(colorsInnitial(data))
setTableBasicPagination(tableBasicPagination)
setAreaAlarmMsg(data)
}).catch(() => {
message.error("请求昨日的区域报警总览状况")
}).finally(() => {
setIsLoading(false)
})
}
//拿到整个点击区域
let scrollDiv = document.getElementById("scrollDiv")
//定义点击事件
const scrollRow = (event: any) => {
let scrollPercentage = 0
if (scrollDiv) {
scrollPercentage = (event.clientY - scrollDiv.getBoundingClientRect().top) / scrollDiv.offsetHeight
}
if (AreaAlarmMsg.length) {
handleScroll(scrollPercentage)
}
}
// 导航条滚动
const handleScroll = (percent: number) => {
let scrolls = document.getElementsByClassName("ant-table-body")
scrolls[0].scrollTop = (percent * scrolls[0].scrollHeight)
}
useEffect(() => {
getAreaAlarmMsg(startTime, endedTime)
}, [])
const getAlarmMesByArea = async (coilNo: any) => {
setIsLoading(true)
axios.post('http://170.0.35.145:9876/alarmResultService/getAlarmMesByArea', { coilNo: coilNo }).then((response) => {
const data = response.data.data
setAlarmMesByArea(data)
tableBasicPagination.total = response.data.data.length;
setTableBasicPagination(tableBasicPagination);
}).catch(() => {
message.error("单日报警详情界面表数据失败")
}).finally(() => {
setIsLoading(false)
})
}
useEffect(() => {
getAlarmMesByArea
}, [])
useEffect(() => {
dispatcher.attachment(thisViewName)
dispatcher.waitList(waitList, (viewName: string) => {
waitIndex++
if (waitIndex >= waitList.length) {
startProcess()
}
})
dispatcher.listen(PublicEvent.searchView.query, (_: any, dataRow: any) => {
setSearchRow(lodash.clone(dataRow))
console.log(dataRow)
startTime = dataRow.startTime
endedTime = dataRow.endedTime
getAreaAlarmMsg(startTime, endedTime)
})
}, [])
useEffect(() => {
startProcess()
}, [searchRow])
const startProcess = () => {
dispatcher.send(thisViewName, PublicEvent.mainView.start, searchRow)
}
return (
<div id="uxFetcher" style={{ minWidth: "1400px", overflow: "hidden", height: "calc(100vh - 96px)", minHeight: 600, margin: "-16px" }}>
<div style={{ padding: "12px 24px 12px 8px", background: "#1a357a" }}>
<Breadcrumb style={{ padding: 0 }}>
<Breadcrumb.Item>首页</Breadcrumb.Item>
<Breadcrumb.Item>单卷全过程参数预警</Breadcrumb.Item>
<Breadcrumb.Item>区域工艺精度</Breadcrumb.Item>
</Breadcrumb>
</div>
<div style={{ height: "calc(100% - 64px)", width: "100%", overflow: "auto", marginTop: 8 }}>
<div style={{ width: "100%", padding: 0, background: "rgb(26, 53, 122)" }}>
<div style={{ padding: 8 }}>
<Button icon={<SearchOutlined />} style={{ padding: 0 }} size="small" type="text" onClick={() => {
setSearchConditionDisplay("block")
}}>查询条件</Button>
<Button icon={<ReloadOutlined />} size="small" type="text" onClick={() => {
}}>重置条件</Button>
{/* <Button icon={<ExportOutlined />} size="small" type="text" onClick={() => {
}}>导出</Button> */}
</div>
<div style={{ height: 64, display: searchConditionDisplay, position: "absolute", zIndex: 999, width: "100%", paddingTop: 0, borderTop: "1px solid #001f6b", borderBottom: "1px solid #001f6b", background: "#1a357a", padding: "4px 8px", boxShadow: "0 4px 4px #001c65" }}>
<SearchCondition dispatcher={dispatcher} onClose={() => {
setSearchConditionDisplay("none")
}} visible={isSearchShow} startTime={startTime} endedTime={endedTime}></SearchCondition>
</div>
</div>
<div style={{ width: "100%", marginTop: 8, height: "30%", float: "left", background: "#1a357a" }}>
<EmbaraseSection dispatcher={dispatcher}></EmbaraseSection>
</div>
<div style={{ width: "100%", marginTop: 8, minHeight: '55vh', float: "left", background: "#1a357a" }}>
<Row gutter={16}>
<Col style={{ width: "95%" }}>
<Table
id='ant-table-body'
columns={columns}
dataSource={AreaAlarmMsg}
loading={isLoading}
pagination={tableBasicPagination}
bordered
scroll={{ y: 520 }}
/>
</Col>
<Col id='areaHeatMap' style={{ width: '5%', paddingLeft: 6, height: '100%' }}>
{console.log(222, colors)}
<div id="scrollDiv" style={{ height: '55vh', overflow: 'hidden', background: `linear-gradient(${colors})` }}
onClick={(event) => scrollRow(event)}
/>
</Col>
</Row>
<Modal
title="当日所有报警记录"
wrapClassName="vertical-center-modal"
visible={isShowDetails}
onOk={() => setShowDetails(false)}
onCancel={() => setShowDetails(false)}
width='85%'
>
<Table
columns={coilColumns}
dataSource={AlarmMesByArea}
bordered
pagination={tableBasicPagination}
loading={isLoading}
scroll={{ y: 800 }}
/>
</Modal>
</div>
</div>
</div>
)
}
export default connect(() => { }, () => { })(Index)