import React, {Component} from 'react'
|
import PropTypes from 'prop-types'
|
import { is, fromJS } from 'immutable'
|
import { Chart } from '@antv/g2'
|
import DataSet from '@antv/data-set'
|
import * as echarts from 'echarts'
|
import { Spin, Empty, notification, Modal } from 'antd'
|
|
import Api from '@/api'
|
import Utils from '@/utils/utils.js'
|
import UtilsDM from '@/utils/utils-datamanage.js'
|
import MKEmitter from '@/utils/events.js'
|
import TimerTask from '@/utils/timer-task.js'
|
import NormalHeader from '@/tabviews/custom/components/share/normalheader'
|
import './index.scss'
|
|
class CustomChart extends Component {
|
static propTpyes = {
|
data: PropTypes.array, // 统一查询数据
|
config: PropTypes.object, // 组件配置信息
|
mainSearch: PropTypes.any, // 外层搜索条件
|
}
|
|
state = {
|
BID: '', // 主表ID
|
config: null, // 图表配置信息
|
empty: true, // 图表数据为空
|
loading: false, // 数据加载状态
|
sync: false, // 是否统一请求数据
|
plot: null, // 图表设置
|
search: null, // 搜索条件
|
}
|
|
data = []
|
|
UNSAFE_componentWillMount () {
|
const { config, data, initdata } = this.props
|
let _config = fromJS(config).toJS()
|
let _sync = config.setting.sync === 'true'
|
|
let BID = ''
|
let BData = ''
|
|
if (_config.setting.supModule) {
|
BData = window.GLOB.CacheData.get(_config.setting.supModule)
|
} else {
|
BData = window.GLOB.CacheData.get(_config.$pageId)
|
}
|
if (BData) {
|
BID = BData.$BID || ''
|
}
|
|
if (_sync && data) {
|
this.data = data[config.dataName] || []
|
_sync = false
|
} else if (_sync && initdata) {
|
this.data = initdata || []
|
_sync = false
|
}
|
|
_config.plot.height = Utils.getHeight(_config.plot.height)
|
_config.style.height = 'auto'
|
|
this.setState({
|
config: _config,
|
empty: this.data.length === 0,
|
BID: BID || '',
|
arr_field: _config.columns.map(col => col.field).join(','),
|
plot: _config.plot,
|
sync: _sync,
|
search: Utils.initMainSearch(config.search)
|
}, () => {
|
if (config.setting.sync !== 'true' && config.setting.onload === 'true') {
|
setTimeout(() => {
|
this.loadData()
|
}, _config.setting.delay || 0)
|
}
|
})
|
|
if (this.data.length > 0) {
|
this.handleData()
|
}
|
}
|
|
/**
|
* @description 图表数据更新,刷新内容
|
*/
|
UNSAFE_componentWillReceiveProps (nextProps) {
|
const { sync, config } = this.state
|
|
if (sync && !is(fromJS(this.props.data), fromJS(nextProps.data))) {
|
let _data = []
|
if (nextProps.data && nextProps.data[config.dataName]) {
|
_data = nextProps.data[config.dataName] || []
|
}
|
|
if (!is(fromJS(this.data), fromJS(_data))) {
|
this.data = _data
|
this.handleData()
|
}
|
|
this.setState({sync: false, empty: _data.length === 0})
|
} else if (config.setting.useMSearch && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
|
this.setState({}, () => {
|
this.loadData()
|
})
|
}
|
}
|
|
shouldComponentUpdate (nextProps, nextState) {
|
return !is(fromJS(this.state), fromJS(nextState))
|
}
|
|
componentDidMount () {
|
const { config, sync } = this.state
|
|
MKEmitter.addListener('reloadData', this.reloadData)
|
MKEmitter.addListener('resetSelectLine', this.resetParentParam)
|
MKEmitter.addListener('queryModuleParam', this.queryModuleParam)
|
MKEmitter.addListener('refreshByButtonResult', this.refreshByButtonResult)
|
|
if (config.timer) {
|
this.timer = new TimerTask()
|
this.timer.init(config.uuid, config.timer, config.timerRepeats, () => {
|
this.loadData(true)
|
})
|
}
|
|
if (config.$cache && (config.setting.sync !== 'true' || sync)) {
|
Api.getLCacheConfig(config.uuid).then(res => {
|
if (!res || this.data.length > 0) return
|
|
if (!is(fromJS(this.data), fromJS(res))) {
|
this.data = res
|
this.handleData()
|
}
|
this.setState({empty: false})
|
})
|
}
|
}
|
|
/**
|
* @description 组件销毁,清除state更新,清除快捷键设置
|
*/
|
componentWillUnmount () {
|
this.setState = () => {
|
return
|
}
|
MKEmitter.removeListener('reloadData', this.reloadData)
|
MKEmitter.removeListener('resetSelectLine', this.resetParentParam)
|
MKEmitter.removeListener('queryModuleParam', this.queryModuleParam)
|
MKEmitter.removeListener('refreshByButtonResult', this.refreshByButtonResult)
|
|
this.timer && this.timer.stop()
|
}
|
|
/**
|
* @description 按钮执行完成后页面刷新
|
* @param {*} menuId // 菜单Id
|
* @param {*} position // 刷新位置
|
* @param {*} btn // 执行的按钮
|
*/
|
refreshByButtonResult = (menuId, position, btn) => {
|
const { config, BID } = this.state
|
|
if (config.uuid !== menuId) return
|
|
if ((position === 'mainline' || position === 'popclose') && config.setting.supModule && BID) { // 刷新源组件时,附带刷新上级行与当前组件
|
MKEmitter.emit('reloadData', config.setting.supModule, BID)
|
} else {
|
this.loadData()
|
}
|
|
if (position === 'popclose') { // 执行启动弹窗的按钮所选择的刷新项
|
btn.$tabId && MKEmitter.emit('refreshPopButton', btn.$tabId)
|
}
|
}
|
|
reloadData = (menuId) => {
|
const { config } = this.state
|
|
if (config.uuid !== menuId) return
|
|
this.loadData()
|
}
|
|
resetParentParam = (MenuID, id) => {
|
const { config } = this.state
|
|
if (!config.setting.supModule || config.setting.supModule !== MenuID) return
|
if (id !== this.state.BID || id !== '') {
|
this.setState({ BID: id }, () => {
|
this.loadData()
|
})
|
}
|
}
|
|
/**
|
* @description 导出Excel时,获取页面搜索排序等参数
|
*/
|
queryModuleParam = (menuId, callback) => {
|
const { mainSearch } = this.props
|
const { arr_field, config, search } = this.state
|
|
if (config.uuid !== menuId) return
|
|
let searches = search ? fromJS(search).toJS() : []
|
if (config.setting.useMSearch && mainSearch && mainSearch.length > 0) { // 主表搜索条件
|
let keys = searches.map(item => item.key.toLowerCase())
|
mainSearch.forEach(item => {
|
if (!keys.includes(item.key.toLowerCase())) {
|
searches.push(item)
|
}
|
})
|
}
|
|
callback({
|
arr_field: arr_field,
|
orderBy: config.setting.order || '',
|
search: searches,
|
menuName: config.name
|
})
|
}
|
|
/**
|
* @description 数据加载
|
*/
|
async loadData (hastimer) {
|
const { mainSearch } = this.props
|
const { config, arr_field, BID, search } = this.state
|
|
if (config.setting.supModule && !BID) { // BID 不存在时,不做查询
|
this.setState({
|
empty: true
|
})
|
|
if (!is(fromJS(this.data), fromJS([]))) {
|
this.data = []
|
this.handleData()
|
}
|
return
|
}
|
|
let searches = search ? fromJS(search).toJS() : []
|
if (config.setting.useMSearch && mainSearch && mainSearch.length > 0) { // 主表搜索条件
|
let keys = searches.map(item => item.key)
|
mainSearch.forEach(item => {
|
if (!keys.includes(item.key)) {
|
searches.push(item)
|
}
|
})
|
}
|
|
let requireFields = searches.filter(item => item.required && item.value === '')
|
if (requireFields.length > 0) {
|
return
|
}
|
|
if (!hastimer) {
|
this.setState({
|
loading: true
|
})
|
}
|
|
let _orderBy = config.setting.order || ''
|
let param = UtilsDM.getQueryDataParams(config.setting, arr_field, searches, _orderBy, '', '', BID)
|
|
let result = await Api.genericInterface(param)
|
if (result.status) {
|
if (config.$cache && config.setting.onload !== 'false') {
|
Api.writeCacheConfig(config.uuid, result.data || '')
|
}
|
|
this.setState({
|
loading: false,
|
empty: !result.data || result.data.length === 0
|
})
|
|
if (!is(fromJS(this.data), fromJS(result.data || []))) {
|
this.data = result.data || []
|
this.handleData()
|
}
|
|
if (config.timer && config.clearField && result.data && result.data[0]) {
|
let vals = (config.clearValue || '').split(',')
|
if (vals.includes(result.data[0][config.clearField])) {
|
this.timer && this.timer.stop()
|
}
|
}
|
if (result.message) {
|
if (result.ErrCode === 'Y') {
|
Modal.success({
|
title: result.message
|
})
|
} else if (result.ErrCode === 'S') {
|
notification.success({
|
top: 92,
|
message: result.message,
|
duration: 2
|
})
|
}
|
}
|
} else {
|
this.setState({
|
loading: false
|
})
|
this.timer && this.timer.stop()
|
|
if (!result.message) return
|
if (result.ErrCode === 'N') {
|
Modal.error({
|
title: result.message,
|
})
|
} else if (result.ErrCode !== '-2') {
|
notification.error({
|
top: 92,
|
message: result.message,
|
duration: 10
|
})
|
}
|
}
|
}
|
|
/**
|
* @description 数据预处理,统计数据需要重置
|
*/
|
handleData = () => {
|
if (!this.wrap) return
|
|
this.wrap.innerHTML = ''
|
this.wrap.removeAttribute('_echarts_instance_')
|
this.wrap.removeAttribute('style')
|
|
setTimeout(() => {
|
this.viewrender()
|
}, 100)
|
}
|
|
/**
|
* @description 图表渲染分组
|
*/
|
viewrender = () => {
|
const { config } = this.state
|
|
if (!config.plot.script) return
|
|
if (config.plot.chartType === 'antv') {
|
try {
|
// eslint-disable-next-line
|
let func = new Function('Chart', 'DataSet', 'wrap', 'data', 'config', config.plot.script)
|
func(Chart, DataSet, this.wrap, this.data, config)
|
} catch (e) {
|
console.warn(e)
|
|
notification.warning({
|
top: 92,
|
message: '图表渲染失败!',
|
duration: 5
|
})
|
}
|
} else if (config.plot.chartType === 'echarts') {
|
try {
|
// eslint-disable-next-line
|
let func = new Function('echarts', 'DataSet', 'wrap', 'data', 'config', config.plot.script)
|
func(echarts, DataSet, this.wrap, this.data, config)
|
} catch (e) {
|
console.warn(e)
|
|
notification.warning({
|
top: 92,
|
message: '图表渲染失败!',
|
duration: 5
|
})
|
}
|
}
|
}
|
|
|
refreshSearch = (list) => {
|
this.setState({search: list}, () => {
|
this.loadData()
|
})
|
}
|
|
render() {
|
const { config, loading, empty, BID } = this.state
|
|
return (
|
<div className="custom-chart-plot-box" id={'anchor' + config.uuid} style={config.style}>
|
{loading ?
|
<div className="loading-mask">
|
<div className="ant-spin-blur"></div>
|
<Spin />
|
</div> : null
|
}
|
<NormalHeader config={config} BID={BID} refresh={this.refreshSearch}/>
|
<div className="canvas-wrap" style={{height: (config.plot.height + 30) + 'px'}}>
|
<div className={'canvas' + (empty ? ' empty' : '')} style={{height: config.plot.height + 'px'}} ref={ref => this.wrap = ref}></div>
|
</div>
|
{empty ? <Empty description={false}/> : null}
|
</div>
|
)
|
}
|
}
|
|
export default CustomChart
|