Browse Source

筛选组件设计完善

zhuth 7 years ago
parent
commit
fa736feced

+ 1 - 0
.gitignore

@@ -1,3 +1,4 @@
 dist
 node_modules
 .DS_Store
+*.css

+ 1 - 0
app/component/chartDesigner/charts/echartsView.jsx

@@ -17,6 +17,7 @@ class EchartsView extends React.Component {
             <Echarts ref={(e) => { this.echarts_react = e; }}
                 option={this.state.option}
                 className='rc-echarts'
+                style={{height: '100%'}}
              />
         )
     }

+ 27 - 18
app/component/chartDesigner/content.jsx

@@ -1,6 +1,6 @@
 import React from 'react';
 import { Layout,  Collapse, Form, Select, Input, Tabs } from 'antd';
-const { Sider, Content } = Layout;
+const { Header, Sider, Content } = Layout;
 const CollapsePanel = Collapse.Panel;
 const { TabPane } = Tabs;
 const { FormItem } = Form;
@@ -10,9 +10,11 @@ import BaseConfigForm from './sections/baseConfigForm';
 import PreparingForm from './sections/preparingForm';
 import AggregateTableConfigForm from './sections/aggregateTableConfigForm';
 import DataViewConfigForm from './sections/dataViewConfigForm';
+import LineConfigForm from './sections/lineConfigForm';
 import TableView from './charts/table';
 import EchartsView from './charts/echartsView';
