zhuth 7 years ago
parent
commit
98ff56899a

+ 74 - 86
app/components/chart/list.jsx

@@ -1,12 +1,14 @@
 import React from 'react'
-import { Layout, Tabs, Button, Icon, Input, Menu, Dropdown, Card, Col, Row, Avatar } from 'antd'
+import { Layout, Tabs, Button, Icon, Input, Menu, Dropdown, Card, Col, Row, Tag, Avatar } from 'antd'
 import { Link } from 'react-router-dom'
 const { Header, Content } = Layout
 const { Search } = Input
 const { TabPane } = Tabs
 const { Meta } = Card
+const CardGrid = Card.Grid
 import { connect } from 'dva'
 import '../../models/chart'
+import './list.less'
 
 
 class ChartList extends React.Component {
@@ -16,103 +18,89 @@ class ChartList extends React.Component {
         }
     }
 
+    componentDidMount() {
+        this.setBodyWidth();
+    }
+
+    /**
+     * 设置卡片容器宽度 = 每行卡片数量 * 卡片宽度
+     */
+    setBodyWidth() {
+        const chartBody = document.getElementsByClassName('chart-body')[0]; // 卡片容器
+        const parent = chartBody.parentNode; // 父级容器
+        const pWidth = parent.offsetWidth; // 父级容器宽度
+        const pPadding = 10 + 10; // 父级容器左右padding
+        const cWidth = 207; // 每个卡片宽度
+        const cMargin = 5 + 5; // 每个卡片左右margin
+
+
+        let col = Math.floor((pWidth - pPadding)/(cWidth + cMargin));
+        chartBody.style.width = col * (cWidth + cMargin)  + 'px';
+    }
+
     generateCard() { 
         const list = this.props.chart.list;
-        return list.map(i => {
-            return (
-                <Card key={i.dashboardID}
-                    className='dashboard-card'
-                    cover={<img alt={i.coverAlt} src={i.coverImg} />}
-                    actions={[<Icon type="setting" />, <Icon type="edit" />, <Icon type="ellipsis" />]}
+        return list.map( (l, i) => (
+            <CardGrid className='chart-card' key={i}>
+                <Card
+                    title={
+                        <Row type='flex' justify='space-between'>
+                            <Col>{l.name}</Col>
+                            <Col style={{ textAlign: 'right' }}>
+                                <Icon type='star-o'/>
+                            </Col>
+                        </Row>
+                    }
+                    cover={
+                        <Col style={{ padding: '10px' }}>
+                            <Row style={{ height: '164px' }}>
+                                <img style={{ height: '100px', marginTop: '10%' }} src='https://test-feapp.oss-cn-beijing.aliyuncs.com/feapp/master_a_20180711/images/chart/empty_column_chart.svg'></img>
+                            </Row>
+                            <Row type='flex' justify='end' align='bottom'>
+                                <Col style={{ textAlign: 'left' }} span={22}>
+                                    <Row>{l.creator}</Row>
+                                    <Row>{new Date(l.createTime).format('yyyy-MM-dd')}</Row>
+                                </Col>
+                                <Col span={2} style={{ textAlign: 'right' }}>
+                                    <Icon type="ellipsis" />
+                                </Col>
+                            </Row>
+                        </Col>
+                    }
                 >
-                    <Meta
-                        avatar={<Avatar src={i.avatar} />}
-                        title={i.title}
-                        description={i.description}
-                    />
                 </Card>
-            )
-        })
+            </CardGrid>
+        ));
     }
 
     render() {
         return (
-            <Layout>
-                <Header>
-                    <div className='toolbar'>
-                        <div>
-                        </div>
-                        <div className='tools'>
-                            <Search
-                                placeholder="请输入关键字"
-                                onSearch={value => console.log(value)}
-                            />
-                            <Link to='/chart/chartdesigner/create'>
-                                <Button>
-                                    <Icon type="area-chart" />创建图表
-                                </Button>
-                            </Link>
-                        </div>
-                    </div>
-                </Header>
+            <Layout className='chart-list'>
                 <Content>
+                    <Card title={
+                        <Row type='flex' justify='end'>
+                            <Col style={{ padding: '0 5px' }}>
+                                <Search
+                                    placeholder="请输入关键字"
+                                    onSearch={value => console.log(value)}
+                                />
+                            </Col>
+                            <Col >
+                                <Link to='/chart/chartdesigner/create'>
+                                    <Button>
+                                        <Icon type="area-chart" />创建图表
+                                    </Button>
+                                </Link>
+                            </Col>
+                        </Row>
+                    }>
+                        <div className='chart-body'>
+                            { this.generateCard() }
+                        </div>
+                    </Card>
                 </Content>
             </Layout>
         )
-
-        //     <Tabs
-        //         className='dashboard-tabs'
-        //         type="card"
-        //         defaultActiveKey="1"
-        //         tabBarExtraContent={
-        //             <div className='dashboard-tabs-tools'>
-        //                 <Button onClick={() => {
-        //                     dispatch({ type: activeTab == 'dashboard' ? 'databoard/remoteList' : 'dashboard/testData' });
-        //                 }}>测试数据</Button>
-        //                 <Search
-        //                     placeholder="请输入关键字"
-        //                     onSearch={value => console.log(value)}
-        //                 />
-        //                 <Dropdown overlay={(
-        //                     <Menu onClick={(item, key, keyPath) => {
-        //                         const type = item.key;
-        //                         dispatch({ type: 'dashboard/resetNewModel' });
-        //                         dispatch({ type: 'dashboard/setNewModelField', name: 'type', value: type });
-        //                         dispatch({ type: 'main/redirect', path: { pathname: '/dashboard/' + type + '/create' } });
-        //                     }}>
-        //                         <Menu.Item key='static'>报告</Menu.Item>
-        //                         <Menu.Item key='dynamic'>看板</Menu.Item>
-        //                     </Menu>
-        //                 )} trigger={['click']}>
-        //                     <Button style={{ display: 'inline-block' }}>
-        //                         <Icon type="plus" />创建
-        //                     </Button>
-        //                 </Dropdown>
-        //             </div>
-        //         }
-        //         onChange={(key) => {
-        //             this.setState({
-        //                 activeTab: key == '1' ? 'static' : 'dynamic'
-        //             });
-        //         }}
-
-        //     >
-        //         <TabPane tab="我的看板" key="1" >
-        //             <div style={{ display: 'flex', background: '#ECECEC', padding: '30px', flexWrap: 'wrap' }}>
-        //                 {this.generateCard()}
-        //             </div>
-
-
-        //         </TabPane>
-        //         <TabPane tab="我的报告" key="2" >
-        //             {/* 我的报表View */}
-        //         </TabPane>
-        //         <TabPane tab="最近打开" key="3" >
-        //             {/* 最近打开View */}
-        //         </TabPane>
-        //     </Tabs>
-
-
     }
 }
 

