Browse Source

数据源/图表策略设置

zhuth 7 years ago
parent
commit
5a38146a00

+ 265 - 83
src/components/chart/distributeBox.jsx

@@ -1,8 +1,11 @@
 import React from 'react'
-import { Modal, Table, Col, Row, Button, Input, Icon, Switch, Tag} from 'antd'
+import { Modal, Layout, Card, Table, Col, Row, Button, Input, Icon, Tag, Checkbox } from 'antd'
 import { connect } from 'dva'
-import DistributeObjectBox from './distributeObjectBox';
-import DistributePolicyRuleBox from './distributePolicyRuleBox';
+import FilterBox from '../chartDesigner/sections/filterBox'
+import AccessObjectBox from '../datasource/accessObjectBox'
+import * as moment from 'moment'
+import './distributeBox.less'
+const { Content } = Layout
 
 const Search = Input.Search;
 
@@ -14,8 +17,17 @@ class DistributeBox extends React.Component {
             filterLabel: '',
             visibleDistributeObjectBox: false,
             visibleDistributePolicyRuleBox: false,
+            currentPolicy: null,
+            visiblePolicyRuleBox: false,
+            visibleAccessObjectBox: false
         }
-    } 
+    }
+
+    componentDidMount() {
+        // const { selectedRecord, dispatch } = this.props;
+        // const chartCode = selectedRecord ? selectedRecord.code : '';
+        // dispatch({ type: 'dataSourcePolicy/fetchList', chartCode: chartCode });
+    }
 
     hideDistributeObjectBox= () => {
         this.setState(
@@ -26,100 +38,270 @@ class DistributeBox extends React.Component {
         this.setState(
             {visibleDistributePolicyRuleBox:false})
     }
+
+    showAccessObjectBox = () => {
+        this.setState({ visibleAccessObjectBox:true })
+    }
+
+    hideAccessObjectBox = () => {
+        this.setState({ visibleAccessObjectBox:false })
+    }
+
+    showPolicyRuleBox= () => {
+        this.setState({ visiblePolicyRuleBox: true })
+    }
+
+    hidePolicyRuleBox= () => {
+        this.setState({ visiblePolicyRuleBox: false })
+    }
+
+    setAccessObject = (group, geren) => {
+        const { dispatch } = this.props;
+        const { currentPolicy } = this.state;
+        let targets = group.map(g => ({
+            code: g.code,
+            name: g.name,
+            isGroup: true
+        })).concat(geren.map(g => ({
+            code: g.code,
+            name: g.name,
+            isGroup: false
+        })))
+        dispatch({ type: 'chartPolicy/remoteSetTarget', policy: currentPolicy, targets });
+    }
+
+    createFilters = (filters) => {
+        const { selectedRecord, dispatch } = this.props;
+        let { currentPolicy } = this.state;
+        const chartCode = selectedRecord ? selectedRecord.code : '';
+
+        currentPolicy.filters = filters;
+        dispatch({ type: 'chartPolicy/remoteModify', policy: currentPolicy, chartCode });
+    }
+
+    /**
+     * 生成过滤规则文本
+     */
+    createFilterLabel = (filter) => {
+        let { label, operator, operatorLabel, type, value1, value2 } = filter;
+        let filterLabel;
+
+        if(type === 'string' || type === 'index') {
+            if(operator === 'null' || operator === 'notNull') {
+                filterLabel = `${label} ${operatorLabel}`;
+            }else {
+                filterLabel = `${label} ${operatorLabel} ${value1}`;
+            }
+        }else if(type === 'scale') {
+            if(operator === 'null' || operator === 'notNull') {
+                filterLabel = `${label} ${operatorLabel}`;
+            }else if(operator === 'between') {
+                filterLabel = `${label} ${operatorLabel} ${value1} ~ ${value2}`; 
+            }else {
+                filterLabel = `${label} ${operatorLabel} ${value1}`; 
+            }
+        }else if(type === 'time') {
+            value1 = moment(value1).format('yyyy/MM/dd');
+            value2 = moment(value2).format('yyyy/MM/dd');
+            if(operator === 'null' || operator === 'notNull') {
+                filterLabel = `${label} ${operatorLabel}`;
+            }else if(operator === 'between') {
+                filterLabel = `${label} ${operatorLabel} ${value1} ~ ${value2}`;
+            }else {
+                filterLabel = `${label} ${operatorLabel} ${value1}`;
+            }
+        }else if(type === 'categorical') {
+            if(operator === 'null' || operator === 'notNull') {
+                filterLabel = `${label} ${operatorLabel}`;
+            }else {
+                filterLabel = `${label} ${operatorLabel} ${value1}`;
+            }
+        }else {
+            filterLabel = '错误条件';
+        }
+        return filterLabel;
+    }
+
     render() {
-        const { selectedRecord, visibleDistributeObjectBox, visibleDistributePolicyRuleBox } = this.state;
-        const { dataSource, visibleDistributeBox, hideBox } = this.props
-
-        const columns = [
-            {
-                title: '启用',
-                dataIndex:'enabled',
-                render: enabled => <Switch defaultChecked={enabled} />
-            },
-            {
+        const { selectedRecord, chartPolicy, visibleDistributeBox, hideBox, dispatch } = this.props;
+        const { visiblePolicyRuleBox, visibleAccessObjectBox } = this.state;
+        const polices = chartPolicy.list;
+        const chartCode = selectedRecord ? selectedRecord.code : '';
+
+        const { currentPolicy } = this.state;
+        const group = currentPolicy ? (currentPolicy.targets ? currentPolicy.targets.filter(t => t.isGroup) : []) : [];
+        const geren = currentPolicy ? (currentPolicy.targets ? currentPolicy.targets.filter(t => !t.isGroup) : []) : [];
+        
+        const reg = new RegExp('([+ \\- & | ! ( ) { } \\[ \\] ^ \" ~ * ? : ( ) \/])', 'g'); // 需要转义的字符
+        let filterLabel = (this.state.filterLabel || '').replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(reg, '\\$1'); // 添加转义符号
+        
+        // const columnData = dataSource.newOne.columns ? dataSource.newOne.columns.map(c => ({
+        //     key: c.key,
+        //     label: c.alias,
+        //     name: c.name,
+        //     selection: [],
+        //     type: c.columnType,
+        //     groupable: c.groupable,
+        //     // filterable: c.filterable,
+        //     filterable: true,
+        //     bucketizable: c.bucketizable
+        // })) : [];
+        
+        const columns = [{
+            title:'启用',
+            dataIndex:'enabled',
+            render: (v, r) => <Checkbox
+                checked={v}
+                onChange={(e) => {
+                    let policy = {
+                        ...r,
+                        enabled: e.target.checked
+                    }
+                    dispatch({ type: 'chartPolicy/remoteModify', policy, chartCode  });
+                }}
+            />,
+            width: 50
+        },{
             title: '策略名',
-            dataIndex: 'policyName',
-            render: policyName => <span>{policyName}<Icon type='copy'/></span>
-        }, {
-            title: '分发对象',
-            dataIndex: 'targets',
-            render: targets => {return (
-                <div>
-                    {targets.map((item, index) => {return <Tag key={index}>{item.isGroup ? <Icon type='tags'/>:<Icon type='user'/>}{item.value}</Tag>})}
-                    <Tag onClick={() => {this.setState({visibleDistributeObjectBox:true})}}><Icon type='plus'/></Tag>
-                </div>
-            )}
+            dataIndex: 'name',
+            render: (value, record, index) => <div key={index}>
+                {
+                    this.state.inputRow === index ? <Input value={value} suffix={<Icon style={{ cursor: 'pointer', color: '#52C41A' }} type="check-circle" onClick={() => {
+                        this.setState({
+                            inputRow: -1
+                        });
+                        dispatch({ type: 'chartPolicy/remoteModify', policy: record, chartCode });
+                    }}/>} onChange={(e) => {
+                        dispatch({ type: 'chartPolicy/modify', policy: { ...record, name: e.target.value } });
+                    }} onBlur={() => {
+                        this.setState({
+                            inputRow: -1
+                        });
+                        dispatch({ type: 'chartPolicy/remoteModify', policy: record, chartCode });
+                    }} onPressEnter={() => {
+                        this.setState({
+                            inputRow: -1
+                        });
+                        dispatch({ type: 'chartPolicy/remoteModify', policy: record, chartCode });
+                    }}/> : <span onClick={() => {
+                        this.setState({
+                            inputRow: index
+                        });
+                    }}>
+                        { filterLabel ?
+                            ((value || '').split(new RegExp(`(${filterLabel})`, 'i')).map((fragment, i) => {
+                                return (
+                                    fragment.toLowerCase().replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(reg, '\\$1') === filterLabel.toLowerCase() ?
+                                    <span key={i} style={{fontWeight: 'bold', color: 'red'}} className="highlight">{fragment}</span> :
+                                    fragment
+                                )
+                            }
+                            )) : value
+                        }
+                    </span>
+                }
+                {
+                    this.state.inputRow !== index && <Icon style={{ cursor: 'pointer' }} type='edit' onClick={() => {
+                        this.setState({
+                            inputRow: index
+                        });
+                    }} />
+                }
+            </div>,
+            width: 150
         }, {
             title: '行开放策略',
-            dataIndex: 'rowFilters',
-            render: rowFilters => {return (
-                <div>
-                    {rowFilters.map((filter, index) => {return <p key={index}>{filter.value}</p>})}
-                    <Tag onClick={() => {this.setState({visibleDistributePolicyRuleBox:true})}}><Icon type='plus'/></Tag>
-                </div>
-            )}
+            dataIndex: 'filters',
+            render: filter => <div>
+                {filter.length > 0 ? filter.map((f) =>
+                    <Tag
+                        className='filter-tag'
+                        key={f.code}
+                        closable={false}
+                    >
+                        {this.createFilterLabel(f)}
+                    </Tag>
+                ) : <Tag className='filter-tag' closable={false}>所有数据</Tag>}
+                <Tag
+                    onClick={this.showPolicyRuleBox}
+                    className={`filter-tag filter-tag-add`}
+                >
+                    <Icon type="filter" />
+                </Tag>
+            </div>,
+            width: 500
+        }, {
+            title: '开放对象',
+            dataIndex: 'targets',
+            render: targets => <div>
+                {
+                    targets.map((item, index) => <Tag key={index}>{item.isGroup ? <Icon type='group' /> : <Icon type='geren' />}{item.name}</Tag>)
+                }
+                <Tag onClick={this.showAccessObjectBox}><Icon type="plus" /></Tag>
+            </div>,
+            width: 300
         }, {
             title: '操作',
-            dataIndex: 'operation',
-            render: () => { return <span>删除</span>}
-        }]
-        
-        const data = [{
-            key: '1',
-            enabled: true,
-            policyName: '分发策略1',
-            targets: [{isGroup: true, value:'销售'}, {isGroup: false, value:'陈炜'}],
-            rowFilters: [{value:'部门字段包含售后,销售'}, {value:'公司名包含公司A'}],
-            columns: [{name:'department', alias:'部门', enabled: true}, {name:'company', alias:'公司', enabled: false}]
+            render: (v,r) => <div><span style={{ cursor: 'pointer' }} onClick={() => {
+                dispatch({ type: 'chartPolicy/remoteDelete', code: r.code });
+            }}>删除</span></div>,
+            width: 120
         }];
-
+        
         return (
             <Modal
                 width={1200}
                 className='distribute-box'
-                title={
-                    <Row>图表分发策略
-                    </Row>
-                }
+                title='图表分发策略'
                 visible={visibleDistributeBox}
                 onOk={this.okHandler}
                 onCancel={hideBox}
                 maskClosable={false}
                 destroyOnClose={true}
             >
-                <div>
-                    <Table
-                        columns={columns}
-                        dataSource={data}
-                        bordered
-                        title={() => {
-                            return (
-                                <div style={{ display: "flex", justifyContent: "space-between" }}>
-                                    <Col>
-                                        <Search
-                                            style={{ display: "inline" }}
-                                            placeholder="input search text"
-                                            onSearch={value => console.log(value)}
-                                            enterButton
-                                        />
-                                    </Col>
-                                    <Col>
-                                        <Button>新增策略</Button>
-                                    </Col>
-                                </div>
-                            )
-                        }}
-                    />
-                    <DistributeObjectBox 
-                        visibleDistributeObjectBox={visibleDistributeObjectBox}
-                        onCancel={this.hideDistributeObjectBox}
-                    />
-                    <DistributePolicyRuleBox
-                        visibleDistributePolicyRuleBox={visibleDistributePolicyRuleBox}
-                        onCancel={this.hideDistributePolicyRuleBox}
-                    />
-                </div>
-            
+                <Layout className='chart-policy'>
+                    <Content>
+                        <Card className='policy-body' title={
+                            <Row className='policy-tools' type='flex' justify='end'>
+                                <Col className='search'>
+                                    <Search
+                                        placeholder="请输入关键字"
+                                        onChange={e => {
+                                        }}
+                                    />
+                                    <Button className='add-btn' onClick={() => {
+                                        dispatch({ type: 'chartPolicy/remoteAdd', policy: {
+                                            code: Math.random(),
+                                            anabled: false,
+                                            name: '新策略',
+                                            targets: [],
+                                            filters: []
+                                        }, chartCode });
+                                    }}>
+                                        添加策略
+                                    </Button>
+                                </Col>
+                            </Row>
+                        }>
+                            <Table
+                                className='policy-table'
+                                columns={columns}
+                                dataSource={polices ? polices.filter(l => {
+                                    let reg = new RegExp('(' + filterLabel + '){1}', 'ig');
+                                    return (l.name || '').search(reg) !== -1;
+                                }).map((p, i) => ({ ...p, key: i })) : []}
+                                size='small'
+                                onRow={(record) => {
+                                    return {
+                                        onClick: () => {this.setState({ currentPolicy:  record})}
+                                    }
+                                }}
+                            />
+                        </Card>
+                        <FilterBox key={Math.random()} columns={[]} filterData={currentPolicy ? currentPolicy.filters : []} visibleFilterBox={visiblePolicyRuleBox} hideFilterBox={this.hidePolicyRuleBox} createFilters={this.createFilters} />
+                        <AccessObjectBox key={Math.random()} visibleBox={visibleAccessObjectBox} hideBox={this.hideAccessObjectBox} okHandler={this.setAccessObject} group={group} geren={geren} />
+                    </Content>
+                </Layout>
             </Modal>
         )
 
@@ -127,8 +309,8 @@ class DistributeBox extends React.Component {
     
 }
 
-function mapStateToProps({ present: { dataSource } }) {
-    return { dataSource: dataSource };
+function mapStateToProps({ present: { chartPolicy } }) {
+    return { chartPolicy };
 }
 
 export default connect(mapStateToProps)(DistributeBox)

+ 23 - 0
src/components/chart/distributeBox.less

@@ -0,0 +1,23 @@
+.distribute-box {
+    .ant-modal-body {
+        padding: 0;
+        .ant-card-head {
+            .ant-card-head-title {
+                padding: 8px 0;
+                .policy-tools {
+                    .search {
+                        .ant-input-search {
+                            width: auto;
+                        }
+                        .add-btn {
+                            margin-left: 5px;
+                        }
+                    }
+                }
+            }
+        }
+        .ant-card-body {
+            padding: 0;
+        }
+    }
+}

+ 22 - 7
src/components/chart/list.jsx

@@ -1,3 +1,6 @@
+/**
+ * 图表列表
+ */
 import React from 'react'
 import { Layout, Button, Icon, Input, Menu, Dropdown, Card, Col, Row, Popover, Breadcrumb, Tree, Tag } from 'antd'
 import { connect } from 'dva'
@@ -65,6 +68,11 @@ class ChartList extends React.Component {
         const operationMenu = (
             <Menu className='menu-operation'>
                 <Menu.Item onClick={() => {
+                    const { selectedRecord } = this.state;
+                    // const selectedChartDataSourceCode = selectedRecord ? selectedRecord.code : '';
+                    const selectedChartCode = selectedRecord ? selectedRecord.code : '';
+                    dispatch({ type: 'chartPolicy/fetchList', chartCode: selectedChartCode });
+                    // dispatch({ type: 'chartDesigner/remoteDataColumn', code:  });
                     this.setState({visibleDistributeBox: true})
                 }}> 
                     <Icon type='share-alt'/>分发
@@ -141,6 +149,9 @@ class ChartList extends React.Component {
                                 dispatch({ type: 'chartDesigner/reset' });
                                 dispatch({ type: 'main/redirect', path: '/chart/' + l.code });
                             }}>
+                                {/* <div className='deny-body'>
+                                    <div className='deny-tip'>您没有对应数据源的权限</div>
+                                </div> */}
                                 <Thumbnail type={l.type} code={l.code} option={l.chartOption}/>
                             </Row>
                             <Row className='desc'>
@@ -187,7 +198,7 @@ class ChartList extends React.Component {
         const pGroups = groupList.filter(d => d.pcode === '-1').sort((a, b) => a.index - b.index);
         const cGroups = groupList.filter(d => d.pcode !== '-1');
 
-        let allGroups = selectedRecord ? [
+        let allGroups = !!selectedRecord ? [
             { code: '-1', label: '未分组' }
         ].concat(pGroups) : [
             { code: 'all', label: '全部分组' },
@@ -197,7 +208,7 @@ class ChartList extends React.Component {
         return allGroups.map(p => {
             let c = cGroups.filter(c => c.pcode === p.code).sort((a, b) => a.index - b.index);
             return c.length > 0 ? (
-                <Menu.SubMenu key={p.code} title={<span style={{ fontWeight: selectedRecord ? 
+                <Menu.SubMenu key={p.code} title={<span style={{ fontWeight: !!selectedRecord ? 
                     (p.code+'' === selectedRecord.groupCode+'' ? 'bold' : (
                         c.find(ch => ch.code+'' === selectedRecord.groupCode+'') && c.find(ch => ch.code+'' === selectedRecord.groupCode+'').pcode === p.code ? 'bold' : 'normal'
                     ))
@@ -214,7 +225,7 @@ class ChartList extends React.Component {
                             if(selectedRecord) {
                                 dispatch({ type: 'chart/remoteSetChartGroup', chart: selectedRecord, group: c });
                             }
-                        }}><span style={{ fontWeight: selectedRecord ? (
+                        }}><span style={{ fontWeight: !!selectedRecord ? (
                             selectedRecord.groupCode+'' === c.code+'' ? 'bold' : 'normal'
                         ) : (chart.currentGroup[1] && (chart.currentGroup[1].code === c.code) ? 'bold' : 'normal') }}>{c.label}</span></Menu.Item>)
                     })}
@@ -226,7 +237,7 @@ class ChartList extends React.Component {
                         dispatch({ type: 'chart/remoteSetChartGroup', chart: selectedRecord, group: p });
                     }
                     this.hideGroupMenu();
-                }}><span style={{ fontWeight: selectedRecord ? (
+                }}><span style={{ fontWeight: !!selectedRecord ? (
                     selectedRecord.groupCode+'' === p.code+'' ? 'bold' : 'normal'
                 ) : chart.currentGroup[0] && (chart.currentGroup[0].code === p.code) ? 'bold' : 'normal' }}>{p.label}</span></Menu.Item>
             );
@@ -421,18 +432,22 @@ class ChartList extends React.Component {
                         </div>
                     </Card>
                 </Content>
-                <DistributeBox visibleDistributeBox={visibleDistributeBox} selectedRecord={this.state.selectedRecord} hideBox={() => {
+                <DistributeBox key={this.state.selectedRecord ? this.state.selectedRecord.code : 'notkey'} visibleDistributeBox={visibleDistributeBox} selectedRecord={this.state.selectedRecord} hideBox={() => {
                     this.setState({
                         visibleDistributeBox: false
                     });
                 }} />
                 <TransferBox
                     visibleTransferBox={visibleTransferBox}
-                    onCancel={() => {
+                    onOk={(obj) => {
+                        console.log(obj);
+                    }}
+                    hideBox={() => {
                         this.setState({
                             visibleTransferBox: false
                         })
-                    }} />
+                    }}
+                />
             </Layout>
         )
     }

+ 9 - 0
src/components/chart/list.less

@@ -54,6 +54,15 @@
                     padding: 10px;
                     .thumb {
                         height: 132px;
+                        .deny-body {
+                            display: flex;
+                            position: absolute;
+                            height: 100%;
+                            text-align: center;
+                            justify-content: center;
+                            flex-direction: column;
+                            z-index: 1;
+                        }
                         cursor: pointer;
                         canvas {
                             cursor: pointer;

+ 105 - 5
src/components/chart/transferBox.jsx

@@ -1,16 +1,116 @@
 import React from 'react'
-import { Modal } from 'antd'
-
+import { Modal, Row, Col, Select, Spin } from 'antd'
+import * as service from '../../services/index'
+import URLS from '../../constants/url'
+const SelectOption = Select.Option
 
 class TransferBox extends React.Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+            selected: null,
+            userData: [],
+            fetching: false
+        }
+    }
+
+    fetchUserData = (keyword) => {
+        // this.setState({ userData: [], fetching: true }, () => {
+        //     setTimeout(() => {
+        //         this.setState({
+        //             userData: dataSource.filter(d => d.name.indexOf(keyword)!==-1),
+        //             fetching: false
+        //         })
+        //     }, 500);
+        // });
+        this.setState({ userData: [], fetching: true }, () => {
+            const body = {
+            };
+            service.fetch({
+                url: URLS.USER_LIST,
+                body: body,
+            }).then(r => {
+                if(!r.err && r.data.code > 0) {
+                    return r;
+                }else {
+                    let obj = {};
+                    throw obj;
+                }
+            }).then(r => {
+                console.log('获得用户数据', body, r);
+                const resData = r.data.data || [];
+                this.setState({
+                    userData: resData.map(d => ({
+                        code: d.id,
+                        name: d.name,
+                        account: d.userName,
+                        password: d.passWord,
+                        role: d.role,
+                        department: d.department,
+                        post: d.post,
+                    })),
+                    fetching: false
+                });
+            }).catch(ex => {
+                this.setState({
+                    userData: [],
+                    fetching: false
+                });
+                console.error('fetch error', ex);
+            });
+        });
+    }
+
     render() {
-        const { visibleTransferBox, onCancel } = this.props
+        const { visibleTransferBox, hideBox, onOk } = this.props;
+        const { userData, fetching } = this.state;
+
         return (
             <Modal 
                 visible={visibleTransferBox}
-                onCancel={onCancel}
+                title='选择移交对象'
+                onOk={() => {
+                    onOk(this.state.selected);
+                    hideBox();
+                }}
+                onCancel={hideBox}
+                maskClosable={false}
+                destroyOnClose={true}
             >
-                <span>Test</span>
+                <Row type='flex'>
+                    <Col span={24}>
+                        <Select
+                            style={{ width: '100%' }}
+                            // mode='multiple'
+                            showSearch
+                            filterOption={false}
+                            labelInValue={true}
+                            notFoundContent={fetching ? <Spin size="small" /> : '无'}
+                            onSearch={(value) => {
+                                const timeout = this.state.timeout;
+                                timeout && window.clearTimeout(timeout);
+                                this.setState({
+                                    timeout: window.setTimeout(() => {
+                                        this.fetchUserData(value)
+                                    }, 500)
+                                });
+                            }}
+                            onChange={(value) => {
+                                console.log(value);
+                                this.setState({
+                                    selected: {
+                                        code: value.key,
+                                        name: value.label
+                                    }
+                                });
+                            }}
+                        >
+                            { userData.map((s, i) => {
+                                return <SelectOption key={i} value={s.code}>{s.name}</SelectOption>
+                            }) }
+                        </Select>
+                    </Col>
+                </Row>
             </Modal>
         )
     }

+ 2 - 3
src/components/chartDesigner/sections/dataViewConfigForm.jsx

@@ -16,8 +16,6 @@ class DataViewConfigForm extends React.Component {
 	
 	render() {
 		const { autoRefresh, dispatch, chartDesigner, formItemLayout } = this.props;
-		const columns = chartDesigner.columns;
-        console.log(chartDesigner.dataViewConfig);
 		return (
             <Form layout='horizontal'>
 				<FormItem label="展示列" {...formItemLayout}>
@@ -76,7 +74,8 @@ class DataViewConfigForm extends React.Component {
 					>
 					</InputNumber>
 				</FormItem>
-				<DisplayColumnBox 
+				<DisplayColumnBox
+					key={Math.random()}
 					autoRefresh={autoRefresh} 
 					visibleDisplayColumnBox={this.state.visibleDisplayColumnBox}
 					hideBox={() => this.setState({visibleDisplayColumnBox:false})}

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

@@ -1,5 +1,5 @@
 import React from 'react'
-import { Modal, Transfer, Icon, Button, Row, Col } from 'antd'
+import { Modal, Transfer, Icon } from 'antd'
 import { connect } from 'dva'
 
 class DisplayColumnBox extends React.Component {

+ 49 - 2
src/components/chartDesigner/sections/filterBox.jsx

@@ -158,9 +158,13 @@ class FilterBox extends React.Component {
      */
     getFilters = () => {
         const { form, createFilters, hideFilterBox } = this.props;
-        form.validateFields(function(err, values) {
+        form.validateFields((err, values) => {
             if(!err) {
-                createFilters(values.filters);
+                let filters = values.filters.map(f => ({
+                    ...f,
+                    filterLabel: this.createFilterLabel(f)
+                }));
+                createFilters(filters);
             }
         })
         hideFilterBox();
@@ -237,6 +241,49 @@ class FilterBox extends React.Component {
         return field;
     }
 
+    /**
+     * 生成过滤规则文本
+     */
+    createFilterLabel = (filter) => {
+        let { label, operator, operatorLabel, type, value1, value2 } = filter;
+        let filterLabel;
+
+        if(type === 'string' || type === 'index') {
+            if(operator === 'null' || operator === 'notNull') {
+                filterLabel = `${label} ${operatorLabel}`;
+            }else {
+                filterLabel = `${label} ${operatorLabel} ${value1}`;
+            }
+        }else if(type === 'scale') {
+            if(operator === 'null' || operator === 'notNull') {
+                filterLabel = `${label} ${operatorLabel}`;
+            }else if(operator === 'between') {
+                filterLabel = `${label} ${operatorLabel} ${value1} ~ ${value2}`; 
+            }else {
+                filterLabel = `${label} ${operatorLabel} ${value1}`; 
+            }
+        }else if(type === 'time') {
+            value1 = moment(value1).format('YYYY/MM/DD');
+            value2 = moment(value2).format('YYYY/MM/DD');
+            if(operator === 'null' || operator === 'notNull') {
+                filterLabel = `${label} ${operatorLabel}`;
+            }else if(operator === 'between') {
+                filterLabel = `${label} ${operatorLabel} ${value1} ~ ${value2}`;
+            }else {
+                filterLabel = `${label} ${operatorLabel} ${value1}`;
+            }
+        }else if(type === 'categorical') {
+            if(operator === 'null' || operator === 'notNull') {
+                filterLabel = `${label} ${operatorLabel}`;
+            }else {
+                filterLabel = `${label} ${operatorLabel} ${value1}`;
+            }
+        }else {
+            filterLabel = '错误条件';
+        }
+        return filterLabel;
+    }
+
     getFilterItems() {
         const { columns } = this.state;
         const { getFieldDecorator, getFieldValue } = this.props.form;

+ 1 - 0
src/components/common/rootLayout.jsx

@@ -32,6 +32,7 @@ class RootLayout extends React.Component {
                 <div className='confirm-body'>
                     <div className='confirm-icon'><Icon type="info-circle" /></div>
                     <div className='confirm-label'>登录已过期</div>
+                    <div className='confirm-text'>距离上次登录已经超过30分钟,您必须重新登录以继续使用本系统</div>
                     <div className='confirm-button'>
                         <Button type="primary" onClick={this.setGoLogin}>重新登录</Button>
                     </div>

+ 2 - 2
src/components/dashboard/distributeBox.jsx

@@ -27,8 +27,8 @@ class DistributeBox extends React.Component {
             {visibleDistributePolicyRuleBox:false})
     }
     render() {
-        const { selectedRecord, visibleDistributeObjectBox, visibleDistributePolicyRuleBox } = this.state;
-        const { dataSource, visibleDistributeBox, hideBox } = this.props
+        const { visibleDistributeObjectBox, visibleDistributePolicyRuleBox } = this.state;
+        const { visibleDistributeBox, hideBox } = this.props
 
         const columns = [
             {

+ 262 - 92
src/components/datasource/accessConfig.jsx

@@ -1,130 +1,300 @@
 import React from 'react'
 import { connect } from 'dva'
-import { Table, Input, Col, Button, Switch, Icon, Tag } from 'antd'
-import PolicyRuleBox from './policyRuleBox'
+import { Layout, Card, Row, Col, Table, Input, Checkbox, Button, Switch, Icon, Tag } from 'antd'
+import FilterBox from '../chartDesigner/sections/filterBox'
 import AccessObjectBox from './accessObjectBox'
+import * as moment from 'moment'
+import './accessConfig.less'
+const { Content } = Layout
 
 const Search = Input.Search;
 
 class DataSourceAccessConfig extends React.Component{
+
     constructor(props){
         super(props);
         this.state={
+            inputRow: -1,
+            filterLabel: '',
             visibleAccessObjectBox: false,
-            visiblePolicyRuleBox: false
+            visiblePolicyRuleBox: false, // 显示过滤规则编辑窗口
+            currentPolicy: {}, // 选中的策略
+            visibleFilterBox: false
         }
-    } 
+    }
 
-    hideAccessObjectBox= () => {
-        this.setState(
-            {visibleAccessObjectBox:false})
+    componentDidMount() {
+        const { dispatch } = this.props;
+        dispatch({ type: 'user/fetchList' });
+    }
+
+    showAccessObjectBox = () => {
+        this.setState({ visibleAccessObjectBox:true })
+    }
+
+    hideAccessObjectBox = () => {
+        this.setState({ visibleAccessObjectBox:false })
+    }
+
+    showPolicyRuleBox= () => {
+        this.setState({ visiblePolicyRuleBox: true })
     }
 
     hidePolicyRuleBox= () => {
-        this.setState(
-            {visiblePolicyRuleBox:false})
+        this.setState({ visiblePolicyRuleBox: false })
+    }
+
+    setAccessObject = (group, geren) => {
+        const { dispatch } = this.props;
+        const { currentPolicy } = this.state;
+        let targets = group.map(g => ({
+            code: g.code,
+            name: g.name,
+            isGroup: true
+        })).concat(geren.map(g => ({
+            code: g.code,
+            name: g.name,
+            isGroup: false
+        })))
+        dispatch({ type: 'dataSourcePolicy/remoteSetTarget', policy: currentPolicy, targets });
+    }
+
+    createFilters = (filters) => {
+        const { dispatch } = this.props;
+        let { currentPolicy } = this.state;
+        currentPolicy.filters = filters;
+        dispatch({ type: 'dataSourcePolicy/remoteModify', policy: currentPolicy });
     }
 
+    /**
+     * 生成过滤规则文本
+     */
+    createFilterLabel = (filter) => {
+        let { label, operator, operatorLabel, type, value1, value2 } = filter;
+        let filterLabel;
 
+        if(type === 'string' || type === 'index') {
+            if(operator === 'null' || operator === 'notNull') {
+                filterLabel = `${label} ${operatorLabel}`;
+            }else {
+                filterLabel = `${label} ${operatorLabel} ${value1}`;
+            }
+        }else if(type === 'scale') {
+            if(operator === 'null' || operator === 'notNull') {
+                filterLabel = `${label} ${operatorLabel}`;
+            }else if(operator === 'between') {
+                filterLabel = `${label} ${operatorLabel} ${value1} ~ ${value2}`; 
+            }else {
+                filterLabel = `${label} ${operatorLabel} ${value1}`; 
+            }
+        }else if(type === 'time') {
+            value1 = moment(value1).format('YYYY/MM/DD');
+            value2 = moment(value2).format('YYYY/MM/DD');
+            if(operator === 'null' || operator === 'notNull') {
+                filterLabel = `${label} ${operatorLabel}`;
+            }else if(operator === 'between') {
+                filterLabel = `${label} ${operatorLabel} ${value1} ~ ${value2}`;
+            }else {
+                filterLabel = `${label} ${operatorLabel} ${value1}`;
+            }
+        }else if(type === 'categorical') {
+            if(operator === 'null' || operator === 'notNull') {
+                filterLabel = `${label} ${operatorLabel}`;
+            }else {
+                filterLabel = `${label} ${operatorLabel} ${value1}`;
+            }
+        }else {
+            filterLabel = '错误条件';
+        }
+        return filterLabel;
+    }
+    
     render() {
-        const { dataSource, dispatch } = this.props
-        const { visibleAccessObjectBox, visiblePolicyRuleBox } = this.state
+        const { dataSourcePolicy, dataSource, dispatch } = this.props;
+        const { visibleAccessObjectBox, visiblePolicyRuleBox, currentPolicy } = this.state;
+        
+        const polices = dataSourcePolicy.list;
+        const group = currentPolicy ? (currentPolicy.targets ? currentPolicy.targets.filter(t => t.isGroup) : []) : [];
+        const geren = currentPolicy ? (currentPolicy.targets ? currentPolicy.targets.filter(t => !t.isGroup) : []) : [];
+        const reg = new RegExp('([+ \\- & | ! ( ) { } \\[ \\] ^ \" ~ * ? : ( ) \/])', 'g'); // 需要转义的字符
+        let filterLabel = (this.state.filterLabel || '').replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(reg, '\\$1'); // 添加转义符号
+        
+        // let owner = user.list.find(l => l.code === dataSource.newOne.creator); // 数据源拥有者
+        // console.log(owner);
+        const columnData = dataSource.newOne.columns ? dataSource.newOne.columns.filter(c => c.using).map(c => ({
+            key: c.key,
+            label: c.alias,
+            name: c.name,
+            selection: [],
+            type: c.columnType,
+            groupable: c.groupable,
+            // filterable: c.filterable,
+            filterable: true,
+            bucketizable: c.bucketizable
+        })) : [];
+        
         const columns = [{
             title:'启用',
             dataIndex:'enabled',
-            render: enabled => <Switch defaultChecked={enabled} />
+            render: (v, r) => <Checkbox
+                onChange={(e) => {
+                    let policy = {
+                        ...r,
+                        enabled: e.target.checked
+                    }
+                    console.log(policy);
+                    dispatch({ type: 'dataSourcePolicy/remoteModify', policy });
+                }}
+                checked={v}
+            />,
+            width: 50
         },{
             title: '策略名',
-            dataIndex: 'policyName',
-            render: policyName => <span>{policyName}<Icon type='copy' /></span>
+            dataIndex: 'name',
+            render: (value, record, index) => <div key={index}>
+                {
+                    this.state.inputRow === index ? <Input value={value} suffix={<Icon style={{ cursor: 'pointer', color: '#52C41A' }} type="check-circle" onClick={() => {
+                        this.setState({
+                            inputRow: -1
+                        });
+                        dispatch({ type: 'dataSourcePolicy/remoteModify', policy: record });
+                    }}/>} onChange={(e) => {
+                        dispatch({ type: 'dataSourcePolicy/modify', policy: { ...record, name: e.target.value } });
+                    }} onBlur={() => {
+                        this.setState({
+                            inputRow: -1
+                        });
+                        dispatch({ type: 'dataSourcePolicy/remoteModify', policy: record });
+                    }} onPressEnter={() => {
+                        this.setState({
+                            inputRow: -1
+                        });
+                        dispatch({ type: 'dataSourcePolicy/remoteModify', policy: record });
+                    }}/> : <span onClick={() => {
+                        this.setState({
+                            inputRow: index
+                        });
+                    }}>
+                        { filterLabel ?
+                            ((value || '').split(new RegExp(`(${filterLabel})`, 'i')).map((fragment, i) => {
+                                return (
+                                    fragment.toLowerCase().replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(reg, '\\$1') === filterLabel.toLowerCase() ?
+                                    <span key={i} style={{fontWeight: 'bold', color: 'red'}} className="highlight">{fragment}</span> :
+                                    fragment
+                                )
+                            }
+                            )) : value
+                        }
+                    </span>
+                }
+                {
+                    this.state.inputRow !== index && <Icon style={{ cursor: 'pointer' }} type='edit' onClick={() => {
+                        this.setState({
+                            inputRow: index
+                        });
+                    }} />
+                }
+            </div>,
+            width: 150
+        }, {
+            title: '行开放策略',
+            dataIndex: 'filters',
+            render: (filter, record, index) => <div key={index}>
+                {filter.length > 0 ? filter.map((f, i) =>
+                    <Tag
+                        className='filter-tag'
+                        key={i}
+                        closable={false}
+                    >
+                        {this.createFilterLabel(f)}
+                    </Tag>
+                ) : <Tag key='all' className='filter-tag' closable={false}>所有数据</Tag>}
+                <Tag
+                    key='add'
+                    onClick={this.showPolicyRuleBox}
+                    className={`filter-tag filter-tag-add`}
+                >
+                    <Icon type="filter" />
+                </Tag>
+            </div>,
+            width: 500
         }, {
             title: '开放对象',
             dataIndex: 'targets',
-            render: targets => {
-                return (
-                    <div>
-                        {targets.map((item, index) => { return <Tag key={index}>{item.isGroup ? <Icon type='tags' /> : <Icon type='user' />}{item.value}</Tag> })}
-                        <Tag onClick={() => { this.setState({ visibleDistributeObjectBox: true }) }}><Icon type='plus' /></Tag>
-                    </div>
-                )
-            }
+            render: (targets, record, index) => <div key={index}>
+                {/* {
+                    <Tag><Icon type='geren' />{owner ? (owner.name || '拥有者' ) : '拥有者'}</Tag>
+                } */}
+                {
+                    targets.map(item => <Tag className='user-tag' key={item.code}>{item.isGroup ? <Icon type='group' /> : <Icon type='geren' />}{item.name}</Tag>)
+                }
+                <Tag key='add' onClick={this.showAccessObjectBox}><Icon type="plus" /></Tag>
+            </div>,
+            width: 300
         }, {
-            title: '行开放策略',
-            dataIndex: 'rowFilters',
-            render: rowFilters => {
-                return (
-                    <div>
-                        {rowFilters.map((filter, index) => {return <p key={index}>{filter.value}</p>})}
-                        <Tag onClick={() => {this.setState({visibleDistributePolicyRuleBox:true})}}><Icon type='plus'/></Tag>
-                    </div>
-                )
-            }
-        }, 
-        // {
-        //     title: '列开放策略',
-        //     dataIndex: 'columns',
-        //     render: columns => { return columns.filter((column) => !column.enabled).map((column, index) => { return (<span key={index}>全部开放</span>) }) }
-        // }, 
-        {
             title: '操作',
-            dataIndex: 'operation',
-            render: () => { return <span>显示影响 删除</span>}
-        }];
-
-        const data = [{
-            key: '1',
-            enabled: true,
-            policyName: '开放策略1',
-            targets: [{ isGroup: true, value: '销售' }, { isGroup:false, value: '李嘉' }],
-            rowFilters: [{ value: '部门字段包含售后,销售' }, { value: '公司名包含公司A' }],
-            columns: [{ name: 'department', alias: '部门', enabled: true }, { name: 'company', alias: '公司', enabled: false }]
+            render: (v,r) => <div><span style={{ cursor: 'pointer' }} onClick={() => {
+                dispatch({ type: 'dataSourcePolicy/remoteDelete', code: r.code });
+            }}>删除</span></div>,
+            width: 120
         }];
 
         return (
-            <div>
-                <Table
-                    columns={columns}
-                    dataSource={data}
-                    bordered
-                    title={() => {
-                        return (
-                            <div style={{ display: "flex", justifyContent: "space-between" }}>
-                                <Col span={4}>
-                                    <Switch></Switch><span>完全开放数据源</span>
-                                </Col>
-                                <Col span={12}>
-                                </Col>
-                                <Col span={4}>
-                                    <Search
-                                        style={{ display: "inline" }}
-                                        placeholder="input search text"
-                                        onSearch={value => console.log(value)}
-                                        enterButton
-                                    />
-                                </Col>
-                                <Col span={4} style={{textAlign:'right'}}>
-                                    <Button>新增策略</Button>
-                                </Col>
-                            </div>)
-                    }
-                    }
-                />
-            <AccessObjectBox 
-                visibleAccessObjectBox={visibleAccessObjectBox} 
-                onCancel={this.hideAccessObjectBox}
-            />
-            <PolicyRuleBox 
-                visiblePolicyRuleBox={visiblePolicyRuleBox}
-                onCancel={this.hidePolicyRuleBox}
-            
-            />
-            </div>
+            <Layout className='datasource-policy'>
+                <Content>
+                    <Card className='policy-body' title={
+                        <Row className='policy-tools' type='flex' justify='space-between'>
+                            <Col className='policy-public' style={{ display: 'flex' }}>
+                                完全开放数据源<Switch size="small"/>
+                            </Col>
+                            <Col className='search'>
+                                <Search
+                                    placeholder="请输入关键字"
+                                    value={this.state.filterLabel}
+                                    onChange={e => {
+                                        this.setState({
+                                            filterLabel: e.target.value
+                                        });
+                                    }}
+                                />
+                                <Button className='add-btn' onClick={() => {
+                                    dispatch({ type: 'dataSourcePolicy/remoteAdd', policy: {
+                                        enabled: false,
+                                        name: '新策略',
+                                        targets: [],
+                                        filters: []
+                                    } });
+                                }}>
+                                    添加策略
+                                </Button>
+                            </Col>
+                        </Row>
+                    }>
+                        <Table
+                            className='policy-table'
+                            columns={columns}
+                            dataSource={polices ? polices.filter(l => {
+                                let reg = new RegExp('(' + filterLabel + '){1}', 'ig');
+                                return (l.name || '').search(reg) !== -1;
+                             }).map((p, i) => ({ ...p, key: i })) : []}
+                            size='small'
+                            onRow={(record) => {
+                                return {
+                                    onClick: () => {this.setState({ currentPolicy:  record})}
+                                }
+                            }}
+                        />
+                    </Card>
+                    <FilterBox key={Math.random()} columns={columnData} filterData={currentPolicy.filters} visibleFilterBox={visiblePolicyRuleBox} hideFilterBox={this.hidePolicyRuleBox} createFilters={this.createFilters} />
+                    <AccessObjectBox key={Math.random()} visibleBox={visibleAccessObjectBox} hideBox={this.hideAccessObjectBox} okHandler={this.setAccessObject} group={group} geren={geren} />
+                </Content>
+            </Layout>
         );
-            }
-        }
+    }
+}
         
-function mapStateToProps({ present: {dataSource} }) {
-    return { dataSource }
+function mapStateToProps({ present: { user, dataSourcePolicy, dataSource } }) {
+    return { user, dataSourcePolicy, dataSource }
 }
 
 export default connect(mapStateToProps)(DataSourceAccessConfig);

+ 38 - 0
src/components/datasource/accessConfig.less

@@ -0,0 +1,38 @@
+.datasource-policy {
+    .ant-card-head {
+        .ant-card-head-title {
+            padding: 8px 0;
+            .policy-tools {
+                .policy-public {
+                    font-size: 14px;
+                    line-height: 2.4;
+                    .ant-switch {
+                        margin-top: 9px;
+                        margin-left: 5px;
+                    }
+                }
+                .policy-tips {
+                    flex-grow: 1;
+                    color: red;
+                    font-size: 14px;
+                    margin-left: 20px;
+                    line-height: 2.4;
+                }
+                .search {
+                    .ant-input-search {
+                        width: auto;
+                    }
+                    .add-btn {
+                        margin-left: 5px;
+                    }
+                }
+            }
+        }
+    }
+    .ant-card-body {
+        padding: 0;
+        .filter-tag, .user-tag {
+            margin: 2px 8px 2px 0;
+        }
+    }
+}

+ 172 - 6
src/components/datasource/accessObjectBox.jsx

@@ -1,19 +1,185 @@
 import React from 'react'
-import { Modal } from 'antd'
+import { Modal, Tabs, Table, Checkbox, Row, Col, Input } from 'antd'
+import { connect } from 'dva'
+import './accessObjectBox.less'
+const { TabPane } = Tabs
+const { Search } = Input
 
 
 class AccessObjectBox extends React.Component {
+    
+    constructor(props) {
+        super(props);
+        this.state = {
+            selectedGroups: props.group || [],
+            selectedUsers: props.geren || []
+        }
+    }
+
+    componentDidMount() {
+        const { dispatch } = this.props;
+        dispatch({ type: 'userGroup/fetchList' });
+        dispatch({ type: 'user/fetchList' });
+    }
+
+    getAccessObject = () => {
+        const { okHandler, hideBox } = this.props;
+        const { selectedGroups, selectedUsers } = this.state;
+        okHandler(selectedGroups, selectedUsers);
+        hideBox();
+    }
+
     render() {
-        const { visibleAccessObjectBox, onCancel} = this.props
+        const { visibleBox, hideBox, userGroup, user } = this.props;
+        const { selectedGroups, selectedUsers } = this.state;
+
+        const reg = new RegExp('([+ \\- & | ! ( ) { } \\[ \\] ^ \" ~ * ? : ( ) \/])', 'g'); // 需要转义的字符
+        let filterLabel = (this.state.filterLabel || '').replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(reg, '\\$1'); // 添加转义符号
+
+        const userGroupColumns = [{
+            key: 'check',
+            title: '选择',
+            render: (v, r, i) => <Checkbox
+                dataKey={r.key}
+                checked={!!selectedGroups.find(g => g.code === r.code)}
+                onChange={(e) => {
+                    const target = e.target;
+                    const checked = target.checked;
+                    let { selectedGroups } = this.state;
+                    if(checked) {
+                        selectedGroups.push({
+                            code: r.code,
+                            name: r.name,
+                            isGroup: true
+                        });
+                    }else {
+                        let index = selectedGroups.findIndex(g => g.code === r.code);
+                        selectedGroups.splice(index, 1);
+                    }
+                    this.setState({ selectedGroups });
+                }}
+            />,
+            width: 50
+        }, {
+            key: 'name',
+            title: '名称',
+            dataIndex: 'name',
+            width: 400,
+            render: value => <span>
+                { filterLabel ?
+                    ((value || '').split(new RegExp(`(${filterLabel})`, 'i')).map((fragment, i) => {
+                        return (
+                            fragment.toLowerCase().replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(reg, '\\$1') === filterLabel.toLowerCase() ?
+                            <span key={i} style={{fontWeight: 'bold', color: 'red'}} className="highlight">{fragment}</span> :
+                            fragment
+                        )
+                    }
+                    )) : value
+                }
+            </span>,
+        }];
+        const userColumns = [{
+            key: 'check',
+            title: '选择',
+            render: (v, r, i) => <Checkbox
+                dataKey={r.key}
+                checked={!!selectedUsers.find(g => g.code === r.code)}
+                onChange={(e) => {
+                    const target = e.target;
+                    const checked = target.checked;
+                    let { selectedUsers } = this.state;
+                    if(checked) {
+                        selectedUsers.push({
+                            code: r.code,
+                            name: r.fullName,
+                            isGroup: false
+                        });
+                    }else {
+                        let index = selectedUsers.findIndex(g => g.code === r.code);
+                        selectedUsers.splice(index, 1);
+                    }
+                    this.setState({ selectedUsers });
+                }}
+            />,
+            width: 50
+        }, {
+            key: 'fullName',
+            title: '名称',
+            dataIndex: 'fullName',
+            width: 400,
+            render: value => <span>
+                { filterLabel ?
+                    ((value || '').split(new RegExp(`(${filterLabel})`, 'i')).map((fragment, i) => {
+                        return (
+                            fragment.toLowerCase().replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(reg, '\\$1') === filterLabel.toLowerCase() ?
+                            <span key={i} style={{fontWeight: 'bold', color: 'red'}} className="highlight">{fragment}</span> :
+                            fragment
+                        )
+                    }
+                    )) : value
+                }
+            </span>,
+        }]
+
         return (
             <Modal
-                visible={visibleAccessObjectBox}
-                onCancel={onCancel}
+                className='accessobject-box'
+                title={
+                    <Row>
+                        <Col span={14}>选择分配对象</Col>
+                        <Col span={8}><Search 
+                            placeholder="请输入关键字"
+                            value={this.state.filterLabel}
+                            onChange={e => {
+                                this.setState({
+                                    filterLabel: e.target.value
+                                });
+                            }}
+                        /></Col>
+                    </Row>
+                }
+                visible={visibleBox}
+                onCancel={hideBox}
+                onOk={this.getAccessObject}
+                maskClosable={false}
+                destroyOnClose={true}
             >
-                <span>Test</span>
+                <Tabs 
+                    className='accessobject-tabs'
+                    defaultActiveKey="userGroup"
+                >
+                    <TabPane tab="用户组" key="userGroup" >
+                        <Table
+                            className='usergroup-table'
+                            columns={userGroupColumns}
+                            dataSource={userGroup.list.filter(l => {
+                                let reg = new RegExp('(' + filterLabel + '){1}', 'ig');
+                                return (l.name || '').search(reg) !== -1;
+                             }).map((l, i) => ({
+                                ...l,
+                                key: i
+                            }))}
+                            size='small'
+                        />
+                    </TabPane>
+                    <TabPane tab="用户" key="user" >
+                        <Table
+                            className='user-table'
+                            columns={userColumns}
+                            dataSource={user.list.filter(l => {
+                                let reg = new RegExp('(' + filterLabel + '){1}', 'ig');
+                                return (l.fullName || '').search(reg) !== -1;
+                             }).map((l, i) => ({
+                                ...l,
+                                key: i
+                            }))}
+                            size='small'
+                        />
+                    </TabPane>
+                </Tabs>
             </Modal>
         )
     }
 }
 
-export default AccessObjectBox
+export default connect(({ present: { userGroup, user } }) => ({ userGroup, user }))(AccessObjectBox);

+ 11 - 0
src/components/datasource/accessObjectBox.less

@@ -0,0 +1,11 @@
+.accessobject-box {
+    width: 60% !important;
+    .ant-modal-body {
+        padding: 0;
+        .ant-tabs {
+            .ant-tabs-bar {
+                margin: 0;
+            }
+        }
+    }
+}

+ 264 - 184
src/components/datasource/baseConfig.jsx

@@ -4,196 +4,276 @@ import { connect } from 'dva'
 const FormItem = Form.Item
 const SelectOption = Select.Option
 
-const DataSourceBaseConfig = ({ dataSource, dataConnect, dispatch, mode }) => {
+class DataSourceBaseConfig extends React.Component {
 
-    dispatch({ type: 'dataSource/remoteGroupList' });
-
-    const formItemLayout = {
-        labelCol: { span: 4 },
-        wrapperCol: { span: 20 },
-    };
-
-    let getGroup = () => {
-        const { groupList } = dataSource;
-        const { group } = dataSource.newOne;
-        let g1 = groupList.filter(g => g.code+'' === group+'')[0];
-        if(!g1) {
-            return ['-1']
-        }
-        if(g1.pcode === '-1') {
-            return [g1.code]
-        }else {
-            let g2 = groupList.filter(g => g.code+'' === g1.pcode+'')[0];
-            return [g2.code, g1.code]
+    constructor(props) {
+        super(props);
+        this.state = {
+            timeout: null
         }
     }
 
-    return (
-        <Form className='form-base' size='small'>
-            <Divider orientation="left">基本配置</Divider>
-            <FormItem label='数据源名称' {...formItemLayout}>
-                <Input
-                    value={dataSource.newOne.name}
-                    onChange={(e) => {
-                        dispatch({ type: 'dataSource/setNewModelField', name: 'name', value: e.target.value });
-                        dispatch({ type: 'dataSource/remoteModify' });
-                    }}>
-                </Input>
-            </FormItem>
-            {
-                dataSource.newOne.type==='file'?(
-                    <div>
-                        <Divider orientation="left">文件</Divider>
-                        <div>此处显示文件名</div>
-                    </div>
-                ):(
-                    <div>
-                        <Divider orientation="left">连接配置</Divider>
-                        <FormItem label='数据库类型' {...formItemLayout}>
-                            <Select
-                                disabled={true}
-                                value={dataSource.newOne.dbType}
-                                onChange={(value) => {
-                                    dispatch({ type: 'dataSource/setNewModelField', name: 'dbType', value: value} );
+    componentDidMount() {
+        const { dispatch } = this.props;
+        dispatch({ type: 'dataSource/remoteGroupList' });
+    }
+
+    render() {
+        const { dataSource, dispatch } = this.props;
+
+        const formItemLayout = {
+            labelCol: { span: 4 },
+            wrapperCol: { span: 20 },
+        };
+    
+        let getGroup = () => {
+            const { groupList } = dataSource;
+            const { group } = dataSource.newOne;
+            let g1 = groupList.filter(g => g.code+'' === group+'')[0];
+            if(!g1) {
+                return ['-1']
+            }
+            if(g1.pcode === '-1') {
+                return [g1.code]
+            }else {
+                let g2 = groupList.filter(g => g.code+'' === g1.pcode+'')[0];
+                return [g2.code, g1.code]
+            }
+        }
+    
+        return (
+            <Form className='form-base' size='small'>
+                <Divider orientation="left">基本配置</Divider>
+                <FormItem label='数据源名称' {...formItemLayout}>
+                    <Input
+                        value={dataSource.newOne.name}
+                        onBlur={(e) => {
+                            dispatch({ type: 'dataSource/setNewModelField', name: 'name', value: e.target.value });
+                            const timeout = this.state.timeout;
+                            timeout && window.clearTimeout(timeout);
+                            this.setState({
+                                timeout: window.setTimeout(() => {
                                     dispatch({ type: 'dataSource/remoteModify' });
-                                }}
-                            >
-                                <SelectOption value='oracle'>
-                                    ORACLE
-                                </SelectOption>
-                                <SelectOption value='mysql'>
-                                    MYSQL
-                                </SelectOption>
-                                <SelectOption value='sqlserver'>
-                                    SQLSERVER
-                                </SelectOption>
-                                <SelectOption value='sqlite'>
-                                    SQLITE
-                                </SelectOption>
-                            </Select>
-                        </FormItem>
-                        <Row>
-                            <Col span={19}>
-                                <FormItem label='数据库地址' {...{
-                                    labelCol: { span: 5 },
-                                    wrapperCol: { span: 19 }
-                                }}>
-                                    <Input
-                                        disabled={true}
-                                        value={dataSource.newOne.address}
-                                        onChange={(e) => {
-                                            dispatch({ type: 'dataSource/setNewModelField', name: 'address', value: e.target.value });
-                                            dispatch({ type: 'dataSource/remoteModify' });
-                                        }}
-                                    />
-                                </FormItem>
-                            </Col>
-                            <Col span={5}>
-                                <FormItem className='input-port' label='端口' {...{
-                                    labelCol: { span: 12 },
-                                    wrapperCol: { span: 12 }
-                                }}>
-                                    <InputNumber
-                                        disabled={true}
-                                        value={dataSource.newOne.port}
-                                        onChange={(value) => {
-                                            dispatch({ type: 'dataSource/setNewModelField', name: 'port', value: value });
-                                            dispatch({ type: 'dataSource/remoteModify' });
-                                        }}
-                                    />
-                                </FormItem>
-                            </Col>
-                        </Row>
-                        <FormItem label='数据库名' {...formItemLayout}>
-                            <Input
-                                disabled={true}
-                                value={dataSource.newOne.dbName}
-                                onChange={(e) => {
-                                    dispatch({ type: 'dataSource/setNewModelField', name: 'dbName', value: e.target.value });
+                                }, 500)
+                            });
+                        }}
+                        onChange={(e) => {
+                            dispatch({ type: 'dataSource/setNewModelField', name: 'name', value: e.target.value });
+                            // const timeout = this.state.timeout;
+                            // timeout && window.clearTimeout(timeout);
+                            // this.setState({
+                            //     timeout: window.setTimeout(() => {
+                            //         dispatch({ type: 'dataSource/remoteModify' });
+                            //     }, 500)
+                            // });
+                        }}>
+                    </Input>
+                </FormItem>
+                {
+                    dataSource.newOne.type==='file'?(
+                        <div>
+                            <Divider orientation="left">文件</Divider>
+                            <div>此处显示文件名</div>
+                        </div>
+                    ):(
+                        <div>
+                            <Divider orientation="left">连接配置</Divider>
+                            <FormItem label='数据库类型' {...formItemLayout}>
+                                <Select
+                                    disabled={true}
+                                    value={dataSource.newOne.dbType}
+                                    onChange={(value) => {
+                                        dispatch({ type: 'dataSource/setNewModelField', name: 'dbType', value: value} );
+                                        const timeout = this.state.timeout;
+                                        timeout && window.clearTimeout(timeout);
+                                        this.setState({
+                                            timeout: window.setTimeout(() => {
+                                                dispatch({ type: 'dataSource/remoteModify' });
+                                            }, 500)
+                                        });
+                                    }}
+                                >
+                                    <SelectOption value='oracle'>
+                                        ORACLE
+                                    </SelectOption>
+                                    <SelectOption value='mysql'>
+                                        MYSQL
+                                    </SelectOption>
+                                    <SelectOption value='sqlserver'>
+                                        SQLSERVER
+                                    </SelectOption>
+                                    <SelectOption value='sqlite'>
+                                        SQLITE
+                                    </SelectOption>
+                                </Select>
+                            </FormItem>
+                            <Row>
+                                <Col span={19}>
+                                    <FormItem label='数据库地址' {...{
+                                        labelCol: { span: 5 },
+                                        wrapperCol: { span: 19 }
+                                    }}>
+                                        <Input
+                                            disabled={true}
+                                            value={dataSource.newOne.address}
+                                            onChange={(e) => {
+                                                dispatch({ type: 'dataSource/setNewModelField', name: 'address', value: e.target.value });
+                                                const timeout = this.state.timeout;
+                                                timeout && window.clearTimeout(timeout);
+                                                this.setState({
+                                                    timeout: window.setTimeout(() => {
+                                                        dispatch({ type: 'dataSource/remoteModify' });
+                                                    }, 500)
+                                                });
+                                            }}
+                                        />
+                                    </FormItem>
+                                </Col>
+                                <Col span={5}>
+                                    <FormItem className='input-port' label='端口' {...{
+                                        labelCol: { span: 12 },
+                                        wrapperCol: { span: 12 }
+                                    }}>
+                                        <InputNumber
+                                            disabled={true}
+                                            value={dataSource.newOne.port}
+                                            onChange={(value) => {
+                                                dispatch({ type: 'dataSource/setNewModelField', name: 'port', value: value });
+                                                const timeout = this.state.timeout;
+                                                timeout && window.clearTimeout(timeout);
+                                                this.setState({
+                                                    timeout: window.setTimeout(() => {
+                                                        dispatch({ type: 'dataSource/remoteModify' });
+                                                    }, 500)
+                                                });
+                                            }}
+                                        />
+                                    </FormItem>
+                                </Col>
+                            </Row>
+                            <FormItem label='数据库名' {...formItemLayout}>
+                                <Input
+                                    disabled={true}
+                                    value={dataSource.newOne.dbName}
+                                    onChange={(e) => {
+                                        dispatch({ type: 'dataSource/setNewModelField', name: 'dbName', value: e.target.value });
+                                        const timeout = this.state.timeout;
+                                        timeout && window.clearTimeout(timeout);
+                                        this.setState({
+                                            timeout: window.setTimeout(() => {
+                                                dispatch({ type: 'dataSource/remoteModify' });
+                                            }, 500)
+                                        });
+                                    }}
+                                />
+                            </FormItem>
+                            <Row>
+                                <Col span={12}>
+                                    <FormItem label='用户名' {...{
+                                        labelCol: { span: 8 },
+                                        wrapperCol: { span: 16 }
+                                    }}>
+                                        <Input
+                                            disabled={true}
+                                            value={dataSource.newOne.userName}
+                                            onChange={(e) => {
+                                                dispatch({ type: 'dataSource/setNewModelField', name: 'userName', value: e.target.value });
+                                                const timeout = this.state.timeout;
+                                                timeout && window.clearTimeout(timeout);
+                                                this.setState({
+                                                    timeout: window.setTimeout(() => {
+                                                        dispatch({ type: 'dataSource/remoteModify' });
+                                                    }, 500)
+                                                });
+                                            }}
+                                        />
+                                    </FormItem>
+                                </Col>
+                                <Col span={12}>
+                                    <FormItem label='密码' {...{
+                                        labelCol: { span: 8 },
+                                        wrapperCol: { span: 16 }
+                                    }}>
+                                        <Input
+                                            disabled={true}
+                                            // value={dataSource.newOne.password}
+                                            onChange={(e) => {
+                                                let value = e.target.value;
+                                                dispatch({ type: 'dataSource/setNewModelField', name: 'password', value: value });
+                                                const timeout = this.state.timeout;
+                                                timeout && window.clearTimeout(timeout);
+                                                this.setState({
+                                                    timeout: window.setTimeout(() => {
+                                                        dispatch({ type: 'dataSource/remoteModify' });
+                                                    }, 500)
+                                                });
+                                                e.target.removeAttribute('value')
+                                            }}
+                                        />
+                                    </FormItem>
+                                </Col>
+                            </Row>
+                        </div>
+                    )
+                }
+                <Divider orientation="left">其他配置</Divider>
+                <FormItem label='所属分组' {...formItemLayout}>
+                    <Cascader
+                        value={getGroup()}
+                        allowClear={true}
+                        changeOnSelect={true}
+                        expandTrigger='hover'
+                        placeholder='未分组'
+                        options={[{pcode: '-1', code: '-1', label: '未分组'}].concat(dataSource.groupList).filter(g => g.pcode === '-1').map((p, i)=>{
+                            return {
+                                key: p.code,
+                                value: p.code,
+                                label: p.label,
+                                children: dataSource.groupList.filter(g => g.pcode === p.code && p.code !== '-1').map(c => {
+                                    return {
+                                        key: c.code,
+                                        value: c.code,
+                                        label: c.label
+                                    }
+                                })
+                            }
+                        })}
+                        onChange={(value, items) => {
+                            let v = value[1] !== undefined ? value[1] : value[0];
+                            dispatch({ type: 'dataSource/setNewModelField', name: 'group', value: v });
+                            
+                            const timeout = this.state.timeout;
+                            timeout && window.clearTimeout(timeout);
+                            this.setState({
+                                timeout: window.setTimeout(() => {
                                     dispatch({ type: 'dataSource/remoteModify' });
-                                }}
-                            />
-                        </FormItem>
-                        <Row>
-                            <Col span={12}>
-                                <FormItem label='用户名' {...{
-                                    labelCol: { span: 8 },
-                                    wrapperCol: { span: 16 }
-                                }}>
-                                    <Input
-                                        disabled={true}
-                                        value={dataSource.newOne.userName}
-                                        onChange={(e) => {
-                                            dispatch({ type: 'dataSource/setNewModelField', name: 'userName', value: e.target.value });
-                                            dispatch({ type: 'dataSource/remoteModify' });
-                                        }}
-                                    />
-                                </FormItem>
-                            </Col>
-                            <Col span={12}>
-                                <FormItem label='密码' {...{
-                                    labelCol: { span: 8 },
-                                    wrapperCol: { span: 16 }
-                                }}>
-                                    <Input
-                                        disabled={true}
-                                        // value={dataSource.newOne.password}
-                                        onChange={(e) => {
-                                            let value = e.target.value;
-                                            dispatch({ type: 'dataSource/setNewModelField', name: 'password', value: value });
-                                            dispatch({ type: 'dataSource/remoteModify' });
-                                            e.target.removeAttribute('value')
-                                        }}
-                                    />
-                                </FormItem>
-                            </Col>
-                        </Row>
-                    </div>
-                )
-            }
-            <Divider orientation="left">其他配置</Divider>
-            <FormItem label='所属分组' {...formItemLayout}>
-                <Cascader
-                    value={getGroup()}
-                    allowClear={true}
-                    changeOnSelect={true}
-                    expandTrigger='hover'
-					placeholder='未分组'
-					options={[{pcode: '-1', code: '-1', label: '未分组'}].concat(dataSource.groupList).filter(g => g.pcode === '-1').map((p, i)=>{
-						return {
-                            key: p.code,
-							value: p.code,
-							label: p.label,
-							children: dataSource.groupList.filter(g => g.pcode === p.code && p.code !== '-1').map(c => {
-                                return {
-                                    key: c.code,
-                                    value: c.code,
-                                    label: c.label
-                                }
-                            })
-						}
-					})}
-					onChange={(value, items) => {
-                        let v = value[1] !== undefined ? value[1] : value[0];
-                        dispatch({ type: 'dataSource/setNewModelField', name: 'group', value: v });
-                        dispatch({ type: 'dataSource/remoteModify' });
-					}}
-					
-				>
-				</Cascader>
-            </FormItem>
-            <FormItem className='textarea-desc' label='说明' {...formItemLayout}>
-                <Input.TextArea
-                    autosize={{ minRows: 2, maxRows: 5 }}
-                    value={dataSource.newOne.description}
-                    onChange={(e) => {
-                        dispatch({ type: 'dataSource/setNewModelField', name: 'description', value: e.target.value });
-                        dispatch({ type: 'dataSource/remoteModify' });
-                    }}
-                />
-            </FormItem>
-        </Form>
-    );
+                                }, 500)
+                            });
+                        }}
+                        
+                    >
+                    </Cascader>
+                </FormItem>
+                <FormItem className='textarea-desc' label='说明' {...formItemLayout}>
+                    <Input.TextArea
+                        autosize={{ minRows: 2, maxRows: 5 }}
+                        value={dataSource.newOne.description}
+                        onChange={(e) => {
+                            dispatch({ type: 'dataSource/setNewModelField', name: 'description', value: e.target.value });
+                            
+                            const timeout = this.state.timeout;
+                            timeout && window.clearTimeout(timeout);
+                            this.setState({
+                                timeout: window.setTimeout(() => {
+                                    dispatch({ type: 'dataSource/remoteModify' });
+                                }, 500)
+                            });
+                        }}
+                    />
+                </FormItem>
+            </Form>
+        );
+    }
 }
 
 function mapStateToProps({ present: {dataSource, dataConnect} }) {

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

@@ -4,7 +4,7 @@ import { connect } from 'dva'
 import './dataSource.less'
 import { dateFormat } from '../../utils/baseUtils'
 import GroupSelector from './groupSelector'
-import TransferBox from './transferBox';
+import TransferBox from '../chart/transferBox';
 import DeleteBox from './deleteBox';
 const { Content } = Layout
 const { Search } = Input
@@ -460,11 +460,15 @@ class DataSource extends React.Component {
                         />
                         <TransferBox
                             visibleTransferBox={visibleTransferBox}
-                            onCancel={() => {
+                            onOk={(obj) => {
+                                console.log(obj);
+                            }}
+                            hideBox={() => {
                                 this.setState({
                                     visibleTransferBox: false
                                 })
-                            }} />
+                            }}
+                        />
                         <DeleteBox
                             visibleDeleteBox={visibleDeleteBox}
                             onCancel={() => {

+ 17 - 12
src/components/datasource/dataSourceDetail.jsx

@@ -20,20 +20,22 @@ class DataSourceDetail extends React.Component {
             type: props.match.params.type,
             code: props.match.params.code,
             tab: props.match.params.tab,
+            // current: ['base', 'column', 'access', 'other'].indexOf(props.match.params.tab)
             current: ['base', 'column', 'access', 'other'].indexOf(props.match.params.tab)
         }
     }
 
     componentDidMount() {
         const { dispatch } = this.props;
-        const { code } = this.props.match.params;
+        const { type, code } = this.props.match.params;
         
         if(code !== 'create') {
             dispatch({ type: 'dataSource/remoteDetail', code: code })
+            dispatch({ type: 'dataSourcePolicy/fetchList', dataSourceCode: code });
         }else {
             dispatch({ type: 'dataSource/setNewModelFields', fields: [
-                { name: 'type', value: this.props.match.params.type },
-                { name: 'code', value: this.props.match.params.code }
+                { name: 'type', value: type },
+                { name: 'code', value: code }
             ] });
         }
     }
@@ -41,14 +43,16 @@ class DataSourceDetail extends React.Component {
     next() {
         const { type, current } = this.state;
         this.setState({ current: current + 1 });
-        let step = ['base', 'column', 'access', 'other'][current + 1];
+        // let step = ['base', 'column', 'access', 'other'][current + 1];
+        let step = ['base', 'column', 'other'][current + 1];
         this.props.dispatch({ type: 'main/redirect', path: '/datasource/' + type + '/create/' + step })
     }
 
     prev() {
         const { type, current } = this.state;
         this.setState({ current: current - 1 });
-        let step = ['base', 'column', 'access', 'other'][current - 1];
+        // let step = ['base', 'column', 'access', 'other'][current - 1];
+        let step = ['base', 'column', 'other'][current - 1];
         this.props.dispatch({ type: 'main/redirect', path: '/datasource/' + type + '/create/' + step })
     }
 
@@ -64,10 +68,10 @@ class DataSourceDetail extends React.Component {
             title: '数据列配置',
             content: <ColumnConfig mode={mode} />
         }, {
-            tabName: 'access',
-            title: '数据开放策略',
-            content: <AccessConfig mode={mode} />
-        }, {
+        //     tabName: 'access',
+        //     title: '数据开放策略',
+        //     content: <AccessConfig mode={mode} />
+        // }, {
             tabName: 'other',
             title: '完成',
             content: <OtherConfig mode={mode} />
@@ -125,15 +129,16 @@ class DataSourceDetail extends React.Component {
                             onChange={(key) => {
                                 dispatch({ type: 'main/redirect', path: '/datasource/' + type + '/' + code + '/' + key })
                                 this.setState({
-                                    tab: key
+                                    tab: key,
+                                    current: ['base', 'column', 'access', 'other'].indexOf(key)
                                 })
                             }}
                             tabBarExtraContent={
-                                <Button disabled={ ( ( type === 'database' && !dataSource.newOne.name) || ( type === 'file' && +1 === 2) ) ||
+                                current === 1 ? (<Button disabled={ ( ( type === 'database' && !dataSource.newOne.name) || ( type === 'file' && +1 === 2) ) ||
                                     (!dataSource.newOne.columns || dataSource.newOne.columns.length === 0)
                                 } type="primary" onClick={() => {
                                     dispatch({ type: 'dataSource/remoteModify' });
-                                }}>保存修改</Button>
+                                }}>保存修改</Button>) : null
                             }
                         >
                             {tabs.map((item, index) => {

+ 0 - 1
src/components/homePage/homePage.jsx

@@ -1,5 +1,4 @@
 import React from 'react';
-import { Layout } from 'antd'
 
 class HomePage extends React.Component {
     constructor(props) {

+ 20 - 0
src/constants/url.js

@@ -48,6 +48,16 @@ const URLS = {
     
     DATASOURCE_QUERY_DATACOLUMNS: BASE_URL + '/getColumnData', // 获得数据源下的列
 
+    DATASOURCE_POLICY_LIST: BASE_URL + '/getDbStrategys', // 获得数据源的策略
+
+    DATASOURCE_POLICY_ADD: BASE_URL + '/addStrategys', // 添加策略
+
+    DATASOURCE_POLICY_UPDATE: BASE_URL + '/updateStrategys', // 修改策略
+
+    DATASOURCE_POLICY_DELETE: BASE_URL + '/delDbStrategys', // 删除策略
+
+    DATASOURCE_POLICY_TARGET_UPDATE: BASE_URL + '/addObject', // 设置策略对象
+
     /***************************************数据连接配置***************************************/
 
     DATACONNECT_ADD: BASE_URL + '/inputDatabases', // 新增数据连接配置
@@ -86,6 +96,16 @@ const URLS = {
 
     CHART_AGGREGATETABLE_OPTION: BASE_URL + '/showPopulation', // 请求总体统计数据
 
+    CHART_POLICY_LIST: BASE_URL + '/getChartStrategys', // 获得图表的策略
+
+    CHART_POLICY_ADD: BASE_URL + '/addStrategys', // 添加策略
+
+    CHART_POLICY_UPDATE: BASE_URL + '/updateStrategys', // 修改策略
+
+    CHART_POLICY_DELETE: BASE_URL + '/delDbStrategys', // 删除策略
+
+    CHART_POLICY_TARGET_UPDATE: BASE_URL + '/addObject', // 设置策略对象
+
     /***************************************数据源分组***************************************/
 
     GROUP_DATASOURCE_LIST: BASE_URL + '/getConnectorGroup', // 获得数据源所有分组/子分组

+ 4 - 0
src/index.js

@@ -10,6 +10,8 @@ import chart from './models/chart'
 import dashboardDesigner from './models/dashboardDesigner';
 import userGroup from './models/userGroup'
 import user from './models/user'
+import chartPolicy from './models/chartPolicy'
+import dataSourcePolicy from './models/dataSourcePolicy'
 import './utils/baseUtils'
 import './index.less'
 import createLoading from 'dva-loading';
@@ -38,6 +40,8 @@ app.model(dashboard);  //报告与看板
 app.model(dashboardDesigner); // 看板设计
 app.model(userGroup); // 用户组
 app.model(user); // 用户
+app.model(chartPolicy); // 图表策略
+app.model(dataSourcePolicy); // 数据源策略
 
 // 4. Router
 app.router(indexRouter);

+ 12 - 0
src/models/chartDesigner.js

@@ -328,21 +328,29 @@ export default {
                 const { barConfig } = chartDesigner;
                 if(barConfig.xAxis.column.value && barConfig.yAxis.column.value) {
                     yield put({ type: 'fetchBarData' });
+                }else {
+                    yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
                 }
             }else if(viewType === 'pie') {
                 const { pieConfig } = chartDesigner;
                 if(pieConfig.xAxis.column.value && pieConfig.yAxis.column.value) {
                     yield put({ type: 'fetchPieData' });
+                }else {
+                    yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
                 }
             }else if(viewType === 'line') {
                 const { lineConfig } = chartDesigner;
                 if(lineConfig.xAxis.column.value && lineConfig.yAxis.column.value) {
                     yield put({ type: 'fetchLineData' });
+                }else {
+                    yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
                 }
             }else if(viewType === 'scatter') {
                 const { scatterConfig } = chartDesigner;
                 if(scatterConfig.xAxis.column.value && scatterConfig.yAxis.column.value) {
                     yield put({ type: 'fetchScatterData' });
+                }else {
+                    yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
                 }
             }else if(viewType === 'dataView') {
                 const { dataViewConfig } = chartDesigner;
@@ -351,11 +359,15 @@ export default {
                     dataViewConfig.sortType && 
                     dataViewConfig.count) {
                     yield put({ type: 'fetchDataViewData' });
+                }else {
+                    yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
                 }
             }else if(viewType === 'aggregateTable') {
                 const { aggregateTableConfig } = chartDesigner;
                 if(aggregateTableConfig.targetColumn.name && aggregateTableConfig.statistics.length > 0) {
                     yield put({ type: 'fetchAggregateTableData' });
+                }else {
+                    yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
                 }
             }else {
                 console.log('no viewType......')

+ 250 - 0
src/models/chartPolicy.js

@@ -0,0 +1,250 @@
+import { message } from 'antd'
+import * as service from '../services/index'
+import URLS from '../constants/url'
+import * as moment from 'moment'
+
+function getBodyFilter(modelFilter) {
+    let { name, label, operator, operatorLabel, type, value1, value2 } = modelFilter;
+    let bodyFilter = {
+        columnName: name,
+        columnLabel: label,
+        columnType: type,
+        symbol: operator,
+        symbolLabel: operatorLabel,
+        value: value1
+    };
+    if(type === 'scale' && operator === 'between') {
+        bodyFilter['value'] = value1 + ',' + value2;
+    }else if(type === 'time') {
+        // let v1 = dateFormat(new Date(value1),'yyyy-MM-dd hh:mm:ss');
+        // let v2 = dateFormat(new Date(value2),'yyyy-MM-dd hh:mm:ss');
+
+        let v1 = moment(value1).format('YYYY-MM-DD HH:mm:ss');
+        let v2 = moment(value2).format('YYYY-MM-DD HH:mm:ss');
+
+        if(operator === 'between') {
+            bodyFilter['value'] = v1 + ',' + v2;
+        }else {
+            bodyFilter['value'] = v1;
+        }
+    }
+    return bodyFilter;
+}
+function getModelFilter(resFilter) {
+    let { columnName, columnLabel, columnType, symbol, symbolLabel, value } = resFilter;
+    let modelFilter = {
+        name: columnName,
+        label: columnLabel,
+        type: columnType,
+        operator: symbol,
+        operatorLabel: symbolLabel,
+        value1: value
+    };
+    if(columnType === 'scale' && symbol === 'between') {
+        modelFilter['value1'] = value.split(',')[0];
+        modelFilter['value2'] = value.split(',')[1];
+    }else if(columnType === 'time') {
+        let value1 = value.split(',')[0];
+        let value2 = value.split(',')[1];
+        let v1 = moment(value1);
+        let v2 = moment(value2);
+
+        if(symbol === 'between') {
+            modelFilter['value1'] = v1;
+            modelFilter['value2'] = v2;
+        }else {
+            modelFilter['value'] = v1;
+        }
+    }
+    return modelFilter;
+}
+
+export default {
+    namespace: 'chartPolicy',
+    state: {
+        chartCode: null,
+        columnData: [],
+        list: [{
+            code: '1',
+                    enabled: true,
+                    name: '开放策略1',
+                    targets: [],
+                    filters: []
+        }]
+    },
+    reducers: {
+        list(state, action) {
+            const { list } = action;
+            return { ...state, list };
+        },
+        modify(state, action) {
+            const { policy } = action;
+            let { list } = state;
+            for(let i = 0; i < list.length; i++) {
+                if(list[i].code === policy.code) {
+                    list[i] = policy;
+                    break;
+                }
+            }
+            return { ...state, list };
+        }
+    },
+    effects: {
+        *fetchList(action, { select, call, put }) {
+            const body = action.chartCode;
+            try {
+                const res = yield call(service.fetch, {
+                    url: URLS.CHART_POLICY_LIST,
+                    body
+                });
+                console.log('请求图表策略列表', body, res);
+                if(!res.err && res.data.code > 0) {
+                    let list = (res.data.data && res.data.data.length > 0) ? res.data.data.sort((a, b) => new Date(a.createDate) - new Date(b.createDate)).map(d => ({
+                        code: d.strategies.id,
+                        enabled: d.strategies.isOpen+'' === '1',
+                        name: d.strategies.name,
+                        targets: d.userGroupName.map(g => ({
+                            code: g.ID+'',
+                            isGroup: true,
+                            name: g.NAME
+                        })).concat(d.userName.map(u => ({
+                            code: u.ID+'',
+                            isGroup: false,
+                            name: u.NAME
+                        }))),
+                        filters: d.strategies.ruleStr ? JSON.parse(d.strategies.ruleStr).map(f => getModelFilter(f)) : [],
+                    })) : [];
+                    yield put({ type: 'list', list });
+                }else {
+                    message.error('请求图表策略列表失败');
+                }
+            }catch(e) {
+                console.log(body, e);
+                message.error('读取图表策略列表错误');
+            }
+        },
+        *remoteAdd(action, { put, call, select }) {
+            const chartPolicy = yield select(state => state.present.chartPolicy);
+            const { chartCode } = action;
+            const { policy } = action;
+            const body = {
+                type: 'chart',
+                tarId: chartCode,
+                name: policy.name,
+                rule: policy.filters.map(f => getBodyFilter(f)),
+                isOpen: policy.enabled ? '1' : '0',
+                createBy: 'zhuth'
+            };
+            try {
+                const res = yield call(service.fetch, {
+                    url: URLS.CHART_POLICY_ADD,
+                    body,
+                });
+                console.log('添加策略', body, res);
+                if(!res.err && res.data.code > 0) {
+                    let { list } = chartPolicy;
+                    list.push({
+                        code: res.data.data,
+                        ...policy
+                    });
+                    yield put({ type: 'list', list });
+                }else {
+                    message.error('添加失败');
+                }
+            }catch(e) {
+                console.log(body, e);
+                message.error('添加失败');
+            }
+        },
+        *remoteModify(action, { put, call, select }) {
+            const { policy, chartCode } = action;
+            console.log(chartCode);
+            const body = {
+                id: policy.code,
+                type: 'chart',
+                tarId: chartCode,
+                name: policy.name,
+                rule: policy.filters.map(f => getBodyFilter(f)),
+                isOpen: policy.enabled ? '1' : '0',
+                createBy: 'zhuth'
+            };
+            try {
+                const res = yield call(service.fetch, {
+                    url: URLS.CHART_POLICY_UPDATE,
+                    body
+                });
+                console.log('修改图表策略', body, res);
+                if(!res.err && res.data.code > 0) {
+                    yield put({ type: 'modify', policy });
+                }else {
+                    message.error('修改失败');
+                }
+            }catch(e) {
+                console.log(body, e);
+                message.error('修改失败');
+            }
+        },
+        *remoteDelete(action, { put, call, select }) {
+            const chartPolicy = yield select(state => state.present.chartPolicy);
+            const { code } = action;
+            const body = [code];
+            try {
+                const res = yield call(service.fetch, {
+                    url: URLS.CHART_POLICY_DELETE,
+                    body
+                });
+                console.log('删除策略', body, res);
+                if(!res.err && res.data.code > 0) {
+                    let { list } = chartPolicy;
+                    for(let i = 0; i < list.length; i++) {
+                        if(list[i].code === code) {
+                            list.splice(i, 1);
+                            break;
+                        }
+                    }
+                    yield put({ type: 'list', list });
+                }else {
+                    message.error('删除失败');
+                }
+            }catch(e) {
+                console.log(body, e);
+                message.error('删除失败');
+            }
+        },
+        *remoteSetTarget(action, { put, call, select }) {
+            const { policy, targets } = action;
+            const body = {
+                stId: policy.code+'',
+                type: 'chart',
+                obj: targets.map(t => ({
+                    obId: t.code,
+                    objectType: t.isGroup ? '0' : '1'
+                }))
+            };
+            try {
+                const res = yield call(service.fetch, {
+                    url: URLS.CHART_POLICY_TARGET_UPDATE,
+                    body
+                });
+                console.log('设置策略对象', body, res);
+                if(!res.err && res.data.code > 0) {
+                    yield put({ type: 'modify', policy: {
+                        ...policy,
+                        targets
+                    } });
+                }else {
+                    message.error('设置对象失败');
+                }
+            }catch(e) {
+                console.log(body, e);
+                message.error('设置对象失败');
+            }
+        }
+    },
+    subscriptions: {
+        setup({ dispatch, history }) {
+            return history.listen(({ pathname, query }) => {
+            })
+        }
+    }
+};

+ 0 - 4
src/models/dashboardDesigner.js

@@ -1,7 +1,3 @@
-import { message } from 'antd'
-import * as service from '../services/index'
-import URLS from '../constants/url'
-
 export default {
     namespace: 'dashboardDesigner',
     state: {

+ 246 - 0
src/models/dataSourcePolicy.js

@@ -0,0 +1,246 @@
+import { message } from 'antd'
+import * as service from '../services/index'
+import URLS from '../constants/url'
+import * as moment from 'moment'
+
+function getBodyFilter(modelFilter) {
+    let { name, label, operator, operatorLabel, type, value1, value2 } = modelFilter;
+    let bodyFilter = {
+        columnName: name,
+        columnLabel: label,
+        columnType: type,
+        symbol: operator,
+        symbolLabel: operatorLabel,
+        value: value1
+    };
+    if(type === 'scale' && operator === 'between') {
+        bodyFilter['value'] = value1 + ',' + value2;
+    }else if(type === 'time') {
+        // let v1 = dateFormat(new Date(value1),'yyyy-MM-dd hh:mm:ss');
+        // let v2 = dateFormat(new Date(value2),'yyyy-MM-dd hh:mm:ss');
+
+        let v1 = moment(value1).format('YYYY-MM-DD HH:mm:ss');
+        let v2 = moment(value2).format('YYYY-MM-DD HH:mm:ss');
+
+        if(operator === 'between') {
+            bodyFilter['value'] = v1 + ',' + v2;
+        }else {
+            bodyFilter['value'] = v1;
+        }
+    }
+    return bodyFilter;
+}
+function getModelFilter(resFilter) {
+    let { columnName, columnLabel, columnType, symbol, symbolLabel, value } = resFilter;
+    let modelFilter = {
+        name: columnName,
+        label: columnLabel,
+        type: columnType,
+        operator: symbol,
+        operatorLabel: symbolLabel,
+        value1: value
+    };
+    if(columnType === 'scale' && symbol === 'between') {
+        modelFilter['value1'] = value.split(',')[0];
+        modelFilter['value2'] = value.split(',')[1];
+    }else if(columnType === 'time') {
+        let value1 = value.split(',')[0];
+        let value2 = value.split(',')[1];
+        let v1 = moment(value1);
+        let v2 = moment(value2);
+
+        if(symbol === 'between') {
+            modelFilter['value1'] = v1;
+            modelFilter['value2'] = v2;
+        }else {
+            modelFilter['value'] = v1;
+        }
+    }
+    return modelFilter;
+}
+
+export default {
+    namespace: 'dataSourcePolicy',
+    state: {
+        dataSourceCode: null,
+        columnData: [],
+        list: []
+    },
+    reducers: {
+        list(state, action) {
+            const { list } = action;
+            return { ...state, list };
+        },
+        modify(state, action) {
+            const { policy } = action;
+            let { list } = state;
+            for(let i = 0; i < list.length; i++) {
+                if(list[i].code === policy.code) {
+                    list[i] = policy;
+                    break;
+                }
+            }
+            return { ...state, list };
+        }
+    },
+    effects: {
+        *fetchList(action, { select, call, put }) {
+            const body = action.dataSourceCode;
+            try {
+                const res = yield call(service.fetch, {
+                    url: URLS.DATASOURCE_POLICY_LIST,
+                    body
+                });
+                console.log('请求数据源策略列表', body, res);
+                if(!res.err && res.data.code > 0) {
+                    let list = (res.data.data && res.data.data.length > 0) ? res.data.data.sort((a, b) => new Date(a.createDate) - new Date(b.createDate)).map(d => ({
+                        code: d.strategies.id,
+                        enabled: d.strategies.isOpen+'' === '1',
+                        name: d.strategies.name,
+                        targets: d.userGroupName.map(g => ({
+                            code: g.ID+'',
+                            isGroup: true,
+                            name: g.NAME
+                        })).concat(d.userName.map(u => ({
+                            code: u.ID+'',
+                            isGroup: false,
+                            name: u.NAME
+                        }))),
+                        filters: d.strategies.ruleStr ? JSON.parse(d.strategies.ruleStr).map(f => getModelFilter(f)) : [],
+                    })) : [];
+                    yield put({ type: 'list', list });
+                }else {
+                    message.error('请求数据源策略列表失败');
+                }
+            }catch(e) {
+                console.log(body, e);
+                message.error('读取数据源策略列表错误');
+            }
+        },
+        *remoteAdd(action, { put, call, select }) {
+            const dataSourcePolicy = yield select(state => state.present.dataSourcePolicy);
+            const dataSource = yield select(state => state.present.dataSource);
+            const { newOne } = dataSource;
+            const { policy } = action;
+            const body = {
+                type: 'base',
+                tarId: newOne.code,
+                name: policy.name,
+                rule: policy.filters.map(f => getBodyFilter(f)),
+                isOpen: policy.enabled ? '1' : '0',
+                createBy: 'zhuth'
+            };
+            try {
+                const res = yield call(service.fetch, {
+                    url: URLS.DATASOURCE_POLICY_ADD,
+                    body,
+                });
+                console.log('添加策略', body, res);
+                if(!res.err && res.data.code > 0) {
+                    let { list } = dataSourcePolicy;
+                    list.push({
+                        code: res.data.data,
+                        ...policy
+                    });
+                    yield put({ type: 'list', list });
+                }else {
+                    message.error('添加失败');
+                }
+            }catch(e) {
+                console.log(body, e);
+                message.error('添加失败');
+            }
+        },
+        *remoteModify(action, { put, call, select }) {
+            const dataSource = yield select(state => state.present.dataSource);
+            const { policy } = action;
+            const { newOne } = dataSource;
+            const body = {
+                id: policy.code,
+                type: 'base',
+                tarId: newOne.code,
+                name: policy.name,
+                rule: policy.filters.map(f => getBodyFilter(f)),
+                isOpen: policy.enabled ? '1' : '0',
+                createBy: 'zhuth'
+            };
+            try {
+                const res = yield call(service.fetch, {
+                    url: URLS.DATASOURCE_POLICY_UPDATE,
+                    body
+                });
+                console.log('修改数据源策略', body, res);
+                if(!res.err && res.data.code > 0) {
+                    yield put({ type: 'modify', policy });
+                }else {
+                    message.error('修改失败');
+                }
+            }catch(e) {
+                console.log(body, e);
+                message.error('修改失败');
+            }
+        },
+        *remoteDelete(action, { put, call, select }) {
+            const dataSourcePolicy = yield select(state => state.present.dataSourcePolicy);
+            const { code } = action;
+            const body = [code];
+            try {
+                const res = yield call(service.fetch, {
+                    url: URLS.DATASOURCE_POLICY_DELETE,
+                    body
+                });
+                console.log('删除策略', body, res);
+                if(!res.err && res.data.code > 0) {
+                    let { list } = dataSourcePolicy;
+                    for(let i = 0; i < list.length; i++) {
+                        if(list[i].code === code) {
+                            list.splice(i, 1);
+                            break;
+                        }
+                    }
+                    yield put({ type: 'list', list });
+                }else {
+                    message.error('删除失败');
+                }
+            }catch(e) {
+                console.log(body, e);
+                message.error('删除失败');
+            }
+        },
+        *remoteSetTarget(action, { put, call, select }) {
+            const { policy, targets } = action;
+            const body = {
+                stId: policy.code+'',
+                type: 'base',
+                obj: targets.map(t => ({
+                    obId: t.code,
+                    objectType: t.isGroup ? '0' : '1'
+                }))
+            };
+            try {
+                const res = yield call(service.fetch, {
+                    url: URLS.DATASOURCE_POLICY_TARGET_UPDATE,
+                    body
+                });
+                console.log('设置策略对象', body, res);
+                if(!res.err && res.data.code > 0) {
+                    yield put({ type: 'modify', policy: {
+                        ...policy,
+                        targets
+                    } });
+                }else {
+                    message.error('设置对象失败');
+                }
+            }catch(e) {
+                console.log(body, e);
+                message.error('设置对象失败');
+            }
+        }
+    },
+    subscriptions: {
+        setup({ dispatch, history }) {
+            return history.listen(({ pathname, query }) => {
+            })
+        }
+    }
+};

+ 2 - 1
src/models/user.js

@@ -28,9 +28,10 @@ export default {
                 const res = yield call(service.fetch, {
                     url: URLS.USER_LIST
                 });
+                console.log('请求用户列表', res);
                 if(!res.err && res.data.code > 0) {
                     const list = res.data.data.map(d => ({
-                        code: d.id,
+                        code: d.id+'',
                         userName: d.userName,
                         fullName: d.name,
                         role: d.role === '管理员' ? '管理员' : '普通用户'

+ 0 - 1
src/models/userGroup.js

@@ -106,7 +106,6 @@ export default {
                         member: []
                     }));
                     yield put({ type: 'list', list: list.sort((a, b) => a.createTime - b.createTime) });
-                    yield put({ type: 'chageSelectedGroup', group: userGroup.selectedGroup || list[0] });
                 }else {
                     console.log(res.err || res.data.msg);
                     message.error('请求用户组列表失败:' + res.err || res.data.msg);

+ 0 - 11
src/routes/router.js

@@ -49,17 +49,6 @@ const PrivateRoute = ({ component: Component, ...rest }) => (
                 }
             }
 
-            // return (window.localStorage.getItem("token") !== "false") ? (
-            //     <Component {...props} />
-            // ) : (
-            //         <Redirect
-            //             to={{
-            //                 pathname: "/login",
-            //                 state: { from: props.location }
-            //             }}
-            //         />
-            //     )
-            // }
             const isAuthenticated = window.localStorage.getItem("token") !== "false";
             return <RootLayout location={props.location} isAuthenticated={isAuthenticated}>
                 <Component isAuthenticated={isAuthenticated} {...props} />

+ 1 - 1
src/xiaomi.jsx

@@ -1,5 +1,5 @@
 import React from 'react'
-import { Table, Row, Col } from 'antd'
+import { Table, Row } from 'antd'
 import Echarts from 'echarts-for-react'
 
 const columns = [