-import Filter from './sections/filterBox';
+import StyleEditor from './sections/styleEditor';
+import ToolBar from './sections/toolBar';
 import './content.less';
 
 class ChartDesignerContent extends React.Component {
@@ -40,7 +42,7 @@ class ChartDesignerContent extends React.Component {
     }
 
     componentWillUnmount() {
-        emitter.removeListener(this.eventEmitter);
+        emitter.removeAllListeners('changeViewType');
     }
 
     render() {
@@ -49,14 +51,20 @@ class ChartDesignerContent extends React.Component {
         let configForm, chartView;
 
         if(viewType.name == 'aggregateTable') {
-            configForm = (<AggregateTableConfigForm />)
-            chartView = (<TableView />)
+            configForm = (<AggregateTableConfigForm />);
+            chartView = (<TableView />);
         }else if(viewType.name == 'dataView') {
-            configForm = (<DataViewConfigForm />)
-            chartView = (<TableView />)
-        }else if(['bar', 'line', 'pie', 'scatter'].indexOf(viewType.name) != -1) {
-            configForm = (<DataViewConfigForm />)
-            chartView = (<EchartsView option={chartOption}/>)
+            configForm = (<DataViewConfigForm />);
+            chartView = (<TableView />);
+        }else if(viewType.name == 'line') {
+            configForm = (<DataViewConfigForm />);
+            chartView = (<EchartsView option={chartOption}/>);
+        }else if(viewType.name == 'bar') {
+            configForm = (<DataViewConfigForm />);
+            chartView = (<EchartsView option={chartOption}/>);
+        }else if(viewType.name == 'pie') {
+            configForm = (<LineConfigForm />);
+            chartView = (<EchartsView option={chartOption}/>);
         }
 
         return (
@@ -72,21 +80,22 @@ class ChartDesignerContent extends React.Component {
                     </Collapse>
                 </Sider>
                 <Content>
-                    <Tabs defaultActiveKey="1">
-                        <TabPane tab="图表预览" key="1">
+                    <Layout>
+                        <Header className='content-header'>
+                            <ToolBar className='header-toolbar'/>
+                        </Header>
+                        <Content>
                             { chartView }
-                        </TabPane>
-                        <TabPane tab="数据列" key="2">Content of Tab Pane 2</TabPane>
-                        <TabPane tab="Echarts配置档" key="3">Content of Tab Pane 3</TabPane>
-                    </Tabs>
+                        </Content>
+                    </Layout>
                 </Content>
                 <Sider width={250}>
                     <Collapse defaultActiveKey={['0', '1']}>
                         <CollapsePanel header={`${viewType.label}选项`}>
                             { configForm }
                         </CollapsePanel>
-                        <CollapsePanel header='筛选'>
-                            <Filter />
+                        <CollapsePanel header='样式'>
+                            <StyleEditor />
                         </CollapsePanel>
                     </Collapse>
                 </Sider>

+ 2 - 4
app/component/chartDesigner/header.jsx

@@ -19,12 +19,10 @@ class Header extends React.Component {
                 title
             });
         });
-        console.log('chartDesigner header\'s emitter has been added...');
     }
 
     componentWillUnmount() {
-        emitter.removeListener(this.eventEmitter);
-        console.log('chartDesigner header\'s emitter has been removed...');
+        emitter.removeAllListeners('headersettitle');
     }
 
     emit(eventName, params) {
@@ -42,7 +40,7 @@ class Header extends React.Component {
             <div className='header-item toolbar-title'>
                 <Input className='input-title' 
                     width={200} 
-                    addonAfter={<Icon type="setting" />} 
+                    addonAfter={<Icon type="edit" />} 
                     value={title}
                     onChange={this.titleChange}
                 />

+ 10 - 17
app/component/chartDesigner/header.less

@@ -1,24 +1,17 @@
 .toolbar-title {
-    float: left;
     .ant-input-group-wrapper {
         display: inline-block;
         vertical-align: middle;
-        width: 100%;
-        margin-bottom: 4px;
-        .input-title {
-            float: left;
-        }
+        width: 200px;
+        margin-bottom: 5px;
     }
 }
-.toolbar-buttons {
-    float: right;
-}
-.ant-layout-header {
-    background: none;
-    padding: 0 10px;
-    height: 40px !important;
-    line-height: 40px !important;
-    border-width: 1px 0;
-    border-style: solid;
-    border-color: #CCCCCC;
+.header {
+    display: flex;
+    justify-content: space-between;
+
+    .toolbar-title {
+        flex: 1;
+        text-align: center;
+    }
 }

+ 9 - 3
app/component/chartDesigner/layout.less

@@ -6,13 +6,19 @@ html,body,#root{
     height: 100%;
 }
 .ant-layout-header {
-    background: none !important;
+    background: none;
+    padding: 0 10px;
+    height: 40px;
+    line-height: 40px;
+    border-width: 1px 0;
+    border-style: solid;
+    border-color: #CCCCCC;
 }
 .ant-layout-content {
-    flex: 1 !important;
+    flex: 1;
 }
 .ant-layout-sider {
-    background: none !important;
+    background: none;
     border-width: 0 1px 0 1px;
     border-style: solid;
     border-color: #CCCCCC;

+ 0 - 3
app/component/chartDesigner/sections/aggregateTableConfigForm.jsx

@@ -14,12 +14,9 @@ class AggregateTableConfigForm extends React.Component {
 	}
 
 	componentDidMount() {
-		// To disabled submit button at the beginning.
-		this.props.form.validateFields();
     }
     
     onChange(checkedList) {
-        console.log(checkedList);
     }
 
 	render() {

+ 11 - 60
app/component/chartDesigner/sections/baseConfigForm.jsx

@@ -57,28 +57,6 @@ class baseConfigForm extends React.Component {
 
 		return (
 			<Form hideRequiredMark={true}>
-				<FormItem label='可视化模式' {...formItemLayout}>
-					{getFieldDecorator('viewType', {
-						rules: [{ required: true, message: '可视化模式不能为空' }],
-						initialValue : viewType
-					})(
-						<Select onChange={this.changeViewType}>
-							<Option value='aggregateTable'>总体统计数据表</Option>
-							<Option value='dataView'>个体统计数据表</Option>
-							<Option value='line'>折线图</Option>
-							<Option value='bar'>柱状图</Option>
-							<Option value='pie'>饼状图</Option>
-							<Option value='scatter'>散点图</Option>
-						</Select>
-					)}
-				</FormItem>
-				<FormItem label='描述' {...formItemLayout}>
-					{getFieldDecorator('note', {
-						initialValue: note
-					})(
-						<Input placeholder="请输入" />
-					)}
-				</FormItem>
 				<FormItem label='数据源' {...formItemLayout}>
 					{getFieldDecorator('dataSource', {
 						rules: [{ required: true, message: '数据源不能为空' }],
@@ -91,48 +69,21 @@ class baseConfigForm extends React.Component {
 						</Select>
 					)}
 				</FormItem>
-				<FormItem label='访问权限' {...formItemLayout}>
-					{getFieldDecorator('accessPermission', {
-						rules: [{ required: true, message: '访问权限不能为空' }],
-						initialValue: accessPermission
-					})(
-						<Select mode="multiple">
-							{allPermission.map((accessPermission, i)=>{
-								return (<Option key={`accessPermission-${i}`} value={accessPermission.value}>{accessPermission.name}</Option>)
-							})}
-						</Select>
-					)}
-				</FormItem>
-				<FormItem label='修改权限' {...formItemLayout}>
-					{getFieldDecorator('editPermission', {
-						rules: [{ required: true, message: '修改权限不能为空' }],
-						initialValue: editPermission
+				<FormItem label='可视化模式' {...formItemLayout}>
+					{getFieldDecorator('viewType', {
+						rules: [{ required: true, message: '可视化模式不能为空' }],
+						initialValue : viewType
 					})(
-						<Select mode="multiple">
-							{allPermission.map((editPermission, i)=>{
-								return (<Option key={`editPermission-${i}`} value={editPermission.value}>{editPermission.name}</Option>)
-							})}
+						<Select onChange={this.changeViewType}>
+							<Option value='aggregateTable'>总体统计数据表</Option>
+							<Option value='dataView'>个体统计数据表</Option>
+							<Option value='line'>折线图</Option>
+							<Option value='bar'>柱状图</Option>
+							<Option value='pie'>饼状图</Option>
+							<Option value='scatter'>散点图</Option>
 						</Select>
 					)}
 				</FormItem>
-				<FormItem label='图例' {...formItemLayout}>
-					<Switch checkedChildren="开" unCheckedChildren="关" defaultChecked={showLegend} />
-				</FormItem>
-				<FormItem label='提示框' {...formItemLayout}>
-					{getFieldDecorator('tooltip')(
-						<Switch checkedChildren="开" unCheckedChildren="关" defaultChecked={showTooltip} />
-					)}
-				</FormItem>
-				<FormItem label='缩放' {...formItemLayout}>
-					{getFieldDecorator('datazoom')(
-						<Switch checkedChildren="开" unCheckedChildren="关" defaultChecked={datazoom} />
-					)}
-				</FormItem>
-				<FormItem label='工具箱' {...formItemLayout}>
-					{getFieldDecorator('toolbox')(
-						<Switch checkedChildren="开" unCheckedChildren="关" defaultChecked={toolbox} />
-					)}
-				</FormItem>
 			</Form>
 		);
 	}

+ 20 - 23
app/component/chartDesigner/sections/dataViewConfigForm.jsx

@@ -9,7 +9,6 @@ class DataViewConfigForm extends React.Component {
 	constructor(props) {
 		super(props);
 		this.state = {
-			config: props.config
 		}
 	}
 
@@ -53,7 +52,7 @@ class DataViewConfigForm extends React.Component {
         };
         
 		return (
-			<Form layout='horizontal'>
+            <Form layout='horizontal'>
 				<FormItem label='分析目标' {...formItemLayout}>
 					{getFieldDecorator('targetColumn', {
 						rules: [{ required: true, message: '分析目标不能为空' }],
@@ -66,32 +65,30 @@ class DataViewConfigForm extends React.Component {
 						</Select>
 					)}
 				</FormItem>
-				<FormItem label='显示行数' {...formItemLayout}>
-					{getFieldDecorator('rows', {
-						initialValue: rows
-					})(
-					    <InputNumber min={1}/>
-					)}
-				</FormItem>
-                <FormItem label='显示顺序' {...formItemLayout}>
-					{getFieldDecorator('sort', {
-						initialValue : sort
+				{/* <FormItem label='横轴' {...formItemLayout}>
+					{getFieldDecorator('x', {
+						initialValue : targetColumn
 					})(
-						<Select>
-                            <Option key={`s-1`} value='asc'>正序</Option>
-                            <Option key={`s-2`} value='desc'>倒序</Option>
+						<Select mode='multiple'>
+							{columns.map((c, i)=>{
+								return (<SelectOption key={`x-${i}`} value={c.value}>{c.label}</SelectOption>)
+							})}
 						</Select>
 					)}
 				</FormItem>
-                <FormItem label='显示个体数据' {...formItemLayout}>
-					{getFieldDecorator('gauge', {
-						initialValue: gaugeOptions.filter(filterFunc).map((s)=>{return s.label})
-					})(
-						<CheckboxGroup options={gaugeOptions.map((s)=>{return s.label})} onChange={this.onChange} />
-					)}
-				</FormItem>
+                    <FormItem label='纵轴' {...formItemLayout}>
+                    {getFieldDecorator('y', {
+                            initialValue : targetColumn
+                    })(
+                        <Select mode='multiple'>
+                            {columns.map((c, i)=>{
+                                return (<SelectOption key={`y-${i}`} value={c.value}>{c.label}</SelectOption>)
+                            })}
+                        </Select>
+                    )}
+				</FormItem> */}
 			</Form>
-		);
+        );
 	}
 }
 

+ 224 - 89
app/component/chartDesigner/sections/filterBox.jsx

@@ -1,14 +1,17 @@
 import React from 'react';
-import { Form, Row, Col, Input, Icon, Button, Select } from 'antd';
+import { Form, Row, Col, Input, Icon, Button, Select, InputNumber, DatePicker } from 'antd';
 const FormItem = Form.Item;
 const SelectOption = Select.Option;
 import emitter from '../../../eventManger/ev';
 import { isEqual } from '../../../utils/baseUtils.js';
+import OPERATORS from './filterOperators.json';
 import './filterBox.less';
 
 let uuid = 0;
 class FilterBox extends React.Component {
+    
     constructor(props) {
+        uuid = 0;
         super(props);
         this.state = {
             columns: [{
@@ -33,127 +36,254 @@ class FilterBox extends React.Component {
                 name: 'zsf',
                 type: 'categorical'
             }],
-            filters: [{
-                key: 'c1-1',
-                name: 'c1',
-                operator: 'like',
-                value: '字符串'
-            }]
+            filterData: props.filterData || []
         }
     }
-    removeFilter(k) {
+
+    componentDidMount() {
+        // 将原本的过滤条件生成可视化组件
+        const { filterData } = this.state;
+        this.addFilter(filterData.map(f => {
+            return {
+                key: uuid++,
+                name: f.name,
+                label: f.label,
+                type: f.type,
+                operator: f.operator,
+                operatorLabel: f.operatorLabel,
+                value1: f.value1,
+                value2: f.value2,
+                using: f.using
+            }
+        }));
+        // 对外提供接口
+        // 在组件装载完成后发布事件
+        this.eventEmitter = emitter.addListener('getFilters', (callback)=>{
+            this.getFilters(callback)
+        });
+    }
+
+    componentWillUnmount() {
+        emitter.removeAllListeners('getFilters');
+    }
+
+    removeFilter = (key) => {
         const { form } = this.props;
-        // can use data-binding to get
-        const keys = form.getFieldValue('keys');
-    
-        // can use data-binding to set
+        const filters = form.getFieldValue('filters');
+        form.setFieldsValue({
+            filters: filters.filter(filter => filter.key !== key),
+        });
+    }
+
+    addFilter = (filtes) => {
+        const { form } = this.props;
+        const filters = form.getFieldValue('filters');
+        const nextFilters = filters.concat(filtes || {
+            key: uuid++,
+        });
         form.setFieldsValue({
-          keys: keys.filter(key => key !== k),
+            filters: nextFilters,
         });
     }
 
-    addFilter() {
+    /**
+     * 改变过滤条件字段
+     */
+    changeFilterName = (filter, value) => {
         const { form } = this.props;
-        // can use data-binding to get
-        const keys = form.getFieldValue('keys');
-        const nextKeys = keys.concat(uuid);
-        uuid++;
-        // can use data-binding to set
-        // important! notify form to detect changes
+        const filters = form.getFieldValue('filters');
+
         form.setFieldsValue({
-          keys: nextKeys,
+            filters: filters.map((f) => {
+                if (f.key == filter.key) {
+                    f.key = uuid++; // 每次重设key值以保证界面重现渲染,解决Select数据残留的问题
+                    f.name = value.key;
+                    f.label = value.label;
+                    f.type = this.getFilterType(value.key);
+                    f.operator = OPERATORS[f.type][0].value;
+                    f.operatorLabel = OPERATORS[f.type][0].label;
+                    f.value1 = f.value2 = undefined;
+                }
+                return f;
+            })
         });
     }
 
-    changeFilterName(value) {
-        if(value == 'c1') {
+    /**
+     * 改变过滤条件连接符
+     */
+    changeFilterOperator = (filter, value) => {
+        const { form } = this.props;
+        const filters = form.getFieldValue('filters');
+
+        form.setFieldsValue({
+            filters: filters.map((f) => {
+                if (f.key == filter.key) {
+                    f.key = uuid++;
+                    f.operator = value.key;
+                    f.operatorLabel = value.label;
+                }
+                return f;
+            })
+        });
+    }
 
+    /** 
+     * 改变过滤条件的值
+     */
+    changeFilterValue = (filter, value, index) => {
+        const { form } = this.props;
+        const filters = form.getFieldValue('filters');
+
+        form.setFieldsValue({
+            filters: filters.map((f) => {
+                if (f.key == filter.key) {
+                    f.key = uuid++;
+                    f[`value${index}`] = value;
+                }
+                return f;
+            })
+        });
+    }
+
+    /**
+     * 通过列名从数据列中获得其类型
+     */
+    getFilterType = (name) => {
+        let {
+            columns
+        } = this.state, i = 0, type;
+        for (i; i < columns.length; i++) {
+            let column = columns[i];
+            if (column.name == name) {
+                type = column.type;
+                break;
+            }
         }
+        return type;
     }
 
-    componentDidMount() {
+    /**
+     * 获得设定的过滤条件规则
+     */
+    getFilters = (callback) => {
+        const { form } = this.props;
+        form.validateFields(function(err, values) {
+            if(!err && callback&&(typeof callback == 'function')) {
+                callback(values.filters);
+            }
+        })
+    }
+
+    getFilterValueField = (key, type, operator, index) => {
+        let field = <Input />, { columns } = this.state;
+        const { form } = this.props;
+        const filters = form.getFieldValue('filters');
+        let filter = filters.filter((f) => {return f.key == key})[0];
+        let column = columns.filter((c) => {return c.name == filter.name});
+        column = column.length>0?column[0]:{selection:[]};
+        column.selection = column.selection || []
+        if(type == 'string') {
+            field = <Input onBlur={(e) => {this.changeFilterValue(filter, e.target.value, index)}}/>
+        }else if(type == 'number') {
+            field = <InputNumber onBlur={(e) => {this.changeFilterValue(filter, e.target.value, index)}}/>
+        }else if(type == 'time') {
+            field = <DatePicker onChange={(value) => {this.changeFilterValue(filter, value, index)}}/>
+        }else if(type == 'categorical') {
+            field = <Select 
+                mode={operator=='in'?'multiple':''}
+                onChange={(value) => {this.changeFilterValue(filter, value, index)}}
+            >
+                { column.selection.map((s, i) => {
+                    return <SelectOption key={i} value={s}>{s}</SelectOption>
+                }) }
+            </Select>
+        }
+
+        return field;
     }
 
     getFilterItems() {
-        const { columns } = this.state;
+        const { columns, filterData } = this.state;
         const { getFieldDecorator, getFieldValue } = this.props.form;
-        getFieldDecorator('keys', { initialValue: []});
-        const keys = getFieldValue('keys');
-        const filterItems = keys.map((k, index) => {
+        getFieldDecorator('filters', { initialValue: [] });
+        const filters = getFieldValue('filters');
+        const filterItems = filters.map((f, index) => {
+            let { key, name, type, operator, value1, value2 } = f;
             return (
-                <div key={`filterDiv[${k}]`}>
-                    <Row key={`filterRow[${k}]`}>
-                        <Col span={24}>
-                            <FormItem required={true} key={k}>
-                                {getFieldDecorator(`filterName[${k}]`, {
-                                    validateTrigger: ['onChange', 'onBlur'],
-                                    rules: [{
-                                        required: true,
-                                        whitespace: true,
-                                        message: "请选择过滤字段",
-                                    }],
-                                    initialValue: ''
+                <Row key={`filterDiv[${key}]`}>
+                    <Col span={22}>
+                        {}
+                        <Col span={6}>
+                            <FormItem key={key}>
+                                {getFieldDecorator(`filterName${key}`, {
+                                    initialValue: name?{key: name}:undefined
                                 })(
                                     <Select
                                         showSearch={true}
-                                        filterOption={function(inputValue, option){
-                                            let { value, children } = option.props;
-                                            return value.toLowerCase().indexOf(inputValue.toLowerCase()) != -1 || 
-                                                children.toLowerCase().indexOf(inputValue.toLowerCase()) != -1;
-                                        }}
-                                        onChange={(value) => {this.changeFilterName(value)}}
+                                        labelInValue={true}
+                                        filterOption={
+                                            function (inputValue, option) {
+                                                let { value, children } = option.props;
+                                                return value.toLowerCase().indexOf(inputValue.toLowerCase()) != -1 ||
+                                                    children.toLowerCase().indexOf(inputValue.toLowerCase()) != -1;
+                                            }
+                                        }
+                                        onChange={(value) => {this.changeFilterName(f, value)}}
                                     >
-                                        {columns.map((c, i) => { return (<SelectOption key={i} value={c.name}>{c.label}</SelectOption>) })}
-                                    </Select>
-                                )}
+                                        {columns.map((c, i) => {
+                                            return (
+                                                <SelectOption key={i} value={c.name}>{c.label}</SelectOption>
+                                            )
+                                        })}
+                                    </Select>)}
                             </FormItem>
                         </Col>
-                    </Row>
-                    <Row>
-                        <Col span={10}>
-                            <FormItem required={true}>
-                                {getFieldDecorator(`filterOperator[${k}]`, {
-                                    validateTrigger: ['onChange', 'onBlur'],
-                                    rules: [{
-                                        required: true,
-                                        whitespace: true,
-                                        message: "请选择过滤条件",
-                                    }],
-                                    initialValue: ''
+                        <Col span={6}>
+                            <FormItem key={key} className='filterOperator'>
+                                {getFieldDecorator(`filterOperator${key}`, {
+                                    initialValue: operator?{key: operator}:undefined
                                 })(
                                     <Select
-                                        onFocus={function(){debugger;}}
-                                        onChange={(value) => { }}
+                                        labelInValue={true}
+                                        onChange={(value) => {this.changeFilterOperator(f, value)}}
                                     >
-                                        { this.getFilterOperator }
+                                        {OPERATORS[type].map((o, i) => {
+                                            return <SelectOption key={i} value={o.value}>{o.label}</SelectOption>;
+                                        })}
                                     </Select>
                                 )}
                             </FormItem>
                         </Col>
-                        <Col span={12}>
-                            <FormItem required={true}>
-                                {getFieldDecorator(`filterValue[${k}]`, {
-                                    validateTrigger: ['onChange', 'onBlur'],
-                                    rules: [{
-                                        required: true,
-                                        whitespace: true,
-                                        message: "请输入条件值",
-                                    }],
-                                    initialValue: ''
-                                })(
-                                    <Input />
-                                )}
+                        <Col span={(operator&&operator!='null')?(operator=='between'?6:12):'0'}>
+                            <FormItem
+                                key={key}
+                                className='filterValueOne'
+                            >
+                                {getFieldDecorator(`filterValueOne${key}`, {
+                                    initialValue: value1
+                                })(this.getFilterValueField(key, type, operator, 1))}
                             </FormItem>
                         </Col>
-                        <Col span={2} className=''>
-                            <Icon
-                                className="dynamic-delete-button"
-                                type="close"
-                                onClick={() => { this.removeFilter(k) }}
-                            />
+                        <Col span={operator=='between'?6:0}>
+                            <FormItem
+                                key={key}
+                                className='filterValueTwo'
+                            >
+                                {getFieldDecorator(`filterValueTwo${key}`, {
+                                    initialValue: value2
+                                })(this.getFilterValueField(key, type, operator, 2))}
+                            </FormItem>
                         </Col>
-                    </Row>
-                </div>
+                    </Col>
+                    <Col span={2} className='' >
+                        <Icon
+                            className="dynamic-delete-button"
+                            type="close"
+                            onClick={() => { this.removeFilter(key) }}
+                        />
+                    </Col>
+                </Row>
             );
         });
 
@@ -162,13 +292,18 @@ class FilterBox extends React.Component {
 
     render() {
         return (
-            <Form>
-                { this.getFilterItems()}
+            <Form size='small'>
+                {this.getFilterItems()}
                 <Row>
                     <Col>
                         <FormItem>
-                            <Button type="dashed" onClick={this.addFilter.bind(this)} style={{ width: '100%' }}>
-                                <Icon type="plus" /> 添加
+                            <Button
+                                className='filter-add-button'
+                                type="dashed"
+                                onClick={() => {this.addFilter()}}
+                            >
+                                <Icon type="plus" />
+                                添加
                             </Button>
                         </FormItem>
                     </Col>

+ 18 - 13
app/component/chartDesigner/sections/filterBox.less

@@ -1,15 +1,20 @@
 .dynamic-delete-button {
-    cursor: pointer;
-    position: relative;
-    top: 10px;
-    font-size: 18px;
-    color: #999;
-    transition: all .3s;
+  cursor: pointer;
+  position: relative;
+  top: 10px;
+  font-size: 18px;
+  color: #999;
+  transition: all .3s;
+}
+.dynamic-delete-button:hover {
+  color: #777;
+}
+.dynamic-delete-button[disabled] {
+  cursor: not-allowed;
+  opacity: 0.5;
+}
+.filterValueOne,.filterValueTwo {
+  .ant-input-number, .ant-calendar-picker {
+    width: 100%;
   }
-  .dynamic-delete-button:hover {
-    color: #777;
-  }
-  .dynamic-delete-button[disabled] {
-    cursor: not-allowed;
-    opacity: 0.5;
-  }
+}

+ 85 - 0
app/component/chartDesigner/sections/filterOperators.json

@@ -0,0 +1,85 @@
+{
+    "string": [{
+        "value": "contain",
+        "label": "包含"
+    }, {
+        "value": "notContain",
+        "label": "不包含"
+    }, {
+        "value": "startsWith",
+        "label": "开头是"
+    }, {
+        "value": "endsWith",
+        "label": "结尾是"
+    }, {
+        "value": "equals",
+        "label": "等于"
+    }, {
+        "value": "notEquals",
+        "label": "不等于"
+    }, {
+        "value": "null",
+        "label": "为空"
+    }],
+    "number": [{
+        "value": ">",
+        "label": "大于"
+    }, {
+        "value": ">=",
+        "label": "大于等于"
+    }, {
+        "value": "=",
+        "label": "等于"
+    }, {
+        "value": "<=",
+        "label": "小于等于"
+    }, {
+        "value": "<",
+        "label": "小于"
+    }, {
+        "value": "<>",
+        "label": "不等于"
+    }, {
+        "value": "between",
+        "label": "介于"
+    }, {
+        "value": "null",
+        "label": "为空"
+    }],
+    "time": [{
+        "value": "equals",
+        "label": "等于"
+    }, {
+        "value": "from",
+        "label": "开始于"
+    }, {
+        "value": "to",
+        "label": "结束于"
+    }, {
+        "value": "between",
+        "label": "介于"
+    }, {
+        "value": "null",
+        "label": "为空"
+    }],
+    "categorical": [{
+        "value": "=",
+        "label": "等于"
+    }, {
+        "value": "in",
+        "label": "包含"
+    }, {
+        "value": "<>",
+        "label": "不等于"
+    }, {
+        "value": "noIn",
+        "label": "不包含"
+    }, {
+        "value": "null",
+        "label": "为空"
+    }],
+    "undefined": [{
+        "value": "null",
+        "label": "无"
+    }]
+}

+ 57 - 0
app/component/chartDesigner/sections/lineConfigForm.jsx

@@ -0,0 +1,57 @@
+import React from 'react';
+import { Form, Select } from 'antd';
+const FormItem = Form.Item;
+const { Option } = Select;
+
+class LineConfigForm extends React.Component {
+
+	constructor(props) {
+		super(props);
+		this.state = {
+		}
+	}
+
+	render() {
+        const columns = [
+			{ value: 'c1', label: '列1' },
+			{ value: 'c2', label: '列2' },
+			{ value: 'c3', label: '列3' },
+			{ value: 'c4', label: '列4' },
+			{ value: 'c5', label: '列5' },
+        ];
+        let targetColumn = ['c3'];
+		const { getFieldDecorator, getFieldsError, getFieldError, isFieldTouched } = this.props.form;
+		const formItemLayout = {
+			labelCol: { span: 10 },
+			wrapperCol: { span: 14 },
+        };
+        
+		return (
+            <Form layout='horizontal'>
+				<FormItem label='横轴' {...formItemLayout}>
+					{getFieldDecorator('xAxis', {
+						initialValue : targetColumn
+					})(
+						<Select mode='multiple'>
+							{columns.map((c, i)=>{
+								return (<Option key={`xaxis-${i}`} value={c.value}>{c.label}</Option>)
+							})}
+						</Select>
+					)}
+				</FormItem>
+                <FormItem label='纵轴' {...formItemLayout}>
+					{getFieldDecorator('yAxis', {
+						initialValue : targetColumn
+					})(
+						<Select mode='multiple'>
+							{columns.map((c, i)=>{
+								return (<Option key={`yaxis-${i}`} value={c.value}>{c.label}</Option>)
+							})}
+						</Select>
+					)}
+				</FormItem>
+			</Form>
+        );
+	}
+}
+export default Form.create()(LineConfigForm);

+ 24 - 0
app/component/chartDesigner/sections/styleEditor.jsx

@@ -0,0 +1,24 @@
+import React from 'react';
+import { Form, Input } from 'antd';
+const FormItem = Form.Item;
+
+class StyleManager extends React.Component {
+    constructor(props) {
+        super(props);
+        this.state = {};
+    }
+
+    render() {
+        const { getFieldDecorator } = this.props.form;
+        return (
+            <Form>
+                <FormItem label='图例'>
+                    {getFieldDecorator('legend')(
+                        <Input />
+                    )}
+                </FormItem>
+            </Form>
+        );
+    }
+}
+export default Form.create()(StyleManager);

+ 49 - 0
app/component/chartDesigner/sections/toolBar.less

@@ -0,0 +1,49 @@
+.filterbar-tag-close {
+    margin-left: 10px;
+}
+.content-header {
+    height: auto;
+    .toolbar {
+        display: flex;
+        justify-content: space-between;
+        height: auto;
+        line-height: 100%;
+        margin-bottom: 6px;
+        .filters {
+            .filter-tag {
+                border-style: dashed;
+                margin: 6px 2px 0 2px;
+            }
+            .filter-tag-using {
+                border-style: solid;
+                border-color: #1890FF;
+                background-color: #1890ff;
+                color: white;
+                i {
+                    color: white;
+                }
+            }
+            .filter-tag-add {
+                background-color: white;
+                border-style: solid;
+            }
+        }
+        .tools {
+            .tools-button {
+                margin-top: 6px;
+            }
+        }
+    }
+}
+.filter-box {
+    max-height: 80vh;
+    width: 50%;
+    .ant-modal-content {
+        max-height: 100vh;
+        .ant-modal-body {
+            padding: 6px;
+            max-height: 60vh;
+            overflow: auto;
+        }
+    }
+}

+ 146 - 0
app/component/chartDesigner/sections/toolbar.jsx

@@ -0,0 +1,146 @@
+import React from 'react';
+import { Tag, Icon, Modal, Button } from 'antd';
+import FilterBox from './filterBox';
+import emitter from '../../../eventManger/ev';
+import './toolbar.less';
+
+class Toolar extends React.Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+            filters: props.filters || [],
+            showFilterBox: false
+        }
+    }
+
+    filterUsingChange = (e) => {
+        const key = e.target.dataset.key;
+        const { filters } = this.state;
+        let nextFilters = filters.map(f => {
+            if(f.key == key) {
+                f.using = !f.using
+            }
+            return f;
+        });
+        this.setState({ filters: nextFilters });
+    }
+
+    showFilterBox = (e) => {
+        this.setState({
+            showFilterBox: true
+        });
+    }
+
+    hideFilterBox = (e) => {
+        this.setState({
+            showFilterBox: false,
+        });
+    }
+
+    /**
+     * 生成过滤条件
+     */
+    createFilters = () => {
+        emitter.emit('getFilters', function(filters) {
+            this.setState({
+                filters: filters
+            }, ()=>{console.log(this.state.filters)});
+            this.hideFilterBox()
+        }.bind(this));
+    }
+
+    /**
+     * 生成过滤规则文本
+     */
+    createFilterLabel = (filter) => {
+        let { label, name, operator, operatorLabel, type, using, value1, value2 } = filter;
+        let filterLabel;
+        if(type == 'string') {
+            if(operator == 'null') {
+                filterLabel = `${label} ${operatorLabel}`;
+            }else {
+                filterLabel = `${label} ${operatorLabel} ${value1}`;
+            }
+        }else if(type == 'number') {
+            if(operator == 'null') {
+                filterLabel = `${label} ${operatorLabel}`;
+            }else if(operator == 'betwen') {
+                filterLabel = `${label} ${operatorLabel} ${value1} ~ ${value2}`; 
+            }else {
+                filterLabel = `${label} ${operatorLabel} ${value1}`; 
+            }
+        }else if(type == 'time') {
+            value1 = new Date(value1).format('yyyy/MM/dd');
+            value2 = new Date(value2).format('yyyy/MM/dd');
+            if(operator == 'null') {
+                filterLabel = `${label} ${operatorLabel}`;
+            }else if(operator == 'betwen') {
+                filterLabel = `${label} ${operatorLabel} ${value1} ~ ${value2}`; 
+            }else {
+                filterLabel = `${label} ${operatorLabel} ${value1}`; 
+            }
+        }else if(type == 'categorical') {
+            if(operator == 'null') {
+                filterLabel = `${label} ${operatorLabel}`;
+            }else {
+                filterLabel = `${label} ${operatorLabel} ${value1}`; 
+            }
+        }else {
+            filterLabel = '错误条件';
+        }
+        return filterLabel;
+    }
+
+    render() {
+        const { filters, showFilterBox } = this.state;
+
+        let tags = filters.map((f, i)=>{
+            return {
+                key: f.key,
+                label: this.createFilterLabel(f),
+                using: f.using
+            }
+        });
+
+        return (
+            <div className='toolbar'>
+                <div className='filters'>
+                    <h6 style={{ marginRight: 8, display: 'inline' }}>筛选:</h6>
+                    {tags.map(tag => (
+                        <Tag
+                            className={`filter-tag ${tag.using?'filter-tag-using':''}`}
+                            key={tag.key}
+                            closable={false}
+                            onClick={this.filterUsingChange}
+                            data-key={tag.key}
+                        >
+                            {tag.label}
+                        </Tag>
+                    ))}
+                    <Tag
+                        onClick={this.showFilterBox}
+                        className={`filter-tag filter-tag-add`}
+                    >
+                        <Icon type="bars" />
+                        筛选条件管理
+                    </Tag>
+                </div>
+                <div className='tools'>
+                    <Button className='tools-button' size='small'>按钮</Button>
+                </div>
+                <Modal 
+                    className='filter-box'
+                    title="筛选条件管理"
+                    visible={showFilterBox}
+                    onOk={this.createFilters}
+                    onCancel={this.hideFilterBox}
+                    maskClosable={false}
+                    destroyOnClose={true}
+                >
+                    <FilterBox filterData={filters}/>  
+                </Modal>
+            </div>
+        );
+    }
+}
+export default Toolar;

+ 18 - 1
app/utils/baseUtils.js

@@ -108,5 +108,22 @@ function hashcode(obj) {
     return hash;
 }
 
+export { remove, isEqual, isEmptyObject, getUrlParam, hashcode };
 
-export { remove, isEqual, isEmptyObject, getUrlParam, hashcode };
+Date.prototype.format = function (fmt) {
+    var o = {
+        "M+": this.getMonth() + 1, //月份   
+        "d+": this.getDate(), //日   
+        "h+": this.getHours(), //时   
+        "m+": this.getMinutes(), //分   
+        "s+": this.getSeconds(), //秒   
+        "q+": Math.floor((this.getMonth() + 3) / 3), //季度   
+        "S": this.getMilliseconds() //毫秒   
+    };
+    if (/(y+)/.test(fmt))
+        fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
+    for (var k in o)
+        if (new RegExp("(" + k + ")").test(fmt))
+            fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
+    return fmt;
+}

+ 6 - 3
main.js

@@ -17,6 +17,7 @@ import {
 import createHistory from 'history/createHashHistory';
 import App from './app/component/chartDesigner/layout';
 const history = createHistory();
+import './app/utils/baseUtils';
 import './main.less';
 
 moment.locale('zh-cn');
@@ -158,7 +159,9 @@ const PlayerAPI = {
 //   )
   
 ReactDOM.render((
-	<HashRouter history={history}>
-		<App />
-	</HashRouter>
+	<LocaleProvider locale={zhCN}>
+		<HashRouter history={history}>
+			<App />
+		</HashRouter>
+	</LocaleProvider>
 ), document.getElementById('root'))

+ 0 - 1
main.less

@@ -1,4 +1,3 @@
-@import "~antd/dist/antd.less";
 @import "custom.less";
 
 .ant-collapse {