+ 41 - 0
app/components/chart/list.less

@@ -0,0 +1,41 @@
+.chart-list {
+    .ant-card-head {
+        padding: 0 10px;
+    }
+    .ant-card-body {
+        padding: 10px;
+        display: flex;
+        flex-wrap: wrap;
+        justify-content: center;
+
+        .chart-card {
+            width: 207px;
+            text-align: center;
+            margin: 5px;
+            padding: 0;
+
+            .ant-card-head {
+                min-height: 20px;
+                cursor: default;
+                .ant-card-head-title {
+                    font-size: 14px;
+                    padding: 6px 0;
+                    .anticon {
+                        cursor: pointer;
+                    }
+                }
+            }
+
+            .ant-card-cover {
+                height: 200px;
+                cursor: pointer;
+                .ant-row-flex-bottom {
+                    cursor: default;
+                    .anticon {
+                        cursor: pointer;
+                    }
+                }
+            }
+        }
+    }
+}

+ 78 - 0
app/components/chartDesigner/charts/resolveChartOption.js

@@ -6,6 +6,9 @@ export default (config) => {
             option = barConfig(data.data);
             break;
         }
+        case 'pie': {
+            option = pieConfig(data.data);
+        }
         default:{
         }
     }
@@ -47,4 +50,79 @@ function barConfig(data) {
         }) 
     }
     return o;
