Browse Source

饼图数据内容格式化/首页报表切换layout错乱问题/权限设置添加报表过滤

zhuth 6 years ago
parent
commit
26cf01e056

+ 99 - 20
src/components/authority/index.jsx

@@ -2,10 +2,10 @@
  * 权限管理
  */
 import React from 'react'
-import { Tabs, Layout, Input, Menu, Table, Checkbox, message } from 'antd'
+import { Tabs, Layout, Row, Col, Input, Menu, Table, Checkbox, message, Button, Icon } from 'antd'
 import { connect } from 'dva'
 import './index.less'
-const { Sider, Content } = Layout
+const { Sider, Header, Content } = Layout
 const { Search } = Input
 const { TabPane } = Tabs
 
@@ -20,19 +20,27 @@ class Authority extends React.Component {
 
     checkIndeterminate = (treeList) => {
         const { authority } = this.props;
-        const { dashboardList } = authority;
+        const { dashboardList, dashboardFilterLabel } = authority;
         let arr = treeList && treeList.length > 0 ? [ ...treeList ] : null;
-        for(let i = 0;arr && i < arr.length; i++) {
+        for(let i = (arr ? (arr.length - 1) : 0); arr && i >= 0; i--) {
             let l = arr[i];
             if(l.type === 'menu') {
                 l.children = this.checkIndeterminate(l.children);
                 let checkedCount = l.children ? l.children.filter(c => c.type === 'dashboard').map(c => c.checked).filter(c => !!c).length : 0;
                 l.indeterminate = l.children ? (checkedCount > 0 && checkedCount < l.children.filter(c => c.type === 'dashboard').length) : false;
                 l.checked = l.children ? checkedCount !== 0 && checkedCount === l.children.filter(c => c.type === 'dashboard').length : false;
+
+                if((!l.children || l.children.length === 0) && !!dashboardFilterLabel && l.name.toLowerCase().indexOf(dashboardFilterLabel.toLowerCase()) === -1) {
+                    arr.splice(i, 1);
+                }
             }else {
                 l.children = null;
                 l.indeterminate = false;
                 l.checked = dashboardList.findIndex(d => d === l.code) > -1;
+
+                if(!!dashboardFilterLabel && l.name.toLowerCase().indexOf(dashboardFilterLabel.toLowerCase()) === -1) {
+                    arr.splice(i, 1);
+                }
             }
         }
         return arr;
@@ -49,8 +57,9 @@ class Authority extends React.Component {
 
     render() {
         const { dashboard, authority, userGroup, user, dispatch } = this.props;
-        const { menuSelectedKeys, tabActiveKey, groupFilterLabel, userFilterLabel, groupLimit, userLimit } = authority;
-        const dashboardTreeList = this.checkIndeterminate(dashboard.menuTree);
+        const { menuSelectedKeys, tabActiveKey, groupFilterLabel, userFilterLabel, dashboardFilterLabel, groupLimit, userLimit } = authority;
+        
+        const dashboardTreeList = this.checkIndeterminate(JSON.parse(JSON.stringify(dashboard.menuTree)));
         return <Layout className='layout-authority'>
             <Sider>
                 <Tabs
@@ -126,7 +135,30 @@ class Authority extends React.Component {
                 </Tabs>
             </Sider>
             <Content>
-                <DashBoardTree dataSource={dashboardTreeList} onRowCheckChange={(record, checked) => {
+                <Header>
+                    <Row className='search-bar' type='flex' justify='end'>
+                        <Col className='search'>
+                            <Button className='btn-refresh' onClick={() => {
+                                dispatch({ type: 'userGroup/fetchList', mandatory: true });
+                                dispatch({ type: 'user/fetchList', mandatory: true });
+                                dispatch({ type: 'dashboard/remoteMenuTree', mandatory: true });
+                            }}>
+                                <Icon type='sync'/>
+                            </Button>
+                            <Search
+                                placehodler='请输入关键字'
+                                value={dashboardFilterLabel}
+                                onChange={e => {
+                                    let value = e.target.value;
+                                    dispatch({ type: 'authority/setFields', fields: [
+                                        { name: 'dashboardFilterLabel', value },
+                                    ] })
+                                }}
+                            />
+                        </Col>
+                    </Row>
+                </Header>
+                <DashBoardTree filterLabel={dashboardFilterLabel} dataSource={dashboardTreeList} onRowCheckChange={(record, checked) => {
                     if(!!menuSelectedKeys[0]) {
                         if(record.type === 'menu') {
                             let dashboardCodes = record.children.filter(c => c.type === 'dashboard').map(c => c.code)
@@ -217,7 +249,8 @@ class DashBoardTree extends React.Component {
         super(props);
         this.state = {
             tableScrollHeight: 0,
-            pageSize: 0,
+            allExpanded: false,
+            expandedRowKeys: [],
         }
     }
 
@@ -235,24 +268,70 @@ class DashBoardTree extends React.Component {
         const content = tableWrapper.getElementsByClassName('ant-spin-nested-loading')[0];
         const tableHeader = content.querySelector('thead');
         const padding = content.getBoundingClientRect().top - tableWrapper.getBoundingClientRect().top;
-        // 容器高度 - padding * 2 - 分页器高度 - 表头高度
-        let tableScrollHeight = tableWrapper.offsetHeight - padding * 2 - 42 - tableHeader.offsetHeight;
+        // 容器高度 - padding * 2 - 表头高度 - 边框线
+        let tableScrollHeight = tableWrapper.offsetHeight - padding * 2 - tableHeader.offsetHeight - 2;
         this.setState({
             tableScrollHeight,
-            pageSize: Math.ceil((tableScrollHeight) / 38)
         });
     }
 
-    render() {
-        const { dataSource, onRowCheckChange } = this.props;
-        const { tableScrollHeight, pageSize } = this.state;
+    onExpandedRowsChange = (expandedRowKeys) => {
+        this.setState({
+            expandedRowKeys
+        });
+    }
+
+    onExpandAll = () => {
+        const { dataSource } = this.props;
+        let expandedRowKeys = this.getAllKeys(dataSource);
+        this.setState({
+            allExpanded: true,
+            expandedRowKeys
+        });
+    }
 
+    collapseAll = () => {
+        this.setState({
+            allExpanded: false,
+            expandedRowKeys: []
+        });
+    }
+
+    getAllKeys = (list) => {
+        let keyArr = [];
+        for(let i = 0;!!list && i < list.length; i++) {
+            if(!!list[i].children) {
+                keyArr.push(list[i].key);
+                keyArr = keyArr.concat(this.getAllKeys(list[i].children, keyArr));
+            }
+        }
+        return keyArr
+    }
+
+    render() {
+        const { dataSource, onRowCheckChange, filterLabel } = this.props;
+        const { tableScrollHeight, expandedRowKeys, allExpanded } = this.state;
+        const reg = new RegExp('([+ \\- & | ! ( ) { } \\[ \\] ^ \" ~ * ? : ( ) \/])', 'g'); // 需要转义的字符
         const columns = [{
-            title: '报表目录',
+            title: <span><Icon type='menu' title='展开/折叠全部' style={{ marginRight: '4px' }} onClick={allExpanded ? this.collapseAll : this.onExpandAll}/>报表目录</span>,
             dataIndex: 'name',
             key: 'name',
             width: '80%',
-            render: (text, record, index) => <span style={{ fontWeight: record.type === 'dashboard' ? 'bold' : 'normal' }}>{text}</span>
+            render: (text, record) => {
+                return <span style={{ fontWeight: record.type === 'dashboard' ? 'bold' : 'normal' }}>
+                    {
+                        filterLabel ?
+                        ((text || '').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
+                            )
+                        }
+                        )) : text
+                    }
+                </span>
+            }
         }, {
             title: '查看',
             dataIndex: 'view',
@@ -267,10 +346,10 @@ class DashBoardTree extends React.Component {
         }];
 
         return <Table columns={columns} dataSource={dataSource} scroll={{x: false, y: tableScrollHeight}}
-            size='small'    
-            pagination={{
-                pageSize: pageSize
-            }}
+            size='small'
+            pagination={false}
+            expandedRowKeys={expandedRowKeys}
+            onExpandedRowsChange={this.onExpandedRowsChange}
         />
     }
 }

+ 26 - 5
src/components/authority/index.less

@@ -81,12 +81,33 @@
         }
     }
     .ant-layout-content {
+        margin: 12px;
+        padding: 8px;
+        background: #fafafa;
+        border: 1px solid rgba(0, 0, 0, 0.1);
+        >.ant-layout-header {
+            height: 43px;
+            line-height: 43px;
+            background: transparent;
+            padding: 1px 0 0 0;
+            >.search-bar {
+                >.search {
+                    display: flex;
+                    >.btn-refresh {
+                        margin-right: 8px;
+                        background: transparent;
+                        border: none;
+                        font-size: 18px;
+                        box-shadow: none;
+                        &:after {
+                            content: none;
+                        }
+                    }
+                }
+            }
+        }
         .ant-table-wrapper {
-            background: #fafafa;
-            margin: 12px;
-            height: calc(~'100% - 24px');
-            padding: 8px;
-            border: 1px solid rgba(0, 0, 0, 0.1);
+            height: calc(~'100% - 43px');
             .ant-table-body {
                 margin-top: 0;
             }

+ 1 - 1
src/components/common/dataPreview/dataPreview.jsx

@@ -76,7 +76,7 @@ class DataPreview extends React.Component {
                     };
                     if(c.type === 'time') {
                         obj.render = v => {
-                            let text = moment(v).isValid() ? moment(v).format('YYYY-MM-DD') : v
+                            let text = v === null ? '空' : (moment(v).isValid() ? moment(v).format('YYYY-MM-DD') : v)
                             return <EllipsisTooltip key={i} title={text}>{text}</EllipsisTooltip>
                         }
                     }else {

+ 3 - 1
src/components/dashboardDesigner/viewLayout.jsx

@@ -103,7 +103,9 @@ class ViewLayout extends React.PureComponent {
 
     onLayoutChange = (layout) => {
         const { dispatch } = this.props;
-        dispatch({ type: 'dashboardDesigner/changeLayout', layout });
+        if(!(layout.length === 1 && layout[0].i === 'default-chartview')) {
+            dispatch({ type: 'dashboardDesigner/changeLayout', layout });
+        }
     }
 
     showPreviewBox = (item) => {

+ 9 - 9
src/components/homePage/index.jsx

@@ -12,15 +12,15 @@ const TabPane = Tabs.TabPane
 
 class Home extends React.Component {
 
-    componentWillUnmount() {
-        const { home, dispatch } = this.props;
-        const { tabs } = home;
-        // 离开页面后将报表中的图表配置移除,以保证下次进入时触发自动刷新
-        tabs.forEach(t => {
-            t.config = null;
-        });
-        dispatch({ type: 'home/setField', name: 'tabs', value: tabs });
-    }
+    // componentWillUnmount() {
+    //     const { home, dispatch } = this.props;
+    //     const { tabs } = home;
+    //     // 离开页面后将报表中的图表配置移除,以保证下次进入时触发自动刷新
+    //     tabs.forEach(t => {
+    //         t.config = null;
+    //     });
+    //     dispatch({ type: 'home/setField', name: 'tabs', value: tabs });
+    // }
 
     generateTabs() {
         const { home } = this.props;

+ 3 - 4
src/components/logs/logs.jsx

@@ -1,10 +1,9 @@
 import React from 'react'
-import { Layout, Row, Col, Table, Card, Button } from 'antd'
+import { Layout, Row, Col, Table, Card, Button, Icon } from 'antd'
 import { connect } from 'dva'
 import moment from 'moment'
 import ListFilter from '../common/listFilter/index'
 import EllipsisTooltip from '../common/ellipsisTooltip/index'
-import CusIcon from '../common/cusIcon/index'
 import './logs.less'
 const { Content } = Layout
 
@@ -180,7 +179,7 @@ class Logs extends React.Component {
                                 <Button className='btn-refresh' onClick={() => {
                                     dispatch({ type: 'logs/fetchList', mandatory: true });
                                 }}>
-                                    <CusIcon type='bi-refresh'/>
+                                    <Icon type='sync'/>
                                 </Button>
                                 <ListFilter modelName='logs' model={logs} />
                             </Col>
@@ -189,7 +188,7 @@ class Logs extends React.Component {
                         <Table
                             className='logs-table'
                             columns={logsColumns}
-                            dataSource={dataList}
+                            dataSource={dataList.map((d, i) => ({ ...d, key: i }))}
                             size='small'
                             // loading={loading}
                             pagination={{

+ 1 - 0
src/models/authority.js

@@ -10,6 +10,7 @@ export default {
             menuSelectedKeys: [],
             groupFilterLabel: '',
             userFilterLabel: '',
+            dashboardFilterLabel: '',
             groupLimit: 30,
             userLimit: 30,
             dashboardList: [],

+ 0 - 3
src/models/dashboardDesigner.js

@@ -223,9 +223,6 @@ export default {
             const { layout } = action;
             let { items, dirty } = state;
             const ly = ['x', 'y', 'w', 'h'];
-            if(layout.length === 0 && layout[0].i === 'default-chartview') {
-                return state;
-            }
             for(let i = 0; i < items.length; i++) {
                 if(layout[i]) { // 非删除引起
                     for(let j = 0; j < ly.length; j ++) {

+ 3 - 2
src/models/dataSource.js

@@ -780,8 +780,9 @@ export default {
                 if(!res.err && res.data.code > 0) {
                     const { columnConfig, values } = res.data.data;
                     const columns = JSON.parse(columnConfig).map(c => ({
-                        title: c.columnLable,
-                        dataIndex: c.columnName,
+                        label: c.columnLable,
+                        name: c.columnName,
+                        type: c.columnType,
                     }));
                     const { list: dataSource, pageSize, total } = values;
 

+ 40 - 2
src/models/parseChartOption.js

@@ -118,7 +118,24 @@ function pieOption(data, pieConfig) {
             formatter: "{a} <br/>{b} : {c} ({d}%)"
         },
         legend: {
-            data: (data.xAxis || []).map(d => d || '空')
+            // data: (data.xAxis || []).map(d => d || '空')
+            data: data.xAxis.map(d => {
+                let gv= pieConfig.xAxis.granularity.value;
+                let xv = d || '空';
+                if(gv === 'halfYear') {
+                    let arr = d.split('-H');
+                    xv = arr[0] + ['上半年', '下半年'][arr[1] - 1]
+                }else if(gv === 'month') {
+                    xv = d.replace('-M', '-');
+                }else if(gv === 'quarter') {
+                    let arr = d.split('-Q');
+                    xv = arr[0] + '-' + ['一', '二', '三', '四'][arr[1] - 1] + '季度'
+                }else if(gv === 'week') {
+                    let arr = d.split('-W');
+                    xv = arr[0] + '-' + arr[1] + '周'
+                }
+                return xv;
+            }),
         },
         grid: {
             left: '10%',
@@ -134,7 +151,28 @@ function pieOption(data, pieConfig) {
                 // radius : '55%',
                 // center: ['50%', '60%'],
                 // data: (data.serieses || [{ value: [] }])[0].value,
-                data: (data.serieses || [{ value: [] }])[0].value.map(v => ({ ...v, name: v.name || '空' })),
+                // data: (data.serieses || [{ value: [] }])[0].value.map(v => ({ ...v, name: v.name || '空' })),
+                data: (data.serieses || [{ value: [] }])[0].value.map(v => {
+                    let obj = { ...v };
+                    if(!v.name) {
+                        obj.name = '空'
+                    }else {
+                        let gv= pieConfig.xAxis.granularity.value;
+                        if(gv === 'halfYear') {
+                            let arr = v.name.split('-H');
+                            obj.name = arr[0] + ['上半年', '下半年'][arr[1] - 1]
+                        }else if(gv === 'month') {
+                            obj.name = v.name.replace('-M', '-');
+                        }else if(gv === 'quarter') {
+                            let arr = v.name.split('-');
+                            obj.name = arr[0] + '-' + ['一', '二', '三', '四'][arr[1] - 1] + '季度'
+                        }else if(gv === 'week') {
+                            let arr = v.name.split('-');
+                            obj.name = arr[0] + '-' + arr[1] + '周'
+                        }
+                    }
+                    return obj;
+                }),
                 itemStyle: {
                     emphasis: {
                         shadowBlur: 10,