Browse Source

图表、数据源预览数据真分页实现/图表查看列功能设计/

zhuth 7 years ago
parent
commit
f7c07d603e

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

@@ -71,8 +71,8 @@ class ChooseDataSourceBox extends React.Component {
             }
         }, {
             title: '创建人',
-            dataIndex: 'creator',
-            key: 'creator',
+            dataIndex: 'creatorName',
+            key: 'creatorName',
             width: 100
         }, {
             title: '创建时间',

+ 6 - 17
src/components/chart/list.jsx

@@ -14,7 +14,6 @@ import Thumbnail from './thumbnail'
 import DistributeBox from './distributeBox';
 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 CardGrid = Card.Grid
@@ -40,6 +39,11 @@ class ChartList extends React.Component {
         this.setBodyWidth();
         dispatch({ type: 'chart/fetchList' });
         dispatch({ type: 'chart/remoteGroupList' });
+        window.addEventListener('resize', this.setBodyWidth);
+    }
+
+    componentWillUnmount() {
+        window.removeEventListener('resize', this.setBodyWidth)
     }
 
     /**
@@ -74,11 +78,6 @@ class ChartList extends React.Component {
                 <Menu.Item>
                     <Icon type="link" />分享
                 </Menu.Item>
-                <Menu.Item onClick={() => {
-                    this.setState({
-                        visibleDataPreviewBox: true
-                    });
-                }}><Icon type="search" />预览数据</Menu.Item>
                 {selectedRecord && currentUser.code === selectedRecord.creatorCode && <Menu.Item onClick={() => {
                     const { selectedRecord } = this.state;
                     // const selectedChartDataSourceCode = selectedRecord ? selectedRecord.code : '';
@@ -362,7 +361,7 @@ class ChartList extends React.Component {
     }
 
     render() {
-        const { visibleChooseDataSourceBox, visibleDistributeBox, visibleTransferBox, visibleDeleteBox, visibleDataPreviewBox, selectedRecord } = this.state;
+        const { visibleChooseDataSourceBox, visibleDistributeBox, visibleTransferBox, visibleDeleteBox, selectedRecord } = this.state;
         const { dispatch, chart } = this.props;
         const TAG_COLOR = ['blue'];
         return (
@@ -476,16 +475,6 @@ class ChartList extends React.Component {
                     okHandler={() => {
                         dispatch({ type: 'chart/remoteDelete', code: this.state.selectedRecord.code })
                 }} />}
-                {visibleDataPreviewBox && <DataPreview
-                    visibleBox={visibleDataPreviewBox}
-                    hideBox={() => {
-                        this.setState({
-                            visibleDataPreviewBox: false
-                        });
-                    }}
-                    byChart={true}
-                    code={selectedRecord.code}
-                />}
             </Layout>
         )
     }

+ 41 - 6
src/components/chartDesigner/charts/tableView.jsx

@@ -5,27 +5,62 @@ import './tableView.less'
 
 class TableView extends React.Component {
 
+    constructor(props) {
+        super(props);
+        this.state = {
+            columnWidth: 100,
+            tableHeaderHeight: 60,
+        };
+    }
+
+    componentDidMount() {
+        window.addEventListener('resize', this.onWindowResize);
+    }
+
+    componentWillUnmount() {
+        window.removeEventListener('resize', this.onWindowResize);
+    }
+
+    onWindowResize = () => {
+        const tableEl = document.getElementsByClassName('table-view')[0];
+        const tableHeaderEl = tableEl.getElementsByTagName('thead')[0];
+
+        this.setState({
+            tableHeaderHeight: tableHeaderEl.offsetHeight + 2, // 表头高度(含边框)
+        });
+    }
+
     render() {
-        const { contentSize, chartDesigner } = this.props;
+        const { contentSize, chartDesigner, dispatch } = this.props;
+        const { columnWidth, tableHeaderHeight } = this.state;
         const { chartOption, styleConfig } = chartDesigner;
-        const { columns, dataSource } = chartOption;
+        const { columns, dataSource, pageSize, total } = chartOption;
     
         const { table } = styleConfig || {};
         const { bordered } = table || {};
-        const tableContentHeight = contentSize.height - 38 - 42;
+        const tableContentHeight = contentSize.height - tableHeaderHeight - 42;
     
         return (
             <Table
                 className='table-view'
-                columns={columns ? columns.map(c => ({ ...c, width: 200 })) : []}
+                columns={columns ? columns.map(c => ({ ...c, width: columnWidth })) : []}
                 dataSource={dataSource || []}
                 size='small'
                 bordered={bordered}
                 scroll={{
-                    x: columns ? columns.length * 200 : 0,
+                    x: columns ? columns.length * columnWidth : 0,
                     y: tableContentHeight
                 }}
-                pagination={{ pageSize: 25 }}
+                pagination={{
+                    pageSize,
+                    total,
+                    showTotal: (total, range) => {
+                        return `第${range[0]}到第${range[1]}条数据,共${total}条数据`;
+                    },
+                    onChange: (page, pageSize) => {
+                        dispatch({ type: 'chartDesigner/fetchDataViewData', page, pageSize });
+                    }
+                }}
                 locale={{
                     emptyText: <div>无数据</div>
                 }}

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

@@ -8,7 +8,7 @@ import BarConfigForm from './sections/barConfigForm'
 import PieConfigForm from './sections/pieConfigForm'
 import LineConfigForm from './sections/lineConfigForm'
 import TableView from './charts/tableView'
-import ScatterConfigForm from './sections/ScatterConfigForm'
+import ScatterConfigForm from './sections/scatterConfigForm'
 import StyleConfigForm from './sections/style/index'
 import OtherConfigForm from './sections/otherConfigForm'
 import EchartsView from './charts/echartsView'

+ 4 - 2
src/components/chartDesigner/sections/ScatterConfigForm.jsx

@@ -104,6 +104,7 @@ const ScatterConfigForm = ({ autoRefresh, chartDesigner, dispatch, formItemLayou
 					}}
 					options={columns.map((c, i)=>{
 						return {
+							type: c.type,
 							value: c.name,
 							label: c.label,
 							children: GAUGE[chartDesigner.baseConfig.viewType].map(g => {
@@ -169,8 +170,9 @@ const ScatterConfigForm = ({ autoRefresh, chartDesigner, dispatch, formItemLayou
 					filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
 					placeholder='请选择...'
 					onChange={(value) => {
-						// value = value.splice(-1);
-						dispatch({ type: 'chartDesigner/changeField', name: 'scatterConfig', value: { ...chartDesigner.scatterConfig, groupBy: value }, autoRefresh });
+						const column = value ? columns.find(c => c.name === value.key) : null;
+						const groupBy = column ? { ...value, type: column.type } : undefined;
+						dispatch({ type: 'chartDesigner/changeField', name: 'scatterConfig', value: { ...chartDesigner.scatterConfig, groupBy }, autoRefresh });
 					}}
 					value={chartDesigner.scatterConfig.groupBy}
 				>

+ 3 - 1
src/components/chartDesigner/sections/aggregateTableConfigForm.jsx

@@ -69,7 +69,9 @@ class AggregateTableConfigForm extends React.Component {
 						showSearch
 						filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
 						onChange={(value) => {
-							dispatch({ type: 'chartDesigner/changeField', name: 'aggregateTableConfig', value: { ...props.chartDesigner.aggregateTableConfig, groupBy: value }, autoRefresh });
+							const column = value ? columns.find(c => c.name === value.key) : null;
+							const groupBy = column ? { ...value, type: column.type } : undefined;
+							dispatch({ type: 'chartDesigner/changeField', name: 'aggregateTableConfig', value: { ...props.chartDesigner.aggregateTableConfig, groupBy }, autoRefresh });
 						}}
 						value={chartDesigner.aggregateTableConfig.groupBy}
 					>

+ 4 - 1
src/components/chartDesigner/sections/barConfigForm.jsx

@@ -107,6 +107,7 @@ const BarConfigForm = ({ autoRefresh, chartDesigner, dispatch, formItemLayout })
 						return {
 							value: c.name,
 							label: c.label,
+							type: c.type,
 							children: GAUGE[chartDesigner.baseConfig.viewType].map(g => {
 								if(g.columnType.indexOf(c.type) !== -1) {
 									return g;
@@ -169,7 +170,9 @@ const BarConfigForm = ({ autoRefresh, chartDesigner, dispatch, formItemLayout })
 					filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
 					allowClear={true}
 					onChange={(value) => {
-						dispatch({ type: 'chartDesigner/changeField', name: 'barConfig', value: { ...chartDesigner.barConfig, groupBy: value }, autoRefresh });
+						const column = value ? columns.find(c => c.name === value.key) : null;
+						const groupBy = column ? { ...value, type: column.type } : undefined;
+						dispatch({ type: 'chartDesigner/changeField', name: 'barConfig', value: { ...chartDesigner.barConfig, groupBy }, autoRefresh });
 					}}
 					value={chartDesigner.barConfig.groupBy}
 				>

+ 4 - 2
src/components/chartDesigner/sections/lineConfigForm.jsx

@@ -103,6 +103,7 @@ const LineConfigForm = ({ autoRefresh, chartDesigner, dispatch, formItemLayout }
 					}}
 					options={columns.map((c, i)=>{
 						return {
+							type: c.type,
 							value: c.name,
 							label: c.label,
 							children: GAUGE[chartDesigner.baseConfig.viewType].map(g => {
@@ -168,8 +169,9 @@ const LineConfigForm = ({ autoRefresh, chartDesigner, dispatch, formItemLayout }
 					filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
 					placeholder='请选择...'
 					onChange={(value) => {
-						// value = value.splice(-1);
-						dispatch({ type: 'chartDesigner/changeField', name: 'lineConfig', value: { ...chartDesigner.lineConfig, groupBy: value }, autoRefresh });
+						const column = value ? columns.find(c => c.name === value.key) : null;
+						const groupBy = column ? { ...value, type: column.type } : undefined;
+						dispatch({ type: 'chartDesigner/changeField', name: 'lineConfig', value: { ...chartDesigner.lineConfig, groupBy }, autoRefresh });
 					}}
 					value={chartDesigner.lineConfig.groupBy}
 				>

+ 1 - 0
src/components/chartDesigner/sections/pieConfigForm.jsx

@@ -105,6 +105,7 @@ const PieConfigForm = ({ autoRefresh, chartDesigner, dispatch, formItemLayout })
 					}}
 					options={columns.map((c, i)=>{
 						return {
+							type: c.type,
 							value: c.name,
 							label: c.label,
 							children: GAUGE[chartDesigner.baseConfig.viewType].map(g => {

+ 16 - 3
src/components/chartDesigner/sections/toolbar.jsx

@@ -3,6 +3,7 @@ import { Tag, Icon, Button } from 'antd'
 import FilterBox from '../../common/filterBox/filterBox'
 import { connect } from 'dva'
 import moment from 'moment'
+import DataPreview from '../../common/dataPreview/dataPreview'
 import './toolbar.less'
 
 class Toolbar extends React.Component {
@@ -89,9 +90,9 @@ class Toolbar extends React.Component {
     }
 
     render() {
-        const { chartDesigner } = this.props;
+        const { chartDesigner, dispatch } = this.props;
         const { code, filters, columns } = chartDesigner;
-        const { visibleFilterBox } = this.state;
+        const { visibleFilterBox, visibleDataPreviewBox } = this.state;
 
         let tags = filters.map((f, i)=>{
             return {
@@ -125,12 +126,24 @@ class Toolbar extends React.Component {
                 </div>
                 <div className='tools'>
                     <Button className='tools-button' size='small' onClick={() => {
+                        // dispatch({ type: 'chartDesigner/remoteChartDataList' });
                         this.setState({
-                            visibleColumnBox: true
+                            visibleDataPreviewBox: true
                         })
                     }}>查看列</Button>
                 </div>
                 {visibleFilterBox && <FilterBox type='chart' code={code} columns={columns} filterData={filters} visibleFilterBox={visibleFilterBox} showFilterBox={this.showFilterBox} hideFilterBox={this.hideFilterBox} createFilters={this.createFilters} />}
+                {visibleDataPreviewBox && <DataPreview
+                    visibleBox={visibleDataPreviewBox}
+                    hideBox={() => {
+                        this.setState({
+                            visibleDataPreviewBox: false
+                        });
+                    }}
+                    fetchFunction={(page, pageSize) => {
+                        dispatch({ type: 'chartDesigner/remoteChartDataList', page, pageSize });
+                    }}
+                />}
             </div>
         );
     }

+ 3 - 28
src/components/common/CardList.jsx

@@ -1,5 +1,5 @@
 import React from 'react'
-import { Menu, Icon, Card, Row, Col, Dropdown } from 'antd'
+import { Card, Row, Col } from 'antd'
 import { connect } from 'dva'
 import Ellipsis from 'ant-design-pro/lib/Ellipsis'
 import moment from 'moment'
@@ -29,37 +29,12 @@ class CardList extends React.Component {
         const { mode, dispatch, list, type } = this.props;
         const reg = new RegExp('([+ \\- & | ! ( ) { } \\[ \\] ^ \" ~ * ? : ( ) \/])', 'g'); // 需要转义的字符
         let filterLabel = (mode !== 'homepage') ? list.filterLabel.replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(reg, '\\$1') : ""; // 添加转义符号
-        const operationMenu = (
-            <Menu className='menu-operation'>
-                <Menu.Item onClick={() => {
-                    // this.setState({visibleDistributeBox: true})
-                }}> 
-                    <Icon type='share-alt'/>分发
-                </Menu.Item>
-                <Menu.Divider />
-                <Menu.Item
-                    onClick={()=>{
-                        // this.setState({ visibleTransferBox: true})
-                    }}
-                >
-                    <Icon type="swap" />移交
-                </Menu.Item>
-                <Menu.Item
-                    onClick={(e) => {
-                        // this.setState({ visibleDeleteBox: true})
-                    }}
-                >
-                    <Icon type="delete" />删除
-                    
-                </Menu.Item>
-            </Menu>
-        )
 
         let cards = list.filter(l => {
             let reg = new RegExp('(' + filterLabel + '){1}', 'ig');
             return (l.name || '').search(reg) !== -1 || (l.description || '').search(reg) !== -1;
         }).sort((a, b) => {
-            return new Date(b.createTime) - new Date(a.createTime)
+            return new Date(b.recentTime) - new Date(a.recentTime)
         }).map( (l, i) => (
             <CardGrid className={(type === 'dashboard')? 'recent-dashboard-card' : 'recent-chart-card'} key={i} onClick={() => {
                 this.setState({ selectedRecord: l })
@@ -80,7 +55,7 @@ class CardList extends React.Component {
                                 }
                             </Col>
                             <Col style={{ textAlign: 'right' }} span={3} >
-                                {<Icon type='star-o'/>}
+                                {/* {<Icon type='star-o'/>} */}
                             </Col>
                         </Row>
                     }

