mh_jy_safe_web/src/components/Chart/TableChart.js
2025-08-25 10:08:30 +08:00

272 lines
9.1 KiB
JavaScript
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 React, { Component } from 'react'
import { connect } from 'dva'
import PropTypes from 'prop-types'
// 组件库
import ReactEcharts from "echarts-for-react"
import { Popover, Table, Icon, Spin, Input, message } from 'antd'
import { ExportToExcel, IFComponent } from '@woowalker/feui'
// 工具库
import { isEqual } from 'lodash'
import { uuid, initFilter } from '../../utils/common'
// 样式
import classNames from 'classnames'
import styles from './chart.css'
class TableChart extends Component {
constructor (props) {
super(props)
this.state = {
data: [],
columns: [],
toggleChart: false,
// 用于列表导出的 id
tableId: uuid(),
// 设定的参考目标值
targetVal: undefined
}
this.refOfChart = null
}
componentDidMount () {
const { option, type, chartId } = this.props
if (option) {
this.getTableData(option, type)
chartId && this.getTargetVal()
}
}
UNSAFE_componentWillReceiveProps (nextProps) {
if (!isEqual(this.props.option, nextProps.option)) {
this.getTableData(nextProps.option, nextProps.type)
}
}
/** 生成 table 数据 */
getTableData = (option, type) => {
const data = []
const columns = []
const { radar, series = [], xAxis = [], yAxis = [] } = option
switch (type) {
// 饼图
case 2:
if (series[0] && Array.isArray(series[0].data) && series[0].data.length) {
// 从 series 取 columns
let total = 0
series[0].data.forEach(({ name, value }) => {
total += value
columns.push({ title: name, dataIndex: name, key: name })
})
// 从 series 取 data
const tempTableData = {}
series[0].data.forEach(({ name, value }) => {
tempTableData[name] = `${value}(${total ? (value / total * 100).toFixed(2) : 0}%)`
})
data.push(tempTableData)
}
break
// 雷达图
case 3:
if (radar[0] && Array.isArray(radar[0].indicator) && radar[0].indicator.length) {
columns.push({ title: '类型', dataIndex: 'name', key: 'name' })
// 从 radar 取 columns
radar[0].indicator.forEach(item => columns.push({ title: `${item.text}(${item.max})`, dataIndex: item.text, key: item.text }))
// 从 series 取 data
series.forEach(serie => {
const tempTableData = {}
columns.forEach((item, index) => {
const { name, data: serieData } = serie
tempTableData[item.key] = item.title === '类型' ? name : serieData[0].value[index - 1]
})
data.push(tempTableData)
})
}
break
// 折线、柱状图
default:
if (xAxis[0] && Array.isArray(xAxis[0].data) && xAxis[0].data.length) {
columns.push({ title: '类型', dataIndex: 'name', key: 'name' })
// 从 xAxis 取 columns
xAxis[0].data.forEach(item => columns.push({ title: item, dataIndex: item, key: item }))
// 从 series 取 data
series.forEach(serie => {
const tempTableData = {}
columns.forEach((item, index) => {
const { name, data: serieData, yAxisIndex } = serie
const yName = (yAxis[yAxisIndex || 0] || {}).name
tempTableData[item.key] = item.title === '类型' ? `${name}${yName ? '(' + yName + ')' : ''}` : (serieData[index - 1]?.TooltipValue || serieData[index - 1]?.value)
})
data.push(tempTableData)
})
}
}
this.setState({ data, columns })
}
/** 获取 chartId 对应的目标值 */
getTargetVal = () => {
const { chartId, login, dispatch } = this.props
const json = initFilter(login.OrgId, chartId)
dispatch({
type: 'app/getRedisValue',
payload: json
}).then(res => {
this.setState({ targetVal: isNaN(res) ? res : Number(res) })
})
}
/** 设置 chartId 对应的目标值 */
setTargetVal = (value) => {
const { chartId, login, dispatch } = this.props
const json = initFilter(login.OrgId, chartId)
json.Parameter1 = value
dispatch({
type: 'app/setRedisValue',
payload: json
}).then(res => {
if (res) {
this.setState({ targetVal: isNaN(value) ? value : Number(value) })
message.success('操作成功')
}
})
}
/** 处理目标值 targetVal 的修改 */
handleTVChange = (evt) => {
const { value } = evt.target
const targetVal = value === '' ? '' : isNaN(value) ? value : Number(value)
this.setState({ targetVal })
}
getChartOption = (option) => {
const { targetVal } = this.state
if (Array.isArray(option.series) && option.series.length && targetVal) {
if (Array.isArray(option.yAxis) && option.yAxis.length) {
// 标记线默认对应的Y轴就是第一条Y轴所以这里默认取第一条Y轴进行设置
const { max, min } = option.yAxis[0]
let maxSet = false
let minSet = false
// 监测对应 max 值是否小于 targetVal 值
if (max) {
maxSet = max < targetVal
} else {
// 取对应默认第一条Y轴的数据取其最大值
const targetSeries = option.series.filter(item => item.yAxisIndex === 0)
const maxData = Math.max.apply(null, targetSeries.map(item => Math.max.apply(null, item.data)))
maxSet = maxData < targetVal
}
// 监测对应 min 值是否大于 targetVal 值
minSet = min > targetVal
// 设置 targetVal 对应的 max 和 min 值
maxSet && (option.yAxis[0].max = targetVal)
minSet && (option.yAxis[0].min = targetVal)
}
return {
...option,
series: [
{
name: '标记线',
type: 'line',
markLine: {
label: {
position: 'insideEndTop',
formatter: '{b}: {c}'
},
lineStyle: {
color: '#096dd9'
},
data: [
{
name: '目标值',
yAxis: targetVal,
lineStyle: { width: 2 }
}
]
}
}
].concat(option.series)
}
}
return option
}
render () {
const { chartId, option, onEvents, loading, style } = this.props
const { targetVal, data, columns, toggleChart } = this.state
const loadingGet = this.props.dvaLoading.effects['app/getRedisValue']
const loadingSet = this.props.dvaLoading.effects['app/setRedisValue']
return (
<Spin size='large' spinning={!!loading} wrapperClassName={styles.chartTable}>
<div className={styles.chartTableSwap}>
{/** 额外需要插入的工具集 */}
{this.props.children}
{/** 目标值设定 */}
<IFComponent IF={!toggleChart && !!chartId}>
<Popover
content={
<Input.Search
autoFocus
allowClear
value={targetVal}
loading={loadingGet || loadingSet}
placeholder='请输入设定目标值'
enterButton='保存'
onChange={this.handleTVChange}
onSearch={this.setTargetVal}
/>
}
trigger='click'
>
<Icon type='setting' title='目标值设定' style={{ marginRight: 10 }} />
</Popover>
</IFComponent>
{/** 图形列表切换按钮 */}
<span onClick={() => { this.setState({ toggleChart: !toggleChart }) }}>
<Icon type='swap' />
<span>{`切换${toggleChart ? '图表' : '列表'}`}</span>
</span>
{/** 导出按钮 */}
<IFComponent IF={toggleChart}>
<ExportToExcel
fileName='列表数据'
tableId={this.state.tableId}
render={({ onExport }) => <Icon type='download' title='导出' onClick={onExport} style={{ paddingLeft: 10 }} />}
/>
</IFComponent>
</div>
<ReactEcharts
ref={ref => this.refOfChart = ref?.getEchartsInstance()}
notMerge
lazyUpdate
option={this.getChartOption(option)}
onEvents={onEvents}
className={classNames(styles.chart, { [styles.show]: !toggleChart, [styles.hide]: toggleChart })}
style={{ width: '100%', height: '100%', ...style }}
/>
<Table
id={this.state.tableId}
bordered
rowKey='ID'
size='small'
dataSource={data}
columns={columns}
scroll={{ x: true }}
pagination={false}
className={classNames(styles.table, { [styles.show]: toggleChart, [styles.hide]: !toggleChart })}
/>
</Spin>
)
}
}
TableChart.propTypes = {
option: PropTypes.object,
type: PropTypes.string,
chartId: PropTypes.string,
onEvents: PropTypes.object,
style: PropTypes.object
}
export default connect(({ login, loading }) => ({ login, dvaLoading: loading }))(TableChart)