Browse Source

报表收藏功能/图表、报表筛选组件逻辑调整

zhuth 6 years ago
parent
commit
1c0e484486

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

@@ -124,7 +124,7 @@ class ChartDesignerContent extends React.Component {
                 <Content className='view-content' >
                     <Layout>
                         <Header className='content-header'>
-                            <ToolBar className='header-toolbar' autoRefresh={autoRefresh} />
+                            <ToolBar className='header-toolbar' autoRefresh={autoRefresh} isOwner={isOwner}/>
                         </Header>
                         <Content className='content-body' ref='contentEl' >
                             { chartView }

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

@@ -4,6 +4,7 @@ import FilterBox from '../../common/filterBox/filterBox'
 import { connect } from 'dva'
 import moment from 'moment'
 import DataPreview from '../../common/dataPreview/dataPreview'
+import Filter from '../../common/filterBox/filter'
 import './toolbar.less'
 
 class Toolbar extends React.Component {
@@ -27,6 +28,17 @@ class Toolbar extends React.Component {
         }), autoRefresh });
     }
 
+    changeFilterValue = (filter) => {
+        const { chartDesigner, dispatch, autoRefresh } = this.props;
+        const filters = chartDesigner.filters;
+        dispatch({ type: 'chartDesigner/changeField', name: 'filters', value: filters.map( f => {
+            if(+f.key === +filter.key) {
+                f = { ...filter }
+            }
+            return f;
+        }), autoRefresh });
+    }
+
     showFilterBox = (e) => {
         this.setState({
             visibleFilterBox: true
@@ -90,39 +102,27 @@ class Toolbar extends React.Component {
     }
 
     render() {
-        const { chartDesigner, dispatch } = this.props;
+        const { chartDesigner, dispatch, isOwner } = this.props;
         const { code, filters, columns } = chartDesigner;
         const { visibleFilterBox, visibleDataPreviewBox } = this.state;
 
-        let tags = filters.map((f, i)=>{
-            return {
-                key: f.key,
-                label: this.createFilterLabel(f),
-                using: f.type ? f.using : false
-            }
-        });
-
         return (
             <div className='toolbar'>
                 <div className='filters'>
-                    <div style={{ margin: '10px 8px 0 10px' }}>筛选:</div>
-                    {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
+                    {isOwner && <Tag
                         onClick={this.showFilterBox}
                         className={`filter-tag filter-tag-add`}
                     >
-                        <Icon type="filter" />
-                    </Tag>
+                        <Icon type="filter" theme="outlined" />
+                    </Tag>}
+                    {filters.map(f => (
+                        <Filter
+                            key={f.key}
+                            filter={f}
+                            code={code}
+                            changeFilterValue={this.changeFilterValue}
+                        />
+                    ))}
                 </div>
                 <div className='tools'>
                     <Button className='tools-button' size='small' onClick={() => {
@@ -132,7 +132,7 @@ class Toolbar extends React.Component {
                         })
                     }}>查看列</Button>
                 </div>
-                {visibleFilterBox && <FilterBox type='chart' code={code} columns={columns} filterData={filters} visibleFilterBox={visibleFilterBox} showFilterBox={this.showFilterBox} hideFilterBox={this.hideFilterBox} createFilters={this.createFilters} />}
+                {visibleFilterBox && <FilterBox code={code} columns={columns} filterData={filters} visibleFilterBox={visibleFilterBox} showFilterBox={this.showFilterBox} hideFilterBox={this.hideFilterBox} createFilters={this.createFilters} />}
                 {visibleDataPreviewBox && <DataPreview
                     visibleBox={visibleDataPreviewBox}
                     hideBox={() => {

+ 4 - 2
src/components/chartDesigner/sections/toolbar.less

@@ -8,10 +8,11 @@
         display: flex;
         justify-content: space-between;
         height: auto;
-        line-height: 100%;
-        margin-bottom: 6px;
         .filters {
             display: flex;
+            width: 100%;
+            flex-wrap: wrap;
+            padding-left: 8px;
             .filter-tag {
                 max-width: 400px;
                 overflow: hidden;
@@ -30,6 +31,7 @@
                 }
             }
             .filter-tag-add {
+                height: 24px;
                 background-color: white;
                 border-style: solid;
             }

+ 193 - 0
src/components/common/filterBox/filter.jsx

@@ -0,0 +1,193 @@
+import React from 'react'
+import { Form, Input, InputNumber, DatePicker, Select, Spin, Divider, Icon } from 'antd'
+import * as service from '../../../services/index'
+import URLS from '../../../constants/url'
+import moment from 'moment'
+import './filter.less'
+const { Option: SelectOption } = Select
+const FormItem = Form.Item
+
+class Filter extends React.Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+            columns: props.columns || [],
+            fetching: false,
+            columnData: [],
+            dropdownOpen: false, // 多选框下拉展开控制
+        }
+    }
+
+    generatFilter = () => {
+        const { filter } = this.props;
+        const { label, operator, operatorLabel } = filter;
+        
+        return <div className='filter'>
+            <span className='filter-name'>{label + ' ' + operatorLabel + ' : '}</span>
+            <FormItem className='filter-value filter-value1'>
+                { this.generateValueItem(filter, 1) }
+            </FormItem>
+            { operator === 'between' && <FormItem className='filter-value filter-value2'>
+                { this.generateValueItem(filter, 2) }
+            </FormItem> }
+        </div>
+    }
+
+    generateValueItem = (filter, index) => {
+        const { columns, fetching, columnData, dropdownOpen } = this.state;
+        let field;
+        const { key, type, operator } = filter;
+        let defaultValue = type==='time' ? ( filter['value' + index] ? moment(filter['value' + index]) : null) : filter['value' + index]
+        const commonProps = { defaultValue }
+
+        if(['index', 'string'].indexOf(type) !== -1) {
+            field = <Input { ...commonProps } onBlur={(e) => {
+                if(e.target.value !== commonProps.defaultValue) {
+                    this.changeFilterValue(filter, e.target.value, index)
+                }
+            }}/>
+        }else if(['scale', 'ordinal'].indexOf(type) !== -1) {
+            field = <InputNumber { ...commonProps } onBlur={(e) => {
+                if(e.target.value !== commonProps.defaultValue) {
+                    this.changeFilterValue(filter, e.target.value, index)
+                }
+            }}/>
+        }else if(type === 'time') {
+            field = <DatePicker { ...commonProps } onChange={(value) => {this.changeFilterValue(filter, value, index)}}/>
+        }else if(type === 'categorical') { // 类别
+            if(operator === 'include' || operator==='notInclude') { // 包含/不包含
+                field = <Input { ...commonProps } onBlur={(e) => {
+                    if(e.target.value !== commonProps.defaultValue) {
+                        this.changeFilterValue(filter, e.target.value, index)
+                    }
+                }}/>
+            }else if(operator === 'contain' || operator === 'notContain') { // 包括/不包括
+                field = (<Select
+                    value={commonProps.defaultValue} // 使用defaultValue不能及时响应全选/取消全选的操作
+                    open={dropdownOpen}
+                    onDropdownVisibleChange={this.onDropdownVisibleChange}
+                    allowClear
+                    mode='multiple'
+                    maxTagCount={5}
+                    showSearch
+                    dropdownMatchSelectWidth={false}
+                    notFoundContent={fetching ? <Spin size="small" /> : '无'}
+                    onSearch={(value) => {this.fetchColumnData(filter, { keyword: value, mandatory: true })}}
+                    onFocus={() => {this.fetchColumnData(filter)}}
+                    onChange={(value) => {this.changeFilterValue(filter, value, index)}}
+                    dropdownRender={menu => (
+                        <div>
+                            {menu}
+                            {columnData.length > 0 && <Divider style={{ margin: '4px 0' }} />}
+                            {columnData.length > 0 && <div style={{ padding: '8px', cursor: 'pointer' }}>
+                                {/** https://github.com/ant-design/ant-design/issues/13448 */}
+                                <span onMouseDown={this.lockClose} onMouseUp={this.lockClose} onClick={() => {
+                                    if(commonProps.defaultValue && commonProps.defaultValue.length === columnData.length) {
+                                        this.changeFilterValue(filter, [], index)
+                                    }else {
+                                        this.changeFilterValue(filter, columnData, index)
+                                    }
+                                    this.setState({ dropdownOpen: false });
+                                }}>    
+                                    <Icon type={ commonProps.defaultValue && commonProps.defaultValue.length === columnData.length ? 'check-square' : 'border' } /> { commonProps.defaultValue && commonProps.defaultValue.length === columnData.length ? '取消全选' : '全选' }
+                                </span>
+                            </div>}
+                        </div>
+                    )}
+                >
+                    { columnData.map((s, i) => {
+                        return <SelectOption key={i} value={s}>{s}</SelectOption>
+                    }) }
+                </Select>)
+            }else { // 等于/不等于
+                field = (<Select
+                    { ...commonProps }
+                    allowClear
+                    mode='single'
+                    showSearch
+                    dropdownMatchSelectWidth={false}
+                    notFoundContent={fetching ? <Spin size="small" /> : '无'}
+                    onSearch={(value) => {this.fetchColumnData(filter, { keyword: value, mandatory: true })}}
+                    onFocus={() => {this.fetchColumnData(filter)}}
+                    onChange={(value) => {this.changeFilterValue(filter, value, index)}}
+                >
+                    { columnData.map((s, i) => {
+                        return <SelectOption key={i} value={s}>{s}</SelectOption>
+                    }) }
+                </Select>)
+            }
+        }else {
+            field = <Input onBlur={(e) => {
+                if(e.target.value !== commonProps.defaultValue) {
+                    this.changeFilterValue(filter, e.target.value, index)
+                }
+            }}/> 
+        }
+
+        return field;
+    }
+
+    lockClose = e => {
+        clearTimeout(this.lock);
+        this.lock = setTimeout(() => {
+          this.lock = null;
+        }, 100);
+    };
+
+    onDropdownVisibleChange = open => {
+        if (this.lock) return;
+        this.setState({ dropdownOpen: open });
+    };
+
+    changeFilterValue = (filter, value, index) => {
+        const { changeFilterValue: propsChangeFilterValue } = this.props;
+        filter['value' + index] = value;
+        if(propsChangeFilterValue && typeof propsChangeFilterValue === 'function') {
+            propsChangeFilterValue({ ...filter });
+        }
+    }
+
+    fetchColumnData = (filter, options) => {
+        const { keyword, mandatory } = options || {};
+        const { code } = this.props;
+        const { columnData } = this.state;
+        if(!columnData || columnData.length === 0 || mandatory) {
+            this.setState({ columnData: [], fetching: true }, () => {
+                const body = {
+                    id: code,
+                    columnName: filter.name,
+                    keyword, 
+                };
+                service.fetch({
+                    url: URLS.CHART_QUERY_COLUMNDATA,
+                    allow: true,
+                    body: body,
+                }).then(r => {
+                    if(!r.err && r.data.code > 0) {
+                        return r;
+                    }else {
+                        let obj = {};
+                        throw obj;
+                    }
+                }).then(r => {
+                    const resData = r.data.data || [];
+                    this.setState({
+                        columnData: resData.map(d => d || 'null'),
+                        fetching: false
+                    });
+                }).catch(ex => {
+                    this.setState({
+                        columnData: [],
+                        fetching: false
+                    });
+                    console.error('fetch error', ex);
+                });
+            });
+        }
+    }
+
+    render() {
+        return this.generatFilter()
+    }
+}
+export default Filter

+ 12 - 0
src/components/common/filterBox/filter.less

@@ -0,0 +1,12 @@
+.filter {
+    display: flex;
+    .filter-name {
+        margin: 0 8px;
+    }
+    .filter-value {
+        flex: 1;
+        .ant-select-selection__rendered, .ant-select-selection {
+            min-width: 100px;
+        }
+    }
+}

+ 226 - 0
src/components/common/filterBox/filter2.jsx

@@ -0,0 +1,226 @@
+import React from 'react'
+import { Form, Input, InputNumber, DatePicker, Select, Spin, Divider, Icon } from 'antd'
+import * as service from '../../../services/index'
+import URLS from '../../../constants/url'
+import moment from 'moment'
+import './filter.less'
+const { Option: SelectOption } = Select
+const FormItem = Form.Item
+
+class Filter extends React.Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+            fetching: false,
+            columnData: [],
+            dropdownOpen: false,
+        }
+    }
+
+    generatFilter = () => {
+        const { filter } = this.props;
+        const { label, operator, operatorLabel } = filter;
+        
+        return <div className='filter'>
+            <span className='filter-name'>{label + ' ' + operatorLabel + ' : '}</span>
+            <FormItem className='filter-value filter-value1'>
+                { this.generateValueItem(filter, 1) }
+            </FormItem>
+            { operator === 'between' && <FormItem className='filter-value filter-value2'>
+                { this.generateValueItem(filter, 2) }
+            </FormItem> }
+        </div>
+    }
+
+    generateValueItem = (filter, index) => {
+        const { fetching, columnData, dropdownOpen } = this.state;
+        let field;
+        let { key, type, operator } = filter;
+
+        let defaultValue = type==='time' ? ( filter['value' + index] ? moment(filter['value' + index]) : null) : filter['value' + index]
+        const commonProps = { defaultValue }
+
+        if(['index', 'string'].indexOf(type) !== -1) {
+            field = <Input { ...commonProps } onBlur={(e) => {
+                if(e.target.value !== commonProps.defaultValue) {
+                    this.changeFilterValue(filter, e.target.value, index)
+                }
+            }}/>
+        }else if(['scale', 'ordinal'].indexOf(type) !== -1) {
+            field = <InputNumber { ...commonProps } onBlur={(e) => {
+                if(e.target.value !== commonProps.defaultValue) {
+                    this.changeFilterValue(filter, e.target.value, index)
+                }
+            }}/>
+        }else if(type === 'time') {
+            field = <DatePicker { ...commonProps } onChange={(value) => {this.changeFilterValue(filter, value, index)}}/>
+        }else if(type === 'categorical') { // 类别
+            if(operator === 'include' || operator==='notInclude') { // 包含/不包含
+                field = <Input { ...commonProps } onBlur={(e) => {
+                    this.changeFilterValue(filter, e.target.value, index)
+                }}/>
+            }else if(operator === 'contain' || operator === 'notContain') { // 包括/不包括
+                field = (<Select
+                    key={key}
+                    value={commonProps.defaultValue} // 使用defaultValue不能及时响应全选/取消全选的操作
+                    allowClear
+                    mode='multiple'
+                    maxTagCount={5}
+                    showSearch
+                    open={dropdownOpen}
+                    onDropdownVisibleChange={this.onDropdownVisibleChange}
+                    dropdownMatchSelectWidth={false}
+                    notFoundContent={fetching ? <Spin size="small" /> : '无'}
+                    onSearch={(value) => {this.fetchColumnData(filter, { keyword: value, mandatory: true })}}
+                    onFocus={() => {this.fetchColumnData(filter)}}
+                    onChange={(value) => {this.changeFilterValue(filter, value, index)}}
+                    dropdownRender={menu => (
+                        <div>
+                            {menu}
+                            {columnData.length > 0 && <Divider style={{ margin: '4px 0' }} />}
+                            {columnData.length > 0 && <div style={{ padding: '8px', cursor: 'pointer' }}>
+                                {/** https://github.com/ant-design/ant-design/issues/13448 */}
+                                <span onMouseDown={this.lockClose} onMouseUp={this.lockClose} onClick={() => {
+                                    if(commonProps.defaultValue && commonProps.defaultValue.length === columnData.length) {
+                                        this.changeFilterValue(filter, [], index)
+                                    }else {
+                                        this.changeFilterValue(filter, columnData, index)
+                                    }
+                                    this.setState({ dropdownOpen: false });
+                                }}>    
+                                    <Icon type={ commonProps.defaultValue && commonProps.defaultValue.length === columnData.length ? 'check-square' : 'border' } /> { commonProps.defaultValue && commonProps.defaultValue.length === columnData.length ? '取消全选' : '全选' }
+                                </span>
+                            </div>}
+                        </div>
+                    )}
+                >
+                    { columnData.map(s => {
+                        return <SelectOption key={s} value={s}>{s}</SelectOption>
+                    }) }
+                </Select>)
+            }else { // 等于/不等于
+                field = (<Select
+                    { ...commonProps }
+                    allowClear
+                    mode='single'
+                    showSearch
+                    dropdownMatchSelectWidth={false}
+                    notFoundContent={fetching ? <Spin size="small" /> : '无'}
+                    onSearch={(value) => {this.fetchColumnData(filter, { keyword: value, mandatory: true })}}
+                    onFocus={() => {this.fetchColumnData(filter)}}
+                    onChange={(value) => {this.changeFilterValue(filter, value, index)}}
+                >
+                    { columnData.map((s, i) => {
+                        return <SelectOption key={i} value={s}>{s}</SelectOption>
+                    }) }
+                </Select>)
+            }
+        }else {
+            field = <Input { ...commonProps } onBlur={(e) => {
+                if(e.target.value !== commonProps.defaultValue) {
+                    this.changeFilterValue(filter, e.target.value, index)
+                }
+            }}/> 
+        }
+
+        return field;
+    }
+
+    lockClose = e => {
+        clearTimeout(this.lock);
+        this.lock = setTimeout(() => {
+          this.lock = null;
+        }, 100);
+    };
+
+    onDropdownVisibleChange = open => {
+        if (this.lock) return;
+        this.setState({ dropdownOpen: open });
+    };
+
+    changeFilterValue = (filter, value, index) => {
+        const { changeFilterValue: propsChangeFilterValue } = this.props;
+        filter['value' + index] = value;
+        if(propsChangeFilterValue && typeof propsChangeFilterValue === 'function') {
+            propsChangeFilterValue({ ...filter });
+        }
+    }
+
+    fetchColumnData = (filter, options) => {
+        const { columnData } = this.state;
+        const { keyword, mandatory } = options || {};
+        const { dataSource, value1 } = filter;
+        const isCusMode = dataSource.name === 'cus';
+        let column;
+        if(isCusMode) {
+            column = dataSource.columns.find(c => c.name === filter.name)
+        }
+        if(!columnData || columnData.length === 0 || mandatory) {
+            this.setState({ columnData: [], fetching: true }, () => {
+                const body = isCusMode ? column.relations.map(r => ({
+                    id: r.dataSource.code,
+                    columnName: r.column.name,
+                    keyword,
+                })) : {
+                    id: dataSource.name,
+                    columnName: filter.name,
+                    keyword, 
+                };
+                service.fetch({
+                    url: isCusMode ? URLS.DATASOURCE_QUERY_COLUMNDATA_MUL : URLS.DATASOURCE_QUERY_COLUMNDATA,
+                    allow: true,
+                    body: body,
+                }).then(r => {
+                    if(!r.err && r.data.code > 0) {
+                        return r;
+                    }else {
+                        let obj = {};
+                        throw obj;
+                    }
+                }).then(r => {
+                    const resData = r.data.data || [];
+                    // 需要把已经选择的值作为选项展示出来
+                    if(value1) {
+                        if(value1 instanceof Array) { // 如果value1是数组
+                            value1.forEach(v => {
+                                let idx = resData.findIndex(r => r === v);
+                                if(idx > -1) {
+                                    resData.splice(idx, 1);
+                                }
+                            });
+                            this.setState({
+                                columnData: value1.concat(resData),
+                                fetching: false
+                            });
+                        }else {
+                            let idx = resData.findIndex(r => r === value1);
+                            if(idx > -1) {
+                                resData.splice(idx, 1);
+                            }
+                            this.setState({
+                                columnData: [value1].concat(resData),
+                                fetching: false
+                            });
+                        }
+                    }else {
+                        this.setState({
+                            columnData: resData,
+                            fetching: false
+                        });
+                    }
+                }).catch(ex => {
+                    this.setState({
+                        columnData: [],
+                        fetching: false
+                    });
+                    console.error('fetch error', ex);
+                });
+            });
+        }
+    }
+
+    render() {
+        return this.generatFilter()
+    }
+}
+export default Filter

+ 79 - 31
src/components/common/filterBox/filterBox.jsx

@@ -1,11 +1,11 @@
 import React from 'react'
 import PropTypes from 'prop-types'
-import { Modal, Form, Row, Col, Input, Icon, Button, Select, InputNumber, DatePicker, Spin } from 'antd'
+import { Modal, Form, Row, Col, Input, Icon, Button, Select, InputNumber, DatePicker, Spin, Divider } from 'antd'
 import OPERATORS from './filterOperators.js';
 import './filterBox.less'
 import * as service from '../../../services/index'
 import URLS from '../../../constants/url'
-import moment from 'moment';
+import moment from 'moment'
 const FormItem = Form.Item
 const SelectOption = Select.Option
 const OptionGroup = Select.OptGroup 
@@ -20,7 +20,6 @@ class FilterBox extends React.Component {
             columns: props.columns || [],
             filterData: props.filterData || [],
             fetching: false, // 请求列数据状态
-            columnData: [], // 列数据
         }
     }
 
@@ -81,7 +80,9 @@ class FilterBox extends React.Component {
         const { form } = this.props;
         const filters = form.getFieldValue('filters');
 
-        this.setState({ columnData: [] });
+        let obj ={};
+        obj['columnData-' + filter.name] = [];
+        this.setState(obj);
         const type = this.getFilterType(value.key);
         form.setFieldsValue({
             filters: filters.map((f) => {
@@ -109,7 +110,7 @@ class FilterBox extends React.Component {
         form.setFieldsValue({
             filters: filters.map((f) => {
                 if (f.key === filter.key) {
-                    f.key = uuid++;
+                    // f.key = uuid++;
                     f.operator = value.key;
                     f.operatorLabel = value.label;
                     f.value1 = f.value2 = (type === 'time' ? moment() : undefined);
@@ -129,7 +130,7 @@ class FilterBox extends React.Component {
         form.setFieldsValue({
             filters: filters.map((f) => {
                 if (f.key === filter.key) {
-                    f.key = uuid++;
+                    // f.key = uuid++;
                     f[`value${index}`] = value;
                 }
                 return f;
@@ -171,16 +172,18 @@ class FilterBox extends React.Component {
 
     fetchColumnData = (column, keyword, mandatory) => {
         const { type, code } = this.props;
-        const { columnData } = this.state;
+        const columnData = this.state['columnData-' + column.name];
         if(!columnData || columnData.length === 0 || mandatory) {
-            this.setState({ columnData: [], fetching: true }, () => {
+            let obj = {fetching: true};
+            obj['columnData-' + column.name] = []
+            this.setState(obj, () => {
                 const body = {
                     id: code,
                     columnName: column.name,
                     keyword, 
                 };
                 service.fetch({
-                    url: type === 'chart' ? URLS.CHART_QUERY_COLUMNDATA : URLS.DATASOURCE_QUERY_COLUMNDATA,
+                    url: URLS.CHART_QUERY_COLUMNDATA,
                     allow: true,
                     body: body,
                 }).then(r => {
@@ -192,15 +195,13 @@ class FilterBox extends React.Component {
                     }
                 }).then(r => {
                     const resData = r.data.data || [];
-                    this.setState({
-                        columnData: resData.map(d => d || 'null'),
-                        fetching: false
-                    });
+                    let obj = {fetching: false}
+                    obj['columnData-' + column.name] = resData.map(d => d || 'null')
+                    this.setState(obj);
                 }).catch(ex => {
-                    this.setState({
-                        columnData: [],
-                        fetching: false
-                    });
+                    let obj = {fetching: false};
+                    obj['columnData-' + column.name] = []
+                    this.setState(obj);
                     console.error('fetch error', ex);
                 });
             });
@@ -208,7 +209,8 @@ class FilterBox extends React.Component {
     }
 
     getFilterValueField = (key, type, operator, index) => {
-        const { columns, fetching, columnData } = this.state;
+        const { columns, fetching } = this.state;
+        
         let field;
         const { form } = this.props;
         const filters = form.getFieldValue('filters');
@@ -216,31 +218,65 @@ class FilterBox extends React.Component {
         let column = columns.filter((c) => {return c.name === filter.name});
         column = column.length > 0 ? column[0] : { selection: [] };
         column.selection = column.selection || [];
+
+        let dropdownOpen = this.state['dropdownOpen-' + filter.key] || false;
+        let columnData = this.state['columnData-' + filter.name] || [];
+
+        const commonProps = { placeholder: '无默认值' }
         if(['index', 'string'].indexOf(type) !== -1) {
-            field = <Input onBlur={(e) => {this.changeFilterValue(filter, e.target.value, index)}}/>
+            field = <Input { ...commonProps } onBlur={(e) => {this.changeFilterValue(filter, e.target.value, index)}}/>
         }else if(['scale', 'ordinal'].indexOf(type) !== -1) {
-            field = <InputNumber onBlur={(e) => {this.changeFilterValue(filter, e.target.value, index)}}/>
+            field = <InputNumber { ...commonProps } onBlur={(e) => {this.changeFilterValue(filter, e.target.value, index)}}/>
         }else if(type === 'time') {
-            field = <DatePicker onChange={(value) => {this.changeFilterValue(filter, value, index)}}/>
+            field = <DatePicker { ...commonProps } onChange={(value) => {this.changeFilterValue(filter, value, index)}}/>
         }else if(type === 'categorical') { // 类别
             if(operator === 'include' || operator==='notInclude') { // 包含/不包含
-                field = <Input onBlur={(e) => {this.changeFilterValue(filter, e.target.value, index)}}/>
+                field = <Input { ...commonProps } onBlur={(e) => {this.changeFilterValue(filter, e.target.value, index)}}/>
             }else if(operator === 'contain' || operator === 'notContain') { // 包括/不包括
-                field = (<Select 
+                field = (<Select
+                    { ...commonProps }
+                    allowClear
                     mode='multiple'
                     showSearch
+                    open={dropdownOpen}
+                    onDropdownVisibleChange={open => {
+                        this.onDropdownVisibleChange(filter, open);
+                    }}
                     dropdownMatchSelectWidth={false}
                     notFoundContent={fetching ? <Spin size="small" /> : '无'}
                     onSearch={(value) => {this.fetchColumnData(column, value, true)}}
                     onFocus={() => {this.fetchColumnData(column)}}
                     onChange={(value) => {this.changeFilterValue(filter, value, index)}}
+                    dropdownRender={menu => (
+                        <div>
+                            {menu}
+                            {columnData.length > 0 && <Divider style={{ margin: '4px 0' }} />}
+                            {columnData.length > 0 && <div style={{ padding: '8px', cursor: 'pointer' }}>
+                                {/** https://github.com/ant-design/ant-design/issues/13448 */}
+                                <span onMouseDown={this.lockClose} onMouseUp={this.lockClose} onClick={() => {
+                                    if(commonProps.defaultValue && commonProps.defaultValue.length === columnData.length) {
+                                        this.changeFilterValue(filter, [], index)
+                                    }else {
+                                        this.changeFilterValue(filter, columnData, index)
+                                    }
+                                    let obj = {};
+                                    obj['dropdownOpen-' + filter.key] = false;
+                                    this.setState(obj);
+                                }}>    
+                                    <Icon type={ commonProps.defaultValue && commonProps.defaultValue.length === columnData.length ? 'check-square' : 'border' } /> { commonProps.defaultValue && commonProps.defaultValue.length === columnData.length ? '取消全选' : '全选' }
+                                </span>
+                            </div>}
+                        </div>
+                    )}
                 >
                     { columnData.map((s, i) => {
                         return <SelectOption key={i} value={s}>{s}</SelectOption>
                     }) }
                 </Select>)
             }else { // 等于/不等于
-                field = (<Select 
+                field = (<Select
+                    { ...commonProps }
+                    allowClear
                     mode='single'
                     showSearch
                     dropdownMatchSelectWidth={false}
@@ -255,12 +291,26 @@ class FilterBox extends React.Component {
                 </Select>)
             }
         }else {
-            field = <Input onBlur={(e) => {this.changeFilterValue(filter, e.target.value, index)}}/> 
+            field = <Input { ...commonProps } onBlur={(e) => {this.changeFilterValue(filter, e.target.value, index)}}/> 
         }
 
         return field;
     }
 
+    lockClose = e => {
+        clearTimeout(this.lock);
+        this.lock = setTimeout(() => {
+          this.lock = null;
+        }, 100);
+    };
+
+    onDropdownVisibleChange = (filter, open) => {
+        if (this.lock) return;
+        let obj = {};
+        obj['dropdownOpen-' + filter.key] = open;
+        this.setState(obj);
+    };
+
     /**
      * 生成过滤规则文本
      */
@@ -383,7 +433,7 @@ class FilterBox extends React.Component {
                                     // />,
                                     <Select
                                         labelInValue={true}
-                                        onChange={(value) => {console.log(value);this.changeFilterOperator(f, value)}}
+                                        onChange={(value) => {this.changeFilterOperator(f, value)}}
                                         dropdownMatchSelectWidth={false}
                                     >
                                         {OPERATORS[type].map((o, i) => {
@@ -393,28 +443,26 @@ class FilterBox extends React.Component {
                                 )}
                             </FormItem>
                         </Col>
-                        <Col span={(operator&&operator!=='null'&&operator!=='notNull')?(operator==='between'?6:12):'0'}>
+                        <Col span={(operator&&operator!=='null'&&operator!=='notNull')?(operator==='between'?6:12):0}>
                             <FormItem
                                 key={key}
                                 className='filterValueOne'
                             >
                                 {getFieldDecorator(`filterValueOne${key}`, {
                                     initialValue: type==='time' ? ( value1 ? moment(value1) : null) : value1,
-                                    rules: operator&&operator!=='null'&&operator!=='notNull' ? [{ required: true, message: '该值不能为空' }] : null
                                 })(this.getFilterValueField(key, type, operator, 1))}
                             </FormItem>
                         </Col>
-                        <Col span={operator==='between'?6:0}>
+                        {operator==='between' && <Col span={6}>
                             <FormItem
                                 key={key}
                                 className='filterValueTwo'
                             >
                                 {getFieldDecorator(`filterValueTwo${key}`, {
                                     initialValue: type==='time' ? ( value2 ? moment(value2) : null) : value2,
-                                    rules: [{ required: operator==='between', message: '该值不能为空' }]
                                 })(this.getFilterValueField(key, type, operator, 2))}
                             </FormItem>
-                        </Col>
+                        </Col>}
                     </Col>
                     <Col span={2} className='filter-remove-col' >
                         <Icon

+ 82 - 39
src/components/common/filterBox/filterBox2.jsx

@@ -3,12 +3,12 @@
  */
 import React from 'react'
 import PropTypes from 'prop-types'
-import { Modal, Form, Row, Col, Input, Icon, Button, Cascader, Select, InputNumber, DatePicker, Spin } from 'antd'
-import OPERATORS from './filterOperators.js';
+import { Modal, Form, Row, Col, Input, Icon, Button, Cascader, Select, InputNumber, DatePicker, Spin, Divider } from 'antd'
+import OPERATORS from './filterOperators.js'
 import './filterBox.less'
 import * as service from '../../../services/index'
 import URLS from '../../../constants/url'
-import moment from 'moment';
+import moment from 'moment'
 const FormItem = Form.Item
 const SelectOption = Select.Option
 
@@ -23,7 +23,6 @@ class FilterBox extends React.Component {
             relationColumns: props.relationColumns || [],
             filterData: props.filterData || [],
             fetching: false, // 请求列数据状态
-            columnData: [], // 列数据
             fieldOptions: props.dataSources.map(d => {
                 return {
                     name: d.code,
@@ -51,9 +50,9 @@ class FilterBox extends React.Component {
     }
 
     componentDidMount() {
-        // 将原本的过滤条件生成可视化组件
         this.loadColumnDatas();
         const { filterData } = this.state;
+        // 将原本的过滤条件生成可视化组件
         this.addFilter(filterData.map(f => {
             return {
                 ...f,
@@ -104,7 +103,9 @@ class FilterBox extends React.Component {
         const { form } = this.props;
         const filters = form.getFieldValue('filters');
 
-        this.setState({ columnData: [] });
+        let obj = {};
+        obj['columnData-' + value[0].name + '-' + value[1].name] = [];
+        this.setState(obj);
         form.setFieldsValue({
             filters: filters.map((f) => {
                 if (f.key === filter.key) {
@@ -132,7 +133,7 @@ class FilterBox extends React.Component {
         form.setFieldsValue({
             filters: filters.map((f) => {
                 if (f.key === filter.key) {
-                    f.key = uuid++;
+                    // f.key = uuid++;
                     f.operator = value.key;
                     f.operatorLabel = value.label;
                     f.value1 = f.value2 = (f.type === 'time' ? moment() : undefined);
@@ -152,7 +153,7 @@ class FilterBox extends React.Component {
         form.setFieldsValue({
             filters: filters.map((f) => {
                 if (f.key === filter.key) {
-                    f.key = uuid++;
+                    // f.key = uuid++;
                     f[`value${index}`] = value;
                 }
                 return f;
@@ -178,7 +179,6 @@ class FilterBox extends React.Component {
     }
 
     fetchColumnData = (filter, options) => {
-        const { columnData } = this.state;
         const { keyword, mandatory } = options || {};
         const { dataSource } = filter;
         const isCusMode = dataSource.name === 'cus';
@@ -186,8 +186,11 @@ class FilterBox extends React.Component {
         if(isCusMode) {
             column = dataSource.columns.find(c => c.name === filter.name)
         }
+        let columnData = this.state['columnData-' + filter.dataSource.name + '-' + filter.name]
         if(!columnData || columnData.length === 0 || mandatory) {
-            this.setState({ columnData: [], fetching: true }, () => {
+            let obj = {fetching: true};
+            obj['columnData-' + filter.dataSource.name + '-' + filter.name] = []
+            this.setState(obj, () => {
                 const body = isCusMode ? column.relations.map(r => ({
                     id: r.dataSource.code,
                     columnName: r.column.name,
@@ -210,15 +213,13 @@ class FilterBox extends React.Component {
                     }
                 }).then(r => {
                     const resData = r.data.data || [];
-                    this.setState({
-                        columnData: resData.map(d => d || 'null'),
-                        fetching: false
-                    });
+                    let obj = {fetching: false};
+                    obj['columnData-' + filter.dataSource.name + '-' + filter.name] = resData.map(d => d || 'null')
+                    this.setState(obj);
                 }).catch(ex => {
-                    this.setState({
-                        columnData: [],
-                        fetching: false
-                    });
+                    let obj = {fetching: false};
+                    obj['columnData-' + filter.dataSource.name + '-' + filter.name] = [];
+                    this.setState(obj);
                     console.error('fetch error', ex);
                 });
             });
@@ -298,28 +299,26 @@ class FilterBox extends React.Component {
                                 )}
                             </FormItem>
                         </Col>
-                        <Col span={(operator&&operator!=='null'&&operator!=='notNull')?(operator==='between'?6:12):'0'}>
+                        <Col span={(operator&&operator!=='null'&&operator!=='notNull')?(operator==='between'?6:12):0}>
                             <FormItem
                                 key={key}
                                 className='filterValueOne'
                             >
                                 {getFieldDecorator(`filterValueOne${key}`, {
                                     initialValue: type==='time' ? ( value1 ? moment(value1) : null) : value1,
-                                    rules: operator&&operator!=='null'&&operator!=='notNull' ? [{ required: true, message: '该值不能为空' }] : null
-                                })(this.generateValueItem(key, type, operator, 1))}
+                                })(this.generateValueItem(f, 1))}
                             </FormItem>
                         </Col>
-                        <Col span={operator==='between'?6:0}>
+                        {operator==='between' && <Col span={6}>
                             <FormItem
                                 key={key}
                                 className='filterValueTwo'
                             >
                                 {getFieldDecorator(`filterValueTwo${key}`, {
                                     initialValue: type==='time' ? ( value2 ? moment(value2) : null) : value2,
-                                    rules: [{ required: operator==='between', message: '该值不能为空' }]
-                                })(this.generateValueItem(key, type, operator, 2))}
+                                })(this.generateValueItem(f, 2))}
                             </FormItem>
-                        </Col>
+                        </Col>}
                     </Col>
                     <Col span={2} className='filter-remove-col' >
                         <Icon
@@ -339,6 +338,7 @@ class FilterBox extends React.Component {
         const { fieldOptions } = this.state;
 
         return <Cascader
+            allowClear={false}
             fieldNames={{ label: 'label', value: 'name', children: 'columns' }}
             options={fieldOptions}
             loadData={this.loadColumnData}
@@ -362,40 +362,69 @@ class FilterBox extends React.Component {
         </Select>)
     }
 
-    generateValueItem = (key, type, operator, index) => {
-        const { fetching, columnData } = this.state;
+    generateValueItem = (filter, index) => {
+        const { fetching } = this.state;
+        const dropdownOpen = filter.key ? (this.state['dropdownOpen-' + filter.key] || false) : false;
+        const columnData = filter.name ? (this.state['columnData-' + filter.dataSource.name + '-' + filter.name] || []) : [];
         let field;
-        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 || [];
+        const { key, type, operator } = filter;
+
+        const commonProps = { placeholder: '无默认值' }
+
         if(['index', 'string'].indexOf(type) !== -1) {
-            field = <Input onBlur={(e) => {this.changeFilterValue(filter, e.target.value, index)}}/>
+            field = <Input { ...commonProps } onBlur={(e) => {this.changeFilterValue(filter, e.target.value, index)}}/>
         }else if(['scale', 'ordinal'].indexOf(type) !== -1) {
-            field = <InputNumber onBlur={(e) => {this.changeFilterValue(filter, e.target.value, index)}}/>
+            field = <InputNumber { ...commonProps } onBlur={(e) => {this.changeFilterValue(filter, e.target.value, index)}}/>
         }else if(type === 'time') {
-            field = <DatePicker onChange={(value) => {this.changeFilterValue(filter, value, index)}}/>
+            field = <DatePicker { ...commonProps } onChange={(value) => {this.changeFilterValue(filter, value, index)}}/>
         }else if(type === 'categorical') { // 类别
             if(operator === 'include' || operator==='notInclude') { // 包含/不包含
-                field = <Input onBlur={(e) => {this.changeFilterValue(filter, e.target.value, index)}}/>
+                field = <Input { ...commonProps } onBlur={(e) => {this.changeFilterValue(filter, e.target.value, index)}}/>
             }else if(operator === 'contain' || operator === 'notContain') { // 包括/不包括
                 field = (<Select 
+                    { ...commonProps }
+                    allowClear
                     mode='multiple'
                     showSearch
+                    open={dropdownOpen}
+                    onDropdownVisibleChange={(open) => {
+                        this.onDropdownVisibleChange(filter, open);
+                    }}
                     dropdownMatchSelectWidth={false}
                     notFoundContent={fetching ? <Spin size="small" /> : '无'}
                     onSearch={(value) => {this.fetchColumnData(filter, { keyword: value, mandatory: true })}}
                     onFocus={() => {this.fetchColumnData(filter)}}
                     onChange={(value) => {this.changeFilterValue(filter, value, index)}}
+                    // dropdownRender={menu => (
+                    //     <div>
+                    //         {menu}
+                    //         {columnData.length > 0 && <Divider style={{ margin: '4px 0' }} />}
+                    //         {columnData.length > 0 && <div style={{ padding: '8px', cursor: 'pointer' }}>
+                    //             {/** https://github.com/ant-design/ant-design/issues/13448 */}
+                    //             <span onMouseDown={this.lockClose} onMouseUp={this.lockClose} onClick={() => {
+                    //                 if(commonProps.defaultValue && commonProps.defaultValue.length === columnData.length) {
+                    //                     this.changeFilterValue(filter, [], index)
+                    //                 }else {
+                    //                     this.changeFilterValue(filter, columnData, index)
+                    //                 }
+                    //                 let obj = {};
+                    //                 obj['dropdownOpen-' + filter.key] = false;
+                    //                 this.setState(obj);
+                    //             }}>    
+                    //                 <Icon type={ commonProps.defaultValue && commonProps.defaultValue.length === columnData.length ? 'check-square' : 'border' } /> { commonProps.defaultValue && commonProps.defaultValue.length === columnData.length ? '取消全选' : '全选' }
+                    //             </span>
+                    //         </div>}
+                    //     </div>
+                    // )}
                 >
                     { columnData.map((s, i) => {
                         return <SelectOption key={i} value={s}>{s}</SelectOption>
                     }) }
                 </Select>)
             }else { // 等于/不等于
-                field = (<Select 
+                field = (<Select
+                    { ...commonProps }
+                    allowClear
                     mode='single'
                     showSearch
                     dropdownMatchSelectWidth={false}
@@ -410,12 +439,26 @@ class FilterBox extends React.Component {
                 </Select>)
             }
         }else {
-            field = <Input onBlur={(e) => {this.changeFilterValue(filter, e.target.value, index)}}/> 
+            field = <Input { ...commonProps } onBlur={(e) => {this.changeFilterValue(filter, e.target.value, index)}}/> 
         }
 
         return field;
     }
 
+    lockClose = e => {
+        clearTimeout(this.lock);
+        this.lock = setTimeout(() => {
+          this.lock = null;
+        }, 100);
+    };
+
+    onDropdownVisibleChange = (filter, open) => {
+        if (this.lock) return;
+        let obj = {};
+        obj['dropdownOpen-' + filter.key] = open;
+        this.setState(obj);
+    };
+
     loadColumnDatas() {
         let { fieldOptions } = this.state;
         fieldOptions.forEach(f => {

+ 36 - 36
src/components/common/filterBox/filterOperators.js

@@ -17,12 +17,12 @@ export default {
     }, {
         value: "notEquals",
         label: "不等于"
-    }, {
-        value: "null",
-        label: "为空"
-    }, {
-        value: "notNull",
-        label: "不为空"
+    // }, {
+    //     value: "null",
+    //     label: "为空"
+    // }, {
+    //     value: "notNull",
+    //     label: "不为空"
     }],
     string: [{
         value: "include",
@@ -42,12 +42,12 @@ export default {
     }, {
         value: "notEquals",
         label: "不等于"
-    }, {
-        value: "null",
-        label: "为空"
-    }, {
-        value: "notNull",
-        label: "不为空"
+    // }, {
+    //     value: "null",
+    //     label: "为空"
+    // }, {
+    //     value: "notNull",
+    //     label: "不为空"
     }],
     scale: [{
         value: ">",
@@ -70,12 +70,12 @@ export default {
     }, {
         value: "between",
         label: "介于"
-    }, {
-        value: "null",
-        label: "为空"
-    }, {
-        value: "notNull",
-        label: "不为空"
+    // }, {
+    //     value: "null",
+    //     label: "为空"
+    // }, {
+    //     value: "notNull",
+    //     label: "不为空"
     }],
     time: [{
         value: "=",
@@ -89,12 +89,12 @@ export default {
     }, {
         value: "between",
         label: "介于"
-    }, {
-        value: "null",
-        label: "为空"
-    }, {
-        value: "notNull",
-        label: "不为空"
+    // }, {
+    //     value: "null",
+    //     label: "为空"
+    // }, {
+    //     value: "notNull",
+    //     label: "不为空"
     }],
     categorical: [{
         value: "=",
@@ -114,12 +114,12 @@ export default {
     }, {
         value: "notInclude",
         label: "不包含"
-    }, {
-        value: "null",
-        label: "为空"
-    }, {
-        value: "notNull",
-        label: "不为空"
+    // }, {
+    //     value: "null",
+    //     label: "为空"
+    // }, {
+    //     value: "notNull",
+    //     label: "不为空"
     }],
     ordinal: [{
         value: ">",
@@ -142,12 +142,12 @@ export default {
     }, {
         value: "between",
         label: "介于"
-    }, {
-        value: "null",
-        label: "为空"
-    }, {
-        value: "notNull",
-        label: "不为空"
+    // }, {
+    //     value: "null",
+    //     label: "为空"
+    // }, {
+    //     value: "notNull",
+    //     label: "不为空"
     }],
     "undefined": [{
         value: "null",

+ 19 - 23
src/components/dashboardDesigner/content.jsx

@@ -3,6 +3,7 @@ import { Layout, Tag, Icon } from 'antd'
 import { connect } from 'dva'
 import ViewLayout from './viewLayout'
 import FilterBox from '../common/filterBox/filterBox2'
+import Filter from '../common/filterBox/filter2'
 import ConfigSider from './configSider'
 import moment from 'moment'
 
@@ -142,44 +143,39 @@ class DashboardDesignerContent extends React.Component {
         });
     }
 
+    changeFilterValue = (filter) => {
+        const { dispatch, afterRefresh } = this.props;
+        dispatch({ type: 'dashboardDesigner/changeFilter', filter }).then(() => {
+            afterRefresh && typeof afterRefresh === 'function' && afterRefresh()
+        });
+    }
+
     render() {
         const { dashboardDesigner, isOwner, isShareView, isShareKeyView, isViewMode } = this.props;
         const { dataSources, editMode, filters, relationColumns } = dashboardDesigner;
         const { visibleFilterBox, lastContentSize } = this.state;
 
         const contentSize = this.getContentSize();
-        let tags = filters.map((f, i)=>{
-            return {
-                key: f.key,
-                label: this.createFilterLabel(f),
-                using: f.type ? f.using : false
-            }
-        });
 
         return <Layout className='content'>
             <Header>
+                {/* <div className='toggle'><Icon type="caret-up" /></div> */}
                 <div className='filters'>
-                    <div style={{ margin: '0 8px 0 10px' }}>筛选:</div>
-                    {tags.map(tag => (
-                        <Tag
-                            className={`filter-tag ${tag.using?'filter-tag-using':''}`}
-                            key={tag.key}
-                            closable={false}
-                            onClick={this.filterUsingChange}
-                            data-key={tag.key}
-                            title={tag.label}
-                        >
-                            {tag.label}
-                        </Tag>
-                    ))}
-                    <Tag
+                    {isOwner && editMode && !isShareView && !isShareKeyView && !isViewMode && <Tag
                         onClick={this.showFilterBox}
                         className={`filter-tag filter-tag-add`}
                     >
                         <Icon type="filter" theme="outlined" />
-                    </Tag>
+                    </Tag>}
+                    {filters.map(f => (
+                        <Filter
+                            key={f.key}
+                            filter={f}
+                            changeFilterValue={this.changeFilterValue}
+                        />
+                    ))}
                 </div>
-                {visibleFilterBox && <FilterBox dataSources={dataSources} relationColumns={relationColumns} filterData={filters} visibleFilterBox={visibleFilterBox} showFilterBox={this.showFilterBox} hideFilterBox={this.hideFilterBox} createFilters={this.createFilters} />}
+                {visibleFilterBox && <FilterBox key={Math.random()} dataSources={dataSources} relationColumns={relationColumns} filterData={filters} visibleFilterBox={visibleFilterBox} showFilterBox={this.showFilterBox} hideFilterBox={this.hideFilterBox} createFilters={this.createFilters} />}
             </Header>
             <Content className={`dashboard-content${(isShareView || isShareKeyView || isViewMode) ? ' nomargin' : ''}`}>
                 <Layout className='content-layout'>

+ 5 - 2
src/components/dashboardDesigner/layout.less

@@ -51,19 +51,22 @@
             }
             .ant-layout-header {
                 background: none;
-                padding: 0 10px;
+                padding: 0;
                 height: auto;
-                min-height: 40px;
+                // min-height: 40px;
                 line-height: 37px;
                 border-width: 1px 0;
                 border-style: solid;
                 border-color: #CCCCCC;
                 display: flex;
                 justify-content: space-between;
+                border: none;
                 .filters {
                     display: flex;
                     width: 100%;
                     flex-wrap: wrap;
+                    border-top: 1px solid #ccc;
+                    border-bottom: 1px solid #ccc;
                     .filter-tag {
                         max-width: 200px;
                         overflow: hidden;

+ 6 - 1
src/components/homePage/index.less

@@ -17,8 +17,13 @@
             }
             &>.ant-tabs-content {
                 height: 100%;
-                &>.ant-tabs-tabpane {
+                &>.ant-tabs-tabpane.ant-tabs-tabpane-active {
                     height: 100%;
+                    display: flex;
+                    flex-direction: column;
+                    &>.dashboarddesigner-layout {
+                        flex: 1;
+                    }
                 }
             }
         }

+ 3 - 3
src/constants/url.js

@@ -187,11 +187,11 @@ const URLS = {
 
     DASHBOARD_SHARE_DETAIL_BY_CODE: BASE_URL + '/getDashboardByCode', // 通过报表编号获得报表数据
 
-    DASHBOARD_COLLECT_LIST: BASE_URL + '/dashboard/collect/list', // 报表收藏列表
+    DASHBOARD_COLLECT_LIST: BASE_URL + '/dashboard/favorite/list', // 报表收藏列表
 
-    DASHBOARD_COLLECT_ADD: BASE_URL + '/dashboard/collect/add', // 添加报表到收藏
+    DASHBOARD_COLLECT_ADD: BASE_URL + '/dashboard/favorite/save', // 添加报表到收藏
 
-    DASHBOARD_COLLECT_REMOVE: BASE_URL + '/dashboard/collect/remove', // 报表取消收藏
+    DASHBOARD_COLLECT_REMOVE: BASE_URL + '/dashboard/favorite/delete', // 报表取消收藏
 
     /***************************************报表目录***************************************/
 

+ 31 - 21
src/models/chartDesigner.js

@@ -5,30 +5,40 @@ import parseChartOption from './parseChartOption'
 import moment from 'moment'
 
 function getBodyFilters(filters) {
-    return filters.filter(f => f.using).map(f => {
+    let bodyFilters = [];
+    filters.filter(f => f.using).forEach(f => {
         let { name, operator, type, value1, value2 } = f;
-        let bodyFilter = {
-            columnName: name,
-            columnType: type,
-            symbol: operator,
-            value: value1
-        };
-        if(type === 'scale' && operator === 'between') {
-            bodyFilter['value'] = value1 + ',' + value2;
-        }else if(type === 'time') {
-            let v1 = moment(value1).format('YYYY-MM-DD');
-            let v2 = moment(value2).format('YYYY-MM-DD');
-
-            if(operator === 'between') {
-                bodyFilter['value'] = v1 + ',' + v2;
-            }else {
-                bodyFilter['value'] = v1;
+        if(((type === 'index' || type === 'string') && !!value1) || // 因为数字类型会生成数字字符串,所以为0也是可以正常传入条件的
+        ((type === 'scale' || type === 'time' || type === 'ordinal') && (operator === 'between' ? (!!value1 && !!value2) : (!!value1))) ||
+        (type === 'categorical' &&
+            (operator === 'contain' || operator === 'notContain' ?
+                (value1 && value1.length > 0) : (!!value1)
+            )
+        )) {
+            let bodyFilter = {
+                columnName: name,
+                columnType: type,
+                symbol: operator,
+                value: value1
+            };
+            if(type === 'scale' && operator === 'between') {
+                bodyFilter['value'] = value1 + ',' + value2;
+            }else if(type === 'time') {
+                let v1 = moment(value1).format('YYYY-MM-DD');
+                let v2 = moment(value2).format('YYYY-MM-DD');
+    
+                if(operator === 'between') {
+                    bodyFilter['value'] = v1 + ',' + v2;
+                }else {
+                    bodyFilter['value'] = v1;
+                }
+            }else if(type === 'categorical' && (operator === 'contain' || operator === 'notContain')) {
+                bodyFilter['value'] = JSON.stringify(value1);
             }
-        }else if(type === 'categorical' && (operator === 'contain' || operator === 'notContain')) {
-            bodyFilter['value'] = JSON.stringify(value1);
+            bodyFilters.push(bodyFilter);
         }
-        return bodyFilter;
-    }); 
+    });
+    return bodyFilters;
 }
 
 export default {

+ 35 - 24
src/models/dashboardDesigner.js

@@ -12,32 +12,43 @@ import CHART_TYPE from './chartType.json'
 function getTrueFilters(item, filters) {
     let trueFilters = [];
     filters.forEach(f => {
-        if(f.combined) {
-            let column = f.dataSource.columns.find(c => c.name === f.name);
-            column.relations.forEach(re => {
-                if(re.dataSource.code === item.dataSourceCode) {
-                    trueFilters.push({
-                        dataSourceCode: re.dataSource.code,
-                        name: re.column.name,
-                        operator: f.operator,
-                        type: f.type,
-                        value1: f.value1,
-                        value2: f.value2,
-                        using: f.using
+        const { type, operator, value1, value2 } = f;
+        if(((type === 'index' || type === 'string') && !!value1) || // 因为数字类型会生成数字字符串,所以为0也是可以正常传入条件的
+        ((type === 'scale' || type === 'time' || type === 'ordinal') && (operator === 'between' ? (!!value1 && !!value2) : (!!value1))) ||
+        (type === 'categorical' &&
+            (operator === 'contain' || operator === 'notContain' ?
+                (value1 && value1.length > 0) : (!!value1)
+            )
+        )) {
+            if(f.operator === 'betweent' ? ( !!f.value1 && (f.value1.length ? f.value1.length > 0 : true) && !!f.value2 && (f.value2.length ? f.value2.length > 0 : true)) : (!!f.value1 && (f.value1.length ? f.value1.length > 0 : true))) {
+                if(f.combined) {
+                    let column = f.dataSource.columns.find(c => c.name === f.name);
+                    column.relations.forEach(re => {
+                        if(re.dataSource.code === item.dataSourceCode) {
+                            trueFilters.push({
+                                dataSourceCode: re.dataSource.code,
+                                name: re.column.name,
+                                operator: f.operator,
+                                type: f.type,
+                                value1: f.value1,
+                                value2: f.value2,
+                                using: f.using
+                            });
+                        }
                     });
+                }else {
+                    if(f.dataSource.name === item.dataSourceCode) {
+                        trueFilters.push({
+                            dataSourceCode: f.dataSource.name,
+                            name: f.name,
+                            operator: f.operator,
+                            type: f.type,
+                            value1: f.value1,
+                            value2: f.value2,
+                            using: f.using
+                        });
+                    }
                 }
-            });
-        }else {
-            if(f.dataSource.name === item.dataSourceCode) {
-                trueFilters.push({
-                    dataSourceCode: f.dataSource.name,
-                    name: f.name,
-                    operator: f.operator,
-                    type: f.type,
-                    value1: f.value1,
-                    value2: f.value2,
-                    using: f.using
-                });
             }
         }
     });

+ 2 - 4
src/models/home.js

@@ -126,8 +126,7 @@ export default {
                 const { data } = action;
                 const { collectionDashboards } = home;
                 const res = yield call(service.fetch, {
-                    url: URLS.DASHBOARD_COLLECT_ADD,
-                    body: {}
+                    url: URLS.DASHBOARD_COLLECT_ADD + '/' + data.code
                 });
                 if(!res.err && res.data.code > 0) {
                     collectionDashboards.unshift({ code: data.code, name: data.name });
@@ -145,8 +144,7 @@ export default {
                 const { data } = action;
                 const { collectionDashboards } = home;
                 const res = yield call(service.fetch, {
-                    url: URLS.DASHBOARD_COLLECT_REMOVE,
-                    body: {}
+                    url: URLS.DASHBOARD_COLLECT_REMOVE + '/' + data.code
                 });
                 if(!res.err && res.data.code > 0) {
                     let idx = collectionDashboards.findIndex(d => d.code === data.code);