+ 35 - 17
src/components/common/dataPreview/dataPreview.jsx

@@ -9,15 +9,19 @@ class DataPreview extends React.Component {
     constructor(props) {
         super(props);
         this.state = {
+            boxW: 0.8, // 0 ~ 1
+            boxH: 0.8, // 0 ~ 1
+            columnWidth: 100,
+            tableHeaderHeight: 60,
             screenWidth: document.documentElement.clientWidth || document.body.clientWidth,
             screenHeight: document.documentElement.clientHeight || document.body.clientHeight
         };
     }
 
     componentDidMount() {
-        const { byChart, dispatch, code } = this.props;
+        const { fetchFunction } = this.props;
         window.addEventListener('resize', this.onWindowResize);
-        dispatch({ type: 'dataList/remoteDataList', byChart, code });
+        typeof fetchFunction === 'function' && fetchFunction();
     }
 
     componentWillUnmount() {
@@ -27,24 +31,28 @@ class DataPreview extends React.Component {
     }
 
     onWindowResize = () => {
+        const boxEl = document.getElementsByClassName('datapreview')[0];
+        const tableHeaderEl = boxEl.getElementsByTagName('thead')[0];
+
         this.setState({
-            screenWidth: document.documentElement.clientWidth || document.body.clientWidth,
-            screenHeight: document.documentElement.clientHeight || document.body.clientHeight
+            screenWidth: document.documentElement.clientWidth || document.body.clientWidth, // 屏幕宽
+            screenHeight: document.documentElement.clientHeight || document.body.clientHeight, // 屏幕高
+            tableHeaderHeight: tableHeaderEl.offsetHeight + 2, // 表头高度(含边框)
         });
     }
 
     render() {
-        const { fetching, dataList, visibleBox, hideBox } = this.props;
-        const { columns, dataSource } = dataList;
-        const { screenWidth, screenHeight } = this.state;
-        const tableBodyWidth = screenWidth * 0.8 - 10 - 10 - 18;
-        const tableBodyHeight = screenHeight * 0.8 - 40 - 40 - 38;
-        const tableRowHeight = 38;
+        const { fetchFunction, fetching, dataList, visibleBox, hideBox } = this.props;
+        const { loading, columns, dataSource, pageSize, total } = dataList;
+        const { screenWidth, screenHeight, boxW, boxH, columnWidth, tableHeaderHeight } = this.state;
+        const tableBodyWidth = screenWidth * boxW - 10 - 10 - 18;
+        const tableBodyHeight = screenHeight * boxH - 40 - 40 - tableHeaderHeight;
 
         return <Modal
             className='datapreview'
-            width='80%'
-            height='80%'
+            style={{ top: `${(1 - boxH) * 0.5 * 100}%` }}
+            width={`${100 * boxW}%`}
+            height={`${100 * boxH}%`}
             visible={visibleBox}
             footer={null}
             onCancel={hideBox}
@@ -53,19 +61,29 @@ class DataPreview extends React.Component {
             <Table
                 columns={columns.map(c => ({
                     ...c,
-                    width: 200,
+                    width: columnWidth,
                 }))}
                 dataSource={dataSource.map((d, i) => ({
                     ...d,
                     key: i
                 }))}
                 size='small'
-                pagination={{ defaultPageSize: Math.floor(tableBodyHeight/tableRowHeight) || 10 }}
-                scroll={{ x: columns ? columns.length * 200 : tableBodyWidth, y: tableBodyHeight }}
-                loading={fetching}
+                loading={loading}
+                pagination={{
+                    pageSize,
+                    total,
+                    // simple: true,
+                    showTotal: (total, range) => {
+                        return `第${range[0]}到第${range[1]}条数据,共${total}条数据`;
+                    },
+                    onChange: (page, pageSize) => {
+                        typeof fetchFunction === 'function' && fetchFunction(page, pageSize);
+                    }
+                }}
+                scroll={{ x: columns ? columns.length * columnWidth : tableBodyWidth, y: tableBodyHeight }}
             />
         </Modal>
     }
 }
 
-export default connect(({ present: { loading, dataList } }) => ({ fetching: loading.effects['dataList/remoteDataList'], dataList }))(DataPreview);
+export default connect(({ present: { dataList } }) => ({ dataList }))(DataPreview);

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

@@ -1,7 +1,7 @@
 .datapreview {
     overflow: hidden;
     padding: 0;
-    top: 10%;
+    top: 0;
 
     .ant-modal-content {
         max-height: 100%;

+ 4 - 4
src/components/common/navigator.jsx

@@ -54,14 +54,14 @@ class Navigator extends React.Component {
                     <Menu.Item className='nav-page' key="home">
                         <Link to='/home'><Icon type="home" />我的</Link>
                     </Menu.Item>
-                    <Menu.Item className='nav-page' key="dashboard">
-                        <Link to='/dashboard'><Icon type="desktop" />报告与看板</Link>
+                    <Menu.Item className='nav-page' key="datasource">
+                        <Link to='/datasource'><Icon type="database" />数据源</Link>
                     </Menu.Item>
                     <Menu.Item className='nav-page' key="chart">
                         <Link to='/chart'><Icon type="area-chart" />图表</Link>
                     </Menu.Item>
-                    <Menu.Item className='nav-page' key="datasource">
-                        <Link to='/datasource'><Icon type="database" />数据源</Link>
+                    <Menu.Item className='nav-page' key="dashboard">
+                        <Link to='/dashboard'><Icon type="desktop" />报告与看板</Link>
                     </Menu.Item>
                     <Menu.Item disabled className='nav-page' key="modeling">
                         <Link to='#'><Icon type="tool" />建模分析</Link>

+ 4 - 3
src/components/common/rootLayout.jsx

@@ -21,6 +21,7 @@ class RootLayout extends React.Component {
         const { location, isAuthenticated, children } = this.props;
         const { goLogin } = this.state;
         const visibleLoginConfimBox = location !=='/login' && !isAuthenticated;
+        const token = window.localStorage.getItem('token');
 
         return (<div className='root-layout'>
             { children }
@@ -33,10 +34,10 @@ 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-label'>{token ? '登录已过期' : '未登录'}</div>
+                    <div className='confirm-text'>{token ? '距离上次登录已经超过30分钟,请重新登录' : ''}</div>
                     <div className='confirm-button'>
-                        <Button type="primary" onClick={this.setGoLogin}>重新登录</Button>
+                        <Button type="primary" onClick={this.setGoLogin}>{token ? '重新登录' : '登录'}</Button>
                     </div>
                 </div>
             </Modal>}

+ 5 - 0
src/components/dashboard/list.jsx

@@ -29,6 +29,11 @@ class DashboardList extends React.Component {
         const { dispatch } = this.props;
         this.setBodyWidth();
         dispatch({ type: 'dashboard/fetchList' });
+        window.addEventListener('resize', this.setBodyWidth);
+    }
+
+    componentWillUnmount() {
+        window.removeEventListener('resize', this.setBodyWidth)
     }
 
     /**

+ 16 - 16
src/components/dashboardDesigner/content.jsx

@@ -172,25 +172,25 @@ class DashboardDesignerContent extends React.Component {
             <Header>
                 <div className='filters'>
                     <div style={{ margin: '0 8px 0 10px' }}>筛选:</div>
-                        {tags.map(tag => (
-                            <Tag
-                                className={`filter-tag ${tag.using?'filter-tag-using':''}`}
-                                key={tag.key}
-                                closable={false}
-                                onClick={this.filterUsingChange}
-                                data-key={tag.key}
-                            >
-                                {tag.label}
-                            </Tag>
-                        ))}
+                    {tags.map(tag => (
                         <Tag
-                            onClick={this.showFilterBox}
-                            className={`filter-tag filter-tag-add`}
+                            className={`filter-tag ${tag.using?'filter-tag-using':''}`}
+                            key={tag.key}
+                            closable={false}
+                            onClick={this.filterUsingChange}
+                            data-key={tag.key}
                         >
-                            <Icon type="filter" theme="outlined" />
+                            {tag.label}
                         </Tag>
-                    </div>
-                    {visibleFilterBox && <FilterBox type='dashboard' code={code} columns={this.getRelationFilterColumns()} filterData={filters} visibleFilterBox={visibleFilterBox} showFilterBox={this.showFilterBox} hideFilterBox={this.hideFilterBox} createFilters={this.createFilters} />}
+                    ))}
+                    <Tag
+                        onClick={this.showFilterBox}
+                        className={`filter-tag filter-tag-add`}
+                    >
+                        <Icon type="filter" theme="outlined" />
+                    </Tag>
+                </div>
+                {visibleFilterBox && <FilterBox type='dashboard' code={code} columns={this.getRelationFilterColumns()} filterData={filters} visibleFilterBox={visibleFilterBox} showFilterBox={this.showFilterBox} hideFilterBox={this.hideFilterBox} createFilters={this.createFilters} />}
                 {isOwner && <div className='viewtype'>
                     <Tooltip title="图表">
                         <Icon className='viewtype-icon' type="area-chart" theme="outlined" onClick={(item) => {

+ 4 - 3
src/components/dataSource/list.jsx

@@ -307,8 +307,7 @@ class DataSource extends React.Component {
             render: (text, record) => {
                 return <div className='datasource-name'>
                     <div className={`datasource-type`}>
-                        <Icon type="file-excel" />
-                        {/* <Icon type="database" /> */}
+                        {record.type === 'database' ? <Icon type="database" theme="outlined" /> : <Icon type="file-excel" theme="outlined" />}
                     </div>
                     <div>
                         <span>
@@ -498,7 +497,9 @@ class DataSource extends React.Component {
                                     visibleDataPreviewBox: false
                                 });
                             }}
-                            code={selectedRecord.code}
+                            fetchFunction={(page, pageSize) => {
+                                dispatch({ type: 'dataSource/remoteDataList', code: selectedRecord.code, page, pageSize });
+                            }}
                         />}
                     </Card>
                 </Content>

+ 1 - 1
src/components/dataSourceDetail/accessConfig.jsx

@@ -1,6 +1,6 @@
 import React from 'react'
 import { connect } from 'dva'
-import { Layout, Card, Row, Col, Table, Input, Checkbox, Button, Switch, Icon, Tag } from 'antd'
+import { Layout, Card, Row, Col, Table, Input, Checkbox, Button, Icon, Tag } from 'antd'
 import FilterBox from '../common/filterBox/filterBox'
 import AccessObjectBox from '../common/accessObjectBox/accessObjectBox'
 import moment from 'moment'

+ 177 - 6
src/models/chartDesigner.js

@@ -514,16 +514,16 @@ export default {
             try {
                 const chartDesigner = yield select(state => state.present.chartDesigner);
                 const { code, dataViewConfig, filters } = chartDesigner;
+                const { page, pageSize } = action;
                 const body = {
                     id: code,
                     columnListName: dataViewConfig.viewColumns.map(c => c.name),
                     sortColumn: dataViewConfig.sortColumn.key,
                     sort: dataViewConfig.sortType,
-                    showLine: 999999,
                     filters: getBodyFilters(filters),
                     testPage: {
-                        pageNum: 1,
-                        pageSize: 999999,
+                        pageNum: page || 1,
+                        pageSize: pageSize || 25,
                     }
                 };
 
@@ -537,14 +537,20 @@ export default {
                 if(!res.err && res.data.code > 0) {
                     let option = parseChartOption('dataView', res.data.data, dataViewConfig);
                     yield put({ type: 'silentSetField', name: 'chartOption', value: option });
+                    // 主动触发一次window的resize事件
+                    yield window.setTimeout(() => {
+                        var e = document.createEvent("Event");
+                        e.initEvent("resize", true, true);
+                        window.dispatchEvent(e);
+                    }, 200);
                 }else {
                     message.error('请求列表数据失败: ' + (res.err || res.data.msg));
-                    yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
+                    // yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
                 }
                 yield put({ type: 'silentSetField', name: 'fetchConfig', value: body });
             }catch(e) {
                 console.error(e);
-                yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
+                // yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
                 message.error('请求列表数据失败: ' + e);
             }
         },
@@ -586,7 +592,172 @@ export default {
                 yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
                 message.error('请求统计数据失败: ' + e);
             }
-        }
+        },
+        /**
+         * 将图表数据以表格的方式作为预览
+         */
+        *remoteChartDataList(action, { select, call, put }) {
+            try {
+                const chartDesigner = yield select(state => state.present.chartDesigner);
+                const { code, baseConfig, aggregateTableConfig, lineConfig, barConfig, pieConfig, scatterConfig, dataViewConfig, filters } = chartDesigner;
+                const { viewType } = baseConfig;
+                const { page, pageSize } = action;
+
+                let columns = [];
+                let columnListName = [];
+                let sortColumn = null;
+
+                if(viewType === 'aggregateTable') {
+                    const { groupBy, targetColumn } = aggregateTableConfig;
+                    groupBy.map(g => ({
+                        title: g.label,
+                        dataIndex: g.key,
+                        // render: g.columnType === 'time' ? ((v, r, i) => moment(v).format('YYYY-MM-DD')) : v => v
+                    })).concat({
+                        title: targetColumn.label,
+                        dataIndex: targetColumn.name,
+                        render: targetColumn.type === 'time' ? ((v, r, i) => moment(v).format('YYYY-MM-DD')) : v => v
+                    }).filter(x => !!x.dataIndex).forEach(x => {
+                        if(!columns.find(c => c.dataIndex === x.dataIndex)) {
+                            columns.push(x);
+                        }
+                    });;
+                    sortColumn = targetColumn.name;
+                }else if(viewType === 'line') {
+                    const { groupBy, xAxis, yAxis } = lineConfig;
+                    [{
+                        title: groupBy ? groupBy.label : '',
+                        dataIndex: groupBy ?groupBy.key : '',
+                        // render: g.columnType === 'time' ? ((v, r, i) => moment(v).format('YYYY-MM-DD')) : v => v
+                    }, {
+                        title: xAxis.column.label,
+                        dataIndex: xAxis.column.value,
+                        render: xAxis.column.type === 'time' ? ((v, r, i) => moment(v).format('YYYY-MM-DD')) : v => v
+                    }, {
+                        title: yAxis.column.label,
+                        dataIndex: yAxis.column.value,
+                        render: yAxis.column.type === 'time' ? ((v, r, i) => moment(v).format('YYYY-MM-DD')) : v => v
+                    }].filter(x => !!x.dataIndex).forEach(x => {
+                        if(!columns.find(c => c.dataIndex === x.dataIndex)) {
+                            columns.push(x);
+                        }
+                    });;
+                    sortColumn = xAxis.column.value;
+                }else if(viewType === 'bar') {
+                    const { groupBy, xAxis, yAxis } = barConfig;
+                    [{
+                        title: groupBy ? groupBy.label : '',
+                        dataIndex: groupBy ? groupBy.key : '',
+                        // render: g.columnType === 'time' ? ((v, r, i) => moment(v).format('YYYY-MM-DD')) : v => v
+                    }, {
+                        title: xAxis.column.label,
+                        dataIndex: xAxis.column.value,
+                        render: xAxis.column.type === 'time' ? ((v, r, i) => moment(v).format('YYYY-MM-DD')) : v => v
+                    }, {
+                        title: yAxis.column.label,
+                        dataIndex: yAxis.column.value,
+                        render: yAxis.column.type === 'time' ? ((v, r, i) => moment(v).format('YYYY-MM-DD')) : v => v
+                    }].filter(x => !!x.dataIndex).forEach(x => {
+                        if(!columns.find(c => c.dataIndex === x.dataIndex)) {
+                            columns.push(x);
+                        }
+                    });;
+                    sortColumn = xAxis.column.value;
+                }else if(viewType === 'pie') {
+                    const { xAxis, yAxis } = pieConfig;
+                    [{
+                        title: xAxis.column.label,
+                        dataIndex: xAxis.column.value,
+                        render: xAxis.column.type === 'time' ? ((v, r, i) => moment(v).format('YYYY-MM-DD')) : v => v
+                    }, {
+                        title: yAxis.column.label,
+                        dataIndex: yAxis.column.value,
+                        render: yAxis.column.type === 'time' ? ((v, r, i) => moment(v).format('YYYY-MM-DD')) : v => v
+                    }].filter(x => !!x.dataIndex).forEach(x => {
+                        if(!columns.find(c => c.dataIndex === x.dataIndex)) {
+                            columns.push(x);
+                        }
+                    });;
+                    sortColumn = xAxis.column.value;
+                }else if(viewType === 'scatter') {
+                    const { groupBy, xAxis, yAxis } = scatterConfig;
+                    [{
+                        title: groupBy ? groupBy.label : '',
+                        dataIndex: groupBy ? groupBy.key : '',
+                        // render: g.columnType === 'time' ? ((v, r, i) => moment(v).format('YYYY-MM-DD')) : v => v
+                    }, {
+                        title: xAxis.column.label,
+                        dataIndex: xAxis.column.value,
+                        render: xAxis.column.type === 'time' ? ((v, r, i) => moment(v).format('YYYY-MM-DD')) : v => v
+                    }, {
+                        title: yAxis.column.label,
+                        dataIndex: yAxis.column.value,
+                        render: yAxis.column.type === 'time' ? ((v, r, i) => moment(v).format('YYYY-MM-DD')) : v => v
+                    }].filter(x => !!x.dataIndex).forEach(x => {
+                        if(!columns.find(c => c.dataIndex === x.dataIndex)) {
+                            columns.push(x);
+                        }
+                    });;
+                    sortColumn = xAxis.column.value;
+                }else if(viewType === 'dataView') {
+                    columns = dataViewConfig.viewColumns.map(c => ({
+                        title: c.label,
+                        dataIndex: c.name,
+                        render: c.type === 'time' ? ((v, r, i) => moment(v).format('YYYY-MM-DD')) : v => v
+                    }));
+                    sortColumn = dataViewConfig.sortColumn.key;
+                }
+                columnListName = columns.map(c => c.dataIndex);
+
+                const body = {
+                    id: code,
+                    columnListName: columnListName,
+                    sortColumn: sortColumn,
+                    sort: 'asc',
+                    filters: getBodyFilters(filters),
+                    testPage: {
+                        pageNum: page || 1,
+                        pageSize: pageSize || 25,
+                    }
+                };
+
+                yield put({ type: 'dataList/setField', name: 'loading', value: true });
+                console.log('请求数据列表', body);
+                let res = yield call(service.fetch, {
+                    url: URLS.CHART_DATAVIEW_OPTION,
+                    body: body,
+                    timeout: 30000
+                });
+                console.log('请求数据列表', body, res);
+                if(!res.err && res.data.code > 0) {
+
+                    const { valueList } = res.data.data;
+                    const { list: dataSource, pageSize, total } = valueList;
+
+                    yield put({ type: 'dataList/setFields', fields: [
+                        { name: 'columns', value: columns },
+                        { name: 'dataSource', value: dataSource },
+                        { name: 'pageSize', value: pageSize },
+                        { name: 'total', value: total }
+                    ] });
+                    // 主动触发一次window的resize事件
+                    yield window.setTimeout(() => {
+                        var e = document.createEvent("Event");
+                        e.initEvent("resize", true, true);
+                        window.dispatchEvent(e);
+                    }, 20);
+
+                }else {
+                    // message.error('请求列表数据失败: ' + (res.err || res.data.msg));
+                    // yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
+                }
+            }catch(e) {
+                console.error(e);
+                message.error('请求数据列表失败: ' + e);
+            }finally {
+                yield put({ type: 'dataList/setField', name: 'loading', value: false });
+            }
+        },
     },
     subscriptions: {
         setup({ dispatch, history }) {

+ 19 - 10
src/models/dashboard.js

@@ -85,6 +85,24 @@ export default {
                     const main = yield select(state => state.present.main);
                     const { currentUser } = main;
 
+                    const allDataSources = items.map(item => {
+                        if(item.viewType === 'chart') {
+                            return {
+                                code: item.dataSourceCode,
+                                name: item.dataSourceName
+                            }
+                        }else {
+                            return null
+                        }
+                    }).filter(item => !!item);
+
+                    const dataSources = [];
+                    allDataSources.forEach(ad => {
+                        if(!dataSources.find(d => d.code === ad.code)) {
+                            dataSources.push(ad);
+                        }
+                    });
+
                     let data = {
                         code:  resData.id+'',
                         name: resData.bdName,
@@ -94,16 +112,7 @@ export default {
                         creatorCode: resData.createId + '',
                         creatorName: resData.createBy,
                         createTime: resData.createDate,
-                        dataSources: items.map(item => {
-                            if(item.viewType === 'chart') {
-                                return {
-                                    code: item.dataSourceCode,
-                                    name: item.dataSourceName
-                                }
-                            }else {
-                                return null
-                            }
-                        }).filter(item => !!item),
+                        dataSources: dataSources,
                         relationColumns: relationColumns,
                         editMode: currentUser.code === resData.createId + ''
                     }

+ 19 - 29
src/models/dataList.js

@@ -7,9 +7,27 @@ export default {
     namespace: 'dataList',
     state: {
         columns: [],
-        dataSource: []
+        dataSource: [],
+
+        loading: false,
+        pageSize: 25,
+        total: 0,
     },
     reducers: {
+        setField(state, action) {
+            const { name, value } = action;
+            let obj = {};
+            obj[name] = value;
+            let newState = Object.assign({}, state, obj);
+            return Object.assign({}, newState);
+        },
+        setFields(state, action) {
+            const { fields } = action;
+            let obj = {};
+            fields.map(f => (obj[f.name] = f.value));
+            let newState = Object.assign({}, state, obj);
+            return Object.assign({}, newState);
+        },
         setColumns(state, action) {
             const { columns } = action;
             return { ...state, columns }
@@ -23,34 +41,6 @@ export default {
         }
     },
     effects: {
-        *remoteDataList(action, { put, call, select }) {
-            const { byChart, code } = action;
-            const body = code;
-            try {
-                const res = yield call(service.fetch, {
-                    url: byChart ? URLS.DATASOURCE_DATA_LIST_BY_CHART : URLS.DATASOURCE_DATA_LIST,
-                    body
-                });
-                console.log('请求数据列表', body, res);
-                if(!res.err && res.data.code > 0) {
-                    const { columnConfig, values } = res.data.data;
-                    let columns = JSON.parse(columnConfig).map(c => ({
-                        title: c.columnLable,
-                        dataIndex: c.columnName,
-                        render: c.columnType === 'time' ? ((v, r, i) => moment(v).format('YYYY-MM-DD')) : v => v
-                    }));
-                    let dataSource = values;
-                    yield put({ type: 'setColumns', columns });
-                    yield put({ type: 'setDataSource', dataSource });
-                }else {
-                    console.log(body, (res.err || res.data.msg));
-                    message.error('请求数据列表失败: ' + (res.err || res.data.msg));
-                }
-            }catch(e) {
-                console.log(body, e);
-                message.error('请求数据列表失败: ' + e);
-            }
-        }
     },
     subscriptions: {
         setup({ dispatch, history }) {

+ 53 - 0
src/models/dataSource.js

@@ -1,6 +1,7 @@
 import { message } from 'antd'
 import * as service from '../services/index'
 import URLS from '../constants/url'
+import moment from 'moment'
 
 export default {
     namespace: 'dataSource',
@@ -732,6 +733,58 @@ export default {
                 console.log(body, e);
                 message.error('移交失败: ' + e);
             }
+        },
+        /**
+         * 预览数据
+         */
+        *remoteDataList(action, { put, call, select }) {
+            const { code, page, pageSize } = action;
+            const body = {
+                id: code,
+                testPage: {
+                    pageNum: page || 1,
+                    pageSize: pageSize || 25,
+                }
+            };
+
+            try {
+                yield put({ type: 'dataList/setField', name: 'loading', value: true });
+                const res = yield call(service.fetch, {
+                    url: URLS.DATASOURCE_DATA_LIST,
+                    body
+                });
+                console.log('请求数据列表', body, res);
+                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,
+                        render: c.columnType === 'time' ? ((v, r, i) => moment(v).format('YYYY-MM-DD')) : v => v
+                    }));
+                    const { list: dataSource, pageSize, total } = values;
+
+                    yield put({ type: 'dataList/setFields', fields: [
+                        { name: 'columns', value: columns },
+                        { name: 'dataSource', value: dataSource },
+                        { name: 'pageSize', value: pageSize },
+                        { name: 'total', value: total }
+                    ] });
+                    // 主动触发一次window的resize事件
+                    yield window.setTimeout(() => {
+                        var e = document.createEvent("Event");
+                        e.initEvent("resize", true, true);
+                        window.dispatchEvent(e);
+                    }, 20);
+                }else {
+                    console.log(body, (res.err || res.data.msg));
+                    message.error('请求数据列表失败: ' + (res.err || res.data.msg));
+                }
+            }catch(e) {
+                console.log(body, e);
+                message.error('请求数据列表失败: ' + e);
+            }finally {
+                yield put({ type: 'dataList/setField', name: 'loading', value: false });
+            }
         }
     },
     subscriptions: {

+ 7 - 3
src/models/parseChartOption.js

@@ -284,10 +284,10 @@ function aggregateTableOption( data, aggregateTableConfig) {
 }
 
 function dataViewOption(data, dataViewConfig) {
-    const resData = data.valueList.list;
+    const { list, pageNum, pageSize, pages, total } = data.valueList;
 
     let columns = dataViewConfig.viewColumns || [];
-    let dataSource = resData || [];
+    let dataSource = list || [];
 
     let option = {
         columns: columns.map(c => ({
@@ -297,7 +297,11 @@ function dataViewOption(data, dataViewConfig) {
         })),
         dataSource: dataSource.map((d, i) => {
             return { ...d, key: i}
-        })
+        }),
+        pageNum,
+        pageSize,
+        pages,
+        total,
     };
 
     return option;

+ 2 - 2
src/models/recent.js

@@ -48,7 +48,7 @@ export default {
                             createTime: r.createDate,
                             recentTime: moment(r.reDate)
                         }
-                    }).sort((a, b) => moment(a.recentTime) - moment(b.recentTime));
+                    });
                     yield put({ type: 'listRecentChart', recentChart });
                 }else {
                     message.error('读取最近访问图表列表错误: ' + (res.err || res.data.msg));
@@ -79,7 +79,7 @@ export default {
                             description: r.bdNote || "",
                             recentTime: r.reDate
                         }
-                    }).sort((a, b) => moment(b.recentTime) - moment(a.recentTime));
+                    });
                     yield put({ type: 'listRecentDashboard', recentDashboard });
                 }else {
                     message.error('读取最近访问看板列表错误: ' + (res.err || res.data.msg));

+ 2 - 14
src/routes/privateRoute.jsx

@@ -7,20 +7,8 @@ export default ({ component: Component, ...rest }) => (
         {...rest}
         render={props =>{
 
-            if (!window.localStorage.getItem('token')) {
-                window.localStorage.setItem("token", "false");
-            }else {
-                /**
-                 * 是否过期
-                 * 1.是-----设置isAuthenticated为false
-                 */
-                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 t = moment(+window.localStorage.getItem('expireTime'));
+            const isAuthenticated = t.isValid() && t.diff(moment()) > 0;
             return <RootLayout location={props.location} isAuthenticated={isAuthenticated}>
                 <Component isAuthenticated={isAuthenticated} {...props} />
             </RootLayout>