+}
+
+function pieConfig(data) {
+    function genData(count) {
+        var nameList = [
+            '赵', '钱', '孙', '李', '周', '吴', '郑', '王', '冯', '陈', '褚', '卫', '蒋', '沈', '韩', '杨', '朱', '秦', '尤', '许', '何', '吕', '施', '张', '孔', '曹', '严', '华', '金', '魏', '陶', '姜', '戚', '谢', '邹', '喻', '柏', '水', '窦', '章', '云', '苏', '潘', '葛', '奚', '范', '彭', '郎', '鲁', '韦', '昌', '马', '苗', '凤', '花', '方', '俞', '任', '袁', '柳', '酆', '鲍', '史', '唐', '费', '廉', '岑', '薛', '雷', '贺', '倪', '汤', '滕', '殷', '罗', '毕', '郝', '邬', '安', '常', '乐', '于', '时', '傅', '皮', '卞', '齐', '康', '伍', '余', '元', '卜', '顾', '孟', '平', '黄', '和', '穆', '萧', '尹', '姚', '邵', '湛', '汪', '祁', '毛', '禹', '狄', '米', '贝', '明', '臧', '计', '伏', '成', '戴', '谈', '宋', '茅', '庞', '熊', '纪', '舒', '屈', '项', '祝', '董', '梁', '杜', '阮', '蓝', '闵', '席', '季', '麻', '强', '贾', '路', '娄', '危'
+        ];
+        var legendData = [];
+        var seriesData = [];
+        var selected = {};
+        for (var i = 0; i < 50; i++) {
+            name = Math.random() > 0.65
+                ? makeWord(4, 1) + '·' + makeWord(3, 0)
+                : makeWord(2, 1);
+            legendData.push(name);
+            seriesData.push({
+                name: name,
+                value: Math.round(Math.random() * 100000)
+            });
+            selected[name] = i < 6;
+        }
+    
+        return {
+            legendData: legendData,
+            seriesData: seriesData,
+            selected: selected
+        };
+    
+        function makeWord(max, min) {
+            var nameLen = Math.ceil(Math.random() * max + min);
+            var name = [];
+            for (var i = 0; i < nameLen; i++) {
+                name.push(nameList[Math.round(Math.random() * nameList.length - 1)]);
+            }
+            return name.join('');
+        }
+    }
+    let o = {
+        title : {
+            text: '同名数量统计',
+            subtext: '纯属虚构',
+            x:'center'
+        },
+        tooltip : {
+            trigger: 'item',
+            formatter: "{a} <br/>{b} : {c} ({d}%)"
+        },
+        legend: {
+            type: 'scroll',
+            orient: 'vertical',
+            right: 10,
+            top: 20,
+            bottom: 20,
+            data: data.legendData,
+    
+            selected: data.selected
+        },
+        series : [
+            {
+                name: '姓名',
+                type: 'pie',
+                radius : '55%',
+                center: ['40%', '50%'],
+                data: data.seriesData,
+                itemStyle: {
+                    emphasis: {
+                        shadowBlur: 10,
+                        shadowOffsetX: 0,
+                        shadowColor: 'rgba(0, 0, 0, 0.5)'
+                    }
+                }
+            }
+        ]
+    }
+    return o;
 }

+ 58 - 27
app/components/chartDesigner/content.jsx

