| | |
| | | import DataSet from '@antv/data-set' |
| | | import { Spin, Empty, Select } from 'antd' |
| | | |
| | | import asyncComponent from './asyncButtonComponent' |
| | | import Utils from '@/utils/utils.js' |
| | | import zhCN from '@/locales/zh-CN/model.js' |
| | | import enUS from '@/locales/en-US/model.js' |
| | | import zhCN from '@/locales/zh-CN/main.js' |
| | | import enUS from '@/locales/en-US/main.js' |
| | | 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 = { |
| | | plot: PropTypes.object, |
| | | data: PropTypes.array, |
| | | loading: PropTypes.bool, |
| | | config: PropTypes.object |
| | | BID: PropTypes.any, // 父级Id |
| | | Tab: PropTypes.any, // 标签信息 |
| | | plot: PropTypes.object, // 图标设置信息 |
| | | data: PropTypes.array, // 图表传入数据 |
| | | loading: PropTypes.bool, // 数据加载中 |
| | | config: PropTypes.object, // 页面配置信息 |
| | | } |
| | | |
| | | state = { |
| | | dict: (!localStorage.getItem('lang') || localStorage.getItem('lang') === 'zh-CN') ? zhCN : enUS, |
| | | empty: true, |
| | | chartId: Utils.getuuid(), |
| | | chartData: [], |
| | | chartFields: [], |
| | | selectFields: [] |
| | | dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS, // 字典 |
| | | empty: true, // 图表数据为空 |
| | | actions: [], // 图表绑定的按钮组 |
| | | chartId: Utils.getuuid(), // 图表Id |
| | | chartData: [], // 图表数据 |
| | | chartFields: [], // 统计图表生成字段集 |
| | | selectFields: [], // 统计图表选择字段 |
| | | percentFields: [] // 设置为百分比的字段,tooltip时增加% |
| | | } |
| | | |
| | | /** |
| | | * @description 校验图表的按钮组,如果为统计图表,计算图表字段 |
| | | */ |
| | | componentDidMount () { |
| | | const { plot, data } = this.props |
| | | |
| | | const { plot, data, config } = this.props |
| | | let _state = {} |
| | | let actions = [] |
| | | let percentFields = [] |
| | | |
| | | config.action.forEach(item => { |
| | | if (!plot.actions || plot.actions.length === 0) return |
| | | if (!(item.OpenType === 'excelOut' || (item.OpenType === 'excelIn' && item.Ot === 'notRequired'))) return |
| | | if (plot.actions.includes(item.uuid)) { |
| | | actions.push(fromJS(item).toJS()) |
| | | } |
| | | }) |
| | | |
| | | if (plot.datatype === 'statistics' && (plot.chartType === 'line' || plot.chartType === 'bar')) { |
| | | let result = this.getStaticMsg(data) |
| | | _state.chartData = result.data |
| | | _state.chartFields = result.chartFields |
| | | _state.selectFields = result.selectFields |
| | | _state.actions = actions |
| | | |
| | | let _column = config.columns.filter(col => plot.InfoValue === col.field)[0] |
| | | |
| | | if (_column && _column.format === 'percent') { |
| | | percentFields.push(plot.InfoValue) |
| | | _state.percentFields = percentFields |
| | | } |
| | | |
| | | this.setState(_state, () => { |
| | | this.viewrender() |
| | | }) |
| | | } else { |
| | | this.viewrender() |
| | | if (plot.chartType === 'line' || plot.chartType === 'bar') { |
| | | try { |
| | | plot.Yaxis.forEach(yaxis => { |
| | | let _column = config.columns.filter(col => yaxis === col.field)[0] |
| | | if (_column && _column.format === 'percent') { |
| | | percentFields.push(_column.label) |
| | | } |
| | | }) |
| | | } catch (e) { |
| | | console.warn('Incorrect percentage setting') |
| | | } |
| | | } |
| | | this.setState({ actions, percentFields }, () => { |
| | | this.viewrender() |
| | | }) |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * @description 图表数据更新,刷新内容 |
| | | */ |
| | | UNSAFE_componentWillReceiveProps (nextProps) { |
| | | const { plot } = this.props |
| | | if (!is(fromJS(this.props.data), fromJS(nextProps.data))) { |
| | |
| | | return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState)) |
| | | } |
| | | |
| | | /** |
| | | * @description 图表数据预处理 |
| | | * 1、通过显示列进行数据类型转换 |
| | | * 2、重复数据:取平均值、累计、去重 |
| | | * 3、柱状图数据补齐 |
| | | */ |
| | | getdata = () => { |
| | | const { data, plot, config } = this.props |
| | | |
| | | let vFields = plot.Yaxis && typeof(plot.Yaxis) === 'string' ? [plot.Yaxis] : plot.Yaxis |
| | | let _columns = config.columns.filter(col => vFields.includes(col.field)) |
| | | |
| | |
| | | _data = [..._mdata.values()] |
| | | } |
| | | |
| | | if (plot.correction && plot.chartType === 'bar' && _data.length > 0 && _data.length < plot.correction) { |
| | | if (plot.enabled !== 'true' || (plot.customs && plot.customs.filter(cell => cell.chartType !== 'bar').length === 0)) { |
| | | let _num = plot.correction - _data.length |
| | | for (let i = 0; i < _num; i++) { |
| | | let _val = Array( i + 2 ).join(' ') |
| | | let _cell = {} |
| | | _cell[plot.Xaxis] = _val |
| | | _columns.forEach(col => { |
| | | _cell[col.field] = '' |
| | | }) |
| | | |
| | | _data.push(_cell) |
| | | } |
| | | } |
| | | } |
| | | |
| | | this.setState({empty: _data.length === 0}) |
| | | return _data |
| | | } |
| | | |
| | | /** |
| | | * @description 统计数据预处理,动态生成统计字段并进行数据转换 |
| | | */ |
| | | getStaticMsg = (data) => { |
| | | const { plot, config } = this.props |
| | | |
| | |
| | | |
| | | item.$uuid = item[plot.InfoType] + item[plot.Xaxis] |
| | | if (typeof(item[plot.InfoValue]) !== 'number') { |
| | | item[plot.InfoValue] = parseFloat(plot.InfoValue) |
| | | item[plot.InfoValue] = parseFloat(item[plot.InfoValue]) |
| | | if (isNaN(item[plot.InfoValue])) { |
| | | item[plot.InfoValue] = 0 |
| | | } |
| | |
| | | item.$uuid = item[plot.InfoType] + item[plot.Xaxis] |
| | | |
| | | if (typeof(item[plot.InfoValue]) !== 'number') { |
| | | item[plot.InfoValue] = parseFloat(plot.InfoValue) |
| | | item[plot.InfoValue] = parseFloat(item[plot.InfoValue]) |
| | | if (isNaN(item[plot.InfoValue])) { |
| | | item[plot.InfoValue] = 0 |
| | | } |
| | |
| | | |
| | | if (!_mdata.has(item.$uuid)) { |
| | | if (typeof(item[plot.InfoValue]) !== 'number') { |
| | | item[plot.InfoValue] = parseFloat(plot.InfoValue) |
| | | item[plot.InfoValue] = parseFloat(item[plot.InfoValue]) |
| | | if (isNaN(item[plot.InfoValue])) { |
| | | item[plot.InfoValue] = 0 |
| | | } |
| | |
| | | return {data: _data, chartFields: _chartFields, selectFields: _selectFields} |
| | | } |
| | | |
| | | /** |
| | | * @description 获取统计图表展示数据,通过选择类型筛选 |
| | | */ |
| | | getStaticData = () => { |
| | | const { plot } = this.props |
| | | const { chartData, chartFields, selectFields } = this.state |
| | |
| | | return _data |
| | | } |
| | | |
| | | /** |
| | | * @description 图表渲染分组 |
| | | */ |
| | | viewrender = () => { |
| | | const { plot } = this.props |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * @description 折线图渲染 |
| | | */ |
| | | linerender = () => { |
| | | const { plot, config } = this.props |
| | | const { percentFields } = this.state |
| | | |
| | | let _data = [] |
| | | let _valfield = 'value' |
| | | let _typefield = 'key' |
| | | let ispercent = false |
| | | |
| | | if (plot.datatype === 'statistics') { |
| | | _valfield = plot.InfoValue |
| | | _typefield = plot.InfoType |
| | | |
| | | if (percentFields.length > 0) { |
| | | ispercent = true |
| | | } |
| | | |
| | | _data = this.getStaticData() |
| | | } else { |
| | |
| | | }) |
| | | } |
| | | chart.scale(_valfield, { |
| | | nice: true |
| | | nice: true, |
| | | range: [0, 0.93] |
| | | }) |
| | | |
| | | // 坐标轴格式化 |
| | |
| | | formatter: (val) => { |
| | | if (!val || /^\s*$/.test(val)) return val |
| | | let _val = `${val}` |
| | | if (_val.length <= 10) return val |
| | | return _val.substring(0, 5) + '...' |
| | | if (_val.length <= 11) return val |
| | | return _val.substring(0, 8) + '...' |
| | | } |
| | | } |
| | | }) |
| | |
| | | .position(`${plot.Xaxis}*${_valfield}`) |
| | | .color(_typefield) |
| | | .shape(plot.shape || 'smooth') |
| | | .tooltip(`${plot.Xaxis}*${_valfield}*${_typefield}`, (name, value, type) => { |
| | | return { |
| | | name: type, |
| | | value: percentFields.includes(type) || ispercent ? value + '%' : value |
| | | } |
| | | }) |
| | | |
| | | if (plot.label === 'true') { |
| | | _chart.label(_valfield) |
| | |
| | | chart.render() |
| | | } |
| | | |
| | | /** |
| | | * @description 自定义渲染 |
| | | */ |
| | | customrender = (data, transfield) => { |
| | | const { plot } = this.props |
| | | const { percentFields } = this.state |
| | | |
| | | let barfields = [] |
| | | let fields = [] |
| | |
| | | formatter: (val) => { |
| | | if (!val || /^\s*$/.test(val)) return val |
| | | let _val = `${val}` |
| | | if (_val.length <= 10) return val |
| | | return _val.substring(0, 5) + '...' |
| | | if (_val.length <= 11) return val |
| | | return _val.substring(0, 8) + '...' |
| | | } |
| | | } |
| | | }) |
| | |
| | | label: null |
| | | }) |
| | | } |
| | | |
| | | |
| | | 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: percentFields.includes(item.name) ? value + '%' : value |
| | | } |
| | | }) |
| | | |
| | | if (item.label === 'true') { |
| | | _chart.label(item.name) |
| | |
| | | .position(`${plot.Xaxis}*${item.name}`) |
| | | .color(item.color) |
| | | .shape(item.shape) |
| | | .tooltip(`${item.name}`, (value) => { |
| | | return { |
| | | name: item.name, |
| | | value: percentFields.includes(item.name) ? value + '%' : value |
| | | } |
| | | }) |
| | | |
| | | if (item.label === 'true') { |
| | | _chart.label(item.name) |
| | |
| | | chart.render() |
| | | } |
| | | |
| | | /** |
| | | * @description 柱状图渲染 |
| | | */ |
| | | barrender = () => { |
| | | const { plot, config } = this.props |
| | | const { percentFields } = this.state |
| | | |
| | | let _data = [] |
| | | let _valfield = 'value' |
| | | let _typefield = 'key' |
| | | let ispercent = false |
| | | |
| | | if (plot.datatype === 'statistics') { |
| | | _valfield = plot.InfoValue |
| | | _typefield = plot.InfoType |
| | | |
| | | if (percentFields.length > 0) { |
| | | ispercent = true |
| | | } |
| | | |
| | | _data = this.getStaticData() |
| | | } else { |
| | |
| | | |
| | | // 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 |
| | | nice: true, |
| | | range: [0, 0.93] |
| | | }) |
| | | |
| | | // 坐标轴格式化 |
| | |
| | | formatter: (val) => { |
| | | if (!val || /^\s*$/.test(val)) return val |
| | | let _val = `${val}` |
| | | if (_val.length <= 10) return val |
| | | return _val.substring(0, 5) + '...' |
| | | if (_val.length <= 11) return val |
| | | return _val.substring(0, 8) + '...' |
| | | } |
| | | } |
| | | }) |
| | |
| | | let _chart = chart |
| | | .interval() |
| | | .position(`${plot.Xaxis}*${_valfield}`) |
| | | .size(30) |
| | | .color(_typefield) |
| | | .adjust([ |
| | | { |
| | |
| | | } |
| | | ]) |
| | | .shape(plot.shape || 'rect') |
| | | .tooltip(`${plot.Xaxis}*${_valfield}*${_typefield}`, (name, value, type) => { |
| | | return { |
| | | name: type, |
| | | value: percentFields.includes(type) || ispercent ? value + '%' : value |
| | | } |
| | | }) |
| | | |
| | | if (plot.label === 'true') { |
| | | _chart.label(_valfield) |
| | |
| | | let _chart = chart |
| | | .interval() |
| | | .position(`${plot.Xaxis}*${_valfield}`) |
| | | .size(30) |
| | | .color(_typefield) |
| | | .adjust('stack') |
| | | .shape(plot.shape || 'rect') |
| | | .tooltip(`${plot.Xaxis}*${_valfield}*${_typefield}`, (name, value, type) => { |
| | | return { |
| | | name: type, |
| | | value: percentFields.includes(type) || ispercent ? value + '%' : value |
| | | } |
| | | }) |
| | | |
| | | if (plot.label === 'true') { |
| | | _chart.label(_valfield) |
| | |
| | | chart.render() |
| | | } |
| | | |
| | | /** |
| | | * @description 饼图渲染 |
| | | */ |
| | | pierender = () => { |
| | | const { plot, config } = this.props |
| | | |
| | |
| | | } |
| | | }) |
| | | |
| | | if (plot.label === 'true') { |
| | | let setting = { |
| | | if (plot.label !== 'false') { |
| | | _chart.label('percent', { |
| | | layout: { type: 'pie-spider' }, |
| | | labelHeight: 20, |
| | | content: (data) => { |
| | | return `${data[plot.Xaxis]}: ${(data.percent * 100).toFixed(2)}%` |
| | | let val = data[plot.Xaxis] |
| | | if (val) { |
| | | val = `${val}` |
| | | if (val.length > 10) { |
| | | val = val.substring(0, 7) + '...' |
| | | } |
| | | } |
| | | return `${val}: ${(data.percent * 100).toFixed(2)}%` |
| | | }, |
| | | labelLine: { |
| | | style: { |
| | | lineWidth: 0.5, |
| | | }, |
| | | } |
| | | } |
| | | |
| | | if (plot.labelLayout === 'overlap') { |
| | | setting.type = 'pie' |
| | | setting.layout = { |
| | | type: 'overlap' |
| | | } |
| | | setting.offset = 0 |
| | | } |
| | | |
| | | _chart.label('percent', setting) |
| | | }) |
| | | } |
| | | |
| | | } else { |
| | | let _chart = chart |
| | | .interval() |
| | |
| | | if (plot.label === 'true') { |
| | | let setting = { |
| | | content: (data) => { |
| | | return `${data[plot.Xaxis]}: ${data[plot.Yaxis]}` |
| | | let val = data[plot.Xaxis] |
| | | if (val) { |
| | | val = `${val}` |
| | | if (val.length > 10) { |
| | | val = val.substring(0, 7) + '...' |
| | | } |
| | | } |
| | | return `${val}: ${data[plot.Yaxis]}` |
| | | } |
| | | } |
| | | |
| | |
| | | chart.render() |
| | | } |
| | | |
| | | /** |
| | | * @description 统计图表,统计类型切换 |
| | | */ |
| | | handleChange = (val) => { |
| | | this.setState({selectFields: val}, () => { |
| | | let _element = document.getElementById(this.state.chartId) |
| | |
| | | } |
| | | |
| | | render() { |
| | | const { plot, loading } = this.props |
| | | const { empty, chartFields, selectFields } = this.state |
| | | const { plot, loading, config, BID, Tab } = this.props |
| | | const { empty, chartFields, selectFields, actions } = this.state |
| | | |
| | | return ( |
| | | <div className="line-chart-plot-box"> |
| | |
| | | > |
| | | {chartFields.map((item, i) => <Select.Option key={i} value={item}>{item}</Select.Option>)} |
| | | </Select> : null} |
| | | <div className={'canvas' + (empty ? ' empty' : '')} style={{minHeight: plot.height ? plot.height : 400}} id={this.state.chartId}></div> |
| | | <div className="canvas-wrap"> |
| | | <div className={'chart-action ' + (plot.title ? 'with-title' : '')}> |
| | | {actions.map(item => { |
| | | if (item.OpenType === 'excelOut') { |
| | | return ( |
| | | <ExcelOutButton |
| | | key={item.uuid} |
| | | BID={BID} |
| | | Tab={Tab} |
| | | btn={item} |
| | | show="icon" |
| | | setting={config.setting} |
| | | /> |
| | | ) |
| | | } else { |
| | | return ( |
| | | <ExcelInButton |
| | | key={item.uuid} |
| | | BID={BID} |
| | | Tab={Tab} |
| | | btn={item} |
| | | show="icon" |
| | | setting={config.setting} |
| | | /> |
| | | ) |
| | | } |
| | | })} |
| | | </div> |
| | | <div className={'canvas' + (empty ? ' empty' : '')} style={{minHeight: plot.height ? plot.height : 400}} id={this.state.chartId}></div> |
| | | </div> |
| | | {empty ? <Empty description={false}/> : null} |
| | | </div> |
| | | ) |