From 5046d0d13dc6a8563b8e54e31913bc44cfa1072f Mon Sep 17 00:00:00 2001 From: king <18310653075@163.com> Date: 星期二, 26 四月 2022 19:23:18 +0800 Subject: [PATCH] 2022-04-26 --- src/tabviews/custom/components/chart/antv-dashboard/index.jsx | 310 +++++++++++++++++++++++++++++++++++++++++---------- 1 files changed, 248 insertions(+), 62 deletions(-) diff --git a/src/tabviews/custom/components/chart/antv-dashboard/index.jsx b/src/tabviews/custom/components/chart/antv-dashboard/index.jsx index d561d7d..280fa16 100644 --- a/src/tabviews/custom/components/chart/antv-dashboard/index.jsx +++ b/src/tabviews/custom/components/chart/antv-dashboard/index.jsx @@ -3,16 +3,15 @@ import { is, fromJS } from 'immutable' import { Chart, registerShape } from '@antv/g2' import { Spin, notification } from 'antd' +import { DownloadOutlined } from '@ant-design/icons' import moment from 'moment' import Api from '@/api' import Utils from '@/utils/utils.js' -import asyncComponent from '@/utils/asyncComponent' import UtilsDM from '@/utils/utils-datamanage.js' import MKEmitter from '@/utils/events.js' +import NormalHeader from '@/tabviews/custom/components/share/normalheader' import './index.scss' - -const NormalHeader = asyncComponent(() => import('@/tabviews/custom/components/share/normalheader')) registerShape('point', 'pointer', { draw(cfg, container) { @@ -45,6 +44,12 @@ }, }) +registerShape('point', 'hidden', { + draw(cfg, container) { + return container.addGroup({}) + } +}) + class DashboardChart extends Component { static propTpyes = { BID: PropTypes.any, // 鐖剁骇Id @@ -62,28 +67,33 @@ title: '', // 缁勪欢鏍囬 sync: false, // 鏄惁缁熶竴璇锋眰鏁版嵁 plot: null, // 鍥捐〃璁剧疆 - data: null, // 鏁版嵁 + data: {}, // 鏁版嵁 + chart: null } UNSAFE_componentWillMount () { const { config, data, initdata, BID } = this.props let _config = fromJS(config).toJS() - let _data = {} + let _data = null let _sync = _config.setting.sync === 'true' - + if (_sync && data) { - _data = data[_config.dataName] || {} - if (_data && Array.isArray(_data)) { - _data = _data[0] || {} - } + _data = data[config.dataName] || [] _sync = false } else if (_sync && initdata) { - _data = initdata || {} + _data = initdata || [] + _sync = false + } + + if (_config.subtype === 'ratioboard') { + _data = _data || [] + } else { if (_data && Array.isArray(_data)) { _data = _data[0] || {} + } else { + _data = {} } - _sync = false } _config.style.height = config.plot.height || 400 @@ -98,8 +108,10 @@ title: config.plot.title }, () => { if (config.setting.sync !== 'true' && config.setting.onload === 'true') { - this.loadData() - } else if (config.setting.sync === 'true' && _data) { + setTimeout(() => { + this.loadData() + }, _config.setting.delay || 0) + } else if (config.setting.sync === 'true') { this.handleData() } }) @@ -112,23 +124,30 @@ 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 (_data.hasOwnProperty(config.plot.valueField)) { - _data.value = _data[config.plot.valueField] + let _data = null + + if (config.subtype === 'ratioboard') { + _data = [] + if (nextProps.data && nextProps.data[config.dataName]) { + _data = nextProps.data[config.dataName] || [] + } + } else { + _data = {} + if (nextProps.data && nextProps.data[config.dataName]) { + _data = nextProps.data[config.dataName] || {} + } + if (_data.hasOwnProperty(config.plot.valueField)) { + _data.value = _data[config.plot.valueField] + } } this.setState({sync: false, data: _data}, () => { this.handleData() }) - } else if (nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) { - if (config.setting.syncRefresh === 'true') { - this.setState({}, () => { - this.loadData() - }) - } + } else if (config.setting.syncRefresh && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) { + this.setState({}, () => { + this.loadData() + }) } } @@ -159,16 +178,7 @@ if (!config.timer) return - const _change = { - '15s': 15000, - '30s': 30000, - '1min': 60000, - '5min': 300000, - '10min': 600000, - '15min': 900000, - '30min': 1800000, - '1hour': 3600000 - } + const _change = { '5s': 5000, '15s': 15000, '30s': 30000, '1min': 60000, '5min': 300000, '10min': 600000, '15min': 900000, '30min': 1800000, '1hour': 3600000 } let timer = _change[config.timer] @@ -225,7 +235,7 @@ const { config } = this.state if (!config.setting.supModule || config.setting.supModule !== MenuID) return - if (id !== this.state.BID) { + if (id !== this.state.BID || id !== '') { this.setState({ BID: id }, () => { this.loadData() }) @@ -237,7 +247,7 @@ if (_element) { _element.innerHTML = '' } - this.dashboardrender() + this.viewrender() } async loadData (hastimer) { @@ -253,21 +263,10 @@ return } - let searches = [] - if (mainSearch && mainSearch.length > 0) { // 涓昏〃鎼滅储鏉′欢 - let keys = [] - mainSearch.forEach(item => { - if (!keys.includes(item.key)) { - searches.push(item) - } - }) - } + let searches = config.setting.useMSearch && mainSearch ? mainSearch : [] - let requireFields = searches.filter(item => item.required && (!item.value || item.value.length === 0)) + let requireFields = searches.filter(item => item.required && item.value === '') if (requireFields.length > 0) { - this.setState({ - loading: false - }) return } @@ -282,15 +281,26 @@ let result = await Api.genericInterface(param) if (result.status) { - let data = {} - if (result.data && result.data[0] && result.data[0].hasOwnProperty(config.plot.valueField)) { - data.value = result.data[0][config.plot.valueField] + let data = null + if (config.subtype === 'ratioboard') { + data = result.data || [] + } else { + data = {} + if (result.data && result.data[0] && result.data[0].hasOwnProperty(config.plot.valueField)) { + data.value = result.data[0][config.plot.valueField] + } + } + let reset = true + + if (hastimer && is(fromJS(data), fromJS(this.state.data))) { + reset = false } this.setState({ data, loading: false }, () => { + if (!reset) return this.handleData() }) } else { @@ -306,6 +316,131 @@ } } + viewrender = () => { + const { config } = this.state + if (config.subtype === 'ratioboard') { + this.ratioboardrender() + } else { + this.dashboardrender() + } + } + + getratiodata = () => { + const { data, plot } = this.state + + let colors = {} + if (plot.colors && plot.colors.length > 0) { + plot.colors.forEach(item => { + colors[item.tick] = item.color + }) + } + + return data.map(item => { + let val = +item[plot.valueField] + let type = item[plot.labelField] || '' + if (isNaN(val)) { + val = 0 + } + return { + type: type, + value: val, + $percent: val / plot.maxValue, + $color: colors[type] || '#1890ff' + } + }) + } + + ratioboardrender = () => { + const { plot, chartId } = this.state + const data = this.getratiodata() + + const chart = new Chart({ + container: chartId, + autoFit: true, + height: this.wrap.offsetHeight - 30, + }) + + chart.data(data) + chart.coordinate('polar', { + startAngle: -Math.PI / 2, + endAngle: 3 * Math.PI / 2, + radius: (plot.radius || 75) / 100 + }) + chart.scale('$percent', { + min: 0, + max: 1, + tickInterval: 1, + }) + chart.axis(false) + chart.facet('rect', { + fields: ['type'], + showTitle: false, + eachView: function eachView(view, facet) { + const data = facet.data[0] + + view.point().position('').shape('hidden') + + view.annotation().arc({ + top: false, + start: [0, 1], + end: [0.9999, 1], + style: { + stroke: plot.backColor, + lineWidth: 10 + } + }) + + let _tick = data.$percent + if (_tick >= 1) { + _tick = 0.9999 + } + + view.annotation().arc({ + start: [0, 1], + end: [_tick, 1], + style: { + stroke: data.$color, + lineWidth: 10, + } + }) + // 浠〃鐩樹俊鎭� + let text = '' + if (plot.percent === 'true') { + text = +(data.$percent * 100).toFixed(2) + '%' + } else { + text = data.value + } + + view.annotation().text({ + position: ['50%', '45%'], + content: data.type, + style: { + fontSize: plot.fontSize * 0.8, + fill: plot.labelColor, + fontWeight: 300, + textAlign: 'center' + }, + offsetX: 0 + }) + view.annotation().text({ + position: ['50%', '55%'], + content: text, + style: { + fontSize: plot.fontSize, + fill: plot.labelColor, + fontWeight: 500, + textAlign: 'center' + }, + offsetX: 0, + offsetY: 10 + }) + } + }) + chart.render() + + this.setState({chart}) + } + /** * @description 浠〃鐩樻覆鏌� */ @@ -313,17 +448,28 @@ const { plot, chartId, data } = this.state let _data = fromJS(data).toJS() - if (_data.value && _data.value > plot.maxValue) { - _data.value = plot.maxValue + + if (_data.hasOwnProperty('value')) { + if (_data.value === '' || _data.value === null) { + delete _data.value + } else { + _data.value = +_data.value + + if (isNaN(_data.value)) { + delete _data.value + } else if (_data.value > plot.maxValue) { + _data.value = plot.maxValue + } + } } const chart = new Chart({ container: chartId, autoFit: true, - height: plot.height ? (plot.height - 80) : 320, + height: this.wrap.offsetHeight - 30, padding: [0, 0, 0, 0], }) - chart.data([_data]); + chart.data([_data]) chart.scale('value', { min: 0, max: plot.maxValue, @@ -335,7 +481,7 @@ radius: 0.75, }) - chart.axis('1', false); + chart.axis('1', false) chart.axis('value', { line: null, label: { @@ -451,10 +597,49 @@ } chart.render() + + this.setState({chart}) + } + + downloadImage = () => { + const { chart, config } = this.state + const link = document.createElement('a'); + const filename = `${config.name}${moment().format('YYYY-MM-DD HH_mm_ss')}.png`; + const canvas = chart.getCanvas(); + canvas.get('timeline').stopAllAnimations(); + + setTimeout(() => { + const canvas = chart.getCanvas(); + const canvasDom = canvas.get('el'); + const dataURL = canvasDom.toDataURL('image/png'); + + if (window.Blob && window.URL) { + const arr = dataURL.split(','); + const mime = arr[0].match(/:(.*?);/)[1]; + const bstr = atob(arr[1]); + let n = bstr.length; + const u8arr = new Uint8Array(n); + while (n--) { + u8arr[n] = bstr.charCodeAt(n); + } + const blobObj = new Blob([u8arr], { type: mime }); + if (window.navigator.msSaveBlob) { + window.navigator.msSaveBlob(blobObj, filename); + } else { + link.addEventListener('click', () => { + link.download = filename; + link.href = window.URL.createObjectURL(blobObj); + }); + } + } + const e = document.createEvent('MouseEvents'); + e.initEvent('click', false, false); + link.dispatchEvent(e); + }, 16); } render() { - const { config, loading, empty } = this.state + const { config, loading } = this.state return ( <div className="custom-dashboard-plot-box" style={config.style}> @@ -465,8 +650,9 @@ </div> : null } <NormalHeader config={config} /> - <div className="canvas-wrap"> - <div className={'canvas' + (empty ? ' empty' : '')} id={this.state.chartId}></div> + <div className="canvas-wrap" ref={ref => this.wrap = ref}> + {config.plot.download === 'enable' && this.state.chart ? <DownloadOutlined onClick={this.downloadImage} className="system-color download"/> : null} + <div className="canvas" id={this.state.chartId}></div> </div> </div> ) -- Gitblit v1.8.0