@@ -1,28 +1,45 @@
-import React from 'react';
-import { Layout,  Collapse, Form, Select, Input, Tabs } from 'antd';
-const { Header, Sider, Content } = Layout;
-const CollapsePanel = Collapse.Panel;
-const { TabPane } = Tabs;
-const { FormItem } = Form;
-const { Option } = Select;
-import BaseConfigForm from './sections/baseConfigForm';
-import PreparingForm from './sections/preparingForm';
-import AggregateTableConfigForm from './sections/aggregateTableConfigForm';
-import DataViewConfigForm from './sections/dataViewConfigForm';
-import BarConfigForm from './sections/barConfigForm';
-import LineConfigForm from './sections/lineConfigForm';
-import TableView from './charts/table';
-import EchartsView from './charts/echartsView';
-import StyleEditor from './sections/styleEditor';
-import ToolBar from './sections/toolBar';
-import './content.less';
-import { connect } from 'dva';
-import chartDesigner from '../../models/chartDesigner';
-import chartOption from '../../data/charts/option/bar.json';
+import React from 'react'
+import { Layout,  Collapse, Form, Select, Input, Tabs, Switch, Button } from 'antd'
+const { Header, Sider, Content, Footer } = Layout
+const CollapsePanel = Collapse.Panel
+const { TabPane } = Tabs
+const { FormItem } = Form
+const { Option } = Select
+import BaseConfigForm from './sections/baseConfigForm'
+import PreparingForm from './sections/preparingForm'
+import AggregateTableConfigForm from './sections/aggregateTableConfigForm'
+import DataViewConfigForm from './sections/dataViewConfigForm'
+import BarConfigForm from './sections/barConfigForm'
+import PieConfigForm from './sections/pieConfigForm'
+import LineConfigForm from './sections/lineConfigForm'
+import TableView from './charts/table'
+import EchartsView from './charts/echartsView'
+import StyleEditor from './sections/styleEditor'
+import ToolBar from './sections/toolBar'
+import { connect } from 'dva'
+import '../../models/chartDesigner'
+import '../../data/charts/option/bar.json'
+import './content.less'
 
 class ChartDesignerContent extends React.Component {
+
+    constructor(props) {
+        super(props);
+        this.state = {
+            autoRefresh: true // 自动刷新
+        }
+    }
+
+    changeRefreshMode = () => {
+        this.setState({
+            autoRefresh: !this.state.autoRefresh
+        })
+    }
+
     render() {
-        const { baseConfig } = this.props.chartDesigner;
+        const { autoRefresh } = this.state;
+        const { chartDesigner, dispatch } = this.props;
+        const { baseConfig } = chartDesigner;
         const { viewType } = baseConfig;
         const formItemLayout = {
             labelCol: { span: 8 },
@@ -44,23 +61,37 @@ class ChartDesignerContent extends React.Component {
             configForm = (<BarConfigForm formItemLayout={formItemLayout}/>);
             chartView = (<EchartsView />);
         }else if(viewType.key == 'pie') {
-            // configForm = (<LineConfigForm formItemLayout={formItemLayout}/>);
+            configForm = (<PieConfigForm formItemLayout={formItemLayout}/>);
             // chartView = (<EchartsView option={chartOption}/>);
         }
 
         return (
-            <Layout>
+            <Layout className='chartdesigner'>
                 <Sider className='sider-left' width={300}>
-                    <Tabs calssName='content-tabs'>
+                    <Tabs className='sider-tabs'>
                         <TabPane className='chartconfig' tab='图表设置' key='1'>
                             <BaseConfigForm formItemLayout={formItemLayout}/>
                             { configForm }
-                            <PreparingForm formItemLayout={formItemLayout}/>
                         </TabPane>
                         <TabPane className='otherconfig' tab='其他设置' key='2'>
                             <StyleEditor formItemLayout={formItemLayout}/>
                         </TabPane>
-                    </Tabs> 
+                    </Tabs>
+                    <Footer className='sider-footer'>
+                        <div className='fresh-bar'>
+                            <Switch defaultChecked checkedChildren='自动刷新' unCheckedChildren='手动刷新' onChange={this.changeRefreshMode}/>
+                            <Button hidden={autoRefresh} size='small' onClick={() => {
+                                const viewType = chartDesigner.baseConfig.viewType.key;
+                                if(viewType == 'bar') {
+                                    dispatch({ type: 'chartDesigner/fetchBarData'});
+                                }else if(viewType == 'pie') {
+                                    console.log(111);
+                                    //dispatch({ type: 'chartDesigner/fetchPieData' });
+                                }
+                            }}
+                            >立即刷新</Button>
+                        </div>
+                    </Footer> 
                 </Sider>
                 <Content>
                     <Layout>

+ 26 - 7
app/components/chartDesigner/content.less

@@ -1,8 +1,27 @@
-.ant-tabs-nav .ant-tabs-tab {
-    height: 37px;
-    margin: 0;
-    padding: 7px 16px;
-}
-.chartconfig, .otherconfig {
-    padding: 0 10px;
+.chartdesigner {
+    .sider-left {
+        .sider-tabs {
+            .ant-tabs-nav .ant-tabs-tab {
+                height: 37px;
+                margin: 0;
+                padding: 7px 16px;
+            }
+            .chartconfig, .otherconfig {
+                padding: 0 10px;
+            }
+        }
+        .sider-footer {
+            position: absolute;
+            bottom: 0;
+            width: 100%;
+            padding: 0;
+            border-top: 1px solid #CCCCCC;
+            height: 40px;
+            .fresh-bar {
+                display: flex;
+                justify-content: space-between;
+                margin: 9px 10px 0;
+            }
+        }
+    }
 }

+ 9 - 4
app/components/chartDesigner/header.jsx

@@ -13,7 +13,7 @@ const Header = ({ chartDesigner, dispatch }) => {
         <div className='header'>
             <div className='header-item toolbar-back'>
                 <Button onClick={() => {
-                    dispatch({ type: 'main/redirect', path: '/' });
+                    window.history.back();
                 }}>
                     返回
                 </Button>
@@ -35,9 +35,14 @@ const Header = ({ chartDesigner, dispatch }) => {
             </div>
             <div className='header-item toolbar-buttons'>
                 <div className=''>
-                    <Button onClick={() => {
-                        dispatch({ type: 'chartDesigner/fetchChartData'});
-                    }}>请求测试</Button>
+                    {/* <Button onClick={() => {
+                        const viewType = chartDesigner.baseConfig.viewType.key;
+                        if(viewType == 'bar') {
+                            dispatch({ type: 'chartDesigner/fetchBarData'});
+                        }else if(viewType == 'pie') {
+                            dispatch({ type: 'chartDesigner/fetchPieData' });
+                        }
+                    }}>请求测试</Button> */}
                     <Button className='button-uodo' icon='undo' onClick={() => {
                         dispatch(ActionCreators.undo());
                     }}>撤销</Button>

+ 29 - 21
app/components/chartDesigner/sections/barConfigForm.jsx

@@ -5,11 +5,11 @@ const { Option } = Select;
 const { SubMenu, MenuItemGroup } = Menu;
 import { connect } from 'dva';
 import chartDesigner from '../../../models/chartDesigner';
-import gauge from './gauge.json';
-import granularity from './granularity.json';
+import GAUGE from './gauge.json';
+import GRACULARITY from './granularity.json';
 import './barConfigForm.less';
 
-const barConfigForm = ({ chartDesigner, dispatch, formItemLayout }) => {
+const BarConfigForm = ({ chartDesigner, dispatch, formItemLayout }) => {
 	
 	
 	const columns = chartDesigner.columns;
@@ -29,19 +29,6 @@ const barConfigForm = ({ chartDesigner, dispatch, formItemLayout }) => {
 	return (
 		<Form hideRequiredMark={true}>
 			<FormItem label='横轴' {...formItemLayout}>
-				{/* <Select
-					value={chartDesigner.barConfig.xAxis}
-					allowClear={true}
-					placeholder='请选择...'
-					labelInValue={true}
-					onChange={(value) => {
-						dispatch({ type: 'chartDesigner/setModel', name: 'barConfig', value: { ...chartDesigner.barConfig, xAxis: value } });
-					}}
-				>
-					{columns.filter(c => ['ordinal', 'categorical'].indexOf(c.type)!=-1).map((c, i)=>{
-						return (<Option key={i} value={c.name}>{c.label}</Option>)
-					})}
-				</Select> */}
 				<Cascader
 					className='barconfig-yaxis'
 					value={[chartDesigner.barConfig.xAxis.column.value, chartDesigner.barConfig.xAxis.granularity.value]}
@@ -52,7 +39,7 @@ const barConfigForm = ({ chartDesigner, dispatch, formItemLayout }) => {
 							type: c.type,
 							value: c.name,
 							label: c.label,
-							children: granularity[c.type]
+							children: GRACULARITY[c.type]
 						}
 					})}
 					onChange={(value, items) => {
@@ -89,11 +76,17 @@ const barConfigForm = ({ chartDesigner, dispatch, formItemLayout }) => {
 					className='barconfig-yaxis'
 					value={[chartDesigner.barConfig.yAxis.column.value, chartDesigner.barConfig.yAxis.gauge.value]}
 					allowClear={true}
-					options={columns.filter(c =>c.type == 'scale').map((c, i)=>{
+					options={columns.map((c, i)=>{
 						return {
 							value: c.name,
 							label: c.label,
-							children: gauge
+							children: GAUGE.map(g => {
+                                if(g.columnType.indexOf(c.type) != -1) {
+                                    return g;
+                                }else {
+                                    return null;
+                                }
+                            }).filter( g => g!==null)
 						}
 					})}
 					onChange={(value, items) => {
@@ -103,7 +96,7 @@ const barConfigForm = ({ chartDesigner, dispatch, formItemLayout }) => {
 							column = { value: items[0].value, label: items[0].label };
 							gauge = { value: items[1].value, label: items[1].label };
 						}
-						dispatch({ type: 'chartDesigner/setModel', name: 'barConfig', value: { ...chartDesigner.barConfig, yAxis: { column, gauge } } });
+						dispatch({ type: 'chartDesigner/changeModel', name: 'barConfig', value: { ...chartDesigner.barConfig, yAxis: { column, gauge } } });
 					}}
 					displayRender={(label, selectedOptions) => {
 						let menu = selectedOptions.length > 0 ? <Menu
@@ -141,6 +134,21 @@ const barConfigForm = ({ chartDesigner, dispatch, formItemLayout }) => {
 				>
 				</Cascader>
 			</FormItem>
+			<FormItem label='分组' {...formItemLayout}>
+				<Select
+					mode="multiple"
+					labelInValue={true}
+					placeholder='请选择...'
+					onChange={(value) => {
+						props.dispatch({ type: 'chartDesigner/setModel', name: 'preparing', value: { ...props.chartDesigner.preparing, groupBy: value } });
+					}}
+					value={chartDesigner.preparing.groupBy}
+				>
+					{columns.map((c, i)=>{
+						return (<Option key={i} value={c.name}>{c.label}</Option>)
+					})}
+				</Select>
+			</FormItem>
 		</Form>
 	);
 }
@@ -149,4 +157,4 @@ function mapStateToProps({ present: { chartDesigner } }) {
     return { chartDesigner: chartDesigner }
 }
 
-export default connect(mapStateToProps)(barConfigForm);
+export default connect(mapStateToProps)(BarConfigForm);

+ 15 - 63
app/components/chartDesigner/sections/baseConfigForm.jsx

@@ -4,8 +4,9 @@ const FormItem = Form.Item
 const { Option } = Select
 const { Search } = Input
 import { connect } from 'dva'
-import chartDesigner from '../../../models/chartDesigner'
-import dataSource from '../../../models/dataSource'
+import '../../../models/chartDesigner'
+import '../../../models/dataSource'
+import CHART_TYPE from './chartType.json'
 import './baseConfigForm.less'
 
 class baseConfigForm extends React.Component {
@@ -14,14 +15,6 @@ class baseConfigForm extends React.Component {
 		const allDataSource = props.dataSource.list;
 		const { formItemLayout } = props
 
-		const menu = (
-			<Menu>
-				<Menu.Item key="1">1st menu item</Menu.Item>
-				<Menu.Item key="2">2nd memu item</Menu.Item>
-				<Menu.Item key="3">3rd menu item</Menu.Item>
-			</Menu>
-		);
-
 		return (
 			<Form hideRequiredMark={true}>
 				<FormItem label='数据源' {...formItemLayout}>
@@ -47,60 +40,19 @@ class baseConfigForm extends React.Component {
 							props.dispatch({ type: 'chartDesigner/setModel', name: 'baseConfig', value: { ...props.chartDesigner.baseConfig, viewType: { key: value, label: item.title } } });
 						}}
 					>
-						<Option value="aggregateTable" title='总体统计数据表'>
-							<div className='viewtype-box'>
-								<div className='viewtype-icon viewtype-icon-agg'>
-								</div>
-								<div className='viewtype-text'>
-									总体统计数据表
-								</div>
-							</div>
-						</Option>
-						<Option value="dataView" title='个体统计数据表'>
-							<div className='viewtype-box'>
-								<div className='viewtype-icon'>
-								</div>
-								<div className='viewtype-text'>
-									个体统计数据表
-								</div>
-							</div>
-						</Option>
-						<Option value="line" title='折线图'>
-							<div className='viewtype-box'>
-								<div className='viewtype-icon'>
-								</div>
-								<div className='viewtype-text'>
-									折线图
-								</div>
-							</div>
-						</Option>
-						<Option value="bar" title='柱状图'>
-							<div className='viewtype-box'>
-								<div className='viewtype-icon'>
-								</div>
-								<div className='viewtype-text'>
-									柱状图
-								</div>
-							</div>
-						</Option>
-						<Option value="pie" title='饼状图'>
-							<div className='viewtype-box'>
-								<div className='viewtype-icon'>
-								</div>
-								<div className='viewtype-text'>
-									饼状图
-								</div>
-							</div>
-						</Option>
-						<Option value="scatter" title='散点图'>
-							<div className='viewtype-box'>
-								<div className='viewtype-icon'>
-								</div>
-								<div className='viewtype-text'>
-									散点图
+					{
+						CHART_TYPE.map( c => (
+							<Option key={c.type} value={c.type} title={c.label}>
+								<div className='viewtype-box'>
+									<div className='viewtype-icon'>
+									</div>
+									<div className='viewtype-text'>
+										{c.label}
+									</div>
 								</div>
-							</div>
-						</Option>
+							</Option>
+						))
+					}
 					</Select>
 				</FormItem>
 			</Form>

+ 19 - 0
app/components/chartDesigner/sections/chartType.json

@@ -0,0 +1,19 @@
+[{
+    "type": "aggregateTable",
+    "label": "总体统计数据表"
+}, {
+    "type": "dataView",
+    "label": "个体统计数据表"
+}, {
+    "type": "line",
+    "label": "折线图"
+}, {
+    "type": "bar",
+    "label": "柱状图"
+}, {
+    "type": "pie",
+    "label": "饼图"
+}, {
+    "type": "scatter",
+    "label": "散点图"
+}]

+ 1 - 0
app/components/chartDesigner/sections/filterBox.less

@@ -29,4 +29,5 @@
   margin-right: 32px;
   cursor: pointer;
   color: red;
+  font-size: 14px;
 }

+ 14 - 10
app/components/chartDesigner/sections/gauge.json

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

+ 145 - 0
app/components/chartDesigner/sections/pieConfigForm.jsx

@@ -0,0 +1,145 @@
+import React from 'react';
+import { Form, Select, Icon, Tag, Cascader, Dropdown, Menu } from 'antd';
+const FormItem = Form.Item;
+const { Option } = Select;
+const { SubMenu, MenuItemGroup } = Menu;
+import { connect } from 'dva';
+import chartDesigner from '../../../models/chartDesigner';
+import GAUGE from './gauge.json';
+import GRANULARITY from './granularity.json';
+import './barConfigForm.less';
+
+const PieConfigForm = ({ chartDesigner, dispatch, formItemLayout }) => {
+	
+	
+	const columns = chartDesigner.columns;
+
+	const menu = <Menu>
+		<Menu.Item>
+		<a target="_blank" rel="noopener noreferrer" href="http://www.alipay.com/">1st menu item</a>
+		</Menu.Item>
+		<Menu.Item>
+		<a target="_blank" rel="noopener noreferrer" href="http://www.taobao.com/">2nd menu item</a>
+		</Menu.Item>
+		<Menu.Item>
+		<a target="_blank" rel="noopener noreferrer" href="http://www.tmall.com/">3rd menu item</a>
+		</Menu.Item>
+	</Menu>
+
+	return (
+		<Form hideRequiredMark={true}>
+			<FormItem label='扇区索引' {...formItemLayout}>
+				<Cascader
+					className='barconfig-yaxis'
+					value={[chartDesigner.barConfig.xAxis.column.value, chartDesigner.barConfig.xAxis.granularity.value]}
+					allowClear={true}
+					options={columns.filter(c =>['categorical'].indexOf(c.type) != -1).map((c, i)=>{
+						
+						return {
+							type: c.type,
+							value: c.name,
+							label: c.label,
+							children: GRANULARITY[c.type]
+						}
+					})}
+					onChange={(value, items) => {
+						let column = {};
+						let granularity = {};
+						if(value.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/setModel', name: 'barConfig', value: { ...chartDesigner.barConfig, 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='barconfig-yaxis'
+					value={[chartDesigner.barConfig.yAxis.column.value, chartDesigner.barConfig.yAxis.gauge.value]}
+					allowClear={true}
+					options={columns.map((c, i)=>{
+						return {
+							value: c.name,
+							label: c.label,
+							children: GAUGE.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 = { value: items[0].value, label: items[0].label };
+							gauge = { value: items[1].value, label: items[1].label };
+						}
+						dispatch({ type: 'chartDesigner/setModel', name: 'barConfig', value: { ...chartDesigner.barConfig, yAxis: { column, gauge } } });
+					}}
+					displayRender={(label, selectedOptions) => {
+						let menu = selectedOptions.length > 0 ? <Menu
+							selectedKeys={[chartDesigner.barConfig.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/setModel', name: 'barConfig', value: { 
+										...chartDesigner.barConfig,
+										yAxis: {
+											column: chartDesigner.barConfig.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>
+		</Form>
+	);
+}
+
+function mapStateToProps({ present: { chartDesigner } }) {
+    return { chartDesigner: chartDesigner }
+}
+
+export default connect(mapStateToProps)(PieConfigForm);

+ 9 - 21
app/components/datasource/columnConfig.jsx

@@ -1,14 +1,10 @@
 import React from 'react'
-import { Modal, Form, Row, Col, Input, Button, Select, Icon, Menu, Dropdown, Table, Checkbox, Switch, Divider } from 'antd'
+import { Form, Input, Button, Select, Table, Checkbox, Switch, Divider } from 'antd'
 const FormItem = Form.Item
 const SelectOption = Select.Option
-const OptionGroup = Select.OptGroup
-const InputGroup = Input.Group
-const SubMenu = Menu.SubMenu
-const MenuItem = Menu.Item
-const MenuItemGroup = Menu.ItemGroup;
 import { connect } from 'dva'
-import dataSource from '../../models/dataSource'
+import '../../models/dataSource'
+import COLUMN_TYPE from './columnType.json'
 
 class DataSourceColumnConfig extends React.Component {
 
@@ -24,6 +20,7 @@ class DataSourceColumnConfig extends React.Component {
     }
 
     render() {
+
         const { dataSource, dispatch } = this.props;
 
         const columns = [{
@@ -117,23 +114,14 @@ class DataSourceColumnConfig extends React.Component {
                         }}
                     >
                     {
-                        [
-                            <SelectOption value='index' key='index'>索引</SelectOption>,
-                            <SelectOption value='time' key='time'>时间</SelectOption>,
-                            <SelectOption value='categorical' key='categorical'>类别</SelectOption>,
-                            <SelectOption value='scale' key='scale'>标量</SelectOption>,
-                            <SelectOption value='ordinal' key='ordinal'>序值</SelectOption>,
-                            <SelectOption value='string' key='string'>字符串</SelectOption>
-                        ].map((s, i) => {
-                            if(record.dataType=='VARCHAR2' && [0,2,5].indexOf(i) != -1) {
-                                return s;
-                            }else if(record.dataType=='DATE' && [1].indexOf(i) != -1) {
-                                return s;
-                            }else if(record.dataType=='NUMBER' && [0,2,3,4,5].indexOf(i) != -1) {
-                                return s;
+                        COLUMN_TYPE.map( c => {
+                            let dataType = record.dataType;
+                            if(c.dataType.indexOf(dataType) != -1) {
+                                return <SelectOption value={c.columnType} key={c.columnType}>{c.label}</SelectOption>
                             }else {
                                 return null
                             }
+                            
                         }).filter((s)=>s!=null)
                     }
                     </Select>

+ 25 - 0
app/components/datasource/columnType.json

@@ -0,0 +1,25 @@
+[{
+    "dataType": ["VARCHAR", "VARCHAR2", "NUMBER"],
+    "columnType": "index",
+    "label": "索引"
+}, {
+    "dataType": ["DATE"],
+    "columnType": "time",
+    "label": "时间"
+}, {
+    "dataType": ["VARCHAR", "VARCHAR2", "NUMBER"],
+    "columnType": "categorical",
+    "label": "类别"
+}, {
+    "dataType": ["NUMBER"],
+    "columnType": "scale",
+    "label": "标量"
+}, {
+    "dataType": ["VARCHAR", "VARCHAR2", "NUMBER"],
+    "columnType": "ordinal",
+    "label": "序值"
+}, {
+    "dataType": ["VARCHAR", "VARCHAR2", "NUMBER", "CLOB"],
+    "columnType": "string",
+    "label": "字符串"
+}]

+ 17 - 1
app/models/chart.js

@@ -1,7 +1,23 @@
 export default {
     namespace: 'chart',
     state: {
-        list: []
+        list: [{
+            name: '图表1',
+            creator: 'zhuth',
+            createTime: '2018-07-15'
+        }, {
+            name: '图表1',
+            creator: 'zhuth',
+            createTime: '2018-07-15'
+        }, {
+            name: '图表1',
+            creator: 'zhuth',
+            createTime: '2018-07-15'
+        }, {
+            name: '图表1',
+            creator: 'zhuth',
+            createTime: '2018-07-15'
+        }]
     },
     reducer: {
 

+ 81 - 16
app/models/chartDesigner.js

@@ -68,6 +68,12 @@ export default {
         },
         preparing: {
             groupBy: []
+        },
+        aggregateTableConfig: {
+
+        },
+        dataViewConfig: {
+
         },
         barConfig: {
             xAxis: {
@@ -79,11 +85,20 @@ export default {
                 gauge: {}
             }
         },
-        aggregateTable: {
-
-        },
-        dataView: {
-
+        pieConfig: {
+            series: [{
+                name: '花花',
+                value: 30
+            }, {
+                name: '欢欢',
+                value: 21
+            }, {
+                name: '强强',
+                value: 64
+            }, {
+                name: '糖糖',
+                value: 49
+            }]
         },
         style: {
 
@@ -108,7 +123,67 @@ export default {
         }
     },
     effects: {
-        *['fetchChartData'](action, { select, call, put }) {
+        *changeModel(action, { select, call, put }) {
+            const chartDesigner = yield select(state => state.present.chartDesigner);
+            const { baseConfig } = chartDesigner;
+            const { viewType } = baseConfig;
+            const viewTypeKey = viewType.key;
+            const { name, value } = action;
+            yield put({ type: 'setModel', name, value });
+            if(viewTypeKey == 'bar') {
+                yield put({ type: 'fetchBarData' });
+            }else if(viewTypeKey == 'pie') {
+                //yield put({ type: 'fetchPieData' });
+            }else {
+                console.log(13)
+            }
+        },
+        *fetchBarData(action, { select, call, put }) {
+            try {
+                const chartDesigner = yield select(state => state.present.chartDesigner);
+                const { barConfig, preparing } = chartDesigner;
+                const res = yield call(service.fetch, {
+                    url: URLS.CHART_BAR_OPTION,
+                    body: {
+                        "tableName": "TEST_BI_DATA",
+                        "groups": preparing.groupBy.map(g => g.key),
+                        "xAxis": {
+                            "columnRename": barConfig.xAxis.column.value,
+                            "columnType": barConfig.xAxis.column.type,
+                            "dataType": barConfig.xAxis.granularity.value
+                        },
+                        "yAxis": {
+                            "columnRename": barConfig.yAxis.column.value,
+                            "dataType": barConfig.yAxis.gauge.value
+                        }
+                    }
+                });
+
+                console.log({
+                    "tableName": "TEST_BI_DATA",
+                    "groups": preparing.groupBy.map(g => g.key),
+                    "xAxis": {
+                        "columnRename": barConfig.xAxis.column.value,
+                        "columnType": barConfig.xAxis.column.type,
+                        "dataType": barConfig.xAxis.granularity.value
+                    },
+                    "yAxis": {
+                        "columnRename": barConfig.yAxis.column.value,
+                        "dataType": barConfig.yAxis.gauge.value
+                    }
+                })
+                
+                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:null;
+                res.data.data.gauge = barConfig.yAxis?barConfig.yAxis.gauge.label:null;
+                yield put({ type: 'setChartOption', option: res });
+
+            }catch(e) {
+                yield put({ type: 'setChartOption', option: {} });
+            }
+        },
+        *fetchPieData(action, { select, call, put }) {
             try {
                 const chartDesigner = yield select(state => state.present.chartDesigner);
                 const { barConfig, preparing } = chartDesigner;
@@ -152,16 +227,6 @@ export default {
             }catch(e) {
                 yield put({ type: 'setChartOption', option: {} });
             }
-            // 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:null;
-            //     res.data.data.gauge = barConfig.yAxis?barConfig.yAxis.gauge.label:null;
-            //     yield put({ type: 'setChartOption', option: res });
-            // }else {
-            //     yield put({ type: 'setChartOption', option: {} });
-            //     // 弹出错误提示
-            // }
         }
     },
     subscriptions: {

+ 0 - 2
app/models/dataConnect.js

@@ -203,8 +203,6 @@ export default {
     subscriptions: {
         setup({ dispatch, history }) {
             dispatch({ type: 'fetchList' })
-            return history.listen(({ pathname, query }) => {
-            })
         }
     }
 };

+ 1 - 1
app/routes/mainLayout.less

@@ -20,7 +20,7 @@ html,body,#root{
     }
     .main-content {
         height: 100%;
-        padding: 0 10%;
+        padding: 10px 10%;
         background-color: white;
         overflow: auto;
     }