Ver Fonte

Merge branch 'master' of ssh://10.10.100.21/source/platform-bi-web

xiaoct há 7 anos atrás
pai
commit
2dbd3a0516

+ 5 - 1
package.json

@@ -10,9 +10,13 @@
     "antd": "^3.0.0",
     "app": "^0.1.0",
     "braft-editor": "^1.9.8",
+    "canvas2image": "^1.0.5",
     "dva": "^2.3.1",
+    "dva-loading": "^2.0.3",
     "echarts": "^4.1.0",
     "echarts-for-react": "^2.0.12-beta.0",
+    "html2canvas": "^1.0.0-alpha.12",
+    "jspdf": "^1.4.1",
     "moment": "^2.19.3",
     "prop-types": "^15.6.2",
     "react": "^16.2.0",
@@ -28,7 +32,7 @@
     "babel-plugin-import": "^1.8.0",
     "eslint": "^4.19.1",
     "eslint-config-standard": "^11.0.0",
-    "eslint-config-umi": "^0.1.1",
+    "eslint-config-umi": "^0.1.4",
     "eslint-plugin-flowtype": "^2.34.1",
     "eslint-plugin-import": "^2.13.0",
     "eslint-plugin-jsx-a11y": "^5.1.1",

BIN
public/fonts/iconfont/custom/iconfont.eot


Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
public/fonts/iconfont/custom/iconfont.js


Diff do ficheiro suprimidas por serem muito extensas
+ 2 - 2
public/fonts/iconfont/custom/iconfont.svg


BIN
public/fonts/iconfont/custom/iconfont.ttf


BIN
public/fonts/iconfont/custom/iconfont.woff


+ 3 - 6
src/components/chart/chooseDataSourceBox.jsx

