| | |
| | | import React, {Component} from 'react' |
| | | import PropTypes from 'prop-types' |
| | | import { is, fromJS } from 'immutable' |
| | | import { Icon, Popover } from 'antd' |
| | | import { Popover, message } from 'antd' |
| | | import { ToolOutlined, DeleteOutlined, FontColorsOutlined } from '@ant-design/icons' |
| | | import { Chart, registerShape } from '@antv/g2' |
| | | |
| | | import MKEmitter from '@/utils/events.js' |
| | | import asyncComponent from '@/utils/asyncComponent' |
| | | import asyncIconComponent from '@/utils/asyncIconComponent' |
| | | import { resetStyle } from '@/utils/utils-custom.js' |
| | | import zhCN from '@/locales/zh-CN/model.js' |
| | | import enUS from '@/locales/en-US/model.js' |
| | | import { resetStyle, getTables, getHeight } from '@/utils/utils-custom.js' |
| | | import './index.scss' |
| | | |
| | | const SettingComponent = asyncIconComponent(() => import('@/menu/datasource')) |
| | |
| | | }, |
| | | }) |
| | | |
| | | registerShape('point', 'hidden', { |
| | | draw(cfg, container) { |
| | | return container.addGroup({}) |
| | | } |
| | | }) |
| | | |
| | | class antvDashboardChart extends Component { |
| | | static propTpyes = { |
| | | card: PropTypes.object, |
| | |
| | | } |
| | | |
| | | state = { |
| | | dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS, |
| | | card: null, |
| | | ismob: sessionStorage.getItem('appType') === 'mob', |
| | | appType: sessionStorage.getItem('appType'), |
| | | eventListener: null |
| | | } |
| | | |
| | | UNSAFE_componentWillMount () { |
| | | const { card, ismob } = this.props |
| | | const { card } = this.props |
| | | const { appType } = this.state |
| | | |
| | | if (card.isNew) { |
| | | let _plot = { |
| | | width: card.width || 12, |
| | | height: 400, |
| | | label: '', |
| | | valueField: '', |
| | | name: card.name, |
| | | maxValue: 100, |
| | | tickInterval: 10, |
| | | labelColor: '#545454', |
| | | tickColor: '#CBCBCB', |
| | | percent: 'true' |
| | | let _plot = null |
| | | if (card.subtype === 'ratioboard') { |
| | | _plot = { |
| | | width: card.width || 12, |
| | | height: 400, |
| | | labelField: '', |
| | | valueField: '', |
| | | name: card.name, |
| | | maxValue: 100, |
| | | radius: 75, |
| | | fontSize: 28, |
| | | percent: 'true', |
| | | backColor: '#ebedf0', |
| | | labelColor: '#8c8c8c' |
| | | } |
| | | } else { |
| | | _plot = { |
| | | width: card.width || 12, |
| | | height: 400, |
| | | label: '', |
| | | valueField: '', |
| | | name: card.name, |
| | | maxValue: 100, |
| | | tickInterval: 10, |
| | | labelColor: '#545454', |
| | | tickColor: '#CBCBCB', |
| | | percent: 'true' |
| | | } |
| | | } |
| | | |
| | | if (ismob) { |
| | | if (appType === 'mob') { |
| | | _plot.width = 24 |
| | | } |
| | | |
| | | let _card = { |
| | | uuid: card.uuid, |
| | | type: card.type, |
| | | floor: card.floor, |
| | | tabId: card.tabId || '', |
| | | parentId: card.parentId || '', |
| | | format: 'object', // 组件属性 - 数据格式 |
| | | format: card.subtype === 'ratioboard' ? 'array' : 'object', // 组件属性 - 数据格式 |
| | | pageable: false, // 组件属性 - 是否可分页 |
| | | switchable: false, // 组件属性 - 数据是否可切换 |
| | | dataName: card.dataName || '', |
| | | width: _plot.width, |
| | | name: _plot.name, |
| | | subtype: card.subtype, |
| | |
| | | search: [], |
| | | action: [], |
| | | plot: _plot, |
| | | btnlog: [], |
| | | } |
| | | |
| | | if (card.config) { |
| | |
| | | _card.plot.name = card.name |
| | | _card.style = config.style |
| | | _card.headerStyle = config.headerStyle |
| | | |
| | | _card.setting = config.setting |
| | | _card.columns = config.columns |
| | | _card.scripts = config.scripts |
| | | } |
| | | |
| | | this.props.updateConfig(_card) |
| | | this.setState({ |
| | | card: _card |
| | | }) |
| | | this.updateComponent(_card, true) |
| | | } else { |
| | | this.setState({ |
| | | card: fromJS(card).toJS() |
| | |
| | | } |
| | | |
| | | componentDidMount () { |
| | | this.dashboardrender() |
| | | MKEmitter.addListener('tabsChange', this.handleTabsChange) |
| | | MKEmitter.addListener('submitStyle', this.getStyle) |
| | | |
| | | setTimeout(() => { |
| | | this.viewrender() |
| | | }, 1000) |
| | | } |
| | | |
| | | shouldComponentUpdate (nextProps, nextState) { |
| | |
| | | return |
| | | } |
| | | MKEmitter.removeListener('tabsChange', this.handleTabsChange) |
| | | MKEmitter.removeListener('submitStyle', this.getStyle) |
| | | } |
| | | |
| | | handleTabsChange = (parentId) => { |
| | | const { card } = this.state |
| | | |
| | | if (parentId === card.parentId) { |
| | | let _element = document.getElementById(card.uuid + 'canvas') |
| | | if (parentId.indexOf(card.uuid) > -1 || parentId === 'all') { |
| | | let _element = document.getElementById(card.uuid + 'dashboard') |
| | | if (_element) { |
| | | _element.innerHTML = '' |
| | | } |
| | | |
| | | this.$timer && clearTimeout(this.$timer) |
| | | this.$timer = setTimeout(this.dashboardrender, 100) |
| | | this.$timer = setTimeout(this.viewrender, 100) |
| | | } |
| | | } |
| | | |
| | | viewrender = () => { |
| | | const { card } = this.state |
| | | if (card.subtype === 'ratioboard') { |
| | | this.ratioboardrender() |
| | | } else { |
| | | this.dashboardrender() |
| | | } |
| | | } |
| | | |
| | | getratiodata = () => { |
| | | const { card } = this.state |
| | | |
| | | let val = (Math.random() * card.plot.maxValue).toFixed(1) |
| | | let data = [ |
| | | { type: '新用户', value: val, $percent: val / card.plot.maxValue, $color: '#1890ff' }, |
| | | ] |
| | | if (card.plot.colors && card.plot.colors.length > 0) { |
| | | data = [] |
| | | card.plot.colors.forEach(item => { |
| | | let _val = (Math.random() * card.plot.maxValue).toFixed(1) |
| | | data.push({ |
| | | type: item.tick, |
| | | value: _val, |
| | | $percent: _val / card.plot.maxValue, |
| | | $color: item.color |
| | | }) |
| | | }) |
| | | } |
| | | |
| | | return data |
| | | } |
| | | |
| | | ratioboardrender = () => { |
| | | const { card } = this.state |
| | | const plot = card.plot |
| | | |
| | | const data = this.getratiodata() |
| | | |
| | | const chart = new Chart({ |
| | | container: card.uuid + 'dashboard', |
| | | autoFit: true, |
| | | height: getHeight(plot.height) |
| | | }) |
| | | |
| | | 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() |
| | | } |
| | | |
| | | getdata = () => { |
| | | const { card } = this.state |
| | | |
| | | const data = []; |
| | | const val = (Math.random() * card.plot.maxValue).toFixed(1); |
| | | data.push({ value: +val }); |
| | | return data; |
| | | const data = [] |
| | | const val = (Math.random() * card.plot.maxValue).toFixed(1) |
| | | data.push({ value: +val }) |
| | | return data |
| | | } |
| | | |
| | | dashboardrender = () => { |
| | |
| | | const chart = new Chart({ |
| | | container: card.uuid + 'dashboard', |
| | | autoFit: true, |
| | | height: plot.height ? (plot.height - 75) : 325, |
| | | height: getHeight(plot.height), |
| | | padding: [0, 0, 0, 0], |
| | | }) |
| | | chart.data(data); |
| | | chart.data(data) |
| | | chart.scale('value', { |
| | | min: 0, |
| | | max: plot.maxValue, |
| | | tickInterval: plot.tickInterval, |
| | | }); |
| | | }) |
| | | chart.coordinate('polar', { |
| | | startAngle: (-9 / 8) * Math.PI, |
| | | endAngle: (1 / 8) * Math.PI, |
| | | radius: 0.75, |
| | | }); |
| | | }) |
| | | |
| | | chart.axis('1', false); |
| | | chart.axis('1', false) |
| | | chart.axis('value', { |
| | | line: null, |
| | | label: { |
| | |
| | | } |
| | | }, |
| | | grid: null, |
| | | }); |
| | | chart.legend(false); |
| | | chart.tooltip(false); |
| | | }) |
| | | chart.legend(false) |
| | | chart.tooltip(false) |
| | | chart |
| | | .point() |
| | | .position('value*1') |
| | |
| | | appear: { |
| | | animation: 'fade-in' |
| | | } |
| | | }); |
| | | }) |
| | | |
| | | // 绘制仪表盘背景 |
| | | chart.annotation().arc({ |
| | |
| | | start: [0, 1], |
| | | end: [plot.maxValue, 1], |
| | | style: { |
| | | stroke: '#CBCBCB', |
| | | stroke: '#ebedf0', |
| | | lineWidth: 18, |
| | | lineDash: null, |
| | | }, |
| | |
| | | textAlign: 'center', |
| | | }, |
| | | offsetY: 15, |
| | | }); |
| | | }) |
| | | |
| | | chart.render() |
| | | } |
| | | |
| | | updateComponent = (component) => { |
| | | const card = fromJS(this.state.card).toJS() |
| | | |
| | | if (!is(fromJS(component.plot), fromJS(card.plot)) || !is(fromJS(component.style), fromJS(card.style))) { |
| | | let _element = document.getElementById(card.uuid + 'dashboard') |
| | | if (_element) { |
| | | _element.innerHTML = '' |
| | | updateComponent = (card, init) => { |
| | | if (!init) { |
| | | if (!is(fromJS({plot: card.plot, style: card.style}), fromJS({plot: this.state.card.plot, style: this.state.card.style}))) { |
| | | let _element = document.getElementById(this.state.card.uuid + 'dashboard') |
| | | if (_element) { |
| | | _element.innerHTML = '' |
| | | } |
| | | this.$timer && clearTimeout(this.$timer) |
| | | this.$timer = setTimeout(() => { |
| | | this.viewrender() |
| | | }, 150) |
| | | } |
| | | this.$timer && clearTimeout(this.$timer) |
| | | this.$timer = setTimeout(() => { |
| | | this.dashboardrender() |
| | | }, 150) |
| | | } |
| | | |
| | | component.width = component.plot.width |
| | | component.name = component.plot.name |
| | | card.width = card.plot.width |
| | | card.name = card.plot.name |
| | | |
| | | card.errors = [] |
| | | |
| | | let columns = card.columns.map(c => c.field) |
| | | |
| | | if (card.setting.interType === 'system' && card.setting.execute !== 'false' && !card.setting.dataresource) { |
| | | card.errors.push({ level: 0, detail: '未设置数据源!'}) |
| | | } else if (card.setting.interType === 'system' && card.setting.execute === 'false' && card.scripts.filter(script => script.status !== 'false').length === 0) { |
| | | card.errors.push({ level: 0, detail: '数据源中无可用脚本!'}) |
| | | } else if (!card.setting.primaryKey) { |
| | | card.errors.push({ level: 0, detail: '未设置主键!'}) |
| | | } else if (!columns.includes(card.setting.primaryKey)) { |
| | | card.errors.push({ level: 0, detail: '主键已失效!'}) |
| | | } else if (!card.setting.supModule) { |
| | | card.errors.push({ level: 0, detail: '未设置上级组件!'}) |
| | | } |
| | | |
| | | if (card.errors.length === 0) { |
| | | card.$tables = getTables(card) |
| | | } |
| | | |
| | | if (!card.plot.valueField) { |
| | | card.errors.push({ level: 0, detail: '显示值尚未设置!'}) |
| | | } else if (!columns.includes(card.plot.valueField)) { |
| | | card.errors.push({ level: 1, detail: '显示值在字段集中不存在'}) |
| | | } |
| | | |
| | | this.setState({ |
| | | card: component |
| | | card: card |
| | | }) |
| | | this.props.updateConfig(component) |
| | | this.props.updateConfig(card) |
| | | } |
| | | |
| | | changeStyle = () => { |
| | | const { card } = this.state |
| | | |
| | | MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style) |
| | | MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow', 'clear'], card.style, this.getStyle) |
| | | } |
| | | |
| | | getStyle = (comIds, style) => { |
| | | const { card } = this.state |
| | | |
| | | if (comIds[0] !== card.uuid || comIds.length > 1) return |
| | | |
| | | let _card = {...card, style} |
| | | getStyle = (style) => { |
| | | let _card = {...this.state.card, style} |
| | | |
| | | this.updateComponent(_card) |
| | | } |
| | |
| | | clickComponent = (e) => { |
| | | if (sessionStorage.getItem('style-control') === 'true' || sessionStorage.getItem('style-control') === 'component') { |
| | | e.stopPropagation() |
| | | MKEmitter.emit('clickComponent', this.state.card) |
| | | MKEmitter.emit('clickComponent', this.state.card.uuid, null, (style) => { |
| | | let _card = {...this.state.card} |
| | | _card.style = {..._card.style, ...style} |
| | | |
| | | this.updateComponent(_card) |
| | | }) |
| | | } |
| | | } |
| | | |
| | | render() { |
| | | const { card } = this.state |
| | | let _style = resetStyle(card.style) |
| | | _style.height = 'auto' |
| | | |
| | | return ( |
| | | <div className="menu-dashboard-edit-box" style={{..._style, height: card.plot.height || 400}} onClick={this.clickComponent} id={card.uuid}> |
| | | <NormalHeader config={card} updateComponent={this.updateComponent}/> |
| | | <div className="menu-dashboard-edit-box" style={_style} onClick={this.clickComponent} id={card.uuid}> |
| | | <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={ |
| | | <div className="mk-popover-control"> |
| | | <ChartCompileForm config={card} dict={this.state.dict} plotchange={this.updateComponent}/> |
| | | <ChartCompileForm config={card} plotchange={this.updateComponent}/> |
| | | <CopyComponent type="dashboard" card={card}/> |
| | | <Icon className="style" title="调整样式" onClick={this.changeStyle} type="font-colors" /> |
| | | <FontColorsOutlined className="style" title="调整样式" onClick={this.changeStyle}/> |
| | | <ClockComponent config={card} updateConfig={this.updateComponent}/> |
| | | <UserComponent config={card}/> |
| | | <Icon className="close" title="delete" type="delete" onClick={() => this.props.deletecomponent(card.uuid)} /> |
| | | <DeleteOutlined className="close" title="delete" onClick={() => this.props.deletecomponent(card.uuid)} /> |
| | | <SettingComponent config={card} updateConfig={this.updateComponent}/> |
| | | </div> |
| | | } trigger="hover"> |
| | | <Icon type="tool" /> |
| | | <ToolOutlined/> |
| | | </Popover> |
| | | <NormalHeader hideSearch="true" config={card} updateComponent={this.updateComponent}/> |
| | | <div className="canvas" id={card.uuid + 'dashboard'}></div> |
| | | <div className="component-name"> |
| | | <div className="center"> |
| | | <div className="title" onDoubleClick={() => { |
| | | let oInput = document.createElement('input') |
| | | oInput.value = 'anchor' + card.uuid |
| | | document.body.appendChild(oInput) |
| | | oInput.select() |
| | | document.execCommand('Copy') |
| | | document.body.removeChild(oInput) |
| | | message.success('复制成功。') |
| | | }}>{card.name}</div> |
| | | <div className="content"> |
| | | {card.errors && card.errors.map((err, index) => { |
| | | if (err.level === 0) { |
| | | return <span key={index} className="error">{err.detail}</span> |
| | | } else { |
| | | return <span key={index} className="waring">{err.detail};</span> |
| | | } |
| | | })} |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | ) |
| | | } |