Explorar o código

请求过程添加loading标识/数据请求时机调整/悬浮提示默认设置调整

zhuth %!s(int64=7) %!d(string=hai) anos
pai
achega
d3704a488d

+ 1 - 0
package.json

@@ -11,6 +11,7 @@
     "app": "^0.1.0",
     "braft-editor": "^1.9.8",
     "dva": "^2.3.1",
+    "dva-loading": "^2.0.3",
     "echarts": "^4.1.0",
     "echarts-for-react": "^2.0.12-beta.0",
     "moment": "^2.19.3",

+ 3 - 6
src/components/chart/chooseDataSourceBox.jsx

@@ -20,13 +20,10 @@ class ChooseDataSourceBox extends React.Component {
 
     okHandler = (model) => {
         const { selectedRecord } = this.state;
-        const { dispatch } = this.props;
+        const { dispatch, hideBox } = this.props;
         if(selectedRecord) {
-            dispatch({ type: 'main/redirect', path: '/chart/create' });
-            dispatch({ type: 'chartDesigner/changeDataSource', value: {
-                dataSource: selectedRecord.code,
-                viewType: 'bar'
-            } });
+            dispatch({ type: 'chartDesigner/remoteQucikAdd', dataSource: selectedRecord });
+            hideBox();
         }else {
             message.warning('未选中数据源');
         }

+ 12 - 3
src/components/chart/list.jsx

@@ -54,7 +54,9 @@ class ChartList extends React.Component {
             </Menu>
         )
 
-        return list.map( (l, i) => (
+        let cards = list.sort((a, b) => {
+            return new Date(b.createTime) - new Date(a.createTime)
+        }).map( (l, i) => (
             <CardGrid className='chart-card' key={i} onClick={() => {
                 this.setState({ selectedCode: l.code })
             }}>
@@ -92,10 +94,16 @@ class ChartList extends React.Component {
                 </Card>
             </CardGrid>
         ));
+        if(cards.length === 0) {
+            cards = <div style={{ padding: '7px', textAlign: 'center', fontSize: '14px', color: 'rgba(0, 0, 0, 0.45)' }}>暂无数据</div>
+        }
+        return cards;
     }
 
     render() {
         const { visibleBox } = this.state;
+        const { dispatch } = this.props;
+
         return (
             <Layout className='chart-list'>
                 <Content>
@@ -109,6 +117,7 @@ class ChartList extends React.Component {
                             </Col>
                             <Col >
                                 <Button onClick={() => {
+                                    dispatch({ type: 'dataSource/fetchList' });
                                     this.setState({
                                         visibleBox: true
                                     });
@@ -134,8 +143,8 @@ class ChartList extends React.Component {
 }
 
 
-function mapStateToProps({present: {chart}}) {
-    return { chart }
+function mapStateToProps(state) {
+    return { chart: state.present.chart }
 }
 
 export default connect(mapStateToProps)(ChartList)

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

@@ -12,7 +12,7 @@ const Header = ({ chartDesigner, dispatch }) => {
                 <Button onClick={() => {
                     dispatch({ type: 'main/redirect', path: '/chart' });
                 }}>
-                    <Icon type='back' />返回
+                    <Icon type='left' />返回
                 </Button>
             </div>
             <div className='header-item toolbar-title'>

+ 16 - 8
src/components/common/loading.jsx

@@ -1,18 +1,26 @@
 import React from 'react'
-import { Modal, Spin } from 'antd'
+import { Spin } from 'antd'
 import { connect } from 'dva'
-import '../../models/dataSource'
+import './loading.less'
 
-const Loading = () => {
+const Loading = ({ loading }) => {
     return (
-        <Modal className='loading-box'visible={false}>
-            <Spin />
-        </Modal>
+        <div className='loading-box'style={{ display: loading ? 'block' : 'none' }}>
+            <Spin size="large"/>
+        </div>
     );
 }
 
-function mapStateToProps({ present: { dataSource } }) {
-    return { dataSource: dataSource };
+function mapStateToProps(state) {
+    const models = state.present.loading.models;
+    let loading = false;
+    for(let model in models) {
+        if(models[model]) {
+            loading = true;
+            break;
+        }
+    }
+    return { loading: loading };
 }
 
 export default connect(mapStateToProps)(Loading)

+ 7 - 0
src/components/common/loading.less

@@ -0,0 +1,7 @@
+.loading-box {
+    position: absolute;
+    margin: 0 auto;
+    top: 60px;
+    right: 20px;
+    z-index: 9999;
+}

+ 2 - 1
src/components/datasource/baseConfig.jsx

@@ -108,7 +108,8 @@ const DataSourceBaseConfig = ({ dataSource, dataConnect, dispatch, mode }) => {
                                     wrapperCol: { span: 16 }
                                 }}>
                                     <Input
-                                        value={dataSource.newOne.password}
+                                        // value={dataSource.newOne.password}
+                                        emptyText='********'
                                         onChange={(e) => {
                                             let value = e.target.value;
                                             dispatch({ type: 'dataSource/setNewModelField', name: 'password', value: value });

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

@@ -99,27 +99,6 @@ class DataSource extends React.Component {
             dataIndex: 'name',
             key: 'name',
             width: 100,
-            sorter: (a, b) => a.name.localeCompare(b.name, 'zh-Hans-CN', {sensitivity: 'accent'}),
-            filterDropdown: (
-                <div className="custom-filter-dropdown">
-                    <Input
-                        ref={ele => this.searchInput = ele}
-                        placeholder="Search name"
-                        value={this.state.search.name}
-                        onChange={(e) => { this.onInputChange('name', e.target.value) }}
-                        onPressEnter={this.onSearch}
-                    />
-                    <Button type="primary" onClick={this.onSearch}>Search</Button>
-                </div>
-            ),
-            className: `column-${this.state.search.name?'filtered':'nofiltered'}`,
-            filterIcon: <Icon type="smile-o"/>,
-            filterDropdownVisible: this.state.filterDropdownVisible,
-            onFilterDropdownVisibleChange: (visible) => {
-                this.setState({
-                    filterDropdownVisible: visible,
-                }, () => this.searchInput && this.searchInput.focus());
-            },
             render: (text, record) => {
                 return <div className='datasource-name'>
                     <div className={`datasource-type type-${record.type.key}`}></div>
@@ -205,7 +184,9 @@ class DataSource extends React.Component {
                         <Table
                             className='datasource-table datasource-table'
                             columns={dataSourceColumns}
-                            dataSource={dataSource.list}
+                            dataSource={dataSource.list.sort((a, b) => {
+                                return new Date(b.createTime) - new Date(a.createTime);
+                            })}
                             loading={loading}
                             size='small'
                             scroll={{x: false, y: true}}

+ 2 - 1
src/index.js

@@ -10,6 +10,7 @@ import chart from './models/chart'
 import './utils/baseUtils'
 import './index.less'
 import dashboardDesigner from './models/dashboardDesigner';
+import createLoading from 'dva-loading';
 
 // 1. Initialize
 const app = dva({
@@ -23,7 +24,7 @@ const app = dva({
 });
 
 // 2. Plugins
-// app.use({});
+app.use(createLoading());
 
 // 3. Model
 app.model(mainModel); // 通用action

+ 9 - 3
src/models/chart.js

@@ -20,8 +20,12 @@ export default {
         },
     },
     effects: {
-        *fetchList(action, { call, put }) {
+        *fetchList(action, { select, call, put }) {
             try{
+                const chart = yield select(state => state.present.chart);
+                if(!action.mandatory && chart.list.length > 0) {
+                    return;
+                }
                 const res = yield call(service.fetch, {
                     url: URLS.CHART_LIST
                 });
@@ -71,7 +75,7 @@ export default {
 
                     let resData = res.data.data;
                     let groupBy = JSON.parse(resData.groupBy);
-                    let chartConfig = JSON.parse(JSON.parse(resData.chartConfig));
+                    let chartConfig = JSON.parse(resData.chartConfig);
                     let viewType = getViewType(resData.chartType);
 
                     let data = {
@@ -153,8 +157,10 @@ export default {
     },
     subscriptions: {
         setup({ dispatch, history }) {
-            dispatch({ type: 'fetchList' });
             return history.listen(({ pathname, query }) => {
+                if(pathname === '/chart') {
+                    dispatch({ type: 'fetchList' });
+                }
                 let detail = pathname.match(/chart\/(\w+)/);
                 if(detail) {
                     let code = detail[1];

+ 79 - 54
src/models/chartDesigner.js

@@ -80,10 +80,21 @@ export default {
     },
     reducers: {
         /**
-         * 初始化model字段值
-         * 设置撤销重做的起点,不进入撤销重做的历史
+         * 更新model字段值
+         * 1. 进入撤销重做历史
          */
-        defaultSetFields(state, action) {
+        setField(state, action) {
+            const { name, value } = action;
+            let obj = {};
+            obj[name] = value;
+            let newState = Object.assign({}, state, obj);
+            return newState;
+        },
+        /**
+         * 批量更新model字段值
+         * 1. 进入撤销重做历史
+         */
+        setFields(state, action) {
             const { fields } = action;
             let obj = {};
             fields.map(f => (obj[f.name] = f.value));
@@ -91,11 +102,10 @@ export default {
             return newState;
         },
         /**
-         * 更新model字段值方法1
-         * 1. 为保持撤销重做的功能有效性,能够撤销重做的action动作才使用该方法
-         * 2. 对数据刷新没有影响的model字段改变一般用该action
+         * 更新model字段值
+         * 1. 不进入撤销重做历史
          */
-        setField(state, action) {
+        silentSetField(state, action) {
             const { name, value } = action;
             let obj = {};
             obj[name] = value;
@@ -103,42 +113,18 @@ export default {
             return newState;
         },
         /**
-         * 批量更新model字段值方法1
+         * 批量更新model字段值
+         * 1. 不进入撤销重做历史
          */
-        setFields(state, action) {
+        silentSetFields(state, action) {
             const { fields } = action;
             let obj = {};
             fields.map(f => (obj[f.name] = f.value));
             let newState = Object.assign({}, state, obj);
             return newState;
         },
-        setDataSource(state, action) {
-            const { value } = action;
-            let obj = {};
-            obj['baseConfig'] = value;
-            return Object.assign({}, state, obj);
-        },
-        setColumns(state, action) {
-            const { value } = action;
-            let obj = {};
-            obj['columns'] = value;
-            return Object.assign({}, state, obj);
-        },
-        setAutoRefresh(state, action) {
-            const { value } = action;
-            let obj = {};
-            obj['autoRefresh'] = value;
-            return Object.assign({}, state, obj);
-        },
-        setChartOption(state, action) {
-            const { option } = action;
-            let obj = {};
-            obj['chartOption'] = option;
-            return Object.assign({}, state, obj);
-        },
         reset(state, action) {
             let obj = Object.assign({}, state, state.originData);
-            console.log(obj);
             return obj;
         }
     },
@@ -149,7 +135,7 @@ export default {
          */
         *defaultChangeFields(action, { select, call, put }) {
             const { fields } = action;
-            yield put({ type: 'defaultSetFields', fields });
+            yield put({ type: 'silentSetFields', fields });
 
             const chartDesigner = yield select(state => state.present.chartDesigner);
             const { autoRefresh } = chartDesigner;
@@ -158,7 +144,7 @@ export default {
             }
         },
         /**
-         * 更新model字段值方法2
+         * 更新model字段值
          * 可能影响到数据刷新的model字段改变一般用该action
          */
         *changeField(action, { select, call, put }) {
@@ -173,7 +159,7 @@ export default {
             }
         },
         /**
-         * 批量更新model字段值方法2
+         * 批量更新model字段值
          */
         *changeFields(action, { select, call, put }) {
             const { fields } = action;
@@ -187,9 +173,48 @@ export default {
         },
         *changeDataSource(action, { select, call, put }) {
             const { value } = action;
-            yield put({ type: 'setDataSource', value });
+            yield put({ type: 'silentSetField', name: 'baseConfig', value });
             yield put({ type: 'remoteDataColumn', code: value.dataSource });
         },
+        *remoteQucikAdd(action, { select, call, put }) {
+            try{
+                const { dataSource } = action;
+
+                yield put({ type: 'silentSetFields', fields: [
+                    { name: 'baseConfig', value: { dataSource: dataSource.code, viewType: '' } }
+                ] });
+                const chartDesigner = yield select(state => state.present.chartDesigner);
+                const { baseConfig, preparing } = chartDesigner;
+
+                let body = {
+                    chartName: dataSource.name + '(未命名)',
+                    dataId: baseConfig.dataSource,
+                    groupBy: preparing.groupBy && preparing.groupBy.key ? [{
+                        columnName: preparing.groupBy.key,
+                        columnRamane: preparing.groupBy.label
+                    }] : [],
+                    createBy: 'zhuth',
+                    describes: '',
+                    style: '',
+                    chartConfig: '{}',
+                    chartType: ''
+                };
+                const res = yield call(service.fetch, {
+                    url: URLS.CHART_ADD,
+                    body: body
+                })
+                if(!res.err && res.data.code > 0) {
+                    yield put({ type: 'chart/fetchList', mandatory: true });
+                    // TODO 
+                    // yield put({ type: 'main/redirect', path: '/chart/' + 32 });
+                }else {
+                    message.error('新增失败');
+                }
+            }catch(e) {
+                console.error(e);
+                message.error('新增失败');
+            }
+        },
         *remoteAdd(action, { select, call, put }) {
             try{
                 const chartDesigner = yield select(state => state.present.chartDesigner);
@@ -198,7 +223,7 @@ export default {
                 let body = {
                     chartName: header.label,
                     dataId: baseConfig.dataSource,
-                    groupBy: preparing.groupBy ? [{
+                    groupBy: preparing.groupBy && preparing.groupBy.key ? [{
                         columnName: preparing.groupBy.key,
                         columnRamane: preparing.groupBy.label
                     }] : [],
@@ -223,7 +248,8 @@ export default {
                 })
                 if(!res.err && res.data.code > 0) {
                     message.success('新增成功!');
-                    yield put({ type: 'chart/fetchList' });
+                    // yield put({ type: 'silentSetField', name: 'code', value: code });
+                    yield put({ type: 'chart/fetchList', mandatory: true });
                 }else {
                     message.error('新增失败');
                 }
@@ -265,7 +291,7 @@ export default {
                 })
                 if(!res.err && res.data.code > 0) {
                     message.success('修改成功!');
-                    yield put({ type: 'chart/fetchList' });
+                    yield put({ type: 'chart/fetchList', mandatory: true });
                 }
             }catch(e) {
                 console.log(e);
@@ -291,7 +317,7 @@ export default {
                         }
                     })
                     console.log(columns);
-                    yield put({ type: 'setColumns', value: columns });
+                    yield put({ type: 'silentSetField', name: 'columns', value: columns });
                 }else {
                     console.log(res);
                 }
@@ -321,7 +347,7 @@ export default {
                 const { barConfig, preparing } = chartDesigner;
                 const body = {
                     tableName: "TEST_BI_DATA",
-                    groups: preparing.groupBy ? [preparing.groupBy.key] : [],
+                    groups: preparing.groupBy && preparing.groupBy.key ? [preparing.groupBy.key] : [],
                     xAxis: {
                         columnRename: barConfig.xAxis.column.value,
                         columnType: barConfig.xAxis.column.type,
@@ -343,13 +369,13 @@ export default {
                     res.viewType = 'bar';
                     res.data.data.xTitle = barConfig.xAxis?`${barConfig.xAxis.column.label}${barConfig.xAxis.granularity.value?'('+barConfig.xAxis.granularity.label+')':''}`:null
                     res.data.data.yTitle = barConfig.yAxis?`${barConfig.yAxis.column.label}${barConfig.yAxis.gauge.value?'('+barConfig.yAxis.gauge.label+')':''}`:null
-                    yield put({ type: 'setChartOption', option: res });
+                    yield put({ type: 'silentSetField', name: 'chartOption', value: res });
                 }else {
-                    yield put({ type: 'setChartOption', option: {} });
+                    yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
                 }
             }catch(e) {
                 console.error(e);
-                yield put({ type: 'setChartOption', option: {} });
+                yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
             }
         },
         *fetchPieData(action, { select, call, put }) {
@@ -379,13 +405,13 @@ export default {
                     console.log('res: ', res);
                     res.viewType = 'pie';
                     res.data.data.columnName = pieConfig.xAxis.column.label + (pieConfig.xAxis.granularity.value ? '('+pieConfig.xAxis.granularity.label+')' : '');
-                    yield put({ type: 'setChartOption', option: res });
+                    yield put({ type: 'silentSetField', name: 'chartOption', value: res });
                 }else {
-                    yield put({ type: 'setChartOption', option: {} });
+                    yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
                 }
             }catch(e) {
                 console.error(e);
-                yield put({ type: 'setChartOption', option: {} });
+                yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
             }
         },
         *fetchLineData(action, { select, call, put }) {
@@ -403,7 +429,7 @@ export default {
                         columnRename: lineConfig.yAxis.column.value,
                         showDataType: lineConfig.yAxis.gauge.value
                     },
-                    groups: preparing.groupBy ? [preparing.groupBy.key] : []
+                    groups: preparing.groupBy && preparing.groupBy.key ? [preparing.groupBy.key] : [],
                 };
                 console.log('lineBody: ', body);
                 let res = yield call(service.fetch, {
@@ -413,14 +439,13 @@ export default {
                 console.log('line res', res);
                 if(!res.err && res.data.code > 0) {
                     res.viewType = 'line';
-                    // res.data.data.columnName = pieConfig.xAxis.column.label + (pieConfig.xAxis.granularity.value ? '('+pieConfig.xAxis.granularity.label+')' : '');
-                    yield put({ type: 'setChartOption', option: res });
+                    yield put({ type: 'silentSetField', name: 'chartOption', value: res });
                 }else {
-                    yield put({ type: 'setChartOption', option: {} });
+                    yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
                 }
             }catch(e) {
                 console.error(e);
-                yield put({ type: 'setChartOption', option: {} });
+                yield put({ type: 'silentSetField', name: 'chartOption', value: {} });
             }
         }
     },

+ 9 - 1
src/models/dataConnect.js

@@ -61,6 +61,10 @@ export default {
     effects: {
         *fetchList(action, { select, call, put, takeEvery, takeLatest }) {
             try {
+                const dataConnect = yield select(state => state.present.dataConnect);
+                if(!action.mandatory && dataConnect.list.length > 0) {
+                    return;
+                }
                 const res = yield call(service.fetch, {
                     url: URLS.DATACONNECT_LIST,
                     body: {}
@@ -184,7 +188,11 @@ export default {
     },
     subscriptions: {
         setup({ dispatch, history }) {
-            dispatch({ type: 'fetchList' })
+            return history.listen(({ pathname, query }) => {
+                if((pathname+'').startsWith('/datasource/database/')) {
+                    dispatch({ type: 'fetchList' })
+                }
+            })
         }
     }
 };

+ 10 - 2
src/models/dataSource.js

@@ -102,6 +102,10 @@ export default {
     effects: {
         *fetchList(action, { select, call, put, takeEvery, takeLatest }) {
             try {
+                const dataSource = yield select(state => state.present.dataSource);
+                if(!action.mandatory && dataSource.list.length > 0) {
+                    return;
+                }
                 const res = yield call(service.fetch, {
                     url: URLS.DATASOURCE_LIST,
                     body: {}
@@ -170,8 +174,10 @@ export default {
                     body: data
                 });
                 if(!res.err && res.data.code > 0) {
+                    let list = dataSource.list;
+                    list.unshift(model);
+                    yield put({ type: 'list', data: list });
                     message.success('新增成功!');
-                    yield put({ type: 'fetchList' });
                 }else {
                     message.error('新增失败!');
                 }
@@ -363,8 +369,10 @@ export default {
     },
     subscriptions: {
         setup({ dispatch, history }) {
-            dispatch({ type: 'fetchList' })
             return history.listen(({ pathname, query }) => {
+                if(pathname === '/datasource' || pathname.match(/chart\/(\w+)/)) {
+                    dispatch({ type: 'fetchList' });
+                }
                 let detail = pathname.match(/datasource\/(\w+)\/(\w+)/);
                 if(detail) {
                     if(pathname.match(/datasource\/(\w+)\/(\w+)\/(\w+)/)) {

+ 6 - 0
src/models/main.js

@@ -1,4 +1,5 @@
 import { routerRedux } from 'dva/router'
+import { message } from 'antd'
 
 export default {
     namespace: 'main',
@@ -18,6 +19,11 @@ export default {
     },
     subscriptions: {
         setup({ dispatch, history }) {
+            message.config({
+                top: 60,
+                duration: 2,
+                maxCount: 3,
+            });
             return history.listen(({ pathname, query }) => {
                 let page = pathname.match(/\/(\w*)/)[1];
                 dispatch({ type: 'setPage', page });