@@ -20,13 +20,10 @@ class ChooseDataSourceBox extends React.Component {
 
     okHandler = (model) => {
         const { selectedRecord } = this.state;
-        const { dispatch } = this.props;
+        const { dispatch, hideBox } = this.props;
         if(selectedRecord) {
-            dispatch({ type: 'main/redirect', path: '/chart/create' });
-            dispatch({ type: 'chartDesigner/changeDataSource', value: {
-                dataSource: selectedRecord.code,
-                viewType: 'bar'
-            } });
+            dispatch({ type: 'chartDesigner/remoteQucikAdd', dataSource: selectedRecord });
+            hideBox();
         }else {
             message.warning('未选中数据源');
         }

+ 12 - 3
src/components/chart/list.jsx

@@ -54,7 +54,9 @@ class ChartList extends React.Component {
             </Menu>
         )
 
-        return list.map( (l, i) => (
+        let cards = list.sort((a, b) => {
+            return new Date(b.createTime) - new Date(a.createTime)
+        }).map( (l, i) => (
             <CardGrid className='chart-card' key={i} onClick={() => {
                 this.setState({ selectedCode: l.code })
             }}>
@@ -92,10 +94,16 @@ class ChartList extends React.Component {
                 </Card>
             </CardGrid>
         ));
+        if(cards.length === 0) {
+            cards = <div style={{ padding: '7px', textAlign: 'center', fontSize: '14px', color: 'rgba(0, 0, 0, 0.45)' }}>暂无数据</div>
+        }
+        return cards;
     }
 
     render() {
         const { visibleBox } = this.state;
+        const { dispatch } = this.props;
+
         return (
             <Layout className='chart-list'>
                 <Content>
@@ -109,6 +117,7 @@ class ChartList extends React.Component {
                             </Col>
                             <Col >
                                 <Button onClick={() => {
+                                    dispatch({ type: 'dataSource/fetchList' });
                                     this.setState({
                                         visibleBox: true
                                     });
@@ -134,8 +143,8 @@ class ChartList extends React.Component {
 }
 
 
-function mapStateToProps({present: {chart}}) {
-    return { chart }
+function mapStateToProps(state) {
+    return { chart: state.present.chart }
 }
 
 export default connect(mapStateToProps)(ChartList)

+ 176 - 0
src/components/chartDesigner/charts/resolveChartOption.js

@@ -15,6 +15,10 @@ export default (config) => {
             option = lineConfig(data ? data.data : {});
             break;
         }
+        case 'scatter': {
+            option = scatterConfig();
+            break;
+        }
         default:{
         }
     }
@@ -117,5 +121,177 @@ function lineConfig(data) {
         })
     };
 
+    return o;
+}
+
+function scatterConfig() {
+    let o = {
+        tooltip : {
+            // trigger: 'axis',
+            showDelay : 0,
+            formatter : function (params) {
+                if (params.value.length > 1) {
+                    return params.seriesName + ' :<br/>'
+                    + params.value[0] + 'cm '
+                    + params.value[1] + 'kg ';
+                }
+                else {
+                    return params.seriesName + ' :<br/>'
+                    + params.name + ' : '
+                    + params.value + 'kg ';
+                }
+            },
+            axisPointer:{
+                show: true,
+                type : 'cross',
+                lineStyle: {
+                    type : 'dashed',
+                    width : 1
+                }
+            }
+        },
+        legend: {
+            data: ['女性','男性'],
+            left: 'center'
+        },
+        xAxis : [
+            {
+                type : 'value',
+                scale:true,
+                axisLabel : {
+                    formatter: '{value} cm'
+                },
+                splitLine: {
+                    show: false
+                }
+            }
+        ],
+        yAxis : [
+            {
+                type : 'value',
+                scale:true,
+                axisLabel : {
+                    formatter: '{value} kg'
+                },
+                splitLine: {
+                    show: false
+                }
+            }
+        ],
+        series : [
+            {
+                name:'女性',
+                type:'scatter',
+                data: [[161.2, 51.6], [167.5, 59.0], [159.5, 49.2], [157.0, 63.0], [155.8, 53.6],
+                    [170.0, 59.0], [159.1, 47.6], [166.0, 69.8], [176.2, 66.8], [160.2, 75.2],
+                    [172.5, 55.2], [170.9, 54.2], [172.9, 62.5], [153.4, 42.0], [160.0, 50.0],
+                    [147.2, 49.8], [168.2, 49.2], [175.0, 73.2], [157.0, 47.8], [167.6, 68.8],
+                    [159.5, 50.6], [175.0, 82.5], [166.8, 57.2], [176.5, 87.8], [170.2, 72.8],
+                    [174.0, 54.5], [173.0, 59.8], [179.9, 67.3], [170.5, 67.8], [160.0, 47.0],
+                    [154.4, 46.2], [162.0, 55.0], [176.5, 83.0], [160.0, 54.4], [152.0, 45.8],
+                    [162.1, 53.6], [170.0, 73.2], [160.2, 52.1], [161.3, 67.9], [166.4, 56.6],
+                    [168.9, 62.3], [163.8, 58.5], [167.6, 54.5], [160.0, 50.2], [161.3, 60.3],
+                    [167.6, 58.3], [165.1, 56.2], [160.0, 50.2], [170.0, 72.9], [157.5, 59.8],
+                    [167.6, 61.0], [160.7, 69.1], [163.2, 55.9], [152.4, 46.5], [157.5, 54.3],
+                    [168.3, 54.8], [180.3, 60.7], [165.5, 60.0], [165.0, 62.0], [164.5, 60.3],
+                    [156.0, 52.7], [160.0, 74.3], [163.0, 62.0], [165.7, 73.1], [161.0, 80.0],
+                    [162.0, 54.7], [166.0, 53.2], [174.0, 75.7], [172.7, 61.1], [167.6, 55.7],
+                    [151.1, 48.7], [164.5, 52.3], [163.5, 50.0], [152.0, 59.3], [169.0, 62.5],
+                    [164.0, 55.7], [161.2, 54.8], [155.0, 45.9], [170.0, 70.6], [176.2, 67.2],
+                    [170.0, 69.4], [162.5, 58.2], [170.3, 64.8], [164.1, 71.6], [169.5, 52.8],
+                    [163.2, 59.8], [154.5, 49.0], [159.8, 50.0], [173.2, 69.2], [170.0, 55.9],
+                    [161.4, 63.4], [169.0, 58.2], [166.2, 58.6], [159.4, 45.7], [162.5, 52.2],
+                    [159.0, 48.6], [162.8, 57.8], [159.0, 55.6], [179.8, 66.8], [162.9, 59.4],
+                    [161.0, 53.6], [151.1, 73.2], [168.2, 53.4], [168.9, 69.0], [173.2, 58.4],
+                    [171.8, 56.2], [178.0, 70.6], [164.3, 59.8], [163.0, 72.0], [168.5, 65.2],
+                    [166.8, 56.6], [172.7, 105.2], [163.5, 51.8], [169.4, 63.4], [167.8, 59.0],
+                    [159.5, 47.6], [167.6, 63.0], [161.2, 55.2], [160.0, 45.0], [163.2, 54.0],
+                    [162.2, 50.2], [161.3, 60.2], [149.5, 44.8], [157.5, 58.8], [163.2, 56.4],
+                    [172.7, 62.0], [155.0, 49.2], [156.5, 67.2], [164.0, 53.8], [160.9, 54.4],
+                    [162.8, 58.0], [167.0, 59.8], [160.0, 54.8], [160.0, 43.2], [168.9, 60.5],
+                    [158.2, 46.4], [156.0, 64.4], [160.0, 48.8], [167.1, 62.2], [158.0, 55.5],
+                    [167.6, 57.8], [156.0, 54.6], [162.1, 59.2], [173.4, 52.7], [159.8, 53.2],
+                    [170.5, 64.5], [159.2, 51.8], [157.5, 56.0], [161.3, 63.6], [162.6, 63.2],
+                    [160.0, 59.5], [168.9, 56.8], [165.1, 64.1], [162.6, 50.0], [165.1, 72.3],
+                    [166.4, 55.0], [160.0, 55.9], [152.4, 60.4], [170.2, 69.1], [162.6, 84.5],
+                    [170.2, 55.9], [158.8, 55.5], [172.7, 69.5], [167.6, 76.4], [162.6, 61.4],
+                    [167.6, 65.9], [156.2, 58.6], [175.2, 66.8], [172.1, 56.6], [162.6, 58.6],
+                    [160.0, 55.9], [165.1, 59.1], [182.9, 81.8], [166.4, 70.7], [165.1, 56.8],
+                    [177.8, 60.0], [165.1, 58.2], [175.3, 72.7], [154.9, 54.1], [158.8, 49.1],
+                    [172.7, 75.9], [168.9, 55.0], [161.3, 57.3], [167.6, 55.0], [165.1, 65.5],
+                    [175.3, 65.5], [157.5, 48.6], [163.8, 58.6], [167.6, 63.6], [165.1, 55.2],
+                    [165.1, 62.7], [168.9, 56.6], [162.6, 53.9], [164.5, 63.2], [176.5, 73.6],
+                    [168.9, 62.0], [175.3, 63.6], [159.4, 53.2], [160.0, 53.4], [170.2, 55.0],
+                    [162.6, 70.5], [167.6, 54.5], [162.6, 54.5], [160.7, 55.9], [160.0, 59.0],
+                    [157.5, 63.6], [162.6, 54.5], [152.4, 47.3], [170.2, 67.7], [165.1, 80.9],
+                    [172.7, 70.5], [165.1, 60.9], [170.2, 63.6], [170.2, 54.5], [170.2, 59.1],
+                    [161.3, 70.5], [167.6, 52.7], [167.6, 62.7], [165.1, 86.3], [162.6, 66.4],
+                    [152.4, 67.3], [168.9, 63.0], [170.2, 73.6], [175.2, 62.3], [175.2, 57.7],
+                    [160.0, 55.4], [165.1, 104.1], [174.0, 55.5], [170.2, 77.3], [160.0, 80.5],
+                    [167.6, 64.5], [167.6, 72.3], [167.6, 61.4], [154.9, 58.2], [162.6, 81.8],
+                    [175.3, 63.6], [171.4, 53.4], [157.5, 54.5], [165.1, 53.6], [160.0, 60.0],
+                    [174.0, 73.6], [162.6, 61.4], [174.0, 55.5], [162.6, 63.6], [161.3, 60.9],
+                    [156.2, 60.0], [149.9, 46.8], [169.5, 57.3], [160.0, 64.1], [175.3, 63.6],
+                    [169.5, 67.3], [160.0, 75.5], [172.7, 68.2], [162.6, 61.4], [157.5, 76.8],
+                    [176.5, 71.8], [164.4, 55.5], [160.7, 48.6], [174.0, 66.4], [163.8, 67.3]
+                ],
+            },
+            {
+                name:'男性',
+                type:'scatter',
+                data: [[174.0, 65.6], [175.3, 71.8], [193.5, 80.7], [186.5, 72.6], [187.2, 78.8],
+                    [181.5, 74.8], [184.0, 86.4], [184.5, 78.4], [175.0, 62.0], [184.0, 81.6],
+                    [180.0, 76.6], [177.8, 83.6], [192.0, 90.0], [176.0, 74.6], [174.0, 71.0],
+                    [184.0, 79.6], [192.7, 93.8], [171.5, 70.0], [173.0, 72.4], [176.0, 85.9],
+                    [176.0, 78.8], [180.5, 77.8], [172.7, 66.2], [176.0, 86.4], [173.5, 81.8],
+                    [178.0, 89.6], [180.3, 82.8], [180.3, 76.4], [164.5, 63.2], [173.0, 60.9],
+                    [183.5, 74.8], [175.5, 70.0], [188.0, 72.4], [189.2, 84.1], [172.8, 69.1],
+                    [170.0, 59.5], [182.0, 67.2], [170.0, 61.3], [177.8, 68.6], [184.2, 80.1],
+                    [186.7, 87.8], [171.4, 84.7], [172.7, 73.4], [175.3, 72.1], [180.3, 82.6],
+                    [182.9, 88.7], [188.0, 84.1], [177.2, 94.1], [172.1, 74.9], [167.0, 59.1],
+                    [169.5, 75.6], [174.0, 86.2], [172.7, 75.3], [182.2, 87.1], [164.1, 55.2],
+                    [163.0, 57.0], [171.5, 61.4], [184.2, 76.8], [174.0, 86.8], [174.0, 72.2],
+                    [177.0, 71.6], [186.0, 84.8], [167.0, 68.2], [171.8, 66.1], [182.0, 72.0],
+                    [167.0, 64.6], [177.8, 74.8], [164.5, 70.0], [192.0, 101.6], [175.5, 63.2],
+                    [171.2, 79.1], [181.6, 78.9], [167.4, 67.7], [181.1, 66.0], [177.0, 68.2],
+                    [174.5, 63.9], [177.5, 72.0], [170.5, 56.8], [182.4, 74.5], [197.1, 90.9],
+                    [180.1, 93.0], [175.5, 80.9], [180.6, 72.7], [184.4, 68.0], [175.5, 70.9],
+                    [180.6, 72.5], [177.0, 72.5], [177.1, 83.4], [181.6, 75.5], [176.5, 73.0],
+                    [175.0, 70.2], [174.0, 73.4], [165.1, 70.5], [177.0, 68.9], [192.0, 102.3],
+                    [176.5, 68.4], [169.4, 65.9], [182.1, 75.7], [179.8, 84.5], [175.3, 87.7],
+                    [184.9, 86.4], [177.3, 73.2], [167.4, 53.9], [178.1, 72.0], [168.9, 55.5],
+                    [157.2, 58.4], [180.3, 83.2], [170.2, 72.7], [177.8, 64.1], [172.7, 72.3],
+                    [165.1, 65.0], [186.7, 86.4], [165.1, 65.0], [174.0, 88.6], [175.3, 84.1],
+                    [185.4, 66.8], [177.8, 75.5], [180.3, 93.2], [180.3, 82.7], [177.8, 58.0],
+                    [177.8, 79.5], [177.8, 78.6], [177.8, 71.8], [177.8, 116.4], [163.8, 72.2],
+                    [188.0, 83.6], [198.1, 85.5], [175.3, 90.9], [166.4, 85.9], [190.5, 89.1],
+                    [166.4, 75.0], [177.8, 77.7], [179.7, 86.4], [172.7, 90.9], [190.5, 73.6],
+                    [185.4, 76.4], [168.9, 69.1], [167.6, 84.5], [175.3, 64.5], [170.2, 69.1],
+                    [190.5, 108.6], [177.8, 86.4], [190.5, 80.9], [177.8, 87.7], [184.2, 94.5],
+                    [176.5, 80.2], [177.8, 72.0], [180.3, 71.4], [171.4, 72.7], [172.7, 84.1],
+                    [172.7, 76.8], [177.8, 63.6], [177.8, 80.9], [182.9, 80.9], [170.2, 85.5],
+                    [167.6, 68.6], [175.3, 67.7], [165.1, 66.4], [185.4, 102.3], [181.6, 70.5],
+                    [172.7, 95.9], [190.5, 84.1], [179.1, 87.3], [175.3, 71.8], [170.2, 65.9],
+                    [193.0, 95.9], [171.4, 91.4], [177.8, 81.8], [177.8, 96.8], [167.6, 69.1],
+                    [167.6, 82.7], [180.3, 75.5], [182.9, 79.5], [176.5, 73.6], [186.7, 91.8],
+                    [188.0, 84.1], [188.0, 85.9], [177.8, 81.8], [174.0, 82.5], [177.8, 80.5],
+                    [171.4, 70.0], [185.4, 81.8], [185.4, 84.1], [188.0, 90.5], [188.0, 91.4],
+                    [182.9, 89.1], [176.5, 85.0], [175.3, 69.1], [175.3, 73.6], [188.0, 80.5],
+                    [188.0, 82.7], [175.3, 86.4], [170.5, 67.7], [179.1, 92.7], [177.8, 93.6],
+                    [175.3, 70.9], [182.9, 75.0], [170.8, 93.2], [188.0, 93.2], [180.3, 77.7],
+                    [177.8, 61.4], [185.4, 94.1], [168.9, 75.0], [185.4, 83.6], [180.3, 85.5],
+                    [174.0, 73.9], [167.6, 66.8], [182.9, 87.3], [160.0, 72.3], [180.3, 88.6],
+                    [167.6, 75.5], [186.7, 101.4], [175.3, 91.1], [175.3, 67.3], [175.9, 77.7],
+                    [175.3, 81.8], [179.1, 75.5], [181.6, 84.5], [177.8, 76.6], [182.9, 85.0],
+                    [177.8, 102.5], [184.2, 77.3], [179.1, 71.8], [176.5, 87.9], [188.0, 94.3],
+                    [174.0, 70.9], [167.6, 64.5], [170.2, 77.3], [167.6, 72.3], [188.0, 87.3],
+                    [174.0, 80.0], [176.5, 82.3], [180.3, 73.6], [167.6, 74.1], [188.0, 85.9],
+                    [180.3, 73.2], [167.6, 76.3], [183.0, 65.9], [183.0, 90.9], [179.1, 89.1],
+                    [170.2, 62.3], [177.8, 82.7], [179.1, 79.1], [190.5, 98.2], [177.8, 84.1],
+                    [180.3, 83.2], [180.3, 83.2]
+                ],
+            }
+        ]
+    };
     return o;
 }

+ 5 - 1
src/components/chartDesigner/content.jsx

@@ -7,6 +7,7 @@ import BarConfigForm from './sections/barConfigForm'
 import PieConfigForm from './sections/pieConfigForm'
 import LineConfigForm from './sections/lineConfigForm'
 import TableView from './charts/table'
+import ScatterConfigForm from './sections/ScatterConfigForm'
 import EchartsView from './charts/echartsView'
 import StyleEditor from './sections/styleEditor'
 import ToolBar from './sections/toolbar'
@@ -44,6 +45,9 @@ class ChartDesignerContent extends React.Component {
         }else if(viewType === 'pie') {
             configForm = (<PieConfigForm formItemLayout={formItemLayout}/>);
             chartView = (<EchartsView />);
+        }else if(viewType === 'scatter') {
+            // configForm = (<ScatterConfigForm formItemLayout={formItemLayout}/>);
+            chartView = (<EchartsView />);
         }
 
         return (
@@ -65,7 +69,7 @@ class ChartDesignerContent extends React.Component {
                                 if(checked) {
                                     dispatch({ type: 'chartDesigner/fetchChartData' });
                                 }
-                                dispatch({ type: 'chartDesigner/setAutoRefresh', value: checked })
+                                dispatch({ type: 'chartDesigner/silentSetField', name: 'autoRefresh', value: checked })
                             }}/>
                             <Button hidden={autoRefresh} size='small' onClick={() => { dispatch({ type: 'chartDesigner/fetchChartData'}) }}
                             >立即刷新</Button>

+ 1 - 1
src/components/chartDesigner/header.jsx

@@ -12,7 +12,7 @@ const Header = ({ chartDesigner, dispatch }) => {
                 <Button onClick={() => {
                     dispatch({ type: 'main/redirect', path: '/chart' });
                 }}>
-                    <Icon type='back' />返回
+                    <Icon type='left' />返回
                 </Button>
             </div>
             <div className='header-item toolbar-title'>

+ 145 - 0
src/components/chartDesigner/sections/ScatterConfigForm.jsx

@@ -0,0 +1,145 @@
+import React from 'react'
+import { Form, Select, Tag, Cascader, Dropdown, Menu } from 'antd'
+import { connect } from 'dva'
+import '../../../models/chartDesigner'
+import GAUGE from './gauge.json'
+const FormItem = Form.Item
+const { Option } = Select
+
+const ScatterConfigForm = ({ chartDesigner, dispatch, formItemLayout }) => {
+	
+	const columns = chartDesigner.columns;
+
+	return (
+		<Form hideRequiredMark={true}>
+			<FormItem label='横轴' {...formItemLayout}>
+				<Cascader
+					value={[chartDesigner.scatterConfig.xAxis.column.value, chartDesigner.scatterConfig.xAxis.granularity.value]}
+					allowClear={true}
+					options={columns.filter(c =>['scale', 'ordinal'].indexOf(c.type) !== -1).map((c, i)=>{
+						return {
+							type: c.type,
+							value: c.name,
+							label: c.label
+						}
+					})}
+					onChange={(value, items) => {
+						let column = {};
+						let granularity = {};
+						console.log('items', items)
+						console.log('value', value)
+						if(items.length > 0) {
+							column = { type: items[0].type, value: items[0].value, label: items[0].label };
+						}
+						if(items.length > 1) {
+							granularity = { value: items[1].value, label: items[1].label };
+						}
+						dispatch({ type: 'chartDesigner/changeField', name: 'scatterConfig', value: { ...chartDesigner.scatterConfig, xAxis: { column, granularity } } });
+					}}
+					displayRender={(label, selectedOptions) => {
+						let text = '';
+						let className = 'cascader-label';
+						if(label.length > 0) {
+							className += ' full-label';
+							text += label[0];
+							if(label.length > 1) {
+								text += '(' + label[1] + ')';
+							}
+						}else {
+							className += ' empty-label';
+							text = '请选择...';
+						}
+						return <div className={className}>{text}</div>;
+					}}
+				>
+				</Cascader>
+			</FormItem>
+			<FormItem label='纵轴' {...formItemLayout}>
+				<Cascader
+					className='gauge-item'
+					value={[chartDesigner.scatterConfig.yAxis.column.value, chartDesigner.scatterConfig.yAxis.gauge.value]}
+					allowClear={true}
+					options={columns.map((c, i)=>{
+						return {
+							value: c.name,
+							label: c.label,
+							children: GAUGE[chartDesigner.baseConfig.viewType].map(g => {
+                                if(g.columnType.indexOf(c.type) !== -1) {
+                                    return g;
+                                }else {
+                                    return null;
+                                }
+                            }).filter( g => g!==null)
+						}
+					})}
+					onChange={(value, items) => {
+						let column = {};
+						let gauge = {};
+						if(value.length > 0) {
+							column = { type: items[0].type, value: items[0].value, label: items[0].label };
+							gauge = { value: items[1].value, label: items[1].label };
+						}
+						dispatch({ type: 'chartDesigner/changeField', name: 'ScatterConfigForm', value: { ...chartDesigner.scatterConfig, yAxis: { column, gauge } } });
+					}}
+					displayRender={(label, selectedOptions) => {
+						let menu = selectedOptions.length > 0 ? <Menu
+							selectedKeys={[chartDesigner.scatterConfig.yAxis.gauge.value]}
+							selectable={true}
+						>
+							{selectedOptions[0].children.map((c, i) => {
+								return <Menu.Item  data-value={c.value} data-label={c.label} key={c.value} onClick={(e) => {
+									let value = e.domEvent.target.getAttribute('data-value');
+									let label = e.domEvent.target.getAttribute('data-label');
+									dispatch({ type: 'chartDesigner/changeField', name: 'scatterConfig', value: { 
+										...chartDesigner.scatterConfig,
+										yAxis: {
+											column: chartDesigner.scatterConfig.yAxis.column,
+											gauge: { value, label }
+										}
+									}});
+									e.domEvent.stopPropagation();
+								}}>{c.label}</Menu.Item>
+							})}
+						</Menu>: [];
+						let tag = selectedOptions.length > 0 ? <Dropdown
+							trigger={['click']}
+							overlay={menu}
+						>
+							<Tag size='small' onClick={(e) => {e.stopPropagation()}}>{label[1]}</Tag>
+						</Dropdown>
+						: null;
+
+						return <div className={`cascader-label ${tag?'full' : 'empty'}-label`}>
+							{tag}
+							<span>{label[0] || '请选择...'}</span>
+						</div>
+					}}
+				>
+				</Cascader>
+			</FormItem>
+			<FormItem label='分组' {...formItemLayout}>
+				<Select
+					maxTagCount={1}
+					labelInValue={true}
+					allowClear={true}
+					placeholder='请选择...'
+					onChange={(value) => {
+						// value = value.splice(-1);
+						dispatch({ type: 'chartDesigner/changeField', name: 'preparing', value: { ...chartDesigner.preparing, groupBy: value } });
+					}}
+					value={chartDesigner.preparing.groupBy}
+				>
+					{columns.filter(c => c.type === 'categorical').map((c, i)=>{
+						return (<Option key={i} value={c.name}>{c.label}</Option>)
+					})}
+				</Select>
+			</FormItem>
+		</Form>
+	);
+}
+
+function mapStateToProps({ present: { chartDesigner } }) {
+    return { chartDesigner: chartDesigner }
+}
+
+export default connect(mapStateToProps)(ScatterConfigForm);

+ 5 - 3
src/components/chartDesigner/sections/baseConfigForm.jsx

@@ -1,5 +1,5 @@
 import React from 'react'
-import { Form, Select } from 'antd'
+import { Form, Select, Icon } from 'antd'
 import { connect } from 'dva'
 import '../../../models/chartDesigner'
 import '../../../models/dataSource'
@@ -32,6 +32,7 @@ class baseConfigForm extends React.Component {
 				</FormItem>
 				<FormItem label='可视化模式' {...formItemLayout}>
 					<Select
+						className='viewtype-field'
 						dropdownClassName='baseconfig-viewtype'
 						value={props.chartDesigner.baseConfig.viewType}
 						dropdownMatchSelectWidth={false}
@@ -43,8 +44,9 @@ class baseConfigForm extends React.Component {
 						CHART_TYPE.map( c => (
 							<Option key={c.type} value={c.type} title={c.label}>
 								<div className='viewtype-box'>
-									<div className={`viewtype-icon viewtype-${c.type}`}>
-									</div>
+									{/* <div className={`viewtype-icon viewtype-${c.type}`}> */}
+									<Icon className='viewtype-icon' type={'chart-' + c.type} />
+									{/* </div> */}
 									<div className='viewtype-text'>
 										{c.label}
 									</div>

+ 8 - 9
src/components/chartDesigner/sections/baseConfigForm.less

@@ -1,3 +1,11 @@
+.viewtype-field {
+    .viewtype-box {
+        display: flex;
+        i {
+            line-height: 30px;
+        }
+    }
+}
 .baseconfig-viewtype {
     width: 260px;
     .ant-select-dropdown-menu {
@@ -13,15 +21,6 @@
                     background-size: contain;
                     margin: 0 auto;
                 }
-                .viewtype-bar {
-                    background-image: url(/public/images/chart-bar.png);
-                }
-                .viewtype-pie {
-                    background-image: url(/public/images/chart-pie.png);
-                }
-                .viewtype-line {
-                    background-image: url(/public/images/chart-line.png);
-                }
             }
         }
     }

+ 29 - 0
src/components/chartDesigner/sections/gauge.json

@@ -73,5 +73,34 @@
         "value": "distinctCount",
         "label": "不重复计数",
         "columnType": ["index", "time", "categorical", "scale", "ordinal", "string"]
+    }],
+    "scatter": [{
+        "value": "sum",
+        "label": "累计",
+        "columnType": ["scale", "ordinal"]
+    }, {
+        "value": "avg",
+        "label": "平均值",
+        "columnType": ["scale", "ordinal"]
+    }, {
+        "value": "median",
+        "label": "中位数",
+        "columnType": ["scale", "ordinal"]
+    }, {
+        "value": "count",
+        "label": "计数",
+        "columnType": ["index", "time", "categorical", "scale", "ordinal", "string"]
+    }, {
+        "value": "distinctCount",
+        "label": "不重复计数",
+        "columnType": ["index", "time", "categorical", "scale", "ordinal", "string"]
+    }, {
+        "value": "max",
+        "label": "最大值",
+        "columnType": ["scale", "ordinal"]
+    }, {
+        "value": "min",
+        "label": "最小值",
+        "columnType": ["scale", "ordinal"]
     }]
 }

+ 1 - 1
src/components/chartDesigner/sections/toolbar.jsx

@@ -21,7 +21,7 @@ class Toolbar extends React.Component {
         const chartDesigner = props.chartDesigner;
         const filters = chartDesigner.filters;
         props.dispatch({ type: 'chartDesigner/changeField', name: 'filters', value: filters.map( f => {
-            if(f.key === key) {
+            if(+f.key === +key) {
                 f = { ...f, using: !f.using }
             }
             return f;

+ 16 - 8
src/components/common/loading.jsx

@@ -1,18 +1,26 @@
 import React from 'react'
-import { Modal, Spin } from 'antd'
+import { Spin } from 'antd'
 import { connect } from 'dva'
-import '../../models/dataSource'
+import './loading.less'
 
-const Loading = () => {
+const Loading = ({ loading }) => {
     return (
-        <Modal className='loading-box'visible={false}>
-            <Spin />
-        </Modal>
+        <div className='loading-box'style={{ display: loading ? 'block' : 'none' }}>
+            <Spin size="large"/>
+        </div>
     );
 }
 
-function mapStateToProps({ present: { dataSource } }) {
-    return { dataSource: dataSource };
+function mapStateToProps(state) {
+    const models = state.present.loading.models;
+    let loading = false;
+    for(let model in models) {
+        if(models[model]) {
+            loading = true;
+            break;
+        }
+    }
+    return { loading: loading };
 }
 
 export default connect(mapStateToProps)(Loading)

+ 7 - 0
src/components/common/loading.less

@@ -0,0 +1,7 @@
+.loading-box {
+    position: absolute;
+    margin: 0 auto;
+    top: 60px;
+    right: 20px;
+    z-index: 9999;
+}

+ 22 - 3
src/components/dashboardDesigner/layout.jsx

@@ -10,6 +10,9 @@ import ElementConfig from './elementConfig'
 import './layout.less'
 import Element from './element'
 import '../../models/dataConnect'
+import html2canvas from 'html2canvas'
+import jsPDF from 'jspdf'
+
 
 class DashboardDesigner extends React.Component {
 
@@ -24,6 +27,20 @@ class DashboardDesigner extends React.Component {
         };
     }
 
+    saveToPNG = (element) => {
+        html2canvas(element).then(function(canvas) {
+                var pageData = canvas.toDataURL('image/png', 1.0);
+
+                //方向默认竖直,尺寸ponits,格式a4[595.28,841.89]
+                var pdf = new jsPDF('', 'pt', 'a4');
+
+                //addImage后两个参数控制添加图片的尺寸,此处将页面高度按照a4纸宽高比列进行压缩
+                pdf.addImage(pageData, 'PNG', 0, 0, 595.28, 592.28/canvas.width * canvas.height );
+
+                pdf.save('stone.pdf');
+        });
+    }
+
 
     showElementConfigBox = (o) => {
         this.setState({
@@ -141,14 +158,16 @@ class DashboardDesigner extends React.Component {
                                 }}>
                                     添加元素
                                 </Button>
+                                <Button onClick={() => {
+                                    this.saveToPNG(document.body)
+                                }}>
+                                    保存为PNG
+                                </Button>
                                 <span>编辑模式</span>
                                 <Switch 
                                     checkedChildren="开" 
                                     unCheckedChildren="关" 
                                     checked= {editMode}
-                                    onChange={(checked) => {
-                                        this.setState({editMode:checked}, () => {this.forceItUpdate()})
-                                    }}
                                     
                                 />
                             </div>

+ 2 - 1
src/components/datasource/baseConfig.jsx

@@ -108,7 +108,8 @@ const DataSourceBaseConfig = ({ dataSource, dataConnect, dispatch, mode }) => {
                                     wrapperCol: { span: 16 }
                                 }}>
                                     <Input
-                                        value={dataSource.newOne.password}
+                                        // value={dataSource.newOne.password}
+                                        emptyText='********'
                                         onChange={(e) => {
                                             let value = e.target.value;
                                             dispatch({ type: 'dataSource/setNewModelField', name: 'password', value: value });

+ 3 - 22
src/components/datasource/dataSource.jsx

@@ -99,27 +99,6 @@ class DataSource extends React.Component {
             dataIndex: 'name',
             key: 'name',
             width: 100,
-            sorter: (a, b) => a.name.localeCompare(b.name, 'zh-Hans-CN', {sensitivity: 'accent'}),
-            filterDropdown: (
-                <div className="custom-filter-dropdown">
-                    <Input
-                        ref={ele => this.searchInput = ele}
-                        placeholder="Search name"
-                        value={this.state.search.name}
-                        onChange={(e) => { this.onInputChange('name', e.target.value) }}
-                        onPressEnter={this.onSearch}
-                    />
-                    <Button type="primary" onClick={this.onSearch}>Search</Button>
-                </div>
-            ),
-            className: `column-${this.state.search.name?'filtered':'nofiltered'}`,
-            filterIcon: <Icon type="smile-o"/>,
-            filterDropdownVisible: this.state.filterDropdownVisible,
-            onFilterDropdownVisibleChange: (visible) => {
-                this.setState({
-                    filterDropdownVisible: visible,
-                }, () => this.searchInput && this.searchInput.focus());
-            },
             render: (text, record) => {
                 return <div className='datasource-name'>
                     <div className={`datasource-type type-${record.type.key}`}></div>
@@ -205,7 +184,9 @@ class DataSource extends React.Component {
                         <Table
                             className='datasource-table datasource-table'
                             columns={dataSourceColumns}
-                            dataSource={dataSource.list}
+                            dataSource={dataSource.list.sort((a, b) => {
+                                return new Date(b.createTime) - new Date(a.createTime);
+                            })}
                             loading={loading}
                             size='small'
                             scroll={{x: false, y: true}}

+ 2 - 0
src/constants/url.js

@@ -46,5 +46,7 @@ const URLS = {
     CHART_PIE_OPTION: BASE_URL + '/showPie', // 请求饼图展示数据
 
     CHART_LINE_OPTION: BASE_URL + '/showLine', // 请求折线图展示数据
+
+    CHART_SCATTER_OPTION: BASE_URL + '/showXXXXX', // 请求散点图展示四数据
 }
 export default URLS

+ 2 - 1
src/index.js

@@ -10,6 +10,7 @@ import chart from './models/chart'
 import './utils/baseUtils'
 import './index.less'
 import dashboardDesigner from './models/dashboardDesigner';
+import createLoading from 'dva-loading';
 
 // 1. Initialize
 const app = dva({
@@ -23,7 +24,7 @@ const app = dva({
 });
 
 // 2. Plugins
-// app.use({});
+app.use(createLoading());
 
 // 3. Model
 app.model(mainModel); // 通用action

+ 13 - 10
src/models/chart.js

@@ -20,12 +20,15 @@ export default {
         },
     },
     effects: {
-        *fetchList(action, { call, put }) {
+        *fetchList(action, { select, call, put }) {
             try{
+                const chart = yield select(state => state.present.chart);
+                if(!action.mandatory && chart.list.length > 0) {
+                    return;
+                }
                 const res = yield call(service.fetch, {
                     url: URLS.CHART_LIST
                 });
-                console.log(res);
                 if(!res.err && res.data.code > 0) {
                     let list = res.data.data.map(d => {
                         return {
@@ -42,6 +45,7 @@ export default {
                 }
             }catch(e) {
                 console.log(e);
+                message.error('请求图表列表失败');
             }
         },
         *remoteDetail(action, { select, call, put }) {
@@ -55,8 +59,6 @@ export default {
                     body: code
                 });
                 if(!res.err && res.data.code > 0) {
-                    console.log(res);
-
                     const getViewType = function(type) {
                         if(type === 'Histogram') {
                             return 'bar';
@@ -70,8 +72,8 @@ export default {
                     }
 
                     let resData = res.data.data;
-                    let groupBy = JSON.parse(resData.groupBy);
-                    let chartConfig = JSON.parse(JSON.parse(resData.chartConfig));
+                    let groupBy = JSON.parse(resData.groupBy) || [];
+                    let chartConfig = JSON.parse(resData.chartConfig) || { xAxis: { column: {}, granularity: {} }, yAxis: { column: {}, gauge: {} } };
                     let viewType = getViewType(resData.chartType);
 
                     let data = {
@@ -108,7 +110,6 @@ export default {
                             value: data[key]
                         })
                     }
-                    console.log(fields);
                     yield put({ type: 'chartDesigner/defaultChangeFields', fields: fields });
                     
                     yield put({ type: 'chartDesigner/changeDataSource', value: {
@@ -116,11 +117,11 @@ export default {
                         viewType: data.baseConfig.viewType
                     } });
                 }else {
-                    console.log(res);
+                    message.error('解析图表错误');
                 }
             }catch(e) {
-                message.error('失败');
                 console.log(e);
+                message.error('解析图表错误');
                 yield put({ type: 'list', data: [] });
             }
         },
@@ -153,8 +154,10 @@ export default {
     },
     subscriptions: {
         setup({ dispatch, history }) {
-            dispatch({ type: 'fetchList' });
             return history.listen(({ pathname, query }) => {
+                if(pathname === '/chart') {
+                    dispatch({ type: 'fetchList' });
+                }
                 let detail = pathname.match(/chart\/(\w+)/);
                 if(detail) {
                     let code = detail[1];

+ 125 - 69
src/models/chartDesigner.js

@@ -80,10 +80,21 @@ export default {
     },
     reducers: {
         /**
-         * 初始化model字段值
-         * 设置撤销重做的起点,不进入撤销重做的历史
+         * 更新model字段值
+         * 1. 进入撤销重做历史
          */
-        defaultSetFields(state, action) {
+        setField(state, action) {
+            const { name, value } = action;
+            let obj = {};
+            obj[name] = value;
+            let newState = Object.assign({}, state, obj);
+            return newState;
+        },
+        /**
+         * 批量更新model字段值
+         * 1. 进入撤销重做历史
+         */
+        setFields(state, action) {
             const { fields } = action;
             let obj = {};
             fields.map(f => (obj[f.name] = f.value));
@@ -91,11 +102,10 @@ export default {
             return newState;
         },
         /**
-         * 更新model字段值方法1
-         * 1. 为保持撤销重做的功能有效性,能够撤销重做的action动作才使用该方法
-         * 2. 对数据刷新没有影响的model字段改变一般用该action
+         * 更新model字段值
+         * 1. 不进入撤销重做历史
          */
-        setField(state, action) {
+        silentSetField(state, action) {
             const { name, value } = action;
             let obj = {};
             obj[name] = value;
@@ -103,42 +113,18 @@ export default {
             return newState;
         },
         /**
-         * 批量更新model字段值方法1
+         * 批量更新model字段值
+         * 1. 不进入撤销重做历史
          */
-        setFields(state, action) {
+        silentSetFields(state, action) {
             const { fields } = action;
             let obj = {};
             fields.map(f => (obj[f.name] = f.value));
             let newState = Object.assign({}, state, obj);
             return newState;
         },
-        setDataSource(state, action) {
-            const { value } = action;
-            let obj = {};
-            obj['baseConfig'] = value;
-            return Object.assign({}, state, obj);
-        },
-        setColumns(state, action) {
-            const { value } = action;
-            let obj = {};
-            obj['columns'] = value;
-            return Object.assign({}, state, obj);
-        },
-        setAutoRefresh(state, action) {
-            const { value } = action;
-            let obj = {};
-            obj['autoRefresh'] = value;
-            return Object.assign({}, state, obj);
-        },
-        setChartOption(state, action) {
-            const { option } = action;
-            let obj = {};
-            obj['chartOption'] = option;
-            return Object.assign({}, state, obj);
-        },
         reset(state, action) {
             let obj = Object.assign({}, state, state.originData);
-            console.log(obj);
             return obj;
         }
     },
@@ -149,7 +135,7 @@ export default {
          */
         *defaultChangeFields(action, { select, call, put }) {
             const { fields } = action;
-            yield put({ type: 'defaultSetFields', fields });
+            yield put({ type: 'silentSetFields', fields });
 
             const chartDesigner = yield select(state => state.present.chartDesigner);
             const { autoRefresh } = chartDesigner;
@@ -158,7 +144,7 @@ export default {
             }
         },
         /**
-         * 更新model字段值方法2
+         * 更新model字段值
          * 可能影响到数据刷新的model字段改变一般用该action
          */
         *changeField(action, { select, call, put }) {
@@ -173,7 +159,7 @@ export default {
             }
         },
         /**
-         * 批量更新model字段值方法2
+         * 批量更新model字段值
          */
         *changeFields(action, { select, call, put }) {
             const { fields } = action;
@@ -187,9 +173,48 @@ export default {
         },
         *changeDataSource(action, { select, call, put }) {
             const { value } = action;
-            yield put({ type: 'setDataSource', value });
+            yield put({ type: 'silentSetField', name: 'baseConfig', value });
             yield put({ type: 'remoteDataColumn', code: value.dataSource });
         },
+        *remoteQucikAdd(action, { select, call, put }) {
+            try{
+                const { dataSource } = action;
+
+                yield put({ type: 'silentSetFields', fields: [
+                    { name: 'baseConfig', value: { dataSource: dataSource.code, viewType: '' } }
+                ] });
+                const chartDesigner = yield select(state => state.present.chartDesigner);
+                const { baseConfig, preparing } = chartDesigner;
+
+                let body = {
+                    chartName: dataSource.name + '(未命名)',
+                    dataId: baseConfig.dataSource,
+                    groupBy: preparing.groupBy && preparing.groupBy.key ? [{
+                        columnName: preparing.groupBy.key,
+                        columnRamane: preparing.groupBy.label
+                    }] : [],
+                    createBy: 'zhuth',
+                    describes: '',
+                    style: '',
+                    chartConfig: '{}',
+                    chartType: ''
+                };
+                const res = yield call(service.fetch, {
+                    url: URLS.CHART_ADD,
+                    body: body
+                })
+                if(!res.err && res.data.code > 0) {
+                    yield put({ type: 'chart/fetchList', mandatory: true });
+                    // TODO 
+                    // yield put({ type: 'main/redirect', path: '/chart/' + 32 });
+                }else {
+                    message.error('新增失败');
+                }
+            }catch(e) {
+                console.error(e);
+                message.error('新增失败');
+            }
+        },
         *remoteAdd(action, { select, call, put }) {
             try{
                 const chartDesigner = yield select(state => state.present.chartDesigner);
@@ -198,7 +223,7 @@ export default {
                 let body = {
                     chartName: header.label,
                     dataId: baseConfig.dataSource,
-                    groupBy: preparing.groupBy ? [{
+                    groupBy: preparing.groupBy && preparing.groupBy.key ? [{
                         columnName: preparing.groupBy.key,
                         columnRamane: preparing.groupBy.label
                     }] : [],
@@ -216,14 +241,14 @@ export default {
                     body.chartType = 'Line';
                     body.chartConfig = JSON.stringify(lineConfig);
                 }
-                console.log('remoteAdd-body', body);
                 const res = yield call(service.fetch, {
                     url: URLS.CHART_ADD,
                     body: body
                 })
                 if(!res.err && res.data.code > 0) {
                     message.success('新增成功!');
-                    yield put({ type: 'chart/fetchList' });
+                    // yield put({ type: 'silentSetField', name: 'code', value: code });
+                    yield put({ type: 'chart/fetchList', mandatory: true });
                 }else {
                     message.error('新增失败');
                 }
@@ -258,17 +283,19 @@ export default {
                     body.chartType = 'Line';
                     body.chartConfig = JSON.stringify(lineConfig);
                 }
-                console.log(body);
                 const res = yield call(service.fetch, {
                     url: URLS.CHART_UPDATE,
                     body: body
                 })
                 if(!res.err && res.data.code > 0) {
                     message.success('修改成功!');
-                    yield put({ type: 'chart/fetchList' });
+                    yield put({ type: 'chart/fetchList', mandatory: true });
+                }else {
+                    message.error('修改失败');
                 }
             }catch(e) {
-                console.log(e);
+                console.error(e);
+                message.error('修改失败');
             }
         },
         *remoteDataColumn(action, { select, call, put }) {
@@ -290,14 +317,14 @@ export default {
                             selection: []
                         }
                     })
-                    console.log(columns);
-                    yield put({ type: 'setColumns', value: columns });
+                    yield put({ type: 'silentSetField', name: 'columns', value: columns });
                 }else {
-                    console.log(res);
+                    message.error('请求列数据失败');
+                    yield put({ type: 'silentSetField', name: 'columns', value: [] });
                 }
             }catch(e) {
-                console.log(e);
-                yield put({ type: 'list', data: [] });
+                message.error('请求列数据失败');
+                yield put({ type: 'silentSetField', name: 'columns', value: [] });
             }
         },
         *fetchChartData(action, { select, call, put }) {
@@ -311,6 +338,8 @@ export default {
                 yield put({ type: 'fetchPieData' });
             }else if(viewType === 'line') {
                 yield put({ type: 'fetchLineData' });
+            }else if(viewType === 'scatter') {
+                yield put({ type: 'fetchScatterData' })
             }else {
                 console.log('nothing.......')
             }
@@ -321,7 +350,7 @@ export default {
                 const { barConfig, preparing } = chartDesigner;
                 const body = {
                     tableName: "TEST_BI_DATA",
-                    groups: preparing.groupBy ? [preparing.groupBy.key] : [],
+                    groups: preparing.groupBy && preparing.groupBy.key ? [preparing.groupBy.key] : [],
                     xAxis: {
                         columnRename: barConfig.xAxis.column.value,
                         columnType: barConfig.xAxis.column.type,
@@ -333,23 +362,21 @@ export default {
                     }
                 };
                 
-                console.log(body)
                 let res = yield call(service.fetch, {
                     url: URLS.CHART_BAR_OPTION,
                     body: body
                 });
-                console.log('res: ', res);
                 if(!res.err && res.data.code > 0) {
                     res.viewType = 'bar';
                     res.data.data.xTitle = barConfig.xAxis?`${barConfig.xAxis.column.label}${barConfig.xAxis.granularity.value?'('+barConfig.xAxis.granularity.label+')':''}`:null
                     res.data.data.yTitle = barConfig.yAxis?`${barConfig.yAxis.column.label}${barConfig.yAxis.gauge.value?'('+barConfig.yAxis.gauge.label+')':''}`:null
-                    yield put({ type: 'setChartOption', option: res });
+                    yield put({ type: 'silentSetField', name: 'chartOption', value: res });
                 }else {
-                    yield put({ type: 'setChartOption', option: {} });
+                    yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
                 }
             }catch(e) {
                 console.error(e);
-                yield put({ type: 'setChartOption', option: {} });
+                yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
             }
         },
         *fetchPieData(action, { select, call, put }) {
@@ -369,30 +396,27 @@ export default {
                         showDataType: pieConfig.yAxis.gauge.value
                     }
                 };
-                console.log(body);
                 let res = yield call(service.fetch, {
                     url: URLS.CHART_PIE_OPTION,
                     body: body
                 });
 
                 if(!res.err && res.data.code > 0) {
-                    console.log('res: ', res);
                     res.viewType = 'pie';
                     res.data.data.columnName = pieConfig.xAxis.column.label + (pieConfig.xAxis.granularity.value ? '('+pieConfig.xAxis.granularity.label+')' : '');
-                    yield put({ type: 'setChartOption', option: res });
+                    yield put({ type: 'silentSetField', name: 'chartOption', value: res });
                 }else {
-                    yield put({ type: 'setChartOption', option: {} });
+                    yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
                 }
             }catch(e) {
                 console.error(e);
-                yield put({ type: 'setChartOption', option: {} });
+                yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
             }
         },
         *fetchLineData(action, { select, call, put }) {
             try {
                 const chartDesigner = yield select(state => state.present.chartDesigner);
                 const { lineConfig, preparing } = chartDesigner;
-                console.log(lineConfig);
                 const body = {
                     tableName: "TEST_BI_DATA",
                     xAxis: {
@@ -403,26 +427,58 @@ export default {
                         columnRename: lineConfig.yAxis.column.value,
                         showDataType: lineConfig.yAxis.gauge.value
                     },
-                    groups: preparing.groupBy ? [preparing.groupBy.key] : []
+                    groups: preparing.groupBy && preparing.groupBy.key ? [preparing.groupBy.key] : [],
                 };
-                console.log('lineBody: ', body);
                 let res = yield call(service.fetch, {
                     url: URLS.CHART_LINE_OPTION,
                     body: body
                 });
-                console.log('line res', res);
                 if(!res.err && res.data.code > 0) {
                     res.viewType = 'line';
-                    // res.data.data.columnName = pieConfig.xAxis.column.label + (pieConfig.xAxis.granularity.value ? '('+pieConfig.xAxis.granularity.label+')' : '');
-                    yield put({ type: 'setChartOption', option: res });
+                    yield put({ type: 'silentSetField', name: 'chartOption', value: res });
                 }else {
-                    yield put({ type: 'setChartOption', option: {} });
+                    yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
                 }
             }catch(e) {
                 console.error(e);
-                yield put({ type: 'setChartOption', option: {} });
+                yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
             }
-        }
+        },
+        *fetchScatterData(action, { select, call, put }) {
+            try {
+                let res = {
+                    viewType: 'scatter'
+                }
+
+                // const chartDesigner = yield select(state => state.present.chartDesigner);
+                // const { lineConfig, preparing } = chartDesigner;
+                // const body = {
+                //     tableName: "TEST_BI_DATA",
+                //     xAxis: {
+                //         columnRename: lineConfig.xAxis.column.value,
+                //         columnType: lineConfig.xAxis.column.type
+                //     },
+                //     yAxis: {
+                //         columnRename: lineConfig.yAxis.column.value,
+                //         showDataType: lineConfig.yAxis.gauge.value
+                //     },
+                //     groups: preparing.groupBy && preparing.groupBy.key ? [preparing.groupBy.key] : [],
+                // };
+                // let res = yield call(service.fetch, {
+                //     url: URLS.CHART_LINE_OPTION,
+                //     body: body
+                // });
+                // if(!res.err && res.data.code > 0) {
+                //     res.viewType = 'scatter';
+                    yield put({ type: 'silentSetField', name: 'chartOption', value: res });
+                // }else {
+                //     yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
+                // }
+            }catch(e) {
+                console.error(e);
+                yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
+            }
+        },
     },
     subscriptions: {
         setup({ dispatch, history }) {

+ 9 - 3
src/models/dataConnect.js

@@ -61,6 +61,10 @@ export default {
     effects: {
         *fetchList(action, { select, call, put, takeEvery, takeLatest }) {
             try {
+                const dataConnect = yield select(state => state.present.dataConnect);
+                if(!action.mandatory && dataConnect.list.length > 0) {
+                    return;
+                }
                 const res = yield call(service.fetch, {
                     url: URLS.DATACONNECT_LIST,
                     body: {}
@@ -80,7 +84,6 @@ export default {
                             description: r.note
                         }
                     });
-                    console.log(data);
                     yield put({ type: 'list', data });
                 }else {
                     message.error('读取数据连接配置列表错误');
@@ -133,7 +136,6 @@ export default {
                     passWord: model.password,
                     note: model.description
                 };
-                console.log(data);
                 const res = yield call(service.fetch, {
                     url: URLS.DATACONNECT_UPDATE,
                     body: data
@@ -184,7 +186,11 @@ export default {
     },
     subscriptions: {
         setup({ dispatch, history }) {
-            dispatch({ type: 'fetchList' })
+            return history.listen(({ pathname, query }) => {
+                if((pathname+'').startsWith('/datasource/database/')) {
+                    dispatch({ type: 'fetchList' })
+                }
+            })
         }
     }
 };

+ 12 - 12
src/models/dataSource.js

@@ -75,7 +75,6 @@ export default {
                 newOne[fields[i]['name']] = fields[i]['value'];
             }
             let obj = Object.assign({}, state, {newOne});
-            console.log(obj);
             return obj;
         },
         setNewModelField(state, action) {
@@ -83,7 +82,6 @@ export default {
             let newOne = state.newOne;
             newOne[name] = value;
             let obj = Object.assign({}, state, {newOne});
-            console.log(obj);
             return obj;
         },
         setNewModel(state, action) {
@@ -95,13 +93,16 @@ export default {
             return Object.assign({}, state, {newOne: {}});
         },
         printNewOne(state, action) {
-            console.log(state.newOne);
             return state;
         }
     },
     effects: {
         *fetchList(action, { select, call, put, takeEvery, takeLatest }) {
             try {
+                const dataSource = yield select(state => state.present.dataSource);
+                if(!action.mandatory && dataSource.list.length > 0) {
+                    return;
+                }
                 const res = yield call(service.fetch, {
                     url: URLS.DATASOURCE_LIST,
                     body: {}
@@ -128,7 +129,6 @@ export default {
                 }
             }catch(e) {
                 message.error('读取数据源列表错误');
-                console.log(e);
             }
             
         },
@@ -164,14 +164,15 @@ export default {
                         }
                     })
                 };
-                console.log(data);
                 const res = yield call(service.fetch, {
                     url: URLS.DATASOURCE_ADD,
                     body: data
                 });
                 if(!res.err && res.data.code > 0) {
+                    let list = dataSource.list;
+                    list.unshift(model);
+                    yield put({ type: 'list', data: list });
                     message.success('新增成功!');
-                    yield put({ type: 'fetchList' });
                 }else {
                     message.error('新增失败!');
                 }
@@ -223,10 +224,12 @@ export default {
                     }
                     yield put({ type: 'setNewModel', model: data });
                 }else {
+                    message.error('数据源解析错误');
                     console.log(res);
                 }
             }catch(e) {
                 console.log(e);
+                message.error('数据源解析错误');
                 yield put({ type: 'list', data: [] });
             }
         },
@@ -239,7 +242,6 @@ export default {
                     "String": sqlStr
                 }
             });
-            console.log(res);
 
             const getColumnType = (dataType) => {
                 let columnType = 'string';
@@ -338,7 +340,6 @@ export default {
                         }
                     })
                 };
-                console.log(data);
                 const res = yield call(service.fetch, {
                     url: URLS.DATASOURCE_UPDATE,
                     body: data
@@ -363,16 +364,15 @@ export default {
     },
     subscriptions: {
         setup({ dispatch, history }) {
-            dispatch({ type: 'fetchList' })
             return history.listen(({ pathname, query }) => {
+                if(pathname === '/datasource' || pathname.match(/chart\/(\w+)/)) {
+                    dispatch({ type: 'fetchList' });
+                }
                 let detail = pathname.match(/datasource\/(\w+)\/(\w+)/);
                 if(detail) {
                     if(pathname.match(/datasource\/(\w+)\/(\w+)\/(\w+)/)) {
                         detail = pathname.match(/datasource\/(\w+)\/(\w+)\/(\w+)/);
-                        let type = detail[1];
                         let code = detail[2];
-                        let tab = detail[3];
-                        console.log(type, code, tab);
                         if(code !== 'create') {
                             dispatch({ type: 'remoteDetail', code: code })
                         }

+ 6 - 0
src/models/main.js

@@ -1,4 +1,5 @@
 import { routerRedux } from 'dva/router'
+import { message } from 'antd'
 
 export default {
     namespace: 'main',
@@ -18,6 +19,11 @@ export default {
     },
     subscriptions: {
         setup({ dispatch, history }) {
+            message.config({
+                top: 60,
+                duration: 2,
+                maxCount: 3,
+            });
             return history.listen(({ pathname, query }) => {
                 let page = pathname.match(/\/(\w*)/)[1];
                 dispatch({ type: 'setPage', page });

+ 0 - 3
src/models/mapRule.js

@@ -1,3 +0,0 @@
-function dataSource_detail() {
-    
-}

Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff