Browse Source

样式设计/切换账号清空model/数据源图表前端权限控制

zhuth 7 years ago
parent
commit
c6a896cd66

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

@@ -58,7 +58,8 @@ class ChartList extends React.Component {
     }
 
     generateCard() {
-        const { chart, dispatch } = this.props;
+        const { main, chart, dispatch } = this.props;
+        const { currentUser } = main;
         const groupList = chart.groupList;
         const { selectedRecord } = this.state;
         const list = chart.list;
@@ -66,10 +67,13 @@ class ChartList extends React.Component {
 
         const reg = new RegExp('([+ \\- & | ! ( ) { } \\[ \\] ^ \" ~ * ? : ( ) \/])', 'g'); // 需要转义的字符
         let filterLabel = chart.filterLabel.replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(reg, '\\$1'); // 添加转义符号
-
+        console.log(selectedRecord, currentUser);
         const operationMenu = (
             <Menu className='menu-operation'>
-                <Menu.Item onClick={() => {
+                <Menu.Item>
+                    <Icon type="link" />分享
+                </Menu.Item>
+                {selectedRecord && currentUser.code === selectedRecord.creatorCode && <Menu.Item onClick={() => {
                     const { selectedRecord } = this.state;
                     // const selectedChartDataSourceCode = selectedRecord ? selectedRecord.code : '';
                     const selectedChartCode = selectedRecord ? selectedRecord.code : '';
@@ -79,26 +83,26 @@ class ChartList extends React.Component {
                     this.setState({visibleDistributeBox: true})
                 }}> 
                     <Icon type='share-alt'/>分发
-                </Menu.Item>
-                <Menu.SubMenu className='setgroupmenu' title={<div><Icon style={{ marginRight: '6px' }} type='profile' />移动到</div>}>
+                </Menu.Item>}
+                { selectedRecord && currentUser.code === selectedRecord.creatorCode && <Menu.SubMenu className='setgroupmenu' title={<div><Icon style={{ marginRight: '6px' }} type='profile' />移动到</div>}>
                     {this.createGroupMenu(selectedRecord)}
-                </Menu.SubMenu>
+                </Menu.SubMenu>}
                 <Menu.Divider />
-                <Menu.Item
+                { selectedRecord && currentUser.code === selectedRecord.creatorCode && <Menu.Item
                     onClick={()=>{
                         this.setState({ visibleTransferBox: true})
                     }}
                 >
                     <Icon type="swap" />移交
-                </Menu.Item>
-                <Menu.Item
+                </Menu.Item>}
+                { selectedRecord && currentUser.code === selectedRecord.creatorCode && <Menu.Item
                     onClick={(e) => {
                         this.setState({ visibleDeleteBox: true})
                     }}
                 >
                     <Icon type="delete" />删除
                     
-                </Menu.Item>
+                </Menu.Item>}
             </Menu>
         )
         
@@ -151,14 +155,14 @@ class ChartList extends React.Component {
                     }
                     cover={
                         <Col className='cover-body'>
-                            <Row className='thumb' onClick={() => {
+                            <Row className='thumb' onClick={l.access ? () => {
                                 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}/>
+                            } : () => {}}>
+                                {!l.access && <div className='deny-body'>
+                                    <div className='deny-tip'>没有对应数据源的权限</div>
+                                </div>}
+                                <Thumbnail style={{ opacity: l.access ? 1 : 0.3 }} type={l.type} code={l.code} option={l.chartOption}/>
                             </Row>
                             <Row className='desc'>
                                 <Ellipsis tooltip={l.description&&l.description.length > 16} lines={2}>{
@@ -178,7 +182,7 @@ class ChartList extends React.Component {
                             </Row>
                             <Row className='footer' type='flex' justify='end' align='bottom'>
                                 <Col style={{ textAlign: 'left' }} span={22}>
-                                    <Row>{l.creator} {dateFormat(l.createTime, 'yyyy-MM-dd')}</Row>
+                                    <Row>{l.creatorName} {dateFormat(l.createTime, 'yyyy-MM-dd')}</Row>
                                 </Col>
                                 <Col span={2} style={{ textAlign: 'right' }}>
                                     <Dropdown overlay={operationMenu} trigger={['click']}>
@@ -472,9 +476,4 @@ class ChartList extends React.Component {
     }
 }
 
-
-function mapStateToProps(state) {
-    return { chart: state.present.chart }
-}
-
-export default connect(mapStateToProps)(ChartList)
+export default connect(({ present: { main, chart } }) => ({ main, chart }))(ChartList)

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

@@ -55,13 +55,21 @@
                     .thumb {
                         height: 132px;
                         .deny-body {
+                            height: 100%;
+                            width: 100%;
                             display: flex;
                             position: absolute;
-                            height: 100%;
                             text-align: center;
                             justify-content: center;
                             flex-direction: column;
                             z-index: 1;
+                            .deny-tip {
+                                height: 40px;
+                                line-height: 40px;
+                                background: rgba(255,255,255,.7);
+                                border: 2px solid #dd6a6a;
+                                font-weight: bold;
+                            }
                         }
                         cursor: pointer;
                         canvas {

+ 28 - 6
src/components/chart/resolveChartOption.js

@@ -1,11 +1,12 @@
 import * as moment from 'moment'
+import GRANULARITY from '../chartDesigner/sections/granularity'
 
 export default (config, styleConfig, silent, thumbnail) => {
-    const { viewType, option } = config;
+    const { viewType, chartConfig, option } = config;
     let o;
     switch(viewType) {
         case 'bar': {
-            o = barConfig(option, styleConfig, silent, thumbnail);
+            o = barConfig(chartConfig, option, styleConfig, silent, thumbnail);
             break;
         }
         case 'pie': {
@@ -35,13 +36,14 @@ export default (config, styleConfig, silent, thumbnail) => {
     return o;
 }
 
-function barConfig(option, styleConfig, silent, thumbnail) {
+function barConfig(chartConfig, option, styleConfig, silent, thumbnail) {
     const { xAxis, serieses, xTitle, yTitle } = option;
-
+    const { bar } = styleConfig;
+    const { stack, visibleToolTip } = (bar || { stack: false, visibleToolTip: true });
     let o = {
         animation: !thumbnail,
         tooltip : {
-            show: !silent && !thumbnail,
+            show: visibleToolTip && !silent && !thumbnail,
             trigger: "axis",
             axisPointer: {
                 type: "cross",
@@ -64,7 +66,7 @@ function barConfig(option, styleConfig, silent, thumbnail) {
         xAxis: [{
             show: !thumbnail,
             type: 'category',
-            data: xAxis,
+            data: barXAxis(chartConfig, xAxis),
             name: xTitle || '横轴',
         }],
         yAxis: [{
@@ -77,6 +79,7 @@ function barConfig(option, styleConfig, silent, thumbnail) {
                 name: s.name,
                 type: 'bar',
                 data: s.value,
+                stack: stack ? '1' : null,
                 showSymbol: !thumbnail,
                 silent,
             }
@@ -306,4 +309,23 @@ function tableViewConfig(option, styleConfig, silent, thumbnail) {
         })
     };
     return o;
+}
+
+function barXAxis(chartConfig, xAxis) {
+    let data = xAxis;
+    
+    if(chartConfig) {
+        const { xAxis: cx } = chartConfig;
+        const { column, granularity } = cx;
+        const { label: cLabel, type: cType, value: cValue } = column;
+        const { label: gLabel, value: gValue } = granularity;
+
+        if(cType === 'time') {
+            let s = GRANULARITY['time'];
+            let g = s.find(d => d.value === gValue);
+            data = xAxis.map(x => (g.replaceFunction && typeof g.replaceFunction === 'function') ? g.replaceFunction(x) : x);
+        }
+    }
+    
+    return data;
 }

+ 2 - 2
src/components/chart/thumbnail.jsx

@@ -2,13 +2,13 @@ import React from 'react'
 import Echarts from 'echarts-for-react'
 import resolveChartOption from './resolveChartOption'
 
-const Thumbnail = ({ type, code, option }) => {
+const Thumbnail = ({ style, type, code, option }) => {
     const newOption = resolveChartOption(option || {}, {}, true, true);
     const viewType = ['bar', 'line', 'pie', 'scatter'].indexOf(type) !== -1 ? 'echarts' :
         (['aggregateTable', 'dataView'].indexOf(type) !== -1 ? 'table' : 'default');
         
     return (
-        <div style={{ width: '100%', height: '100%' }}>
+        <div style={{ ...style, width: '100%', height: '100%' }}>
             {viewType === 'echarts' ? <Echarts
                 key={code}
                 option={newOption}

+ 2 - 1
src/components/chartDesigner/charts/echartsView.jsx

@@ -5,7 +5,8 @@ import resolveChartOption from '../../chart/resolveChartOption'
 import { hashcode } from '../../../utils/baseUtils'
 
 const EchartsView = ({ chartDesigner, dispatch }) => {
-    const option = resolveChartOption(chartDesigner.chartOption);
+    const { chartOption, styleConfig } = chartDesigner;
+    const option = resolveChartOption(chartOption, styleConfig);
     return <Echarts
         key={hashcode(option)}
         option={option}

+ 34 - 3
src/components/chartDesigner/content.less

@@ -10,7 +10,7 @@
             padding-bottom: 40px;
             .sider-tabs {
                 height: 100%;
-                padding-top: 36px;
+                padding-top: 38px;
                 
                 .ant-tabs-bar {
                     margin-top: -36px;
@@ -26,8 +26,39 @@
                     margin: 0;
                     padding: 7px 16px;
                 }
-                .chartconfig, .otherconfig {
-                    padding: 0 10px;
+                .ant-tabs-tabpane {
+                    .ant-collapse {
+                        .ant-collapse-item {
+                            .ant-collapse-header {
+                                padding: 4px 0 4px 40px;
+                                .arrow {
+                                    line-height: 30px;
+                                }
+                            }
+                            .ant-collapse-content {
+                                .ant-collapse-content-box {
+                                    padding: 0;
+                                }
+                            }
+                        }
+                    }
+                    .ant-form {
+                        padding: 0px 10px;
+                    }
+                }
+                .chartconfig {
+                    .ant-cascader-picker {
+                        .ant-cascader-picker-label {
+                            height: 100%;
+                            padding: 0 6px;
+                            .cascader-label {
+                                margin-left: 6px;
+                            }
+                            .empty-label {
+                                color: #bfbfbf;
+                            }
+                        }
+                    }
                 }
             }
         }

+ 23 - 10
src/components/chartDesigner/header.jsx

@@ -27,13 +27,21 @@ class Header extends React.Component {
         });
     }
 
+    isOwner = () => {
+        const { chartDesigner, main } = this.props;
+        const { creatorCode } = chartDesigner;
+        const { currentUser } = main;
+        console.log(currentUser, chartDesigner);
+        return currentUser.code === creatorCode;
+    }
+
     render() {
         const { chartDesigner, dispatch, past, future } = this.props;
         const { newHeaderLabel } = this.state;
         return (
             <div className='header'>
                 <div className='header-item toolbar-back'>
-                    <Popconfirm
+                    {this.isOwner() && <Popconfirm
                         placement="bottomLeft"
                         title="离开前保存修改吗?"
                         visible={this.state.visibleConfirm}
@@ -61,15 +69,20 @@ class Header extends React.Component {
                         }}>
                             <Icon type='left' />返回
                         </Button>
-                    </Popconfirm>
-                    <Button className='button-uodo' onClick={() => {
+                    </Popconfirm>}
+                    {!this.isOwner() && <Button onClick={(e) => {
+                        dispatch({ type: 'main/goBack', path: '/chart' });
+                    }}>
+                        <Icon type='left' />返回
+                    </Button>}
+                    {this.isOwner() && <Button className='button-uodo' onClick={() => {
                         if(chartDesigner.code && chartDesigner.code !== -1) {
                             dispatch({ type: 'chart/remoteModify' });
                         }else {
                             dispatch({ type: 'chart/remoteAdd' });
                         }
                         dispatch({ type: 'chartDesigner/setDirty', dirty: false });
-                    }}><Icon type='save' />保存</Button>
+                    }}><Icon type='save' />保存</Button>}
                 </div>
                 <div className='header-item toolbar-title'>
                     <Input
@@ -88,7 +101,7 @@ class Header extends React.Component {
                 </div>
                 <div className='header-item toolbar-buttons'>
                     <div>
-                        <Tooltip title='复制新增'>
+                        {this.isOwner() && <Tooltip title='复制新增'>
                             <Button className='tools-btn' icon='copy' onClick={() => {
                                 this.setState({
                                     visibleCopyBox: true,
@@ -96,8 +109,8 @@ class Header extends React.Component {
                                 })
                             }}>
                             </Button>
-                        </Tooltip>
-                        <Modal
+                        </Tooltip>}
+                        {this.isOwner() && <Modal
                             title="以当前图表配置创建新的图表副本"
                             visible={this.state.visibleCopyBox}
                             okText='创建'
@@ -110,7 +123,7 @@ class Header extends React.Component {
                                     newHeaderLabel: e.target.value
                                 });
                             }} />
-                        </Modal>
+                        </Modal>}
                         <Tooltip title='撤销'>
                             <Button className='tools-btn' icon='undo' disabled={past.length === 0} onClick={() => {
                                 dispatch(ActionCreators.undo());
@@ -130,7 +143,7 @@ class Header extends React.Component {
 
 function mapStateToProps(state) {
     const { past, present, future } = state;
-    const chartDesigner = present.chartDesigner;
-    return { chartDesigner, past, future }
+    const { main, chartDesigner } = present;
+    return { main, chartDesigner, past, future }
 }
 export default connect(mapStateToProps)(Header);

+ 0 - 16
src/components/chartDesigner/layout.less

@@ -38,22 +38,6 @@
             margin-bottom: 0;
         }
     }
-    .ant-tabs {
-        .chartconfig {
-            .ant-cascader-picker {
-                .ant-cascader-picker-label {
-                    height: 100%;
-                    padding: 0 6px;
-                    .cascader-label {
-                        margin-left: 6px;
-                    }
-                    .empty-label {
-                        color: #bfbfbf;
-                    }
-                }
-            }
-        }
-    }
     .ant-tabs-bar {
         margin: 0;
     }

+ 29 - 0
src/components/chartDesigner/sections/granularity.js

@@ -0,0 +1,29 @@
+export default {
+    ordinal: null,
+    categorical: null,
+    time: [{
+        value: 'year',
+        label: '年',
+        replaceFunction: (v) => v + '年',
+    }, {
+        value: 'halfYear',
+        label: '半年',
+        replaceFunction: (v) => v.replace('-H1', '上半年').replace('-H2', '下半年'),
+    }, {
+        value: 'month',
+        label: '月',
+        replaceFunction: (v) => v.replace('-M', '年') + '月',
+    }, {
+        value: 'quarter',
+        label: '季度',
+        replaceFunction: (v) => v.replace('-Q1', '年1季度').replace('-Q2', '年2季度').replace('-Q3', '年3季度').replace('-Q4', '年4季度'),
+    }, {
+        value: 'week',
+        label: '周',
+        replaceFunction: (v) => v.replace('-W', '年') + '周',
+    }, {
+        value: 'day',
+        label: '日',
+        replaceFunction: (v) => v,
+    }]
+}

+ 90 - 0
src/components/chartDesigner/sections/style/bar.jsx

@@ -0,0 +1,90 @@
+import React from 'react'
+import { connect } from 'dva'
+import { Collapse, Menu, Form, Checkbox, Row, Col, Select, Radio, Input, InputNumber } from 'antd'
+const SubMenu = Menu.SubMenu;
+const MenuItemGroup = Menu.ItemGroup;
+
+class BarStyle extends React.Component {
+    
+    constructor(props) {
+        super(props);
+        this.state = {
+            layoutColumn: null,
+            formatColumn: null,
+            colorColumn: null,
+        }
+    }
+
+    render() {
+        const { chartDesigner, formItemLayout, dispatch } = this.props;
+        const { layoutColumn, formatColumn, colorColumn } = this.state;
+        const { styleConfig } = chartDesigner;
+        const { bar } = (styleConfig || {  });
+
+        return (
+            <Collapse defaultActiveKey={['chart', 'legend', 'color']}>
+                <Collapse.Panel className='chart' header='图形' key='chart'>
+                    <Form>
+                        <Form.Item label='类型' {...formItemLayout}>
+                            <Select defaultValue='default' onChange={(value) => {
+                                const stack = value === 'stack';
+                                dispatch({ type: 'chartDesigner/setField', name: 'styleConfig', value: { ...styleConfig, bar: { ...bar, stack }} });
+                            }}>
+                                <Select.Option value='default'>标准</Select.Option>
+                                <Select.Option value='stack'>堆叠</Select.Option>
+                            </Select>
+                        </Form.Item>
+                        <Form.Item label='显示ToolTip' {...formItemLayout}>
+                            <Checkbox onChange={(e) => {
+                                const checked = e.target.checked;
+                                dispatch({ type: 'chartDesigner/setField', name: 'styleConfig', value: { ...styleConfig, bar: { ...bar, visibleToolTip: checked }} });
+                            }}/>
+                        </Form.Item>
+                        <Form.Item label='显示标签' {...formItemLayout}>
+                            <div>
+                                <Checkbox defaultChecked={false} onChange={(e) => {
+                                    const checked = e.target.checked;
+                                    // dispatch({ type: 'chartDesigner/setField', name: 'styleConfig', value: { ...styleConfig, table: { ...table, bordered: checked }} });
+                                }}/>
+                                <Select onChange={null} >
+                                    <Select.Option value='left'>内部</Select.Option>
+                                    <Select.Option value='center'>外部</Select.Option>
+                                </Select>
+                            </div>
+                        </Form.Item>
+                    </Form>
+                </Collapse.Panel>
+                <Collapse.Panel header='图例' key='legend'>
+                    <Form>
+                        <Form.Item label='显示' {...formItemLayout}>
+                            <Checkbox onChange={(e) => {
+                                    const checked = e.target.checked;
+                                    // dispatch({ type: 'chartDesigner/setField', name: 'styleConfig', value: { ...styleConfig, table: { ...table, bordered: checked }} });
+                                }}/>
+                        </Form.Item>
+                        <Form.Item label='位置' {...formItemLayout}>
+                            <Select>
+                                <Select.Option value='top'>顶部</Select.Option>
+                                <Select.Option value='left'>左侧</Select.Option>
+                                <Select.Option value='bottom'>右侧</Select.Option>
+                                <Select.Option value='right'>底部</Select.Option>
+                            </Select>
+                        </Form.Item>
+                    </Form>
+                </Collapse.Panel>
+                <Collapse.Panel header='颜色' key='color'>
+                    <Form>
+                        <Form.Item label='交替色' {...formItemLayout}>
+                            <Radio.Group>
+                                <Radio value={1}>行交替</Radio>
+                                <Radio value={2}>列交替</Radio>
+                            </Radio.Group>
+                        </Form.Item>
+                    </Form>
+                </Collapse.Panel>
+            </Collapse>
+        )
+    }
+}
+
+export default connect(({ present: { chartDesigner } }) => ({ chartDesigner }))(BarStyle);

+ 3 - 0
src/components/chartDesigner/sections/style/index.jsx

@@ -1,4 +1,5 @@
 import TableStyle from './table'
+import BarStyle from './bar'
 
 export default ({ viewType }) => {
     let styleForm = <div>无可用样式配置</div>;
@@ -9,6 +10,8 @@ export default ({ viewType }) => {
 
     if(viewType === 'dataView') {
         styleForm = <TableStyle formItemLayout={formItemLayout}/>
+    }else if(viewType === 'bar') {
+        styleForm = <BarStyle formItemLayout={formItemLayout}/>
     }
     return styleForm;
 }

+ 0 - 1
src/components/chartDesigner/sections/style/table.jsx

@@ -99,7 +99,6 @@ class TableStyle extends React.Component {
         const { chartDesigner, formItemLayout, dispatch } = this.props;
         const { layoutColumn, formatColumn, colorColumn } = this.state;
         const { styleConfig } = chartDesigner;
-        console.log(styleConfig);
         const { table } = (styleConfig || { aligns: {} });
 
         return (

+ 19 - 0
src/components/common/dataPreview/dataPreview.jsx

@@ -0,0 +1,19 @@
+import { Modal, Table } from 'antd'
+import { connect } from 'dva'
+import './dataPreview.less'
+
+const DataPreview = ({ visibleBox, hideBox, columns, dataSource }) => {
+    
+    return <Modal
+        className='datapreview'
+        visible={visibleBox}
+        footer={null}
+        onCancel={hideBox}
+    ><Table
+        columns={columns}
+        dataSource={dataSource}
+        size='small'
+    /></Modal>
+}
+
+export default connect()(DataPreview);

+ 14 - 0
src/components/common/dataPreview/dataPreview.less

@@ -0,0 +1,14 @@
+.datapreview {
+    .ant-modal-content {
+        .ant-modal-close {
+            .ant-modal-close-x {
+                width: 40px;
+                height: 40px;
+                line-height: 40px;
+            }
+        }
+        .ant-modal-body {
+            padding: 40px 24px 0 24px;
+        }
+    }
+}

+ 43 - 33
src/components/common/login/login.jsx

@@ -1,6 +1,6 @@
 import React from 'react'
 import Login from 'ant-design-pro/lib/Login'
-import { Alert, Checkbox } from 'antd'
+import { Alert, Checkbox, message } from 'antd'
 import { Link, Redirect } from 'dva/router'
 import { connect } from 'dva'
 import * as service from '../../../services/index'
@@ -52,38 +52,42 @@ class LoginComponent extends React.Component {
             userName: username,
             passWord: password
         };
-        service.fetch({
-            url: URLS.LOGIN,
-            body: body
-        }).then(r => {
-            if(!r.err && r.data.code > 0) {
-                return r.data.data;
-            }else {
-                self.setState({
-                    notice: r.err || r.data.msg,
+        try{
+            service.fetch({
+                url: URLS.LOGIN,
+                body: body
+            }).then(r => {
+                if(!r.err && r.data.code > 0) {
+                    return r.data.data;
+                }else {
+                    self.setState({
+                        notice: r.err || r.data.msg,
+                    });
+                    let obj = {};
+                    throw obj;
+                }
+            }).then(resData => {
+                console.log('登录', body, resData);
+                const token = resData.token;
+                const expireTime = resData.times;
+                const user = resData.user;
+                const currentUser = {
+                    code: user.id+'',
+                    account: user.userName,
+                    password: user.passWord,
+                    name: user.name,
+                    role: user.role
+                };
+                dispatch({ type: 'main/setCurrentUser', user: currentUser });
+                authenticate(token, expireTime, currentUser, () => {
+                    this.setState({ redirectToReferrer: true });
                 });
-                let obj = {};
-                throw obj;
-            }
-        }).then(resData => {
-            console.log('登录', body, resData);
-            const token = resData.token;
-            const expireTime = resData.times;
-            const user = resData.user;
-            const currentUser = {
-                code: user.id,
-                account: user.userName,
-                password: user.passWord,
-                name: user.name,
-                role: user.role
-            };
-            dispatch({ type: 'main/setCurrentUser', user: currentUser });
-            authenticate(token, expireTime, currentUser, () => {
-                this.setState({ redirectToReferrer: true });
+            }).catch(ex => {
+                console.error('fetch error', ex);
             });
-        }).catch(ex => {
-            console.error('fetch error', ex);
-        });
+        }catch(e) {
+            message.error('登录失败');
+        }
     };
 
     render() {
@@ -111,12 +115,18 @@ class LoginComponent extends React.Component {
                                 this.setState({
                                     notice: ''
                                 });
-                            }}/>
+                            }} rules={[{
+                                required: true,
+                                message: '用户名不能为空'
+                            }]}/>
                             <Password name="password" placeholder='输入密码' onChange={() => {
                                 this.setState({
                                     notice: ''
                                 });
-                            }}/>
+                            }} rules={[{
+                                required: true,
+                                message: '密码不能为空'
+                            }]}/>
                             <div>
                                 <Checkbox checked={this.state.autoLogin} onChange={this.changeAutoLogin}>记住密码</Checkbox>
                                 <a style={{ float: 'right' }} href="">忘记密码</a>

+ 2 - 3
src/components/common/navigator.jsx

@@ -24,12 +24,11 @@ class Navigator extends React.Component {
                 {currentUser.role === 'admin' && <Menu.Item key="1" onClick={() => {
                     dispatch({ type: 'main/redirect', path: '/admin' });
                 }}>
-                    <Icon type="setting" />系统管理
+                    <Icon type="setting" />用户管理
                 </Menu.Item>}
                 <Menu.Divider />
                 <Menu.Item key="3" onClick={() => {
-                    window.localStorage.removeItem("token");
-                    window.localStorage.removeItem("expireTime");
+                    dispatch({ type: 'main/logout' });
                     dispatch({ type: 'main/redirect', path: '/login' });
                 }}><Icon type="logout" />注销</Menu.Item>
             </Menu>

+ 10 - 5
src/components/common/selectUserBox/selectUserBox.jsx

@@ -40,7 +40,7 @@ class AddGroupMemberBox extends React.Component {
                         name: d.name,
                         account: d.userName,
                         password: d.passWord,
-                        role: d.role,
+                        role: d.role === 'admin' ? 'admin' : 'default',
                         department: d.department,
                         post: d.post,
                     })),
@@ -57,7 +57,7 @@ class AddGroupMemberBox extends React.Component {
     }
 
     render() {
-        const { visibleBox, title, hideBox, okHandler, multiple } = this.props;
+        const { visibleBox, title, hideBox, okHandler, multiple, onlyAdmin } = this.props;
         const { userData, fetching } = this.state;
 
         return (
@@ -66,8 +66,10 @@ class AddGroupMemberBox extends React.Component {
                 title={title}
                 visible={visibleBox}
                 onOk={() => {
-                    okHandler(this.state.selectedUser);
-                    hideBox();
+                    if(!!this.state.selectedUser) {
+                        okHandler(this.state.selectedUser);
+                        hideBox();
+                    }
                 }}
                 onCancel={hideBox}
                 maskClosable={false}
@@ -102,8 +104,11 @@ class AddGroupMemberBox extends React.Component {
                                     }
                                 });
                             }}
+                            onKeyPress={() => {
+                                console.log(112131231);
+                            }}
                         >
-                            { userData.map((s, i) => {
+                            { userData.filter(u => !!onlyAdmin ? (u.role === 'admin') : ( true )).map((s, i) => {
                                 return <SelectOption key={i} value={s.code}>{s.name}</SelectOption>
                             }) }
                         </Select>

+ 11 - 14
src/components/dashboard/list.jsx

@@ -51,7 +51,9 @@ class DashboardList extends React.Component {
     }
 
     generateCard() {
-        const { dashboard, dispatch } = this.props;
+        const { main, dashboard, dispatch } = this.props;
+        const { selectedRecord } = this.state;
+        const { currentUser } = main;
         const list = dashboard.list;
 
         const reg = new RegExp('([+ \\- & | ! ( ) { } \\[ \\] ^ \" ~ * ? : ( ) \/])', 'g'); // 需要转义的字符
@@ -59,27 +61,27 @@ class DashboardList extends React.Component {
 
         const operationMenu = (
             <Menu className='menu-operation'>
-                <Menu.Item onClick={() => {
+                { selectedRecord && currentUser.code === selectedRecord.code && <Menu.Item onClick={() => {
                     this.setState({visibleDistributeBox: true})
                 }}> 
                     <Icon type='share-alt'/>分发
-                </Menu.Item>
-                <Menu.Divider />
-                <Menu.Item
+                </Menu.Item>}
+                { selectedRecord && currentUser.code === selectedRecord.code && <Menu.Divider />}
+                { selectedRecord && currentUser.code === selectedRecord.code && <Menu.Item
                     onClick={()=>{
                         this.setState({ visibleTransferBox: true})
                     }}
                 >
                     <Icon type="swap" />移交
-                </Menu.Item>
-                <Menu.Item
+                </Menu.Item>}
+                { selectedRecord && currentUser.code === selectedRecord.code && <Menu.Item
                     onClick={(e) => {
                         this.setState({ visibleDeleteBox: true})
                     }}
                 >
                     <Icon type="delete" />删除
                     
-                </Menu.Item>
+                </Menu.Item>}
             </Menu>
         )
 
@@ -240,9 +242,4 @@ class DashboardList extends React.Component {
     }
 }
 
-
-function mapStateToProps({ present: { dashboard } }) {
-    return { dashboard }
-}
-
-export default connect(mapStateToProps)(DashboardList)
+export default connect(({ present: { main, dashboard } }) => ({ main, dashboard }))(DashboardList)

+ 2 - 0
src/components/dashboardDesigner/chartView.jsx

@@ -25,6 +25,7 @@ const ChartView = ({ item, itemSize, chart, editMode, dispatch }) => {
             }else if(type === 'table') {
                 const { columns, data } = option;
                 const { height } = itemSize;
+                const tableRowHeight = 38;
                 children = <Table
                     className='dashboard-table'
                     size='small'
@@ -32,6 +33,7 @@ const ChartView = ({ item, itemSize, chart, editMode, dispatch }) => {
                         x: columns ? columns.length * 100 : 0,
                         y: height - 38 - 50 - 16 , // 减去工具栏高度、表格标题栏高度、分页工具高度、缩放调整按钮高度
                     }}
+                    pagination={{ defaultPageSize: Math.floor(height/tableRowHeight) || 10 }}
                     columns={columns ? columns.map(c => ({
                         ...c,
                         width: 100

+ 64 - 20
src/components/datasource/dataSource.jsx

@@ -4,8 +4,9 @@ import { connect } from 'dva'
 import './dataSource.less'
 import { dateFormat } from '../../utils/baseUtils'
 import GroupSelector from './groupSelector'
-import TransferBox from '../common/selectUserBox/selectUserBox';
-import DeleteBox from '../common/deleteBox/deleteBox';
+import TransferBox from '../common/selectUserBox/selectUserBox'
+import DeleteBox from '../common/deleteBox/deleteBox'
+import DataPreview from '../common/dataPreview/dataPreview'
 const { Content } = Layout
 const { Search } = Input
 const { TreeNode } = Tree
@@ -19,6 +20,7 @@ class DataSource extends React.Component {
             visibleSetGroupMenu: false, //
             visibleTransferBox: false,
             visibleDeleteBox: false,
+            visibleDataPreviewBox: false, // 显示数据预览
             groupEditing: false, // 是否处于编辑状态
         }
     };
@@ -244,8 +246,9 @@ class DataSource extends React.Component {
 
     render() {
         
-        const { dataSource, dispatch } = this.props;
-        const { selectedRecord, visibleTransferBox, visibleDeleteBox } = this.state;
+        const { main, dataSource, dispatch } = this.props;
+        const { selectedRecord, visibleTransferBox, visibleDeleteBox, visibleDataPreviewBox } = this.state;
+        const { currentUser } = main;
 
         const reg = new RegExp('([+ \\- & | ! ( ) { } \\[ \\] ^ \" ~ * ? : ( ) \/])', 'g'); // 需要转义的字符
         let filterLabel = dataSource.filterLabel.replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(reg, '\\$1'); // 添加转义符号
@@ -253,6 +256,7 @@ class DataSource extends React.Component {
         const TAG_COLOR = ['blue'];
         // const TAG_COLOR1 = ['magenta', 'red', 'volcano', 'orange', 'gold', 'lime', 'green', 'cyan', 'blue', 'geekblue', 'purple'];
 
+        console.log(selectedRecord, currentUser.code)
         const moreOperatingMenu = (
             <Menu className='operationmenu' visible={true}>
                 <Menu.Item
@@ -262,35 +266,38 @@ class DataSource extends React.Component {
                 >
                     <Icon type="file-add" />创建图表
                 </Menu.Item>
-                <Menu.Item
-                    onClick={(e) => {
+                {selectedRecord && currentUser.code === selectedRecord.creatorCode && <Menu.Item onClick={(e) => {
                         dispatch({ type: 'dataSource/resetNewModel' });
                         let selectedModel = dataSource.list.find((i) => { return i.code === selectedRecord.code })
                         dispatch({type: 'main/redirect', path: {pathname: '/datasource/'+ selectedModel.type +'/' + selectedModel.code + '/base'}});
                     }}>
                     <Icon type="info-circle-o" />属性设置
-                </Menu.Item>
-                <Menu.Item><Icon type="search" />预览数据</Menu.Item>
+                </Menu.Item>}
+                <Menu.Item onClick={() => {
+                    this.setState({
+                        visibleDataPreviewBox: true
+                    });
+                }}><Icon type="search" />预览数据</Menu.Item>
                 <Menu.Divider />
-                <Menu.SubMenu className='setgroupmenu' title={<div><Icon style={{ marginRight: '6px' }} type='profile' />移动到</div>}>
+                { selectedRecord && currentUser.code === selectedRecord.creatorCode && <Menu.SubMenu className='setgroupmenu' title={<div><Icon style={{ marginRight: '6px' }} type='profile' />移动到</div>}>
                     {this.createGroupMenu(selectedRecord)}
-                </Menu.SubMenu>
+                </Menu.SubMenu>}
                 <Menu.Divider />
-                <Menu.Item
+                { selectedRecord && currentUser.code === selectedRecord.creatorCode && <Menu.Item
                     onClick={()=>{
                         this.setState({ visibleTransferBox: true})
                     }}
                 >
                     <Icon type="swap" />移交
-                </Menu.Item>
-                <Menu.Item
+                </Menu.Item>}
+                { selectedRecord && currentUser.code === selectedRecord.creatorCode && <Menu.Item
                     onClick={(e) => {
                         this.setState({ visibleDeleteBox: true})
                     }}
                 >
                     <Icon type="delete" />删除
                     
-                </Menu.Item>
+                </Menu.Item>}
             </Menu>
         );
         const dataSourceColumns = [{
@@ -340,8 +347,8 @@ class DataSource extends React.Component {
             }
         }, {
             title: '创建人',
-            dataIndex: 'creator',
-            key: 'creator',
+            dataIndex: 'creatorName',
+            key: 'creatorName',
             width: 100
         }, {
             title: '创建时间',
@@ -429,7 +436,7 @@ class DataSource extends React.Component {
                                             dispatch({ type: 'dataSource/setNewModelField', name: 'type', value: type });
                                             dispatch({type: 'main/redirect', path: {pathname: '/datasource/'+ type +'/create/base'}});
                                         }}>
-                                            <Menu.Item key='database'>数据库</Menu.Item>
+                                            { currentUser.role === 'admin' && <Menu.Item key='database'>数据库</Menu.Item>}
                                             <Menu.Item key='file'>文件</Menu.Item>
                                         </Menu>
                                     )} trigger={['click']}>
@@ -469,6 +476,7 @@ class DataSource extends React.Component {
                                     visibleTransferBox: false
                                 })
                             }}
+                            onlyAdmin={true}
                         />
                         <DeleteBox
                             visibleDeleteBox={visibleDeleteBox}
@@ -481,7 +489,43 @@ class DataSource extends React.Component {
                             selectedRecord={selectedRecord}
                             onOk={() =>{
                                 dispatch({ type: 'dataSource/remoteDelete', code: selectedRecord.code })
-                            }} />
+                        }} />
+                        <DataPreview
+                            visibleBox={visibleDataPreviewBox}
+                            hideBox={() => {
+                                this.setState({
+                                    visibleDataPreviewBox: false
+                                });
+                            }}
+                            columns={[{
+                                title: '列1',
+                                dataIndex: 'c1'
+                            }, {
+                                title: '列2',
+                                dataIndex: 'c2'
+                            }, {
+                                title: '列3',
+                                dataIndex: 'c3'
+                            }, {
+                                title: '列4',
+                                dataIndex: 'c4'
+                            }, {
+                                title: '列5',
+                                dataIndex: 'c5'
+                            }, {
+                                title: '列6',
+                                dataIndex: 'c6'
+                            }]}
+                            dataSource={((count) => {
+                                let arr = [];
+                                for(let i = 0; i < count; i++) {
+                                    arr.push({
+                                        key: i, c1: 0, c2: 0, c3: 0, c4: 0, c5: 0, c6: 0
+                                    });
+                                }
+                                return arr;
+                            })(20)}
+                        />
                     </Card>
                 </Content>
             </Layout>
@@ -491,8 +535,8 @@ class DataSource extends React.Component {
     }
 }
 
-function mapStateToProps({present: {dataSource, dataConnect}}) {
-    return { dataSource, dataConnect }
+function mapStateToProps({present: {main, dataSource, dataConnect}}) {
+    return { main, dataSource, dataConnect }
 }
 
 export default connect(mapStateToProps)(DataSource)

+ 1 - 1
src/constants/url.js

@@ -62,7 +62,7 @@ const URLS = {
 
     DATASOURCE_POLICY_TARGET_UPDATE: BASE_URL + '/addObject', // 设置策略对象
 
-    DATASOURCE_TRANSFER: BASE_URL + '/changeDbOrder', // 数据源移交
+    DATASOURCE_TRANSFER: BASE_URL + '/Connector/changeDbOrder', // 数据源移交
 
     /***************************************数据连接配置***************************************/
 

+ 5 - 1
src/models/chart.js

@@ -143,8 +143,10 @@ export default {
                         return {
                             code:  d.chartId+'',
                             name: d.chartName,
+                            access: d.authority === '1',
                             type: getViewType(d.chartType),
-                            creator: d.createBy,
+                            creatorCode: d.createId+'',
+                            creatorName: d.createBy,
                             createTime: d.createDate,
                             description: d.describes || '',
                             groupCode: d.chartsGroup + '',
@@ -184,6 +186,8 @@ export default {
 
                     let data = {
                         code: resData.chartId,
+                        creatorCode: resData.createId+'',
+                        creatorName: resData.createBy,
                         header: {
                             label: resData.chartName
                         },

+ 3 - 0
src/models/chartDesigner.js

@@ -34,6 +34,8 @@ export default {
     state: {
         originData: {
             code: null,
+            creatorCode: null,
+            creatorName: null,
             header: { label: '标题' },
             baseConfig: { dataSource: '', viewType: '' },
             aggregateTableConfig: { targetColumn: {}, statistics: [], groupBy: [] },
@@ -403,6 +405,7 @@ export default {
                     
                     let config = {
                         viewType: 'bar',
+                        chartConfig: barConfig,
                         option: {
                             xAxis: res.data.data.xAxis,
                             serieses: res.data.data.serieses,

+ 4 - 12
src/models/dataSource.js

@@ -22,16 +22,6 @@ export default {
             const { list } = action;
             return { ...state, list };
         },
-        add(state, action) {
-            let newOne = Object.assign({}, state.newOne);
-            let list = state.list;
-            newOne.key = new Date().getMilliseconds()+(Math.random()*100).toFixed(0);
-            newOne.code = new Date().getMilliseconds()+(Math.random()*100).toFixed(0);
-            newOne.creator = 'zhuth';
-            newOne.createTime = new Date();
-            list.push(newOne);
-            return Object.assign({}, state, {list});
-        },
         modify(state, action) {
             const { newOne } = state;
             let list = state.list;
@@ -188,7 +178,8 @@ export default {
                             name: r.dataName,
                             type: r.type || 'database',
                             dbType: dbConfig.databaseType,
-                            creator: r.createBy,
+                            creatorName: r.createBy,
+                            creatorCode: r.createId+'',
                             createTime: new Date(r.createDate),
                             tags: tags,
                             description: r.note,
@@ -285,7 +276,8 @@ export default {
                         address: dbConfig.addrass,
                         port: dbConfig.port,
                         target: resData.loadObject,
-                        creator: resData.createBy,
+                        creatorName: resData.createBy,
+                        creatorCode: resData.createId+'',
                         createTime: new Date(resData.createDate),
                         userName: dbConfig.userName,
                         password: dbConfig.passWord,

+ 15 - 0
src/models/main.js

@@ -56,6 +56,21 @@ export default {
                 window.location.reload();
             }
         },
+        *logout( action, { put, call, select }) {
+            try {
+                window.localStorage.removeItem("token");
+                window.localStorage.removeItem("expireTime");
+                yield put({ type: 'dataConnect/list', list: [] });
+                yield put({ type: 'dataSource/list', list: [] });
+                yield put({ type: 'chart/list', list: [] });
+                yield put({ type: 'dashboard/list', list: [] });
+                yield put({ type: 'user/list', list: [] });
+                yield put({ type: 'userGroup/list', list: [] });
+            }catch(e) {
+                console.log(e);
+                message.error('注销失败');
+            }
+        }
     },
     subscriptions: {
         setup({ dispatch, history }) {

+ 5 - 3
src/routes/router.js

@@ -11,6 +11,7 @@ import DashboardDesigner from '../components/dashboardDesigner/layout'
 import zhCN from 'antd/lib/locale-provider/zh_CN'
 import Demo from '../demo';
 import Xiaomi from '../xiaomi'
+import * as moment from 'moment'
 
 function RouterConfig({ history }) {
     return (
@@ -37,19 +38,20 @@ const PrivateRoute = ({ component: Component, ...rest }) => (
         {...rest}
         render={props =>{
 
-            if (!window.localStorage.getItem("token")) {
+            if (!window.localStorage.getItem('token')) {
                 window.localStorage.setItem("token", "false");
             }else {
                 /**
                  * 是否过期
                  * 1.是-----设置isAuthenticated为false
                  */
-                if(new Date(+window.localStorage.getItem("expireTime")) < new Date()) {
+                const t = moment(+window.localStorage.getItem('expireTime'));
+                if(t.isValid() && t.diff(moment()) < 0) {
                     window.localStorage.setItem("token", "false");
                 }
             }
 
-            const isAuthenticated = window.localStorage.getItem("token") !== "false";
+            const isAuthenticated = window.localStorage.getItem('token') !== "false";
             return <RootLayout location={props.location} isAuthenticated={isAuthenticated}>
                 <Component isAuthenticated={isAuthenticated} {...props} />
             </RootLayout>

+ 3 - 0
src/services/index.js

@@ -7,6 +7,9 @@ export function fetch(option) {
   // client.send(JSON.stringify(values));
   const { url, body } = option;
   const token = window.localStorage.getItem("token");
+  // if(token === 'false') {
+  //   return {err: 'token已过期'}
+  // }
   return request(url, {
     method: 'POST',
     headers: {