| | |
| | | import React, {Component} from 'react' |
| | | import PropTypes from 'prop-types' |
| | | import { is, fromJS } from 'immutable' |
| | | import { Icon, Popover, notification } from 'antd' |
| | | import { Icon, Popover } from 'antd' |
| | | import { Chart } from '@antv/g2' |
| | | import DataSet from '@antv/data-set' |
| | | import DataSet, { DataView } from '@antv/data-set' |
| | | |
| | | import MKEmitter from '@/utils/events.js' |
| | | import asyncComponent from '@/utils/asyncComponent' |
| | | import asyncIconComponent from '@/utils/asyncIconComponent' |
| | | |
| | | import { resetStyle } from '@/utils/utils-custom.js' |
| | | import Utils from '@/utils/utils.js' |
| | | import zhCN from '@/locales/zh-CN/model.js' |
| | | import enUS from '@/locales/en-US/model.js' |
| | |
| | | const ChartCompileForm = asyncIconComponent(() => import('./chartcompile')) |
| | | const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent')) |
| | | const PasteComponent = asyncIconComponent(() => import('@/menu/components/share/pastecomponent')) |
| | | const LogComponent = asyncIconComponent(() => import('@/menu/components/share/logcomponent')) |
| | | const NormalHeader = asyncComponent(() => import('@/menu/components/share/normalheader')) |
| | | const UserComponent = asyncIconComponent(() => import('@/menu/components/share/usercomponent')) |
| | | const ClockComponent = asyncIconComponent(() => import('@/menu/components/share/clockcomponent')) |
| | |
| | | state = { |
| | | dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS, |
| | | card: null, |
| | | ismob: sessionStorage.getItem('appType') === 'mob', |
| | | eventListener: null |
| | | } |
| | | |
| | | UNSAFE_componentWillMount () { |
| | | const { card } = this.props |
| | | const { card, ismob } = this.props |
| | | |
| | | if (card.isNew) { |
| | | let _plot = { |
| | |
| | | name: card.name |
| | | } |
| | | |
| | | if (ismob) { |
| | | _plot.width = 24 |
| | | } |
| | | |
| | | if (card.subtype === 'ring') { |
| | | _plot.innerRadius = 50 |
| | | } else if (card.subtype === 'nest') { |
| | | _plot.innerRadius = 30 |
| | | _plot.radius = 80 |
| | | } |
| | | |
| | | let _card = { |
| | |
| | | } |
| | | |
| | | componentDidMount () { |
| | | this.pierender() |
| | | MKEmitter.addListener('tabsChange', this.handleTabsChange) |
| | | MKEmitter.addListener('submitStyle', this.getStyle) |
| | | |
| | | setTimeout(() => { |
| | | this.viewrender() |
| | | }, 1000) |
| | | } |
| | | |
| | | shouldComponentUpdate (nextProps, nextState) { |
| | |
| | | _element.innerHTML = '' |
| | | } |
| | | |
| | | setTimeout(this.pierender, 100) |
| | | this.$timer && clearTimeout(this.$timer) |
| | | this.$timer = setTimeout(this.viewrender, 100) |
| | | } |
| | | } |
| | | |
| | | viewrender = () => { |
| | | const { card } = this.state |
| | | |
| | | if (card.plot.shape === 'nest') { |
| | | this.nestrender() |
| | | } else { |
| | | this.pierender() |
| | | } |
| | | } |
| | | |
| | |
| | | { label: '2003', value: 33.7 }, |
| | | { label: '2004', value: 30.7 }, |
| | | { label: '2005', value: 25.8 }, |
| | | { label: '2006', value: 31.7 }, |
| | | { label: '2007', value: 33 }, |
| | | { label: '2008', value: 46 }, |
| | | { label: '2009', value: 38.3 }, |
| | | { label: '2010', value: 28 }, |
| | | { label: '2011', value: 42.5 }, |
| | | { label: '2012', value: 30.3 } |
| | | ] |
| | | |
| | | let data = xdata.map(item => { |
| | |
| | | return data |
| | | } |
| | | |
| | | pierender = () => { |
| | | const { card } = this.state |
| | | let plot = {...card.plot, height: card.plot.height - 80} |
| | | let color = plot.color || 'rgba(0, 0, 0, 0.85)' |
| | | getnestdata = (X_axis, Y_axis, type) => { |
| | | const xdata = [ |
| | | { name: '狮子', type: '火象', value: 11 }, |
| | | { name: '白羊', type: '火象', value: 10 }, |
| | | { name: '水瓶', type: '风向', value: 14 }, |
| | | { name: '射手', type: '火象', value: 10 }, |
| | | { name: '双子', type: '风向', value: 7 }, |
| | | { name: '天平', type: '风向', value: 7 }, |
| | | { name: '摩羯', type: '土象', value: 14 }, |
| | | { name: '金牛', type: '土象', value: 3 }, |
| | | { name: '处女', type: '土象', value: 3 }, |
| | | { name: '天蝎', type: '水象', value: 11 }, |
| | | { name: '巨蟹', type: '水象', value: 5 }, |
| | | { name: '双鱼', type: '水象', value: 5 }, |
| | | ] |
| | | |
| | | let map = new Map() |
| | | let sort = 1 |
| | | let data = xdata.map(item => { |
| | | let _sort = sort |
| | | if (map.has(item.type)) { |
| | | _sort = map.get(item.type) |
| | | } else { |
| | | map.set(item.type, _sort) |
| | | sort++ |
| | | } |
| | | return { |
| | | [X_axis]: item.name, |
| | | [Y_axis]: item.value, |
| | | [type]: item.type, |
| | | $sort: _sort |
| | | } |
| | | }) |
| | | |
| | | return data |
| | | } |
| | | |
| | | nestrender = () => { |
| | | const { card } = this.state |
| | | const plot = card.plot |
| | | |
| | | let color = plot.color || 'rgba(0, 0, 0, 0.85)' |
| | | let X_axis = plot.Xaxis || 'x' |
| | | let Y_axis = plot.Yaxis || 'y' |
| | | let type = plot.type || 'type' |
| | | let height = plot.height || 400 |
| | | if (card.plot.title || card.search.length > 0) { |
| | | height = height - 45 |
| | | } |
| | | |
| | | const _data = this.getnestdata(X_axis, Y_axis, type) |
| | | const dvx = new DataView().source(_data) |
| | | |
| | | dvx.transform({ |
| | | type: 'sort-by', |
| | | fields: ['$sort'] |
| | | }) |
| | | |
| | | let data = dvx.rows |
| | | |
| | | // 通过 DataSet 计算百分比 |
| | | const dv = new DataView() |
| | | dv.source(data).transform({ |
| | | type: 'percent', |
| | | field: Y_axis, |
| | | dimension: type, |
| | | as: '$percent' |
| | | }) |
| | | |
| | | const dv1 = new DataView() |
| | | dv1.source(data).transform({ |
| | | type: 'percent', |
| | | field: Y_axis, |
| | | dimension: X_axis, |
| | | as: '$percent', |
| | | }) |
| | | |
| | | const chart = new Chart({ |
| | | container: card.uuid + 'canvas', |
| | | autoFit: true, |
| | | height: height, |
| | | padding: 0, |
| | | }) |
| | | |
| | | chart.data(dv.rows) |
| | | |
| | | if (plot.show !== 'value') { |
| | | chart.scale('percent', { |
| | | formatter: (val) => { |
| | | val = val * 100 + '%' |
| | | return val |
| | | } |
| | | }) |
| | | |
| | | Y_axis = '$percent' |
| | | } |
| | | let radius = plot.radius / 100 |
| | | let innerRadius = plot.innerRadius / 100 |
| | | |
| | | chart.coordinate('theta', { |
| | | innerRadius: innerRadius, |
| | | radius: innerRadius + (radius - innerRadius) / 2, |
| | | }) |
| | | |
| | | if (plot.tooltip !== 'true') { |
| | | chart.tooltip(false) |
| | | } else { |
| | | chart.tooltip({ |
| | | showTitle: false, |
| | | showMarkers: false |
| | | }) |
| | | } |
| | | |
| | | chart.legend(false) |
| | | let chart1 = chart |
| | | .interval() |
| | | .adjust('stack') |
| | | .position(Y_axis) |
| | | .color(type) |
| | | .tooltip(`${type}*${Y_axis}`, (type, percent) => { |
| | | if (plot.show !== 'value') { |
| | | percent = (percent * 100).toFixed(2) + '%' |
| | | } |
| | | return { |
| | | name: type, |
| | | value: percent, |
| | | } |
| | | }) |
| | | |
| | | if (plot.splitLine) { |
| | | chart1.style({ |
| | | lineWidth: plot.splitLine, |
| | | stroke: plot.splitColor, |
| | | }) |
| | | } |
| | | if (plot.label !== 'false') { |
| | | chart1.label(type, { |
| | | offset: -10, |
| | | }) |
| | | } |
| | | |
| | | const outterView = chart.createView() |
| | | |
| | | outterView.data(dv1.rows) |
| | | |
| | | if (plot.show !== 'value') { |
| | | outterView.scale('percent', { |
| | | formatter: (val) => { |
| | | val = val * 100 + '%' |
| | | return val |
| | | } |
| | | }) |
| | | } |
| | | outterView.coordinate('theta', { |
| | | innerRadius: (innerRadius + (radius - innerRadius) / 2) / radius, |
| | | radius: radius |
| | | }) |
| | | let chart2 = outterView |
| | | .interval() |
| | | .adjust('stack') |
| | | .position(Y_axis) |
| | | .color(X_axis) |
| | | .tooltip(`${X_axis}*${Y_axis}`, (name, value) => { |
| | | if (plot.show !== 'value') { |
| | | value = (value * 100).toFixed(2) + '%' |
| | | } |
| | | return { |
| | | name: name, |
| | | value: value |
| | | } |
| | | }) |
| | | |
| | | if (plot.splitLine) { |
| | | chart2.style({ |
| | | lineWidth: plot.splitLine, |
| | | stroke: plot.splitColor, |
| | | }) |
| | | } |
| | | |
| | | if (plot.label !== 'false') { |
| | | if (plot.label === 'inner') { |
| | | chart2.label(Y_axis, { |
| | | offset: -30, |
| | | content: (data) => { |
| | | let _val = '' |
| | | if (plot.show !== 'value') { |
| | | _val = `${(data[Y_axis] * 100).toFixed(2)}%` |
| | | } else { |
| | | _val = `${data[Y_axis]}` |
| | | } |
| | | return _val |
| | | }, |
| | | style: { |
| | | textAlign: 'center', |
| | | fontSize: 16, |
| | | shadowBlur: 2, |
| | | shadowColor: 'rgba(0, 0, 0, .45)', |
| | | fill: '#fff', |
| | | } |
| | | }) |
| | | } else { |
| | | chart2.label(Y_axis, { |
| | | layout: { type: plot.label === 'outer' ? 'pie-spider' : 'fixed-overlap' }, |
| | | labelHeight: 20, |
| | | content: (data) => { |
| | | let _val = '' |
| | | if (plot.show !== 'value') { |
| | | _val = `${(data[Y_axis] * 100).toFixed(2)}%` |
| | | } else { |
| | | _val = `${data[Y_axis]}` |
| | | } |
| | | |
| | | return `${data[X_axis]}: ${_val}` |
| | | }, |
| | | labelLine: { |
| | | style: { |
| | | lineWidth: 0.5, |
| | | }, |
| | | }, |
| | | style: { |
| | | fill: color |
| | | } |
| | | }) |
| | | } |
| | | } |
| | | |
| | | if (plot.interaction && plot.interaction.length) { |
| | | plot.interaction.forEach(t => { |
| | | chart.interaction(t) |
| | | }) |
| | | } |
| | | |
| | | chart.render() |
| | | } |
| | | |
| | | pierender = () => { |
| | | const { card } = this.state |
| | | const plot = card.plot |
| | | |
| | | let color = plot.color || 'rgba(0, 0, 0, 0.85)' |
| | | let X_axis = plot.Xaxis || 'x' |
| | | let Y_axis = plot.Yaxis || 'y' |
| | | let height = plot.height || 400 |
| | | if (card.plot.title || card.search.length > 0) { |
| | | height = height - 45 |
| | | } |
| | | |
| | | let data = this.getdata(X_axis, Y_axis) |
| | | |
| | |
| | | const chart = new Chart({ |
| | | container: card.uuid + 'canvas', |
| | | autoFit: true, |
| | | height: plot.height || 400 |
| | | height: height |
| | | }) |
| | | |
| | | if (plot.shape !== 'nightingale' && plot.show !== 'value') { |
| | |
| | | }) |
| | | } |
| | | |
| | | // 饼图或环图 |
| | | if (plot.shape !== 'nightingale') { |
| | | let _chart = chart |
| | | .interval() |
| | |
| | | value: value |
| | | } |
| | | }) |
| | | |
| | | if (plot.splitLine) { |
| | | _chart.style({ |
| | | lineWidth: plot.splitLine, |
| | | stroke: plot.splitColor, |
| | | }) |
| | | } |
| | | |
| | | if (plot.label !== 'false') { |
| | | if (plot.label === 'inner') { |
| | | _chart.label(Y_axis, { |
| | | offset: -30, |
| | | content: (data) => { |
| | | let _label = '' |
| | | let _val = '' |
| | | if (plot.show !== 'value') { |
| | | _val = `${(data[Y_axis] * 100).toFixed(2)}%` |
| | | } else { |
| | | _val = `${data[Y_axis]}` |
| | | } |
| | | if (plot.label === 'inner') { |
| | | _label = _val |
| | | } else { |
| | | _label = `${data[X_axis]}: ${_val}` |
| | | } |
| | | return _label |
| | | return _val |
| | | }, |
| | | style: { |
| | | textAlign: 'center', |
| | |
| | | }) |
| | | } else { |
| | | _chart.label(Y_axis, { |
| | | layout: { type: 'pie-spider' }, |
| | | layout: { type: plot.label === 'outer' ? 'pie-spider' : 'fixed-overlap' }, |
| | | labelHeight: 20, |
| | | content: (data) => { |
| | | let _label = '' |
| | | let _val = '' |
| | | if (plot.show !== 'value') { |
| | | _val = `${(data[Y_axis] * 100).toFixed(2)}%` |
| | | } else { |
| | | _val = `${data[Y_axis]}` |
| | | } |
| | | if (plot.label === 'inner') { |
| | | _label = _val |
| | | } else { |
| | | _label = `${data[X_axis]}: ${_val}` |
| | | } |
| | | return _label |
| | | |
| | | return `${data[X_axis]}: ${_val}` |
| | | }, |
| | | labelLine: { |
| | | style: { |
| | |
| | | }) |
| | | } |
| | | } |
| | | chart.interaction('element-active') |
| | | } else { |
| | | chart.axis(false) |
| | | chart.interaction('element-highlight') |
| | | let _chart = chart |
| | | .interval() |
| | | .position(`${X_axis}*${Y_axis}`) |
| | |
| | | } |
| | | |
| | | _chart.label(X_axis, _label) |
| | | .style({ |
| | | lineWidth: 1, |
| | | stroke: '#fff', |
| | | } |
| | | if (plot.splitLine) { |
| | | _chart.style({ |
| | | lineWidth: plot.splitLine, |
| | | stroke: plot.splitColor, |
| | | }) |
| | | } |
| | | } |
| | | |
| | | if (plot.interaction && plot.interaction.length) { |
| | | plot.interaction.forEach(t => { |
| | | chart.interaction(t) |
| | | }) |
| | | } |
| | | |
| | | chart.render() |
| | |
| | | |
| | | updateComponent = (component) => { |
| | | const card = fromJS(this.state.card).toJS() |
| | | let refresh = false |
| | | |
| | | if (!is(fromJS(component.plot), fromJS(card.plot)) || !is(fromJS(component.style), fromJS(card.style))) { |
| | | let _element = document.getElementById(card.uuid + 'canvas') |
| | | if (_element) { |
| | | _element.innerHTML = '' |
| | | } |
| | | refresh = true |
| | | this.$timer && clearTimeout(this.$timer) |
| | | this.$timer = setTimeout(() => { |
| | | this.viewrender() |
| | | }, 150) |
| | | } |
| | | |
| | | component.width = component.plot.width |
| | |
| | | |
| | | this.setState({ |
| | | card: component |
| | | }, () => { |
| | | if (refresh) { |
| | | setTimeout(() => { |
| | | this.pierender() |
| | | }, 100) |
| | | } |
| | | }) |
| | | this.props.updateConfig(component) |
| | | } |
| | |
| | | this.updateComponent(_card) |
| | | } |
| | | |
| | | handleLog = (type, logs, item) => { |
| | | let card = fromJS(this.state.card).toJS() |
| | | |
| | | if (type === 'revert') { |
| | | card.action = card.action ? [...card.action, item] : [item] |
| | | card.btnlog = logs |
| | | |
| | | this.setState({ card }) |
| | | this.props.updateConfig(card) |
| | | notification.success({ |
| | | top: 92, |
| | | message: '恢复成功!', |
| | | duration: 2 |
| | | }) |
| | | } else { |
| | | card.btnlog = logs |
| | | this.setState({ card }) |
| | | this.props.updateConfig(card) |
| | | notification.success({ |
| | | top: 92, |
| | | message: '清除成功!', |
| | | duration: 2 |
| | | }) |
| | | } |
| | | } |
| | | |
| | | clickComponent = (e) => { |
| | | if (sessionStorage.getItem('style-control') === 'true' || sessionStorage.getItem('style-control') === 'component') { |
| | | e.stopPropagation() |
| | |
| | | } |
| | | |
| | | render() { |
| | | const { card } = this.state |
| | | const { card, ismob } = this.state |
| | | let _style = resetStyle(card.style) |
| | | |
| | | return ( |
| | | <div className="menu-pie-chart-edit-box" style={{...card.style, height: card.plot.height || 400}} onClick={this.clickComponent} id={card.uuid}> |
| | | <NormalHeader config={card} updateComponent={this.updateComponent}/> |
| | | <div className="menu-pie-chart-edit-box" style={{..._style, height: card.plot.height || 400}} onClick={this.clickComponent} id={card.uuid}> |
| | | <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={ |
| | | <div className="mk-popover-control"> |
| | | <Icon className="plus" title="添加搜索" onClick={this.addSearch} type="plus-circle" /> |
| | | {!ismob ? <Icon className="plus" title="添加搜索" onClick={this.addSearch} type="plus-circle" /> : null} |
| | | <ChartCompileForm config={card} dict={this.state.dict} plotchange={this.updateComponent}/> |
| | | <CopyComponent type="pie" card={card}/> |
| | | <PasteComponent config={card} options={['search', 'form']} updateConfig={this.updateComponent} /> |
| | | <Icon className="style" title="调整样式" onClick={this.changeStyle} type="font-colors" /> |
| | | <LogComponent btnlog={card.btnlog || []} handlelog={this.handleLog} /> |
| | | <ClockComponent config={card} updateConfig={this.updateComponent}/> |
| | | <UserComponent config={card}/> |
| | | <Icon className="close" title="delete" type="delete" onClick={() => this.props.deletecomponent(card.uuid)} /> |
| | |
| | | } trigger="hover"> |
| | | <Icon type="tool" /> |
| | | </Popover> |
| | | {card.plot.title || card.search.length > 0 ? <NormalHeader config={card} updateComponent={this.updateComponent}/> : null} |
| | | <div className="canvas" id={card.uuid + 'canvas'}></div> |
| | | </div> |
| | | ) |