From 772308eba58d2209e7cc108dc5567ef4ca7ee3b2 Mon Sep 17 00:00:00 2001 From: king <18310653075@163.com> Date: 星期四, 15 四月 2021 16:17:20 +0800 Subject: [PATCH] 2021-04-15 --- src/assets/mobimg/scatter.png | 0 src/tabviews/custom/components/chart/antv-scatter/index.jsx | 442 ++++++++++++++++ src/tabviews/custom/components/chart/antv-bar-line/index.jsx | 11 src/tabviews/custom/components/chart/antv-dashboard/index.jsx | 26 src/tabviews/custom/components/chart/antv-scatter/index.scss | 65 ++ src/menu/components/group/groupcomponents/index.jsx | 1 src/menu/components/chart/antv-scatter/chartcompile/index.jsx | 271 ++++++++++ src/menu/components/chart/antv-scatter/chartcompile/formconfig.jsx | 137 +++++ src/menu/components/chart/antv-bar/index.jsx | 25 src/menu/components/chart/antv-scatter/index.jsx | 390 ++++++++++++++ src/menu/components/tabs/tabcomponents/index.jsx | 1 src/views/billprint/index.jsx | 1 src/menu/menushell/card.jsx | 3 src/menu/components/chart/antv-scatter/chartcompile/index.scss | 41 + src/views/mobdesign/index.jsx | 2 src/views/menudesign/index.jsx | 2 src/menu/modulesource/option.jsx | 2 src/menu/components/chart/antv-bar/chartcompile/formconfig.jsx | 13 src/menu/components/share/pastecomponent/index.jsx | 10 src/tabviews/custom/components/share/tabtransfer/index.jsx | 7 src/views/pcdesign/index.jsx | 2 src/menu/components/chart/antv-scatter/index.scss | 65 ++ src/tabviews/custom/components/group/normal-group/index.jsx | 7 src/tabviews/custom/components/chart/antv-scatter/asyncButtonComponent.jsx | 34 + src/tabviews/custom/index.jsx | 7 src/menu/menushell/index.jsx | 1 26 files changed, 1,525 insertions(+), 41 deletions(-) diff --git a/src/assets/mobimg/scatter.png b/src/assets/mobimg/scatter.png new file mode 100644 index 0000000..dfeb28a --- /dev/null +++ b/src/assets/mobimg/scatter.png Binary files differ diff --git a/src/menu/components/chart/antv-bar/chartcompile/formconfig.jsx b/src/menu/components/chart/antv-bar/chartcompile/formconfig.jsx index 29d0d30..8a0c978 100644 --- a/src/menu/components/chart/antv-bar/chartcompile/formconfig.jsx +++ b/src/menu/components/chart/antv-bar/chartcompile/formconfig.jsx @@ -378,16 +378,9 @@ type: 'color', key: 'color', label: '鑹茬郴', - initVal: card.color || 'rgba(0, 0, 0, 0.85)', - tooltip: '鍧愭爣杞村強绀轰緥绛夋彁绀烘枃瀛椾娇鐢ㄧ殑棰滆壊銆�', - required: false, - options: [{ - value: 'black', - text: '榛戣壊' - }, { - value: 'white', - text: '鐧借壊' - }] + initVal: card.color || 'rgba(0, 0, 0, 0.65)', + tooltip: '鍧愭爣杞存彁绀烘枃瀛楀強绀轰緥鐨勯鑹层��', + required: false } ] } diff --git a/src/menu/components/chart/antv-bar/index.jsx b/src/menu/components/chart/antv-bar/index.jsx index a80aa93..2c27c5f 100644 --- a/src/menu/components/chart/antv-bar/index.jsx +++ b/src/menu/components/chart/antv-bar/index.jsx @@ -51,6 +51,7 @@ width: card.width || 24, height: 400, barSize: 35, + color: 'rgba(0, 0, 0, 0.65)', name: card.name } @@ -195,7 +196,7 @@ linerender = () => { const { card } = this.state let plot = {...card.plot, height: card.plot.height - 80} // 鍘婚櫎title鎵�鍗犵┖闂� - let color = plot.color || 'rgba(0, 0, 0, 0.85)' + let color = plot.color || 'rgba(0, 0, 0, 0.65)' let X_axis = plot.Xaxis || 'x' let Y_axis = plot.Yaxis || ['y'] @@ -236,8 +237,10 @@ chart.data(dv.rows) - chart.axis(X_axis, { label: { style: { fill: color } }, line: { style: { fill: color } } }) - chart.axis('value', { grid: { style: { fill: color } }, label: { style: { fill: color } } }) + // chart.axis(X_axis, { label: { style: { fill: color } }, tickLine: {style: { stroke: color }}, line: { style: { stroke: color } } }) + // chart.axis('value', { grid: { line: { style: { stroke: color } }}, label: { style: { fill: color } } }) + chart.axis(X_axis, { label: { style: { fill: color } } }) + chart.axis('value', { label: { style: { fill: color } } }) if (plot.coordinate !== 'polar') { chart.scale(X_axis, { @@ -349,7 +352,7 @@ customrender = (data) => { let card = fromJS(this.state.card).toJS() let plot = {...card.plot, height: card.plot.height - 80} // 鍘婚櫎title鎵�鍗犵┖闂� - let color = plot.color || 'rgba(0, 0, 0, 0.85)' + let color = plot.color || 'rgba(0, 0, 0, 0.65)' let fields = [] let legends = [] let transfield = {} @@ -396,7 +399,8 @@ if (item.axis === 'true' && axisIndex < 2) { if (axisIndex === 0) { - item.axis = { grid: {style: { fill: color }}, title: { style: { fill: color } }, label: {style: { fill: color }} } + // item.axis = { grid: {line: { style: { stroke: color } }}, title: { style: { fill: color } }, label: {style: { fill: color }} } + item.axis = { title: { style: { fill: color } }, label: {style: { fill: color }} } fields.unshift(item) } else { item.axis = { grid: null, title: {style: { fill: color }}, label: {style: { fill: color }} } @@ -435,7 +439,8 @@ chart.data(dv.rows) - chart.axis(plot.Xaxis, { label: { style: { fill: color } }, line: { style: { fill: color } } }) + // chart.axis(plot.Xaxis, { label: { style: { fill: color } }, tickLine: {style: { stroke: color }}, line: { style: { stroke: color } } }) + chart.axis(plot.Xaxis, { label: { style: { fill: color } } }) if (!hasBar) { chart.scale(plot.Xaxis, { @@ -556,7 +561,7 @@ barrender = () => { const { card } = this.state let plot = {...card.plot, height: card.plot.height - 80} - let color = plot.color || 'rgba(0, 0, 0, 0.85)' + let color = plot.color || 'rgba(0, 0, 0, 0.65)' let X_axis = plot.Xaxis || 'x' let Y_axis = plot.Yaxis || ['y'] @@ -598,8 +603,10 @@ chart.data(dv.rows) - chart.axis(X_axis, { label: { style: { fill: color } }, line: { style: { fill: color } } }) - chart.axis('value', { grid: { style: { fill: color } }, label: { style: { fill: color } } }) + // chart.axis(X_axis, { label: { style: { fill: color } }, tickLine: {style: { stroke: color }}, line: { style: { stroke: color } } }) + // chart.axis('value', { grid: { line: { style: { stroke: color } }}, label: { style: { fill: color } } }) + chart.axis(X_axis, { label: { style: { fill: color } } }) + chart.axis('value', { label: { style: { fill: color } } }) chart.scale('value', { nice: true, diff --git a/src/menu/components/chart/antv-scatter/chartcompile/formconfig.jsx b/src/menu/components/chart/antv-scatter/chartcompile/formconfig.jsx new file mode 100644 index 0000000..e6a8a2f --- /dev/null +++ b/src/menu/components/chart/antv-scatter/chartcompile/formconfig.jsx @@ -0,0 +1,137 @@ +/** + * @description 鑾峰彇鍥捐〃瑙嗗浘閰嶇疆琛ㄥ崟 + * @param {object} card // 鍥捐〃瀵硅薄 + */ +export function getBaseForm (card) { + let roleList = sessionStorage.getItem('sysRoles') + if (roleList) { + try { + roleList = JSON.parse(roleList) + } catch { + roleList = [] + } + } else { + roleList = [] + } + + return [ + { + type: 'text', + key: 'title', + label: '鏍囬', + initVal: card.title, + required: false + }, + { + type: 'text', + key: 'name', + label: '缁勪欢鍚嶇О', + initVal: card.name, + tooltip: '鐢ㄤ簬缁勪欢闂寸殑鍖哄垎銆�', + required: true + }, + { + type: 'number', + key: 'width', + label: '瀹藉害', + initVal: card.width, + tooltip: '鏍呮牸甯冨眬锛屾瘡琛岀瓑鍒嗕负24鍒椼��', + min: 1, + max: 24, + decimal: 0, + required: true + }, + { + type: 'number', + key: 'height', + label: '楂樺害', + initVal: card.height, + min: 100, + max: 1000, + decimal: 0, + required: true + }, + { + type: 'select', + key: 'blacklist', + label: '榛戝悕鍗�', + initVal: card.blacklist || [], + multi: true, + required: false, + options: roleList + } + ] +} + +/** + * @description 鑾峰彇鍥捐〃瑙嗗浘閰嶇疆琛ㄥ崟 + * @param {object} card // 鍥捐〃瀵硅薄 + * @param {Array} columns // 鏄剧ず鍒� + */ +export function getOptionForm (card, columns) { + let xfields = columns.filter(item => /^Nvarchar/ig.test(item.datatype)) + let yfields = columns.filter(item => /^(Int|Decimal)/ig.test(item.datatype)) + + return [ + { + type: 'select', + key: 'gender', + label: '绫诲瀷', + initVal: card.gender || '', + required: true, + options: xfields + }, + { + type: 'select', + key: 'Xaxis', + label: 'X-杞�', + initVal: card.Xaxis || '', + required: true, + options: columns + }, + { + type: 'select', + key: 'Yaxis', + label: 'Y-杞�', + initVal: card.Yaxis || '', + required: true, + options: yfields + }, + { + type: 'radio', + key: 'shape', + label: '褰㈢姸', + initVal: card.shape || 'circle', + required: false, + options: [{ + value: 'circle', + text: 'circle' + }, { + value: 'square', + text: 'square' + }] + }, + { + type: 'text', + key: 'Xunit', + label: 'X杞村崟浣�', + initVal: card.Xunit || '', + required: false + }, + { + type: 'text', + key: 'Yunit', + label: 'Y杞村崟浣�', + initVal: card.Yunit || '', + required: false + }, + { + type: 'color', + key: 'color', + label: '鑹茬郴', + initVal: card.color || 'rgba(0, 0, 0, 0.65)', + tooltip: '鍧愭爣杞存彁绀烘枃瀛楀強绀轰緥鐨勯鑹层��', + required: false + } + ] +} diff --git a/src/menu/components/chart/antv-scatter/chartcompile/index.jsx b/src/menu/components/chart/antv-scatter/chartcompile/index.jsx new file mode 100644 index 0000000..0982251 --- /dev/null +++ b/src/menu/components/chart/antv-scatter/chartcompile/index.jsx @@ -0,0 +1,271 @@ +import React, {Component} from 'react' +import PropTypes from 'prop-types' +import { fromJS } from 'immutable' +import { Modal, Form, Row, Col, Select, Icon, Radio, Tooltip, Input, InputNumber, Tabs } from 'antd' + +import { getBaseForm, getOptionForm } from './formconfig' +import asyncComponent from '@/utils/asyncComponent' +import ColorSketch from '@/mob/colorsketch' +import './index.scss' + +const { TabPane } = Tabs +const NormalForm = asyncComponent(() => import('@/menu/components/share/normalform')) + +class LineChartDrawerForm extends Component { + static propTpyes = { + dict: PropTypes.object, + plot: PropTypes.object, + config: PropTypes.object, + plotchange: PropTypes.func + } + + state = { + view: 'normal', + visible: false, + plot: null, + formlist: null, + baseFormlist: null + } + + showDrawer = () => { + const { config } = this.props + + this.setState({ + visible: true, + view: 'normal', + plot: fromJS(config.plot).toJS(), + baseFormlist: getBaseForm(config.plot), + formlist: getOptionForm(config.plot, config.columns) + }) + } + + getFields() { + const { formlist } = this.state + const { getFieldDecorator } = this.props.form + const fields = [] + + if (!formlist) { + return fields + } + + formlist.forEach((item, index) => { + if (item.hidden || item.forbid) return + + if (item.type === 'text') { + fields.push( + <Col span={12} key={index}> + <Form.Item label={item.tooltip ? + <Tooltip placement="topLeft" title={item.tooltip}> + <Icon type="question-circle" /> + {item.label} + </Tooltip> : item.label + }> + {getFieldDecorator(item.key, { + initialValue: item.initVal, + rules: [ + { + required: !!item.required, + message: this.props.dict['form.required.input'] + item.label + '!' + } + ] + })(<Input placeholder="" autoComplete="off" disabled={item.readonly} onPressEnter={this.onSubmit}/>)} + </Form.Item> + </Col> + ) + } else if (item.type === 'number') { + fields.push( + <Col span={12} key={index}> + <Form.Item label={item.tooltip ? + <Tooltip placement="topLeft" title={item.tooltip}> + <Icon type="question-circle" /> + {item.label} + </Tooltip> : item.label + }> + {getFieldDecorator(item.key, { + initialValue: item.initVal, + rules: [ + { + required: !!item.required, + message: this.props.dict['form.required.input'] + item.label + '!' + } + ] + })(<InputNumber min={item.min} max={item.max} precision={item.decimal} onPressEnter={this.onSubmit}/>)} + </Form.Item> + </Col> + ) + } else if (item.type === 'select') { // 涓嬫媺 + fields.push( + <Col span={12} key={index}> + <Form.Item label={item.tooltip ? + <Tooltip placement="topLeft" title={item.tooltip}> + <Icon type="question-circle" /> + {item.label} + </Tooltip> : item.label + }> + {getFieldDecorator(item.key, { + initialValue: item.initVal, + rules: [ + { + required: !!item.required, + message: this.props.dict['form.required.select'] + item.label + '!' + } + ] + })( + <Select mode={item.multi ? 'multiple' : ''}> + {item.options.map((option, index) => + <Select.Option key={index} value={option.field || option.value}> + {option.label || option.text} + </Select.Option> + )} + </Select> + )} + </Form.Item> + </Col> + ) + } else if (item.type === 'radio') { + fields.push( + <Col span={12} key={index}> + <Form.Item label={item.tooltip ? + <Tooltip placement="topLeft" title={item.tooltip}> + <Icon type="question-circle" /> + {item.label} + </Tooltip> : item.label + }> + {getFieldDecorator(item.key, { + initialValue: item.initVal, + rules: [ + { + required: !!item.required, + message: this.props.dict['form.required.select'] + item.label + '!' + } + ] + })( + <Radio.Group disabled={item.readonly}> + {item.options.map(option => { + return ( + <Radio key={option.value} value={option.value}>{option.text}</Radio> + ) + })} + </Radio.Group> + )} + </Form.Item> + </Col> + ) + } else if (item.type === 'color') { + fields.push( + <Col span={12} key={index}> + <Form.Item label={item.tooltip ? + <Tooltip placement="topLeft" title={item.tooltip}> + <Icon type="question-circle" /> + {item.label} + </Tooltip> : item.label + }> + {getFieldDecorator(item.key, { + initialValue: item.initVal + })( + <ColorSketch /> + )} + </Form.Item> + </Col> + ) + } + }) + return fields + } + + onSubmit = () => { + const { config } = this.props + const { plot, view } = this.state + + if (view === 'normal') { + this.props.form.validateFieldsAndScroll((err, values) => { + if (!err) { + let _plot = {...plot, ...values} + + this.setState({ + plot: _plot, + visible: false + }) + + this.props.plotchange({...config, plot: _plot}) + } + }) + } else if (view === 'base') { + this.baseRef.handleConfirm().then(res => { + let _plot = {...plot, ...res} + + this.setState({ + plot: _plot, + visible: false + }) + + this.props.plotchange({...config, plot: _plot}) + }) + } + } + + changeTab = (tab) => { + const { plot, view } = this.state + + if (view === 'normal') { + this.props.form.validateFieldsAndScroll((err, values) => { + if (!err) { + this.setState({ + plot: {...plot, ...values}, + view: tab + }) + } + }) + } else if (view === 'base') { + this.baseRef.handleConfirm().then(res => { + this.setState({ + plot: {...plot, ...res}, + view: tab + }) + }) + } + } + + render() { + const { view, visible, baseFormlist } = this.state + const formItemLayout = { + labelCol: { + xs: { span: 24 }, + sm: { span: 6 } + }, + wrapperCol: { + xs: { span: 24 }, + sm: { span: 18 } + } + } + + return ( + <div className="line-chart-drawer-form"> + <Icon type="edit" title="缂栬緫" onClick={this.showDrawer} /> + <Modal + wrapClassName="popview-modal menu-chart-edit-modal" + title="鍥捐〃缂栬緫" + visible={visible} + width={850} + maskClosable={false} + onOk={this.onSubmit} + onCancel={() => { this.setState({ visible: false }) }} + destroyOnClose + > + <Tabs activeKey={view} className="menu-chart-edit-box" onChange={this.changeTab}> + <TabPane tab="缁勪欢璁剧疆" key="base"> + <NormalForm dict={this.props.dict} formlist={baseFormlist} inputSubmit={this.onSubmit} wrappedComponentRef={(inst) => this.baseRef = inst}/> + </TabPane> + <TabPane tab="鍥捐〃璁剧疆" key="normal"> + <Form {...formItemLayout}> + <Row gutter={16}>{this.getFields()}</Row> + </Form> + </TabPane> + </Tabs> + </Modal> + </div> + ); + } +} + +export default Form.create()(LineChartDrawerForm) \ No newline at end of file diff --git a/src/menu/components/chart/antv-scatter/chartcompile/index.scss b/src/menu/components/chart/antv-scatter/chartcompile/index.scss new file mode 100644 index 0000000..5928db8 --- /dev/null +++ b/src/menu/components/chart/antv-scatter/chartcompile/index.scss @@ -0,0 +1,41 @@ +.line-chart-drawer-form { + display: inline-block; + > .anticon-edit { + color: #1890ff; + } +} +.menu-chart-edit-modal { + .ant-modal { + top: 50px; + .ant-modal-body { + max-height: calc(100vh - 190px); + min-height: 50vh; + padding-top: 10px; + .menu-chart-edit-box { + .anticon-question-circle { + color: #c49f47; + position: relative; + left: -3px; + } + .ant-input-number { + width: 100%; + } + .ant-tabs-nav-wrap { + text-align: center; + } + .color-sketch-block { + position: relative; + top: 5px; + width: 240px; + } + .color-add { + float: right; + margin-bottom: 10px; + position: relative; + z-index: 1; + } + } + } + } +} + diff --git a/src/menu/components/chart/antv-scatter/index.jsx b/src/menu/components/chart/antv-scatter/index.jsx new file mode 100644 index 0000000..5151695 --- /dev/null +++ b/src/menu/components/chart/antv-scatter/index.jsx @@ -0,0 +1,390 @@ +import React, {Component} from 'react' +import PropTypes from 'prop-types' +import { is, fromJS } from 'immutable' +import { Icon, Popover, notification } from 'antd' +import { Chart } 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 Utils from '@/utils/utils.js' +import zhCN from '@/locales/zh-CN/model.js' +import enUS from '@/locales/en-US/model.js' +import './index.scss' + +const SettingComponent = asyncIconComponent(() => import('@/menu/datasource')) +const ChartCompileForm = asyncIconComponent(() => import('./chartcompile')) +const LogComponent = asyncIconComponent(() => import('@/menu/components/share/logcomponent')) +const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent')) +const PasteComponent = asyncIconComponent(() => import('@/menu/components/share/pastecomponent')) +const NormalHeader = asyncComponent(() => import('@/menu/components/share/normalheader')) +const ActionComponent = asyncComponent(() => import('@/menu/components/share/actioncomponent')) +const UserComponent = asyncIconComponent(() => import('@/menu/components/share/usercomponent')) +const ClockComponent = asyncIconComponent(() => import('@/menu/components/share/clockcomponent')) + +class antvScatterChart extends Component { + static propTpyes = { + card: PropTypes.object, + updateConfig: PropTypes.func, + deletecomponent: PropTypes.func, + } + + state = { + dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS, + card: null, + ismob: sessionStorage.getItem('appType') === 'mob', + eventListener: null + } + + UNSAFE_componentWillMount () { + const { card } = this.props + + if (card.isNew) { + let _plot = { + width: card.width || 24, + height: 400, + gender: '', + Xaxis: '', + Xunit: '', + Yaxis: '', + Yunit: '', + shape: 'circle', + color: 'rgba(0, 0, 0, 0.65)', + name: card.name + } + + let _card = { + uuid: card.uuid, + type: card.type, + floor: card.floor, + tabId: card.tabId || '', + parentId: card.parentId || '', + format: 'array', // 缁勪欢灞炴�� - 鏁版嵁鏍煎紡 + pageable: false, // 缁勪欢灞炴�� - 鏄惁鍙垎椤� + switchable: false, // 缁勪欢灞炴�� - 鏁版嵁鏄惁鍙垏鎹� + dataName: card.dataName || '', + width: _plot.width, + name: _plot.name, + subtype: card.subtype, + setting: { interType: 'system' }, + style: { + borderWidth: '1px', borderColor: 'rgb(217, 217, 217)', + marginLeft: '8px', marginRight: '8px', marginTop: '8px', marginBottom: '8px' + }, + headerStyle: { fontSize: '16px', borderBottomWidth: '1px', borderBottomColor: '#e8e8e8' }, + columns: [], + scripts: [], + search: [], + action: [], + plot: _plot, + btnlog: [], + } + + if (card.config) { + let config = fromJS(card.config).toJS() + + _card.plot = config.plot + _card.plot.name = card.name + _card.style = config.style + _card.headerStyle = config.headerStyle + + _card.action = config.action.map(col => { + col.uuid = Utils.getuuid() + return col + }) + _card.search = config.search.map(col => { + col.uuid = Utils.getuuid() + return col + }) + } + this.setState({ + card: _card + }) + this.props.updateConfig(_card) + } else { + this.setState({ + card: fromJS(card).toJS() + }) + } + } + + componentDidMount () { + this.ponitrender() + MKEmitter.addListener('submitStyle', this.getStyle) + MKEmitter.addListener('tabsChange', this.handleTabsChange) + } + + shouldComponentUpdate (nextProps, nextState) { + return !is(fromJS(this.state), fromJS(nextState)) + } + + /** + * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊锛屾竻闄ゅ揩鎹烽敭璁剧疆 + */ + componentWillUnmount () { + this.setState = () => { + return + } + MKEmitter.removeListener('submitStyle', this.getStyle) + MKEmitter.removeListener('tabsChange', this.handleTabsChange) + } + + handleTabsChange = (parentId) => { + const { card } = this.state + + if (parentId === card.parentId) { + let _element = document.getElementById(card.uuid + 'canvas') + if (_element) { + _element.innerHTML = '' + } + + setTimeout(this.ponitrender, 100) + } + } + + getdata = () => { + let data = [] + + for (let i = 0; i < 500; i++) { + let item = {} + let n = Math.random() + let m = Math.random() + if (n > 0.7) { + n = n * n + } else if (n < 0.3) { + n = Math.sqrt(n) + } + if (m > 0.7) { + m = m * m + } else if (m < 0.3) { + m = Math.sqrt(m) + } + + if (i % 2 === 0) { + item.gender = 'male' + item.height = 160 + Math.floor(n * 35) + item.weight = 50 + Math.floor(m * 55) + } else { + item.gender = 'female' + item.height = 140 + Math.floor(n * 40) + item.weight = 41 + Math.floor(m * 45) + } + + data.push(item) + } + + return data + } + + /** + * @description 鏁g偣鍥� + */ + ponitrender = () => { + const { card } = this.state + const plot = card.plot + const data = this.getdata() + + const chart = new Chart({ + container: card.uuid + 'canvas', + autoFit: true, + height: plot.height - 80 + }) + + chart.data(data); + chart.scale({ + height: { nice: true }, + weight: { nice: true }, + }) + + // chart.axis('height', { label: { style: { fill: plot.color } }, tickLine: {style: { stroke: plot.color }}, line: { style: { stroke: plot.color } } }) + // chart.axis('weight', { grid: { line: { style: { stroke: plot.color } }}, label: { style: { fill: plot.color } } }) + chart.axis('height', { label: { style: { fill: plot.color } } }) + chart.axis('weight', { label: { style: { fill: plot.color } } }) + chart.legend({ + position: plot.legend || 'bottom', + itemName: { style: { fill: plot.color } } + }) + + chart.tooltip({ + showTitle: false, + showCrosshairs: true, + crosshairs: { + type: 'xy', + } + }) + chart + .point() + .position('height*weight') + .color('gender') + .shape(plot.shape) + .tooltip('gender*height*weight', (gender, height, weight) => { + return { + name: gender, + value: height + (plot.Xunit ? `(${plot.Xunit}), ` : ', ') + weight + (plot.Yunit ? `(${plot.Yunit})` : '') + }; + }) + .style({ + fillOpacity: 0.85 + }) + chart.interaction('legend-highlight'); + 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 + } + + component.width = component.plot.width + component.name = component.plot.name + + this.setState({ + card: component + }, () => { + if (refresh) { + setTimeout(() => { + this.ponitrender() + }, 100) + } + }) + this.props.updateConfig(component) + } + + addSearch = () => { + const { card } = this.state + + let newcard = {} + newcard.uuid = Utils.getuuid() + newcard.focus = true + + newcard.label = 'label' + newcard.initval = '' + newcard.type = 'select' + newcard.resourceType = '0' + newcard.options = [] + newcard.setAll = 'false' + newcard.orderType = 'asc' + newcard.display = 'dropdown' + newcard.match = '=' + + // 娉ㄥ唽浜嬩欢-娣诲姞鎼滅储 + MKEmitter.emit('addSearch', card.uuid, newcard) + } + + addButton = () => { + const { card } = this.state + + let newcard = {} + newcard.uuid = Utils.getuuid() + newcard.focus = true + + newcard.label = '瀵煎嚭Excel' + newcard.sqlType = '' + newcard.Ot = 'requiredSgl' + newcard.OpenType = 'excelOut' + newcard.icon = 'download' + newcard.class = 'dgreen' + newcard.intertype = card.setting.interType + newcard.innerFunc = card.setting.innerFunc || '' + newcard.sysInterface = card.setting.sysInterface || '' + newcard.outerFunc = card.setting.outerFunc || '' + newcard.interface = card.setting.interface || '' + newcard.execSuccess = 'grid' + newcard.execError = 'never' + newcard.popClose = 'never' + newcard.errorTime = 10 + newcard.verify = null + newcard.show = 'icon' + + // 娉ㄥ唽浜嬩欢-娣诲姞鎸夐挳 + MKEmitter.emit('addButton', card.uuid, newcard) + } + + changeStyle = () => { + const { card } = this.state + + MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style) + } + + getStyle = (comIds, style) => { + const { card } = this.state + + if (comIds[0] !== card.uuid || comIds.length > 1) return + + let _card = {...card, style} + + 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() + MKEmitter.emit('clickComponent', this.state.card) + } + } + + render() { + const { card, ismob } = this.state + let _style = resetStyle(card.style) + + return ( + <div className="menu-line-chart-edit-box" style={{..._style, height: card.plot.height || 400}} onClick={this.clickComponent} id={card.uuid}> + <NormalHeader config={card} updateComponent={this.updateComponent}/> + <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={ + <div className="mk-popover-control"> + {!ismob ? <Icon className="plus" title="娣诲姞鎼滅储" onClick={this.addSearch} type="plus-circle" /> : null} + <Icon className="plus" title="娣诲姞鎸夐挳" onClick={this.addButton} type="plus-square" /> + <ChartCompileForm config={card} dict={this.state.dict} plotchange={this.updateComponent}/> + <CopyComponent type="line" card={card}/> + <PasteComponent config={card} options={['action', '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)} /> + <SettingComponent config={card} updateConfig={this.updateComponent}/> + </div> + } trigger="hover"> + <Icon type="tool" /> + </Popover> + <ActionComponent type="chart" config={card} updateaction={this.updateComponent} /> + <div className="canvas" id={card.uuid + 'canvas'}></div> + </div> + ) + } +} + +export default antvScatterChart \ No newline at end of file diff --git a/src/menu/components/chart/antv-scatter/index.scss b/src/menu/components/chart/antv-scatter/index.scss new file mode 100644 index 0000000..9754b3c --- /dev/null +++ b/src/menu/components/chart/antv-scatter/index.scss @@ -0,0 +1,65 @@ +.menu-line-chart-edit-box { + position: relative; + box-sizing: border-box; + background: #ffffff; + background-position: center center; + background-repeat: no-repeat; + background-size: cover; + + .canvas { + margin: 0px; + // padding: 20px 15px 15px; + padding: 15px; + letter-spacing: 0px; + } + + .chart-header { + position: relative; + height: 45px; + border-bottom: 1px solid #e8e8e8; + overflow: hidden; + padding-right: 35px; + + .chart-title { + text-decoration: inherit; + font-weight: inherit; + font-style: inherit; + float: left; + line-height: 45px; + margin-left: 10px; + position: relative; + z-index: 1; + } + } + + >.anticon-tool { + position: absolute; + right: 1px; + top: 1px; + z-index: 2; + font-size: 16px; + padding: 5px; + cursor: pointer; + color: rgba(0, 0, 0, 0.85); + background: rgba(255, 255, 255, 0.55); + } + + .model-menu-action-list { + position: absolute; + right: 0px; + z-index: 4; + font-size: 16px; + + .ant-row .anticon-plus { + float: right; + } + + .page-card { + float: right; + } + } +} +.menu-line-chart-edit-box:hover { + z-index: 1; + box-shadow: 0px 0px 4px #1890ff; +} diff --git a/src/menu/components/group/groupcomponents/index.jsx b/src/menu/components/group/groupcomponents/index.jsx index 0baa248..acffe01 100644 --- a/src/menu/components/group/groupcomponents/index.jsx +++ b/src/menu/components/group/groupcomponents/index.jsx @@ -105,6 +105,7 @@ table: '琛ㄦ牸', editor: '瀵屾枃鏈�', dashboard: '浠〃鐩�', + scatter: '鏁g偣鍥�', card: '鍗$墖' } let i = 1 diff --git a/src/menu/components/share/pastecomponent/index.jsx b/src/menu/components/share/pastecomponent/index.jsx index 2d12bb3..85be01c 100644 --- a/src/menu/components/share/pastecomponent/index.jsx +++ b/src/menu/components/share/pastecomponent/index.jsx @@ -106,11 +106,7 @@ const { options } = this.props this.pasteFormRef.handleConfirm().then(res => { if (!options.includes(res.copyType)) { - notification.warning({ - top: 92, - message: '閰嶇疆淇℃伅鏍煎紡閿欒锛�', - duration: 5 - }) + notification.warning({ top: 92, message: '閰嶇疆淇℃伅鏍煎紡閿欒锛�', duration: 5 }) return } @@ -131,6 +127,10 @@ config.action = config.action || [] config.action = config.action.filter(item => !item.origin) + if (['line', 'bar', 'scatter'].includes(config.type) && !['excelOut', 'excelIn'].includes(res.OpenType)) { + notification.warning({ top: 92, message: '鍥捐〃涓笉鏀寔姝ょ被鎸夐挳锛�', duration: 5 }) + return + } MKEmitter.emit('addButton', config.uuid, res) } else if (type === 'search' || type === 'form') { config.search = config.search || [] diff --git a/src/menu/components/tabs/tabcomponents/index.jsx b/src/menu/components/tabs/tabcomponents/index.jsx index 9b34445..816547b 100644 --- a/src/menu/components/tabs/tabcomponents/index.jsx +++ b/src/menu/components/tabs/tabcomponents/index.jsx @@ -99,6 +99,7 @@ editor: '瀵屾枃鏈�', carousel: '杞挱', dashboard: '浠〃鐩�', + scatter: '鏁g偣鍥�', card: '鍗$墖' } let i = 1 diff --git a/src/menu/menushell/card.jsx b/src/menu/menushell/card.jsx index 17310a5..e086365 100644 --- a/src/menu/menushell/card.jsx +++ b/src/menu/menushell/card.jsx @@ -8,6 +8,7 @@ const MainSearch = asyncComponent(() => import('@/menu/components/search/main-search')) const AntvPie = asyncComponent(() => import('@/menu/components/chart/antv-pie')) const AntvDashboard = asyncComponent(() => import('@/menu/components/chart/antv-dashboard')) +const AntvScatter = asyncComponent(() => import('@/menu/components/chart/antv-scatter')) const AntvTabs = asyncComponent(() => import('@/menu/components/tabs/antv-tabs')) const DataCard = asyncComponent(() => import('@/menu/components/card/data-card')) const PropCard = asyncComponent(() => import('@/menu/components/card/prop-card')) @@ -62,6 +63,8 @@ return (<AntvPie card={card} updateConfig={updateConfig} deletecomponent={delCard}/>) } else if (card.type === 'dashboard') { return (<AntvDashboard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>) + } else if (card.type === 'scatter') { + return (<AntvScatter card={card} updateConfig={updateConfig} deletecomponent={delCard}/>) } else if (card.type === 'form') { return (<NormalForm card={card} updateConfig={updateConfig} deletecomponent={delCard}/>) } else if (card.type === 'tabs') { diff --git a/src/menu/menushell/index.jsx b/src/menu/menushell/index.jsx index 5121c17..8a9fcfa 100644 --- a/src/menu/menushell/index.jsx +++ b/src/menu/menushell/index.jsx @@ -98,6 +98,7 @@ carousel: '杞挱', form: '琛ㄥ崟', dashboard: '浠〃鐩�', + scatter: '鏁g偣鍥�', card: '鍗$墖' } let i = 1 diff --git a/src/menu/modulesource/option.jsx b/src/menu/modulesource/option.jsx index 0161c6f..9bd7d81 100644 --- a/src/menu/modulesource/option.jsx +++ b/src/menu/modulesource/option.jsx @@ -18,6 +18,7 @@ import Carousel1 from '@/assets/mobimg/carousel1.png' import form from '@/assets/mobimg/form.png' import dashboard from '@/assets/mobimg/dashboard.png' +import scatter from '@/assets/mobimg/scatter.png' // 缁勪欢閰嶇疆淇℃伅 export const menuOptions = [ @@ -38,6 +39,7 @@ { type: 'menu', url: Pie1, component: 'pie', subtype: 'ring', title: '鐜浘', width: 12 }, { type: 'menu', url: Pie2, component: 'pie', subtype: 'nightingale', title: '鍗椾竵鏍煎皵鍥�', width: 12 }, { type: 'menu', url: dashboard, component: 'dashboard', subtype: 'dashboard', title: '浠〃鐩�', width: 12 }, + { type: 'menu', url: scatter, component: 'scatter', subtype: 'scatter', title: '鏁g偣鍥�', width: 24 }, { type: 'menu', url: Editor, component: 'editor', subtype: 'brafteditor', title: '瀵屾枃鏈�', width: 24 }, { type: 'menu', url: SandBox, component: 'code', subtype: 'sandbox', title: '鑷畾涔�', width: 24 }, { type: 'menu', url: group, component: 'group', subtype: 'normalgroup', title: '鍒嗙粍', width: 24, forbid: ['billPrint'] }, diff --git a/src/tabviews/custom/components/chart/antv-bar-line/index.jsx b/src/tabviews/custom/components/chart/antv-bar-line/index.jsx index 01878f0..73dc126 100644 --- a/src/tabviews/custom/components/chart/antv-bar-line/index.jsx +++ b/src/tabviews/custom/components/chart/antv-bar-line/index.jsx @@ -117,7 +117,7 @@ } }) - _config.plot.color = _config.plot.color || 'rgba(0, 0, 0, 0.85)' + _config.plot.color = _config.plot.color || 'rgba(0, 0, 0, 0.65)' if (_config.plot.enabled === 'true' && _config.plot.customs && _config.plot.customs.length > 0) { let colors = new Map() @@ -158,7 +158,7 @@ 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 }} } + item.axis = { 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 }} } @@ -846,9 +846,8 @@ }, style: { fill: plot.color } }, - line: { style: { fill: plot.color } } }) - chart.axis(_valfield, { grid: { style: { fill: plot.color } }, label: { style: { fill: plot.color } } }) + chart.axis(_valfield, { label: { style: { fill: plot.color } } }) if (!plot.legend || plot.legend === 'hidden') { chart.legend(false) @@ -998,7 +997,6 @@ }, style: { fill: plot.color } }, - line: { style: { fill: plot.color } } }) if (!plot.legend || plot.legend === 'hidden') { @@ -1199,9 +1197,8 @@ }, style: { fill: plot.color } }, - line: { style: { fill: plot.color } } }) - chart.axis(_valfield, { grid: { style: { fill: plot.color } }, label: { style: { fill: plot.color } } }) + chart.axis(_valfield, { label: { style: { fill: plot.color } } }) if (!plot.legend || plot.legend === 'hidden') { chart.legend(false) diff --git a/src/tabviews/custom/components/chart/antv-dashboard/index.jsx b/src/tabviews/custom/components/chart/antv-dashboard/index.jsx index 46a23ab..2fe7eb4 100644 --- a/src/tabviews/custom/components/chart/antv-dashboard/index.jsx +++ b/src/tabviews/custom/components/chart/antv-dashboard/index.jsx @@ -304,27 +304,33 @@ } /** - * @description 楗煎浘娓叉煋 + * @description 浠〃鐩樻覆鏌� */ dashboardrender = () => { const { plot, chartId, data } = this.state + + let _data = fromJS(data).toJS() + if (_data.value && _data.value > plot.maxValue) { + _data.value = plot.maxValue + } + const chart = new Chart({ container: chartId, autoFit: true, height: plot.height ? (plot.height - 80) : 320, 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('value', { @@ -345,9 +351,9 @@ } }, grid: null, - }); - chart.legend(false); - chart.tooltip(false); + }) + chart.legend(false) + chart.tooltip(false) chart .point() .position('value*1') @@ -371,7 +377,7 @@ appear: { animation: 'fade-in' } - }); + }) // 缁樺埗浠〃鐩樿儗鏅� chart.annotation().arc({ @@ -394,7 +400,7 @@ lineWidth: 18, lineDash: null, }, - }); + }) } else { let start = 0 plot.colors.forEach(item => { @@ -438,7 +444,7 @@ textAlign: 'center', }, offsetY: 15, - }); + }) } chart.render() diff --git a/src/tabviews/custom/components/chart/antv-scatter/asyncButtonComponent.jsx b/src/tabviews/custom/components/chart/antv-scatter/asyncButtonComponent.jsx new file mode 100644 index 0000000..5fb9c1a --- /dev/null +++ b/src/tabviews/custom/components/chart/antv-scatter/asyncButtonComponent.jsx @@ -0,0 +1,34 @@ +import React, {Component} from 'react' +import { Button } from 'antd' + +/** + * @description 寮傛鍔犺浇妯″潡 + * @param {*} importComponent + */ +export default function asyncComponent(importComponent) { + return class extends Component { + constructor(props) { + super(props) + + this.state = { + component: null + } + } + + async componentDidMount() { + const {default: component} = await importComponent() + + this.setState({component}) + } + + // <Button className="loading-skeleton" disabled={true}></Button> // 楠ㄦ灦鎸夐挳 + render() { + const C = this.state.component + const btn = this.props.btn || {} + + return C ? + <C {...this.props} /> : + <Button icon={btn.OpenType === 'excelOut' ? 'download' : 'upload'} disabled={true} title={btn.label} style={{border: 0, background: 'transparent'}}></Button> + } + } +} \ No newline at end of file diff --git a/src/tabviews/custom/components/chart/antv-scatter/index.jsx b/src/tabviews/custom/components/chart/antv-scatter/index.jsx new file mode 100644 index 0000000..892847a --- /dev/null +++ b/src/tabviews/custom/components/chart/antv-scatter/index.jsx @@ -0,0 +1,442 @@ +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 { Spin, Empty, notification } from 'antd' +import moment from 'moment' + +import Api from '@/api' +import Utils from '@/utils/utils.js' +import asyncComponent from './asyncButtonComponent' +import UtilsDM from '@/utils/utils-datamanage.js' +import { modifyTabview } from '@/store/action' +import MKEmitter from '@/utils/events.js' +import './index.scss' + +const NormalHeader = asyncComponent(() => import('@/tabviews/custom/components/share/normalheader')) +const ExcelOutButton = asyncComponent(() => import('@/tabviews/zshare/actionList/exceloutbutton')) +const ExcelInButton = asyncComponent(() => import('@/tabviews/zshare/actionList/excelInbutton')) + +class ScatterChart extends Component { + static propTpyes = { + BID: PropTypes.any, // 鐖剁骇Id + data: PropTypes.array, // 缁熶竴鏌ヨ鏁版嵁 + config: PropTypes.object, // 缁勪欢閰嶇疆淇℃伅 + mainSearch: PropTypes.any, // 澶栧眰鎼滅储鏉′欢 + menuType: PropTypes.any, // 鑿滃崟绫诲瀷 + } + + state = { + BID: '', // 涓昏〃ID + config: null, // 鍥捐〃閰嶇疆淇℃伅 + empty: true, // 鍥捐〃鏁版嵁涓虹┖ + loading: false, // 鏁版嵁鍔犺浇鐘舵�� + chartId: Utils.getuuid(), // 鍥捐〃Id + sync: false, // 鏄惁缁熶竴璇锋眰鏁版嵁 + plot: null, // 鍥捐〃璁剧疆 + data: null, // 鏁版嵁 + search: null, // 鎼滅储鏉′欢 + } + + UNSAFE_componentWillMount () { + const { config, data, initdata, BID } = 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 + } + + if (config.plot.title || config.search.length > 0) { + _config.plot.height = _config.plot.height - 80 + } else { + _config.plot.height = _config.plot.height - 30 + } + + _config.style = {..._config.style, minHeight: config.plot.height} + + this.setState({ + config: _config, + data: _data, + BID: BID || '', + empty: !_data, + arr_field: _config.columns.map(col => col.field).join(','), + plot: _config.plot, + sync: _sync, + search: Utils.initMainSearch(config.search), + }, () => { + 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, empty: !_data,}, () => { + this.handleData() + }) + } else if (nextProps.mainSearch && !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)) + } + + componentDidMount () { + MKEmitter.addListener('reloadData', this.reloadData) + MKEmitter.addListener('resetSelectLine', this.resetParentParam) + MKEmitter.addListener('getexceloutparam', this.getexceloutparam) + MKEmitter.addListener('refreshByButtonResult', this.refreshByButtonResult) + this.handleTimer() + } + + /** + * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊锛屾竻闄ゅ揩鎹烽敭璁剧疆 + */ + componentWillUnmount () { + clearTimeout(this.timer) + this.setState = () => { + return + } + MKEmitter.removeListener('reloadData', this.reloadData) + MKEmitter.removeListener('resetSelectLine', this.resetParentParam) + MKEmitter.removeListener('getexceloutparam', this.getexceloutparam) + MKEmitter.removeListener('refreshByButtonResult', this.refreshByButtonResult) + } + + handleTimer = () => { + const { config } = this.state + + if (!config.timer) return + + const _change = { + '15s': 15000, + '30s': 30000, + '1min': 60000, + '5min': 300000, + '10min': 600000, + '15min': 900000, + '30min': 1800000, + '1hour': 3600000 + } + + let timer = _change[config.timer] + + if (!timer) return + + let _param = { + func: 's_get_timers_role', + LText: `select '${window.GLOB.appkey || ''}','${config.uuid}'`, + timer_type: config.timer, + component_id: config.uuid + } + + _param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss') // 鏃堕棿鎴� + _param.LText = Utils.formatOptions(_param.LText) // 鍏抽敭瀛楃鏇挎崲锛宐ase64鍔犲瘑 + _param.secretkey = Utils.encrypt(_param.LText, _param.timestamp) // md5瀵嗛挜 + + Api.getSystemConfig(_param).then(result => { + if (!result.status) { + notification.warning({ + top: 92, + message: result.message, + duration: 5 + }) + return + } else if (result.run_type) { + this.setState({timer}) + this.timer = setTimeout(() => { + this.timerTask() + }, timer) + } + }) + } + + timerTask = () => { + const { timer } = this.state + if (!timer) return + + this.loadData(true) + + this.timer = setTimeout(() => { + this.timerTask() + }, timer) + } + + /** + * @description 鎸夐挳鎵ц瀹屾垚鍚庨〉闈㈠埛鏂� + * @param {*} menuId // 鑿滃崟Id + * @param {*} position // 鍒锋柊浣嶇疆 + * @param {*} btn // 鎵ц鐨勬寜閽� + */ + refreshByButtonResult = (menuId, position, btn) => { + const { config, BID } = this.state + + if (config.uuid !== menuId) return + + this.loadData() // 鏁版嵁鍒锋柊 + + if (btn.syncComponentId && btn.syncComponentId !== config.uuid && btn.syncComponentId !== config.setting.supModule) { + MKEmitter.emit('reloadData', btn.syncComponentId) // 鍚岀骇鏍囩鍒锋柊 + } + + if (position === 'mainline' && config.setting.supModule) { // 涓昏〃琛屽埛鏂� + MKEmitter.emit('reloadData', config.setting.supModule, (BID || 'empty')) + } + } + + reloadData = (menuId) => { + const { config } = this.state + + if (config.uuid !== menuId) return + + this.loadData() + } + + resetParentParam = (MenuID, id) => { + const { config } = this.state + + if (!config.setting.supModule || config.setting.supModule !== MenuID) return + if (id !== this.state.BID) { + this.setState({ BID: id }, () => { + this.loadData() + }) + } + } + + /** + * @description 瀵煎嚭Excel鏃讹紝鑾峰彇椤甸潰鎼滅储鎺掑簭绛夊弬鏁� + */ + getexceloutparam = (menuId, btnId) => { + const { mainSearch } = this.props + const { arr_field, config, search } = this.state + + if (config.uuid !== menuId) return + + let searches = search ? fromJS(search).toJS() : [] + if (mainSearch && mainSearch.length > 0) { // 涓昏〃鎼滅储鏉′欢 + let keys = searches.map(item => item.key.toLowerCase()) + mainSearch.forEach(item => { + if (!keys.includes(item.key.toLowerCase())) { + searches.push(item) + } + }) + } + + MKEmitter.emit('execExcelout', config.uuid, btnId, { + arr_field: arr_field, + orderBy: config.setting.order || '', + search: searches, + menuName: config.name + }) + } + + /** + * @description 鏁版嵁鍔犺浇 + */ + async loadData (hastimer) { + const { mainSearch, menuType } = this.props + const { config, arr_field, BID, search } = this.state + + if (config.setting.supModule && !BID) { // BID 涓嶅瓨鍦ㄦ椂锛屼笉鍋氭煡璇� + this.setState({ + data: [], + empty: false, + }, () => { + this.handleData() + }) + return + } + + let searches = search ? 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) + } + }) + } + + if (!hastimer) { + this.setState({ + loading: true + }) + } + + let _orderBy = config.setting.order || '' + let param = UtilsDM.getQueryDataParams(config.setting, arr_field, searches, _orderBy, '', '', BID, menuType) + + let result = await Api.genericInterface(param) + if (result.status) { + this.setState({ + data: result.data, + empty: false, + loading: false + }, () => { + this.handleData() + }) + } else { + this.setState({ + loading: false, + timer: null + }) + notification.error({ + top: 92, + message: result.message, + duration: 10 + }) + } + } + + /** + * @description 鏁版嵁棰勫鐞嗭紝缁熻鏁版嵁闇�瑕侀噸缃� + */ + handleData = () => { + let _element = document.getElementById(this.state.chartId) + if (_element) { + _element.innerHTML = '' + } + this.scatterrender() + } + + /** + * @description 鎶樼嚎鍥炬覆鏌� + */ + scatterrender = () => { + const { plot, data, chartId } = this.state + const chart = new Chart({ + container: chartId, + autoFit: true, + height: plot.height + }) + + chart.data(data); + chart.scale({ + [plot.Xaxis]: { nice: true }, + [plot.Yaxis]: { nice: true }, + }) + + chart.axis(plot.Xaxis, { label: { style: { fill: plot.color } } }) + chart.axis(plot.Yaxis, { label: { style: { fill: plot.color } } }) + chart.legend({ + position: 'bottom', + itemName: { style: { fill: plot.color } } + }) + + chart.tooltip({ + showTitle: false, + showCrosshairs: true, + crosshairs: { + type: 'xy', + } + }) + chart + .point() + .position(`${plot.Xaxis}*${plot.Yaxis}`) + .color(plot.gender) + .shape(plot.shape) + .tooltip(`${plot.gender}*${plot.Xaxis}*${plot.Yaxis}`, (gender, height, weight) => { + return { + name: gender, + value: height + (plot.Xunit ? `(${plot.Xunit}), ` : ', ') + weight + (plot.Yunit ? `(${plot.Yunit})` : '') + }; + }) + .style({ + fillOpacity: 0.85 + }) + chart.interaction('legend-highlight'); + chart.render() + } + + refreshSearch = (list) => { + this.setState({search: list}, () => { + this.loadData() + }) + } + + render() { + const { config, loading, empty, BID } = this.state + + return ( + <div className="custom-scatter-plot-box" style={config.style}> + {loading ? + <div className="loading-mask"> + <div className="ant-spin-blur"></div> + <Spin /> + </div> : null + } + <NormalHeader config={config} BID={BID} menuType={this.props.menuType} refresh={this.refreshSearch} /> + <div className="canvas-wrap"> + <div className="chart-action"> + {config.action.map(item => { + if (item.OpenType === 'excelOut') { + return ( + <ExcelOutButton + key={item.uuid} + BID={BID} + btn={item} + show="icon" + setting={config.setting} + /> + ) + } else { + return ( + <ExcelInButton + key={item.uuid} + BID={BID} + btn={item} + show="icon" + setting={config.setting} + /> + ) + } + })} + </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)(ScatterChart) \ No newline at end of file diff --git a/src/tabviews/custom/components/chart/antv-scatter/index.scss b/src/tabviews/custom/components/chart/antv-scatter/index.scss new file mode 100644 index 0000000..c8f3c8c --- /dev/null +++ b/src/tabviews/custom/components/chart/antv-scatter/index.scss @@ -0,0 +1,65 @@ +.custom-scatter-plot-box { + position: relative; + background: #ffffff; + background-position: center center; + background-repeat: no-repeat; + background-size: cover; + min-height: 100px; + + .canvas-wrap { + margin: 0 0px; + position: relative; + .chart-action { + position: absolute; + top: 0px; + right: 5px; + z-index: 1; + .ant-btn { + float: right; + } + } + } + + .canvas { + margin: 0; + padding: 20px 15px 15px; + letter-spacing: 0px; + } + .canvas.empty { + div { + opacity: 0; + } + } + .ant-empty { + position: absolute; + top: calc(50% - 34px); + left: calc(50% - 92px); + + .ant-empty-image { + height: 60px; + } + } + .loading-mask { + position: absolute; + left: 0px; + top: 0; + right: 0px; + bottom: 0px; + z-index: 1; + + .ant-spin-blur { + position: absolute; + width: 100%; + height: 100%; + opacity: 0.5; + background: #ffffff; + } + } + + .g2-tooltip-list{ + display: none; + } + .g2-tooltip-title + .g2-tooltip-list{ + display: block; + } +} diff --git a/src/tabviews/custom/components/group/normal-group/index.jsx b/src/tabviews/custom/components/group/normal-group/index.jsx index 066f8c7..9de350a 100644 --- a/src/tabviews/custom/components/group/normal-group/index.jsx +++ b/src/tabviews/custom/components/group/normal-group/index.jsx @@ -16,6 +16,7 @@ const AntvBarAndLine = asyncComponent(() => import('@/tabviews/custom/components/chart/antv-bar-line')) const AntvPie = asyncComponent(() => import('@/tabviews/custom/components/chart/antv-pie')) const AntvDashboard = asyncComponent(() => import('@/tabviews/custom/components/chart/antv-dashboard')) +const AntvScatter = asyncComponent(() => import('@/tabviews/custom/components/chart/antv-scatter')) const DataCard = asyncComponent(() => import('@/tabviews/custom/components/card/data-card')) const TableCard = asyncComponent(() => import('@/tabviews/custom/components/card/table-card')) const NormalTable = asyncComponent(() => import('@/tabviews/custom/components/table/normal-table')) @@ -260,6 +261,12 @@ <AntvDashboard config={item} data={data} BID={_bid} mainSearch={mainSearch} menuType={menuType} /> </Col> ) + } else if (item.type === 'scatter') { + return ( + <Col span={item.width} key={item.uuid}> + <AntvScatter config={item} data={data} BID={_bid} mainSearch={mainSearch} menuType={menuType} /> + </Col> + ) } else if (item.type === 'card' && item.subtype === 'datacard') { return ( <Col span={item.width} key={item.uuid}> diff --git a/src/tabviews/custom/components/share/tabtransfer/index.jsx b/src/tabviews/custom/components/share/tabtransfer/index.jsx index 855a989..8b8a49b 100644 --- a/src/tabviews/custom/components/share/tabtransfer/index.jsx +++ b/src/tabviews/custom/components/share/tabtransfer/index.jsx @@ -19,6 +19,7 @@ const AntvDashboard = asyncComponent(() => import('@/tabviews/custom/components/chart/antv-dashboard')) const AntvTabs = asyncComponent(() => import('@/tabviews/custom/components/tabs/antv-tabs')) const DataCard = asyncComponent(() => import('@/tabviews/custom/components/card/data-card')) +const AntvScatter = asyncComponent(() => import('@/tabviews/custom/components/chart/antv-scatter')) const TableCard = asyncComponent(() => import('@/tabviews/custom/components/card/table-card')) const NormalTable = asyncComponent(() => import('@/tabviews/custom/components/table/normal-table')) const PropCard = asyncComponent(() => import('@/tabviews/custom/components/card/prop-card')) @@ -279,6 +280,12 @@ <AntvDashboard config={item} data={data} BID={BID} mainSearch={mainSearch} menuType={menuType} /> </Col> ) + } else if (item.type === 'scatter') { + return ( + <Col span={item.width} key={item.uuid}> + <AntvScatter config={item} data={data} BID={BID} mainSearch={mainSearch} menuType={menuType} /> + </Col> + ) } else if (item.type === 'search') { return ( <Col span={item.width} key={item.uuid}> diff --git a/src/tabviews/custom/index.jsx b/src/tabviews/custom/index.jsx index ed3932d..d3c9587 100644 --- a/src/tabviews/custom/index.jsx +++ b/src/tabviews/custom/index.jsx @@ -22,6 +22,7 @@ const AntvPie = asyncComponent(() => import('./components/chart/antv-pie')) const AntvTabs = asyncComponent(() => import('./components/tabs/antv-tabs')) const AntvDashboard = asyncComponent(() => import('./components/chart/antv-dashboard')) +const AntvScatter = asyncComponent(() => import('./components/chart/antv-scatter')) const DataCard = asyncComponent(() => import('./components/card/data-card')) const PropCard = asyncComponent(() => import('./components/card/prop-card')) const NormalForm = asyncComponent(() => import('./components/form/normal-form')) @@ -1006,6 +1007,12 @@ <AntvPie config={item} data={data} BID={_bid} mainSearch={mainSearch} menuType={menuType} /> </Col> ) + } else if (item.type === 'scatter') { + return ( + <Col span={item.width} key={item.uuid}> + <AntvScatter config={item} data={data} BID={_bid} mainSearch={mainSearch} menuType={menuType} /> + </Col> + ) } else if (item.type === 'dashboard') { return ( <Col span={item.width} key={item.uuid}> diff --git a/src/views/billprint/index.jsx b/src/views/billprint/index.jsx index 66e09e1..eaa9f8b 100644 --- a/src/views/billprint/index.jsx +++ b/src/views/billprint/index.jsx @@ -502,6 +502,7 @@ let linkList = document.getElementsByTagName('link') // 鑾峰彇鐖剁獥鍙ink鏍囩瀵硅薄鍒楄〃 let styleList = document.getElementsByTagName('style') // 鑾峰彇鐖剁獥鍙tyle鏍囩瀵硅薄鍒楄〃 + iframe.style.marginTop = '600px' document.body.appendChild(iframe) let doc = iframe.contentWindow.document diff --git a/src/views/menudesign/index.jsx b/src/views/menudesign/index.jsx index 825a9ca..c4da4dd 100644 --- a/src/views/menudesign/index.jsx +++ b/src/views/menudesign/index.jsx @@ -859,6 +859,8 @@ } } else if (item.type === 'dashboard' && !item.plot.valueField) { error = `缁勪欢銆�${item.name}銆嬫樉绀哄�煎皻鏈缃紒` + } else if (item.type === 'scatter' && (!item.plot.Xaxis || !item.plot.Yaxis || !item.plot.gender)) { + error = `缁勪欢銆�${item.name}銆嬪潗鏍囪酱灏氭湭璁剧疆锛乣 } }) } diff --git a/src/views/mobdesign/index.jsx b/src/views/mobdesign/index.jsx index 81fabf1..1db649d 100644 --- a/src/views/mobdesign/index.jsx +++ b/src/views/mobdesign/index.jsx @@ -1302,6 +1302,8 @@ } } else if (item.type === 'dashboard' && !item.plot.valueField) { error = `缁勪欢銆�${item.name}銆嬫樉绀哄�煎皻鏈缃紒` + } else if (item.type === 'scatter' && (!item.plot.Xaxis || !item.plot.Yaxis || !item.plot.gender)) { + error = `缁勪欢銆�${item.name}銆嬪潗鏍囪酱灏氭湭璁剧疆锛乣 } }) } diff --git a/src/views/pcdesign/index.jsx b/src/views/pcdesign/index.jsx index 806e36d..8a56f67 100644 --- a/src/views/pcdesign/index.jsx +++ b/src/views/pcdesign/index.jsx @@ -1347,6 +1347,8 @@ } } else if (item.type === 'dashboard' && !item.plot.valueField) { error = `缁勪欢銆�${item.name}銆嬫樉绀哄�煎皻鏈缃紒` + } else if (item.type === 'scatter' && (!item.plot.Xaxis || !item.plot.Yaxis || !item.plot.gender)) { + error = `缁勪欢銆�${item.name}銆嬪潗鏍囪酱灏氭湭璁剧疆锛乣 } }) } -- Gitblit v1.8.0