import React, {Component} from 'react'
|
import PropTypes from 'prop-types'
|
import { is, fromJS } from 'immutable'
|
import { Chart } from '@antv/g2'
|
import { connect } from 'react-redux'
|
import DataSet from '@antv/data-set'
|
import { Spin, Empty, Select, notification } from 'antd'
|
|
import asyncComponent from './asyncButtonComponent'
|
import { chartColors } from '@/utils/option.js'
|
// import searchLine from '../../share/searchLine'
|
import Api from '@/api'
|
import Utils from '@/utils/utils.js'
|
import UtilsDM from '@/utils/utils-datamanage.js'
|
import { modifyTabview } from '@/store/action'
|
import './index.scss'
|
|
const ExcelOutButton = asyncComponent(() => import('@/tabviews/zshare/actionList/exceloutbutton'))
|
const ExcelInButton = asyncComponent(() => import('@/tabviews/zshare/actionList/excelInbutton'))
|
|
class LineChart extends Component {
|
static propTpyes = {
|
BID: PropTypes.any, // 父级Id
|
data: PropTypes.array, // 统一查询数据
|
config: PropTypes.object, // 组件配置信息
|
mainSearch: PropTypes.any, // 外层搜索条件
|
menuType: PropTypes.any, // 菜单类型
|
dataManager: PropTypes.any, // 数据权限
|
}
|
|
state = {
|
config: null, // 图表配置信息
|
empty: true, // 图表数据为空
|
loading: false, // 数据加载状态
|
chartId: Utils.getuuid(), // 图表Id
|
transfield: {}, // 字段名称翻译
|
title: '', // 组件标题
|
sync: false, // 是否统一请求数据
|
plot: null, // 图表设置
|
data: null, // 数据
|
search: null, // 搜索条件
|
vFields: [], // 数值字段
|
vstFields: null, // 统计数据值字段信息
|
chartData: [], // 图表数据
|
chartFields: [], // 统计图表生成字段集
|
selectFields: [], // 统计图表选择字段
|
showHeader: false // 存在标题、搜索、或统计数据时显示
|
}
|
|
UNSAFE_componentWillMount () {
|
const { config, data, initdata } = this.props
|
let _config = fromJS(config).toJS()
|
|
let _data = null
|
let _sync = config.setting.sync === 'true'
|
|
if (config.setting.sync === 'true' && data) {
|
_data = data[config.dataName] || []
|
_sync = false
|
} else if (config.setting.sync === 'true' && initdata) {
|
_data = initdata || []
|
_sync = false
|
}
|
|
let vFields = []
|
let vstFields = null
|
|
if (_config.plot.datatype === 'statistics') {
|
let _column = _config.columns.filter(col => _config.plot.InfoValue === col.field)[0]
|
if (_column) {
|
let decimal = 0
|
|
if (/Decimal/ig.test(_column.datatype)) {
|
decimal = +_column.datatype.replace(/^Decimal\(18,/ig, '').replace(/\)/ig, '')
|
}
|
|
vstFields = {
|
label: _column.label,
|
field: _column.field,
|
show: _config.plot.show,
|
decimal
|
}
|
}
|
} else {
|
let _vFields = _config.plot.Yaxis && typeof(_config.plot.Yaxis) === 'string' ? [_config.plot.Yaxis] : _config.plot.Yaxis
|
_config.columns.forEach(col => {
|
if (_vFields.includes(col.field)) {
|
let decimal = 0
|
if (/Decimal/ig.test(col.datatype)) {
|
decimal = +col.datatype.replace(/^Decimal\(18,/ig, '').replace(/\)/ig, '')
|
}
|
vFields.push({
|
label: col.label,
|
field: col.field,
|
show: _config.plot.show,
|
decimal
|
})
|
}
|
})
|
}
|
|
let showHeader = false
|
if (config.plot.title || _config.plot.datatype === 'statistics' || config.search.length > 0) {
|
showHeader = true
|
_config.plot.height = _config.plot.height - 80
|
} else {
|
_config.plot.height = _config.plot.height - 30
|
}
|
|
if (_config.style) {
|
_config.style = {..._config.style, minHeight: (config.plot.height || 400)}
|
} else {
|
_config.style = {minHeight: (config.plot.height || 400)}
|
}
|
|
let transfield = {}
|
_config.columns.forEach(col => {
|
if (col.field) {
|
transfield[col.field] = col.label
|
}
|
})
|
|
_config.plot.color = _config.plot.color || 'rgba(0, 0, 0, 0.85)'
|
|
if (_config.plot.enabled === 'true' && _config.plot.customs && _config.plot.customs.length > 0) {
|
let colors = new Map()
|
let colorIndex = 0
|
let limit = chartColors.length
|
|
if (_config.plot.colors && _config.plot.colors.length > 0) {
|
_config.plot.colors.forEach(item => {
|
if (!colors.has(item.type)) {
|
colors.set(item.type, item.color)
|
}
|
})
|
}
|
|
let axisIndex = 0
|
let hasBar = false
|
let fields = []
|
let legends = []
|
|
_config.plot.customs.forEach(item => {
|
item.name = transfield[item.type] || item.type
|
item.chartType = item.shape ? (item.shape[0] || 'bar') : 'bar'
|
item.shape = item.shape ? (item.shape[1] || '') : ''
|
|
if (colors.get(item.type)) {
|
item.color = colors.get(item.type)
|
} else {
|
item.color = chartColors[colorIndex % limit]
|
colorIndex++
|
}
|
|
if (item.chartType === 'bar' && !hasBar) {
|
hasBar = true
|
} else if (item.chartType === 'bar') {
|
item.chartType = 'line'
|
item.shape = 'smooth'
|
}
|
|
if (item.axis === 'true' && axisIndex < 2) {
|
if (axisIndex === 0) {
|
item.axis = { grid: {style: { fill: _config.plot.color }}, title: { style: { fill: _config.plot.color } }, label: {style: { fill: _config.plot.color }} }
|
fields.unshift(item)
|
} else {
|
item.axis = { grid: null, title: {style: { fill: _config.plot.color }}, label: {style: { fill: _config.plot.color }} }
|
fields.splice(1, 0, item)
|
}
|
axisIndex++
|
} else {
|
item.axis = { grid: null, title: null, label: null }
|
fields.push(item)
|
}
|
|
|
legends.push({
|
value: item.name,
|
name: item.name,
|
marker: { symbol: item.chartType === 'bar' ? 'square' : 'hyphen', style: { stroke: item.color,fill: item.color, r: 5, lineWidth: 2 } }
|
})
|
})
|
_config.plot.customs = fields
|
_config.plot.legends = legends
|
_config.plot.hasBar = hasBar
|
} else {
|
_config.plot.enabled = 'false'
|
}
|
|
this.setState({
|
config: _config,
|
data: _data,
|
vFields: vFields,
|
vstFields: vstFields,
|
arr_field: _config.columns.map(col => col.field).join(','),
|
plot: _config.plot,
|
sync: _sync,
|
title: config.plot.title,
|
search: Utils.initMainSearch(config.search),
|
transfield,
|
showHeader
|
}, () => {
|
if (config.setting.sync !== 'true' && config.setting.onload === 'true') {
|
this.loadData()
|
} else if (config.setting.sync === 'true' && _data) {
|
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] || []
|
}
|
|
this.setState({sync: false, data: _data}, () => {
|
this.handleData()
|
})
|
} else if (!is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
|
if (config.setting.syncRefresh === 'true') {
|
this.setState({}, () => {
|
this.loadData()
|
})
|
}
|
}
|
}
|
|
shouldComponentUpdate (nextProps, nextState) {
|
return !is(fromJS(this.state), fromJS(nextState))
|
}
|
|
handleData = () => {
|
const { data, plot } = this.state
|
|
if (plot.datatype === 'statistics') {
|
let result = this.getStaticMsg(data)
|
|
this.setState({
|
chartData: result.data,
|
chartFields: result.chartFields,
|
selectFields: result.selectFields
|
}, () => {
|
let _element = document.getElementById(this.state.chartId)
|
if (_element) {
|
_element.innerHTML = ''
|
}
|
this.viewrender()
|
})
|
} else {
|
let _element = document.getElementById(this.state.chartId)
|
if (_element) {
|
_element.innerHTML = ''
|
}
|
this.viewrender()
|
}
|
}
|
|
async loadData () {
|
const { mainSearch, BID, menuType, dataManager } = this.props
|
const { config, arr_field, search } = this.state
|
|
let searches = fromJS(search).toJS()
|
if (mainSearch && mainSearch.length > 0) { // 主表搜索条件
|
let keys = searches.map(item => item.key)
|
mainSearch.forEach(item => {
|
if (!keys.includes(item.key)) {
|
searches.push(item)
|
}
|
})
|
}
|
|
this.setState({
|
loading: true
|
})
|
|
let _orderBy = config.setting.order || ''
|
let param = UtilsDM.getQueryDataParams(config.setting, arr_field, searches, _orderBy, '', '', BID, menuType, dataManager)
|
|
let result = await Api.genericInterface(param)
|
if (result.status) {
|
this.setState({
|
data: result.data,
|
loading: false
|
}, () => {
|
this.handleData()
|
})
|
} else {
|
this.setState({
|
loading: false
|
})
|
notification.error({
|
top: 92,
|
message: result.message,
|
duration: 10
|
})
|
}
|
}
|
|
/**
|
* @description 图表数据预处理
|
* 1、通过显示列进行数据类型转换
|
* 2、重复数据:取平均值、累计、去重
|
* 3、柱状图数据补齐
|
*/
|
getdata = () => {
|
const { data, plot, vFields } = this.state
|
|
if (!data) {
|
this.setState({empty: true})
|
return []
|
}
|
|
let _data = []
|
let _cdata = fromJS(data).toJS()
|
|
if (plot.repeat === 'average') {
|
let _mdata = new Map()
|
_cdata.forEach(item => {
|
vFields.forEach(col => {
|
if (typeof(item[col.field]) !== 'number') {
|
item[col.field] = parseFloat(item[col.field])
|
if (isNaN(item[col.field])) {
|
item[col.field] = 0
|
}
|
}
|
if (col.show === 'percent') {
|
item[col.field] = item[col.field] * 100
|
}
|
})
|
|
if (item[plot.Xaxis] && !_mdata.has(item[plot.Xaxis])) {
|
item.$count = 1
|
_mdata.set(item[plot.Xaxis], item)
|
} else if (item[plot.Xaxis]) {
|
let _item = _mdata.get(item[plot.Xaxis])
|
_item.$count++
|
vFields.forEach(col => {
|
_item[col.field] += item[col.field]
|
})
|
_mdata.set(item[plot.Xaxis], _item)
|
}
|
})
|
|
_data = [..._mdata.values()]
|
_data = _data.map(item => {
|
vFields.forEach(col => {
|
item[col.field] = item[col.field] / item.$count
|
item[col.field] = item[col.field].toFixed(col.decimal)
|
item[col.field] = +item[col.field]
|
})
|
return item
|
})
|
} else if (plot.repeat === 'cumsum') {
|
let _mdata = new Map()
|
_cdata.forEach(item => {
|
vFields.forEach(col => {
|
if (typeof(item[col.field]) !== 'number') {
|
item[col.field] = parseFloat(item[col.field])
|
if (isNaN(item[col.field])) {
|
item[col.field] = 0
|
}
|
}
|
if (col.show === 'percent') {
|
item[col.field] = item[col.field] * 100
|
}
|
})
|
|
if (item[plot.Xaxis] && !_mdata.has(item[plot.Xaxis])) {
|
_mdata.set(item[plot.Xaxis], item)
|
} else if (item[plot.Xaxis]) {
|
let _item = _mdata.get(item[plot.Xaxis])
|
vFields.forEach(col => {
|
_item[col.field] += item[col.field]
|
})
|
_mdata.set(item[plot.Xaxis], _item)
|
}
|
})
|
|
_data = [..._mdata.values()]
|
_data = _data.map(item => {
|
vFields.forEach(col => {
|
item[col.field] = item[col.field].toFixed(col.decimal)
|
item[col.field] = +item[col.field]
|
})
|
return item
|
})
|
} else { // plot.repeat === 'unrepeat'
|
let _mdata = new Map()
|
_cdata.forEach(item => {
|
if (item[plot.Xaxis] && !_mdata.has(item[plot.Xaxis])) {
|
vFields.forEach(col => {
|
if (typeof(item[col.field]) !== 'number') {
|
item[col.field] = parseFloat(item[col.field])
|
if (isNaN(item[col.field])) {
|
item[col.field] = 0
|
}
|
}
|
if (col.show === 'percent') {
|
item[col.field] = item[col.field] * 100
|
}
|
item[col.field] = item[col.field].toFixed(col.decimal)
|
item[col.field] = +item[col.field]
|
})
|
_mdata.set(item[plot.Xaxis], item)
|
}
|
})
|
|
_data = [..._mdata.values()]
|
}
|
|
this.setState({empty: _data.length === 0})
|
return _data
|
}
|
|
/**
|
* @description 统计数据预处理,动态生成统计字段并进行数据转换
|
*/
|
getStaticMsg = (data) => {
|
const { plot, vstFields } = this.state
|
|
let percent = false
|
let decimal = 0
|
|
if (plot.show === 'percent') {
|
percent = true
|
}
|
if (vstFields) {
|
decimal = vstFields.decimal
|
}
|
|
if (!data) {
|
this.setState({empty: true})
|
return {data: [], chartFields: [], selectFields: []}
|
}
|
|
let _data = []
|
let _cdata = fromJS(data).toJS()
|
let _chartFields = []
|
let _selectFields = []
|
|
if (plot.repeat === 'average') {
|
let _mdata = new Map()
|
_cdata.forEach(item => {
|
if (!item[plot.InfoType] || !item[plot.Xaxis]) return
|
_chartFields.push(item[plot.InfoType])
|
|
item.$uuid = item[plot.InfoType] + item[plot.Xaxis]
|
if (typeof(item[plot.InfoValue]) !== 'number') {
|
item[plot.InfoValue] = parseFloat(item[plot.InfoValue])
|
if (isNaN(item[plot.InfoValue])) {
|
item[plot.InfoValue] = 0
|
}
|
}
|
if (percent) {
|
item[plot.InfoValue] = item[plot.InfoValue] * 100
|
}
|
|
if (!_mdata.has(item.$uuid)) {
|
item.$count = 1
|
_mdata.set(item.$uuid, item)
|
} else {
|
let _item = _mdata.get(item.$uuid)
|
_item.$count++
|
_item[plot.InfoValue] += item[plot.InfoValue]
|
_mdata.set(item.$uuid, _item)
|
}
|
})
|
|
_data = [..._mdata.values()]
|
_data = _data.map(item => {
|
item[plot.InfoValue] = item[plot.InfoValue] / item.$count
|
item[plot.InfoValue] = item[plot.InfoValue].toFixed(decimal)
|
item[plot.InfoValue] = +item[plot.InfoValue]
|
|
return item
|
})
|
} else if (plot.repeat === 'cumsum') {
|
let _mdata = new Map()
|
_cdata.forEach(item => {
|
if (!item[plot.InfoType] || !item[plot.Xaxis]) return
|
_chartFields.push(item[plot.InfoType])
|
|
item.$uuid = item[plot.InfoType] + item[plot.Xaxis]
|
|
if (typeof(item[plot.InfoValue]) !== 'number') {
|
item[plot.InfoValue] = parseFloat(item[plot.InfoValue])
|
if (isNaN(item[plot.InfoValue])) {
|
item[plot.InfoValue] = 0
|
}
|
}
|
if (percent) {
|
item[plot.InfoValue] = item[plot.InfoValue] * 100
|
}
|
|
if (!_mdata.has(item.$uuid)) {
|
_mdata.set(item.$uuid, item)
|
} else {
|
let _item = _mdata.get(item.$uuid)
|
_item[plot.InfoValue] += item[plot.InfoValue]
|
_mdata.set(item.$uuid, _item)
|
}
|
})
|
|
_data = [..._mdata.values()]
|
_data = _data.map(item => {
|
item[plot.InfoValue] = item[plot.InfoValue].toFixed(decimal)
|
item[plot.InfoValue] = +item[plot.InfoValue]
|
|
return item
|
})
|
} else { // plot.repeat === 'unrepeat'
|
let _mdata = new Map()
|
_cdata.forEach(item => {
|
if (!item[plot.InfoType] || !item[plot.Xaxis]) return
|
_chartFields.push(item[plot.InfoType])
|
|
item.$uuid = item[plot.InfoType] + item[plot.Xaxis]
|
|
if (!_mdata.has(item.$uuid)) {
|
if (typeof(item[plot.InfoValue]) !== 'number') {
|
item[plot.InfoValue] = parseFloat(item[plot.InfoValue])
|
if (isNaN(item[plot.InfoValue])) {
|
item[plot.InfoValue] = 0
|
}
|
}
|
if (percent) {
|
item[plot.InfoValue] = item[plot.InfoValue] * 100
|
}
|
|
item[plot.InfoValue] = item[plot.InfoValue].toFixed(decimal)
|
item[plot.InfoValue] = +item[plot.InfoValue]
|
|
_mdata.set(item.$uuid, item)
|
}
|
})
|
|
_data = [..._mdata.values()]
|
}
|
|
_chartFields = Array.from(new Set(_chartFields))
|
|
if (plot.InfoDefNumber >= _chartFields.length) {
|
_selectFields = _chartFields
|
} else {
|
_selectFields = _chartFields.slice(0, plot.InfoDefNumber)
|
}
|
|
return {data: _data, chartFields: _chartFields, selectFields: _selectFields}
|
}
|
|
/**
|
* @description 获取统计图表展示数据,通过选择类型筛选
|
*/
|
getStaticData = () => {
|
const { plot, chartData, chartFields, selectFields } = this.state
|
|
let _data = []
|
if (selectFields.length === chartFields.length) {
|
_data = chartData
|
} else {
|
_data = chartData.filter(item => selectFields.includes(item[plot.InfoType]))
|
}
|
|
this.setState({empty: _data.length === 0})
|
return _data
|
}
|
|
/**
|
* @description 图表渲染分组
|
*/
|
viewrender = () => {
|
const { plot } = this.state
|
|
if (plot.chartType === 'line') {
|
this.linerender()
|
} else if (plot.chartType === 'bar') {
|
this.barrender()
|
}
|
}
|
|
/**
|
* @description 折线图渲染
|
*/
|
linerender = () => {
|
const { plot, transfield } = this.state
|
|
let _data = []
|
let _valfield = 'value'
|
let _typefield = 'key'
|
|
let colors = new Map()
|
let colorIndex = 0
|
|
if (plot.datatype === 'statistics') {
|
_valfield = plot.InfoValue
|
_typefield = plot.InfoType
|
|
if (plot.colors && plot.colors.length > 0) { // 颜色设置
|
plot.colors.forEach(item => {
|
if (!colors.has(item.type)) {
|
colors.set(item.type, item.color)
|
}
|
})
|
}
|
|
_data = this.getStaticData()
|
} else {
|
let data = this.getdata()
|
|
if (plot.enabled === 'true') {
|
this.customrender(data)
|
return
|
}
|
|
if (plot.colors && plot.colors.length > 0) { // 颜色设置
|
plot.colors.forEach(item => {
|
if (!colors.has(transfield[item.type])) {
|
colors.set(transfield[item.type], item.color)
|
}
|
})
|
}
|
|
const ds = new DataSet()
|
const dv = ds.createView().source(data)
|
|
dv.transform({
|
type: 'fold',
|
fields: [...plot.Yaxis],
|
key: 'key', // key字段
|
value: _valfield, // value字段
|
// retains: [], // 保留字段集,默认为除 fields 以外的所有字段
|
})
|
|
if (plot.Xaxis) {
|
dv.transform({
|
type: 'map',
|
callback(row) {
|
row.key = transfield[row.key]
|
return row
|
},
|
})
|
}
|
|
_data = dv.rows
|
}
|
|
const chart = new Chart({
|
container: this.state.chartId,
|
autoFit: true,
|
height: plot.height || 400
|
})
|
|
chart.data(_data)
|
|
if (plot.coordinate !== 'polar') {
|
chart.scale(plot.Xaxis, {
|
range: [0, 1]
|
})
|
}
|
chart.scale(_valfield, {
|
nice: true,
|
range: [0, 0.93]
|
})
|
|
// 坐标轴格式化
|
chart.axis(plot.Xaxis, {
|
label: {
|
formatter: (val) => {
|
if (!val || /^\s*$/.test(val)) return val
|
let _val = `${val}`
|
if (_val.length <= 11) return val
|
return _val.substring(0, 8) + '...'
|
},
|
style: { fill: plot.color }
|
},
|
line: { style: { fill: plot.color } }
|
})
|
chart.axis(_valfield, { grid: { style: { fill: plot.color } }, label: { style: { fill: plot.color } } })
|
|
if (!plot.legend || plot.legend === 'hidden') {
|
chart.legend(false)
|
} else {
|
chart.legend({
|
position: plot.legend,
|
itemName: { style: { fill: plot.color } }
|
})
|
}
|
|
if (plot.tooltip !== 'true') {
|
chart.tooltip(false)
|
} else {
|
chart.tooltip({
|
shared: true
|
})
|
}
|
|
if (plot.transpose === 'true') {
|
chart.coordinate().transpose()
|
}
|
|
if (plot.coordinate === 'polar') {
|
chart.coordinate('polar', {
|
innerRadius: 0.1,
|
radius: 0.8
|
})
|
}
|
|
let _chart = chart
|
.line()
|
.position(`${plot.Xaxis}*${_valfield}`)
|
.shape(plot.shape || 'smooth')
|
.tooltip(`${plot.Xaxis}*${_valfield}*${_typefield}`, (name, value, type) => {
|
return {
|
name: type,
|
value: plot.show === 'percent' ? value + '%' : value
|
}
|
})
|
|
if (plot.colors && plot.colors.length > 0) {
|
let limit = chartColors.length
|
_chart.color(_typefield, (key) => {
|
if (colors.get(key)) {
|
return colors.get(key)
|
} else {
|
colors.set(key, chartColors[colorIndex % limit])
|
colorIndex++
|
}
|
})
|
} else {
|
_chart.color(_typefield)
|
}
|
if (plot.label === 'true') {
|
_chart.label(_valfield, (value) => {
|
if (plot.show === 'percent') {
|
value = value + '%'
|
}
|
return {
|
content: value,
|
style: {
|
fill: plot.color
|
}
|
}
|
})
|
}
|
|
if (plot.point === 'true' && plot.colors && plot.colors.length > 0) {
|
let limit = chartColors.length
|
chart
|
.point()
|
.position(`${plot.Xaxis}*${_valfield}`)
|
.color(_typefield, (key) => {
|
if (colors.get(key)) {
|
return colors.get(key)
|
} else {
|
colors.set(key, chartColors[colorIndex % limit])
|
colorIndex++
|
}
|
})
|
.size(3)
|
.shape('circle')
|
} else if (plot.point === 'true') {
|
chart
|
.point()
|
.position(`${plot.Xaxis}*${_valfield}`)
|
.color(_typefield)
|
.size(3)
|
.shape('circle')
|
}
|
|
chart.render()
|
}
|
|
/**
|
* @description 自定义渲染
|
*/
|
customrender = (data) => {
|
const { plot } = this.state
|
const ds = new DataSet()
|
const dv = ds.createView().source(data)
|
dv.transform({
|
type: 'map',
|
callback(row) {
|
plot.customs.forEach(line => {
|
row[line.name] = row[line.type]
|
})
|
return row
|
}
|
})
|
|
const chart = new Chart({
|
container: this.state.chartId,
|
autoFit: true,
|
height: plot.height || 400
|
})
|
|
let _data = dv.rows
|
// dodge is not support linear attribute, please use category attribute! 时间格式
|
if (_data[0] && _data[0][plot.Xaxis] && /^\d{4}-\d{2}-\d{2}(\s\d{2}:\d{2}:\d{2})?/.test(_data[0][plot.Xaxis])) {
|
for (let i = 1; i < 12; i++) {
|
if (_data[i] && _data[i][plot.Xaxis] === _data[0][plot.Xaxis]) {
|
_data[i][plot.Xaxis] += ' '
|
} else {
|
break;
|
}
|
}
|
_data[0][plot.Xaxis] += ' '
|
}
|
|
chart.data(_data)
|
|
if (!plot.hasBar) {
|
chart.scale(plot.Xaxis, {
|
range: [0, 1]
|
})
|
}
|
|
// 坐标轴格式化
|
chart.axis(plot.Xaxis, {
|
label: {
|
formatter: (val) => {
|
if (!val || /^\s*$/.test(val)) return val
|
let _val = `${val}`
|
if (_val.length <= 11) return val
|
return _val.substring(0, 8) + '...'
|
},
|
style: { fill: plot.color }
|
},
|
line: { style: { fill: plot.color } }
|
})
|
|
if (!plot.legend || plot.legend === 'hidden') {
|
chart.legend(false)
|
} else {
|
chart.legend({
|
custom: true,
|
position: plot.legend,
|
items: plot.legends,
|
itemName: { style: { fill: plot.color } }
|
})
|
}
|
|
if (plot.tooltip !== 'true') {
|
chart.tooltip(false)
|
} else {
|
chart.tooltip({
|
shared: true
|
})
|
}
|
|
plot.customs.forEach((item, i) => {
|
chart.axis(item.name, item.axis)
|
|
chart.scale(item.name, {
|
nice: true,
|
range: [0, 0.93]
|
})
|
|
if (item.chartType === 'bar') {
|
let _chart = chart
|
.interval()
|
.position(`${plot.Xaxis}*${item.name}`)
|
.color(item.color)
|
.shape(item.shape)
|
.tooltip(`${item.name}`, (value) => {
|
return {
|
name: item.name,
|
value: plot.show === 'percent' ? value + '%' : value
|
}
|
})
|
|
if (plot.barSize) {
|
_chart.size(plot.barSize || 35)
|
}
|
if (item.label === 'true') {
|
_chart.label(item.name, (value) => {
|
if (plot.show === 'percent') {
|
value = value + '%'
|
}
|
return {
|
content: value,
|
style: {
|
fill: plot.color
|
}
|
}
|
})
|
}
|
} else if (item.chartType === 'line') {
|
let _chart = chart
|
.line()
|
.position(`${plot.Xaxis}*${item.name}`)
|
.color(item.color)
|
.shape(item.shape)
|
.tooltip(`${item.name}`, (value) => {
|
return {
|
name: item.name,
|
value: plot.show === 'percent' ? value + '%' : value
|
}
|
})
|
|
if (item.label === 'true') {
|
_chart.label(item.name, (value) => {
|
if (plot.show === 'percent') {
|
value = value + '%'
|
}
|
return {
|
content: value,
|
style: {
|
fill: plot.color
|
}
|
}
|
})
|
}
|
|
if (plot.point === 'true') {
|
chart
|
.point()
|
.position(`${plot.Xaxis}*${item.name}`)
|
.color(item.color)
|
.size(3)
|
.shape('circle')
|
}
|
}
|
})
|
|
chart.render()
|
}
|
|
/**
|
* @description 柱状图渲染
|
*/
|
barrender = () => {
|
const { plot, transfield } = this.state
|
|
let _data = []
|
let _valfield = 'value'
|
let _typefield = 'key'
|
|
let colors = new Map()
|
let colorIndex = 0
|
|
if (plot.datatype === 'statistics') {
|
_valfield = plot.InfoValue
|
_typefield = plot.InfoType
|
|
if (plot.colors && plot.colors.length > 0) { // 颜色设置
|
plot.colors.forEach(item => {
|
if (!colors.has(item.type)) {
|
colors.set(item.type, item.color)
|
}
|
})
|
}
|
|
_data = this.getStaticData()
|
} else {
|
let data = this.getdata()
|
|
if (plot.enabled === 'true') {
|
this.customrender(data)
|
return
|
}
|
|
if (plot.colors && plot.colors.length > 0) { // 颜色设置
|
plot.colors.forEach(item => {
|
if (!colors.has(transfield[item.type])) {
|
colors.set(transfield[item.type], item.color)
|
}
|
})
|
}
|
|
const ds = new DataSet()
|
const dv = ds.createView().source(data)
|
|
dv.transform({
|
type: 'fold',
|
fields: [...plot.Yaxis],
|
key: 'key',
|
value: _valfield
|
})
|
|
if (plot.Xaxis) {
|
dv.transform({
|
type: 'map',
|
callback(row) {
|
row.key = transfield[row.key]
|
return row
|
},
|
})
|
}
|
|
_data = dv.rows
|
}
|
|
const chart = new Chart({
|
container: this.state.chartId,
|
autoFit: true,
|
height: plot.height || 400
|
})
|
|
// dodge is not support linear attribute, please use category attribute! 时间格式
|
if (_data[0] && _data[0][plot.Xaxis] && /^\d{4}-\d{2}-\d{2}(\s\d{2}:\d{2}:\d{2})?/.test(_data[0][plot.Xaxis])) {
|
for (let i = 1; i < 12; i++) {
|
if (_data[i] && _data[i][plot.Xaxis] === _data[0][plot.Xaxis]) {
|
_data[i][plot.Xaxis] += ' '
|
} else {
|
break;
|
}
|
}
|
_data[0][plot.Xaxis] += ' '
|
}
|
|
chart.data(_data)
|
|
chart.scale(_valfield, {
|
nice: true,
|
range: [0, 0.93]
|
})
|
|
// 坐标轴格式化
|
chart.axis(plot.Xaxis, {
|
label: {
|
formatter: (val) => {
|
if (!val || /^\s*$/.test(val)) return val
|
let _val = `${val}`
|
if (_val.length <= 11) return val
|
return _val.substring(0, 8) + '...'
|
},
|
style: { fill: plot.color }
|
},
|
line: { style: { fill: plot.color } }
|
})
|
chart.axis(_valfield, { grid: { style: { fill: plot.color } }, label: { style: { fill: plot.color } } })
|
|
if (!plot.legend || plot.legend === 'hidden') {
|
chart.legend(false)
|
} else {
|
chart.legend({
|
position: plot.legend,
|
itemName: { style: { fill: plot.color } }
|
})
|
}
|
|
if (plot.tooltip !== 'true') {
|
chart.tooltip(false)
|
} else {
|
chart.tooltip({
|
showMarkers: false,
|
shared: true
|
})
|
}
|
|
if (plot.transpose === 'true') {
|
chart.coordinate().transpose()
|
}
|
|
if (plot.coordinate === 'polar') {
|
chart.coordinate('polar', {
|
innerRadius: 0.1,
|
radius: 0.8
|
})
|
}
|
|
if (plot.adjust !== 'stack') {
|
let _chart = chart
|
.interval()
|
.position(`${plot.Xaxis}*${_valfield}`)
|
.adjust([
|
{
|
type: 'dodge',
|
marginRatio: 0
|
}
|
])
|
.shape(plot.shape || 'rect')
|
.tooltip(`${plot.Xaxis}*${_valfield}*${_typefield}`, (name, value, type) => {
|
return {
|
name: type,
|
value: plot.show === 'percent' ? value + '%' : value
|
}
|
})
|
|
if (plot.colors && plot.colors.length > 0) {
|
let limit = chartColors.length
|
_chart.color(_typefield, (key) => {
|
if (colors.get(key)) {
|
return colors.get(key)
|
} else {
|
colors.set(key, chartColors[colorIndex % limit])
|
colorIndex++
|
}
|
})
|
} else {
|
_chart.color(_typefield)
|
}
|
if (plot.label === 'true') {
|
_chart.label(_valfield, (value) => {
|
if (plot.show === 'percent') {
|
value = value + '%'
|
}
|
return {
|
content: value,
|
style: {
|
fill: plot.color
|
}
|
}
|
})
|
}
|
|
if (plot.barSize || plot.correction) {
|
_chart.size(plot.barSize || 35)
|
}
|
} else if (plot.adjust === 'stack') {
|
let _chart = chart
|
.interval()
|
.position(`${plot.Xaxis}*${_valfield}`)
|
.adjust('stack')
|
.shape(plot.shape || 'rect')
|
.tooltip(`${plot.Xaxis}*${_valfield}*${_typefield}`, (name, value, type) => {
|
return {
|
name: type,
|
value: plot.show === 'percent' ? value + '%' : value
|
}
|
})
|
|
if (plot.colors && plot.colors.length > 0) {
|
let limit = chartColors.length
|
_chart.color(_typefield, (key) => {
|
if (colors.get(key)) {
|
return colors.get(key)
|
} else {
|
colors.set(key, chartColors[colorIndex % limit])
|
colorIndex++
|
}
|
})
|
} else {
|
_chart.color(_typefield)
|
}
|
if (plot.label === 'true') {
|
_chart.label(_valfield, (value) => {
|
if (plot.show === 'percent') {
|
value = value + '%'
|
}
|
return {
|
content: value,
|
style: {
|
fill: plot.color
|
}
|
}
|
})
|
}
|
|
if (plot.barSize || plot.correction) {
|
_chart.size(plot.barSize || 35)
|
}
|
}
|
|
if (plot.linkmenu && plot.linkmenu.length > 0) {
|
let menu_id = plot.linkmenu.slice(-1)[0]
|
let menu = this.props.permMenus.filter(m => m.MenuID === menu_id)[0] || ''
|
|
chart.on('element:dblclick', (ev) => {
|
if (!menu) {
|
notification.warning({
|
top: 92,
|
message: '菜单已删除或没有访问权限!',
|
duration: 5
|
})
|
return
|
}
|
|
try {
|
let data = ev.data.data
|
let primaryId = ''
|
if (this.state.config.setting.primaryKey && data) {
|
primaryId = data[this.state.config.setting.primaryKey] || ''
|
}
|
|
let newtab = {
|
...menu,
|
selected: true,
|
param: {
|
BID: primaryId,
|
data: data
|
}
|
}
|
|
let tabs = this.props.tabviews.filter(tab => {
|
tab.selected = false
|
return tab.MenuID !== newtab.MenuID
|
})
|
|
if (this.props.tabviews.length !== tabs.length) {
|
this.props.modifyTabview(fromJS(tabs).toJS())
|
}
|
|
this.setState({}, () => {
|
tabs.push(newtab)
|
this.props.modifyTabview(tabs)
|
})
|
} catch {
|
console.warn('菜单打开失败!')
|
}
|
})
|
}
|
|
chart.render()
|
}
|
|
/**
|
* @description 统计图表,统计类型切换
|
*/
|
handleChange = (val) => {
|
this.setState({selectFields: val}, () => {
|
let _element = document.getElementById(this.state.chartId)
|
if (_element) {
|
_element.innerHTML = ''
|
}
|
this.viewrender()
|
})
|
}
|
|
render() {
|
const { BID } = this.props
|
const { showHeader, config, loading, title, plot, empty, chartFields, selectFields } = this.state
|
|
return (
|
<div className="custom-line-chart-plot-box" style={config.style}>
|
{loading ?
|
<div className="loading-mask">
|
<div className="ant-spin-blur"></div>
|
<Spin />
|
</div> : null
|
}
|
{showHeader ? <div className="chart-header" style={config.headerStyle}>
|
<span className="chart-title">{title}</span>
|
{/* <searchLine /> */}
|
{plot.datatype === 'statistics' && chartFields.length > 0 ? <Select
|
mode="multiple"
|
showSearch
|
showArrow={true}
|
value={selectFields}
|
onChange={this.handleChange}
|
maxTagCount={0}
|
maxTagPlaceholder={(option) => <div className="type-label">{option.join('、')}</div>}
|
>
|
{chartFields.map((item, i) => <Select.Option key={i} value={item}>{item}</Select.Option>)}
|
</Select> : null}
|
</div> : null}
|
<div className="canvas-wrap">
|
<div className={'chart-action ' + (plot.title ? 'with-title' : '')}>
|
{config.action.map(item => {
|
if (item.OpenType === 'excelOut') {
|
return (
|
<ExcelOutButton
|
key={item.uuid}
|
BID={BID}
|
btn={item}
|
show="icon"
|
setting={config.setting}
|
getexceloutparam={this.props.getexceloutparam}
|
updateStatus={() => {}}
|
/>
|
)
|
} else {
|
return (
|
<ExcelInButton
|
key={item.uuid}
|
BID={BID}
|
btn={item}
|
show="icon"
|
setting={config.setting}
|
updateStatus={() => {}}
|
/>
|
)
|
}
|
})}
|
</div>
|
<div className={'canvas' + (empty ? ' empty' : '')} id={this.state.chartId}></div>
|
</div>
|
{empty ? <Empty description={false}/> : null}
|
</div>
|
)
|
}
|
}
|
|
const mapStateToProps = (state) => {
|
return {
|
tabviews: state.tabviews,
|
permMenus: state.permMenus,
|
}
|
}
|
|
const mapDispatchToProps = (dispatch) => {
|
return {
|
modifyTabview: (tabviews) => dispatch(modifyTabview(tabviews))
|
}
|
}
|
|
export default connect(mapStateToProps, mapDispatchToProps)(LineChart)
|